MCP Authorization with Dynamic Client Registration Christian Posta published a bonus blog post implementing MCP Authorization with Dynamic Client Registration, building an MCP client that follows RFC 7591 to automatically discover and register OAuth clients with MCP servers. The implementation enables plug-and-play interoperability between MCP clients and servers by handling HTTP 401 responses and parsing WWW-Authenticate headers. MCP Authorization With Dynamic Client Registration This is a bonus post following on from my Understanding MCP Authorization three part series covering building and understanding an MCP HTTP based server and implementing the MCP Authorization spec 2025-06-18 https://modelcontextprotocol.io/specification/2025-06-18/changelog . In the previous series, we built the server side of the spec, leaving the client side up to the reader since obtaining OAuth clients is usually fairly opinionated in enterprise environments. The MCP Authorization spec actually has opinions https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization dynamic-client-registration about how MCP clients and thus, OAuth clients should be created. The idea behind the spec is to allow MCP clients and servers to be “plug and play” automatically https://aws.amazon.com/blogs/opensource/open-protocols-for-agent-interoperability-part-2-authentication-on-mcp/ . That is, allow any MCP client to automatically discover what it needs to connect to an MCP server. In this blog post, we implement an MCP client with Dynamic Client Registration for the OAuth client. This series of blog posts three parts + source code https://github.com/christian-posta/mcp-auth-step-by-step , walks “step-by-step” through the latest MCP Authorization spec and implement it. I have made all of the source code for each of the steps available on GitHub https://github.com/christian-posta/mcp-auth-step-by-step . - Part 1: Implement a spec compliant remote MCP server with HTTP Transport https://blog.christianposta.com/understanding-mcp-authorization-step-by-step/ - Part 2: Layer in Authorization specification with OAuth 2.1 https://blog.christianposta.com/understanding-mcp-authorization-step-by-step-part-two/ - Part 3: Bring in a production Identity Provider Keycloak https://blog.christianposta.com/understanding-mcp-authorization-step-by-step-part-three/ Follow @christianposta https://x.com/christianposta or /in/ceposta https://linkedin.com/in/ceposta for the next parts. Building an MCP Client Follow along with the source code for this step https://github.com/christian-posta/mcp-auth-step-by-step/blob/main/src/mcp http/step11.py . The MCP client we build for this blog will focus on Dynamic Client Registration following RFC 7591 https://datatracker.ietf.org/doc/html/rfc7591 . The process starts when an MCP client makes a request for a resource it is not authenticated for HTTP 401 . In that case, the MCP server would return a header WWW-Authenticate to help the MCP client figure out how to authenticate. From the spec: MCP servers MUST use the HTTP header WWW-Authenticate when returning a 401 Unauthorized to indicate the location of the resource server metadata URL as described in RFC9728 Section 5.1 “WWW-Authenticate Response”. MCP clients MUST be able to parse WWW-Authenticate headers and respond appropriately to HTTP 401 Unauthorized responses from the MCP server. In our implementation in step10 https://github.com/christian-posta/mcp-auth-step-by-step/blob/main/src/mcp http/step11.py , we built the MCP server to return a 401 and Header: 1 WWW-Authenticate: Bearer realm="mcp-server", resource metadata="http://localhost:9000/.well-known/oauth-protected-resource" Note this part in the value of the header: resource metadata=”http://localhost:9000/.well-known/oauth-protected-resource . The MCP client will then request the oauth-protected-resource metadata following RFC 9728 https://datatracker.ietf.org/doc/html/rfc9728 . In our MCP server, it looks like this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "resource": "http://localhost:9000", "authorization servers": "http://localhost:8080/realms/mcp-realm" , "scopes supported": "echo-mcp-server-audience", "mcp:read", "mcp:tools", "mcp:prompts" , "bearer methods supported": "header" , "resource documentation": "http://localhost:9000/docs", "mcp protocol version": "2025-06-18", "resource type": "mcp-server" } A number of interesting points here. The authorization servers is where the client should look for how to connect to the Authorization Server AS . The scopes supported is how the MCP server tells its clients what scopes it will need to make calls. Interestingly this will be ALL of the scopes needed to access all parts of the MCP server. When the MCP client registers an OAuth client, it should use these scopes. It’s on the AS to determine which users have which roles and what scopes will actually appear in their tokens. When the MCP client initiates an authorization code flow, it should use these scopes. The next step is to call the Authorization Server’s AS metadata resource based on what was in the authorization servers list. The MCP client will append /.well-known/oauth-authorization-server to the value of authorization servers to discover the endpoints for authorization, client registration, etc on the Authorization Server AS . In this case, the metadata is located here: 1 http://localhost:8080/realms/mcp-realm/.well-known/oauth-authorization-server In our example, since we are using Keycloak, you’ll note the /realms/