coreX MCP Server
The mcp-server container is a standalone MCP (Model Context Protocol) server that exposes the full coreX Platform backend REST API as MCP tools. It speaks MCP spec 2025-11-25 over Streamable HTTP, so any MCP-compatible AI agent (Devin, Claude Code, Cursor, Windsurf, Continue, or a custom client) can connect and manage the platform.
The server auto-discovers tools by introspecting the backend FastAPI app’s routes — one MCP tool per API endpoint. Tools are executed in-process via an ASGI transport with a service admin JWT, so all route logic, validation, audit middleware, and rate limiting run exactly as they would for a normal HTTP request.
AI Agent
│
│ MCP over Streamable HTTP (POST /mcp)
│ Authorization: Bearer <COREX_MCP_TOKEN> (optional)
▼
┌───────────────────────────────────────────────────┐
│ mcp-server (Python, :8082) │
│ │
│ JSON-RPC dispatch: │
│ ├── initialize / ping │
│ ├── tools/list → discover_tools() │
│ ├── tools/call → call_tool() → ASGI transport │
│ ├── resources/list → 7 curated resources │
│ ├── resources/read → ASGI transport │
│ ├── prompts/list → 6 workflow prompts │
│ └── prompts/get → render prompt template │
│ │
│ GET /healthz → liveness │
│ GET /.well-known/oauth-protected-resource │
└──────────────────────┬────────────────────────────┘
│ httpx ASGITransport (in-process)
│ Authorization: Bearer <service admin JWT>
│ X-MCP-Service-Token: <MCP_SERVICE_TOKEN> (rate-limit bypass)
▼
┌───────────────────────────────────────────────────┐
│ backend (FastAPI app) │
│ All v1 routes run exactly as a normal HTTP call: │
│ auth, validation, audit, rate limit, config gen │
└───────────────────────────────────────────────────┘
Why a Separate Server?
The MCP server is a thin adapter that wraps the existing REST API. It doesn’t reimplement any business logic — it maps JSON-RPC calls to in-process HTTP requests against the backend. This means:
- No duplicated logic — every tool call hits the real backend with full validation, audit logging, and middleware
- Automatic coverage — new backend endpoints appear as MCP tools automatically (no manual tool definitions)
- Full API access — all 339+ v1 endpoints are exposed (minus file-upload endpoints, which can’t be represented as JSON)
Tool Discovery
Tools are auto-generated at startup by tools.discover_tools(), which introspects app.main:app.routes:
- Walk all routes — recursively walks the FastAPI app’s route tree, handling
_IncludedRouterwrappers (FastAPI 0.141+ sub-routers) - Skip non-API routes — excludes
/openapi.json,/docs,/redoc,/healthz, static mounts, andHEAD/OPTIONSmethods - Generate one tool per method — each
APIRoute+ HTTP method becomes a tool - Derive tool name — from the endpoint function name, stripping
_endpoint/_routesuffixes. If the same name appears for different routes, the HTTP method is appended (e.g.list_backends_get,list_backends_post) - Build input schema — from the route’s dependant:
- Path params → required string properties
- Query params → typed properties (type inferred from default value;
PydanticUndefinedTypesentinel treated as no default) - Body params → a
bodyproperty with a JSON Schema from the Pydantic model’smodel_json_schema()(sanitized viajson.dumps(default=str)to coerce non-serializable objects)
- Skip file uploads — endpoints with
UploadFilebody params are skipped (can’t be represented as JSON)
The tool list is cached after first discovery and pre-warmed at startup so the first tools/list is fast.
The current backend exposes 339+ MCP tools. The exact count changes as new endpoints are added — call tools/list to see the current set.
Tool Execution
When an agent calls tools/call, the server:
- Looks up the tool by name in the cached list
- Substitutes path params — replaces
{param}placeholders in the URL path with values from the arguments - Collects query params — from arguments (excluding path params and body)
- Extracts body — from the
bodyargument if the tool has a body param - Sets headers:
Authorization: Bearer <service admin JWT>— a 24-hour admin JWT minted viacreate_access_token({"sub": "admin"}), cached for 20 hoursX-MCP-Service-Token: <MCP_SERVICE_TOKEN>— if set, bypasses per-user rate limits (see below)
- Executes in-process — via
httpx.AsyncClient(transport=httpx.ASGITransport(app=app))with a 60-second timeout - Returns the result — JSON responses are pretty-printed; non-JSON responses are returned as text. Errors (HTTP >= 400) are prefixed with the status code and flagged with
isError: true
Rate-Limit Bypass
In-process MCP calls would be rate-limited like any other API request, which is problematic for agents making many rapid tool calls. To avoid this:
- Set
MCP_SERVICE_TOKENto a shared secret - The MCP server sends it as the
X-MCP-Service-Tokenheader on every tool call - The backend’s
rate_limit()dependency checks this header and bypasses per-user limits when it matches - The backend’s
get_current_user()dependency also skips the Valkey token-revocation check for service calls (the service admin JWT is never revoked) and uses a cached user lookup to avoid a per-request DB query
Resources
The server exposes 7 curated read-only MCP resources, each mapping to a backend GET endpoint. Resources are fetched in-process via the same ASGI transport as tools.
| URI | Name | Description | Backend Path |
|---|---|---|---|
corex://config/preview | config-preview | Preview the generated HAProxy config without applying it | /api/v1/config/preview |
corex://config/status | config-status | Current config apply status (applied vs pending changes) | /api/v1/config/status |
corex://config/snapshots | config-snapshots | List of config snapshots (applied config history) | /api/v1/config/snapshots |
corex://system/stats | system-stats | System stats (HAProxy process info, uptime, connections) | /api/v1/stats |
corex://system/haproxy-stats | haproxy-stats | HAProxy stats (frontend/backend/server metrics) | /api/v1/haproxy-stats |
corex://audit-events | audit-events | Recent audit events (config mutations, auth events) | /api/v1/audit-events |
corex://health | health | Backend health check | /api/v1/health |
Resources are read-only — agents can fetch them via resources/read but cannot modify them. Use tools for mutations.
Prompts
The server exposes 6 MCP prompts. The primary prompt is corex-manager-guide, which returns the full operating guide. The other 5 are workflow-specific prompts that guide an agent through common tasks.
| Prompt | Description | Arguments |
|---|---|---|
corex-manager-guide | Full operating guide for coreX Manager via MCP tools | none |
diagnose-traffic | Diagnose traffic or routing issues | symptom (required) |
security-review | Review the current security configuration | none |
add-backend | Guided workflow to add a new backend pool with servers | name (required), servers (required) |
waf-investigation | Investigate WAF events and tune rules | concern (optional) |
apply-and-verify | Apply pending config changes and verify the result | comment (optional) |
corex-manager-guide
The corex-manager-guide prompt is the primary skill-delivery mechanism. Any MCP-compatible client can fetch it via prompts/get and inject the operating instructions into its conversation, making the skill fully agent-agnostic. The guide covers:
- Tool naming (bare names for direct connections,
corex-manager__prefix via the gateway) - Key workflows (inspect state, make changes, WAF/security, rollback)
- Safety rules (confirm before destructive ops, always apply config, check dependencies)
- Available resources and other prompts
The guide content is mirrored in:
.devin/skills/corex-manager/SKILL.md— for Devin CLI auto-discoverydocs/mcp-skill.md— portable guide with install snippets for Claude Code, Cursor, Windsurf, Continue, and generic MCP clients
Authentication
The MCP server supports optional bearer token authentication:
- If
COREX_MCP_TOKENis set, direct connections must includeAuthorization: Bearer <token> - If unset, the endpoint is open (the gateway is the gatekeeper when used via the gateway)
When connecting directly to the MCP server (:8082/mcp), set COREX_MCP_TOKEN to require authentication. When connecting via the gateway (:8089/mcp), the gateway handles authentication (PAT/JWT) and forwards calls to the MCP server with its own service token — COREX_MCP_TOKEN is not used in this path.
Self-Registration
When the MCP gateway is enabled, the backend automatically registers the MCP server into the gateway’s database on startup. This is handled by ensure_self_registration() in backend/app/services/mcp_self_register.py, called from the main.py lifespan.
Prerequisites
Self-registration runs only when all three conditions are met:
| Condition | Setting | Default |
|---|---|---|
| Gateway enabled | MCP_GATEWAY_ENABLED | false |
| Self-registration enabled | MCP_SELF_REGISTER | true |
| Secrets key configured | MCP_SECRETS_KEY | (none) |
If MCP_SECRETS_KEY is not set, self-registration is skipped with a warning (the gateway needs it to encrypt the server’s auth secret).
Registration Steps
The registration is idempotent — it runs on every startup and only creates/updates what’s missing or changed:
- Ensure “platform” team — creates a
Teamwith slugplatformif it doesn’t exist - Ensure McpServer row — creates an
McpServerwith:- Namespace:
corex-manager - URL:
http://<MCP_SERVER_INTERNAL_HOST>:<MCP_SERVER_INTERNAL_PORT>/mcp(e.g.http://mcp-server:8082/mcp) - Transport:
streamable_http - Auth type:
bearer(header:Authorization) - Auth secret:
COREX_MCP_TOKENenv var, or a randomly generated 32-byte token - Timeout: 60000ms, max body: 10MB
- Enabled: true
- If the server already exists, it updates the URL and auth secret if they’ve changed (e.g. if
COREX_MCP_TOKENwas rotated)
- Namespace:
- Ensure McpSkill + published version — creates an
McpSkillnamedcorex-managerwith:- Tags:
haproxy,waf,load-balancer,corex - A published
McpSkillVersioncontaining the skill guide (frontmatter + body) - A new version is created only if the content has changed (compared by body text and frontmatter)
- Tags:
- Regenerate config bundle — calls
write_config_bundle(db)so the gateway picks up the new server and skill - Trigger catalog refresh — calls
trigger_background_catalog_refresh([server.id])with retries every 5 seconds for up to 1 minute (the mcp-server may still be starting when the control plane first tries; this avoids waiting for the gateway’s 60-second refresh cycle)
What This Means for Users
After enabling the gateway for the first time:
- A “platform” team appears in Management > MCP Gateway > Teams
- The
corex-managerserver appears in Servers with a healthy catalog - The
corex-managerskill appears in Skills (published) - Agents connecting via the gateway can immediately call
corex-manager__*tools - The
corex-manager-guideprompt is available viaprompts/get
No manual server registration is needed for the coreX Platform’s own tools.
Disabling Self-Registration
To disable self-registration (e.g. in a multi-instance deployment where only one instance should register):
MCP_SELF_REGISTER=false
The previously-registered server and skill remain in the database; only the automatic registration/update is skipped.
Environment Variables
| Variable | Default | Description |
|---|---|---|
COREX_MCP_TOKEN | (none) | Bearer token for direct connections. If set, direct clients must include Authorization: Bearer <token>. If unset, the endpoint is open. Also used as the auth secret when self-registering into the gateway. |
MCP_SERVICE_TOKEN | (none) | Shared secret for rate-limit bypass on in-process MCP calls. Sent as X-MCP-Service-Token header. |
MCP_SELF_REGISTER | true | Auto-register the coreX Manager MCP server into the gateway on startup |
MCP_SERVER_INTERNAL_HOST | mcp-server | Hostname the backend uses to reach the MCP server (for self-registration URL) |
MCP_SERVER_INTERNAL_PORT | 8082 | Port the backend uses to reach the MCP server |
Deployment
The MCP server runs as a separate container in the stack:
- Image: built from
mcp-server/Dockerfile(Python 3.14-slim) - Port: 8082
- Dependencies:
postgres,valkey,api(the backend must be running for tool discovery) - Volumes: mounts
backend/app/at runtime (shares the same code as the api container for in-process imports) and thehaproxy-datavolume (read-only access to config files) - PYTHONPATH:
/app(sofrom app...andfrom shared...work) - Command:
uvicorn server:app --host 0.0.0.0 --port 8082
On Kubernetes, the MCP server runs as a sidecar in the same pod as the API and HAProxy, communicating over localhost.
Connecting Directly (Without the Gateway)
You can connect an AI agent directly to the MCP server, bypassing the gateway. This skips policy enforcement, DLP, guardrails, and rate limiting, but is simpler for single-agent setups.
Devin CLI
devin mcp add corex-manager http://localhost:8082/mcp
The skill file at .devin/skills/corex-manager/SKILL.md is auto-discovered by Devin CLI. Invoke with /corex-manager [task].
Claude Code
claude mcp add corex-manager http://localhost:8082/mcp
To load the skill guidance, call prompts/get with corex-manager-guide, or copy the operating guide from docs/mcp-skill.md into .claude/commands/corex-manager.md.
Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"corex-manager": {
"url": "http://localhost:8082/mcp"
}
}
}
Paste the operating guide from docs/mcp-skill.md into .cursor/rules/corex-manager.mdc.
Windsurf
windsurf mcp add corex-manager http://localhost:8082/mcp
Continue / Generic MCP Client
Connect to http://localhost:8082/mcp. To load the skill guidance, call prompts/get with corex-manager-guide.
With Authentication
If COREX_MCP_TOKEN is set, include it in the connection:
{
"mcpServers": {
"corex-manager": {
"url": "http://localhost:8082/mcp",
"headers": {
"Authorization": "Bearer <your-corex-mcp-token>"
}
}
}
}
Connecting Via the Gateway
When connecting through the gateway (http://localhost:8089/mcp):
- Tools appear with the
corex-manager__prefix (e.g.corex-manager__list_backends) - The skill appears as the
corex-managerprompt - The gateway applies policies, DLP, guardrails, and rate limiting
- Authentication is via PAT or JWT (see MCP Gateway > Identities)
See MCP Gateway for the full gateway documentation.
Verification
-
Check the server is running:
curl http://localhost:8082/healthzExpect:
{"status":"ok","server":"corex-manager","version":"1.0.0"} -
Check OAuth metadata:
curl http://localhost:8082/.well-known/oauth-protected-resourceExpect: RFC 9728 metadata with the resource URL.
-
List tools:
curl -X POST http://localhost:8082/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Expect: a JSON-RPC response with 339+ tools.
-
Call a tool:
curl -X POST http://localhost:8082/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_listeners","arguments":{}}}'Expect: a JSON-RPC response with the listener list.
-
Read a resource:
curl -X POST http://localhost:8082/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":3,"method":"resources/read","params":{"uri":"corex://config/status"}}'Expect: a JSON-RPC response with the config status.
-
Get the skill guide:
curl -X POST http://localhost:8082/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":4,"method":"prompts/get","params":{"name":"corex-manager-guide"}}'Expect: a JSON-RPC response with the full operating guide.
-
Verify self-registration (gateway enabled):
- Navigate to Management > MCP Gateway > Servers
- Verify the
corex-managerserver appears with a healthy catalog - Navigate to Skills
- Verify the
corex-managerskill is published
Next Steps
- MCP Gateway — Gateway with policies, DLP, guardrails, and multi-server support
- Settings — Enable the
mcp_gateway_enabledfeature flag - Architecture — MCP gateway architecture diagram
- Quick Start — MCP environment variables