Skip to main content

Call an MCP tool with an identity provider token

Enterprise Tier

An application can call an MCP tool with a token from the organization's own identity provider, and the token stays at the gateway. This guide sets up the oauth MCP profile that makes this possible, with every step a management API call.


How it works​

An application obtains a token from the identity provider, connects to the profile's address, and presents the token. The gateway checks it against the issuer named on the profile and admits the call to the tools the profile lists. Step 5 lists what the gateway answers.

The caller's token is never passed on to the MCP server. The server receives the credential stored for it in the profile, if there is one, and otherwise no Authorization header.

The example​

One project, payments-assist, exposes the documentation search of one catalog server to an application. The values used throughout:

ItemValue in this example
Identity providerIssuer https://idp.example.com, signing keys at https://idp.example.com/.well-known/jwks.json
Organization (customer ID)acme
Projectpayments-assist
Catalog servercontext7, with the tools resolve-library-id and get-library-docs
Tool the profile exposesresolve-library-id
Profile ownerThe user whose ID is ${USER_ID}

The management API calls use ${MANAGEMENT_URL} for the management API address and ${ADMIN_API_KEY} for a management key, sent as a Bearer token.

Prerequisites​

  • The management plane and every data plane serving the project run 0.11.0 or later. The rules below are 0.11.0 behavior.
  • The project exists and is served by a data plane. See Serve a project from a data plane.
  • The identity provider serves its key set from an absolute http or https address, and can issue a token for the application's client.
  • The catalog server exists. See Govern MCP server access.

Step 1: grant the server to the project​

A profile can attach only a server that its project has been granted. Without the grant, attaching the server is refused with 403 permission_denied and the detail server is not allowed for this project.

curl -sS -X POST "${MANAGEMENT_URL}/admin/v1/customers/acme/projects/payments-assist/mcp-servers" \
-H "Authorization: Bearer ${ADMIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"serverId": "context7"}'

The response carries the grant:

{
"entry": {
"id": "<grant-id>",
"customerId": "acme",
"projectId": "payments-assist",
"serverId": "context7",
"createdAt": "2026-11-06T10:38:09.332183Z"
}
}

Step 2: create the profile with the identity provider​

A profile of type oauth names its identity provider in security_policy_value: the issuer, and remote_jwks, the address of the provider's key set as an absolute http or https URL. The value is a JSON string.

curl -sS -X POST "${MANAGEMENT_URL}/v1/customers/acme/projects/payments-assist/users/${USER_ID}/mcp-profiles" \
-H "Authorization: Bearer ${ADMIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "payments-docs",
"description": "Documentation search for the payments assistant.",
"securityPolicyType": "oauth",
"securityPolicyValue": "{\"issuer\":\"https://idp.example.com\",\"remote_jwks\":\"https://idp.example.com/.well-known/jwks.json\"}"
}'

The profile is created with the status pending, and with an empty endpoints list until a data plane serves it:

{
"id": "<profile-id>",
"user_id": "<user-id>",
"customer_id": "acme",
"project_id": "payments-assist",
"name": "payments-docs",
"description": "Documentation search for the payments assistant.",
"url": "/mcp/<path-assigned-by-the-server>",
"endpoints": [],
"status": "pending",
"error_message": "",
"security_policy_type": "oauth",
"security_policy_value": "{\"issuer\":\"https://idp.example.com\",\"remote_jwks\":\"https://idp.example.com/.well-known/jwks.json\"}",
"servers": []
}

Three refusals follow from the 0.11.0 rules. Each is a 400 invalid_argument, and the response bodies below show the fields that matter:

  • A security type other than oauth, api_key, or none. Spellings such as OAuth and oauth2 were accepted before 0.11.0 and served with no token check.

    {
    "title": "invalid_argument",
    "status": 400,
    "detail": "security_policy_type must be one of oauth, api_key or none",
    "reason": "INVALID_ARGUMENT"
    }
  • An oauth profile with no issuer or key set address.

    {
    "title": "invalid_argument",
    "status": 400,
    "detail": "security_policy_value.issuer is required for an oauth profile",
    "reason": "INVALID_ARGUMENT"
    }
  • An update that changes the type to oauth, the issuer, or the key set address and leaves either missing or invalid. An update that changes none of them is accepted, so a profile saved without them can still be renamed.

Step 3: attach the server with the tools it exposes​

On an oauth profile, each server lists 1 to 32 distinct tool names or regex: patterns, and regex:* means every tool. An empty list is refused (the responses in this step show the fields that matter):

curl -sS -X POST "${MANAGEMENT_URL}/v1/customers/acme/projects/payments-assist/users/${USER_ID}/mcp-profiles/${PROFILE_ID}/servers" \
-H "Authorization: Bearer ${ADMIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"serverId": "context7", "tools": []}'
{
"title": "invalid_argument",
"status": 400,
"detail": "tools must list at least one tool name for an oauth profile",
"reason": "INVALID_ARGUMENT"
}

Listing the tool exposes only that tool:

curl -sS -X POST "${MANAGEMENT_URL}/v1/customers/acme/projects/payments-assist/users/${USER_ID}/mcp-profiles/${PROFILE_ID}/servers" \
-H "Authorization: Bearer ${ADMIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"serverId": "context7", "tools": ["resolve-library-id"]}'
{
"server_id": "context7",
"status": "active",
"enabled_tools": ["resolve-library-id"],
"has_credential": false,
"created_at": "2026-11-06T10:38:14.813398Z"
}

Blank, padded, and repeated entries are refused as well. Sending the list a server already has is accepted, so a server's token can be rotated. The same rule applies to the CLI: tetrate agentrouter api mcp profiles servers add and servers set-tools both need --tools on an oauth profile.

Step 4: wait until the profile is served​

A new profile stays pending until the data plane has accepted the route for its path. A profile with no servers, or one refused under strict OAuth settings, has no route and stays pending. The endpoints list fills in as soon as a data plane serving the project reports an address, with an absolute url and the projectGatewayId of the serving gateway. An empty list means no data plane serving the project reports an address yet. A client connects once the status is active, to the address as the profile returns it, and not to one joined onto a host by hand. See Aggregate MCP servers into a profile.

Step 5: connect with a token from the identity provider​

The application gets a token from the identity provider with the client-credentials grant, then connects to the profile's address with the token as a Bearer value. The gateway answers as follows:

CallAnswer
A valid token, and a tool the profile listsThe call reaches the MCP server
A tool the profile does not list400 invalid tool name, and the call never reaches the server
A missing, expired, wrong-issuer, or wrongly signed token401
An Agent Router API key401

Profiles saved before 0.11.0​

A profile saved before 0.11.0 with an empty tool list, or with no issuer, is still served on a 0.11 data plane, with a warning. From 0.12 a server with an empty tool list is left out, and a profile with no issuer or no server left is not served. See Upgrade the data plane for how to find the affected profiles and to try the 0.12 behavior early.