Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Local Docker Stack

docker/docker-compose.yml brings up the full control-plane + dataplane stack locally and drives an MCP session through it against the bundled fast_time_server test backend. This is separate from the cf-integration harness (see Testing) — use it for a quick local smoke test of the built dataplane image.

Quick Start

make docker-prod
make testing-up

make docker-prod builds dataplane:latest from docker/Dockerfile; testing-up refuses to start if this image doesn’t exist yet. testing-up starts: nginx, control-plane, redis, postgres, pgbouncer, data-plane, fast_time_server, register_fast_time (see Makefile).

Resource budget for this stack (docker/docker-compose.yml deploy.resources):

ServiceCPU LimitMem Limit
nginx0.5256 M
control-plane22 G
redis11 G
postgres22 G
pgbouncer0.5256 M
data-plane22 G
fast_time_server1512 M
Total9 cores8 G

register_fast_time is a one-shot init container (no steady-state resources): it logs into control-plane, registers fast_time_server as an upstream gateway, and creates a virtual server with a fixed id (b8e3f1a2c4d5e6f7a1b2c3d4e5f6a7b8) exposing all of its tools.

Watch it finish:

docker compose -f docker/docker-compose.yml logs -f register_fast_time

Look for Fast Time Server registration complete!.

Config Propagation

The control plane doesn’t talk to the dataplane directly — it publishes UserConfig snapshots into Redis (DATAPLANE_PUBLISHER=true), and the dataplane reads/caches from Redis. Worst-case propagation delay:

publisher interval + dataplane user-config cache expiry

Both default to ~60s in this stack (see Control-Plane Integration). After register_fast_time finishes, give it up to a minute before the virtual host resolves on the dataplane side.

Get A Bearer Token

Data-plane can self-issue a test JWT (RS256, signed with the same assets/jwt.key the control plane also signs with) — no need to shell into any container:

TOKEN=$(curl --silent --show-error --request GET \
  --url http://localhost:8080/contextforge-rs/admin/tokens/admin@example.com \
  --header 'accept: application/json')

Use admin@example.com — that’s PLATFORM_ADMIN_EMAIL, the identity register_fast_time used, so it’s the subject the publisher’s UserConfig is keyed under. Token is valid for 1 hour.

Point mcp-inspector At It

npx @modelcontextprotocol/inspector
FieldValue
URLhttp://localhost:8080/contextforge-rs/servers/b8e3f1a2c4d5e6f7a1b2c3d4e5f6a7b8/mcp
TransportStreamable HTTP
Auth token$TOKEN from the previous step

Note the /contextforge-rs prefix — that’s what routes to the dataplane via nginx (docker/nginx.conf, location ^~ /contextforge-rs). Without it, the request goes to control-plane instead and you’ll get a {"detail": "..."}-shaped error from mcpgateway, not the dataplane.

Run tools/list — you should get back the fast_time_server tools (prefixed with the gateway name, e.g. fast_time-<tool_name>).

Tear Down

make testing-down

Stops the stack (containers/volumes kept; rerun testing-up to resume).

Troubleshooting

SymptomCauseFix
403 {"detail":"Token is invalid: User is no longer a member of the associated team"}Hit control-plane instead of dataplane — URL missing /contextforge-rs prefix.Use /contextforge-rs/servers/<id>/mcp.
400 "Problem occurred retrieving the configuration"Dataplane has no UserConfig yet for the token’s subject (register_fast_time/publisher hasn’t run or synced).Re-check config propagation above; confirm register_fast_time logs completed successfully and wait out the publisher interval.
tools/list returns {"tools": [], "resultType": "complete"}Dataplane resolved the UserConfig fine, but couldn’t connect to a declared backend.docker compose logs data-plane | grep -i "worker quit|BadScheme|backend". A BadScheme error means CONTEXTFORGE_GATEWAY_RS_UPSTREAM_CONNECTION_MODE isn’t set to plain-text-or-tls for a plain-http:// backend.
401 on every requestBad/expired token, or control-plane and dataplane are signing/verifying with different keys or algorithms.Confirm both sides have matching JWT_ALGORITHM/key config — see Control-Plane Integration.