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",