When Should You Use Authorization?
While authorization for MCP servers is optional, it is strongly recommended when:- Your server accesses user-specific data (emails, documents, databases)
- You need to audit who performed which actions
- Your server grants access to its APIs that require user consent
- You’re building for enterprise environments with strict access controls
- You want to implement rate limiting or usage tracking per user
Authorization for Local MCP ServersFor MCP servers using the STDIO transport, you can use environment-based credentials or credentials provided by third-party libraries embedded directly in the MCP server instead. Because a STDIO-built MCP server runs locally, it has access to a range of flexible options when it comes to acquiring user credentials that may or may not rely on in-browser authentication and authorization flows.OAuth flows, in turn, are designed for HTTP-based transports where the MCP server is remotely-hosted and the client uses OAuth to establish that a user is authorized to access said remote server.
The Authorization Flow: Step by Step
Let’s walk through what happens when a client wants to connect to your protected MCP server:1
Initial Handshake
When your MCP client first tries to connect, your server responds with a This tells the client that authorization is required for the MCP server and where to get the necessary information to kickstart the authorization flow.
401 Unauthorized
and tells the client where to find authorization information, captured in a Protected Resource Metadata (PRM) document. The document is hosted by the MCP server, follows a predictable path pattern, and is provided to the client in the resource_metadata
parameter within the WWW-Authenticate
header.2
Protected Resource Metadata Discovery
With the URI pointer to the PRM document, the client will fetch the metadata to learn about the authorization server, supported scopes, and other resource information. The data is typically encapsulated in a JSON blob, similar to the one below.You can see a more comprehensive example in RFC 9278 Section 3.2.
3
Authorization Server Discovery
Next, the client discovers what the authorization server can do by fetching its metadata. If the PRM document lists more than one authorization server, the client can decide which one to use.With an authorization server selected, the client will then construct a standard metadata URI and issue a request to the OpenID Connect (OIDC) Discovery or OAuth 2.0 Auth Server Metadata endpoints (depending on authorization server support)
and retrieve another set of metadata properties that will allow it to know the endpoints it needs to complete the authorization flow.
4
Client Registration
With all the metadata out of the way, the client now needs to make sure that it’s registered with the authorization server. This can be done in two ways.First, the client can be pre-registered with a given authorization server, in which case it can have embedded client registration information that it uses to complete the authorization flow.Alternatively, the client can use Dynamic Client Registration (DCR) to dynamically register itself with the authorization server. The latter scenario requires the authorization server to support DCR. If the authorization server does support DCR, the client will send a request to the If the registration succeeds, the authorization server will return a JSON blob with client registration information.
registration_endpoint
with its information:No DCR or Pre-RegistrationIn case a MCP client connects to a MCP server that doesn’t use an authorization server that supports DCR and the client is not pre-registered with said authorization server, it’s the responsibility of the client developer to provide an affordance for the end-user to enter client information manually.
5
User Authorization
The client will now need to open a browser to the The access token is what the client will use to authenticate requests to the MCP server. This step follows standard OAuth 2.1 authorization code with PKCE conventions.
/authorize
endpoint, where the user can log in and grant the required permissions. The authorization server will then redirect back to the client with an authorization code that the client exchanges for tokens:6
Making Authenticated Requests
Finally, the client can make requests to your MCP server using the access token embedded in the The MCP server will need to validate the token and process the request if the token is valid and has the required permissions.
Authorization
header:Implementation Example
To get started with a practical implementation, we will use a Keycloak authorization server hosted in a Docker container. Keycloak is an open-source authorization server that can be easily deployed locally for testing and experimentation. Make sure that you download and install Docker Desktop. We will need it to deploy Keycloak on our development machine.Keycloak Setup
From your terminal application, run the following command to start the Keycloak container:8080
and have an admin
user with admin
password.
Not for ProductionThe configuration above may be suitable for testing and experimentation; however, you should never use it in production. Refer to the Configuring Keycloak for production guide for additional details on how to deploy the authorization server for scenarios that require reliability, security, and high availability.
http://localhost:8080
.

mcp:tools
scope. We will use this to access all of the tools on our MCP server.

mcp:tools
client scope and click on Mappers, followed by Configure a new mapper. Select Audience.

audience-config
. Add a value for Included Custom Audience, set to http://localhost:3000
. This will be the URI of our test server.
Not for ProductionThe audience configuration above is meant for testing. For production scenarios, additional set-up and configuration will be required to ensure that audiences are properly constrained for issued tokens. Specifically, the audience needs to be based on the resource parameter passed from the client, not a fixed value.
ifconfig
command on Linux or macOS, or ipconfig
on Windows. You can see the IP address you need to add by looking at the keycloak logs for a line that looks like Failed to verify remote host : 192.168.215.1
. Check that the IP address is associated with your host. This may be for a bridge network depending on your docker setup.

