Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 43 additions & 70 deletions apps/mcp/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,52 +45,41 @@ export interface Project {
documentCount?: number
}

// Graph API types
export interface GraphApiMemory {
// Documents API types
export interface DocumentMemoryEntry {
id: string
memory: string
isStatic: boolean
isLatest: boolean
isForgotten: boolean
forgetAfter: string | null
version: number
parentMemoryId: string | null
spaceId: string
isStatic?: boolean
isLatest?: boolean
isForgotten?: boolean
forgetAfter?: string | null
forgetReason?: string | null
version?: number
parentMemoryId?: string | null
rootMemoryId?: string | null
createdAt: string
updatedAt: string
}

export interface GraphApiDocument {
export interface DocumentWithMemories {
id: string
title: string | null
summary: string | null
documentType: string
summary?: string | null
type: string
createdAt: string
updatedAt: string
x: number
y: number
memories: GraphApiMemory[]
memoryEntries: DocumentMemoryEntry[]
}

export interface GraphApiEdge {
source: string
target: string
similarity: number
}

export interface GraphViewportResponse {
documents: GraphApiDocument[]
edges: GraphApiEdge[]
viewport: { minX: number; maxX: number; minY: number; maxY: number }
totalCount: number
}

export interface GraphBoundsResponse {
bounds: {
minX: number
maxX: number
minY: number
maxY: number
} | null
export interface DocumentsApiResponse {
documents: DocumentWithMemories[]
pagination: {
currentPage: number
limit: number
totalItems: number
totalPages: number
}
}

export function getMemoryText(m: Memory): string {
Expand Down Expand Up @@ -171,9 +160,13 @@ export class SupermemoryClient {
message: `Successfully forgot memory (exact match) with ID: ${result.id}`,
containerTag: this.containerTag,
}
} catch (error: any) {
} catch (error: unknown) {
// If not 404, it's a real error - re-throw it
if (error?.status !== 404) {
const status =
error && typeof error === "object" && "status" in error
? (error as Record<string, unknown>).status
: undefined
if (status !== 404) {
throw error
}
// Otherwise continue to semantic search fallback
Expand Down Expand Up @@ -332,53 +325,33 @@ export class SupermemoryClient {
}
}

// Fetch graph bounds for coordinate range
async getGraphBounds(containerTags?: string[]): Promise<GraphBoundsResponse> {
try {
const params = new URLSearchParams()
if (containerTags?.length) {
params.set("containerTags", JSON.stringify(containerTags))
}
const url = `${this.apiUrl}/v3/graph/bounds${params.toString() ? `?${params}` : ""}`
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${this.bearerToken}`,
"Content-Type": "application/json",
},
})
if (!response.ok) {
throw Object.assign(new Error("Failed to fetch graph bounds"), {
status: response.status,
})
}
return (await response.json()) as GraphBoundsResponse
} catch (error) {
this.handleError(error)
}
}

// Fetch graph data for a viewport region
async getGraphViewport(
viewport: { minX: number; maxX: number; minY: number; maxY: number },
// Fetch documents with their memory entries
async getDocuments(
containerTags?: string[],
page = 1,
limit = 200,
): Promise<GraphViewportResponse> {
): Promise<DocumentsApiResponse> {
try {
const response = await fetch(`${this.apiUrl}/v3/graph/viewport`, {
const response = await fetch(`${this.apiUrl}/v3/documents/documents`, {
method: "POST",
headers: {
Authorization: `Bearer ${this.bearerToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ viewport, containerTags, limit }),
body: JSON.stringify({
page,
limit,
sort: "createdAt",
order: "desc",
containerTags,
}),
})
if (!response.ok) {
throw Object.assign(new Error("Failed to fetch graph viewport"), {
throw Object.assign(new Error("Failed to fetch documents"), {
status: response.status,
})
}
return (await response.json()) as GraphViewportResponse
return (await response.json()) as DocumentsApiResponse
} catch (error) {
this.handleError(error)
}
Expand Down
45 changes: 13 additions & 32 deletions apps/mcp/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,21 +311,14 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> {
? [effectiveContainerTag]
: undefined

const [bounds, viewport] = await Promise.all([
client.getGraphBounds(containerTags),
client.getGraphViewport(
{ minX: 0, maxX: 1000, minY: 0, maxY: 1000 },
containerTags,
200,
),
])

const memoryCount = viewport.documents.reduce(
(sum, d) => sum + d.memories.length,
const result = await client.getDocuments(containerTags, 1, 200)

const memoryCount = result.documents.reduce(
(sum, d) => sum + d.memoryEntries.length,
0,
)
const textParts = [
`Memory Graph: ${viewport.documents.length} documents, ${memoryCount} memories, ${viewport.edges.length} connections`,
`Memory Graph: ${result.documents.length} documents, ${memoryCount} memories`,
]
if (effectiveContainerTag) {
textParts.push(`Project: ${effectiveContainerTag}`)
Expand All @@ -335,10 +328,8 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> {
content: [{ type: "text" as const, text: textParts.join(". ") }],
structuredContent: {
containerTag: effectiveContainerTag,
bounds: bounds.bounds,
documents: viewport.documents,
edges: viewport.edges,
totalCount: viewport.totalCount,
documents: result.documents,
totalCount: result.pagination.totalItems,
},
}
} catch (error) {
Expand All @@ -359,20 +350,15 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> {
},
)

// App-only tool for the UI to fetch additional graph data
// App-only tool for the UI to fetch additional documents (pagination)
registerAppTool(
this.server,
"fetch-graph-data",
{
description: "Fetch graph data for a viewport region",
description: "Fetch documents with memories for graph display",
inputSchema: z.object({
containerTag: z.string().optional(),
viewport: z.object({
minX: z.number(),
maxX: z.number(),
minY: z.number(),
maxY: z.number(),
}),
page: z.number().optional().default(1),
limit: z.number().optional().default(200),
}),
_meta: {
Expand All @@ -385,12 +371,7 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> {
// @ts-expect-error - zod type inference issue with MCP SDK
async (args: {
containerTag?: string
viewport: {
minX: number
maxX: number
minY: number
maxY: number
}
page?: number
limit?: number
}) => {
try {
Expand All @@ -400,9 +381,9 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> {
const containerTags = effectiveContainerTag
? [effectiveContainerTag]
: undefined
const data = await client.getGraphViewport(
args.viewport,
const data = await client.getDocuments(
containerTags,
args.page,
args.limit,
)

Expand Down
Loading
Loading