feat(TRUEREF-0011): implement MCP server with stdio transport
Adds a Model Context Protocol server that exposes resolve-library-id and query-docs tools via stdio, with tool schemas identical to context7 for drop-in compatibility with Claude Code, Cursor, and Zed. - src/mcp/index.ts — server entry point (io.github.trueref/trueref) - src/mcp/client.ts — HTTP client for TrueRef REST API (TRUEREF_API_URL) - src/mcp/tools/resolve-library-id.ts — library search tool handler - src/mcp/tools/query-docs.ts — documentation retrieval tool handler - src/mcp/index.test.ts — integration tests spawning real server subprocess - .claude/rules/trueref.md — Claude Code rule file for MCP usage - package.json: mcp:start script using tsx Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
98
src/mcp/tools/query-docs.ts
Normal file
98
src/mcp/tools/query-docs.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* query-docs tool handler
|
||||
*
|
||||
* Fetches documentation and code examples from TrueRef for a specific library.
|
||||
* Tool schema is identical to context7 for drop-in compatibility.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
import { fetchContext } from '../client.js';
|
||||
|
||||
export const QueryDocsSchema = z.object({
|
||||
libraryId: z
|
||||
.string()
|
||||
.describe('The TrueRef library ID obtained from resolve-library-id, e.g. /facebook/react'),
|
||||
query: z
|
||||
.string()
|
||||
.describe('Specific question about the library to retrieve relevant documentation'),
|
||||
tokens: z.number().optional().describe('Maximum token budget for the response (default: 10000)')
|
||||
});
|
||||
|
||||
export type QueryDocsInput = z.infer<typeof QueryDocsSchema>;
|
||||
|
||||
export const QUERY_DOCS_TOOL = {
|
||||
name: 'query-docs',
|
||||
description: [
|
||||
'Fetches documentation and code examples from TrueRef for a specific library.',
|
||||
'Requires a library ID obtained from resolve-library-id.',
|
||||
'Returns relevant snippets formatted for LLM consumption.',
|
||||
'Call at most 3 times per user question.'
|
||||
].join(' '),
|
||||
inputSchema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
libraryId: {
|
||||
type: 'string',
|
||||
description: 'TrueRef library ID, e.g. /facebook/react'
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
description: 'Specific question about the library'
|
||||
},
|
||||
tokens: {
|
||||
type: 'number',
|
||||
description: 'Max token budget (default: 10000)'
|
||||
}
|
||||
},
|
||||
required: ['libraryId', 'query']
|
||||
}
|
||||
};
|
||||
|
||||
export async function handleQueryDocs(args: unknown) {
|
||||
const { libraryId, query, tokens } = QueryDocsSchema.parse(args);
|
||||
|
||||
const response = await fetchContext({ libraryId, query, tokens, type: 'txt' });
|
||||
|
||||
if (!response.ok) {
|
||||
const status = response.status;
|
||||
|
||||
if (status === 404) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text' as const,
|
||||
text: `Library "${libraryId}" not found. Please run resolve-library-id first.`
|
||||
}
|
||||
],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
if (status === 503) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text' as const,
|
||||
text: `Library "${libraryId}" is currently being indexed. Please try again in a moment.`
|
||||
}
|
||||
],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text' as const,
|
||||
text: `Error fetching documentation: ${response.status} ${response.statusText}`
|
||||
}
|
||||
],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
return {
|
||||
content: [{ type: 'text' as const, text }]
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user