fix(testing/ci/tooling): consistent unittest, venv guidance, runnable lab commands

- #9: standardize the test chain on stdlib unittest (nothing-to-install, which
  keeps M13's claims true and its planted bug intact). Aligned M5/M14/M16 prose,
  M14 lab/test_tasks.py, and ci/gitlab starters; ruff stays the only pip install.
- #20: add venv / PEP 668 / which-python guidance to M20 (+ M14/M15 local
  installs); point MCP config at the venv's absolute python.
- #21: replace M21 Part D's empty `git diff HEAD~1` with `git log -p` (no
  .gitignore added — device preserved).
- #22: add a dependency-install step before M23's green baseline on a fresh clone.
- #23: M24 reviewer/triage now tolerate code-fence-wrapped JSON (stdlib only);
  feature.patch trap untouched.
- #28: fix M27 Part D CI snippet path (working-directory) and require the gate to
  target a varying candidate; swapped_model regression kept as the fixture.

Closes #9
Closes #20
Closes #21
Closes #22
Closes #23
Closes #28

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TfzV5QvtPDz8LJS3Pu5VLT
This commit is contained in:
2026-06-22 16:07:47 -04:00
parent a6a3cfdc50
commit f98eacb196
17 changed files with 216 additions and 82 deletions
@@ -239,10 +239,40 @@ is the one that lands the concept.
- Your agentic coding tool from Module 4, which is the **MCP client**. Find, in its docs, *where it
reads MCP server configuration* and *how it shows that a server is connected* (often a list of
connected servers or available tools).
- Python 3.10+ and the official MCP Python SDK: `pip install "mcp[cli]"`.
- Python 3.10+ and the official MCP Python SDK, installed into a virtual environment — read the
**Python packages and which `python`** note just below *before* you run `pip`.
- The starter files in this module's `lab/` folder: `tasks_mcp_server.py` and
`mcp-config-example.json`.
> **Python packages and which `python`.** This lab's one dependency is the MCP SDK, and *how* you
> install it decides whether the server ever connects. Two things bite people:
>
> - **PEP 668 ("externally-managed-environment").** On modern Debian/Ubuntu and Homebrew Python, a
> global `pip install` is refused on purpose. The clean fix is a virtual environment per project:
>
> ```bash
> cd ~/workflow-course/tasks-app
> python3 -m venv .venv # one-time
> source .venv/bin/activate # Windows: .venv\Scripts\activate
> python3 -m pip install "mcp[cli]"
> ```
>
> (If you'd rather not manage a venv: `pipx`, or `pip install --break-system-packages` — but a venv
> is the clean default and keeps this lab's dependency out of your system Python.)
> - **The install interpreter must match the config's launch command.** Your MCP client starts the
> server by running the `"command"` in its config — *not* your activated shell — so activating a
> venv does nothing to help the client find the SDK. You must point `"command"` at the venv's
> **absolute** python path (e.g. `~/workflow-course/tasks-app/.venv/bin/python`, or
> `...\.venv\Scripts\python.exe` on Windows). If they don't match, the server dies on `import mcp`
> and your tool just says "not connected" with no obvious reason — the exact failure this lab is
> about avoiding.
>
> Before wiring anything, verify with the *same* interpreter the config will launch:
>
> ```bash
> ~/workflow-course/tasks-app/.venv/bin/python -c "import mcp; print('mcp ok')"
> ```
### Part A — Connect an existing server (warm-up, ~10 min)
Before building anything, prove the plumbing works by connecting a server someone else already
@@ -291,8 +321,8 @@ That's the entire client/server loop, end to end, with zero code you wrote. Now
2. Sanity-check it starts. From inside `tasks-app`:
```bash
pip install "mcp[cli]" # once
python tasks_mcp_server.py # it will sit there waiting for a client — that's correct
python3 -m pip install "mcp[cli]" # into the venv from the note above, once
python tasks_mcp_server.py # it will sit there waiting for a client — that's correct
```
It looks like it's hanging. It isn't — a stdio server waits for a client on its stdin/stdout.
@@ -301,20 +331,26 @@ That's the entire client/server loop, end to end, with zero code you wrote. Now
### Part C — Wire it into your agentic tool
3. Open `lab/mcp-config-example.json`. Copy the `tasks` entry into wherever your tool reads MCP
config, and replace the path with the **absolute** path to your `tasks_mcp_server.py`. (Use
`python3` or a venv's python if that's what runs the SDK on your system.)
config. Set `"command"` to the **absolute path of the python that has `mcp` installed** — the venv
python from the note above, *not* a bare `python` — and set `args` to the **absolute** path to
your `tasks_mcp_server.py`:
```json
"tasks": {
"command": "python",
"command": "/ABSOLUTE/PATH/TO/workflow-course/tasks-app/.venv/bin/python",
"args": ["/ABSOLUTE/PATH/TO/workflow-course/tasks-app/tasks_mcp_server.py"]
}
```
(On Windows the venv python is `...\.venv\Scripts\python.exe`.) A bare `"command": "python"` is the
single most common reason the server "won't connect": the client launches whatever `python` is on
*its* PATH, which is usually not the interpreter that has the SDK.
4. Reload your agentic tool and confirm it shows the `tasks` server **connected**, with `list_tasks`
and `add_task` among its available tools. If it doesn't connect, the usual culprits are a wrong
path, the wrong `python`, or the SDK not installed for that interpreter — check the tool's MCP
logs.
path, the wrong `python`, or the SDK not installed for that interpreter — re-run the
`... .venv/bin/python -c "import mcp"` check from the note above against the *exact* path you put
in `"command"`, then check the tool's MCP logs.
### Part D — Watch the AI use its new hands
@@ -1,8 +1,8 @@
{
"_comment": "Common shape of an MCP server entry for a local (stdio) server. Many agentic tools accept this 'mcpServers' map; yours may use a different key or location (check its docs). Replace the path with the ABSOLUTE path to tasks_mcp_server.py in your tasks-app. Use 'python3' instead of 'python' if that's what your system calls it, or the full path to a virtualenv's python.",
"_comment": "Common shape of an MCP server entry for a local (stdio) server. Many agentic tools accept this 'mcpServers' map; yours may use a different key or location (check its docs). IMPORTANT: 'command' must be the ABSOLUTE path to the python interpreter that has the MCP SDK installed (e.g. your venv's python) -- a bare 'python' makes the client launch whatever is on its PATH, which usually does NOT have the SDK, and the server then reports 'not connected'. On Windows the venv python is ...\\.venv\\Scripts\\python.exe. Set 'args' to the ABSOLUTE path to tasks_mcp_server.py in your tasks-app.",
"mcpServers": {
"tasks": {
"command": "python",
"command": "/ABSOLUTE/PATH/TO/workflow-course/tasks-app/.venv/bin/python",
"args": ["/ABSOLUTE/PATH/TO/workflow-course/tasks-app/tasks_mcp_server.py"]
}
}