<?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: Zied Mnif</title>
    <description>The latest articles on DEV Community by Zied Mnif (@zied_mnif_2f4225fb8d70342).</description>
    <link>https://dev.to/zied_mnif_2f4225fb8d70342</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%2F3967191%2F2a75c276-62f6-4d7a-84dd-928dd7a334ea.jpg</url>
      <title>DEV Community: Zied Mnif</title>
      <link>https://dev.to/zied_mnif_2f4225fb8d70342</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zied_mnif_2f4225fb8d70342"/>
    <language>en</language>
    <item>
      <title>The 20% of your AI agent's tool schemas that's pure cruft (and the one-liner to strip it)</title>
      <dc:creator>Zied Mnif</dc:creator>
      <pubDate>Sun, 07 Jun 2026 20:08:19 +0000</pubDate>
      <link>https://dev.to/zied_mnif_2f4225fb8d70342/the-20-of-your-ai-agents-tool-schemas-thats-pure-cruft-and-the-one-liner-to-strip-it-5agn</link>
      <guid>https://dev.to/zied_mnif_2f4225fb8d70342/the-20-of-your-ai-agents-tool-schemas-thats-pure-cruft-and-the-one-liner-to-strip-it-5agn</guid>
      <description>&lt;p&gt;Your AI agent re-sends every tool's JSON schema on every single turn. A surprising chunk of that — often ~20% — is &lt;strong&gt;non-semantic cruft&lt;/strong&gt;: tokens that carry zero tool-selection signal, billed on every request.&lt;/p&gt;

&lt;p&gt;I found this measuring the per-turn token cost of 13 real agents (&lt;a href="https://mnifzied-create.github.io/agentloop/token-tax/" rel="noopener noreferrer"&gt;full study&lt;/a&gt;). Here's where the waste hides, and the one-liner to remove it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the cruft comes from
&lt;/h2&gt;

&lt;p&gt;When you generate tool schemas from code, the converter quietly adds fields the model doesn't need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pydantic&lt;/strong&gt; adds a "title" to every field via model_json_schema().&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;zod-to-json-schema&lt;/strong&gt; appends "$schema" and "additionalProperties" to everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pretty-printing&lt;/strong&gt; alone swings a tool ~20%: the Fetch MCP tool is 236 tokens compact vs 288 pretty-printed — pure whitespace, re-sent every turn.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of it helps the model pick the right tool. On a 20-tool agent that's easily hundreds of wasted tokens per turn, paid on every message.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix (Node / TypeScript)
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const strip = (o) =&amp;gt; Array.isArray(o) ? o.map(strip)
  : (o &amp;amp;&amp;amp; typeof o === "object")
    ? Object.fromEntries(Object.entries(o)
        .filter(([k]) =&amp;gt; !["$schema", "additionalProperties", "title"].includes(k))
        .map(([k, v]) =&amp;gt; [k, strip(v)]))
    : o;

// compact, cruft-free — send this to the model
const tools_slim = JSON.stringify(strip(tools));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  The fix (Python)
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json

def strip(o):
    if isinstance(o, list):  return [strip(x) for x in o]
    if isinstance(o, dict):  return {k: strip(v) for k, v in o.items()
                                     if k not in ("$schema", "additionalProperties", "title")}
    return o

tools_slim = json.dumps(strip(tools), separators=(",", ":"))  # compact, cruft-free
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;One caveat: additionalProperties:false is occasionally intentional (strict validation) — drop it from the strip list if you rely on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure before/after
&lt;/h2&gt;

