Lesson 3 · Stage 2: Tools in Practice · visual edition

How Coding Agents Work

~30 min · 3 modules · Claude Code · Cursor · MCP deep dive

Published

Coding agents workClaude codeCursorMCPAgent loop

Your bar: explain step-by-step how Claude Code executes a real task, how Cursor retrieves context from a large repo, and how MCP turns any business function into a standardized tool an agent can call.

Claude Code is an agent with filesystem + terminal tools

Cursor is an agent with an embedding index

MCP is the USB-C for tools

all three use the same agent loop underneath

4

How Claude Code Actually Works

Claude Code is a runtime that wraps Claude with filesystem, git, terminal, and MCP tools — and nothing more complicated than that.

User Claude Code Runtime orchestrates the agent loop Claude LLM tool result Plan file (optional) Read / Write Files Git commit, diff, log Terminal (Bash) MCP Servers
Claude Code = Claude (the LLM) + a runtime that exposes filesystem, git, terminal, and MCP as tools. The loop runs until Claude returns a final answer.

IsA CLI agent runtime that wraps the Claude API with a specific toolset: file read/write, Bash execution, git operations, and MCP server connections. Claude chooses which tool to call; the runtime executes it.

Why it exists To let an LLM take real actions in a developer environment — read code, edit files, run tests, commit, and call external services — without the developer writing orchestration code.

Like (world)A senior developer with access to the codebase, terminal, and git. You describe the problem; they decide what to read, what to change, and how to verify it. You review the diff.

Like (code)A CLI application where the “business logic” is an LLM and the “I/O” is filesystem + Bash. The agent loop is the event loop. Tool results are the I/O callbacks.

✗ “Claude Code runs code by generating and evaluating it internally”

✓ Claude Code emits a Bash tool call; the runtime executes it in your shell and returns stdout/stderr back to Claude

🖥️ Memory rule: Claude Code = Claude + tools. The LLM describes what to do; the runtime does it. You are the approval gate.

Walkthrough: “Fix the authentication bug”

  1. Understand scope — Claude reads CLAUDE.md, lists directory tree, reads files mentioned in the user message. (Tool: Read, Bash ls)

  2. Locate the bug — Claude reads auth-related files (auth.js, middleware/auth.js). Searches for the suspect pattern via Bash grep. (Tool: Read, Bash)

  3. Form a hypothesis — Claude returns text: “The JWT expiry check uses < instead of <=. Here is my plan.” (No tool — pure reasoning output)

  4. Edit the file — Claude calls the Edit tool with the old string and the corrected new string. Runtime patches the file. (Tool: Edit)

  5. Run tests — Claude calls Bash: npm test — auth. Runtime executes, captures output. (Tool: Bash)

  6. Interpret results — Claude reads test output. If failures remain, it reads the failing test, forms a new hypothesis, loops back to step 4.

  7. Verify — Claude runs git diff to confirm only the intended change was made. (Tool: Bash)

  8. Report — Claude returns the final summary: what the bug was, what was changed, what tests confirm it. Loop ends.

Failure handling: errors are data, not exceptions

The defining property of the loop is that a failure is just another observation fed back to the model — up to limits the runtime enforces. The model adapts to errors; it does not decide when to stop.

FailureWhat the runtime doesHow recovery happens
Tool failure (bad path, schema violation)Catches it, returns the error text as the tool resultModel sees “file not found”, lists the dir, retries with the right path
Terminal failure (non-zero exit)Captures stderr + exit code, feeds both backModel reads the compiler/test error and edits accordingly
Test failureReturns the failing outputModel edits and re-runs — the core fix-iterate loop
Infinite loop / thrashLoop cap, no-progress detection, wall-clock timeout, user interruptRuntime halts and reports — the model can’t be trusted to self-terminate
Bad planRe-planning on new observations; plan-mode approval gateHuman catches it before damage, or new evidence forces a re-plan

