---
title: "03: Dynamic Client Registration"
source: Panaversity Learn Agentic AI
source_url: https://github.com/panaversity/learn-agentic-ai
licence: Apache-2.0
domain: agentic-ai
subdomain: panaversity-agentic
date_added: 2026-04-25
---

# 03: Dynamic Client Registration

## What You Will Learn in This Step

This step demonstrates **Dynamic Client Registration** as defined in [RFC 7591](https://datatracker.ietf.org/doc/rfc7591/), completing the OAuth 2.1 discovery and registration process needed for MCP authentication.

### Why Dynamic Client Registration Matters for MCP

The [MCP Authorization specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization.md) emphasizes Dynamic Client Registration because:

- **Seamless MCP Discovery**: AI agents may discover new MCP servers dynamically and need to register automatically
- **No Manual Setup**: Users shouldn't need to pre-register every possible MCP client
- **Scalability**: Enables the **DACA vision** of 10 million concurrent agents registering with servers automatically
- **Real-world AI Workflows**: Agents can connect to new MCP servers without human intervention

### Building on Previous Steps:
1. **Step 01:** Discovered the Authorization Server URL from the MCP server
2. **Step 02:** Queried the Authorization Server to discover its endpoints

Now we implement **Step 03**: Dynamic client registration with Keycloak using Initial Access Tokens.

---

## Learning Objectives

After completing this module, you will be able to:
1. Implement Dynamic Client Registration according to RFC 7591
2. Understand the complete 3-stage OAuth preparation process
3. Use Initial Access Tokens for secure client registration
4. Handle client credentials and validate registration responses

## Standards Compliance

This implementation follows these specifications:
- **[MCP Authorization](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization.md)** - Primary MCP OAuth specification
- [RFC 7591 - OAuth 2.0 Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/rfc7591/)
- [OAuth 2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/)
- [OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html)

### MCP Specification Requirements

According to the MCP spec:
> "MCP clients and authorization servers **SHOULD** support the OAuth 2.0 Dynamic Client Registration Protocol [RFC7591] to allow MCP clients to obtain OAuth client IDs without user interaction."

## Prerequisites

- Completed Step 01 (Protected Resource Metadata Discovery)
- Completed Step 02 (Authorization Server Metadata Discovery)
- Basic understanding of OAuth 2.1 client registration concepts

## Core Concepts

### What is OAuth Dynamic Client Registration? (For Beginners)

Think of Dynamic Client Registration like **automatically getting a library card**:
- **Traditional way**: You manually fill out forms, wait for approval, get a card number
- **Dynamic way**: You show up with ID, machine reads it, instantly issues a card

In OAuth terms:
- **Traditional**: Admin manually creates `client_id` and `client_secret` in advance
- **Dynamic**: Client software automatically registers itself and gets credentials instantly

### 1. Dynamic Client Registration (RFC 7591)
- **Purpose**: Allows OAuth clients to register with Authorization Servers programmatically
- **Why Important**: Essential for AI agents that discover new MCP servers at runtime
- **Security**: Uses Initial Access Tokens to control who can register clients
- **Flow**: Client metadata → Registration endpoint → Client credentials

### 2. Initial Access Token (IAT)
- **What it is**: A special "permission slip" that authorizes dynamic client registration
- **Real-world analogy**: Like a guest pass that lets you register for a conference
- **Creation**: Generated by admins through Keycloak admin console
- **Usage**: Included in Authorization header during registration
- **Security**: Has configurable expiration and usage limits

### 3. Registration Process (The Complete Journey)
1. **Discovery**: Client discovers `registration_endpoint` from AS metadata (from Step 02)
2. **Preparation**: Client assembles metadata about itself (name, redirect URIs, scopes)
3. **Request**: Client POSTs metadata with Initial Access Token to prove it's authorized
4. **Validation**: Authorization Server checks token and metadata
5. **Response**: AS validates and returns `client_id`, `client_secret`
6. **Storage**: Client securely stores credentials for future OAuth flows

### 4. MCP-Specific Considerations
- **Resource Parameter**: Clients must request scopes specific to MCP servers (`mcp:read`, `mcp:write`)
- **Audience Binding**: Tokens will be bound to specific MCP server URLs (covered in Step 06)
- **AI Agent Context**: Registration happens programmatically without human intervention

## Implementation Guide

### File Structure
```
03_dynamic_client_registration/
└── open_source/keycloak/
    ├── client.py              # Complete registration client
    ├── server.py              # MCP server with OAuth discovery
    ├── docker-compose.yml     # Keycloak configuration
    ├── realm-export.json      # Keycloak realm setup
    └── pyproject.toml         # Python dependencies
```

### Running the Demo

#### 1. Start Keycloak
```bash
cd open_source/keycloak
docker-compose up -d

# Wait for Keycloak to start
docker-compose logs -f keycloak
```

#### 2. Start MCP Server
```bash
# In the same directory
uvicorn server:app --reload --port 8000
```

#### 3. Create Initial Access Token
1. **Go to**: http://localhost:9000/admin
2. **Login**: admin / admin123
3. **Select realm**: mcp-oauth
4. **Navigate to**: Clients → Client registration → Initial access tokens
5. **Click**: Create
6. **Set**: Expiration: 3600 seconds, Count: 10
7. **Copy the token immediately**

#### 4. Run Registration Client
```bash
# With environment variable
INITIAL_ACCESS_TOKEN="your-token-here" uv run client.py

# Or interactive mode
uv run client.py
```

### Expected Output

The client demonstrates the complete 3-stage OAuth preparation process required by MCP:

#### 🔍 **Stage 1**: MCP Protected Resource Metadata Discovery ([RFC 9728](https://datatracker.ietf.org/doc/rfc9728/))
```
📡 Making unauthenticated request to MCP server: http://localhost:8000/mcp
❌ HTTP 401 Unauthorized (expected!)
📋 Fetching MCP metadata: http://localhost:8000/.well-known/oauth-protected-resource
✅ Successfully retrieved MCP server metadata
📍 Authorization Server discovered: http://localhost:9000/realms/mcp-oauth
```

#### 🔍 **Stage 2**: Authorization Server Metadata Discovery ([RFC 8414](https://datatracker.ietf.org/doc/rfc8414/))
```
📋 Fetching Keycloak metadata: http://localhost:9000/.well-known/oauth-authorization-server
✅ Successfully retrieved Keycloak metadata
🔐 Authorization Endpoint: http://localhost:9000/realms/mcp-oauth/protocol/openid-connect/auth
🎫 Token Endpoint: http://localhost:9000/realms/mcp-oauth/protocol/openid-connect/token
📝 Registration Endpoint: http://localhost:9000/realms/mcp-oauth/clients-registrations/openid-connect
```

#### 📝 **Stage 3**: Dynamic Client Registration ([RFC 7591](https://datatracker.ietf.org/doc/rfc7591/))
```
📤 Sending registration request with Initial Access Token
✅ Registration Status: SUCCESS
🆔 Client ID: cf285ac3-70a8-417d-9ec4-c8b88b4f5bd8
🔐 Client Secret: ******************** (hidden for security)
📍 Redirect URIs: http://localhost:8888/callback, http://127.0.0.1:8888/callback
🎭 Scopes: mcp:write openid offline_access mcp:read
```

**What just happened?** Your MCP client successfully:
1. ✅ **Discovered** where to get authorization (Steps 01-02 knowledge)
2. ✅ **Registered** itself with the Authorization Server
3. ✅ **Received** credentials to use in future OAuth flows (Step 04)

### Sample Registration Request
```json
{
  "client_name": "MCP OAuth Demo Client - Step 03",
  "client_uri": "http://localhost:8888",
  "redirect_uris": [
    "http://localhost:8888/callback",
    "http://127.0.0.1:8888/callback"
  ],
  "grant_types": ["authorization_code", "client_credentials"],
  "response_types": ["code"],
  "scope": "openid mcp:read mcp:write offline_access",
  "token_endpoint_auth_method": "client_secret_post",
  "application_type": "web"
}
```

### Sample Registration Response
```json
{
  "client_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "client_secret": "supersecret123...",
  "client_id_issued_at": 1234567890,
  "redirect_uris": [
    "http://localhost:8888/callback",
    "http://127.0.0.1:8888/callback"
  ],
  "grant_types": ["authorization_code", "client_credentials"],
  "scope": "openid mcp:read mcp:write offline_access"
}
```

## Keycloak Configuration

### Admin Console Access
- **URL**: http://localhost:9000/admin
- **Username**: admin
- **Password**: admin123

### Pre-configured Resources
- **Realm**: mcp-oauth
- **User**: mcpuser / password123
- **Client**: mcp-demo-client (for testing)
- **Scopes**: openid, mcp:read, mcp:write, offline_access

## Troubleshooting

### Common Issues

1. **401 Unauthorized**
   - **Cause**: Initial Access Token is missing, expired, or invalid
   - **Solution**: Create a new token in Keycloak admin console

2. **403 Forbidden**
   - **Cause**: Client Registration Policies are blocking the request
   - **Solution**: Use Initial Access Token or check Keycloak policies

3. **MCP Server Not Found**
   - **Cause**: MCP server is not running
   - **Solution**: Start with `uvicorn server:app --reload --port 8000`

4. **Keycloak Not Accessible**
   - **Cause**: Keycloak is not running or still starting
   - **Solution**: Check with `docker-compose logs keycloak`

## Security Best Practices

1. **Initial Access Token Management**
   - Store tokens securely (environment variables)
   - Set appropriate expiration times
   - Monitor token usage

2. **Client Credentials Protection**
   - Store `client_secret` securely
   - Never log or expose credentials
   - Use HTTPS in production

3. **Redirect URI Validation**
   - Specify exact redirect URIs
   - Never use wildcards
   - Validate all URIs match expected patterns

## Next Steps

With client registration complete, **Step 04** will implement the **Authorization Code Flow**:

1. **Authorization Request**: Direct user to Keycloak login
2. **User Authentication**: User logs in with credentials
3. **Authorization Code**: Keycloak redirects with code
4. **Token Exchange**: Exchange code for access token
5. **API Access**: Use token to call protected MCP endpoints

The `client_id` and `client_secret` from this step are essential for Step 04.

## Resources

- [RFC 7591 - OAuth 2.0 Dynamic Client Registration](https://datatracker.ietf.org/doc/rfc7591/)
- [OpenID Connect Dynamic Client Registration](https://openid.net/specs/openid-connect-registration-1_0.html)
- [Keycloak Documentation](https://www.keycloak.org/documentation)
- [OAuth 2.1 Security Best Practices](https://datatracker.ietf.org/doc/draft-ietf-oauth-security-topics/)
