Lab 8 — Agent skills and tool scaling
This standalone snapshot extends harness ask in two ways:
- a skill: a packaged procedure (
SKILL.md) that the harness loads only when it’s relevant. - an MCP server: an external process that offers new tools. Here it is a fake order system the app can’t otherwise see.
The exercise matches the Claude Code Lab 8.
What changes
harness/project_skills.py:- Skills live in
.harness/skills/<name>/SKILL.mdin the project, or~/.harness/skills/<name>/SKILL.mdfor you alone. Front matter gives thename, adescriptionand optionalallowed-tools. - Only each skill’s name and description go in the system prompt. The
body is loaded on demand: by the model, with the
use_skilltool, when a task matches the description; or by you, with/<skill-name> [extra instructions]. allowed-toolslists permission rules (Lab 6 syntax) that are pre-approved once the skill is in use, so its routine steps run without prompts. They never override a deny or an explicit ask rule, and/permissionsshows them.
- Skills live in
harness/mcp_client.py:- A minimal MCP client over stdio: start the server,
initialize,tools/list,tools/call. Each server tool becomesmcp__<server>__<tool>, so it can’t collide with a local tool. - MCP tools go through the same hooks, permissions and trace as local tools. A tool the rules don’t mention asks (Lab 6 default).
harness mcp add NAME -- COMMAND...registers a server for this project only, in~/.harness/projects/<project>/mcp.json(outside the repository, like Claude Code’s local scope).harness mcp liststarts each server and lists its tools;harness mcp remove NAMEunregisters it.
- A minimal MCP client over stdio: start the server,
orders_mcp.pyis a ~100-line, dependency-free MCP server with synthetic orders:list_orders(status)andget_order(order_id).- The CLI:
/skillsandharness skillslist skills;/mcplists servers and their tools.
harness/skills.py keeps the earlier in-process
models of a skill registry with approval state, tool discovery and MCP
namespacing.
Learner steps
Start from: labs/app/ at tag lab07-done (see the
track guide).
-
Install this lab and configure Foundry as before:
python -m venv .venv . .venv/bin/activate # Windows: .\.venv\Scripts\Activate.ps1 pip install -e '.[dev]' cp .env.example .env az login cd ../app
Part A: a release-notes skill
-
Read
assets/.harness/skills/release-notes/SKILL.md, then install and commit it:cp -R ../lab08-skills-tools/assets/. . git add .harness && git commit -m "chore: add release-notes skill" harness skills -
Invoke it explicitly:
harness ask/release-notesThen
/exitand, in a new session, invoke it implicitly:harness ask "What changed since tag lab04-done? Write it up as release notes."Observe: in the second run, a
use_skilltool call picked from the description. Thengit logruns andCHANGELOG.mdis written, although a one-shot run has no one to answer a permission prompt: the skill’sallowed-toolspre-approvedwrite_file(CHANGELOG.md). Commits are grouped by Conventional Commit type (your Lab 5 convention pays off here). Nothing is committed. ReviewCHANGELOG.mdand commit it yourself.
Part B: an MCP server for orders
-
Register the server for this project, with an absolute path:
harness mcp add orders -- python "$(cd ../lab08-skills-tools && pwd)/orders_mcp.py" harness mcp listharness mcp add orders -- python (Resolve-Path ..\lab08-skills-tools\orders_mcp.py).Path harness mcp list -
Use it:
harness ask -n restockType
/mcpto see the server and its tools, then:Using the orders tools, find pending orders that we cannot fulfil with the current stock in catalog.csv. Write RESTOCK.md listing each SKU, quantity needed, stock on hand and shortfall.Observe: tool calls named
mcp__orders__list_orders/mcp__orders__get_order, the permission prompt for each new tool (answerato allow the same call for the session), and how the model joins MCP data with local files. CheckRESTOCK.mdagainstcatalog.csvyourself. Did it correctly ignore the large cancelled order? -
Unregister the server so it doesn’t follow you into later labs:
harness mcp remove orders - Record
- How the skill was triggered (slash command vs description match), and
which calls its
allowed-toolslet through without a prompt. - The MCP tool names and whether you were asked to approve them.
- Anything in
RESTOCK.mdthat was wrong.
- How the skill was triggered (slash command vs description match), and
which calls its
-
Checkpoint. Commit
CHANGELOG.mdandRESTOCK.mdif you’re keeping them, then inapp/:git tag lab08-done -
Back in the lab folder, run the offline checks and inspect the declared capabilities:
pytest checks/ harness lab-info
harness ask keeps tracing (Lab 7), permissions (Lab 6), file memory
(Lab 5), plan mode and todos (Lab 4), sessions (Lab 3) and the Lab 2B
tools, built-in policy, project hooks and rules. This is a teaching
harness, not a sandbox: only use it on the practice app.