@extenshi/mcp
Version:
Extenshi MCP server — bring the extension catalog (search, security, market research) and pre-publish scanning into your AI tools
90 lines • 4.17 kB
TypeScript
/**
* Documentation access for the Extenshi MCP server (`search_docs` tool).
*
* Teaches the assistant to consult the LIVE extenshi.io documentation —
* including the full `@extenshi/cli` command reference — so it can answer
* "how do I…" questions and quote exact CLI commands instead of guessing.
*
* Source of truth is the docs site's machine-readable export (the llms.txt
* convention), regenerated on every docs deploy:
* - <docsUrl>/llms.txt a one-line index of every page
* - <docsUrl>/llms-full.txt the full text of every page (one H1 per page)
*
* Both are PUBLIC static files — no API key, no metering — so this tool works
* even before a developer has configured a key (handy for guiding setup). We
* fetch lazily, cache in-process with a short TTL, and rank sections locally.
*
* stdout is the MCP protocol channel — this module must NEVER write to it.
*/
/** A failure the tool layer turns into an actionable `UserError`. */
export declare class DocsError extends Error {
constructor(message: string);
}
/** One page-sized chunk of `llms-full.txt`, split on top-level headings. */
export interface DocSection {
title: string;
body: string;
}
/** Clear the cache — test seam, also usable to force a refresh. */
export declare function clearDocsCache(): void;
/**
* Split `llms-full.txt` into per-page sections on top-level (`# `) headings.
* Fence-aware: a `# comment` inside a ``` code block is NOT a boundary, so CLI
* examples never get chopped in half.
*/
export declare function splitSections(full: string): DocSection[];
/**
* Rank sections against a query. A term in the title is worth more than one in
* the body. Pure (no I/O) so it is unit-testable with a fixture.
*/
export declare function rankSections(full: string, query: string, limit: number): DocSection[];
/**
* One searchable passage: a `##` subsection of a docs page (or the page intro
* before its first `##`). Returning passages instead of whole pages keeps a
* search result proportional to the question — a page can run to 8k+ chars,
* while the answer usually lives in one subsection.
*/
export interface DocPassage {
page: string;
heading?: string;
url?: string;
body: string;
}
/** Split `llms-full.txt` into `##`-level passages, fence-aware, carrying the page source URL. */
export declare function splitPassages(full: string): DocPassage[];
/**
* Rank passages against a query with BM25 over passage bodies, so a long
* passage that merely repeats common words does not outrank a short one that
* answers the question. Heading and page-title matches add a bonus; hyphenated
* or long tokens (command and flag names) get a phrase boost. Pure (no I/O) so
* it is unit-testable with a fixture.
*/
export declare function rankPassages(full: string, query: string, limit: number): DocPassage[];
/** Default and bounds for the per-result excerpt length (characters). */
export declare const DEFAULT_EXCERPT_CHARS = 1500;
export declare const MIN_EXCERPT_CHARS = 200;
export declare const MAX_EXCERPT_CHARS = 8000;
/**
* Cut a passage to `maxChars` on a line boundary. An unterminated code fence is
* closed so the rest of the response still renders as prose.
*/
export declare function excerpt(body: string, maxChars: number): {
text: string;
truncated: boolean;
};
/** Fetch the docs index (`llms.txt`) — the page list returned when no query is given. */
export declare function getDocsIndex(docsUrl: string): Promise<string>;
export interface SearchDocsOptions {
/** Max passages returned. */
limit: number;
/** Max characters per passage excerpt. */
maxChars?: number;
}
/** Render ranked passages: page › heading, source URL, bounded excerpt. Pure. */
export declare function renderPassages(query: string, matches: DocPassage[], maxChars: number): string;
/**
* Search the docs and return the top matching passages, each with its source
* link and a bounded excerpt. Falls back to a "nothing matched" note.
*/
export declare function searchDocs(docsUrl: string, query: string, options: SearchDocsOptions): Promise<string>;
//# sourceMappingURL=docs.d.ts.map