fix(mcp): reject nonexistent container tags in set-active-tag and search_memory

set-active-tag accepted any tag string even if it did not exist, and
search_memory surfaced a generic "Access forbidden" 403 for unknown tags.
Both tools now validate the tag against the user's container tag list and
return a clear "does not exist" error pointing at listSpaces.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ved015 2026-07-03 20:31:15 +05:30
parent 440e9e94bd
commit df671c6e41
4 changed files with 45 additions and 1 deletions

View file

@ -377,7 +377,9 @@ export class SupermemoryClient {
case 402:
throw new Error("Memory limit reached. Upgrade at supermemory.ai")
case 403:
throw new Error("Access forbidden.")
throw new Error(
`Access forbidden: you don't have access to container tag '${this.containerTag}', or it doesn't exist. Use listSpaces to see the available container tags.`,
)
case 404:
throw new Error("Not found.")
case 429:

View file

@ -1,6 +1,10 @@
import { z } from "zod"
import { getMemoryText } from "../client"
import type { ToolDeps } from "./types"
import {
containerTagExists,
unknownContainerTagError,
} from "./validate-container-tag"
export function register(deps: ToolDeps) {
const containerTagField: Record<string, z.ZodTypeAny> = deps.rbac
@ -43,6 +47,12 @@ export function register(deps: ToolDeps) {
),
)
}
if (
args.containerTag &&
!(await containerTagExists(deps, args.containerTag))
) {
return deps.errorResult(unknownContainerTagError(args.containerTag))
}
const effectiveTag = await deps.resolveContainerTag(args.containerTag)
const client = deps.getClient(effectiveTag)

View file

@ -2,6 +2,10 @@ import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"
import { z } from "zod"
import { SUPERMEMORY_RESOURCE_URI, type ViewMessage } from "../../shared/types"
import type { ToolDeps } from "./types"
import {
containerTagExists,
unknownContainerTagError,
} from "./validate-container-tag"
export function register(deps: ToolDeps) {
registerAppTool(
@ -26,6 +30,13 @@ export function register(deps: ToolDeps) {
new Error(`No access to container tag '${containerTag}'.`),
)
}
try {
if (!(await containerTagExists(deps, containerTag))) {
return deps.errorResult(unknownContainerTagError(containerTag))
}
} catch (error) {
return deps.errorResult(error)
}
await deps.storage.put("activeContainerTag", containerTag)
const sc: ViewMessage = {
view: "confirmation",

View file

@ -0,0 +1,21 @@
import type { ToolDeps } from "./types"
/**
* Checks whether a container tag actually exists for this user.
* Fast path is the cached tag list from init; on a miss we re-fetch once so
* tags created after the session started are not falsely rejected.
*/
export async function containerTagExists(
deps: ToolDeps,
containerTag: string,
): Promise<boolean> {
if (deps.cachedContainerTags().includes(containerTag)) return true
await deps.refreshContainerTags()
return deps.cachedContainerTags().includes(containerTag)
}
export function unknownContainerTagError(containerTag: string): Error {
return new Error(
`Container tag '${containerTag}' does not exist. Use listSpaces to see the available container tags.`,
)
}