Container/runner/MCP lab polish (#41,#42,#45,#46) #65
@@ -173,7 +173,11 @@ containerize and run the app you already have.
|
||||
- The `tasks-app` folder from Module 1 (`tasks.py`, `cli.py`).
|
||||
- A container engine. **Docker Desktop** (macOS/Windows) or **Docker Engine** (Linux) is the common
|
||||
choice; **Podman** works too and the commands below map 1:1 (`podman` for `docker`). Verify with
|
||||
`docker --version` (or `podman --version`).
|
||||
`docker --version` (or `podman --version`). **The engine must be *running* before you build:**
|
||||
`docker --version` reports the client version even when the engine is stopped, so it's false
|
||||
reassurance — `docker build` then fails with "Cannot connect to the Docker daemon." On
|
||||
macOS/Windows start it first (launch Docker Desktop, or `podman machine start`); confirm the daemon
|
||||
is up with `docker info` (or `podman info`), which only succeeds when the engine is actually live.
|
||||
- The starter files from this module's `lab/`: [`Dockerfile`](lab/Dockerfile) and
|
||||
[`dockerignore-starter`](lab/dockerignore-starter).
|
||||
- Your AI assistant.
|
||||
@@ -234,6 +238,16 @@ containerize and run the app you already have.
|
||||
> Git Bash), or from PowerShell — `${PWD}` resolves correctly in each. The other `docker run`
|
||||
> commands mount nothing of yours and are identical everywhere.
|
||||
|
||||
> **On native Linux:** the container runs as root by default, and the bind mount maps that straight
|
||||
> onto your real project folder — so the `__pycache__` directories Python writes during the test
|
||||
> run land in your repo owned by `root:root`, and you can't delete them without `sudo rm -rf`.
|
||||
> Prevent it by telling Python not to write bytecode in the container: add
|
||||
> `-e PYTHONDONTWRITEBYTECODE=1` to the `docker run` line (with pytest you'd also pass
|
||||
> `pytest -p no:cacheprovider` to suppress `.pytest_cache`). A `.gitignore` won't help — it hides
|
||||
> the files from Git but they're still on disk and still sudo-only to remove. Avoid `--user
|
||||
> $(id -u):$(id -g)` here: it fixes ownership but breaks any in-container `pip install` into the
|
||||
> image's root-owned site-packages.
|
||||
|
||||
This is, in miniature, exactly what containerized CI does. If it passes here, it passes the same
|
||||
way on any machine with the engine — your laptop's local Python version is now irrelevant.
|
||||
|
||||
|
||||
@@ -208,7 +208,10 @@ account. The five deploy steps are real; only the *target* is your laptop instea
|
||||
**You'll need:**
|
||||
|
||||
- A container runtime from Module 16 — Docker or Podman. (Commands below use `docker`; if you run
|
||||
Podman, `alias docker=podman` or substitute.)
|
||||
Podman, `alias docker=podman` or substitute.) As in Module 16, the engine must be **running**
|
||||
before you build or deploy — on macOS/Windows start Docker Desktop (or `podman machine start`);
|
||||
`docker --version` succeeds even when the engine is stopped, so confirm it's live with
|
||||
`docker info` first, or `deploy.sh`'s build step fails with "Cannot connect to the Docker daemon."
|
||||
- The `tasks-app` from Modules 1–2, now a Git repo.
|
||||
- `curl` (for the health check) and a bash-capable shell. On Windows, use WSL or Git Bash.
|
||||
- Your AI assistant — by now, ideally editor-integrated (Module 4).
|
||||
|
||||
@@ -222,7 +222,9 @@ a repo also works). If a real runner is too heavy right now, Track A alone satis
|
||||
(the same place your Module 14 `ci.yml` lives — for Actions-style forges that's
|
||||
`.github/`/`.forgejo/`/`.gitea/` under `workflows/`; the file comments tell you where). Commit and
|
||||
push. It runs the same lint-and-test as Module 14, then prints the runner's hostname, OS, user,
|
||||
whether it looks ephemeral, and whether it can reach the public internet.
|
||||
whether it looks ephemeral, and whether it can reach the public internet. The receipt step carries
|
||||
`if: always()` so it still prints even when lint or test fail — a diagnostic shouldn't disappear on
|
||||
a red build (the job still reports red). On GitLab CI the same idea is `when: always` on the job.
|
||||
|
||||
2. **Read the receipt.** Open the job logs on your forge and read the `Where did this run?` step.
|
||||
You're now able to answer, for a real job, the question this module opened with: *whose computer
|
||||
|
||||
@@ -44,7 +44,11 @@ jobs:
|
||||
run: pytest -q
|
||||
|
||||
# The point of THIS workflow: make the runner identify itself.
|
||||
# if: always() so the receipt prints even when Lint/Test fail above — a diagnostic step
|
||||
# shouldn't vanish on a red build. The job still reports red; only this step is unconditional.
|
||||
# (On GitLab CI the same idea is `when: always` on the job/step.)
|
||||
- name: Where did this run?
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
echo "=== runner identity ==="
|
||||
|
||||
@@ -35,7 +35,8 @@ By the end of this module you can:
|
||||
1. Explain the MCP client/server model — what a server exposes (tools, resources, prompts), what the
|
||||
client (your agentic tool) does, and why "it's a protocol, not a vendor feature" is the whole
|
||||
point.
|
||||
2. Connect an existing MCP server to your agentic tool and confirm the AI can call its tools.
|
||||
2. Connect an MCP server to your agentic tool and confirm the AI can call its tools — an existing
|
||||
reference server (the optional Part A warm-up) or the one you build in Part B/C.
|
||||
3. Build a tiny MCP server in Python that exposes one real capability over the `tasks-app`, and wire
|
||||
it into your tool.
|
||||
4. Watch the AI *use* that server — read and change real state through a tool call — and verify the
|
||||
@@ -243,6 +244,9 @@ is the one that lands the concept.
|
||||
**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`.
|
||||
- **Only for the optional Part A warm-up:** the reference server your tool points you at typically
|
||||
runs via `npx` (needs Node) or `uvx` (needs uv) — install whichever its documented `command`
|
||||
needs. Part B/C, the load-bearing path, need only the Python SDK above, so you can skip this.
|
||||
|
||||
> **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:
|
||||
@@ -273,15 +277,22 @@ is the one that lands the concept.
|
||||
> ~/workflow-course/tasks-app/.venv/bin/python -c "import mcp; print('mcp ok')"
|
||||
> ```
|
||||
|
||||
### Part A — Connect an existing server (warm-up, ~10 min)
|
||||
### Part A — Connect an existing server (optional warm-up, ~10 min)
|
||||
|
||||
Before building anything, prove the plumbing works by connecting a server someone else already
|
||||
wrote. The MCP ecosystem ships a set of **reference servers** (filesystem, fetch, git, and more) —
|
||||
pick a simple, read-only one your tool's docs point you at (a "filesystem" or "fetch" server is a
|
||||
good first choice).
|
||||
This part is **optional**: it proves the plumbing works by connecting a server someone else already
|
||||
wrote, but it's a warm-up, not the load-bearing concept — Part B/C land that on the Python SDK you
|
||||
already installed. The catch is the runtime: most **reference servers** (filesystem, fetch, git, and
|
||||
more) are distributed for `npx` (Node) or `uvx` (uv), *not* Python, so this warm-up needs whichever
|
||||
runtime its documented command uses. If you don't already have Node or uv and don't want to install
|
||||
one for a 10-minute warm-up, **skip straight to Part B** — you lose nothing the rest of the lab needs.
|
||||
|
||||
To do it: pick a simple, read-only reference server your tool's docs point you at (a "filesystem" or
|
||||
"fetch" server is a good first choice), and install the runtime its command needs (Node for `npx`, uv
|
||||
for `uvx`).
|
||||
|
||||
1. Add the server to your tool's MCP config, following the tool's docs. Most reference servers are
|
||||
launched the same stdio way as the JSON shape shown in *Key concepts* — a `command` and `args`.
|
||||
launched the same stdio way as the JSON shape shown in *Key concepts* — a `command` (e.g. `npx` or
|
||||
`uvx`) and `args`.
|
||||
2. Restart or reload your agentic tool so it picks up the config. Confirm it reports the server as
|
||||
**connected** and lists its tools.
|
||||
3. Ask the AI to do something only that server enables — e.g. with a fetch server, *"fetch
|
||||
@@ -429,8 +440,9 @@ The honest caveats — and one of them is large enough that it gets its own modu
|
||||
|
||||
**You're done when:**
|
||||
|
||||
- You connected an **existing** MCP server to your agentic tool and watched the AI call one of its
|
||||
tools (Part A).
|
||||
- (Optional, Part A) If you ran the warm-up, you connected an **existing** reference MCP server to
|
||||
your agentic tool and watched the AI call one of its tools. Skipping it costs nothing — Part C
|
||||
connects the server you build and shows the same tool call.
|
||||
- You built `tasks_mcp_server.py`, wired it into your tool, and saw the `tasks` server report as
|
||||
connected with `list_tasks` and `add_task` available.
|
||||
- You asked the AI a question and it answered by **calling a tool** against the live system, and you
|
||||
@@ -461,8 +473,9 @@ MCP is moving fast; re-check these at build/publish time rather than trusting th
|
||||
- [ ] **The `mcpServers` config shape.** Confirm it's still the widely-shared convention for stdio
|
||||
servers, and that the `command`/`args` fields are current. Keep the lesson tool-agnostic about
|
||||
*where* the config file lives.
|
||||
- [ ] **Reference servers (Part A).** Verify which first-party reference servers exist and how
|
||||
they're launched today; the catalogue and launch commands change. Don't name a specific server
|
||||
that may have moved or been retired without checking.
|
||||
- [ ] **Reference servers (optional Part A).** Verify which first-party reference servers exist and
|
||||
how they're launched today; the catalogue and launch commands change. Don't name a specific
|
||||
server that may have moved or been retired without checking. Confirm the named runtimes (`npx`
|
||||
via Node, `uvx` via uv) are still how the common reference servers are distributed.
|
||||
- [ ] **Adoption framing.** Re-confirm the "open standard, adopted across vendors regardless of
|
||||
model" claim is still accurate and still vendor-neutral; update if the ecosystem has shifted.
|
||||
|
||||
Reference in New Issue
Block a user