The crucial design point: the hard stops live in the runtime, never in the model. Left alone, the model will retry the same failing command forever or burn the token budget. Bounded retries with backoff, a loop cap, and a cost ceiling are what make the agent safe to run unattended — and for mutating tools (a retried git push or a charge), idempotency is the difference between a retry and an incident.

Memory check — M4
  • What happens when Claude Code “runs a test”?

    → it calls a Bash tool; the runtime executes the shell command and returns stdout/stderr to Claude

  • What is the role of the plan file in Claude Code?

    → it’s a structured text artifact Claude writes (and the runtime can read back) to track multi-step intent across the loop

  • Why does Claude Code read files explicitly rather than using an embedding index?

    → it uses the agent tool-call loop to read what it judges relevant, giving precise file-level control without requiring an upfront indexing step

A user reports Claude Code is reading files that seem irrelevant to the task. What is likely causing this and how would you fix it?

Without a good initial prompt + CLAUDE.md scoping, the agent reads broadly to form context. Fix by: providing a tighter task description; using CLAUDE.md to specify project structure so Claude knows where relevant code lives; using /compact to drop old context that is pulling in stale references; and breaking the task into smaller scoped steps so each loop iteration has a clear, bounded goal.

How does Claude Code handle a failing test during an iterative fix loop?

It reads the test output (stderr/stdout returned by the Bash tool), identifies which assertion failed and why, re-reads the relevant source code if needed, forms a new hypothesis, makes a targeted edit, and re-runs the test — repeating until all tests pass or the max iteration limit is hit. Each iteration adds tool results to the context window, consuming token budget. Long loops on large repos can exhaust context; breaking the task into focused sub-tasks is the mitigation.

5

How Cursor Actually Works

Cursor front-loads the context problem. It indexes your repo at open time so any query can retrieve relevant code instantly.

PHASE 1 — INDEX TIME PHASE 2 — QUERY TIME stored Repo files Chunking Vector index (disk) Embedding model User query Embed query Similarity search vs vector index Context assembly LLM Edit generation
Cursor pre-computes relevance. When you ask a question, retrieval is a fast vector search — not a file-by-file read.

Is An IDE with an embedded coding agent that pre-indexes your repo via embeddings, retrieves relevant code snippets by semantic similarity at query time, and passes them as context to the LLM.

Why it exists To solve the context problem at IDE speed — for a 1M-token codebase you can’t read file-by-file. A vector index makes “find the most relevant code” a millisecond operation.

Like (world)A librarian who has read and catalogued every book. When you ask a question, they don’t re-read the library — they retrieve the right pages instantly from their index.

Like (code) Elasticsearch for your codebase. Documents are code chunks; queries are developer intent; results are injected into the LLM context window.

✗ “Cursor reads the whole codebase on every query, like a human developer would”

✓ Cursor reads the codebase once at index time; each query is a fast similarity search against pre-computed embeddings

🔍 Memory rule: Cursor = vector search + LLM. Index-time cost is paid once. Query-time cost is a fast lookup. The trade-off is index freshness vs read-on-demand precision.

Cursor vs Claude Code

DimensionCursorClaude Code
Context strategyIndex-first: embed repo at open time, retrieve by similarityTool-first: agent explicitly reads files it judges relevant
Repo understandingBroad semantic similarity; good at “find files related to X”Deep structural understanding; good at “read this specific file”
Speed to first resultFast (index lookup ~ms)Slower (each file read is a tool call round-trip)
Index freshnessMust re-index after large changesAlways reads current file state
Multi-file editsStrong (can apply diffs across files in one response)Strong (iterative edits via Edit tool, verified with tests)
Agent modeYes — executes multi-step tasks using retrieved contextYes — the primary mode; loop-based with approval gates
Best fitLarge established codebases, exploration and refactorPrecise bug fixes, new feature implementation, test-driven work

Three axes that place any coding tool

