<?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: Ganesh J</title>
    <description>The latest articles on DEV Community by Ganesh J (@codemuscle00).</description>
    <link>https://dev.to/codemuscle00</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%2F3985379%2Fba022292-811a-41e4-94fe-6f5846c7280f.jpg</url>
      <title>DEV Community: Ganesh J</title>
      <link>https://dev.to/codemuscle00</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codemuscle00"/>
    <language>en</language>
    <item>
      <title>I gave an AI SRE copilot write access to SigNoz — here's what the MCP server actually returns</title>
      <dc:creator>Ganesh J</dc:creator>
      <pubDate>Sun, 26 Jul 2026 14:20:58 +0000</pubDate>
      <link>https://dev.to/codemuscle00/i-gave-an-ai-sre-copilot-write-access-to-signoz-heres-what-the-mcp-server-actually-returns-1m6f</link>
      <guid>https://dev.to/codemuscle00/i-gave-an-ai-sre-copilot-write-access-to-signoz-heres-what-the-mcp-server-actually-returns-1m6f</guid>
      <description>&lt;p&gt;AI agents fail in ways classic monitoring misses. A tool call silently retries. A prompt balloons your token bill 4×. A judge step times out. An agent quietly loops. Your observability platform has all of it — but staring at a trace waterfall at 2am isn't the same as &lt;em&gt;knowing what broke&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So for &lt;a href="https://www.wemakedevs.org/hackathons/signoz" rel="noopener noreferrer"&gt;WeMakeDevs' Agents of SigNoz&lt;/a&gt;, I built &lt;strong&gt;Cerberus&lt;/strong&gt;: an AI SRE copilot that reads your agent's telemetry back out of SigNoz, explains the incident in plain English with trace citations, and then writes the alert rule that would have caught it — all through SigNoz's own MCP server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://cerberus-sre.vercel.app" rel="noopener noreferrer"&gt;https://cerberus-sre.vercel.app&lt;/a&gt; &lt;br&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/CodeMuscle/cerberus" rel="noopener noreferrer"&gt;https://github.com/CodeMuscle/cerberus&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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%2Fobcwzgye1aahm7o1kb38.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%2Fobcwzgye1aahm7o1kb38.png" alt="screenshot: dashboard.png*" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: emit &lt;em&gt;and&lt;/em&gt; consume
&lt;/h2&gt;

&lt;p&gt;Most observability projects only &lt;em&gt;emit&lt;/em&gt; telemetry — they push spans and stop. Cerberus does the agent-native thing: it emits, then &lt;strong&gt;consumes its own agent's traces back and acts on them&lt;/strong&gt;. Three heads, three jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Observe&lt;/strong&gt; — every agent run lands in SigNoz as OpenTelemetry traces, with token usage, cost, latency and errors per step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explain&lt;/strong&gt; — a copilot reads the ranked incidents and answers "what just went wrong?" grounded only in the facts, citing the exact &lt;code&gt;trace_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prevent&lt;/strong&gt; — one click writes the SigNoz alert rule for the incident's root cause.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why MCP, not the REST API
&lt;/h2&gt;

&lt;p&gt;The interesting decision was the read path. SigNoz ships an &lt;strong&gt;MCP server&lt;/strong&gt; — the same tool surface an AI client gets: &lt;code&gt;signoz_execute_builder_query&lt;/code&gt;, &lt;code&gt;signoz_search_traces&lt;/code&gt;, &lt;code&gt;signoz_create_alert&lt;/code&gt;, and ~40 more. Instead of scraping a REST endpoint, Cerberus speaks MCP. That means the exact interface an LLM would use &lt;em&gt;is&lt;/em&gt; the interface the product is built on.&lt;/p&gt;

&lt;p&gt;Deploying it is one command, because SigNoz's &lt;a href="https://github.com/SigNoz/foundry" rel="noopener noreferrer"&gt;Foundry&lt;/a&gt; installer brings up the whole stack plus the MCP server from a single casting file:&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;-fsSL&lt;/span&gt; https://signoz.io/foundry.sh | bash
foundryctl cast &lt;span class="nt"&gt;-f&lt;/span&gt; casting.yaml     &lt;span class="c"&gt;# SigNoz on :8080, MCP on :8000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;casting.yaml&lt;/code&gt; and its lockfile are committed, so the deployment is reproducible — anyone can re-run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part nobody tells you: the payload is not the docs
&lt;/h2&gt;

