Skip to content

Commit 2e546e5

Browse files
feat: Add MCP Inspector compatibility to Keycloak OAuth example
Enable MCP Inspector support for the Keycloak OAuth example by addressing three key compatibility requirements: 1. CORS Configuration (server.py): - Add CORSMiddleware with proper headers for browser-based clients - Expose mcp-session-id header required for stateful HTTP transport - Configure allowed headers: mcp-protocol-version, mcp-session-id, Authorization - Reference: https://gofastmcp.com/deployment/http#cors-for-browser-based-clients 2. Trusted Hosts (realm-fastmcp.json): - Add github.com to trusted hosts for MCP Inspector origin - Enable dynamic client registration from Inspector's GitHub-hosted UI 3. Offline Access Support (realm-fastmcp.json): - Add defaultOptionalClientScopes: ["offline_access"] - Grant offline_access realm role to test user - Enable long-lived refresh tokens for Inspector sessions Additional improvements: - Remove FileTokenStorage in favor of automatic retry mechanism - Disable audience validation by default (Keycloak doesn't include aud claim) - Simplify realm configuration (minimal scopes: openid, profile, email, offline_access) - Add comprehensive MCP Inspector documentation to README - Update troubleshooting guide with Inspector-specific restart instructions - Document Docker Compose v2 syntax and realm configuration reload process Both Python client and MCP Inspector now work seamlessly with Keycloak OAuth.
1 parent de97d8a commit 2e546e5

File tree

5 files changed

+81
-18
lines changed

5 files changed

+81
-18
lines changed

examples/auth/keycloak_auth/README.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,41 @@ Manually import the realm:
4949
python server.py
5050
```
5151

52-
3. In another terminal, run the client:
52+
3. Test the server:
53+
54+
You have two options to test the OAuth-protected server:
55+
56+
**Option A: Using the Python Client (Programmatic)**
57+
58+
In another terminal, run the example client:
5359

5460
```bash
5561
python client.py
5662
```
5763

58-
The client will open your browser for Keycloak authentication.
64+
The client will open your browser for Keycloak authentication, then demonstrate calling the protected tools.
65+
66+
**Option B: Using MCP Inspector (Interactive)**
67+
68+
The MCP Inspector provides an interactive web UI to explore and test your MCP server.
69+
70+
**Prerequisites**: Node.js must be installed on your system.
71+
72+
1. Launch the Inspector:
73+
```bash
74+
npx -y @modelcontextprotocol/inspector
75+
```
76+
77+
2. In the Inspector UI (opens in your browser):
78+
- Click "Add Server"
79+
- Enter server URL: `http://localhost:8000/mcp`
80+
- Select connection type: **"Direct"** (connects directly to the HTTP server)
81+
- Click "Connect"
82+
- The Inspector will automatically handle the OAuth flow and open Keycloak for authentication
83+
- Once authenticated, you can interactively explore available tools and test them
84+
85+
The Inspector is particularly useful for:
86+
- Exploring the server's capabilities without writing code
87+
- Testing individual tools with custom inputs
88+
- Debugging authentication and authorization issues
89+
- Viewing request/response details

examples/auth/keycloak_auth/keycloak/README.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,33 @@ This script will:
2323

2424
## Preconfigured Realm
2525

26-
The Docker setup automatically imports a preconfigured realm configured for dynamic client registration. The default settings are described below and can be adjusted or complemented as needed by editing the [`realm-fastmcp.json`](realm-fastmcp.json) file before starting Keycloak. If settings are changed after Keycloak has been started, restart Keycloak with
26+
The Docker setup automatically imports a preconfigured realm configured for dynamic client registration. The default settings are described below and can be adjusted or complemented as needed by editing the [`realm-fastmcp.json`](realm-fastmcp.json) file before starting Keycloak.
27+
28+
### Updating Realm Configuration
29+
30+
If you modify the `realm-fastmcp.json` file after Keycloak has been started, you need to recreate the container to apply the changes:
2731

2832
```bash
29-
docker-compose restart
33+
docker compose down -v # Stop and remove volumes
34+
docker compose up -d # Start fresh with updated config
3035
```
3136

32-
to apply the changes.
37+
**Note**: The `-v` flag removes the volumes, which forces Keycloak to re-import the realm configuration. Without it, Keycloak will skip the import with "Realm already exists."
38+
39+
**Expected Warning**: You may see this warning in the logs during realm import:
40+
```
41+
Failed to deserialize client policies in the realm fastmcp. Fallback to return empty profiles.
42+
```
43+
This is a harmless Keycloak parser issue with the JSON format and doesn't affect functionality. The realm and policies are imported correctly.
3344

3445
### Realm: `fastmcp`
3546

3647
The realm is configured with:
3748