Features blur and change monthly; these three architectural axes do not. Any tool sits somewhere on each: (1) does an autonomous loop exist (chat vs agent), (2) how is context found (you paste it · index-and-retrieve · read-on-demand), and (3) how big is the blast radius (hosted sandbox vs your machine).

SystemWhere it runsHow it gets contextLoop / agencyBlast radius
ChatGPT (consumer app)Hosted, sandboxedYou paste it; uploads / sandboxed Python onlyChat + light tool use; weak autonomous loopLow (sandboxed)
Claude CodeYour machine (CLI)Read-on-demand — agentic, lazy, no indexStrong agent loop: plan, multi-tool, iterateHigh — runs with your permissions
CursorYour IDE (VS Code fork)Embedding index + similarity retrieval (eager)Agent mode + inline edits, IDE-integratedHigh (editor + tools)
Codex (generic CLI/cloud agent)CLI / cloud runnerTool-based retrieval over a checkoutAgent loop over a sandboxed checkoutBounded to its sandbox

ChatGPT is “chat with optional tools.” Cursor and Claude Code are full agents that differ mainly on retrieval philosophy — index-first vs read-on-demand. Codex sits in the same agent family with a sandboxed-checkout execution model.

Memory check — M5
  • What does Cursor do at repo-open time that Claude Code does not?

    → embeds and indexes the entire repo into a vector store for fast similarity retrieval

  • What is the main trade-off of index-first vs read-on-demand?

    → index-first is faster and scales to large repos but can miss structural/logical relationships; read-on-demand is precise but slower and consumes more context budget per task

  • In Cursor’s agent mode, what does “context assembly” do?

    → takes the similarity-search results and assembles them into the token budget before calling the LLM, prioritizing highest-similarity chunks

When would you use Claude Code instead of Cursor for a coding task?

Use Claude Code when you need: precise iterative fixing (run tests, read output, fix, repeat); tasks that require Bash/shell commands or git operations; or when the task is well-scoped and the correct files are already known. Use Cursor when you need: broad codebase exploration; multi-file refactors where you don’t know which files are relevant; or IDE-integrated tab completion and inline edits during active development.

What is the main weakness of an embedding-based retrieval system for code?

Semantic similarity does not equal logical or structural relevance. An embedding search for “authentication” may miss files that implement auth logic but use domain-specific naming conventions. Import graphs and call graphs carry structural relationship information that embeddings do not capture. Cursor partially mitigates this with additional signals (file structure, user-provided @mentions), but it remains a known limitation — especially in large monorepos with non-obvious dependency chains.

6

MCP — USB-C for tools

Before MCP every agent had proprietary tool bindings. MCP makes any tool callable by any agent without custom integration code.

Business Function (e.g. Check Invoice Status) Tool (function / API wrapper) Tool Registry What tools exist? MCP Tool Standardized description + schema MCP Server JSON-RPC / stdio / SSE MCP Client (Agent runtime) Agent LLM
MCP standardizes the tool-agent interface. Write one MCP server; every MCP-compatible agent can use your tools without any additional integration.

IsA protocol (Model Context Protocol) that standardizes how agents discover and call external tools. An MCP Server exposes tools/resources/prompts; an MCP Client (in the runtime) connects, discovers them, and invokes them on the LLM’s behalf.

Why it exists Before MCP, every agent framework had its own tool format — OpenAI function calling, Anthropic tool use, LangChain tools, etc. MCP is the shared interface: write once, run in any compatible agent.

Like (world) USB-C. Before it: every device had its own cable. After it: one port, all devices. MCP is USB-C for tool integrations.

Like (code)A gRPC/REST service registry combined with a standard API gateway. The MCP server is the service. The MCP client is the API gateway. Tool discovery is service registry lookup. Tool invocation is a standard RPC call.

✗ “MCP is an AI feature — it only works with LLMs”

✓ MCP is just a protocol over JSON-RPC; the server is ordinary code that doesn’t know or care about LLMs — it exposes tools, the client calls them

