UNPKG

@apideck/docs-mcp

Version:

MCP server that lets agents search and retrieve your documentation at query time instead of relying on stale training data.

46 lines • 2.46 kB
// SPDX-License-Identifier: MIT import { McpServer, ResourceTemplate } from '@modelcontextprotocol/server'; import { z } from 'zod'; import { createDocTools } from './tools.js'; const instructionsFor = (about, extraTools) => [ `This server exposes ${about ?? 'a documentation set'} as read-only, always-current content.`, 'Prefer it over training data when answering questions about this product.', 'Workflow: call search_docs with a few specific terms, then get_doc on the best path (optionally with a section anchor) to read the actual text before answering.', 'Use list_docs to browse structure when a search returns nothing useful.', extraTools.length > 0 ? `Additional tools for this documentation set: ${extraTools.map((t) => t.name).join(', ')}.` : '', ] .filter(Boolean) .join(' '); export const createServer = (opts) => { const extraTools = opts.extraTools ?? []; const server = new McpServer({ name: opts.name ?? 'docs-mcp', version: opts.version ?? process.env['npm_package_version'] ?? '0.0.0' }, { capabilities: { tools: {}, resources: {} }, instructions: instructionsFor(opts.about, extraTools) }); for (const tool of [...createDocTools(opts.store), ...extraTools]) { server.registerTool(tool.name, { title: tool.title, description: tool.description, inputSchema: z.object(tool.inputSchema), annotations: tool.annotations, }, async (args) => ({ ...(await tool.handler(args)) })); } server.registerResource('doc', new ResourceTemplate('docs://{+path}', { list: async () => ({ resources: opts.store.listPages().map((p) => ({ uri: `docs://${p.path}`, name: p.title, mimeType: 'text/markdown', ...(p.description !== undefined ? { description: p.description } : {}), })), }), }), { title: 'Documentation page', description: 'Markdown source of one documentation page.', mimeType: 'text/markdown' }, async (uri, variables) => { const raw = variables['path']; const ref = Array.isArray(raw) ? raw.join('/') : (raw ?? ''); const page = opts.store.get(ref); if (!page) throw new Error(`No page at ${uri.href}`); return { contents: [{ uri: uri.href, mimeType: 'text/markdown', text: page.body.trim() }] }; }); return server; }; //# sourceMappingURL=server.js.map