TAI BUI
Back to curriculum
BuildPython

Capstone — Build a Complete Tool Ecosystem

Phase 13 taught every piece. This capstone wires them into one production-shaped system: an MCP server with tools, resources, prompts, tasks and a UI, OAuth 2.1 at the edge, an RBAC gateway, a multi-server client, an A2A sub-agent call, OTel tracing into a collector, tool-poisoning detection in CI, and an AGENTS.md + SKILL.md bundle. By the end you can defend every architectural choice.

TIME: ~120 minutesPREREQUISITES: Phase 13 · 01 through 21

Learning Objectives

  • Compose an MCP server exposing tools, resources, prompts, and a task with a `ui://` app.
  • Front the server with an OAuth 2.1 gateway that enforces RBAC and pinned hashes.
  • Write a multi-server client that traces with OTel GenAI attributes end-to-end.
  • Delegate part of a workload to an A2A sub-agent; verify opacity is preserved.
  • Package the whole stack with AGENTS.md + SKILL.md so other agents can drive it.

The Problem

Ship the "research and report" system:

  • User asks: "summarize the three most-cited 2026 arXiv papers on agent protocols."
  • System: search arXiv via MCP; delegate paper summarization to a specialized writer agent via A2A; aggregate results; render an interactive report as an MCP Apps ui:// resource; log every step to OTel.

All the primitives from Phase 13 show up. This is not a toy. Production research-assistant systems shipped in 2026 by Anthropic (the Claude Research product), OpenAI (GPTs with Apps SDK), and third parties have this exact shape.

Warm-up0/4
1. Which mechanism does OAuth 2.1 require so that an intercepted authorization code cannot be redeemed by an attacker?Warm-up
Diagram of an authorization code flow. A mobile app sends a login request to an authorization server, the server redirects back with a code, and the app sends the code to the token endpoint. A dashed arrow shows an attacker on the device copying the code.
Typed schematic of the setup in the stem. The numbered steps follow the standard flow.
2. A request passes through five services, and each one writes its own logs. What lets you rebuild the path of that single request afterward?Warm-up
Five boxes labelled service 1 to service 5 in a row with arrows between them, representing one request passing through. Under each box sits a separate box labelled own log.
Typed schematic of the setup in the stem.
3. You stored the SHA-256 hash of a tool's description yesterday. Today the hash of that description is different. What does that tell you?Warm-up
Two cards. The left one, stored yesterday, shows a 16 character prefix of a SHA-256 hash of a tool description. The right one, computed today, shows a different prefix for the hash of that description.
Prefixes are real SHA-256 values of a tool description; the yesterday and today labels are typed. The text is not shown.
4. A job takes several minutes to finish. Which API pattern avoids holding one request open the whole time?Warm-up
A timeline with a long bar labelled the job runs: several minutes. A marker at the left end reads client sends the request, and an arrow along the bottom shows time moving right.
Typed schematic of the setup in the stem.
Check: The Problem0/2
1. What is a capstone in this curriculum?Define
A row of numbered boxes for lessons 01 to 22 of phase 13, followed by a highlighted box 23 marked this lesson: capstone. Below is the task quoted from the lesson about summarizing the three most-cited 2026 arXiv papers on agent protocols.
Lesson numbers from the repo; task quoted from The Problem section.
2. What is the "research and report" scenario?Define
Two cards. The first quotes the user's request to summarize the three most-cited 2026 arXiv papers on agent protocols. The second gives the scenario name research and report.
Quote and name taken from the lesson.

The Concept

Architecture

[user] -> [client] -> [gateway (OAuth 2.1 + RBAC)] -> [research MCP server]
                                                      |
                                                      +- MCP tool: arxiv_search (pure)
                                                      +- MCP resource: notes://recent
                                                      +- MCP prompt: /research_topic
                                                      +- MCP task: generate_report (long)
                                                      +- MCP Apps UI: ui://report/current
                                                      +- A2A call: writer-agent (tasks/send)
                                                      |
                                                      +- OTel GenAI spans

Trace hierarchy

