This server is one Python process exposing two ports: 8000 (MCP) and
8080 (GUI). Prometheus metrics live on 9100.
The Dockerfile produces a small image (~250 MB) with Python 3.12 slim.
make docker # builds cb-analytics-mcp:latest
docker run --rm -p 8000:8000 -p 8080:8080 -p 9100:9100 \
--env-file .env \
cb-analytics-mcp:latestA full reverse-proxy + MCP + metrics stack:
docker compose up -dThe compose file wires:
mcp: this server, reading.envand the optional cluster JSON.nginx: TLS termination on:443proxying to:8000and:8080.- (optional)
prometheus: scrapesmcp:9100.
Two unauthenticated endpoints on the GUI port (:8080 by default):
| endpoint | purpose | semantics |
|---|---|---|
/healthz |
liveness | always returns 200 with {"status": "ok"} if the process is up. Use for load-balancer health and Kubernetes livenessProbe. |
/readyz |
readiness | returns 200 only if at least one configured cluster is reachable; 503 if every cluster's ping() fails. Body lists each cluster's state. Use for Kubernetes readinessProbe so the pod is removed from rotation when its cluster is unreachable. |
Neither endpoint sets a session cookie or touches the audit log.
When TLS-terminating elsewhere, you usually want:
MCP_SERVER_URL=https://mcp.example.com
MCP_ISSUER_URL=https://mcp.example.comSo the OAuth metadata FastMCP advertises matches the URL claude.ai is hitting.
In your proxy, forward:
| upstream path | from | comment |
|---|---|---|
/mcp, /mcp/* |
claude.ai | MCP streamable HTTP endpoint |
/, /login, /query, /admin, /logs, /static, … |
browsers | GUI |
/metrics |
Prometheus | usually firewalled to internal network |
An example nginx config is in nginx/nginx.conf.
A minimal Deployment + Service looks like:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cb-analytics-mcp
spec:
replicas: 2 # stateless; safe to scale
template:
spec:
containers:
- name: mcp
image: ghcr.io/celticht32/cb-analytics-mcp:1.0.0
ports:
- { name: mcp, containerPort: 8000 }
- { name: gui, containerPort: 8080 }
- { name: metrics, containerPort: 9100 }
envFrom:
- secretRef:
name: cb-analytics-mcp-env
readinessProbe:
httpGet: { path: /readyz, port: gui }
initialDelaySeconds: 2
periodSeconds: 10
livenessProbe:
httpGet: { path: /healthz, port: gui }
initialDelaySeconds: 10
periodSeconds: 15The audit log is best treated as ephemeral inside the pod and shipped to a
log aggregator via your existing pipeline. If you need an on-disk audit,
mount a volume at AUDIT_LOG_FILE's parent directory.
-
MCP_API_KEYis generated cryptographically, ≥48 chars. -
GUI_SESSION_SECRETis a fresh 48-byte URL-safe token. -
GUI_PASSWORDis notchangeme. - Couchbase user has only the roles it needs (not Full Admin).
- TLS terminates upstream;
MCP_SERVER_URLpoints to the HTTPS URL. -
:9100(metrics) is not exposed to the public internet. - Audit log is rotated and shipped off-host.
- Container runs as non-root.
This project follows semantic versioning. Patch releases (1.0.x) never change tool signatures; minor releases (1.x.0) may add tools; major releases may remove or rename them.