3849
- **Dynamic Client Registration** enabled for `http://localhost:8000/*`
3950
- **Registration Allowed**: Yes
40-
- **Allowed Client Scopes**: `openid`, `profile`, `email`, `roles`, `offline_access`, `web-origins`, `basic`
41-
- **Trusted Hosts**: `localhost`, `172.17.0.1`, `172.18.0.1`
51+
- **Allowed Client Scopes**: `openid`, `profile`, `email`, `offline_access`
52+
- **Trusted Hosts**: `localhost`, `172.17.0.1`, `172.18.0.1`, `github.com` (allows MCP Inspector and other GitHub-hosted clients)
4253

4354
### Test User
4455

@@ -91,7 +102,7 @@ For production use, consider:
91102
### View Keycloak Logs
92103

93104
```bash
94-
docker-compose logs -f keycloak
105+
docker compose logs -f keycloak
95106
```
96107

97108
### Common Issues
@@ -115,5 +126,5 @@ docker-compose logs -f keycloak
115126

116127
4. **"Client not found" error after Keycloak restart**
117128
- This can happen when Keycloak is restarted and the previously registered OAuth client no longer exists
118-
- **No action needed**: The FastMCP OAuth client automatically detects this condition and re-registers with Keycloak
119-
- Simply run your client again and it will automatically handle the re-registration process
129+
- **Python client**: No action needed - the FastMCP client automatically detects this condition and re-registers with Keycloak. Simply run your client again and it will handle the re-registration process.
130+
- **MCP Inspector**: Stop the Inspector with Ctrl+C in the terminal where you started it, then restart it with `npx -y @modelcontextprotocol/inspector` and reconnect to the server. This triggers a fresh OAuth flow and client registration.

examples/auth/keycloak_auth/keycloak/realm-fastmcp.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"enabled": true,
55
"keycloakVersion": "26.3.5",
66
"registrationAllowed": true,
7+
"defaultOptionalClientScopes": ["offline_access"],
78
"components": {
89
"org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [
910
{
@@ -18,7 +19,8 @@
1819
"trusted-hosts": [
1920
"localhost",
2021
"172.17.0.1",
21-
"172.18.0.1"
22+
"172.18.0.1",
23+
"github.com"
2224
],
2325
"client-uris-must-match": [
2426
"true"
@@ -41,7 +43,8 @@
4143
"value": "password123",
4244
"temporary": false
4345
}
44-
]
46+
],
47+
"realmRoles": ["offline_access"]
4548
}
4649
],
4750
"clientPolicies": {
@@ -57,10 +60,7 @@
5760
"openid",
5861
"profile",
5962
"email",
60-
"roles",
61-
"offline_access",
62-
"web-origins",
63-
"basic"
63+
"offline_access"
6464
]
6565
}
6666
}

examples/auth/keycloak_auth/keycloak/start-keycloak.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,7 @@ echo ""
8484
echo "Useful commands:"
8585
echo " • Check Keycloak logs: docker logs -f keycloak-fastmcp"
8686
echo " • Stop Keycloak: $DOCKER_COMPOSE_DISPLAY down"
87-
echo " • Restart Keycloak: $DOCKER_COMPOSE_DISPLAY restart"
87+
echo " • Reload realm config: $DOCKER_COMPOSE_DISPLAY down -v && $DOCKER_COMPOSE_DISPLAY up -d"
88+
echo ""
89+
echo "⚠️ Note: To apply changes to realm-fastmcp.json, you must stop and remove volumes:"
90+
echo " $DOCKER_COMPOSE_DISPLAY down -v && $DOCKER_COMPOSE_DISPLAY up -d"

examples/auth/keycloak_auth/server.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import os
1818

1919
from dotenv import load_dotenv
20+
from starlette.middleware import Middleware
21+
from starlette.middleware.cors import CORSMiddleware
2022

2123
from fastmcp import FastMCP
2224
from fastmcp.server.auth.providers.keycloak import KeycloakAuthProvider
@@ -75,7 +77,23 @@ async def get_access_token_claims() -> dict:
7577

7678
if __name__ == "__main__":
7779
try:
78-
mcp.run(transport="http", port=8000)
80+
# Enable CORS for MCP Inspector and other browser-based clients
81+
# See: https://gofastmcp.com/deployment/http#cors-for-browser-based-clients
82+
cors_middleware = Middleware(
83+
CORSMiddleware,
84+
allow_origins=["*"], # Allow all origins for development
85+
allow_credentials=True,
86+
allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
87+
allow_headers=[
88+
"mcp-protocol-version",
89+
"mcp-session-id",
90+
"Authorization",
91+
"Content-Type",
92+
],
93+
expose_headers=["mcp-session-id"], # Required for MCP Inspector
94+
)
95+
96+
mcp.run(transport="http", port=8000, middleware=[cors_middleware])
7997
except KeyboardInterrupt:
8098
# Graceful shutdown, suppress noisy logs resulting from asyncio.run task cancellation propagation
8199
pass

0 commit comments

Comments
 (0)