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
48 changes: 9 additions & 39 deletions apps/mcp/src/server/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import { z } from "zod"
import {
containerTagSchema,
documentsApiResponseSchema,
paginationSchema,
memoriesListSchema,
type ContainerTag,
type DocumentMemoryEntry,
type DocumentsApiResponse,
type DocumentWithMemories,
type MemoriesList,
type MemoryEntry,
type MemoryEntryHistory,
} from "../../shared/types"

const MAX_CHARS = 200000
Expand All @@ -34,43 +37,10 @@ export interface DocumentsListResponse {
pagination: SdkDocumentListResponse["pagination"]
}

const memoryEntryHistorySchema = z.looseObject({
id: z.string(),
memory: z.string(),
version: z.number(),
createdAt: z.string(),
updatedAt: z.string(),
parentMemoryId: z.string().nullish(),
rootMemoryId: z.string().nullish(),
isLatest: z.boolean().optional(),
isForgotten: z.boolean().optional(),
})

export type MemoryEntryHistory = z.infer<typeof memoryEntryHistorySchema>

const memoryEntrySchema = z.looseObject({
id: z.string(),
memory: z.string(),
version: z.number(),
isLatest: z.boolean(),
isForgotten: z.boolean(),
isStatic: z.boolean().optional(),
isInference: z.boolean().optional(),
createdAt: z.string(),
updatedAt: z.string(),
sourceCount: z.number().optional(),
documentIds: z.array(z.string()).optional(),
history: z.array(memoryEntryHistorySchema).optional(),
})

export type MemoryEntry = z.infer<typeof memoryEntrySchema>

const memoryEntriesResponseSchema = z.object({
memoryEntries: z.array(memoryEntrySchema),
pagination: paginationSchema,
})

export type MemoryEntriesResponse = z.infer<typeof memoryEntriesResponseSchema>
// Memory-entry shapes live in shared/types so the client parser and the
// listMemories output schema share one definition and can't drift.
export type { MemoryEntry, MemoryEntryHistory }
export type MemoryEntriesResponse = MemoriesList

export type Memory =
| {
Expand Down Expand Up @@ -452,7 +422,7 @@ export class SupermemoryClient {
})
}

return memoryEntriesResponseSchema.parse(await response.json())
return memoriesListSchema.parse(await response.json())
} catch (error) {
this.handleError(error)
}
Expand Down
35 changes: 4 additions & 31 deletions apps/mcp/src/server/tools/output-schemas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from "zod"
import {
containerTagAccessSchema,
memoriesListSchema,
paginationSchema,
sessionScopeSchema,
} from "../../shared/types"
Expand Down Expand Up @@ -42,33 +43,6 @@ const documentSummarySchema = z.object({
summary: z.string().nullable(),
})

const memoryHistorySchema = z.object({
id: z.string(),
memory: z.string(),
version: z.number(),
createdAt: z.string(),
updatedAt: z.string(),
parentMemoryId: z.string().nullish(),
rootMemoryId: z.string().nullish(),
isLatest: z.boolean().optional(),
isForgotten: z.boolean().optional(),
})

const memoryEntryOutputSchema = z.object({
id: z.string(),
memory: z.string(),
version: z.number(),
isLatest: z.boolean(),
isForgotten: z.boolean(),
isStatic: z.boolean().optional(),
isInference: z.boolean().optional(),
createdAt: z.string(),
updatedAt: z.string(),
sourceCount: z.number().optional(),
documentIds: z.array(z.string()).optional(),
history: z.array(memoryHistorySchema).optional(),
})

export const addMemoryOutputSchema = z.object({
action: z.enum(["save", "forget"]),
success: z.boolean(),
Expand Down Expand Up @@ -104,10 +78,9 @@ export const listDocumentsOutputSchema = z.object({

export type ListDocumentsOutput = z.infer<typeof listDocumentsOutputSchema>

export const listMemoriesOutputSchema = z.object({
memoryEntries: z.array(memoryEntryOutputSchema),
pagination: paginationSchema,
})
// Reuse the shared schema so the tool's output contract stays identical to what
// the client parses — the two can't drift.
export const listMemoriesOutputSchema = memoriesListSchema

export type ListMemoriesOutput = z.infer<typeof listMemoriesOutputSchema>

Expand Down
43 changes: 43 additions & 0 deletions apps/mcp/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,49 @@ export const documentsApiResponseSchema = z.object({

export type DocumentsApiResponse = z.infer<typeof documentsApiResponseSchema>

// Extracted memory entries from /v4/memories/list. Single source of truth for
// both the client parser and the listMemories tool output schema, so the two
// can't drift (a mismatch previously produced Ajv "must NOT have additional
// properties"). z.object strips unknown API fields on parse, keeping parsed data
// matched to the strict MCP output contract while tolerating new API fields.
export const memoryEntryHistorySchema = z.object({
id: z.string(),
memory: z.string(),
version: z.number(),
createdAt: z.string(),
updatedAt: z.string(),
parentMemoryId: z.string().nullish(),
rootMemoryId: z.string().nullish(),
isLatest: z.boolean().optional(),
isForgotten: z.boolean().optional(),
})

export type MemoryEntryHistory = z.infer<typeof memoryEntryHistorySchema>

export const memoryEntrySchema = z.object({
id: z.string(),
memory: z.string(),
version: z.number(),
isLatest: z.boolean(),
isForgotten: z.boolean(),
isStatic: z.boolean().optional(),
isInference: z.boolean().optional(),
createdAt: z.string(),
updatedAt: z.string(),
sourceCount: z.number().optional(),
documentIds: z.array(z.string()).optional(),
history: z.array(memoryEntryHistorySchema).optional(),
})

export type MemoryEntry = z.infer<typeof memoryEntrySchema>

export const memoriesListSchema = z.object({
memoryEntries: z.array(memoryEntrySchema),
pagination: paginationSchema,
})

export type MemoriesList = z.infer<typeof memoriesListSchema>

// ViewMessage — discriminated union returned by app tools as `structuredContent`.
// The widget uses an exhaustive switch on `view` to dispatch to the correct view component.
// Adding a new view here is a compile error in App.tsx until the case is handled.
Expand Down
Loading