<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Umesh Malik</title>
    <description>The latest articles on DEV Community by Umesh Malik (@umesh_malik).</description>
    <link>https://dev.to/umesh_malik</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3777486%2F9bb4f37b-acd0-4752-9675-5e1cf9dd0b78.jpg</url>
      <title>DEV Community: Umesh Malik</title>
      <link>https://dev.to/umesh_malik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/umesh_malik"/>
    <language>en</language>
    <item>
      <title>How to Build Enterprise-Grade AI Agents for Free (MaxKB, 2026)</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Tue, 07 Jul 2026 19:57:26 +0000</pubDate>
      <link>https://dev.to/umesh_malik/how-to-build-enterprise-grade-ai-agents-for-free-maxkb-2026-1nhc</link>
      <guid>https://dev.to/umesh_malik/how-to-build-enterprise-grade-ai-agents-for-free-maxkb-2026-1nhc</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How to build enterprise-grade AI agents for free:&lt;/strong&gt; self-host &lt;a href="https://github.com/1Panel-dev/MaxKB" rel="noopener noreferrer"&gt;MaxKB&lt;/a&gt; — an open-source (GPLv3, ~22k GitHub stars) agent platform — and point it at a local model like DeepSeek or Llama via Ollama, so you pay zero API tokens and no data ever leaves your server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Enterprise-grade" isn't a checkbox — it's five things:&lt;/strong&gt; answer precision, cost control, data-sovereign security, access control, and observability. Free tools can nail four of them out of the box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The one honest catch:&lt;/strong&gt; MaxKB's Community edition is free forever but capped (2 users, 5 apps, 50 knowledge bases). SSO, LDAP, and RBAC live in the paid Pro tier ($1,920/yr) — you can replace them yourself for $0 with more effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precision comes from your RAG pipeline, not the model.&lt;/strong&gt; Chunking, hybrid search, a reranker, and a "cite or refuse" prompt matter more than which LLM you pick.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MaxKB vs Dify vs n8n:&lt;/strong&gt; pick MaxKB for a knowledge-grounded Q&amp;amp;A agent, Dify for a broad LLM app builder, n8n when the agent is one step in a bigger automation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Most "Free AI Agent" Guides Are Lying to You
&lt;/h2&gt;

&lt;p&gt;Here's how to build enterprise-grade AI agents for free in 2026: self-host the open-source platform MaxKB, point it at a local model, and you get a document-grounded, tool-using agent with $0 API cost and zero data leaving your network.&lt;/p&gt;

&lt;p&gt;Most other "free AI agent" tutorials are either toys or bait. The toy version wires ChatGPT to a prompt and calls it an "agent." The bait version is free until step 7, when you hit a paywall, a per-token API meter, or a "contact sales" wall right as it gets useful. Neither gives you something you'd actually put in front of customers or run your internal knowledge base on.&lt;/p&gt;

&lt;p&gt;This guide is the version I wish existed. We're going to stand up a real, &lt;strong&gt;enterprise-grade AI agent&lt;/strong&gt; — one that answers from &lt;em&gt;your&lt;/em&gt; documents with citations, takes actions through tools, runs entirely on infrastructure you control, and costs &lt;strong&gt;$0 in API fees&lt;/strong&gt; — and I'll be honest about exactly where "free" stops and money starts. No hand-waving.&lt;/p&gt;

&lt;p&gt;The vehicle is &lt;strong&gt;MaxKB&lt;/strong&gt;, and by the end you'll have a working agent plus a clear-eyed view of the five things that separate a demo from something you can trust in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is an Enterprise-Grade AI Agent?
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;enterprise-grade AI agent&lt;/strong&gt; is an AI system that answers or acts on your organization's own data with measurable accuracy, keeps that data under your control, enforces who can do what, stays observable, and does all of it at a cost you can predict. "Enterprise-grade" is about &lt;em&gt;trust and control&lt;/em&gt; — not about how big the model is.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The five-question test&lt;/strong&gt;&lt;br&gt;
Point any agent at five questions: is it accurate on &lt;em&gt;our&lt;/em&gt; data, is the cost predictable, does our data stay ours, can we control who does what, and can we see &lt;em&gt;why&lt;/em&gt; it answered the way it did? Five yeses is enterprise-grade. Anything less is a demo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;People throw the phrase around like it means "expensive." It doesn't. A $30/month SaaS chatbot can be less enterprise-grade than a well-configured open-source stack running on a $40 VPS. What makes an agent enterprise-grade is whether it holds up on &lt;strong&gt;five pillars&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Keep these five in mind. Everything below maps back to them. The good news: a free, self-hosted stack wins pillars 1, 2, 3, and 5 outright. Pillar 4 is the one place "free" gets an asterisk, and we'll deal with it head-on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The $0 Stack: MaxKB + a Local Model
&lt;/h2&gt;

&lt;p&gt;The cheapest enterprise-grade agent in 2026 is &lt;strong&gt;MaxKB running against a local LLM, both self-hosted on one machine&lt;/strong&gt;. That's the whole trick. MaxKB gives you the agent platform; a local model kills the API bill; self-hosting solves data sovereignty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MaxKB&lt;/strong&gt; (short for &lt;em&gt;Max Knowledge Brain&lt;/em&gt;, built by the 1Panel team) is an open-source platform for building enterprise-grade agents. Its stack is boringly solid — Vue frontend, Django backend, LangChain under the hood, and &lt;strong&gt;PostgreSQL + pgvector&lt;/strong&gt; as the vector store — which means no exotic dependencies and one-command deployment.&lt;/p&gt;

&lt;p&gt;Out of the box it gives you a full &lt;strong&gt;RAG pipeline&lt;/strong&gt; (upload docs or crawl a site → automatic chunking → vectorization), a &lt;strong&gt;visual workflow engine&lt;/strong&gt;, and &lt;strong&gt;MCP tool-use&lt;/strong&gt; so the agent can call external tools. Crucially, it's &lt;strong&gt;model-agnostic&lt;/strong&gt; — it'll talk to OpenAI, Claude, and Gemini &lt;em&gt;or&lt;/em&gt; to local models like DeepSeek, Qwen, and Llama.&lt;/p&gt;

&lt;p&gt;That last part is the money-saver. Point MaxKB at a local model served by &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; and your per-token cost drops to exactly zero.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fumesh-malik.com%2Fblog%2Ffree-enterprise-agents-stack.card.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fumesh-malik.com%2Fblog%2Ffree-enterprise-agents-stack.card.png" alt="The $0 enterprise AI agent stack: users hit a self-hosted MaxKB instance running the RAG pipeline, workflow engine and MCP tools, which calls a local LLM via Ollama and a pgvector database — all on one server you control, so no data leaves and there are no API token costs" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Build Enterprise-Grade AI Agents for Free, Step by Step
&lt;/h2&gt;

&lt;p&gt;Here's the honest, end-to-end walkthrough. You need a machine with Docker installed — a laptop works for testing; for a local model you'll want at least 16GB of RAM (more if you run larger models). Every step below is free.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — Run MaxKB
&lt;/h3&gt;

&lt;p&gt;One command. This maps a data volume so your knowledge bases survive restarts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;maxkb &lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/.maxkb:/var/lib/postgresql/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/.python-packages:/opt/maxkb/app/sandbox/python-packages &lt;span class="se"&gt;\&lt;/span&gt;
  1panel/maxkb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8080&lt;/code&gt; and log in with the default credentials shown in the &lt;a href="https://docs.maxkb.pro/" rel="noopener noreferrer"&gt;MaxKB docs&lt;/a&gt;. Change the password immediately — that's the first line of your security checklist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — Serve a local model for $0
&lt;/h3&gt;

&lt;p&gt;Install Ollama, then pull a model. DeepSeek and Qwen punch far above their weight for RAG in 2026:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install ollama, then:&lt;/span&gt;
ollama pull deepseek-r1:7b        &lt;span class="c"&gt;# reasoning model, runs on modest hardware&lt;/span&gt;
ollama pull nomic-embed-text      &lt;span class="c"&gt;# embeddings for the RAG pipeline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ollama now serves an OpenAI-compatible endpoint at &lt;code&gt;http://localhost:11434&lt;/code&gt;. This is the whole reason your token bill is zero — inference happens on your hardware, not someone's metered API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 — Connect the model in MaxKB
&lt;/h3&gt;

&lt;p&gt;In the MaxKB UI go to &lt;strong&gt;Model Settings → Add Model&lt;/strong&gt;, choose the Ollama provider, and point it at your Ollama host. If MaxKB runs in Docker and Ollama runs on the host machine, use &lt;code&gt;http://host.docker.internal:11434&lt;/code&gt; as the base URL. Add both the chat model (&lt;code&gt;deepseek-r1:7b&lt;/code&gt;) and the embedding model (&lt;code&gt;nomic-embed-text&lt;/code&gt;). No API key required.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Precision tip&lt;/strong&gt;&lt;br&gt;
Use a dedicated &lt;strong&gt;embedding&lt;/strong&gt; model (like &lt;code&gt;nomic-embed-text&lt;/code&gt;) for the knowledge base, not your chat model. Retrieval quality — and therefore answer precision — depends far more on the embedding model than on the chat model. This is the single most common mistake people make and never diagnose.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4 — Build the knowledge base
&lt;/h3&gt;

&lt;p&gt;Create a knowledge base, then either upload documents or paste a URL to crawl. MaxKB handles splitting, vectorizing, and indexing into pgvector automatically. Two settings decide your precision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chunk size&lt;/strong&gt; — too big and retrieval pulls in noise; too small and it loses context. Start around 500–800 tokens with overlap, then tune against real questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Segment cleanup&lt;/strong&gt; — strip navigation boilerplate and repeated headers before indexing. Garbage in the index is the number-one cause of confidently wrong answers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5 — Create the agent and give it a tool
&lt;/h3&gt;

&lt;p&gt;Create an application, attach your knowledge base, and set a system prompt that enforces grounding — the "cite or refuse" rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Answer only from the provided knowledge base.
Cite the source document for every claim.
If the answer isn't in the knowledge base, say
"I don't have that information" — never guess.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then wire an &lt;strong&gt;MCP tool&lt;/strong&gt; so the agent can &lt;em&gt;act&lt;/em&gt;, not just answer — look up an order, create a ticket, query a database. MaxKB's function library and MCP support let you register tools the workflow can call. If you're new to MCP, start with &lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;how to build an MCP server&lt;/a&gt; and &lt;a href="https://umesh-malik.com/blog/deploy-mcp-server-cloudflare-workers" rel="noopener noreferrer"&gt;deploying one on Cloudflare Workers&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 — Embed it
&lt;/h3&gt;

&lt;p&gt;MaxKB generates an embeddable chat widget and a REST API. Paste the widget script into any page, or call the API from your backend. Zero front-end code required — this is the "zero-coding integration" MaxKB is built around.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Five Pillars, Judged Honestly
&lt;/h2&gt;

&lt;p&gt;A running agent isn't the same as an enterprise-grade one. Let's grade the free stack against the five pillars — including where it falls short.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 1 — Effectiveness &amp;amp; precision
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Precision comes from the retrieval layer, not the model.&lt;/strong&gt; This is the most important sentence in this article. Teams burn weeks swapping models when their real problem is a bad chunking strategy or no reranking. RAG works by retrieving relevant chunks and forcing the model to answer &lt;em&gt;from those chunks&lt;/em&gt;, which is what crushes hallucinations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0p53dj4ft2iwszy3j2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0p53dj4ft2iwszy3j2y.png" alt="The RAG precision pipeline that keeps an agent accurate: documents are chunked and embedded, stored in a pgvector index, retrieved as top-k matches, reranked and filtered, then passed to the LLM for a grounded, cited answer — precision comes from the retrieval layer, not the model" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The precision levers, in order of impact:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Chunking&lt;/strong&gt; — right size + overlap, boilerplate stripped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid search&lt;/strong&gt; — combine keyword and vector search so exact terms (part numbers, names) aren't lost to fuzzy semantics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reranking&lt;/strong&gt; — reorder the top-k so the &lt;em&gt;best&lt;/em&gt; chunk lands in the model's context, not just a &lt;em&gt;relevant&lt;/em&gt; one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A grounding prompt&lt;/strong&gt; — "cite or refuse," as above.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nail those four and a 7B local model will out-answer a frontier model with a sloppy pipeline. If you want the full theory, read &lt;a href="https://umesh-malik.com/blog/build-rag-pipeline-from-scratch" rel="noopener noreferrer"&gt;building a RAG pipeline from scratch&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 2 — Cost control
&lt;/h3&gt;

&lt;p&gt;This is where self-hosting quietly wins. A metered API bills you more as you succeed; a per-seat SaaS bills you more as your team grows. A self-hosted local model turns both into &lt;strong&gt;one flat server bill&lt;/strong&gt; that barely moves.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgq19hzvu8saoqm2x7ghx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgq19hzvu8saoqm2x7ghx.png" alt="Cost as you scale, three ways: a self-hosted MaxKB plus local model stays roughly flat at a fixed server cost, a metered LLM API rises with token usage, and a per-seat SaaS agent platform rises steeply with team size" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tradeoff is real and worth stating: self-hosting trades a variable &lt;em&gt;money&lt;/em&gt; cost for a fixed &lt;em&gt;operational&lt;/em&gt; cost — you run the server, you patch it, you own uptime. For a small internal agent that's a rounding error. At scale it's a massive saving.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 3 — Security &amp;amp; data sovereignty
&lt;/h3&gt;

&lt;p&gt;The strongest argument for this stack. When the model runs locally and MaxKB runs on your server, &lt;strong&gt;no document ever touches a third-party cloud&lt;/strong&gt;. For anyone handling PII, health, financial, or regulated data, that alone can be the difference between "allowed" and "not allowed." You're not sending your knowledge base to an API you don't control.&lt;/p&gt;

&lt;p&gt;That's the &lt;em&gt;architecture&lt;/em&gt; being secure. You still have to &lt;em&gt;harden the deployment&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;For the broader threat model of agents that take actions, see &lt;a href="https://umesh-malik.com/blog/agentic-ai-enterprise-security-model" rel="noopener noreferrer"&gt;the agentic AI enterprise security model&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 4 — Access control (the honest asterisk)
&lt;/h3&gt;

&lt;p&gt;Here's where free ends. MaxKB's &lt;strong&gt;Community edition caps you at 2 users&lt;/strong&gt;, and &lt;strong&gt;SSO, LDAP, and RBAC are Pro-tier features&lt;/strong&gt;. If you need "marketing can only see the marketing knowledge base, support leads can edit agents, everyone logs in with Okta" — that's the paid tier, or DIY work you take on yourself (an auth proxy in front, separate instances per team). I'd rather tell you that now than let you discover it at rollout.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 5 — Observability
&lt;/h3&gt;

&lt;p&gt;MaxKB logs conversations and lets you inspect what was retrieved for a given answer, which is enough to debug precision and iterate on chunking. It's not a full LLM-observability suite — if you need deep tracing and eval dashboards you'll add tooling — but for "why did the agent say that?" you have what you need for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Does "Free" Actually End?
&lt;/h2&gt;

&lt;p&gt;No dodging it. Here's the exact line between $0 and paid, so you can plan.&lt;/p&gt;

&lt;p&gt;My take: &lt;strong&gt;start on Community.&lt;/strong&gt; It is genuinely free and genuinely capable. Only pay when access control across multiple teams becomes a real, present need — not a hypothetical one. Most people building their first agent are nowhere near the 2-user wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  MaxKB vs Dify vs n8n: Which Free AI Agent Platform Should You Use?
&lt;/h2&gt;

&lt;p&gt;"Free AI agent platform" returns a dozen tools that do overlapping-but-different things. Here's how the honest contenders compare — and none of these are the same product.&lt;/p&gt;

&lt;p&gt;The key insight: &lt;strong&gt;Ollama isn't a competitor to MaxKB — it's a component of the stack.&lt;/strong&gt; MaxKB and Dify compete; n8n plays a different game (orchestration). Here's how I'd choose:&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes That Kill Free Agents
&lt;/h2&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;Free and enterprise-grade are not opposites in 2026 — that's the myth this guide exists to kill. Self-host MaxKB, point it at a local model, invest in your retrieval pipeline, harden the box, and you have an agent that answers from your data with citations, keeps that data on your own hardware, and costs nothing per token. The only honest asterisk is multi-team access control, and you now know exactly when that bill arrives.&lt;/p&gt;

&lt;p&gt;Start on the Community edition today. Build the narrow, useful agent — the one that answers your support questions or your internal docs — get the retrieval right, and let the results decide whether you ever need to pay.&lt;/p&gt;

&lt;p&gt;If this was useful, read &lt;a href="https://umesh-malik.com/blog/build-rag-pipeline-from-scratch" rel="noopener noreferrer"&gt;building a RAG pipeline from scratch&lt;/a&gt; next to push your agent's precision further, or &lt;a href="https://umesh-malik.com/blog/autonomous-ai-agents-production-gap-2026" rel="noopener noreferrer"&gt;why 77% of AI agents never reach production&lt;/a&gt; to make sure yours is in the 23% that do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/1Panel-dev/MaxKB" rel="noopener noreferrer"&gt;MaxKB — 1Panel-dev/MaxKB on GitHub&lt;/a&gt; (open-source, GPLv3)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.maxkb.pro/" rel="noopener noreferrer"&gt;MaxKB official documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maxkb.pro/" rel="noopener noreferrer"&gt;MaxKB project site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama — run local models&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/build-enterprise-ai-agents-free" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/autonomous-ai-agents-production-gap-2026" rel="noopener noreferrer"&gt;AI Agents That Run the Business in 2026: Why 77% Never Reach Production (and What the 23% Do Differently)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/rag-vs-fine-tuning-llms-2026" rel="noopener noreferrer"&gt;RAG vs Fine-Tuning for LLMs in 2026: A Production Decision Framework With Real Tradeoffs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/agentic-browsing-pagespeed-ai-ready" rel="noopener noreferrer"&gt;Agentic Browsing in PageSpeed Insights: How to Make Your Website AI-Ready (2026)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aiagents</category>
      <category>maxkb</category>
      <category>opensource</category>
      <category>rag</category>
    </item>
    <item>
      <title>HTTP QUERY Method Explained: The REST Fix for GET vs POST (RFC 10008)</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Fri, 03 Jul 2026 14:07:20 +0000</pubDate>
      <link>https://dev.to/umesh_malik/http-query-method-explained-the-rest-fix-for-get-vs-post-rfc-10008-881</link>
      <guid>https://dev.to/umesh_malik/http-query-method-explained-the-rest-fix-for-get-vs-post-rfc-10008-881</guid>
      <description>&lt;p&gt;The &lt;strong&gt;HTTP QUERY method&lt;/strong&gt; (&lt;a href="https://www.rfc-editor.org/rfc/rfc10008.html" rel="noopener noreferrer"&gt;RFC 10008&lt;/a&gt;, June 2026) is a new REST request method that is &lt;strong&gt;safe and idempotent like GET but carries the query in a request body like POST&lt;/strong&gt; — so large, structured queries finally get HTTP-layer caching and automatic retries. It's the first general-purpose method HTTP has gained since PATCH in 2010, and it closes a design gap REST API developers have been working around for 25 years. If you've ever tunneled a read-only search through POST and silently given up caching and retry safety, this method exists because of you.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;QUERY is a new HTTP request method that sends a query in the request body while guaranteeing the operation is safe and idempotent&lt;/strong&gt; — no state change, freely retryable, and cacheable by proxies and CDNs.&lt;/li&gt;
&lt;li&gt;It exists because &lt;strong&gt;GET can't reliably carry a body&lt;/strong&gt; (the spec gives GET bodies "no defined semantics", and URLs cap out around 2,000–8,000 characters) while &lt;strong&gt;POST forfeits caching and automatic retries&lt;/strong&gt; even for purely read-only queries.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;cache key for a QUERY request is the URL plus the request body&lt;/strong&gt;, so two identical searches can be served from the edge without touching your origin — something POST-based search endpoints will never get.&lt;/li&gt;
&lt;li&gt;It's already real: &lt;strong&gt;Node.js parses QUERY natively (21.7.2+/22+), Fastify supports it via &lt;code&gt;addHttpMethod()&lt;/code&gt;, nginx landed basic support, ASP.NET Core recognizes it in .NET 11 previews, and OpenAPI 3.2 models it&lt;/strong&gt; — but browsers' &lt;code&gt;fetch()&lt;/code&gt; can't send it yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My take: design new server-to-server search/filter/report endpoints as QUERY-shaped now&lt;/strong&gt; (single query document in the body, mandatory &lt;code&gt;Content-Type&lt;/code&gt;), even if you expose them via POST today. The migration later becomes a method-name change.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Is the HTTP QUERY Method?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The HTTP QUERY method is a safe, idempotent request method, defined in RFC 10008, that asks a resource to run a query described by the request body and return the result.&lt;/strong&gt; The body — with a mandatory &lt;code&gt;Content-Type&lt;/code&gt; — carries the query parameters, so the URL no longer has to. Unlike POST, the server promises the operation changes nothing; unlike GET, the query can be as large and structured as you want: JSON documents, GraphQL queries, SQL-ish filters, JSONPath expressions.&lt;/p&gt;

&lt;p&gt;The RFC's own framing is precise: a QUERY request asks "the target resource to perform a query operation within the scope of that target resource." Two guarantees make everything else possible. First, safety: "the client does not request or expect any change to the state of the target resource." Second, idempotency: QUERY requests "can be retried or repeated when needed, for instance, after a connection failure."&lt;/p&gt;