Getting the HostIf you are running Keycloak from a container, you will also be able to see the host IP from the Terminal in the container logs.
- Go to Clients.
- Click Create client.
- Give your client a unique Client ID and click Next.
- Enable Client authentication and click Next.
- Click Save.

Handling SecretsNever embed client credentials directly in your code. We recommend using environment variables or specialized solutions for secret storage.
Embedded AudienceNotice the
aud
claim embedded in the token - it’s currently set to be the URI of the test MCP server and it’s inferred from the scope that we’ve previously configured. This will be important in our implementation to validate.MCP Server Setup
We will now set up our MCP server to use the locally-running Keycloak authorization server. Depending on your programming language preference, you can use one of the supported MCP SDKs. For our testing purposes, we will create an extremely simple MCP server that exposes two tools - one for addition and another for multiplication. The server will require authorization to access these.- TypeScript
- Python
- C#
You can see the complete TypeScript project in the sample repository.Prior to running the code below, ensure that you have a When you run the server, you can add it to your MCP client, such as Visual Studio Code, by providing the MCP server endpoint.For more details about implementing MCP servers in TypeScript, refer to the TypeScript SDK documentation.
.env
file with the following content:OAUTH_CLIENT_ID
and OAUTH_CLIENT_SECRET
are associated with the MCP server client we created earlier.In addition to implementing the MCP authorization specification, the server below also does token introspection via Keycloak to make sure that the token it receives from the client is valid. It also implements basic logging to allow you to easily diagnose any issues.Testing the MCP Server
For testing purposes, we will be using Visual Studio Code, but any client that supports MCP and the new authorization specification will fit. Press Cmd + Shift + P and select MCP: Add server…. Select HTTP and enterhttp://localhost:3000
. Give the server a unique name to be used inside Visual Studio Code. In mcp.json
you should now see an entry like this:
mcp:tools
scope.

mcp.json
.

#
sign in the chat view.

Common Pitfalls and How to Avoid Them
For comprehensive security guidance, including attack vectors, mitigation strategies, and implementation best practices, make sure to read through Security Best Practices. A few key issues are called out below.- Do not implement token validation or authorization logic by yourself. Use off-the-shelf, well-tested, and secure libraries for things like token validation or authorization decisions. Doing everything from scratch means that you’re more likely to implement things incorrectly unless you are a security expert.
- Use short-lived access tokens. Depending on the authorization server used, this setting might be customizable. We recommend to not use long-lived tokens - if a malicious actor steals them, they will be able to maintain their access for longer periods.
- Always validate tokens. Just because your server received a token does not mean that the token is valid or that it’s meant for your server. Always verify that what your MCP server is getting from the client matches the required constraints.
- Store tokens in secure, encrypted storage. In certain scenarios, you might need to cache tokens server-side. If that is the case, ensure that the storage has the right access controls and cannot be easily exfiltrated by malicious parties with access to your server. You should also implement robust cache eviction policies to ensure that your MCP server is not re-using expired or otherwise invalid tokens.
- Enforce HTTPS in production. Do not accept tokens or redirect callbacks over plain HTTP except for
localhost
during development. - Least-privilege scopes. Don’t use catch‑all scopes. Split access per tool or capability where possible and verify required scopes per route/tool on the resource server.
- Don’t log credentials. Never log
Authorization
headers, tokens, codes, or secrets. Scrub query strings and headers. Redact sensitive fields in structured logs. - Separate app vs. resource server credentials. Don’t reuse your MCP server’s client secret for end‑user flows. Store all secrets in a proper secret manager, not in source control.
- Return proper challenges. On 401, include
WWW-Authenticate
withBearer
,realm
, andresource_metadata
so clients can discover how to authenticate. - DCR (Dynamic Client Registration) controls. If enabled, be aware of constraints specific to your organization, such as trusted hosts, required vetting, and audited registrations. Unauthenticated DCR means that anyone can register any client with your authorization server.
- Multi‑tenant/realm mix-ups. Pin to a single issuer/tenant unless explicitly multi‑tenant. Reject tokens from other realms even if signed by the same authorization server.
- Audience/resource indicator misuse. Don’t configure or accept generic audiences (like
api
) or unrelated resources. Require the audience/resource to match your configured server. - Error detail leakage. Return generic messages to clients, but log detailed reasons with correlation IDs internally to aid troubleshooting without exposing internals.
- Session identifier hardening. Treat
Mcp-Session-Id
as untrusted input; never tie authorization to it. Regenerate on auth changes and validate lifecycle server‑side.
Related Standards and Documentation
MCP authorization builds on these well-established standards:- OAuth 2.1: The core authorization framework
- RFC 8414: Authorization Server Metadata discovery
- RFC 7591: Dynamic Client Registration
- RFC 9728: Protected Resource Metadata
- RFC 8707: Resource Indicators