@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
JavaScript
// 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