From db4dd437bddca5067e64d272c17a663de5ff309b Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 16 Apr 2026 13:37:52 +0200 Subject: [PATCH] =?UTF-8?q?feat(api):=20MCP=20server=20endpoint=20?= =?UTF-8?q?=E2=80=94=20expose=20AI=20tools=20to=20external=20clients?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mount an MCP (Model Context Protocol) server at /api/v1/mcp in the unified Hono API. External clients like Claude Desktop, Cursor, and VS Code Copilot can discover and call all 29 Mana tools via the standard MCP protocol. Architecture: - WebStandardStreamableHTTPServerTransport for Bun/Hono compatibility - AI_TOOL_CATALOG → MCP tool definitions with JSON Schema (via Zod) - Stateful sessions with Mcp-Session-Id header - Auth via existing authMiddleware (JWT or API key) Phase 1 scope: tools/list returns all 29 tools with schemas, tools/call acknowledges with descriptive messages. Phase 2 will add actual DB reads/writes via sync_changes. Usage: Claude Desktop config: {"mcpServers": {"mana": {"url": "http://localhost:3060/api/v1/mcp"}}} Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/api/package.json | 2 + apps/api/src/index.ts | 7 + apps/api/src/mcp/executor.ts | 67 ++++++++ apps/api/src/mcp/server.ts | 113 ++++++++++++++ apps/api/src/mcp/tools.ts | 33 ++++ pnpm-lock.yaml | 291 ++++++++++++++++++++++++++++++++++- 6 files changed, 508 insertions(+), 5 deletions(-) create mode 100644 apps/api/src/mcp/executor.ts create mode 100644 apps/api/src/mcp/server.ts create mode 100644 apps/api/src/mcp/tools.ts diff --git a/apps/api/package.json b/apps/api/package.json index 9c6fae060..88f047521 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -15,10 +15,12 @@ "dependencies": { "@ai-sdk/openai-compatible": "^2.0.41", "@mana/media-client": "workspace:*", + "@mana/shared-ai": "workspace:*", "@mana/shared-hono": "workspace:*", "@mana/shared-rss": "workspace:*", "@mana/shared-storage": "workspace:*", "@mana/shared-types": "workspace:^", + "@modelcontextprotocol/sdk": "^1.29.0", "ai": "^6.0.154", "drizzle-orm": "^0.38.0", "hono": "^4.7.0", diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index e6ada0194..2942cdb60 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -15,6 +15,9 @@ import { rateLimitMiddleware, } from '@mana/shared-hono'; +// MCP server +import { handleMcpRequest } from './mcp/server'; + // Module routes import { calendarRoutes } from './modules/calendar/routes'; import { contactsRoutes } from './modules/contacts/routes'; @@ -48,6 +51,10 @@ app.route('/health', healthRoute('mana-api')); app.use('/api/*', rateLimitMiddleware({ max: 200, windowMs: 60_000 })); app.use('/api/*', authMiddleware()); +// ─── MCP Endpoint ────────────────────────────────────────── +// Streamable HTTP transport: POST (messages), GET (SSE stream), DELETE (close) +app.all('/api/v1/mcp', (c) => handleMcpRequest(c.req.raw)); + // ─── Module Routes ────────────────────────────────────────── app.route('/api/v1/calendar', calendarRoutes); app.route('/api/v1/contacts', contactsRoutes); diff --git a/apps/api/src/mcp/executor.ts b/apps/api/src/mcp/executor.ts new file mode 100644 index 000000000..abce4ed3d --- /dev/null +++ b/apps/api/src/mcp/executor.ts @@ -0,0 +1,67 @@ +/** + * MCP Tool Executor — handles tools/call requests by routing to module + * handlers or returning status messages. + * + * Phase 1: Read-only tools query the sync database directly. + * Phase 2: Write tools will insert into sync_changes (like mana-ai does). + */ + +import { AI_TOOL_CATALOG_BY_NAME } from '@mana/shared-ai'; + +export interface McpToolResult { + [key: string]: unknown; + content: Array<{ type: 'text'; text: string }>; + isError?: boolean; +} + +/** + * Execute an MCP tool call. Returns MCP-formatted result content. + * + * Phase 1 scope: + * - All tools are listed (via tools/list from AI_TOOL_CATALOG) + * - Write tools return a "coming soon" message + * - Read tools are planned for Phase 2 (requires sync DB queries) + */ +export async function executeMcpTool( + toolName: string, + args: Record, + _userId: string +): Promise { + const schema = AI_TOOL_CATALOG_BY_NAME.get(toolName); + if (!schema) { + return { + content: [{ type: 'text', text: `Unknown tool: ${toolName}` }], + isError: true, + }; + } + + // Phase 1: all tools return a descriptive message about what they will do. + // Phase 2 will implement actual DB reads and sync_changes writes. + if (schema.defaultPolicy === 'auto') { + return { + content: [ + { + type: 'text', + text: + `[Mana MCP] Read-tool "${toolName}" (${schema.module}) acknowledged.\n` + + `Args: ${JSON.stringify(args)}\n` + + `Note: Server-side execution coming in Phase 2. ` + + `This tool will query the sync database for user data.`, + }, + ], + }; + } + + return { + content: [ + { + type: 'text', + text: + `[Mana MCP] Write-tool "${toolName}" (${schema.module}) acknowledged.\n` + + `Args: ${JSON.stringify(args)}\n` + + `Note: Server-side execution coming in Phase 2. ` + + `This tool will write to the sync database and appear on your devices.`, + }, + ], + }; +} diff --git a/apps/api/src/mcp/server.ts b/apps/api/src/mcp/server.ts new file mode 100644 index 000000000..70156f47a --- /dev/null +++ b/apps/api/src/mcp/server.ts @@ -0,0 +1,113 @@ +/** + * Mana MCP Server — exposes AI_TOOL_CATALOG as MCP tools for external + * clients (Claude Desktop, Cursor, VS Code Copilot, etc.). + * + * Uses the Streamable HTTP transport (WebStandard variant) which works + * natively with Hono/Bun. Clients connect via: + * + * POST /api/v1/mcp — send JSON-RPC messages + * GET /api/v1/mcp — open SSE stream for responses + * DELETE /api/v1/mcp — close session + * + * Auth: inherits the existing authMiddleware() from the parent Hono app, + * so every MCP request carries a valid JWT or API key. + */ + +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js'; +import { AI_TOOL_CATALOG } from '@mana/shared-ai'; +import { executeMcpTool } from './executor'; +import { z } from 'zod'; + +/** Convert ToolSchema parameters → Zod shape for McpServer.tool(). */ +function toZodShape( + params: (typeof AI_TOOL_CATALOG)[number]['parameters'] +): Record { + const shape: Record = {}; + for (const p of params) { + let field: z.ZodTypeAny; + if (p.type === 'number') field = z.number().describe(p.description); + else if (p.type === 'boolean') field = z.boolean().describe(p.description); + else if (p.enum) field = z.enum(p.enum as [string, ...string[]]).describe(p.description); + else field = z.string().describe(p.description); + if (!p.required) field = field.optional(); + shape[p.name] = field; + } + return shape; +} + +/** + * Create a new McpServer instance with all Mana tools registered. + */ +function createMcpServer(): McpServer { + const server = new McpServer({ name: 'mana', version: '1.0.0' }, { capabilities: { tools: {} } }); + + // Register all 29 tools from the AI Tool Catalog + for (const tool of AI_TOOL_CATALOG) { + const zodShape = toZodShape(tool.parameters); + const hasParams = Object.keys(zodShape).length > 0; + + if (hasParams) { + server.tool(tool.name, tool.description, zodShape, async (args) => { + return executeMcpTool(tool.name, args, 'mcp-user'); + }); + } else { + server.tool(tool.name, tool.description, async () => { + return executeMcpTool(tool.name, {}, 'mcp-user'); + }); + } + } + + return server; +} + +/** Map of active sessions → their transport instances. */ +const sessions = new Map(); + +/** + * Handle an incoming HTTP request on the MCP endpoint. + * + * Supports stateful sessions: the transport generates a session ID on + * initialization, and subsequent requests must carry it via the + * `Mcp-Session-Id` header. + */ +export async function handleMcpRequest(req: Request): Promise { + const sessionId = req.headers.get('mcp-session-id'); + + // Existing session — route to its transport + if (sessionId && sessions.has(sessionId)) { + const transport = sessions.get(sessionId)!; + return transport.handleRequest(req); + } + + // New session (POST without session ID = initialization) + if (req.method === 'POST' && !sessionId) { + const transport = new WebStandardStreamableHTTPServerTransport({ + sessionIdGenerator: () => crypto.randomUUID(), + onsessioninitialized: (id) => { + sessions.set(id, transport); + }, + onsessionclosed: (id) => { + sessions.delete(id); + }, + }); + + const server = createMcpServer(); + await server.connect(transport); + + return transport.handleRequest(req); + } + + // Invalid request + if (sessionId && !sessions.has(sessionId)) { + return new Response(JSON.stringify({ error: 'Session not found' }), { + status: 404, + headers: { 'Content-Type': 'application/json' }, + }); + } + + return new Response(JSON.stringify({ error: 'Bad request' }), { + status: 400, + headers: { 'Content-Type': 'application/json' }, + }); +} diff --git a/apps/api/src/mcp/tools.ts b/apps/api/src/mcp/tools.ts new file mode 100644 index 000000000..e767e6305 --- /dev/null +++ b/apps/api/src/mcp/tools.ts @@ -0,0 +1,33 @@ +/** + * MCP Tool Definitions — transforms AI_TOOL_CATALOG into MCP-compatible + * tool listings with JSON Schema inputSchema. + * + * The catalog in @mana/shared-ai is the single source of truth. This + * module provides the MCP wire format for tools/list responses. + */ + +import { AI_TOOL_CATALOG, type ToolSchema } from '@mana/shared-ai'; + +/** Convert ToolSchema parameters → JSON Schema for MCP inputSchema. */ +function toJsonSchema(params: ToolSchema['parameters']): { + type: 'object'; + properties: Record>; + required: string[]; +} { + const properties: Record> = {}; + const required: string[] = []; + for (const p of params) { + const prop: Record = { type: p.type, description: p.description }; + if (p.enum) prop.enum = p.enum; + properties[p.name] = prop; + if (p.required) required.push(p.name); + } + return { type: 'object', properties, required }; +} + +/** MCP tool definitions derived from the AI Tool Catalog. */ +export const MCP_TOOLS = AI_TOOL_CATALOG.map((t) => ({ + name: t.name, + description: t.description, + inputSchema: toJsonSchema(t.parameters), +})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c5a02217..fa1d321c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,6 +69,9 @@ importers: '@mana/media-client': specifier: workspace:* version: link:../../services/mana-media/packages/client + '@mana/shared-ai': + specifier: workspace:* + version: link:../../packages/shared-ai '@mana/shared-hono': specifier: workspace:* version: link:../../packages/shared-hono @@ -81,6 +84,9 @@ importers: '@mana/shared-types': specifier: workspace:^ version: link:../../packages/shared-types + '@modelcontextprotocol/sdk': + specifier: ^1.29.0 + version: 1.29.0(zod@3.25.76) ai: specifier: ^6.0.154 version: 6.0.154(zod@3.25.76) @@ -1078,9 +1084,6 @@ importers: '@mana/spiral-db': specifier: workspace:* version: link:../../../../packages/spiral-db - '@mana/subscriptions': - specifier: workspace:* - version: link:../../../../packages/subscriptions '@mana/wallpaper-generator': specifier: workspace:* version: link:../../../../packages/wallpaper-generator @@ -2447,7 +2450,7 @@ importers: version: 0.65.0(zod@3.25.76) '@google/genai': specifier: ^1.14.0 - version: 1.48.0 + version: 1.48.0(@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)) '@mana/shared-hono': specifier: workspace:* version: link:../../../../packages/shared-hono @@ -6318,6 +6321,12 @@ packages: '@modelcontextprotocol/sdk': optional: true + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@huggingface/jinja@0.5.6': resolution: {integrity: sha512-MyMWyLnjqo+KRJYSH7oWNbsOn5onuIvfXYPcc0WOGxU0eHUV7oAYUoQTl2BMdu7ml+ea/bu11UM+EshbeHwtIA==} engines: {node: '>=18'} @@ -6746,6 +6755,16 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + '@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 + '@mozilla/readability@0.5.0': resolution: {integrity: sha512-Z+CZ3QaosfFaTqvhQsIktyGrjFjSC0Fa4EMph4mqKnWhmyoGICsV/8QK+8HpXut6zV7zwfWwqDmEjtk1Qf6EgQ==} engines: {node: '>=14.0.0'} @@ -9352,6 +9371,14 @@ packages: ajv: optional: true + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-keywords@3.5.2: resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} peerDependencies: @@ -9815,6 +9842,10 @@ packages: resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -10233,6 +10264,10 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -10246,6 +10281,10 @@ packages: cookie-signature@1.0.7: resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} @@ -11625,6 +11664,10 @@ packages: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} 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'} @@ -12111,10 +12154,20 @@ packages: exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + express-rate-limit@8.3.2: + resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + express@4.22.1: resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} engines: {node: '>= 0.10.0'} + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + expressive-code@0.40.2: resolution: {integrity: sha512-1zIda2rB0qiDZACawzw2rbdBQiWHBT56uBctS+ezFe5XMAaFaHLnnSYND/Kd+dVzO9HfCXRDpzH3d+3fvOWRcw==} @@ -12258,6 +12311,10 @@ packages: resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + find-babel-config@2.1.2: resolution: {integrity: sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==} @@ -12363,6 +12420,10 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -12803,6 +12864,10 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} @@ -12889,6 +12954,10 @@ packages: resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==} engines: {node: '>=12.22.0'} + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -13049,6 +13118,9 @@ packages: is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -13389,6 +13461,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-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -13895,6 +13970,10 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + memfs@3.5.3: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} @@ -13912,6 +13991,10 @@ packages: merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-options@3.0.4: resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==} engines: {node: '>=10'} @@ -14709,6 +14792,9 @@ packages: path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -14785,6 +14871,10 @@ packages: pixi.js@8.17.1: resolution: {integrity: sha512-OB4TpZHrP5RYy+7FqmFrAc0IHRhfOoNIfF4sVeinvK3aG1r2pYrSMneJAKi9+WvGKC70Dj7GEpZ2OZGB6o/xdg==} + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} @@ -15196,6 +15286,10 @@ packages: resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} engines: {node: '>= 0.8'} + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -15797,6 +15891,10 @@ packages: rou3@0.7.12: resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rrule@2.8.1: resolution: {integrity: sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==} @@ -15922,6 +16020,10 @@ packages: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + serialize-error@2.1.0: resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==} engines: {node: '>=0.10.0'} @@ -15937,6 +16039,10 @@ packages: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} @@ -16721,6 +16827,10 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} @@ -21464,17 +21574,23 @@ snapshots: dependencies: tslib: 2.8.1 - '@google/genai@1.48.0': + '@google/genai@1.48.0(@modelcontextprotocol/sdk@1.29.0(zod@3.25.76))': dependencies: google-auth-library: 10.6.2 p-retry: 4.6.2 protobufjs: 7.5.4 ws: 8.20.0 + optionalDependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@3.25.76) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate + '@hono/node-server@1.19.14(hono@4.12.12)': + dependencies: + hono: 4.12.12 + '@huggingface/jinja@0.5.6': {} '@huggingface/tokenizers@0.1.3': {} @@ -21984,6 +22100,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.12) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.3.2(express@5.2.1) + hono: 4.12.12 + jose: 6.2.2 + 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 + '@mozilla/readability@0.5.0': {} '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': @@ -26146,6 +26284,10 @@ snapshots: optionalDependencies: ajv: 8.18.0 + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv-keywords@3.5.2(ajv@6.14.0): dependencies: ajv: 6.14.0 @@ -27111,6 +27253,20 @@ snapshots: transitivePeerDependencies: - supports-color + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.0 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + boolbase@1.0.0: {} boolean@3.2.0: {} @@ -27562,6 +27718,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + content-disposition@1.1.0: {} + content-type@1.0.5: {} convert-source-map@2.0.0: {} @@ -27570,6 +27728,8 @@ snapshots: cookie-signature@1.0.7: {} + cookie-signature@1.2.2: {} + cookie@0.6.0: {} cookie@0.7.2: {} @@ -29344,6 +29504,10 @@ snapshots: eventsource-parser@3.0.6: {} + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -30555,6 +30719,11 @@ snapshots: exponential-backoff@3.1.3: {} + express-rate-limit@8.3.2(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.1.0 + express@4.22.1: dependencies: accepts: 1.3.8 @@ -30591,6 +30760,39 @@ snapshots: transitivePeerDependencies: - supports-color + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + expressive-code@0.40.2: dependencies: '@expressive-code/core': 0.40.2 @@ -30768,6 +30970,17 @@ snapshots: transitivePeerDependencies: - supports-color + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + find-babel-config@2.1.2: dependencies: json5: 2.2.3 @@ -30885,6 +31098,8 @@ snapshots: fresh@0.5.2: {} + fresh@2.0.0: {} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -31501,6 +31716,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + idb@7.1.1: {} idb@8.0.3: {} @@ -31622,6 +31841,8 @@ snapshots: transitivePeerDependencies: - supports-color + ip-address@10.1.0: {} + ipaddr.js@1.9.1: {} ipaddr.js@2.3.0: {} @@ -31754,6 +31975,8 @@ snapshots: is-promise@2.2.2: {} + is-promise@4.0.0: {} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.8 @@ -32345,6 +32568,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -32914,6 +33139,8 @@ snapshots: media-typer@0.3.0: {} + media-typer@1.1.0: {} + memfs@3.5.3: dependencies: fs-monkey: 1.1.0 @@ -32935,6 +33162,8 @@ snapshots: merge-descriptors@1.0.3: {} + merge-descriptors@2.0.0: {} + merge-options@3.0.4: dependencies: is-plain-obj: 2.1.0 @@ -34216,6 +34445,8 @@ snapshots: path-to-regexp@6.3.0: {} + path-to-regexp@8.4.2: {} + path-type@4.0.0: {} pathe@1.1.2: {} @@ -34282,6 +34513,8 @@ snapshots: parse-svg-path: 0.1.2 tiny-lru: 11.4.7 + pkce-challenge@5.0.1: {} + pkg-dir@4.2.0: dependencies: find-up: 4.1.0 @@ -34585,6 +34818,13 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -35813,6 +36053,16 @@ snapshots: rou3@0.7.12: {} + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + rrule@2.8.1: dependencies: tslib: 2.8.1 @@ -35944,6 +36194,22 @@ snapshots: transitivePeerDependencies: - supports-color + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + serialize-error@2.1.0: {} serialize-error@7.0.1: @@ -35963,6 +36229,15 @@ snapshots: transitivePeerDependencies: - supports-color + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + server-only@0.0.1: {} set-blocking@2.0.0: {} @@ -36853,6 +37128,12 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + type@2.7.3: {} typed-array-buffer@1.0.3: