Skip to content
Status

MCP Gateway / Upstream credentials

Upstream credentials

Two ways an MCP server authenticates to the API behind it: a token per end user through an authorization server, or one shared credential used for every caller, either a static key or an OAuth client credentials token.

Once a server knows who is calling it, a second question remains: which credential it presents to the API behind it. There are two answers, and a server can use both at once for different upstreams.

  • A credential per end user. The platform acts as an OAuth client of the upstream service and holds an access token and refresh token for each person. A Linear tool call runs as that person in Linear.
  • One credential for the whole server. A credential held once and used for every caller. It can be a static API key, supplied as an environment variable on a hosted server or as an upstream header on a remote one, or, on a remote server, an access token the platform obtains for itself with the OAuth client credentials grant.

Per-user credentials require user sessions, since a token has to be stored against a known person. Shared credentials work on any server, including an anonymous public one.

On a remote server, the Identity section of the Settings tab makes this one choice: User Identity for a credential per end user, Service Account for one shared credential, or Manual for no managed identity. See Picking an authentication model.

Upstream credentials decide what the server can do upstream, not who may call it. Which people and agents may connect, and which tools they reach, is set separately on the server’s Team Access tab.

Viewing a server’s upstream credential settings requires the mcp:read scope and changing them requires mcp:write. Creating a service account client from a server’s Identity section also requires project:write, and picking a signing key set for it requires org:read. Editing a service account client after it is saved requires org:admin. Creating and managing authorization servers, and creating signing key sets, requires org:admin, which only the default Admin role holds.

Per-user upstream credentials come from an authorization server: an upstream OAuth authorization server such as Linear, GitHub, or Slack that the platform delegates to on the end user’s behalf. Tokens are encrypted at rest and refreshed silently.

On a remote server, select User Identity and pick the Authorization server, as described in Picking an authentication model.

On a hosted server, a tunneled server, or a gateway, open the Authentication section of the Settings tab. Find Authorization servers and click Add authorization server. The Attach Authorization Server sheet asks for two things:

  • Authorization Server, the upstream OAuth authorization server the platform delegates to. Either select an existing authorization server or add a new one by issuer URL, which is validated by fetching its metadata.
  • Session Client, described as the OAuth client the platform registers and uses with this authorization server.

A session client can be created three ways:

Client typeUse when
Dynamic Client RegistrationThe authorization server supports DCR and can mint a client automatically
Client ID Metadata DocumentThe authorization server accepts a hosted metadata URL as the client identity, with no registration step
ManualA client ID and secret already exist, created by hand in the authorization server’s console

Authorization servers and clients live at three tiers: project, organization, and platform. A lookup prefers a project-scoped record, then an organization-scoped one, then the platform default, taking the oldest match within a tier. Platform-tier authorization servers are what make a common service work with no setup at all.

A server can attach one client per authorization server, and an individual server holds at most one authorization server. Two clients for the same issuer on the same server is not a supported configuration. A gateway is the exception: it can hold several authorization servers, one per member that needs its own upstream grant.

Attaching a new authorization server invalidates every existing consent for that server. Consent is recorded against the exact set of authorization servers in force when it was given, so every connected user is prompted again on their next connection. Plan authorization server changes the same way as a breaking change.

Attached authorization servers appear on the consent screen under Connect required services, one card per upstream. Each card shows one of three states:

  • Connected, with a check mark.
  • Expired, prompting a reconnect.
  • Not connected, which is simply the absence of any stored credential rather than a state of its own.

Clicking Connect or Reconnect sends the user through the upstream’s own OAuth flow and returns them to the consent screen. Access cannot be granted until at least one service is connected, and an expired card does not satisfy that requirement.

Expiry is all or nothing. One expired upstream credential fails every tool served through that issuer, not only the tools that need it. Reconnecting restores all of them at once.

The platform refreshes upstream tokens when they are needed, and can also refresh idle connections in the background depending on the organization’s policy. See Session length and refresh.

Stored credentials carry only two statuses, active and expired. Disconnecting an upstream service deletes the stored credential, which puts the card back to not connected and forces a reconnect on the next consent screen. Revoking a person’s session in the platform does not revoke the grant at the provider, as described in Revoking access. The upstream providers each connection reaches are listed on the server’s Clients and Sessions tab.

Credentials through Enterprise Managed Auth

Section titled “Credentials through Enterprise Managed Auth”