&lt;p&gt;Here's what actually cost me a day, and what I'd want another builder to know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;code&gt;signoz_search_traces&lt;/code&gt; &lt;strong&gt;returns a fixed column projection — with no&lt;/strong&gt; &lt;code&gt;gen_ai.*&lt;/code&gt; &lt;strong&gt;attributes.&lt;/strong&gt; The obvious tool for "give me the spans" hands back a fixed set of columns. My token and cost data — &lt;code&gt;gen_ai.usage.input_tokens&lt;/code&gt;, &lt;code&gt;gen_ai.usage.cost_usd&lt;/code&gt; — came back &lt;em&gt;empty every time&lt;/em&gt;. The whole product is about token and cost spikes, and they were zero.&lt;/p&gt;

&lt;p&gt;The fix was to drop to the &lt;strong&gt;Query Builder v5&lt;/strong&gt; tool and select those columns explicitly:&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&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;gen_ai.usage.input_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;fieldDataType&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;number&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;signal&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;traces&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;fieldContext&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;tag&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;&lt;code&gt;signoz_execute_builder_query&lt;/code&gt; lets you name exactly the attributes you want. Once I selected the &lt;code&gt;gen_ai.*&lt;/code&gt; tags by hand, the real numbers showed up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The response carries a human-readable note &lt;em&gt;after&lt;/em&gt; the JSON.&lt;/strong&gt; The payload looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ ...valid JSON... }
note: returned 3 rows (limit 3) — more results likely exist (hasMore=true).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A naive &lt;code&gt;json.loads()&lt;/code&gt; throws on the trailing text — and if you catch that broadly, you silently get zero spans and no error. The fix is to decode a prefix (&lt;code&gt;JSONDecoder().raw_decode&lt;/code&gt;) instead of parsing the whole string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Rows nest one level down, and an empty window returns&lt;/strong&gt; &lt;code&gt;null&lt;/code&gt;&lt;strong&gt;.&lt;/strong&gt; Each row wraps its fields under a &lt;code&gt;data&lt;/code&gt; sub-object alongside a &lt;code&gt;timestamp&lt;/code&gt;, and an empty time window comes back as &lt;code&gt;"rows": null&lt;/code&gt; rather than &lt;code&gt;[]&lt;/code&gt; — which will happily crash a flat-map if you assume a list. One &lt;code&gt;or []&lt;/code&gt; guard fixes it.&lt;/p&gt;

&lt;p&gt;None of these are in the docs. All three are captured as tests against real captured payloads, so they don't regress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grounded, not hallucinated
&lt;/h2&gt;

&lt;p&gt;The copilot doesn't get raw traces dumped into a prompt. Cerberus computes &lt;strong&gt;deterministic incident facts first&lt;/strong&gt; — groups spans by trace, flags errors and token/cost spikes, ranks worst-first — and only &lt;em&gt;then&lt;/em&gt; hands those facts to the LLM. The model's job is to explain, not to detect. So you get answers like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Two of the last five runs failed at the judge and agent.run steps, each flagged
error, token_spike, cost_spike (trace_id 5287c917… and 8e112c9a…). Each failed
run used 4,080 tokens and cost $0.122 — roughly 10× the 380 tokens and $0.0114
of the successful runs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real trace IDs, real numbers, and it says so when the facts don't cover something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing the loop
&lt;/h2&gt;

&lt;p&gt;Explaining an incident is half of it. Cerberus then &lt;strong&gt;writes the guardrail back into SigNoz&lt;/strong&gt;: &lt;code&gt;signoz_create_alert&lt;/code&gt; creates a &lt;code&gt;TRACES_BASED&lt;/code&gt; threshold rule over &lt;code&gt;max(gen_ai.usage.input_tokens)&lt;/code&gt; — the runaway prompt that drives the cost spike — wired to a notification channel. One click. Here it is live in SigNoz's Alerts UI:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[screenshot: the armed alert in SigNoz]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's the whole point: Cerberus never leaves SigNoz. It reads through the MCP server and writes through the same MCP server. Observe → Explain → Prevent, inside one platform.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The MCP server is the best part of SigNoz for building agents on&lt;/strong&gt; — but treat its payloads as things to discover, not things to trust from docs. Capture real responses and pin them in tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Builder v5 is the escape hatch&lt;/strong&gt; whenever a convenience tool doesn't return the field you need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Determinism before the LLM&lt;/strong&gt; is what makes the answers trustworthy — the model explains facts it can't fabricate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cerberus is MIT-licensed and reproducible with one Foundry command. Built with AI coding assistance (Claude), as the hackathon allows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://cerberus-sre.vercel.app" rel="noopener noreferrer"&gt;https://cerberus-sre.vercel.app&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/CodeMuscle/cerberus" rel="noopener noreferrer"&gt;https://github.com/CodeMuscle/cerberus&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>opentelemetry</category>
      <category>observability</category>
    </item>
    <item>
      <title>Building an AI research copilot that catches its sources lying</title>
      <dc:creator>Ganesh J</dc:creator>
      <pubDate>Mon, 06 Jul 2026 07:00:24 +0000</pubDate>
      <link>https://dev.to/codemuscle00/building-an-ai-research-copilot-that-catches-its-sources-lying-go1</link>
      <guid>https://dev.to/codemuscle00/building-an-ai-research-copilot-that-catches-its-sources-lying-go1</guid>
      <description>&lt;p&gt;Research tools forget across sessions, and they never notice when two sources disagree. Crosscheck is a small copilot on top of &lt;a href="https://cognee.ai" rel="noopener noreferrer"&gt;cognee&lt;/a&gt;that does both: persistent memory of everything you feed it, and a hero feature that flags when sources contradict each other — e.g. "FooDB sustained 50,000 req/s" (2021) vs "only 10,000 req/s" (2024).&lt;/p&gt;

