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