🔌 Memory rule: MCP = standardized tool interface. The server owns the capability; the agent runtime owns the invocation. They are decoupled by the protocol.

MCP Server Tools Functions the agent can call: check_invoice, send_email, query_db Resources Data the agent can read: file:// URLs, database records, config Prompts Pre-built templates: 'summarize this ticket', 'draft reply'
An MCP server can expose all three primitives. Most start with Tools only; Resources and Prompts are for richer agent-server integration.

The chain from business need to agent tool is: a Business Function (check invoice status) → wrapped in a Tool (a function with input/output schema) → registered in a Tool Registry (the MCP server’s tool list) → described as an MCP Tool (name, description, JSON schema) → served by an MCP Server → discovered by the MCP Client → available to the LLM as a callable tool. The only part that touches agent-specific code is the MCP server declaration. Everything above it is ordinary application logic.

Memory check — M6
  • What is the MCP client’s job in the agent runtime?

    → discover available tools from MCP servers, receive tool-call requests from the LLM, invoke the server, and return results

  • Name three transport options for MCP.

    → stdio (local process pipe), SSE (Server-Sent Events over HTTP), WebSocket — the protocol is transport-agnostic

  • Why does MCP make tool re-use easier than proprietary tool bindings?

    → any MCP-compatible runtime can consume any MCP server without custom code — write the server once, use it across Claude Code, any Claude-based agent, and any other MCP client

Your team is building a billing agent. Walk me through how you’d expose the billing system to the agent using MCP.

(1) Identify the business functions: get_invoice, apply_credit, retry_charge, get_customer_balance. (2) Implement each as a function with validated input/output (e.g. TypeScript or Python with schema validation). (3) Create an MCP server using an MCP SDK; declare each function as an MCP Tool with name, description, and JSON schema for args. (4) Configure the agent runtime to connect to the MCP server via stdio or SSE. (5) At runtime, the agent discovers tools via tools/list; the LLM sees them as available capabilities. (6) When the LLM calls get_invoice, the MCP client routes the call to the server, executes the function, and returns the structured result.

What is the difference between an MCP Tool, an MCP Resource, and an MCP Prompt?

Tool = a function the agent can invoke to take an action or query data (read or write); it has a name, description, and JSON schema, and returns a result. Resource = a URI-addressable, read-only data source (like a file or database row) that the host application chooses to surface to the model — fetched, not invoked. Prompt = a pre-authored template for a common task the user or agent can invoke to get a structured starting prompt (e.g. “draft a support reply”). Tools are the most commonly used; Resources and Prompts add richness for advanced server-agent integration.

Retrieval practice — Modules 4–6

Q1. When Claude Code "runs a test", what actually executes it?

Q2. Cursor's primary advantage over a read-on-demand approach is...

Q3. The MCP "Tools" primitive is best described as...

Q4. In the "fix authentication bug" walkthrough, step 6 says "if failures remain, loop back to step 4." This is...

Q5. What is the main limitation of embedding-based retrieval for code context?

Interview — pick your bar

Answer out loud in ~60s, then reveal. Core = recall · Senior = trade-offs & failure modes · Staff = synthesis under ambiguity · System Design = open design round (a different axis, not a harder level).

