mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-03 02:21:23 +00:00
Merge eb841e9807 into cfa6c7cb17
This commit is contained in:
commit
7f4fcf4e93
8 changed files with 176 additions and 2 deletions
|
|
@ -52,6 +52,7 @@ The client discovers the OAuth authorization server through
|
||||||
| `get_profile` | Get stable and recent profile context for a space |
|
| `get_profile` | Get stable and recent profile context for a space |
|
||||||
| `list_documents` | List document metadata and summaries in a space |
|
| `list_documents` | List document metadata and summaries in a space |
|
||||||
| `get_document` | Read one document's available content by ID |
|
| `get_document` | Read one document's available content by ID |
|
||||||
|
| `delete_document` | Permanently delete a stored document by ID |
|
||||||
| `list_memories` | List extracted memory entries and their source document IDs |
|
| `list_memories` | List extracted memory entries and their source document IDs |
|
||||||
| `list_spaces` | List spaces visible to the authenticated account |
|
| `list_spaces` | List spaces visible to the authenticated account |
|
||||||
| `who_am_i` | Return identity, access, and active-space context |
|
| `who_am_i` | Return identity, access, and active-space context |
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
|
|
||||||
const EXPECTED_TOOLS = [
|
const EXPECTED_TOOLS = [
|
||||||
"add_memory",
|
"add_memory",
|
||||||
|
"delete_document",
|
||||||
"fetch-graph-data",
|
"fetch-graph-data",
|
||||||
"get_document",
|
"get_document",
|
||||||
"get_profile",
|
"get_profile",
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ const TOOL_SURFACES: Record<string, McpToolSurface> = {
|
||||||
get_profile: "model_tool",
|
get_profile: "model_tool",
|
||||||
list_documents: "model_tool",
|
list_documents: "model_tool",
|
||||||
get_document: "model_tool",
|
get_document: "model_tool",
|
||||||
|
delete_document: "model_tool",
|
||||||
list_memories: "model_tool",
|
list_memories: "model_tool",
|
||||||
list_spaces: "model_tool",
|
list_spaces: "model_tool",
|
||||||
who_am_i: "model_tool",
|
who_am_i: "model_tool",
|
||||||
|
|
|
||||||
|
|
@ -144,11 +144,12 @@ export class SupermemoryClient {
|
||||||
containerTag?: string,
|
containerTag?: string,
|
||||||
apiUrl = "https://api.supermemory.ai",
|
apiUrl = "https://api.supermemory.ai",
|
||||||
) {
|
) {
|
||||||
|
const normalizedApiUrl = apiUrl.trim().replace(/\/+$/, "")
|
||||||
this.bearerToken = bearerToken
|
this.bearerToken = bearerToken
|
||||||
this.apiUrl = apiUrl
|
this.apiUrl = normalizedApiUrl
|
||||||
this.client = new Supermemory({
|
this.client = new Supermemory({
|
||||||
apiKey: bearerToken,
|
apiKey: bearerToken,
|
||||||
baseURL: apiUrl,
|
baseURL: normalizedApiUrl,
|
||||||
timeout: FETCH_TIMEOUT_MS,
|
timeout: FETCH_TIMEOUT_MS,
|
||||||
defaultHeaders: { "x-sm-source": MCP_SOURCE },
|
defaultHeaders: { "x-sm-source": MCP_SOURCE },
|
||||||
})
|
})
|
||||||
|
|
@ -236,6 +237,21 @@ export class SupermemoryClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteDocument(
|
||||||
|
documentId: string,
|
||||||
|
): Promise<{ success: boolean; message: string; documentId: string }> {
|
||||||
|
try {
|
||||||
|
await this.client.documents.delete(documentId)
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Document ${documentId} deleted successfully.`,
|
||||||
|
documentId,
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.handleOperationError("Delete document request", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async search(
|
async search(
|
||||||
query: string,
|
query: string,
|
||||||
limit = 10,
|
limit = 10,
|
||||||
|
|
|
||||||
96
apps/mcp/src/server/tools/delete-document.test.ts
Normal file
96
apps/mcp/src/server/tools/delete-document.test.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { describe, expect, it, vi } from "vitest"
|
||||||
|
import { register } from "./delete-document"
|
||||||
|
import { errorResult, type ToolDeps } from "./types"
|
||||||
|
|
||||||
|
function setupHarness() {
|
||||||
|
let handler: ((args: { documentId: string }) => Promise<unknown>) | undefined
|
||||||
|
let toolConfig: Record<string, unknown> | undefined
|
||||||
|
|
||||||
|
const registerTool = vi.fn(
|
||||||
|
(_name: string, config: Record<string, unknown>, fn: typeof handler) => {
|
||||||
|
toolConfig = config
|
||||||
|
handler = fn
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const deleteDocument = vi.fn()
|
||||||
|
const mockClient = {
|
||||||
|
deleteDocument,
|
||||||
|
}
|
||||||
|
|
||||||
|
const deps: Partial<ToolDeps> = {
|
||||||
|
server: { registerTool } as unknown as ToolDeps["server"],
|
||||||
|
getClient: vi.fn().mockReturnValue(mockClient),
|
||||||
|
errorResult,
|
||||||
|
}
|
||||||
|
|
||||||
|
register(deps as ToolDeps)
|
||||||
|
|
||||||
|
if (!handler || !toolConfig) {
|
||||||
|
throw new Error("delete_document tool failed to register")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
handler,
|
||||||
|
toolConfig,
|
||||||
|
deleteDocument,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("delete_document tool", () => {
|
||||||
|
it("registers with destructive memory annotations", () => {
|
||||||
|
const harness = setupHarness()
|
||||||
|
expect(harness.toolConfig.annotations).toEqual({
|
||||||
|
readOnlyHint: false,
|
||||||
|
destructiveHint: true,
|
||||||
|
idempotentHint: false,
|
||||||
|
openWorldHint: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls client.deleteDocument and returns structured result", async () => {
|
||||||
|
const harness = setupHarness()
|
||||||
|
harness.deleteDocument.mockResolvedValueOnce({
|
||||||
|
success: true,
|
||||||
|
message: "Document doc-123 deleted successfully.",
|
||||||
|
documentId: "doc-123",
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = (await harness.handler({ documentId: "doc-123" })) as {
|
||||||
|
content: Array<{ type: string; text: string }>
|
||||||
|
structuredContent: {
|
||||||
|
success: boolean
|
||||||
|
documentId: string
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(harness.deleteDocument).toHaveBeenCalledWith("doc-123")
|
||||||
|
expect(result.structuredContent).toEqual({
|
||||||
|
success: true,
|
||||||
|
documentId: "doc-123",
|
||||||
|
message: "Document doc-123 deleted successfully.",
|
||||||
|
})
|
||||||
|
expect(result.content[0]?.text).toContain(
|
||||||
|
"Document doc-123 deleted successfully.",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("handles client errors gracefully", async () => {
|
||||||
|
const harness = setupHarness()
|
||||||
|
harness.deleteDocument.mockRejectedValueOnce(
|
||||||
|
new Error("Document not found"),
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = (await harness.handler({
|
||||||
|
documentId: "doc-missing",
|
||||||
|
})) as {
|
||||||
|
content: Array<{ type: string; text: string }>
|
||||||
|
isError: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result.isError).toBe(true)
|
||||||
|
expect(result.content[0]?.text).toContain("Document not found")
|
||||||
|
})
|
||||||
|
})
|
||||||
49
apps/mcp/src/server/tools/delete-document.ts
Normal file
49
apps/mcp/src/server/tools/delete-document.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { z } from "zod"
|
||||||
|
import { MEMORY_TOOL_ANNOTATIONS } from "./annotations"
|
||||||
|
import {
|
||||||
|
deleteDocumentOutputSchema,
|
||||||
|
type DeleteDocumentOutput,
|
||||||
|
} from "./output-schemas"
|
||||||
|
import { textContent, type ToolDeps } from "./types"
|
||||||
|
|
||||||
|
export function register(deps: ToolDeps) {
|
||||||
|
const inputSchema = z.object({
|
||||||
|
documentId: z
|
||||||
|
.string()
|
||||||
|
.min(1, "Document ID is required")
|
||||||
|
.max(255, "Document ID exceeds maximum length")
|
||||||
|
.describe(
|
||||||
|
"Document ID to delete, as returned by list_documents or a memory result",
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
deps.server.registerTool(
|
||||||
|
"delete_document",
|
||||||
|
{
|
||||||
|
title: "Delete Document",
|
||||||
|
description:
|
||||||
|
"Permanently delete a stored document and its associated memories by document ID. Use list_documents to find the document ID.",
|
||||||
|
inputSchema,
|
||||||
|
outputSchema: deleteDocumentOutputSchema,
|
||||||
|
annotations: MEMORY_TOOL_ANNOTATIONS,
|
||||||
|
},
|
||||||
|
async (args) => {
|
||||||
|
try {
|
||||||
|
const client = deps.getClient()
|
||||||
|
const result = await client.deleteDocument(args.documentId)
|
||||||
|
const structuredContent: DeleteDocumentOutput = {
|
||||||
|
success: true,
|
||||||
|
documentId: args.documentId,
|
||||||
|
message: result.message,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [textContent(result.message)],
|
||||||
|
structuredContent,
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return deps.errorResult(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import * as addMemory from "./add-memory"
|
import * as addMemory from "./add-memory"
|
||||||
|
import * as deleteDocument from "./delete-document"
|
||||||
import * as fetchGraphData from "./fetch-graph-data"
|
import * as fetchGraphData from "./fetch-graph-data"
|
||||||
import * as getDocument from "./get-document"
|
import * as getDocument from "./get-document"
|
||||||
import * as getProfile from "./get-profile"
|
import * as getProfile from "./get-profile"
|
||||||
|
|
@ -21,6 +22,7 @@ export function registerAllTools(deps: ToolDeps) {
|
||||||
getProfile.register(deps)
|
getProfile.register(deps)
|
||||||
listDocuments.register(deps)
|
listDocuments.register(deps)
|
||||||
getDocument.register(deps)
|
getDocument.register(deps)
|
||||||
|
deleteDocument.register(deps)
|
||||||
listMemories.register(deps)
|
listMemories.register(deps)
|
||||||
listContainerTags.register(deps)
|
listContainerTags.register(deps)
|
||||||
whoAmI.register(deps)
|
whoAmI.register(deps)
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,14 @@ export const getDocumentOutputSchema = z.object({
|
||||||
|
|
||||||
export type GetDocumentOutput = z.infer<typeof getDocumentOutputSchema>
|
export type GetDocumentOutput = z.infer<typeof getDocumentOutputSchema>
|
||||||
|
|
||||||
|
export const deleteDocumentOutputSchema = z.object({
|
||||||
|
success: z.boolean(),
|
||||||
|
documentId: z.string(),
|
||||||
|
message: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type DeleteDocumentOutput = z.infer<typeof deleteDocumentOutputSchema>
|
||||||
|
|
||||||
export const listDocumentsOutputSchema = z.object({
|
export const listDocumentsOutputSchema = z.object({
|
||||||
documents: z.array(documentSummarySchema),
|
documents: z.array(documentSummarySchema),
|
||||||
pagination: paginationSchema,
|
pagination: paginationSchema,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue