Skip to content

Commit 271906c

Browse files
HanSur94claude
andcommitted
docs(quick-260403-nka): track Docker Compose remote integration test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5533acd commit 271906c

1 file changed

Lines changed: 167 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
---
2+
phase: quick
3+
plan: 260403-nka
4+
type: execute
5+
wave: 1
6+
depends_on: []
7+
files_modified:
8+
- docker-compose.test.yml
9+
- tests/remote_client.py
10+
- .github/workflows/ci.yml
11+
autonomous: true
12+
must_haves:
13+
truths:
14+
- "docker compose -f docker-compose.test.yml up --exit-code-from client exits 0 when server is healthy"
15+
- "Client container connects to server container over Docker network using bearer auth"
16+
- "Client validates expected MCP tools are present in tools/list response"
17+
- "CI job runs the remote integration test on every push/PR"
18+
artifacts:
19+
- path: "docker-compose.test.yml"
20+
provides: "Two-service compose file simulating PC A (client) and PC B (server)"
21+
- path: "tests/remote_client.py"
22+
provides: "Standalone MCP client script that exits 0/1"
23+
- path: ".github/workflows/ci.yml"
24+
provides: "remote-integration-test job"
25+
key_links:
26+
- from: "tests/remote_client.py"
27+
to: "server:8765/mcp"
28+
via: "streamable HTTP MCP client with bearer token"
29+
pattern: "streamable_http_client.*server.*8765"
30+
- from: "docker-compose.test.yml"
31+
to: "Dockerfile"
32+
via: "build context for server service"
33+
pattern: "build:"
34+
---
35+
36+
<objective>
37+
Add a Docker Compose-based remote MCP integration test that simulates a client (PC A) connecting to a server (PC B) over a Docker network. This validates that the MCP server is accessible over HTTP with bearer auth from a separate container, proving the server works in a networked deployment scenario.
38+
39+
Purpose: Catch networking, auth, and transport issues that the single-process integration test cannot detect.
40+
Output: docker-compose.test.yml, tests/remote_client.py, updated CI workflow.
41+
</objective>
42+
43+
<execution_context>
44+
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
45+
@$HOME/.claude/get-shit-done/templates/summary.md
46+
</execution_context>
47+
48+
<context>
49+
@Dockerfile
50+
@docker-compose.yml
51+
@.github/workflows/ci.yml
52+
@tests/test_mcp_integration.py
53+
54+
<interfaces>
55+
<!-- The existing integration test (tests/test_mcp_integration.py) shows the exact MCP client pattern to follow -->
56+
57+
From tests/test_mcp_integration.py:
58+
```python
59+
from mcp import ClientSession
60+
from mcp.client.streamable_http import streamable_http_client
61+
62+
_AUTH_TOKEN = "test-token-for-ci"
63+
_EXPECTED_TOOLS = {"execute_code", "get_workspace", "list_toolboxes", "get_pool_status"}
64+
65+
# Client connection pattern:
66+
async with streamable_http_client(url, http_client=http_client) as (read, write, _):
67+
async with ClientSession(read, write) as session:
68+
await session.initialize()
69+
result = await session.list_tools()
70+
```
71+
72+
From Dockerfile:
73+
```dockerfile
74+
FROM python:3.12-slim
75+
ENTRYPOINT ["matlab-mcp"]
76+
CMD ["--transport", "sse"]
77+
```
78+
79+
Server CLI supports: `--inspect` (no MATLAB needed), `--transport streamablehttp`
80+
Auth via env var: `MATLAB_MCP_AUTH_TOKEN=<token>`
81+
Pool skip via: `MATLAB_MCP_POOL_MIN_ENGINES=0`
82+
</interfaces>
83+
</context>
84+
85+
<tasks>
86+
87+
<task type="auto">
88+
<name>Task 1: Create docker-compose.test.yml and tests/remote_client.py</name>
89+
<files>docker-compose.test.yml, tests/remote_client.py</files>
90+
<action>
91+
1. Create `docker-compose.test.yml` at repo root with two services on a shared Docker network:
92+
93+
**server** service:
94+
- `build: .` (uses existing Dockerfile)
95+
- Override command: `["--inspect", "--transport", "streamablehttp"]`
96+
- Set environment: `MATLAB_MCP_AUTH_TOKEN=test-token-for-ci`, `MATLAB_MCP_POOL_MIN_ENGINES=0`
97+
- Expose port 8765 internally (no host mapping needed, containers share network)
98+
- Add healthcheck: poll `http://localhost:8765/mcp` with a JSON-RPC initialize POST (same pattern as test_mcp_integration.py readiness check). Use `curl` with `--fail` and the bearer token header. Interval 2s, timeout 3s, retries 10, start_period 5s.
99+
100+
**client** service:
101+
- `build: .` (same Dockerfile base — it already has Python 3.12)
102+
- Override entrypoint to run the client script: `["python", "/app/tests/remote_client.py"]`
103+
- Mount `tests/remote_client.py` into the container at `/app/tests/remote_client.py:ro`
104+
- Set environment: `MCP_SERVER_URL=http://server:8765/mcp`, `MCP_AUTH_TOKEN=test-token-for-ci`
105+
- `depends_on: server` with `condition: service_healthy`
106+
107+
Define a custom network `mcp-test-net` (bridge driver).
108+
109+
2. Create `tests/remote_client.py` as a standalone script (no pytest dependency):
110+
- Read `MCP_SERVER_URL` and `MCP_AUTH_TOKEN` from environment variables.
111+
- Install `mcp` and `httpx` at runtime is NOT needed — they are already in the server image's pip packages. However, `mcp` SDK may not be installed in the server image (it installs `fastmcp` not `mcp`). To handle this: the client service should run a pip install of `mcp httpx` before running the script, OR better, use a multi-stage approach. Simplest approach: override the client entrypoint to `["sh", "-c", "pip install mcp httpx && python /app/tests/remote_client.py"]`.
112+
- Use the same MCP client pattern from test_mcp_integration.py: `streamable_http_client` with `httpx.AsyncClient` carrying bearer auth header.
113+
- Add a retry loop (up to 30s, polling every 2s) for the MCP connection in case the healthcheck passes but the server needs a moment more.
114+
- Call `session.list_tools()` and verify `_EXPECTED_TOOLS = {"execute_code", "get_workspace", "list_toolboxes", "get_pool_status"}` are all present.
115+
- Print clear success/failure messages to stdout.
116+
- `sys.exit(0)` on success, `sys.exit(1)` on failure (any missing tool or connection error).
117+
- Use `asyncio.run()` as the entry point (standalone script, not pytest).
118+
- Include a module docstring explaining purpose and usage.
119+
</action>
120+
<verify>
121+
<automated>docker compose -f docker-compose.test.yml build 2>&1 | tail -5</automated>
122+
</verify>
123+
<done>docker-compose.test.yml defines server and client services; tests/remote_client.py is a standalone MCP client script that connects, lists tools, and exits 0/1.</done>
124+
</task>
125+
126+
<task type="auto">
127+
<name>Task 2: Add remote-integration-test job to CI workflow</name>
128+
<files>.github/workflows/ci.yml</files>
129+
<action>
130+
Add a new job `remote-integration-test` to `.github/workflows/ci.yml`:
131+
132+
- `needs: lint` (same dependency as the existing docker and integration-test jobs)
133+
- `runs-on: ubuntu-latest`
134+
- Steps:
135+
1. `actions/checkout@v4`
136+
2. Run: `docker compose -f docker-compose.test.yml up --build --exit-code-from client`
137+
3. Cleanup step (always run): `docker compose -f docker-compose.test.yml down -v`
138+
139+
Place the job after the existing `docker` job in the file for logical grouping.
140+
141+
Do NOT modify any existing jobs. Only add the new job.
142+
</action>
143+
<verify>
144+
<automated>python -c "import yaml; y=yaml.safe_load(open('.github/workflows/ci.yml')); assert 'remote-integration-test' in y['jobs'], 'Job missing'; j=y['jobs']['remote-integration-test']; assert j['needs']=='lint', f'Wrong needs: {j[\"needs\"]}'; print('CI job validated OK')"</automated>
145+
</verify>
146+
<done>CI workflow contains remote-integration-test job that runs docker compose -f docker-compose.test.yml up --exit-code-from client on every push/PR.</done>
147+
</task>
148+
149+
</tasks>
150+
151+
<verification>
152+
1. `docker compose -f docker-compose.test.yml config` validates compose syntax
153+
2. `python -c "import ast; ast.parse(open('tests/remote_client.py').read()); print('Syntax OK')"` validates Python syntax
154+
3. CI workflow YAML parses and contains the new job
155+
4. (Optional, requires Docker) `docker compose -f docker-compose.test.yml up --build --exit-code-from client` exits 0
156+
</verification>
157+
158+
<success_criteria>
159+
- docker-compose.test.yml exists with server (streamablehttp + inspect + bearer auth) and client services on a shared network
160+
- tests/remote_client.py connects to server via MCP SDK, lists tools, asserts expected tools, exits 0/1
161+
- .github/workflows/ci.yml has remote-integration-test job that runs the compose test
162+
- All files pass lint (ruff) and syntax validation
163+
</success_criteria>
164+
165+
<output>
166+
After completion, create `.planning/quick/260403-nka-docker-compose-remote-mcp-integration-te/260403-nka-SUMMARY.md`
167+
</output>

0 commit comments

Comments
 (0)