docs: add 3 deep-dive guides (configuration, MCP, data flow) + update linking
This commit is contained in:
parent
e0379f981d
commit
d62995e2ff
@ -106,4 +106,4 @@ Custom hooks encapsulate complex logic:
|
||||
|
||||
---
|
||||
|
||||
**Previous:** [← API Client](./08-api-client.md)
|
||||
**Previous:** [<- API Client](./08-api-client.md) | **Next:** [Configuration and System Prompt ->](./10-configuration-and-system-prompt.md)
|
||||
|
||||
253
learn/10-configuration-and-system-prompt.md
Normal file
253
learn/10-configuration-and-system-prompt.md
Normal file
@ -0,0 +1,253 @@
|
||||
# 10. Configuration and System Prompt
|
||||
|
||||
> How settings cascade from enterprise policy down to project CLAUDE.md, and how the system prompt is assembled.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Hierarchy
|
||||
|
||||
Claude Code loads settings from **5 layers**, each with strict priority. Higher layers override lower ones.
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
|
||||
flowchart TD
|
||||
subgraph Sources["Configuration Sources — Highest to Lowest Priority"]
|
||||
direction TB
|
||||
ENT["Enterprise / MDM Policy<br/>managed-settings.json<br/>Org-level, cannot override"]:::ent
|
||||
USER["User Settings<br/>~/.claude/settings.json<br/>Per-user global defaults"]:::user
|
||||
PROJ["Project Settings<br/>.claude/settings.json<br/>Per-repo configuration"]:::proj
|
||||
MCP_CFG[".mcp.json<br/>Project MCP servers<br/>Committed to repo"]:::proj
|
||||
CLAUDE_MD["CLAUDE.md files<br/>Instructions, rules,<br/>memory for the model"]:::md
|
||||
end
|
||||
|
||||
ENT --> MERGE
|
||||
USER --> MERGE
|
||||
PROJ --> MERGE
|
||||
MCP_CFG --> MERGE
|
||||
CLAUDE_MD --> MERGE
|
||||
|
||||
MERGE["getInitialSettings<br/>Merge all layers<br/>Higher priority wins"]:::merge
|
||||
|
||||
APPSTATE["AppState.settings<br/>Runtime configuration"]:::state
|
||||
|
||||
MERGE --> APPSTATE
|
||||
|
||||
classDef ent fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
|
||||
classDef user fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef proj fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
|
||||
classDef md fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef merge fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef state fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
### Enterprise / MDM Policy
|
||||
|
||||
Highest priority. Set by organization admins via Mobile Device Management (macOS profiles). Stored at a managed file path. Users **cannot** override these settings. Controls things like:
|
||||
- Allowed/denied MCP servers
|
||||
- Permission mode restrictions
|
||||
- Feature availability
|
||||
- Analytics opt-out
|
||||
|
||||
### User Settings (`~/.claude/settings.json`)
|
||||
|
||||
Per-user defaults. Controls preferences like allowed tools, custom deny rules, and hooks.
|
||||
|
||||
### Project Settings (`.claude/settings.json`)
|
||||
|
||||
Per-repository settings. Committed to the repo so all collaborators share the same configuration.
|
||||
|
||||
### `.mcp.json`
|
||||
|
||||
MCP server definitions for the project. Lives at the repo root. Defines which MCP servers are available. Validated via Zod schema (`McpServerConfigSchema`).
|
||||
|
||||
### CLAUDE.md
|
||||
|
||||
Markdown instruction files that the model reads as part of its system prompt. These are the "memory" files that tell Claude about this specific project.
|
||||
|
||||
---
|
||||
|
||||
## CLAUDE.md Discovery
|
||||
|
||||
CLAUDE.md files are discovered from multiple locations and merged:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#6f42c1', 'primaryBorderColor': '#6f42c1'}}}%%
|
||||
flowchart TD
|
||||
subgraph Discovery["CLAUDE.md Discovery — getMemoryFiles"]
|
||||
GLOBAL["~/.claude/CLAUDE.md<br/>Global instructions"]:::global
|
||||
CWD_WALK["Walk from cwd up to git root<br/>Check each dir for CLAUDE.md<br/>and .claude/CLAUDE.md"]:::walk
|
||||
ADDITIONAL["--add-dir paths<br/>Extra directories to scan"]:::additional
|
||||
end
|
||||
|
||||
FILTER["filterInjectedMemoryFiles<br/>Remove duplicates,<br/>check setting sources"]:::filter
|
||||
|
||||
PARSE["getClaudeMds<br/>Read and concatenate<br/>all discovered files"]:::parse
|
||||
|
||||
CACHE["setCachedClaudeMdContent<br/>Cache for auto-mode<br/>classifier to read"]:::cache
|
||||
|
||||
INJECT["Injected into getUserContext<br/>becomes part of system prompt"]:::inject
|
||||
|
||||
Discovery --> FILTER --> PARSE --> CACHE --> INJECT
|
||||
|
||||
classDef global fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef walk fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
|
||||
classDef additional fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef filter fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
|
||||
classDef parse fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef cache fill:#333,stroke:#888,color:#e0e0e0,stroke-width:1px
|
||||
classDef inject fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
The `--bare` flag skips auto-discovery but still honors explicit `--add-dir` paths.
|
||||
|
||||
---
|
||||
|
||||
## System Prompt Assembly
|
||||
|
||||
The system prompt is what the model "sees" before any conversation messages. It's assembled from multiple pieces:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#e83e8c', 'primaryBorderColor': '#e83e8c'}}}%%
|
||||
flowchart TD
|
||||
subgraph Priority["System Prompt Priority — buildEffectiveSystemPrompt"]
|
||||
OVERRIDE["1. Override System Prompt<br/>Set via loop mode<br/>REPLACES everything"]:::override
|
||||
COORD["2. Coordinator System Prompt<br/>If coordinator mode active"]:::coord
|
||||
AGENT["3. Agent System Prompt<br/>If mainThreadAgentDefinition set<br/>In proactive mode: APPENDED<br/>Otherwise: REPLACES default"]:::agent
|
||||
CUSTOM["4. Custom System Prompt<br/>Via --system-prompt flag"]:::custom
|
||||
DEFAULT["5. Default System Prompt<br/>The standard Claude Code prompt"]:::default
|
||||
end
|
||||
|
||||
APPEND["appendSystemPrompt<br/>Always appended at end<br/>except when override is set"]:::append
|
||||
|
||||
EFFECTIVE["Effective System Prompt<br/>Sent as system param<br/>in API request"]:::result
|
||||
|
||||
OVERRIDE -->|"if set"| EFFECTIVE
|
||||
COORD -->|"if coordinator"| EFFECTIVE
|
||||
AGENT -->|"if agent defined"| EFFECTIVE
|
||||
CUSTOM -->|"if --system-prompt"| EFFECTIVE
|
||||
DEFAULT -->|"fallback"| EFFECTIVE
|
||||
APPEND --> EFFECTIVE
|
||||
|
||||
classDef override fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
|
||||
classDef coord fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
|
||||
classDef agent fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef custom fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef default fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef append fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
|
||||
classDef result fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
### System Prompt Sections
|
||||
|
||||
Individual sections of the system prompt are defined via `systemPromptSection()`:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
|
||||
flowchart LR
|
||||
subgraph Sections["System Prompt Sections"]
|
||||
CACHED["Cached Sections<br/>Computed once at start<br/>Reused every turn<br/>Cleared on /clear or /compact"]:::cached
|
||||
VOLATILE["DANGEROUS Uncached Sections<br/>Recomputed every turn<br/>BREAKS prompt cache"]:::volatile
|
||||
end
|
||||
|
||||
RESOLVE["resolveSystemPromptSections<br/>Check cache, compute if needed"]:::resolve
|
||||
|
||||
PROMPT["Final system prompt string"]:::result
|
||||
|
||||
CACHED --> RESOLVE
|
||||
VOLATILE --> RESOLVE
|
||||
RESOLVE --> PROMPT
|
||||
|
||||
classDef cached fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
classDef volatile fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
|
||||
classDef resolve fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef result fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
Most sections are **cached** (computed once, reused every turn) to preserve prompt cache hits. Volatile sections that recompute every turn are explicitly named `DANGEROUS_uncachedSystemPromptSection` as a warning.
|
||||
|
||||
---
|
||||
|
||||
## Context Injection
|
||||
|
||||
Beyond the system prompt, two context objects are injected into every conversation:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#28a745', 'primaryBorderColor': '#28a745'}}}%%
|
||||
flowchart LR
|
||||
subgraph SystemCtx["getSystemContext — memoized"]
|
||||
GIT["Git status snapshot<br/>branch, status, log,<br/>default branch, user"]
|
||||
INJECT_SYS["System prompt injection<br/>internal debugging only"]
|
||||
end
|
||||
|
||||
subgraph UserCtx["getUserContext — memoized"]
|
||||
CMD["CLAUDE.md content<br/>all discovered files<br/>concatenated"]
|
||||
DATE["Current date<br/>ISO format"]
|
||||
end
|
||||
|
||||
PREPEND["prependUserContext<br/>Prepended to messages<br/>before API call"]:::merge
|
||||
|
||||
SystemCtx --> PREPEND
|
||||
UserCtx --> PREPEND
|
||||
|
||||
classDef merge fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
Both are **memoized** and cached for the duration of the conversation. They're cleared on `/clear` and `/compact`.
|
||||
|
||||
---
|
||||
|
||||
## Markdown Config Discovery
|
||||
|
||||
Commands, agents, skills, and workflows are all loaded via `markdownConfigLoader.ts`:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#fd7e14', 'primaryBorderColor': '#fd7e14'}}}%%
|
||||
flowchart TD
|
||||
subgraph Dirs["Search Directories — Priority Order"]
|
||||
MANAGED["Managed (enterprise)<br/>Policy-level configs"]:::ent
|
||||
USER_DIR["User (~/.claude/)<br/>Personal configs"]:::user
|
||||
PROJ_DIRS["Project (.claude/)<br/>Walk cwd up to git root<br/>Check each ancestor"]:::proj
|
||||
end
|
||||
|
||||
LOADER["loadMarkdownFilesForSubdir<br/>Discover + parse YAML frontmatter<br/>+ markdown content"]:::loader
|
||||
|
||||
DEDUP["Deduplicate by inode<br/>Handle symlinks gracefully"]:::dedup
|
||||
|
||||
FILES["MarkdownFile array<br/>filePath, frontmatter,<br/>content, source"]:::result
|
||||
|
||||
MANAGED --> LOADER
|
||||
USER_DIR --> LOADER
|
||||
PROJ_DIRS --> LOADER
|
||||
LOADER --> DEDUP --> FILES
|
||||
|
||||
classDef ent fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
|
||||
classDef user fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef proj fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
|
||||
classDef loader fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef dedup fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
|
||||
classDef result fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
This same loader powers:
|
||||
- Slash commands (`.claude/commands/`)
|
||||
- Agents (`.claude/agents/`)
|
||||
- Skills (`.claude/skills/`)
|
||||
- Workflows (`.claude/workflows/`)
|
||||
- Output styles (`.claude/output-styles/`)
|
||||
|
||||
Each entry is parsed with YAML frontmatter for metadata (description, tools, etc.) and the markdown body becomes the instructions.
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
- `src/utils/systemPrompt.ts` — `buildEffectiveSystemPrompt()` logic
|
||||
- `src/constants/systemPromptSections.ts` — Section caching system
|
||||
- `src/context.ts` — `getSystemContext()`, `getUserContext()`, git status
|
||||
- `src/utils/markdownConfigLoader.ts` — CLAUDE.md and `.claude/` directory discovery
|
||||
- `src/utils/claudemd.ts` — Memory file reading and concatenation
|
||||
- `src/utils/settings/settings.ts` — Settings merge logic
|
||||
|
||||
---
|
||||
|
||||
**Previous:** [<- UI Architecture](./09-ui-architecture.md) | **Next:** [MCP Deep Dive ->](./11-mcp-deep-dive.md)
|
||||
290
learn/11-mcp-deep-dive.md
Normal file
290
learn/11-mcp-deep-dive.md
Normal file
@ -0,0 +1,290 @@
|
||||
# 11. MCP Deep Dive
|
||||
|
||||
> The Model Context Protocol subsystem -- transports, tool wrapping, authentication, and server lifecycle.
|
||||
|
||||
---
|
||||
|
||||
## What is MCP?
|
||||
|
||||
MCP (Model Context Protocol) is an open protocol for connecting AI models to external tools and data sources. Claude Code is both an **MCP client** (connecting to external MCP servers) and can act as an **MCP server** (exposing its own tools to other systems).
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
|
||||
graph TB
|
||||
subgraph ClaudeCode["Claude Code"]
|
||||
CLIENT["MCP Client<br/>Connects to external servers"]:::client
|
||||
SERVER["MCP Server Mode<br/>Exposes tools to other systems"]:::server
|
||||
TOOLS["42 Built-in Tools<br/>+ MCP tools merged in"]:::tools
|
||||
end
|
||||
|
||||
subgraph External["External MCP Servers"]
|
||||
STDIO_S["stdio servers<br/>Local processes"]:::external
|
||||
SSE_S["SSE/HTTP servers<br/>Remote endpoints"]:::external
|
||||
WS_S["WebSocket servers<br/>Persistent connections"]:::external
|
||||
CLAUDE_AI["claude.ai proxy<br/>Managed connectors"]:::external
|
||||
IDE_S["IDE servers<br/>Editor integration"]:::external
|
||||
end
|
||||
|
||||
CLIENT --> STDIO_S
|
||||
CLIENT --> SSE_S
|
||||
CLIENT --> WS_S
|
||||
CLIENT --> CLAUDE_AI
|
||||
CLIENT --> IDE_S
|
||||
|
||||
STDIO_S --> TOOLS
|
||||
SSE_S --> TOOLS
|
||||
WS_S --> TOOLS
|
||||
CLAUDE_AI --> TOOLS
|
||||
IDE_S --> TOOLS
|
||||
|
||||
classDef client fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef server fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef tools fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
classDef external fill:#333,stroke:#888,color:#aaa,stroke-width:1px,stroke-dasharray: 5 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transport Types
|
||||
|
||||
Claude Code supports **6 transport types** for connecting to MCP servers:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#17a2b8', 'primaryBorderColor': '#17a2b8'}}}%%
|
||||
flowchart TD
|
||||
subgraph Local["Local Transports"]
|
||||
STDIO["stdio<br/>Spawn local process<br/>Communicate via stdin/stdout<br/>Most common type"]:::local
|
||||
SDK_T["sdk<br/>In-process transport<br/>SDK-managed placeholder<br/>Tool calls route to SDK"]:::local
|
||||
end
|
||||
|
||||
subgraph Remote["Remote Transports"]
|
||||
SSE["sse<br/>Server-Sent Events<br/>Legacy remote protocol<br/>OAuth authentication"]:::remote
|
||||
HTTP["http<br/>Streamable HTTP<br/>Modern remote protocol<br/>Per-request timeouts"]:::remote
|
||||
WS["ws<br/>WebSocket<br/>Persistent bidirectional<br/>TLS/proxy support"]:::remote
|
||||
CLAUDEAI["claudeai-proxy<br/>Via claude.ai OAuth<br/>Managed connectors<br/>Auto token refresh"]:::remote
|
||||
end
|
||||
|
||||
subgraph IDE["IDE Transports"]
|
||||
SSE_IDE["sse-ide<br/>IDE SSE connection<br/>No authentication"]:::ide
|
||||
WS_IDE["ws-ide<br/>IDE WebSocket<br/>Auth token in header"]:::ide
|
||||
end
|
||||
|
||||
classDef local fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
classDef remote fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef ide fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Connection Lifecycle
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#28a745', 'actorTextColor': '#e0e0e0', 'actorBorder': '#28a745', 'signalColor': '#28a745', 'noteBkgColor': '#16213e', 'noteTextColor': '#e0e0e0', 'activationBkgColor': '#1b3a1b', 'activationBorderColor': '#28a745'}}}%%
|
||||
sequenceDiagram
|
||||
participant H as useManageMCPConnections
|
||||
participant C as connectToServer (memoized)
|
||||
participant T as Transport Layer
|
||||
participant S as External Server
|
||||
|
||||
H->>H: getAllMcpConfigs - merge all sources
|
||||
H->>H: Batch servers (local=3, remote=20)
|
||||
|
||||
loop For each server
|
||||
H->>C: connectToServer(name, config)
|
||||
activate C
|
||||
|
||||
alt Auth cached as needed
|
||||
C-->>H: return needs-auth
|
||||
else Server disabled
|
||||
C-->>H: return disabled
|
||||
else Policy blocked
|
||||
C-->>H: return not allowed
|
||||
end
|
||||
|
||||
C->>T: Create transport (stdio/sse/http/ws)
|
||||
T->>S: Connect
|
||||
activate S
|
||||
|
||||
alt auth required (401/403)
|
||||
S-->>C: Auth error
|
||||
C-->>H: return needs-auth
|
||||
else connection timeout
|
||||
S-->>C: Timeout (30s default)
|
||||
C-->>H: return error
|
||||
else success
|
||||
S-->>T: Connected
|
||||
deactivate S
|
||||
C->>S: tools/list
|
||||
S-->>C: Available tools
|
||||
C->>C: Wrap as MCPTool objects
|
||||
C-->>H: return connected + tools
|
||||
end
|
||||
deactivate C
|
||||
end
|
||||
|
||||
H->>H: Update AppState.mcp
|
||||
```
|
||||
|
||||
### Batched Connections
|
||||
|
||||
Servers are connected in parallel batches to avoid overwhelming the system:
|
||||
- **Local (stdio) servers**: batch size of 3 (spawning processes is heavy)
|
||||
- **Remote servers**: batch size of 20 (network connections are lighter)
|
||||
|
||||
The batch sizes are configurable via `MCP_SERVER_CONNECTION_BATCH_SIZE` and `MCP_REMOTE_SERVER_CONNECTION_BATCH_SIZE` environment variables.
|
||||
|
||||
---
|
||||
|
||||
## Tool Wrapping
|
||||
|
||||
Each MCP tool is wrapped in a `MCPTool` object that implements the standard `Tool` interface:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#ffc107', 'primaryBorderColor': '#ffc107'}}}%%
|
||||
flowchart LR
|
||||
MCP_TOOL["MCP Server Tool<br/>name, inputSchema,<br/>description"]:::raw
|
||||
|
||||
WRAP["MCPTool wrapper<br/>Implements Tool interface<br/>name: mcp__server__tool"]:::wrap
|
||||
|
||||
subgraph Lifecycle["Tool Call Lifecycle"]
|
||||
VALIDATE["Validate input<br/>via JSON Schema"]:::step
|
||||
PERM["Check permissions<br/>Same system as built-in"]:::step
|
||||
CALL["Forward to MCP server<br/>tools/call RPC"]:::step
|
||||
RESULT["Process result<br/>Truncate if needed<br/>Handle images"]:::step
|
||||
end
|
||||
|
||||
MCP_TOOL --> WRAP --> VALIDATE --> PERM --> CALL --> RESULT
|
||||
|
||||
classDef raw fill:#333,stroke:#888,color:#e0e0e0,stroke-width:1px
|
||||
classDef wrap fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef step fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
### Naming Convention
|
||||
|
||||
MCP tools are named `mcp__<server>__<tool>`:
|
||||
- `mcp__slack__send_message`
|
||||
- `mcp__github__create_issue`
|
||||
- `mcp__ide__getDiagnostics`
|
||||
|
||||
The double-underscore separators prevent ambiguity. In SDK no-prefix mode, tools keep their original names.
|
||||
|
||||
### Description Capping
|
||||
|
||||
MCP tool descriptions are capped at **2,048 characters**. OpenAPI-generated servers have been observed dumping 15-60KB of endpoint docs into descriptions, which wastes context tokens.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Sources
|
||||
|
||||
MCP server definitions come from multiple sources, merged with priority rules:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#dc3545', 'primaryBorderColor': '#dc3545'}}}%%
|
||||
flowchart TD
|
||||
subgraph Sources["MCP Config Sources"]
|
||||
ENT["Enterprise managed-mcp.json<br/>Exclusive control if present"]:::ent
|
||||
GLOBAL["Global ~/.claude/settings.json<br/>mcpServers field"]:::user
|
||||
LOCAL[".claude/settings.local.json<br/>Local project overrides"]:::local
|
||||
PROJECT[".mcp.json at project root<br/>Committed to repo"]:::proj
|
||||
PLUGIN["Plugin MCP servers<br/>From installed plugins"]:::plugin
|
||||
CLAUDEAI["claude.ai connectors<br/>Fetched at startup"]:::claudeai
|
||||
CLI["--mcp-config flag<br/>Runtime override"]:::cli
|
||||
SDK_CFG["SDK mcp_set_servers<br/>Programmatic control"]:::sdk
|
||||
end
|
||||
|
||||
MERGE["getAllMcpConfigs<br/>Merge + dedup + policy filter"]:::merge
|
||||
|
||||
POLICY{"Enterprise policy<br/>allowedMcpServers?<br/>deniedMcpServers?"}:::check
|
||||
|
||||
ALLOWED["Allowed servers<br/>proceed to connect"]:::allow
|
||||
BLOCKED["Blocked servers<br/>silently dropped"]:::deny
|
||||
|
||||
Sources --> MERGE --> POLICY
|
||||
POLICY -->|"allowed"| ALLOWED
|
||||
POLICY -->|"denied"| BLOCKED
|
||||
|
||||
classDef ent fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
|
||||
classDef user fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef local fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
|
||||
classDef proj fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
|
||||
classDef plugin fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef claudeai fill:#1a1a4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef cli fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef sdk fill:#333,stroke:#888,color:#e0e0e0,stroke-width:1px
|
||||
classDef merge fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
classDef check fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef allow fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
classDef deny fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
### Deduplication
|
||||
|
||||
Plugin and claude.ai connector servers are deduplicated against manually-configured servers:
|
||||
- **Signature-based**: Same `command+args` (stdio) or same `url` (remote) = same server
|
||||
- **Manual wins**: If a user manually configured a server, the plugin/connector duplicate is suppressed
|
||||
- **First-loaded wins**: Between plugins, the first one loaded takes priority
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
Remote MCP servers may require OAuth authentication:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#dc3545', 'primaryBorderColor': '#dc3545'}}}%%
|
||||
flowchart TD
|
||||
CONNECT["Connect to remote server"]:::start
|
||||
|
||||
AUTH{"Auth required?"}:::check
|
||||
|
||||
PROVIDER["ClaudeAuthProvider<br/>OAuth flow per server"]:::auth
|
||||
TOKENS["Retrieve/refresh tokens"]:::auth
|
||||
|
||||
OK["Connection established"]:::success
|
||||
NEEDS["Status: needs-auth<br/>Cached for 15 min"]:::needsauth
|
||||
|
||||
CALL["MCP tool call"]:::start
|
||||
CALL_AUTH{"401 on call?"}:::check
|
||||
|
||||
RETRY["Refresh token<br/>retry once"]:::auth
|
||||
MCE["McpAuthError<br/>Update status to needs-auth"]:::error
|
||||
|
||||
CONNECT --> AUTH
|
||||
AUTH -->|"no"| OK
|
||||
AUTH -->|"yes"| PROVIDER --> TOKENS
|
||||
TOKENS -->|"success"| OK
|
||||
TOKENS -->|"failure"| NEEDS
|
||||
|
||||
CALL --> CALL_AUTH
|
||||
CALL_AUTH -->|"no"| OK
|
||||
CALL_AUTH -->|"yes"| RETRY
|
||||
RETRY -->|"success"| OK
|
||||
RETRY -->|"failure"| MCE
|
||||
|
||||
classDef start fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef check fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef auth fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef success fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
classDef needsauth fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
|
||||
classDef error fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
### Session Expiry Handling
|
||||
|
||||
MCP servers return HTTP 404 + JSON-RPC error code `-32001` when a session expires. Claude Code detects this, clears the connection cache, and reconnects automatically (`McpSessionExpiredError`).
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
- `src/services/mcp/client.ts` (3,349 lines) -- Connection manager, tool wrapping, transport creation
|
||||
- `src/services/mcp/config.ts` (1,579 lines) -- Config merging, policy filtering, deduplication
|
||||
- `src/services/mcp/types.ts` -- Type definitions for server configs and connections
|
||||
- `src/services/mcp/auth.ts` -- OAuth provider and step-up authentication
|
||||
- `src/services/mcp/useManageMCPConnections.ts` -- React hook managing connection lifecycle
|
||||
- `src/tools/MCPTool/MCPTool.ts` -- MCPTool wrapper implementing the Tool interface
|
||||
|
||||
---
|
||||
|
||||
**Previous:** [<- Configuration](./10-configuration-and-system-prompt.md) | **Next:** [Data Flow Walkthrough ->](./12-data-flow-walkthrough.md)
|
||||
341
learn/12-data-flow-walkthrough.md
Normal file
341
learn/12-data-flow-walkthrough.md
Normal file
@ -0,0 +1,341 @@
|
||||
# 12. Data Flow Walkthrough
|
||||
|
||||
> Tracing a single user request from keystroke to rendered response -- end to end.
|
||||
|
||||
---
|
||||
|
||||
## The Complete Flow
|
||||
|
||||
This diagram traces exactly what happens when a user types a prompt and presses Enter.
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
|
||||
flowchart TD
|
||||
TYPE["User types in terminal"]:::input
|
||||
PASTE["Paste: text, image, file"]:::input
|
||||
VOICE["Voice input via STT"]:::input
|
||||
IDE_IN["IDE sends prompt"]:::input
|
||||
SDK_IN["SDK submitMessage"]:::input
|
||||
|
||||
PARSE["Parse input<br/>slash commands, @mentions"]:::process
|
||||
SLASH{"Slash<br/>command?"}:::decision
|
||||
LOCAL["Execute locally<br/>clear, compact, model"]:::process
|
||||
ATTACH["Build attachments<br/>images, PDFs, CLAUDE.md"]:::process
|
||||
UMSG["Create UserMessage"]:::process
|
||||
|
||||
SYS["Build system prompt<br/>tool descriptions +<br/>user rules + CLAUDE.md"]:::context
|
||||
GIT["Inject git context<br/>branch, commits, status"]:::context
|
||||
USR["Inject user context<br/>CLAUDE.md, date"]:::context
|
||||
CMP["Run compaction pipeline"]:::context
|
||||
CACHE["Apply cache_control<br/>prompt cache breakpoints"]:::context
|
||||
|
||||
BUILD["Build API request<br/>model, betas, effort,<br/>task_budget, thinking"]:::api
|
||||
STREAM["Stream SSE response<br/>from Anthropic API"]:::api
|
||||
RETRY["withRetry wrapper<br/>429 backoff, 529 fallback"]:::api
|
||||
|
||||
THINK["Thinking blocks"]:::response
|
||||
TEXT["Text blocks"]:::response
|
||||
TOOL_USE["tool_use blocks"]:::response
|
||||
|
||||
VALIDATE["Validate input"]:::toolexec
|
||||
PERMS["Check permissions"]:::toolexec
|
||||
EXEC["Execute tool"]:::toolexec
|
||||
RESULT["Map to tool_result"]:::toolexec
|
||||
FEEDBACK["Push to messages<br/>LOOP BACK"]:::feedback
|
||||
|
||||
RENDER["Render in terminal"]:::output
|
||||
TRANSCRIPT["Persist transcript"]:::output
|
||||
SDK_OUT["Yield SDK events"]:::output
|
||||
COST["Track cost + usage"]:::output
|
||||
|
||||
TYPE --> PARSE
|
||||
PASTE --> PARSE
|
||||
VOICE --> PARSE
|
||||
IDE_IN --> PARSE
|
||||
SDK_IN --> PARSE
|
||||
|
||||
PARSE --> SLASH
|
||||
SLASH -->|"yes"| LOCAL --> RENDER
|
||||
SLASH -->|"no"| ATTACH --> UMSG
|
||||
|
||||
UMSG --> SYS --> GIT --> USR --> CMP --> CACHE
|
||||
|
||||
CACHE --> BUILD --> STREAM
|
||||
STREAM --> RETRY --> STREAM
|
||||
|
||||
STREAM --> THINK --> RENDER
|
||||
STREAM --> TEXT --> RENDER
|
||||
STREAM --> TOOL_USE
|
||||
|
||||
TOOL_USE --> VALIDATE --> PERMS --> EXEC --> RESULT --> FEEDBACK
|
||||
FEEDBACK ==>|"loop back to<br/>compaction"| CMP
|
||||
|
||||
TEXT -->|"end_turn"| TRANSCRIPT --> SDK_OUT --> COST
|
||||
|
||||
classDef input fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
|
||||
classDef process fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef decision fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef context fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef api fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
classDef response fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
|
||||
classDef toolexec fill:#1a1a4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
|
||||
classDef feedback fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:3px
|
||||
classDef output fill:#333,stroke:#888,color:#e0e0e0,stroke-width:1px
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase by Phase
|
||||
|
||||
### Phase 1: Input Capture
|
||||
|
||||
The user's input can arrive from **5 different sources**, all converging into `processUserInput()`:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#17a2b8', 'primaryBorderColor': '#17a2b8'}}}%%
|
||||
flowchart LR
|
||||
subgraph Sources["Input Sources"]
|
||||
KB["Keyboard input<br/>Direct typing in terminal"]
|
||||
PASTE["Clipboard paste<br/>Text, images, files"]
|
||||
VOICE["Voice<br/>STT transcription"]
|
||||
IDE["IDE Bridge<br/>Prompt from editor"]
|
||||
SDK["SDK<br/>Programmatic submitMessage"]
|
||||
end
|
||||
|
||||
PUI["processUserInput<br/>Parse slash commands<br/>Resolve @mentions<br/>Build attachments"]:::process
|
||||
|
||||
subgraph Output["Parse Result"]
|
||||
MSGS["messages array"]
|
||||
SHOULD["shouldQuery boolean"]
|
||||
end
|
||||
|
||||
Sources --> PUI --> Output
|
||||
|
||||
classDef process fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
Slash commands (`/clear`, `/compact`, `/model`) are intercepted here and handled locally without an API call. Everything else proceeds to the model.
|
||||
|
||||
### Phase 2: Context Assembly
|
||||
|
||||
Before sending to the API, the system assembles context from multiple sources:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#6f42c1', 'primaryBorderColor': '#6f42c1'}}}%%
|
||||
flowchart TD
|
||||
subgraph SystemPrompt["System Prompt Assembly"]
|
||||
DEFAULT["Default prompt<br/>Claude Code identity"]
|
||||
TOOL_DESC["Tool descriptions<br/>42+ tool schemas"]
|
||||
SECTIONS["System prompt sections<br/>Cached or volatile"]
|
||||
end
|
||||
|
||||
subgraph UserContext["User Context"]
|
||||
CLAUDE_MD["CLAUDE.md files<br/>Project instructions"]
|
||||
DATE["Current date"]
|
||||
end
|
||||
|
||||
subgraph SystemContext["System Context"]
|
||||
GIT_STATUS["Git snapshot<br/>Branch, status, log"]
|
||||
end
|
||||
|
||||
subgraph Compaction["Compaction Pipeline"]
|
||||
SNIP["Snip compact"]
|
||||
MICRO["Micro compact"]
|
||||
AUTO["Auto compact"]
|
||||
COLLAPSE["Context collapse"]
|
||||
end
|
||||
|
||||
SystemPrompt --> FINAL["Final API request"]:::api
|
||||
UserContext --> FINAL
|
||||
SystemContext --> FINAL
|
||||
Compaction --> FINAL
|
||||
|
||||
classDef api fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
### Phase 3: API Call and Streaming
|
||||
|
||||
The request goes through `claude.ts` with retry logic:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#28a745', 'primaryBorderColor': '#28a745'}}}%%
|
||||
flowchart LR
|
||||
REQ["API Request<br/>messages + system + tools"]:::req
|
||||
|
||||
STREAM["SSE Stream"]:::stream
|
||||
|
||||
subgraph Events["Stream Events in Order"]
|
||||
E1["message_start<br/>model, id, usage"]
|
||||
E2["content_block_start<br/>type + index"]
|
||||
E3["content_block_delta<br/>incremental text/tool JSON"]
|
||||
E4["content_block_stop"]
|
||||
E5["message_delta<br/>stop_reason + final usage"]
|
||||
E6["message_stop"]
|
||||
end
|
||||
|
||||
REQ --> STREAM --> Events
|
||||
|
||||
classDef req fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef stream fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
Each content block is one of three types: **thinking** (internal reasoning), **text** (user-facing response), or **tool_use** (triggers tool execution).
|
||||
|
||||
### Phase 4: Tool Execution Loop
|
||||
|
||||
When the model responds with `tool_use`, the agentic loop kicks in:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#e83e8c', 'primaryBorderColor': '#e83e8c'}}}%%
|
||||
flowchart TD
|
||||
TOOL_USE["Model returns tool_use blocks"]:::start
|
||||
|
||||
PAR{"Multiple tools?<br/>isConcurrencySafe?"}:::check
|
||||
|
||||
PARALLEL["Execute in parallel<br/>Read-only tools"]:::exec
|
||||
SEQUENTIAL["Execute sequentially<br/>Write tools"]:::exec
|
||||
|
||||
VALIDATE["Zod schema validation"]:::step
|
||||
HOOKS_PRE["PreToolUse hooks"]:::step
|
||||
PERM_CHECK["Permission check chain<br/>deny - allow - tool -<br/>hooks - classifier - dialog"]:::step
|
||||
EXECUTE["tool.call(input, context)"]:::step
|
||||
HOOKS_POST["PostToolUse hooks"]:::step
|
||||
MAP_RESULT["Map to tool_result message"]:::step
|
||||
|
||||
INJECT["Inject CLAUDE.md attachments<br/>for newly-discovered dirs"]:::step
|
||||
|
||||
PUSH["Push tool_results to messages<br/>Continue loop"]:::feedback
|
||||
|
||||
TOOL_USE --> PAR
|
||||
PAR -->|"yes"| PARALLEL
|
||||
PAR -->|"no"| SEQUENTIAL
|
||||
|
||||
PARALLEL --> VALIDATE
|
||||
SEQUENTIAL --> VALIDATE
|
||||
|
||||
VALIDATE --> HOOKS_PRE --> PERM_CHECK --> EXECUTE --> HOOKS_POST --> MAP_RESULT --> INJECT --> PUSH
|
||||
|
||||
PUSH ==>|"Back to<br/>compaction"| TOOL_USE
|
||||
|
||||
classDef start fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
classDef check fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
|
||||
classDef exec fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
|
||||
classDef step fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
|
||||
classDef feedback fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:3px
|
||||
```
|
||||
|
||||
The loop continues until the model returns `stop_reason: end_turn`, hits `max_turns`, or is cancelled.
|
||||
|
||||
### Phase 5: Response Rendering
|
||||
|
||||
The final response is rendered and persisted:
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#fd7e14', 'primaryBorderColor': '#fd7e14'}}}%%
|
||||
flowchart LR
|
||||
RESPONSE["Model final response<br/>stop_reason: end_turn"]:::input
|
||||
|
||||
subgraph Rendering["Terminal Rendering"]
|
||||
MD["Markdown rendering<br/>via marked library"]
|
||||
SYNTAX["Syntax highlighting<br/>for code blocks"]
|
||||
DIFF["Diff rendering<br/>for file changes"]
|
||||
end
|
||||
|
||||
subgraph Persistence["Persistence"]
|
||||
TRANSCRIPT["recordTranscript<br/>Persist to disk"]
|
||||
USAGE["Accumulate usage<br/>input + output tokens"]
|
||||
COST["Track cost<br/>per-model pricing"]
|
||||
end
|
||||
|
||||
subgraph SDK_Events["SDK Events"]
|
||||
MSG["SDKMessage stream<br/>Normalized events"]
|
||||
RESULT_E["Result event<br/>total_cost, usage, duration"]
|
||||
end
|
||||
|
||||
RESPONSE --> Rendering
|
||||
RESPONSE --> Persistence
|
||||
RESPONSE --> SDK_Events
|
||||
|
||||
classDef input fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A Concrete Example
|
||||
|
||||
Let's trace what happens when a user types: `fix the bug in utils.ts`
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'actorTextColor': '#e0e0e0', 'actorBorder': '#4a9eff', 'signalColor': '#4a9eff', 'noteBkgColor': '#16213e', 'noteTextColor': '#e0e0e0', 'activationBkgColor': '#2d1b4e', 'activationBorderColor': '#e83e8c'}}}%%
|
||||
sequenceDiagram
|
||||
participant U as User
|
||||
participant R as REPL.tsx
|
||||
participant QE as QueryEngine
|
||||
participant Q as query.ts
|
||||
participant C as claude.ts
|
||||
participant T as Tools
|
||||
|
||||
U->>R: Types "fix the bug in utils.ts"
|
||||
R->>QE: submitMessage(prompt)
|
||||
|
||||
Note over QE: Not a slash command, proceed
|
||||
|
||||
QE->>QE: Persist transcript
|
||||
QE->>Q: query(messages, systemPrompt, tools)
|
||||
|
||||
Note over Q: Turn 1 - Model reads code
|
||||
|
||||
Q->>Q: Run compaction pipeline
|
||||
Q->>C: queryModel(messages)
|
||||
C-->>Q: tool_use: FileRead("utils.ts")
|
||||
Q->>T: Execute FileRead
|
||||
T-->>Q: File contents
|
||||
|
||||
Note over Q: Turn 2 - Model analyzes
|
||||
|
||||
Q->>C: queryModel(messages + file contents)
|
||||
C-->>Q: tool_use: Grep("error pattern")
|
||||
Q->>T: Execute Grep
|
||||
T-->>Q: Search results
|
||||
|
||||
Note over Q: Turn 3 - Model fixes
|
||||
|
||||
Q->>C: queryModel(messages + grep results)
|
||||
C-->>Q: tool_use: FileEdit("utils.ts", patch)
|
||||
|
||||
Note over T: Permission check!
|
||||
T->>U: Allow FileEdit? (Y/n/always)
|
||||
U->>T: Y
|
||||
|
||||
T-->>Q: Edit applied
|
||||
|
||||
Note over Q: Turn 4 - Model confirms
|
||||
|
||||
Q->>C: queryModel(messages + edit result)
|
||||
C-->>Q: text: "Fixed the bug" + end_turn
|
||||
|
||||
Q-->>QE: Terminal result
|
||||
QE->>QE: Record transcript, track usage
|
||||
QE-->>R: Render response
|
||||
R->>U: Display formatted answer
|
||||
```
|
||||
|
||||
This shows the typical pattern: **read first, analyze, then write** -- with permission checks only on the write operation.
|
||||
|
||||
---
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
1. **Multiple entry points, single pipeline**: Whether input comes from keyboard, IDE, or SDK, it all converges into the same `processUserInput -> query -> claude.ts` pipeline.
|
||||
|
||||
2. **Compaction runs every turn**: The 5-stage compaction pipeline runs *before every single API call*, not just when limits are hit.
|
||||
|
||||
3. **Tools loop back**: Tool results are pushed to messages, and the entire pipeline (including compaction) runs again. This is the "agentic" part -- the model keeps going until it's done.
|
||||
|
||||
4. **Permissions only interrupt writes**: Read-only tools (FileRead, Grep, Glob) in default mode flow through silently. Only write operations (FileEdit, Bash, FileWrite) trigger permission dialogs.
|
||||
|
||||
5. **Everything is streamed**: From SSE events to async generators to React state updates, nothing blocks waiting for a full response. The user sees tokens as they arrive.
|
||||
|
||||
---
|
||||
|
||||
**Previous:** [<- MCP Deep Dive](./11-mcp-deep-dive.md)
|
||||
@ -39,6 +39,9 @@ Start from the top and work down, or jump to whatever interests you:
|
||||
| 7 | [Extension Model](./07-extension-model.md) | Skills, plugins, hooks, sub-agents, and swarms |
|
||||
| 8 | [API Client](./08-api-client.md) | `claude.ts` — streaming, retries, caching, and model fallback |
|
||||
| 9 | [UI Architecture](./09-ui-architecture.md) | React Ink, 113 components, and the 896KB REPL |
|
||||
| 10 | [Configuration and System Prompt](./10-configuration-and-system-prompt.md) | Settings hierarchy, CLAUDE.md discovery, prompt assembly |
|
||||
| 11 | [MCP Deep Dive](./11-mcp-deep-dive.md) | Transports, tool wrapping, auth, server lifecycle |
|
||||
| 12 | [Data Flow Walkthrough](./12-data-flow-walkthrough.md) | End-to-end trace of a single user request |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user