Skip to main content

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:

  1. Discovery: The AI assistant discovers the authorization server via protected resource metadata
  2. Dynamic Registration: The client automatically registers itself using RFC 7591
  3. Authorization: User authenticates through the browser and grants access
  4. Token Exchange: Authorization code is exchanged for access tokens
  5. 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_uri is 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 ID
  • redirect_uri (required): Must match a registered redirect URI
  • response_type (required): Must be code
  • scope (required): Space-separated list of scopes
  • code_challenge (required): PKCE code challenge (S256)
  • code_challenge_method (required): Must be S256
  • state (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​

ScopeDescription
mcp:readRead access to TMS data via MCP protocol
offline_accessEnables 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"
}
}
caution

Only disable TLS verification for local development. Never use this setting in production.

Authentication Flow​

When Claude Code first connects:

  1. Claude Code discovers the authorization server from the protected resource metadata
  2. A browser window opens for you to log in to your CXTMS account
  3. After successful login, Claude Code receives access tokens
  4. 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:

  1. Routes /api/* and /connect/* are forwarded to the backend
  2. Routes /.well-known/* are forwarded to the backend
  3. All other routes go to the frontend
  4. The Host header 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:

  1. Check GET /.well-known/oauth-authorization-server returns registration_endpoint
  2. Verify mcp.enabled: true in Helm values
  3. Check the /connect/register endpoint is accessible

"The specified grant type is not supported"​

The token endpoint must support authorization_code grant type:

  1. Verify the OAuth server configuration includes authorization code flow
  2. Check the client was registered with correct grant types

Browser doesn't open for authentication​

  1. Ensure the frontend URL is correctly configured or derivable from headers
  2. Check the authorization endpoint redirects to the frontend
  3. Verify the reverse proxy forwards all required paths

Token refresh fails​

  1. Ensure offline_access scope was requested during authorization
  2. Check the refresh token hasn't expired
  3. Verify the client ID matches the original registration

API Limits​

ResourceLimit
Access token lifetime3600 seconds (1 hour)
Refresh token lifetime30 days
Rate limit100 requests/minute per user
Request timeout30 seconds

See Also​