With Enterprise Managed Auth, Okta supplies each person’s upstream token instead of a per-service Connect step. When a person calls a tool, the platform exchanges their Okta identity for an access token at the downstream service. Okta decides, under policies the organization manages centrally, which people may reach which services.

A server can use Enterprise Managed Auth when:

  • The downstream service has its own authorization server that supports Cross App Access (XAA), the Okta implementation of Enterprise Managed Auth.
  • The server signs people in through a user session issuer that trusts Okta. See Trust Okta for MCP sign-in.
  • The server’s resource connection is set up in Okta. See Connect each server in Okta.

Services reached this way need no reconnecting and show as Connected through your organization on the consent screen. Removing a person or a resource connection in Okta stops new access to every server that uses it.

Authorization server and client records are managed at the organization level in Authorization servers, under Identity in the project sidebar. Setup walkthroughs for individual services live in the guides.

Shared credentials reach the upstream two different ways depending on the server type.

Hosted servers use an environment attached to the MCP server. Remote servers use either upstream headers configured on the server itself, encrypted at rest, or a service account client that obtains an OAuth access token for itself.

Each variable declared by a source appears in the server’s configuration with one of three modes:

ModeMeaning
SystemValue is stored securely and injected by the system
UserUser must provide this value when connecting
OmitVariable is not included in the configuration

Only variables set to System are pulled from the attached environment. A variable set to User becomes part of the install snippet the connecting client has to fill in, which is how a server can be shared without sharing its credentials.

Values are resolved in layers, each overriding the last: the source’s own environment, then the environment attached to the server’s tool selection, then the environment attached to the MCP server for system variables, then the authenticated caller’s selected environment, then MCP- prefixed request headers, then any OAuth token injection, and finally environment variables passed in the tool call arguments.

A public server does serve its System environment variables and configured upstream headers to anonymous callers. That is the design, not an oversight. These are the server’s own credentials and are used for every caller, so public means anonymous callers get to spend them, not that the credentials are withheld. A credential that should not be spent by strangers does not belong on a public server.

Two further behaviors apply:

  • Every system environment variable that is not consumed as a security scheme is still sent on HTTP tool calls, as a header named after the variable in title case with dashes. MY_CUSTOM_THING arrives at the upstream as My-Custom-Thing.
  • A missing credential is not an error. The request is made without the header and the upstream answers, usually with a 401 that surfaces to the model as a normal tool result.

A related guard: a user-supplied server URL is refused whenever the server has any system environment variable set, which stops a caller from redirecting a credentialed request to a host of their choosing.

Remote servers are not built from sources and therefore have no environment. A static shared credential for the Authorization header is set by selecting Service Account in the Identity section of the Settings tab and choosing one of the Static formats:

FormatFieldsSent as
BearerPrefix (default Bearer) and TokenAuthorization: <prefix> <token>
BasicUsername and PasswordAuthorization: Basic <encoded username and password>
ManualHeader valueAuthorization: <header value>, exactly as typed

The section previews the header that will be sent upstream. Other shared headers go in the collapsible Custom Headers row of the same section, described as upstream headers sent with every request.

The identity choice owns the Authorization header. Service Account writes it, and User Identity and Manual do not allow one to be added under Custom Headers. Upstream headers are applied to every proxied request, last in the chain.

A header whose configured value resolves to empty removes the header instead of sending a blank one, which is the way to strip a header the client sent rather than replace it.

OAuth client credentials on remote servers

Section titled “OAuth client credentials on remote servers”

Instead of a static key, a Service Account can use an OAuth client registered with the upstream’s authorization server. The platform requests an access token for the client itself with the OAuth client credentials grant and sends it upstream as the Authorization header for every caller. Nobody connects their own account, the consent screen shows no connect step for the upstream, and there is no long-lived key to paste and rotate by hand.

Use this when the upstream service offers machine-to-machine apps, sometimes called service apps or API clients, that are allowed the client credentials grant. Before starting:

  • Create the app at the authorization server and allow it the client credentials grant.
  • Turn off DPoP for the app. The platform does not send DPoP proofs.
  • Make sure the upstream’s authorization server already exists in Authorization servers, and that it has a token endpoint. The Identity section only picks existing authorization servers, and creating one requires org:admin. An authorization server without a token endpoint is listed with no token endpoint and cannot be chosen. The token endpoint can be entered by hand on the authorization server’s Settings tab, with no metadata discovery required.

