The MCP Credential Boundary: Why an MCP Server Should Not Decide Where Your OAuth Credentials Go

A vulnerability in the MCP Python SDK exposed an important security boundary: credentials must be bound to the authorization server they belong to, not simply sent wherever a remote MCP server directs them.

Read in: English · తెలుగు · हिन्दी

Diagram showing an MCP client sending credentials only to a trusted authorization server, which then issues an access token for the intended MCP resource.

A vulnerability in the official MCP Python SDK exposed a simple but important security rule. Protecting a credential is not enough. A client must also know which authorization server is allowed to receive it.

A credential can be completely valid and still be sent to the wrong place.

The secret does not have to be guessed. Encryption does not have to be broken. The OAuth flow can even look normal.

The failure can happen because the wrong system was trusted to answer one question:

Where should this credential be sent?

A security vulnerability disclosed on September 28, 2026 in the official Model Context Protocol (MCP) Python SDK exposed exactly this problem. In affected OAuth client configurations, an MCP server could influence which authorization server received the client’s credentials. A malicious or compromised MCP server could therefore redirect sensitive OAuth material to an endpoint it controlled.

The immediate fix matters.

The more durable lesson is:

The system asking you to authenticate should not automatically be trusted to decide where your credentials are delivered.

Where the extra trust decision appears

Consider a remote MCP client connecting to a protected MCP server.

The MCP server can publish metadata telling the client which authorization servers are associated with it.

That flexibility is useful. Different MCP servers can use different identity systems.

But discovery and trust are not the same thing.

A client holding credentials for one authorization server must not automatically send them somewhere else simply because the MCP server points there.

What failed in the Python SDK

The vulnerability was in the SDK’s OAuth client support.

According to the GitHub advisory, two protections were incomplete.

  • Authorization-server metadata did not have its issuer validated on every discovery path.
  • Stored or pre-provisioned client credentials were not consistently bound to the authorization server they belonged to.

A malicious or compromised MCP server could therefore direct the client to an attacker-controlled token endpoint.

Depending on the OAuth flow, the attacker might receive a client_secret, an authorization code, a PKCE code_verifier, or a signed client assertion.

The attacker does not defeat the credential.

They change its destination.

This is why the issue is better understood as a credential-boundary failure rather than simply an OAuth bug.

Expected versus broken credential boundary Side-by-side comparison: the expected flow validates the authorization server against an expected issuer before sending a credential to a trusted token endpoint, while the broken flow lets the MCP server influence the destination, sending the credential to an attacker-controlled authorization server. {“creator”:”TechiesJournal”,”author”:”Prasad Kukkala”,”asset”:”mcp-credential-boundary-figure-1”,”source_revision”:”mcp-credential-boundary-v1-2026-09-30”,”created”:”2026-09-30”,”rights”:”Copyright 2026 TechiesJournal. All rights reserved.”} EXPECTED BROKEN 1. MCP client Holds a credential for one authorization server 2. Expected issuer Client already knows which issuer it trusts 3. Authorization server validated Discovered metadata is checked against it 4. Trusted token endpoint Credential sent only if the identity matches 1. MCP client Holds a credential for one authorization server 2. MCP server influences destination Advertises where authorization should happen 3. Client accepts that destination Issuer was never independently checked 4. Attacker-controlled endpoint Credential is exposed, not merely misused The vulnerability did not require breaking the credential. The problem was allowing the remote MCP server to influence where it was sent. TECHIESJOURNAL
Figure 1: The vulnerability did not require breaking the credential. The problem was allowing the remote MCP server to influence where the credential was sent.
Accessible text alternative for Figure 1

Expected flow: 1. MCP client holds a credential for one authorization server. 2. Expected issuer, the client already knows which issuer it trusts. 3. Authorization server validated, discovered metadata is checked against it. 4. Trusted token endpoint, credential sent only if the identity matches. Broken flow: 1. MCP client holds a credential for one authorization server. 2. MCP server influences destination, advertising where authorization should happen. 3. Client accepts that destination, the issuer was never independently checked. 4. Attacker-controlled endpoint, the credential is exposed, not merely misused.

What does issuer protect?

OAuth terminology can make this sound more complicated than it is.

For this discussion, the issuer answers a practical question:

Which authorization server do we trust for this relationship?

Imagine a client secret belongs to auth.example.com.

The client then connects to an MCP server that tries to redirect authentication toward another authorization server.

The presence of valid-looking OAuth metadata should not be enough.

The client needs an independent expectation that says: this credential belongs to this issuer.

The fixed SDK determines the expected issuer before processing authorization-server metadata, rejects metadata with a different issuer, and binds stored registrations to that issuer.

The July 2026 MCP specification also reinforces the same principle. Persisted client credentials must remain associated with the authorization server they belong to rather than being reused when the authorization server changes.

Who is actually affected?

This issue does not mean every MCP client or server is vulnerable.

The advisory applies when both of these conditions are true.

  1. The application uses the Python SDK as an MCP client over HTTP with one of the affected built-in OAuth providers.
  2. The client may connect to an MCP server it does not fully trust while holding credentials for a legitimate authorization server.

Affected providers include OAuthClientProvider, ClientCredentialsOAuthProvider, PrivateKeyJWTOAuthProvider, and the deprecated 1.x RFC7523OAuthClientProvider.

The advisory specifically says the issue does not affect MCP servers built using the SDK, stdio clients, or clients that attach their own tokens or headers rather than using the affected OAuth handlers.

