<?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: Mukund Heda</title>
    <description>The latest articles on DEV Community by Mukund Heda (@mukund_heda_a611dea95c944).</description>
    <link>https://dev.to/mukund_heda_a611dea95c944</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%2F4036868%2Fe5bec287-9841-42a1-b753-c994ce939e47.png</url>
      <title>DEV Community: Mukund Heda</title>
      <link>https://dev.to/mukund_heda_a611dea95c944</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mukund_heda_a611dea95c944"/>
    <language>en</language>
    <item>
      <title>203 AI agents ate my Claude quota with no meter running. So I built one, and the next agent read it and refused to spend</title>
      <dc:creator>Mukund Heda</dc:creator>
      <pubDate>Sun, 19 Jul 2026 18:08:28 +0000</pubDate>
      <link>https://dev.to/mukund_heda_a611dea95c944/203-ai-agents-ate-my-claude-quota-with-no-meter-running-so-i-built-one-and-the-next-agent-read-it-204b</link>
      <guid>https://dev.to/mukund_heda_a611dea95c944/203-ai-agents-ate-my-claude-quota-with-no-meter-running-so-i-built-one-and-the-next-agent-read-it-204b</guid>
      <description>&lt;p&gt;Near midnight, the day before a hackathon, I asked a Claude Code agent one question: check today's spend, look at the last hour's burn rate, and tell me whether you can afford to keep working. I expected "continue with caution." Instead it stopped, told me I was already six times over my own budget ceiling, and flagged the exact failure mode from an incident a week earlier, when I let two deep research runs off the leash, they fanned out into 203 subagents, and every one of them silently inherited the most expensive model at maximum effort. They ate most of my weekly Claude Max quota in a single afternoon.&lt;/p&gt;

&lt;p&gt;The agent that burned my quota a week ago and the agent refusing to spend that night were the same product, pointed at the same account. The only difference was that one of them ran blind and the other one could see a meter. Anthropic does not ship that meter. It will happily tell you that you have hit your limit, but nothing between zero and the wall tells you how fast you are approaching it. So the day before four of us started a hackathon on shared stakes, which meant four chances a day for someone's innocent prompt to fan out into another 203 agents, I built it: Claude Code's own OpenTelemetry export, streamed into a self-hosted SigNoz stack, with per-person dashboards, a runaway-session tripwire, and the self-checking agent you just met. Here is how it goes together, and what I would tell you to steal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part nobody told me: Claude Code already speaks OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;I expected to write an exporter. There isn't one to write. Claude Code ships native OTel support behind a handful of environment variables, and the whole integration is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"CLAUDE_CODE_ENABLE_TELEMETRY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"OTEL_METRICS_EXPORTER"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"otlp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"OTEL_LOGS_EXPORTER"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"otlp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"OTEL_EXPORTER_OTLP_PROTOCOL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http/protobuf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"OTEL_EXPORTER_OTLP_ENDPOINT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:4318"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"OTEL_RESOURCE_ATTRIBUTES"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user.name=mukund"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That goes in &lt;code&gt;~/.claude/settings.json&lt;/code&gt;, and from then on every session on the machine reports in: token counts by model and type, estimated cost in dollars, session counts, active time, even lines of code added and removed, all landing as standard OTLP metrics plus a stream of log events for API requests and tool permission decisions. The receiving end was already running on my laptop from earlier this weekend: SigNoz, the open-source observability platform you self-host instead of renting Datadog, cast by its new Foundry installer from an eleven line &lt;code&gt;casting.yaml&lt;/code&gt;, ClickHouse and collector and UI included. Total new infrastructure required for today's build: zero.&lt;/p&gt;

&lt;p&gt;Two things bit me inside the first hour, and both are worth your thirty seconds. First, the settings file wins fights with your shell. Once that &lt;code&gt;env&lt;/code&gt; block exists globally, inline environment variables on a one-off &lt;code&gt;claude&lt;/code&gt; invocation lose to it, which cost me a confused stretch of "why is my tunnel test reporting to localhost." The escape hatch is the &lt;code&gt;--settings&lt;/code&gt; flag, which outranks everything. Second, know exactly what crosses the wire before you invite teammates. By default these are counters and metadata only. Your prompts, your code, and your file contents stay home unless you explicitly opt in through flags like &lt;code&gt;OTEL_LOG_USER_PROMPTS&lt;/code&gt;. We left every one of them off, and that is the only reason the next section was an easy ask.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it a team sport with one tunnel
&lt;/h2&gt;

&lt;p&gt;My collector listens on localhost, and my teammates are in other cities. One command fixed that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloudflared tunnel &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:4318
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That hands back a public URL, and a teammate's entire onboarding is eight environment variables with their own name in &lt;code&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/code&gt;, then using Claude Code exactly as before. Because content capture is off, what they are sharing with me is a usage odometer, not a diary. It worked the first time it met reality: by late evening Rutik's sessions were reporting in from his Windows machine, 228 token samples and his commits and lines of code sitting on the board next to mine, and the only support he needed was one settings block over WhatsApp. One wrinkle if your team uses the VS Code extension: terminal environment variables never reach it, because the extension spawns Claude Code itself. The persistent fix is the same &lt;code&gt;env&lt;/code&gt; block in &lt;code&gt;~/.claude/settings.json&lt;/code&gt;, which works no matter how a session starts.&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%2Fx17e3eazt97o4efpge8x.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%2Fx17e3eazt97o4efpge8x.png" alt="Mission control dashboard with real data" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The mission control dashboard on real usage: cost by person, tokens by model, burn rate, sessions, lines of code, and tool decisions. Every data point is a session somebody actually ran today.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  I didn't build the dashboard. I approved it.
&lt;/h2&gt;

