Write safety: expected_tenant and target_tenant_id

A tenant with `mcp.writes_require_expected_tenant` enabled refuses every write that omits `expected_tenant`, raising `expected_tenant_required`. A platform PAT calling a tenant-scoped tool must pass `target_tenant_id`. Both exist so a connector pointed at the wrong tenant errors rather than silently acting.

Why these exist

Several connectors can appear in one client at once — a live business tenant, a test tenant, the platform connector — and they are indistinguishable by tool name. create_invoice looks identical on all of them. A write lands in whichever tenant the connection is bound to.

These two parameters turn "I assumed the right connector" into an assertion the server checks.

expected_tenant — assert the tenant you meant

Available on every tool. Pass the tenant's name exactly as it appears in meta.tenant.name, or its UUID.

propose_send_invoice({
  invoice_id: "…",
  expected_tenant: "Example Facilities Pty Ltd"
})

If the connector resolves to a different tenant, the call fails and does nothing:

code: expected_tenant_mismatch

When it is mandatory

A tenant can require it on every write by enabling mcp.writes_require_expected_tenant. On such a tenant, omitting it fails before anything happens:

code: expected_tenant_required
detail: This tenant requires expected_tenant on all write calls
        (mcp.writes_require_expected_tenant is enabled). Pass
        expected_tenant with the tenant name or UUID to confirm
        intent, or disable the setting.

This is a first-call surprise for any integration. If you are writing a client or an agent loop, pass expected_tenant on every write from the start — it costs nothing on tenants that do not require it, and it is the difference between a caught mistake and a silent one.

Reads are unaffected.

target_tenant_id — platform PATs calling tenant tools

The platform connector can act across tenants. When it calls a tenant-scoped tool, it must name the tenant explicitly:

error: Platform PAT must include target_tenant_id when calling a
       tenant tool. Use list_all_tenants to discover tenant ids.

Discover the ids with list_all_tenants. Tenant-PAT callers omit the parameter entirely — their tenant is inferred from the session, and passing it is neither needed nor accepted.

The route validates and strips target_tenant_id before the handler runs, so handler behaviour is identical either way.

The orientation habit that avoids both

Call get_session_context when you first connect. It returns the active tenant's name, id and mode (LIVE, TEST, or PLATFORM). Every tool response also carries meta.tenant with the same three fields.

Check meta.tenant.mode before any write. LIVE means real client data.

Error reference

CodeCauseFix
expected_tenant_requiredTenant requires the assertion; call omitted itAdd expected_tenant
expected_tenant_mismatchThe assertion did not match the resolved tenantYou are on the wrong connector
(message, no code) Platform PAT must include target_tenant_idPlatform connector calling a tenant toolAdd target_tenant_id, or use the tenant connector

Related

Last updated 2026-08-28