Affected and patched MCP Python SDK versions
SDK lineAffectedPatched
1.x>=1.9.1, <1.30.01.30.0
2.x>=2.0.0a1, <2.2.02.2.0

The correct conclusion is therefore not that MCP authentication is insecure.

It is that a specific OAuth client implementation failed to preserve an authorization-server trust boundary.

Why upgrading may not be enough

Teams using affected versions should upgrade to 1.30.0 or later on the 1.x line, or 2.2.0 or later on the 2.x line.

But there is an important configuration detail.

Applications using ClientCredentialsOAuthProvider or PrivateKeyJWTOAuthProvider must also provide the expected issuer=.

Without it, those providers can still follow whichever authorization server the MCP server advertises.

This exposes another useful security principle:

A patched library cannot infer a trust relationship the application has never defined.

The library can enforce issuer binding.

The application still has to tell it which issuer is trusted.

Old registrations may remain a problem

An upgrade changes future behaviour. It does not automatically repair every credential relationship created in the past.

OAuth registrations stored without issuer information can remain unbound after upgrading.

Applications may therefore need to clear those records and register again, or add the correct issuer where they manage pre-registered client information themselves.

If an affected client may previously have connected to an untrusted MCP server, teams should also consider rotating its client secret and revoking associated tokens.

The distinction is important. Patching the software does not always mean removing previous exposure.

Issuer and audience protect different boundaries

There is another MCP authorization control that is easy to confuse with issuer binding.

The MCP authorization specification requires clients to identify the intended resource and requires servers to verify that access tokens were issued for that resource.

This is audience or resource binding.

The two controls protect different stages.

Issuer binding asks: which authorization server is trusted to receive and manage these credentials?

Audience or resource binding asks: which MCP service is the resulting access token meant for?

Issuer binding versus audience binding Six-step chain from client credential through a trusted issuer and authorization server to an access token, expected resource and MCP server, with the first three steps grouped as issuer binding and the last three grouped as audience or resource binding. {“creator”:”TechiesJournal”,”author”:”Prasad Kukkala”,”asset”:”mcp-credential-boundary-figure-2”,”source_revision”:”mcp-credential-boundary-v1-2026-09-30”,”created”:”2026-09-30”,”rights”:”Copyright 2026 TechiesJournal. All rights reserved.”} 1 Client credential A secret, code or signed assertion 2 Trusted issuer The authorization server the client expects 3 Authorization server Validated against that trusted issuer 4 Access token Issued after the issuer check succeeds 5 Expected resource The MCP server the token names as audience 6 MCP server Accepts the token only if it is the named audience ISSUER BINDING AUDIENCE / RESOURCE BINDING Issuer binding protects where a credential is sent. Audience binding protects where the resulting token can be used. TECHIESJOURNAL
Figure 2: Issuer binding protects where credentials are sent. Resource or audience binding protects where the resulting token can be used.
Accessible text alternative for Figure 2

Six-step chain: 1. Client credential, a secret, code or signed assertion. 2. Trusted issuer, the authorization server the client expects. 3. Authorization server, validated against that trusted issuer. 4. Access token, issued after the issuer check succeeds. 5. Expected resource, the MCP server the token names as audience. 6. MCP server, accepts the token only if it is the named audience. Steps 1 to 3 are grouped as issuer binding. Steps 4 to 6 are grouped as audience or resource binding. A closing note states issuer binding protects where a credential is sent, while audience binding protects where the resulting token can be used.

A simplified relationship looks like this: client credential, trusted issuer, authorization server, access token, expected resource, MCP server.

Issuer binding protects the first trust relationship.

Audience binding protects the token after it has been issued.

Both matter.

Why MCP makes this especially important

MCP clients may connect to many servers and interact with different authorization systems.

That creates opportunities for authorization-server mix-up. Credentials or authorization responses from one relationship can accidentally be used in another.

The MCP specification includes protections for this class of problem, including validating authorization responses against the issuer recorded earlier in the flow and keeping persisted client credentials associated with the correct issuer.

This reinforces the larger lesson:

OAuth security is not only about keeping credentials secret. It is also about preserving the relationships those credentials belong to.

Which client? Which authorization server? Which resource? Which MCP server?

A credential can be cryptographically valid and still be dangerous if one of those relationships changes without independent verification.

What should MCP teams check now?

The response can remain focused.

1. Check the SDK version

Move affected installations to 1.30.0 or later on 1.x, or 2.2.0 or later on 2.x.

2. Identify the OAuth provider

Check whether the client uses one of the affected built-in OAuth providers.

3. Configure the expected issuer

For ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider, explicitly define the authorization server the credentials belong to.

4. Review stored registrations

Clear or repair older registrations that do not contain issuer binding.

5. Consider previous exposure

If the client may have connected to an untrusted MCP server while vulnerable, consider credential rotation and token revocation.

6. Verify token audience separately

Confirm that access tokens are requested for and accepted only by their intended MCP resource.

Protect the relationship, not only the secret

This SDK vulnerability will eventually become an old advisory.

The security principle behind it will remain relevant.

Modern systems increasingly discover services dynamically. Clients follow metadata. Agents connect to tools. Authentication crosses several independent components.

That makes one question increasingly important: who is allowed to define where a credential goes?

For this Python SDK flaw, the answer is enforced through authorization-server issuer binding.

For the resulting access token, MCP separately requires resource or audience binding.

The durable security rule is therefore broader than protect the credential.

Bind the credential to the relationship it belongs to.

A secret is not safe merely because nobody can guess it.

It also has to arrive at the right place.

References and further reading

Report a correction

Corrections go to the editor and are never published automatically. No account needed.