Skip to content

fix(mcp): resolve workspace display names and tenant ids in qualified project routes#979

Merged
groksrc merged 1 commit into
mainfrom
fix/workspace-identifier-resolution
Jun 11, 2026
Merged

fix(mcp): resolve workspace display names and tenant ids in qualified project routes#979
groksrc merged 1 commit into
mainfrom
fix/workspace-identifier-resolution

Conversation

@groksrc

@groksrc groksrc commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up agreed in the #964 review discussion.

The edit_note docstring/ValueError text advertise that the workspace segment of a composed workspace/project route may be a slug, name, or tenant_id (PR #964 copies the same wording into write_note). But resolve_workspace_project_identifier() in src/basic_memory/mcp/project_context.py only matched the first segment against WorkspaceInfo.slug (casefold). Cloud routes whose display name or tenant UUID differs from the slug failed with Workspace ... was not found, contradicting the documented contract.

Per the maintainer's decision, this makes the resolver honor the documented contract rather than walking the docs back. It makes the edit_note "slug, name, or tenant_id" promise real today, and write_note once #964 merges. Deliberately scoped to the resolver + tests; it does not touch write_note/edit_note, so it won't conflict with the unmerged #964.

Matching precedence

The first-segment workspace match (and only the matching logic — the overall resolution flow is unchanged) now accepts, in priority order:

  1. slug (casefold) — existing behavior, checked first so nothing currently working changes meaning.
  2. tenant_id — exact match on the opaque id (no casefolding, mirroring workspace_matches_exact_identifier precedent).
  3. display name (casefold) — names are not guaranteed unique.

Ambiguity (fail fast): a name matching multiple workspaces raises a clear error listing the candidate slugs and telling the caller to use the slug or tenant_id.

Precedence: because slug is matched first, a workspace whose display name equals another workspace's slug yields to the slug owner.

Not found: unknown identifiers raise an extended error naming the forms that were tried (slug, tenant_id, name) plus the available slugs.

Tests

Added to tests/mcp/test_project_context.py:

  • resolve by slug, tenant_id, and display name (case-insensitive)
  • ambiguous display name -> error naming candidate slugs
  • unknown identifier -> not-found error listing tried forms
  • precedence: name colliding with another workspace's slug resolves to the slug owner

The existing qualified-route/collision test stays green. Gates run: targeted pytest for project_context + edit_note routing, ruff check/format --check on changed files, and ty check src tests test-int (full scope) — all pass.

🤖 Generated with Claude Code

… project routes

The edit_note docstring (and write_note in PR #964) advertise that the
workspace segment of a "workspace/project" route may be a slug, name, or
tenant_id, but resolve_workspace_project_identifier() only matched against
WorkspaceInfo.slug. Cloud routes using a display name or tenant UUID failed
with "Workspace ... was not found" despite the documented contract.

Extend the first-segment matching (only the matching logic; the overall
resolution flow is unchanged) to honor, in priority order:

1. slug (casefold) — unchanged, checked first so working routes keep meaning
2. tenant_id — exact match on the opaque id
3. display name (casefold) — fails fast on collisions, listing candidate slugs

A name that collides with another workspace's slug resolves to the slug owner.
Unknown identifiers raise a not-found error naming the forms that were tried.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
@groksrc groksrc marked this pull request as ready for review June 11, 2026 20:25
@groksrc groksrc merged commit 33e741f into main Jun 11, 2026
23 checks passed
@groksrc groksrc deleted the fix/workspace-identifier-resolution branch June 11, 2026 20:28
groksrc added a commit that referenced this pull request Jun 11, 2026
…space

Live prod QA found `bm cloud share ... --workspace <slug>` failed with an
opaque 400: resolve_configured_workspace returns the explicit --workspace
value verbatim, but the cloud's X-Workspace-ID resolver only accepts a
workspace/tenant UUID. Users see slugs and display names (list-workspaces
output, memory:// URLs), so the natural input never routed.

The share commands now resolve the workspace header client-side: a UUID is
forwarded verbatim (covers per-project config workspace_id and the default
chain, zero extra API calls), while any other value is treated as a human
identifier and mapped to the tenant UUID via a single get_available_workspaces
lookup, matching with slug > tenant_id > name precedence (mirroring #979).
Ambiguous and unknown identifiers fail fast with errors that name the
candidate / available workspace slugs, and the list command now re-raises
typer.Exit ahead of its broad handler so the resolution errors aren't
re-wrapped as "Unexpected error".

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant