diff --git a/.mcp.json b/.mcp.json index e44a33d4d68..26ef0abac6d 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,5 +1,15 @@ { "mcpServers": { + "typespec": { + "type": "stdio", + "command": "npx", + "args": ["-y", "mcp-server-typespec@latest"] + }, + "typespec-docs": { + "type": "stdio", + "command": "node", + "args": ["./packages/mcp-server-typespec-docs/dist/server.js"] + }, "github": { "type": "http", "url": "https://api.githubcopilot.com/mcp", diff --git a/packages/mcp-server-typespec-docs/.gitignore b/packages/mcp-server-typespec-docs/.gitignore new file mode 100644 index 00000000000..1eae0cf6700 --- /dev/null +++ b/packages/mcp-server-typespec-docs/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/packages/mcp-server-typespec-docs/package.json b/packages/mcp-server-typespec-docs/package.json new file mode 100644 index 00000000000..9df3931d5ef --- /dev/null +++ b/packages/mcp-server-typespec-docs/package.json @@ -0,0 +1,29 @@ +{ + "name": "@pinterest-tools/mcp-server-typespec-docs", + "version": "0.1.0", + "type": "module", + "description": "MCP server for querying TypeSpec documentation and API signatures", + "bin": { + "mcp-server-typespec-docs": "./dist/server.js" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc -p tsconfig.json", + "start": "node dist/server.js" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "minisearch": "^7.1.2", + "zod": "^3.24.4", + "gray-matter": "^4.0.3" + }, + "devDependencies": { + "typescript": "~5.8.3", + "@types/node": "^22.0.0" + }, + "engines": { + "node": ">=20.0.0" + } +} diff --git a/packages/mcp-server-typespec-docs/src/indexer.ts b/packages/mcp-server-typespec-docs/src/indexer.ts new file mode 100644 index 00000000000..fafcca31393 --- /dev/null +++ b/packages/mcp-server-typespec-docs/src/indexer.ts @@ -0,0 +1,291 @@ +import { existsSync, readFileSync, readdirSync } from "fs"; +import matter from "gray-matter"; +import MiniSearch from "minisearch"; +import { join, relative } from "path"; + +const REPO = "pinterest/typespec"; +const BRANCH = "main"; +const DOCS_PREFIX = "website/src/content/docs/docs/"; +const COMPILER_PREFIX = "packages/compiler/src/"; +const RAW_BASE = `https://raw.githubusercontent.com/${REPO}/${BRANCH}/`; + +export interface DocEntry { + id: string; + path: string; + title: string; + topic: string; + headings: string[]; + content: string; +} + +let cachedIndex: MiniSearch | null = null; +let cachedDocs: Map | null = null; +let cachedCompilerFiles: Map | null = null; + +function getLocalDocsRoot(): string | null { + const paths = [ + join(import.meta.dirname, "..", "assets", "docs"), + join(import.meta.dirname, "..", "..", "..", "website", "src", "content", "docs", "docs"), + ]; + for (const p of paths) { + if (existsSync(p)) return p; + } + return null; +} + +function getLocalCompilerSrcRoot(): string | null { + const paths = [ + join(import.meta.dirname, "..", "assets", "compiler-src"), + join(import.meta.dirname, "..", "..", "..", "packages", "compiler", "src"), + ]; + for (const p of paths) { + if (existsSync(p)) return p; + } + return null; +} + +function walkDir(dir: string, ext: string[]): string[] { + const results: string[] = []; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + results.push(...walkDir(fullPath, ext)); + } else if (ext.some((e) => entry.name.endsWith(e))) { + results.push(fullPath); + } + } + return results; +} + +async function fetchGitHubTree(): Promise { + const url = `https://api.github.com/repos/${REPO}/git/trees/${BRANCH}?recursive=1`; + const res = await fetch(url); + if (!res.ok) throw new Error(`GitHub API error: ${res.status}`); + const data = (await res.json()) as { tree: Array<{ path: string; type: string }> }; + return data.tree.filter((t) => t.type === "blob").map((t) => t.path); +} + +async function fetchFileContent(repoPath: string): Promise { + const res = await fetch(`${RAW_BASE}${repoPath}`); + if (!res.ok) throw new Error(`Failed to fetch ${repoPath}: ${res.status}`); + return res.text(); +} + +function extractTopic(relPath: string): string { + const first = relPath.split("/")[0]; + return first ?? "other"; +} + +function extractHeadings(content: string): string[] { + const headings: string[] = []; + for (const line of content.split("\n")) { + const match = line.match(/^#{1,4}\s+(.+)/); + if (match) headings.push(match[1]); + } + return headings; +} + +function buildIndexFromLocal(docsRoot: string): { + index: MiniSearch; + docs: Map; +} { + const files = walkDir(docsRoot, [".md", ".mdx"]); + const docs = new Map(); + + for (const file of files) { + const relPath = relative(docsRoot, file); + if (relPath.startsWith("release-notes/")) continue; + + const raw = readFileSync(file, "utf-8"); + const { data: frontmatter, content } = matter(raw); + + const entry: DocEntry = { + id: relPath, + path: relPath, + title: (frontmatter.title as string) ?? relPath, + topic: extractTopic(relPath), + headings: extractHeadings(content), + content, + }; + docs.set(relPath, entry); + } + + const index = new MiniSearch({ + fields: ["title", "headings", "content"], + storeFields: ["path", "title", "topic"], + searchOptions: { boost: { title: 3, headings: 2 }, fuzzy: 0.2, prefix: true }, + }); + index.addAll(Array.from(docs.values())); + return { index, docs }; +} + +async function buildIndexFromGitHub(): Promise<{ + index: MiniSearch; + docs: Map; +}> { + const tree = await fetchGitHubTree(); + const docPaths = tree.filter( + (p) => + p.startsWith(DOCS_PREFIX) && + (p.endsWith(".md") || p.endsWith(".mdx")) && + !p.includes("release-notes/"), + ); + + const docs = new Map(); + const fetches = docPaths.map(async (fullPath) => { + const relPath = fullPath.slice(DOCS_PREFIX.length); + const raw = await fetchFileContent(fullPath); + const { data: frontmatter, content } = matter(raw); + + const entry: DocEntry = { + id: relPath, + path: relPath, + title: (frontmatter.title as string) ?? relPath, + topic: extractTopic(relPath), + headings: extractHeadings(content), + content, + }; + docs.set(relPath, entry); + }); + + await Promise.all(fetches); + + const index = new MiniSearch({ + fields: ["title", "headings", "content"], + storeFields: ["path", "title", "topic"], + searchOptions: { boost: { title: 3, headings: 2 }, fuzzy: 0.2, prefix: true }, + }); + index.addAll(Array.from(docs.values())); + return { index, docs }; +} + +async function ensureIndex(): Promise<{ + index: MiniSearch; + docs: Map; +}> { + if (cachedIndex && cachedDocs) return { index: cachedIndex, docs: cachedDocs }; + + const localRoot = getLocalDocsRoot(); + const result = localRoot ? buildIndexFromLocal(localRoot) : await buildIndexFromGitHub(); + + cachedIndex = result.index; + cachedDocs = result.docs; + return result; +} + +async function ensureCompilerFiles(): Promise> { + if (cachedCompilerFiles) return cachedCompilerFiles; + + const localRoot = getLocalCompilerSrcRoot(); + if (localRoot) { + const files = new Map(); + for (const subdir of ["typekit/kits", "typekit", "core", "experimental"]) { + const dir = join(localRoot, subdir); + if (!existsSync(dir)) continue; + for (const file of walkDir(dir, [".ts"])) { + const relPath = relative(localRoot, file); + files.set(relPath, readFileSync(file, "utf-8")); + } + } + cachedCompilerFiles = files; + return files; + } + + const tree = await fetchGitHubTree(); + const compilerPaths = tree.filter( + (p) => + p.startsWith(COMPILER_PREFIX) && + p.endsWith(".ts") && + (p.includes("/typekit/") || p.includes("/core/") || p.includes("/experimental/")), + ); + + const files = new Map(); + const fetches = compilerPaths.map(async (fullPath) => { + const relPath = fullPath.slice(COMPILER_PREFIX.length); + const content = await fetchFileContent(fullPath); + files.set(relPath, content); + }); + await Promise.all(fetches); + + cachedCompilerFiles = files; + return files; +} + +export async function searchDocs( + query: string, + topic?: string, + maxResults: number = 3, +): Promise { + const { index, docs } = await ensureIndex(); + + let results = index.search(query); + if (topic) { + results = results.filter((r) => r.topic === topic); + } + + return results + .slice(0, maxResults) + .map((r) => docs.get(r.id)!) + .filter(Boolean); +} + +export async function getDocByPath(path: string): Promise { + const { docs } = await ensureIndex(); + return docs.get(path); +} + +export async function getTypeSignature(symbol: string): Promise { + const files = await ensureCompilerFiles(); + const results: string[] = []; + + for (const [relPath, content] of files) { + const lines = content.split("\n"); + + for (let i = 0; i < lines.length; i++) { + const trimmed = lines[i].trimStart(); + if (trimmed.startsWith("*") || trimmed.startsWith("//")) continue; + const symbolPattern = new RegExp( + `\\b(export\\s+)?(interface|type|function|const|class)\\s+${escapeRegex(symbol)}\\b`, + ); + if (!symbolPattern.test(lines[i])) continue; + + let start = i; + while ( + start > 0 && + (lines[start - 1].trimStart().startsWith("*") || + lines[start - 1].trimStart().startsWith("/**") || + lines[start - 1].trimStart().startsWith("//") || + lines[start - 1].trim() === "") + ) { + start--; + } + if (lines[start].trim() === "") start++; + + let end = i; + let braceCount = 0; + let started = false; + while (end < lines.length) { + for (const ch of lines[end]) { + if (ch === "{" || ch === "(") { + braceCount++; + started = true; + } + if (ch === "}" || ch === ")") braceCount--; + } + if (started && braceCount <= 0) break; + if (!started && (lines[end].endsWith(";") || lines[end].endsWith(","))) break; + end++; + } + + const block = lines.slice(start, end + 1).join("\n"); + results.push(`// ${relPath}:${start + 1}\n${block}`); + break; + } + } + + return results.length > 0 ? results.join("\n\n---\n\n") : null; +} + +function escapeRegex(s: string): string { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} diff --git a/packages/mcp-server-typespec-docs/src/server.ts b/packages/mcp-server-typespec-docs/src/server.ts new file mode 100644 index 00000000000..807752ac199 --- /dev/null +++ b/packages/mcp-server-typespec-docs/src/server.ts @@ -0,0 +1,103 @@ +#!/usr/bin/env node +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { z } from "zod"; +import { getDocByPath, getTypeSignature, searchDocs } from "./indexer.js"; + +const server = new McpServer({ + name: "typespec-docs", + version: "0.1.0", +}); + +server.registerTool( + "search_docs", + { + description: + "Search TypeSpec documentation by query. Returns full content of matching doc pages. Use this to find information about TypeSpec language features, libraries, emitters, decorators, and how to extend TypeSpec.", + inputSchema: { + query: z + .string() + .describe( + "Search query (e.g., 'Realm', 'union composition', 'create decorator', 'emitter framework')", + ), + topic: z + .optional( + z.enum([ + "language-basics", + "extending-typespec", + "libraries", + "emitters", + "getting-started", + "standard-library", + "handbook", + ]), + ) + .describe("Optional: filter results to a specific documentation section"), + }, + }, + async ({ query, topic }) => { + const results = await searchDocs(query, topic); + if (results.length === 0) { + return { + content: [{ type: "text", text: `No documentation found for: "${query}"` }], + }; + } + const text = results + .map((doc) => `# ${doc.title}\n\n\n${doc.content}`) + .join("\n\n---\n\n"); + return { content: [{ type: "text", text }] }; + }, +); + +server.registerTool( + "get_doc", + { + description: + "Fetch a specific TypeSpec documentation page by its path. Use this when you already know which doc page you need.", + inputSchema: { + path: z + .string() + .describe( + "Relative path within the docs directory (e.g., 'extending-typespec/create-decorators.md', 'libraries/http/operations.md')", + ), + }, + }, + async ({ path }) => { + const doc = await getDocByPath(path); + if (!doc) { + return { + content: [{ type: "text", text: `Document not found: "${path}"` }], + }; + } + return { + content: [{ type: "text", text: `# ${doc.title}\n\n${doc.content}` }], + }; + }, +); + +server.registerTool( + "get_type_signature", + { + description: + "Get the TypeScript type definition and JSDoc for a TypeSpec compiler API symbol. Use this to find exact signatures of compiler APIs, typekit methods, and experimental features like Realm and mutators.", + inputSchema: { + symbol: z + .string() + .describe( + "Name of the compiler API symbol (e.g., 'Model', 'mutateSubgraph', 'defineKit', 'Realm')", + ), + }, + }, + async ({ symbol }) => { + const result = await getTypeSignature(symbol); + if (!result) { + return { + content: [{ type: "text", text: `Symbol not found: "${symbol}"` }], + }; + } + return { content: [{ type: "text", text: result }] }; + }, +); + +const transport = new StdioServerTransport(); +await server.connect(transport); diff --git a/packages/mcp-server-typespec-docs/tsconfig.json b/packages/mcp-server-typespec-docs/tsconfig.json new file mode 100644 index 00000000000..c2524e50de1 --- /dev/null +++ b/packages/mcp-server-typespec-docs/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "resolveJsonModule": true + }, + "include": ["src/**/*.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8eb10c54b4f..97ed3c463ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1627,6 +1627,28 @@ importers: specifier: 'catalog:' version: 4.1.3(@types/node@25.5.2)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.1.3)(happy-dom@20.8.9)(jsdom@25.0.1)(vite@8.0.7(@types/node@25.5.2)(esbuild@0.28.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + packages/mcp-server-typespec-docs: + dependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.12.1 + version: 1.29.0(zod@3.25.76) + gray-matter: + specifier: ^4.0.3 + version: 4.0.3 + minisearch: + specifier: ^7.1.2 + version: 7.2.0 + zod: + specifier: ^3.24.4 + version: 3.25.76 + devDependencies: + '@types/node': + specifier: ^22.0.0 + version: 22.19.19 + typescript: + specifier: ~5.8.3 + version: 5.8.3 + packages/monarch: dependencies: monaco-editor-core: @@ -4789,6 +4811,12 @@ packages: resolution: {integrity: sha512-7bQW+gkKa2kKZPeJf6+c6gFK9ARxQfn+FKy9ScTBppyKRWH2KzsmweXUoklqeEiHiNVWaeP5csIdsNq6w7QhzA==} engines: {node: '>=12.20'} + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -5219,6 +5247,16 @@ packages: '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + '@napi-rs/wasm-runtime@1.1.3': resolution: {integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==} peerDependencies: @@ -6887,6 +6925,9 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + '@types/node@22.19.19': + resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==} + '@types/node@24.12.2': resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} @@ -8315,6 +8356,10 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -9070,6 +9115,14 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + eventsource-parser@3.1.0: + resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -9089,6 +9142,12 @@ packages: exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + express-rate-limit@8.5.2: + resolution: {integrity: sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + express@5.2.1: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} @@ -9562,6 +9621,10 @@ packages: resolution: {integrity: sha512-EqYpWyTF2s8nMfttfBA2yLKPNoZCO33pLS4MnbXQ4hECf1TKujCt1Kq7QAdrio7roL4+CqsfjqwYj4tYgq0pJQ==} engines: {node: '>=12.0.0'} + hono@4.12.23: + resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==} + engines: {node: '>=16.9.0'} + hosted-git-info@4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} @@ -9767,6 +9830,10 @@ packages: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -9963,6 +10030,9 @@ packages: jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} @@ -10016,6 +10086,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -10721,6 +10794,9 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + minisearch@7.2.0: + resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} + minizlib@3.1.0: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} @@ -11296,6 +11372,10 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + pkg-dir@3.0.0: resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} engines: {node: '>=6'} @@ -12729,6 +12809,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -12772,6 +12857,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} @@ -13543,6 +13631,11 @@ packages: resolution: {integrity: sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g==} engines: {node: '>=8'} + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} @@ -16153,6 +16246,10 @@ snapshots: '@gwhitney/detect-indent@7.0.1': {} + '@hono/node-server@1.19.14(hono@4.12.23)': + dependencies: + hono: 4.12.23 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -16623,6 +16720,28 @@ snapshots: '@microsoft/tsdoc@0.16.0': {} + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.23) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.1.0 + express: 5.2.1 + express-rate-limit: 8.5.2(express@5.2.1) + hono: 4.12.23 + jose: 6.2.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + '@napi-rs/wasm-runtime@1.1.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': dependencies: '@emnapi/core': 1.9.1 @@ -18654,7 +18773,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/braces@3.0.5': {} @@ -18662,7 +18781,7 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.2.0 '@types/keyv': 3.1.4 - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/responselike': 1.0.3 '@types/chai@5.2.3': @@ -18672,7 +18791,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/cross-spawn@6.0.6': dependencies: @@ -18817,7 +18936,7 @@ snapshots: '@types/express-serve-static-core@5.1.1': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/qs': 6.15.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -18846,7 +18965,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/mdast@4.0.4': dependencies: @@ -18878,6 +18997,10 @@ snapshots: dependencies: undici-types: 5.26.5 + '@types/node@22.19.19': + dependencies: + undici-types: 6.21.0 + '@types/node@24.12.2': dependencies: undici-types: 7.16.0 @@ -18890,7 +19013,7 @@ snapshots: '@types/plist@3.0.5': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 xmlbuilder: 15.1.1 '@types/prismjs@1.26.6': {} @@ -18915,28 +19038,28 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/sarif@2.1.7': {} '@types/sax@1.2.7': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/semver@7.7.1': {} '@types/send@1.2.1': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/ssri@7.1.5': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/swagger-ui-dist@3.30.6': {} @@ -18964,7 +19087,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/yargs-parser@21.0.3': {} @@ -19633,7 +19756,7 @@ snapshots: algoliasearch: 4.27.0 clipanion: 4.0.0-rc.4(typanion@3.14.0) diff: 5.2.2 - ink: 3.2.0(@types/react@19.2.14)(react@17.0.2) + ink: 3.2.0(@types/react@19.2.14)(react@19.2.5) ink-text-input: 4.0.3(ink@3.2.0(@types/react@19.2.14)(react@17.0.2))(react@17.0.2) react: 17.0.2 semver: 7.7.4 @@ -19783,7 +19906,7 @@ snapshots: '@yarnpkg/plugin-git': 3.1.4(@yarnpkg/core@4.6.0(typanion@3.14.0))(typanion@3.14.0) clipanion: 4.0.0-rc.4(typanion@3.14.0) es-toolkit: 1.45.1 - ink: 3.2.0(@types/react@19.2.14)(react@19.2.5) + ink: 3.2.0(@types/react@19.2.14)(react@17.0.2) react: 17.0.2 semver: 7.7.4 tslib: 2.8.1 @@ -20737,6 +20860,11 @@ snapshots: core-util-is@1.0.3: {} + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -21488,8 +21616,8 @@ snapshots: '@babel/parser': 7.29.0 eslint: 10.2.0 hermes-parser: 0.25.1 - zod: 4.3.6 - zod-validation-error: 4.0.2(zod@4.3.6) + zod: 3.25.76 + zod-validation-error: 4.0.2(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -21630,6 +21758,12 @@ snapshots: events@3.3.0: {} + eventsource-parser@3.1.0: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.1.0 + execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -21664,6 +21798,11 @@ snapshots: exponential-backoff@3.1.3: {} + express-rate-limit@8.5.2(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.2.0 + express@5.2.1: dependencies: accepts: 2.0.0 @@ -22119,7 +22258,7 @@ snapshots: happy-dom@20.8.9: dependencies: - '@types/node': 25.5.2 + '@types/node': 22.19.19 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 entities: 7.0.1 @@ -22355,6 +22494,8 @@ snapshots: highlight.js@11.0.1: {} + hono@4.12.23: {} + hosted-git-info@4.1.0: dependencies: lru-cache: 6.0.0 @@ -22593,6 +22734,8 @@ snapshots: ip-address@10.1.0: {} + ip-address@10.2.0: {} + ipaddr.js@1.9.1: {} iron-webcrypto@1.2.1: {} @@ -22735,6 +22878,8 @@ snapshots: jju@1.4.0: {} + jose@6.2.3: {} + js-tokens@10.0.0: {} js-tokens@4.0.0: {} @@ -22813,6 +22958,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-stable-stringify-without-jsonify@1.0.1: {} json-with-bigint@3.5.7: {} @@ -23824,6 +23971,8 @@ snapshots: minipass@7.1.3: {} + minisearch@7.2.0: {} + minizlib@3.1.0: dependencies: minipass: 7.1.3 @@ -24435,6 +24584,8 @@ snapshots: pirates@4.0.7: {} + pkce-challenge@5.0.1: {} + pkg-dir@3.0.0: dependencies: find-up: 3.0.0 @@ -26134,6 +26285,8 @@ snapshots: typescript@5.8.2: {} + typescript@5.8.3: {} + typescript@5.9.3: {} typescript@6.0.2: {} @@ -26158,6 +26311,8 @@ snapshots: undici-types@5.26.5: {} + undici-types@6.21.0: {} + undici-types@7.16.0: {} undici-types@7.18.2: {} @@ -26787,9 +26942,13 @@ snapshots: dependencies: '@types/yoga-layout': 1.9.2 - zod-validation-error@4.0.2(zod@4.3.6): + zod-to-json-schema@3.25.2(zod@3.25.76): dependencies: - zod: 4.3.6 + zod: 3.25.76 + + zod-validation-error@4.0.2(zod@3.25.76): + dependencies: + zod: 3.25.76 zod@3.25.76: {}