From c3b16f871d3bfd137ddde1b4b672978224cd2499 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:27:58 -0700 Subject: [PATCH] feat(mcp): add export-memories tool for data portability Add an export-memories MCP tool that dumps all documents with their extracted memories as JSON or JSONL. Uses the existing graph viewport endpoint to retrieve documents and memories without requiring new backend API endpoints. Supports per-project filtering via containerTag and two output formats (json for readability, jsonl for streaming/piping). Also adds an exportMemories() method to SupermemoryClient that fetches the full graph bounds and retrieves all documents within. Co-Authored-By: Claude Opus 4.6 --- apps/mcp/src/client.ts | 27 +++++++++++++++++ apps/mcp/src/server.ts | 68 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/apps/mcp/src/client.ts b/apps/mcp/src/client.ts index 2924f6fb4..95295eefb 100644 --- a/apps/mcp/src/client.ts +++ b/apps/mcp/src/client.ts @@ -384,6 +384,33 @@ export class SupermemoryClient { } } + // Export all memories via the graph viewport endpoint. + // Returns documents with their extracted memories for backup/migration. + async exportMemories(containerTags?: string[]): Promise<{ + documents: GraphApiDocument[] + totalCount: number + }> { + try { + // Get bounds first to know the full coordinate range + const bounds = await this.getGraphBounds(containerTags) + const { minX, maxX, minY, maxY } = bounds.bounds + + // Fetch all documents within the full bounds, high limit + const viewport = await this.getGraphViewport( + { minX, maxX, minY, maxY }, + containerTags, + 10000, + ) + + return { + documents: viewport.documents, + totalCount: viewport.totalCount, + } + } catch (error) { + this.handleError(error) + } + } + private handleError(error: unknown): never { // Handle network/fetch errors if (error instanceof TypeError) { diff --git a/apps/mcp/src/server.ts b/apps/mcp/src/server.ts index 17f8ecd4f..56ecd3526 100644 --- a/apps/mcp/src/server.ts +++ b/apps/mcp/src/server.ts @@ -242,6 +242,74 @@ export class SupermemoryMCP extends McpAgent { }, ) + // Register export-memories tool + const exportSchema = z.object({ + ...(hasRootContainerTag ? {} : containerTagField), + format: z + .enum(["json", "jsonl"]) + .optional() + .default("json") + .describe("Output format: json (single array) or jsonl (one document per line)"), + }) + + type ExportArgs = z.infer + + this.server.registerTool( + "export-memories", + { + description: + "Export all memories as structured data for backup or migration. Returns documents with their extracted memories.", + inputSchema: exportSchema, + }, + // @ts-expect-error - zod type inference issue with MCP SDK + async (args: ExportArgs) => { + try { + const effectiveContainerTag = + (args as { containerTag?: string }).containerTag || + this.props?.containerTag + const client = this.getClient(effectiveContainerTag) + const containerTags = effectiveContainerTag + ? [effectiveContainerTag] + : undefined + + const result = await client.exportMemories(containerTags) + + const isJsonl = args.format === "jsonl" + const output = isJsonl + ? result.documents.map((d) => JSON.stringify(d)).join("\n") + : JSON.stringify(result.documents, null, 2) + + const memoryCount = result.documents.reduce( + (sum, d) => sum + d.memories.length, + 0, + ) + + return { + content: [ + { + type: "text" as const, + text: `Exported ${result.documents.length} documents with ${memoryCount} memories (${result.totalCount} total).${effectiveContainerTag ? ` Project: ${effectiveContainerTag}` : ""}\n\n${output}`, + }, + ], + } + } catch (error) { + const message = + error instanceof Error + ? error.message + : "An unexpected error occurred" + return { + content: [ + { + type: "text" as const, + text: `Error exporting memories: ${message}`, + }, + ], + isError: true, + } + } + }, + ) + // Register whoAmI tool this.server.registerTool( "whoAmI",