&lt;h2&gt;
  
  
  The obvious design — and why it wasn't enough
&lt;/h2&gt;

&lt;p&gt;The first instinct is to make contradiction detection a pure graph query: build cognee's knowledge graph, then look for the same entity+attribute with different values across sources. Elegant, but it breaks on a real local stack (llama3.1:8b via Ollama). Two failures show up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The knowledge graph flattens quantities.&lt;/strong&gt; The extractor turns "50,000 requests per second" into a generic &lt;code&gt;requests per second&lt;/code&gt; node and drops the number. The conflicting values never make it into the graph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entities get merged across sources.&lt;/strong&gt; Every mention of FooDB collapses to one node, so the two throughput claims dedup into a single edge — nothing left to compare.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the graph is great for storage, visualization, and cited retrieval, but it can't be the &lt;em&gt;source of truth&lt;/em&gt; for a quantitative contradiction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: extract faithful claims, judge them structurally
&lt;/h2&gt;

&lt;p&gt;Crosscheck reads claims straight from each source's raw text — a thin, flat &lt;code&gt;(subject, predicate, object)&lt;/code&gt; extraction that keeps the number verbatim and tags it with the source id and timestamp. Extracting one value is easy even for a small local model, unlike a full graph schema. Then a two-stage engine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structural pre-filter:&lt;/strong&gt; group claims by normalized (subject, predicate); flag pairs with the same key, different value, different source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM judge:&lt;/strong&gt; confirm each candidate actually contradicts ("cannot both be true"), with the reason.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the FooDB pack this fires exactly once: 50k (2021) vs 10k (2024), confirmed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making cognee survive a weak local model
&lt;/h2&gt;

&lt;p&gt;Getting the graph to build at all on llama3.1:8b took three settings, all in &lt;code&gt;.env.example&lt;/code&gt;: switch cognee's structured-output framework to &lt;strong&gt;BAML&lt;/strong&gt; (its schema-aligned parsing tolerates loose JSON), neutralize the fragile chunk &lt;strong&gt;summarization&lt;/strong&gt; task (which small models can't satisfy and which Crosscheck doesn't use), and turn off &lt;strong&gt;multi-user access control&lt;/strong&gt; so a direct graph read sees the whole store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-improving
&lt;/h2&gt;

&lt;p&gt;A thin gap finder ranks sparsely-connected nodes and asks the LLM for the next research question — so the copilot tells you what it's missing. Everything persists in cognee's stores, so a fresh process re-answers without re-ingesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same engine, a second product: Argus
&lt;/h2&gt;

&lt;p&gt;Once the contradiction engine existed, it turned out to be domain-agnostic — "the same fact, two sources, two different values" is a shape that shows up far beyond research. So we pointed it at money. &lt;strong&gt;Argus&lt;/strong&gt; is a spend &amp;amp; contract &lt;em&gt;leakage*auditor: a contract line ("early-pay credit due: $2,400") and an invoice line ("credit applied: $0") are just a contradiction with a dollar gap. Argus reuses Crosscheck's &lt;code&gt;claims&lt;/code&gt; + &lt;code&gt;contradictions&lt;/code&gt; code **unchanged&lt;/em&gt;* and adds one thing — a deterministic dollar-impact number on each finding. On a small demo pack it surfaces $5,300 in leakage across three issues, each with the exact documents and dates a finance team could act on. Same engine, a completely different problem — which is the real point: catching sources that disagree is a primitive, not a feature.&lt;/p&gt;

&lt;p&gt;Runs fully offline on Ollama; an OpenAI or Gemini key is a drop-in alternative.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/CodeMuscle/crosscheck" rel="noopener noreferrer"&gt;https://github.com/CodeMuscle/crosscheck&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
