The setup
The capstone is a mini production system: read surfaces, prompt templates, safe automation, guarded mutation, and receipts. If it feels like work, congratulations—you are ready for the boring parts that keep companies out of court.
This is not "one more tutorial server." It is the shape of an internal assistant that will not age into a liability in six weeks because you skipped audit logs and called it MVP.
Picture this
Mental model
If you cannot explain each arrow to security, you are not done.
| Piece | Governance mapping |
|---|---|
| Read resources (code search, docs) | Low risk tier; cacheable; ACL at URI roots |
| Prompt templates | Versioned instructions; review on change |
| Write tools | Mutating; approval queue; identity in audit |
| Approval queue | Human scaffolding; not rubber-stamp theater |
| Audit log (hash-chained JSONL) | Append-only evidence; correlates actor + action |
| Path normalization | Single boundary; traversal defense before FS |
Reads are nouns. Writes are verbs with paperwork. Audits are receipts. I would have shipped it faster; you need the practice.
Walkthrough
Follow the lab and challenge—this is where chapters 11–14 stop being theory:
- Lab:
labs/lab-10-capstone-enterprise-mcp-server— runnpm start, compare withnpm run solutionwhen stuck (not when lazy; there is a difference). - Challenge:
challenges/capstone-internal-dev-assistant— validates you shipped resources, guarded writes, and audit behavior, not just vibes.
Read plane
- Code search resource — parameterized reads inside allowed roots; no shell-shaped "read tools."
- Docs resource — stable URIs for documentation slices; easy to cache and scan.
Resources answer "what can we read?" without giving the model a generic filesystem grab bag.
Write plane and approvals
Write tools require human approval and audit log entries—not optional, not "later." Flow:
- Model proposes write with args
- Host surfaces approval queue (who can approve is a policy decision—document it)
- On approve, execute once; on deny, structured error back to model
- Append audit event: identity, tool, args summary, outcome, server version
Mutations without receipts are hobbies, not enterprise software.
Path traversal defenses
All filesystem paths go through one normalization boundary before touch:
path.resolve/realpathagainst allowed roots- Reject
..and symlinks that escape roots - Never trust model-supplied paths as raw strings
Doing this in the README is not defense. Doing it in CSS is absurd. Doing it in the model is fantasy. Centralize in server code.
Audit log (not console.log theater)
console.log("wrote file");
Fix: append-only structured events with integrity chaining and redaction rules. Fields your incident runbook actually queries: actor, action, target, correlationId, prevHash, timestamp. stdout is for dev; JSONL with hash chain is for investigations.
Threat-model the approval queue: who can approve, what is logged, what is replay-safe, what happens if the approver is compromised. If you cannot answer, you are not done.
Hall of Shame
Hall of shame: Audit log as console.logsecurity
console.log("wrote file");
Fix: append-only structured events with integrity chaining and redaction rules. Your SOC cannot grep your vibes. They need fields.
Why this matters in production
This is the shape of an internal assistant that will not age into a liability: reads are narrow, writes are gated, paths are jailed, audits are real. Ship the capstone, then map every piece to your org's catalog and policy tiers. Brag about the boring parts—that is what impresses people who have seen incidents.
Mini challenge
Threat-model the approval queue: who can approve, what is logged, what is replay-safe, what stops a malicious approver? One page. If it hurts, good—that means you are thinking.
Reflection
What would you cut if you had one day to ship—without lying about risk? Honest cuts are fine; hidden risk is not. "We skipped audit" is not a one-day cut; it is a six-week incident loan.
You can now brag that…
You built the boring parts on purpose—and that is why it is actually impressive. Magnificent, even. I would know.