Authentication (JWT/JWKS)¶
These recipes cover enabling JWT/JWKS verification on mcp-v8 and presenting tokens from various clients. See the reference page for the complete flag and header specification.
Enable JWKS verification¶
JWKS verification is optional. It activates only when --jwks-url is supplied (or the JWKS_URL environment variable is set). The flag accepts any URL that returns a JSON Web Key Set.
CLI flag:
mcp-v8 --http-port=3000 \
--jwks-url=https://idp.example.com/.well-known/jwks.json
Environment variable (useful in containers):
export JWKS_URL=https://idp.example.com/.well-known/jwks.json
mcp-v8 --http-port=3000
The server fetches keys from the JWKS endpoint at startup. If a token arrives with an unknown kid, the keys are refreshed automatically from the same URL.
Pass a token via Authorization: Bearer¶
Send the JWT as the value of the standard Authorization header on the initialize request. The Bearer prefix (including the trailing space) is required; the server strips it before verification.
TOKEN="<your-jwt>"
curl -s -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "my-agent", "version": "1.0"}
}
}'
Pass a token via agent-session header¶
As an alternative to Authorization: Bearer, supply the raw JWT in the agent-session header. No prefix is needed.
curl -s -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "agent-session: ${TOKEN}" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "my-agent", "version": "1.0"}
}
}'
If both Authorization: Bearer and agent-session are present, Authorization: Bearer takes precedence.
Use Keycloak as the JWKS issuer¶
The repository ships a pre-built Keycloak realm at keycloak/mcp-realm.json. The realm is named mcp and contains a confidential client mcp-client.
Start the stack:
docker compose -f docker-compose.secure-sessions.yml up --build -d
Configure mcp-v8 to trust Keycloak's keys:
JWKS_URL=http://keycloak:8080/realms/mcp/protocol/openid-connect/certs
The docker-compose.secure-sessions.yml file sets this automatically via an environment variable inside the container.
Acquire a client-credentials token:
TOKEN=$(curl -s \
-X POST http://localhost:8080/realms/mcp/protocol/openid-connect/token \
-d grant_type=client_credentials \
-d client_id=mcp-client \
-d client_secret=mcp-client-secret \
| jq -r .access_token)
Use the token with an MCP client (Deno/TypeScript):
import { Client } from "npm:@modelcontextprotocol/sdk@1.12.1/client/index.js";
import { StreamableHTTPClientTransport } from "npm:@modelcontextprotocol/sdk@1.12.1/client/streamableHttp.js";
import { ClientCredentialsProvider } from "./scripts/oauth-client-credentials-provider.ts";
const provider = new ClientCredentialsProvider({
tokenUrl: "http://localhost:8080/realms/mcp/protocol/openid-connect/token",
clientId: "mcp-client",
clientSecret: "mcp-client-secret",
});
const transport = new StreamableHTTPClientTransport(
new URL("http://localhost:3000/mcp"),
{
authProvider: provider,
requestInit: {
headers: { "X-MCP-Session-Id": "my-session" },
},
},
);
const client = new Client({ name: "my-agent", version: "1.0.0" });
await client.connect(transport);
The ClientCredentialsProvider fetches and caches tokens, injecting them as Authorization: Bearer on each request.
Add a session ID alongside the token¶
The JWT authenticates the connection; it is separate from the session identifier. Send X-MCP-Session-Id as an additional header on the same initialize request. Both headers are read independently:
curl -s -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-MCP-Session-Id: my-agent-session" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "my-agent", "version": "1.0"}
}
}'