Lab 5: MCP and Skills
AI-Assisted Programming for PhD Researchers
Two things extend an agent past the code in front of it: skills, which hand it reusable instructions (here, a set of academic-writing editors plus one that looks up real papers), and MCP servers, which hand it new tools (here, Context7’s documentation lookup). The core of this lab is three exercises: run a writing skill over your report’s prose (Task 1), build a verified reading list with the reference-lookup skill (Task 3), and split work across two agents with a git worktree (Task 4). I demo the worktree live before you run it. Two more are optional: writing your own skill from scratch (Task 2) and wiring up the Context7 MCP server for live library documentation (Task 5); do those if you are ahead. The throughline is the same one from Lab 4: the tools do more, but you still confirm every claim before you trust it.
Budget about 50 minutes for the three core tasks. Continue in the same repository, on the state you reached at the end of Lab 4.
Task 1: Writing Skills
Goal: install the course’s academic-writing skills and run one over the prose in your report, reviewing each proposed edit rather than accepting a rewrite.
The skills ship with your project: the starter repository you cloned on Day 1 has a skills/ folder at its root. Install the writing pack and the reference-lookup skill by copying them into your project’s skill directory:
mkdir -p .claude/skills
cp -r skills/writing/* skills/reference-lookup .claude/skills/
rm .claude/skills/README.mdCopy the glob exactly as printed, without a trailing slash after *. On macOS, cp -r skills/writing/*/ … copies the folders’ contents instead of the folders themselves, so all eight skills collapse into a single SKILL.md and none of them load.
OpenCode discovers Claude Code-compatible skills at .claude/skills/<name>/SKILL.md (project-level), no conversion needed. After the copy you should have eight writing skills: academic-grammar, conciseness, hedging-language, literature-grounder, humanizer, acronym-check, term-consistency, and tense-consistency, plus the reference-lookup skill you’ll use in Task 3.
Restart OpenCode (or start a fresh session) so it picks up the new skills, then point the conciseness skill at the prose introduction of the report you generated in Lab 4. (Caught up via tags/lab-04-done and have no out/? Regenerate it first: uv run pipeline run --raw data/raw/measurements.csv --out out/.)
Apply the conciseness skill to the introduction section of out/report.md. Show me the proposed edits as a diff. Don’t write the file yet.
Read the diff edit by edit. Conciseness cuts wordiness (“in order to” → “to”, redundant qualifiers), but a cut that removes a number, a hedge, or a qualifier can change what the sentence claims. Accept selectively: keep the edits that only tighten, reject any that alter meaning.
The eight writing skills (and the reference-lookup skill) exist under .claude/skills/, the agent produced a diff against out/report.md’s introduction, and you have gone through it and decided edit-by-edit which to accept.
Pick one edit you rejected and say why: what did the “concise” version drop (a unit, a number, a hedge, a distinction), and how would the tightened sentence mislead a reader compared to the original?
The starter also ships skills/superpowers/: two workflow skills from the open-source Superpowers framework (MIT) shown in the lecture. systematic-debugging makes the agent find the root cause before proposing any fix; verification-before-completion makes it show evidence before claiming “done”. Install them the same way, and read both SKILL.md files first; that is the vetting habit from the lecture:
cp -r skills/superpowers/systematic-debugging \
skills/superpowers/verification-before-completion .claude/skills/Task 2: Write Your Own Skill (optional, if you are ahead)
Goal: write a skill of your own from scratch, run it, and confirm its shape matches the ones you just installed. Skip ahead to Task 3 if you are short on time and come back to this one at the end.
A skill is nothing more than a folder with a SKILL.md inside it. Create .claude/skills/figure-caption-checker/SKILL.md with the template below:
---
name: figure-caption-checker
description: Checks that figure captions in a document state what is shown, how it was measured, and the sample size, with units, and without smuggling interpretation into the caption. Use when reviewing figures in a report or manuscript.
---
Review each figure caption in the document and check that it:
- states **what** is shown (the variable and the comparison), not just a label;
- states **how** it was measured or computed (method, instrument, or statistic);
- reports the **sample size** (n) behind the figure;
- carries **units** on every quantity mentioned;
- contains **no interpretation**: the caption describes the figure; conclusions belong in the text.
Report each caption that fails one or more of these as a short list: the figure,
which check failed, and the minimal fix. Show your findings as a list, not a
rewrite of the document.Restart OpenCode (or start a fresh session) so it picks up the new skill, then run it against your report and ask for a list of problems, not a rewrite. (Caught up via tags/lab-04-done and have no out/? Regenerate it first: uv run pipeline run --raw data/raw/measurements.csv --out out/.)
Apply the figure-caption-checker skill to out/report.md. List the problems you find; don’t edit the file.
Now compare the skill you wrote to the eight you installed in Task 1: open one of their SKILL.md files alongside yours. Every one has the same shape: a name, a description that says when it applies, and a short instruction body that ends by asking for a list, not a rewrite.
.claude/skills/figure-caption-checker/SKILL.md exists, a fresh session loaded it, and the agent used it to return a list of caption problems (or a clean pass) against out/report.md without editing the file.
The agent loaded your skill for this task but not for unrelated ones. Which line made that happen, and how? Point to the part of the file the agent reads to decide whether a skill applies, and say what would go wrong if it were vague or missing.
Task 3: A Verified Reading List
Goal: use the reference-lookup skill to assemble a short, checked reading list, and prove to yourself that a returned DOI is not the same as a correct DOI.
You installed the reference-lookup skill alongside the writing pack in Task 1. It resolves a paper title to a DOI and year through the free, keyless OpenAlex API, then cross-checks that DOI against Crossref: no server to wire up, no key to manage. Restart OpenCode (or start a fresh session) so the skill is loaded, then ask the agent to do the lookup and write the file:
Using the reference-lookup skill, find the DOI, publication year, and a one-sentence statement of relevance for these three papers, then write docs/references.md as a short list. Look everything up through the skill, not from memory:
- Wilson et al., “Good enough practices in scientific computing”;
- Sandve et al., “Ten simple rules for reproducible computational research”;
- one more paper you pick from your own field on data quality or reproducibility.
For each entry give: full title, authors, year, DOI, and one sentence on why it matters for a research pipeline.
Now the rule that makes this a reading list and not a hallucination:
Open every DOI in your browser and confirm it resolves to the paper the agent claimed. Type https://doi.org/<DOI> for each entry. Check that the title, authors, and year on the landing page match what’s in docs/references.md, and that the one-sentence relevance is actually supported by the abstract, not invented. A returned DOI is a candidate, not proof: OpenAlex can surface a plausible-looking DOI for the wrong paper, or one that resolves to nothing. That’s exactly why the skill cross-checks it against Crossref before handing the final check back to you. You only have a reference once you’ve seen it resolve.
Fix any entry where the DOI, year, or title didn’t check out, and re-verify.
OpenAlex title search sometimes returns the wrong paper for short or common titles, and “never guess a DOI” still applies. The fallback is not to give up: search Crossref by title at https://search.crossref.org, or take the DOI printed on the paper’s own journal page, then verify it through https://doi.org/<DOI> exactly like the others. The rule is never trust an unverified DOI, not never type one yourself.
docs/references.md lists three papers, and you have personally opened all three DOIs in a browser and confirmed each resolves to the paper named in the file.
Did every DOI resolve to the claimed paper? If one didn’t, say exactly what went wrong: was it a DOI that 404’d, a DOI that resolved to a different paper (which one?), a wrong year on a right paper, or a relevance sentence the abstract doesn’t support? Name the specific failure. If all three were perfect, say how you’d have caught it if one had been off.
Task 4: A Parallel Worktree
Goal: run two agents at once on the same repository without them colliding, by giving the second one its own working directory via a git worktree. Watch the live demo first, then run it yourself.
A worktree is a second checkout of the same repo on its own branch. Create one for a documentation branch (-b creates the branch, which does not exist yet):
git worktree add -b improve-docs ../starter-docs(In Zed, the git: worktree picker from the Command Palette does the same and opens the worktree in its own window.)
Open a second terminal, move into that worktree, and start a separate OpenCode session there:
cd ../starter-docs && opencodeGive that second agent a self-contained documentation task:
Add a one-line docstring to every public function in src/pipeline/. Don’t change any behaviour: docstrings only.
While that runs, go back to your first terminal and session and do something small there in parallel, for example, add a --version flag to the pipeline CLI:
Add a --version flag to the pipeline CLI that prints the package version and exits.
Both sessions are editing the same repository at the same time, but in different directories on different branches, so neither steps on the other. When the docstring agent is done, commit its work on improve-docs. Commit the --version change in your main session too: merging, and later removing the worktree, both need a clean tree:
git add -A
git commit -m "Add --version flag to CLI"Then bring the docstring work back into your main branch and clean up the worktree:
git switch main
git merge improve-docs
git worktree remove ../starter-docsBoth agents edited src/pipeline/cli.py, so the merge will very likely stop with a conflict in it. That is the honest outcome of parallel work, not a mistake: worktrees stop the agents from overwriting each other’s files, they do not stop two edits to the same lines. Open the file, keep both changes (the docstring and the --version flag), delete the <<<<<<<, =======, and >>>>>>> markers, then:
git add src/pipeline/cli.py
git commit # finishes the mergeZed’s Project Diff shows the conflicted hunks with Take-Ours / Take-Theirs buttons if you prefer clicking to editing.
improve-docs is merged into main, src/pipeline/ functions now have docstrings, the CLI has a --version flag, and git worktree list no longer shows ../starter-docs.
The reference tag lab-05-done captures the durable deliverable: a verified five-entry docs/references.md (a superset of your three). The hands-on work in Tasks 1, 2, 4, and 5 stays in your own copy and is not part of the tag. Fell behind? Reset to the reference state (nothing is pushed, so this is safe):
git fetch --tags
git stash -u # park your work (tracked and untracked)
git switch -C main tags/lab-05-doneRestate the mechanism in your own words: what would have happened if both sessions had worked in the same directory instead of separate worktrees? Describe concretely how one agent’s edits or branch switch would have collided with the other’s, and why giving the second agent its own worktree prevents it.
Task 5: Wire Up Context7 (optional, if you are ahead)
Goal: add an MCP server that fetches current library documentation into the agent’s context, then check whether what it returns is actually right.
Models are trained on a snapshot of the world, so their knowledge of a library is frozen at that snapshot and drifts further out of date every release. That is where invented function signatures come from: the same failure mode as the invented citations you will see in Lecture VIII, one layer down. Context7 answers this by looking the documentation up at the moment you ask.
It is a hosted server, so the entry in opencode.json is a remote one pointing at its URL. Open (or create) opencode.json at the repository root and add:
{
"mcp": {
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp",
"enabled": true
}
}
}
If opencode.json already has other keys (a model, a permission block), merge the mcp key in alongside them rather than overwriting the file.
That is the whole setup: no login, no token. Context7 rate-limits anonymous use, which is fine for one exercise. If you hit a limit, a free API key from context7.com/dashboard raises it, and you pass it as a Bearer header the same way you would for any other server:
"headers": { "Authorization": "Bearer {env:CONTEXT7_API_KEY}" }
Set that variable in the same terminal you start opencode from: an env var set in one terminal is invisible to a process started in another.
MCP endpoints and config keys are version-sensitive and change often. Verify these against the current Context7 docs, or use the values announced in class, before you restart.
Restart OpenCode so it re-reads the config, and confirm the server registered:
opencode mcp listNow ask it something about a library you are actually using in the pipeline. The phrase use context7 is the conventional trigger:
What is the current recommended way to write a parametrized test in pytest, and which arguments does the decorator take? use context7
The agent should make two calls: one to resolve “pytest” into a library ID, and one to pull documentation for it. Watch the tool calls go by; that is the whole mechanism, visible.
opencode mcp list shows context7, and in the session you can see the agent call the library-resolution tool and then the documentation tool before it answers.
Check the answer against the real thing. Open the pytest documentation yourself and compare it to what came back. Did the decorator arguments match? Was the snippet written for the version you have installed (uv pip list), or an older one?
Then the harder question: Context7’s library entries are community-contributed, and the tool output lands directly in your context window. How is trusting this different from trusting an answer the model made up? You have moved the risk, not removed it. Name where it moved to.
Other Servers
Context7 is one server of many. GitHub’s hosted server is the other one worth knowing early: same remote shape in opencode.json, pointed at https://api.githubcopilot.com/mcp/, but it needs authentication (opencode mcp auth github, or a read-only fine-grained token as a Bearer header). With it, the agent can list issues and read pull requests on a repo without cloning it.
The difference is worth noticing: Context7 only reads public documentation, so the worst case is a bad answer. A GitHub token scopes what the server can reach in your account, and a write-scoped one lets an agent change things there. Give a server the narrowest access that still does the job, and prefer read-only whenever reading is all you need.
If You Are Ahead
Push your own skills further. Pick one:
- Write a second, field-specific skill. Something you actually repeat: a reference-format checker, a units-consistency pass, a notation linter for your subfield. Give it a sharp
descriptionand run it againstout/report.md. - Chain two writing skills in one pass. Ask the agent to run, say, conciseness and then hedging-language over the same paragraph, and show the combined diff. Read whether the second skill undoes or compounds the first one’s edits, and decide which changes survive.
- Grab a discipline-specific editor. The course ships optional field-specific skills (
math-notation,algorithm-check,model-formulation,english-variant) inskills/writing-extras/. Copy the one that fits your field into.claude/skills/and run it onout/report.md.
That’s it for this lab. If anything is unclear, ask now. The next session builds on this state. If you fell behind, use the checkpoint tag from the last checkpoint box to catch up.