MCP Integration (Model Context Protocol)
CXTMS supports the Model Context Protocol (MCP), enabling AI assistants like Claude Code to interact with your TMS data securely. MCP provides a standardized way for AI tools to access and work with your transportation management data through authenticated API calls.
Overviewβ
- Protocol: MCP (Model Context Protocol) over HTTP
- Authentication: OAuth 2.1 with PKCE (Proof Key for Code Exchange)
- Dynamic Client Registration: RFC 7591 compliant automatic client registration
- Security: Browser-based authentication with user consent
- Scopes:
mcp:read,offline_access
How It Worksβ
The MCP integration follows a secure OAuth 2.1 flow:
- Discovery: The AI assistant discovers the authorization server via protected resource metadata
- Dynamic Registration: The client automatically registers itself using RFC 7591
- Authorization: User authenticates through the browser and grants access
- Token Exchange: Authorization code is exchanged for access tokens
- API Access: The AI assistant accesses MCP endpoints using the bearer token
βββββββββββββββ ββββββββββββββββ ββββ βββββββββββ
β AI Assistantβ β Browser β β CXTMS API β
ββββββββ¬βββββββ ββββββββ¬ββββββββ ββββββββ¬βββββββ
β β β
β 1. Discovery β β
ββββββββββββββββββββββββββββββββββββββββ>β
β (/.well-known/oauth-protected-resource)
β<ββββββββββββββββββββββββββββββββββββββββ
β β β
β 2. Dynamic Client Registration β
ββββββββββββββββββββββββββββββββββββββββ>β
β (POST /connect/register) β
β<ββββββββββββββββββββββββββββββββββββββββ
β (client_id) β β
β β β
β 3. Open Browser β β
βββββββββββββββββββ>β β
β β 4. User Login β
β ββββββββββββββββββββ>β
β β<ββββββββββββββββββββ
β β (auth code) β
β<βββββββββββββββββββ β
β β β
β 5. Token Exchange β β
ββββββββββββββββββββββββββββββββββββββββ>β
β (POST /connect/token) β
β<ββββββββββββββββββββββββββββββββββββββββ
β (access_token, refresh_token) β
β β β
β 6. MCP API Calls β β
ββββββββββββββββββββββββββββββββββββββββ>β
β (Authorization: Bearer ...) β
β<ββββββββββββββββββββββββββββββββββββββββ
β (MCP responses) β
OAuth 2.1 Endpointsβ
Authorization Server Metadataβ
Endpoint: GET /.well-known/oauth-authorization-server
Returns the OAuth 2.1 authorization server metadata (RFC 8414):
{
"issuer": "https://api.example.com",
"authorization_endpoint": "https://api.example.com/connect/authorize",
"token_endpoint": "https://api.example.com/connect/token",
"registration_endpoint": "https://api.example.com/connect/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"token_endpoint_auth_methods_supported": ["none"],
"code_challenge_methods_supported": ["S256"],
"scopes_supported": ["mcp:read", "offline_access"]
}
Protected Resource Metadataβ
Endpoint: GET /.well-known/oauth-protected-resource
Returns the protected resource metadata (RFC 9728):
{
"resource": "https://api.example.com/api/mcp",
"authorization_servers": ["https://api.example.com"],
"scopes_supported": ["mcp:read", "offline_access"],
"bearer_methods_supported": ["header"]
}
Dynamic Client Registrationβ
Endpoint: POST /connect/register
Registers a new OAuth client dynamically (RFC 7591). MCP clients use this to automatically register themselves.
Request:
{
"redirect_uris": ["http://localhost:8080/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none",
"client_name": "My MCP Client"
}
Response:
{
"client_id": "mcp-a1b2c3d4e5f6...",
"redirect_uris": ["http://localhost:8080/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"client_name": "My MCP Client"
}
Validation Rules:
- At least one
redirect_uriis required - Non-localhost redirect URIs must use HTTPS
- Localhost URIs can use HTTP (for local development)
Authorization Endpointβ
Endpoint: GET /connect/authorize
Initiates the OAuth authorization flow. Users are redirected to the frontend login page if not authenticated.
Parameters:
client_id(required): The registered client IDredirect_uri(required): Must match a registered redirect URIresponse_type(required): Must becodescope(required): Space-separated list of scopescode_challenge(required): PKCE code challenge (S256)code_challenge_method(required): Must beS256state(recommended): Client state for CSRF protection
Token Endpointβ
Endpoint: POST /connect/token
Exchanges authorization codes for access tokens or refreshes existing tokens.
Authorization Code Exchange:
POST /connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE_HERE
&client_id=mcp-a1b2c3d4e5f6...
&redirect_uri=http://localhost:8080/callback
&code_verifier=ORIGINAL_CODE_VERIFIER
Token Refresh:
POST /connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=REFRESH_TOKEN_HERE
&client_id=mcp-a1b2c3d4e5f6...
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGciOiJSUzI1NiIs...",
"scope": "mcp:read offline_access"
}
MCP API Endpointβ
Endpoint: POST /api/mcp (or GET /api/mcp for SSE)
The main MCP endpoint for AI assistant communication. Requires a valid Bearer token.
Request Headers:
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Content-Type: application/json
Unauthenticated Response:
When accessing without authentication, returns 401 Unauthorized with:
WWW-Authenticate: Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"
This header enables MCP clients to discover the authentication requirements automatically.
Scopesβ
| Scope | Description |
|---|---|
mcp:read | Read access to TMS data via MCP protocol |
offline_access | Enables refresh tokens for long-lived sessions |
Claude Code Configurationβ
To connect Claude Code to your CXTMS instance, create or update your .mcp.json file:
{
"mcpServers": {
"tms": {
"type": "http",
"url": "https://your-instance.cargoxplorer.com/api/mcp"
}
}
}
For local development with self-signed certificates, add to .claude/settings.json:
{
"env": {
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}
Only disable TLS verification for local development. Never use this setting in production.
Authentication Flowβ
When Claude Code first connects:
- Claude Code discovers the authorization server from the protected resource metadata
- A browser window opens for you to log in to your CXTMS account
- After successful login, Claude Code receives access tokens
- Subsequent requests use the stored tokens (refreshed automatically)
Helm Chart Configurationβ
When deploying CXTMS with Helm, configure MCP settings in your values file:
mcp:
enabled: true
# Base URL for the MCP server endpoint (defaults to service URL)
baseUrl: ""
# OAuth 2.1 Authorization Server URL (defaults to same as baseUrl)
authorizationServerUrl: ""
# Frontend URL for OAuth redirects
# If empty, derives from request Host header (works with reverse proxy)
frontendUrl: ""
# OAuth scopes supported by the MCP server
scopes:
- "mcp:read"
- "offline_access"
# Dynamic Client Registration settings
dcr:
enabled: true
clientIdPrefix: "mcp-"
Reverse Proxy Configurationβ
When running behind a reverse proxy (nginx, Traefik, etc.), ensure:
- Routes
/api/*and/connect/*are forwarded to the backend - Routes
/.well-known/*are forwarded to the backend - All other routes go to the frontend
- The
Hostheader is preserved for correct URL generation
Example nginx configuration:
location ~ ^/(api|connect|\.well-known)/ {
proxy_pass http://backend:80;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
}
Security Considerationsβ
PKCE Requirementβ
All MCP clients must use PKCE (Proof Key for Code Exchange) with the S256 challenge method. This prevents authorization code interception attacks.
Token Securityβ
- Access tokens expire after 1 hour
- Refresh tokens enable long-lived sessions
- Tokens inherit the authenticated user's permissions
- All API calls are audited with the user context
Redirect URI Validationβ
- Localhost URIs (for CLI tools) can use HTTP
- All other redirect URIs must use HTTPS
- URIs must be registered during Dynamic Client Registration
Client Registrationβ
- Dynamically registered clients are public clients (no client secret)
- Client IDs are prefixed with
mcp-for identification - Each registration creates a unique client
Troubleshootingβ
"Incompatible auth server: does not support dynamic client registration"β
Ensure the MCP server is enabled and the registration_endpoint is advertised in the OAuth metadata:
- Check
GET /.well-known/oauth-authorization-serverreturnsregistration_endpoint - Verify
mcp.enabled: truein Helm values - Check the
/connect/registerendpoint is accessible
"The specified grant type is not supported"β
The token endpoint must support authorization_code grant type:
- Verify the OAuth server configuration includes authorization code flow
- Check the client was registered with correct grant types
Browser doesn't open for authenticationβ
- Ensure the frontend URL is correctly configured or derivable from headers
- Check the authorization endpoint redirects to the frontend
- Verify the reverse proxy forwards all required paths
Token refresh failsβ
- Ensure
offline_accessscope was requested during authorization - Check the refresh token hasn't expired
- Verify the client ID matches the original registration
API Limitsβ
| Resource | Limit |
|---|---|
| Access token lifetime | 3600 seconds (1 hour) |
| Refresh token lifetime | 30 days |
| Rate limit | 100 requests/minute per user |
| Request timeout | 30 seconds |
See Alsoβ
- API Credentials - OAuth2 Client Credentials for server-to-server access
- GraphQL API Overview - GraphQL API documentation
- Security - Security best practices