{"slug": "session-11-cheat-sheet-claude-code-the-claude-agent-sdk", "title": "Session 11 Cheat Sheet — Claude Code & the Claude Agent SDK", "summary": "Anthropic's Claude Code and Claude Agent SDK enable developers to build agentic applications by combining a terminal-based agent loop with a programmable SDK. The SDK's query() function returns an async stream of typed messages, and safety is enforced through tool allowlists and permission modes rather than input trust. A typical architecture involves building a chat app skeleton with Claude Code in plan mode, then replacing an echo stub with a real agent loop behind a FastAPI /api/chat endpoint.", "body_md": "A\n\nframeto help you reason through the assignment — concepts, diagrams, and the API map. It deliberately doesnotcontain the answers or filled-in activity code. Instead it gives you thequestions to ask yourselfand themethodto get there. The work — and the learning — is in building the app with Claude Code, reading the message stream, and writing your own conclusions.Source:\n\n`11_Claude_Code/README.md`\n\n(markdown-only session —no notebook; you write your Q1–Q4 answers directly in the README). Guides:`01_Installing…`\n\n,`02_Using…`\n\n,`03_Claude_Agent_SDK.md`\n\n. Deliverable: your own`chat-app/`\n\n.\n\n| You want to… | Reach for | One-liner |\n|---|---|---|\n| Start Claude Code in a project | `claude` (CLI) |\nLaunches the agent loop in the current directory |\n| Explore/scaffold before changing anything | plan mode (Shift+Tab, or `--permission-mode plan` ) |\nRead-only recon; nothing executes until you approve |\n| Give the agent durable project memory | `CLAUDE.md` |\nAuto-loaded into context at the start of every session |\n| Trim / reset the context window | `/compact` , `/clear` |\nManual context-budget control (the Session 3 middleware, by hand) |\n| Run the SDK agent loop | `query(prompt=…, options=…)` |\nReturns an async stream of typed messages until done |\n| Configure the agent | `ClaudeAgentOptions(…)` |\nallowed_tools · permission_mode · max_turns · cwd · resume · mcp_servers |\n| Constrain what the agent can do | `allowed_tools=[\"Read\",\"Glob\",\"Grep\"]` |\nA read-only allowlist = the server-side safety story |\n| Read the loop's steps | the message stream | SystemMessage (init → session_id) · AssistantMessage · ResultMessage.result |\n| Continue a conversation across messages | `resume=session_id` |\nMap each browser `conversation_id` → the SDK `session_id` |\n| Add a custom tool | `@tool` + `create_sdk_mcp_server` |\nWire via `mcp_servers` ; allowlist as `mcp__<server>__<tool>` |\n| Serve the agent to a browser | FastAPI `POST /api/chat` |\nThe seam: one swappable function calls `query()` |\n\n**Anchor:** *In the terminal, YOU are the permission gate. On a headless server there is no human to click \"approve\" — so the tool allowlist + permission mode ARE the gate. Safety comes from what the agent is allowed to do, not from trusting the input.*\n\n```\nflowchart LR\n  subgraph Build[\"Build it WITH Claude Code (plan → implement → verify)\"]\n    You[You] -->|plan mode| CC[Claude Code]\n    CC -->|scaffold| Skel[chat-app skeleton\\nFastAPI + echo stub + CLAUDE.md]\n  end\n  subgraph Run[\"Run the agent BEHIND the app\"]\n    Browser[Browser chat UI] -->|fetch /api/chat| API[FastAPI /api/chat]\n    API -->|query&#40;&#41;| SDK[Agent SDK loop]\n    SDK --> Tools[Read / Glob / Grep + your custom tool]\n    Tools --> SDK\n    SDK -->|ResultMessage.result| API --> Browser\n  end\n  Skel -.->|replace the echo seam| API\n```\n\nASCII fallback:\n\n```\nBUILD (with Claude Code):   you ─plan mode─► Claude Code ─► chat-app skeleton (echo stub + CLAUDE.md)\n                                                                    │  replace the seam\n                                                                    v\nRUN (agent behind the app): browser ─fetch─► /api/chat ─query()─► Agent SDK loop ─► Read/Glob/Grep + custom tool\n                                                  ▲                                          │\n                                                  └────────────  ResultMessage.result  ◄─────┘\n```\n\n**Why this shape?** Two breakout rooms, one arc. First you *build the app with Claude Code* (the agent as your pair-programmer, gated by plan mode). Then you *put an agent inside the app* — the same loop that powers Claude Code, embedded via `query()`\n\n. The load-bearing design fact: **the chat logic lives in one swappable function; /api/chat is the seam** where the echo stub becomes a real agent.\n\n```\n# Install Claude Code (pick one): native installer | brew install claude | winget | npm i -g @anthropic-ai/claude-code\nclaude            # first run → authenticate\n\n# The chat app (built with Claude Code):\nuv init chat-app && cd chat-app       # Python 3.12+, uv\nuv add fastapi uvicorn claude-agent-sdk\nexport ANTHROPIC_API_KEY=...          # server-side only; STRIP before committing\nuv run uvicorn app:app --reload       # serves http://localhost:8000\n```\n\n| Component | Role |\n|---|---|\nClaude Code |\nThe builder — scaffolds + extends the app; you gate it with plan/permission modes |\n`CLAUDE.md` |\nCurated project memory, auto-loaded every session |\nFastAPI `/api/chat` |\nThe server seam — one swappable function that calls the agent |\nAgent SDK `query()` |\nThe runtime — the agent loop embedded in your app |\nTools |\n`Read` /`Glob` /`Grep` (read-only built-ins) + ≥1 custom `@tool` |\n\nClaude Code runs **real side-effecting tools** (Bash/Edit/Write) and picks the next tool itself — so every action can change the world irreversibly. The permission system (`default`\n\n/ `acceptEdits`\n\n/ `plan`\n\n/ `bypassPermissions`\n\n) is the human-in-the-loop gate. **Plan mode is read-only**: propose-before-act. Docs: [https://code.claude.com/docs](https://code.claude.com/docs)\n\nAuto-loaded into context every session → high value per line. Belongs: the run/test commands, the `/api/chat`\n\nseam, conventions, gotchas. Not: code-discoverable detail, transient state, **secrets**. It's the long-term-memory half of Session 3's finite-context discipline (`/compact`\n\n·`/clear`\n\nare the S3 summarization middleware, by hand).\n\n``` python\nfrom claude_agent_sdk import query, ClaudeAgentOptions\nasync for message in query(prompt=\"…\", options=ClaudeAgentOptions(allowed_tools=[\"Read\",\"Glob\",\"Grep\"])):\n    ...   # the whole loop (call → tool → feed result back → repeat) in one call\n```\n\nDocs: [https://docs.anthropic.com/en/api/agent-sdk/overview](https://docs.anthropic.com/en/api/agent-sdk/overview)\n\n`SystemMessage`\n\n(init — carries `session_id`\n\n) · `AssistantMessage`\n\n(text + tool-use blocks) · `UserMessage`\n\n(tool results) · `ResultMessage`\n\n(`.result`\n\n+ usage). This anatomy is what makes progress-streaming and grounded answers possible.\n\n`POST /api/chat`\n\naccepts `{message, conversation_id}`\n\n→ `{reply}`\n\n. The stub calls `query()`\n\nand returns `ResultMessage.result`\n\n. Read-only `allowed_tools`\n\n+ `permission_mode`\n\n+ `max_turns`\n\nare the server-side gate.\n\nEach `query()`\n\nis a fresh conversation unless you resume it. Capture `session_id`\n\nfrom the init `SystemMessage`\n\n; keep a `conversation_id → session_id`\n\ndict; pass `resume=session_id`\n\nso follow-ups carry context.\n\n``` python\nfrom claude_agent_sdk import tool, create_sdk_mcp_server\n@tool(\"count_lines\", \"Count lines in a file\", {\"file_path\": str})\nasync def count_lines(args): ...\nserver = create_sdk_mcp_server(name=\"concierge\", version=\"1.0.0\", tools=[count_lines])\n# options: mcp_servers={\"concierge\": server}, allowed_tools=[..., \"mcp__concierge__count_lines\"]\n```\n\nOn a server there's no human approver. `allowed_tools=[\"Read\",\"Glob\",\"Grep\"]`\n\nmeans the agent **structurally cannot** modify the filesystem, no matter what a user types (prompt injection included). The allowlist is safety, not performance. `max_turns`\n\ncaps runaway loops/cost.\n\nThese are the four questions you answer in the README. Below is the\n\nmethodand thequestions to ask yourself— not the answers. Reason from the guides and your own build.\n\n### Q1 — Why does an agent that runs shell commands need a permission system, and why is plan mode valuable from an empty directory?\n\nAsk yourself: what is the difference between a chat model that gets something wrong and an *agent* that gets something wrong? What could a tool like `Bash`\n\nor `Edit`\n\nactually do? When you started from an empty folder, what did plan mode let you see *before* anything ran — and why does \"empty folder\" raise the stakes versus editing an existing repo? (Revisit Guide 2's \"Permission Modes\".)\n\nAsk yourself: this file is re-read at the start of *every* session — so what earns a permanent seat, and what's cheaper to let the agent rediscover? What would be actively dangerous to put here? Then connect it to Session 3: what did you learn about the context window as a budget, and which S3 tool do `/compact`\n\nand `/clear`\n\necho?\n\nAsk yourself: list what you had to wire by hand in LangGraph that `query()`\n\nnow hands you for free. Then flip it — name something you *could* do in a hand-built graph that the fixed SDK loop won't let you do. Is this a strict upgrade, or a trade? For a read-and-answer concierge, which side of the trade matters?\n\n### Q4 — Why route through `query()`\n\nvs a raw chat completion; what new risk, and how did your controls address it?\n\nAsk yourself: what can the agent *do* that a plain chat completion can't? Now the flip side — those same tools are driven by whatever a stranger types into your chat box, and no human is at the server to approve. What could go wrong? Look at your own `allowed_tools`\n\nand `permission_mode`\n\n: what do they make *impossible*, regardless of the input? (Guide 3's \"Why these controls matter (Question #4)\" is your anchor.)\n\nBuild a FastAPI app where `POST /api/chat`\n\nroutes each message through the Agent SDK `query()`\n\n, configured with a read-only `allowed_tools=[\"Read\",\"Glob\",\"Grep\"]`\n\nallowlist; add conversation memory (map `conversation_id`\n\n→ SDK `session_id`\n\n, pass `resume=`\n\n) and at least one custom tool. Build it *with* Claude Code — plan → implement → verify.\n**Check yourself:** Does the chat answer real questions about a repo, and do the answers actually reflect the files (not a guess)? Ask a follow-up (\"what are *its* dependencies?\") — does context carry? Force your custom tool with a targeted question — does it fire? Is your key ever visible in the browser? Does `chat-app/CLAUDE.md`\n\nname the seam?\n\nPick one: (a) live progress streaming (SSE) so users see tool calls instead of a spinner; (b) a multi-conversation sidebar, each thread its own SDK session; (c) a second custom tool useful for your target repo. Demo it in your Loom and explain the design decision in one paragraph.\n**Check yourself:** does the enhancement visibly work in your demo, and can you justify *why* you chose it? (Any one, working, is enough.)\n\nIf you're curious: connect your Session 8 cat-shop MCP server to your chat app via the SDK's `mcp_servers`\n\noption, so users can browse/cart/checkout in natural language. It's **unscored** — no bonus, no penalty — pure exploration; share findings + a demo in your Loom if you try it.", "url": "https://wpnews.pro/news/session-11-cheat-sheet-claude-code-the-claude-agent-sdk", "canonical_source": "https://gist.github.com/donbr/73d14fdac568c6f0f16f958d3da1f182", "published_at": "2026-07-08 00:10:47+00:00", "updated_at": "2026-07-08 00:28:13.125389+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "large-language-models", "ai-safety"], "entities": ["Anthropic", "Claude Code", "Claude Agent SDK", "FastAPI"], "alternates": {"html": "https://wpnews.pro/news/session-11-cheat-sheet-claude-code-the-claude-agent-sdk", "markdown": "https://wpnews.pro/news/session-11-cheat-sheet-claude-code-the-claude-agent-sdk.md", "text": "https://wpnews.pro/news/session-11-cheat-sheet-claude-code-the-claude-agent-sdk.txt", "jsonld": "https://wpnews.pro/news/session-11-cheat-sheet-claude-code-the-claude-agent-sdk.jsonld"}}