On October 5, 2026, the MCP TypeScript SDK 2.3.1 release added an optional token-audience check to requireBearerAuth in @modelcontextprotocol/server-legacy. It affects maintainers still using that legacy bearer-auth middleware: they can now require the token resource reported by their verifier to match the intended MCP server. Upgrading alone does not switch the check on.
Should you enable it? Yes, once your issuer and token verifier preserve the intended resource identity and legitimate clients can obtain matching tokens. If audience validation already happens in your verifier, this adds an explicit middleware check; it does not establish that your previous deployment was unprotected. The MCP authorization specification already requires protected servers following its flow to validate the intended audience.
Check your import before changing packages
Inspect the resolved lockfile and the import that supplies requireBearerAuth. The relevant distinction is the middleware package, not simply whether clients use an older MCP protocol version:
@modelcontextprotocol/server-legacy/auth: this is the path covered by the new option. The public auth entry point exports the helper.@modelcontextprotocol/serveror@modelcontextprotocol/express: use the modern implementation on compatible versions. The 2.3.0 release introduced this facility on that path; do not add the legacy package to obtain it.@modelcontextprotocol/sdk: the v1 package line is a separate upgrade decision. Do not treat the split v2 packages as a drop-in version bump. Follow the v2 migration guide if changing package families.
The npm record for server-legacy 2.3.1 confirms availability and declares Node.js 20 or later, with Express ^4.18.0 || ^5.0.0. For an existing npm-based legacy deployment, a version-pinned update is:
npm install --save-exact @modelcontextprotocol/server-legacy@2.3.1Use your project’s package manager and review intervening releases if coming from before 2.3.0. The legacy README still marks this package deprecated, with removal planned for v3 and no calendar deadline supplied. This patch does not replace migration planning.
Connect the option to validated token data
The merged change compares expectedResource with AuthInfo.resource returned by your verifier. It does not independently parse and validate a JWT audience. Treat configuration and verifier mapping as one change.
This illustrative configuration fragment assumes verifier is your application’s real token verifier. Merge the option into your existing configuration, preserving requiredScopes and resourceMetadataUrl where used; it is not a complete or tested server:
import { requireBearerAuth } from "@modelcontextprotocol/server-legacy/auth";
const auth = requireBearerAuth({
verifier,
expectedResource: new URL("https://api.example.com/mcp"),
});The verifier contract calls for local token validation or trusted introspection. Validate the issuer and signature as appropriate, then populate AuthInfo.resource from validated audience/resource information. Preserve client identity, scopes and expiration. Decoding a JWT without verifying it is not sufficient.
Implementation guidance: when an audience claim is an array, validate membership of the intended audience instead of blindly choosing its first entry. If the issuer uses an opaque audience identifier, establish and validate its mapping to the resource URI. RFC 8707 allows an authorization server to map resource identifiers to audience values; they are not universally identical strings.
Never copy an untrusted request parameter into AuthInfo.resource or assign the configured URL to every signature-valid token. Either shortcut would discard the distinction this comparison needs. Conversely, enabling the option while the verifier omits resource makes otherwise-valid requests fail. These are implications of the middleware’s control flow, not observed deployment results.
expectedResource identifies the intended token resource; resourceMetadataUrl points clients to a metadata document in an authentication challenge. They serve different purposes in the middleware options. Behind a reverse proxy, align the public resource identifier across discovery, issuance and validation—not an internal container URL or an unvalidated Host header. Mount authentication before every protected HTTP route.
Match the resource, not just the host
With expectedResource configured, the middleware rejects a missing or different token resource before checking scopes and expiry. The comparison serializes the URL values, removes fragments and one final slash, then checks equality. It is not a host-only or path-prefix rule. Tagged implementation.
For expectedResource = new URL("https://api.example.com/mcp"), the following expectations come from the tagged upstream tests. Assume the verifier succeeds, expiration is valid and required scopes are present. “Continues” means the middleware calls the next handler, not that every MCP operation is authorized. These tests were not run for this article.
Verifier-reported resource or configuration | Expected result |
|---|---|
https://api.example.com/mcp | Continues |
https://api.example.com/mcp/ | Continues; one final slash is ignored |
HTTPS://API.example.com:443/mcp | Continues after URL normalization |
No resource reported | 401 invalid_token |
https://other.example.com/mcp; http://api.example.com/mcp; https://api.example.com:8443/mcp | Each returns 401 invalid_token |
https://api.example.com/; https://api.example.com/mcp/tools | Each returns 401 invalid_token |
https://api.example.com/mcp?tenant=a; https://api.example.com/mcp// | Each returns 401 invalid_token |
Option unset; resource absent or different | Continues under the same otherwise-valid assumptions |
The helper tolerates fragments, but do not put them in resource identifiers: RFC 8707 prohibits fragments. Query components are discouraged, although allowed when necessary; the changed-query example above does not match the query-free expected URL.
A practical consequence: two endpoints can share an issuer and scope names yet remain distinct token destinations, provided issuance and verifier mapping preserve different resource identities. Two tenants sharing one audience do not gain tenant isolation from this option. Per-user and per-tool authorization still need their own checks.
Verify refusal paths before production
Use the matrix as a starting point for your own staging checks, then test the real issuer and client flow. Mocked verifier fixtures cannot establish identity-provider interoperability. Recommended acceptance checks:
Separate the package update from option activation so failures are easier to localize. Record resolved versions, the resource identifier and verifier-mapping configuration.
With synthetic staging identities, obtain one token intended for this resource and another for a separate test resource. Verify discovery, fresh authorization and refresh flows through the actual reverse-proxy path. Confirm an allowed MCP operation succeeds and the wrong-resource request never reaches its handler.
Check error ordering. Matching resource plus missing required scopes should yield
403 insufficient_scope; a resource mismatch plus missing scopes should yield401 invalid_token. With matching resource and sufficient scopes, expired or missing expiration should yield 401. CheckWWW-Authenticateand handler non-execution, not just response bodies. These outcomes follow the tagged legacy middleware.At the real verifier boundary, include invalid signatures, wrong issuers and tokens considered revoked by your issuer’s validation mechanism. Exercise every protected HTTP method. Keep redacted status/error evidence, not bearer tokens.
Do not promote if legitimate clients cannot obtain matching tokens or wrong-audience requests reach a handler. Diagnose issuance and verifier mapping first. Removing
expectedResourceis not an equivalent recovery unless another verified audience check remains in force.
Monitor invalid_token alongside insufficient_scope: the resource check runs first, so some failures previously classified as scope errors can become 401 responses. This is a control-flow implication, not a measured change in error rates.
If you also migrate to modern middleware, handle that separately: the migration guide warns that verifiers must throw the v2 OAuthError with OAuthErrorCode.InvalidToken. Retaining a legacy InvalidTokenError on that modern path can produce 500 instead of 401. This error-class change is not required merely to stay on legacy middleware and enable its new option.
For deployments that also use an MCP gateway, the LiteLLM 1.105 RC migration guide covers the adjacent distinction between gateway authentication and downstream permissions. Neither replaces this inbound server-audience decision.
Methodology: AI-assisted reporting and source analysis based on the official release, npm metadata, tagged code and test fixtures, migration documentation and protocol standards, checked on October 5, 2026. No package installation, compilation, test execution, token issuance or production rollout was performed. Configuration snippets and acceptance checks are illustrative; issuer-specific integration remains unverified.
