|
| 1 | +--- |
| 2 | +phase: quick |
| 3 | +plan: 260403-nka |
| 4 | +subsystem: ci |
| 5 | +tags: [docker, integration-test, mcp-client, ci, remote, bearer-auth] |
| 6 | +dependency_graph: |
| 7 | + requires: [Dockerfile, tests/test_mcp_integration.py] |
| 8 | + provides: [docker-compose.test.yml, tests/remote_client.py, remote-integration-test CI job] |
| 9 | + affects: [.github/workflows/ci.yml] |
| 10 | +tech_stack: |
| 11 | + added: [mcp SDK (runtime install), httpx (runtime install)] |
| 12 | + patterns: [docker-compose healthcheck, streamable_http_client with bearer auth, asyncio retry loop] |
| 13 | +key_files: |
| 14 | + created: |
| 15 | + - docker-compose.test.yml |
| 16 | + - tests/remote_client.py |
| 17 | + modified: |
| 18 | + - .github/workflows/ci.yml |
| 19 | +decisions: |
| 20 | + - pip install mcp httpx at client container entrypoint — mcp SDK not in server image (only fastmcp installed); avoids Dockerfile changes |
| 21 | + - healthcheck uses curl POST to /mcp with JSON-RPC initialize body matching existing test_mcp_integration.py readiness pattern |
| 22 | + - retry loop in remote_client.py (30s, 2s interval) provides resilience beyond healthcheck for any post-healthy startup lag |
| 23 | + - exit-code-from client makes docker compose reflect client script exit code (0=pass, 1=fail) to CI |
| 24 | +metrics: |
| 25 | + duration_minutes: 8 |
| 26 | + completed_date: "2026-04-03" |
| 27 | + tasks_completed: 2 |
| 28 | + files_created: 2 |
| 29 | + files_modified: 1 |
| 30 | +--- |
| 31 | + |
| 32 | +# Quick 260403-nka: Docker Compose Remote MCP Integration Test Summary |
| 33 | + |
| 34 | +**One-liner:** Two-container Docker Compose test that connects a client container to a server container via streamable HTTP MCP with bearer auth, validating expected tools over a Docker bridge network, wired into CI. |
| 35 | + |
| 36 | +## Tasks Completed |
| 37 | + |
| 38 | +| # | Name | Commit | Files | |
| 39 | +|---|------|--------|-------| |
| 40 | +| 1 | Create docker-compose.test.yml and tests/remote_client.py | bd853ee | docker-compose.test.yml, tests/remote_client.py | |
| 41 | +| 2 | Add remote-integration-test job to CI workflow | d0321f4 | .github/workflows/ci.yml | |
| 42 | + |
| 43 | +## What Was Built |
| 44 | + |
| 45 | +**docker-compose.test.yml** defines two services on a shared `mcp-test-net` bridge network: |
| 46 | + |
| 47 | +- `server`: builds from existing Dockerfile, runs with `--inspect --transport streamablehttp`, sets `MATLAB_MCP_AUTH_TOKEN=test-token-for-ci` and `MATLAB_MCP_POOL_MIN_ENGINES=0`. Healthcheck POSTs a JSON-RPC initialize request to `/mcp` with bearer auth every 2s, retries 10 times, start_period 5s. |
| 48 | +- `client`: same Dockerfile base, overrides entrypoint to `sh -c "pip install --quiet mcp httpx && python /app/tests/remote_client.py"`. Mounts `tests/remote_client.py` read-only. Depends on `server:service_healthy`. |
| 49 | + |
| 50 | +**tests/remote_client.py** is a standalone async script that: |
| 51 | +1. Reads `MCP_SERVER_URL` and `MCP_AUTH_TOKEN` from environment. |
| 52 | +2. Connects via `streamable_http_client` with `httpx.AsyncClient` carrying bearer auth header. |
| 53 | +3. Calls `session.list_tools()` and verifies `{"execute_code", "get_workspace", "list_toolboxes", "get_pool_status"}` are present. |
| 54 | +4. Retries connection for up to 30s at 2s intervals on failure. |
| 55 | +5. Exits 0 on success, 1 on failure, with clear stdout messages. |
| 56 | + |
| 57 | +**CI job `remote-integration-test`** in `.github/workflows/ci.yml`: |
| 58 | +- `needs: lint`, `runs-on: ubuntu-latest` |
| 59 | +- Step 1: `actions/checkout@v4` |
| 60 | +- Step 2: `docker compose -f docker-compose.test.yml up --build --exit-code-from client` |
| 61 | +- Step 3 (always): `docker compose -f docker-compose.test.yml down -v` |
| 62 | + |
| 63 | +## Deviations from Plan |
| 64 | + |
| 65 | +### Auto-fixed Issues |
| 66 | + |
| 67 | +None — plan executed exactly as written. |
| 68 | + |
| 69 | +**Note on mcp SDK:** The plan correctly anticipated that `mcp` SDK is not in the server image. The chosen approach (pip install at container entrypoint) was explicitly the preferred option in the plan spec. |
| 70 | + |
| 71 | +## Verification Results |
| 72 | + |
| 73 | +- `python -c "import ast; ast.parse(...)"` — Python syntax: PASS |
| 74 | +- `yaml.safe_load(docker-compose.test.yml)` — Compose YAML structure: PASS |
| 75 | +- CI job exists with `needs: lint` and `--exit-code-from client`: PASS |
| 76 | +- `ruff check tests/remote_client.py` — Lint: PASS |
| 77 | + |
| 78 | +## Self-Check: PASSED |
| 79 | + |
| 80 | +Files exist: |
| 81 | +- docker-compose.test.yml: FOUND |
| 82 | +- tests/remote_client.py: FOUND |
| 83 | +- .github/workflows/ci.yml: FOUND (modified) |
| 84 | + |
| 85 | +Commits exist: |
| 86 | +- bd853ee: FOUND |
| 87 | +- d0321f4: FOUND |
0 commit comments