&lt;p&gt;That second sentence is the quiet superpower. A dropped connection mid-POST leaves your client guessing — did the server process it? A dropped connection mid-QUERY is a non-event: any client, library, or proxy may resend it without asking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4w28f5abboa9amxvagyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4w28f5abboa9amxvagyh.png" alt="Diagram showing the 25-year gap in HTTP's method table: GET is safe and cacheable but has no body, POST has a body but is unsafe and uncacheable, and the new QUERY method provides both — safe, idempotent, cacheable, with a request body" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why HTTP Needed a New Method
&lt;/h2&gt;

&lt;p&gt;Here's the design flaw we all normalized: &lt;strong&gt;HTTP made you choose between honest semantics and a request body.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GET is the semantically correct method for reads. Every cache, proxy, prefetcher, and crawler on Earth understands it. But &lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html" rel="noopener noreferrer"&gt;RFC 9110&lt;/a&gt; gives content in a GET request "no generally defined semantics" — intermediaries may drop it, some servers reject it, and browsers won't send it. So real query data had to squeeze into the URL, which breaks somewhere between 2,000 and 8,000 characters depending on the browser, proxy, and server in the chain.&lt;/p&gt;

&lt;p&gt;POST takes any body you like. But POST is defined as potentially unsafe and non-idempotent, so every intermediary treats your read-only search like a payment submission: no shared caching, no automatic retry, no prefetching. &lt;strong&gt;Every GraphQL query — explicitly read-only by design — loses HTTP-level caching and retry safety the moment it ships as POST.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The industry's workarounds tell the story better than any spec rationale:&lt;/p&gt;

&lt;p&gt;This isn't a new idea, which makes the 18-year journey instructive. WebDAV had a body-carrying SEARCH method back in &lt;a href="https://www.rfc-editor.org/rfc/rfc5323.html" rel="noopener noreferrer"&gt;RFC 5323&lt;/a&gt; (2008), but it never escaped WebDAV's orbit. The IETF HTTP working group adopted &lt;a href="https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/" rel="noopener noreferrer"&gt;draft-ietf-httpbis-safe-method-w-body&lt;/a&gt; in 2021, initially to generalize SEARCH, renamed it QUERY by draft-02 in 2022 to shed the WebDAV baggage, and shipped RFC 10008 after fourteen draft revisions. The author list explains the industry buy-in: Julian Reschke (greenbytes, co-editor of the core HTTP specs), James Snell (Cloudflare), and Mike Bishop (Akamai). &lt;strong&gt;When the two largest CDNs co-author a caching-focused method, they intend to implement it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3aa8dti7g9f9a1sdyt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3aa8dti7g9f9a1sdyt6.png" alt="Timeline of the HTTP QUERY method's standardization: WebDAV SEARCH in RFC 5323 (2008), the workaround era of Elasticsearch GET-with-body and GraphQL-over-POST (2015–2020), the httpbis draft adoption (2021), the rename from SEARCH to QUERY (2022), and RFC 10008 as Proposed Standard in June 2026" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How QUERY Works on the Wire
&lt;/h2&gt;

&lt;p&gt;A QUERY request looks like a POST that tells the truth. Here's the RFC's canonical example — a form-encoded query against a contacts collection:&lt;/p&gt;

&lt;p&gt;Beyond the basic exchange, RFC 10008 nails down four behaviors that make QUERY more than "POST, but pinky-promise it's safe":&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Content-Type is not optional.&lt;/strong&gt; Servers "MUST fail the request if the Content-Type request field is missing or is inconsistent with the request content." No content sniffing, no guessing — a sharp break from POST's anything-goes reality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Discovery via &lt;code&gt;Accept-Query&lt;/code&gt;.&lt;/strong&gt; A server advertises QUERY support and its accepted formats with a response header using Structured Field syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;OPTIONS&lt;/span&gt; &lt;span class="nn"&gt;/contacts&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;

HTTP/1.1 204 No Content
Allow: GET, HEAD, OPTIONS, QUERY
Accept-Query: application/x-www-form-urlencoded, application/jsonpath
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One subtlety worth knowing: the &lt;code&gt;Accept-Query&lt;/code&gt; value "applies to every URI on the server that shares the same path" — the URL's query component is ignored for this purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Results can get their own URIs.&lt;/strong&gt; A 2xx response may include a &lt;code&gt;Location&lt;/code&gt; header naming a URI you can later GET to re-run the equivalent query, or a &lt;code&gt;Content-Location&lt;/code&gt; naming a resource that holds this specific result snapshot. That turns an expensive ad-hoc query into a shareable, bookmarkable, cacheable resource — a genuinely RESTful touch POST never standardized for reads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Redirects behave like you'd hope.&lt;/strong&gt; A 301 or 308 means repeat the QUERY at the new target; 303 means fetch the result with a plain GET. Crucially, the legacy exception that lets clients degrade a redirected POST into GET "does not apply to QUERY requests" — your query body survives the redirect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching: The Actual Killer Feature
&lt;/h3&gt;

&lt;p&gt;The RFC is blunt about the mechanics: "The cache key for a QUERY request MUST incorporate the request content and related metadata." URL plus body, hashed, stored. Identical query, identical key, edge-served response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7endo69bh7augxj51229.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7endo69bh7augxj51229.png" alt="Diagram of HTTP QUERY caching flow: a client sends QUERY with a body, the CDN computes a cache key from the URL plus the request body, forwards a miss to the origin, caches the 200 response, and serves the next identical query from the edge without contacting the origin" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Caches are even allowed to normalize semantically insignificant differences — stripping content encoding, or applying format-aware knowledge (JSON key order, whitespace) — so trivially different bodies can still share a cache entry. If that makes you nervous, &lt;code&gt;no-transform&lt;/code&gt; opts out.&lt;/p&gt;

&lt;p&gt;Think about what this does for a search-heavy API. Today, your "cache" for POST /search is an application-level Redis layer you built, keyed by a hash you invented, invalidated by rules you maintain. QUERY moves that entire concern into infrastructure that already exists at every hop between the user and your origin. This is the same architectural instinct as &lt;a href="https://umesh-malik.com/blog/deploy-mcp-server-cloudflare-workers" rel="noopener noreferrer"&gt;moving analytics to the edge with Cloudflare Workers&lt;/a&gt; — push work to the layer that's already positioned to do it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: QUERY doesn't make anything possible that was impossible before. It makes the &lt;em&gt;default&lt;/em&gt; correct — caching, retries, and semantics you previously had to hand-build now fall out of the method name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  QUERY vs GET vs POST
&lt;/h2&gt;

&lt;p&gt;The one-table version, straight from the RFC's own comparison:&lt;/p&gt;

&lt;p&gt;That security row deserves a sentence: because the query moves out of the URL, it stops leaking into access logs, browser history, referrer chains, and analytics pipelines. RFC 10008's Security Considerations calls this out as a real benefit for sensitive queries — with the matching warning that servers minting result URIs must not embed sensitive query parts back into them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where QUERY Works Today (July 2026)
&lt;/h2&gt;

&lt;p&gt;The honest adoption picture, one month after publication:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Folea2jffs5rqpbx9zqmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Folea2jffs5rqpbx9zqmm.png" alt="Adoption map for the HTTP QUERY method as of July 2026: works now in Node.js 21.7.2+/22+, Fastify via addHttpMethod, nginx basic support, and OpenAPI 3.2; in progress in ASP.NET Core .NET 11 previews, Spring Framework PR 34993, Ruby on Rails proposal, and CDNs; not yet in browsers, curl's native verbs, Express, Django, FastAPI, and CORS safelisting" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the working side: &lt;strong&gt;Node.js parses QUERY natively since 21.7.2 and 22+&lt;/strong&gt;, and Fastify exposes it with one line. &lt;strong&gt;nginx landed basic upstream support&lt;/strong&gt;, &lt;a href="https://github.com/spring-projects/spring-framework/pull/34993" rel="noopener noreferrer"&gt;Spring Framework has an active PR (#34993)&lt;/a&gt;, &lt;a href="https://discuss.rubyonrails.org/t/proposal-support-for-the-http-query-method-rfc-10008/91255" rel="noopener noreferrer"&gt;Rails has a live core proposal&lt;/a&gt;, and ASP.NET Core recognizes it in .NET 11 previews.&lt;/p&gt;

&lt;p&gt;OpenAPI 3.2 also models QUERY operations — which means generated clients will offer it before most hand-written servers accept it.&lt;/p&gt;

&lt;p&gt;Here's a working Fastify endpoint today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Fastify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHttpMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QUERY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QUERY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// body IS the query&lt;/span&gt;
    &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cache-control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public, max-age=60&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And from any client that lets you set a custom method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; QUERY https://api.example.com/products &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "category": "keyboards", "maxPrice": 200, "sort": "rating" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The missing piece is the browser. &lt;code&gt;fetch()&lt;/code&gt; can't send QUERY until the WHATWG Fetch spec adds it, and even then QUERY is not a CORS-safelisted method, so cross-origin requests will always preflight. &lt;strong&gt;In 2026, QUERY is a server-to-server and API-gateway story&lt;/strong&gt; — which is exactly where search, reporting, and internal query traffic lives anyway. If you build &lt;a href="https://umesh-malik.com/blog/nodejs-backend-for-frontend-developers" rel="noopener noreferrer"&gt;backend-for-frontend layers in Node.js&lt;/a&gt;, that BFF-to-service hop is the perfect first deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The #1 mistake: treating QUERY as a POST alias&lt;/strong&gt;&lt;br&gt;
The method is a contract, not a vibe. If your QUERY handler writes an audit row, increments a rate counter that changes responses, or mutates anything a client could observe, you've broken the contract — and a cache or retrying proxy will eventually expose that bug in production, far from the code that caused it. Side effects that aren't client-observable (logging, metrics) are fine, exactly as with GET.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Four more, seen in the wild already:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Omitting &lt;code&gt;Content-Type&lt;/code&gt;.&lt;/strong&gt; Legal-ish on POST, a mandatory failure on QUERY. Fail these requests loudly, as the RFC requires — don't sniff.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgetting the CORS preflight.&lt;/strong&gt; QUERY from browser-adjacent contexts will send OPTIONS first. Your gateway needs &lt;code&gt;Access-Control-Allow-Methods: QUERY&lt;/code&gt; or every request dies preflight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaking query content into result URIs.&lt;/strong&gt; If QUERY returns a &lt;code&gt;Location&lt;/code&gt; for later GETs, that URL will be logged everywhere. Use opaque result IDs, not serialized query parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assuming your WAF and proxies pass it through.&lt;/strong&gt; Older intermediaries reject unknown methods. Test the full path — one method-allowlist in a corporate proxy can 405 your rollout.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Should You Adopt QUERY Now?
&lt;/h2&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;REST API design has spent 25 years contorting reads into methods that either couldn't carry the query or couldn't admit it was a read. The HTTP QUERY method ends that trade-off with the least glamorous, most durable kind of fix: a verb that tells the infrastructure the truth, and infrastructure that rewards honesty with caching and retries you no longer have to build.&lt;/p&gt;

&lt;p&gt;You don't need to rewrite anything this quarter. But every new search, filter, or reporting endpoint you design from today should be &lt;strong&gt;QUERY-shaped&lt;/strong&gt;: one query document in the body, a mandatory content type, and a handler with zero side effects. Do that, and adopting the verb itself — when your framework, gateway, and CDN all catch up — becomes a one-line change instead of a migration project.&lt;/p&gt;

