# Labs — Module 4: Extending AI Intelligence
> Four short labs that ship one of each extension type (skill, MCP,
> hook) and a triage exercise that prevents you from building the
> wrong kind. Each fits in a single focused block; the whole set is
> ~70 minutes.
| Lab | Title | Time | Maps to |
|-----|--------------------------------------------------|--------|----------------------------------|
| 4.1 | Ship one minimal skill | 20 min | §4.2 Skills |
| 4.2 | Install one read-only MCP server | 20 min | §4.3 Model Context Protocol |
| 4.3 | Wire two hooks (lint + secret-scan) | 15 min | §4.4 Hooks |
| 4.4 | Triage 5 capabilities with the decision rubric | 15 min | §4.5 Skill vs MCP vs Hook |
---
## Lab 4.1 — Ship one minimal skill
### Objective
Promote one repetitive procedure into a `SKILL.md` the agent loads
on demand and follows.
### Time
20 minutes.
### Real-world scenarios
- **A — Release process.** Cut version, update changelog, tag,
push tag. STOP before publishing.
- **B — On-call handoff.** Incidents-still-open, follow-ups,
recent pages, blockers for next shift.
- **C — Scaffold a new microservice.** Copy template, rename,
regenerate IDs, register in platform catalog.
### Setup
```bash
mkdir -p .claude/skills/<name>
```
### Steps
1. Write `.claude/skills/<name>/SKILL.md` with frontmatter (`name`,
`description` listing trigger phrases) and a body of ≤10 numbered
steps. Include at least one explicit STOP point.
2. Fresh session. Use one of the trigger phrases. Confirm the skill
loads.
3. Negative test: use an *off-topic* phrase. Confirm the skill does
**not** load.
### Deliverable
`.claude/skills/<name>/SKILL.md` committed + `labs/notes/4.1-skill.md`
with the trigger phrases and a transcript snippet showing the skill
loading.
### Success criteria
- The skill loads on the trigger phrase, in a fresh session.
- The negative test doesn't load it. If it does, narrow the
description.
### Reflection
- Which step needed the most clarifying detail to keep the agent
on rails? That's where your tribal knowledge lived.
### Stretch
- Add a supporting file (template, fixture, script) next to
`SKILL.md` and reference it from the body.
---
## Lab 4.2 — Install one read-only MCP server
### Objective
Give the agent first-class access to one external system without
shelling out. Read-only — mutations belong in Module 6.
### Time
20 minutes.
### Real-world scenarios
- **A — Staging Postgres.** Read-only credential. Agent can answer
"what's the schema for `orders`?" without you pasting `\d+`.
- **B — Linear / Jira.** Agent reads your assigned issues and
recent comments. Cuts the standup-prep ritual.
- **C — Feature-flag service.** Read-only flag state per env.
Pays off during outages and code review.
### Setup
```bash
mkdir -p .claude && touch .claude/mcp.json
```
### Steps
1. Pick an off-the-shelf server matching your scenario. Register it
in `.claude/mcp.json` using `${ENV_VAR}` references (no inline
secrets):
```jsonc
{ "mcpServers": {
"<name>": {
"command": "npx",
"args": ["-y", "<package>", "<scoped-conn-string>"],
"env": { "TOKEN": "${TOKEN}" }
}
} }
```
2. `claude /mcp`. Confirm the server is listed and reports its
tool count.
3. Use it. Prompt the agent in plan mode with a question only the
new server can answer. Watch it invoke the MCP tool.
### Deliverable
`.claude/mcp.json` committed (no inline secrets) +
`labs/notes/4.2-mcp.md` with the server, credential scope, and one
example prompt the agent now answers without you.
### Success criteria
- The agent answered a real question via MCP — not by guessing.
- The credential cannot mutate (confirmed by attempting it).
### Reflection
- What did you used to bridge by pasting data into chat that's now
one tool call away?
### Stretch
- Add a second MCP server. Prompt with a question that joins their
outputs ("for each open Linear issue tagged `database`, summarize
the affected table").
---
## Lab 4.3 — Wire two hooks (lint + secret-scan)
### Objective
Move two repetitive enforcement tasks from prompts to deterministic
hooks. Verify each fires.
### Time
15 minutes.
### Real-world scenarios
- **A — TS team.** PostToolUse on Edit: `pnpm lint --fix`. Plus a
secret-scanner.
- **B — Python team.** PostToolUse on Edit: `ruff check --fix`.
Plus a secret-scanner.
- **C — Infrastructure.** PostToolUse on `*.tf` edits:
`terraform fmt && terraform validate`. Plus a secret-scanner.
### Setup
`mkdir -p scripts && touch scripts/check-secrets.sh && chmod +x scripts/*.sh`.
### Steps
1. Write `scripts/check-secrets.sh` using the §4.4 template (AWS
key, OpenAI/Anthropic key, GitHub PAT, RSA header). Exit 2 on
match.
2. Register two hooks in `.claude/settings.json`:
```jsonc
"hooks": { "PostToolUse": [{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{ "type": "command", "command": "<your formatter>" },
{ "type": "command", "command": "scripts/check-secrets.sh" }
]
}] }
```
3. Trigger each: have the agent make a bad-format edit (lint hook
fires) and another with a fake `sk-...` string (secret hook
blocks). Capture the surfaced messages.
### Deliverable
Hooks committed + `labs/notes/4.3-hooks.md` with the firing
transcripts.
### Success criteria
- Both hooks fire on the intended events.
- The secret hook **blocks** (exit 2) with a *useful stderr message*
— not silently.
### Reflection
- Which `AGENTS.md` rule could you now delete because a hook
enforces it?
### Stretch
- Add a Stop hook that appends one-line session summaries to
`.claude/sessions.log`.
---
## Lab 4.4 — Triage 5 capabilities with the decision rubric
### Objective
Decide where each wished-for capability belongs before any code is
written: Skill / MCP / Hook / `AGENTS.md`.
### Time
15 minutes.
### Real-world scenarios
If you don't have 5 of your own, pick from:
- **A — Backend.** Query staging DB. Run data-quality SQL on
migrations. Snake_case columns. Walk-through for new tenant.
- **B — Frontend.** Screenshot every PR's changed pages. Block
inline styles. Tokens design system not Tailwind. Read Figma.
- **C — Compliance fintech.** Refuse `pii/` commits without
reviewer tag. Look up customers (redacted). No `JSON.parse` on
untrusted input.
### Setup
Open `labs/notes/4.4-rubric.md` with columns:
`# | Capability | Home | Why`.
### Steps
1. List **5 capabilities** you want.
2. Per row, pick a home: Skill (procedure the model decides when
to invoke), MCP (data/tool needed mid-task), Hook (deterministic
on an event), `AGENTS.md` (standing fact every session).
3. Write the *why* in one sentence each.
### Deliverable
The 5-row table + 3 "next sprint" picks (one of each type ideally),
plus a "not yet" list with reasons.
### Success criteria
- At least 3 of the 4 homes appear in the table.
- Every *why* is specific (not "useful").
### Reflection
- Which row was hardest to place? That row likely composes — see
the SQL-on-migration example in §4.5.
### Stretch
- Convert the rubric into a backlog. Each shipped row's PR
description references the row.
---
## Wrap-up
In the repo (committed):
`.claude/skills/<name>/`, `.claude/mcp.json`, hooks in
`.claude/settings.json`, `scripts/check-secrets.sh`.
In `labs/notes/`: `4.1` through `4.4`.
Your CLI is now a platform. Module 5 gives it memory.
**Next:** [Labs — Module 5: Memory Orchestration](05-memory-orchestration-labs.md)