How Coding Agents Work
Published
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
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.
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 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”
Understand scope — Claude reads
CLAUDE.md, lists directory tree, reads files mentioned in the user message. (Tool: Read, Bashls)Locate the bug — Claude reads auth-related files (
auth.js,middleware/auth.js). Searches for the suspect pattern via Bashgrep. (Tool: Read, Bash)Form a hypothesis — Claude returns text: “The JWT expiry check uses
<instead of<=. Here is my plan.” (No tool — pure reasoning output)Edit the file — Claude calls the Edit tool with the old string and the corrected new string. Runtime patches the file. (Tool: Edit)
Run tests — Claude calls Bash:
npm test — auth. Runtime executes, captures output. (Tool: Bash)Interpret results — Claude reads test output. If failures remain, it reads the failing test, forms a new hypothesis, loops back to step 4.
Verify — Claude runs
git diffto confirm only the intended change was made. (Tool: Bash)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.
| Failure | What the runtime does | How recovery happens |
|---|---|---|
| Tool failure (bad path, schema violation) | Catches it, returns the error text as the tool result | Model sees “file not found”, lists the dir, retries with the right path |
| Terminal failure (non-zero exit) | Captures stderr + exit code, feeds both back | Model reads the compiler/test error and edits accordingly |
| Test failure | Returns the failing output | Model edits and re-runs — the core fix-iterate loop |
| Infinite loop / thrash | Loop cap, no-progress detection, wall-clock timeout, user interrupt | Runtime halts and reports — the model can’t be trusted to self-terminate |
| Bad plan | Re-planning on new observations; plan-mode approval gate | Human 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.
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.
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
| Dimension | Cursor | Claude Code |
|---|---|---|
| Context strategy | Index-first: embed repo at open time, retrieve by similarity | Tool-first: agent explicitly reads files it judges relevant |
| Repo understanding | Broad semantic similarity; good at “find files related to X” | Deep structural understanding; good at “read this specific file” |
| Speed to first result | Fast (index lookup ~ms) | Slower (each file read is a tool call round-trip) |
| Index freshness | Must re-index after large changes | Always reads current file state |
| Multi-file edits | Strong (can apply diffs across files in one response) | Strong (iterative edits via Edit tool, verified with tests) |
| Agent mode | Yes — executes multi-step tasks using retrieved context | Yes — the primary mode; loop-based with approval gates |
| Best fit | Large established codebases, exploration and refactor | Precise 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).
| System | Where it runs | How it gets context | Loop / agency | Blast radius |
|---|---|---|---|---|
| ChatGPT (consumer app) | Hosted, sandboxed | You paste it; uploads / sandboxed Python only | Chat + light tool use; weak autonomous loop | Low (sandboxed) |
| Claude Code | Your machine (CLI) | Read-on-demand — agentic, lazy, no index | Strong agent loop: plan, multi-tool, iterate | High — runs with your permissions |
| Cursor | Your IDE (VS Code fork) | Embedding index + similarity retrieval (eager) | Agent mode + inline edits, IDE-integrated | High (editor + tools) |
| Codex (generic CLI/cloud agent) | CLI / cloud runner | Tool-based retrieval over a checkout | Agent loop over a sandboxed checkout | Bounded 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.
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.
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 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.
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?
What is Claude Code, concretely — what is the LLM and what is the runtime?
How does Cursor get context from a large repo, and how is that different from Claude Code?
Name MCP's three primitives and say what each one is.
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?
Why does MCP matter? What was the world like before it?
In the loop, who decides when to stop — and why does that placement matter?
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.
Design the tool registry and guardrails for a coding agent that runs unattended. What goes in?
You're choosing a context strategy for a huge monorepo. How do you reason it through?
A teammate says "just give the agent a Resource for the whole DB and let it read whatever it needs." Push back.
- Restate the goal and name constraints: who calls it, scale, latency budget, security/blast-radius limits.
- Enumerate capabilities and split them read vs mutating — that split drives most later decisions.
- Choose the integration surface: which capabilities are Tools (schema + auth), which data is a host-selected Resource, which flows deserve a Prompt template.
- Define the loop contract: validate → execute → append result → re-invoke, with runtime-owned stops (loop cap, timeout, cost ceiling).
- Guardrails: least privilege, approval/idempotency for mutating actions, schema validation, audit logging.
- Failure modes: stale data, partial writes, retries, prompt injection via tool results — and how each is contained.
- 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.
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?
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.These notes reflect my current understanding and are updated as I learn, build, and discover better explanations.