supermemory/apps/mcp/e2e/discovery.test.ts
Prasanna721 a99cf4f7e1 fix mcp graph and file uploads (#1397)
Fixes cross-host graph rendering and moves widget uploads off the JSON/base64 tool transport.

- render graph data from the launcher result without a second tool call
- stream multipart uploads through one-time, short-lived upload sessions
- remove temporary widget diagnostics and redundant unit tests

Tested with Biome, TypeScript, 17 unit tests, a Wrangler deployment dry-run, and live graph rendering in ChatGPT and Claude. Authenticated E2E setup is currently blocked by the saved OAuth refresh session returning `invalid_grant: session not found`.
2026-08-04 18:58:14 +00:00

108 lines
2.6 KiB
TypeScript

import { afterAll, beforeAll, describe, expect, it } from "vitest"
import {
OAUTH_CREDENTIALS_AVAILABLE,
callTool,
connect,
textOf,
type Session,
} from "./helpers"
const EXPECTED_TOOLS = [
"add_memory",
"fetch-graph-data",
"getDocument",
"guided-save",
"listDocuments",
"listMemories",
"listSpaces",
"memory-graph",
"prepare-file-upload",
"save-memory",
"search_memory",
"select-space",
"set-active-tag",
"upload-file",
"whoAmI",
]
const describeWithAuth = describe.skipIf(!OAUTH_CREDENTIALS_AVAILABLE)
const READ_ONLY_TOOL_NAMES = [
"search_memory",
"listDocuments",
"listMemories",
"getDocument",
"listSpaces",
"whoAmI",
"memory-graph",
]
const READ_ONLY_ANNOTATIONS = {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
}
const MEMORY_TOOL_ANNOTATIONS = {
readOnlyHint: false,
destructiveHint: true,
idempotentHint: false,
openWorldHint: false,
}
describeWithAuth("MCP — discovery & identity", () => {
let s: Session
beforeAll(async () => {
s = await connect()
})
afterAll(async () => {
await s?.close()
})
it("handshakes and lists the expected tools", async () => {
const { tools } = await s.client.listTools()
const names = tools.map((t) => t.name).sort()
expect(names).toEqual([...EXPECTED_TOOLS].sort())
})
it("marks read-only tools as non-destructive", async () => {
const { tools } = await s.client.listTools()
for (const name of READ_ONLY_TOOL_NAMES) {
const tool = tools.find((t) => t.name === name)
expect(tool?.annotations).toMatchObject(READ_ONLY_ANNOTATIONS)
}
})
it("marks add_memory as mutating", async () => {
const { tools } = await s.client.listTools()
const memory = tools.find((t) => t.name === "add_memory")
expect(memory?.annotations).toMatchObject(MEMORY_TOOL_ANNOTATIONS)
})
it("lists profile and space resources", async () => {
const { resources } = await s.client.listResources()
const uris = resources.map((r) => r.uri)
expect(uris).toContain("supermemory://profile")
expect(uris).toContain("supermemory://spaces")
})
it("lists the context prompt", async () => {
const { prompts } = await s.client.listPrompts()
expect(prompts.map((p) => p.name)).toContain("context")
})
it("whoAmI resolves to the authenticated account", async () => {
const res = await callTool(s.client, "whoAmI")
expect(res.isError).toBeFalsy()
const parsed = JSON.parse(textOf(res))
expect(parsed.userId).toBeTruthy()
expect(parsed).toHaveProperty("activeSpace")
})
it("listSpaces returns content", async () => {
const res = await callTool(s.client, "listSpaces")
expect(res.isError).toBeFalsy()
expect(textOf(res).length).toBeGreaterThan(0)
})
})