Skip to main content

Require a user identity

Catalyst carries an end-user identity to an MCP server only when that server asks for one. By default a server asks for nothing, and its tools see calls with no user attached — the right default where caller identity does not matter.

Requiring a user identity turns propagation on and makes it mandatory in one step. Set it, and every call to that MCP server has to arrive with a verified user: Catalyst passes that identity on to the tool, and rejects any call it cannot, before the call reaches the server.

When to require a user identity

Require one when a call that cannot be traced back to a person is a problem rather than an inconvenience:

  • User-scoped side effects — writing to a CRM record, approving a ticket, sending mail as someone.
  • Tenant-scoped reads — queries whose result set must be limited to what the calling user is entitled to see.
  • Audit and compliance — any tool whose actions an auditor must be able to attribute to a named user.

Leave it off where caller identity genuinely does not matter: read-only reference lookups, public data, and tools that scheduled or event-driven agents call with no user in the picture. Enforcement is a property of the MCP server, not of the individual caller, so a server with enforcement on rejects every caller that arrives without a user — including your own cron-triggered agents. If a tool has to serve both kinds of caller, use the two-server pattern.

Before you begin

  • An MCP server in your project. Its access policy is created with it, so there is nothing to create first.
  • That server can read the end-user identity Catalyst delivers, in the X-Diagrid-User-Token header. A server that ignores the header still accepts the calls; it just cannot act on who is calling, which defeats the point of requiring one.
  • The Diagrid CLI, authenticated with diagrid login.
  • Agents that call the server are already running under enterprise identity: invoked with a user token, and calling MCP through the identity-aware HTTP client. Enforcement rejects any agent that is not.

Turn on enforcement

Requiring a user identity is a property of the MCP server's access policy, the same place its tool grants live. One command turns it on:

diagrid mcpserver access user-identity require salesforce-mcp --project my-project --wait

Every call to salesforce-mcp now has to arrive with a verified user. Read back what is set at any time:

diagrid mcpserver access user-identity get salesforce-mcp --project my-project

diagrid mcpserver access get carries a REQUIRE USER column alongside the policy's tool grants, so you can see the requirement without a separate lookup. Use user-identity get when you also need the required scopes.

In the Catalyst console, this is the Require authenticated user toggle in the User identity section of the MCP server's access policy. Policies that have it on are badged Requires authenticated user in the policy list, so you can see at a glance which of your servers enforce identity.

Setting this does two things. Calls that carry no verified user are rejected, and calls that do carry one now reach the tool with that identity attached. A server without it does not pass the user through — its tools see no user at all.

If you manage your project declaratively, the same requirement is part of the access policy resource and can be applied with diagrid apply instead.

Require specific scopes

To demand more than the presence of a user, list the scopes the user's token must carry. Every listed scope must be present and is matched exactly; a call missing any of them is rejected.

diagrid mcpserver access user-identity require salesforce-mcp \
--project my-project \
--required-scope crm.read,crm.write \
--wait

--required-scope is repeatable or comma-separated, and it replaces the whole list rather than adding to it. Re-running require without it keeps the requirement and drops the scopes, so pass the full set every time. Requiring a scope also requires a user — get reports the requirement as on whenever any scope is set, since a scope can only be checked against an identity that is present.

Scopes are read from the token's standard scp, scope, or scopes claim, and are issued by your identity provider; see IdP federation for the per-provider conventions. In the console they go in Required scopes; leaving it empty requires only an authenticated user.

This is a separate gate from the scopes you set on OAuthConfig in the helper library. That one guards the way in to your agent; this one guards the way out to a specific tool. A tool that needs a stronger scope than the agent as a whole is exactly the case for setting it here.

Roll it out safely

Enforcement takes effect immediately

There is no warn-only mode: once the requirement is on, callers without a user are rejected, not just logged. Start with one server and one low-traffic caller.

  1. Confirm who calls the server. diagrid mcpserver access get <mcpserver> lists every granted caller. Each one has to be able to supply a user, or it breaks.
  2. Enable on a canary server first. Pick the lowest-traffic server whose tools need enforcement, and run user-identity require against that one alone.
  3. Exercise the real path. Invoke the agent as a user would, and confirm the tool call succeeds end to end.
  4. Check what got rejected. A rejection comes back to your agent as an error on the MCP call, carrying one of the codes below. Log it in your agent so you can see which callers are affected, and follow it up in Troubleshooting.
  5. Extend to the rest. Once the canary's rejections match what you expect — ideally none — run the same command against your remaining servers.

What a rejected call returns

A rejected call never reaches the MCP server. Your agent gets an HTTP error whose body is {"error": "<code>"}:

CodeHTTP statusWhen it firesWhat to do
oauth.missing_token401The call carried no user, and the server requires one.The caller was not user-triggered. Route it through a server without enforcement — see the two-server pattern.
oauth.missing_scope403The user's token lacks one of the required scopes.Grant the scope in your identity provider, or re-run user-identity require with the scope dropped.
oauth.decode_error401The user token could not be read.Confirm the caller sends the token Catalyst issued, unmodified.
oauth.verifier_unavailable503Catalyst could not verify the identity right now.Transient. Retry with backoff.
obo.exchange_failed502Catalyst could not issue a delegated token for the call.Not something you configure. Retry, and open a support case if it persists.

Each code has a longer walkthrough in Troubleshooting.

Turn enforcement off

diagrid mcpserver access user-identity none salesforce-mcp --project my-project --wait

The command asks you to confirm; pass --yes to skip the prompt in a script. In the console, switch off Require authenticated user. Nothing is redeployed or restarted, so this is the fastest way out if enforcement rejects traffic you did not expect.

Be clear about what you get back, though: the server stops requiring a user and stops receiving one. Its tools then see no user identity at all. There is no middle setting that passes a user through when one happens to be present — so if you turn enforcement off to unblock one caller, you also give up the audit trail on every other caller to that server.

Serve both user and service callers

Some tools legitimately serve both: a sales agent a person is talking to, and a nightly sync job with no person behind it. Because enforcement is per server, you cannot exempt one caller from it. Register the same endpoint twice instead, and let each MCP server's access policy decide who reaches it.

  • salesforce-user has user-identity require set, and grants tools only to the agents a person invokes. Calls through it carry a delegated user identity.
  • salesforce-svc is left at user-identity none, and grants tools only to your scheduled and event-driven agents. Calls through it carry no user identity, which is what you want for a caller that has none.

Keep the grants disjoint. A caller that appears in both policies can reach the endpoint through the unenforced server, which defeats the requirement you set on the other one. Per-caller exemptions within a single server are not available today.

What's next

  • Troubleshooting — the full rejection-code reference and how to debug each one.
  • On behalf of — how the user identity reaches the tool in the first place.
  • Control tool access — the rest of the access policy these settings live on.