<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Edward&#x27;s BlogPost</title>
    <subtitle>Notes on software, APIs, and AI systems</subtitle>
    <link rel="self" type="application/atom+xml" href="https://edwardhong.net/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://edwardhong.net"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-07-10T22:07:05+08:00</updated>
    <id>https://edwardhong.net/atom.xml</id>
    <entry xml:lang="en">
        <title>From Agentic Programming to Loop Engineering</title>
        <published>2026-07-10T22:07:05+08:00</published>
        <updated>2026-07-10T22:07:05+08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://edwardhong.net/blog/loop-is-goal-seeking-not-routine/"/>
        <id>https://edwardhong.net/blog/loop-is-goal-seeking-not-routine/</id>
        
        <content type="html" xml:base="https://edwardhong.net/blog/loop-is-goal-seeking-not-routine/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;edwardhong.net&#x2F;blog&#x2F;loop-is-goal-seeking-not-routine&#x2F;loop_vs_workflow.gif&quot; alt=&quot;loop engineering&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-shift-in-one-sentence&quot;&gt;The shift in one sentence&lt;&#x2F;h2&gt;
&lt;p&gt;Loop engineering is the move from &lt;em&gt;prompting&lt;&#x2F;em&gt; an AI agent by hand to &lt;em&gt;designing the system that prompts it for you&lt;&#x2F;em&gt;. The leverage stops living in the quality of a single prompt and moves into the design of the wrapper that generates prompts, checks the result, and decides whether to run again.&lt;&#x2F;p&gt;
&lt;p&gt;The term crystallized in the second week of June 2026, off a couple of widely-shared posts. The one people keep quoting is Boris Cherny, creator of Claude Code at Anthropic:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I don&#x27;t prompt Claude anymore. I have loops that are running. They&#x27;re the ones that are prompting Claude and figuring out what to do. My job is to write loops.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That single reframing — &lt;em&gt;my job is to write loops&lt;&#x2F;em&gt; — is the whole idea. You stop being the conversationalist inside the turn-by-turn exchange and become the person who builds the runtime that runs the exchange without you.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-common-misconception-loop-a-well-defined-harnessed-automated-flow&quot;&gt;A common misconception: &quot;loop = a well-defined, harnessed automated flow&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;A natural first read of the word is that a &quot;loop&quot; is just an automated flow — take agentic programming, make it repeatable, wrap it in a harness, done. That captures something real, but it undersells the actual claim in two ways.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;First, it misses the inversion of control.&lt;&#x2F;strong&gt; The point isn&#x27;t that the flow is automated; it&#x27;s that the &lt;em&gt;author of the prompts changes&lt;&#x2F;em&gt;. The agent isn&#x27;t waiting for your next message — the loop is. You supply a goal once, and the machinery iterates.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Second, &quot;harnessed&quot; is borrowed from the layer below.&lt;&#x2F;strong&gt; The community frames loop engineering as the fourth rung in a lineage:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;prompt engineering → context engineering → harness engineering → loop engineering&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Harness engineering is its own prior step: the harness is the tool surface, the sandbox, the scaffolding the agent acts &lt;em&gt;through&lt;&#x2F;em&gt;. &quot;Well-defined and harnessed&quot; is a precondition you inherit from that layer. Loop engineering sits &lt;em&gt;above&lt;&#x2F;em&gt; it and adds three things the harness doesn&#x27;t have: a &lt;strong&gt;trigger&lt;&#x2F;strong&gt;, a &lt;strong&gt;verifiable goal&lt;&#x2F;strong&gt;, and — the part that&#x27;s actually hard — the &lt;strong&gt;guardrails that decide when to stop&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;anatomy-of-an-agentic-loop&quot;&gt;Anatomy of an agentic loop&lt;&#x2F;h2&gt;
&lt;p&gt;Stripped down, a loop needs only two ingredients:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A trigger&lt;&#x2F;strong&gt; — something that starts it (a PR opening, a schedule, a human saying &quot;go&quot;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;A verifiable goal&lt;&#x2F;strong&gt; — a defined end state the agent works toward (all tests pass, CI green, a &lt;code&gt;&#x2F;goal&lt;&#x2F;code&gt; condition graded by a separate model).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Given those, the agent runs a cycle:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Observe&lt;&#x2F;strong&gt; the current state (read files, run a test, take a screenshot).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Evaluate&lt;&#x2F;strong&gt; the result against the goal.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Decide&lt;&#x2F;strong&gt; — continue, stop because it succeeded, or stop because it&#x27;s blocked or out of budget.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Act&lt;&#x2F;strong&gt; — take one bounded step, then observe again.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;It keeps going until the goal is met or a stopping condition fires. You give it a goal, not a prompt.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-real-work-is-on-the-stopping-side&quot;&gt;The real work is on the &lt;em&gt;stopping&lt;&#x2F;em&gt; side&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s the counterintuitive part: the repetition scaffolding is trivial — it&#x27;s a &lt;code&gt;while&lt;&#x2F;code&gt; loop, one line any programmer can write. The engineering is almost entirely in halting well. The canonical production failures are not edge cases:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Infinite loops&lt;&#x2F;strong&gt; — the loop never satisfies its exit condition.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Goal drift&lt;&#x2F;strong&gt; — the target quietly mutates across iterations until the agent is solving the wrong problem.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Token-cost explosions&lt;&#x2F;strong&gt; — an unbounded loop burns budget with nothing to show.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Mode collapse &#x2F; &quot;spinning&quot;&lt;&#x2F;strong&gt; — a loop that retries the same action after the same error isn&#x27;t learning; it&#x27;s spinning.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As one field guide put it: the production version of the job is that you write the loops, and most of your work is making sure they halt.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-archetype-ralph-productized&quot;&gt;The archetype: Ralph, productized&lt;&#x2F;h2&gt;
&lt;p&gt;Everyone traces the pattern back to the &lt;strong&gt;Ralph loop&lt;&#x2F;strong&gt;. Ralph sidesteps context rot by making every iteration a &lt;em&gt;fresh&lt;&#x2F;em&gt; agent with a clean context that reads the current repo state and task list from disk, does exactly one unit of work, commits, and exits. The intelligence doesn&#x27;t live in one heroic run — it lives in clear, granular specs and verifiable outcomes applied over and over against an external memory the model can&#x27;t pollute.&lt;&#x2F;p&gt;
&lt;p&gt;A tidy way to describe the current state of the art:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Loop engineering is Ralph, productized.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The &lt;code&gt;while&lt;&#x2F;code&gt; loop becomes a scheduled automation, the context reset becomes a worktree plus a sub-agent, and the &lt;code&gt;ALL TASKS DONE&lt;&#x2F;code&gt; grep becomes a &lt;code&gt;&#x2F;goal&lt;&#x2F;code&gt; condition graded by a &lt;em&gt;separate&lt;&#x2F;em&gt; model. Same shape, fewer sharp edges. That last detail — the grader being a different model from the generator — is the familiar &lt;strong&gt;critic-must-not-equal-generator&lt;&#x2F;strong&gt; principle, now baked into the loop&#x27;s exit condition.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-core-distinction-goal-seeking-not-routine&quot;&gt;The core distinction: goal-seeking, not routine&lt;&#x2F;h2&gt;
&lt;p&gt;So can we conclude the &quot;loop&quot; is not a routine but a process of goal-seeking? &lt;strong&gt;Yes — with one refinement that makes the conclusion stronger, not weaker.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A loop is &lt;em&gt;implemented as&lt;&#x2F;em&gt; a routine but is not &lt;em&gt;essentially&lt;&#x2F;em&gt; one. The word borrows from control flow — there&#x27;s a literal &lt;code&gt;while&lt;&#x2F;code&gt; underneath — but that repetition is the dumb, deterministic part. The substance is the goal-seeking: observe, evaluate against a verifiable goal, decide, halt or continue.&lt;&#x2F;p&gt;
&lt;p&gt;The cleanest way to state the difference:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A routine repeats the same &lt;strong&gt;actions&lt;&#x2F;strong&gt;. A loop repeats the same &lt;strong&gt;question&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The actions differ every iteration — different file, different fix, different test — but the question is constant: &lt;em&gt;&quot;Is the goal met, and if not, what&#x27;s the best next move?&quot;&lt;&#x2F;em&gt; A cron job or an RPA script is a genuine routine: fixed steps, no evaluation, no adaptation. If the environment shifts, it either breaks or blindly repeats. The agentic loop is defined precisely by &lt;em&gt;not&lt;&#x2F;em&gt; being that.&lt;&#x2F;p&gt;
&lt;p&gt;And here&#x27;s what vindicates the framing hardest: &lt;strong&gt;when a loop degenerates into a routine, that&#x27;s the bug.&lt;&#x2F;strong&gt; &quot;Spinning&quot; and mode collapse are exactly the case where the loop stops asking the question and just repeats an action. Collapsing into a fixed routine is the canonical &lt;em&gt;failure&lt;&#x2F;em&gt; of loop engineering — not its definition.&lt;&#x2F;p&gt;
&lt;p&gt;So the sharpest conclusion isn&#x27;t merely &quot;loop = goal-seeking, not routine.&quot; It&#x27;s that &lt;strong&gt;goal-seeking and routine are the two poles&lt;&#x2F;strong&gt;, and loop engineering is the discipline of keeping the process pinned to the goal-seeking pole — via the verifiable goal and the halt conditions — so it never decays into a spinning routine on one side or an unbounded wander on the other. The routine is what you get when the loop stops thinking; the goal is what keeps it a loop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-this-meets-a-real-orchestration-stack&quot;&gt;Where this meets a real orchestration stack&lt;&#x2F;h2&gt;
&lt;p&gt;Most of the current writing implicitly assumes a &lt;em&gt;single&lt;&#x2F;em&gt; cloud coding agent (Claude Code, Codex) driving the loop. But the pattern generalizes cleanly to formal orchestration and heterogeneous fleets:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Escalation ladders, hard retry ceilings, and human gates&lt;&#x2F;strong&gt; at requirements &#x2F; architecture &#x2F; pre-merge &lt;em&gt;are&lt;&#x2F;em&gt; the halt conditions and guardrails the blog discourse is now naming. A Temporal-style workflow is loop engineering with a rigorous orchestrator underneath — the opposite end of the rigor spectrum from a &lt;code&gt;while true&lt;&#x2F;code&gt; + &lt;code&gt;grep&lt;&#x2F;code&gt;, but the same shape.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Heterogeneous fleets&lt;&#x2F;strong&gt; sharpen the &quot;who prompts whom&quot; inversion: when the loop &lt;em&gt;driver&lt;&#x2F;em&gt; and the loop &lt;em&gt;workers&lt;&#x2F;em&gt; are different models on different silicon (e.g. a strong orchestrator model dispatching to local executor models), the halt-condition grading may want to live on a specific tier — and the critic-must-not-equal-generator rule can be enforced at the &lt;em&gt;hardware&lt;&#x2F;em&gt; level, not just the prompt level.&lt;&#x2F;li&gt;
&lt;li&gt;The academic framing (see the arXiv paper &lt;em&gt;Agentic Software Engineering: Foundational Pillars and a Research Roadmap&lt;&#x2F;em&gt;, 2509.06216) proposes &lt;strong&gt;Agentic Loop Engineering (ALE)&lt;&#x2F;strong&gt; as disciplined, DevOps-lineage orchestration — a declarative &lt;code&gt;LoopScript&lt;&#x2F;code&gt; producing a &quot;Merge-Readiness Pack.&quot; It&#x27;s a useful vocabulary for anyone already hand-rolling this in a workflow engine.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Loop engineering&lt;&#x2F;strong&gt; = designing the system that prompts, checks, remembers, and re-runs an agent — instead of typing each instruction yourself.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s the &lt;strong&gt;fourth layer&lt;&#x2F;strong&gt;: prompt → context → harness → loop. &quot;Harnessed and automated&quot; is a precondition it inherits, not what makes it a loop.&lt;&#x2F;li&gt;
&lt;li&gt;A loop = &lt;strong&gt;trigger + verifiable goal + iteration + halt conditions&lt;&#x2F;strong&gt;. The hard engineering is the halting.&lt;&#x2F;li&gt;
&lt;li&gt;The &quot;loop&quot; is &lt;strong&gt;goal-seeking, not routine&lt;&#x2F;strong&gt;. A routine repeats actions; a loop repeats a question. When it collapses back into a routine (&quot;spinning&quot;), that&#x27;s the failure mode — which is exactly why the goal-seeking reading is the right one.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Written from a working conversation, July 2026. Sources for the June 2026 framing include commentary from Boris Cherny (Claude Code), the Ralph loop pattern, and the arXiv &quot;Agentic Software Engineering&quot; roadmap (2509.06216).&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Plain RAG vs. Agentic RAG: It&#x27;s the Control Flow, Not the Plumbing</title>
        <published>2026-07-10T21:08:38+08:00</published>
        <updated>2026-07-10T21:08:38+08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://edwardhong.net/blog/plain-rag-vs-agentic-rag-it-s-the-control-flow-not-the-plumbing/"/>
        <id>https://edwardhong.net/blog/plain-rag-vs-agentic-rag-it-s-the-control-flow-not-the-plumbing/</id>
        
        <content type="html" xml:base="https://edwardhong.net/blog/plain-rag-vs-agentic-rag-it-s-the-control-flow-not-the-plumbing/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;edwardhong.net&#x2F;blog&#x2F;plain-rag-vs-agentic-rag-it-s-the-control-flow-not-the-plumbing&#x2F;rag_vs_agentic_rag.gif&quot; alt=&quot;rag_vs_agentic_rag&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Retrieval-Augmented Generation gets discussed as if &quot;RAG&quot; and &quot;agentic RAG&quot; were two different architectures. They mostly aren&#x27;t. They&#x27;re two different &lt;em&gt;control flows&lt;&#x2F;em&gt; over the same parts. Getting that distinction right changes how you build, and — as we&#x27;ll see — it means the same retrieval tool can be plain RAG one day and agentic RAG the next, with nothing changed but the loop around it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;edwardhong.net&#x2F;blog&#x2F;plain-rag-vs-agentic-rag-it-s-the-control-flow-not-the-plumbing&#x2F;rag_vs_agentic_rag.gif&quot; alt=&quot;Plain RAG runs straight through the pipeline once; Agentic RAG loops — deciding, grading, and re-retrieving before it answers.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;plain-rag-retrieval-wired-into-generation&quot;&gt;Plain RAG: retrieval wired into generation&lt;&#x2F;h2&gt;
&lt;p&gt;Plain RAG is a linear, one-shot pipeline:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;query → embed → vector search → top-k chunks → generate → answer&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The retrieval step is deterministic and happens exactly once, orchestrated by &lt;em&gt;your&lt;&#x2F;em&gt; code rather than by the model. The LLM never decides whether to retrieve, what to retrieve, or whether what came back was any good. It consumes whatever the pipeline hands it and composes an answer.&lt;&#x2F;p&gt;
&lt;p&gt;Two things are worth stating precisely, because the casual mental model (&quot;similarity fetches the relevant content, the model just stitches it together&quot;) oversells both halves.&lt;&#x2F;p&gt;
&lt;p&gt;First, vector search returns &lt;strong&gt;semantic proximity in embedding space&lt;&#x2F;strong&gt; — nearest neighbours by cosine or dot-product. That is &lt;em&gt;not&lt;&#x2F;em&gt; the same thing as relevance. Chunks can sit close to the query vector and still be useless, and the genuinely relevant chunk can fall just outside your top-k. Similarity is a lossy proxy for &quot;actually answers the question.&quot; What you retrieve is &lt;em&gt;plausibly related&lt;&#x2F;em&gt; content, not guaranteed-relevant content.&lt;&#x2F;p&gt;
&lt;p&gt;Second, the model is doing more than concatenation. It reads the retrieved chunks as grounding, resolves conflicts between them, decides what to ignore, and synthesises. But the intuition underneath is correct and important: in plain RAG the &lt;strong&gt;retrieval is dumb and the generation is where the intelligence sits&lt;&#x2F;strong&gt;. That asymmetry is the whole reason the agentic variant exists.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;agentic-rag-retrieval-governed-by-generation&quot;&gt;Agentic RAG: retrieval governed by generation&lt;&#x2F;h2&gt;
&lt;p&gt;Agentic RAG promotes retrieval from a fixed pipeline stage to a &lt;strong&gt;tool the model invokes inside a reasoning loop&lt;&#x2F;strong&gt;. Now the model exercises judgment over retrieval. It can:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;decide whether retrieval is needed at all,&lt;&#x2F;li&gt;
&lt;li&gt;decompose a complex question into sub-queries (multi-hop),&lt;&#x2F;li&gt;
&lt;li&gt;rewrite a query that returned weak results and try again,&lt;&#x2F;li&gt;
&lt;li&gt;route between multiple sources — a vector store, SQL, a graph index, the web,&lt;&#x2F;li&gt;
&lt;li&gt;and — the crucial one — &lt;strong&gt;grade what came back and re-retrieve&lt;&#x2F;strong&gt; before committing to an answer.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is the family that includes patterns like corrective RAG and self-RAG: the model checks its own grounding and corrects course. In the animation above, that&#x27;s the amber loop — the pulse reaches &lt;em&gt;Grade&lt;&#x2F;em&gt;, judges the evidence thin, and arcs back to &lt;em&gt;Retrieve&lt;&#x2F;em&gt; to rewrite and try again, twice, before it finally answers. The plain-RAG pulse, meanwhile, has long since finished and is just waiting.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-comparison&quot;&gt;The comparison&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Dimension&lt;&#x2F;th&gt;&lt;th&gt;Plain RAG&lt;&#x2F;th&gt;&lt;th&gt;Agentic RAG&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Control flow&lt;&#x2F;td&gt;&lt;td&gt;Static pipeline (fixed DAG)&lt;&#x2F;td&gt;&lt;td&gt;Dynamic, model-driven loop&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Who orchestrates&lt;&#x2F;td&gt;&lt;td&gt;Your code&lt;&#x2F;td&gt;&lt;td&gt;The model&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Query handling&lt;&#x2F;td&gt;&lt;td&gt;Single embedded query&lt;&#x2F;td&gt;&lt;td&gt;Decomposition, multi-hop, rewriting&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Sources&lt;&#x2F;td&gt;&lt;td&gt;Usually one vector store&lt;&#x2F;td&gt;&lt;td&gt;Many tools, routed between&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Self-correction&lt;&#x2F;td&gt;&lt;td&gt;None&lt;&#x2F;td&gt;&lt;td&gt;Grading + re-retrieval&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Cost &amp;amp; latency&lt;&#x2F;td&gt;&lt;td&gt;Low, predictable&lt;&#x2F;td&gt;&lt;td&gt;Higher, variable&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Best for&lt;&#x2F;td&gt;&lt;td&gt;Straightforward lookup&lt;&#x2F;td&gt;&lt;td&gt;Ambiguous or multi-hop queries, heterogeneous corpora&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;A one-line way to hold it: &lt;strong&gt;plain RAG is retrieval &lt;em&gt;wired into&lt;&#x2F;em&gt; generation; agentic RAG is retrieval &lt;em&gt;governed by&lt;&#x2F;em&gt; generation.&lt;&#x2F;strong&gt; The tradeoff is exactly what you&#x27;d expect — robustness on hard queries in exchange for determinism, latency, and token spend. For a lot of everyday lookup workloads, plain RAG is still the right call. The agentic layer earns its cost when queries genuinely need planning or the corpus is heterogeneous.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-rag-as-an-mcp-tool&quot;&gt;Building RAG as an MCP tool&lt;&#x2F;h2&gt;
&lt;p&gt;A natural instinct is to expose retrieval as an MCP tool — something like &lt;code&gt;search_knowledge_base(query, k, filters)&lt;&#x2F;code&gt; returning ranked chunks with metadata — and let an agent call it. This is arguably the cleanest way to express agentic RAG, because it &lt;em&gt;is&lt;&#x2F;em&gt; the &quot;retrieval as a tool the model governs&quot; pattern made concrete.&lt;&#x2F;p&gt;
&lt;p&gt;But here&#x27;s the subtlety that trips people up: &lt;strong&gt;the transport doesn&#x27;t determine the category — the control flow does.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If the agent calls the retrieval tool once, takes whatever comes back, and composes it into an answer, that&#x27;s plain RAG. It&#x27;s plain RAG that happens to be &lt;em&gt;plumbed through&lt;&#x2F;em&gt; MCP. Wrapping retrieval in a tool interface doesn&#x27;t make it agentic. The loop still collapses to a straight line — call → compose — the same shape as query → search → generate, just with an MCP hop in the middle.&lt;&#x2F;p&gt;
&lt;p&gt;Agentic-ness is a property of the &lt;strong&gt;trajectory&lt;&#x2F;strong&gt;, not the architecture. The very same MCP tool behaves either way depending on what the agent does with it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Always calls it once, regardless of the query, never re-examines → &lt;strong&gt;plain RAG&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Decides per-query whether to call it, rewrites and re-calls on weak results, routes between tools, grades before answering → &lt;strong&gt;agentic RAG&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So you can ship one &lt;code&gt;search_knowledge_base&lt;&#x2F;code&gt; tool and have it be &quot;normal RAG&quot; on Monday and &quot;agentic RAG&quot; on Tuesday, purely by changing the orchestration prompt and the loop around it. The tool is identical; the behaviour is what moves it across the line.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;design-notes-if-you-go-the-mcp-route&quot;&gt;Design notes if you go the MCP route&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The tool description is the contract.&lt;&#x2F;strong&gt; In MCP, the tool description is &lt;em&gt;runtime model input&lt;&#x2F;em&gt;, not just documentation — it&#x27;s what the agent reads to decide when to call the tool and how to phrase the query. A vague description means the agent retrieves at the wrong times, or collapses to a single blind call. This is the highest-leverage thing to get right. If your intent is agentic behaviour but the agent keeps doing one unconditional call, that&#x27;s usually a description-or-prompt problem, not an architecture problem: the model wasn&#x27;t given the signal (or the permission) to retrieve conditionally, rewrite, and re-check.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Return structure matters more than in plain RAG.&lt;&#x2F;strong&gt; Because the agent may re-retrieve, return scores, source IDs, and chunk provenance — not just concatenated text — so the model can grade relevance and decide whether to go again.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Granularity is a real fork.&lt;&#x2F;strong&gt; One generic &lt;code&gt;search&lt;&#x2F;code&gt; tool vs. several typed tools (&lt;code&gt;search_code&lt;&#x2F;code&gt;, &lt;code&gt;search_docs&lt;&#x2F;code&gt;, &lt;code&gt;search_graph&lt;&#x2F;code&gt;) is a genuine design decision. Multiple typed tools hand the agent a routing signal for free; a single generic tool pushes that routing logic into the query. If you have more than one retrieval &lt;em&gt;primitive&lt;&#x2F;em&gt; — say embedding similarity &lt;strong&gt;and&lt;&#x2F;strong&gt; a graph-expansion method like Personalized PageRank — exposing them as separate tools lets the agent do a &quot;seed then expand&quot; pattern: similarity to find entry points, graph expansion to grow the neighbourhood. Neither primitive does that alone, and the agent combining them is exactly the kind of judgment that makes RAG agentic.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;takeaway&quot;&gt;Takeaway&lt;&#x2F;h2&gt;
&lt;p&gt;Don&#x27;t reach for &quot;agentic RAG&quot; because it sounds more capable. Reach for it when the &lt;em&gt;trajectory&lt;&#x2F;em&gt; your queries need — conditional retrieval, rewriting, grading, multi-source routing — actually justifies the extra round-trips. If a single retrieval pass answers the question, a single retrieval pass is the correct design, MCP tool or not. The intelligence you&#x27;re paying for in agentic RAG isn&#x27;t the retrieval; it&#x27;s the model &lt;em&gt;deciding how to retrieve&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>MCP Is Not a New Paradigm: What It Actually Standardizes, and How to Design For It</title>
        <published>2026-07-10T13:30:00+08:00</published>
        <updated>2026-07-10T13:30:00+08:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://edwardhong.net/blog/mcp-is-not-a-new-paradigm-what-it-actually-standardizes-and-how-to-design-for-it/"/>
        <id>https://edwardhong.net/blog/mcp-is-not-a-new-paradigm-what-it-actually-standardizes-and-how-to-design-for-it/</id>
        
        <content type="html" xml:base="https://edwardhong.net/blog/mcp-is-not-a-new-paradigm-what-it-actually-standardizes-and-how-to-design-for-it/">&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;&#x2F;h2&gt;
&lt;p&gt;The Model Context Protocol (MCP) is best understood not as a new kind of API but as a &lt;strong&gt;standardized profile of existing API patterns&lt;&#x2F;strong&gt; — JSON-RPC 2.0, plus mandatory runtime introspection, plus Backend-for-Frontend–style aggregation — specialized for one unusual consumer: a stochastic, forgetful model that self-discovers your capabilities and re-reads your interface on every turn.&lt;&#x2F;p&gt;
&lt;p&gt;Almost all the mechanics transfer from ordinary API design. What is genuinely MCP-specific is narrow: enforced uniform discovery (so a generic client works against a server it has never seen), a few LLM-native semantics (sampling, elicitation, the tools&#x2F;resources&#x2F;prompts trichotomy), and the fact that your tool descriptions stop being documentation and become runtime input that steers model behavior.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-what-mcp-is-mechanically&quot;&gt;1. What MCP is, mechanically&lt;&#x2F;h2&gt;
&lt;p&gt;Strip the branding and MCP is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JSON-RPC 2.0&lt;&#x2F;strong&gt; as the wire format.&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;strong&gt;fixed, standardized method vocabulary&lt;&#x2F;strong&gt;: &lt;code&gt;initialize&lt;&#x2F;code&gt;, &lt;code&gt;tools&#x2F;list&lt;&#x2F;code&gt;, &lt;code&gt;tools&#x2F;call&lt;&#x2F;code&gt;, &lt;code&gt;resources&#x2F;read&lt;&#x2F;code&gt;, &lt;code&gt;prompts&#x2F;list&lt;&#x2F;code&gt;, and so on.&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;strong&gt;mandatory discovery handshake&lt;&#x2F;strong&gt;: a client connects knowing nothing, calls &lt;code&gt;tools&#x2F;list&lt;&#x2F;code&gt;, and receives the full menu with machine-readable JSON schemas.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Your &lt;code&gt;add(a, b)&lt;&#x2F;code&gt; function is not reachable at some URL you chose. It is reachable via &lt;code&gt;tools&#x2F;call&lt;&#x2F;code&gt; with &lt;code&gt;{&quot;name&quot;: &quot;add&quot;, ...}&lt;&#x2F;code&gt;. The consumer is an LLM&#x2F;agent that does not know your API in advance: it connects, enumerates capabilities, reads the schemas, and decides at runtime what to invoke.&lt;&#x2F;p&gt;
&lt;p&gt;MCP exposes three primitives:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tools&lt;&#x2F;strong&gt; — actions the model can invoke.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Resources&lt;&#x2F;strong&gt; — read-only context the model or client can pull.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Prompts&lt;&#x2F;strong&gt; — reusable templates&#x2F;instructions.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Transport can be stdio, streamable HTTP, or SSE. HTTP is just one option; MCP is not &quot;an HTTP API.&quot;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-mcp-vs-rest-who-is-the-caller&quot;&gt;2. MCP vs REST: who is the caller?&lt;&#x2F;h2&gt;
&lt;p&gt;Both speak over a transport. They answer different questions.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;REST &#x2F; OpenAPI&lt;&#x2F;th&gt;&lt;th&gt;MCP&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Standardizes&lt;&#x2F;td&gt;&lt;td&gt;Exposing &lt;strong&gt;resources&lt;&#x2F;strong&gt; to programs&lt;&#x2F;td&gt;&lt;td&gt;Exposing &lt;strong&gt;capabilities&lt;&#x2F;strong&gt; to models&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Consumer&lt;&#x2F;td&gt;&lt;td&gt;A developer who reads docs, holds state, orchestrates deliberately&lt;&#x2F;td&gt;&lt;td&gt;A model that sees a flat menu, picks under token pressure, has no memory of your workflow&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Contract delivery&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;Out-of-band&lt;&#x2F;strong&gt; — you must already hold the endpoints&#x2F;shapes before you can call&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;In-band&lt;&#x2F;strong&gt; — the contract travels with the connection; discovered through the communication itself&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Discovery&lt;&#x2F;td&gt;&lt;td&gt;Optional, heterogeneous (some ship OpenAPI, some don&#x27;t)&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;Mandatory and uniform&lt;&#x2F;strong&gt; — the reason a generic client can connect to any server&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Granularity unit&lt;&#x2F;td&gt;&lt;td&gt;One resource operation (&lt;code&gt;POST &#x2F;carts&#x2F;{id}&#x2F;items&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;One unit of user intent (&lt;code&gt;place_order&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The relationship is clearest in one fact: &lt;strong&gt;FastMCP can generate an MCP server from a FastAPI app or an OpenAPI spec.&lt;&#x2F;strong&gt; MCP sits a layer up. FastAPI is how you expose an API to programs you control; MCP is how you expose capabilities to an agent you don&#x27;t, in a form it can enumerate and reason about.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;&#x2F;strong&gt; if a service is called by code you write, plain REST&#x2F;gRPC is simpler and faster. Reach for MCP when the caller is a model that needs to &lt;em&gt;discover and choose&lt;&#x2F;em&gt; tools. For deterministic plumbing between machines you control, MCP adds protocol overhead you don&#x27;t need.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-the-in-band-vs-out-of-band-distinction&quot;&gt;3. The in-band vs out-of-band distinction&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MCP is self-describing.&lt;&#x2F;strong&gt; A caller with zero prior knowledge can obtain the full capability set purely through the communication. Discovery is part of the required method set.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Plain REST is prior-knowledge.&lt;&#x2F;strong&gt; Nothing in the wire format obligates the server to describe itself, so the caller must already possess the contract. OpenAPI exists, but it&#x27;s a separate document a human fetches and wires up ahead of time — bolted on, aimed at humans and build-time codegen, not intrinsic to the call vocabulary.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Important correction to a common overstatement:&lt;&#x2F;strong&gt; in-band runtime discovery is &lt;em&gt;not&lt;&#x2F;em&gt; unique to MCP. GraphQL introspection queries the schema at runtime through the same endpoint. gRPC server reflection enumerates services and methods without prior stubs. Both are self-describing and discovered through the communication itself. The property is a &lt;em&gt;general&lt;&#x2F;em&gt; API capability that MCP happens to &lt;strong&gt;require&lt;&#x2F;strong&gt;. gRPC &lt;em&gt;can&lt;&#x2F;em&gt; do reflection; MCP &lt;em&gt;mandates&lt;&#x2F;em&gt; it. That mandate — not the capability itself — is what lets a generic client work everywhere.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-the-dominant-pattern-wrap-an-existing-api-and-its-failure-mode&quot;&gt;4. The dominant pattern (wrap an existing API) and its failure mode&lt;&#x2F;h2&gt;
&lt;p&gt;Most MCP servers in the wild wrap an existing API. That&#x27;s not wrong. The failure mode is doing it &lt;strong&gt;too literally&lt;&#x2F;strong&gt;: a 1:1 mapping where every REST endpoint becomes one tool.&lt;&#x2F;p&gt;
&lt;p&gt;The temptation is natural because REST endpoints and MCP tools are both &quot;callable operations,&quot; and OpenAPI-to-MCP generators produce exactly this. It&#x27;s a great &lt;em&gt;starting point&lt;&#x2F;em&gt; and a fine way to see what a model actually reaches for. But an API contract is designed for a programmer who reads docs, holds state, and sequences calls deliberately. An MCP tool is consumed by a model that sees a flat menu, picks under token pressure, and has no memory of your intended workflow. Different audiences.&lt;&#x2F;p&gt;
&lt;p&gt;Where the thin wrapper goes wrong:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Granularity mismatch.&lt;&#x2F;strong&gt; A workflow that a human chains correctly (create cart → add items → apply coupon → checkout) becomes a call-ordering puzzle the model fumbles. Often the right tool is one that hides the choreography.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Schema noise.&lt;&#x2F;strong&gt; Auto-generated tools inherit every optional field, pagination param, and internal ID. That&#x27;s schema the model must read and reason over every turn — token cost and a source of wrong calls.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Return shape.&lt;&#x2F;strong&gt; APIs return complete objects; a model usually needs a compact projection, not the raw payload.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Tool-count blowup.&lt;&#x2F;strong&gt; 60 endpoints → 60 tools degrades selection accuracy. Curation and tag-based filtering matter.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Better framing:&lt;&#x2F;strong&gt; the API is the &lt;em&gt;transport layer underneath&lt;&#x2F;em&gt; your MCP server, not the design of it. Wrap the API, but &lt;strong&gt;redesign the surface&lt;&#x2F;strong&gt; — task-oriented tools, trimmed schemas, projected outputs — treating the model as the user you&#x27;re doing UX for.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Gut check:&lt;&#x2F;strong&gt; if a competent teammate would need your API docs open to use the tool correctly, the model will struggle too.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-worked-example-order-management&quot;&gt;5. Worked example: order management&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;the-underlying-rest-api&quot;&gt;The underlying REST API&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;POST &#x2F;carts                      → {cart_id}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;POST &#x2F;carts&#x2F;{id}&#x2F;items           → add one line item&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;GET  &#x2F;catalog&#x2F;products?q=...     → search, paginated&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;POST &#x2F;carts&#x2F;{id}&#x2F;apply-coupon    → {discount}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;GET  &#x2F;carts&#x2F;{id}                 → full cart w&#x2F; totals&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;POST &#x2F;carts&#x2F;{id}&#x2F;checkout        → requires payment_method_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;thin-1-1-wrapper-the-tempting-mistake&quot;&gt;Thin 1:1 wrapper (the tempting mistake)&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;@mcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; create_cart&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;@mcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; add_item&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;cart_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; product_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; qty&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;             gift_wrap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; False&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; warehouse_hint&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;             price_override_cents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;@mcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; search_products&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;q&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; page_size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 20&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;                    sort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;relevance&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; locale&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;en-US&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;@mcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; apply_coupon&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;cart_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; code&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;@mcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; get_cart&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;cart_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;@mcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; checkout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;cart_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; payment_method_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To buy two items with a coupon, the model must: create a cart, thread the returned &lt;code&gt;cart_id&lt;&#x2F;code&gt; into search, parse a paginated result to pick a &lt;code&gt;product_id&lt;&#x2F;code&gt;, add each item, apply the coupon, then check out with a &lt;code&gt;payment_method_id&lt;&#x2F;code&gt; it has to source from somewhere. Six-plus turns, strict ordering, hand-carried state. Every schema carries noise (&lt;code&gt;warehouse_hint&lt;&#x2F;code&gt;, &lt;code&gt;price_override_cents&lt;&#x2F;code&gt;, &lt;code&gt;locale&lt;&#x2F;code&gt;, pagination) the model must read past. And &lt;code&gt;get_cart&lt;&#x2F;code&gt; returns the whole object when the model wanted one number.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;redesigned-surface-task-oriented&quot;&gt;Redesigned surface (task-oriented)&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;@mcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; find_products&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; max_results&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; list[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;dict&lt;&#x2F;span&gt;&lt;span&gt;]:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;Search the catalog. Returns [{product_id, name, price}].&amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    raw&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; api&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;&#x2F;catalog&#x2F;products&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; params&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;q&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;page_size&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; max_results})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;    # projection: drop everything the model doesn&amp;#39;t need to decide&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; [{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;product_id&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; p[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; p[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;             &amp;quot;price&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; p[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;price_cents&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; raw[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;items&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;@mcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6C08A;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; place_order&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; list[OrderItem]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; coupon&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;                payment_method_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; Depends&lt;&#x2F;span&gt;&lt;span&gt;(get_default_payment))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;Create a cart, add all items, apply an optional coupon, and check out&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;    in one call. items = [{product_id, quantity}]. Returns {order_id, total, status}.&amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; api&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;&#x2F;carts&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;cart_id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; items&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        api&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;&#x2F;carts&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;cart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&#x2F;items&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                 json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;product_id&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;product_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;qty&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;quantity})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; coupon&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        api&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;&#x2F;carts&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;cart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&#x2F;apply-coupon&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;code&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; coupon})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; api&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;&#x2F;carts&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;cart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&#x2F;checkout&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;payment_method_id&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; payment_method_id})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;order_id&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; result[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;total&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; result[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;total_cents&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;            &amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; result[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Two tools instead of six. The moves that mattered:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Collapsed the workflow&lt;&#x2F;strong&gt; into one tool so call-ordering can&#x27;t be gotten wrong.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Depends(get_default_payment)&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; injects the payment method server-side, so it never appears in the schema and can&#x27;t be hallucinated or leaked.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Projected outputs&lt;&#x2F;strong&gt; — three fields, not the raw paginated payload; a 3-key summary, not the full order object.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Dropped internal knobs&lt;&#x2F;strong&gt; (&lt;code&gt;warehouse_hint&lt;&#x2F;code&gt;, &lt;code&gt;price_override_cents&lt;&#x2F;code&gt;) from the model-facing surface. They still exist in the API; the model just doesn&#x27;t see them.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In FastMCP 3.0 you express this without hand-writing every wrapper: source the raw tools from the OpenAPI spec via a &lt;strong&gt;Provider&lt;&#x2F;strong&gt;, then apply &lt;strong&gt;Transforms&lt;&#x2F;strong&gt; — &lt;code&gt;place_order&lt;&#x2F;code&gt; is a custom tool, &lt;code&gt;find_products&lt;&#x2F;code&gt; is the raw endpoint with an output transform plus hidden params, and internal endpoints get filtered out by tag. The API stays the transport; the Transform chain is where the model-facing UX lives.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-does-this-violate-single-responsibility&quot;&gt;6. Does this violate Single Responsibility?&lt;&#x2F;h2&gt;
&lt;p&gt;No — it &lt;strong&gt;relocates&lt;&#x2F;strong&gt; it. SRP is about one reason to change, not one HTTP call. &lt;code&gt;place_order&lt;&#x2F;code&gt; has a single responsibility: &quot;fulfill the intent of ordering.&quot; Making four API calls internally is an implementation detail, exactly like a service-layer &lt;code&gt;OrderService.placeOrder()&lt;&#x2F;code&gt; that touches carts, coupons, and payments. Nobody calls that an SRP violation.&lt;&#x2F;p&gt;
&lt;p&gt;The unit SRP applies to differs by layer:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;REST endpoint:&lt;&#x2F;strong&gt; one responsibility = one resource operation.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;MCP tool:&lt;&#x2F;strong&gt; one responsibility = one unit of user intent.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The thin 1:1 wrapper&lt;&#x2F;strong&gt; actually has &lt;em&gt;worse&lt;&#x2F;em&gt; cohesion — it spreads one responsibility (placing an order) across six tools plus the model&#x27;s reasoning, forcing the model to become the orchestrator. That&#x27;s the real SRP smell.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;SRP is defined relative to the consumer. The consumer changed, so the grain got coarser. Same principle.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-is-mcp-a-standard-for-api-design&quot;&gt;7. Is MCP &quot;a standard for API design&quot;?&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s a &lt;strong&gt;wire protocol standard&lt;&#x2F;strong&gt;: it standardizes the transport, the method vocabulary, and the discovery handshake. It does &lt;strong&gt;not&lt;&#x2F;strong&gt; standardize how you design the tool surface. No spec clause says &quot;collapse workflows&quot; or &quot;project outputs.&quot; All the design advice above is convention and emerging best practice, not protocol. &lt;strong&gt;MCP standardizes the envelope, not the content.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Consequence: a 1:1 REST-wrapping server is fully protocol-compliant &lt;em&gt;and&lt;&#x2F;em&gt; often badly designed — the same way a REST API with 40 endpoints named &lt;code&gt;&#x2F;doStuff1&lt;&#x2F;code&gt;, &lt;code&gt;&#x2F;doStuff2&lt;&#x2F;code&gt; is valid HTTP and bad design. &lt;strong&gt;Compliance and good design are orthogonal.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;8-so-are-mcp-s-design-patterns-just-general-api-design&quot;&gt;8. So are MCP&#x27;s design patterns just general API design?&lt;&#x2F;h2&gt;
&lt;p&gt;Largely, yes — this is the key realization.&lt;&#x2F;p&gt;
&lt;p&gt;The design advice (collapse workflows, project outputs, coarse task-level granularity) is the &lt;strong&gt;Backend-for-Frontend (BFF)&lt;&#x2F;strong&gt; pattern. An MCP server is essentially a &lt;strong&gt;&quot;Backend for Model&quot;&lt;&#x2F;strong&gt;: an aggregation&#x2F;adaptation layer shaped for one specific consumer, exactly like a BFF aggregates chatty microservice calls into one endpoint tuned for a mobile client. &lt;code&gt;place_order&lt;&#x2F;code&gt; isn&#x27;t an MCP idea; it&#x27;s good API design that a forgiving human developer will tolerate you skipping and a model won&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;What is &lt;em&gt;genuinely&lt;&#x2F;em&gt; MCP-specific is narrow:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Standardization &#x2F; universality.&lt;&#x2F;strong&gt; REST discovery is optional and heterogeneous. MCP mandates one uniform discovery handshake — the whole reason a generic client can connect to any server it&#x27;s never seen. A property of the &lt;em&gt;standard&lt;&#x2F;em&gt;, not the pattern.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;A few LLM-native semantics.&lt;&#x2F;strong&gt; &lt;strong&gt;Sampling&lt;&#x2F;strong&gt; (the server asks the client&#x27;s LLM to generate mid-call) and &lt;strong&gt;elicitation&lt;&#x2F;strong&gt; (the server asks the client to prompt its user) have no clean equivalent in a normal API. Even these are callback patterns underneath, but the tools&#x2F;resources&#x2F;prompts trichotomy is a deliberate semantic layer aimed at a model.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Descriptions become runtime input, not docs.&lt;&#x2F;strong&gt; The subtle one. In a human API, a field description is documentation — read once, then ignored at runtime. In MCP, the tool description and schema are injected into the model&#x27;s context and &lt;em&gt;steer selection on every call&lt;&#x2F;em&gt;. Your naming and docstrings stop being annotation and become part of the program&#x27;s behavior. No conventional API has this property, because human consumers don&#x27;t recompile their intent from your docstring each turn.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;9-the-one-liner-to-remember&quot;&gt;9. The one-liner to remember&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;MCP is a standardized profile of existing API patterns (JSON-RPC + mandatory introspection + BFF-style aggregation), specialized for a stochastic, forgetful, self-discovering consumer.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The mechanics transfer almost entirely. What doesn&#x27;t reduce is the &lt;em&gt;enforced uniformity&lt;&#x2F;em&gt; (so a generic client works everywhere) and the fact that your interface copy is now runtime input rather than reference material.&lt;&#x2F;p&gt;
&lt;p&gt;Push it one step further and most of &quot;MCP design&quot; is really just &lt;strong&gt;&quot;API design where the consumer can&#x27;t read your docs, won&#x27;t hold state for you, and re-reads the menu every turn.&quot;&lt;&#x2F;strong&gt; Constraints, not a new discipline.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;design-checklist-for-an-mcp-server&quot;&gt;Design checklist for an MCP server&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Are tools shaped around &lt;strong&gt;units of intent&lt;&#x2F;strong&gt;, not units of REST?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Have you &lt;strong&gt;collapsed multi-step choreography&lt;&#x2F;strong&gt; so the model can&#x27;t mis-order calls?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Are &lt;strong&gt;schemas trimmed&lt;&#x2F;strong&gt; to only what the model should decide on? Internal knobs hidden?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Are &lt;strong&gt;outputs projected&lt;&#x2F;strong&gt; to the relevant fields, not raw payloads?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Is the &lt;strong&gt;tool count&lt;&#x2F;strong&gt; small enough to preserve selection accuracy? (Curate &#x2F; tag-filter.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Are secrets&#x2F;credentials&#x2F;IDs &lt;strong&gt;injected server-side&lt;&#x2F;strong&gt; (e.g. dependency injection) rather than exposed in schemas?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Do &lt;strong&gt;tool names and descriptions&lt;&#x2F;strong&gt; read as clear instructions to a model, given they&#x27;re runtime input?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Gut check: could a teammate use each tool &lt;strong&gt;without your API docs open&lt;&#x2F;strong&gt;?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
</feed>
