This document is the operator's guide to running GitPilot with the optional MCP Context Forge stack in production. It complements install-mcp.md (which targets developers).
The stack is strictly additive: enabling it never changes how
GitPilot core behaves. Disabling it (GITPILOT_MCP_ENABLED=false) is a
single env-var flip that returns the system to its baseline shape.
make install # uv + npm + MCP image cache (skip-safe without Docker)
make run # Forge + 3 reference MCP servers + GitPilot core
make smoke-mcp # post-deploy health sweep
make sync-mcp # mirror Forge's registry into GitPilot's local store ┌─────────────────────────────┐
│ GitPilot core (port 8000) │
│ - mcp_forge_sync │
│ - mcp_tools_bridge │
│ - admin REST /api/mcp/* │
└──────────────┬──────────────┘
│ HTTP
┌──────────────▼──────────────┐
│ MCP Context Forge (4444) │
│ - registry of MCP servers │
│ - tool dispatcher │
└─┬────────┬───────────────┬──┘
│ │ │
┌──────────▼─┐ ┌───▼──────┐ ┌─────▼──────┐
│ postgre │ │ inspector│ │ milvus │
│ :8080 │ │ :8081 │ │ :8082 │
└────────────┘ └──────────┘ └────────────┘
All four MCP services live under the Compose mcp profile in
docker-compose.mcp.yml; default docker compose up ignores them.
| Requirement | Why |
|---|---|
| Docker Engine 24+ with Compose v2 | --profile, healthcheck-gated depends_on, multi-stage builds |
git |
install-mcp.sh clones the four upstream repos |
Outbound HTTPS to github.com |
first-time clone of the four upstreams |
| Outbound to Docker Hub or a private registry | only if you switch to image pulls (see "Pinning to releases") |
| 4 GB free disk | the four images + Postgres data volume |
| 2 GB free RAM (4 GB with Milvus profile) | runtime |
| Two free TCP ports per service (4444, 8080, 8081, 8082, 5432 internal) | overridable via .mcp.env |
git pull
make install # idempotent; safe on already-running hosts
make runThe first make run builds four images (3-8 minutes on a warm
broadband link). Subsequent runs reuse the build cache.
make smoke-mcp # (./scripts/smoke-mcp.sh; exits 1 on any failure)What it checks:
- GitPilot
/api/pingis reachable. - Each MCP service
/healthreturns 200. /api/mcp/statusreportsgateway_reachable=true.POST /api/mcp/syncreturns a structuredSyncReport(noforge_unreachable=true)./api/mcp/agent_toolsreturns a well-formed JSON envelope.
Add --milvus to also smoke the milvus sub-profile:
bash scripts/smoke-mcp.sh --milvus.
make sync-mcp # POST /api/mcp/sync; prints SyncReportYou can also click Sync in the Settings → MCP Servers tab; both paths invoke the same endpoint.
In Settings → MCP Servers, for each server:
- Toggle Enable (servers land disabled by default — opt-in).
- Expand the tool list, tick the safe (
low) tools you want. - Mutation tools (
highrisk) stay locked unless you also flipGITPILOT_MCP_REQUIRE_APPROVAL_FOR_MUTATIONS=falseand mint a separate mutation token.
The Coder agent now sees those tools in its toolbox the same way Claude Code sees its built-ins.
git pull
MCP_UPDATE=1 MCP_BUILD=1 make install-mcp # fetch pinned refs and force image rebuild
make run # starts updated MCP stack + GitPilot
make smoke-mcpmake stop-mcp
sed -i "s|MCP_AUTH_TOKEN=.*|MCP_AUTH_TOKEN=$(openssl rand -hex 32)|" .mcp.env
make run-mcp
make smoke-mcpmake logs-mcp # tail -f all four services
docker compose -f docker-compose.mcp.yml logs mcp-postgre-servermake stop-mcpmake uninstall-mcp # prompts y/N; removes containers, volumes, imagesOnce Docker Hub publish workflows have run (see
extensions/mcp_workflows/README.md)
and tags exist for each image, you have two ways to pin to a known good
release:
Option A — keep build-from-source, pin the git ref
In .mcp.env:
MCP_FORGE_REF=v1.2.3
MCP_POSTGRE_REF=v1.0.0
MCP_MILVUS_REF=v0.3.1
MCP_INSPECTOR_REF=v0.1.0Then make install-mcp && make run-mcp rebuilds against those pinned
checkouts.
Option B — pull pre-built images (faster, no build step)
Edit docker-compose.mcp.yml and replace each service's
build: { context: ./mcp-stack/<repo> } with
image: ruslanmv/<image>:vX.Y.Z. The generated workflows publish
multi-arch (linux/amd64, linux/arm64) so this works on Apple
Silicon too.
| Symptom | Diagnosis | Recovery |
|---|---|---|
make smoke-mcp reports gateway_reachable=false |
Forge container not running or not reachable from GitPilot | docker compose -f docker-compose.mcp.yml ps; make logs-mcp |
forge_unreachable=true from /api/mcp/sync |
GitPilot can reach /api/ping but not Forge |
Check GITPILOT_MCP_GATEWAY_URL and the Forge container's port mapping |
Workflow rejected with workflow scope error |
PAT lacks workflow scope when pushing CI files |
Mint a PAT with repo + workflow and re-run GH_PAT_WORKFLOW=… make install-mcp-workflows |
manifest unknown during compose pull |
A pinned image tag doesn't exist on the registry yet | Either build-from-source (default) or wait for the publish workflow run |
port already in use on 4444 / 8080 / etc. |
Conflict with another local service | Override MCP_FORGE_PORT etc. in .mcp.env |
| CRLF errors on WSL | Windows Git checked the scripts out as CRLF | make fix-line-endings && make install |
npm run build fails inside an inspector image rebuild |
An upstream changed the strict tsconfig flags | Fixed in 200238b+; pull, then make run-mcp |
| Source | Where |
|---|---|
| GitPilot structured logs | gitpilot/api.py writes to stderr; journald / Docker default |
| MCP sync correlation IDs | every /api/mcp/sync response carries one; same id appears in the GitPilot log line mcp forge sync completed |
| Per-tool call tracing | gitpilot/mcp_tools_bridge.py emits one log entry per remote call with X-Gitpilot-Origin: agent |
| Forge access logs | make logs-mcp (the four containers' stdout) |
| MCP Servers tab | gateway health pill, per-server status dot, tool count |
.mcp.envis git-ignored. Never commit; tokens are minted at install time and rotated by re-runningmake install-mcp.- Mutation tools (
highrisk) are locked off by default and require bothGITPILOT_MCP_REQUIRE_APPROVAL_FOR_MUTATIONS=falseand a separate mutation token. X-Gitpilot-Origin: selfrequests are rejected by the GitPilot MCP server — protects against agent self-call loops.- Recursive sync is disabled: GitPilot pulls from Forge, never pushes.
- The Postgres container in the MCP stack is read-only by default
(
POSTGRES_READ_ONLY=true,POSTGRES_BLOCK_DDL=true,POSTGRES_BLOCK_DML=true). - Forge bearer token is required on every authenticated call; missing
token returns
401, wrong scope returns403, recursion returns409.
Each MCP server repo carries .github/workflows/docker-publish.yml
which:
- triggers on a published GitHub Release or manual
workflow_dispatch; - builds multi-arch (
linux/amd64,linux/arm64) via QEMU + Buildx; - tags
<image>:<semver>,<image>:latest,<image>:sha-<short>,<image>:{{major}}.{{minor}}; - applies OCI labels (title, description, source, version, revision, created, license);
- uses GHA cache scoped per service for fast rebuilds;
- runs an import smoke for Python services / a real
/healthround-trip for the inspector; - prints a summary with the published tags and pull commands.
Required org/repo secrets:
| Secret | Scope |
|---|---|
DOCKERHUB_USERNAME |
account that owns the images |
DOCKERHUB_TOKEN |
Docker Hub access token, write scope |
A single env-var flip returns the system to baseline:
echo "GITPILOT_MCP_ENABLED=false" >> .env
make stop-mcp
# 'make run' still works; the MCP Servers tab shows "plugin disabled"The codebase paths are guarded so no MCP traffic ever leaves the process when the flag is off.