Skip to main content

Model tier gate

An organization can require that a specific tool only runs behind a capable model — “this action needs a pro-or-better model”. NexSpace enforces that floor on every surface, including MCP. Because your model is invisible to NexSpace, an external caller is graded by a credential-level tier equivalence instead. The gate is fail-closed: if the tool carries a floor and your credential has no equivalence configured, the call is refused. The same tool call succeeds for the in-app assistant, which is why this is the most confusing -32003 on the surface.
Tier floors are opt-in per tool and no tool ships with one. If your organization has never set a floor in AI Configuration → Tool Management, nothing on this page changes anything for you: every tool resolves to “no floor” and passes untouched.

What a model-tier floor is

A NexSpace operator sets a per-tool floor in AI Configuration → Tool Management. The effective floor for a tool resolves as:
  1. the platform-store override (ai_tool_definitions.required_model_tier), then
  2. the tool’s own requiredModelTier declared in code, then
  3. no floor.
The in-app agent loop compares that floor against the tier of the model actually running the turn. Over MCP there is no such thing to compare: the model belongs to your client — claude.ai, ChatGPT, Claude Code, a script — and NexSpace never sees it and cannot grade it. Before this gate existed, connector traffic simply bypassed tier floors, which made external callers the one surface exempt from the control.

Tier equivalence

A tier equivalence is the tier a headless credential’s calls are treated as running at. It is a property of the credential, not of the request. There are four tiers, ranked lowest to highest: A credential with no equivalence anywhere is ungraded (null).

Resolution order

The equivalence resolves most-specific-first. The first source that yields a valid tier wins: The two maps are JSON objects of the form { "<id>": "<tier>" }. All three are environment configuration an operator sets — there is no API for them, and no request header or tool argument can influence the result.
An OAuth token has no API key id, so source 1 can never match for an OAuth connector. An OAuth credential is graded only by its bound user’s facility (source 2) or by the fleet default (source 3). If you are debugging a connector that is ungraded while an API key on the same facility is graded, that is the reason.
Malformed JSON in either map, or an entry whose value is not one of the four tiers, is dropped silently and resolution falls through to the next source. A bad env var never throws on the tool-call path — it just makes credentials less graded, never more.

Fail-closed semantics

Given a tool’s effective floor and the credential’s equivalence: Two consequences worth internalizing:
  • An ungraded credential is blocked by any floor, even a fast floor. It is not “assume the lowest tier”; it is “no standing at all”.
  • An org that never set a floor sees no change. The gate is narrow by design — it only bites where an operator explicitly asked for a capable model.

Where it runs

The gate is applied in both dispatch paths:
  • Named verbs — checked against the verb id (fillShift, runPayroll, …). Verbs declare no code-level tier, so a verb only carries a floor when an operator set an override row for that verb id.
  • Registry tools — checked against the tool name and its code-declared requiredModelTier, override taking precedence.
The tier gate is independent of scopes, guardrails, approvals, and the tenant-boundary guard — different mechanism, different error payload. In the dispatch order it runs after the credential’s scope check (a missing scope is reported first, as Insufficient credential scope) and before the tenant-boundary guard, the org guardrails, the idempotency reservation, and the approval gate. So a floored tool is refused before any guardrail is evaluated and before an Idempotency-Key is consumed — a tier rejection never wedges a key.

The error you get

A blocked call returns JSON-RPC -32003 (FORBIDDEN). The message names the tool and the required tier; error.data carries requiredTier and tierEquivalence so you can branch on it without parsing prose. Ungraded credential:
Under-graded credential:
tierEquivalence is null exactly when the credential is ungraded, and a tier string when it is graded but too low — that single field tells you which of the two remediations you need.

How to tell this apart from the other -32003s

-32003 is returned for several unrelated reasons. Branch on error.data:
  • requiredTier / tierEquivalence present → this page.
  • required / granted present → a scope gap. See MCP.
  • guardrail present → an org guardrail. See Governance.
  • suggestion reads “Omit facilityId to use your accessible facilities…” → a tenant-boundary violation. See Tenant isolation.

Remediation

retryable is false and it means it — the same call will fail forever until configuration changes. Two ways forward:
  1. Ask a NexSpace operator to grant this credential a model-tier equivalence, at whichever level of the resolution order fits (per key, per facility, or fleet-wide).
  2. Use a tool without a tier floor. The floor is per tool, so an alternative tool in the same category usually works.
An operator can also lower the tool’s floor in AI Configuration → Tool Management, which changes it for every surface — the in-app assistant included — so it is the heavier of the two changes.

See also

  • MCP — where this gate sits in the tool-call path.
  • Governance — guardrails and the headless autonomy dial, the other two operator controls that change how your calls behave.
  • Tenant isolation — the facility-boundary guard.