diff options
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/mcp/package.json | 4 | ||||
| -rw-r--r-- | apps/mcp/src/index.ts | 5 | ||||
| -rw-r--r-- | apps/mcp/src/server.ts | 88 |
3 files changed, 89 insertions, 8 deletions
diff --git a/apps/mcp/package.json b/apps/mcp/package.json index a0042da6..5ba472ad 100644 --- a/apps/mcp/package.json +++ b/apps/mcp/package.json @@ -9,8 +9,8 @@ }, "dependencies": { "@cloudflare/workers-oauth-provider": "^0.2.2", - "@modelcontextprotocol/sdk": "^1.12.1", - "agents": "^0.2.32", + "@modelcontextprotocol/sdk": "^1.25.2", + "agents": "^0.3.5", "hono": "^4.11.1", "posthog-node": "^5.18.0", "supermemory": "^4.0.0", diff --git a/apps/mcp/src/index.ts b/apps/mcp/src/index.ts index a1fe7bac..0586492f 100644 --- a/apps/mcp/src/index.ts +++ b/apps/mcp/src/index.ts @@ -1,8 +1,9 @@ -import { Hono } from "hono" import { cors } from "hono/cors" +import { Hono } from "hono" import { SupermemoryMCP } from "./server" import { isApiKey, validateApiKey, validateOAuthToken } from "./auth" import { initPosthog } from "./posthog" +import type { ContentfulStatusCode } from "hono/utils/http-status" type Bindings = { MCP_SERVER: DurableObjectNamespace @@ -78,7 +79,7 @@ app.get("/.well-known/oauth-authorization-server", async (c) => { if (!response.ok) { return c.json( { error: "Failed to fetch authorization server metadata" }, - response.status, + { status: response.status as ContentfulStatusCode }, ) } diff --git a/apps/mcp/src/server.ts b/apps/mcp/src/server.ts index bbcd493e..d83d2326 100644 --- a/apps/mcp/src/server.ts +++ b/apps/mcp/src/server.ts @@ -20,6 +20,7 @@ type Props = { export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { private clientInfo: { name: string; version?: string } | null = null + private cachedContainerTags: string[] = [] server = new McpServer({ name: "supermemory", @@ -37,7 +38,6 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { initPosthog(this.env.POSTHOG_API_KEY) - // Hook MCP McpAgent to capture client info this.server.server.oninitialized = async () => { const clientVersion = this.server.server.getClientVersion() @@ -49,6 +49,11 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { await this.ctx.storage.put("clientInfo", this.clientInfo) } } + + await this.refreshContainerTags() // Fetch available projects for schema descriptions + + const containerTagDescription = this.getContainerTagDescription() + const memorySchema = z.object({ content: z .string() @@ -58,7 +63,7 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { containerTag: z .string() .max(128, "Container tag exceeds maximum length") - .describe("Optional container tag") + .describe(containerTagDescription) .optional(), }) @@ -71,7 +76,7 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { containerTag: z .string() .max(128, "Container tag exceeds maximum length") - .describe("Optional container tag") + .describe(containerTagDescription) .optional(), }) @@ -79,7 +84,7 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { containerTag: z .string() .max(128, "Container tag exceeds maximum length") - .describe("Optional container tag to scope the profile") + .describe(containerTagDescription) .optional(), includeRecent: z .boolean() @@ -176,6 +181,63 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { }, ) + // Register listProjects tool + this.server.registerTool( + "listProjects", + { + description: + "List all available projects for organizing memories. Use this to discover valid project names for memory/recall operations.", + inputSchema: z.object({ + refresh: z + .boolean() + .optional() + .default(true) + .describe("Refresh the list from the server (default: true)"), + }), + }, + // @ts-expect-error - zod type inference issue with MCP SDK + async (args: { refresh?: boolean }) => { + try { + if (args.refresh !== false) { + await this.refreshContainerTags() + } + const projects = this.cachedContainerTags + + if (projects.length === 0) { + return { + content: [ + { + type: "text" as const, + text: "No projects found. Memories will use the default project.", + }, + ], + } + } + + return { + content: [ + { + type: "text" as const, + text: `Available projects:\n${projects.map((p) => `- ${p}`).join("\n")}`, + }, + ], + } + } catch (error) { + const message = + error instanceof Error ? error.message : "An unexpected error occurred" + return { + content: [ + { + type: "text" as const, + text: `Error listing projects: ${message}`, + }, + ], + isError: true, + } + } + }, + ) + // Register whoAmI tool this.server.registerTool( "whoAmI", @@ -222,6 +284,7 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { "User profile and preferences for system context injection. Returns a formatted system message with user's stable preferences and recent activity.", //argsSchema: contextPromptSchema.shape, TODO: commenting out for now as it will add more friction to the user }, + // @ts-expect-error - zod type inference issue with MCP SDK async (args: ContextPromptArgs) => { try { const { containerTag, includeRecent = true } = args @@ -543,4 +606,21 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> { private getMcpSessionId(): string { return this.ctx.id.name || "unknown" } + + private async refreshContainerTags(): Promise<void> { + try { + const client = this.getClient() + this.cachedContainerTags = await client.getProjects() + } catch (error) { + console.error("Failed to fetch container tags:", error) + } + } + + private getContainerTagDescription(): string { + const baseDescription = "Optional project to scope memories" + if (this.cachedContainerTags.length === 0) { + return baseDescription + } + return `${baseDescription}. Available projects: ${this.cachedContainerTags.join(", ")}` + } } |