agent.invoke_agent
 ├── llm.chat (kick off)
 ├── mcp.call -> tools/call arxiv_search
 ├── mcp.call -> resources/read notes://recent
 ├── mcp.call -> prompts/get research_topic
 ├── a2a.tasks/send -> writer-agent
 │    └── task transitions (opaque internals)
 ├── mcp.call -> tools/call generate_report (task-augmented)
 │    └── tasks/status polling
 │    └── tasks/result (completed, returns ui:// resource)
 └── llm.chat (final synthesis)

One trace id. Every span has the right gen_ai.* attributes.

Security posture

  • OAuth 2.1 + PKCE with resource indicator pinning audience to gateway.
  • Gateway holds upstream credentials; user never sees them.
  • RBAC: alice has research:read, research:write, can call all tools. bob has research:read, cannot call generate_report.
  • Pinned description manifest: dropped any server whose tool hashes changed.
  • Rule of Two audit: no tool combines untrusted input, sensitive data, and consequential action.

Rendering

The final generate_report task returns content blocks plus a ui://report/current resource. The client's host (Claude Desktop, etc.) renders the interactive dashboard in a sandbox iframe. The dashboard contains a sorted paper list, citation counts, and a button that calls host.callTool('summarize_paper', {arxiv_id}) for any paper the user clicks.

Packaging

The whole thing ships as:

research-system/
  AGENTS.md                     # project conventions
  skills/
    run-research/
      SKILL.md                  # the top-level workflow
  servers/
    research-mcp/               # the MCP server
      pyproject.toml
      src/
  agents/
    writer/                     # the A2A agent
  gateway/
    config.yaml                 # RBAC + pinned manifest

Users deploy with docker compose up. Claude Code, Cursor, Codex, and opencode users can drive the system by invoking the run-research skill.

What each Phase 13 lesson contributed

LessonWhat the capstone uses
01-05Tool interface, provider-portability, parallel calls, schemas, linting
06-10MCP primitives, server, client, transports, resources + prompts
11-14Sampling, roots + elicitation, async tasks, ui:// apps
15-17Tool poisoning, OAuth 2.1, gateway + registry
18A2A sub-agent delegation
19OTel GenAI tracing
20Routing gateway for the LLM layer
21SKILL.md + AGENTS.md packaging
Check: The Concept0/26
1. Which architectural piece in the capstone diagram enforces OAuth 2.1 and RBAC before any request reaches the research MCP server?Define
Two boxes, a client on the left and a research MCP server on the right, joined by a dashed arrow labelled request. A note below says OAuth 2.1 and RBAC have to be enforced before a request reaches the server.
Typed schematic of the setup in the stem.
2. In the capstone's RBAC setup, what distinguishes alice from bob?Define
Two boxes, alice with token tok_alice and bob with token tok_bob. Under each, a dashed box reads RBAC with a question mark.
Typed schematic. Token names from main.py.
3. What does the final generate_report task return, according to the Rendering section?Define
A client calls generate_report, a task call, and a dashed box labelled result with a question mark receives the answer. Below, the three papers from main.py are listed with their arXiv ids and titles.
Paper list copied from main.py. The result type is the question.
4. Which file in the capstone's packaging layout holds the RBAC policy and pinned tool-hash manifest?Define
A directory tree for research-system with AGENTS.md at the top, a skills folder holding run-research/SKILL.md, a servers folder with research-mcp holding pyproject.toml and src, an agents folder with writer, and a gateway folder with config.yaml.
Layout from the lesson's Packaging section, comments left off.
5. What is the Rule of Two audit checking for in the capstone's security posture?Define
A table of the two tools in main.py, arxiv_search and generate_report, with their one-line descriptions and an audit column that holds a question mark for each.
Tool names and descriptions copied from main.py.
6. In this lesson, what does "ecosystem" mean?Define
A card showing the term Ecosystem.
Term from the lesson's Key Terms table.
7. What is the trace hierarchy in the capstone?Define
A tree of the top-level spans from a real run of main.py: agent.invoke_agent with children llm.chat, mcp.call, mcp.call and llm.chat, each marked INTERNAL or CLIENT.
Names and kinds from a real run; deeper spans and ids left out.
8. What is a gateway-issued token?Define
A card showing the term Gateway-issued token with the phrase people use: Transitive auth. Below it, a typed header line reads Authorization: Bearer tok_alice.
Term from the lesson; token string from main.py; the header line is typed.
9. What does the gateway's merged namespace give a client that uses several backend servers?Define
Two cards listing tools: a research server with arxiv_search and generate_report, and a bibliography server with cite_lookup and list_refs. A dashed box below reads tool list the client sees with a question mark.
Research tool names from main.py. The bibliography tools are illustrative.
10. What is the opacity boundary in an A2A call?Define
An orchestrator box named research-orchestrator with an arrow labelled tasks/send to a writer-agent box whose skill is summarize_papers.
Names taken from main.py.
11. What are the three layers in the "three-layer stack" the capstone packages?Define
Three stacked dashed boxes labelled layer 1, layer 2 and layer 3, each with a question mark.
Typed schematic.
12. What does defense-in-depth mean in the capstone's security design?Define
A card showing the term Defense-in-depth.
Term from the lesson's Key Terms table.
13. What is a spec compliance matrix?Define
A card showing the term Spec compliance matrix.
Term from the lesson's Key Terms table.
14. What happens to the writer agent's internal reasoning when it is called via A2A in the capstone's trace hierarchy?Explain
A text tree of a trace: agent.invoke_agent with children llm.chat, mcp.call to arxiv_search, a2a.tasks/send to writer-agent, and a final llm.chat. Under the A2A span is a line marked with question marks for the writer's internal reasoning.
Lines copied from the lesson's Trace hierarchy block, with the child of the A2A span hidden.
15. Why is generate_report exposed as a task rather than as a plain tool call like arxiv_search?Explain
Two rows. arxiv_search with its description is marked plain tool call, and generate_report with its description is marked task.
Names and descriptions copied from main.py.
16. Why does the gateway hold the upstream credentials instead of handing them to the client?Explain
Two boxes, client and gateway, side by side. Below them, a dashed box reads upstream credentials with a question mark.
Typed schematic. The two parties are the ones named in the stem.
17. Why do the MCP and A2A spans in one run share a single trace id?Explain
A table of three spans from a real run, mcp.call, mcp.call and a2a.tasks.send, each with its kind and the same trace id 5bc8fbbc....
Values from the seeded real run of main.py; trace id shortened.
18. How do AGENTS.md and SKILL.md differ in the packaged system?Explain
Two file boxes: AGENTS.md at the repo root and SKILL.md in a skill folder under skills/. Under each, a dashed box reads holds with a question mark.
Locations from the lesson's Packaging tree.
19. How can another agent in Claude Code, Cursor, or Codex reproduce the entire capstone workflow?Explain
A box for another agent such as Claude Code, Cursor or Codex, linked by a dashed arrow with a question mark to a research-system box that lists a research MCP server, a writer agent and a gateway.
Typed schematic. Components named in the lesson; the box is not a full file listing.
20. In the rendered dashboard, how does a click on a paper reach the server?Explain
A mock dashboard titled Agent-protocol arXiv report listing three papers by arXiv id and title, with the second row highlighted as clicked. A dashed arrow with a question mark runs from it to a research server box.
Paper ids and titles from main.py. Mock dashboard, typed.
21. After rollout, the search server quietly rewrites the arxiv_search description to add extra instructions. Which capstone control keeps that change from reaching the model?Analyze
Two cards showing the arxiv_search description before, as in main.py, and after, with extra instructions appended in a placeholder.
Original text from main.py. The added text is a placeholder, not captured.
22. A teammate proposes a tool that reads inbound emails, pulls customer records, and issues refunds. Which capstone check fails, and why?Analyze
A card for a proposed tool named support_assistant, with three bullet points: reads inbound emails, pulls customer records and issues refunds.
Abilities from the stem; the tool name is made up.
23. In a trace viewer, the writer-agent span shows up as its own root trace, not under the orchestrator's trace. What is the most likely cause?Analyze
A trace viewer with two traces. The first trace holds agent.invoke_agent and an mcp.call for generate_report. A second, separate trace holds a single writer-agent span.
First trace id from the seeded real run, second id illustrative. Typed layout.
24. A trace shows the writer agent's prompts and intermediate reasoning as attributes on the orchestrator's spans. Which design property has been lost?Analyze
A card for an a2a.tasks.send span listing a2a.peer writer-agent, a2a.skill summarize_papers, and two extra attributes in red, writer.prompt and writer.reasoning, with placeholder text.
First two attributes from main.py. The red attributes and their text are typed examples.
25. You add a bibliography backend whose search tool has the same name as the research server's search. Which gateway behavior keeps both reachable?Design
Two cards. The research server lists arxiv_search and generate_report. The bibliography server lists a tool also named arxiv_search plus list_refs. A dashed box asks how both stay reachable.
Research tool names from main.py. Bibliography tools are illustrative, with the shared name set by the stem.
26. You add a publish_report tool that emails the report to outside readers, and bob must not be able to use it. Which design fits the capstone?Design
A card for a new tool, publish_report, which emails the report to outside readers, with a note that bob must not be able to call it.
Typed from the stem.

Use It

code/main.py stitches the previous lessons' patterns into one runnable demo. It is all stdlib and all in-process, so you can read it end to end. It runs the research-and-report scenario twice, as alice and then as bob. Every call goes through a gateway with simulated OAuth-style auth. Alice's generate_report runs as a task that makes an A2A call to the writer and returns text plus a ui:// resource, with OTel spans emitted along the way.

What to look at:

  • One trace id across every hop.
  • Gateway policy blocks a second user from writing.
  • The task opens a mcp.task.working span and comes back already completed, returning both text and ui:// content.
  • A2A call's inner state is opaque to the orchestrator.
  • AGENTS.md and SKILL.md are the only files another agent needs to reproduce the workflow.
Check: Use It0/10
1. In the demo's gateway_call, the token, scope and hash checks all run before the backend is dispatched. What does that ordering guarantee?Explain
Four boxes in a row joined by arrows: token check, scope check, hash check, and dispatch to the backend.
Typed from the stem and the Author's Note.
2. In the demo, bob calls generate_report. What do the audit log and the trace show for that call?Apply
A code box showing bob's token tok_bob calling generate_report. Below, two dashed boxes with question marks are labelled audit log and trace spans.
Token from main.py.
3. Bob's token is tok_bob, which holds only research:read. What does gateway_call return for his generate_report call in the demo?Apply
A code box with bob's entry in USERS, whose scopes are research:read, and the call gateway_call with tok_bob and generate_report, followed by a returns line holding a question mark.
Entry and call taken from main.py.
4. Alice calls arxiv_search with the query "agent protocol" against the demo's three papers. What does the result text hold?Apply
The three demo papers listed with arXiv ids and titles. Below, a code box shows the query agent protocol, the filter line from research_arxiv_search, and a result text marked with a question mark.
Papers and the filter line copied from main.py.
5. The demo runs alice and then bob without clearing AUDIT. How many audit rows exist at the end, and how many are 403?Apply
Two cards for the two runs. Run 1 with tok_alice and run 2 with tok_bob each call arxiv_search and then generate_report. Below, two dashed boxes ask for the number of rows in AUDIT and the number with 403.
Typed from the stem and main.py.
6. In the bob run alone, how many spans does the orchestrator emit?Apply
A numbered list of the orchestrator's six steps in a bob run: start the root span, an llm.chat span, gateway_call arxiv_search, gateway_call generate_report, a second llm.chat span, and finish the root span. A dashed box asks for the number of spans emitted.
Steps follow orchestrator() in main.py.
7. In alice's run, which span is the parent of the a2a.tasks.send span?Apply
Seven span names from alice's run in creation order with their kinds, and a dashed box asking for the parent span of a2a.tasks.send.
Names and kinds from a real run of main.py.
8. After startup, someone edits the arxiv_search description in the server's tool list, and alice calls it. What happens in the demo's gateway_call?Apply
A code box. After startup the description of arxiv_search in TOOLS is edited, and then gateway_call is invoked with tok_alice for arxiv_search. The result line holds a question mark.
Names from main.py; the edit itself is not specified in the stem.
9. A caller passes the token "tok_carol", which is not in USERS. What does gateway_call do?Apply
A code box listing the keys of USERS, tok_alice and tok_bob, then a call to gateway_call with tok_carol and a result line holding a question mark.
USERS keys copied from main.py.
10. A reviewer says the demo's clean run proves the RBAC and A2A design is production-ready. What is wrong with that claim?Analyze
A code box with two output lines from a real alice run (trace id and ui bytes) and a quote from a reviewer saying the clean run proves the design is production-ready.
Output lines from a seeded run of main.py; trace id shortened.

Ship It

This lesson produces outputs/skill-ecosystem-blueprint.md. Given a product need (research, summarization, automation), the skill produces the full architecture: which MCP primitives, which gateway controls, which A2A calls, which telemetry, which packaging.

Check: Ship It0/1
1. A teammate asks for a design for a meeting-summarizer system built like the capstone. What should the ecosystem-blueprint skill produce?Design
A product need box reading meeting summarizer feeds an ecosystem-blueprint skill box, which feeds a dashed output box with a question mark.
Skill name from the lesson's Ship It section; product need from the stem.
Author's Note

I'd run main.py first and read the output top to bottom before touching the architecture diagram. It runs the orchestrator twice, once as alice and once as bob, and prints the trace id, the search result, the audit log, and every span with its parent. Bob's generate_report call comes back as insufficient_scope naming research:write, which is the whole RBAC story in one line of output.

The part I'd read in the source is gateway_call. It checks the token, then the required scope, then the pinned hash of the tool description, and only after all three does it open the mcp.call span and dispatch to the backend. The order matters. A request that fails the scope check never reaches the backend, and it never gets a span, only an audit row.

I'd also keep the demo in proportion to the prose. The auth is a dictionary lookup on a token string, and the A2A writer agent is a span with a short sleep, not a real peer. main.py has no resource or prompt handlers, and generate_report returns an already completed task with no polling. So the demo shows the shape and the control flow. The exercises are where you replace the stand-ins with real components, and I'd start with the writer agent, since that is where the opacity boundary stops being a label.

Exercises

  1. Run code/main.py. Note the single trace id and how spans nest. Count how many primitives from Phase 13 the demo touches.

  2. Extend the demo: add a second backend MCP server (e.g. bibliography) and confirm the gateway merges its tools into the same namespace.

  3. Replace the fake A2A writer agent with a real one running on a subprocess. Use the Lesson 19 harness.

  4. Add a PII redaction step in the routing gateway between the orchestrator and the LLM. Confirm emails in the user query get scrubbed.

  5. Write an AGENTS.md for a teammate who will maintain this system. It should take under five minutes to read and give them everything they need to drive the capstone in Cursor or Codex.

Key Terms

TermWhat people sayWhat it actually means
Capstone"Phase-13 integration demo"End-to-end system using every primitive
Research and report"The scenario"Search, summarize, render pattern
Ecosystem"All the pieces together"Server + client + gateway + sub-agent + telemetry + package
Trace hierarchy"Single trace id"Every hop's span shares the trace; parent-child via span ids
Gateway-issued token"Transitive auth"Client sees only gateway's token; gateway holds upstream creds
Merged namespace"All tools in one flat list"Multi-server merge at the gateway, prefix-on-collision
Opacity boundary"A2A call hides internals"Sub-agent's reasoning invisible to orchestrator
Three-layer stack"AGENTS.md + SKILL.md + MCP"Project context + workflow + tools
Defense-in-depth"Multiple security layers"Pinned hashes, OAuth, RBAC, Rule of Two, audit log
Spec compliance matrix"What we ship that the spec requires"Checklist mapping deliverables to 2025-11-25 requirements

Further Reading

Code

Ships With