使用说明
Camel MCP Server - Secure Configuration Showcase
A standalone showcase demonstrating how to secure the existing Camel JBang MCP server with Keycloak OIDC bearer-token authentication over HTTP/SSE transport, and how to register it in Claude Code, MCP Inspector, and other coding agents.
The upstream camel-jbang-mcp runs in STDIO-only mode with no authentication. This project repackages it as an HTTP/SSE server secured by Keycloak, exposing the same tools (catalog, transform, validate, harden, kamelets, versions) behind OIDC bearer-token auth.
Prerequisites
- Java 17+
- Maven 3.9+
- Docker & Docker Compose (for Keycloak)
curlandjq(for testing)camel-jbang-mcpinstalled in local Maven repo (mvn installfrom the Camel source tree)
Quick Start (Dev Mode)
Quarkus Dev Services can auto-start a Keycloak container:
mvn quarkus:dev
This starts the MCP server on port 8080 and automatically provisions a Keycloak instance on port 8180 with the pre-configured realm.
Production Mode
1. Start Keycloak
docker-compose up -d
Wait for Keycloak to be ready (admin console at http://localhost:8180, credentials: admin/admin).
2. Build and run the MCP server
mvn clean package -DskipTests
java -jar target/camel-mcp-secure-demo-1.0.0-SNAPSHOT-runner.jar
The server starts on http://localhost:8080.
Obtaining Access Tokens
Password grant (as alice - mcp-user role)
export TOKEN=$(curl -s -X POST \
http://localhost:8180/realms/camel-mcp/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=mcp-client" \
-d "username=alice" \
-d "password=alice" | jq -r '.access_token')
Password grant (as bob - mcp-admin role)
export TOKEN=$(curl -s -X POST \
http://localhost:8180/realms/camel-mcp/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=mcp-client" \
-d "username=bob" \
-d "password=bob" | jq -r '.access_token')
Client credentials grant (service-to-service)
export TOKEN=$(curl -s -X POST \
http://localhost:8180/realms/camel-mcp/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=mcp-server" \
-d "client_secret=mcp-server-secret" | jq -r '.access_token')
Inspect a token
echo $TOKEN | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .
Testing with curl
Verify 401 without token
curl -v http://localhost:8080/mcp/sse
# Returns 401 Unauthorized
Establish SSE connection with token
curl -N -H "Authorization: Bearer $TOKEN" http://localhost:8080/mcp/sse
Invoke a tool via MCP protocol
# First establish an SSE connection to get the messages endpoint,
# then POST a tool call:
curl -X POST http://localhost:8080/mcp/messages/1 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "camel_catalog_components",
"arguments": {
"filter": "kafka",
"label": "",
"limit": 5,
"runtime": "main",
"camelVersion": ""
}
}
}'
Registering with Claude Code
Option 1: CLI with SSE transport
# Get a token (access token lifespan is 1 hour in this demo)
export TOKEN=$(curl -s -X POST \
http://localhost:8180/realms/camel-mcp/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password&client_id=mcp-client&username=alice&password=alice" \
| jq -r '.access_token')
claude mcp add -t sse \
camel-mcp-secure http://localhost:8080/mcp/sse \
-H "Authorization: Bearer $TOKEN"
Option 2: CLI with Streamable HTTP transport
claude mcp add -t http \
camel-mcp-secure http://localhost:8080/mcp \
-H "Authorization: Bearer $TOKEN"
Option 3: .mcp.json (project-level)
Create a .mcp.json file in your project root:
{
"mcpServers": {
"camel-mcp-secure": {
"type": "sse",
"url": "http://localhost:8080/mcp/sse",
"headers": {
"Authorization": "Bearer <YOUR_TOKEN_HERE>"
}
}
}
}
Option 4: Claude Desktop via mcp-remote bridge
For Claude Desktop, which only supports stdio transport, use the mcp-remote npm package:
{
"mcpServers": {
"camel-mcp-secure": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8080/mcp/sse",
"--header",
"Authorization: Bearer <YOUR_TOKEN_HERE>"
]
}
}
}
Testing token revocation with Claude Code
Once the MCP server is registered, you can verify that revoking the token causes Claude Code to lose access:
# 1. Register with a valid token (see options above)
# 2. Verify it works — run Claude Code and invoke a tool, e.g.:
# "list camel components related to kafka"
# Claude Code should successfully call camel_catalog_components
# 3. Revoke the token
curl -s -X POST \
http://localhost:8180/realms/camel-mcp/protocol/openid-connect/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=mcp-client" \
-d "token=$TOKEN"
# 4. Re-run Claude Code and try the same tool — it should fail with 401
# 5. To restore access, get a new token and re-register:
export TOKEN=$(curl -s -X POST \
http://localhost:8180/realms/camel-mcp/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password&client_id=mcp-client&username=alice&password=alice" \
| jq -r '.access_token')
claude mcp remove camel-mcp-secure
claude mcp add -t sse \
camel-mcp-secure http://localhost:8080/mcp/sse \
-H "Authorization: Bearer $TOKEN"
Note: Quarkus OIDC validates JWT tokens offline using cached JWKS keys by default. If revocation does not take effect immediately, you can force it by logging out all sessions for the user via the Keycloak admin API:
# Get admin token ADMIN_TOKEN=$(curl -s -X POST \ http://localhost:8180/realms/master/protocol/openid-connect/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password&client_id=admin-cli&username=admin&password=admin" \ | jq -r '.access_token') # Find alice's user ID and logout all sessions ALICE_ID=$(curl -s \ http://localhost:8180/admin/realms/camel-mcp/users?username=alice \ -H "Authorization: Bearer $ADMIN_TOKEN" | jq -r '.[0].id') curl -s -X POST \ http://localhost:8180/admin/realms/camel-mcp/users/$ALICE_ID/logout \ -H "Authorization: Bearer $ADMIN_TOKEN"
Registering with MCP Inspector
- Open MCP Inspector
- Select SSE transport type
- Enter URL:
http://localhost:8080/mcp/sse - In the Headers section, add:
- Key:
Authorization - Value:
Bearer <YOUR_TOKEN_HERE>
- Key:
- Click Connect
Available Tools
These are the existing tools from camel-jbang-mcp, now secured behind OIDC auth:
| Tool | Description |
|---|---|
camel_catalog_components |
List Camel components (filter by name, label, runtime) |
camel_catalog_component_doc |
Get detailed docs for a specific component |
camel_catalog_dataformats |
List Camel data formats |
camel_catalog_dataformat_doc |
Get detailed docs for a data format |
camel_catalog_languages |
List Camel expression languages |
camel_catalog_language_doc |
Get detailed docs for a language |
camel_catalog_eips |
List Camel EIPs (Enterprise Integration Patterns) |
camel_catalog_eip_doc |
Get detailed docs for an EIP |
camel_catalog_kamelets |
List Camel Kamelets |
camel_catalog_kamelet_doc |
Get detailed docs for a Kamelet |
camel_validate_route |
Validate a Camel endpoint URI or YAML route |
camel_validate_yaml_dsl |
Validate YAML DSL against the JSON schema |
camel_transform_route |
Transform routes between YAML, XML, and Java DSL |
camel_route_context |
Get enriched context (component + EIP docs) for a route |
camel_route_harden_context |
Get security hardening analysis for a route |
camel_version_list |
List available Camel versions by runtime |
Users and Roles
| User | Password | Roles | Notes |
|---|---|---|---|
alice |
alice |
mcp-user |
Standard user |
bob |
bob |
mcp-admin |
Admin user |
Keycloak admin console: http://localhost:8180 (credentials: admin/admin).
Security Flow
Client MCP Server (Quarkus) Keycloak
| | |
|-- POST /token ------------->| |
| | |
|<--- access_token -----------| |
| | |
|-- GET /mcp/sse ------------>| |
| Authorization: Bearer xxx | |
| |-- validate token ----------->|
| |<-- token claims -------------|
| | |
| |-- check @Authenticated |
| | |
|<--- SSE stream ------------| |
| | |
|-- POST /mcp/messages/1 --->| |
| Authorization: Bearer xxx | |
| { tool call } |-- validate token ----------->|
| |<-- token claims -------------|
| | |
|<--- tool result ------------| |
How It Works
This project depends on org.apache.camel:camel-jbang-mcp as a library. The existing MCP tool classes (CatalogTools, TransformTools, HardenTools, KameletTools, VersionTools, ExplainTools) are @ApplicationScoped CDI beans with @Tool annotations — Quarkus discovers them automatically from the dependency.
The key configuration changes from the upstream:
| Setting | Upstream (STDIO) | This Showcase (HTTP + OIDC) |
|---|---|---|
quarkus.http.host-enabled |
false |
true (via POM property) |
| Transport | quarkus-mcp-server-stdio |
quarkus-mcp-server-http |
| Authentication | None | Keycloak OIDC bearer tokens |
| HTTP auth policies | None | /mcp/sse, /mcp/messages/*, /mcp |
| Port | N/A | 8080 |
| Config format | application.properties |
application.yaml |
Why YAML? The upstream camel-jbang-mcp jar bundles its own application.properties at the classpath root. In a Quarkus uber-jar, this can shadow our file. Using application.yaml avoids the filename collision entirely — Quarkus loads both files and YAML properties take precedence.
The build-time property quarkus.http.host-enabled=true is set in the POM <properties> section, which has the highest precedence during Quarkus augmentation.
Project Structure
camel-jbang-mcp-secure-conf/
├── .gitignore
├── README.md
├── docker-compose.yml # Keycloak 26.0 on port 8180
├── keycloak/
│ └── camel-mcp-realm.json # Pre-configured realm, clients, users, roles
├── pom.xml # Depends on camel-jbang-mcp + quarkus-oidc
└── src/main/resources/
└── application.yaml # HTTP + OIDC config (YAML to avoid collision)
No custom Java code is needed — the tools come from the camel-jbang-mcp dependency.