--- since: 1.1.3 --- # gitaiflow MCP — Docker Verification This is the canonical Docker verification procedure for v1.1.3. The procedure assumes a developer workspace containing **multiple independent Git repositories**. The parent workspace is mounted at `/workspace`; the individual repository selected by MCP must have its own `.git` directory. Do not use older mixed Docker commands from historical documentation. Follow this document in order. --- ## 1. What Docker is proving Docker adds a new runtime boundary: ```mermaid flowchart TB Host["Developer machine"] Workspace["Host parent workspace"] Container["Docker container"] MCP["MCP server"] AOT["/app/bin/gitaiflow"] Repo["Selected Git repository"] Config["$HOME/.gitaiflow"] Workspace -->|bind mount| Container Config -->|bind mount| Container Container --> MCP MCP --> AOT AOT --> Repo ``` For example, the host may contain: ```text /Users//pyc/missionmq/gl/ ├── gitaiflow/.git ├── djangoplay-cli/.git ├── astwire/.git ├── issuetracker/.git └── ... ``` and Docker exposes it as: ```text /workspace/ ├── gitaiflow/.git ├── djangoplay-cli/.git ├── astwire/.git ├── issuetracker/.git └── ... ``` `/workspace` is the **allowed workspace container**. It does not need to be a Git repository itself. The selected path must be a real Git repository root. For example: ```text path="gitaiflow" ↓ /workspace/gitaiflow ↓ Git root = /workspace/gitaiflow ``` The Docker test is not complete merely because the container starts. The required sequence is: ```text host parent workspace ↓ Docker image ↓ container ↓ AOT binary ↓ repository mount ↓ selected Git repository ↓ health ↓ direct AOT ↓ MCP protocol ↓ Inspector ``` --- ## 2. Confirm Docker architecture Check the image architecture: ```bash docker run --rm gitaiflow-mcp:local uname -m ``` Typical results: ```text aarch64 ``` or: ```text x86_64 ``` Use the matching Linux AOT binary. For ARM64: ```text installers/AOT/v1.1.3/gitaiflow-linux-arm64 ``` For AMD64: ```text installers/AOT/v1.1.3/gitaiflow-linux-amd64 ``` --- ## 3. Resolve the host repository workspace The Docker mount should be the **parent directory containing the repositories you want MCP to access**. If your repositories are under: ```text /Users//pyc/missionmq/gl/ ``` define: ```bash export MCP_HOST_WORKSPACE="/Users//pyc/missionmq/gl" printf 'Docker host workspace: %s\n' "$MCP_HOST_WORKSPACE" ``` Do not use an arbitrary current working directory. In particular, this is unsafe for a multi-repository workspace: ```bash -v "$PWD:/workspace" ``` If `$PWD` is `.../gl/gitaiflow`, only that repository is mounted as `/workspace` and sibling repositories disappear from the Docker view. Instead use the explicit parent workspace: ```bash -v "$MCP_HOST_WORKSPACE:/workspace" ``` --- ## 4. Build the image ```bash docker build --no-cache -f Dockerfile.mcp -t gitaiflow-mcp:local . ``` --- ## 5. Start the Docker HTTP server ### ARM64 example ```bash docker rm -f gitaiflow-mcp 2>/dev/null || true docker run --rm \ --name gitaiflow-mcp \ -p 8080:8080 \ --env-file .mcp.env \ -e MCP_TRANSPORT=streamable-http \ -e MCP_WORKSPACE=/workspace \ -e MCP_ALLOWED_WORKSPACES=/workspace \ -e GITAIFLOW_BINARY=/app/bin/gitaiflow \ -e MCP_RATE_LIMIT=100 \ -v "$HOME/.gitaiflow:/root/.gitaiflow" \ -v "$MCP_HOST_WORKSPACE:/workspace" \ -v "$MCP_HOST_WORKSPACE/gitaiflow/installers/AOT/v1.1.3/gitaiflow-linux-arm64:/app/bin/gitaiflow:ro" \ gitaiflow-mcp:local ``` For a different repository checkout, adjust the AOT source path to that checkout's versioned binary. The workspace mount must continue to point at the **parent multi-repository directory**. ### AMD64 Replace the AOT mount with: ```bash -v "$MCP_HOST_WORKSPACE/gitaiflow/installers/AOT/v1.1.3/gitaiflow-linux-amd64:/app/bin/gitaiflow:ro" ``` ### Important For the v1.1.3 local verification image, **explicitly mount the versioned AOT binary**. Do not assume: ```text GITAIFLOW_BINARY=/app/bin/gitaiflow ``` means the file exists. Verify it. --- ## 6. Verify the container and effective environment From a second terminal: ```bash docker ps --filter name=gitaiflow-mcp docker logs gitaiflow-mcp ``` Then inspect the actual workspace mount: ```bash docker exec gitaiflow-mcp sh -c ' mount | grep workspace || true ' ``` Then inspect the effective container configuration: ```bash docker exec gitaiflow-mcp sh -c ' echo "MCP_TRANSPORT=$MCP_TRANSPORT" echo "MCP_WORKSPACE=$MCP_WORKSPACE" echo "MCP_ALLOWED_WORKSPACES=$MCP_ALLOWED_WORKSPACES" echo "MCP_EXECUTION_TIMEOUT=$MCP_EXECUTION_TIMEOUT" echo "MCP_RATE_LIMIT=$MCP_RATE_LIMIT" echo "GITAIFLOW_BINARY=$GITAIFLOW_BINARY" ' ``` Expected Docker values include: ```text MCP_WORKSPACE=/workspace MCP_ALLOWED_WORKSPACES=/workspace GITAIFLOW_BINARY=/app/bin/gitaiflow ``` If `.mcp.env` contains host paths, that is fine. The explicit `-e` values override those variables inside the Docker container. --- ## 7. Verify the AOT binary ```bash docker exec -it gitaiflow-mcp sh -c ' ls -lh /app/bin/gitaiflow /app/bin/gitaiflow --version ' ``` Expected: ```text gitaiflow 1.1.3 ``` If you get: ```text /app/bin/gitaiflow: not found ``` stop here. The container is running, but the AOT boundary is not configured. Do not troubleshoot MCP yet. --- ## 8. Verify the multi-repository workspace First verify the parent workspace exists: ```bash docker exec gitaiflow-mcp sh -c ' echo "=== WORKSPACE ===" ls -lad /workspace ' ``` Then verify the repository you intend to call: ```bash docker exec gitaiflow-mcp sh -c ' echo "=== GITAIFLOW ===" ls -lad /workspace/gitaiflow ls -lad /workspace/gitaiflow/.git echo echo "=== GITAIFLOW ROOT ===" git -C /workspace/gitaiflow rev-parse --show-toplevel echo echo "=== OTHER REPO ===" git -C /workspace/djangoplay-cli rev-parse --show-toplevel ' ``` Expected: ```text /workspace/gitaiflow/.git /workspace/gitaiflow /workspace/djangoplay-cli ``` This proves that Docker sees the sibling repositories independently. If `/workspace/gitaiflow/.git` is missing, do not change MCP workspace validation. Fix the Docker mount first. ### Why this check matters If you start Docker from inside `.../gl/gitaiflow` with: ```bash -v "$PWD:/workspace" ``` the container will see: ```text /workspace/.git ``` and there will be no `/workspace/gitaiflow/.git`. Git will therefore report `/workspace` as the repository root. That is a mount-layout problem, not an MCP validation problem. --- ## 9. Verify health ```bash curl -i http://localhost:8080/health ``` Expected: ```text HTTP/1.1 200 OK ``` and: ```json { "status": "ok", "service": "gitaiflow", "version": "1.1.3" } ``` Health proves MCP is alive. It does not prove the AOT binary or repository selection works. --- ## 10. Verify direct AOT execution This deliberately bypasses MCP. For the selected `gitaiflow` repository: ```bash docker exec -it gitaiflow-mcp sh -c \ 'cd /workspace/gitaiflow && /app/bin/gitaiflow --last-summary' ``` This must succeed before MCP tool testing. You can also verify: ```bash docker exec -it gitaiflow-mcp sh -c \ 'cd /workspace/gitaiflow && /app/bin/gitaiflow --release-notes' ``` and: ```bash docker exec -it gitaiflow-mcp sh -c \ 'cd /workspace/gitaiflow && /app/bin/gitaiflow --usage current' ``` --- ## 11. Verify MCP initialize Only after the previous checks pass: ```bash curl -i -X POST http://localhost:8080/mcp \ -H "Authorization: Bearer $MCP_API_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"docker-local-test","version":"1.0.0"}}}' ``` Capture: ```text Mcp-Session-Id ``` ```bash export MCP_SESSION_ID="" ``` --- ## 12. Verify an actual MCP tool For a multi-repository Docker workspace, select the repository by name: ```bash curl -i -X POST http://localhost:8080/mcp \ -H "Authorization: Bearer $MCP_API_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Mcp-Session-Id: $MCP_SESSION_ID" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"gitaiflow_last_summary","arguments":{"path":"gitaiflow"}}}' ``` Expected: ```text HTTP 200 ``` with an MCP result containing: ```text status: ok ``` You can similarly select another repository: ```json {"path":"djangoplay-cli"} ``` The selected directory must be its own Git repository root. ### Docker path rule For the multi-repository layout: ```text MCP_ALLOWED_WORKSPACES=/workspace ``` means `/workspace` is the allowed parent workspace. The `path` argument selects a repository beneath it: ```text path="gitaiflow" → /workspace/gitaiflow path="djangoplay-cli" → /workspace/djangoplay-cli ``` Do **not** replace the multi-repository mount with an individual repository just to make one tool call work. --- ## 13. Verify Docker MCP Inspector Only after the direct MCP call succeeds. See: ```text MCP-INSPECTOR-VERIFICATION.md ``` Connect to: ```text http://localhost:8080/mcp ``` with: ```text Authorization: Bearer ``` --- ## 14. Docker stdio smoke test Claude Desktop uses stdio, not HTTP. Use the same multi-repository parent workspace: ```bash docker run --rm -i \ --env-file .mcp.env \ -e MCP_TRANSPORT=stdio \ -e MCP_WORKSPACE=/workspace \ -e MCP_ALLOWED_WORKSPACES=/workspace \ -e GITAIFLOW_BINARY=/app/bin/gitaiflow \ -v "$HOME/.gitaiflow:/root/.gitaiflow" \ -v "$MCP_HOST_WORKSPACE:/workspace" \ -v "$MCP_HOST_WORKSPACE/gitaiflow/installers/AOT/v1.1.3/gitaiflow-linux-arm64:/app/bin/gitaiflow:ro" \ gitaiflow-mcp:local ``` Expected: ```text stdio transport starts no Uvicorn listener no HTTP port is required ``` Use the matching AOT binary for the container architecture. --- ## 15. Docker state persistence After a command that records usage: ```bash docker exec -it gitaiflow-mcp sh -c \ 'test -f /root/.gitaiflow/usage.jsonl && tail -n 5 /root/.gitaiflow/usage.jsonl' ``` The same state should be visible on the host: ```bash ls -l "$HOME/.gitaiflow/usage.jsonl" tail -n 5 "$HOME/.gitaiflow/usage.jsonl" ```