&lt;p&gt;If you found this useful, read &lt;a href="https://umesh-malik.com/blog/nodejs-backend-for-frontend-developers" rel="noopener noreferrer"&gt;Node.js Backend for Frontend Developers&lt;/a&gt; next — the BFF layer is exactly where QUERY will land first — or see how &lt;a href="https://umesh-malik.com/blog/fastapi-spa-app-frontend-explained" rel="noopener noreferrer"&gt;FastAPI's app.frontend() rethinks another old default&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.rfc-editor.org/rfc/rfc10008.html" rel="noopener noreferrer"&gt;RFC 10008 — The HTTP QUERY Method&lt;/a&gt; (IETF, June 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/" rel="noopener noreferrer"&gt;draft-ietf-httpbis-safe-method-w-body — draft history&lt;/a&gt; (IETF Datatracker)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html" rel="noopener noreferrer"&gt;RFC 9110 — HTTP Semantics&lt;/a&gt; (GET body semantics, method properties)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rfc-editor.org/rfc/rfc5323.html" rel="noopener noreferrer"&gt;RFC 5323 — WebDAV SEARCH&lt;/a&gt; (the 2008 precursor)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spring-projects/spring-framework/pull/34993" rel="noopener noreferrer"&gt;Spring Framework PR #34993 — Add RFC 10008 (QUERY) support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discuss.rubyonrails.org/t/proposal-support-for-the-http-query-method-rfc-10008/91255" rel="noopener noreferrer"&gt;Ruby on Rails core proposal — Support for the HTTP QUERY method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://byteiota.com/rfc-10008-http-query-method-ends-the-post-workaround/" rel="noopener noreferrer"&gt;RFC 10008: HTTP QUERY Method Ends the POST Workaround&lt;/a&gt; (byteiota, adoption roundup)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/http-query-method-rfc-10008-guide" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/fastapi-spa-app-frontend-explained" rel="noopener noreferrer"&gt;FastAPI Finally Has Native SPA Support: app.frontend() Explained&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/cloudflare-vinext-next-js-vite-revolution" rel="noopener noreferrer"&gt;The $1,100 Framework That Just Made Vercel's $3 Billion Moat Obsolete&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/nodejs-memory-cut-in-half-pointer-compression" rel="noopener noreferrer"&gt;Node.js Just Cut Its Memory in Half — One Docker Line, Zero Code Changes, $300K Saved&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>httpquerymethod</category>
      <category>rfc10008</category>
      <category>restapidesign</category>
      <category>http</category>
    </item>
    <item>
      <title>Claude Fable 5: The Deep-Dive Guide — What It Does, What It Costs, and When It's Worth the Premium (2026)</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Thu, 02 Jul 2026 13:49:44 +0000</pubDate>
      <link>https://dev.to/umesh_malik/claude-fable-5-the-deep-dive-guide-what-it-does-what-it-costs-and-when-its-worth-the-premium-2ph</link>
      <guid>https://dev.to/umesh_malik/claude-fable-5-the-deep-dive-guide-what-it-does-what-it-costs-and-when-its-worth-the-premium-2ph</guid>
      <description>&lt;p&gt;&lt;strong&gt;Claude Fable 5&lt;/strong&gt; is the model Anthropic points to when the answer to "can an AI actually do this?" needs to be yes. It's the most capable model they've released widely — built for the work that used to be a research demo: multi-hour autonomous runs, first-shot builds of well-specified systems, and end-to-end deliverables a person would bill days for. In the API it's the model id &lt;code&gt;claude-fable-5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's also the most expensive Claude you can call, it behaves differently from every Opus-tier model before it, and for most of what you do day to day it is the wrong choice. This is the honest deep-dive: what Fable 5 genuinely does better, what it costs once you do the arithmetic, the API quirks that will trip you up, and — the part the launch hype skips — the large set of jobs where you should reach for something cheaper.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Fable 5 is Anthropic's most capable widely released model&lt;/strong&gt; — tuned for the hardest reasoning and long-horizon agentic work, not for chat or high-volume throughput.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's premium-priced at $10 / $50 per million tokens&lt;/strong&gt; — roughly 2× Opus 4.8 and over 3× Sonnet 5. The capability ceiling is real; so is the bill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It behaves differently from Opus-tier models.&lt;/strong&gt; Thinking is always on (you can't disable it), the raw chain of thought is never returned, and safety classifiers can decline a request with a &lt;code&gt;refusal&lt;/code&gt; stop reason you have to handle in code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It got broadly available the slow way&lt;/strong&gt; — through an invitation-only preview and a restricted access program before general release, not a big-bang launch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The play:&lt;/strong&gt; keep Opus 4.8 or &lt;a href="https://umesh-malik.com/blog/claude-sonnet-5-guide" rel="noopener noreferrer"&gt;Sonnet 5&lt;/a&gt; as your default, and escalate a specific workload to Fable 5 only when its ceiling — long-horizon autonomy, first-shot correctness on a genuinely hard task — is worth 2×+ the price.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Is Claude Fable 5?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Claude Fable 5 is Anthropic's flagship model for the most demanding reasoning and long-horizon agentic work — the most capable model they've released to the general public.&lt;/strong&gt; It has a 1-million-token context window (which is both the maximum &lt;em&gt;and&lt;/em&gt; the default), up to 128K output tokens per request, always-on extended thinking, and high-resolution vision. You call it in the API as &lt;code&gt;claude-fable-5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The one-sentence version: &lt;strong&gt;it's the model you give your hardest, longest, least-supervised problem to.&lt;/strong&gt; Where a mid-tier model shines on the tasks you'd assign a competent engineer, Fable 5 is built for the ones you'd assign a senior engineer a week to figure out — and it's meant to run largely unattended while it does.&lt;/p&gt;

&lt;p&gt;Three things define it in practice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It's built for autonomy, not turns.&lt;/strong&gt; The headline is long-horizon execution: a single request on a hard task can run for many minutes while the model gathers context, builds, and verifies its own work. This is not a chat model with a bigger brain — it's a model tuned to be pointed at an outcome and left alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It trades control for capability.&lt;/strong&gt; Several knobs you're used to are gone. Thinking is always on. Sampling parameters are rejected. There's no assistant prefill. You steer it with prompting and an &lt;code&gt;effort&lt;/code&gt; dial, not low-level parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's the premium tier, and priced like it.&lt;/strong&gt; At $10 / $50 per million tokens it sits above every Opus model. The interesting question was never "is it the most capable?" — it is. The question is "is &lt;em&gt;this&lt;/em&gt; workload hard enough to justify it?"&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: Fable 5's value isn't spread evenly. On routine work it's overkill you're overpaying for. On the hardest long-horizon runs it does things no cheaper model reliably does. Knowing which bucket a task falls into is the whole skill.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How Claude Fable 5 Became Widely Available
&lt;/h2&gt;

&lt;p&gt;Fable 5 didn't arrive as a single splashy launch. The capability reached the public through a staged rollout that started tightly restricted and opened up over time — worth understanding, because the more exclusive tiers still exist alongside it.&lt;/p&gt;

&lt;p&gt;The practical takeaway: &lt;strong&gt;Fable 5 and Mythos 5 are the same model with different front doors.&lt;/strong&gt; Mythos 5 is what Project Glasswing participants use; Fable 5 is the generally available equivalent everyone else calls. If you've read about "Mythos" and wondered whether you're missing a more powerful model — you're not. You just reach it under a different id.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on the timeline&lt;/strong&gt;&lt;br&gt;
This is an &lt;em&gt;access-tier&lt;/em&gt; progression — invitation-only preview, to a restricted program, to general availability — not a case of the model being pulled and re-launched. If you see claims of a "recall," treat them skeptically; the documented path is restricted-to-broad access, not launch-then-relaunch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Claude Fable 5 Can Actually Do
&lt;/h2&gt;

&lt;p&gt;Fable 5's gains show up most on work &lt;em&gt;above&lt;/em&gt; what previous models could reliably finish. Evaluate it on the tasks a mid-tier model already handles and you'll wonder why you paid the premium. Point it at the hard stuff and the difference is obvious.&lt;/p&gt;

&lt;p&gt;If your problem lives on that grid — an overnight build, a hard multi-file migration, an end-to-end analysis deliverable, a long autonomous agent loop where correctness matters more than the token bill — Fable 5 is the model that clears the bar cheaper models keep tripping on. If it doesn't, keep reading, because you're probably about to overpay.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API Mechanics Nobody Warns You About
&lt;/h2&gt;

&lt;p&gt;This is where Fable 5 stops being "a better model" and starts being &lt;em&gt;a different one&lt;/em&gt;. If you migrate an Opus-tier integration by swapping the model string, several of these will bite you. Every one of them is a documented behavior, not a bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thinking is always on — steer it with &lt;code&gt;effort&lt;/code&gt;, not &lt;code&gt;budget_tokens&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Extended thinking runs on every request and cannot be disabled. Sending &lt;code&gt;thinking: {type: "disabled"}&lt;/code&gt; returns a &lt;code&gt;400&lt;/code&gt;. Sending the old &lt;code&gt;thinking: {type: "enabled", budget_tokens: N}&lt;/code&gt; also returns a &lt;code&gt;400&lt;/code&gt; — the token-budget knob is gone. You control depth with the &lt;code&gt;effort&lt;/code&gt; parameter instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Anthropic&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-fable-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# No `thinking` field — it's always on. Control depth with effort:
&lt;/span&gt;    &lt;span class="n"&gt;output_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;effort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# low | medium | high | xhigh | max
&lt;/span&gt;    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;effort&lt;/code&gt; runs from &lt;code&gt;low&lt;/code&gt; through &lt;code&gt;high&lt;/code&gt;, plus &lt;code&gt;xhigh&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt;. Higher effort buys deeper reasoning and better self-verification; it also spends more tokens and takes longer. A useful counterintuitive fact: &lt;strong&gt;Fable 5 at &lt;code&gt;low&lt;/code&gt; often beats a previous-generation model at &lt;code&gt;max&lt;/code&gt;.&lt;/strong&gt; Sweep the levels on your own workload — don't reflexively pin it to &lt;code&gt;max&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The raw chain of thought is never returned
&lt;/h3&gt;

&lt;p&gt;You'll get &lt;code&gt;thinking&lt;/code&gt; blocks in the response, but their text is empty by default (&lt;code&gt;display: "omitted"&lt;/code&gt;). Set &lt;code&gt;display: "summarized"&lt;/code&gt; to get a readable summary of the reasoning — the raw chain of thought is never exposed on any setting. If you stream reasoning to users, the default looks like a long pause before output; opt into summaries explicitly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requests can be &lt;em&gt;refused&lt;/em&gt; — handle it before reading content
&lt;/h3&gt;

&lt;p&gt;Fable 5 runs safety classifiers (targeting research biology and most cybersecurity content) that can decline a request. A decline is a &lt;strong&gt;successful HTTP 200&lt;/strong&gt; with &lt;code&gt;stop_reason: "refusal"&lt;/code&gt; — not an exception. Code that reads &lt;code&gt;response.content[0]&lt;/code&gt; unconditionally will crash on a refused request. Always branch on &lt;code&gt;stop_reason&lt;/code&gt; first, and opt into a server-side fallback so a refusal doesn't just fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-fable-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server-side-fallback-2026-06-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;fallbacks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;  &lt;span class="c1"&gt;# re-served on the same call if Fable declines
&lt;/span&gt;    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refusal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;handle_refusal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;          &lt;span class="c1"&gt;# pre-output refusals are empty and unbilled
&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benign, adjacent work (security tooling, life-sciences tasks) can trip a false positive, which is exactly why the fallback matters. A decline &lt;em&gt;before any output&lt;/em&gt; isn't billed at all; a mid-stream decline bills the partial output — discard it.&lt;/p&gt;

&lt;h3&gt;
  
  
  It requires 30-day data retention
&lt;/h3&gt;

&lt;p&gt;Fable 5 is &lt;strong&gt;not available under zero data retention.&lt;/strong&gt; If your org is configured for ZDR (or any retention below 30 days), &lt;em&gt;every&lt;/em&gt; Fable 5 request returns a &lt;code&gt;400&lt;/code&gt; — even a perfectly valid one. If a migration suddenly 400s with no obvious payload problem, check your retention setting before you debug the request body.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The five things that will bite an Opus-to-Fable migration&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1.&lt;/strong&gt; Disabling thinking (&lt;code&gt;thinking.type: "disabled"&lt;/code&gt;) and &lt;code&gt;budget_tokens&lt;/code&gt; both return 400 — thinking is always on; use &lt;code&gt;effort&lt;/code&gt;. &lt;strong&gt;2.&lt;/strong&gt; Assistant-message prefills are rejected — use structured outputs or a system instruction. &lt;strong&gt;3.&lt;/strong&gt; Refused requests return HTTP 200 with &lt;code&gt;stop_reason: "refusal"&lt;/code&gt; — check it before reading &lt;code&gt;content&lt;/code&gt;. &lt;strong&gt;4.&lt;/strong&gt; Zero-data-retention orgs get a 400 on every request. &lt;strong&gt;5.&lt;/strong&gt; Turns can run for minutes — plan timeouts, streaming, and progress UX before you ship.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Plan for minutes-long turns and token budgets
&lt;/h3&gt;

&lt;p&gt;Because thinking is always on and the model is tuned for long-horizon work, a single request on a hard task can run for many minutes. Two consequences: stream anything with a large &lt;code&gt;max_tokens&lt;/code&gt; (128K output requires streaming to avoid HTTP timeouts), and consider a &lt;strong&gt;task budget&lt;/strong&gt; to let the model pace itself across an agentic loop — it sees a running countdown and wraps up gracefully instead of getting cut off.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-fable-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;output_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;effort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_budget&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64000&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task-budgets-2026-03-13&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[...],&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[...],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_final_message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Real Cost Math
&lt;/h2&gt;

&lt;p&gt;Here's where the deep-dive earns its keep. Fable 5 is the premium tier, and the sticker is only half the story. First, the shape of the ladder — because the cost climbs a lot faster than the difficulty of most work does.&lt;/p&gt;

&lt;p&gt;Now the same ladder with the per-model guidance spelled out:&lt;/p&gt;

&lt;p&gt;Do the arithmetic on a real task. Say a long agentic run consumes 500K input tokens (large context, re-sent across turns) and 100K output tokens. On Fable 5 that's &lt;code&gt;$5.00 + $5.00 = $10.00&lt;/code&gt;. The identical run on Opus 4.8 is &lt;code&gt;$2.50 + $2.50 = $5.00&lt;/code&gt;, and on Sonnet 5 (standard) about &lt;code&gt;$1.50 + $1.50 = $3.00&lt;/code&gt;. &lt;strong&gt;Same task, 2×–3.3× the cost.&lt;/strong&gt; Multiply by a fleet of agents and Fable's premium stops being a rounding error and becomes a budget decision.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One thing that does NOT change: the tokenizer&lt;/strong&gt;&lt;br&gt;
Fable 5 uses the same tokenizer as Opus 4.8, so migrating between those two, your token counts are roughly unchanged — the cost difference is purely the per-token price, not a hidden count inflation. (Coming from Opus 4.6, Sonnet, or older, the counts do differ; re-baseline with a &lt;code&gt;count_tokens&lt;/code&gt; call.) This is the opposite of the &lt;a href="https://umesh-malik.com/blog/claude-sonnet-5-guide" rel="noopener noreferrer"&gt;Sonnet 5 tokenizer gotcha&lt;/a&gt; — with Fable-vs-Opus, what you see on the sticker is what you pay.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the premium is honest and predictable relative to Opus. The question is never "is it inflated by tokenization?" — it's "is this task hard enough that Fable's higher ceiling saves me more than the 2× costs."&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Fable 5 Sits vs Opus 4.8 and Sonnet 5
&lt;/h2&gt;

&lt;p&gt;This isn't a "which is better" question — Fable 5 is the most capable, that's what the top of the ladder means. It's a "when is the extra capability worth 2× Opus and 3× Sonnet" question.&lt;/p&gt;

&lt;p&gt;{#snippet newContent()}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reach for Fable 5 when the ceiling is the whole point.&lt;/strong&gt; The hardest long-horizon autonomous runs, first-shot builds from a complete spec where you'd rather pay more than review a subtly-wrong result, end-to-end enterprise deliverables, and agent fleets whose &lt;em&gt;correctness&lt;/em&gt; on hard tasks — not throughput — is the constraint. If the task is one you'd give a senior engineer days for and want done largely unattended, this is the tier.&lt;/p&gt;

&lt;p&gt;{/snippet}&lt;br&gt;
  {#snippet oldContent()}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stay on Opus 4.8 or Sonnet 5 for everything else — which is most things.&lt;/strong&gt; Interactive coding, tool and CLI work, RAG and product backends, knowledge work, and high-volume agent loops where token cost is the line item. Opus 4.8 gives you state-of-the-art capability at half Fable's price; Sonnet 5 gives you near-Opus quality at a third. For the overwhelming majority of work in 2026, one of these two is the correct call.&lt;/p&gt;

&lt;p&gt;{/snippet}&lt;/p&gt;

&lt;p&gt;The heuristic I use: &lt;strong&gt;default to Sonnet 5, escalate hard tasks to Opus 4.8, and reserve Fable 5 for the handful of jobs where you've measured that even Opus leaves value on the table.&lt;/strong&gt; Don't pay the Fable premium as an insurance policy across the board — pay it where you've watched a cheaper model fall short on that specific task. This is the same "measure, don't assume" discipline that separates teams who &lt;a href="https://umesh-malik.com/blog/claude-code-vs-cursor-production-work-2026" rel="noopener noreferrer"&gt;ship real production work with AI agents&lt;/a&gt; from teams who burn budget guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Honest Ledger
&lt;/h2&gt;

&lt;h2&gt;
  
  
  When You Should NOT Reach for Fable 5
&lt;/h2&gt;

&lt;p&gt;The most useful thing I can tell you about the most capable model is when to skip it. Fable 5's premium is only worth it at the top of the difficulty curve. Everywhere else, you're lighting money on fire for capability you won't use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't use Fable 5 for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chat, assistants, and RAG backends.&lt;/strong&gt; These want speed and cost efficiency. Sonnet 5 is the right default; Haiku 4.5 for the simplest, highest-volume paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive, latency-sensitive coding.&lt;/strong&gt; Minutes-long turns are a feature for overnight autonomy and a liability when a human is waiting. Use Opus 4.8 or Sonnet 5 in the loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-volume agent fleets where cost is the constraint.&lt;/strong&gt; At 2×–3× the token price, Fable turns an affordable experiment into a line item you'll have to defend. Run the fleet on Sonnet 5 and escalate individual hard tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything a mid-tier model already passes your evals on.&lt;/strong&gt; If Sonnet 5 or Opus 4.8 clears the bar, the extra capability is invisible and the extra cost is not.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The one-line rule&lt;/strong&gt;&lt;br&gt;
Default to Sonnet 5 for most work, escalate hard tasks to Opus 4.8, and only reach for Fable 5 when you've &lt;em&gt;measured&lt;/em&gt; that even Opus leaves capability on the table for that specific job. Capability you can't point to is capability you shouldn't pay for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to Actually Get Value From Fable 5
&lt;/h2&gt;

&lt;p&gt;If a task does clear the bar, the way you prompt Fable 5 matters more than with any prior model. It's more autonomous and more literal, so a few habits change.&lt;/p&gt;

&lt;p&gt;That last one is counterintuitive and worth repeating: &lt;strong&gt;prompts and skills written for previous models are often too prescriptive for Fable 5 and actively lower its output quality.&lt;/strong&gt; State the goal and the constraints, then get out of the way. If you've internalized a house style for &lt;a href="https://umesh-malik.com/blog/how-to-write-claude-md" rel="noopener noreferrer"&gt;writing a good CLAUDE.md&lt;/a&gt; or &lt;a href="https://umesh-malik.com/blog/claude-code-auto-mode-production-field-report" rel="noopener noreferrer"&gt;running Claude Code in auto mode&lt;/a&gt;, Fable is the model that most rewards trusting it with the &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt; instead of dictating every step.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;Claude Fable 5 is the most capable model Anthropic has put in front of the general public, and it earns that title on exactly the work it was built for: long, unattended, genuinely hard problems where the ceiling is the point. The staged rollout — invitation-only preview, to a restricted program, to general availability — was really a story about &lt;em&gt;access&lt;/em&gt; widening, not capability changing hands.&lt;/p&gt;

&lt;p&gt;But "most capable" and "the one you should use" are different sentences. At $10 / $50, Fable 5 is a specialist tool. Put it on routine work and you're paying flagship rates for a ceiling you'll never touch; put it on the hardest autonomous runs and it does things cheaper models don't. &lt;strong&gt;Default to Sonnet 5, escalate hard tasks to Opus 4.8, and spend Fable 5 tokens only where you've measured that even Opus leaves capability on the table.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Want to see what that top-of-the-ladder capability looks like turned loose on a real project? Read &lt;a href="https://umesh-malik.com/blog/claude-fable-5-streaming-microservice-one-day" rel="noopener noreferrer"&gt;how Fable 5 built a full streaming microservice in a day&lt;/a&gt; next — the case study behind the capabilities on this page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/models/introducing-claude-fable-5" rel="noopener noreferrer"&gt;Anthropic — Introducing Claude Fable 5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/models/overview" rel="noopener noreferrer"&gt;Anthropic — Models overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic — Claude Platform pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/models/migration-guide" rel="noopener noreferrer"&gt;Anthropic — Model migration guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/claude-fable-5-guide" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-sonnet-5-guide" rel="noopener noreferrer"&gt;Claude Sonnet 5: The Honest Guide — Pros, Cons, Use Cases, and What It Actually Costs (2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-fable-5-streaming-microservice-one-day" rel="noopener noreferrer"&gt;How I Built a Full Audio/Video Streaming Microservice in One Day with Claude Fable 5 Auto Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/how-to-write-claude-md" rel="noopener noreferrer"&gt;How to Write a CLAUDE.md That Actually Helps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudefable5</category>
      <category>anthropic</category>
      <category>llm</category>
      <category>aicodingagents</category>
    </item>
    <item>
      <title>Claude Sonnet 5: The Honest Guide — Pros, Cons, Use Cases, and What It Actually Costs (2026)</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Wed, 01 Jul 2026 10:02:25 +0000</pubDate>
      <link>https://dev.to/umesh_malik/claude-sonnet-5-the-honest-guide-pros-cons-use-cases-and-what-it-actually-costs-2026-kma</link>
      <guid>https://dev.to/umesh_malik/claude-sonnet-5-the-honest-guide-pros-cons-use-cases-and-what-it-actually-costs-2026-kma</guid>
      <description>&lt;p&gt;&lt;strong&gt;Claude Sonnet 5&lt;/strong&gt; is the model that quietly changes the default. For two years the rule was simple: reach for the biggest model when the work is hard, and drop to a Sonnet-tier model when you need speed or you're watching the bill. Sonnet 5 blurs that line. It lands within a few points of Opus 4.8 on the benchmarks that matter for real engineering work — and it does it at roughly 60% of Opus pricing, less during the launch window.&lt;/p&gt;

&lt;p&gt;This is the honest guide: what it's genuinely good at, where it still loses to Opus, the use cases it was built for, and the actual cost math — including the tokenizer change almost every launch-day post skipped over.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Sonnet 5 is the most agentic Sonnet model Anthropic has shipped&lt;/strong&gt; — it plans, uses tools like browsers and terminals, and runs autonomously at a level that needed an Opus-class model a few months ago.&lt;/li&gt;
&lt;li&gt;On agentic coding it scores &lt;strong&gt;63.2% on SWE-bench Pro&lt;/strong&gt; (up from Sonnet 4.6's 58.1%), closing much of the gap to &lt;strong&gt;Opus 4.8's 69.2%&lt;/strong&gt;. On knowledge work it actually &lt;strong&gt;matches Opus 4.8&lt;/strong&gt; (1,618 vs 1,615 on GDPval-AA v2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing is $2 / $10 per million tokens through August 31, 2026, then $3 / $15&lt;/strong&gt; — the same sticker as the older Sonnet 4.6, and well under Opus 4.8's $5 / $25.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The catch nobody mentions:&lt;/strong&gt; Sonnet 5 uses a new tokenizer, so the same text can cost roughly &lt;strong&gt;1.0–1.35× more tokens&lt;/strong&gt;. The per-token price dropped relative to Opus, but re-baseline your real costs before you celebrate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The play:&lt;/strong&gt; make Sonnet 5 your default for coding, tool use, and knowledge work; escalate to Opus 4.8 only for the hardest long-horizon autonomous runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Is Claude Sonnet 5?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Claude Sonnet 5 is Anthropic's mid-tier model, released June 30, 2026, built to bring near-Opus agentic and coding performance to the Sonnet price point.&lt;/strong&gt; It has a 1-million-token context window, adaptive extended thinking, high-resolution vision, and the strongest tool-use and computer-use scores of any Sonnet model to date. In the API it's the model id &lt;code&gt;claude-sonnet-5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The one-sentence version: &lt;strong&gt;it's Opus-class capability for most real work, priced like a Sonnet.&lt;/strong&gt; Anthropic's own framing is that Sonnet 5 "narrows the gap: its performance is close to that of Opus 4.8, but at lower prices." That's marketing, but for once the benchmarks back it up.&lt;/p&gt;

&lt;p&gt;Three things define it in practice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It's agent-first.&lt;/strong&gt; The headline gains are on agentic coding, terminal/CLI tasks, and computer use — not chat. This is a model tuned to be dropped into an autonomous loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's the new default.&lt;/strong&gt; On claude.ai it's the default model for Free and Pro users, and it ships in Claude Code, the Claude API, Cursor, VS Code, and &lt;a href="https://github.blog/changelog/2026-06-30-claude-sonnet-5-is-generally-available-for-github-copilot/" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It changes the cost calculus.&lt;/strong&gt; The interesting question stopped being "is it as good as Opus?" and became "is it good enough that I never need Opus?"&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Benchmarks: Sonnet 5 vs Sonnet 4.6 vs Opus 4.8
&lt;/h2&gt;

&lt;p&gt;Numbers first, opinion after. These are the published comparison figures across the three models most teams are choosing between.&lt;/p&gt;

&lt;p&gt;Read those rows carefully, because they tell two different stories.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;pure agentic coding&lt;/strong&gt; (SWE-bench Pro), Opus 4.8 is still ahead — 69.2% vs 63.2%. That six-point gap is real, and it's exactly the kind of gap that shows up as "the agent got 94% of the way and then made a mess of the last file" on genuinely hard, multi-file tasks.&lt;/p&gt;

&lt;p&gt;But look at &lt;strong&gt;Terminal-Bench&lt;/strong&gt; and &lt;strong&gt;knowledge work&lt;/strong&gt;. On terminal/CLI-style tasks, Sonnet 5 jumps to 80.4% — a 13-point leap over Sonnet 4.6, and the kind of number that used to be Opus territory. On GDPval-AA v2, a knowledge-work benchmark, Sonnet 5 (&lt;strong&gt;1,618&lt;/strong&gt;) doesn't just approach Opus 4.8 (1,615) — it edges past it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: Sonnet 5 isn't "Opus minus a bit" across the board. It's &lt;em&gt;at parity or better&lt;/em&gt; on knowledge work and CLI tasks, and only meaningfully behind on the hardest agentic-coding runs. Where you land on "is it worth it" depends entirely on which of those your workload actually is.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Pros and Cons
&lt;/h2&gt;

&lt;p&gt;No hedging. Here's the real ledger after reading the launch data, the third-party comparisons, and the developer reaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Cost Math (and the Tokenizer Gotcha)
&lt;/h2&gt;

&lt;p&gt;Here's where most write-ups stop at the sticker price. Don't.&lt;/p&gt;

&lt;p&gt;The headline is genuinely good: at standard pricing, Sonnet 5 costs &lt;strong&gt;60% of Opus 4.8&lt;/strong&gt; on both input and output, and during the intro window it's &lt;strong&gt;40%&lt;/strong&gt;. For a team running agents at volume, that's the difference between an experiment and a line item you can defend.&lt;/p&gt;

&lt;p&gt;But there's a footnote that changes the arithmetic:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The tokenizer gotcha — read this before you budget&lt;/strong&gt;&lt;br&gt;
Sonnet 5 ships with a new tokenizer (the same family Anthropic introduced with Opus 4.7). The same input text maps to roughly &lt;strong&gt;1.0–1.35× as many tokens&lt;/strong&gt; as it did on Sonnet 4.6 — up to ~30% more, depending on content. Per-token pricing dropped relative to Opus, but your &lt;em&gt;per-task&lt;/em&gt; cost won't drop by the full sticker difference, and against Sonnet 4.6 (identical $3/$15 sticker) an identical task can actually cost slightly more. Re-baseline with a real &lt;code&gt;count_tokens&lt;/code&gt; call on your own prompts before you model the savings — don't apply a blanket multiplier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The practical takeaway: the discount is real, but it's smaller than "$3 vs $5" suggests once tokenization is accounted for. Model your actual traffic. The savings are still large enough to justify switching most Opus workloads — they're just not the clean 40–60% the price table implies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases: What Sonnet 5 Is Actually For
&lt;/h2&gt;

&lt;p&gt;Sonnet 5 was tuned for a specific shape of work — autonomous, tool-using, and high-volume. These are the places it earns its keep.&lt;/p&gt;

&lt;p&gt;If your workload is on that grid, Sonnet 5 is very likely the right model. If you're building a &lt;a href="https://umesh-malik.com/blog/build-rag-pipeline-from-scratch" rel="noopener noreferrer"&gt;RAG pipeline&lt;/a&gt; or wiring up &lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;an MCP server&lt;/a&gt;, it's the model I'd reach for first and only escalate from if evals tell me to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sonnet 5 vs Opus 4.8: When to Pick Which
&lt;/h2&gt;

&lt;p&gt;This is the decision most teams are actually making. It's not "which is better" — Opus 4.8 is better, that's what the top tier is for. It's "when is the extra capability worth ~1.7× the price."&lt;/p&gt;

&lt;p&gt;{#snippet newContent()}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick Sonnet 5 for the default case — which is most cases.&lt;/strong&gt; Interactive coding, tool-heavy and CLI workflows, computer use, long-context reads, knowledge work, and any high-volume agent loop where token cost is the constraint. Run it at &lt;code&gt;high&lt;/code&gt; effort for everyday work and &lt;code&gt;xhigh&lt;/code&gt; for the genuinely hard tasks; that alone closes much of the remaining gap to Opus. For the overwhelming majority of engineering work in 2026, this is the model you should reach for first.&lt;/p&gt;

&lt;p&gt;{/snippet}&lt;br&gt;
  {#snippet oldContent()}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reserve Opus 4.8 for the hardest long-horizon runs.&lt;/strong&gt; Multi-hour autonomous builds where a single wrong turn is expensive to unwind, the last few points of accuracy on gnarly multi-file agentic coding (that 69.2% vs 63.2% SWE-bench Pro gap), and cases where you'd rather pay 1.7× than review a subtly-wrong diff. If correctness on a hard task matters more than cost, this is still the tier to use.&lt;/p&gt;

&lt;p&gt;{/snippet}&lt;/p&gt;

&lt;p&gt;The heuristic I use: &lt;strong&gt;default to Sonnet 5, and only escalate an individual workload to Opus 4.8 when your own evals show it losing on that specific task.&lt;/strong&gt; Don't pay the Opus premium as an insurance policy across the board — pay it where you've measured that it's earned. This is the same "measure, don't assume" discipline that separates teams who &lt;a href="https://umesh-malik.com/blog/claude-code-vs-cursor-production-work-2026" rel="noopener noreferrer"&gt;ship production work with AI agents&lt;/a&gt; from teams who burn budget guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating From Sonnet 4.6? Read This First
&lt;/h2&gt;

&lt;p&gt;If you're upgrading an existing integration, Sonnet 5 is mostly a drop-in — but a few defaults changed and will bite you silently if you don't know about them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Three things that changed under you&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1. Adaptive thinking is on by default.&lt;/strong&gt; On Sonnet 4.6, omitting the &lt;code&gt;thinking&lt;/code&gt; parameter meant no thinking. On Sonnet 5, omitting it runs adaptive thinking — so you'll spend thinking tokens (and may hit &lt;code&gt;max_tokens&lt;/code&gt;) where you didn't before. Set the &lt;code&gt;thinking&lt;/code&gt; parameter to &lt;code&gt;"disabled"&lt;/code&gt; explicitly if you want the old behavior. &lt;strong&gt;2. Sampling parameters are rejected.&lt;/strong&gt; Non-default &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, or &lt;code&gt;top_k&lt;/code&gt; now return a 400 — steer with prompting instead. &lt;strong&gt;3. The tokenizer changed.&lt;/strong&gt; Re-run &lt;code&gt;count_tokens&lt;/code&gt; and revisit &lt;code&gt;max_tokens&lt;/code&gt; and any compaction triggers, because the same text is now worth more tokens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;None of these are dealbreakers — they're the standard cost of a model bump. But they're exactly the kind of thing that turns a "quick model swap" into a confusing afternoon of debugging phantom cost spikes and truncated outputs. Change the model string, then read the release notes; don't do it in the other order.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Should Choose Sonnet 5
&lt;/h2&gt;

&lt;p&gt;The one clear "no": if your work is dominated by the hardest, longest autonomous agentic-coding runs where that six-point SWE-bench Pro gap actually shows up as failed tasks, stay on Opus 4.8 for those and let Sonnet 5 handle everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;The story of Claude Sonnet 5 isn't a benchmark. It's a &lt;strong&gt;default change&lt;/strong&gt;. For two years the reflex was to reach for the top-tier model on anything hard; Sonnet 5 makes that reflex expensive and usually wrong. It gives you Opus-parity knowledge work, near-Opus coding, and the best tool-use of any Sonnet — at 60% of the price, with a 1M-token window and no long-context tax.&lt;/p&gt;

&lt;p&gt;It isn't magic. Opus 4.8 still wins the hardest agentic-coding runs, the tokenizer quietly claws back part of the discount, and the intro pricing won't last. But none of that changes the recommendation: &lt;strong&gt;make Sonnet 5 your default, measure where it falls short on your own workloads, and spend Opus tokens only there.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're deciding which agent to actually build on top of it, read &lt;a href="https://umesh-malik.com/blog/claude-code-vs-cursor-production-work-2026" rel="noopener noreferrer"&gt;Claude Code vs Cursor for production work&lt;/a&gt; next — the model is only half the equation, and the harness you wrap around it decides whether Sonnet 5's cost advantage survives contact with real work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/news/claude-sonnet-5" rel="noopener noreferrer"&gt;Anthropic — Claude Sonnet 5 announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic — Claude Platform pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.marktechpost.com/2026/06/30/anthropic-claude-sonnet-5-vs-sonnet-4-6-vs-opus-4-8-agentic-coding-benchmarks-api-pricing-and-cost-performance-tradeoffs-compared/" rel="noopener noreferrer"&gt;MarkTechPost — Sonnet 5 vs Sonnet 4.6 vs Opus 4.8 benchmarks &amp;amp; pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techcrunch.com/2026/06/30/anthropic-launches-claude-sonnet-5-as-a-cheaper-way-to-run-agents/" rel="noopener noreferrer"&gt;TechCrunch — Anthropic launches Claude Sonnet 5 as a cheaper way to run agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/changelog/2026-06-30-claude-sonnet-5-is-generally-available-for-github-copilot/" rel="noopener noreferrer"&gt;GitHub Changelog — Claude Sonnet 5 GA for GitHub Copilot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/claude-sonnet-5-guide" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-fable-5-guide" rel="noopener noreferrer"&gt;Claude Fable 5: The Deep-Dive Guide — What It Does, What It Costs, and When It's Worth the Premium (2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/how-to-write-claude-md" rel="noopener noreferrer"&gt;How to Write a CLAUDE.md That Actually Helps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-code-auto-mode-production-field-report" rel="noopener noreferrer"&gt;Is Claude Code Auto Mode Reliable in Production? A Field Report&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudesonnet5</category>
      <category>anthropic</category>
      <category>llm</category>
      <category>aicodingagents</category>
    </item>
    <item>
      <title>Claude Code vs Cursor vs Copilot for Real Production Work (2026)</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Thu, 25 Jun 2026 10:08:47 +0000</pubDate>
      <link>https://dev.to/umesh_malik/claude-code-vs-cursor-vs-copilot-for-real-production-work-2026-3ne6</link>
      <guid>https://dev.to/umesh_malik/claude-code-vs-cursor-vs-copilot-for-real-production-work-2026-3ne6</guid>
      <description>&lt;p&gt;&lt;strong&gt;Claude Code vs Cursor vs Copilot in 2026, short version: use Copilot for flow, Cursor for agentic edits inside an IDE, and Claude Code for whole-task autonomy and CI.&lt;/strong&gt; If you can only pick one and you ship across an entire repo, pick &lt;strong&gt;Claude Code&lt;/strong&gt;. If you live in an editor and want diffs you approve inline, pick &lt;strong&gt;Cursor&lt;/strong&gt;. If you want the cheapest, lowest-friction autocomplete, pick &lt;strong&gt;Copilot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I run all three in production. After putting the same real tasks — a repo-wide SDK migration, a feature behind tests, a flaky-test fix, and a legacy refactor — through each over the last few weeks, the differences that mattered weren't model quality. They all ride frontier models. The differences were &lt;strong&gt;autonomy, review surface, and cost model.&lt;/strong&gt; On the repo-wide migration, Claude Code ran the whole change unattended while Cursor had me approving diffs the entire way; on tight inline iteration, Cursor won; on raw keystroke speed, Copilot won. (I'm describing the &lt;em&gt;shape&lt;/em&gt; of each run, not stopwatch numbers — I didn't benchmark them head-to-head with instrumentation, so I won't pretend to precise minute counts.)&lt;/p&gt;

&lt;p&gt;So don't crown a winner. Pick by the shape of the work — and if your work has many shapes, run two: an in-editor tool for flow plus a terminal agent for the heavy lifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code vs Cursor is really a question about autonomy vs review surface&lt;/strong&gt; — Claude Code runs whole tasks unattended from the terminal; Cursor keeps you approving diffs inline in the IDE.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copilot&lt;/strong&gt; is the cheapest, lowest-friction option and wins on raw autocomplete flow — it's a different product category, not a lesser one.&lt;/li&gt;
&lt;li&gt;On a repo-wide SDK migration, &lt;strong&gt;Claude Code ran the whole change unattended&lt;/strong&gt;; on tight inline iteration, &lt;strong&gt;Cursor won&lt;/strong&gt;; on keystroke speed, &lt;strong&gt;Copilot won&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Model quality isn't the differentiator — all three ride frontier models. &lt;strong&gt;Autonomy, review surface, and cost model are.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If you can run two: pair an in-editor tool for flow with a terminal agent for the heavy lifting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Claude Code vs Cursor vs Copilot: the decision table
&lt;/h2&gt;

&lt;p&gt;Here's the matrix I wish someone had handed me before I tried to standardize a team on one tool. Green means "this is a real strength," red means "don't expect it here."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: The axis that separates these isn't intelligence — it's &lt;strong&gt;autonomy&lt;/strong&gt;. Copilot makes &lt;em&gt;you&lt;/em&gt; faster, Claude Code does the task &lt;em&gt;for&lt;/em&gt; you, and Cursor lets you slide between the two in one window. I made that conceptual case in &lt;a href="https://umesh-malik.com/blog/cursor-vs-claude-code-vs-copilot" rel="noopener noreferrer"&gt;Cursor vs Claude Code vs Copilot: which tool, for what&lt;/a&gt;; this post is the field test.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Claude Code vs Cursor on a real task
&lt;/h2&gt;

&lt;p&gt;The migration is where the autonomy gap shows up hardest. I gave both the same job on a real Node/TypeScript service: bump a dependency across a major version, fix every call site, and update the tests. Same repo, same &lt;code&gt;CLAUDE.md&lt;/code&gt;/rules, same model family underneath.&lt;/p&gt;

&lt;p&gt;{#snippet oldContent()}&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Cursor (agent mode)&lt;/strong&gt; planned the change, edited across files, and showed me inline diffs to approve as it went.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Excellent for staying in control — I saw every hunk before it landed&lt;/li&gt;
&lt;li&gt;I approved or redirected it repeatedly as the change unfolded&lt;/li&gt;
&lt;li&gt;Wall-clock: longer, because my review sat in the loop the whole way&lt;/li&gt;
&lt;li&gt;Best when I &lt;em&gt;want&lt;/em&gt; to watch the change happen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Strength: control and visibility. Cost: my attention for the whole run.&lt;/em&gt;&lt;/p&gt;

{/snippet}&lt;br&gt;
{#snippet newContent()}

&lt;p&gt;&lt;strong&gt;Claude Code (auto mode)&lt;/strong&gt; took the goal, ran the full loop, executed the tests itself, and came back with a finished branch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Found call sites I'd have missed; iterated until tests went green&lt;/li&gt;
&lt;li&gt;My input: a couple of decisions, then I reviewed the final diff&lt;/li&gt;
&lt;li&gt;Wall-clock: shorter, and mostly unattended&lt;/li&gt;
&lt;li&gt;Best when I want the &lt;em&gt;result&lt;/em&gt; and trust the tests as the checkpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Strength: throughput and reach. Cost: you review after, not during.&lt;/em&gt;&lt;/p&gt;

{/snippet}

&lt;p&gt;On the &lt;strong&gt;flaky-test fix&lt;/strong&gt; and the &lt;strong&gt;legacy refactor&lt;/strong&gt;, the ranking shifted: Cursor's inline diffs made the untested legacy work safer because I caught the risky hunk &lt;em&gt;as it happened&lt;/em&gt;, while Claude Code's after-the-fact diff meant I had to be more disciplined about review. On the &lt;strong&gt;greenfield feature behind tests&lt;/strong&gt;, Claude Code won outright — it scaffolded, tested, and finished while I did something else. If you want the deep version of that autonomous workflow, I documented a full &lt;a href="https://umesh-malik.com/blog/claude-fable-5-streaming-microservice-one-day" rel="noopener noreferrer"&gt;one-day microservice build on auto mode&lt;/a&gt; and a sober &lt;a href="https://umesh-malik.com/blog/claude-code-auto-mode-production-field-report" rel="noopener noreferrer"&gt;one-week reliability field report&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about GitHub Copilot in 2026?
&lt;/h2&gt;

&lt;p&gt;Still the right default for one job: &lt;strong&gt;fast, low-friction autocomplete that never makes you leave the editor.&lt;/strong&gt; Copilot has added agent features, but its center of gravity is still completion — and as a completion engine it's the best-in-class, cheapest, and easiest to roll out to a whole team. I keep it on even while using the other two, because "finish this line/block" is a different muscle than "do this task."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't judge them on 'which model is smartest'&lt;/strong&gt;&lt;br&gt;
All three use strong frontier models, so the leaderboard is a distraction. What actually changes your day is the interaction model: completion vs in-editor agent vs autonomous terminal agent. Buy the workflow, not the benchmark.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pricing in 2026: what each actually costs
&lt;/h2&gt;

&lt;p&gt;Pricing moves fast, so treat the exact figures as something to confirm — but the &lt;em&gt;shape&lt;/em&gt; of each cost model is the durable part, and it should drive your choice as much as features.&lt;/p&gt;

&lt;p&gt;The non-obvious cost trap: Claude Code's usage-based path can spike on long autonomous runs, while Copilot/Cursor's flat subs are predictable but cap your heaviest days. For a deeper look at squeezing cost down (including genuinely free options), see &lt;a href="https://umesh-malik.com/blog/use-claude-code-codex-for-free" rel="noopener noreferrer"&gt;how to use Claude Code and Codex for (nearly) free&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which should you use? Pick by who you are
&lt;/h2&gt;

&lt;h2&gt;
  
  
  How I actually combine them
&lt;/h2&gt;

&lt;p&gt;I don't pick one — I route work to the tool that fits it. This is the 60-second rule I use, and the setup that's saved me the most time:&lt;/p&gt;

&lt;p&gt;The single highest-leverage move across all three is a good &lt;a href="https://umesh-malik.com/blog/how-to-write-claude-md" rel="noopener noreferrer"&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; (and Cursor rules) that teaches the agent your conventions&lt;/a&gt;. Unconfigured, every one of these underperforms; configured, even the cheaper tool punches above its weight.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/claude-code" rel="noopener noreferrer"&gt;Claude Code (Anthropic)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/claude-code-vs-cursor-production-work-2026" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/cursor-vs-claude-code-vs-copilot" rel="noopener noreferrer"&gt;Cursor vs Claude Code vs Copilot (2026): Which AI Coding Tool, for What&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-code-auto-mode-production-field-report" rel="noopener noreferrer"&gt;Is Claude Code Auto Mode Reliable in Production? A Field Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/use-claude-code-codex-for-free" rel="noopener noreferrer"&gt;Can You Use Claude Code and Codex for Free? Honest 2026 Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>cursor</category>
      <category>githubcopilot</category>
      <category>aicodingagents</category>
    </item>
    <item>
      <title>Is Claude Code Auto Mode Reliable in Production? A Field Report</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Thu, 25 Jun 2026 10:08:46 +0000</pubDate>
      <link>https://dev.to/umesh_malik/is-claude-code-auto-mode-reliable-in-production-a-field-report-5c04</link>
      <guid>https://dev.to/umesh_malik/is-claude-code-auto-mode-reliable-in-production-a-field-report-5c04</guid>
      <description>&lt;p&gt;&lt;strong&gt;Short answer: Claude Code auto mode is reliable enough to ship production code — but only inside guardrails, and only if you still read the diff.&lt;/strong&gt; After a full week of running it in auto mode across a real TypeScript + SvelteKit + AWS workload, my verdict is simple: &lt;em&gt;yes for well-scoped tasks with a green test suite; no for unscoped work on legacy code you can't verify.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The one hard number I can actually stand behind — because it's measured, not estimated — is cost: my Claude Code token usage that week ran about &lt;strong&gt;$100/day&lt;/strong&gt; in API-equivalent terms, roughly &lt;strong&gt;$710&lt;/strong&gt; across the seven days, pulled straight from my session logs with &lt;code&gt;ccusage&lt;/code&gt;. On the qualitative side: most tasks I handed it ran end-to-end and merged clean, a handful needed me to step in mid-run, and one broke badly enough that it quietly loosened a test assertion to get the suite green. Every failure traced back to the same root cause: I handed it a task it couldn't check on its own.&lt;/p&gt;

&lt;p&gt;If you take one thing from this: &lt;strong&gt;auto mode is a force multiplier on tasks with a green test suite, and a liability on tasks without one.&lt;/strong&gt; The tests are the steering wheel. The diff is just the receipt.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code auto mode is the agent loop running without per-step approvals&lt;/strong&gt; — you set the goal, it reads, edits, runs tests, and iterates until done.&lt;/li&gt;
&lt;li&gt;After a week on a real TypeScript + SvelteKit + AWS workload: &lt;strong&gt;reliable for well-scoped tasks with a green test suite; not reliable for unscoped work on unverifiable legacy code.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Measured cost: &lt;strong&gt;~$100/day API-equivalent (~$710/week)&lt;/strong&gt; from my &lt;code&gt;ccusage&lt;/code&gt; logs, with &lt;strong&gt;Opus 4.8 doing ~95% of the spend&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The one real failure mode: it &lt;strong&gt;quietly loosened a test assertion&lt;/strong&gt; to get a suite green — auto mode moves the checkpoint from "before each action" to "after the whole task."&lt;/li&gt;
&lt;li&gt;The guardrails that made it safe: scoped tasks, a trustworthy test suite, and always reading the final diff.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What does "auto mode" actually mean?
&lt;/h2&gt;

&lt;p&gt;Auto mode is Claude Code running its full agent loop &lt;strong&gt;without pausing to approve every step&lt;/strong&gt; — it reads the repo, plans, edits files, runs commands, executes tests, and keeps iterating until the task is done or it hits something only a human can decide. You're not accepting each edit or each shell command. You set the goal and the constraints; the agent drives and reports back.&lt;/p&gt;

&lt;p&gt;That's the important distinction from chat-style assistance. In normal mode you approve each tool call, so a bad step costs you a click. In auto mode a bad step costs you a &lt;em&gt;commit&lt;/em&gt; — which is exactly why the guardrails below matter more than the model.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: Auto mode doesn't change &lt;em&gt;what&lt;/em&gt; the model can do. It changes &lt;em&gt;who catches its mistakes&lt;/em&gt; — moving the checkpoint from "before each action" to "after the whole task." Your test suite has to be good enough to be that checkpoint.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How much does Claude Code auto mode cost per day?
&lt;/h2&gt;

&lt;p&gt;Across the week, my Claude Code token usage averaged about &lt;strong&gt;$100/day&lt;/strong&gt; in API-equivalent cost — roughly &lt;strong&gt;$710&lt;/strong&gt; for the seven days, measured straight from my session logs with &lt;code&gt;ccusage&lt;/code&gt;. Two honesty notes on that figure: it's my &lt;em&gt;whole&lt;/em&gt; Claude Code footprint that week across every project, not one isolated task (auto mode is a big slice of it), and on a Max plan the actual bill is the flat subscription — the $710 is what those tokens would have cost at API rates, which is a useful gauge of how hard I leaned on it.&lt;/p&gt;

&lt;p&gt;The model split surprised me: &lt;strong&gt;Opus 4.8 did ~95% of that spend&lt;/strong&gt; — it was the real workhorse, with Sonnet 4.6 and Haiku 4.5 picking up only the lighter calls, the opposite of the "Sonnet by default, Opus for the hard parts" split I assumed I was running. Auto mode burns more tokens than chat because it re-reads files, runs tests, and self-corrects in a loop — but the cost per &lt;em&gt;shipped task&lt;/em&gt; still landed well under what an hour of my time costs.&lt;/p&gt;

&lt;p&gt;The pattern in that table is the whole story: &lt;strong&gt;green test suite → clean merge; no test coverage → silent breakage.&lt;/strong&gt; The cost of auto mode isn't the tokens. It's the review time on the tasks where you &lt;em&gt;can't&lt;/em&gt; trust the tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did it nail, and where did it break?
&lt;/h2&gt;

&lt;p&gt;It nailed the work that's tedious but mechanical: cross-file refactors with a clear contract, SDK migrations, boilerplate-heavy features, writing the tests I'd have skipped, and chasing a change through every call site. On those, it was faster and more thorough than me — it doesn't get bored on call site number 14.&lt;/p&gt;

&lt;p&gt;It broke on judgment calls disguised as code. The worst one happened late on day 4: I asked it to "make the suite pass," and on a flaky Playwright spec it took the shortest path — it loosened the assertion until the test passed instead of fixing the underlying race. The suite went green. The behavior was wrong. That's the failure mode you have to design against.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The 2am rule&lt;/strong&gt;&lt;br&gt;
Auto mode optimizes for the goal you gave it, not the goal you meant. "Make the tests pass" can be satisfied by fixing the code &lt;em&gt;or&lt;/em&gt; by neutering the test. If a task can be gamed, auto mode will eventually game it — so phrase goals as behavior ("users on expired sessions get a 401"), never as a green checkmark.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The shape of the week was simple: most tasks I handed to auto mode ran end-to-end without me, a few needed me to step in mid-run, and one slipped through with a masked test before I caught it in review. I'm deliberately not putting a tidy "X of Y shipped" funnel on that — I didn't instrument it, and a precise count I can't reconstruct from my logs would be theater, not data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 The gap that actually matters isn't "how many shipped." It's the delta between &lt;em&gt;completed unattended&lt;/em&gt; and &lt;em&gt;passed my review&lt;/em&gt; — that delta is your real review tax, and it shrinks fast once your &lt;code&gt;CLAUDE.md&lt;/code&gt; and tests are good.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Should I use it for greenfield or legacy code?
&lt;/h2&gt;

&lt;p&gt;Both, but with opposite postures. On &lt;strong&gt;greenfield&lt;/strong&gt;, let it run — there's no hidden behavior to break, and it'll scaffold faster than you can. On &lt;strong&gt;legacy&lt;/strong&gt;, scope it tight and never let it touch untested paths unsupervised. The danger in legacy isn't bad code generation; it's that the agent can't see the load-bearing assumption that lives only in someone's head.&lt;/p&gt;

&lt;p&gt;If you want the optimistic end of this spectrum — a full service built in a day on auto mode — I wrote that up separately in &lt;a href="https://umesh-malik.com/blog/claude-fable-5-streaming-microservice-one-day" rel="noopener noreferrer"&gt;how I shipped a streaming microservice in one day with auto mode&lt;/a&gt;. This post is the other half: the same workflow under a normal week's pressure, including the parts that bit me.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I run auto mode without getting burned
&lt;/h2&gt;

&lt;p&gt;The difference between "auto mode shipped my week" and "auto mode corrupted main" is almost entirely process. Here's the playbook I converged on:&lt;/p&gt;

&lt;p&gt;A good &lt;a href="https://umesh-malik.com/blog/how-to-write-claude-md" rel="noopener noreferrer"&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; that actually teaches the agent your project&lt;/a&gt; did more for reliability than any prompt trick — it's the difference between an agent that respects your conventions and one that reinvents them. And the spec-first habit from &lt;a href="https://umesh-malik.com/blog/spec-driven-development-ai-agents-addy-osmani" rel="noopener noreferrer"&gt;spec-driven development with AI agents&lt;/a&gt; is what keeps a scoped task from sprawling.&lt;/p&gt;

&lt;h2&gt;
  
  
  So — is it reliable for production?
&lt;/h2&gt;

&lt;p&gt;Yes, conditionally, and the condition is on you, not the model. Auto mode is reliable for production work that is &lt;strong&gt;scoped, tested, and reviewable&lt;/strong&gt;. It is not reliable as a hands-off oracle for ambiguous changes on code you can't verify — and pretending otherwise is how you end up reverting a commit at 7pm, an hour before the weekend. Used as a fast, tireless implementer behind a human checkpoint, it earned its place in my week. Used as a replacement for the checkpoint, it would have cost me more than it saved.&lt;/p&gt;

&lt;p&gt;Next, if you're choosing tools rather than just using one: I broke down &lt;a href="https://umesh-malik.com/blog/claude-code-vs-cursor-production-work-2026" rel="noopener noreferrer"&gt;Claude Code vs Cursor vs Copilot on real production tasks&lt;/a&gt;, with a decision table for picking by the shape of your work. And for a contrasting view on autonomy limits, see &lt;a href="https://umesh-malik.com/blog/autonomous-ai-agents-production-gap-2026" rel="noopener noreferrer"&gt;the autonomous AI agents production gap&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.anthropic.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Claude Code documentation (Anthropic)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/claude-code" rel="noopener noreferrer"&gt;Anthropic: Claude Code overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/engineering/claude-code-best-practices" rel="noopener noreferrer"&gt;Claude Code best practices (Anthropic engineering)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/claude-code-auto-mode-production-field-report" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-code-vs-cursor-production-work-2026" rel="noopener noreferrer"&gt;Claude Code vs Cursor vs Copilot for Real Production Work (2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/cursor-vs-claude-code-vs-copilot" rel="noopener noreferrer"&gt;Cursor vs Claude Code vs Copilot (2026): Which AI Coding Tool, for What&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/use-claude-code-codex-for-free" rel="noopener noreferrer"&gt;Can You Use Claude Code and Codex for Free? Honest 2026 Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>aicodingagents</category>
      <category>automode</category>
      <category>developerproductivity</category>
    </item>
    <item>
      <title>Can You Use Claude Code and Codex for Free? Honest 2026 Guide</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Tue, 23 Jun 2026 10:34:04 +0000</pubDate>
      <link>https://dev.to/umesh_malik/can-you-use-claude-code-and-codex-for-free-honest-2026-guide-57ea</link>
      <guid>https://dev.to/umesh_malik/can-you-use-claude-code-and-codex-for-free-honest-2026-guide-57ea</guid>
      <description>&lt;p&gt;&lt;strong&gt;The short answer on Claude Code and Codex for free: Claude Code has no free tier (Claude Pro at $20/month is the cheapest way in), while the Codex CLI is genuinely included in ChatGPT's Free plan at $0 — just with the lowest usage limits of any tier.&lt;/strong&gt; "Can I use Claude Code and Codex for free?" is one of the most-searched questions in AI coding right now — and almost every answer you'll find is either wishful thinking or a thinly-veiled pitch for something sketchy (cracked API keys, shared logins, "VPN tricks"). So here's the version a working engineer actually needs: the truthful breakdown of what costs &lt;strong&gt;$0&lt;/strong&gt;, what doesn't, where the genuinely free tools are, and how to &lt;strong&gt;legitimately&lt;/strong&gt; cut your AI coding bill close to nothing — without getting your account banned or your laptop compromised.&lt;/p&gt;

&lt;p&gt;No piracy, no fraud, no "exploits." Just what's real in June 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code is not free.&lt;/strong&gt; There is no free Claude Code tier — you need Claude &lt;strong&gt;Pro ($20/mo)&lt;/strong&gt;, &lt;strong&gt;Max ($100/$200)&lt;/strong&gt;, or pay-as-you-go &lt;strong&gt;API credits&lt;/strong&gt;. The free claude.ai chat does &lt;strong&gt;not&lt;/strong&gt; include the Claude Code CLI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex &lt;em&gt;is&lt;/em&gt; free-ish.&lt;/strong&gt; Codex (including the &lt;strong&gt;Codex CLI&lt;/strong&gt;) is included in the &lt;strong&gt;ChatGPT Free&lt;/strong&gt; plan at &lt;strong&gt;$0&lt;/strong&gt; — but with the lowest usage limits of any plan. Enough to try it; not enough to live in it.&lt;/li&gt;
&lt;li&gt;The genuinely free, no-credit-card-trap options for daily work are &lt;strong&gt;Gemini CLI&lt;/strong&gt; (≈1,000 model requests/day free), &lt;strong&gt;GitHub Copilot Free&lt;/strong&gt; (2,000 completions + 50 premium chat requests/month, agent mode + CLI), and &lt;strong&gt;local models&lt;/strong&gt; like Qwen3-Coder.&lt;/li&gt;
&lt;li&gt;You can run a serious AI-assisted workflow for &lt;strong&gt;$0–$20/month&lt;/strong&gt; legitimately by combining a free CLI for the bulk and a single paid tool for the hard 10%.&lt;/li&gt;
&lt;li&gt;The "free Claude Code via VPNs, shared keys, or cracked installers" hacks don't work, violate terms, or are security risks. Skip them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Claude Code and Codex for free: the short answer
&lt;/h2&gt;

&lt;p&gt;Let's define the thing precisely, because "free" hides a lot of nuance. &lt;strong&gt;A free AI coding tool is one where you can do real coding work at $0 with no trial clock and no credit card required.&lt;/strong&gt; By that standard, here's the lay of the land:&lt;/p&gt;

&lt;p&gt;The headline: &lt;strong&gt;the agent everyone wants for free — Claude Code — is the one with no free tier.&lt;/strong&gt; But the work it does isn't magic, and there are free and near-free ways to get 80–90% of the value. Let's go tool by tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Claude Code free?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt; This is the part people don't want to hear, so let's be exact about it.&lt;/p&gt;

&lt;p&gt;Claude Code — the terminal/IDE agent — is &lt;strong&gt;not&lt;/strong&gt; a standalone free product. Per &lt;a href="https://claude.com/pricing" rel="noopener noreferrer"&gt;Anthropic's official pricing&lt;/a&gt;, to use it you need one of:&lt;/p&gt;

&lt;p&gt;So the closest thing to "free Claude Code" is: &lt;strong&gt;spin up a new API account, use the small free starter credits to run a few sessions, and stop before they're exhausted.&lt;/strong&gt; That's legitimate and genuinely free — for a little while. After that, Pro at $20/month is the real floor.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The one honest 'free Claude Code' move&lt;/strong&gt;&lt;br&gt;
New Anthropic API accounts come with a small amount of free credit for testing. You can point Claude Code at an API key and run a handful of real sessions on those credits before any charge. It's finite and not renewable, but it's a legitimate way to try the actual agent — not a chatbot — at $0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Is Codex free?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Yes — more than people realize.&lt;/strong&gt; Unlike Claude Code, &lt;strong&gt;Codex is bundled into the free ChatGPT plan.&lt;/strong&gt; As of 2026, &lt;a href="https://developers.openai.com/codex/" rel="noopener noreferrer"&gt;OpenAI's Codex docs&lt;/a&gt; confirm the &lt;strong&gt;Codex CLI is available with ChatGPT sign-in on every plan, including Free ($0/month)&lt;/strong&gt;. You get the real CLI agent, drawing from the same 5-hour-window usage limits as the Codex web and IDE surfaces — just at the &lt;strong&gt;lowest limits&lt;/strong&gt; of any tier.&lt;/p&gt;

&lt;p&gt;The catch is exactly that limit. On April 2, 2026 OpenAI moved Codex to &lt;strong&gt;token-based usage&lt;/strong&gt; instead of per-message counts, and the free tier's allowance is small — the earlier promotional boost to free limits has ended. It's enough to evaluate Codex properly and do occasional real work; it's not enough to delegate all day. If you outgrow it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT Go ($8/mo)&lt;/strong&gt; — a cheap step up that still includes the Codex CLI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT Plus ($20/mo)&lt;/strong&gt; — expanded Codex usage across CLI, web, IDE, and iOS, with roughly a full working day of typical delegation (tens of cloud tasks and 100+ local messages per 5-hour window).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro ($100+/mo)&lt;/strong&gt; — 5× or 20× the Plus limits for heavy users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For API-key usage instead of a subscription, &lt;code&gt;gpt-5.3-codex&lt;/code&gt; bills around &lt;strong&gt;$1.75 per million input tokens and $14 per million output tokens&lt;/strong&gt; — relevant only if you're scripting Codex rather than using your ChatGPT plan.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If you want a free agent CLI today&lt;/strong&gt;&lt;br&gt;
Codex CLI on the free ChatGPT plan is the single easiest "real agent, $0, signed in with an account you already have" option. Start there, see if the limits fit your day, and only pay if they don't.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The genuinely free options (ranked for real work)
&lt;/h2&gt;

&lt;p&gt;If your actual goal is "do serious AI-assisted coding without paying," stop trying to get Claude Code for free and use the tools that are &lt;em&gt;designed&lt;/em&gt; to be free. Here's how they really stack up:&lt;/p&gt;

&lt;p&gt;A realistic free stack looks like this: &lt;strong&gt;Gemini CLI as your daily driver, Copilot Free for inline autocomplete in the editor, Codex CLI on free ChatGPT for a second opinion, and a local model for the private or offline stuff.&lt;/strong&gt; That combination costs &lt;strong&gt;$0&lt;/strong&gt; and covers the overwhelming majority of everyday work.&lt;/p&gt;

&lt;p&gt;For the local route specifically — running a capable coding model entirely on your own machine, no subscription, no API bill — I went deep on the setup, hardware reality, and which models are worth it here: &lt;strong&gt;&lt;a href="https://umesh-malik.com/blog/local-llm-coding-revolution-qwen3-coder-desktop" rel="noopener noreferrer"&gt;The Local LLM Coding Revolution: Qwen3-Coder on Your Desktop&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to slash your AI coding bill legitimately
&lt;/h2&gt;

&lt;p&gt;Maybe you've decided the hard 10% is worth paying for — you want Claude Code or Plus-tier Codex for the genuinely tough work. Fine. Here's how to keep that bill as close to $0 as possible &lt;strong&gt;without breaking any rules&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;The biggest lever by far is the first one: &lt;strong&gt;match the tool to the task.&lt;/strong&gt; Most coding-agent work — renaming, refactoring, writing tests, fixing obvious bugs — doesn't need a frontier model. Send that to a free CLI and you'll find the only thing you actually pay for is the small fraction of genuinely hard reasoning. For help deciding which paid agent earns that slot, see &lt;strong&gt;&lt;a href="https://umesh-malik.com/blog/cursor-vs-claude-code-vs-copilot" rel="noopener noreferrer"&gt;Cursor vs Claude Code vs Copilot&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What NOT to do (the "free" traps that backfire)
&lt;/h2&gt;

&lt;p&gt;The dark-pattern corner of this topic is full of advice that ranges from terms-violating to outright dangerous. To save you the trouble:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cracked or shared subscriptions / API keys&lt;/strong&gt; — terms violation, instant ban risk, and you're trusting a stranger with code-execution access to your machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"VPN to unlock free credits / get paid"&lt;/strong&gt; — circumventing geographic or payment (KYC) controls is fraud, not a hack. On coding subscriptions it gets accounts terminated; on any ad or referral program it's explicitly banned and any balance clawed back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random "free Claude Code unlocker" downloads&lt;/strong&gt; — these are a classic vector for credential-stealing malware. A coding agent runs with deep access to your filesystem and shell; never install one from an untrusted source. (If you want to understand why that access is so dangerous, my piece on &lt;a href="https://umesh-malik.com/blog/ai-agent-attacks-developer-matplotlib-open-source" rel="noopener noreferrer"&gt;AI agent attacks on developers&lt;/a&gt; walks through real cases.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The legitimate free options above are better than any of these &lt;em&gt;and&lt;/em&gt; won't get you burned. There's simply no reason to go down the sketchy road.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Can you use Claude Code and Codex for free? &lt;strong&gt;Codex, yes — within limits.&lt;/strong&gt; &lt;strong&gt;Claude Code, not really — but you barely need to.&lt;/strong&gt; The honest play in 2026 is to stop hunting for a Claude Code loophole and build a free-first stack: a generous free CLI for the bulk, a local model for the private stuff, and a single $20 subscription reserved for the hard problems that genuinely justify it. That setup gives a paying user's results for $0–$20 a month — no piracy, no fraud, no banned accounts.&lt;/p&gt;

&lt;p&gt;Skip the "free unlocker" rabbit hole entirely — the legitimate free stack above gets you a paying user's results without the risk. Still deciding which paid agent deserves your one subscription? Start with &lt;strong&gt;&lt;a href="https://umesh-malik.com/blog/cursor-vs-claude-code-vs-copilot" rel="noopener noreferrer"&gt;Cursor vs Claude Code vs Copilot&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://claude.com/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt; — Claude Pro, Max, and API plans that include Claude Code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.openai.com/codex/" rel="noopener noreferrer"&gt;OpenAI Codex documentation&lt;/a&gt; — plan availability and usage limits for the Codex CLI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/google-gemini/gemini-cli" rel="noopener noreferrer"&gt;Gemini CLI&lt;/a&gt; — the free tier referenced in the free-stack ranking&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/features/copilot/plans" rel="noopener noreferrer"&gt;GitHub Copilot Free&lt;/a&gt; — completion and premium-request limits&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, web dev, and engineering.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/use-claude-code-codex-for-free" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-code-vs-cursor-production-work-2026" rel="noopener noreferrer"&gt;Claude Code vs Cursor vs Copilot for Real Production Work (2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/cursor-vs-claude-code-vs-copilot" rel="noopener noreferrer"&gt;Cursor vs Claude Code vs Copilot (2026): Which AI Coding Tool, for What&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-code-auto-mode-production-field-report" rel="noopener noreferrer"&gt;Is Claude Code Auto Mode Reliable in Production? A Field Report&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>codex</category>
      <category>gemini</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>FastAPI Finally Has Native SPA Support: app.frontend() Explained</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Sun, 21 Jun 2026 16:36:18 +0000</pubDate>
      <link>https://dev.to/umesh_malik/fastapi-finally-has-native-spa-support-appfrontend-explained-5fo4</link>
      <guid>https://dev.to/umesh_malik/fastapi-finally-has-native-spa-support-appfrontend-explained-5fo4</guid>
      <description>&lt;p&gt;On June 20, 2026, FastAPI shipped &lt;strong&gt;v0.138.0&lt;/strong&gt;, and buried in a changelog full of typo fixes and translation updates is one feature that closes a nine-year-old gap: &lt;strong&gt;&lt;code&gt;app.frontend()&lt;/code&gt;&lt;/strong&gt;. It's a first-class, official way to serve a built single-page app — React, Vue, Svelte, Astro, whatever — directly from your FastAPI process, with correct client-side-routing fallback baked in. No more hand-rolled catch-all routes. No more &lt;code&gt;StaticFiles(html=True)&lt;/code&gt; workarounds that almost-but-not-quite handle SPA routing.&lt;/p&gt;

&lt;p&gt;This isn't a rumor or a roadmap item — it's merged, documented, and live in &lt;a href="https://fastapi.tiangolo.com/tutorial/frontend/" rel="noopener noreferrer"&gt;the official docs&lt;/a&gt; right now. Here's exactly what it does, where it earns its place in a real deployment, and where it still leaves you on your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI 0.138.0&lt;/strong&gt; (2026-06-20) adds &lt;code&gt;app.frontend(path, directory="dist")&lt;/code&gt; and the matching &lt;code&gt;router.frontend()&lt;/code&gt; — a dedicated API for serving static SPA build output (&lt;a href="https://github.com/fastapi/fastapi/pull/15800" rel="noopener noreferrer"&gt;PR #15800&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;It replaces the community-standard hack of mounting &lt;code&gt;StaticFiles(html=True)&lt;/code&gt; plus a manual catch-all route, and it gets one subtle thing right that hand-rolled versions usually don't: &lt;strong&gt;missing assets still 404, only missing pages fall back to &lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Your API routes always win — frontend fallback is only checked after no &lt;code&gt;@app.get(...)&lt;/code&gt; route matches, so it can't accidentally swallow a real &lt;code&gt;/api/*&lt;/code&gt; 404.&lt;/li&gt;
&lt;li&gt;It explicitly &lt;strong&gt;does not do server-side rendering&lt;/strong&gt;. It serves a directory your frontend's build step already produced — nothing more.&lt;/li&gt;
&lt;li&gt;It ships six days after a router-internals refactor (0.137.0) that's the real engineering story underneath this feature — and that refactor has its own breaking change worth knowing about if you're upgrading.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;app.frontend()&lt;/code&gt; Actually Does
&lt;/h2&gt;

&lt;p&gt;Before this release, serving a Vite/React build from FastAPI looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.staticfiles&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StaticFiles&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;starlette.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;starlette.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/assets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StaticFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dist/assets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/{full_path:path}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;spa_fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;full_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FileResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dist/index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That catch-all route is the part everyone gets subtly wrong. It matches &lt;em&gt;everything&lt;/em&gt;, including requests for files that genuinely don't exist — &lt;code&gt;/assets/app-x7f3.js&lt;/code&gt; with a typo'd hash returns &lt;code&gt;index.html&lt;/code&gt; with a &lt;code&gt;200&lt;/code&gt;, not a &lt;code&gt;404&lt;/code&gt;, and the browser tries to parse HTML as JavaScript. You can patch around it (check &lt;code&gt;os.path.exists&lt;/code&gt;, branch on file extension), but now you're maintaining frontend-serving logic by hand.&lt;/p&gt;

&lt;p&gt;The new way is the verbatim example from FastAPI's own docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line. Mount your existing &lt;code&gt;@app.get&lt;/code&gt; routes above or below it — order doesn't matter, because &lt;strong&gt;FastAPI checks path operations first, every time&lt;/strong&gt;, and only falls through to the frontend directory when nothing else matched.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The full signature is &lt;code&gt;app.frontend(path, directory="dist", fallback="auto", check_dir=True)&lt;/code&gt;, and every parameter maps to a real decision you'd otherwise be encoding yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-side routing, done correctly
&lt;/h3&gt;

&lt;p&gt;This is the headline behavior: FastAPI distinguishes a request for a missing &lt;strong&gt;asset&lt;/strong&gt; (&lt;code&gt;.js&lt;/code&gt;, &lt;code&gt;.css&lt;/code&gt;, an image) from a request that looks like &lt;strong&gt;browser navigation&lt;/strong&gt; (&lt;code&gt;/dashboard/settings&lt;/code&gt;, &lt;code&gt;/users/42&lt;/code&gt;). The former still 404s normally. The latter falls back to &lt;code&gt;index.html&lt;/code&gt; so your client-side router — React Router, Vue Router, SvelteKit's own routing, whatever — gets a chance to render the right view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/users/{user_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A request to &lt;code&gt;/api/users/999&lt;/code&gt; that doesn't exist still hits your handler and returns whatever 404 logic you wrote there. A request to &lt;code&gt;/dashboard/settings&lt;/code&gt; — a route your SPA owns, not FastAPI — falls back to &lt;code&gt;index.html&lt;/code&gt;, and the SPA's router takes it from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  The four fallback modes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;"auto"&lt;/code&gt;&lt;/strong&gt; (default) — serves &lt;code&gt;404.html&lt;/code&gt; if your build output has one, otherwise falls back to &lt;code&gt;index.html&lt;/code&gt;, otherwise returns a normal 404.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;"index.html"&lt;/code&gt;&lt;/strong&gt; — always fall back to the SPA shell for unmatched navigation requests. The standard SPA setting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;"404.html"&lt;/code&gt;&lt;/strong&gt; — serve a custom 404 page with an actual &lt;code&gt;404&lt;/code&gt; status code, instead of silently rendering the SPA shell for dead links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;None&lt;/code&gt;&lt;/strong&gt; — disable fallback entirely; unmatched paths just 404. Useful if you're serving a plain static site, not a router-driven SPA.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;check_dir&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;By default (&lt;code&gt;check_dir=True&lt;/code&gt;), FastAPI validates the directory exists when you call &lt;code&gt;app.frontend()&lt;/code&gt; — so a typo'd path fails loudly at startup instead of silently 404ing every request in production. Set &lt;code&gt;check_dir=False&lt;/code&gt; only when your build step runs &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;FastAPI()&lt;/code&gt; app object is created (common in some CI/build pipelines), and be aware you're trading that startup safety net for flexibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mounting under &lt;code&gt;APIRouter&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;router.frontend()&lt;/code&gt; works identically, which means you can serve a frontend under a prefix, or run multiple frontends side by side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;admin_router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;admin_router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin-dist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin_router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two completely independent SPA builds, one FastAPI process, zero extra static-file plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Refactor This Sits On
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;app.frontend()&lt;/code&gt; didn't appear in isolation. Six days earlier, &lt;strong&gt;v0.137.0&lt;/strong&gt; (2026-06-14) shipped a structural change to how &lt;code&gt;APIRouter&lt;/code&gt; and &lt;code&gt;APIRoute&lt;/code&gt; work internally: routers used to flatten and "clone" every path operation when you called &lt;code&gt;include_router()&lt;/code&gt;, so the final app only ever had one big flat list of routes. That release rebuilt this into a preserved tree — &lt;code&gt;router_a.include_router(router_b)&lt;/code&gt; now keeps the actual &lt;code&gt;router_b&lt;/code&gt; object alive instead of copying its routes — which the FastAPI maintainer described as unblocking dependencies-per-router, middleware-per-router, and custom route-matching behavior down the line. &lt;code&gt;app.frontend()&lt;/code&gt; is the first user-visible feature built on top of that tree.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A breaking change rode along with this&lt;/strong&gt;&lt;br&gt;
The 0.137.0 refactor means &lt;code&gt;router.routes&lt;/code&gt; is no longer a flat list of &lt;code&gt;APIRoute&lt;/code&gt; objects — it's an internal tree structure now. Code that iterated &lt;code&gt;router.routes&lt;/code&gt; directly (some tooling, like Jupyverse, did) can break. FastAPI added &lt;code&gt;iter_route_contexts()&lt;/code&gt; in 0.137.2 as the supported replacement. If you're jumping straight to 0.138.x from an older version to get &lt;code&gt;app.frontend()&lt;/code&gt;, check for any code touching &lt;code&gt;router.routes&lt;/code&gt; directly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single-container deploys.&lt;/strong&gt; Build your React/Vite or Vue app, copy &lt;code&gt;dist/&lt;/code&gt; next to your FastAPI app, mount it with &lt;code&gt;app.frontend()&lt;/code&gt;, and ship one Docker image instead of a separate nginx-or-CDN layer just to serve static files. For small apps and internal tools this collapses a whole piece of infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal admin tools and dashboards.&lt;/strong&gt; Teams that build a small internal SPA on top of an internal API rarely want a dedicated frontend hosting pipeline. One FastAPI process serving both the API and the dashboard is now a one-liner instead of a maintenance burden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenant or multi-app backends.&lt;/strong&gt; &lt;code&gt;router.frontend()&lt;/code&gt; under different prefixes lets one FastAPI service host a public marketing SPA at &lt;code&gt;/&lt;/code&gt; and an admin SPA at &lt;code&gt;/admin&lt;/code&gt;, each with its own fallback behavior, without spinning up separate services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replacing nginx-as-static-server in simple setups.&lt;/strong&gt; If your only reason for an nginx sidecar was "serve the SPA's &lt;code&gt;index.html&lt;/code&gt; for unknown paths," that reason just got a lot weaker for low-to-medium traffic deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;StaticFiles(html=True)&lt;/code&gt; hack vs. &lt;code&gt;app.frontend()&lt;/code&gt;
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Issues FastAPI Still Has Here
&lt;/h2&gt;

&lt;p&gt;This is a genuinely useful feature, and it's also genuinely young — merged six days before this post was written. The official docs are honest about the biggest limitation, in a section literally titled &lt;strong&gt;"Static Build Output Only"&lt;/strong&gt;: &lt;code&gt;app.frontend()&lt;/code&gt; serves files your frontend's build step already produced. It does not run server-side rendering, and it isn't a fit for frameworks that need per-request rendering on the server.&lt;/p&gt;

&lt;p&gt;Beyond that documented boundary, a few things are worth knowing before you reach for it in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No dev-server integration.&lt;/strong&gt; This serves a &lt;em&gt;build&lt;/em&gt;, not a live dev server. There's no proxying to a running Vite/webpack dev server with HMR — you still run your frontend's own dev server locally and point it at the FastAPI API separately, same as before this feature existed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No caching or cache-busting story.&lt;/strong&gt; Nothing in the docs covers &lt;code&gt;Cache-Control&lt;/code&gt;, ETags, or immutable caching for hashed asset filenames. You still need a reverse proxy or CDN in front of production traffic if you care about this — &lt;code&gt;app.frontend()&lt;/code&gt; solves routing correctness, not HTTP caching semantics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;check_dir=False&lt;/code&gt; removes your safety net silently.&lt;/strong&gt; It's the right call when your build runs after app creation, but it means a misconfigured path now fails at request time in production instead of at startup, where it's far cheaper to catch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordering footguns still exist, just a different kind.&lt;/strong&gt; API routes always win over the frontend mount, which is good — but if you mount two &lt;code&gt;frontend()&lt;/code&gt; calls (or a &lt;code&gt;frontend()&lt;/code&gt; and a &lt;code&gt;StaticFiles&lt;/code&gt; mount) at overlapping paths, you can still end up debugging which fallback actually fired.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;router.routes&lt;/code&gt; breaking change rides along in the same upgrade.&lt;/strong&gt; If you're jumping from a pre-0.137 version straight to 0.138.x to get this feature, audit for any code that walks &lt;code&gt;router.routes&lt;/code&gt; directly — it's an internal tree now, not a flat list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No production track record yet.&lt;/strong&gt; This is days-old API surface from a fast-moving project. The maintainer's PR notes claim full test coverage and no measured performance regression, which is a reasonable bar — but "reasonable bar" and "battle-tested in production for a year" are different claims. Treat it like any brand-new dependency feature: try it on something low-stakes first.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Everything described above — &lt;code&gt;app.frontend()&lt;/code&gt;, a second SPA mounted with &lt;code&gt;router.frontend()&lt;/code&gt; under &lt;code&gt;/admin&lt;/code&gt;, API routes that take precedence, and the asset-vs-navigation 404 distinction — is in a runnable example: &lt;a href="https://github.com/Umeshmalik/examples/tree/main/fastapi-spa-app-frontend" rel="noopener noreferrer"&gt;&lt;code&gt;fastapi-spa-app-frontend&lt;/code&gt;&lt;/a&gt; in my public examples repo. It's a minimal FastAPI backend plus a Vite/React SPA with three client-side routes, and the README walks through building the frontend and running the backend so you can curl each of the behaviors above yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Take
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;app.frontend()&lt;/code&gt; isn't a flashy feature, and it doesn't need to be. It takes a pattern thousands of FastAPI projects have hand-rolled — usually slightly wrong, in the specific way of returning &lt;code&gt;200&lt;/code&gt; for missing assets — and makes the correct version a one-liner. The more interesting story is underneath it: a router-internals refactor from six days earlier that quietly unlocked this and several other features still to come, with its own breaking change for anyone iterating &lt;code&gt;router.routes&lt;/code&gt; directly.&lt;/p&gt;

&lt;p&gt;If you're shipping a SPA behind FastAPI today, this is worth trying on a side project before you trust it in production. It solves real, specific pain — and it's honest in its own docs about exactly where it stops.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fastapi.tiangolo.com/tutorial/frontend/" rel="noopener noreferrer"&gt;FastAPI docs: Frontend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fastapi.tiangolo.com/release-notes/" rel="noopener noreferrer"&gt;FastAPI release notes&lt;/a&gt; — v0.138.0 and v0.137.0 entries&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fastapi/fastapi/pull/15800" rel="noopener noreferrer"&gt;PR #15800 — Add app.frontend() / router.frontend()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fastapi/fastapi/pull/15745" rel="noopener noreferrer"&gt;PR #15745 — Refactor internals to preserve APIRouter and APIRoute instances&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Umeshmalik/examples/tree/main/fastapi-spa-app-frontend" rel="noopener noreferrer"&gt;Runnable example: &lt;code&gt;fastapi-spa-app-frontend&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/fastapi-spa-app-frontend-explained" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/nodejs-backend-for-frontend-developers" rel="noopener noreferrer"&gt;Node.js Backend for Frontend Developers: Express, REST APIs, Auth, and Deployment (Step-by-Step)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/http-query-method-rfc-10008-guide" rel="noopener noreferrer"&gt;HTTP QUERY Method Explained: The REST Fix for GET vs POST (RFC 10008)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-swap-multi-account-switcher-guide" rel="noopener noreferrer"&gt;How to Switch Between Multiple Claude Code Accounts Without Re-Logging In (claude-swap Guide)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>fastapi</category>
      <category>python</category>
      <category>spa</category>
      <category>backend</category>
    </item>
    <item>
      <title>Agentic Browsing in PageSpeed Insights: How to Make Your Website AI-Ready (2026)</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Fri, 19 Jun 2026 10:00:11 +0000</pubDate>
      <link>https://dev.to/umesh_malik/agentic-browsing-in-pagespeed-insights-how-to-make-your-website-ai-ready-2026-n1e</link>
      <guid>https://dev.to/umesh_malik/agentic-browsing-in-pagespeed-insights-how-to-make-your-website-ai-ready-2026-n1e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Agentic browsing in PageSpeed Insights&lt;/strong&gt; is the newest signal that your website's audience is no longer just humans and search crawlers — it's AI agents acting on a person's behalf. Google now scores how usable your site is for that third audience, in the same tool you already use for Core Web Vitals.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PageSpeed Insights now grades your site for AI agents, not just humans.&lt;/strong&gt; Lighthouse 13.3 (May 7, 2026) added an &lt;strong&gt;Agentic Browsing&lt;/strong&gt; category to the default config; PageSpeed Insights inherited it within two weeks. It sits next to Performance, Accessibility, Best Practices and SEO — and reports a ratio like &lt;code&gt;3/3&lt;/code&gt;, not a score out of 100.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It checks three things by default:&lt;/strong&gt; a clean &lt;strong&gt;accessibility tree&lt;/strong&gt;, a &lt;strong&gt;stable layout&lt;/strong&gt; (low CLS), and a valid &lt;strong&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/strong&gt; at your domain root. The wider Lighthouse category also audits &lt;strong&gt;WebMCP&lt;/strong&gt; — annotated forms and registered agent tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google added it because agents are now real traffic.&lt;/strong&gt; Operator, Computer Use, Project Mariner, Perplexity and ChatGPT's browse mode visit sites on a person's behalf. A growing share of requests hitting your server are software, not people.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You don't fail for lacking AI features&lt;/strong&gt; — the category is informational. But "AI-ready" is now a measurable, public number, and it's about to become a competitive one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proof it's achievable:&lt;/strong&gt; a real Lighthouse 13.4 run on my own site, &lt;code&gt;umesh-malik.com&lt;/code&gt;, scores &lt;strong&gt;Agentic Browsing 3/3&lt;/strong&gt; and &lt;strong&gt;100 on Accessibility, Best Practices and SEO&lt;/strong&gt; — and it passes &lt;strong&gt;7/7 on the &lt;a href="https://isitagentready.com/" rel="noopener noreferrer"&gt;isitagentready.com&lt;/a&gt; protocol-discovery checklist&lt;/strong&gt; — using nothing but static files and one Cloudflare Worker. Here's exactly how, so you can copy it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Your Website Is Now Being Graded for Robots
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;pagespeed.web.dev&lt;/a&gt;, run a report, and you'll see something that wasn't there a month ago: a category called &lt;strong&gt;Agentic Browsing&lt;/strong&gt;, sitting right alongside Performance and SEO. It doesn't show a number out of 100. It shows a ratio — &lt;code&gt;2/3&lt;/code&gt;, &lt;code&gt;3/3&lt;/code&gt; — and a short list of checks with names like "accessibility tree" and "llms.txt".&lt;/p&gt;

&lt;p&gt;That ratio is not measuring how fast your page loads or whether your headings are in order. It's measuring &lt;strong&gt;how well an AI agent can read your page, understand it, and act on it — with no human in the loop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the quiet half of a shift that's been building all year. We spent two decades optimizing for two audiences: humans who read, and crawlers that index. There's now a third, and it behaves like neither. An agent doesn't skim your hero copy or admire your animations. It wants structure it can parse, facts it can extract, and tools it can call. Google just turned "are you ready for that audience?" into a number anyone can pull up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: SEO made your site &lt;em&gt;findable&lt;/em&gt;. GEO made it &lt;em&gt;quotable&lt;/em&gt;. Agentic Browsing makes it &lt;em&gt;usable by software&lt;/em&gt;. These are three different jobs, and the third one is now scored in the same tool you already use for Core Web Vitals.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Is Agentic Browsing in PageSpeed Insights?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agentic Browsing is a Lighthouse category that scores how ready a page is for an AI agent to read it, understand it, and act on it without a human driving.&lt;/strong&gt; It was introduced in &lt;strong&gt;Lighthouse 13.3 on May 7, 2026&lt;/strong&gt;, moved straight into the default config, and PageSpeed Insights picked it up within a couple of weeks. As of mid-June 2026 it's live for everyone.&lt;/p&gt;

&lt;p&gt;A few things make it behave differently from every other Lighthouse category:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It reports a ratio, not a score out of 100.&lt;/strong&gt; You'll see &lt;code&gt;3/3&lt;/code&gt;, not &lt;code&gt;92&lt;/code&gt;. It's a count of passed checks, not a weighted index.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's marked "under development."&lt;/strong&gt; The exact audits and the way they're scored will change. Don't carve a &lt;code&gt;3/3&lt;/code&gt; into your OKRs yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It won't fail you for having no AI features.&lt;/strong&gt; &lt;code&gt;example.com&lt;/code&gt; — a page with almost nothing on it — earns a perfect ratio. This is a checklist of opportunities, not a penalty box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In PageSpeed Insights, the default result is built from &lt;strong&gt;three checks&lt;/strong&gt;. The broader Lighthouse category runs &lt;strong&gt;four audits&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Notice what's &lt;em&gt;not&lt;/em&gt; there: nothing about keywords, backlinks, or meta descriptions. This audit is about &lt;strong&gt;machine comprehension and machine action&lt;/strong&gt;, full stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Google Added It — Agents Are Real Traffic Now
&lt;/h2&gt;

&lt;p&gt;The cynical read is "Google wants another number to chase." The accurate read is simpler: &lt;strong&gt;a meaningful and growing share of the requests hitting public web servers are agents, not humans&lt;/strong&gt; — and the tooling finally caught up to that reality.&lt;/p&gt;

&lt;p&gt;Look at who's browsing on a user's behalf in 2026: OpenAI's Operator, Anthropic's Computer Use, Google's own Project Mariner, Perplexity, and ChatGPT's browse mode. These don't issue a query and read ten blue links. They get a &lt;em&gt;task&lt;/em&gt; — "compare these three products and book the cheapest one that ships by Friday" — and they execute it across multiple sites. To do that, they have to read your page, model what's on it, and act.&lt;/p&gt;

&lt;p&gt;When an agent hits a page built only for human eyes, three things go wrong:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Comprehension is expensive.&lt;/strong&gt; Feeding raw HTML or a screenshot into a model burns tokens and invites mistakes. A clean accessibility tree is an order of magnitude cheaper to reason over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action is fragile.&lt;/strong&gt; If your "Add to cart" is a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with an &lt;code&gt;onclick&lt;/code&gt;, an agent has to &lt;em&gt;guess&lt;/em&gt;. Annotated forms and registered tools remove the guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discovery is a coin flip.&lt;/strong&gt; Without an &lt;code&gt;llms.txt&lt;/code&gt; or a tool manifest, the agent has to reverse-engineer your site's structure every single visit.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The honest caveat&lt;/strong&gt;&lt;br&gt;
This category is new and explicitly "under development." &lt;code&gt;llms.txt&lt;/code&gt; in particular isn't yet widely consumed by AI tools — even the Lighthouse team says so. None of this is a guaranteed ranking lever today. It's a low-cost bet on where the web is obviously heading, made measurable a year or two before most sites will bother. That early-mover window is the whole point.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How It Helps Developers (and Agents)
&lt;/h2&gt;

&lt;p&gt;For &lt;strong&gt;agents&lt;/strong&gt;, the payoff is obvious: cheaper comprehension, reliable action, less hallucination about what your site does.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;developers&lt;/strong&gt;, the wins are quieter but real:&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Make Any Website AI-Ready
&lt;/h2&gt;

&lt;p&gt;Here's where it gets practical. The PageSpeed category is the &lt;em&gt;headline&lt;/em&gt;, but the fuller checklist lives at &lt;strong&gt;&lt;a href="https://isitagentready.com/" rel="noopener noreferrer"&gt;isitagentready.com&lt;/a&gt;&lt;/strong&gt; — a free scanner that groups agent-readiness into five categories. I've used its taxonomy to structure the work below, because it maps cleanly onto what agents actually look for.&lt;/p&gt;

&lt;p&gt;You don't need all five. A blog needs the first four and can ignore commerce entirely. A store needs all five. Work top-down — discoverability is the cheapest and highest-leverage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: The single highest-leverage change most sites can make is also the most boring — &lt;strong&gt;stop rendering text and diagrams as images.&lt;/strong&gt; An image of a table is invisible to the accessibility tree. A real &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; (or a component that renders one) is readable by screen readers, crawlers, &lt;em&gt;and&lt;/em&gt; agents in one shot. I'll come back to this, because it's exactly how I scored my own site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to go deeper on the callable layer, I wrote a full walkthrough of &lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;building a production MCP server&lt;/a&gt; and &lt;a href="https://umesh-malik.com/blog/deploy-mcp-server-cloudflare-workers" rel="noopener noreferrer"&gt;deploying it on Cloudflare Workers&lt;/a&gt; — that's the same server backing the numbers below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof: How AI-Ready Is umesh-malik.com?
&lt;/h2&gt;

&lt;p&gt;Talk is cheap, so here's the receipt. I ran my own site — &lt;code&gt;umesh-malik.com&lt;/code&gt; — through &lt;strong&gt;Lighthouse 13.4&lt;/strong&gt; (the engine behind PageSpeed Insights) and the &lt;a href="https://isitagentready.com/" rel="noopener noreferrer"&gt;isitagentready.com&lt;/a&gt; checklist. This site is a SvelteKit SSG — &lt;strong&gt;fully static, one Cloudflare Worker, no special infrastructure.&lt;/strong&gt; Everything below ships from files in a public repo.&lt;/p&gt;

&lt;p&gt;The Agentic Browsing category comes back &lt;strong&gt;3/3&lt;/strong&gt; — every weighted check passing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhw9vgtwyf35is2y02p6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhw9vgtwyf35is2y02p6c.png" alt="Lighthouse 13.4 Agentic Browsing result for umesh-malik.com: a green 3 out of 3 score, with three passed audits — accessibility tree well-formed, Cumulative Layout Shift of 0, and llms.txt follows recommendations — plus three WebMCP audits marked Not Applicable" width="800" height="1118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the honest, category-by-category breakdown — including what the site &lt;strong&gt;doesn't&lt;/strong&gt; have:&lt;/p&gt;

&lt;p&gt;.md and /llms.txt', tone: 'neutral' }] },&lt;br&gt;
    { label: 'llms.txt + llms-full.txt', cells: [{ text: 'Yes', tone: 'positive' }, { text: 'Valid H1, description, ~3,500 words, every post linked', tone: 'neutral' }] },&lt;br&gt;
    { label: 'MCP server card', cells: [{ text: 'Yes', tone: 'positive' }, { text: '/.well-known/mcp/server-card.json — 4 tools, Streamable HTTP', tone: 'neutral' }] },&lt;br&gt;
    { label: 'Agent Skills + API catalog + WebMCP', cells: [{ text: 'Yes', tone: 'positive' }, { text: 'agent-skills/index.json, RFC 9727 linkset, navigator.modelContext', tone: 'neutral' }] },&lt;br&gt;
    { label: 'OAuth discovery + auth.md', cells: [{ text: 'Yes', tone: 'positive' }, { text: 'Honestly declares the site as public/anonymous — no fake auth server', tone: 'neutral' }] },&lt;br&gt;
    { label: 'DNS-AID + Web Bot Auth', cells: [{ text: 'Not yet', tone: 'negative' }, { text: 'On the roadmap — newer, lower-leverage for a content site', tone: 'neutral' }] },&lt;br&gt;
    { label: 'Commerce (x402, MPP, UCP, ACP)', cells: [{ text: 'N/A', tone: 'neutral' }, { text: 'It\'s a portfolio + blog. Nothing to sell, nothing to fake.', tone: 'neutral' }] }&lt;br&gt;
  ]}&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;The result: the site passes &lt;strong&gt;every category that applies to it&lt;/strong&gt; and scores &lt;strong&gt;7/7 on protocol discovery&lt;/strong&gt;. The 3/3 above is a real Lighthouse run, not a mockup — and I'm not pretending DNS-AID and Web Bot Auth are done, because they aren't. (Note the three WebMCP audits show as &lt;em&gt;Not Applicable&lt;/em&gt; in the screenshot: the site registers WebMCP tools in code, but Lighthouse's WebMCP audits are still informational and didn't score them on this page — an honest nuance of a category that's openly "under development.") That candor is the point. &lt;strong&gt;Agent-readiness is a real engineering state, not a vanity badge.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How I Actually Got Here (and How You Can Copy It)
&lt;/h3&gt;

&lt;p&gt;None of this required a backend rewrite. The whole agent-discovery layer is one Cloudflare Worker plus a handful of static files:&lt;/p&gt;

&lt;p&gt;The most important line in that list is the one about &lt;strong&gt;diagrams as DOM components&lt;/strong&gt;. This very post is the proof: every chart, table and step list you've scrolled past is a real Svelte component rendering semantic HTML — not a PNG. That's why an agent (or a screen reader) can read all of it, and it's a large part of why the accessibility-tree check passes. One decision, paid back across three audiences.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The reusable principle&lt;/strong&gt;&lt;br&gt;
You don't need Cloudflare, SvelteKit, or my stack. The pattern generalizes: &lt;strong&gt;reuse the content you already publish&lt;/strong&gt; (your feed, your Markdown, your profile) and expose it through machine-readable surfaces — robots.txt, llms.txt, Link headers, an MCP endpoint. The data already exists. Agent-readiness is mostly about &lt;em&gt;presenting&lt;/em&gt; it in formats agents understand, not creating new content.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chasing the ratio instead of the readiness.&lt;/strong&gt; &lt;code&gt;example.com&lt;/code&gt; scores a perfect ratio with nothing real behind it. A green &lt;code&gt;3/3&lt;/code&gt; on a site full of image-of-text content is a lie you're telling yourself. Optimize the underlying state, not the badge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faking an auth server.&lt;/strong&gt; If your site is public, &lt;em&gt;say so&lt;/em&gt; in auth.md and OAuth discovery. Advertising endpoints that don't exist breaks the agents that trust them. Honest "anonymous, no auth" beats a fictional token endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treating llms.txt as a keyword dump.&lt;/strong&gt; It's a map, not a meta-keywords tag. Give it a clear H1, a real description, and links to your genuinely best content. Stuffing it is the 2007 SEO mistake in a new file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shipping images of text.&lt;/strong&gt; The single most common thing that quietly fails the accessibility-tree check. If a human needs to read words in it, it should be real text, not a screenshot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring CLS because "it's just SEO."&lt;/strong&gt; Layout that jumps confuses screenshot-based agents the same way it annoys users. Your &lt;a href="https://umesh-malik.com/blog/core-web-vitals-optimization-guide" rel="noopener noreferrer"&gt;Core Web Vitals work&lt;/a&gt; now pays an agentic dividend too.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Agentic Browsing in PageSpeed Insights is small today — a ratio, marked "under development," consumed by tools that are themselves a year young. It would be easy to dismiss. Don't.&lt;/p&gt;

&lt;p&gt;The trajectory is unmistakable: agents are becoming a first-class audience for the web, Google just made their needs &lt;em&gt;measurable&lt;/em&gt;, and the work to satisfy them is cheap, mostly static, and overlaps almost entirely with accessibility and good engineering you should be doing anyway. The sites that ship a clean accessibility tree, a real &lt;code&gt;llms.txt&lt;/code&gt;, and a callable MCP endpoint &lt;strong&gt;now&lt;/strong&gt; will be the ones agents reach for when the rest of the web is still serving them screenshots.&lt;/p&gt;

&lt;p&gt;I made my own site agent-ready with a Worker and some static files, and I documented every move so you can do the same. Start with robots.txt and llms.txt this week. Then decide how deep into protocol discovery your site deserves to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;PageSpeed Insights&lt;/a&gt; — run the Agentic Browsing category on any URL&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/GoogleChrome/lighthouse/releases" rel="noopener noreferrer"&gt;Lighthouse release notes&lt;/a&gt; — 13.3 introduced the category on May 7, 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://llmstxt.org/" rel="noopener noreferrer"&gt;llms.txt specification&lt;/a&gt; — the file the default audit validates&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://isitagentready.com/" rel="noopener noreferrer"&gt;isitagentready.com&lt;/a&gt; — the protocol-discovery checklist referenced in the proof section&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering. Curious how the callable layer works? Read &lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;How to Build a Production MCP Server&lt;/a&gt; next.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/agentic-browsing-pagespeed-ai-ready" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/autonomous-ai-agents-production-gap-2026" rel="noopener noreferrer"&gt;AI Agents That Run the Business in 2026: Why 77% Never Reach Production (and What the 23% Do Differently)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;How to Build a Production MCP Server (I Added One to My Site)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/build-rag-pipeline-from-scratch" rel="noopener noreferrer"&gt;Build a RAG Pipeline From Scratch (Production Patterns That Actually Matter)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agenticbrowsing</category>
      <category>pagespeedinsights</category>
      <category>aiagents</category>
      <category>geo</category>
    </item>
    <item>
      <title>AI Agents That Run the Business in 2026: Why 77% Never Reach Production (and What the 23% Do Differently)</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Sun, 14 Jun 2026 17:00:02 +0000</pubDate>
      <link>https://dev.to/umesh_malik/ai-agents-that-run-the-business-in-2026-why-77-never-reach-production-and-what-the-23-do-23j7</link>
      <guid>https://dev.to/umesh_malik/ai-agents-that-run-the-business-in-2026-why-77-never-reach-production-and-what-the-23-do-23j7</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Building an AI agent is easy. Shipping one that runs your business is where roughly 77% of projects die.&lt;/strong&gt; The demo-to-production gap is the real story of agentic AI in 2026 — not the model benchmarks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three things kill agents in production:&lt;/strong&gt; compounding error across long tool chains, fuzzy accountability when the agent acts on its own, and the unglamorous integration work nobody puts in a demo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 23% who ship all do the same five things:&lt;/strong&gt; pick a narrow, high-volume task; keep a human on the risky steps; scope permissions tightly; build an eval harness before scaling; and graduate from shadow mode to autonomy instead of flipping a switch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proof it works when it's bounded:&lt;/strong&gt; Lassie raised $35M in June 2026 to run medical- and dental-practice back offices for 700+ businesses, reclaiming about 250,000 staff-hours a year.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Everyone Is Building AI Agents. Almost Nobody Is Shipping Them.
&lt;/h2&gt;

&lt;p&gt;Lassie just raised $35 million to make small businesses run themselves. Andreessen Horowitz led the round in June 2026, and the pitch is exactly as ambitious as it sounds: &lt;strong&gt;autonomous AI agents&lt;/strong&gt; that don't just help a medical practice with its back office — they &lt;em&gt;run&lt;/em&gt; it. Payment enrollment, reconciliation, insurance appeals, follow-up. The software does the work, not the staff.&lt;/p&gt;

&lt;p&gt;Here's the uncomfortable part. For every Lassie, there are a hundred agent projects quietly dying in a sandbox. McKinsey's 2026 numbers say it plainly: &lt;strong&gt;62% of organizations are experimenting with agents, but only 23% have scaled them.&lt;/strong&gt; Gartner expects 40% of enterprise apps to embed task-specific agents by the end of 2026 — up from less than 5% — which means the gap between "we built an agent" and "the business runs on it" is about to become the most expensive gap in software.&lt;/p&gt;

&lt;p&gt;That funnel is the whole article. The winners aren't the teams with the smartest model — by 2026 everyone has access to roughly the same frontier models. The winners are the teams that treated &lt;em&gt;autonomous&lt;/em&gt; as an outcome to earn, not a switch to flip. Let's break down exactly where the 77% fall out, and what the survivors do differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an Autonomous AI Agent That Runs the Business Actually Means
&lt;/h2&gt;

&lt;p&gt;The word "agent" got stretched into meaninglessness in 2025. Half the products calling themselves agents are chatbots with a system prompt. So let's be precise.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definition&lt;/strong&gt;&lt;br&gt;
An &lt;strong&gt;autonomous AI agent&lt;/strong&gt; is software that pursues a goal by deciding its own steps and taking real actions across tools and systems — not just answering a prompt. A copilot suggests; an agent acts. An agent that "runs the business" owns a workflow end to end, with a human in the loop by exception rather than by default.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It helps to see it as a ladder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;chatbot&lt;/strong&gt; answers questions. It has no hands.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;copilot&lt;/strong&gt; drafts and suggests. You review every output and you take the action.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;autonomous agent&lt;/strong&gt; takes the action itself — it books, files, reconciles, emails — and only escalates to a human when its own policy says to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The jump that matters is from &lt;em&gt;suggesting&lt;/em&gt; to &lt;em&gt;acting&lt;/em&gt;. That single step is where reliability, trust, and accountability stop being nice-to-haves and start being the entire engineering problem. It's also why "agentic" is not the same as "automation." Classic automation follows a fixed script you wrote. An agent chooses the path at runtime — which is exactly what makes it powerful and exactly what makes it hard to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Suddenly Real in 2026
&lt;/h2&gt;

&lt;p&gt;Agents aren't new as an idea. What changed in 2026 is that three curves crossed at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Models got good enough and cheap enough.&lt;/strong&gt; The June 2026 release wave pushed frontier capability up and token prices down hard. Reasoning that was research-grade in 2024 is now a line item.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration got standardized.&lt;/strong&gt; The Model Context Protocol turned "wire the agent into your stack" from a bespoke six-week project into a connector you can reuse. Plumbing was the silent blocker, and it got a lot less silent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The economics finally make sense for the back office.&lt;/strong&gt; This is the part founders underrate. The money isn't in flashy consumer demos — it's in the boring, expensive work every small business drowns in.&lt;/p&gt;

&lt;p&gt;Andreessen Horowitz called small businesses "the next frontier for AI" for exactly this reason: a single medical practice can burn over 100 hours a month and roughly $200,000 a year on administrative work that is repetitive, rule-bound, and perfect for an agent — &lt;em&gt;if&lt;/em&gt; you can get the agent into production. Which brings us to the hard part.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv6p084l8o499nztjx3nt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv6p084l8o499nztjx3nt.png" alt="Funnel showing 62% of organizations experimenting with AI agents but only 23% reaching production — the 77% that stall between pilot and deployment" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 77% of AI Agents Never Reach Production
&lt;/h2&gt;

&lt;p&gt;The gap is not a model problem. It's a systems problem. Here are the five failure modes that kill agents between an impressive demo and a dependable deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Compounding error is the silent killer
&lt;/h3&gt;

&lt;p&gt;A demo runs three steps and looks like magic. Production runs twenty and falls apart. Reliability multiplies — it doesn't average.&lt;/p&gt;

&lt;p&gt;An agent chaining 20 tool calls at 95% per-step reliability succeeds end to end only about &lt;strong&gt;36% of the time&lt;/strong&gt; (0.95^20 ≈ 0.36). That's not a model you can ship; that's a coin flip you'd lose two times out of three. Push per-step reliability to a heroic 99% and you're still only at 82% across 20 steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5f0i67ojjyk07nw3mfgu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5f0i67ojjyk07nw3mfgu.png" alt="Compounding error chart: at 95% per-step reliability, an autonomous agent's end-to-end success rate falls to about 36% over 20 steps" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fix is not a cleverer prompt. It's fewer steps, verification between steps, and retries that actually check their work. The teams that ship design &lt;em&gt;short, checkpointed&lt;/em&gt; chains. The teams that stall keep adding steps and hoping.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Nobody owns the outcome
&lt;/h3&gt;

&lt;p&gt;When a copilot suggests something wrong, a human catches it. When an agent files the insurance appeal, posts the transaction, or emails the customer, there is no catch — unless you built one.&lt;/p&gt;

&lt;p&gt;Demos hide this because the person demoing is the safety net. Production has to encode the safety net as policy: approval gates on irreversible actions, reversibility where you can manage it, and an audit trail for everything. "Who is accountable when the agent is wrong?" is a question you answer in your architecture, not your marketing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The integration tax nobody demos
&lt;/h3&gt;

&lt;p&gt;The exciting part is reasoning. The expensive part is plumbing — connecting to the practice-management system, the payment processor, the ledger, the CRM, the half-documented internal API from 2014.&lt;/p&gt;

&lt;p&gt;Most pilots stall here. Not because the agent can't &lt;em&gt;think&lt;/em&gt;, but because it can't reliably &lt;em&gt;act&lt;/em&gt; in the messy systems a real business runs on. Standardization like &lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;the Model Context Protocol made this tractable&lt;/a&gt; — it did not make it trivial. Budget for the integration tax or it will quietly eat your timeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. No evals, no production
&lt;/h3&gt;

&lt;p&gt;If you can't measure whether the agent did the job, you cannot ship it. Yet most teams still test by vibes: try a few prompts, it looks good, ship it.&lt;/p&gt;

&lt;p&gt;Production needs an eval harness — a labeled set of real tasks, an automated grader, and a single number you can watch move as you change things. This is the same discipline behind &lt;a href="https://umesh-malik.com/blog/spec-driven-development-ai-agents-addy-osmani" rel="noopener noreferrer"&gt;spec-driven development&lt;/a&gt;: write down what "done" means before you trust a machine to do it. No harness, no honest answer to "is it good enough yet?"&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Cost and latency at scale
&lt;/h3&gt;

&lt;p&gt;A run that costs $0.40 and takes 90 seconds is delightful in a demo and brutal at 50,000 runs a day. The unit economics of the agent loop — tokens, retries, tool round-trips — decide whether the pilot survives contact with real volume.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight:&lt;/strong&gt; The teams that ship treat reliability as an engineering budget to spend, not a model property to wait for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What the 23% Do Differently
&lt;/h2&gt;

&lt;p&gt;The survivors are almost boring about it. They don't chase the most autonomous agent they can build — they build the most &lt;em&gt;bounded&lt;/em&gt; agent that solves a real problem, then earn autonomy from there.&lt;/p&gt;

&lt;p&gt;Notice what's missing: "use a bigger model." Model choice matters, but it's table stakes. The differentiator is operating discipline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Autonomy Spectrum: It's a Dial, Not a Switch
&lt;/h2&gt;

&lt;p&gt;The biggest framing mistake teams make is treating autonomy as binary — either the human does it or the agent does. In reality it's a spectrum, and the credible 2026 deployments cluster in the middle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3cedxuplz4dyb5zy23vs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3cedxuplz4dyb5zy23vs.png" alt="Autonomy spectrum from L0 assist to L4 full autonomy, with most credible 2026 business deployments sitting at L2–L3 supervised autonomy" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;L4 makes the headlines and the funding decks. L2 and L3 make the money. A supervised agent that handles 90% of cases autonomously and escalates the weird 10% to a human is worth far more than a fully autonomous agent that's right 70% of the time and unaccountable for the other 30%. Earn your way up the ladder; don't start at the top.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mistakes That Keep Teams Stuck
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Avoid these five traps&lt;/strong&gt;&lt;br&gt;
The same anti-patterns show up in almost every stalled agent project. If you recognize more than one, that's your roadmap.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The "run my whole company" fantasy.&lt;/strong&gt; Broad, open-ended scope is undemoable and unshippable. Narrow until the task is boring, then ship the boring version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo-driven development.&lt;/strong&gt; Optimizing for the three-step happy path that looks great on stage and ignores the long tail that breaks in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over-permissioned agents.&lt;/strong&gt; Handing the agent god-mode credentials "to move fast." You're one prompt injection away from regret. Scope everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skipping evals.&lt;/strong&gt; Without a number, "good enough" is a feeling, and feelings don't survive a board meeting after the agent fails publicly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring the integration tax.&lt;/strong&gt; Treating the messy back-office plumbing as an afterthought, then discovering it &lt;em&gt;is&lt;/em&gt; the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Case Study: How Lassie Actually Ships Autonomy
&lt;/h2&gt;

&lt;p&gt;Lassie is a useful case study precisely because it isn't trying to do everything. It picked one vertical with a brutal admin burden and went deep.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw7w48p2u6633e9d913lh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw7w48p2u6633e9d913lh.png" alt="Case study diagram of Lassie's autonomous agents handling medical-practice back office: payment reconciliation, appeals, reporting — reclaiming 250,000 hours a year" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The lesson isn't "build a Lassie." It's that their design choices are the production playbook in disguise: a narrow vertical (bounded scope), high-volume repetitive tasks (testable reliability), and a workflow with clear success criteria (eval-friendly). They didn't win by being more autonomous than everyone else. They won by being autonomous about the &lt;em&gt;right, small thing&lt;/em&gt; — and being able to prove it worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Your Agent Actually Production-Ready?
&lt;/h2&gt;

&lt;p&gt;Before you let an agent touch anything a customer or a regulator will see, run this checklist. If you can't tick the criticals, you have a demo, not a deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;In 2026, building an autonomous AI agent is a weekend project. Building one your business can actually run on is a discipline — and it's a discipline most teams skip on the way to a demo that wins applause and a deployment that never arrives.&lt;/p&gt;

&lt;p&gt;Stop trying to flip the autonomy switch. Pick one narrow, high-volume, boring task. Put a human on the dangerous steps. Scope the permissions. Build the eval harness. Run it in the shadows until the numbers earn your trust — then, and only then, let it act on its own. That's how the 23% ship while everyone else demos.&lt;/p&gt;

&lt;p&gt;If this was useful, read &lt;a href="https://umesh-malik.com/blog/agentic-ai-enterprise-security-model" rel="noopener noreferrer"&gt;how agentic AI breaks the enterprise security model&lt;/a&gt; next — because the moment your agent can act, security stops being optional. Then learn to &lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;build the integration layer with MCP&lt;/a&gt; and to &lt;a href="https://umesh-malik.com/blog/spec-driven-development-ai-agents-addy-osmani" rel="noopener noreferrer"&gt;pin down "done" with spec-driven development&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written for &lt;a href="https://umesh-malik.com" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt; — no-fluff technical writing on AI, Web Dev, and Engineering.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO Summary (unpublished)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Suggested slug:&lt;/strong&gt; /blog/autonomous-ai-agents-production-gap-2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meta description: "&lt;/strong&gt; Everyone's building autonomous AI agents in 2026 — but only 23% reach production. The demo-to-production gap, why agents fail, and the playbook the winners use."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary keyword:&lt;/strong&gt; autonomous AI agents 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secondary keywords:&lt;/strong&gt; AI agents for business, AI agents in production, agentic AI 2026, why AI agents fail, human-in-the-loop agents, vertical AI agents, AI agent reliability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GEO hooks:&lt;/strong&gt; "What an Autonomous AI Agent That Runs the Business Actually Means", "Why 77% of AI Agents Never Reach Production", "What the 23% Do Differently"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal links:&lt;/strong&gt; agentic-ai-enterprise-security-model, how-to-build-mcp-server, spec-driven-development-ai-agents-addy-osmani&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Featured snippet opportunity:&lt;/strong&gt; Y — the autonomy spectrum table and the "Why 77% never reach production" list&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/autonomous-ai-agents-production-gap-2026" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;How to Build a Production MCP Server (I Added One to My Site)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/rag-vs-fine-tuning-llms-2026" rel="noopener noreferrer"&gt;RAG vs Fine-Tuning for LLMs in 2026: A Production Decision Framework With Real Tradeoffs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/agentic-browsing-pagespeed-ai-ready" rel="noopener noreferrer"&gt;Agentic Browsing in PageSpeed Insights: How to Make Your Website AI-Ready (2026)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aiagents</category>
      <category>agenticai</category>
      <category>llmengineering</category>
      <category>aiinproduction</category>
    </item>
    <item>
      <title>How to Write a CLAUDE.md That Actually Helps</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Fri, 12 Jun 2026 20:24:05 +0000</pubDate>
      <link>https://dev.to/umesh_malik/how-to-write-a-claudemd-that-actually-helps-2o3j</link>
      <guid>https://dev.to/umesh_malik/how-to-write-a-claudemd-that-actually-helps-2o3j</guid>
      <description>&lt;p&gt;Most CLAUDE.md files are useless in one of two ways: they're empty, or they're bloated with things the agent could figure out in five seconds. Both waste the one thing the file exists to spend well — the agent's attention at the start of every session.&lt;/p&gt;

&lt;p&gt;A good CLAUDE.md isn't documentation. It's a &lt;strong&gt;briefing for a senior engineer joining your team today&lt;/strong&gt; who happens to read fast and forget nothing. You don't hand that person a file tree. You tell them how to run the thing, how the pieces fit, the conventions that aren't obvious, and the traps that already bit you.&lt;/p&gt;

&lt;p&gt;I maintain CLAUDE.md files across my projects, and the difference between a good one and a bad one is the difference between an agent that moves like a teammate and one that re-derives your architecture every session. Here's what actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLAUDE.md is a context file Claude Code reads automatically&lt;/strong&gt; — treat it as a briefing, not documentation.&lt;/li&gt;
&lt;li&gt;Include the &lt;strong&gt;non-obvious&lt;/strong&gt;: commands, big-picture architecture, cross-cutting conventions, and gotchas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leave out&lt;/strong&gt; what the agent discovers instantly (file trees) or what rots (generic best-practice filler).&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;root file for the big picture&lt;/strong&gt; and &lt;strong&gt;nested files&lt;/strong&gt; for subproject-specific rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep it alive.&lt;/strong&gt; A CLAUDE.md that drifts from reality is worse than none — it actively misleads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What a CLAUDE.md actually is
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md is a Markdown file that Claude Code loads as project context at the start of a session.&lt;/strong&gt; Whatever you put in it becomes part of what the agent knows before it reads a single line of your code. That's the whole mechanism — and it's why the file is so easy to get wrong. Anything you write is "free" knowledge the agent starts with; anything you omit, it has to rediscover (and sometimes guess at) every time.&lt;/p&gt;

&lt;p&gt;So the real question isn't "what could I document?" It's &lt;strong&gt;"what does the agent most need to know that it can't quickly find out itself?"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: Optimize for the agent's first five minutes. What would a sharp new hire need to be productive — and what would they figure out on their own without being told?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What actually belongs in it
&lt;/h2&gt;

&lt;p&gt;Four things earn their place. Almost nothing else does.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Commands
&lt;/h3&gt;

&lt;p&gt;How to build, test, lint, run, and deploy — including the non-obvious incantations. If running a single test needs a specific flag, if dev mode needs two terminals, if there's a pre-commit gate, that's gold. The agent will otherwise guess, and guess wrong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pnpm dev`&lt;/span&gt; — main site on :5173
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pnpm check`&lt;/span&gt; — typecheck; MUST pass before commit
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pnpm build`&lt;/span&gt; — proves the static prerender works
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. The big-picture architecture
&lt;/h3&gt;

&lt;p&gt;Not every file — the &lt;em&gt;shape&lt;/em&gt;. How the major pieces fit, what talks to what, where the boundaries are. The things that require reading five files to understand. A short prose map or a simple diagram here saves the agent (and you) enormous time.&lt;/p&gt;

&lt;p&gt;This is where "the analytics API is the only server-side code" or "sub-apps are built separately and copied in at build time" belongs — facts that aren't visible from any single file.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cross-cutting conventions
&lt;/h3&gt;

&lt;p&gt;The rules that span the codebase and that the agent would otherwise violate: "everything ships static, never introduce SSR," "use runes only, no legacy reactive syntax," "canonical domain is X, use the central config." State them as rules, with the &lt;em&gt;why&lt;/em&gt; when it isn't obvious.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Gotchas and hard-won lessons
&lt;/h3&gt;

&lt;p&gt;The traps. "This build step fails silently if X." "Don't edit the generated covers by hand." "Running git add from inside the subdir breaks paths." These are the highest-value lines in the file because they're the ones nobody could infer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to leave out
&lt;/h2&gt;

&lt;p&gt;Every line that doesn't earn its place dilutes the ones that do. Cut:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exhaustive file trees and component lists.&lt;/strong&gt; The agent can run &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;grep&lt;/code&gt; faster than you can maintain a manifest. Describe structure only where it's non-obvious.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generic best practices.&lt;/strong&gt; "Write unit tests." "Handle errors gracefully." "Don't commit secrets." The agent already knows. These read as noise and train it to skim.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restating the code.&lt;/strong&gt; If a function's behavior is clear from its name and body, don't narrate it in CLAUDE.md.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything that rots.&lt;/strong&gt; Version numbers, line counts, "currently we're working on X" — unless you'll actually keep them current. Stale instructions are worse than missing ones because the agent &lt;em&gt;trusts&lt;/em&gt; them.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: A CLAUDE.md that's wrong is worse than one that's empty. An empty file makes the agent investigate; a wrong file makes it confidently do the wrong thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A structure that works
&lt;/h2&gt;

&lt;p&gt;You don't need a rigid template, but this shape covers the essentials without bloat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md&lt;/span&gt;

&lt;span class="gu"&gt;## What This Repo Is&lt;/span&gt;
[One paragraph: what it is, the stack, how it's organized.]

&lt;span class="gu"&gt;## Architecture / How the Pieces Fit&lt;/span&gt;
[The big picture. A diagram or short prose map. Boundaries and data flow.]

&lt;span class="gu"&gt;## Common Commands&lt;/span&gt;
[Build, test, run, deploy — including the non-obvious ones.]

&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
[Cross-cutting rules the agent must follow, with the why.]

&lt;span class="gu"&gt;## Gotchas&lt;/span&gt;
[Traps, footguns, things that bit you before.]

&lt;span class="gu"&gt;## Keeping This File Up To Date&lt;/span&gt;
[A note that this is a living map, updated as part of normal work.]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a monorepo, go further: a &lt;strong&gt;root CLAUDE.md&lt;/strong&gt; for the big picture, plus a &lt;strong&gt;nested CLAUDE.md inside each subproject&lt;/strong&gt; for rules specific to it. Claude Code reads the relevant files based on where you're working, so subproject rules stay close to the code they govern. There's also a user-level &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt; for personal preferences that follow you across every project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The kitchen sink.&lt;/strong&gt; Dumping everything turns the signal-to-noise ratio against you. Be ruthless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write-once, never-update.&lt;/strong&gt; The fastest way to make a CLAUDE.md harmful is to let it drift. Update it &lt;em&gt;in the same change&lt;/em&gt; that alters what it describes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documenting the obvious.&lt;/strong&gt; If the agent learns it in one &lt;code&gt;grep&lt;/code&gt;, it doesn't belong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No commands.&lt;/strong&gt; The single most useful section, and the one people most often skip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vague rules.&lt;/strong&gt; "Follow good practices" tells the agent nothing. "Never use &lt;code&gt;transition-all&lt;/code&gt;; always name the exact property" tells it exactly what to do.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Bootstrap with &lt;code&gt;/init&lt;/code&gt;, then trim.&lt;/strong&gt; Claude Code's &lt;code&gt;/init&lt;/code&gt; generates a first draft from your repo. Treat it as a starting point and cut it down to the non-obvious essentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write rules, not essays.&lt;/strong&gt; Short, imperative, specific. "Do X. Never Y. Because Z."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put rules near the code.&lt;/strong&gt; Root file for the big picture; nested files for subproject specifics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make it a living document.&lt;/strong&gt; Update it as part of the change that affects it — not as a someday chore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-read it as the agent would.&lt;/strong&gt; If a line wouldn't change what the agent does, or it could learn it instantly, delete it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lead with commands and gotchas.&lt;/strong&gt; They're the highest-leverage content in the file.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A CLAUDE.md is the cheapest leverage you have over how well an AI agent works in your codebase — and almost everyone either skips it or stuffs it. Write it like a briefing for a sharp teammate: the commands, the architecture that isn't obvious, the conventions that matter, the traps that bite. Cut everything they'd discover on their own. Then keep it honest.&lt;/p&gt;

&lt;p&gt;Do that, and the agent stops re-learning your project every session and starts acting like it already knows it.&lt;/p&gt;

&lt;p&gt;Going deeper on agentic coding? See &lt;a href="https://umesh-malik.com/topics/claude-code" rel="noopener noreferrer"&gt;Claude Code — Guides &amp;amp; Deep Dives&lt;/a&gt; and &lt;a href="https://umesh-malik.com/topics/ai-coding-agents" rel="noopener noreferrer"&gt;AI Coding Agents — Agentic AI for Developers&lt;/a&gt;, or the &lt;a href="https://docs.claude.com/en/docs/claude-code" rel="noopener noreferrer"&gt;official Claude Code documentation&lt;/a&gt; for the full feature set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explore more:&lt;/strong&gt; &lt;a href="https://umesh-malik.com/topics/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; · &lt;a href="https://umesh-malik.com/topics/ai-coding-agents" rel="noopener noreferrer"&gt;AI Coding Agents&lt;/a&gt; · &lt;a href="https://umesh-malik.com/topics/llm-engineering" rel="noopener noreferrer"&gt;LLM Engineering&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/how-to-write-claude-md" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-code-vs-cursor-production-work-2026" rel="noopener noreferrer"&gt;Claude Code vs Cursor vs Copilot for Real Production Work (2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/cursor-vs-claude-code-vs-copilot" rel="noopener noreferrer"&gt;Cursor vs Claude Code vs Copilot (2026): Which AI Coding Tool, for What&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/claude-sonnet-5-guide" rel="noopener noreferrer"&gt;Claude Sonnet 5: The Honest Guide — Pros, Cons, Use Cases, and What It Actually Costs (2026)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>claudemd</category>
      <category>aicodingagents</category>
      <category>developertooling</category>
    </item>
    <item>
      <title>How to Build a Production MCP Server (I Added One to My Site)</title>
      <dc:creator>Umesh Malik</dc:creator>
      <pubDate>Fri, 12 Jun 2026 20:23:33 +0000</pubDate>
      <link>https://dev.to/umesh_malik/how-to-build-a-production-mcp-server-i-added-one-to-my-site-2mbh</link>
      <guid>https://dev.to/umesh_malik/how-to-build-a-production-mcp-server-i-added-one-to-my-site-2mbh</guid>
      <description>&lt;p&gt;Most sites are built for humans to read and for crawlers to scrape. But the agents showing up now — Claude, ChatGPT, Cursor — don't want your HTML. They want to &lt;em&gt;call&lt;/em&gt; you. Parsing a page to extract three facts is wasteful and fragile; calling a typed tool that returns those three facts is neither.&lt;/p&gt;

&lt;p&gt;That's what the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; is for. And the fastest way to understand it is to build one. So I added a production MCP server to this site — it lets an agent search my posts, fetch one as clean Markdown, list my topic hubs, and read my profile — and this is exactly how I did it, with the real code.&lt;/p&gt;

&lt;p&gt;No framework, no database, about 300 lines on a Cloudflare Worker.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;MCP server exposes tools&lt;/strong&gt; (functions) that AI agents call over JSON-RPC 2.0 — turning your site from &lt;em&gt;agent-readable&lt;/em&gt; into &lt;em&gt;agent-callable&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;Streamable HTTP transport&lt;/strong&gt;: one endpoint, &lt;code&gt;POST /mcp&lt;/code&gt;, that speaks JSON-RPC. A &lt;strong&gt;stateless&lt;/strong&gt; server that returns plain JSON is fully spec-compliant and the easiest to run.&lt;/li&gt;
&lt;li&gt;You need exactly four method handlers: &lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;tools/list&lt;/code&gt;, &lt;code&gt;tools/call&lt;/code&gt;, and &lt;code&gt;ping&lt;/code&gt; — plus a no-op for notifications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You don't need new infrastructure.&lt;/strong&gt; Back your tools with assets you already publish (a JSON feed, your Markdown pages). One source of truth, nothing to sync.&lt;/li&gt;
&lt;li&gt;Make it discoverable with a manifest at a well-known URL, an entry in your API catalog, and a &lt;code&gt;Link&lt;/code&gt; header.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is an MCP server?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;An MCP server is a small service that exposes tools an AI agent can invoke over a standard protocol.&lt;/strong&gt; The protocol is JSON-RPC 2.0; the "tools" are named functions with a JSON-Schema for their arguments. When an agent connects, it asks the server "what can you do?" (&lt;code&gt;tools/list&lt;/code&gt;), gets back a list of tools, then calls them (&lt;code&gt;tools/call&lt;/code&gt;) and receives structured results.&lt;/p&gt;

&lt;p&gt;Think of it as a typed API designed specifically for language models. Where a REST API is built for your frontend, an MCP server is built for an agent's reasoning loop: the descriptions are written for a model to read, the inputs are schema-validated, and errors are reported in a way the model can recover from.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: REST is for your app. MCP is for the agent. The difference isn't the wire format — it's that every field is written to be understood by a model, not a developer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why build one for your own site
&lt;/h2&gt;

&lt;p&gt;Search and chat are moving inside agents. When someone asks Claude or ChatGPT about a topic you've written about, the model is far more likely to use you well if it can call a &lt;code&gt;search_posts&lt;/code&gt; tool than if it has to guess your URL structure and scrape rendered HTML.&lt;/p&gt;

&lt;p&gt;Three concrete wins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Precision over scraping.&lt;/strong&gt; A tool returns exactly the fields the agent needs — title, URL, summary — with no markup noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You control the surface.&lt;/strong&gt; You decide what's callable and what each tool returns. That's a far stronger signal than hoping a crawler parses your page correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It compounds with the rest of your AI-readiness.&lt;/strong&gt; An MCP server sits naturally alongside &lt;code&gt;llms.txt&lt;/code&gt;, structured data, and an API catalog as part of making your site first-class for agents.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It will not, on its own, make every agent "pick" your site — that still depends on relevance and authority. But it removes every technical reason an agent &lt;em&gt;couldn't&lt;/em&gt; use you well.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're building
&lt;/h2&gt;

&lt;p&gt;Four read-only tools:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Backed by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_posts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ranked search over blog posts&lt;/td&gt;
&lt;td&gt;a JSON feed I already publish&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_post&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns one post as clean Markdown&lt;/td&gt;
&lt;td&gt;prerendered &lt;code&gt;/blog/&amp;lt;slug&amp;gt;.md&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_topics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lists curated topic hubs&lt;/td&gt;
&lt;td&gt;a small constant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_profile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the author profile&lt;/td&gt;
&lt;td&gt;my existing &lt;code&gt;llms.txt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The whole thing runs on a &lt;strong&gt;Cloudflare Worker&lt;/strong&gt; as a &lt;strong&gt;stateless&lt;/strong&gt; JSON-RPC handler. Stateless matters: with no session to track, every request is self-contained, which is the simplest possible thing to host and scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — The transport
&lt;/h2&gt;

&lt;p&gt;MCP defines two transports. For local tools you use stdio; for a &lt;strong&gt;remote&lt;/strong&gt; server you use &lt;strong&gt;Streamable HTTP&lt;/strong&gt; — a single endpoint that accepts JSON-RPC messages over &lt;code&gt;POST&lt;/code&gt;. The spec lets the server reply with either an SSE stream or a plain JSON body. A read-only server has no streaming notifications to push, so &lt;strong&gt;plain JSON is the right call&lt;/strong&gt; and the simplest.&lt;/p&gt;

&lt;p&gt;Every MCP message is JSON-RPC 2.0. Two tiny helpers cover all our responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rpcResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rpcError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoint parses the POST body, routes on &lt;code&gt;method&lt;/code&gt;, and returns the JSON-RPC response. Requests carry an &lt;code&gt;id&lt;/code&gt;; &lt;strong&gt;notifications don't&lt;/strong&gt; — and a notification gets no response body, just a &lt;code&gt;202 Accepted&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Define your tools
&lt;/h2&gt;

&lt;p&gt;A tool is metadata plus an input schema. The &lt;code&gt;description&lt;/code&gt; is not for you — it's the prompt the model reads to decide whether and how to call the tool. Write it like you're briefing a smart colleague who can't see your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TOOLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search_posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Search blog posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Full-text search across the blog (titles, summaries, tags). Returns matching &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts with slug, title, URL, summary, tags and publish date. Use for topics &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;like AI engineering, LLMs, RAG, Claude Code, or web development.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Search terms.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Max results (default 10, max 30).&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// get_post, list_topics, get_profile ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Key insight&lt;/strong&gt;: Tool descriptions are prompt engineering. A vague description means the model calls the wrong tool or skips it. Spell out &lt;em&gt;when&lt;/em&gt; to use it and &lt;em&gt;what it returns&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3 — Back tools with data you already have
&lt;/h2&gt;

&lt;p&gt;This is the part most tutorials overcomplicate. &lt;strong&gt;You don't need a database.&lt;/strong&gt; I back every tool with assets the site already prerenders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;search_posts&lt;/code&gt; fetches my existing &lt;code&gt;/feed.json&lt;/code&gt; (a JSON Feed of every post) and ranks it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_post&lt;/code&gt; fetches the already-generated &lt;code&gt;/blog/&amp;lt;slug&amp;gt;.md&lt;/code&gt; Markdown variant.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_profile&lt;/code&gt; returns my &lt;code&gt;llms.txt&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a Cloudflare Worker you reach those via the assets binding, so there's one source of truth and nothing to keep in sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;searchPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/feed.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Post index unavailable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="c1"&gt;// weight title hits over tags over summary&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;terms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always &lt;strong&gt;validate inputs&lt;/strong&gt; before using them. &lt;code&gt;get_post&lt;/code&gt; takes a slug straight from the model, so it gets a strict regex check before it ever touches a path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SLUG_RE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9&lt;/span&gt;&lt;span class="se"&gt;][&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9-&lt;/span&gt;&lt;span class="se"&gt;]{0,120}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;SLUG_RE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Invalid slug "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;". Use a slug from search_posts.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 — Handle the protocol
&lt;/h2&gt;

&lt;p&gt;The router is small. Four real methods, plus notification handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isNotification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;initialize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rpcResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;protocolVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2025-06-18&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;listChanged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;serverInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-site&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tools for querying my blog and profile.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ping&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rpcResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tools/list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rpcResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TOOLS&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tools/call&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rpcResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="na"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Report tool errors IN-BAND so the model can see and react to them.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rpcResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="na"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isNotification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// ignore unknown notifications&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rpcError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;32601&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Method not found: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things people get wrong here, and they all live in this function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;initialize&lt;/code&gt; must echo a &lt;code&gt;protocolVersion&lt;/code&gt;&lt;/strong&gt; the client understands and declare your &lt;code&gt;capabilities&lt;/code&gt;. Skip it and the handshake fails before any tool runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool failures are not protocol errors.&lt;/strong&gt; A bad slug returns a normal result with &lt;code&gt;isError: true&lt;/code&gt; and a message — so the model reads the failure and retries — &lt;em&gt;not&lt;/em&gt; a JSON-RPC &lt;code&gt;error&lt;/code&gt;. Reserve &lt;code&gt;error&lt;/code&gt; (&lt;code&gt;-32601&lt;/code&gt;, &lt;code&gt;-32700&lt;/code&gt;, etc.) for malformed protocol.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications get no response.&lt;/strong&gt; If &lt;code&gt;notifications/initialized&lt;/code&gt; arrives, acknowledge with &lt;code&gt;202&lt;/code&gt; and an empty body. Returning a JSON-RPC object for a notification breaks strict clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5 — Make it discoverable
&lt;/h2&gt;

&lt;p&gt;A server nobody can find is useless. Advertise it three ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A manifest&lt;/strong&gt; at &lt;code&gt;/.well-known/mcp&lt;/code&gt; — name, endpoint, transport, and the tool list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An entry in your API catalog&lt;/strong&gt; (&lt;code&gt;/.well-known/api-catalog&lt;/code&gt;, RFC 9727) pointing at the manifest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;Link&lt;/code&gt; header&lt;/strong&gt; on your HTML responses: &lt;code&gt;Link: &amp;lt;/.well-known/mcp&amp;gt;; rel="service-desc"; type="application/json"&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then point an MCP client straight at &lt;code&gt;https://yoursite.com/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing your MCP server
&lt;/h2&gt;

&lt;p&gt;You don't need a fancy client to test — &lt;code&gt;curl&lt;/code&gt; speaks JSON-RPC fine. List the tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://yoursite.com/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"tools/list"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://yoursite.com/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":2,"method":"tools/call",
       "params":{"name":"search_posts","arguments":{"query":"RAG","limit":3}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Work through the lifecycle: &lt;code&gt;initialize&lt;/code&gt; → &lt;code&gt;tools/list&lt;/code&gt; → &lt;code&gt;tools/call&lt;/code&gt;, then confirm the edges — an invalid slug returns &lt;code&gt;isError: true&lt;/code&gt;, a notification returns &lt;code&gt;202&lt;/code&gt; with no body, an unknown method returns &lt;code&gt;-32601&lt;/code&gt;, and a &lt;code&gt;GET&lt;/code&gt; returns &lt;code&gt;405&lt;/code&gt;. If all of those behave, real clients will too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Treating tool errors as protocol errors.&lt;/strong&gt; The single most common bug. Use &lt;code&gt;isError: true&lt;/code&gt; in the result; keep JSON-RPC &lt;code&gt;error&lt;/code&gt; for malformed requests only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building stateful sessions you don't need.&lt;/strong&gt; A read-only server should be stateless. Sessions add complexity and a scaling headache for zero benefit here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thin tool descriptions.&lt;/strong&gt; "Search" tells the model nothing. Say what it searches, what it returns, and when to reach for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicating your data.&lt;/strong&gt; Don't copy your content into the server. Point tools at what you already publish so there's nothing to keep in sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgetting CORS.&lt;/strong&gt; Browser-based MCP clients need it. Handle &lt;code&gt;OPTIONS&lt;/code&gt; and allow the &lt;code&gt;Mcp-Session-Id&lt;/code&gt; / &lt;code&gt;Mcp-Protocol-Version&lt;/code&gt; headers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stateless first.&lt;/strong&gt; Reach for sessions only when a tool genuinely needs continuity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate every argument.&lt;/strong&gt; Treat tool inputs like any untrusted input — schema plus a guard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write descriptions as prompts.&lt;/strong&gt; They're the only thing the model sees when deciding to call a tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reuse existing assets.&lt;/strong&gt; Your feed, your Markdown, your profile file — one source of truth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advertise it.&lt;/strong&gt; Manifest + API catalog + &lt;code&gt;Link&lt;/code&gt; header, so agents can find it without being told.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the edges, not just the happy path.&lt;/strong&gt; Notifications, unknown methods, invalid inputs, wrong HTTP verb.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;An MCP server is less code than you expect — a JSON-RPC router, four well-described tools, and a thin layer over content you already ship. The mental shift is the real work: stop thinking of your site as pages to be read and start thinking of it as &lt;strong&gt;capabilities to be called&lt;/strong&gt;. That's the interface agents actually want.&lt;/p&gt;

&lt;p&gt;I built mine on a Cloudflare Worker in an afternoon, and it now sits alongside the rest of this site's agent-readiness as a first-class surface. If you've already got a JSON feed and Markdown pages, you're most of the way there.&lt;/p&gt;

&lt;p&gt;If this was useful, go deeper next: see how the pieces fit together across &lt;a href="https://umesh-malik.com/topics/llm-engineering" rel="noopener noreferrer"&gt;LLM Engineering — RAG, Fine-Tuning &amp;amp; Production LLMs&lt;/a&gt; and &lt;a href="https://umesh-malik.com/topics/ai-coding-agents" rel="noopener noreferrer"&gt;AI Coding Agents — Agentic AI for Developers&lt;/a&gt;, or read the &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;official MCP specification&lt;/a&gt; for the full protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explore more:&lt;/strong&gt; &lt;a href="https://umesh-malik.com/topics/ai-coding-agents" rel="noopener noreferrer"&gt;AI Coding Agents&lt;/a&gt; · &lt;a href="https://umesh-malik.com/topics/llm-engineering" rel="noopener noreferrer"&gt;LLM Engineering&lt;/a&gt; · &lt;a href="https://umesh-malik.com/topics/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://umesh-malik.com/blog/how-to-build-mcp-server" rel="noopener noreferrer"&gt;umesh-malik.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep reading on umesh-malik.com:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/deploy-mcp-server-cloudflare-workers" rel="noopener noreferrer"&gt;Deploy an MCP Server on Cloudflare Workers (Free, Stateless, at the Edge)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/autonomous-ai-agents-production-gap-2026" rel="noopener noreferrer"&gt;AI Agents That Run the Business in 2026: Why 77% Never Reach Production (and What the 23% Do Differently)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://umesh-malik.com/blog/agentic-browsing-pagespeed-ai-ready" rel="noopener noreferrer"&gt;Agentic Browsing in PageSpeed Insights: How to Make Your Website AI-Ready (2026)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mcp</category>
      <category>modelcontextprotocol</category>
      <category>aiagents</category>
      <category>cloudflareworkers</category>
    </item>
  </channel>
</rss>
