Tenant isolation
Every registry tool call over MCP passes through a facility-scope boundary guard before it reaches the tool. The guard either rewrites your arguments to your accessible facilities or rejects the whole call. It is fail-closed: an argument the guard cannot prove is in scope is never passed through. This is the layer that makesfacilityId behave differently over MCP than it
does for the in-app assistant, and the difference surprises people. Read the
four behaviours before you hardcode a facility id.
What it applies to
On the registry path the guard runs after your arguments have been validated
against the tool’s
inputSchema and before the
org guardrails and the idempotency reservation. A call
rejected on the boundary therefore never evaluates a guardrail and never
consumes an Idempotency-Key.
Your accessible set
The guard resolves an accessible facility set from the credential’s bound user — the user the API key or OAuth token belongs to — using their role, org unit, and facility:
An empty set is not a pass-through. Every guarded call fails with:
API keys carry their own facility binding, which operators use to key the
per-facility autonomy and
tier-equivalence dials. The
accessible facility set on this page comes from the bound user, so a key
without a bound user cannot call a guarded tool at all — governed tools reject
org-only credentials with “This tool requires an authenticated user identity”.
The four behaviours
(a) You omit facilityId → NexSpace injects your accessible facilities
Omitting the argument can never mean “all tenants”. The guard injects your
accessible set in whatever shape the tool’s facilityId field accepts: the
array when the field takes one, or the single id when the field takes a
number and you have exactly one accessible facility.
facilityId field at all is treated as
tenant-agnostic (a catalog or metadata listing) and its arguments pass through
unchanged.
(b) You pass a facility you can access → honored
(c) You pass any id outside your scope → the whole call is rejected
(d) You can access several facilities but the tool takes a single facilityId → hard error
There is no way to represent “facilities 3 and 4” in a number field, and the
guard will not pick one for you or widen the query:
Recognizing a boundary rejection
All four rejections come back as-32003 (FORBIDDEN) with retryable: false,
and all of them carry this exact suggestion:
-32003 causes — a scope gap carries required / granted, a
guardrail carries guardrail, and a
tier floor carries requiredTier /
tierEquivalence. Boundary rejections carry none of those.
Practical guidance
- Prefer omitting
facilityId. Injection (behaviour a) is the safe default, works for single- and multi-facility credentials, and needs no knowledge of ids. - Discover ids before you use them. The
search_facilitiesregistry tool (facility:read) and thelistFacilitiesverb (facilities:read) return facilities you can reach; a hardcoded id from another environment is behaviour (c) waiting to happen. - Do not retry.
retryable: falseis accurate for all four — the call cannot succeed until the arguments or the credential change. - Expect (d) on single-facility tools if your bound user spans an org. Either bind the credential’s user to one facility or always pass an explicit id.
See also
- MCP — the full
-32003decision tree. - Governance — the guardrail layer that runs immediately after this guard.
- Tool catalog — which tools take a
facilityId.

