From 8a9fcd52dcfdca6325f66942e0e99c16e02189b7 Mon Sep 17 00:00:00 2001 From: Prasanna A P <106952318+Prasanna721@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:53:03 -0700 Subject: [PATCH] use space across mcp surfaces --- apps/mcp/README.md | 32 ++++----- apps/mcp/e2e/discovery.test.ts | 19 +++++- apps/mcp/e2e/graph.test.ts | 16 ++--- apps/mcp/e2e/widgets.test.ts | 10 +-- apps/mcp/e2e/workspace-scope.test.ts | 2 +- apps/mcp/src/server/analytics.test.ts | 12 ++-- apps/mcp/src/server/analytics.ts | 10 +-- apps/mcp/src/server/client/index.test.ts | 2 +- apps/mcp/src/server/container-tag.ts | 4 +- apps/mcp/src/server/prompts/context.ts | 10 +-- .../src/server/resources/container-tags.ts | 67 +++++++++---------- apps/mcp/src/server/resources/profile.ts | 10 +-- apps/mcp/src/server/tools/add-memory.ts | 4 +- apps/mcp/src/server/tools/get-document.ts | 2 +- apps/mcp/src/server/tools/index.ts | 4 +- .../src/server/tools/list-container-tags.ts | 2 +- apps/mcp/src/server/tools/list-documents.ts | 2 +- apps/mcp/src/server/tools/list-memories.ts | 2 +- apps/mcp/src/server/tools/memory-graph.ts | 4 +- apps/mcp/src/server/tools/search-memory.ts | 2 +- .../{select-workspace.ts => select-space.ts} | 8 +-- apps/mcp/src/server/tools/set-active-tag.ts | 4 +- apps/mcp/src/server/tools/who-am-i.ts | 6 +- .../src/server/workspace-presentation.test.ts | 18 ++--- apps/mcp/src/server/workspace.test.ts | 6 +- .../src/widget/design/ui/WorkspaceSelect.tsx | 8 +-- apps/mcp/src/widget/lib/formatTag.ts | 2 +- .../mcp/src/widget/lib/viewCheckpoint.test.ts | 2 +- apps/mcp/src/widget/lib/viewCheckpoint.ts | 6 +- apps/mcp/src/widget/studio/Studio.tsx | 4 +- apps/mcp/src/widget/views/Confirmation.tsx | 4 +- apps/mcp/src/widget/views/Picker.tsx | 28 ++++---- apps/mcp/src/widget/views/Save.tsx | 10 +-- apps/mcp/src/widget/views/Upload.tsx | 10 +-- 34 files changed, 170 insertions(+), 162 deletions(-) rename apps/mcp/src/server/tools/{select-workspace.ts => select-space.ts} (81%) diff --git a/apps/mcp/README.md b/apps/mcp/README.md index dcf3155b..a252d46e 100644 --- a/apps/mcp/README.md +++ b/apps/mcp/README.md @@ -1,7 +1,7 @@ # Supermemory MCP Server The Supermemory MCP server gives authenticated AI clients access to a user's -memories, profile, workspaces, and interactive MCP Apps. +memories, profile, spaces, and interactive MCP Apps. ## Runtime Model @@ -9,17 +9,17 @@ memories, profile, workspaces, and interactive MCP Apps. - Modern MCP `2026-07-28` plus stateless compatibility for 2025 clients - OAuth token validation on every request - No MCP protocol session or protocol Durable Object -- Active workspace stored as application state in a dedicated Durable Object -- Workspace state keyed by authenticated `organizationId + userId` +- Active space stored as application state in a dedicated Durable Object +- Space state keyed by authenticated `organizationId + userId` -The workspace used by an operation resolves in this order: +The space used by an operation resolves in this order: 1. An explicit `containerTag` tool or prompt argument -2. The account's durable active workspace +2. The account's durable active space 3. The Supermemory client default, `sm_project_default` An explicit override applies only to that call. It does not mutate the active -workspace. +space. ## Server URL @@ -49,18 +49,18 @@ The client discovers the OAuth authorization server through | Tool | Purpose | | --- | --- | | `search_memory` | Search memories and optionally include profile context | -| `listDocuments` | List document metadata and summaries in a workspace | +| `listDocuments` | List document metadata and summaries in a space | | `getDocument` | Read one document's available content by ID | | `listMemories` | List extracted memory entries and their source document IDs | -| `listSpaces` | List workspaces visible to the authenticated account | -| `whoAmI` | Return identity, access, and active-workspace context | +| `listSpaces` | List spaces visible to the authenticated account | +| `whoAmI` | Return identity, access, and active-space context | | `add_memory` | Save or forget a memory | ### MCP App launchers | Tool | Purpose | | --- | --- | -| `select-workspace` | Open the interactive workspace picker | +| `select-space` | Open the interactive space picker | | `memory-graph` | Open the interactive memory graph | | `guided-save` | Open the guided memory form | | `upload-file` | Open the file upload form | @@ -71,7 +71,7 @@ These tools are available to the embedded MCP App and hidden from the model. | Tool | Purpose | | --- | --- | -| `set-active-tag` | Persist the selected active workspace | +| `set-active-tag` | Persist the selected active space | | `save-memory` | Submit the guided save form | | `upload-file-submit` | Submit an encoded file upload | | `fetch-graph-data` | Fetch graph documents for the app | @@ -80,10 +80,10 @@ These tools are available to the embedded MCP App and hidden from the model. | Kind | Name or URI | Purpose | | --- | --- | --- | -| Resource | `supermemory://profile` | Profile facts in the effective workspace | -| Resource | `supermemory://container-tags` | Visible workspaces | +| Resource | `supermemory://profile` | Profile facts in the effective space | +| Resource | `supermemory://spaces` | Visible spaces | | Resource | `ui://supermemory/app-v3.html` | Embedded MCP App bundle | -| Prompt | `context` | Profile and recent context for an optional workspace | +| Prompt | `context` | Profile and recent context for an optional space | The App resource and tool metadata include both current nested `ui` metadata and the legacy flat resource URI key while MCP Apps completes its SDK v2 migration. @@ -131,10 +131,12 @@ discovery and rejection tests still run. | `API_URL` | Supermemory API and OAuth issuer | `https://api.supermemory.ai` | | `MCP_RESOURCE` | Expected OAuth audience | `https://mcp.supermemory.ai/mcp` | | `ALLOWED_MCP_ORIGIN_HOSTNAMES` | Additional comma-separated browser origins | Built-in host allowlist | +| `POSTHOG_API_KEY` | Server-side MCP tool analytics project key | Disabled | +| `POSTHOG_HOST` | PostHog ingestion host | `https://us.i.posthog.com` | ## Storage And Rollout -`WorkspaceState` stores only the active container tag. It never stores bearer +`WorkspaceState` stores only the active space's container tag. It never stores bearer tokens, MCP client identity, or protocol messages. The old `SupermemoryMCP` class and binding remain inert for one rollout. This diff --git a/apps/mcp/e2e/discovery.test.ts b/apps/mcp/e2e/discovery.test.ts index beed605c..9366a03d 100644 --- a/apps/mcp/e2e/discovery.test.ts +++ b/apps/mcp/e2e/discovery.test.ts @@ -18,7 +18,7 @@ const EXPECTED_TOOLS = [ "memory-graph", "save-memory", "search_memory", - "select-workspace", + "select-space", "set-active-tag", "upload-file", "upload-file-submit", @@ -80,11 +80,22 @@ describeWithAuth("MCP — discovery & identity", () => { expect(memory?.annotations).toMatchObject(MEMORY_TOOL_ANNOTATIONS) }) - it("lists profile and container-tag resources", async () => { + 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://container-tags") + expect(uris).toContain("supermemory://spaces") + }) + + it("uses space terminology across exposed MCP metadata", async () => { + const [{ tools }, { resources }, { prompts }] = await Promise.all([ + s.client.listTools(), + s.client.listResources(), + s.client.listPrompts(), + ]) + expect(JSON.stringify({ tools, resources, prompts })).not.toMatch( + /\bworkspaces?\b/i, + ) }) it("lists the context prompt", async () => { @@ -97,6 +108,8 @@ describeWithAuth("MCP — discovery & identity", () => { expect(res.isError).toBeFalsy() const parsed = JSON.parse(textOf(res)) expect(parsed.userId).toBeTruthy() + expect(parsed).toHaveProperty("activeSpace") + expect(parsed).not.toHaveProperty("activeWorkspace") }) it("listSpaces returns content", async () => { diff --git a/apps/mcp/e2e/graph.test.ts b/apps/mcp/e2e/graph.test.ts index 2ca16128..249886f3 100644 --- a/apps/mcp/e2e/graph.test.ts +++ b/apps/mcp/e2e/graph.test.ts @@ -49,25 +49,25 @@ describeWithAuth("MCP — graph, resources & prompts", () => { const res = await s.client.readResource({ uri: "supermemory://profile" }) expect(res.contents.length).toBeGreaterThan(0) expect(res.contents[0].mimeType).toBe("text/plain") - expect(res.contents[0].text).toMatch(/# Active Workspace Profile/) - expect(res.contents[0].text).toMatch(/Workspace:/) + expect(res.contents[0].text).toMatch(/# Active Space Profile/) + expect(res.contents[0].text).toMatch(/Space:/) expect(res.contents[0].text).toMatch( - /Use `listSpaces` to find the relevant workspace key/, + /Use `listSpaces` to find the relevant space key/, ) }) - it("reads all workspaces in a compact human-readable format", async () => { + it("reads all spaces in a compact human-readable format", async () => { const res = await s.client.readResource({ - uri: "supermemory://container-tags", + uri: "supermemory://spaces", }) const text = res.contents[0].text as string expect(res.contents[0].mimeType).toBe("text/plain") - expect(text).toMatch(/# My Workspaces/) + expect(text).toMatch(/# My Spaces/) expect(text).toMatch(/Active:/) expect(text).not.toMatch(/"containerTags":/) }) - it("gets compact active-workspace context without prompt arguments", async () => { + it("gets compact active-space context without prompt arguments", async () => { const prompts = await s.client.listPrompts() const contextPrompt = prompts.prompts.find( (prompt) => prompt.name === "context", @@ -78,6 +78,6 @@ describeWithAuth("MCP — graph, resources & prompts", () => { expect(res.messages.length).toBeGreaterThan(0) const text = res.messages[0].content.text as string expect(text).toMatch(/# Supermemory Context/) - expect(text).toMatch(/Active workspace:/) + expect(text).toMatch(/Active space:/) }) }) diff --git a/apps/mcp/e2e/widgets.test.ts b/apps/mcp/e2e/widgets.test.ts index 52951b29..4bfc73a9 100644 --- a/apps/mcp/e2e/widgets.test.ts +++ b/apps/mcp/e2e/widgets.test.ts @@ -20,8 +20,8 @@ describe.skipIf(!OAUTH_CREDENTIALS_AVAILABLE)( await session?.close() }) - it("loads visible workspaces and effective permissions on demand", async () => { - const result = await callTool(session.client, "select-workspace") + it("loads visible spaces and effective permissions on demand", async () => { + const result = await callTool(session.client, "select-space") expect(result.isError).toBeFalsy() const content = result.structuredContent as { view?: string @@ -43,8 +43,8 @@ describe.skipIf(!OAUTH_CREDENTIALS_AVAILABLE)( ).toBe(true) }) - it("shares the selected workspace across MCP transport sessions", async () => { - const picker = await callTool(session.client, "select-workspace") + it("shares the selected space across MCP transport sessions", async () => { + const picker = await callTool(session.client, "select-space") const pickerContent = picker.structuredContent as { containerTags?: Array<{ containerTag: string }> } @@ -65,7 +65,7 @@ describe.skipIf(!OAUTH_CREDENTIALS_AVAILABLE)( const identity = await callTool(separateSession.client, "whoAmI") expect(identity.isError).toBeFalsy() expect(JSON.parse(textOf(identity))).toMatchObject({ - activeWorkspace: firstTag, + activeSpace: firstTag, }) } finally { await separateSession.close() diff --git a/apps/mcp/e2e/workspace-scope.test.ts b/apps/mcp/e2e/workspace-scope.test.ts index ccff5eb7..1dab7267 100644 --- a/apps/mcp/e2e/workspace-scope.test.ts +++ b/apps/mcp/e2e/workspace-scope.test.ts @@ -11,7 +11,7 @@ const propsOf = (tools: ToolLike[], name: string): Record => const describeWithAuth = describe.skipIf(!OAUTH_CREDENTIALS_AVAILABLE) -describeWithAuth("MCP - workspace scoping", () => { +describeWithAuth("MCP - space scoping", () => { it("keeps per-call containerTag overrides when an obsolete header is sent", async () => { const scoped = await connect({ headers: { "x-sm-project": "obsolete-root-scope" }, diff --git a/apps/mcp/src/server/analytics.test.ts b/apps/mcp/src/server/analytics.test.ts index 3e449455..ac016419 100644 --- a/apps/mcp/src/server/analytics.test.ts +++ b/apps/mcp/src/server/analytics.test.ts @@ -54,7 +54,7 @@ describe("MCP tool analytics", () => { ) await harness.invoke( - { query: "private query", containerTag: "private-workspace" }, + { query: "private query", containerTag: "private-space" }, context, ) @@ -64,14 +64,12 @@ describe("MCP tool analytics", () => { toolName: "search_memory", surface: "model_tool", outcome: "success", - workspaceExplicit: true, + spaceExplicit: true, client: { name: "claude", version: "1.2.3" }, }), ) expect(JSON.stringify(record.mock.calls[0])).not.toContain("private query") - expect(JSON.stringify(record.mock.calls[0])).not.toContain( - "private-workspace", - ) + expect(JSON.stringify(record.mock.calls[0])).not.toContain("private-space") expect(JSON.stringify(record.mock.calls[0])).not.toContain("secret result") }) @@ -148,7 +146,7 @@ describe("MCP tool analytics", () => { surface: "app_launcher", outcome: "success", durationMs: 42, - workspaceExplicit: false, + spaceExplicit: false, }, ) @@ -163,7 +161,7 @@ describe("MCP tool analytics", () => { duration_ms: 42, mcp_runtime: "stateless", mcp_surface: "app_launcher", - workspace_explicit: false, + space_explicit: false, oauth_client_id: "client_123", }, }) diff --git a/apps/mcp/src/server/analytics.ts b/apps/mcp/src/server/analytics.ts index 5f1b87e2..6e7fd548 100644 --- a/apps/mcp/src/server/analytics.ts +++ b/apps/mcp/src/server/analytics.ts @@ -17,7 +17,7 @@ export interface McpToolExecution { surface: McpToolSurface outcome: McpToolOutcome durationMs: number - workspaceExplicit: boolean + spaceExplicit: boolean client?: { name: string; version?: string } errorType?: string } @@ -40,7 +40,7 @@ const TOOL_SURFACES: Record = { listSpaces: "model_tool", whoAmI: "model_tool", add_memory: "model_tool", - "select-workspace": "app_launcher", + "select-space": "app_launcher", "memory-graph": "app_launcher", "guided-save": "app_launcher", "upload-file": "app_launcher", @@ -87,7 +87,7 @@ export function posthogEventForToolExecution( duration_ms: execution.durationMs, mcp_runtime: "stateless", mcp_surface: execution.surface, - workspace_explicit: execution.workspaceExplicit, + space_explicit: execution.spaceExplicit, ...(execution.client ? { mcp_client_name: execution.client.name, @@ -126,7 +126,7 @@ export function createPosthogAnalytics( } } -function workspaceWasExplicit(value: unknown): boolean { +function spaceWasExplicit(value: unknown): boolean { if (!value || typeof value !== "object") return false const containerTag = Reflect.get(value, "containerTag") return typeof containerTag === "string" && containerTag.trim().length > 0 @@ -185,7 +185,7 @@ export function createTrackedToolServer( surface: TOOL_SURFACES[name] ?? "model_tool", outcome, durationMs: Math.max(0, Math.round(performance.now() - startedAt)), - workspaceExplicit: workspaceWasExplicit(input), + spaceExplicit: spaceWasExplicit(input), ...(client ? { client } : {}), ...(errorType ? { errorType } : {}), }) diff --git a/apps/mcp/src/server/client/index.test.ts b/apps/mcp/src/server/client/index.test.ts index 2a84f605..19c2de2d 100644 --- a/apps/mcp/src/server/client/index.test.ts +++ b/apps/mcp/src/server/client/index.test.ts @@ -6,7 +6,7 @@ describe("SupermemoryClient memory listing", () => { vi.unstubAllGlobals() }) - it("calls the canonical memory-list endpoint with the selected workspace", async () => { + it("calls the canonical memory-list endpoint with the selected space", async () => { const responseBody = { memoryEntries: [ { diff --git a/apps/mcp/src/server/container-tag.ts b/apps/mcp/src/server/container-tag.ts index bad06e7d..decb4586 100644 --- a/apps/mcp/src/server/container-tag.ts +++ b/apps/mcp/src/server/container-tag.ts @@ -4,10 +4,10 @@ export const containerTagSchema = z .string() .min(1, "Container tag is required") .max(128, "Container tag exceeds maximum length") - .describe("Workspace key returned by listSpaces") + .describe("Space key returned by listSpaces") export const optionalContainerTagSchema = containerTagSchema .optional() .describe( - "Workspace key to use for this call. If the user names a workspace, call listSpaces to resolve its key and pass it here. Omit only when the user means the active workspace.", + "Space key to use for this call. If the user names a space, call listSpaces to resolve its key and pass it here. Omit only when the user means the active space.", ) diff --git a/apps/mcp/src/server/prompts/context.ts b/apps/mcp/src/server/prompts/context.ts index 927cb88f..ba3f0a3b 100644 --- a/apps/mcp/src/server/prompts/context.ts +++ b/apps/mcp/src/server/prompts/context.ts @@ -20,7 +20,7 @@ export function registerContextPrompt( server.registerPrompt( "context", { - description: "Attach compact context for the active workspace", + description: "Attach compact context for the active space", }, async () => { try { @@ -37,7 +37,7 @@ export function registerContextPrompt( const fallback = selectedTag ? "" : " (default)" const parts: string[] = [ "# Supermemory Context", - `Active workspace: ${activeLabel} [${activeKey}]${fallback}`, + `Active space: ${activeLabel} [${activeKey}]${fallback}`, ] if (activeWorkspace) { @@ -65,7 +65,7 @@ export function registerContextPrompt( profileResult.profile.static.length === 0 && profileResult.profile.dynamic.length === 0 ) { - parts.push("No profile facts are available for this workspace yet.") + parts.push("No profile facts are available for this space yet.") } const recentWorkspaces = sortWorkspaces(workspaces, activeKey) @@ -74,7 +74,7 @@ export function registerContextPrompt( if (recentWorkspaces.length > 0) { parts.push( "", - "## Recently Active Workspaces", + "## Recently Active Spaces", ...recentWorkspaces.map((workspace) => formatWorkspaceRow(workspace, activeKey, 100), ), @@ -83,7 +83,7 @@ export function registerContextPrompt( parts.push( "", - "Use a workspace key with workspace-aware tools when the user asks about another workspace. Keep workspace contexts separate unless the user asks to combine them.", + "Use a space key with space-aware tools when the user asks about another space. Keep space contexts separate unless the user asks to combine them.", ) return { diff --git a/apps/mcp/src/server/resources/container-tags.ts b/apps/mcp/src/server/resources/container-tags.ts index f31a7902..615bb608 100644 --- a/apps/mcp/src/server/resources/container-tags.ts +++ b/apps/mcp/src/server/resources/container-tags.ts @@ -11,41 +11,36 @@ export function registerContainerTagsResource( getClient: () => SupermemoryClient, resolveContainerTag: () => Promise, ) { - server.registerResource( - "My Workspaces", - "supermemory://container-tags", - {}, - async () => { - const client = getClient() - const [containerTags, selectedTag] = await Promise.all([ - client.listContainerTags(), - resolveContainerTag(), - ]) - const activeKey = selectedTag ?? DEFAULT_PROJECT_ID - const activeWorkspace = containerTags.find( - (workspace) => workspace.containerTag === activeKey, - ) - const rows = sortWorkspaces(containerTags, activeKey).map((workspace) => - formatWorkspaceRow(workspace, activeKey), - ) - const activeLabel = workspaceDisplayName(activeWorkspace, activeKey) - const fallback = selectedTag ? "" : " (default)" - const text = [ - "# My Workspaces", - `${containerTags.length} available · Active: ${activeLabel} [${activeKey}]${fallback}`, - "", - ...rows, - ].join("\n") + server.registerResource("My Spaces", "supermemory://spaces", {}, async () => { + const client = getClient() + const [containerTags, selectedTag] = await Promise.all([ + client.listContainerTags(), + resolveContainerTag(), + ]) + const activeKey = selectedTag ?? DEFAULT_PROJECT_ID + const activeWorkspace = containerTags.find( + (workspace) => workspace.containerTag === activeKey, + ) + const rows = sortWorkspaces(containerTags, activeKey).map((workspace) => + formatWorkspaceRow(workspace, activeKey), + ) + const activeLabel = workspaceDisplayName(activeWorkspace, activeKey) + const fallback = selectedTag ? "" : " (default)" + const text = [ + "# My Spaces", + `${containerTags.length} available · Active: ${activeLabel} [${activeKey}]${fallback}`, + "", + ...rows, + ].join("\n") - return { - contents: [ - { - uri: "supermemory://container-tags", - mimeType: "text/plain", - text, - }, - ], - } - }, - ) + return { + contents: [ + { + uri: "supermemory://spaces", + mimeType: "text/plain", + text, + }, + ], + } + }) } diff --git a/apps/mcp/src/server/resources/profile.ts b/apps/mcp/src/server/resources/profile.ts index 00149eff..995f95dd 100644 --- a/apps/mcp/src/server/resources/profile.ts +++ b/apps/mcp/src/server/resources/profile.ts @@ -15,7 +15,7 @@ export function registerProfileResource( resolveContainerTag: () => Promise, ) { server.registerResource( - "Active Workspace Profile", + "Active Space Profile", "supermemory://profile", {}, async () => { @@ -31,8 +31,8 @@ export function registerProfileResource( const activeLabel = workspaceDisplayName(activeWorkspace, activeKey) const fallback = selectedTag ? "" : " (default)" const parts: string[] = [ - "# Active Workspace Profile", - `Workspace: ${activeLabel} [${activeKey}]${fallback}`, + "# Active Space Profile", + `Space: ${activeLabel} [${activeKey}]${fallback}`, ] if (activeWorkspace) { @@ -60,12 +60,12 @@ export function registerProfileResource( profileResult.profile.static.length === 0 && profileResult.profile.dynamic.length === 0 ) { - parts.push("No profile facts are available for this workspace yet.") + parts.push("No profile facts are available for this space yet.") } parts.push( "", - "Other workspaces are available. Use `listSpaces` to find the relevant workspace key, then use that key with workspace-aware tools when the user asks about another workspace. Keep workspace contexts separate unless the user asks to combine them.", + "Other spaces are available. Use `listSpaces` to find the relevant space key, then use that key with space-aware tools when the user asks about another space. Keep space contexts separate unless the user asks to combine them.", ) return { diff --git a/apps/mcp/src/server/tools/add-memory.ts b/apps/mcp/src/server/tools/add-memory.ts index 91badcc5..295b3b88 100644 --- a/apps/mcp/src/server/tools/add-memory.ts +++ b/apps/mcp/src/server/tools/add-memory.ts @@ -17,7 +17,7 @@ export function register(deps: ToolDeps) { "add_memory", { description: - "Add (save) or forget a memory in the user's ACTIVE workspace. Defaults to 'save'. The target workspace is the one the user selected via select-workspace; pass containerTag only to override it. Use 'forget' when information is outdated or the user asks to remove it.", + "Add (save) or forget a memory in the user's ACTIVE space. Defaults to 'save'. The target space is the one the user selected via select-space; pass containerTag only to override it. Use 'forget' when information is outdated or the user asks to remove it.", inputSchema, annotations: MEMORY_TOOL_ANNOTATIONS, }, @@ -38,7 +38,7 @@ export function register(deps: ToolDeps) { content: [ { type: "text" as const, - text: `Memory saved (ID: ${result.id}, workspace: ${result.containerTag})`, + text: `Memory saved (ID: ${result.id}, space: ${result.containerTag})`, }, ], } diff --git a/apps/mcp/src/server/tools/get-document.ts b/apps/mcp/src/server/tools/get-document.ts index 59cfbbf0..c1d30a6e 100644 --- a/apps/mcp/src/server/tools/get-document.ts +++ b/apps/mcp/src/server/tools/get-document.ts @@ -17,7 +17,7 @@ export function register(deps: ToolDeps) { { title: "Get Document", description: - "Read one stored document by ID, including its summary and available content. Use listDocuments in the intended workspace to discover document IDs.", + "Read one stored document by ID, including its summary and available content. Use listDocuments in the intended space to discover document IDs.", inputSchema, annotations: READ_ONLY_TOOL_ANNOTATIONS, }, diff --git a/apps/mcp/src/server/tools/index.ts b/apps/mcp/src/server/tools/index.ts index ca9684df..869016c2 100644 --- a/apps/mcp/src/server/tools/index.ts +++ b/apps/mcp/src/server/tools/index.ts @@ -8,7 +8,7 @@ import * as listMemories from "./list-memories" import * as memoryGraph from "./memory-graph" import * as saveMemory from "./save-memory" import * as searchMemory from "./search-memory" -import * as selectWorkspace from "./select-workspace" +import * as selectSpace from "./select-space" import * as setActiveTag from "./set-active-tag" import type { ToolDeps } from "./types" import * as uploadFile from "./upload-file" @@ -22,7 +22,7 @@ export function registerAllTools(deps: ToolDeps) { listMemories.register(deps) listContainerTags.register(deps) whoAmI.register(deps) - selectWorkspace.register(deps) + selectSpace.register(deps) setActiveTag.register(deps) memoryGraph.register(deps) fetchGraphData.register(deps) diff --git a/apps/mcp/src/server/tools/list-container-tags.ts b/apps/mcp/src/server/tools/list-container-tags.ts index b3cf832d..d8d871c0 100644 --- a/apps/mcp/src/server/tools/list-container-tags.ts +++ b/apps/mcp/src/server/tools/list-container-tags.ts @@ -7,7 +7,7 @@ export function register(deps: ToolDeps) { "listSpaces", { description: - "List the workspaces available to the user. Returns each workspace's name, key, emoji, document/memory counts, and last activity. Use this first to resolve a named workspace before calling a workspace-aware tool, or when the user asks which workspace may contain something. The list is auto-filtered to workspaces the user can access.", + "List the spaces available to the user. Returns each space's name, key, emoji, document/memory counts, and last activity. Use this first to resolve a named space before calling a space-aware tool, or when the user asks which space may contain something. The list is auto-filtered to spaces the user can access.", inputSchema: z.object({}), annotations: READ_ONLY_TOOL_ANNOTATIONS, }, diff --git a/apps/mcp/src/server/tools/list-documents.ts b/apps/mcp/src/server/tools/list-documents.ts index 1a722766..6d436ca5 100644 --- a/apps/mcp/src/server/tools/list-documents.ts +++ b/apps/mcp/src/server/tools/list-documents.ts @@ -29,7 +29,7 @@ export function register(deps: ToolDeps) { { title: "List Documents", description: - "List documents in one workspace with their IDs, titles, types, processing status, dates, and summaries. This does not return full document content; use getDocument with an ID from this result to read one document. When the user names a workspace, resolve it with listSpaces and pass containerTag; otherwise use the active workspace.", + "List documents in one space with their IDs, titles, types, processing status, dates, and summaries. This does not return full document content; use getDocument with an ID from this result to read one document. When the user names a space, resolve it with listSpaces and pass containerTag; otherwise use the active space.", inputSchema, annotations: READ_ONLY_TOOL_ANNOTATIONS, }, diff --git a/apps/mcp/src/server/tools/list-memories.ts b/apps/mcp/src/server/tools/list-memories.ts index 6f70772d..a74ac31c 100644 --- a/apps/mcp/src/server/tools/list-memories.ts +++ b/apps/mcp/src/server/tools/list-memories.ts @@ -29,7 +29,7 @@ export function register(deps: ToolDeps) { { title: "List Memories", description: - "List the latest extracted memory entries in one workspace, including stable memory IDs, version information, and source document IDs. This lists memories directly, not documents. When the user names a workspace, resolve it with listSpaces and pass containerTag; otherwise use the active workspace. Use search_memory instead for semantic recall.", + "List the latest extracted memory entries in one space, including stable memory IDs, version information, and source document IDs. This lists memories directly, not documents. When the user names a space, resolve it with listSpaces and pass containerTag; otherwise use the active space. Use search_memory instead for semantic recall.", inputSchema, annotations: READ_ONLY_TOOL_ANNOTATIONS, }, diff --git a/apps/mcp/src/server/tools/memory-graph.ts b/apps/mcp/src/server/tools/memory-graph.ts index b826a803..77a2aa14 100644 --- a/apps/mcp/src/server/tools/memory-graph.ts +++ b/apps/mcp/src/server/tools/memory-graph.ts @@ -15,7 +15,7 @@ export function register(deps: ToolDeps) { { title: "Memory Graph", description: - "Render the workspace's memory graph directly as an interactive MCP App. This tool is the final visualization; do not create another graph, file, or artifact unless the user explicitly asks for one. When the user names a workspace, resolve it with listSpaces and pass containerTag.", + "Render the space's memory graph directly as an interactive MCP App. This tool is the final visualization; do not create another graph, file, or artifact unless the user explicitly asks for one. When the user names a space, resolve it with listSpaces and pass containerTag.", inputSchema, annotations: READ_ONLY_TOOL_ANNOTATIONS, _meta: appToolMeta(), @@ -44,7 +44,7 @@ export function register(deps: ToolDeps) { content: [ { type: "text" as const, - text: `Rendered the interactive Memory Graph MCP App: ${result.documents.length} documents, ${memoryCount} memories${effectiveTag ? `. Workspace: ${effectiveTag}` : ""}. Do not create a duplicate graph or artifact unless the user explicitly requests one.`, + text: `Rendered the interactive Memory Graph MCP App: ${result.documents.length} documents, ${memoryCount} memories${effectiveTag ? `. Space: ${effectiveTag}` : ""}. Do not create a duplicate graph or artifact unless the user explicitly requests one.`, }, ], structuredContent: sc, diff --git a/apps/mcp/src/server/tools/search-memory.ts b/apps/mcp/src/server/tools/search-memory.ts index ca0fc1cd..37c05719 100644 --- a/apps/mcp/src/server/tools/search-memory.ts +++ b/apps/mcp/src/server/tools/search-memory.ts @@ -18,7 +18,7 @@ export function register(deps: ToolDeps) { "search_memory", { description: - "Search memories in one workspace with a natural-language query. Returns relevant memories plus that workspace's profile summary. When the user names a workspace, resolve it with listSpaces and pass containerTag; otherwise use the active workspace.", + "Search memories in one space with a natural-language query. Returns relevant memories plus that space's profile summary. When the user names a space, resolve it with listSpaces and pass containerTag; otherwise use the active space.", inputSchema, annotations: READ_ONLY_TOOL_ANNOTATIONS, }, diff --git a/apps/mcp/src/server/tools/select-workspace.ts b/apps/mcp/src/server/tools/select-space.ts similarity index 81% rename from apps/mcp/src/server/tools/select-workspace.ts rename to apps/mcp/src/server/tools/select-space.ts index a0422f46..3f74cb62 100644 --- a/apps/mcp/src/server/tools/select-workspace.ts +++ b/apps/mcp/src/server/tools/select-space.ts @@ -6,11 +6,11 @@ import type { ToolDeps } from "./types" export function register(deps: ToolDeps) { deps.server.registerTool( - "select-workspace", + "select-space", { - title: "Select Workspace", + title: "Select Space", description: - "Choose which container tag to work in. Shows available container tags as interactive cards.", + "Choose the active Supermemory space. Shows available spaces as interactive cards.", inputSchema: z.object({}), _meta: appToolMeta(), }, @@ -40,7 +40,7 @@ export function register(deps: ToolDeps) { content: [ { type: "text" as const, - text: `${tags.length} container tags available. Select one to set your active context.`, + text: `${tags.length} spaces available. Select one to set your active context.`, }, ], structuredContent: sc, diff --git a/apps/mcp/src/server/tools/set-active-tag.ts b/apps/mcp/src/server/tools/set-active-tag.ts index b0669780..3d93d57e 100644 --- a/apps/mcp/src/server/tools/set-active-tag.ts +++ b/apps/mcp/src/server/tools/set-active-tag.ts @@ -8,7 +8,7 @@ export function register(deps: ToolDeps) { deps.server.registerTool( "set-active-tag", { - description: "Set the active container tag for this account", + description: "Set the active Supermemory space for this account", inputSchema: z.object({ containerTag: containerTagSchema, viewId: z.string().uuid().optional(), @@ -35,7 +35,7 @@ export function register(deps: ToolDeps) { content: [ { type: "text" as const, - text: `Active workspace set to ${containerTag}`, + text: `Active space set to ${containerTag}`, }, ], structuredContent: sc, diff --git a/apps/mcp/src/server/tools/who-am-i.ts b/apps/mcp/src/server/tools/who-am-i.ts index d4b05f2d..c168d57c 100644 --- a/apps/mcp/src/server/tools/who-am-i.ts +++ b/apps/mcp/src/server/tools/who-am-i.ts @@ -6,7 +6,7 @@ export function register(deps: ToolDeps) { deps.server.registerTool( "whoAmI", { - description: "Get current user info, role, and workspace context", + description: "Get current user info, role, and space context", inputSchema: z.object({}), annotations: READ_ONLY_TOOL_ANNOTATIONS, }, @@ -28,8 +28,8 @@ export function register(deps: ToolDeps) { name: session.user.name, role: session.role ?? "unknown", accessType: session.accessType ?? "full", - activeWorkspace: activeTag ?? null, - assignedTags: + activeSpace: activeTag ?? null, + assignedSpaces: session.accessType === "restricted" ? session.containerTags : null, diff --git a/apps/mcp/src/server/workspace-presentation.test.ts b/apps/mcp/src/server/workspace-presentation.test.ts index ce50019f..9d4958f9 100644 --- a/apps/mcp/src/server/workspace-presentation.test.ts +++ b/apps/mcp/src/server/workspace-presentation.test.ts @@ -7,14 +7,14 @@ import { sortWorkspaces, } from "./workspace-presentation" -const workspace = ( +const space = ( containerTag: string, lastActivityAt: string | null, ): ContainerTag => ({ id: containerTag, - name: `Workspace ${containerTag}`, + name: `Space ${containerTag}`, containerTag, - description: "A compact workspace description.", + description: "A compact space description.", visibility: "private", createdAt: "2026-01-01T00:00:00.000Z", updatedAt: "2026-01-01T00:00:00.000Z", @@ -25,13 +25,13 @@ const workspace = ( lastActivityAt, }) -describe("workspace presentation", () => { - it("keeps the active workspace first, then sorts by activity", () => { +describe("space presentation", () => { + it("keeps the active space first, then sorts by activity", () => { const sorted = sortWorkspaces( [ - workspace("older", "2026-01-01T00:00:00.000Z"), - workspace("active", "2025-01-01T00:00:00.000Z"), - workspace("newer", "2026-02-01T00:00:00.000Z"), + space("older", "2026-01-01T00:00:00.000Z"), + space("active", "2025-01-01T00:00:00.000Z"), + space("newer", "2026-02-01T00:00:00.000Z"), ], "active", ) @@ -45,7 +45,7 @@ describe("workspace presentation", () => { it("formats compact rows without internal database IDs", () => { const row = formatWorkspaceRow( - workspace("project-key", "2026-07-29T19:44:28.177Z"), + space("project-key", "2026-07-29T19:44:28.177Z"), "project-key", ) diff --git a/apps/mcp/src/server/workspace.test.ts b/apps/mcp/src/server/workspace.test.ts index a9a466d4..1464dd46 100644 --- a/apps/mcp/src/server/workspace.test.ts +++ b/apps/mcp/src/server/workspace.test.ts @@ -35,11 +35,11 @@ describe("workspace application state", () => { ).resolves.toBeUndefined() }) - it("tells the model how to route explicit workspace requests", () => { + it("tells the model how to route explicit space requests", () => { expect(optionalContainerTagSchema.description).toContain( - "If the user names a workspace", + "If the user names a space", ) expect(optionalContainerTagSchema.description).toContain("listSpaces") - expect(optionalContainerTagSchema.description).toContain("active workspace") + expect(optionalContainerTagSchema.description).toContain("active space") }) }) diff --git a/apps/mcp/src/widget/design/ui/WorkspaceSelect.tsx b/apps/mcp/src/widget/design/ui/WorkspaceSelect.tsx index 290e80f1..9576077b 100644 --- a/apps/mcp/src/widget/design/ui/WorkspaceSelect.tsx +++ b/apps/mcp/src/widget/design/ui/WorkspaceSelect.tsx @@ -24,14 +24,14 @@ interface WorkspaceSelectProps { } // Ports console-v2's ChipSelect/ContainerTagSelect: a Popover-anchored, -// searchable list. Scales to hundreds of workspaces where chips can't. +// searchable list. Scales to hundreds of spaces where chips can't. export function WorkspaceSelect({ value, onValueChange, options, - placeholder = "Select workspace", - emptyText = "No workspaces", - searchPlaceholder = "Search workspaces…", + placeholder = "Select space", + emptyText = "No spaces", + searchPlaceholder = "Search spaces…", disabled = false, className, }: WorkspaceSelectProps) { diff --git a/apps/mcp/src/widget/lib/formatTag.ts b/apps/mcp/src/widget/lib/formatTag.ts index 5d7f97f0..015b82fe 100644 --- a/apps/mcp/src/widget/lib/formatTag.ts +++ b/apps/mcp/src/widget/lib/formatTag.ts @@ -3,7 +3,7 @@ * * "sm_project_marketing" → "Marketing" * "sm_project_eng_rfcs" → "Eng Rfcs" - * "my_custom_workspace" → "My Custom Workspace" + * "my_custom_space" → "My Custom Space" */ export function formatTagLabel(raw: string): string { const slug = raw.replace(/^sm_project_/i, "").replace(/^sm_/i, "") diff --git a/apps/mcp/src/widget/lib/viewCheckpoint.test.ts b/apps/mcp/src/widget/lib/viewCheckpoint.test.ts index 3dc041d2..5109601f 100644 --- a/apps/mcp/src/widget/lib/viewCheckpoint.test.ts +++ b/apps/mcp/src/widget/lib/viewCheckpoint.test.ts @@ -76,7 +76,7 @@ describe("view checkpoints", () => { saveViewCheckpoint(view) expect(setWidgetState).toHaveBeenCalledWith({ - modelContent: 'Supermemory active workspace is now "model_test".', + modelContent: 'Supermemory active space is now "model_test".', privateContent: { existing: true, supermemoryView: view, diff --git a/apps/mcp/src/widget/lib/viewCheckpoint.ts b/apps/mcp/src/widget/lib/viewCheckpoint.ts index 3706e2f6..2d150620 100644 --- a/apps/mcp/src/widget/lib/viewCheckpoint.ts +++ b/apps/mcp/src/widget/lib/viewCheckpoint.ts @@ -48,11 +48,11 @@ function getStorage(): Storage | undefined { function modelContentForView(view: ViewMessage): string { switch (view.view) { case "confirmation": - return `Supermemory active workspace is now "${view.containerTag}".` + return `Supermemory active space is now "${view.containerTag}".` case "save-success": - return `A memory was saved to Supermemory workspace "${view.containerTag}" with memory ID "${view.id}".` + return `A memory was saved to Supermemory space "${view.containerTag}" with memory ID "${view.id}".` case "upload-success": - return `"${view.fileName}" was uploaded to Supermemory workspace "${view.containerTag}" with document ID "${view.id}".` + return `"${view.fileName}" was uploaded to Supermemory space "${view.containerTag}" with document ID "${view.id}".` default: return "Supermemory widget state updated." } diff --git a/apps/mcp/src/widget/studio/Studio.tsx b/apps/mcp/src/widget/studio/Studio.tsx index b499a29e..4fcea91e 100644 --- a/apps/mcp/src/widget/studio/Studio.tsx +++ b/apps/mcp/src/widget/studio/Studio.tsx @@ -331,8 +331,8 @@ export function Studio() {
- Active workspace set + Active space set

- Saves and recalls will use this workspace until you change it. + Saves and recalls will use this space until you change it.

) diff --git a/apps/mcp/src/widget/views/Picker.tsx b/apps/mcp/src/widget/views/Picker.tsx index 09c40a7b..0734103b 100644 --- a/apps/mcp/src/widget/views/Picker.tsx +++ b/apps/mcp/src/widget/views/Picker.tsx @@ -56,17 +56,17 @@ export function Picker({ setPending(null) if (!result.ok || !result.data) { log("error", `[picker] set-active-tag failed: ${result.error}`) - onError(result.error ?? "Failed to set active workspace") + onError(result.error ?? "Failed to set active space") return } onAdvance(result.data) const handoff = await handoffToModel({ - context: `Supermemory workspace selection changed. Active workspace: "${containerTag}". Use it for future Supermemory actions until another workspace is selected.`, - message: `I selected "${containerTag}" as my active Supermemory workspace. Use this workspace for future Supermemory actions until I select another one.`, + context: `Supermemory space selection changed. Active space: "${containerTag}". Use it for future Supermemory actions until another space is selected.`, + message: `I selected "${containerTag}" as my active Supermemory space. Use this space for future Supermemory actions until I select another one.`, structuredContent: { supermemory: { - action: "workspace-selected", - activeWorkspace: containerTag, + action: "space-selected", + activeSpace: containerTag, }, }, }) @@ -87,22 +87,22 @@ export function Picker({ const count = containerTags.length const description = count === 0 - ? "Create a workspace in Supermemory to get started." - : "Pick the workspace to save and recall from." + ? "Create a space in Supermemory to get started." + : "Pick the space to save and recall from." return (
- +
{count === 0 ? (

- No workspaces yet + No spaces yet

- Workspaces you create in Supermemory show up here, ready to save - and recall from. + Spaces you create in Supermemory show up here, ready to save and + recall from.

) : ( @@ -113,7 +113,7 @@ export function Picker({ setQuery(e.target.value)} - placeholder="Search workspaces…" + placeholder="Search spaces…" value={query} />
@@ -122,7 +122,7 @@ export function Picker({ {filtered.length === 0 ? (

- No workspaces match “{query}”. + No spaces match “{query}”.

) : ( @@ -148,7 +148,7 @@ export function Picker({ {pending ? (

- Setting workspace to {formatTagLabel(pending)}… + Setting space to {formatTagLabel(pending)}…

) : null}
diff --git a/apps/mcp/src/widget/views/Save.tsx b/apps/mcp/src/widget/views/Save.tsx index fa354398..df204018 100644 --- a/apps/mcp/src/widget/views/Save.tsx +++ b/apps/mcp/src/widget/views/Save.tsx @@ -76,12 +76,12 @@ export function Save({ result.data.view === "save-success" ? result.data.id : undefined onAdvance(result.data) const handoff = await handoffToModel({ - context: `Supermemory widget action completed. A memory was saved to workspace "${selectedTag}"${memoryId ? ` with memory ID "${memoryId}"` : ""}. Saved content:\n\n${trimmed}\n\nIt is already saved; do not save it again.`, - message: `I used the Supermemory widget to save a memory to workspace "${selectedTag}"${memoryId ? ` (memory ID: ${memoryId})` : ""}. The memory is already saved; do not save it again.`, + context: `Supermemory widget action completed. A memory was saved to space "${selectedTag}"${memoryId ? ` with memory ID "${memoryId}"` : ""}. Saved content:\n\n${trimmed}\n\nIt is already saved; do not save it again.`, + message: `I used the Supermemory widget to save a memory to space "${selectedTag}"${memoryId ? ` (memory ID: ${memoryId})` : ""}. The memory is already saved; do not save it again.`, structuredContent: { supermemory: { action: "memory-saved", - activeWorkspace: selectedTag, + activeSpace: selectedTag, memoryId, content: trimmed, }, @@ -104,7 +104,7 @@ export function Save({ return (
@@ -120,7 +120,7 @@ export function Save({ {writableTags.length > 0 ? ( - +
@@ -150,7 +150,7 @@ export function Upload({ )} {writableTags.length > 0 ? ( - +