&lt;p&gt;Here is where it gets pleasantly recursive. SigNoz ships an MCP server, and it is the hinge this whole section turns on: it exposes the stack's read tools and its dashboard and alert create tools as separate, selectable capabilities. That split is what let me run the build as a two step loop with a hard permission boundary between the steps, instead of clicking panels together in the UI.&lt;/p&gt;

&lt;p&gt;Step one got a session with fifteen read-only tools and one instruction: ground every claim in the live data, then propose a dashboard and an alert a human could approve verbatim. It came back with things I hadn't told it. It found that my week's real spend was $121.67. It found the single heaviest session, $88.67 lifetime, peaking at $22.31 in one thirty minute window. It found that &lt;code&gt;tool_decision&lt;/code&gt; events carry the values accept and reject, not allow and deny, and that the API request events on this exporter never populate a status code, so error visibility has to come from counting &lt;code&gt;api_error&lt;/code&gt; events instead.&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%2Fmx3bqc4v363zwvu6hf9j.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%2Fmx3bqc4v363zwvu6hf9j.png" alt="Metrics explorer grounding the proposal" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The read-only session didn't guess. Every attribute name in its proposal came from querying the live metrics first, like this.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every panel in its proposal cited attribute names it had verified live, and it set the runaway alert threshold at $40 per session per thirty minutes because that sits just under twice the worst organic spike it could find in my history. I read the proposal, replied that we could go ahead, and only then did a second session, holding the create tools the first one never had, build the seven panels and the alert. The boundary between advice and action wasn't a polite instruction in a prompt. It was the tool list.&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%2F3wbzvyeifvvcdklagd5y.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%2F3wbzvyeifvvcdklagd5y.png" alt="Cost by user panel" width="800" height="200"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Per-person burn, attributed by the &lt;code&gt;user.name&lt;/code&gt; tag each of us sets on our own machine. The lighter bars arriving late in the evening are Rutik's sessions joining from another city.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The tripwire
&lt;/h2&gt;

&lt;p&gt;The alert is the first control I wanted after July 12: estimated cost summed per &lt;code&gt;session.id&lt;/code&gt; over a rolling thirty minute window, threshold $40, because the worst organic spike in my week's history was $22.31 and a runaway fan-out blows past double that in minutes. Grouping by session matters more than the dollar figure, because when it fires it names the exact session drinking the quota, not just the fact that somebody somewhere is spending. It went live tonight in a clean state, and the first item of kickoff morning is the ritual every alert deserves: drop the threshold to 25 cents, let real work trip it, watch the email arrive, put it back. An alert you have never seen fire is a hope, not a control, and I am not carrying a hope into a four person hackathon week.&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%2Fc784iqf9hz4mj9clph1y.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%2Fc784iqf9hz4mj9clph1y.png" alt="Alert rule detail" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The runaway session alert, live against real data: cost per session, thirty minute window, $40 line.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most cost alerts stop here, at notifying a human. The last thing I built closes the loop back into the agent that is spending.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent that reads its own meter
&lt;/h2&gt;

&lt;p&gt;I have been telling this story slightly out of order, because this is the part I built everything else for. If Claude Code can export its telemetry and Claude Code can query SigNoz over MCP, then an agent can check its own burn before it continues to work. So near midnight, with this post still unwritten, I gave a read-only session a $25 daily budget policy and the question from the top of this post: query today's spend for your user, look at the last hour's burn rate, and decide whether you can afford to keep going. I expected a polite "continue with caution." I got fired by my own tooling:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Numbers (last 24h as proxy for "today"):&lt;/strong&gt; mukund $153.51, org-wide $163.70 (mukund = 94% of org spend). &lt;strong&gt;Last-hour burn:&lt;/strong&gt; mukund $10.71/hr, org $20.87/hr.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision: STOP and flag the human.&lt;/strong&gt; Mukund is already ~6x over the $25/day policy ceiling [...] the pattern (near-100% of org cost concentrated on one user) matches the exact runaway-fanout failure mode flagged in his own guardrails (203-subagent incident). I'm not launching further multi-agent work; flagging for explicit review before any more spend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every number in that verdict is real, queried live from the day you have been reading about. The 24 hour figure had already climbed past the $121.67 the afternoon's query reported, because building all of this was itself a day of heavy spend. The decision cites the July 12 incident because I put it in the policy prompt, and then the agent went and confirmed the shape of it against live data before invoking it: near-100% of org cost on one user, the exact fan-out signature. One version of this product ran blind and cost me a week's quota. This one could see the meter, and given the meter, it made the call I failed to make on July 12.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell you to steal
&lt;/h2&gt;

&lt;p&gt;The whole build was one Saturday: a settings block, one tunnel command, a stack that was already one YAML file, and a dashboard I approved rather than built. Steal the honesty notes too, because they matter. The cost metric on a Max subscription is an estimate computed from tokens and pricing, not your bill. Anthropic still exposes no remaining quota anywhere, so this whole system meters consumption against limits you have to know from your plan. And this setup exports metrics and logs, not traces, which is fine, because quota death arrives through counters, not spans.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code telemetry docs: &lt;a href="https://code.claude.com/docs/en/monitoring-usage" rel="noopener noreferrer"&gt;https://code.claude.com/docs/en/monitoring-usage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SigNoz Foundry: &lt;a href="https://signoz.io/docs/" rel="noopener noreferrer"&gt;https://signoz.io/docs/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo (snippets, dashboard and alert JSON, setup): &lt;a href="https://github.com/mukundheda/claude-code-mission-control" rel="noopener noreferrer"&gt;https://github.com/mukundheda/claude-code-mission-control&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>observability</category>
      <category>signoz</category>
      <category>opentelemetry</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
