<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Shipengtao&apos;s Blog</title>
  <subtitle>Notes on AI applications, products going global, and building a one-person company — by Shi Pengtao.</subtitle>
  <id>https://shipengtao.com/en/</id>
  <link rel="alternate" type="text/html" href="https://shipengtao.com/en/"/>
  <link rel="self" type="application/atom+xml" href="https://shipengtao.com/en/atom.xml"/>
  <updated>2026-07-01T00:00:00+08:00</updated>
  <author><name>Shi Pengtao</name></author>
  <entry>
    <title>Understanding Memory: How AI Learns to Remember You</title>
    <id>https://shipengtao.com/en/ai-memory/</id>
    <link rel="alternate" type="text/html" href="https://shipengtao.com/en/ai-memory/"/>
    <published>2026-07-01T00:00:00+08:00</published>
    <updated>2026-07-01T00:00:00+08:00</updated>
    <summary type="text">Understanding Memory: How AI Learns to Remember You What is AI memory, and why is it not the same as a larger context window? This guide…</summary>
    <content type="html"><![CDATA[<h1 id="understanding-memory-how-ai-learns-to-remember-you">Understanding Memory: How AI Learns to Remember You</h1>
<blockquote>
<p>What is AI memory, and why is it not the same as a larger context window? This guide explains four types of memory and the four jobs every memory system performs; examines Generative Agents, MemGPT, Voyager, MemoryBank, and HippoRAG; compares ChatGPT, Claude, and Mem0; and tackles the two hardest questions: what to remember and what to forget.</p>
</blockquote>
<p>Large language models have an easily overlooked property: <strong>they have no memory by default.</strong> Each call starts from a blank slate. The model remembers neither what you just said nor what you discussed last time unless that information is placed into the context window again. When ChatGPT seems to "remember me," that is not an intrinsic model capability; an external <strong>memory system</strong> is storing and retrieving information on its behalf.</p>
<p>This article explains what that system contains, how it works, and how real products implement it. It is a sequel of sorts to the context engineering article: if context engineering decides what belongs in the window for this step, memory decides what is worth preserving across time and when it should be recalled.</p>
<hr>
<h2 id="1-first-clarify-memory--larger-context-window">1. First clarify: memory ≠ larger context window</h2>
<p>This is the most common misconception. Many people assume that expanding a context window from 8K to 1M tokens solves memory. It does not.</p>
<p>context window is the <strong>workbench</strong>, and the memory is the <strong>warehouse</strong>. There are three differences:</p>
<ul>
<li><strong>Persistence</strong>: context window is volatile and will be cleared at the end of a conversation; the memory must exist across sessions, days, and months.</li>
<li><strong>Capacity</strong>: No matter how large the window is, there is an upper limit, and the more crowded it becomes, the lower the signal-to-noise ratio, the worse the effect (context rot, see "context engineering"); the warehouse is theoretically unlimited.</li>
<li><strong>Cost</strong>: Every token in the window must be recalculated and recharged at every step; the information in the warehouse lies quietly, and only a small part is taken into the window when needed.</li>
</ul>
<p>Therefore, the core task of a memory system is not to keep all history in the window, but the opposite: move information outside the window, then retrieve the right small piece at the right time. In one sentence:</p>
<blockquote>
<p>Memory trades abundant storage outside the window for scarce attention inside it.</p>
</blockquote>
<p>This is why an "infinitely long context" cannot replace memory: even if a window could hold a year of conversations, a model would struggle to use all of it effectively, and the cost would be enormous. A warehouse and a workbench serve different purposes.</p>
<hr>
<h2 id="2-the-map-given-by-the-human-brain-several-types-of-memory">2. The map given by the human brain: several types of memory</h2>
<p>The classification of AI memory almost copies the division of human memory by cognitive science. Remember this picture, and all subsequent architectures will fall into place.</p>
<p><strong>short-term memory (Short-term / Working Memory)</strong>: The "working memory" used for the current task is equivalent to the computer's RAM. In LLM, it is basically equal to <strong>current context window</strong> - the ongoing conversation and the return of the tool just obtained. Volatile, limited capacity, fast.</p>
<p><strong>long-term memory (Long-term Memory)</strong>: Information retained across sessions, equivalent to a hard disk. It is further divided into three types. This distinction is very important:</p>
<table>
<thead>
<tr>
<th>type</th>
<th>What to save</th>
<th>example</th>
<th>analogy</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>episodic memory</strong> (Episodic)</td>
<td>Specific events and experiences that have occurred</td>
<td>"Last week you helped me book a flight to London for a meeting."</td>
<td>personal diary</td>
</tr>
<tr>
<td><strong>semantic memory</strong> (Semantic)</td>
<td>abstract facts, concepts, preferences</td>
<td>"The user is a software engineer and prefers concise answers"</td>
<td>knowledge base</td>
</tr>
<tr>
<td><strong>procedural memory</strong> (Procedural)</td>
<td>Learned skills and fixed procedures</td>
<td>"When processing a refund, you must first check the order and then go through the approval process."</td>
<td>Muscle Memory/SOP</td>
</tr>
</tbody>
</table>
<p>A useful judgment method: <strong>episodic memory answers "what happened", semantic memory answers "what is true", procedural memory answers "what to do".</strong> The most solid one for most products today is semantic memory (remember your preferences and facts), followed by episodic memory, and procedural memory is the most difficult and cutting-edge - it means that Agent can "learn a set of practices" from experience and solidify them.</p>
<hr>
<h2 id="3-four-things-a-memory-system-really-does">3. Four things a memory system really does</h2>
<p>Fancy nouns aside, any memory system does four things in a loop. These four verbs are the backbone of your evaluation of any solution:</p>
<p><strong>1) Writing/Encoding</strong> - From this interaction, determine "what is worth remembering" and organize it into a storable memory. The difficulty is <strong>choose</strong>: remembering everything will pollute the future, missing memory will cause amnesia.</p>
<p><strong>2) Storage</strong> - put memory into a carrier outside the window: database, vector library, knowledge graph or pure file. This step determines how to search later.</p>
<p><strong>3) Retrieve</strong> - During the next interaction, determine "which items should be remembered this time" and enter them into context window. If you take too much, it will be noise; if you take too little, it will be insufficient.</p>
<p><strong>4) Update/Forget</strong> - Memories will become outdated and conflict ("He lived in Shanghai last year and moved to Beijing this year"). The system must be able to correct old memories and eliminate useless memories instead of mindless accumulation.</p>
<p>If you read the article "context engineering", you will find that this is exactly the expansion of the two operations "<strong>Write</strong>" and "<strong>Select</strong>" in the time dimension - write = Write, retrieve = Select. Memory is not a new thing, but a branch that grows along the "time axis".</p>
<blockquote>
<p>To judge whether a memory system is good or not, don't look at "how much it can store", but whether it does these four steps accurately - especially the <strong>Writing trade-off</strong> and the <strong>Forgetting update</strong>, these two steps are the most difficult and can widen the gap the most.</p>
</blockquote>
<hr>
<h2 id="4-several-classic-architectures-that-you-must-know">4. Several classic architectures that you must know</h2>
<p>There is more than one classic design for memory. The following five are the skeletons for understanding all memory systems. <strong>Each one pegs a different key ability</strong>. After reading this, you will have a complete "capability map".</p>
<h3 id="1-generative-agents-memory-stream--reflection">1. Generative Agents: memory stream + Reflection</h3>
<p>In 2023, Stanford's famous "Generative Agents" (allowing 25 AI villains to live independently in a virtual town) explained the memory architecture clearly for the first time, and it is still a must-read for beginners. It consists of three parts:</p>
<ul>
<li><strong>memory stream (Memory Stream)</strong>: A continuously appended list of experiences recorded in natural language. Everything the villain sees, hears, says, and does is written down in chronological order.</li>
<li><strong>Retrieval</strong>: It is impossible to fit the entire stream into the window, so each time <strong>score selection</strong> is based on the current situation - the score is a weighted sum of three dimensions: <strong>relevance</strong> (semantic similarity to the current situation), <strong>recency</strong> (the longer I don’t think about it, the lower the score, exponentially decaying over time), <strong>importance</strong> (the score when writing, low for "having breakfast", high for "breaking up with my lover").</li>
<li><strong>Reflection</strong>: The most important step. The Agent regularly reviews recent memories, <strong>synthesizes higher-level conclusions</strong> (summarizes "I seem to like Klaus next door" from scattered interactions), and then writes them back into the stream as new memories. Reflection upgrades memory from "running account" to "insight".</li>
</ul>
<blockquote>
<p>Remember these three words - <strong>memory stream, search by relevance/recency/importance, and reflect</strong>. Almost all memory systems later performed addition and subtraction on this skeleton.</p>
</blockquote>
<h3 id="2-memgpt-treat-llm-as-an-operating-system-layered--self-editing">2. MemGPT: Treat LLM as an operating system (layered + self-editing)</h3>
<p>If Generative Agents gave a paradigm of "retrieval", then <strong>MemGPT</strong> in 2023 (the subtitle of the paper is called <em>Towards LLMs as Operating Systems</em>, now incorporated into the open source framework <strong>Letta</strong>) gave a paradigm of "hierarchical management". Its insight is wonderful: <strong>context window is like the physical memory (RAM) of the operating system, and the storage outside the window is like a disk; the operating system relies on "paging" to swap in and out to create the illusion of "infinite memory" - LLM can also do it.</strong></p>
<table>
<thead>
<tr>
<th>Hierarchy</th>
<th>Location</th>
<th>analogy</th>
<th>illustrate</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Core Memory</strong> (Core)</td>
<td>inside window</td>
<td>RAM</td>
<td>The most critical information that must be visible at all times (user identity, current tasks, key preferences). The resident context has a small capacity and is the most valuable. The model can be rewritten by oneself, added or deleted - it is equivalent to something that is "always remembered".</td>
</tr>
<tr>
<td><strong>Recall Memory</strong> (Recall)</td>
<td>outside the window</td>
<td>disk cache</td>
<td><strong>The system automatically records</strong> the complete conversation history, which is stored outside the window and can be searched by semantics or keywords. Usually it does not occupy the real-time context, and only brings back relevant fragments when needed - to answer "What did we talk about before"</td>
</tr>
<tr>
<td><strong>Archival Memory</strong> (Archival)</td>
<td>outside the window</td>
<td>cold storage</td>
<td>Any long-term knowledge that Agent <strong>actively writes</strong> (refined facts, external documents, notes, <strong>not limited to conversations</strong>), has almost unlimited capacity, and is retrieved by tool calls - answering "What knowledge have I accumulated about this user/project?"</td>
</tr>
</tbody>
</table>
<p>The most critical design is: <strong>The model has a tool to "edit its own memory"</strong> - when the core memory is almost full, it decides which items to move to the archive and which items to transfer back to the core. This changes "memory management" from hard-written rules to the ability of the model itself. This set of "layered + self-editing" ideas has directly affected product-level implementation: For example, Anthropic added a "memory tool" to Claude in September 2025 - giving the model a dedicated file directory, allowing it to add, delete, modify and check by itself, and accumulate knowledge across sessions; combined with "context editing (automatic cleaning of expired tool calls in the window)", official data shows that 84% of tokens were reduced in 100 rounds of web search evaluations, improving compared to the baseline 39%. The same skeleton, productized look.</p>
<h3 id="3-pin-a-key-point-on-each-of-the-other-three-voyager-memorybank-hipporag">3. Pin a key point on each of the other three: Voyager, MemoryBank, HippoRAG</h3>
<ul>
<li><strong>Voyager——The skill library is "procedural memory". ** This autonomous exploration agent in "Minecraft" in 2023 allows the model to <strong>generate executable code</strong> for each new task</strong> and store the run-through programs in natural language description** into the growing "skill library"; next time it encounters a similar task, it can directly retrieve and combine old skills without thinking about it from scratch. This is exactly what the second section says - what is stored is not "facts", but "the ability to do it".</li>
<li>**MemoryBank - Equip memory with a "forgetting curve". ** It brings the <strong>Ebbinghaus forgetting curve</strong> of psychology into AI: each memory has a "strength", which is strengthened when it is recalled, and decays if it is not touched for a long time. During retrieval, semantic similarity and this "retention score" are combined. It provides an elegant machine answer to the seventh section "What to forget" - it is not a one-size-fits-all deletion, but a natural forgetfulness and awakening like a human being.</li>
<li><strong>HippoRAG - map the hippocampus.</strong> It borrows the "hippocampal index theory" from neuroscience: treat LLM as the "neocortex", build another knowledge graph as the "hippocampal index", and use an algorithm similar to PageRank to associate on the graph during retrieval. It is the most theoretically ambitious line of <strong>picture memory</strong>-it is good at <strong>connecting</strong> scattered clues to answer questions.</li>
</ul>
<blockquote>
<p>Putting the five together is a "capability map" of memory design: <strong>Generative Agents is responsible for retrieval and reflection, MemGPT is responsible for hierarchical scheduling, Voyager is responsible for skill accumulation, MemoryBank is responsible for forgetting, and HippoRAG is responsible for association.</strong> Whichever ability you lack, just look at the design of that area.</p>
</blockquote>
<hr>
<h2 id="5-what-does-the-memory-in-the-product-look-like">5. What does the memory in the product look like?</h2>
<p>Put the architecture into the products you use every day:</p>
<p><strong>ChatGPT</strong>: Two layers of memory. One layer is <strong>Saved Memories</strong> - a list of explicit facts that you can view and delete ("I am a vegetarian"), and the model automatically writes in it; the other layer is <strong>Reference Chat History</strong> that will be launched in April 2025 - it does not make a list, but implicitly captures patterns from all your past conversations to make the answers more relevant to you. Both layers can be turned off individually in settings, and temporary conversations do not read or write memory. In 2026, OpenAI added a background mechanism to automatically organize memory, making the memory more and more accurate over time.</p>
<p><strong>Claude</strong>: We are following the "file-based memory tool + contextual editing" route mentioned in the previous section, which is more controllable by developers - the memory is stored in your own infrastructure, and you have full control of the data.</p>
<p><strong>Gemini</strong>: Google has also added memory to Gemini - it can not only remember the personal information you saved in the settings, but also selectively refer to your past conversations to personalize answers. It also supports viewing, editing and closing.</p>
<p><strong>Common Product Philosophy</strong>: Memory is not a black box. Allowing users to be able to see, change, and turn things off is becoming a standard feature - because once the memory is remembered incorrectly or something should not be remembered, the collapse of experience and trust will be doubled.</p>
<hr>
<h2 id="6-engineering-implementation-three-mainstream-approaches">6. Engineering implementation: three mainstream approaches</h2>
<p>If you really want to build your own memory system for storage and retrieval, the industry basically has three routes (often mixed):</p>
<p><strong>Route 1: Full-context/Summary</strong>
The simplest: put all or a summary of the history back into the window. Simple, but it will run into context rot as it grows, which is slow and expensive. Suitable for short-cycle, lightweight scenarios.</p>
<p><strong>Route 2: Vector retrieval (Vector/RAG-style)</strong>
Convert each memory into a vector and store it in the vector library, and then retrieve the most relevant ones based on semantic similarity. This is currently the most mainstream approach. The open source framework <strong>Mem0</strong> is representative: it does not save the original conversation, but uses LLM to extract key facts from the conversation and save them, and only retrieves relevant memories during retrieval. On the long-range conversation memory benchmark <strong>LOCOMO</strong> commonly used in the industry, Mem0 officially reports that its accuracy is about 26% higher than OpenAI's memory solution, latency is about 91% lower, and token costs are saved by about 90%. The core win is "only feeding a small amount of relevant history, rather than the entire history."</p>
<p><strong>Route 3: Knowledge Graph (Graph)</strong>
Save memory as an "entity-relationship" diagram (Zhang San - works for - a certain company). Good at handling <strong>multi-hop reasoning</strong> and <strong>relationship changes over time</strong> ("He changed jobs" only needs to change one edge). Expressive, but expensive to build and maintain.</p>
<table>
<thead>
<tr>
<th>route</th>
<th>Advantages</th>
<th>Weakness</th>
<th>Suitable</th>
</tr>
</thead>
<tbody>
<tr>
<td>Full/Summary</td>
<td>Simple, no information loss</td>
<td>It will collapse as it grows, and it will become expensive.</td>
<td>short session</td>
</tr>
<tr>
<td>vector search</td>
<td>Mainstream, balanced, easy to use</td>
<td>Weaker than relational/sequential reasoning</td>
<td>most products</td>
</tr>
<tr>
<td>Knowledge graph</td>
<td>Strong in relational and sequential reasoning</td>
<td>Build and maintain</td>
<td>Complex fields and strong relationship scenarios</td>
</tr>
</tbody>
</table>
<p>In practice, there is no silver bullet. Mature systems often focus on vectors, supplemented by maps, and abstracts.</p>
<hr>
<h2 id="7-the-two-most-difficult-things-what-to-remember-and-what-to-forget">7. The two most difficult things: what to remember and what to forget</h2>
<p>Storage and retrieval are engineering projects, and the real difficulty is two true or false questions:</p>
<p><strong>Problem 1: What to write?</strong>
Not every sentence is worth remembering. Random memorization will bring about two kinds of disasters: one is <strong>noise</strong> - writing down all irrelevant trivial matters, which will interfere with future retrieval; the other is <strong>memory poisoning</strong> - an early wrong conclusion is written into long-term memory, and then retrieved every time, making mistakes along the way, and even becoming an entry point for attacks (someone specializes in "poisoning" memories). So writing must have trade-offs, deduplication, and <strong>conflict detection</strong>.</p>
<p><strong>Problem 2: What should be forgotten?</strong>
Memories become outdated and contradictory. The user said "I am in graduate school" last year and graduated this year; what I liked last month is boring this month. A memory bank that only increases but never decreases will sooner or later contradict itself. A good system requires <strong>update</strong> (overwriting old ones with new memories), <strong>attenuation</strong> (natural fading out if not used for a long time), and even <strong>active forgetting</strong> (users request deletion, which is also strictly required for compliance).</p>
<blockquote>
<p>In a memory system, storage is the beginner level, retrieval is competence, and forgetting is mastery. Memory that can forget remains alive; otherwise, you do not get an assistant that understands you better over time, but one that carries stale assumptions and grows increasingly stubborn.</p>
</blockquote>
<hr>
<h2 id="8-developers-want-to-add-memory-dont-reinvent-the-wheel-look-at-these-solutions-first">8. Developers want to add memory: Don’t reinvent the wheel, look at these solutions first</h2>
<p>The good news is that by 2026, "adding memory to the Agent" will no longer need to be written from scratch - a number of mature open source frameworks have encapsulated the above routes. For developers who want to get started, the mainstream options are roughly the following (all can be self-hosted):</p>
<table>
<thead>
<tr>
<th>plan</th>
<th>Positioning in one sentence</th>
<th>underlying route</th>
<th>Suitable</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Mem0</strong></td>
<td>The fastest to get started and the most popular</td>
<td>Vector + Graphic + KV Mix</td>
<td>Universal and personalized, want quick results</td>
</tr>
<tr>
<td><strong>Zep / Graphiti</strong></td>
<td>Sequential reasoning is the strongest</td>
<td>Time series knowledge graph</td>
<td>Scenarios where facts change over time</td>
</tr>
<tr>
<td><strong>Letta</strong> (formerly MemGPT)</td>
<td>Stateful Agent Runtime</td>
<td>Layering (core/recall/archival)</td>
<td>Requires an Agent that "can manage its own memory"</td>
</tr>
<tr>
<td><strong>Cognee</strong></td>
<td>The richest search mode and self-improvement</td>
<td>Graph + Vector + Relation Mix</td>
<td>Complex domain, enterprise knowledge extraction</td>
</tr>
<tr>
<td><strong>LangMem</strong></td>
<td>LangGraph native</td>
<td>Relying on LangGraph storage</td>
<td>Projects already using LangGraph</td>
</tr>
<tr>
<td><strong>Built-in on each platform</strong> (such as Claude memory tool)</td>
<td>Don't want to take it by myself</td>
<td>File/Hosted</td>
<td>Just want to use it out of the box</td>
</tr>
</tbody>
</table>
<p>Don’t remember the parameters when choosing a model, remember this main line: <strong>What ability do you lack most, choose a framework that focuses on that ability</strong> - if it’s fast, choose Mem0, if you want to handle timing changes like "Zhang San quits," choose Zep/Graphiti, and if you want a stateful Agent that can manage its own memory, choose Letta.</p>
<p>The most popular new project in 2026 is exactly the opposite answer to the seventh section of "What to remember and what to forget" - <strong>MemPalace</strong>. It was developed by "Resident Evil" heroine <strong>Milla Jovovich</strong> and engineer Ben Sigman, using Claude Code, and is open sourced by MIT. It reached more than 20,000 stars two days after its release in April 2026. It started with a complaint: <strong>"I'm tired of AI deciding what to remember for me and then throwing out the context I really need."</strong></p>
<p>So MemPalace went in the opposite direction and borrowed the ancient "memory palace" mnemonic technique: save the dialogue word for word without summaries, and then use spatial metaphors to organize it into wings (by people and projects) → rooms (by themes) → drawers (for original text). During retrieval, it can be "partitioned" to search accurately, instead of fishing blindly in a large group of vectors. It uses hybrid retrieval (keyword weighting + temporal proximity), and achieved a recall@5 of 96.6% on the LongMemEval benchmark for pure retrieval. The storage backend is pluggable (default is ChromaDB, SQLite/Qdrant/pgvector is also supported) and can be connected to Claude Code, Cursor, etc.</p>
<blockquote>
<p>The value of MemPalace is not only that "a female star wrote a popular open source project", but that it puts the philosophical differences in Section 7 on the table: Should AI automatically cut the memory for you, or should you keep it all and control the structure yourself? ** The former is worry-free, the latter is controllable - there is no standard answer to this, but it reminds you: ** "Let AI decide what to remember" itself is a design decision that requires your own decision.</p>
</blockquote>
<hr>
<h2 id="9-ready-to-use-memory-system-checklist">9. Ready-to-use memory-system checklist</h2>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Do I distinguish between <strong>short-term memory (inside the window)</strong> and <strong>long-term memory (outside the window)</strong>? Didn't you think of "a bigger window" as the answer to memory?</li>
<li class="task-list-item"><input type="checkbox" disabled> In long-term memory, there are three categories: <strong>Semantics/Scenarios/Programs</strong>. Which ones do I need? What do you use to save?</li>
<li class="task-list-item"><input type="checkbox" disabled> Are there any <strong>trade-offs</strong> for writing? Or just keep all the conversations mindless? Is there any duplication and conflict detection?</li>
<li class="task-list-item"><input type="checkbox" disabled> Should the search be sorted based on relevance/recency/importance scores, or should all be returned to the window?</li>
<li class="task-list-item"><input type="checkbox" disabled> Is there an update and forget mechanism? Will old, wrong, expired memories be corrected or eliminated?</li>
<li class="task-list-item"><input type="checkbox" disabled> Can users <strong>see, modify, delete, and close</strong> their own memories (experience + compliance)?</li>
<li class="task-list-item"><input type="checkbox" disabled> Is the storage selection correct? Is <strong>vector</strong> enough? Do I need <strong>map</strong> to handle relationships and timing?</li>
<li class="task-list-item"><input type="checkbox" disabled> Is there any protection against <strong>memory pollution</strong>? Will the wrong conclusion be biased along the way after being written into long-term memory?</li>
<li class="task-list-item"><input type="checkbox" disabled> Have you considered <strong>layering</strong> like MemGPT, or added <strong>reflection</strong> like Generative Agents, so that memory can be upgraded from a running account to an insight?</li>
</ul>
<blockquote>
<p>Models will get smarter and smarter, and windows will get bigger and bigger. But as long as the two "attention is limited and information will be out of date" are true, whether <strong>can remember you and think of the right things at the right time will always be a system that requires careful design, rather than an ability that is given away by the model.</strong></p>
</blockquote>
<hr>
<h2 id="references">References</h2>
<ul>
<li>Packer et al., <a href="https://arxiv.org/abs/2310.08560">MemGPT: Towards LLMs as Operating Systems</a> (2023; now an open source framework <a href="https://www.letta.com/blog/memgpt-and-letta/">Letta</a>)</li>
<li>Park et al., <a href="https://arxiv.org/abs/2304.03442">Generative Agents: Interactive Simulacra of Human Behavior</a> (2023, memory stream / Search / Reflection)</li>
<li>Wang et al., <a href="https://arxiv.org/abs/2305.16291">Voyager: An Open-Ended Embodied Agent with Large Language Models</a> (2023, Skill Library / procedural memory)</li>
<li>Zhong et al., <a href="https://arxiv.org/abs/2305.10250">MemoryBank: Enhancing LLMs with Long-Term Memory</a> (2023, Ebbinghaus Forgetting Curve)</li>
<li>Gutiérrez et al., <a href="https://arxiv.org/abs/2405.14831">HippoRAG: Neurobiologically Inspired Long-Term Memory for LLMs</a> (2024, Hippocampal Index/Graphic Memory)</li>
<li>Mem0, <a href="https://arxiv.org/abs/2504.19413">Building Production-Ready AI Agents with Scalable Long-Term Memory</a> (2025, LOCOMO benchmark); open source warehouse <a href="https://github.com/mem0ai/mem0">mem0ai/mem0</a></li>
<li>Open source memory framework: <a href="https://github.com/getzep/graphiti">Zep/Graphiti</a>, <a href="https://github.com/letta-ai/letta">Letta</a>, <a href="https://github.com/topoteretes/cognee">Cognee</a>, <a href="https://github.com/langchain-ai/langmem">LangMem</a></li>
<li>MemPalace, <a href="https://github.com/milla-jovovich/mempalace">GitHub repository</a> (2026, Milla Jovovich + Ben Sigman, "memory palace" style); <a href="https://www.forbes.com/sites/joshpearce/2026/04/09/milla-jovovich-goes-open-source-guns-blazing-with--top-ai-memory-code/">Forbes report</a></li>
<li>Anthropic / Claude，<a href="https://claude.com/blog/context-management">Managing context on the Claude Developer Platform</a>（2025，memory tool + context editing）</li>
<li>OpenAI, <a href="https://openai.com/index/memory-and-new-controls-for-chatgpt/">Memory and new controls for ChatGPT</a> and <a href="https://help.openai.com/en/articles/8590148-memory-faq">Reference chat history</a> (2025)</li>
<li><a href="https://github.com/Shichun-Liu/Agent-Memory-Paper-List">Memory in the Age of AI Agents: A Survey</a> (2025, review and paper list)</li>
</ul>]]></content>
  </entry>
  <entry>
    <title>Context Engineering: What You Feed the Model Sets Its Ceiling</title>
    <id>https://shipengtao.com/en/context-engineering/</id>
    <link rel="alternate" type="text/html" href="https://shipengtao.com/en/context-engineering/"/>
    <published>2026-06-25T00:00:00+08:00</published>
    <updated>2026-06-25T00:00:00+08:00</updated>
    <summary type="text">Context Engineering: What You Feed the Model Sets Its Ceiling Context engineering determines an AI agent&apos;s ceiling by controlling what the…</summary>
    <content type="html"><![CDATA[<h1 id="context-engineering-what-you-feed-the-model-sets-its-ceiling">Context Engineering: What You Feed the Model Sets Its Ceiling</h1>
<blockquote>
<p>Context engineering determines an AI agent's ceiling by controlling what the model sees. This guide explains four core operations—write, select, compress, and isolate—along with RAG versus long context, memory, KV-cache optimization, practical patterns, and a self-checklist.</p>
</blockquote>
<p>Anyone building agents eventually hits the same wall: the prompt has been tuned to exhaustion, yet the results remain unstable. The problem is often not the wording of that instruction, but what the model can see at each step. This article is about <strong>managing the model's field of view</strong>. It takes engineers and technical decision-makers from concepts to practical patterns, with a checklist you can use directly.</p>
<hr>
<h2 id="1-a-counter-intuitive-fact">1. A counter-intuitive fact</h2>
<p>Many people tune an agent like this: poor result → rewrite the prompt → still poor → add more instructions → cram in every potentially useful document, conversation, and tool description → get an even worse result.</p>
<p>The problem is often not whether the prompt is well written, but whether the model sees the right context at that particular step.</p>
<p>In 2025, the industry converged on a term for this work. Karpathy put it most plainly:</p>
<blockquote>
<p>"Context engineering" is a better term than "prompt engineering." People associate prompts with short, everyday task descriptions, but in any production-grade LLM application, the real craft is <strong>filling the context window with exactly the right information for the next step</strong>.</p>
<p><em>「+1 for "context engineering" over "prompt engineering". People associate prompts with short task descriptions you'd give an LLM in your day-to-day use. When in every industrial-strength LLM app, context engineering is the delicate art and science of filling the context window with just the right information for the next step.」</em></p>
</blockquote>
<p>Shopify CEO Tobi Lütke has expressed the same preference. "Context engineering" more accurately describes the core skill: <em>"the art of providing all the context for the task to be plausibly solvable by the LLM."</em></p>
<p>One sentence to distinguish:</p>
<blockquote>
<p><strong>prompt engineering</strong>: Write a relatively static instruction.
<strong>context engineering</strong>: In a multi-step, ever-changing Agent loop, dynamically decide what to put and what not to put into the limited context window at each step.</p>
</blockquote>
<p>Prompt engineering is a subset of context engineering. Once a task grows from a single question and answer into dozens of autonomous steps, tool calls, and results, the main challenge shifts from writing one instruction to managing the entire window.</p>
<hr>
<h2 id="2-why-context-is-a-scarce-resource">2. Why context is a scarce resource</h2>
<p>Intuitively, the bigger the context window, the better, and 1M token sounds like it can fit anything. But in practice, context is the scarcest resource for three reasons:</p>
<p><strong>1) Attention is limited, and models can "forget the middle."</strong> The classic <em>Lost in the Middle</em> study found that models often overlook key information placed midway through a long context: they attend more strongly to the beginning and end than to the middle. Later, Chroma and others used more systematic experiments to describe <strong>context rot</strong>: performance on the same task declines steadily as context grows, even when needle-in-a-haystack tests suggest the model can still retrieve the fact. <strong>Finding information is not the same as using it well.</strong></p>
<p><strong>2) Irrelevant information will actively interfere.</strong> Every extraneous piece of content in the context is competing for attention with the correct answer. Three common symptoms:</p>
<ul>
<li><strong>Distraction</strong>: Being biased by irrelevant history;</li>
<li><strong>Poisoning</strong>: An early wrong conclusion remains in the context, and the wrong conclusion will be wrong all the way;</li>
<li><strong>Clash</strong>: The data inserted are contradictory to each other, and the model is at a loss.</li>
</ul>
<p><strong>3) Cost and delay increase linearly with token.</strong> Every additional token costs money and time. An Agent that fills up the context is slow, expensive, and inaccurate.</p>
<p>The conclusion is counter-intuitive, but extremely important:</p>
<blockquote>
<p><strong>More context is not necessarily better; a higher signal-to-noise ratio is. Context engineering is the practice of keeping only the high-signal information needed for each step.</strong></p>
</blockquote>
<hr>
<h2 id="3-what-is-in-the-context">3. What is in the context?</h2>
<p>Let's break down the "context" first. What you see when the model is inferring is far more than the user's words, but a whole assembled window:</p>
<table>
<thead>
<tr>
<th>Element</th>
<th>What it contains</th>
<th>Who controls it</th>
</tr>
</thead>
<tbody>
<tr>
<td>System prompts/role settings</td>
<td>Identity, rules, output format</td>
<td>Engineer, relatively static</td>
</tr>
<tr>
<td>Tool definitions</td>
<td>Available tools and how to call them</td>
<td>Engineer + dynamic filtering</td>
</tr>
<tr>
<td>User input</td>
<td>Request for the current turn</td>
<td>User</td>
</tr>
<tr>
<td>Conversation history</td>
<td>Previous rounds of back and forth</td>
<td>Needs management (compression/cropping)</td>
</tr>
<tr>
<td>Tool results</td>
<td>Retrieved documents, API results, errors</td>
<td>Highly dynamic; easiest to overload</td>
</tr>
<tr>
<td>Memory</td>
<td>Long-term information across sessions</td>
<td>Must be written and retrieved</td>
</tr>
</tbody>
</table>
<p>context engineering is to make the decision of "what to add, what to subtract, and how long to keep" for each cell in this table. One of the things that is most likely to get out of control is the "tool return result" - a web page crawl or a database query may contain thousands of tokens.</p>
<hr>
<h2 id="4-four-basic-operations-write-select-compress-isolate">4. Four basic operations: write, select, compress, isolate</h2>
<p>LangChain summarizes all the methods of context engineering into four types of operations. This is the best mental map currently available:</p>
<h3 id="1-writestore-information-outside-the-window">1. Write—store information outside the window</h3>
<p>Not everything has to stay in context. Write intermediate conclusions, plans, and notes<strong>outside the window</strong> and retrieve them when needed:</p>
<ul>
<li><strong>Scratchpad</strong>: Temporary notes within the current task, allowing the model to remember "how many steps have I taken and what have I arrived at".</li>
<li><strong>Memory</strong>: Persistent information (user preferences, project conventions) across tasks and sessions.</li>
</ul>
<h3 id="2-selectretrieve-only-what-is-relevant">2. Select—retrieve only what is relevant</h3>
<p>This is the essence of <strong>RAG (retrieval-augmented generation)</strong>, tool selection, and example selection:</p>
<ul>
<li>Instead of stuffing in the entire knowledge base, retrieve the passages most relevant to the current question;</li>
<li>When there are too many tools, first use a layer of search to filter out "several tools that may be used in this step" instead of listing hundreds of tool definitions;</li>
<li>Even few-shot examples can be dynamically selected based on similarity.</li>
</ul>
<h3 id="3-compressexpress-the-same-information-with-fewer-tokens">3. Compress—express the same information with fewer tokens</h3>
<p>When the context is almost full, it is not roughly truncated, but <strong>compressed</strong>:</p>
<ul>
<li><strong>Summary / compaction</strong>: Summarize the previous dozens of rounds of dialogue into a "current progress summary", discard the original process, and retain the conclusions and unresolved matters;</li>
<li><strong>Crop</strong>: Delete the oldest and most irrelevant parts according to rules.</li>
</ul>
<h3 id="4-isolatesplit-context-across-independent-spaces">4. Isolate—split context across independent spaces</h3>
<p>Split a large task to multiple sub-Agents. Each sub-Agent takes a clean and exclusive context window to do the sub-task, and only returns the "condensed conclusion" to the main Agent. The main Agent's window therefore always looks clean. Sandboxed, standalone runtime environments also fall into this category - isolating operations that may produce massive output.</p>
<blockquote>
<p>Remember these four verbs—write, select, compress, isolate—and you’ll have a toolbox for nearly any contextual problem.</p>
</blockquote>
<p>In the next few sections, we will pick out the most frequently asked questions and the most common mistakes in this framework, and go into depth one by one.</p>
<hr>
<h2 id="5-rag-or-long-context-the-answer-in-2026">5. RAG or long context? The answer in 2026</h2>
<p>When long context window first appeared, many people asserted that "RAG is dying - why bother searching when you can fit the entire book into it?" Two years later, the conclusion is clear: <strong>long context did not kill RAG, but allowed both to return to their respective places.</strong></p>
<ul>
<li><strong>The weakness of long context</strong> is the context rot mentioned in the second section: the more stuffed it is, the easier it is to lose memory in the middle, the slower and more expensive it is. Putting all 500,000 tokens into it does not mean that the model has really "understood" 500,000 tokens.</li>
<li>The value of <strong>RAG</strong> is precisely that it can select the relevant 5,000 tokens from a corpus of 500,000, producing the high-signal, low-noise context that context engineering aims for.</li>
</ul>
<p>Practical options:</p>
<table>
<thead>
<tr>
<th>scene</th>
<th>tendency</th>
</tr>
</thead>
<tbody>
<tr>
<td>The knowledge base is huge and continuously updated</td>
<td>RAG (search + cite sources)</td>
</tr>
<tr>
<td>A single document requires overall understanding (such as reviewing a long contract)</td>
<td>Read long context directly</td>
</tr>
<tr>
<td>Both big and fine</td>
<td>Hybrid: First search for rough screening, and then put the candidate passages into long-context intensive reading.</td>
</tr>
</tbody>
</table>
<p>The more cutting-edge approach is <strong>agentic retrieval (intelligent retrieval)</strong>: It is no longer "retrieve once, get the results and go", but let the agent conduct multiple rounds of searches by itself and judge "whether it is enough and whether to check again", turning the retrieval into a feedback loop.</p>
<hr>
<h2 id="6-memory-make-agent-smarter-across-sessions">6. Memory: Make Agent smarter across sessions</h2>
<p>Memory is the part of context engineering that specifically deals with "time" and is divided into two layers:</p>
<ul>
<li><strong>short-term memory</strong>: Continuity within a single session/task is mainly maintained by compaction and scratchpad in Section 4.</li>
<li><strong>long-term memory</strong>: Information retained across sessions - who the user is, what their preferences are, and what commitments the project has. Behind the "remember your preferences" you see in ChatGPT and Claude is a mechanism of writing (what is worth remembering) + retrieving (what should be remembered this time).</li>
</ul>
<p>The difficulty in memory is never "storage", but two judgments:</p>
<ol>
<li><strong>What to write</strong>: Not all dialogues are worth remembering, and random memorization will contaminate future context;</li>
<li><strong>What to retrieve</strong>: Which items should be remembered this time? If you retrieve too many, it will be noise.</li>
</ol>
<p>After all, the memory system is the application of the two operations "Write + Select" in the time dimension.</p>
<hr>
<h2 id="7-one-of-the-most-overlooked-practices-make-context-cache-friendly">7. One of the most overlooked practices: Make context cache-friendly</h2>
<p>The previous sections have been talking about "what to put and what not to put", and there is another path that almost directly determines the cost that is often ignored - <strong>KV-cache (hint cache)</strong>.</p>
<p>When the model processes the context, it calculates the prefix into a reusable intermediate state (KV-cache). As long as the context prefix of this round is exactly the same as that of the previous round, this part can directly hit the cache without having to recalculate - it is fast and economical (the hit token is usually only charged at a fraction of the original price). In a multi-step Agent, each step carries almost the same system prompts and history, and the cache hit rate almost directly determines billing and latency. There is even a saying in the industry: "KV-cache hit rate is the most important single indicator for production-level Agents."</p>
<p>This leads to several construction principles that are equally important as "signal-to-noise ratio":</p>
<ul>
<li><strong>Stable things are placed in the front, and volatile things are placed in the back.</strong> System prompts and tool definitions that remain unchanged in each round are fixed at the front to ensure the stability of the prefix.</li>
<li><strong>Only append, not rewrite.</strong> New information is appended to the end; once you go back and change the previous content (even if only one timestamp is changed), all subsequent caches will be invalid.</li>
<li><strong>Compression requires timing.</strong> Compaction and cropping will rewrite the prefix and invalidate the cache, so don't trigger it too frequently, and do it once at a cost-effective point.</li>
</ul>
<p>In a word: <strong>context engineering not only determines "whether the model is accurate", but also directly determines "whether the system is expensive or not."</strong></p>
<hr>
<h2 id="8-in-practice-two-common-patterns">8. In practice: two common patterns</h2>
<p><strong>Pattern 1: Compaction.</strong> This is the lifeline of long-running tasks. Take Claude Code as an example: when the context window is nearly full, it does not simply stop. It summarizes the work so far into a compact handoff record—files changed, current state, and next steps—then continues from that record. The key is to <strong>retain open issues and important decisions while discarding the raw process</strong>. Poor compaction makes an agent forget its progress and repeat work.</p>
<p><strong>Pattern 2: Sub-agent context isolation.</strong> The main agent orchestrates while a sub-agent handles a context-heavy task, such as reading 20 files to find bugs. The sub-agent works in a clean window and returns only its condensed conclusions. The main agent's context is never flooded by all 20 files. This is the power of isolation—and one of the most underrated benefits of multi-agent systems: they are a <strong>context-management technique</strong>, not merely a way to run work in parallel.</p>
<hr>
<h2 id="9-what-is-its-relationship-with-these-concepts">9. What is its relationship with these concepts?</h2>
<p>context engineering is surrounded by a ring of confusing words. There is no need to memorize it by rote, just sort it out according to the "type of relationship", and the boundaries will naturally be clear:</p>
<p><strong>① Upstream and downstream - MCP supplies materials from below, and Harness takes care of the details from the outside.</strong></p>
<ul>
<li><strong>MCP (Model Context Protocol)</strong>: Solve the problem of "what can be connected", using a unified protocol to connect external tools and data sources in a standardized manner, which is the "source" of context.</li>
<li><strong>Harness (the engineering shell of the agent)</strong>: In addition to the model weight, all the engineering (tools, status, verification, context management...) that determine whether the intelligence can be stably implemented. Context management is only one of the subsystems, but it is often the first to overturn.</li>
</ul>
<p><strong>② Subset - prompt engineering and RAG, are part of it, not itself.</strong></p>
<ul>
<li><strong>prompt engineering</strong>: <strong>Predecessor and subset</strong> of context engineering. The task has changed from "asking and answering" to "running dozens of steps independently", and the main battlefield has expanded from "writing that sentence" to "managing the entire window."</li>
<li><strong>RAG (Retrieval Enhancement)</strong>: It is just a specific method in the "<strong>select</strong>" type of operation, responsible for "fetching relevant knowledge on demand". <strong>It is a subset of context engineering, not a synonym</strong> - equating the two misses out the other three categories of writing, compression and isolation.</li>
</ul>
<p><strong>③ Alternate route - context engineering vs fine-tuning.</strong></p>
<p>There are two fundamentally different ways to inject knowledge and capabilities into the model: feeding it into the context (in-context, all discussed in this article), or training into the weights (in-weights, that is, fine-tuning).</p>
<ul>
<li>Knowledge is updated quickly, sources must be brought, and additions and deletions must be made at any time → use context (RAG/long context);</li>
<li>To solidify a stable style, format or proprietary capability, and the data is sufficient → consider fine-tuning.</li>
</ul>
<p>Most mature systems use both: <strong>fine-tuning determines the "base color", and context engineering feeds the "current thing".</strong></p>
<p>Concluding in one sentence: <strong>context engineering determines the "field of view" of each step of the model - prompt engineering and RAG are its parts, fine-tuning is its other way, MCP supplies materials below, and Harness takes care of the outside.</strong></p>
<hr>
<h2 id="10-ready-to-use-context-checklist">10. Ready-to-use context checklist</h2>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Can I explain clearly what components the model has in the context it sees at each step?</li>
<li class="task-list-item"><input type="checkbox" disabled> Have the results returned by the tool been "slimmed down" (leaving only conclusions and truncating overly long output)? Or just stuff it back as it is?</li>
<li class="task-list-item"><input type="checkbox" disabled> Is there any "history that is no longer useful" in the context? Is there any regular compression/cropping?</li>
<li class="task-list-item"><input type="checkbox" disabled> Is there a compaction mechanism for long tasks? Are open items and key decisions retained during compression?</li>
<li class="task-list-item"><input type="checkbox" disabled> Is knowledge retrieval "full stuffing" or "retrieval on demand"? Do the search results include sources?</li>
<li class="task-list-item"><input type="checkbox" disabled> Are the tools/examples "available in full"? Is it possible to dynamically filter based on the current step?</li>
<li class="task-list-item"><input type="checkbox" disabled> Have you offloaded the heavy work to the sub-Agent and used independent windows to isolate the massive intermediate information it generates?</li>
<li class="task-list-item"><input type="checkbox" disabled> Is the context "stable content first and only appended at the end" to hit the KV-cache as much as possible and suppress costs?</li>
<li class="task-list-item"><input type="checkbox" disabled> Am I on the right side between "longer context is better" and "higher signal-to-noise ratio is better"?</li>
</ul>
<blockquote>
<p>Models will keep getting smarter and context windows will keep growing. But as long as attention remains limited and information remains noisy, an agent's ceiling will depend not only on the model itself, but on the context you feed it.</p>
</blockquote>
<hr>
<h2 id="references">References</h2>
<ul>
<li>Andrej Karpathy, <a href="https://x.com/karpathy/status/1937902205765607626">Tweet about "context engineering"</a> (2025)</li>
<li>LangChain, <a href="https://www.langchain.com/blog/context-engineering-for-agents">Context Engineering for Agents</a> (Write/Select/Compress/Isolate framework)</li>
<li>Anthropic, <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents">Effective context engineering for AI agents</a></li>
<li>Chroma, <a href="https://www.trychroma.com/research/context-rot">Context Rot: How Increasing Input Tokens Impacts LLM Performance</a> (2025)</li>
<li>Liu et al. (arXiv), <a href="https://arxiv.org/abs/2307.03172">Lost in the Middle: How Language Models Use Long Contexts</a> (2023)</li>
</ul>]]></content>
  </entry>
  <entry>
    <title>The Thinking Toolbox: An Interdisciplinary Guide from Questions to Reflection</title>
    <id>https://shipengtao.com/en/thinking-toolbox/</id>
    <link rel="alternate" type="text/html" href="https://shipengtao.com/en/thinking-toolbox/"/>
    <published>2026-06-11T00:00:00+08:00</published>
    <updated>2026-06-11T00:00:00+08:00</updated>
    <summary type="text">The Thinking Toolbox: An Interdisciplinary Guide from Questions to Reflection &quot;If all you have is a hammer, everything looks like a nail…</summary>
    <content type="html"><![CDATA[<h1 id="the-thinking-toolbox-an-interdisciplinary-guide-from-questions-to-reflection">The Thinking Toolbox: An Interdisciplinary Guide from Questions to Reflection</h1>
<blockquote>
<p>"If all you have is a hammer, everything looks like a nail." -Abraham Maslow (Charlie Munger loves to quote this saying and calls it the "hammer syndrome")</p>
</blockquote>
<p>The smartest brains in human history are scattered in different disciplines: philosophers have honed questioning techniques for more than two thousand years, mathematicians have invented the most rigorous reasoning tools, physicists are best at simplifying the complex world into computable models, and psychologists specialize in studying where our brains go wrong.</p>
<p>The problem is that these methods are scattered in their respective fields, and few people put them in the same toolbox. Munger calls this "Latticework of Mental Models": You don't need to be an expert in every field, but you need to master the most important models in each field and know which one to use when.</p>
<p>This is what this article does - take the most important thinking methods from philosophy, mathematics, physics, psychology, economics, biology, and engineering, and then string them together according to the six stages of a complete thinking:</p>
<p><strong>Define the problem → decompose and model → reason and explore → validate and correct → decide and act → review and iterate</strong></p>
<p>Any serious thinking—whether it’s making technology selections, writing an article, or deciding whether to move to another city—will generally go through these six steps. At every step, there is a discipline that has polished a handy tool for you.</p>
<p>The article first explains the genealogy of this set of stages (I did not invent it, it has been passed down for more than a hundred years), and then introduces the tools of each stage one by one - each item is accompanied by a <strong>genealogy note</strong>: where it comes from and how strong the foundation is. The foundation of several of them is still a philosophical unsolved problem. Knowing this will make you use it more clearly.</p>
<p>Before setting off, go back to the hammer at the beginning. The solution to the hammer problem has never been to have more hammers—the more tools you have, the higher the chance of returning to a familiar one when you panic—but rather an <strong>index</strong> that tells you which item to reach for when. The six stages are that index.</p>
<hr>
<h2 id="chapter-0-where-the-framework-comes-from">Chapter 0: Where the framework comes from</h2>
<p>Be honest about your family tree: The six stages are not a natural law of thinking, but a <strong>normative model</strong> - it describes "how you should think", not how the brain actually works (the brain is parallel, jumping, and driven by emotions). This model has a clear line of inheritance:</p>
<ul>
<li><strong>Dewey's "How We Think" (1910)</strong> proposed five steps of reflective thinking: feel difficult → define the problem → formulate a hypothesis → reason → test. This is the direct ancestor of stages one to four of this article. Dewey's most important insight was to separate "feeling difficulty" and "defining the problem" into two steps - what you initially feel is never the problem itself, just the symptoms.</li>
<li><strong>Polya's "How to Solve Problems" (1945)</strong> provides four steps for solving mathematical problems: understand the problem → make a plan → execute → review. "Review" is one of the sources of Stage Six.</li>
<li><strong>PDCA cycle, OODA cycle, design thinking</strong> and other twentieth-century engineering and management variants have contributed one thing together: incorporating "action" into the cycle of thinking, rather than treating it as the end point of thinking.</li>
</ul>
<p>Distilling all these solutions to the bottom, there is only one algorithm left in the core: <strong>Generate-Verify-Retain</strong>. Popper called it "conjecture and refutation", psychologist Donald Campbell called it "blind mutation and selective retention", and evolution is its unconscious version - as you will see later, the third, fourth and sixth stages are the core itself, the first and second stages are the problem construction front end added by Dewey, and the fifth stage is the action back end added by decision theory.</p>
<p>The insertion of the fifth stage hides the hardest dividing line in the entire framework. Its basis comes from Hume: <strong>What is cannot deduce what should be</strong>. From stage four to stage five, there is a category switch in thinking - the first four stages ask "what is true" (cognitive rationality), and the fifth stage asks "what should be done under the constraints of scarcity" (practical rationality). No matter how perfect the verification is, it will not automatically tell you which one to choose, because "should" must introduce values ​​and constraints. The other stage boundaries are all gradual, but this one is a cliff.</p>
<p>One last reminder: the stages are spirals rather than straight lines. When you get to the back and find that you made a mistake at the front, go back and start again - the instructions at the end of the article will return to this point.</p>
<hr>
<h2 id="stage-one-defining-the-problemthe-territory-of-philosophy">Stage One: Defining the Problem—The Territory of Philosophy</h2>
<p>There is a saying that is widely attributed to Einstein but is actually unsubstantiated: If I were given an hour to save the world, I would spend fifty-five minutes figuring out what the problem was. No matter who it comes from, the truth is true - most low-quality thinking fails in the first step: solving a wrong problem.</p>
<h3 id="1-socratic-questioning-pin-down-vague-terms">1. Socratic questioning: pin down vague terms</h3>
<p>The first tool of philosophical contribution is the method Socrates used all his life on the streets of Athens: <strong>Continuously ask "What do you mean by X?"</strong> for each key concept.</p>
<p>"I want to make a better product" - what does "better" mean? Better for whom? What is the measure? "Team efficiency is too low" - "efficiency" refers to delivery speed, unit output or rework rate? You will find that in many arguments that last for three hours, the two sides are not talking about the same thing at all. <strong>Until the concept is nailed down, all discussions are in vain.</strong></p>
<p>_Genealogy: "elenchus" in Plato's dialogues, fifth century BC. It implies a presupposition that concepts all have a definable essence; more than two thousand years later, Wittgenstein used "family resemblance" to refute: most everyday concepts (such as "game") have no unified definition at all. Therefore, when nailing a concept, what we seek is not a perfect definition, but "everyone uses the same definition in this discussion." _</p>
<h3 id="2-first-principles-thinking-reduce-the-problem-to-irreducible-facts">2. First-principles thinking: reduce the problem to irreducible facts</h3>
<p>This word originated from Aristotle and was popularized by Musk: Don't reason from "everyone does this" (analogous thinking), but start from "which facts are physically determined to be true" and start from the beginning.</p>
<p>The classic case is batteries: analogical thinking says "batteries have always been expensive in history, so they will be expensive in the future"; first-principles thinking asks "What raw materials are batteries made of? How much are these raw materials worth in the market?" - the answer is that the material cost only accounts for a small part of the selling price, and the rest are links that can be redesigned.</p>
<p>_Genealogy: ἀρχή (origin) in Aristotle's "Metaphysics"; "Post-Analysis" advocates that all proofs must ultimately fall on self-evident starting points - this is the source of "foundationalism" in the theory of knowledge, and Descartes's universal doubt is the same action. But the foundation is not as solid as it sounds: the "Agrippa's Trilemma" of ancient Greek skepticism states that any chain of justification must either go back infinitely, cycle, or stop arbitrarily. In practice we choose the third option, so remember: your "fact list" itself can be wrong. _</p>
<h3 id="3-reframing-what-are-you-really-trying-to-solve">3. Reframing: what are you really trying to solve?</h3>
<p>There is also a simple but sharp habit in philosophical training: distinguishing between <strong>surface issues</strong> and <strong>underlying issues</strong>. The elevator is too slow, so the superficial solution is to change the elevator; but the underlying problem may be that "waiting makes people irritated", so a mirror is installed next to the elevator, and the complaints disappear. Before taking action, ask one more question: "Is this problem a symptom of a larger problem?"</p>
<p>_Genealogy: Dewey's "How We Think" divides "feeling difficult" and "defining the problem" into two independent steps, which is to admit that the two are often inconsistent; design thinking institutionalizes this step as "problem reframing". _</p>
<blockquote>
<p><strong>Output at this stage: Write clearly in one sentence "The problem I want to solve is <strong>_, and the criterion for judging whether it is solved or not is _</strong>". If you can’t write it, don’t move to the next stage.</strong></p>
</blockquote>
<hr>
<h2 id="stage-two-decomposition-and-modelingmathematics-and-physics">Stage Two: Decomposition and Modeling—Mathematics and Physics</h2>
<p>Even after a problem is clearly defined, it is often still too large and complex. The task of the second stage is: make it smaller, simpler, and actionable.</p>
<h3 id="4-abstraction-and-divide-and-conquer-the-mathematicians-two-tools">4. Abstraction and divide-and-conquer: the mathematician's two tools</h3>
<p><strong>Abstraction</strong> is the ability to discard irrelevant details and retain only the skeleton of the problem. Mathematicians don't care whether it's seven apples or seven cows, they only care about "7". When faced with a complex problem, ask first: What is the structure of this problem after removing all surface information? Does it look like a problem I already know how to solve?</p>
<p><strong>Divide and Conquer</strong> (Divide and Conquer) is to cut a large problem that cannot be solved into several small problems that can be solved. The key is that the cuts should be "orthogonal" - the sub-problems should not be entangled with each other as much as possible, otherwise if they are cut, they will not be cut.</p>
<p>_Genealogy: Abstraction has been the essence of mathematics since Euclid. The classic expression of divide and conquer is the second rule of Descartes' "Discourse on Method" (1637) - "Divide each difficult problem under consideration into as many small parts as possible until it can be properly solved." It became the pillar of algorithm design three hundred years later. _</p>
<h3 id="5-idealized-model-physicists-frictionless-slope">5. Idealized model: Physicist’s “frictionless slope”</h3>
<p>The core method of physics is not mathematics, but <strong>dare to simplify</strong>. When Galileo studied falling bodies, he assumed there was no air resistance. Mechanics was full of "particles", "rigid bodies" and "ideal gases" - all things that did not exist in reality, but it was these "wrong" models that made the problem solvable.</p>
<p>The practical version is: <strong>Build a rough but calculable model first, and then gradually add back the ignored factors.</strong> First assume that users are rational, the network will never jitter, and needs will not change, and run through the main logic; then return to the complexity of reality one by one to see which one really changes the conclusion.</p>
<p><em>Genealogy: Galileo's ideal slope is the starting point of modern science - he was the first to realize that "first studying a simplified world that does not exist" is more effective than directly facing the chaotic reality. Statistician George Box summed up what became the norm: "All models are wrong, but some are useful."</em></p>
<h3 id="6-fermi-estimation-and-dimensional-checking-fast-approximation-of-orders-of-magnitude">6. Fermi estimation and dimensional checking: fast approximation of orders of magnitude</h3>
<p>Fermi could train his students with questions like "How many tuners are there in Chicago?" by dividing an unattainable quantity into several factors that can be roughly estimated and multiplied together. The errors will cancel each other out, and the results are often correct by orders of magnitude. <strong>Many decisions don’t require an exact answer, just knowing whether it’s 10 or 10,000.</strong></p>
<p>The supporting tool is <strong>dimensional analysis</strong>: after completing the calculation, check whether the units are correct and whether the order of magnitude is reasonable. An estimate of "3 million new users per day" multiplied by 365 days becomes ridiculous. This is a cost-effective error correction method.</p>
<p>_Pedage: The estimation method is named after Fermi; the source of dimensional analysis is Fourier, developed by Rayleigh, and systematized by Buckingham's π theorem (1914) - the dimensions of both sides of the physical equation must be consistent. This simple constraint is so strong that the shape of the formula can sometimes be directly guessed. _</p>
<blockquote>
<p><strong>Output of this stage: a simplified model or disassembly diagram - you know which factors are retained and which ones are temporarily ignored.</strong></p>
</blockquote>
<hr>
<h2 id="stage-three-reasoning-and-explorationdeduction-induction-and-thought-experiments">Stage Three: Reasoning and Exploration—Deduction, Induction, and Thought Experiments</h2>
<p>The model is built, now we need to reason on the model and generate candidate answers.</p>
<h3 id="7-deduction-induction-and-abduction-three-reasoning-engines">7. Deduction, induction and abduction: three reasoning engines</h3>
<p>Logic distinguishes three types of reasoning: <strong>deduction</strong> (from the general to the specific, if the premises are true, the conclusion must be true), <strong>induction</strong> (summarizing the rules from the sample, the conclusion is only a high probability), <strong>abduction</strong> (working backwards from the results to the most likely cause - it is used by doctors to diagnose and engineers to troubleshoot bugs; when candidate explanations are fighting, <strong>Occam's razor</strong> is used first: the one with the fewest assumptions is given priority).</p>
<p>The key is not which one to use, but to know which one you are using at the moment. Treating the summarized experience as a deductive iron rule ("The first three times were caching problems, this time must be the same") is the most common trap when troubleshooting problems.</p>
<p>_Genealogy: Deduction originated from Aristotle's syllogism and was completely formalized by Frege in 1879; abduction was named by Peirce at the end of the 19th century, and Harman later called it "inference to the best explanation" - but there is still no generally accepted answer to "why the best explanation is more likely to be true"; Occam's razor is named after the fourteenth-century scholastic philosopher William of Ockham, and the popular expression "Do not add entities unless necessary" is actually a refinement of later generations. The foundation of induction is one of the most famous collapses in the history of philosophy: Hume (1748) proved that "as it was in the past, so it will be in the future" cannot be proved non-circularly - you can only use induction itself to defend induction. This is the "problem of induction". The main responses - Kant's transcendental categories, Popper's falsificationism (section 11), Bayesianism (section 15) - will be encountered later, and Goodman's 1955 "Green-Blue Paradox" demonstrated that the problem went deeper than Hume said. Even deduction is not immune: Lewis Carroll's What the Tortoise Said to Achilles (1895) shows that the rules of inference themselves cannot be justified by reason. We used these three engines, but none of them had their foundations welded shut. _</p>
<h3 id="8-proof-by-contradiction-and-extreme-situations-a-mathematicians-probe">8. proof by contradiction and extreme situations: a mathematician’s probe</h3>
<p><strong>proof by contradiction</strong>: Let’s first assume that the conclusion is not valid and see what absurdity will be derived. The everyday version is "Suppose this solution is wrong, what is it most likely to be wrong about?" - this is more likely to expose blind spots than positive arguments.</p>
<p><strong>Extreme case test</strong>: Push the parameters to 0 and push them to infinity to see if the conclusion still holds. "If the number of users is 1,000 times the current number, where will this architecture collapse first?" "If only one person is left to maintain it, can this process still be transformed?" Extreme values ​​are the cheapest stress test.</p>
<p>_Pedage: The earliest known masterpiece of proof by contradiction is the Pythagorean school's proof that "√2 is an irrational number"; the law of contradiction it relies on is called "the most certain of all principles" by Aristotle. One exception worth knowing: the intuitionist mathematician (Brouwer) rejects one type of usage that does not allow the direct assertion of "existence" from "non-existence would lead to a contradiction." This commandment is not needed for everyday thinking, but it reminds us that even the boundaries of proof by contradiction have been seriously debated. _</p>
<h3 id="9-thought-experiments-and-symmetry-the-imagination-of-physicists">9. Thought experiments and symmetry: the imagination of physicists</h3>
<p>Einstein ran after light and came up with the theory of relativity; Maxwell raised a "demon" that sorted molecules. <strong>Thought experiments are to build a laboratory in the mind and use imagination to run experiments that cannot be done in reality.</strong> The counterpart in decision-making is the philosopher Rawls' "veil of ignorance": If you didn't know which party you would be under this rule, would you still make this rule?</p>
<p>Physics also contributed to the intuition of symmetry and conservation: the search for invariants in change. No matter how complex the system is, some things are always conserved - total budget, total time, and trust. "This plan claims to be a win-win situation for all three parties, so who will the cost be transferred to in a conservation-oriented manner?"</p>
<p>_Genealogy: The first masterpiece of thought experiment came from Galileo - "Heavy objects fall faster" can deduce the contradiction purely through imagination (tie two large and small stones together, and according to Aristotle's theory, it will be faster and slower at the same time); the term "thought experiment" (Gedanken experiment) was coined by the physicist Oersted, and it became a conscious methodology through the hands of Mach; its epistemological status is still debated. Norton believes that thought experiments are just arguments in disguise. The "veil of ignorance" comes from Rawls's A Theory of Justice (1971). The foundation of conservation intuition is the hardest among the tools in the whole text - Noether's theorem (1918): each continuous symmetry strictly corresponds to a conserved quantity (time translation symmetry ⇒ conservation of energy). It's a proven theorem, not a heuristic; applying conservation to budgets and trust is certainly an analogy, but the analogy's parent is extremely reliable. _</p>
<h3 id="10-reverse-thinking-think-the-other-way-around-always-think-the-other-way-around">10. Reverse thinking: think the other way around, always think the other way around</h3>
<p>Mathematician Jacobi's famous saying "Invert, always invert" was adopted by Munger as his lifelong creed. Instead of asking "How can I make the project succeed?" start by asking "How can I ensure it fails?" - and then avoid every item on the list. The path to failure is far more clear and enumerable than the path to success.</p>
<p>Munger's most famous demonstration was his graduation speech at Harvard-Westlake School in 1986: when others talked about how to obtain happiness, he talked about "how to ensure that you live a miserable life" - jealousy, resentment, capriciousness, not learning from the lessons of others, and being unable to recover after encountering setbacks. Implementing each one in reverse is the answer.</p>
<p>_Genealogy: Jacobi was originally talking about mathematical practice - many problems have simpler structures from the opposite side. Behind this is the ubiquitous duality in mathematics; Munger moved it from mathematics to life and investment. _</p>
<blockquote>
<p><strong>Output of this stage: more than one candidate solution. When there is only one option, you are not actually thinking, you are just obeying.</strong></p>
</blockquote>
<hr>
<h2 id="stage-four-validation-and-correctionscience-and-psychology">Stage Four: Validation and Correction—Science and Psychology</h2>
<p>At this point you already have a few candidate answers that look good. The whole point of Stage 4 is this: <strong>The most important thing your brain wants to do at this moment is prove that it is right, and you have to force it to do the opposite.</strong></p>
<h3 id="11-falsifiability-poppers-dividing-line">11. falsifiability: Popper’s dividing line</h3>
<p>The philosopher of science Popper pointed out that the dividing line between science and non-science lies not in "whether it can be verified", but in "whether it can be falsified". A theory that is right no matter what ("It's all fate") conveys no message.</p>
<p>Practical method: Write a <strong>falsification condition</strong> for your conclusion - "If X is observed, I admit that this judgment is wrong." A judgment that cannot write X is not called a judgment, it is called a belief. Then, go for X instead of waiting for it to hit you.</p>
<p>Popper's own epiphany came from a real comparison: In 1919, Eddington led an expedition to observe the total solar eclipse to test the starlight deflection predicted by the general theory of relativity - Einstein stuck his neck into the guillotine and died if the data did not conform to the theory; while the Freudian theory of the same era, in Popper's view, could be justified no matter how the patient behaved. The dividing line is drawn between those who dare to be killed and those who are always right.</p>
<p>_Genealogy: Popper, "The Logic of Scientific Discovery" (1934). It's worth knowing its motivations: Popper accepted Hume's verdict on induction in its entirety (see Section 7), and then took the next step - science does not rely on induction at all, theories are always just conjectures, and the entire rationality of science lies in the efficient elimination of wrong conjectures. The subsequent important revision was the Dion-Quine thesis: a single hypothesis can never be falsified in isolation, and you can always blame an auxiliary hypothesis ("the instrument is broken"); Lakatos distinguished between "progressive" and "degenerate" research programs - a belief that survives by constantly patching is degenerate. Doing this check on your own beliefs is quite cruel and quite effective. _</p>
<h3 id="12-confirmation-bias-and-system-1system-2-know-where-your-brain-lies-to-you">12. Confirmation Bias and System 1/System 2: Know Where Your Brain Lies to You</h3>
<p>Psychologist Kahneman divided thinking into two systems: <strong>System 1</strong> is fast, automatic, and intuitive; <strong>System 2</strong> is slow, laborious, and logical. The trouble is that System 1 is always on and good at masquerading as rationality—you think you’re reasoning, but you’re actually making excuses for your intuition.</p>
<p>The most dangerous biases are worth naming: <strong>confirmation bias</strong> (only seeing evidence that supports oneself), <strong>anchoring effect</strong> (the first number kidnaps subsequent judgments), <strong>loss aversion</strong> (the pain of loss is about twice as much as the pleasure of the same gain, so people will cling to sunk costs), <strong>survivor bias</strong> (during World War II, the military wanted to armor the parts with dense bullet holes on returning bombers, statistician Wald pointed out that the parts that should be reinforced were precisely the parts without bullet holes - the planes that were hit in those places failed to fly back). The first step in correcting mistakes is not to "try harder to be objective", but to admit that there is a high probability that you are biased at the moment, and then use processes to hedge against it - such as forcing you to write down the opposing view, or finding someone who really dares to speak up to be a red team.</p>
<p>_Genealogy: Kahneman and Tversky pioneered the "heuristics and biases" research program in the 1970s ("Thinking, Fast and Slow" is its popular version summary); a deeper level is Simon's "bounded rationality" in the 1950s: biases are not bugs in the brain, but engineering compromises under limited computing power. Listen to the opposing side: Ji Renze argued that many heuristics are "ecologically rational" in real environments, faster and often more accurate than complete calculations; the universality of "loss aversion is about twice as much" has also been debated in recent literature. Conclusion: The deviation list is a guidepost, not a verdict. _</p>
<h3 id="13-correlation-does-not-equal-causation-appearing-together-does-not-mean-who-caused-whom">13. Correlation does not equal causation: appearing together does not mean who caused whom.</h3>
<p>The bias in the previous section is that your brain actively deceives you. The trap in this section is hidden in the data: <strong>Two variables always rise and fall together, which does not mean that one of them causes the other.</strong> Ice cream sales are highly positively correlated with the number of drownings, but it is not ice cream that causes people to drown - it is the common cause of "summer" (confounding variable). Seeing the correlation, there are at least four possible coexistences: A causes B, B causes A, and the third factor C drives both at the same time, which is pure coincidence.</p>
<p>Practical discipline: Look for confounding variables before taking action, and ask "Is there a C that drives both sides at the same time?" If you can do a randomized controlled experiment (randomly divide the sample into two groups), do it. If you can't do it, then lower the credibility of the conclusion by one level. A hidden variant is <strong>Simpson's Paradox</strong> - the same data can lead to completely opposite conclusions when viewed in groups and combined.</p>
<p>_Genealogy: Hume has long pointed out that we never "see" cause and effect, only constant succession (again the source of the induction problem in Section 7); what turned causal inference into an operational tool was statistics - Fisher's randomized controlled experiments (1920s) and Judea Pearl's causal diagrams and do-calculus (1990s). Everyone knows "correlation does not imply causation", but the difficulty has always been to identify the specific confounding variable. _</p>
<h3 id="14-external-perspective-and-basic-probability-first-look-at-your-peers-then-look-at-yourself">14. External perspective and basic probability: first look at your peers, then look at yourself</h3>
<p>There are two ways to predict how something will end. <strong>Internal Perspective</strong> Focus on the details of the matter itself and deduce: "Our team is strong, the plan is thorough, and we can go online in three months." <strong>External Perspective</strong> First ask: "What is the usual outcome of similar things?" - How many projects of similar scale are online on time? What is the median number of deferrals? Humans naturally prefer an internal perspective, and it is systematically over-optimistic. This is the <strong>planning fallacy</strong>: almost every big project is over time and over budget because every team feels that "we are different."</p>
<p>There is only one corrective action: for any prediction, first find a <strong>reference class</strong> for it, use the true distribution of this group of similar people as an anchor, and then make limited adjustments based on the particularity of this case. <strong>Basic probability is your starting point, not a background that can be skipped.</strong></p>
<p>Kahneman told his own experience: He led a team to compile a textbook, and the team optimistically estimated that it would be completed in two years; he asked a senior member, "How long does it take on average for similar projects you have seen, and how many of them aborted halfway?" The answer was seven to ten years, and about 40% of them were aborted. They didn't take this external data seriously, and it took eight years - even though they knew the basic probability, they still lost to the internal perspective.</p>
<p>_Genealogy: Kahneman and Lovullo distinguished "external perspective vs. internal perspective" in 1993; its operationalization is reference class forecasting, which Frufbeagle uses to systematically improve cost estimates of large-scale infrastructure, and has been written into specifications by many governments. It's the same thing as Bayes in the next section: the outside perspective gives you a decent prior, and Bayes tells you how much to move it when you get new evidence. _</p>
<h3 id="15-bayesian-updating-maintaining-beliefs-as-probabilities">15. Bayesian updating: maintaining beliefs as probabilities</h3>
<p>The everyday version of Bayes' theorem is this: <strong>Beliefs are not black-and-white switches, but probabilities that slide with the evidence.</strong> Ask three questions when you get new evidence: What was my original confidence (prior)? If I'm right, what are the chances of seeing this evidence? What if I'm wrong?</p>
<p>Keynes (according to legend) said: "As the facts change, my thoughts change. What about you, sir?" The virtue of a Bayesian is not to stand firm, but to update quickly and to the right extent - not overturning it entirely because of one counterexample, nor remaining unchanged despite ten counterexamples.</p>
<p>This simple algorithm has found aircraft wreckage: Air France Flight 447 crashed into the Atlantic Ocean in 2009, and two years of search found nothing; in 2011, the search party invited Bayesian search experts to use every previous failure as evidence to update the probability distribution of wreckage hidden in various areas of the seabed, and the fuselage was found within a week of the start of a new round of searches. The same method was used to locate the sunken nuclear submarine "Scorpion" in 1968.</p>
<p>_Genealogy: Bayes (posthumous 1763) and Laplace. It has two rare and hard proofs: Cox's theorem proves that the "credibility" operation that satisfies several rationality axioms must be probability theory; De Finetti's "Dutch Gamble" argument proves that people who do not act according to the axioms of probability can be harvested by constructing a set of gambling games that are guaranteed to make money without losing money. In other words: Refusing Bayesian updating is not just stubbornness, it is stubbornness that can be priced at. _</p>
<h3 id="16-pre-mortem-hold-a-memorial-service-before-failure-occurs">16. pre-mortem: Hold a memorial service before failure occurs</h3>
<p><strong>Premortem (pre-mortem)</strong> invented by psychologist Gary Klein: Before the project is launched, everyone assumes that "it is now one year later and this project has failed miserably", and then everyone writes down the reasons for the failure. This simple change of frame can turn "raising concerns" from a disappointment to a task, and uncover risks that no one usually dares to mention. It can also be used by one person: Write a letter of failure to yourself one year from now.</p>
<p>Klein recorded an example in the original article: A Fortune 50 company launched a billion-dollar sustainable development project. At the pre-mortem meeting, an executive wrote that the cause of death was that once the CEO who supported it retired, the project would lose its backer. No one dared to say this at the mobilization meeting, but blurted it out at the "memorial service".</p>
<p>_Genealogy: Klein, published in Harvard Business Review in 2007; based his experiment on a 1989 study on "prospective hindsight"—which allows people to assume that an effect has already occurred and imagine the cause in a much more concrete way. _</p>
<blockquote>
<p><strong>The output of this stage: a list of falsification conditions + a pre-mortem report. Only if your plan is still standing after being attacked by yourself will you be qualified to enter the decision-making process.</strong></p>
</blockquote>
<hr>
<h2 id="stage-five-decision-and-actioneconomics-and-engineering">Stage Five: Decision and Action—Economics and Engineering</h2>
<p>There may still be several verified solutions, but only one resource. The fifth stage switches from "truth seeking" to "<strong>weighing</strong>" - remember Hume's cliff in Chapter Zero? Just cross over from here.</p>
<h3 id="17-opportunity-cost-and-marginal-thinking-two-cornerstones-of-economics">17. Opportunity cost and marginal thinking: two cornerstones of economics</h3>
<p><strong>Opportunity cost</strong>: The true cost of a choice is not what you paid, but the <strong>best option you gave up</strong> as a result. "Is this thing worth doing?" is a pseudo question. The real question is "Is this thing worth doing more than other things I can do?"</p>
<p><strong>Marginal Thinking</strong>: Decision-making always depends on the increment, not the total amount. Don't ask "Do you want to do marketing?", ask "How much more can you get back if you invest more of this dollar in marketing?" Accompanied by this is the iron law of <strong>sunk costs</strong>: the money you have spent, the emotions you have invested, and the code you have written have nothing to do with future decisions—even though loss aversion will try your best to make you feel that they are.</p>
<p>The most famous move to cut sunk costs occurred at Intel in 1985. Memory is the business where the company started. It has been beaten back by Japanese manufacturers, but no one can stop it. Grove asked Moore: "If the board of directors replaced us, what would the new CEO do?" Moore replied: "Exit the memory." Grove said: "Then why don't we walk out of this door ourselves, walk back, and do it ourselves?" The new CEO does not carry old scores-the whole content of this ideological move is to zero out the sunk costs. Intel has since moved on to processors.</p>
<p>_Genealogy: The "marginal revolution" in the 1870s - Jevons, Menger, and Walras independently proposed it almost at the same time. Economics has since shifted from "what determines value" to "how increments are compared"; opportunity cost was named by Wieser. The foundation is the axiom of scarcity: as long as resources are limited, choice must mean giving up. _</p>
<h3 id="18-expected-value-and-asymmetry-living-with-uncertainty">18. Expected value and asymmetry: living with uncertainty</h3>
<p>The skeleton of rational decision-making is <strong>expected value</strong>: payoff × probability. But real masters also look at the shape of the distribution - Taleb calls it asymmetry: things with limited downside and huge upside (writing, open source, goodwill in social circles) are worth doing repeatedly; things with limited upside and fatal downside (leverage, single point dependence, gray areas) are too many once. <strong>Never risk getting kicked out, no matter how good the expectations are.</strong></p>
<p>The negative teaching material is Long-Term Capital Management (LTCM): there are two Nobel Prize winners in economics on the partner list, and the expected value of each arbitrage is positive, so it adds about 25 times leverage to make repeated bets. When Russia defaulted on its national debt in 1998, a tail event completely kicked it out of the game, and the Federal Reserve finally stepped in to organize a rescue. The expectations of each step are right, but what is wrong is that they are playing a game that does not allow them to lose even once.</p>
<p>_Genealogy: In 1654, Pascal and Fermat corresponded about the gambling problem, which gave birth to probability theory; in 1944, von Neumann and Morgenstern completed the axiomization of expected utility. The shortcomings of naked expected value were exposed very early - Bernoulli's St. Petersburg Paradox in 1738; and the modern mathematical basis for "don't risk being out" is the ergodic problem (physicist Peters): <strong>The time average is not equal to the set average</strong>, and a gamble with a positive expected value can be devastating to individuals who must make continuous bets. This is what Taleb said. _</p>
<h3 id="19-incentives-and-games-there-are-people-on-the-opposite-side">19. Incentives and games: There are people on the opposite side</h3>
<p>Opportunity cost and expected value both imply that you are dealing with "nature" - costs and probabilities will not change because of your choices. But across from most real decisions, there are people who will predict and respond to you. At this time, two additional checks are required. The first is <strong>Incentives</strong>: To judge what a person or organization will do, looking at its incentive structure is far more reliable than listening to its promises - Munger's version is "show me the incentives and I will show you the results." The second is <strong>Opponent's optimal response</strong>: Count other people's reactions into the plan - "If I lower the price, will my opponent follow? Will I still make money after following?" A plan that does not consider responses is written for a static world.</p>
<p>A textbook case of incentives overriding slogans is Wells Fargo Bank in 2016: the headquarters set a hard target for tellers to "cross-sell eight products per customer" and linked it to salary, so employees opened about 3.5 million false accounts without customers' knowledge. Everyone is responding rationally to incentives, which adds up to a disaster - the slogan is about serving customers, the incentive award is the number of accounts opened, and employees listen to incentives. This is <strong>Goodhart's Law</strong>: Once an indicator is used as an assessment target, it will be optimized for numbers, and it will no longer be the thing it was originally intended to measure.</p>
<p>_Genealogy: Game theory and expected utility in Section 18 come from the same book - von Neumann and Morgenstern's Game Theory and Economic Behavior (1944); Nash's 1950 concept of equilibrium generalized it to non-zero-sum situations. The academic version of "looking at incentives" is mechanism design theory (Hurwitz, Maskin, Myerson, 2007 Nobel Prize in Economics): simply treat incentives as objects that can be designed. _</p>
<h3 id="20-reversible-and-irreversible-bezos-two-doors">20. Reversible and irreversible: Bezos’ two doors</h3>
<p>Bezos divides decision-making into two categories: <strong>two-way doors</strong> (you can go back if you make a mistake) and <strong>one-way doors</strong> (you can't go back when you go through them). Two-way door decisions should be fast, cheap, and delegated to the nearest person - the cost of delay at this time is much higher than making a mistake; only one-way door decisions are worth slowing down and using all the tools in the first four stages. <strong>The problem most people have is that they treat two-way doors with the caution of a one-way door, but walk through a one-way door with the carelessness of a two-way door.</strong></p>
<p>Amazon's own example of a one-way door is Prime in 2005: the financial model is not fair no matter how you calculate it, and once "free two-day shipping" is given out, it is almost impossible to get it back - taking away existing benefits from users is much more costly than never giving them - Bezos asked the team to deduce it repeatedly before making a decision. In contrast, the hundreds of interface experiments that run every day on Amazon's website are a standard two-way door: ugly data, offline for the day.</p>
<p>_Genealogy: Bezos’ 2015 Annual Letter to Shareholders. The academic counterpart is Simon's "satisficing": the optimal strategy for a bounded rational person is not to seek the best in everything, but to allocate cognitive budget according to the importance of the decision. _</p>
<h3 id="21-the-engineers-art-of-compromise-there-is-no-optimality-only-trade-offs">21. The engineer’s art of compromise: there is no optimality, only trade-offs</h3>
<p>The core worldview of engineering contributions is: <strong>All designs are trade-offs</strong>, and you can only choose two: fast, good, or cheap. The supporting method is <strong>Minimum Viable Product</strong> (MVP): when analysis cannot continue to reduce uncertainty, stop analysis, use the minimum cost to push the plan into reality, and let reality take over verification - doing it is itself a higher-bandwidth thinking.</p>
<p>Both textbook cases are included in "The Lean Startup". When Dropbox's products were not yet available for public use, founder Houston first released a three-minute demonstration video, and the waiting list grew from 5,000 to 75,000 people overnight - not one more line of code was written, and the demand was verified. The founder of Zappos first went to a nearby shoe store to take photos of the shoes and put them on the shelves. When someone placed an order, he bought them at the original price and sent them out. The zero inventory proved that "someone is willing to buy shoes online."</p>
<p>_Genealogy: MVP was popularized by Eric Rice in "The Lean Startup" (2011), but its philosophical roots are much older - the pragmatism of Peirce and Dewey: the meaning of an idea lies in its practical effect, and action is not the end of inquiry, but a part of it. _</p>
<blockquote>
<p><strong>Output of this stage: A clear decision + its opportunity cost + what kind of door it is + minimal first step.</strong></p>
</blockquote>
<hr>
<h2 id="stage-six-review-and-iterationcybernetics-and-evolution">Stage Six: Review and Iteration—Cybernetics and Evolution</h2>
<p>Action is not the end of thinking, but the input for the next round of thinking.</p>
<h3 id="22-feedback-loops-the-heart-of-cybernetics">22. Feedback loops: the heart of cybernetics</h3>
<p>Wiener's cybernetics boils down all intelligent behavior to <strong>feedback</strong>: the output is measured, compared with the target, and the error is sent back to correct the input. This is how missiles catch up with airplanes, how thermostats stabilize temperatures, and how people learn to ride bicycles.</p>
<p>The corollary is practical: the rate at which a system progresses depends on the speed and fidelity of its feedback loop. A system with a one-year feedback cycle (annual performance) is destined to evolve less quickly than a system with a one-day feedback cycle (daily review). The most powerful way to improve something is often not to work harder, but to shorten its feedback loop.</p>
<p>_Pedage: Wiener's "Cybernetics" (1948); an earlier mathematization was Maxwell's 1868 "On the Governor" - written to analyze the centrifugal governor of Watt's steam engine and regarded as the first paper on control theory. _</p>
<h3 id="23-mutation-selection-retention-the-algorithm-of-evolution">23. Mutation-selection-retention: the algorithm of evolution</h3>
<p>Darwin gave us perhaps the most powerful problem-solving algorithm ever devised, which requires no intelligence whatsoever to create eyes and brains: <strong>Generate variation→Environmental selection→Keep the winner→Repeat</strong>.</p>
<p>Applied to individuals and organizations: Keep diverse attempts at a low cost (mutation), use real-world feedback rather than internal opinions to filter (selection), and solidify proven practices into habits and processes (retention). Note that all three are indispensable - only mutating without retaining is a fool's errand, only retaining without mutating is rigid.</p>
<p>The most complete three-shot ensemble in business history is the Post-it note: Silver, a chemist at 3M, invented a "failed" weak glue that could not stick (mutation); a few years later, his colleague Fry used it to bookmark the choir's hymnbook, and found that "it sticks and can be peeled off without leaving traces" is exactly what countless people need (choice); 3M solidified it into a permanent product line, which sells well to this day (retention). There is also an answer to where the variation comes from - 3M allows employees to spend 15% of their working hours on self-chosen projects: the organization cannot design the variation itself, but it can design the environment in which variation can occur.</p>
<p>_Genealogy: Darwin's "The Origin of Species" (1859); Campbell promoted it in 1960 as "blind mutation and selective retention" - a universal algorithm for the growth of all knowledge. Note that it is isomorphic with Popper's "Conjecture and Refutation" in Section 11: Conjecture is mutation, and refutation is selection. This is no coincidence - this is the engine mentioned in Chapter Zero, and the same algorithm is running deep within the entire six-stage framework. _</p>
<h3 id="24-metacognition-and-deliberate-practice-thinking-about-thinking-itself">24. Metacognition and deliberate practice: thinking about thinking itself</h3>
<p>The closing gift of psychology is <strong>metacognition</strong>: the ability to monitor one's own thought processes. When reviewing, you should not only ask "Was the result correct?" but also "Was my thinking process correct?" - Good decisions may have bad luck, and bad decisions may also have good results. Poker player Anne Duke calls confusion between the two "resulting", which is the most hidden mistake in review. Duke's book begins with a real precedent: In the last 26 seconds of the 2015 Super Bowl, the Seahawks chose to pass the ball on the one-yard line instead of rushing on the ground. They were intercepted by the opponent and lost the championship. Head coach Carroll was scolded as "the stupidest decision in history." Its twin trap is <strong>mean reversion</strong>: extreme results are likely to be followed by more mediocre results. This is a purely statistical phenomenon - attributing the drop after the outbreak to "your own laxity", and the rebound after the trough to "effective corrections", and review becomes making up stories for the noise.</p>
<p>Research on deliberate practice reminds: Mere repetition does not produce progress, only repetition with clear goals, focusing on weaknesses, and obtaining immediate feedback does. Thinking methods themselves are skills, and this law also applies.</p>
<p>_Genealogy: Metacognition was named by Flavell in 1976; mean regression was discovered by Galton (1886) in the study of height inheritance; deliberate practice was Eriksson's research program in 1993 - the "10,000-hour rule" was a simplified paraphrase of Gladwell, and Eriksson himself did not admit it; subsequent meta-analysis (McNamara, 2014) showed that the difference in performance explained by deliberate practice is much smaller than the popular statement. The direction is effective, but the amplitude is not magical. "Resulting" comes from Anne Duke's "The Bet" (2018). _</p>
<blockquote>
<p><strong>Output of this stage: Write it down. A review that is not written down will be tampered with by memory into "I expected it" two weeks later (hindsight bias, the final blow of psychology).</strong></p>
</blockquote>
<hr>
<h2 id="the-complete-framework-a-cheat-sheet">The complete framework: a cheat sheet</h2>
<table>
<thead>
<tr>
<th>stage</th>
<th>core issues</th>
<th>Main subjects</th>
<th>key tools</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. definition problem</td>
<td>What exactly am I trying to solve?</td>
<td>philosophy</td>
<td>Socratic questioning, first-principles thinking, question translation</td>
</tr>
<tr>
<td>2. decomposition and modeling</td>
<td>How to make it actionable?</td>
<td>Mathematics, Physics</td>
<td>Abstraction, divide and conquer, idealized models, Fermi estimation</td>
</tr>
<tr>
<td>3. reasoning and exploration</td>
<td>What might the answer be?</td>
<td>Logic, mathematics, physics</td>
<td>Three types of reasoning, Occam's razor, proof by contradiction, thought experiments, reverse thinking</td>
</tr>
<tr>
<td>4. validation and correction</td>
<td>How do I know I'm wrong?</td>
<td>scientific method, psychology</td>
<td>falsifiability, Cognitive Bias Checklist, Correlation ≠ Causation, External Perspective, Bayesian Update, pre-mortem</td>
</tr>
<tr>
<td>5. decision and action</td>
<td>Which one to choose under constraints?</td>
<td>Economics, Engineering</td>
<td>Opportunity cost, margin, expected value, incentives and games, two-way door, MVP</td>
</tr>
<tr>
<td>6. review and iteration</td>
<td>How can the next round be better?</td>
<td>Cybernetics, Evolution, Psychology</td>
<td>Feedback loops, variation-selection-retention, metacognition</td>
</tr>
</tbody>
</table>
<p>Three instructions for use:</p>
<p><strong>First, the stages are spirals rather than straight lines.</strong> When the verification phase finds that the problem definition is wrong, it returns to the first phase - this is not a failure, this is precisely the process at work. Real thinking jumps repeatedly between six stages.</p>
<p><strong>Second, you don’t have to walk the entire distance every time.</strong> For small decisions about two-way doors, it is enough to use intuition and a quick "think back"; only for big decisions about one-way doors, it is worth going through the six stages. When faced with a one-way door, the most leveraged step is often to first translate it into a two-way door question - "Do you want to spend a year writing a book?" and first into "Write one of the chapters into an article and send it out. Are there any strangers who are willing to read and forward it?" The latter can be answered with a minimal experiment over a weekend. The choice of tools itself is a matter of judgment.</p>
<p><strong>Third, the tool does not work automatically.</strong> People who know about confirmation bias still make confirmation bias, and people who are familiar with sunk costs are still reluctant to cut their flesh. These methods can only work when you need them most and least want to use them when you need them most and least want to use them.</p>
<p>Munger said that his life was just "holding a few big ideas in hand and waiting patiently." Behind each of the twenty-four tools in this toolbox stands a discipline that has been polished for hundreds or even thousands of years - the foundations of some tools are still being reworked by philosophers, but this does not prevent them from being useful, just as you do not need to wait for the completion of quantum gravity to use Newtonian mechanics. You don’t need to invent new ways of thinking, you just need to reach for the right thing at the right stage.</p>]]></content>
  </entry>
  <entry>
    <title>Harness Engineering: The Engineering Around the Model Determines Whether an Agent Succeeds</title>
    <id>https://shipengtao.com/en/harness-engineering/</id>
    <link rel="alternate" type="text/html" href="https://shipengtao.com/en/harness-engineering/"/>
    <published>2026-06-05T00:00:00+08:00</published>
    <updated>2026-06-05T00:00:00+08:00</updated>
    <summary type="text">Harness Engineering: The Engineering Around the Model Determines Whether an Agent Succeeds Models keep getting smarter, so why do agents…</summary>
    <content type="html"><![CDATA[<h1 id="harness-engineering-the-engineering-around-the-model-determines-whether-an-agent-succeeds">Harness Engineering: The Engineering Around the Model Determines Whether an Agent Succeeds</h1>
<blockquote>
<p>Models keep getting smarter, so why do agents still fail so often? The answer lies in the engineering system around the model: the harness. This guide moves from concepts to practical implementation and ends with a checklist you can use directly.</p>
</blockquote>
<hr>
<h2 id="1-lets-first-look-at-a-counter-intuitive-fact">1. Let’s first look at a counter-intuitive fact</h2>
<p>The same Claude / Codex model, the same task, changing two sets of "shells", the success rate can jump from <strong>20% to nearly 100%</strong> - the model has not changed a word.</p>
<p>This is a repeatedly observed conclusion in the <code class="language-text">learn-harness-engineering</code> course (see the reference document at the bottom): for a TypeScript/React project, the success rate is about 20% when the model is only given a README; after the "rules, status, verification" shell is completed, the success rate is close to 100%. <strong>The model was not changed, only the harness was changed.</strong></p>
<p>OpenAI's case is even more striking (Ryan Lopopolo, February 2026). A small team began with an empty repository in late August 2025. Over roughly five months, humans wrote <strong>zero lines of code</strong>: Codex generated every line. The result was about one million lines across application logic, infrastructure, tooling, and documentation, plus 1,500 merged PRs. The team grew from three to seven people while per-person throughput rose to <strong>3.5 PRs per day</strong>. Rather than writing business code, the engineers focused on designing an environment in which the model could produce reliable work. They estimated that the project took about one-tenth the time of writing the code by hand.</p>
<p>The thing now has a name: <strong>Harness Engineering</strong>.</p>
<p>One sentence definition:</p>
<blockquote>
<p><strong>Agent = Model + Harness.</strong>
The model is responsible for "smartness", and the harness is responsible for "reliability".</p>
</blockquote>
<hr>
<h2 id="2-what-is-harness">2. What is Harness?</h2>
<p>The most common misconception is that a harness is merely a well-written system prompt or a <code class="language-text">CLAUDE.md</code> file. <strong>It is not.</strong></p>
<p>The definitions given by various companies are highly consistent, and the core is the same sentence:</p>
<blockquote>
<p>Harness = <strong>In addition to model weight, all engineering infrastructure that determines "how much intelligence can be implemented".</strong>
(LangChain's statement: "every piece of code, configuration, and execution logic that isn't the model itself.")</p>
</blockquote>
<p>It includes but goes far beyond prompt: tool definition and execution, file system and sandbox, state persistence, verification mechanism, context management, sub-agent orchestration, <strong>deterministic hooks/middleware</strong> (compaction, continuation, lint interception and other links that do not rely on model awareness and are enforced by code), observability...</p>
<p>To give the most appropriate analogy:</p>
<blockquote>
<p><strong>Think of Agent as a senior engineer joining today.</strong> He's smart, but knows nothing about your code base. He needs: project documentation, a running environment, usable tools, knowing the "done" criteria, and shift records.
The sum of these things is the harness. The more complete the scaffolding you provide, the closer the ability of this "new person" will be to his true level.</p>
</blockquote>
<p>Key insights: <strong>Today's Agent failures were mostly not "not smart enough" but "can't see/can't run/don't know when it's over" - these are all harness problems, not model problems.</strong> This is the so-called <strong>capability–execution gap</strong>.</p>
<hr>
<h2 id="3-five-subsystems-of-harness">3. Five subsystems of Harness</h2>
<p>A complete harness can be disassembled into five subsystems. The following five-point method is synthesized from three sources: WalkingLabs’ <code class="language-text">learn-harness-engineering</code> course, LangChain’s component panorama, and Anthropic’s long task practice. It is recommended to self-check item by item according to the following table:</p>
<blockquote>
<p><strong>A little explanation (the divisions among different companies are not completely consistent)</strong>: The original five-piece set of the <code class="language-text">learn-harness-engineering</code> course is <strong>Instructions / State / Verification / Scope / Session Lifecycle</strong> - "Session Life Cycle" is listed as a separate item. This article changes to a method closer to LangChain/Anthropic: "Tools and environment" (which both emphasize, and most directly determines whether the Agent can run) is made a core item, and "session life cycle" is incorporated into the long task method in Section 4 (initialization/execution separation, clean restart). <strong>It doesn't matter how the subsystem is named or how many items it is classified into. It is important not to omit items.</strong></p>
</blockquote>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">                ┌─ Instructions  What the project is and what constrains it
                ├─ Tools/Env     What the agent can do and whether it can run
   Harness ─────┼─ State         Where the previous session stopped
                ├─ Scope         What this session will do and what counts as done
                └─ Verification  How the agent proves its work is correct</code></pre></div>
<table>
<thead>
<tr>
<th>Subsystem</th>
<th>Problem solved</th>
<th>Typical carrier</th>
<th>input-output ratio</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Instructions</strong></td>
<td>Agent does not know the project background and red lines</td>
<td><code class="language-text">AGENTS.md</code> / <code class="language-text">CLAUDE.md</code>, about 100 lines</td>
<td>high</td>
</tr>
<tr>
<td><strong>Tools/Environment</strong></td>
<td>Is the ability sufficient and the environment suitable for running?</td>
<td>Toolset, <code class="language-text">pyproject.toml</code>/<code class="language-text">package.json</code>, <code class="language-text">.nvmrc</code>, Docker/devcontainer</td>
<td>high</td>
</tr>
<tr>
<td><strong>State</strong></td>
<td>Cross-session amnesia, duplication of effort</td>
<td><code class="language-text">PROGRESS.md</code> / <code class="language-text">claude-progress.txt</code>, git submission history</td>
<td>Middle to high</td>
</tr>
<tr>
<td><strong>Scope</strong></td>
<td>Agent wants to do too much at once and gives up halfway.</td>
<td>Machine-readable feature list (JSON)</td>
<td>Middle to high</td>
</tr>
<tr>
<td><strong>Verification</strong></td>
<td>Feel good about yourself and pretend you're done</td>
<td>Test, lint, type-check, E2E</td>
<td><strong>Highest</strong></td>
</tr>
</tbody>
</table>
<p>Dismantle them one by one below.</p>
<h3 id="1-instructions-put-the-onboarding-guide-in-the-repository">1. Instructions: Put the onboarding guide in the repository</h3>
<p>The principle is not "the more you write, the better", but <strong>progressive disclosure</strong>: a core instruction file of about 100 lines that clearly explains what the project is, the technology stack, the first run command, inviolable constraints, and the document entry. Link out details as needed, rather than gluing an encyclopedia to the model all at once, which would only dilute attention.</p>
<p>Core iron rule: <strong>The repository is the only source of truth (repo as the system of record).</strong> Agent cannot turn around and ask colleagues like humans. Everything it needs must be visible in the warehouse.</p>
<h3 id="2-toolsenvironment-give-less-constraints-and-more-capabilities">2. Tools/Environment: Give less constraints and more capabilities</h3>
<ul>
<li>Tool permissions follow <strong>least permission</strong> instead of banning them all - if the restrictions are too strict, the agent can only rely on guesswork.</li>
<li>The environment must be <strong>self-describing and reproducible</strong>: the dependency files, runtime versions, and container configurations are all in place so that the Agent can start serving with one click. In Anthropic's practice, there is a dedicated <code class="language-text">init.sh</code> responsible for pulling up the development environment.</li>
<li>An underrated ability: <strong>Give Agent a sandbox that can run bash/execute code</strong>. Rather than pre-configuring a bunch of narrow tools, "being able to write code and run it yourself" is the key to universal autonomy - it can verify and debug by itself.</li>
</ul>
<h3 id="3-state-handover-like-a-shift-worker">3. State: Handover like a shift worker</h3>
<p>Long tasks span multiple context window, <strong>each new session is a blank slate</strong>. Anthropic likens this to "engineers working shifts without handover documentation."</p>
<p>The solution is to put the state on the disk:</p>
<ul>
<li>Progress file (<code class="language-text">claude-progress.txt</code> / <code class="language-text">PROGRESS.md</code>) records: what has been done, what is being done, and where it is stuck.</li>
<li>Use <strong>git commits</strong> and commit messages to leave traceable traces.</li>
<li>Each session starts by reading the progress and git log, and updates the progress before ending.</li>
</ul>
<p>Effect: When there is a harness, the Agent can accurately "continue from the last breakpoint"; when there is no harness, it will either redo it or start from the beginning every time.</p>
<h3 id="4-scope-do-one-thing-at-a-time">4. Scope: Do one thing at a time</h3>
<p>A common problem with Agents is being greedy for too much—trying to implement too many functions at once, but not finishing any of them.</p>
<p>Anthropic's approach is very straightforward: split the task into <strong>200+ extremely fine-grained feature lists</strong>, mark each item with pass/fail, and <strong>use JSON instead of Markdown</strong> (JSON is structured, and the model is less likely to be rewritten without authorization). Each session<strong>only works on one feature</strong>, completes it, tests it, submits it, updates the progress, and then moves on to the next one.</p>
<h3 id="5-verification-change-good-or-bad-into-pass-or-not">5. Verification: Change "good or bad" into "pass or not"</h3>
<p>This is the subsystem with the lowest investment and the highest return, and it is also the most easily ignored.</p>
<p>The model has two fatal tendencies:</p>
<ol>
<li><strong>Premature declaration of victory</strong> - Saying "done" without testing.</li>
<li><strong>Self-praise</strong> - Ask it to evaluate its own output, and it will almost always say "excellent", even if it looks like a mess to others.</li>
</ol>
<p>Countermeasures:</p>
<ul>
<li><strong>Explicitly list the verification commands</strong> (test/lint/typecheck) in the document so that the Agent can run it every time.</li>
<li>For web applications, it is required to use browser automation (such as Puppeteer MCP) for end-to-end verification, rather than "just look right". Anthropic's actual measurement has greatly improved the accuracy of "the function is really usable".</li>
<li>Turn subjective quality<strong>into an objective standard that can be scored</strong>. When Anthropic does front-end work, it splits the subjective judgment of "good or bad" into four evaluable dimensions: design quality, originality, craft, and functionality, and then uses few-shot examples to calibrate reviewers.</li>
</ul>
<hr>
<h2 id="4-advanced-methods-for-long-tasks">4. Advanced methods for long tasks</h2>
<p>For short tasks, it is enough to do the above five things well. But when the task needs to run for several hours and span dozens of context window, three advanced modes are needed.</p>
<h3 id="1-separate-initializing-agent-and-executing-agent">1. Separate initializing Agent and executing Agent</h3>
<p>Anthropic's long-term task harness is a dual-role architecture:</p>
<ul>
<li><strong>Initializer Agent</strong>: The first session lays the foundation - write <code class="language-text">init.sh</code>, build a progress file, make the first git commit, and generate the 200+ feature list.</li>
<li><strong>Coding Agent (Execution)</strong>: Each subsequent session follows a fixed process - read the progress → run the E2E smoke test first → complete a feature → submit → update the progress, <strong>must leave a clean state that can be merged</strong> when leaving.</li>
</ul>
<h3 id="2-separation-of-generation-and-evaluation-generatorevaluator">2. Separation of generation and evaluation (Generator/Evaluator)</h3>
<p>Don't let the same Agent be both a player and a referee. <strong>Independent Generators and Evaluators</strong> can crack "self-praise". The evaluator only pays for it when the task is beyond the scope of "a single model can reliably complete it alone" - when the task is simple, this set of overhead is a waste.</p>
<blockquote>
<p>Anthropic's classic controlled experiment: one sentence prompt makes a retro game.</p>
<ul>
<li><strong>Single Agent running alone (solo run)</strong>: $9, 20 minutes, producing an application that cannot run.</li>
<li><strong>Three Agent harness (planning + generation + evaluation)</strong>: $200, 6 hours, to produce a playable game with exquisite UI.</li>
</ul>
<p>The quality gap can support this investment - the premise is that the task is really difficult.</p>
</blockquote>
<h3 id="3-context-reset-is-better-than-context-compression">3. Context reset is better than context compression</h3>
<p>When the model feels that "the token is about to run out", <strong>context anxiety</strong> will occur - ending in a hurry. The intuitive approach is to compress the history into a new window; but Anthropic found that it is better to directly clear the context and start over with a structured handover product (progress file + feature list). This also echoes what LangChain said <strong>"Ralph Loop"</strong>: re-inject prompt in a clean new window to combat <strong>context rot (context rot)</strong>.</p>
<hr>
<h2 id="5-in-depth-reading-of-the-case-how-did-openai-sustain-its-1-million-lines-of-code">5. In-depth reading of the case: How did OpenAI sustain its 1 million lines of code?</h2>
<p>OpenAI's "Harness Engineering: Leveraging Codex in an Agent-First World" is currently the most detailed practical record. Taking it apart, almost every piece of experience can be mapped back to the previous five subsystems - but there are a few points that are counter-intuitive enough and worth talking about separately.</p>
<h3 id="1-the-role-of-engineers-has-changed-dont-write-code-build-the-environment">1. The role of engineers has changed: don’t write code, build the environment</h3>
<p>Early progress was slower than expected. The reason was not that the Codex was not good, but that the environment lacked specifications—the Agent lacked tools, abstractions, and internal structures. So the core work of engineers becomes one sentence: <strong>"What capabilities are missing, and how to make them visible and mandatory to the Agent?"</strong></p>
<p>When something goes wrong, the fix is ​​almost never "make the model work harder" but go back and fix the harness. Humans only work at higher levels of abstraction: prioritizing, translating user feedback into acceptance criteria, and validating results. Even the repairs are done by Codex itself.</p>
<h3 id="2-give-a-map-not-a-thousand-page-manual">2. Give a map, not a thousand-page manual</h3>
<p>They also tried "a big AGENTS.md" at first, and it failed very typically:</p>
<ul>
<li><strong>Context is a scarce resource</strong> - large files crowd out tasks, code, and related documents;</li>
<li><strong>All focus = no focus</strong> - Agent degenerates into partial pattern matching;</li>
<li><strong>Instant rot</strong> - quickly filled with expired rules and left unmaintained;</li>
<li><strong>Difficult to verify</strong> - A large file cannot be mechanically checked (coverage, freshness, cross-linking).</li>
</ul>
<p>Change the method: <strong>Treat AGENTS.md as a table of contents, not an encyclopedia.</strong> An AGENTS.md of about 100 lines is just a "map", and the real sources of truth are placed in the structured <code class="language-text">docs/</code> directory - design documents, execution plans (active/completed/technical debt tracking), generated DB schema, product specifications, <code class="language-text">*-llms.txt</code> references, etc. <strong>Plans are first-class citizens</strong>: Use temporary lightweight plans for small changes, and execute plans signed into the warehouse after completion of complex work, with progress and decision logs.</p>
<p>This is <strong>Progressive Disclosure</strong>: Agent enters from a small but stable entrance and is "taught" where to find the next step. Moreover, <strong>mechanical enforcement</strong> - specialized linter and CI verify whether the knowledge base is up to date and cross-linked; there is also a <strong>"doc-gardening" Agent</strong> that regularly scans expired documents and automatically proposes repair PRs.</p>
<h3 id="3-the-most-important-thing-to-remember-if-the-agent-cannot-be-seen-it-does-not-exist">3. The most important thing to remember: If the Agent cannot be seen, it does not exist.</h3>
<blockquote>
<p>From the agent's point of view, anything it can't access in-context while running effectively doesn't exist.</p>
</blockquote>
<p>That Slack discussion that aligned the team structure? If the Agent cannot retrieve it, it is the same as "newcomers joining the company three months later don't know" and it does not exist. Conclusion: <strong>Continuously stuff context into the warehouse</strong> - code, markdown, schema, executable plan, these are the only things that the Agent can see.</p>
<p>This leads to a set of counter-intuitive trade-offs: <strong>Prefer dependencies and abstractions that the Agent can fully internalize</strong>. The so-called "boring" technologies are better modeled because their APIs are stable, composable, and appear frequently in the training set. Sometimes it is more cost-effective to rewrite a subset yourself than to bypass an opaque upstream library. Instead of using the universal <code class="language-text">p-limit</code>, they hand-write a map-with-concurrency, deeply integrating their own OpenTelemetry, 100% coverage, and fully controllable behavior.</p>
<h3 id="4-make-the-application-itself-readable-by-the-agent-this-is-a-real-bottleneck-in-throughput">4. Make the application itself readable by the Agent (this is a real bottleneck in throughput)</h3>
<p>After the code throughput increases, the bottleneck becomes <strong>human QA capabilities</strong>. The countermeasure is to make the UI, logs, and indicators directly visible to Codex:</p>
<ul>
<li><strong>Each git worktree can independently start an application instance</strong>, one instance is allocated for each change;</li>
<li><strong>Chrome DevTools Protocol is connected to the Agent runtime</strong>, and DOM snapshots, screenshots, and navigation skills are created - Codex can reproduce bugs, verify repairs, and reason about UI behavior by itself;</li>
<li>Each worktree is equipped with a temporary observability stack, and Codex uses LogQL to check logs and PromQL to check indicators. So <strong>"Guarantee that the service startup is completed within 800ms" and "There is no span in these four critical links for more than 2 seconds"</strong> This kind of prompt becomes executable.</li>
</ul>
<p>Effect: <strong>A single Codex run often lasts for more than 6 hours</strong> (usually while the person is sleeping).</p>
<h3 id="5-enforce-invariants-not-micromanage">5. Enforce invariants, not micromanage</h3>
<p>Documentation cannot support the coherence of a fully automatically generated code base, only architectural constraints can. Their slogan is <strong>"enforce invariants, not micromanage implementations"</strong>:</p>
<ul>
<li>It requires "<strong>Resolve data shape at the boundary</strong>", but does not specify how to do it (the model itself likes to use Zod, no one specifies it);</li>
<li>The entire application is built on a <strong>rigid layered architecture</strong>: each business domain has a fixed layer (Types → Config → Repo → Service → Runtime → UI), the dependency direction is strictly verified, and cross-cutting concerns (authentication, connectors, telemetry, feature switches) can only be entered from a single <strong>Providers</strong> interface;</li>
<li>All use <strong>custom linter (also written in Codex, of course) and structured testing mechanical force</strong>; customize the error message of lint<strong>and directly inject repair guidance into the Agent context</strong>.</li>
</ul>
<p>A highlight: **This kind of architecture was originally needed by hundreds of engineers in the company; with Agent, it became a prerequisite from the beginning - constraints are the prerequisite for "fast but not out of order". ** The principle is <strong>"The center enforces boundaries, and local autonomy is allowed"</strong>: Strictly maintain boundaries, correctness, and reproducibility; within the boundaries, give the Agent full freedom. Code does not always conform to human aesthetics. As long as it is correct, maintainable, and readable by future Agents, it will meet the standard.</p>
<h3 id="6-throughput-changes-the-merge-philosophy">6. Throughput changes the merge philosophy</h3>
<p>When the Agent throughput far exceeds the attention span of a human being, many traditional engineering specifications are harmful: the warehouse has almost no blocking merge access control, PR is short-lived, and test flake is solved with "rerun once" instead of blocking indefinitely. Because in this kind of system, <strong>correction is very cheap and waiting is expensive</strong>. - This is irresponsible in a low-throughput environment, but it is often true here.</p>
<p>Going deeper, behind this is the economic core of the entire engineering practice: tokens, computing power, and Agent working hours are no longer scarce. The only thing that is truly scarce is the attention that "requires simultaneous intervention." ** So almost all engineering decisions converge to the same goal - **to save people's simultaneous attention to the extreme. The trade-offs of merging access control, cracking QA bottlenecks, and automatic background cleaning are all essentially saving this item. Once you understand this, all the previous counterintuitive choices will become logical.</p>
<h3 id="7-entropy-and-garbage-collection-encoding-peoples-taste-once-and-forcing-it-permanently">7. Entropy and garbage collection: "encoding people's taste once and forcing it permanently"</h3>
<p>Fully automatic also brings new problems: <strong>Codex will copy the existing patterns in the warehouse, including bad</strong>, so it will inevitably drift.</p>
<p>They initially relied on manual cleaning - <strong>Every Friday (20% of working hours) was dedicated to cleaning "AI slop", without scaling at all.</strong> The real solution is to encode the <strong>golden principles</strong> into the warehouse and equip it with a <strong>periodic cleaning process</strong>:</p>
<ul>
<li>Prioritize using <strong>shared toolkit</strong> to converge the invariants in one place instead of hand-writing scattered helpers everywhere;</li>
<li><strong>Not "YOLO-style" geophysical data</strong> - but check at the boundary or use a typed SDK to prevent the Agent from being built on the guessed data shape;</li>
<li>A set of <strong>background Codex tasks</strong> regularly scan for deviations, update quality scores, and propose directional refactoring PRs - most of which can be reviewed within one minute and automatically merged.</li>
</ul>
<blockquote>
<p>This mechanism is like garbage collection. Technical debt is a high-interest loan: it is always better to continue to repay it in small amounts than to let it compound and repay it in painful clusters. Human taste only needs to be captured once and then continuously enforced on every line of code.</p>
</blockquote>
<h3 id="8-the-tipping-point-of-autonomy">8. The tipping point of autonomy</h3>
<p>As testing, verification, review, feedback processing, and recovery are all coded into the system, this warehouse has recently crossed a threshold: given a prompt, Codex can verify the current status end-to-end → reproduce the bug → record a video of the failure → implement the repair → drive application verification → record a video of the repair → open a PR → respond to feedback from people and agents → detect and fix build failures → <strong>Only upgrade to people when judgment is needed</strong> → Merge.</p>
<blockquote>
<p>But they clearly reminded: <strong>This is highly dependent on the specific structure and tool investment of the warehouse. Don't assume that it can be directly generalized - at least not yet.</strong></p>
</blockquote>
<p><strong>In one sentence:</strong> human discipline has not disappeared; it has moved from the code itself into the harness.</p>
<hr>
<h2 id="6-comparing-approaches-how-far-should-you-go">6. Comparing approaches: how far should you go?</h2>
<p>According to the investment, it is divided into three levels from light to heavy:</p>
<ul>
<li><strong>A. Naked prompt</strong> - only give a README and add nothing.</li>
<li><strong>B. Single Agent + Five Subsystems</strong> - Complete the five items in Section 3; one Agent can span sessions.</li>
<li><strong>C. Multi-Agent complete set</strong> - Layer the advanced methods of Section 4 on top of B (initialization/execution separation, generation/evaluation separation, context reset).</li>
</ul>
<p>How to choose depends on a question: <strong>Can an Agent complete this task in one session?</strong></p>
<ul>
<li>Yes, and throw it away → <strong>A</strong></li>
<li>To continuously iterate and span multiple sessions → <strong>B</strong> (in most cases)</li>
<li>An Agent cannot handle it alone (several hours, spanning dozens of windows, high quality requirements) → <strong>C</strong></li>
</ul>
<table>
<thead>
<tr>
<th></th>
<th align="center">A</th>
<th align="center">B</th>
<th align="center">C</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>▲Get</strong></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td>Mission success rate</td>
<td align="center">Low</td>
<td align="center">high</td>
<td align="center">very high</td>
</tr>
<tr>
<td>Cross-session continuity</td>
<td align="center">none</td>
<td align="center">powerful</td>
<td align="center">powerful</td>
</tr>
<tr>
<td>Quality controllability</td>
<td align="center">Low</td>
<td align="center">high</td>
<td align="center">very high</td>
</tr>
<tr>
<td><strong>▼ Pay</strong></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td>Construction cost</td>
<td align="center">almost zero</td>
<td align="center">middle</td>
<td align="center">high</td>
</tr>
<tr>
<td>Unit task cost</td>
<td align="center">Low</td>
<td align="center">middle</td>
<td align="center">high</td>
</tr>
<tr>
<td>maintenance burden</td>
<td align="center">Low</td>
<td align="center">middle</td>
<td align="center">Middle to high</td>
</tr>
<tr>
<td><strong>→ Select its scene</strong></td>
<td align="center">One-time exploration</td>
<td align="center"><strong>Default</strong>: activities that require continuous iteration</td>
<td align="center">Hard tasks beyond the capabilities of a single model</td>
</tr>
</tbody>
</table>
<p><strong>In summary:</strong></p>
<ul>
<li><strong>The optimal solution for most teams is B</strong> - the highest input-output ratio and the best cost-effectiveness point. <strong>Don’t overlay multiple Agents as soon as you start.</strong></li>
<li><strong>C is only cost-effective when the task is "beyond the capabilities of a single model alone"</strong>: more than a few hours, spanning multiple windows, and with high quality requirements (such as complete applications, complex reconstructions). Its overall rating is not higher than B because the set of overhead in simple tasks is purely wasteful - the complexity must match the difficulty of the task.</li>
<li><strong>A For one-time, disposable exploration only.</strong> Anything that requires continuous iteration, stopping at A will pay the price of repeated rework.</li>
</ul>
<p>A discipline that has been proven repeatedly: After the model is upgraded, go back and perform ablation testing on the harness piece by piece. ** Anthropic discovered on Opus 4.6 that after the model becomes stronger, the original "sprint contract" mechanism can be deleted entirely without a drop in quality and a significant drop in tokens. **Harness does not just increase but does not decrease - as the model adds capabilities, the burden on the harness should be reduced.</p>
<hr>
<h2 id="7-ready-to-use-harness-checklist">7. Ready-to-use harness checklist</h2>
<p>When you land, check this list:</p>
<ul>
<li><strong>Instructions</strong>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> There is a <code class="language-text">AGENTS.md</code> / <code class="language-text">CLAUDE.md</code> with ≤100 lines. Write down the project, technology stack, first run command, and red line constraints.</li>
<li class="task-list-item"><input type="checkbox" disabled> Follow the links for details, don’t pile them up into an encyclopedia.</li>
<li class="task-list-item"><input type="checkbox" disabled> Everything the Agent needs is in the repository (repo = single source of truth)</li>
</ul>
</li>
<li><strong>Tools / Environment</strong>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Dependencies, runtime versions, and container configurations are complete, and the environment can be started with one click (<code class="language-text">init.sh</code>)</li>
<li class="task-list-item"><input type="checkbox" disabled> Provides a sandbox that can run bash/execute code</li>
<li class="task-list-item"><input type="checkbox" disabled> Permissions should be granted based on the least privileges, rather than banning them across the board.</li>
</ul>
</li>
<li><strong>State</strong>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> There is a progress file to record "finished/in progress/stuck"</li>
<li class="task-list-item"><input type="checkbox" disabled> Reading progress + git log at the beginning of the session, updated before the end</li>
<li class="task-list-item"><input type="checkbox" disabled> Use git to commit and leave traces</li>
</ul>
</li>
<li><strong>Scope</strong>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Split the task into a machine-readable feature list (JSON is recommended)</li>
<li class="task-list-item"><input type="checkbox" disabled> Only do one feature per session</li>
</ul>
</li>
<li><strong>Verification</strong>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> The test / lint / typecheck commands are explicitly listed in the documentation.</li>
<li class="task-list-item"><input type="checkbox" disabled> Web applications have end-to-end validation (browser automation)</li>
<li class="task-list-item"><input type="checkbox" disabled> Subjective quality has been broken down into objective dimensions that can be scored</li>
</ul>
</li>
<li><strong>Long mission extras</strong>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> Separation of initialization and execution of Agent</li>
<li class="task-list-item"><input type="checkbox" disabled> Separation of generation and evaluation (only if the task is difficult enough)</li>
<li class="task-list-item"><input type="checkbox" disabled> Use context reset + handover products instead of blind compression</li>
</ul>
</li>
</ul>
<hr>
<h2 id="8-why-this-matter-will-be-important-in-the-long-run">8. Why this matter will be important in the long run</h2>
<p>Some people may ask: Isn’t the model getting stronger and stronger? When it is smart enough, won’t the harness be useless?</p>
<p>Quite the opposite. The judgments of LangChain and OpenAI are consistent: the stronger the model, the focus of harness is just shifting from "patch-filling the pits of the model" to "optimizing the system for intelligence" - but it will not disappear. Just as models are no longer stupid today, prompt engineering is still important.</p>
<p>There is also a layer of harder coupling that is often overlooked: Today's strong models are trained together with the harness. <strong>LangChain pointed out that products such as Claude Code put "model + harness" into the same loop for joint debugging in the post-training stage, and the model is specially optimized to fit the shell it was trained in. This also explains a counter-intuitive phenomenon - putting a strong model into an unfamiliar harness often fails to achieve its true level.</strong> The relationship between model and harness is co-evolution, not that one will replace the other sooner or later.</p>
<p>OpenAI’s words pointed out the industry’s turn:</p>
<blockquote>
<p>In the agent-first world, the engineer's main job is no longer to write code, but to design an environment that allows the Agent to produce reliable output.</p>
</blockquote>
<p>The model is the engine, and the harness is the chassis, transmission and instrument panel. <strong>With just an engine, it can't run far and it's not safe.</strong></p>
<hr>
<h2 id="references">References</h2>
<ul>
<li>LangChain — <a href="https://www.langchain.com/blog/the-anatomy-of-an-agent-harness">The Anatomy of an Agent Harness</a>: Component panorama of <code class="language-text">Agent = Model + Harness</code>.</li>
<li>Anthropic — <a href="https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents">Effective harnesses for long-running agents</a>: Actual implementation of initializing Agent, feature list, and self-verification.</li>
<li>Anthropic — <a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running apps</a>: Generation/evaluation separation, context reset, scoreable quality.</li>
<li>OpenAI — <a href="https://openai.com/index/harness-engineering/">Harness Engineering: Leveraging Codex in an Agent-First World</a>: One million lines of code and the golden rule.</li>
<li>WalkingLabs — <a href="https://github.com/walkinglabs/learn-harness-engineering">learn-harness-engineering</a> (<a href="https://walkinglabs.github.io/learn-harness-engineering/">Online Handouts</a>): 12 lectures + 6 hands-on projects systems course (source of the Five Subsystems Framework).</li>
<li>WalkingLabs — <a href="https://github.com/walkinglabs/awesome-harness-engineering">awesome-harness-engineering</a>: Collection of resources (Context, Assessment, Guardrails, Orchestration, Benchmark).</li>
</ul>]]></content>
  </entry>
  <entry>
    <title>Understanding MCP (Model Context Protocol)</title>
    <id>https://shipengtao.com/en/mcp-guide/</id>
    <link rel="alternate" type="text/html" href="https://shipengtao.com/en/mcp-guide/"/>
    <published>2026-05-28T00:00:00+08:00</published>
    <updated>2026-05-28T00:00:00+08:00</updated>
    <summary type="text">An introductory guide for engineers and technical decision-makers. From concepts to practice, with Python SDK examples. 1. Why MCP? Before…</summary>
    <content type="html"><![CDATA[<blockquote>
<p>An introductory guide for engineers and technical decision-makers. From concepts to practice, with Python SDK examples.</p>
</blockquote>
<hr>
<h2 id="1-why-mcp">1. Why MCP?</h2>
<p>Before MCP, wiring an AI application to external systems looked roughly like this:</p>
<ul>
<li>Want the model to query a database? Write a function calling schema, write a handler, write permission checks.</li>
<li>Want the model to read files? Do it all again.</li>
<li>The same "look up a GitHub Issue" capability — Claude Desktop, Cursor, VS Code, your own Agent — each implements it separately.</li>
<li>As tools accumulate, prompt-maintenance cost grows fast, and cross-application reuse is effectively zero.</li>
</ul>
<p>This is the classic <strong>M × N integration problem</strong>: M AI applications × N data sources/tools = M × N integration codebases.</p>
<p><strong>MCP (Model Context Protocol)</strong> is an open protocol led by Anthropic that has become the de facto standard. Its goal is to collapse M × N into <strong>M + N</strong>:</p>
<ul>
<li>Data-source/tool vendors only need to implement an MCP <strong>Server</strong> once;</li>
<li>AI applications only need to implement an MCP <strong>Client</strong> once;</li>
<li>Any Client can plug into any Server.</li>
</ul>
<p>The official one-liner: <strong>MCP is to AI applications what USB-C is to electronics</strong> — a single, unified interface that connects everything.</p>
<hr>
<h2 id="2-the-big-picture-in-one-diagram">2. The Big Picture in One Diagram</h2>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">                          ┌─ Tools       (model-controlled, side effects)
              Server ─────┼─ Resources   (application-controlled, read-only context)
             /            └─ Prompts     (user-controlled, workflow templates)
            /
        Client
       /
  Host
       \                  ┌─ Sampling    (Server asks LLM in reverse)
        Client ───────────┼─ Elicitation (Server asks user in reverse)
             \            ├─ Roots       (Client tells Server its working directories)
              \           └─ Logging     (Server emits logs)
               Server

         Data layer:      JSON-RPC 2.0
         Transport layer: stdio (local) / Streamable HTTP (remote, OAuth supported)</code></pre></div>
<ul>
<li><strong>Transport layer</strong>: Client and Server speak JSON-RPC 2.0, carried over stdio or Streamable HTTP.</li>
<li><strong>Server primitives</strong>: capabilities Server actively exposes to the Host, targeting three different "decision-makers" — the <strong>model</strong> (Tools), the <strong>application</strong> (Resources), and the <strong>user</strong> (Prompts).</li>
<li><strong>Client primitives</strong>: capabilities Client exposes for Server to call back, letting the Server complete agentic workflows without depending directly on an LLM/UI itself.</li>
</ul>
<hr>
<h2 id="3-the-three-most-confusable-roles-host--client--server">3. The Three Most Confusable Roles: Host / Client / Server</h2>
<p>This is the easiest place for newcomers to get tangled up. The relationship looks like this:</p>
<p><svg id="mermaid-0" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="flowchart" style="max-width: 946.3359375px;" viewBox="0 0 946.3359375 296" role="graphics-document document" aria-roledescription="flowchart-v2"><style>#mermaid-0{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-0 .error-icon{fill:#552222;}#mermaid-0 .error-text{fill:#552222;stroke:#552222;}#mermaid-0 .edge-thickness-normal{stroke-width:1px;}#mermaid-0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-0 .marker{fill:#333333;stroke:#333333;}#mermaid-0 .marker.cross{stroke:#333333;}#mermaid-0 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-0 p{margin:0;}#mermaid-0 .label{font-family:arial,sans-serif;color:#333;}#mermaid-0 .cluster-label text{fill:#333;}#mermaid-0 .cluster-label span{color:#333;}#mermaid-0 .cluster-label span p{background-color:transparent;}#mermaid-0 .label text,#mermaid-0 span{fill:#333;color:#333;}#mermaid-0 .node rect,#mermaid-0 .node circle,#mermaid-0 .node ellipse,#mermaid-0 .node polygon,#mermaid-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-0 .rough-node .label text,#mermaid-0 .node .label text,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-anchor:middle;}#mermaid-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-0 .rough-node .label,#mermaid-0 .node .label,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-align:center;}#mermaid-0 .node.clickable{cursor:pointer;}#mermaid-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-0 .arrowheadPath{fill:#333333;}#mermaid-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#mermaid-0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-0 .cluster text{fill:#333;}#mermaid-0 .cluster span{color:#333;}#mermaid-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-0 rect.text{fill:none;stroke-width:0;}#mermaid-0 .icon-shape,#mermaid-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .icon-shape p,#mermaid-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-0 .icon-shape .label rect,#mermaid-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-0 .node .neo-node{stroke:#9370DB;}#mermaid-0 [data-look="neo"].node rect,#mermaid-0 [data-look="neo"].cluster rect,#mermaid-0 [data-look="neo"].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look="neo"].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-0 [data-look="neo"].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look="neo"].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-0 [data-look="neo"].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look="neo"].node circle .state-start{fill:#000000;}#mermaid-0 [data-look="neo"].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look="neo"].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 :root{--mermaid-font-family:arial,sans-serif;}</style><g><marker id="mermaid-0_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="mermaid-0_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="mermaid-0_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></path></marker><marker id="mermaid-0_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></polygon></marker><marker id="mermaid-0_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="mermaid-0_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="mermaid-0_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="mermaid-0_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="mermaid-0_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="mermaid-0_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="mermaid-0_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"></path></marker><marker id="mermaid-0_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"></path></marker><g class="root"><g class="clusters"><g class="cluster" id="mermaid-0-subGraph0" data-look="classic"><rect style="" x="8" y="8" width="930.3359375" height="104"></rect><g class="cluster-label" transform="translate(380.83203125, 8)"><foreignObject width="184.671875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5;"><span class="nodeLabel"><p>MCP Host (AI Application)</p></span></div></foreignObject></g></g></g><g class="edgePaths"><path d="M119.984,87L119.984,91.167C119.984,95.333,119.984,103.667,119.984,116C119.984,128.333,119.984,144.667,119.984,161C119.984,177.333,119.984,193.667,119.984,201.833L119.984,210" id="mermaid-0-L_Client1_ServerA_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_Client1_ServerA_0" data-points="W3sieCI6MTE5Ljk4NDM3NSwieSI6ODd9LHsieCI6MTE5Ljk4NDM3NSwieSI6MTEyfSx7IngiOjExOS45ODQzNzUsInkiOjE2MX0seyJ4IjoxMTkuOTg0Mzc1LCJ5IjoyMTB9XQ==" data-look="classic"></path><path d="M383.523,87L383.523,91.167C383.523,95.333,383.523,103.667,383.523,116C383.523,128.333,383.523,144.667,383.523,161C383.523,177.333,383.523,193.667,383.523,201.833L383.523,210" id="mermaid-0-L_Client2_ServerB_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_Client2_ServerB_0" data-points="W3sieCI6MzgzLjUyMzQzNzUsInkiOjg3fSx7IngiOjM4My41MjM0Mzc1LCJ5IjoxMTJ9LHsieCI6MzgzLjUyMzQzNzUsInkiOjE2MX0seyJ4IjozODMuNTIzNDM3NSwieSI6MjEwfV0=" data-look="classic"></path><path d="M622.383,87L622.383,91.167C622.383,95.333,622.383,103.667,622.383,116C622.383,128.333,622.383,144.667,631.847,161C641.312,177.333,660.241,193.667,669.705,201.833L679.17,210" id="mermaid-0-L_Client3_ServerC_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_Client3_ServerC_0" data-points="W3sieCI6NjIyLjM4MjgxMjUsInkiOjg3fSx7IngiOjYyMi4zODI4MTI1LCJ5IjoxMTJ9LHsieCI6NjIyLjM4MjgxMjUsInkiOjE2MX0seyJ4Ijo2NzkuMTY5NTY2NzYxMzYzNiwieSI6MjEwfV0=" data-look="classic"></path><path d="M826.352,87L826.352,91.167C826.352,95.333,826.352,103.667,826.352,116C826.352,128.333,826.352,144.667,816.887,161C807.423,177.333,788.494,193.667,779.029,201.833L769.565,210" id="mermaid-0-L_Client4_ServerC_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_Client4_ServerC_0" data-points="W3sieCI6ODI2LjM1MTU2MjUsInkiOjg3fSx7IngiOjgyNi4zNTE1NjI1LCJ5IjoxMTJ9LHsieCI6ODI2LjM1MTU2MjUsInkiOjE2MX0seyJ4Ijo3NjkuNTY0ODA4MjM4NjM2NCwieSI6MjEwfV0=" data-look="classic"></path></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(119.984375, 161)"><g class="label" data-id="L_Client1_ServerA_0" transform="translate(-38.6953125, -24)"><foreignObject width="77.390625" height="48"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Dedicated<br></br>connection</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(383.5234375, 161)"><g class="label" data-id="L_Client2_ServerB_0" transform="translate(-38.6953125, -24)"><foreignObject width="77.390625" height="48"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Dedicated<br></br>connection</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(622.3828125, 161)"><g class="label" data-id="L_Client3_ServerC_0" transform="translate(-38.6953125, -24)"><foreignObject width="77.390625" height="48"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Dedicated<br></br>connection</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(826.3515625, 161)"><g class="label" data-id="L_Client4_ServerC_0" transform="translate(-38.6953125, -24)"><foreignObject width="77.390625" height="48"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Dedicated<br></br>connection</p></span></div></foreignObject></g></g></g><g class="nodes"><g class="node default" id="mermaid-0-flowchart-Client1-0" data-look="classic" transform="translate(119.984375, 60)"><rect class="basic label-container" style="" x="-76.984375" y="-27" width="153.96875" height="54"></rect><g class="label" style="" transform="translate(-46.984375, -12)"><rect></rect><foreignObject width="93.96875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>MCP Client 1</p></span></div></foreignObject></g></g><g class="node default" id="mermaid-0-flowchart-Client2-1" data-look="classic" transform="translate(383.5234375, 60)"><rect class="basic label-container" style="" x="-76.984375" y="-27" width="153.96875" height="54"></rect><g class="label" style="" transform="translate(-46.984375, -12)"><rect></rect><foreignObject width="93.96875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>MCP Client 2</p></span></div></foreignObject></g></g><g class="node default" id="mermaid-0-flowchart-Client3-2" data-look="classic" transform="translate(622.3828125, 60)"><rect class="basic label-container" style="" x="-76.984375" y="-27" width="153.96875" height="54"></rect><g class="label" style="" transform="translate(-46.984375, -12)"><rect></rect><foreignObject width="93.96875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>MCP Client 3</p></span></div></foreignObject></g></g><g class="node default" id="mermaid-0-flowchart-Client4-3" data-look="classic" transform="translate(826.3515625, 60)"><rect class="basic label-container" style="" x="-76.984375" y="-27" width="153.96875" height="54"></rect><g class="label" style="" transform="translate(-46.984375, -12)"><rect></rect><foreignObject width="93.96875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>MCP Client 4</p></span></div></foreignObject></g></g><g class="node default" id="mermaid-0-flowchart-ServerA-4" data-look="classic" transform="translate(119.984375, 249)"><rect class="basic label-container" style="" x="-106.328125" y="-39" width="212.65625" height="78"></rect><g class="label" style="" transform="translate(-76.328125, -24)"><rect></rect><foreignObject width="152.65625" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>MCP Server A - Local<br></br>(e.g. Filesystem)</p></span></div></foreignObject></g></g><g class="node default" id="mermaid-0-flowchart-ServerB-5" data-look="classic" transform="translate(383.5234375, 249)"><rect class="basic label-container" style="" x="-107.2109375" y="-39" width="214.421875" height="78"></rect><g class="label" style="" transform="translate(-77.2109375, -24)"><rect></rect><foreignObject width="154.421875" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>MCP Server B - Local<br></br>(e.g. Database)</p></span></div></foreignObject></g></g><g class="node default" id="mermaid-0-flowchart-ServerC-6" data-look="classic" transform="translate(724.3671875, 249)"><rect class="basic label-container" style="" x="-116.5390625" y="-39" width="233.078125" height="78"></rect><g class="label" style="" transform="translate(-86.5390625, -24)"><rect></rect><foreignObject width="173.078125" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>MCP Server C - Remote<br></br>(e.g. Sentry)</p></span></div></foreignObject></g></g></g></g></g><defs><filter id="mermaid-0-drop-shadow" height="130%" width="130%"><feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><defs><filter id="mermaid-0-drop-shadow-small" height="150%" width="150%"><feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs></svg></p>
<p>Notice that Server C in the lower right is connected to two Clients at once — <strong>a remote Server typically serves multiple Clients</strong>, while a local stdio Server is usually 1:1.</p>
<table>
<thead>
<tr>
<th>Role</th>
<th>What it is</th>
<th>Examples</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Host</strong></td>
<td>The AI application the user interacts with directly; coordinates multiple Clients</td>
<td>Claude Desktop, Cursor, VS Code, ChatGPT, your own Agent</td>
</tr>
<tr>
<td><strong>Client</strong></td>
<td>A component inside the Host; each Client maintains a 1:1 dedicated connection to one Server</td>
<td>The Host creates one Client per configured Server at startup</td>
</tr>
<tr>
<td><strong>Server</strong></td>
<td>A program that exposes tools/data/templates, either local or remote</td>
<td>filesystem, postgres, github, sentry, Slack, etc.</td>
</tr>
</tbody>
</table>
<blockquote>
<p>A common misconception: that a "Server" must be a remote service. Wrong. <strong>Server is a protocol role, not a deployment shape.</strong> A local subprocess launched over stdio is just as much a Server.</p>
</blockquote>
<hr>
<h2 id="4-the-two-layers-of-the-protocol-data--transport">4. The Two Layers of the Protocol: Data + Transport</h2>
<p>MCP's design cleanly separates two layers so they can be reused in different scenarios.</p>
<h3 id="1-data-layer">1. Data Layer</h3>
<p>Built on <strong>JSON-RPC 2.0</strong>, defining message structure and semantics. It includes:</p>
<ul>
<li><strong>Lifecycle management</strong>: connection setup, capability negotiation, connection teardown</li>
<li><strong>Server capabilities</strong>: tools, resources, prompts</li>
<li><strong>Client capabilities</strong>: sampling, elicitation, logging, roots</li>
<li><strong>Notification mechanism</strong>: list changes, progress updates, etc.</li>
</ul>
<h3 id="2-transport-layer">2. Transport Layer</h3>
<p>Responsible for shuttling data-layer JSON messages between the two ends. MCP currently defines two official transports:</p>
<table>
<thead>
<tr>
<th>Transport</th>
<th>Use case</th>
<th>Characteristics</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>stdio</strong></td>
<td>Local Server</td>
<td>The Host launches the Server as a subprocess and communicates over stdin/stdout. Zero network overhead, ideal for local tools (files, Git, local DBs).</td>
</tr>
<tr>
<td><strong>Streamable HTTP</strong></td>
<td>Remote Server</td>
<td>Clients send requests via HTTP POST; servers optionally push streaming responses via Server-Sent Events (SSE). Supports standard HTTP auth (Bearer Token, OAuth) — well suited to SaaS-style MCP Servers.</td>
</tr>
</tbody>
</table>
<blockquote>
<p>Older versions of the protocol had a separate SSE transport, but <strong>the new spec folds it into Streamable HTTP</strong>. For new code, just pick Streamable HTTP.</p>
</blockquote>
<hr>
<h2 id="5-the-three-server-primitives-tools--resources--prompts">5. The Three Server Primitives: Tools / Resources / Prompts</h2>
<p>This is the part you'll touch most often in day-to-day development. The three are easy to confuse, but their design intent is completely different — <strong>the key distinction is "who decides to invoke them"</strong>.</p>
<table>
<thead>
<tr>
<th>Primitive</th>
<th>Control</th>
<th>Analogy</th>
<th>Typical use</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Tools</strong></td>
<td><strong>Model</strong>-controlled</td>
<td>Like REST <code class="language-text">POST</code></td>
<td>The model decides to invoke; produces side effects: writing a DB, sending a message, calling an API</td>
</tr>
<tr>
<td><strong>Resources</strong></td>
<td><strong>Application</strong>-controlled</td>
<td>Like REST <code class="language-text">GET</code></td>
<td>Passive, read-only data source. The host application chooses when to push which context to the model</td>
</tr>
<tr>
<td><strong>Prompts</strong></td>
<td><strong>User</strong>-controlled</td>
<td>Like a Slash Command</td>
<td>Prebuilt workflow templates the user actively invokes (e.g. <code class="language-text">/plan-vacation</code>)</td>
</tr>
</tbody>
</table>
<p>I like to remember it as:</p>
<blockquote>
<p><strong>Tools are verbs, Resources are nouns, Prompts are workflows.</strong></p>
</blockquote>
<h3 id="tools-invoked-by-the-model">Tools: invoked by the model</h3>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token comment"># Hearing the user say "check the weather in San Francisco", the model decides to call this tool itself</span>
<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>tool</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">get_weather</span><span class="token punctuation">(</span>city<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Get the current weather for a city"""</span>
    <span class="token keyword">return</span> fetch_weather_api<span class="token punctuation">(</span>city<span class="token punctuation">)</span></code></pre></div>
<p>Related methods: <code class="language-text">tools/list</code> (discovery), <code class="language-text">tools/call</code> (execution).</p>
<h3 id="resources-read-on-demand-by-the-application">Resources: read on demand by the application</h3>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token comment"># The application decides whether to feed this README to the model as context</span>
<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>resource</span><span class="token punctuation">(</span><span class="token string">"file:///{path}"</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">read_file</span><span class="token punctuation">(</span>path<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> <span class="token builtin">open</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre></div>
<p>Each resource has a URI (e.g. <code class="language-text">file:///README.md</code>, <code class="language-text">postgres://mydb/schema</code>) and supports templating (<code class="language-text">weather://forecast/{city}</code>). Related methods: <code class="language-text">resources/list</code>, <code class="language-text">resources/templates/list</code>, <code class="language-text">resources/read</code>, <code class="language-text">resources/subscribe</code>.</p>
<h3 id="tools-vs-resources-full-data-flow-and-how-to-choose">Tools vs Resources: full data flow and how to choose</h3>
<p>Looking only at "who controls it" isn't intuitive enough. Compare the full data flow of the two:</p>
<p><strong>Tools flow</strong> (triggered within a model turn):</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">1. Host starts up → calls tools/list to get schemas
2. Host injects schemas into the tools field of the LLM call
3. User sends a message → LLM reasons → emits a tool_use block
4. Host intercepts → routes to the right Server → sends tools/call
5. Server executes (runs SQL, hits an API) → returns content
6. Host stitches content back into the conversation as tool_result
7. LLM continues generating (may trigger another round, looping 3-6)</code></pre></div>
<p><strong>Resources flow</strong> (injected outside the conversation turn):</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">1. Host starts up → calls resources/list and resources/templates/list
2. Host surfaces resources in the UI (file tree / @ completion / auto-suggestion)
3. Three ways a resource enters conversation context:
   a. User manually @-references it
   b. Application heuristically injects it
   c. Bridged as a Tool so the model can invoke it (detailed below)
4. Host calls resources/read(uri) → Server returns contents
5. Host attaches contents as part of a user message to the LLM
6. By the time the LLM sees it, it's already "in the context" as a fact</code></pre></div>
<p><strong>One-line distinction</strong>:</p>
<blockquote>
<p><strong>Tools are pulled by the model during an LLM turn; Resources are pushed by the application outside an LLM turn.</strong></p>
</blockquote>
<p><strong>"Data, but requires computation" — which should I pick?</strong> Decision order:</p>
<ol>
<li><strong>Side effects?</strong> Yes → must be a Tool (even if just for telemetry).</li>
<li><strong>Want the model to decide autonomously?</strong> → Tool.</li>
<li><strong>Want the user/application to preselect what gets pushed as context?</strong> → Resource Template.</li>
<li><strong>Client ecosystem considerations</strong>: today, Tools are supported in every Host, while UI support for Resources varies a lot. <strong>Many Servers expose the same capability as both a Tool and a Resource</strong> — Tool for fallback compatibility, Resource for a more elegant presentation on Hosts that support it.</li>
</ol>
<p><strong>Can Resources also be driven by the LLM?</strong> The protocol layer doesn't forbid it — "application-controlled" is design intent, not a technical constraint. The common pattern is <strong>Resource-as-Tool bridging</strong>: the Host injects two synthetic tools into the LLM's tool list:</p>
<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"list_resources"</span><span class="token punctuation">,</span> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"List all available MCP resources"</span><span class="token punctuation">,</span> ... <span class="token punctuation">}</span>
<span class="token punctuation">{</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"read_resource"</span><span class="token punctuation">,</span>  <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Read a resource by URI"</span><span class="token punctuation">,</span>         ... <span class="token punctuation">}</span></code></pre></div>
<p>The LLM decides to call <code class="language-text">read_resource(uri="postgres://schema/users")</code> → the Host forwards it as <code class="language-text">resources/read</code>. <strong>The form is a Tool, but the essence is the LLM driving a Resource.</strong> Claude Code does this by default.</p>
<p><strong>What exactly is "application heuristic injection"?</strong> A few real-world scenarios:</p>
<ul>
<li><strong>Context injection in IDE-style Hosts</strong>: every message automatically includes the file currently under the cursor, the few most recently edited files, and convention files like <code class="language-text">.cursorrules</code> / <code class="language-text">CLAUDE.md</code>.</li>
<li><strong>Project-open auto-loading</strong>: when a project is opened, automatically read <code class="language-text">README.md</code>, <code class="language-text">package.json</code>, <code class="language-text">.env.example</code> as system context.</li>
<li><strong>Semantic search auto-recall</strong>: embed the user message, vector-search across all resources, automatically splice in the Top-K hits.</li>
<li><strong>Subscription-based live sync</strong>: user edits a file → Server pushes <code class="language-text">notifications/resources/updated</code> → Host refreshes the new content into the next turn of context.</li>
</ul>
<p>None of these require explicit initiation from user or model; the Host decides via its own ruleset.</p>
<h3 id="prompts-explicitly-user-triggered">Prompts: explicitly user-triggered</h3>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>prompt</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">plan_vacation</span><span class="token punctuation">(</span>destination<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> days<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Generate a vacation plan"""</span>
    <span class="token keyword">return</span> <span class="token string-interpolation"><span class="token string">f"Please help me plan a </span><span class="token interpolation"><span class="token punctuation">{</span>days<span class="token punctuation">}</span></span><span class="token string">-day trip to </span><span class="token interpolation"><span class="token punctuation">{</span>destination<span class="token punctuation">}</span></span><span class="token string">..."</span></span></code></pre></div>
<p>In applications like Claude Desktop, Claude Code, and Cursor, Prompts typically surface as <code class="language-text">/</code> commands or shortcuts in a command palette.</p>
<p>Three commonly confused points about Prompts deserve clarification:</p>
<p><strong>1. "User-only triggering" is a UX convention, not a protocol law.</strong> The protocol layer doesn't restrict who calls <code class="language-text">prompts/get</code> — any party with a session can. "user-controlled" is a UX recommendation given to Hosts in the spec, intended to make users feel that they are actively initiating the action and to discourage models from silently invoking templates. A custom Agent can absolutely <code class="language-text">prompts/get</code> automatically at a workflow node — technically nothing stops it.</p>
<p><strong>2. The return value is not just a string.</strong> What <code class="language-text">prompts/get</code> actually returns is a <code class="language-text">messages</code> array:</p>
<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
  <span class="token property">"messages"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span> <span class="token property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>      <span class="token property">"content"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>  <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"..."</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> <span class="token property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span> <span class="token property">"content"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>  <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"..."</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> <span class="token property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>      <span class="token property">"content"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image"</span><span class="token punctuation">,</span> <span class="token property">"data"</span><span class="token operator">:</span> <span class="token string">"base64..."</span><span class="token punctuation">,</span> <span class="token property">"mimeType"</span><span class="token operator">:</span> <span class="token string">"image/png"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> <span class="token property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>      <span class="token property">"content"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"resource"</span><span class="token punctuation">,</span> <span class="token property">"resource"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"uri"</span><span class="token operator">:</span> <span class="token string">"..."</span><span class="token punctuation">,</span> <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"..."</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre></div>
<p>You can construct <strong>multi-turn few-shot examples</strong>, <strong>multimodal prompts containing images/audio</strong>, or <strong>embedded resource references</strong>. In FastMCP, having <code class="language-text">@mcp.prompt()</code> return a string is just a convenience wrapper — it gets auto-wrapped into a single user message. For richer structures, explicitly return <code class="language-text">list[Message]</code>:</p>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">from</span> mcp<span class="token punctuation">.</span>server<span class="token punctuation">.</span>fastmcp<span class="token punctuation">.</span>prompts <span class="token keyword">import</span> base

<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>prompt</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">debug_session</span><span class="token punctuation">(</span>error<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">list</span><span class="token punctuation">[</span>base<span class="token punctuation">.</span>Message<span class="token punctuation">]</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> <span class="token punctuation">[</span>
        base<span class="token punctuation">.</span>UserMessage<span class="token punctuation">(</span><span class="token string">"I ran into this error:"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        base<span class="token punctuation">.</span>UserMessage<span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">,</span>
        base<span class="token punctuation">.</span>AssistantMessage<span class="token punctuation">(</span><span class="token string">"Let me help analyze. First, a few clarifying questions..."</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span></code></pre></div>
<p><strong>3. Prompts ≠ a lightweight version of Skills.</strong> These two are often conflated, but they solve completely different problems:</p>
<table>
<thead>
<tr>
<th>Dimension</th>
<th><strong>MCP Prompts</strong></th>
<th><strong>Claude Skills</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Core problem</td>
<td>Let the user initiate a <strong>known task</strong> with a template</td>
<td>Let the model <strong>autonomously acquire a capability</strong> when appropriate</td>
</tr>
<tr>
<td>Trigger</td>
<td>User (actively selects in UI)</td>
<td>Model (decides based on <code class="language-text">SKILL.md</code> frontmatter)</td>
</tr>
<tr>
<td>Form</td>
<td>Protocol messages, fetched over JSON-RPC</td>
<td>A markdown bundle on the filesystem (with optional scripts)</td>
</tr>
<tr>
<td>Content</td>
<td>Returns a messages array fed to the LLM</td>
<td>Markdown instructions + executable code + resource files</td>
</tr>
<tr>
<td>Execution</td>
<td>Pure template substitution; no code execution</td>
<td>The Agent may execute scripts inside</td>
</tr>
<tr>
<td>Control plane</td>
<td>Server primitive</td>
<td>Model-side capability</td>
</tr>
</tbody>
</table>
<p>A more accurate analogy:</p>
<blockquote>
<p><strong>Prompts are like IDE Code Snippets or Notion Templates</strong>: user selects → template gets filled in.
<strong>Skills are like Unix man pages + executable scripts</strong>: the model reads the manual → decides whether to use it → may also execute the embedded code.</p>
</blockquote>
<p>In a sentence: <strong>Prompts are "templates the user can use"; Skills are "capabilities the model can learn".</strong></p>
<hr>
<h2 id="6-client-primitives-letting-the-server-call-the-client-back">6. Client Primitives: Letting the Server "Call" the Client Back</h2>
<p>The Server isn't just a passive responder — it can also actively request capabilities from the Client. There are four client primitives:</p>
<table>
<thead>
<tr>
<th>Client primitive</th>
<th>Purpose</th>
<th>Canonical scenario</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Sampling</strong></td>
<td>Server asks the Host's LLM to run inference on its behalf</td>
<td>Server wants to analyze 47 flight options but doesn't want to embed an LLM SDK, so it has the Client do it</td>
</tr>
<tr>
<td><strong>Elicitation</strong></td>
<td>Server asks the user for information or confirmation</td>
<td>"Please confirm this $3,000 order," paired with a JSON Schema describing the required fields</td>
</tr>
<tr>
<td><strong>Roots</strong></td>
<td>Client tells the Server "you may only operate within these directories"</td>
<td>The IDE exposes the currently open project directory to the Server</td>
</tr>
<tr>
<td><strong>Logging</strong></td>
<td>Server emits logs to the Client for debugging and observability</td>
<td>Print debug info during tool execution</td>
</tr>
</tbody>
</table>
<p><strong>Why Sampling is elegant</strong>: the Server wants to use an LLM but doesn't want to pay for one or be locked to a specific provider, so it reverse-delegates "calling the LLM" to the Client. The Client already has LLM access (the user has paid for/authorized it), so the Client bears the cost and effort, and the user can audit both the prompt and the response in the middle — this is human-in-the-loop by design.</p>
<hr>
<h2 id="7-lifecycle-a-full-handshake">7. Lifecycle: A Full Handshake</h2>
<p>With roles and primitives in mind, let's walk through a minimal complete JSON-RPC flow — useful for the next time you need to debug with packet captures.</p>
<h3 id="step-1-initialization-handshake">Step 1. Initialization handshake</h3>
<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token comment">// Client → Server</span>
<span class="token punctuation">{</span>
  <span class="token property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span> <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token property">"method"</span><span class="token operator">:</span> <span class="token string">"initialize"</span><span class="token punctuation">,</span>
  <span class="token property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">"protocolVersion"</span><span class="token operator">:</span> <span class="token string">"2025-06-18"</span><span class="token punctuation">,</span>
    <span class="token property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"elicitation"</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token property">"clientInfo"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"example-client"</span><span class="token punctuation">,</span> <span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span> <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre></div>
<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token comment">// Server → Client</span>
<span class="token punctuation">{</span>
  <span class="token property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span> <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
  <span class="token property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">"protocolVersion"</span><span class="token operator">:</span> <span class="token string">"2025-06-18"</span><span class="token punctuation">,</span>
    <span class="token property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token property">"tools"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"listChanged"</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token property">"resources"</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token property">"serverInfo"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"example-server"</span><span class="token punctuation">,</span> <span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span> <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre></div>
<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token comment">// Client → Server (notification — no id, no return value)</span>
<span class="token punctuation">{</span> <span class="token property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span> <span class="token property">"method"</span><span class="token operator">:</span> <span class="token string">"notifications/initialized"</span> <span class="token punctuation">}</span></code></pre></div>
<p>A few key points here:</p>
<ol>
<li><strong>Protocol version negotiation</strong>: both sides must use compatible versions, otherwise the connection terminates.</li>
<li><strong>Capability declarations</strong>: the Server <strong>explicitly lists the primitives it supports</strong> under <code class="language-text">capabilities</code> — in this example only <code class="language-text">tools</code> and <code class="language-text">resources</code> are declared, meaning the Server doesn't support prompts and the Client won't issue any <code class="language-text">prompts/*</code> calls. A server that declares <code class="language-text">tools.listChanged: true</code> will proactively send <code class="language-text">notifications/tools/list_changed</code> whenever its tool list changes.</li>
<li><strong><code class="language-text">{}</code> is not an empty object</strong>: it means "I support this capability, with no configurable options" — the minimal form of a declaration.</li>
</ol>
<h3 id="step-2-discover-tools">Step 2. Discover tools</h3>
<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token comment">// Client → Server</span>
<span class="token punctuation">{</span> <span class="token property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span> <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token property">"method"</span><span class="token operator">:</span> <span class="token string">"tools/list"</span> <span class="token punctuation">}</span></code></pre></div>
<h3 id="step-3-invoke-a-tool">Step 3. Invoke a tool</h3>
<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token comment">// Client → Server</span>
<span class="token punctuation">{</span>
  <span class="token property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span> <span class="token property">"id"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token property">"method"</span><span class="token operator">:</span> <span class="token string">"tools/call"</span><span class="token punctuation">,</span>
  <span class="token property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"weather_current"</span><span class="token punctuation">,</span>
    <span class="token property">"arguments"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"location"</span><span class="token operator">:</span> <span class="token string">"San Francisco"</span><span class="token punctuation">,</span> <span class="token property">"units"</span><span class="token operator">:</span> <span class="token string">"imperial"</span> <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre></div>
<h3 id="step-4-server-initiated-notification">Step 4. Server-initiated notification</h3>
<div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token comment">// Server → Client (tool list changed — no response needed)</span>
<span class="token punctuation">{</span> <span class="token property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span> <span class="token property">"method"</span><span class="token operator">:</span> <span class="token string">"notifications/tools/list_changed"</span> <span class="token punctuation">}</span></code></pre></div>
<hr>
<h2 id="8-hands-on-with-the-python-sdk">8. Hands-On with the Python SDK</h2>
<p>The rest uses the official Python SDK (<code class="language-text">mcp</code>) throughout.</p>
<h3 id="1-install">1. Install</h3>
<p><a href="https://docs.astral.sh/uv/">uv</a> is recommended:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">uv init mcp-demo
<span class="token builtin class-name">cd</span> mcp-demo
uv <span class="token function">add</span> <span class="token string">"mcp[cli]"</span></code></pre></div>
<p>Or with pip:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">pip <span class="token function">install</span> <span class="token string">"mcp[cli]"</span></code></pre></div>
<h3 id="2-a-minimal-server-tools--resources--prompts-all-together">2. A minimal Server: Tools + Resources + Prompts all together</h3>
<p><code class="language-text">server.py</code>:</p>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">from</span> mcp<span class="token punctuation">.</span>server<span class="token punctuation">.</span>fastmcp <span class="token keyword">import</span> FastMCP

mcp <span class="token operator">=</span> FastMCP<span class="token punctuation">(</span><span class="token string">"Demo"</span><span class="token punctuation">)</span>

<span class="token comment"># ---- Tool: invoked by the model; has side effects ----</span>
<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>tool</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">add</span><span class="token punctuation">(</span>a<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">,</span> b<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">int</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Add two numbers"""</span>
    <span class="token keyword">return</span> a <span class="token operator">+</span> b

<span class="token comment"># ---- Resource: read on demand by the application ----</span>
<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>resource</span><span class="token punctuation">(</span><span class="token string">"greeting://{name}"</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">get_greeting</span><span class="token punctuation">(</span>name<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Generate a personalized greeting based on name"""</span>
    <span class="token keyword">return</span> <span class="token string-interpolation"><span class="token string">f"Hello, </span><span class="token interpolation"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token string">!"</span></span>

<span class="token comment"># ---- Prompt: explicitly user-triggered ----</span>
<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>prompt</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">greet_user</span><span class="token punctuation">(</span>name<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> style<span class="token punctuation">:</span> <span class="token builtin">str</span> <span class="token operator">=</span> <span class="token string">"friendly"</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Generate a greeting prompt template in different styles"""</span>
    styles <span class="token operator">=</span> <span class="token punctuation">{</span>
        <span class="token string">"friendly"</span><span class="token punctuation">:</span> <span class="token string">"Please write a warm, friendly greeting"</span><span class="token punctuation">,</span>
        <span class="token string">"formal"</span><span class="token punctuation">:</span>   <span class="token string">"Please write a formal greeting"</span><span class="token punctuation">,</span>
        <span class="token string">"casual"</span><span class="token punctuation">:</span>   <span class="token string">"Please write a casual, easygoing greeting"</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> <span class="token string-interpolation"><span class="token string">f"</span><span class="token interpolation"><span class="token punctuation">{</span>styles<span class="token punctuation">[</span>style<span class="token punctuation">]</span><span class="token punctuation">}</span></span><span class="token string">, addressed to a person named </span><span class="token interpolation"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token string">."</span></span>

<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span>
    mcp<span class="token punctuation">.</span>run<span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment"># stdio transport by default</span></code></pre></div>
<p>Three ways to launch:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token comment"># Debug: MCP Inspector provides a web UI</span>
uv run mcp dev server.py

<span class="token comment"># Install into Claude Desktop</span>
uv run mcp <span class="token function">install</span> server.py

<span class="token comment"># Switch to HTTP transport (recommended for production)</span>
<span class="token comment"># In code, change to mcp.run(transport="streamable-http")</span></code></pre></div>
<h3 id="3-structured-output-auto-generate-schemas-with-pydantic">3. Structured output: auto-generate schemas with Pydantic</h3>
<p>FastMCP auto-derives JSON Schema from type annotations, making return data easier for the model to understand:</p>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">from</span> pydantic <span class="token keyword">import</span> BaseModel<span class="token punctuation">,</span> Field
<span class="token keyword">from</span> mcp<span class="token punctuation">.</span>server<span class="token punctuation">.</span>fastmcp <span class="token keyword">import</span> FastMCP

mcp <span class="token operator">=</span> FastMCP<span class="token punctuation">(</span><span class="token string">"Weather"</span><span class="token punctuation">)</span>

<span class="token keyword">class</span> <span class="token class-name">WeatherData</span><span class="token punctuation">(</span>BaseModel<span class="token punctuation">)</span><span class="token punctuation">:</span>
    temperature<span class="token punctuation">:</span> <span class="token builtin">float</span> <span class="token operator">=</span> Field<span class="token punctuation">(</span>description<span class="token operator">=</span><span class="token string">"degrees Celsius"</span><span class="token punctuation">)</span>
    humidity<span class="token punctuation">:</span> <span class="token builtin">float</span> <span class="token operator">=</span> Field<span class="token punctuation">(</span>description<span class="token operator">=</span><span class="token string">"percent"</span><span class="token punctuation">)</span>
    condition<span class="token punctuation">:</span> <span class="token builtin">str</span> <span class="token operator">=</span> Field<span class="token punctuation">(</span>description<span class="token operator">=</span><span class="token string">"weather condition, e.g. sunny / cloudy"</span><span class="token punctuation">)</span>

<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>tool</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">get_weather</span><span class="token punctuation">(</span>city<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> WeatherData<span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Get weather for a city (structured return)"""</span>
    <span class="token keyword">return</span> WeatherData<span class="token punctuation">(</span>temperature<span class="token operator">=</span><span class="token number">22.5</span><span class="token punctuation">,</span> humidity<span class="token operator">=</span><span class="token number">45.0</span><span class="token punctuation">,</span> condition<span class="token operator">=</span><span class="token string">"sunny"</span><span class="token punctuation">)</span></code></pre></div>
<h3 id="4-context-injection-logging-progress-sampling-elicitation">4. Context injection: logging, progress, Sampling, Elicitation</h3>
<p>Add a <code class="language-text">Context</code> parameter to a tool and FastMCP will auto-inject it. With it you can do logging, progress reporting, reverse LLM calls, and prompting the user for input:</p>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">from</span> mcp<span class="token punctuation">.</span>server<span class="token punctuation">.</span>fastmcp <span class="token keyword">import</span> Context<span class="token punctuation">,</span> FastMCP
<span class="token keyword">from</span> mcp<span class="token punctuation">.</span>types <span class="token keyword">import</span> SamplingMessage<span class="token punctuation">,</span> TextContent

mcp <span class="token operator">=</span> FastMCP<span class="token punctuation">(</span><span class="token string">"Advanced"</span><span class="token punctuation">)</span>

<span class="token comment"># Progress reporting + logging</span>
<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>tool</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">long_task</span><span class="token punctuation">(</span>name<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> ctx<span class="token punctuation">:</span> Context<span class="token punctuation">,</span> steps<span class="token punctuation">:</span> <span class="token builtin">int</span> <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
    <span class="token keyword">await</span> ctx<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Starting task: </span><span class="token interpolation"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
    <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>steps<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">await</span> ctx<span class="token punctuation">.</span>report_progress<span class="token punctuation">(</span>
            progress<span class="token operator">=</span><span class="token punctuation">(</span>i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">/</span> steps<span class="token punctuation">,</span>
            total<span class="token operator">=</span><span class="token number">1.0</span><span class="token punctuation">,</span>
            message<span class="token operator">=</span><span class="token string-interpolation"><span class="token string">f"step </span><span class="token interpolation"><span class="token punctuation">{</span>i<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">}</span></span><span class="token string">/</span><span class="token interpolation"><span class="token punctuation">{</span>steps<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token string-interpolation"><span class="token string">f"Task </span><span class="token interpolation"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token string"> done"</span></span>

<span class="token comment"># Sampling: ask the Client's LLM in reverse</span>
<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>tool</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">summarize</span><span class="token punctuation">(</span>topic<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> ctx<span class="token punctuation">:</span> Context<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Have the Host's LLM summarize for me"""</span>
    result <span class="token operator">=</span> <span class="token keyword">await</span> ctx<span class="token punctuation">.</span>session<span class="token punctuation">.</span>create_message<span class="token punctuation">(</span>
        messages<span class="token operator">=</span><span class="token punctuation">[</span>
            SamplingMessage<span class="token punctuation">(</span>
                role<span class="token operator">=</span><span class="token string">"user"</span><span class="token punctuation">,</span>
                content<span class="token operator">=</span>TextContent<span class="token punctuation">(</span><span class="token builtin">type</span><span class="token operator">=</span><span class="token string">"text"</span><span class="token punctuation">,</span> text<span class="token operator">=</span><span class="token string-interpolation"><span class="token string">f"Summarize </span><span class="token interpolation"><span class="token punctuation">{</span>topic<span class="token punctuation">}</span></span><span class="token string"> in one sentence"</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token punctuation">)</span>
        <span class="token punctuation">]</span><span class="token punctuation">,</span>
        max_tokens<span class="token operator">=</span><span class="token number">100</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span>
    <span class="token keyword">return</span> result<span class="token punctuation">.</span>content<span class="token punctuation">.</span>text <span class="token keyword">if</span> result<span class="token punctuation">.</span>content<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">==</span> <span class="token string">"text"</span> <span class="token keyword">else</span> <span class="token builtin">str</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span>content<span class="token punctuation">)</span></code></pre></div>
<h3 id="5-lifecycle-management-set-up-connections-on-startup-clean-up-on-shutdown">5. Lifecycle management: set up connections on startup, clean up on shutdown</h3>
<p>When you need to initialize long-lived resources like database connection pools at startup, use <code class="language-text">lifespan</code>:</p>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">from</span> dataclasses <span class="token keyword">import</span> dataclass
<span class="token keyword">from</span> contextlib <span class="token keyword">import</span> asynccontextmanager
<span class="token keyword">from</span> mcp<span class="token punctuation">.</span>server<span class="token punctuation">.</span>fastmcp <span class="token keyword">import</span> Context<span class="token punctuation">,</span> FastMCP

<span class="token decorator annotation punctuation">@dataclass</span>
<span class="token keyword">class</span> <span class="token class-name">AppContext</span><span class="token punctuation">:</span>
    db<span class="token punctuation">:</span> <span class="token string">"Database"</span>

<span class="token decorator annotation punctuation">@asynccontextmanager</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">app_lifespan</span><span class="token punctuation">(</span>server<span class="token punctuation">:</span> FastMCP<span class="token punctuation">)</span><span class="token punctuation">:</span>
    db <span class="token operator">=</span> <span class="token keyword">await</span> Database<span class="token punctuation">.</span>connect<span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">try</span><span class="token punctuation">:</span>
        <span class="token keyword">yield</span> AppContext<span class="token punctuation">(</span>db<span class="token operator">=</span>db<span class="token punctuation">)</span>
    <span class="token keyword">finally</span><span class="token punctuation">:</span>
        <span class="token keyword">await</span> db<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span>

mcp <span class="token operator">=</span> FastMCP<span class="token punctuation">(</span><span class="token string">"MyApp"</span><span class="token punctuation">,</span> lifespan<span class="token operator">=</span>app_lifespan<span class="token punctuation">)</span>

<span class="token decorator annotation punctuation">@mcp<span class="token punctuation">.</span>tool</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">query</span><span class="token punctuation">(</span>sql<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> ctx<span class="token punctuation">:</span> Context<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
    app_ctx<span class="token punctuation">:</span> AppContext <span class="token operator">=</span> ctx<span class="token punctuation">.</span>request_context<span class="token punctuation">.</span>lifespan_context
    <span class="token keyword">return</span> app_ctx<span class="token punctuation">.</span>db<span class="token punctuation">.</span>execute<span class="token punctuation">(</span>sql<span class="token punctuation">)</span></code></pre></div>
<h3 id="6-client-stdio-connection-to-a-local-server">6. Client: stdio connection to a local Server</h3>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">import</span> asyncio
<span class="token keyword">from</span> mcp <span class="token keyword">import</span> ClientSession<span class="token punctuation">,</span> StdioServerParameters
<span class="token keyword">from</span> mcp<span class="token punctuation">.</span>client<span class="token punctuation">.</span>stdio <span class="token keyword">import</span> stdio_client

<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    params <span class="token operator">=</span> StdioServerParameters<span class="token punctuation">(</span>
        command<span class="token operator">=</span><span class="token string">"uv"</span><span class="token punctuation">,</span>
        args<span class="token operator">=</span><span class="token punctuation">[</span><span class="token string">"run"</span><span class="token punctuation">,</span> <span class="token string">"server.py"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span>
    <span class="token keyword">async</span> <span class="token keyword">with</span> stdio_client<span class="token punctuation">(</span>params<span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token punctuation">(</span>read<span class="token punctuation">,</span> write<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">async</span> <span class="token keyword">with</span> ClientSession<span class="token punctuation">(</span>read<span class="token punctuation">,</span> write<span class="token punctuation">)</span> <span class="token keyword">as</span> session<span class="token punctuation">:</span>
            <span class="token keyword">await</span> session<span class="token punctuation">.</span>initialize<span class="token punctuation">(</span><span class="token punctuation">)</span>

            tools <span class="token operator">=</span> <span class="token keyword">await</span> session<span class="token punctuation">.</span>list_tools<span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"Tools:"</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>t<span class="token punctuation">.</span>name <span class="token keyword">for</span> t <span class="token keyword">in</span> tools<span class="token punctuation">.</span>tools<span class="token punctuation">]</span><span class="token punctuation">)</span>

            result <span class="token operator">=</span> <span class="token keyword">await</span> session<span class="token punctuation">.</span>call_tool<span class="token punctuation">(</span><span class="token string">"add"</span><span class="token punctuation">,</span> arguments<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">"a"</span><span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"Result:"</span><span class="token punctuation">,</span> result<span class="token punctuation">.</span>content<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>text<span class="token punctuation">)</span>

asyncio<span class="token punctuation">.</span>run<span class="token punctuation">(</span>main<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre></div>
<h3 id="7-client-streamable-http-connection-to-a-remote-server">7. Client: Streamable HTTP connection to a remote Server</h3>
<div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">import</span> asyncio
<span class="token keyword">from</span> mcp <span class="token keyword">import</span> ClientSession
<span class="token keyword">from</span> mcp<span class="token punctuation">.</span>client<span class="token punctuation">.</span>streamable_http <span class="token keyword">import</span> streamablehttp_client

<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">async</span> <span class="token keyword">with</span> streamablehttp_client<span class="token punctuation">(</span><span class="token string">"http://localhost:8000/mcp"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token punctuation">(</span>read<span class="token punctuation">,</span> write<span class="token punctuation">,</span> _<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">async</span> <span class="token keyword">with</span> ClientSession<span class="token punctuation">(</span>read<span class="token punctuation">,</span> write<span class="token punctuation">)</span> <span class="token keyword">as</span> session<span class="token punctuation">:</span>
            <span class="token keyword">await</span> session<span class="token punctuation">.</span>initialize<span class="token punctuation">(</span><span class="token punctuation">)</span>
            tools <span class="token operator">=</span> <span class="token keyword">await</span> session<span class="token punctuation">.</span>list_tools<span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"Tools:"</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>t<span class="token punctuation">.</span>name <span class="token keyword">for</span> t <span class="token keyword">in</span> tools<span class="token punctuation">.</span>tools<span class="token punctuation">]</span><span class="token punctuation">)</span>

asyncio<span class="token punctuation">.</span>run<span class="token punctuation">(</span>main<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre></div>
<hr>
<h2 id="9-concrete-mapping-in-claude-code">9. Concrete Mapping in Claude Code</h2>
<p>Theory done — let's see what the three primitives actually look like inside a real Host. Using Claude Code as the example:</p>
<h3 id="tools--mcp__server__tool-tools">Tools → <code class="language-text">mcp__&lt;server>__&lt;tool></code> tools</h3>
<p>Each Server's tools are renamed and injected into the model's tool list. For example, with the <code class="language-text">github</code> MCP server installed, the model sees a tool called <code class="language-text">mcp__github__create_issue</code>.</p>
<p>A noteworthy mechanism is <strong>Tool Search</strong> (on by default): tool definitions are not stuffed into the context window all at once — the model first sees a <code class="language-text">ToolSearch</code> tool and looks up tools on demand. So even with 20 Servers installed, the context window doesn't blow up. You can use <code class="language-text">alwaysLoad: true</code> to force frequently used Servers to stay resident.</p>
<h3 id="resources---references--bridged-tools-both-at-once">Resources → <code class="language-text">@</code> references + bridged Tools, both at once</h3>
<p>Claude Code's support for Resources is a textbook illustration of the previous section — <strong>application-driven and model-driven exist side by side</strong>:</p>
<p><strong>Path A: user-initiated <code class="language-text">@</code> reference</strong></p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">Can you analyze @github:issue://123 and suggest a fix?
Compare @postgres:schema://users with @docs:file://database/user-model</code></pre></div>
<p>Typing <code class="language-text">@</code> triggers autocomplete, listing available resources from all connected Servers, with fuzzy search. The referenced resource is pulled into the context as an attachment.</p>
<p><strong>Path B: model-initiated invocation (bridged as Tool)</strong></p>
<p>From the official docs:</p>
<blockquote>
<p>"Claude Code automatically provides tools to list and read MCP resources when servers support them"</p>
</blockquote>
<p>Claude Code automatically wraps <code class="language-text">resources/list</code> and <code class="language-text">resources/read</code> as built-in tools exposed to the model — so the model can decide on its own whether to look up resources.</p>
<h3 id="prompts--mcp__server__prompt-slash-commands">Prompts → <code class="language-text">/mcp__&lt;server>__&lt;prompt></code> slash commands</h3>
<p>Each Server-defined prompt maps to a slash command:</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">/mcp__github__list_prs
/mcp__github__pr_review 456
/mcp__jira__create_issue "Bug in login flow" high</code></pre></div>
<ul>
<li>Arguments are parsed according to the prompt's schema, space-separated;</li>
<li>Spaces in server and prompt names are normalized to underscores;</li>
<li>The messages array returned by a prompt is spliced directly into the conversation.</li>
</ul>
<h3 id="support-status-for-client-primitives">Support status for client primitives</h3>
<ul>
<li><strong>Elicitation</strong>: pops an interactive dialog (form mode or URL mode); hooks can auto-respond.</li>
<li><strong>Roots</strong>: at Server startup, the <code class="language-text">CLAUDE_PROJECT_DIR</code> environment variable is set; the Server can also call <code class="language-text">roots/list</code> to query the current working directory.</li>
</ul>
<h3 id="takeaways-for-server-authors">Takeaways for Server authors</h3>
<p>Different clients vary widely in their support for MCP primitives. If you want your Server to maximize compatibility:</p>
<ul>
<li><strong>Tools support is mandatory</strong> — this is currently the greatest common denominator across all MCP clients.</li>
<li><strong>Expose important capabilities twice</strong>: write a Tool as the fallback, and also a Resource Template so Hosts that support it can present it more elegantly. For a lookup, for example, provide both a <code class="language-text">query_user(id)</code> Tool and a <code class="language-text">user://{id}</code> Resource Template.</li>
<li><strong>Prompts are a nice-to-have</strong>: for Hosts that support Prompts, they noticeably improve the UX for common workflows.</li>
</ul>
<hr>
<h2 id="10-mcp-vs-function-calling-vs-custom-plugin-protocols">10. MCP vs Function Calling vs Custom Plugin Protocols</h2>
<p>This is one of the most common questions in internal tech talks.</p>
<table>
<thead>
<tr>
<th>Dimension</th>
<th>Function Calling</th>
<th>Custom plugin protocol</th>
<th><strong>MCP</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Standardization</td>
<td>Each LLM vendor defines its own</td>
<td>Single company, internal</td>
<td>Cross-vendor open standard</td>
</tr>
<tr>
<td>Ecosystem reuse</td>
<td>One schema per model</td>
<td>Effectively zero</td>
<td>Implement once, reused across Hosts</td>
</tr>
<tr>
<td>Context resources</td>
<td>Usually just "function calls"</td>
<td>Custom</td>
<td>Three primitives: Tools / Resources / Prompts</td>
</tr>
<tr>
<td>Reverse capabilities</td>
<td>Not supported</td>
<td>Generally not supported</td>
<td>Sampling / Elicitation / Roots</td>
</tr>
<tr>
<td>Transport &#x26; auth</td>
<td>Embedded API</td>
<td>Custom</td>
<td>stdio / Streamable HTTP + OAuth</td>
</tr>
<tr>
<td>Best for</td>
<td>Narrow scope within a single app</td>
<td>Heavily customized private use</td>
<td>Tools/data reused across multiple endpoints; standardization required</td>
</tr>
</tbody>
</table>
<p>A rough decision rule:</p>
<ul>
<li><strong>Capability serves a single AI app and is essentially never reused</strong> → Function Calling is enough.</li>
<li><strong>Capability will be consumed by multiple AI apps</strong> (multiple internal products + third-party tools like Claude Desktop / Cursor) → <strong>prefer MCP</strong>.</li>
<li><strong>You need to expose resources and workflow templates to users/models, not just functions</strong> → <strong>MCP's Resources / Prompts are what Function Calling doesn't have</strong>.</li>
</ul>
<hr>
<h2 id="11-when-not-to-use-mcp">11. When <strong>Not</strong> to Use MCP</h2>
<p>Every recommendation has a counter-example. In these situations, forcing MCP only adds complexity:</p>
<ol>
<li><strong>Pure prompt engineering</strong>: no interaction with external systems, purely prompt tuning — you don't need MCP.</li>
<li><strong>Latency-critical, same-process calls</strong>: even with stdio, MCP incurs JSON-RPC serialization overhead; for latency-sensitive scenarios a direct function call is cheaper.</li>
<li><strong>Capability tightly coupled to the application and never reused</strong>: rolling your own function calling is lighter.</li>
<li><strong>General-purpose cross-language, cross-process RPC</strong>: that's not MCP's goal — use gRPC / OpenAPI.</li>
</ol>
<hr>
<h2 id="12-a-few-tips-for-production">12. A Few Tips for Production</h2>
<p>When actually deploying MCP, here are a few lessons from stepping on the rakes:</p>
<h3 id="1-namespace-your-tool-names">1. Namespace your tool names</h3>
<p>Don't call it <code class="language-text">search</code>; call it <code class="language-text">github_search_issues</code> or <code class="language-text">jira_search_tickets</code>. Only then can the model route accurately when several Servers are connected at once.</p>
<h3 id="2-description-is-written-for-the-model-not-for-humans">2. <code class="language-text">description</code> is written for the model, not for humans</h3>
<p>Tool descriptions go directly into the model's context, and wording materially affects invocation accuracy. Be explicit: <strong>what it does, when to use it, when not to, and the input constraints</strong>.</p>
<h3 id="3-dangerous-operations-must-use-elicitation">3. Dangerous operations must use Elicitation</h3>
<p>Writing to a DB, sending messages, charging a card — operations with real side effects should <strong>proactively call <code class="language-text">elicitation/create</code> to get user confirmation</strong>, rather than trusting that "the model won't go off the rails."</p>
<h3 id="4-pair-streamable-http-with-oauth">4. Pair Streamable HTTP with OAuth</h3>
<p>For remote Servers, stop hardcoding Bearer Tokens in config. OAuth is the auth method recommended by the spec; pair it with <code class="language-text">roots</code> to scope the Server's working area.</p>
<h3 id="5-make-good-use-of-listchanged-notifications">5. Make good use of <code class="language-text">listChanged</code> notifications</h3>
<p>When the tool set changes dynamically with business needs (e.g. a new data source is hooked up), emit <code class="language-text">notifications/tools/list_changed</code> — the Client refreshes immediately, no restart required.</p>
<h3 id="6-use-the-mcp-inspector-during-development">6. Use the MCP Inspector during development</h3>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">uv run mcp dev server.py</code></pre></div>
<p>It spins up a web UI to browse tools/resources/prompts, invoke them manually, and inspect JSON-RPC frames — ten times faster than print-debugging.</p>
<hr>
<h2 id="13-references">13. References</h2>
<ul>
<li>Official documentation homepage: <a href="https://modelcontextprotocol.io/docs/">https://modelcontextprotocol.io/docs/</a></li>
<li>Architecture overview: <a href="https://modelcontextprotocol.io/docs/learn/architecture">https://modelcontextprotocol.io/docs/learn/architecture</a></li>
<li>Server concepts (Tools / Resources / Prompts): <a href="https://modelcontextprotocol.io/docs/learn/server-concepts">https://modelcontextprotocol.io/docs/learn/server-concepts</a></li>
<li>Client concepts (Sampling / Elicitation / Roots): <a href="https://modelcontextprotocol.io/docs/learn/client-concepts">https://modelcontextprotocol.io/docs/learn/client-concepts</a></li>
<li>Latest specification: <a href="https://modelcontextprotocol.io/specification/latest">https://modelcontextprotocol.io/specification/latest</a></li>
<li>Python SDK: <a href="https://github.com/modelcontextprotocol/python-sdk">https://github.com/modelcontextprotocol/python-sdk</a></li>
<li>Official reference Server repository: <a href="https://github.com/modelcontextprotocol/servers">https://github.com/modelcontextprotocol/servers</a></li>
<li>MCP Inspector (a powerful debugging tool): <a href="https://github.com/modelcontextprotocol/inspector">https://github.com/modelcontextprotocol/inspector</a></li>
</ul>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>MCP debuted in late 2024 and has since gained broad support from major AI applications, including Claude, ChatGPT, VS Code, and Cursor. It is quickly becoming the de facto standard for connecting AI applications to tools and data sources.</p>
<p>Its design philosophy is simple:</p>
<blockquote>
<p><strong>Keep the protocol separate from the model.</strong>
MCP does not dictate how you use an LLM or manage its context. It defines one thing: how AI applications communicate with external systems. And it does so through a clean, general-purpose interface.</p>
</blockquote>
<p>For engineers, the payoff of understanding MCP is substantial: build a Server once and any Host in the ecosystem can reuse it; build a Client once and it can connect to any compatible community Server. As the AI application landscape becomes increasingly fragmented, <strong>MCP is one of the few protocol layers that preserves engineering value across vendors and products</strong>.</p>]]></content>
  </entry>
  <entry>
    <title>Hello World</title>
    <id>https://shipengtao.com/en/hello-world/</id>
    <link rel="alternate" type="text/html" href="https://shipengtao.com/en/hello-world/"/>
    <published>2024-05-23T00:00:00+08:00</published>
    <updated>2024-05-23T00:00:00+08:00</updated>
    <summary type="text">Hi, there!</summary>
    <content type="html"><![CDATA[<p>Hi, there!</p>]]></content>
  </entry>
</feed>