OAuth formats are not offered while creating a remote server. Create the server first, then set them up from its Settings tab. In the Identity section, select Service Account, then choose one of the OAuth formats and pick the Authorization server:

  • Client Secret: paste the Client ID and Client secret the authorization server issued for the app. The secret is encrypted at rest and never shown again. Saving creates the client and attaches it to the server in one step.

  • Signing Client: the platform authenticates its token requests with an assertion signed by a key in the organization’s own KMS (private_key_jwt), so no shared secret is stored anywhere. This format requires the customer-managed encryption keys entitlement and shows Requires KMS without it. Setup takes three steps:

    1. Create the app at the authorization server and paste its client ID.
    2. Choose the signing key. Select Self-Managed to sign with a key set backed by a Google Cloud KMS key, pick the Key set, or create one, and select Create client. Creating a key set opens the guided setup described in Encryption Keys.
    3. Add the key to the authorization server. Copy the Key set URL into the app’s settings, or copy the Public key for authorization servers that take a pasted key set instead of a URL.

    Then select Save to attach the client to the server. A client created in step 2 but never saved is deleted when leaving the page, picking a different authorization server, or saving a different choice.

Before saving a Client Secret client, or before selecting Create client for a Signing Client, the collapsible Advanced row sets how the client asks for tokens:

  • Scopes, requested each time the client asks for a token. The list offers the scopes the authorization server advertises, and other scopes can be typed in.
  • Audience (optional), for authorization servers that need an audience parameter to issue a token for the right API.
  • Send the secret in (Client Secret only): Authorization header (default) for client_secret_basic, or Request body for client_secret_post.
  • Client assertion audience (Signing Client only): Authorization server URL (default), or Token endpoint URL for authorization servers that require it, such as Okta.

Once a client is saved, the section shows its Client ID. The Advanced button beside the authorization server opens the client’s page under Authorization servers, where its scopes, audience, secret, and key set are edited. Clear starts replacing the client with another credential, and the saved client stays in use until the replacement is saved. Cancel backs out of the replacement. Replacing or removing a service account client, or replacing a static Authorization credential with one, asks for confirmation before saving. A detached client stays available to other servers.

A service account token carries the permissions of the app at the authorization server, not of the person calling the tool. Every caller the server’s access rules reach acts upstream as that app. Grant the app only the scopes the server needs, and narrow the server’s Team Access to the people entitled to use it.

  • One token for every caller. The token is cached and shared across callers until shortly before it expires, and for at most an hour. A token with no reported expiry is reused for at most five minutes. Rotating the client secret or the signing key, or changing the scopes or audience, requests a fresh token on the next call.
  • Request parameters. Each token request includes the client’s scopes and audience, and usually an RFC 8707 resource indicator naming the upstream. When the authorization server rejects the resource indicator with invalid_target, the platform retries once without it.
  • Rejected tokens. When the upstream answers 401, the platform discards the token, requests a new one, and replays the request once, unless the token was obtained in the last 30 seconds. If the upstream still rejects it, answers 403, or the authorization server refuses the client, callers get an error saying the upstream credential is misconfigured and to contact the MCP server administrator. They are never asked to reauthorize, because there is nothing for them to connect. A refusal from the authorization server is remembered for 30 seconds, so a fix made only at the provider can take that long to apply.
  • Revoked credentials. When the authorization server answers invalid_client, the server’s Identity section shows that the authorization server stopped accepting the client’s credentials, and on which date. The alert clears when a new client secret is saved, or after the next successful token request.
  • Outages. When the authorization server’s token endpoint is unreachable, failing, or rate limiting, or the KMS cannot sign, callers are told the upstream authorization server is temporarily unavailable and to retry shortly. The response is a 503 with Retry-After: 30.

Deleting a service account client is allowed while servers use it. Those servers then stop authenticating upstream until a new identity is saved.

Service account clients can also be created from the authorization server’s page, by choosing Service account under Signs in as in the New Client sheet. See Register a client.

Tunneled servers have no Custom Headers setting, because the server runs inside the organization’s network and holds its own configuration. Requests forwarded through the tunnel still carry two platform headers: Authorization with the caller’s upstream OAuth credential when an authorization server is attached, and, on private tunneled servers, X-Speakeasy-Identity with a signed JWT describing the authenticated caller. The server can verify that JWT to apply its own policy. See Signed caller identity.