&lt;p&gt;Want to see exactly how much your schemas carry (and which tool is worst)? The free &lt;a href="https://mnifzied-create.github.io/agentloop/" rel="noopener noreferrer"&gt;Agent Token Profiler&lt;/a&gt; flags this cruft automatically and shows the per-turn cost across models — paste your tools, no signup, no key.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;From &lt;a href="https://github.com/mnifzied-create/agentloop" rel="noopener noreferrer"&gt;AgentLoop&lt;/a&gt;, a readable MIT Claude-agent starter. Production token-metering + multi-provider routing live in &lt;a href="https://ko-fi.com/s/7306cb3140" rel="noopener noreferrer"&gt;AgentLoop Pro&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>performance</category>
    </item>
    <item>
      <title>I measured the token cost of 13 real AI agents (GitHub's MCP server alone is 3,546 tokens/turn)</title>
      <dc:creator>Zied Mnif</dc:creator>
      <pubDate>Sun, 07 Jun 2026 18:07:05 +0000</pubDate>
      <link>https://dev.to/zied_mnif_2f4225fb8d70342/i-measured-the-token-cost-of-13-real-ai-agents-githubs-mcp-server-alone-is-3546-tokensturn-50b7</link>
      <guid>https://dev.to/zied_mnif_2f4225fb8d70342/i-measured-the-token-cost-of-13-real-ai-agents-githubs-mcp-server-alone-is-3546-tokensturn-50b7</guid>
      <description>&lt;p&gt;Every AI agent re-sends its entire system prompt &lt;strong&gt;and every tool/function schema&lt;/strong&gt; on &lt;em&gt;every single turn&lt;/em&gt;. That fixed payload is billed as input tokens on each request — invisibly — until the bill arrives. I measured exactly how much across &lt;strong&gt;13 real open-source agents and MCP servers (79 tools total)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The headline
&lt;/h2&gt;

&lt;p&gt;The GitHub MCP server carries &lt;strong&gt;3,546 tokens of tool schemas every turn&lt;/strong&gt; — about &lt;strong&gt;$12.89 per 1,000 turns&lt;/strong&gt; on Claude Sonnet, paid &lt;em&gt;before the model reads a word of the user's question&lt;/em&gt;. 26 tools is all it takes to make the plumbing cost more than the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I found
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Median per-turn overhead: 547 tokens&lt;/strong&gt; — against a realistic 57-token user request, that's &lt;strong&gt;9.6× larger than the question itself&lt;/strong&gt;, ~91% of the input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The fattest single tool is 827 tokens&lt;/strong&gt; (&lt;code&gt;sequentialthinking&lt;/code&gt; from the official MCP servers repo) — bigger than the &lt;em&gt;entire&lt;/em&gt; toolset of 8 of the 12 tool-providers measured.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A 35× spread&lt;/strong&gt; separates the leanest agent (101 tok) from the most bloated (3,546). Same pricing, same tokenizer — bloat is a design choice, not a tax of nature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~20% of typical schema bytes is non-semantic cruft&lt;/strong&gt; — pretty-printing, Pydantic's auto-added &lt;code&gt;title&lt;/code&gt;, zod's &lt;code&gt;$schema&lt;/code&gt;/&lt;code&gt;additionalProperties&lt;/code&gt;. Invisible tokens nobody wrote, billed every turn.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full dataset — every number reproducible, repos pinned to commits: &lt;strong&gt;&lt;a href="https://mnifzied-create.github.io/agentloop/token-tax/" rel="noopener noreferrer"&gt;The Hidden Token Tax&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure your own agent
&lt;/h2&gt;

&lt;p&gt;I built a free, in-browser tool that does this for your setup — paste your system prompt + tool schemas + sample outputs and it shows the per-turn breakdown, a $ projection across Claude / Haiku / OpenRouter, and flags the tool inflating your context (including the serialization cruft): &lt;strong&gt;&lt;a href="https://mnifzied-create.github.io/agentloop/" rel="noopener noreferrer"&gt;Agent Token Profiler&lt;/a&gt;&lt;/strong&gt; — no signup, no key.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to fix it
&lt;/h2&gt;

&lt;p&gt;Strip the cruft (compact JSON, drop auto-added fields), trim tool descriptions to what actually aids tool selection, load tools on demand (progressive disclosure), and route easy turns to a cheaper model like Haiku. The schema overhead is the half nobody meters — until they do.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Measured while building &lt;a href="https://github.com/mnifzied-create/agentloop" rel="noopener noreferrer"&gt;AgentLoop&lt;/a&gt;, a readable MIT Claude-agent starter. The production patterns — token metering, retries, multi-provider routing — are drop-in code in &lt;a href="https://ko-fi.com/s/7306cb3140" rel="noopener noreferrer"&gt;AgentLoop Pro&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Claude agent vs Claude Code: which one are you actually building?</title>
      <dc:creator>Zied Mnif</dc:creator>
      <pubDate>Fri, 05 Jun 2026 00:02:54 +0000</pubDate>
      <link>https://dev.to/zied_mnif_2f4225fb8d70342/claude-agent-vs-claude-code-which-one-are-you-actually-building-5ek9</link>
      <guid>https://dev.to/zied_mnif_2f4225fb8d70342/claude-agent-vs-claude-code-which-one-are-you-actually-building-5ek9</guid>
      <description>&lt;p&gt;Search "claude agent boilerplate" and you'll drown in Claude &lt;strong&gt;Code&lt;/strong&gt; results — the agentic CLI, &lt;code&gt;CLAUDE.md&lt;/code&gt; files, slash commands, hooks. Great tools. But none of that is what you want if you're trying to &lt;strong&gt;build your own agent&lt;/strong&gt; on the Anthropic SDK.&lt;/p&gt;

&lt;p&gt;Here's the disambiguation, and the ~40 lines that are actually the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two different "Claude agents"
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; — Anthropic's agentic &lt;em&gt;coding CLI&lt;/em&gt;. You configure it; you don't build it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An agent &lt;em&gt;you&lt;/em&gt; build&lt;/strong&gt; — your app calls the Anthropic SDK in a loop: the model asks for a tool, you run it, feed the result back, repeat until it's done. &lt;em&gt;That loop is the agent.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most "agent frameworks" just hide that loop from you. It's small enough that you don't need them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole loop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Anthropic&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@anthropic-ai/sdk&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userText&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="nx"&gt;runners&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;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;userText&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claude-sonnet-4-6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1024&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="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assistant&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// No tool requested -&amp;gt; the model is done.&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool_use&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="k"&gt;return&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;content&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;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="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&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;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;text&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="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Run every tool it asked for this turn, collect one result each.&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="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;for &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;block&lt;/span&gt; &lt;span class="k"&gt;of&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;content&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool_use&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="nx"&gt;out&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;runners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;block&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;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="s2"&gt;tool_result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tool_use_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;out&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="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&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="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;That's it. The four things people get wrong:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Append the assistant turn &lt;strong&gt;verbatim&lt;/strong&gt; — so the model sees its own tool request on the next call.&lt;/li&gt;
&lt;li&gt;One &lt;code&gt;tool_result&lt;/code&gt; per request, matched by &lt;code&gt;tool_use_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Send all results back in a &lt;strong&gt;single&lt;/strong&gt; user message.&lt;/li&gt;
&lt;li&gt;Cap the turns so a confused model can't loop forever.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A runnable starter
&lt;/h2&gt;

&lt;p&gt;If you'd rather start from a streaming Next.js app you can deploy in one command, I open-sourced exactly this (MIT): &lt;strong&gt;&lt;a href="https://github.com/mnifzied-create/agentloop" rel="noopener noreferrer"&gt;AgentLoop&lt;/a&gt;&lt;/strong&gt; — the whole agent in ~150 readable lines, no framework. Clone it, add a key, deploy.&lt;/p&gt;

&lt;p&gt;There's a $29 Pro pack for the patterns you hit in production (parallel tools, persistent memory, retries, rate limiting, approval gates, evals, token metering, and a multi-provider seam so it runs on any model) — but the free core stands alone forever.&lt;/p&gt;

&lt;p&gt;Build the loop. Own it. Don't import a black box.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>claude</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your 150-line AI agent works in the demo. Here's what breaks in production.</title>
      <dc:creator>Zied Mnif</dc:creator>
      <pubDate>Thu, 04 Jun 2026 11:54:30 +0000</pubDate>
      <link>https://dev.to/zied_mnif_2f4225fb8d70342/your-150-line-ai-agent-works-in-the-demo-heres-what-breaks-in-production-g99</link>
      <guid>https://dev.to/zied_mnif_2f4225fb8d70342/your-150-line-ai-agent-works-in-the-demo-heres-what-breaks-in-production-g99</guid>
      <description>&lt;p&gt;A minimal agent — call the model, run the tool it asks for, feed the result back, repeat — is genuinely complete for a demo. I wrote one in ~150 readable lines: &lt;a href="https://github.com/mnifzied-create/agentloop" rel="noopener noreferrer"&gt;https://github.com/mnifzied-create/agentloop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But the moment real users hit it, eight things break. None of them need a framework — each is a small, readable layer on top of the loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The model asks for three tools at once — and you run them one at a time.&lt;/strong&gt; Wrap the tool calls in &lt;code&gt;Promise.all&lt;/code&gt;. Parallel by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. One flaky API call kills the whole turn.&lt;/strong&gt; Wrap each tool in a retry with backoff, and return the error &lt;em&gt;as a string&lt;/em&gt; to the model instead of throwing — it can recover on the next step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It forgets everything between requests.&lt;/strong&gt; Persist threads. Node's built-in &lt;code&gt;node:sqlite&lt;/code&gt; is enough — no service, no native build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. One user (or a runaway loop) runs up your bill.&lt;/strong&gt; A token-bucket rate limiter, per user / IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The agent deletes a record / sends an email / charges a card — with no confirmation.&lt;/strong&gt; Wrap irreversible tools in a human-in-the-loop approval gate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. You tweak the prompt and three behaviors silently regress.&lt;/strong&gt; A tiny eval harness with pass/fail cases you run in CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. One agent juggling twelve tools gets confused.&lt;/strong&gt; Expose a whole agent as a single tool — a sub-agent — and let a parent delegate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. You're regex-parsing the model's prose for data.&lt;/strong&gt; Force a tool call whose &lt;code&gt;input_schema&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; your output type. Typed JSON, no parsing.&lt;/p&gt;

&lt;p&gt;That's the entire gap between "works in the demo" and "works in production" — and every item is a small composable piece you can read top to bottom, not magic hidden in a dependency.&lt;/p&gt;

&lt;p&gt;The free core (the loop) and these eight patterns are all in the repo — read every line: &lt;a href="https://github.com/mnifzied-create/agentloop" rel="noopener noreferrer"&gt;https://github.com/mnifzied-create/agentloop&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The point isn't the code. It's that you can &lt;em&gt;own&lt;/em&gt; an agent instead of importing one.&lt;/p&gt;

&lt;p&gt;What breaks for you in production that isn't on this list?&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>javascript</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>The AI agent loop, in ~150 lines (no framework)</title>
      <dc:creator>Zied Mnif</dc:creator>
      <pubDate>Wed, 03 Jun 2026 22:19:56 +0000</pubDate>
      <link>https://dev.to/zied_mnif_2f4225fb8d70342/the-ai-agent-loop-in-150-lines-no-framework-3kla</link>
      <guid>https://dev.to/zied_mnif_2f4225fb8d70342/the-ai-agent-loop-in-150-lines-no-framework-3kla</guid>
      <description>&lt;p&gt;"AI agent" sounds like it needs a framework. It doesn't. Strip away the branding and an agent is one loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send the conversation to the model.&lt;/li&gt;
&lt;li&gt;If it asks to use a tool, run the tool.&lt;/li&gt;
&lt;li&gt;Feed the result back.&lt;/li&gt;
&lt;li&gt;Repeat until it answers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the whole thing with Claude:&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&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;step&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;MAX_STEPS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;step&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;system&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="nx"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&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="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;            &lt;span class="c1"&gt;// stream tokens out&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;final&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;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finalMessage&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;final&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool_use&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// model answered, we're done&lt;/span&gt;

  &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assistant&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;final&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&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="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &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;block&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;final&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool_use&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="nx"&gt;result&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;runTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&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;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// your code&lt;/span&gt;
      &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="s2"&gt;tool_result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tool_use_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&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;content&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="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;results&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;    &lt;span class="c1"&gt;// feed the results back, loop&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Tool use is just: the model emits a &lt;code&gt;tool_use&lt;/code&gt; block, you run the matching function, you hand back a &lt;code&gt;tool_result&lt;/code&gt;. Streaming is just forwarding token deltas as they arrive.&lt;/p&gt;

&lt;p&gt;I packaged this into a runnable starter — Next.js, a streaming UI, two example tools, one-command Vercel deploy. Clone it, add a key, ship:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/mnifzied-create/agentloop" rel="noopener noreferrer"&gt;https://github.com/mnifzied-create/agentloop&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The free core is the loop above, MIT-licensed. If you want the production patterns next — running multiple tools in parallel, persistent memory on SQLite, retries, an eval harness, human-in-the-loop approval, sub-agents — I wrote those up as a small, equally-readable kit too (linked in the repo).&lt;/p&gt;

&lt;p&gt;The point isn't the kit, though. It's that you can read every line of an agent in one sitting. Go look.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