Walk the agent tool-loop in one breath: what happens between the LLM and the runtime on each turn?
Hit these points: the LLM is the reasoning model — it doesn't act, it emits a tool call → the runtime (the orchestration layer) validates the call against the tool's schema, then executes it → the result is appended to the context as the next observation → the runtime re-invokes the LLM with that result → loop repeats until the LLM returns a final answer instead of a tool call.
What is Claude Code, concretely — what is the LLM and what is the runtime?
Hit these points: Claude Code = Claude (the LLM, the reasoning model) + a runtime that exposes filesystem read/write, Bash/terminal, git, and MCP as tools → the LLM chooses which tool to call; the runtime executes it on your machine with your permissions → it reads files on demand via tool calls — no pre-built index → you are the approval gate.
How does Cursor get context from a large repo, and how is that different from Claude Code?
Hit these points: Cursor pre-builds an embedding index of the repo at open time (chunk → embed → vector store) → at query time it embeds the question and does a similarity search, then assembles the top chunks into the window → retrieval is a fast lookup, not a file-by-file read → Claude Code instead reads-on-demand: it fetches live file contents via tools each turn, so it always sees current state but pays a round-trip per read.
Name MCP's three primitives and say what each one is.
Hit these points: MCP is a protocol exposing tools, resources, and prompts to AI clients → Tool = an invokable function with a name + JSON schema that takes an action or queries data → Resource = host-selected, read-only data the client surfaces to the model (a URI like file://… or a DB row) — fetched, not freely browsed by the agent → Prompt = a pre-authored template the user/agent invokes (e.g. "draft a support reply").
Cursor's pre-built embedding index vs Claude Code's read-on-demand — what's the real trade-off?
Hit these points: index-first buys speed and scale — similarity search is ~ms even on a 1M-token repo — at the cost of freshness: the index goes stale after edits and must be re-indexed → read-on-demand is always current because it fetches live file state, but each read is a round-trip, so latency and token cost grow with how much it has to explore → embeddings also retrieve by semantic similarity, which can miss structurally-related code under domain-specific names → pick by workload: broad exploration/refactor on a settled repo → index; precise, verify-as-you-go fixes on changing code → read-on-demand.
Why does MCP matter? What was the world like before it?
Hit these points: before MCP every framework had its own tool format — OpenAI function calling, Anthropic tool use, LangChain tools — so each integration was rewritten per agent (N×M glue) → MCP standardizes the tool-agent interface: write the server once, any MCP-compatible client consumes it → it decouples capability (the server owns it) from invocation (the runtime owns it) over plain JSON-RPC → the server is ordinary code that doesn't know it's talking to an LLM → net effect: tool integration becomes a reusable ecosystem instead of bespoke per-vendor wiring.
In the loop, who decides when to stop — and why does that placement matter?
Hit these points: the LLM adapts to errors (errors are just observations fed back), but it does not decide when to stop → hard limits live in the runtime: loop cap, no-progress detection, wall-clock timeout, cost ceiling, user interrupt → left alone the model will retry the same failing command forever or burn the token budget → for mutating tools (a retried git push or a charge) idempotency is what separates a safe retry from an incident → the design rule: agency in the model, stops in the runtime.
An agent keeps reading files that seem irrelevant to the task. Diagnose it.
Hit these points: read-on-demand means a vague task forces the agent to explore broadly to build context → tighten the task description so the goal is bounded → use a project scoping file (CLAUDE.md) so it knows where relevant code lives instead of crawling to find out → compact/drop stale context that's pulling in old references → split the work into smaller scoped steps so each loop iteration has a clear target → note the cost angle: every extra read adds tool results to the window and eats token budget on a long loop.
Design the tool registry and guardrails for a coding agent that runs unattended. What goes in?
Hit these points: registry = each tool declared with a strict input/output schema the runtime validates before execution (reject malformed calls, don't pass them through) → classify tools read vs mutating; gate mutating ones (write, push, deploy) behind approval or dry-run, and make them idempotent so a retry isn't a second action → least privilege: scope filesystem and shell access, deny destructive commands, bound the blast radius → runtime-owned limits: loop cap, per-tool timeout, retry-with-backoff, cost ceiling, no-progress kill → observability: log every tool call + result for audit and replay → the principle — the model proposes, the runtime authorizes, validates, bounds, and records.
You're choosing a context strategy for a huge monorepo. How do you reason it through?
Hit these points: no single answer — place it on the freshness-vs-round-trips axis for the dominant workload → a huge, relatively stable repo with lots of exploration favors a pre-built embedding index for ms-scale retrieval; but embeddings miss structural relationships, so layer in structural signals (import/call graphs, path filters, @mentions) rather than relying on similarity alone → for surgical edits on actively-changing files, read-on-demand avoids stale-index risk → the pragmatic design is hybrid: index for "find the candidate files," read-on-demand to load the exact current versions before editing → account for re-index cost/cadence and the token budget each strategy spends per task.
A teammate says "just give the agent a Resource for the whole DB and let it read whatever it needs." Push back.
Hit these points: that misreads the primitive — an MCP Resource is host-selected, read-only data the client surfaces, not a pipe the agent browses freely → unbounded reads blow the token budget and bury signal in noise, degrading answers → uncurated data is a security/privacy surface: the agent could surface PII or rows it shouldn't → for parameterized, action-shaped access you want a Tool with a schema and authorization, not a firehose Resource → the right design: expose narrow Tools for queries (validated, scoped, audited) and surface only specific Resources the host deliberately chooses — capability stays decoupled from invocation.
Design-round framework — there's no single right answer; the interviewer is watching how you move from a vague ask to a bounded design. Hit these beats:
  1. Restate the goal and name constraints: who calls it, scale, latency budget, security/blast-radius limits.
  2. Enumerate capabilities and split them read vs mutating — that split drives most later decisions.
  3. Choose the integration surface: which capabilities are Tools (schema + auth), which data is a host-selected Resource, which flows deserve a Prompt template.
  4. Define the loop contract: validate → execute → append result → re-invoke, with runtime-owned stops (loop cap, timeout, cost ceiling).
  5. Guardrails: least privilege, approval/idempotency for mutating actions, schema validation, audit logging.
  6. Failure modes: stale data, partial writes, retries, prompt injection via tool results — and how each is contained.
  7. State the trade-offs you took and what you'd revisit at higher scale.
Design an MCP server that exposes a business system (say, a billing platform) to an agent.
A strong answer covers: identify the business functions and split them — reads (get_invoice, get_customer_balance) vs mutating (apply_credit, retry_charge) → implement each as a Tool with a validated JSON input/output schema; make mutating ones idempotent (idempotency key) so a retried call isn't a double charge → expose only deliberately-chosen, read-only Resources (e.g. a specific invoice doc) rather than the whole DB; authorize and scope every call → declare the server with an MCP SDK; the client discovers tools via tools/list and the LLM sees them as capabilities → runtime concerns: transport (stdio/SSE), per-tool timeouts, audit logging of every call+result, and approval gates on money-moving actions → call out failure modes: retries on charges, stale balances, and partial multi-step operations.
Design a coding agent tailored to one specific codebase (e.g. a large internal monorepo). What do you build?
A strong answer covers: start from the codebase's shape — size, change rate, language(s), test setup — and pick a context strategy on the freshness-vs-round-trips axis → likely hybrid: a pre-built embedding index plus structural signals (import/call graphs, path scoping) to find candidate files, then read-on-demand to load current versions before editing → a project scoping file so the agent knows conventions and where things live, cutting wasted exploration → tool registry scoped to this repo: read/write files, run the test command, git, and a few repo-specific Tools (codegen, lint), all schema-validated → loop with runtime-owned stops and a verify step (run tests, git diff) before reporting → guardrails: least-privilege shell, approval before pushing, audit log → trade-offs: index staleness vs read latency, and how re-index cadence is triggered by large changes.
Sources
1. Anthropic, Claude Code documentation — docs.anthropic.com. Tool use, agent loop, file/bash/git tools.
2. Cursor, documentation — cursor.com/docs. Codebase indexing, embeddings, agent mode.
3. Anthropic, MCP specification — modelcontextprotocol.io. Tools, Resources, Prompts, transports.
4. Anthropic, "Building effective agents" — anthropic.com/engineering. Agent patterns and tool use.
Was this lesson helpful? Thanks — noted.

These notes reflect my current understanding and are updated as I learn, build, and discover better explanations.