@monadeo.com/grimoire-mcp 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +5 -1
- package/dist/index.js.map +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -31,9 +31,13 @@ var TOOLS = [
|
|
|
31
31
|
max_response_tokens: z.number().optional().describe("Cap response size to control context bloat")
|
|
32
32
|
},
|
|
33
33
|
async handler(args) {
|
|
34
|
+
const sources = sourcesFromArg(args.sources);
|
|
35
|
+
if (sources.length === 0) {
|
|
36
|
+
return 'No sources specified and this project has no defaults. Call list_sources to discover what is indexed, then retry with sources: ["<source_id>"].';
|
|
37
|
+
}
|
|
34
38
|
const res = await client.search({
|
|
35
39
|
query: String(args.query),
|
|
36
|
-
sources
|
|
40
|
+
sources,
|
|
37
41
|
language: args.language,
|
|
38
42
|
max_response_tokens: args.max_response_tokens ?? loadGlobalConfig().maxResponseTokens
|
|
39
43
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/tools.ts"],
|
|
4
|
-
"sourcesContent": ["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport express from \"express\";\nimport { ApiError } from \"@monadeo.com/grimoire-core\";\nimport { TOOLS } from \"./tools.js\";\n\nfunction buildServer(): McpServer {\n const server = new McpServer(\n { name: \"grimoire\", version: \"0.1.0\" },\n {\n instructions:\n \"Use grimoire for any question about a library, framework, or tool \u2014 even when you think you know the answer, to get version-correct docs. Not for refactoring, business logic, or code review.\",\n },\n );\n\n for (const tool of TOOLS) {\n server.registerTool(\n tool.name,\n { description: tool.description, inputSchema: tool.schema },\n async (rawArgs: Record<string, unknown>) => {\n try {\n const text = await tool.handler(rawArgs);\n return { content: [{ type: \"text\" as const, text }] };\n } catch (err) {\n const message =\n err instanceof ApiError\n ? err.status === 401\n ? \"Not authenticated \u2014 the user should run `grimoire login`.\"\n : err.status === 429\n ? \"Quota or rate limit reached \u2014 inform the user; do not retry.\"\n : err.status === 404\n ? `Not found: ${JSON.stringify(err.body)}`\n : `Grimoire error: ${err.code}`\n : `Error: ${(err as Error).message}`;\n return { content: [{ type: \"text\" as const, text: message }], isError: true };\n }\n },\n );\n }\n return server;\n}\n\nasync function runStdio(): Promise<void> {\n await buildServer().connect(new StdioServerTransport());\n}\n\nfunction runHttp(port: number): void {\n const app = express();\n app.use(express.json());\n // Fresh server + transport per request; session lifecycle owned by the client.\n app.post(\"/mcp\", async (req, res) => {\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableDnsRebindingProtection: true,\n allowedHosts: [\"127.0.0.1\", `127.0.0.1:${port}`, \"localhost\", `localhost:${port}`],\n });\n const server = buildServer();\n res.on(\"close\", () => {\n void transport.close();\n void server.close();\n });\n await server.connect(transport);\n await transport.handleRequest(req, res, req.body);\n });\n app.get(\"/mcp\", (_req, res) => res.status(405).end()); // no server-initiated SSE\n app.listen(port, \"127.0.0.1\");\n}\n\nif (process.argv.includes(\"--http\")) {\n runHttp(Number(process.env.PORT ?? 8080));\n} else {\n void runStdio();\n}\n", "import { z } from \"zod\";\nimport { GrimoireClient, resolveDefaultSources, loadGlobalConfig } from \"@monadeo.com/grimoire-core\";\n\nconst client = new GrimoireClient();\n\nfunction sourcesFromArg(arg: unknown): { source: string; version?: string }[] {\n if (Array.isArray(arg)) {\n return arg.map((s) =>\n typeof s === \"string\"\n ? { source: s.split(\"@\")[0], version: s.split(\"@\")[1] }\n : (s as { source: string; version?: string }),\n );\n }\n return resolveDefaultSources();\n}\n\nexport interface ToolDef {\n name: string;\n description: string;\n schema: z.ZodRawShape;\n handler: (args: Record<string, unknown>) => Promise<string>;\n}\n\nexport const TOOLS: ToolDef[] = [\n {\n name: \"search\",\n description:\n \"Query technical documentation. One concept per query \u2014 iterate with refined queries rather than raising top_k. Specify sources and versions; omit version for latest. If the response says confidence is weak, the documentation likely does not cover the topic \u2014 tell the user rather than guessing. Good: 'revalidateTag on-demand cache invalidation'. Bad (too broad): 'routing and auth and caching'.\",\n schema: {\n query: z.string().describe(\"One documentation concept to look up\"),\n sources: z\n .array(z.string())\n .optional()\n .describe(\"Source ids, optionally source@version; omit to use project defaults\"),\n language: z.string().optional(),\n max_response_tokens: z.number().optional().describe(\"Cap response size to control context bloat\"),\n },\n async handler(args) {\n const res = await client.search({\n query: String(args.query),\n sources
|
|
5
|
-
"mappings": ";;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,qCAAqC;AAC9C,OAAO,aAAa;AACpB,SAAS,gBAAgB;;;ACJzB,SAAS,SAAS;AAClB,SAAS,gBAAgB,uBAAuB,wBAAwB;AAExE,IAAM,SAAS,IAAI,eAAe;AAElC,SAAS,eAAe,KAAsD;AAC5E,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI;AAAA,MAAI,CAAC,MACd,OAAO,MAAM,WACT,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,EAAE,IACnD;AAAA,IACP;AAAA,EACF;AACA,SAAO,sBAAsB;AAC/B;AASO,IAAM,QAAmB;AAAA,EAC9B;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO,EAAE,OAAO,EAAE,SAAS,sCAAsC;AAAA,MACjE,SAAS,EACN,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,qEAAqE;AAAA,MACjF,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,MAC9B,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,IAClG;AAAA,IACA,MAAM,QAAQ,MAAM;AAClB,YAAM,MAAM,MAAM,OAAO,OAAO;AAAA,QAC9B,OAAO,OAAO,KAAK,KAAK;AAAA,QACxB
|
|
4
|
+
"sourcesContent": ["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport express from \"express\";\nimport { ApiError } from \"@monadeo.com/grimoire-core\";\nimport { TOOLS } from \"./tools.js\";\n\nfunction buildServer(): McpServer {\n const server = new McpServer(\n { name: \"grimoire\", version: \"0.1.0\" },\n {\n instructions:\n \"Use grimoire for any question about a library, framework, or tool \u2014 even when you think you know the answer, to get version-correct docs. Not for refactoring, business logic, or code review.\",\n },\n );\n\n for (const tool of TOOLS) {\n server.registerTool(\n tool.name,\n { description: tool.description, inputSchema: tool.schema },\n async (rawArgs: Record<string, unknown>) => {\n try {\n const text = await tool.handler(rawArgs);\n return { content: [{ type: \"text\" as const, text }] };\n } catch (err) {\n const message =\n err instanceof ApiError\n ? err.status === 401\n ? \"Not authenticated \u2014 the user should run `grimoire login`.\"\n : err.status === 429\n ? \"Quota or rate limit reached \u2014 inform the user; do not retry.\"\n : err.status === 404\n ? `Not found: ${JSON.stringify(err.body)}`\n : `Grimoire error: ${err.code}`\n : `Error: ${(err as Error).message}`;\n return { content: [{ type: \"text\" as const, text: message }], isError: true };\n }\n },\n );\n }\n return server;\n}\n\nasync function runStdio(): Promise<void> {\n await buildServer().connect(new StdioServerTransport());\n}\n\nfunction runHttp(port: number): void {\n const app = express();\n app.use(express.json());\n // Fresh server + transport per request; session lifecycle owned by the client.\n app.post(\"/mcp\", async (req, res) => {\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableDnsRebindingProtection: true,\n allowedHosts: [\"127.0.0.1\", `127.0.0.1:${port}`, \"localhost\", `localhost:${port}`],\n });\n const server = buildServer();\n res.on(\"close\", () => {\n void transport.close();\n void server.close();\n });\n await server.connect(transport);\n await transport.handleRequest(req, res, req.body);\n });\n app.get(\"/mcp\", (_req, res) => res.status(405).end()); // no server-initiated SSE\n app.listen(port, \"127.0.0.1\");\n}\n\nif (process.argv.includes(\"--http\")) {\n runHttp(Number(process.env.PORT ?? 8080));\n} else {\n void runStdio();\n}\n", "import { z } from \"zod\";\nimport { GrimoireClient, resolveDefaultSources, loadGlobalConfig } from \"@monadeo.com/grimoire-core\";\n\nconst client = new GrimoireClient();\n\nfunction sourcesFromArg(arg: unknown): { source: string; version?: string }[] {\n if (Array.isArray(arg)) {\n return arg.map((s) =>\n typeof s === \"string\"\n ? { source: s.split(\"@\")[0], version: s.split(\"@\")[1] }\n : (s as { source: string; version?: string }),\n );\n }\n return resolveDefaultSources();\n}\n\nexport interface ToolDef {\n name: string;\n description: string;\n schema: z.ZodRawShape;\n handler: (args: Record<string, unknown>) => Promise<string>;\n}\n\nexport const TOOLS: ToolDef[] = [\n {\n name: \"search\",\n description:\n \"Query technical documentation. One concept per query \u2014 iterate with refined queries rather than raising top_k. Specify sources and versions; omit version for latest. If the response says confidence is weak, the documentation likely does not cover the topic \u2014 tell the user rather than guessing. Good: 'revalidateTag on-demand cache invalidation'. Bad (too broad): 'routing and auth and caching'.\",\n schema: {\n query: z.string().describe(\"One documentation concept to look up\"),\n sources: z\n .array(z.string())\n .optional()\n .describe(\"Source ids, optionally source@version; omit to use project defaults\"),\n language: z.string().optional(),\n max_response_tokens: z.number().optional().describe(\"Cap response size to control context bloat\"),\n },\n async handler(args) {\n const sources = sourcesFromArg(args.sources);\n // Guide the agent instead of surfacing the API's bare 400: this happens on\n // every search outside a configured project until it learns the pattern.\n if (sources.length === 0) {\n return \"No sources specified and this project has no defaults. Call list_sources to discover what is indexed, then retry with sources: [\\\"<source_id>\\\"].\";\n }\n const res = await client.search({\n query: String(args.query),\n sources,\n language: args.language as string | undefined,\n max_response_tokens:\n (args.max_response_tokens as number | undefined) ?? loadGlobalConfig().maxResponseTokens,\n });\n if (res.results.length === 0) return \"No results. The documentation likely does not cover this.\";\n const header =\n res.confidence === \"weak\"\n ? \"CONFIDENCE: WEAK \u2014 the docs may not cover this; say so rather than guessing.\\n\\n\"\n : \"\";\n return (\n header +\n res.results\n .map(\n (r) =>\n `## ${(r.heading_path ?? []).join(\" \u203A \")} (${r.source}@${r.version})\\nSource: ${r.origin_url}\\nchunk_id: ${r.chunk_id}\\n\\n${r.text}`,\n )\n .join(\"\\n\\n---\\n\\n\")\n );\n },\n },\n {\n name: \"fetch_document\",\n description: \"Expand context around a search hit by chunk id (neighbors in the same section).\",\n schema: { chunk_id: z.string(), window: z.number().int().min(0).max(5).optional() },\n async handler(args) {\n const res = await client.getContext(String(args.chunk_id), (args.window as number) ?? 2);\n return JSON.stringify(res.chunks ?? [], null, 2);\n },\n },\n {\n name: \"list_sources\",\n description: \"Discover indexed documentation sources; supports alias text search.\",\n schema: { q: z.string().optional() },\n async handler(args) {\n return JSON.stringify((await client.listSources(args.q as string | undefined)).sources ?? [], null, 2);\n },\n },\n {\n name: \"list_versions\",\n description: \"List indexed versions of a source.\",\n schema: { source: z.string() },\n async handler(args) {\n return JSON.stringify((await client.listVersions(String(args.source))).versions ?? [], null, 2);\n },\n },\n {\n name: \"report_result\",\n description:\n \"Report a retrieved chunk as helpful, incorrect, or outdated \u2014 use when documentation contradicted observed behavior.\",\n schema: {\n chunk_id: z.string(),\n verdict: z.enum([\"helpful\", \"incorrect\", \"outdated\"]),\n note: z.string().optional(),\n },\n async handler(args) {\n await client.reportResult(String(args.chunk_id), String(args.verdict), args.note as string | undefined);\n return \"Reported. Thank you \u2014 this feeds the quality benchmark.\";\n },\n },\n];\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monadeo.com/grimoire-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"@modelcontextprotocol/sdk": "^1.24.0",
|
|
14
14
|
"express": "^5.2.1",
|
|
15
15
|
"zod": "^4.1.12",
|
|
16
|
-
"@monadeo.com/grimoire-core": "0.2.
|
|
16
|
+
"@monadeo.com/grimoire-core": "0.2.3"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/express": "^5.0.6",
|