Core System

Query Pipeline

The complete journey from pressing Enter to Claude's response

~8 min read

0

Core lines of code

0

Pipeline files

0

Processing stages

The query pipeline is like a complete restaurant ordering process.

You (the customer) say something to the waiter → the waiter translates it into a complete order the kitchen understands (adding your allergies, table number, etc.) → the kitchen cooks (Claude thinks) → if ingredients are needed, someone goes to the storeroom (tool calls) → cooking resumes → the dish is served (response).

The whole process is streaming — like omakase, served course by course, not all at once.

Message Pipeline #

Click each node for details

preprocess
inject context
send request
tool_use
results

System Prompt Construction #

The complete instructions Claude actually receives

~5KB

Default system prompt

Defines Claude Code's identity, capabilities, and behavioral norms

~1KB

User environment

OS, shell, working directory, git status, current date

~20KB

Tool definitions

Names, descriptions, and input JSON Schemas for 45+ tools

Variable

Project rules

User-defined rules from CLAUDE.md / .claude/config.json

Variable

Memory attachments

Saved memories and file references from prior conversations

What the API Actually Sees #

Real request and response structures from Claude Code

This is what Claude Code sends to the Anthropic API. The system prompt, conversation history, and all 45+ tool definitions are packed into a single streaming request.

// POST https://api.anthropic.com/v1/messages (stream: true)
{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 16384,
  "stream": true,
  "system": [
    {
      "type": "text",
      "text": "You are Claude Code, Anthropic's official CLI..."
      // ~5KB of system prompt
    },
    {
      "type": "text",
      "text": "# Environment\nPlatform: darwin\nShell: zsh\nCwd: /Users/dev/myproject\n..."
    }
  ],
  "messages": [
    {
      "role": "user",
      "content": "Help me fix the failing test in src/utils.ts"
    }
  ],
  "tools": [
    {
      "name": "Bash",
      "description": "Execute shell commands...",
      "input_schema": {
        "type": "object",
        "properties": {
          "command": { "type": "string" },
          "timeout": { "type": "number" }
        },
        "required": ["command"]
      }
    }
    // ... 44 more tool definitions
  ],
  "metadata": {
    "user_id": "user_abc123"
  }
}

Streaming #

Why does the response appear character by character?

Imagine the difference between a faucet and a bucket. The traditional approach waits for the bucket to fill up (complete response). Claude Code uses faucet mode — water flows to your glass as soon as it comes out. Technically called an 'async generator', it transmits data bit by bit. Benefits: fast response, low memory, cancel anytime.

Tool Call Loop #

How does Claude keep calling tools until the task is done?

  1. 1 Claude's response contains tool_use blocks ("I want to read src/main.tsx")
  2. 2 Permission system evaluates: is this operation safe? → execute if passed
  3. 3 Tool results appended to conversation history as tool_result messages
  4. 4 Call the API again with the updated history — Claude sees the results and decides next step
  5. 5 Repeat until Claude returns stop_reason: "end_turn" (task complete)

Interactive Code Walkthrough #

Click through the query pipeline execution, step by step

src/query.tsEntry: submitMessage()
Step 1 of 6
1// src/query.ts — The core conversation loop
2async function* submitMessage(
3 prompt: string,
4 deps: QueryDeps
5): AsyncGenerator<MessageUpdate> {
6 // Assemble full context for Claude
7 const systemPrompt = (deps);
8 const messages = (deps.messages, prompt);
9
10 // Stream response from API
11 const stream = ({
12 model: deps.model,
13 system: systemPrompt,
14 messages,
15 tools: deps.tools,
16 });
17
18 for await (const event of stream) {
19 yield processStreamEvent(event);
20 }
21
22 // If Claude called tools, execute them and loop
23 if (hasToolUse(response)) {
24 yield* (response.toolUses, deps);
25 yield* submitMessage("", deps); // ← recursive
26 }
27}

Everything starts here. submitMessage() is an async generator that orchestrates the entire conversation turn — from receiving the user's prompt to streaming back Claude's response.

Click highlighted function names to follow the call chain

← → arrow keys to navigate