From 906ffe56faf7bd824cd44125e2468077a22f80d8 Mon Sep 17 00:00:00 2001 From: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com> Date: Fri, 2 Jan 2026 11:26:32 -0500 Subject: [PATCH] fix: Update MCP Server to latest SDK version --- mcp/bun.lock | 15 +- mcp/package.json | 4 +- mcp/worker.ts | 2628 +++++++++++++++++++++++++--------------------- 3 files changed, 1461 insertions(+), 1186 deletions(-) diff --git a/mcp/bun.lock b/mcp/bun.lock index 61644df3..26363de9 100644 --- a/mcp/bun.lock +++ b/mcp/bun.lock @@ -4,11 +4,10 @@ "": { "name": "honcho-mcp-proxy", "dependencies": { - "@honcho-ai/sdk": "^1.2.1", + "@honcho-ai/sdk": "^1.6.0", }, "devDependencies": { "@cloudflare/workers-types": "^4.20241002.0", - "only-allow": "^1.2.1", "typescript": "^5.3.3", "wrangler": "^4.24.3", }, @@ -85,9 +84,9 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], - "@honcho-ai/core": ["@honcho-ai/core@1.2.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-VPHCFIGfC00GeE4P83DDIT7hkuMnMVkWlMTmMd2tw4HSEUciqLBh09AX/6aMKfJAzprg1diub6pJJ6LJP6eJ+g=="], + "@honcho-ai/core": ["@honcho-ai/core@1.8.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-qxBNoXLezH8yx4iBoz4Bsxkm9zp4Gm1fNwuP8gHRdSelxhR0dXpvLffx8B5V0XFWsx+SfPaJFyaKw0X2sYMwLA=="], - "@honcho-ai/sdk": ["@honcho-ai/sdk@1.2.1", "", { "dependencies": { "@honcho-ai/core": "1.2.0", "@types/node": "^24.0.1" } }, "sha512-/RFHq9R9XsD1uj3KPZkZ4aGMuyan6gmIXu3HFND7Aipe3PGFeMEmteFmcMVGi3FO3F5S+NJsfqppV9Z3dNfeyQ=="], + "@honcho-ai/sdk": ["@honcho-ai/sdk@1.6.0", "", { "dependencies": { "@honcho-ai/core": "^1.6.1", "@types/node": "^24.0.1", "zod": "4.0.0" } }, "sha512-6HSjTidVwchEWw18p5Gqp4e2/I1Um0EppTZYZ22+hG1UemKRcJX+VDRvlu5ja8inD/WWYTEOJ/HBSdT8OC9biw=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], @@ -247,8 +246,6 @@ "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], - "only-allow": ["only-allow@1.2.1", "", { "dependencies": { "which-pm-runs": "^1.1.0" }, "bin": { "only-allow": "bin.js" } }, "sha512-M7CJbmv7UCopc0neRKdzfoGWaVZC+xC1925GitKH9EAqYFzX9//25Q7oX4+jw0tiCCj+t5l6VZh8UPH23NZkMA=="], - "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], @@ -283,8 +280,6 @@ "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], - "workerd": ["workerd@1.20250712.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250712.0", "@cloudflare/workerd-darwin-arm64": "1.20250712.0", "@cloudflare/workerd-linux-64": "1.20250712.0", "@cloudflare/workerd-linux-arm64": "1.20250712.0", "@cloudflare/workerd-windows-64": "1.20250712.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-7h+k1OxREpiZW0849g0uQNexRWMcs5i5gUGhJzCY8nIx6Tv4D/ndlXJ47lEFj7/LQdp165IL9dM2D5uDiedZrg=="], "wrangler": ["wrangler@4.26.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.4.1", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250712.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250712.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250712.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-EXuwyWlgYQZv6GJlyE0lVGk9hHqASssuECECT1XC5aIijTwNLQhsj/TOZ0hKSFlMbVr1E+OAdevAxd0kaF4ovA=="], @@ -295,10 +290,12 @@ "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], - "zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + "zod": ["zod@4.0.0", "", {}, "sha512-9diLdTPc/L7w/5jI4C3gHYNiGHDV9IZYxo1e5LSD8cabi65WVTWWb+g2BGPEpUUCOxR4D+6O5B0AzyMdUAXwrw=="], "@honcho-ai/core/@types/node": ["@types/node@18.19.120", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-WtCGHFXnVI8WHLxDAt5TbnCM4eSE+nI0QN2NJtwzcgMhht2eNz6V9evJrk+lwC8bCY8OWV5Ym8Jz7ZEyGnKnMA=="], + "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + "@honcho-ai/core/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], } } diff --git a/mcp/package.json b/mcp/package.json index c77cd31e..c13b93ba 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -9,13 +9,13 @@ "bun": ">=1.2.0" }, "scripts": { - "preinstall": "node -e \"if(process.env.npm_config_user_agent?.includes('npm')){console.error('āŒ Please use bun instead of npm!\\nšŸ“¦ Run: bun install\\n🌐 Install bun: https://bun.sh/');process.exit(1)}\"", + "preinstall": "node -e \"const ua=process.env.npm_config_user_agent||'';if(ua.includes('npm')&&!ua.includes('bun')){console.error('āŒ Please use bun instead of npm!\\nšŸ“¦ Run: bun install\\n🌐 Install bun: https://bun.sh/');process.exit(1)}\"", "dev": "wrangler dev", "deploy": "wrangler deploy", "deploy:staging": "wrangler deploy --env staging" }, "dependencies": { - "@honcho-ai/sdk": "^1.2.1" + "@honcho-ai/sdk": "^1.6.0" }, "devDependencies": { "@cloudflare/workers-types": "^4.20241002.0", diff --git a/mcp/worker.ts b/mcp/worker.ts index 74dcc5bd..c597be9b 100644 --- a/mcp/worker.ts +++ b/mcp/worker.ts @@ -1,515 +1,593 @@ -import { Honcho } from '@honcho-ai/sdk'; +import { Honcho } from "@honcho-ai/sdk"; interface HonchoConfig { - apiKey: string; - userName: string; - baseUrl?: string; - workspaceId?: string; - assistantName?: string; + apiKey: string; + userName: string; + baseUrl?: string; + workspaceId?: string; + assistantName?: string; } interface Message { - role: 'user' | 'assistant'; - content: string; - metadata?: Record; + role: "user" | "assistant"; + content: string; + metadata?: Record; } /** * JSON-RPC 2.0 request interface */ interface JsonRpcRequest { - jsonrpc: '2.0'; - method: string; - params?: any; - id?: string | number; + jsonrpc: "2.0"; + method: string; + params?: any; + id?: string | number; } /** * JSON-RPC 2.0 response interface */ interface JsonRpcResponse { - jsonrpc: '2.0'; - id?: string | number | null; - result?: any; - error?: { - code: number; - message: string; - data?: any; - }; + jsonrpc: "2.0"; + id?: string | number | null; + result?: any; + error?: { + code: number; + message: string; + data?: any; + }; } // MCP Tool definitions interface Tool { - name: string; - description: string; - inputSchema: { - type: 'object'; - properties: Record; - required?: string[]; - }; + name: string; + description: string; + inputSchema: { + type: "object"; + properties: Record; + required?: string[]; + }; } /** * Helper function to validate required arguments and create error responses */ -function validateArguments(args: Record, required: string[], requestId: string | number | null): Response | null { - for (const param of required) { - if (!args[param]) { - return createErrorResponse(requestId, -32602, `${param} is required`); - } +function validateArguments( + args: Record, + required: string[], + requestId: string | number | null, +): Response | null { + for (const param of required) { + if (!args[param]) { + return createErrorResponse(requestId, -32602, `${param} is required`); } + } - // Special validation for arrays - if (args.messages && !Array.isArray(args.messages)) { - return createErrorResponse(requestId, -32602, 'messages must be an array'); - } - if (args.peer_ids && !Array.isArray(args.peer_ids)) { - return createErrorResponse(requestId, -32602, 'peer_ids must be an array'); - } + // Special validation for arrays + if (args.messages && !Array.isArray(args.messages)) { + return createErrorResponse(requestId, -32602, "messages must be an array"); + } + if (args.peer_ids && !Array.isArray(args.peer_ids)) { + return createErrorResponse(requestId, -32602, "peer_ids must be an array"); + } - return null; + return null; } /** * Helper function to create error responses */ -function createErrorResponse(id: string | number | null, code: number, message: string): Response { - return new Response(JSON.stringify(createJsonRpcResponse(id, undefined, createJsonRpcError(code, message))), { - status: code === -32602 ? 400 : (code === -32601 ? 404 : 500), - headers: { 'Content-Type': 'application/json' }, - }); +function createErrorResponse( + id: string | number | null, + code: number, + message: string, +): Response { + return new Response( + JSON.stringify( + createJsonRpcResponse(id, undefined, createJsonRpcError(code, message)), + ), + { + status: code === -32602 ? 400 : code === -32601 ? 404 : 500, + headers: { "Content-Type": "application/json" }, + }, + ); } /** * Helper function to format messages for async iteration */ async function formatMessages(messagesPage: any): Promise { - const messages = []; - for await (const message of messagesPage) { - messages.push({ - id: message.id, - content: message.content, - peer_id: message.peer_id, - session_id: message.session_id, - metadata: message.metadata, - created_at: message.created_at, - }); - } - return messages; + const messages = []; + for await (const message of messagesPage) { + messages.push({ + id: message.id, + content: message.content, + peer_id: message.peer_id, + session_id: message.session_id, + metadata: message.metadata, + created_at: message.created_at, + }); + } + return messages; } class HonchoWorker { - private honcho: Honcho; - private config: HonchoConfig; + private honcho: Honcho; + private config: HonchoConfig; - constructor(config: HonchoConfig) { - this.config = { - baseUrl: 'https://api.honcho.dev', - workspaceId: 'default', - assistantName: 'Assistant', - ...config, - }; + constructor(config: HonchoConfig) { + this.config = { + baseUrl: "https://api.honcho.dev", + workspaceId: "default", + assistantName: "Assistant", + ...config, + }; - this.honcho = new Honcho({ - apiKey: this.config.apiKey, - baseURL: this.config.baseUrl, - workspaceId: this.config.workspaceId, - }); - } + this.honcho = new Honcho({ + apiKey: this.config.apiKey, + baseURL: this.config.baseUrl, + workspaceId: this.config.workspaceId, + }); + } - //////////////////////////////////////////////////////////////////////////////// - /// /// - /// "Bespoke" tools: easy to use for user-assistant conversation paradigms /// - /// /// - //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + /// /// + /// "Bespoke" tools: easy to use for user-assistant conversation paradigms /// + /// /// + //////////////////////////////////////////////////////////////////////////////// - /** - * Start a new conversation with a user. Call this when a user starts a new conversation. - * @returns A session ID for the conversation - */ - async startConversation(): Promise { - // Get/create the assistant peer with observe_me=false - const assistant = this.honcho.peer(this.config.assistantName!, { config: { observe_me: false } }); + /** + * Start a new conversation with a user. Call this when a user starts a new conversation. + * @returns A session ID for the conversation + */ + async startConversation(): Promise { + // Get/create the assistant peer with observe_me=false + const assistant = await this.honcho.peer(this.config.assistantName!, { + config: { observe_me: false }, + }); - // Create a new session - const sessionId = crypto.randomUUID(); - const session = this.honcho.session(sessionId); + // Create a new session + const sessionId = crypto.randomUUID(); + const session = await this.honcho.session(sessionId); - // Add the user and assistant peers to the session - // @ts-expect-error - API accepts null for observe_me despite type definition - await session.addPeers([this.config.userName, [assistant, { observe_me: null, observe_others: false }]]); + // Add the user and assistant peers to the session + await session.addPeers([ + this.config.userName, + [assistant, { observe_me: null, observe_others: false }], + ]); - return sessionId; - } + return sessionId; + } - /** - * Add a turn to a conversation. Call this after a user has sent a message and the assistant has responded. - * @param sessionId - The ID of the session to add the turn to - * @param messages - A list of messages to add to the session - */ - async addTurn(sessionId: string, messages: Message[]): Promise { - const session = this.honcho.session(sessionId); - const userPeer = this.honcho.peer(this.config.userName); - const assistantPeer = this.honcho.peer(this.config.assistantName!); + /** + * Add a turn to a conversation. Call this after a user has sent a message and the assistant has responded. + * @param sessionId - The ID of the session to add the turn to + * @param messages - A list of messages to add to the session + */ + async addTurn(sessionId: string, messages: Message[]): Promise { + const session = await this.honcho.session(sessionId); + const userPeer = await this.honcho.peer(this.config.userName); + const assistantPeer = await this.honcho.peer(this.config.assistantName!); - const sessionMessages = []; + const sessionMessages = []; - for (let i = 0; i < messages.length; i++) { - const message = messages[i]; + for (let i = 0; i < messages.length; i++) { + const message = messages[i]; - // Validate required fields - if (!message || typeof message !== 'object') { - throw new Error(`Message at index ${i} must be a dictionary`); - } + // Validate required fields + if (!message || typeof message !== "object") { + throw new Error(`Message at index ${i} must be a dictionary`); + } - if (!message.role) { - throw new Error(`Message at index ${i} is missing required field 'role'`); - } + if (!message.role) { + throw new Error( + `Message at index ${i} is missing required field 'role'`, + ); + } - if (!message.content) { - throw new Error(`Message at index ${i} is missing required field 'content'`); - } + if (!message.content) { + throw new Error( + `Message at index ${i} is missing required field 'content'`, + ); + } - const { role, content, metadata } = message; + const { role, content, metadata } = message; - // Create message with appropriate peer - if (role === 'user') { - if (metadata) { - sessionMessages.push(userPeer.message(content, { metadata })); - } else { - sessionMessages.push(userPeer.message(content)); - } - } else if (role === 'assistant') { - if (metadata) { - sessionMessages.push(assistantPeer.message(content, { metadata })); - } else { - sessionMessages.push(assistantPeer.message(content)); - } - } else { - throw new Error(`Invalid role '${role}' at message index ${i}. Role must be one of: 'user' or 'assistant'`); - } - } - - await session.addMessages(sessionMessages); - } - - /** - * Get personalization insights about the user, based on the query and the accumulated knowledge of the user across all conversations. - * @param sessionId - The ID of the session for context - * @param query - The question about the user's preferences, habits, etc. - * @returns A string with the personalization insights - */ - async getPersonalizationInsights(sessionId: string, query: string): Promise { - const userPeer = this.honcho.peer(this.config.userName); - - // Get the personalization insights - const personalizationInsights = await userPeer.chat(query, { sessionId }); - - if (!personalizationInsights) { - return "No personalization insights found."; - } - - return personalizationInsights; - } - - //////////////////////////////////////////////////////////////////////////////// - /// /// - /// General tools for using Honcho /// - /// /// - //////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////// - /// /// - /// Workspace operations /// - /// /// - ////////////////////////////////////////////////////// - - /** - * Search for messages across the entire workspace. - * @param query - The search query to use - * @returns A list of message dictionaries matching the search query - */ - async searchWorkspace(query: string): Promise { - const messagesPage = await this.honcho.search(query); - return await formatMessages(messagesPage); - } - - /** - * Get metadata for the current workspace. - * @returns A dictionary containing the workspace's metadata - */ - async getWorkspaceMetadata(): Promise> { - return await this.honcho.getMetadata(); - } - - /** - * Set metadata for the current workspace. - * @param metadata - A dictionary of metadata to associate with the workspace - */ - async setWorkspaceMetadata(metadata: Record): Promise { - await this.honcho.setMetadata(metadata); - } - - ////////////////////////////////////////////////////// - /// /// - /// Peer operations /// - /// /// - ////////////////////////////////////////////////////// - - /** - * Create or get a peer with the specified ID and optional configuration. - * @param peerId - Unique identifier for the peer - * @param config - Optional configuration dictionary for the peer - * @returns A dictionary with the peer ID and confirmation of creation - */ - async createPeer(peerId: string, config?: Record): Promise<{ peer_id: string; config?: Record }> { - const peer = this.honcho.peer(peerId, { config }); - return { - peer_id: peer.id, - config, - }; - } - - /** - * Get metadata for a specific peer. - * @param peerId - The ID of the peer to get metadata for - * @returns A dictionary containing the peer's metadata - */ - async getPeerMetadata(peerId: string): Promise> { - const peer = this.honcho.peer(peerId); - return await peer.getMetadata(); - } - - /** - * Set metadata for a specific peer. - * @param peerId - The ID of the peer to set metadata for - * @param metadata - A dictionary of metadata to associate with the peer - */ - async setPeerMetadata(peerId: string, metadata: Record): Promise { - const peer = this.honcho.peer(peerId); - await peer.setMetadata(metadata); - } - - /** - * Search for messages sent by a peer. - * @param peerId - The ID of the peer to search messages for - * @param query - The search query to use - * @returns A list of message dictionaries matching the search query - */ - async searchPeerMessages(peerId: string, query: string): Promise { - const peer = this.honcho.peer(peerId); - const messagesPage = await peer.search(query); - return await formatMessages(messagesPage); - } - - /** - * Query a peer's representation with natural language questions. - * @param peerId - The ID of the peer to query - * @param query - The natural language question to ask - * @param targetPeerId - Optional target peer ID for local representation queries - * @param sessionId - Optional session ID to scope the query to a specific session - * @returns Response string containing the answer to the query, or "None" if no relevant information - */ - async chat(peerId: string, query: string, targetPeerId?: string, sessionId?: string): Promise { - const peer = this.honcho.peer(peerId); - let targetPeer; - if (targetPeerId) { - targetPeer = this.honcho.peer(targetPeerId); - } - - const result = await peer.chat(query, { target: targetPeer, sessionId }); - return result || "None"; - } - - /** - * Get all peers in the current workspace. - * @returns A list of peer dictionaries with their IDs - */ - async listPeers(): Promise<{ id: string }[]> { - const peersPage = await this.honcho.getPeers(); - const peers = []; - - for await (const peer of peersPage) { - peers.push({ - id: peer.id, - }); - } - - return peers; - } - - ////////////////////////////////////////////////////// - /// /// - /// Session operations /// - /// /// - ////////////////////////////////////////////////////// - - /** - * Create or get a session with the specified ID and optional configuration. - * @param sessionId - Unique identifier for the session - * @param config - Optional configuration dictionary for the session - * @returns A dictionary with the session ID and confirmation of creation - */ - async createSession(sessionId: string, config?: Record): Promise<{ session_id: string; config?: Record }> { - const session = this.honcho.session(sessionId, { config }); - return { - session_id: session.id, - config, - }; - } - - /** - * Get metadata for a specific session. - * @param sessionId - The ID of the session to get metadata for - * @returns A dictionary containing the session's metadata - */ - async getSessionMetadata(sessionId: string): Promise> { - const session = this.honcho.session(sessionId); - return await session.getMetadata(); - } - - /** - * Set metadata for a specific session. - * @param sessionId - The ID of the session to set metadata for - * @param metadata - A dictionary of metadata to associate with the session - */ - async setSessionMetadata(sessionId: string, metadata: Record): Promise { - const session = this.honcho.session(sessionId); - await session.setMetadata(metadata); - } - - /** - * Add peers to a session. - * @param sessionId - The ID of the session to add peers to - * @param peerIds - List of peer IDs to add to the session - */ - async addPeersToSession(sessionId: string, peerIds: string[]): Promise { - const session = this.honcho.session(sessionId); - await session.addPeers(peerIds); - } - - /** - * Remove peers from a session. - * @param sessionId - The ID of the session to remove peers from - * @param peerIds - List of peer IDs to remove from the session - */ - async removePeersFromSession(sessionId: string, peerIds: string[]): Promise { - const session = this.honcho.session(sessionId); - await session.removePeers(peerIds); - } - - /** - * Get all peer IDs in a session. - * @param sessionId - The ID of the session to get peers from - * @returns A list of peer IDs that are members of the session - */ - async getSessionPeers(sessionId: string): Promise { - const session = this.honcho.session(sessionId); - const peers = await session.getPeers(); - return peers.map(peer => peer.id); - } - - /** - * Add messages to a session. - * @param sessionId - The ID of the session to add messages to - * @param messages - List of message dictionaries - */ - async addMessagesToSession(sessionId: string, messages: { peer_id: string; content: string; metadata?: Record }[]): Promise { - const session = this.honcho.session(sessionId); - - const sessionMessages = []; - for (const message of messages) { - const peer = this.honcho.peer(message.peer_id); - if (message.metadata) { - sessionMessages.push(peer.message(message.content, { metadata: message.metadata })); - } else { - sessionMessages.push(peer.message(message.content)); - } - } - - await session.addMessages(sessionMessages); - } - - /** - * Get messages from a session with optional filtering. - * @param sessionId - The ID of the session to get messages from - * @param filters - Optional dictionary of filter criteria - * @returns A list of message dictionaries - */ - async getSessionMessages(sessionId: string, filters?: Record): Promise { - const session = this.honcho.session(sessionId); - const messagesPage = await session.getMessages({ filter: filters }); - return await formatMessages(messagesPage); - } - - /** - * Get optimized context for a session within a token limit. - * @param sessionId - The ID of the session to get context for - * @param summary - Whether to include summary information - * @param tokens - Maximum number of tokens to include in the context - * @returns A dictionary containing the session context with messages and optional summary - */ - async getSessionContext(sessionId: string, summary: boolean = true, tokens?: number): Promise { - const session = this.honcho.session(sessionId); - const context = await session.getContext({ summary, tokens }); - - return { - session_id: context.sessionId, - summary: context.summary, - messages: context.messages.map(msg => ({ - id: msg.id, - content: msg.content, - peer_id: msg.peer_id, - metadata: msg.metadata, - created_at: msg.created_at, - })), - }; - } - - /** - * Search for messages in a specific session. - * @param sessionId - The ID of the session to search messages in - * @param query - The search query to use - * @returns A list of message dictionaries matching the search query - */ - async searchSessionMessages(sessionId: string, query: string): Promise { - const session = this.honcho.session(sessionId); - const messagesPage = await session.search(query); - return await formatMessages(messagesPage); - } - - /** - * Get the current working representation of a peer in a session. - * @param sessionId - The ID of the session - * @param peerId - The ID of the peer to get the working representation of - * @param targetPeerId - Optional target peer ID to get the representation of what peer_id knows about target_peer_id - * @returns A dictionary containing information about the peer - */ - async getWorkingRepresentation(sessionId: string, peerId: string, targetPeerId?: string): Promise> { - const session = this.honcho.session(sessionId); - if (targetPeerId) { - return await session.workingRep(peerId, targetPeerId); + // Create message with appropriate peer + if (role === "user") { + if (metadata) { + sessionMessages.push(userPeer.message(content, { metadata })); } else { - return await session.workingRep(peerId); + sessionMessages.push(userPeer.message(content)); } + } else if (role === "assistant") { + if (metadata) { + sessionMessages.push(assistantPeer.message(content, { metadata })); + } else { + sessionMessages.push(assistantPeer.message(content)); + } + } else { + throw new Error( + `Invalid role '${role}' at message index ${i}. Role must be one of: 'user' or 'assistant'`, + ); + } } - /** - * Get all sessions in the current workspace. - * @returns A list of session dictionaries with their IDs - */ - async listSessions(): Promise<{ id: string }[]> { - const sessionsPage = await this.honcho.getSessions(); - const sessions = []; + await session.addMessages(sessionMessages); + } - for await (const session of sessionsPage) { - sessions.push({ - id: session.id, - }); - } + /** + * Get personalization insights about the user, based on the query and the accumulated knowledge of the user across all conversations. + * @param sessionId - The ID of the session for context + * @param query - The question about the user's preferences, habits, etc. + * @returns A string with the personalization insights + */ + async getPersonalizationInsights( + sessionId: string, + query: string, + ): Promise { + const userPeer = await this.honcho.peer(this.config.userName); - return sessions; + // Get the personalization insights (non-streaming returns string | null) + const personalizationInsights = await userPeer.chat(query, { + session: sessionId, + }); + + if (!personalizationInsights || typeof personalizationInsights !== 'string') { + return "No personalization insights found."; } + + return personalizationInsights; + } + + //////////////////////////////////////////////////////////////////////////////// + /// /// + /// General tools for using Honcho /// + /// /// + //////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////// + /// /// + /// Workspace operations /// + /// /// + ////////////////////////////////////////////////////// + + /** + * Search for messages across the entire workspace. + * @param query - The search query to use + * @returns A list of message dictionaries matching the search query + */ + async searchWorkspace(query: string): Promise { + const messagesPage = await this.honcho.search(query); + return await formatMessages(messagesPage); + } + + /** + * Get metadata for the current workspace. + * @returns A dictionary containing the workspace's metadata + */ + async getWorkspaceMetadata(): Promise> { + return await this.honcho.getMetadata(); + } + + /** + * Set metadata for the current workspace. + * @param metadata - A dictionary of metadata to associate with the workspace + */ + async setWorkspaceMetadata(metadata: Record): Promise { + await this.honcho.setMetadata(metadata); + } + + ////////////////////////////////////////////////////// + /// /// + /// Peer operations /// + /// /// + ////////////////////////////////////////////////////// + + /** + * Create or get a peer with the specified ID and optional configuration. + * @param peerId - Unique identifier for the peer + * @param config - Optional configuration dictionary for the peer + * @returns A dictionary with the peer ID and confirmation of creation + */ + async createPeer( + peerId: string, + config?: Record, + ): Promise<{ peer_id: string; config?: Record }> { + const peer = await this.honcho.peer(peerId, { config }); + return { + peer_id: peer.id, + config, + }; + } + + /** + * Get metadata for a specific peer. + * @param peerId - The ID of the peer to get metadata for + * @returns A dictionary containing the peer's metadata + */ + async getPeerMetadata(peerId: string): Promise> { + const peer = await this.honcho.peer(peerId); + return await peer.getMetadata(); + } + + /** + * Set metadata for a specific peer. + * @param peerId - The ID of the peer to set metadata for + * @param metadata - A dictionary of metadata to associate with the peer + */ + async setPeerMetadata( + peerId: string, + metadata: Record, + ): Promise { + const peer = await this.honcho.peer(peerId); + await peer.setMetadata(metadata); + } + + /** + * Search for messages sent by a peer. + * @param peerId - The ID of the peer to search messages for + * @param query - The search query to use + * @returns A list of message dictionaries matching the search query + */ + async searchPeerMessages(peerId: string, query: string): Promise { + const peer = await this.honcho.peer(peerId); + const messagesPage = await peer.search(query); + return await formatMessages(messagesPage); + } + + /** + * Query a peer's representation with natural language questions. + * @param peerId - The ID of the peer to query + * @param query - The natural language question to ask + * @param targetPeerId - Optional target peer ID for local representation queries + * @param sessionId - Optional session ID to scope the query to a specific session + * @returns Response string containing the answer to the query, or "None" if no relevant information + */ + async chat( + peerId: string, + query: string, + targetPeerId?: string, + sessionId?: string, + ): Promise { + const peer = await this.honcho.peer(peerId); + let targetPeer; + if (targetPeerId) { + targetPeer = await this.honcho.peer(targetPeerId); + } + + const result = await peer.chat(query, { + target: targetPeer, + session: sessionId, + }); + + if (!result || typeof result !== 'string') { + return "None"; + } + return result; + } + + /** + * Get all peers in the current workspace. + * @returns A list of peer dictionaries with their IDs + */ + async listPeers(): Promise<{ id: string }[]> { + const peersPage = await this.honcho.getPeers(); + const peers = []; + + for await (const peer of peersPage) { + peers.push({ + id: peer.id, + }); + } + + return peers; + } + + ////////////////////////////////////////////////////// + /// /// + /// Session operations /// + /// /// + ////////////////////////////////////////////////////// + + /** + * Create or get a session with the specified ID and optional configuration. + * @param sessionId - Unique identifier for the session + * @param config - Optional configuration dictionary for the session + * @returns A dictionary with the session ID and confirmation of creation + */ + async createSession( + sessionId: string, + config?: Record, + ): Promise<{ session_id: string; config?: Record }> { + const session = await this.honcho.session(sessionId, { config }); + return { + session_id: session.id, + config, + }; + } + + /** + * Get metadata for a specific session. + * @param sessionId - The ID of the session to get metadata for + * @returns A dictionary containing the session's metadata + */ + async getSessionMetadata(sessionId: string): Promise> { + const session = await this.honcho.session(sessionId); + return await session.getMetadata(); + } + + /** + * Set metadata for a specific session. + * @param sessionId - The ID of the session to set metadata for + * @param metadata - A dictionary of metadata to associate with the session + */ + async setSessionMetadata( + sessionId: string, + metadata: Record, + ): Promise { + const session = await this.honcho.session(sessionId); + await session.setMetadata(metadata); + } + + /** + * Add peers to a session. + * @param sessionId - The ID of the session to add peers to + * @param peerIds - List of peer IDs to add to the session + */ + async addPeersToSession(sessionId: string, peerIds: string[]): Promise { + const session = await this.honcho.session(sessionId); + await session.addPeers(peerIds); + } + + /** + * Remove peers from a session. + * @param sessionId - The ID of the session to remove peers from + * @param peerIds - List of peer IDs to remove from the session + */ + async removePeersFromSession( + sessionId: string, + peerIds: string[], + ): Promise { + const session = await this.honcho.session(sessionId); + await session.removePeers(peerIds); + } + + /** + * Get all peer IDs in a session. + * @param sessionId - The ID of the session to get peers from + * @returns A list of peer IDs that are members of the session + */ + async getSessionPeers(sessionId: string): Promise { + const session = await this.honcho.session(sessionId); + const peers = await session.getPeers(); + return peers.map((peer) => peer.id); + } + + /** + * Add messages to a session. + * @param sessionId - The ID of the session to add messages to + * @param messages - List of message dictionaries + */ + async addMessagesToSession( + sessionId: string, + messages: { + peer_id: string; + content: string; + metadata?: Record; + }[], + ): Promise { + const session = await this.honcho.session(sessionId); + + const sessionMessages = []; + for (const message of messages) { + const peer = await this.honcho.peer(message.peer_id); + if (message.metadata) { + sessionMessages.push( + peer.message(message.content, { metadata: message.metadata }), + ); + } else { + sessionMessages.push(peer.message(message.content)); + } + } + + await session.addMessages(sessionMessages); + } + + /** + * Get messages from a session with optional filtering. + * @param sessionId - The ID of the session to get messages from + * @param filters - Optional dictionary of filter criteria + * @returns A list of message dictionaries + */ + async getSessionMessages( + sessionId: string, + filters?: Record, + ): Promise { + const session = await this.honcho.session(sessionId); + const messagesPage = await session.getMessages({ filter: filters }); + return await formatMessages(messagesPage); + } + + /** + * Get optimized context for a session within a token limit. + * @param sessionId - The ID of the session to get context for + * @param summary - Whether to include summary information + * @param tokens - Maximum number of tokens to include in the context + * @returns A dictionary containing the session context with messages and optional summary + */ + async getSessionContext( + sessionId: string, + summary: boolean = true, + tokens?: number, + ): Promise { + const session = await this.honcho.session(sessionId); + const context = await session.getContext({ summary, tokens }); + + return { + session_id: context.sessionId, + summary: context.summary, + messages: context.messages.map((msg) => ({ + id: msg.id, + content: msg.content, + peer_id: msg.peer_id, + metadata: msg.metadata, + created_at: msg.created_at, + })), + }; + } + + /** + * Search for messages in a specific session. + * @param sessionId - The ID of the session to search messages in + * @param query - The search query to use + * @returns A list of message dictionaries matching the search query + */ + async searchSessionMessages( + sessionId: string, + query: string, + ): Promise { + const session = await this.honcho.session(sessionId); + const messagesPage = await session.search(query); + return await formatMessages(messagesPage); + } + + /** + * Get the current working representation of a peer in a session. + * @param sessionId - The ID of the session + * @param peerId - The ID of the peer to get the working representation of + * @param targetPeerId - Optional target peer ID to get the representation of what peer_id knows about target_peer_id + * @returns A dictionary containing information about the peer + */ + async getWorkingRepresentation( + sessionId: string, + peerId: string, + targetPeerId?: string, + ): Promise> { + const session = await this.honcho.session(sessionId); + if (targetPeerId) { + return await session.workingRep(peerId, targetPeerId); + } else { + return await session.workingRep(peerId); + } + } + + /** + * Get all sessions in the current workspace. + * @returns A list of session dictionaries with their IDs + */ + async listSessions(): Promise<{ id: string }[]> { + const sessionsPage = await this.honcho.getSessions(); + const sessions = []; + + for await (const session of sessionsPage) { + sessions.push({ + id: session.id, + }); + } + + return sessions; + } } /** @@ -518,32 +596,34 @@ class HonchoWorker { * @returns Configuration object or null if invalid */ function parseConfig(request: Request): HonchoConfig | null { - // Get API key from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader || !authHeader.startsWith('Bearer ')) { - return null; - } - const apiKey = authHeader.substring(7); + // Get API key from Authorization header + const authHeader = request.headers.get("Authorization"); + if (!authHeader || !authHeader.startsWith("Bearer ")) { + return null; + } + const apiKey = authHeader.substring(7); - if (!apiKey) { - return null; - } + if (!apiKey) { + return null; + } - const userName = request.headers.get('X-Honcho-User-Name'); - if (!userName) { - return null; - } + const userName = request.headers.get("X-Honcho-User-Name"); + if (!userName) { + return null; + } - // Get configuration from headers with proper defaults - const config: HonchoConfig = { - apiKey, - userName, - baseUrl: request.headers.get('X-Honcho-Base-URL') || 'https://api.honcho.dev', - workspaceId: request.headers.get('X-Honcho-Workspace-ID') || 'default', - assistantName: request.headers.get('X-Honcho-Assistant-Name') || 'Assistant', - }; + // Get configuration from headers with proper defaults + const config: HonchoConfig = { + apiKey, + userName, + baseUrl: + request.headers.get("X-Honcho-Base-URL") || "https://api.honcho.dev", + workspaceId: request.headers.get("X-Honcho-Workspace-ID") || "default", + assistantName: + request.headers.get("X-Honcho-Assistant-Name") || "Assistant", + }; - return config; + return config; } /** @@ -553,19 +633,23 @@ function parseConfig(request: Request): HonchoConfig | null { * @param error - Error object if any * @returns JSON-RPC response object */ -function createJsonRpcResponse(id: string | number | null, result?: any, error?: { code: number; message: string; data?: any }): JsonRpcResponse { - const response: JsonRpcResponse = { - jsonrpc: '2.0', - id, - }; +function createJsonRpcResponse( + id: string | number | null, + result?: any, + error?: { code: number; message: string; data?: any }, +): JsonRpcResponse { + const response: JsonRpcResponse = { + jsonrpc: "2.0", + id, + }; - if (error) { - response.error = error; - } else { - response.result = result; - } + if (error) { + response.error = error; + } else { + response.result = result; + } - return response; + return response; } /** @@ -575,770 +659,964 @@ function createJsonRpcResponse(id: string | number | null, result?: any, error?: * @param data - Optional error data * @returns Error object */ -function createJsonRpcError(code: number, message: string, data?: any): { code: number; message: string; data?: any } { - return { code, message, data }; +function createJsonRpcError( + code: number, + message: string, + data?: any, +): { code: number; message: string; data?: any } { + return { code, message, data }; } // Define all MCP tools based on the Python server.py functions const tools: Tool[] = [ - // Bespoke tools - { - name: 'start_conversation', - description: 'Start a new conversation with a user. Call this when a user starts a new conversation.', - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, + // Bespoke tools + { + name: "start_conversation", + description: + "Start a new conversation with a user. Call this when a user starts a new conversation.", + inputSchema: { + type: "object", + properties: {}, + required: [], }, - { - name: 'add_turn', - description: 'Add a turn to a conversation. Call this after a user has sent a message and the assistant has responded.', - inputSchema: { - type: 'object', + }, + { + name: "add_turn", + description: + "Add a turn to a conversation. Call this after a user has sent a message and the assistant has responded.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to add the turn to.", + }, + messages: { + type: "array", + description: "A list of messages to add to the session.", + items: { + type: "object", properties: { - session_id: { - type: 'string', - description: 'The ID of the session to add the turn to.', - }, - messages: { - type: 'array', - description: 'A list of messages to add to the session.', - items: { - type: 'object', - properties: { - role: { - type: 'string', - enum: ['user', 'assistant'], - description: 'The role of the message author.', - }, - content: { - type: 'string', - description: 'The content of the message.', - }, - metadata: { - type: 'object', - description: 'Optional metadata about the message.', - }, - }, - required: ['role', 'content'], - }, - }, + role: { + type: "string", + enum: ["user", "assistant"], + description: "The role of the message author.", + }, + content: { + type: "string", + description: "The content of the message.", + }, + metadata: { + type: "object", + description: "Optional metadata about the message.", + }, }, - required: ['session_id', 'messages'], + required: ["role", "content"], + }, }, + }, + required: ["session_id", "messages"], }, - { - name: 'get_personalization_insights', - description: 'Get personalization insights about the user, based on the query and the accumulated knowledge of the user across all conversations.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session for context.', - }, - query: { - type: 'string', - description: 'The question about the user\'s preferences, habits, etc.', - }, - }, - required: ['session_id', 'query'], + }, + { + name: "get_personalization_insights", + description: + "Get personalization insights about the user, based on the query and the accumulated knowledge of the user across all conversations.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session for context.", }, + query: { + type: "string", + description: + "The question about the user's preferences, habits, etc.", + }, + }, + required: ["session_id", "query"], }, + }, - // Workspace operations - { - name: 'search_workspace', - description: 'Search for messages across the entire workspace.', - inputSchema: { - type: 'object', - properties: { - query: { - type: 'string', - description: 'The search query to use.', - }, - }, - required: ['query'], + // Workspace operations + { + name: "search_workspace", + description: "Search for messages across the entire workspace.", + inputSchema: { + type: "object", + properties: { + query: { + type: "string", + description: "The search query to use.", }, + }, + required: ["query"], }, - { - name: 'get_workspace_metadata', - description: 'Get metadata for the current workspace.', - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, - }, - { - name: 'set_workspace_metadata', - description: 'Set metadata for the current workspace.', - inputSchema: { - type: 'object', - properties: { - metadata: { - type: 'object', - description: 'A dictionary of metadata to associate with the workspace.', - }, - }, - required: ['metadata'], + }, + { + name: "get_workspace_metadata", + description: "Get metadata for the current workspace.", + inputSchema: { + type: "object", + properties: {}, + required: [], + }, + }, + { + name: "set_workspace_metadata", + description: "Set metadata for the current workspace.", + inputSchema: { + type: "object", + properties: { + metadata: { + type: "object", + description: + "A dictionary of metadata to associate with the workspace.", }, + }, + required: ["metadata"], }, + }, - // Peer operations - { - name: 'create_peer', - description: 'Create or get a peer with the specified ID and optional configuration.', - inputSchema: { - type: 'object', - properties: { - peer_id: { - type: 'string', - description: 'Unique identifier for the peer.', - }, - config: { - type: 'object', - description: 'Optional configuration dictionary for the peer.', - }, - }, - required: ['peer_id'], + // Peer operations + { + name: "create_peer", + description: + "Create or get a peer with the specified ID and optional configuration.", + inputSchema: { + type: "object", + properties: { + peer_id: { + type: "string", + description: "Unique identifier for the peer.", }, - }, - { - name: 'get_peer_metadata', - description: 'Get metadata for a specific peer.', - inputSchema: { - type: 'object', - properties: { - peer_id: { - type: 'string', - description: 'The ID of the peer to get metadata for.', - }, - }, - required: ['peer_id'], + config: { + type: "object", + description: "Optional configuration dictionary for the peer.", }, + }, + required: ["peer_id"], }, - { - name: 'set_peer_metadata', - description: 'Set metadata for a specific peer.', - inputSchema: { - type: 'object', - properties: { - peer_id: { - type: 'string', - description: 'The ID of the peer to set metadata for.', - }, - metadata: { - type: 'object', - description: 'A dictionary of metadata to associate with the peer.', - }, - }, - required: ['peer_id', 'metadata'], + }, + { + name: "get_peer_metadata", + description: "Get metadata for a specific peer.", + inputSchema: { + type: "object", + properties: { + peer_id: { + type: "string", + description: "The ID of the peer to get metadata for.", }, + }, + required: ["peer_id"], }, - { - name: 'search_peer_messages', - description: 'Search for messages sent by a peer.', - inputSchema: { - type: 'object', - properties: { - peer_id: { - type: 'string', - description: 'The ID of the peer to search messages for.', - }, - query: { - type: 'string', - description: 'The search query to use.', - }, - }, - required: ['peer_id', 'query'], + }, + { + name: "set_peer_metadata", + description: "Set metadata for a specific peer.", + inputSchema: { + type: "object", + properties: { + peer_id: { + type: "string", + description: "The ID of the peer to set metadata for.", }, - }, - { - name: 'chat', - description: 'Query a peer\'s representation with natural language questions.', - inputSchema: { - type: 'object', - properties: { - peer_id: { - type: 'string', - description: 'The ID of the peer to query.', - }, - query: { - type: 'string', - description: 'The natural language question to ask.', - }, - target_peer_id: { - type: 'string', - description: 'Optional target peer ID for local representation queries.', - }, - session_id: { - type: 'string', - description: 'Optional session ID to scope the query to a specific session.', - }, - }, - required: ['peer_id', 'query'], + metadata: { + type: "object", + description: "A dictionary of metadata to associate with the peer.", }, + }, + required: ["peer_id", "metadata"], }, - { - name: 'list_peers', - description: 'Get all peers in the current workspace.', - inputSchema: { - type: 'object', - properties: {}, - required: [], + }, + { + name: "search_peer_messages", + description: "Search for messages sent by a peer.", + inputSchema: { + type: "object", + properties: { + peer_id: { + type: "string", + description: "The ID of the peer to search messages for.", }, + query: { + type: "string", + description: "The search query to use.", + }, + }, + required: ["peer_id", "query"], }, + }, + { + name: "chat", + description: + "Query a peer's representation with natural language questions.", + inputSchema: { + type: "object", + properties: { + peer_id: { + type: "string", + description: "The ID of the peer to query.", + }, + query: { + type: "string", + description: "The natural language question to ask.", + }, + target_peer_id: { + type: "string", + description: + "Optional target peer ID for local representation queries.", + }, + session_id: { + type: "string", + description: + "Optional session ID to scope the query to a specific session.", + }, + }, + required: ["peer_id", "query"], + }, + }, + { + name: "list_peers", + description: "Get all peers in the current workspace.", + inputSchema: { + type: "object", + properties: {}, + required: [], + }, + }, - // Session operations - { - name: 'create_session', - description: 'Create or get a session with the specified ID and optional configuration.', - inputSchema: { - type: 'object', + // Session operations + { + name: "create_session", + description: + "Create or get a session with the specified ID and optional configuration.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "Unique identifier for the session.", + }, + config: { + type: "object", + description: "Optional configuration dictionary for the session.", + }, + }, + required: ["session_id"], + }, + }, + { + name: "get_session_metadata", + description: "Get metadata for a specific session.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to get metadata for.", + }, + }, + required: ["session_id"], + }, + }, + { + name: "set_session_metadata", + description: "Set metadata for a specific session.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to set metadata for.", + }, + metadata: { + type: "object", + description: + "A dictionary of metadata to associate with the session.", + }, + }, + required: ["session_id", "metadata"], + }, + }, + { + name: "add_peers_to_session", + description: "Add peers to a session.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to add peers to.", + }, + peer_ids: { + type: "array", + items: { type: "string" }, + description: "List of peer IDs to add to the session.", + }, + }, + required: ["session_id", "peer_ids"], + }, + }, + { + name: "remove_peers_from_session", + description: "Remove peers from a session.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to remove peers from.", + }, + peer_ids: { + type: "array", + items: { type: "string" }, + description: "List of peer IDs to remove from the session.", + }, + }, + required: ["session_id", "peer_ids"], + }, + }, + { + name: "get_session_peers", + description: "Get all peer IDs in a session.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to get peers from.", + }, + }, + required: ["session_id"], + }, + }, + { + name: "add_messages_to_session", + description: "Add messages to a session.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to add messages to.", + }, + messages: { + type: "array", + items: { + type: "object", properties: { - session_id: { - type: 'string', - description: 'Unique identifier for the session.', - }, - config: { - type: 'object', - description: 'Optional configuration dictionary for the session.', - }, + peer_id: { + type: "string", + description: "ID of the peer sending the message", + }, + content: { + type: "string", + description: "Message content", + }, + metadata: { + type: "object", + description: "Optional metadata dictionary", + }, }, - required: ['session_id'], + required: ["peer_id", "content"], + }, + description: "List of message dictionaries.", }, + }, + required: ["session_id", "messages"], }, - { - name: 'get_session_metadata', - description: 'Get metadata for a specific session.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to get metadata for.', - }, - }, - required: ['session_id'], + }, + { + name: "get_session_messages", + description: "Get messages from a session with optional filtering.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to get messages from.", }, - }, - { - name: 'set_session_metadata', - description: 'Set metadata for a specific session.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to set metadata for.', - }, - metadata: { - type: 'object', - description: 'A dictionary of metadata to associate with the session.', - }, - }, - required: ['session_id', 'metadata'], + filters: { + type: "object", + description: "Optional dictionary of filter criteria.", }, + }, + required: ["session_id"], }, - { - name: 'add_peers_to_session', - description: 'Add peers to a session.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to add peers to.', - }, - peer_ids: { - type: 'array', - items: { type: 'string' }, - description: 'List of peer IDs to add to the session.', - }, - }, - required: ['session_id', 'peer_ids'], + }, + { + name: "get_session_context", + description: "Get optimized context for a session within a token limit.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to get context for.", }, - }, - { - name: 'remove_peers_from_session', - description: 'Remove peers from a session.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to remove peers from.', - }, - peer_ids: { - type: 'array', - items: { type: 'string' }, - description: 'List of peer IDs to remove from the session.', - }, - }, - required: ['session_id', 'peer_ids'], + summary: { + type: "boolean", + description: "Whether to include summary information.", + default: true, }, - }, - { - name: 'get_session_peers', - description: 'Get all peer IDs in a session.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to get peers from.', - }, - }, - required: ['session_id'], + tokens: { + type: "integer", + description: "Maximum number of tokens to include in the context.", }, + }, + required: ["session_id"], }, - { - name: 'add_messages_to_session', - description: 'Add messages to a session.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to add messages to.', - }, - messages: { - type: 'array', - items: { - type: 'object', - properties: { - peer_id: { - type: 'string', - description: 'ID of the peer sending the message', - }, - content: { - type: 'string', - description: 'Message content', - }, - metadata: { - type: 'object', - description: 'Optional metadata dictionary', - }, - }, - required: ['peer_id', 'content'], - }, - description: 'List of message dictionaries.', - }, - }, - required: ['session_id', 'messages'], + }, + { + name: "search_session_messages", + description: "Search for messages in a specific session.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session to search messages in.", }, - }, - { - name: 'get_session_messages', - description: 'Get messages from a session with optional filtering.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to get messages from.', - }, - filters: { - type: 'object', - description: 'Optional dictionary of filter criteria.', - }, - }, - required: ['session_id'], + query: { + type: "string", + description: "The search query to use.", }, + }, + required: ["session_id", "query"], }, - { - name: 'get_session_context', - description: 'Get optimized context for a session within a token limit.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to get context for.', - }, - summary: { - type: 'boolean', - description: 'Whether to include summary information.', - default: true, - }, - tokens: { - type: 'integer', - description: 'Maximum number of tokens to include in the context.', - }, - }, - required: ['session_id'], + }, + { + name: "get_working_representation", + description: + "Get the current working representation of a peer in a session.", + inputSchema: { + type: "object", + properties: { + session_id: { + type: "string", + description: "The ID of the session.", }, - }, - { - name: 'search_session_messages', - description: 'Search for messages in a specific session.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session to search messages in.', - }, - query: { - type: 'string', - description: 'The search query to use.', - }, - }, - required: ['session_id', 'query'], + peer_id: { + type: "string", + description: + "The ID of the peer to get the working representation of.", }, - }, - { - name: 'get_working_representation', - description: 'Get the current working representation of a peer in a session.', - inputSchema: { - type: 'object', - properties: { - session_id: { - type: 'string', - description: 'The ID of the session.', - }, - peer_id: { - type: 'string', - description: 'The ID of the peer to get the working representation of.', - }, - target_peer_id: { - type: 'string', - description: 'Optional target peer ID to get the representation of what peer_id knows about target_peer_id.', - }, - }, - required: ['session_id', 'peer_id'], + target_peer_id: { + type: "string", + description: + "Optional target peer ID to get the representation of what peer_id knows about target_peer_id.", }, + }, + required: ["session_id", "peer_id"], }, - { - name: 'list_sessions', - description: 'Get all sessions in the current workspace.', - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, + }, + { + name: "list_sessions", + description: "Get all sessions in the current workspace.", + inputSchema: { + type: "object", + properties: {}, + required: [], }, + }, ]; /** * Execute a tool with validation and consistent response handling */ -async function executeToolCall(honcho: HonchoWorker, toolName: string, toolArguments: any, requestId: string | number | null): Promise { - let result: any; +async function executeToolCall( + honcho: HonchoWorker, + toolName: string, + toolArguments: any, + requestId: string | number | null, +): Promise { + let result: any; - switch (toolName) { - // Bespoke tools - case 'start_conversation': - result = await honcho.startConversation(); - break; + switch (toolName) { + // Bespoke tools + case "start_conversation": + result = await honcho.startConversation(); + break; - case 'add_turn': { - const validation = validateArguments(toolArguments, ['session_id', 'messages'], requestId); - if (validation) return validation; + case "add_turn": { + const validation = validateArguments( + toolArguments, + ["session_id", "messages"], + requestId, + ); + if (validation) return validation; - await honcho.addTurn(toolArguments.session_id, toolArguments.messages); - result = 'Turn added successfully'; - break; - } - - case 'get_personalization_insights': { - const validation = validateArguments(toolArguments, ['session_id', 'query'], requestId); - if (validation) return validation; - - result = await honcho.getPersonalizationInsights(toolArguments.session_id, toolArguments.query); - break; - } - - // Workspace operations - case 'search_workspace': { - const validation = validateArguments(toolArguments, ['query'], requestId); - if (validation) return validation; - - result = await honcho.searchWorkspace(toolArguments.query); - break; - } - - case 'get_workspace_metadata': - result = await honcho.getWorkspaceMetadata(); - break; - - case 'set_workspace_metadata': { - const validation = validateArguments(toolArguments, ['metadata'], requestId); - if (validation) return validation; - - await honcho.setWorkspaceMetadata(toolArguments.metadata); - result = 'Workspace metadata set successfully'; - break; - } - - // Peer operations - case 'create_peer': { - const validation = validateArguments(toolArguments, ['peer_id'], requestId); - if (validation) return validation; - - result = await honcho.createPeer(toolArguments.peer_id, toolArguments.config); - break; - } - - case 'get_peer_metadata': { - const validation = validateArguments(toolArguments, ['peer_id'], requestId); - if (validation) return validation; - - result = await honcho.getPeerMetadata(toolArguments.peer_id); - break; - } - - case 'set_peer_metadata': { - const validation = validateArguments(toolArguments, ['peer_id', 'metadata'], requestId); - if (validation) return validation; - - await honcho.setPeerMetadata(toolArguments.peer_id, toolArguments.metadata); - result = 'Peer metadata set successfully'; - break; - } - - case 'search_peer_messages': { - const validation = validateArguments(toolArguments, ['peer_id', 'query'], requestId); - if (validation) return validation; - - result = await honcho.searchPeerMessages(toolArguments.peer_id, toolArguments.query); - break; - } - - case 'chat': { - const validation = validateArguments(toolArguments, ['peer_id', 'query'], requestId); - if (validation) return validation; - - result = await honcho.chat(toolArguments.peer_id, toolArguments.query, toolArguments.target_peer_id, toolArguments.session_id); - break; - } - - case 'list_peers': - result = await honcho.listPeers(); - break; - - // Session operations - case 'create_session': { - const validation = validateArguments(toolArguments, ['session_id'], requestId); - if (validation) return validation; - - result = await honcho.createSession(toolArguments.session_id, toolArguments.config); - break; - } - - case 'get_session_metadata': { - const validation = validateArguments(toolArguments, ['session_id'], requestId); - if (validation) return validation; - - result = await honcho.getSessionMetadata(toolArguments.session_id); - break; - } - - case 'set_session_metadata': { - const validation = validateArguments(toolArguments, ['session_id', 'metadata'], requestId); - if (validation) return validation; - - await honcho.setSessionMetadata(toolArguments.session_id, toolArguments.metadata); - result = 'Session metadata set successfully'; - break; - } - - case 'add_peers_to_session': { - const validation = validateArguments(toolArguments, ['session_id', 'peer_ids'], requestId); - if (validation) return validation; - - await honcho.addPeersToSession(toolArguments.session_id, toolArguments.peer_ids); - result = 'Peers added to session successfully'; - break; - } - - case 'remove_peers_from_session': { - const validation = validateArguments(toolArguments, ['session_id', 'peer_ids'], requestId); - if (validation) return validation; - - await honcho.removePeersFromSession(toolArguments.session_id, toolArguments.peer_ids); - result = 'Peers removed from session successfully'; - break; - } - - case 'get_session_peers': { - const validation = validateArguments(toolArguments, ['session_id'], requestId); - if (validation) return validation; - - result = await honcho.getSessionPeers(toolArguments.session_id); - break; - } - - case 'add_messages_to_session': { - const validation = validateArguments(toolArguments, ['session_id', 'messages'], requestId); - if (validation) return validation; - - await honcho.addMessagesToSession(toolArguments.session_id, toolArguments.messages); - result = 'Messages added to session successfully'; - break; - } - - case 'get_session_messages': { - const validation = validateArguments(toolArguments, ['session_id'], requestId); - if (validation) return validation; - - result = await honcho.getSessionMessages(toolArguments.session_id, toolArguments.filters); - break; - } - - case 'get_session_context': { - const validation = validateArguments(toolArguments, ['session_id'], requestId); - if (validation) return validation; - - result = await honcho.getSessionContext(toolArguments.session_id, toolArguments.summary, toolArguments.tokens); - break; - } - - case 'search_session_messages': { - const validation = validateArguments(toolArguments, ['session_id', 'query'], requestId); - if (validation) return validation; - - result = await honcho.searchSessionMessages(toolArguments.session_id, toolArguments.query); - break; - } - - case 'get_working_representation': { - const validation = validateArguments(toolArguments, ['session_id', 'peer_id'], requestId); - if (validation) return validation; - - result = await honcho.getWorkingRepresentation(toolArguments.session_id, toolArguments.peer_id, toolArguments.target_peer_id); - break; - } - - case 'list_sessions': - result = await honcho.listSessions(); - break; - - default: - return createErrorResponse(requestId, -32601, `Method not found: ${toolName}`); + await honcho.addTurn(toolArguments.session_id, toolArguments.messages); + result = "Turn added successfully"; + break; } - const responseData = typeof result === 'string' ? result : JSON.stringify(result); - return new Response(JSON.stringify(createJsonRpcResponse(requestId, { - content: [{ - type: 'text', + case "get_personalization_insights": { + const validation = validateArguments( + toolArguments, + ["session_id", "query"], + requestId, + ); + if (validation) return validation; + + result = await honcho.getPersonalizationInsights( + toolArguments.session_id, + toolArguments.query, + ); + break; + } + + // Workspace operations + case "search_workspace": { + const validation = validateArguments(toolArguments, ["query"], requestId); + if (validation) return validation; + + result = await honcho.searchWorkspace(toolArguments.query); + break; + } + + case "get_workspace_metadata": + result = await honcho.getWorkspaceMetadata(); + break; + + case "set_workspace_metadata": { + const validation = validateArguments( + toolArguments, + ["metadata"], + requestId, + ); + if (validation) return validation; + + await honcho.setWorkspaceMetadata(toolArguments.metadata); + result = "Workspace metadata set successfully"; + break; + } + + // Peer operations + case "create_peer": { + const validation = validateArguments( + toolArguments, + ["peer_id"], + requestId, + ); + if (validation) return validation; + + result = await honcho.createPeer( + toolArguments.peer_id, + toolArguments.config, + ); + break; + } + + case "get_peer_metadata": { + const validation = validateArguments( + toolArguments, + ["peer_id"], + requestId, + ); + if (validation) return validation; + + result = await honcho.getPeerMetadata(toolArguments.peer_id); + break; + } + + case "set_peer_metadata": { + const validation = validateArguments( + toolArguments, + ["peer_id", "metadata"], + requestId, + ); + if (validation) return validation; + + await honcho.setPeerMetadata( + toolArguments.peer_id, + toolArguments.metadata, + ); + result = "Peer metadata set successfully"; + break; + } + + case "search_peer_messages": { + const validation = validateArguments( + toolArguments, + ["peer_id", "query"], + requestId, + ); + if (validation) return validation; + + result = await honcho.searchPeerMessages( + toolArguments.peer_id, + toolArguments.query, + ); + break; + } + + case "chat": { + const validation = validateArguments( + toolArguments, + ["peer_id", "query"], + requestId, + ); + if (validation) return validation; + + result = await honcho.chat( + toolArguments.peer_id, + toolArguments.query, + toolArguments.target_peer_id, + toolArguments.session_id, + ); + break; + } + + case "list_peers": + result = await honcho.listPeers(); + break; + + // Session operations + case "create_session": { + const validation = validateArguments( + toolArguments, + ["session_id"], + requestId, + ); + if (validation) return validation; + + result = await honcho.createSession( + toolArguments.session_id, + toolArguments.config, + ); + break; + } + + case "get_session_metadata": { + const validation = validateArguments( + toolArguments, + ["session_id"], + requestId, + ); + if (validation) return validation; + + result = await honcho.getSessionMetadata(toolArguments.session_id); + break; + } + + case "set_session_metadata": { + const validation = validateArguments( + toolArguments, + ["session_id", "metadata"], + requestId, + ); + if (validation) return validation; + + await honcho.setSessionMetadata( + toolArguments.session_id, + toolArguments.metadata, + ); + result = "Session metadata set successfully"; + break; + } + + case "add_peers_to_session": { + const validation = validateArguments( + toolArguments, + ["session_id", "peer_ids"], + requestId, + ); + if (validation) return validation; + + await honcho.addPeersToSession( + toolArguments.session_id, + toolArguments.peer_ids, + ); + result = "Peers added to session successfully"; + break; + } + + case "remove_peers_from_session": { + const validation = validateArguments( + toolArguments, + ["session_id", "peer_ids"], + requestId, + ); + if (validation) return validation; + + await honcho.removePeersFromSession( + toolArguments.session_id, + toolArguments.peer_ids, + ); + result = "Peers removed from session successfully"; + break; + } + + case "get_session_peers": { + const validation = validateArguments( + toolArguments, + ["session_id"], + requestId, + ); + if (validation) return validation; + + result = await honcho.getSessionPeers(toolArguments.session_id); + break; + } + + case "add_messages_to_session": { + const validation = validateArguments( + toolArguments, + ["session_id", "messages"], + requestId, + ); + if (validation) return validation; + + await honcho.addMessagesToSession( + toolArguments.session_id, + toolArguments.messages, + ); + result = "Messages added to session successfully"; + break; + } + + case "get_session_messages": { + const validation = validateArguments( + toolArguments, + ["session_id"], + requestId, + ); + if (validation) return validation; + + result = await honcho.getSessionMessages( + toolArguments.session_id, + toolArguments.filters, + ); + break; + } + + case "get_session_context": { + const validation = validateArguments( + toolArguments, + ["session_id"], + requestId, + ); + if (validation) return validation; + + result = await honcho.getSessionContext( + toolArguments.session_id, + toolArguments.summary, + toolArguments.tokens, + ); + break; + } + + case "search_session_messages": { + const validation = validateArguments( + toolArguments, + ["session_id", "query"], + requestId, + ); + if (validation) return validation; + + result = await honcho.searchSessionMessages( + toolArguments.session_id, + toolArguments.query, + ); + break; + } + + case "get_working_representation": { + const validation = validateArguments( + toolArguments, + ["session_id", "peer_id"], + requestId, + ); + if (validation) return validation; + + result = await honcho.getWorkingRepresentation( + toolArguments.session_id, + toolArguments.peer_id, + toolArguments.target_peer_id, + ); + break; + } + + case "list_sessions": + result = await honcho.listSessions(); + break; + + default: + return createErrorResponse( + requestId, + -32601, + `Method not found: ${toolName}`, + ); + } + + const responseData = + typeof result === "string" ? result : JSON.stringify(result); + return new Response( + JSON.stringify( + createJsonRpcResponse(requestId, { + content: [ + { + type: "text", text: responseData, - }], - })), { - status: 200, - headers: { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*', - }, - }); + }, + ], + }), + ), + { + status: 200, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", + }, + }, + ); } /** * Main Cloudflare Worker export */ export default { - async fetch(request: Request): Promise { - // Handle CORS preflight requests - if (request.method === 'OPTIONS') { - return new Response(null, { - status: 200, - headers: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Honcho-User-Name, X-Honcho-Base-URL, X-Honcho-Workspace-ID, X-Honcho-Assistant-Name', + async fetch(request: Request): Promise { + // Handle CORS preflight requests + if (request.method === "OPTIONS") { + return new Response(null, { + status: 200, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Allow-Headers": + "Content-Type, Authorization, X-Honcho-User-Name, X-Honcho-Base-URL, X-Honcho-Workspace-ID, X-Honcho-Assistant-Name", + }, + }); + } + + // Only accept POST requests for JSON-RPC + if (request.method !== "POST") { + return createErrorResponse(null, -32600, "Invalid Request"); + } + + let requestData: JsonRpcRequest; + + try { + requestData = (await request.json()) as JsonRpcRequest; + } catch (error) { + return createErrorResponse(null, -32700, "Parse error"); + } + + // Validate JSON-RPC format + if (requestData.jsonrpc !== "2.0") { + return createErrorResponse( + requestData.id ?? null, + -32600, + "Invalid Request", + ); + } + + if (!requestData.method) { + return createErrorResponse( + requestData.id ?? null, + -32600, + "Invalid Request", + ); + } + + // Parse configuration + const config = parseConfig(request); + if (!config && requestData.method !== "initialize") { + return createErrorResponse( + requestData.id ?? null, + -32602, + "Missing or invalid API key", + ); + } + + const honcho = config ? new HonchoWorker(config) : null; + + try { + switch (requestData.method) { + case "initialize": + return new Response( + JSON.stringify( + createJsonRpcResponse(requestData.id ?? null, { + protocolVersion: "2024-11-05", + capabilities: { + tools: {}, }, - }); - } + serverInfo: { + name: "Honcho MCP Server", + version: "1.0.0", + }, + }), + ), + { + status: 200, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", + }, + }, + ); - // Only accept POST requests for JSON-RPC - if (request.method !== 'POST') { - return createErrorResponse(null, -32600, 'Invalid Request'); - } + case "notifications/initialized": + // MCP initialized notification - no response needed + return new Response(null, { + status: 204, + headers: { + "Access-Control-Allow-Origin": "*", + }, + }); - let requestData: JsonRpcRequest; + case "tools/list": + return new Response( + JSON.stringify( + createJsonRpcResponse(requestData.id ?? null, { + tools: tools, + }), + ), + { + status: 200, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", + }, + }, + ); - try { - requestData = await request.json() as JsonRpcRequest; - } catch (error) { - return createErrorResponse(null, -32700, 'Parse error'); - } + case "tools/call": + if (!honcho) { + return createErrorResponse( + requestData.id ?? null, + -32602, + "Missing API key", + ); + } - // Validate JSON-RPC format - if (requestData.jsonrpc !== '2.0') { - return createErrorResponse(requestData.id ?? null, -32600, 'Invalid Request'); - } + const toolName = requestData.params?.name; + const toolArguments = requestData.params?.arguments || {}; - if (!requestData.method) { - return createErrorResponse(requestData.id ?? null, -32600, 'Invalid Request'); - } + return await executeToolCall( + honcho, + toolName, + toolArguments, + requestData.id ?? null, + ); - // Parse configuration - const config = parseConfig(request); - if (!config && requestData.method !== 'initialize') { - return createErrorResponse(requestData.id ?? null, -32602, 'Missing or invalid API key'); - } - - const honcho = config ? new HonchoWorker(config) : null; - - try { - switch (requestData.method) { - case 'initialize': - return new Response(JSON.stringify(createJsonRpcResponse(requestData.id ?? null, { - protocolVersion: '2024-11-05', - capabilities: { - tools: {} - }, - serverInfo: { - name: 'Honcho MCP Server', - version: '1.0.0', - }, - })), { - status: 200, - headers: { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*', - }, - }); - - case 'notifications/initialized': - // MCP initialized notification - no response needed - return new Response(null, { - status: 204, - headers: { - 'Access-Control-Allow-Origin': '*', - }, - }); - - case 'tools/list': - return new Response(JSON.stringify(createJsonRpcResponse(requestData.id ?? null, { - tools: tools, - })), { - status: 200, - headers: { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*', - }, - }); - - case 'tools/call': - if (!honcho) { - return createErrorResponse(requestData.id ?? null, -32602, 'Missing API key'); - } - - const toolName = requestData.params?.name; - const toolArguments = requestData.params?.arguments || {}; - - return await executeToolCall(honcho, toolName, toolArguments, requestData.id ?? null); - - default: - return createErrorResponse(requestData.id ?? null, -32601, `Method not found: ${requestData.method}`); - } - } catch (error) { - console.error('Worker error:', error); - const errorMessage = error instanceof Error ? error.message : 'Internal server error'; - return createErrorResponse(requestData.id ?? null, -32603, errorMessage); - } - }, + default: + return createErrorResponse( + requestData.id ?? null, + -32601, + `Method not found: ${requestData.method}`, + ); + } + } catch (error) { + console.error("Worker error:", error); + const errorMessage = + error instanceof Error ? error.message : "Internal server error"; + return createErrorResponse(requestData.id ?? null, -32603, errorMessage); + } + }, };