<?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: Komal deep</title>
    <description>The latest articles on DEV Community by Komal deep (@komal_deep_355).</description>
    <link>https://dev.to/komal_deep_355</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%2F4047872%2F8ff91215-5a7e-4982-a0f0-d213cfb89ee7.png</url>
      <title>DEV Community: Komal deep</title>
      <link>https://dev.to/komal_deep_355</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/komal_deep_355"/>
    <language>en</language>
    <item>
      <title>Connecting My Agent to SigNoz's MCP Server: A First-Timer's Debugging Diary</title>
      <dc:creator>Komal deep</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:35:24 +0000</pubDate>
      <link>https://dev.to/komal_deep_355/connecting-my-agent-to-signozs-mcp-server-a-first-timers-debugging-diary-4bmh</link>
      <guid>https://dev.to/komal_deep_355/connecting-my-agent-to-signozs-mcp-server-a-first-timers-debugging-diary-4bmh</guid>
      <description>&lt;p&gt;I want to start with the number that actually matters: &lt;strong&gt;three hours&lt;/strong&gt;. That's roughly how long I spent just getting a connection to succeed, out of a five-day hackathon window. If you're building something similar and hit the same walls, this post should save you most of that time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I was building, and why this angle
&lt;/h2&gt;

&lt;p&gt;This was my first hackathon. My background is data science and NLP — classification, prediction models, the kind of ML work where inputs and outputs are known quantities. Agents, MCP servers, observability — none of that was in my vocabulary two weeks ago. So this post is written from that specific angle: &lt;strong&gt;what actually trips someone up coming from ML into agent-plus-observability tooling for the first time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Agents of SigNoz (WeMakeDevs x SigNoz)&lt;/strong&gt;, I built an agent that connects to a self-hosted SigNoz instance through its MCP server, answers natural-language questions grounded in live telemetry, and then classifies what it finds — latency, error-rate, resource-exhaustion, dependency-failure, or healthy — with a confidence score traced back into SigNoz itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up SigNoz via Foundry
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
&lt;span class="sb"&gt;`&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://signoz.io/foundry.sh | bash&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;
&lt;span class="na"&gt;`apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Installation&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;signoz&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;flavor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;compose&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
  &lt;span class="na"&gt;mcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
&lt;span class="sb"&gt;`&lt;/span&gt;foundryctl cast &lt;span class="nt"&gt;-f&lt;/span&gt; casting.yaml&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This brought up SigNoz and its bundled MCP server as separate Docker containers — a detail that turned out to matter a lot more than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #1: 401 Unauthorized
&lt;/h2&gt;

&lt;p&gt;My first connection attempt to &lt;strong&gt;&lt;a href="http://localhost:8000/mcp" rel="noopener noreferrer"&gt;http://localhost:8000/mcp&lt;/a&gt;&lt;/strong&gt; returned a flat &lt;strong&gt;401&lt;/strong&gt;. Self-hosted SigNoz's MCP server needs two specific headers, not the generic Bearer-token pattern I tried first:&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;python&lt;/span&gt;
&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;streamablehttp_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;MCP_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&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;SIGNOZ-API-KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SIGNOZ_API_KEY&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;X-SigNoz-URL&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;http://signoz-signoz-0:8080&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="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;as &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bug #2: 400 Bad Request — the Docker networking trap
&lt;/h2&gt;

&lt;p&gt;Adding the headers got me past the** 401*&lt;em&gt;, straight into a **400&lt;/em&gt;*. I initially set X-SigNoz-URL to &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;, since that's where SigNoz's UI loaded from my own machine. But the MCP server runs inside its own container — and inside that container, localhost refers to the container itself, not my SigNoz instance. The server's own logs said it plainly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`"msg":"Invalid X-SigNoz-URL header" ... "error":"host \"localhost\" is not allowed"`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;docker ps showed the actual container name — signoz-signoz-0 —, and that's what needed to go in the header instead. This is the single biggest lesson I'd hand to my past self: when a container-to-container request fails, check what hostname the containers actually use to reach each other on the Docker network. It's rarely localhost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #3: a plain variable-naming mistake
&lt;/h2&gt;

&lt;p&gt;Once I fixed the headers, I hit a &lt;strong&gt;NameError:&lt;/strong&gt; name &lt;strong&gt;'read_stream'&lt;/strong&gt; &lt;strong&gt;is not defined&lt;/strong&gt;. I'd renamed the tuple unpacking on the async with line but hadn't updated where those names were used two lines later:&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;python&lt;/span&gt;
&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;as &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&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;with&lt;/span&gt; &lt;span class="nc"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;read_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;write_stream&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;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Small, but the traceback pointed into the MCP library's internals, not my own line — a good reminder that an error surfacing deep in someone else's stack trace can still be your own typo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #4: 403 — a permissions gap, not a connection problem
&lt;/h2&gt;

&lt;p&gt;With the connection finally working, calling signoz_list_services returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`403: only viewers/editors/admins can access this resource`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API key itself was valid — the service account behind it just had no role assigned. The fix was in Settings → Service Accounts, assigning Editor or Admin. &lt;em&gt;Worth remembering: a valid key and a valid role are two separate things.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually built on top: reasoning, not just retrieval
&lt;/h2&gt;

&lt;p&gt;SigNoz's MCP server gives you raw retrieval — service lists, traces, metrics, logs. It has no opinion about what any of that data means. Coming from a classification background, my instinct was to add exactly that missing layer:&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;python&lt;/span&gt;
&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;classify_incident&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_question&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="n"&gt;telemetry_data&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;classification_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are classifying an observability finding.

User question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Telemetry data retrieved: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;telemetry_data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Classify this into exactly ONE category: latency, error-rate, resource-exhaustion, dependency-failure, healthy, or unknown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Respond with ONLY valid JSON, nothing else:&lt;/strong&gt;&lt;br&gt;
`{{"category": "", "confidence": , "reasoning": ""}}"""&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;response = hf_client.chat_completion(
    model=MODEL_ID,
    messages=[{"role": "user", "content": classification_prompt}],
    max_tokens=150,
    temperature=0,
)
raw = response.choices[0].message.content.strip()
return json.loads(raw)`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The part I actually care about is where the result goes — not just into the chat response, but into SigNoz's own trace:&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;python&lt;/span&gt;
&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident_classification&lt;/span&gt;&lt;span class="sh"&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;classify_span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;classification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify_incident&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;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;telemetry_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;classify_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident.category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classification&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;classify_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident.confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classification&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means my agent's own judgment becomes something you can trace inside SigNoz, the same way you'd trace any other part of the system. SigNoz observes the target service; this makes it also observe the agent's reasoning about that service.&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%2Fwbefsa0fhejkohk6fz64.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%2Fwbefsa0fhejkohk6fz64.png" alt="Screenshot: SigNoz trace view showing the incident_classification span with incident.category and incident.confidence attributes" width="799" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd tell someone starting this from an ML background
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Docker networking will very likely be your biggest time sink — not the AI part. Budget for it honestly.&lt;/li&gt;
&lt;li&gt;Read the server's logs the moment a request fails for an unclear reason — docker logs_ _ told me more in thirty seconds than an hour of guessing at headers did.&lt;/li&gt;
&lt;li&gt;A service account's API key and its assigned role are two separate failure points — check both before assuming the key is broken.&lt;/li&gt;
&lt;li&gt;Look for where your actual background transfers, instead of trying to become a full infra person overnight. For me that was "don't just retrieve data, judge it" — that's ML thinking applied to an observability tool, and it's the one piece of this that's genuinely mine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;With more time, I'd add a small retrieval layer surfacing similar past incidents, and wire a SigNoz alert to trigger the agent automatically instead of waiting on a question.&lt;/p&gt;

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

&lt;p&gt;If you're self-hosting SigNoz's MCP server and hit a &lt;strong&gt;401, 400, or 403&lt;/strong&gt; in that order — check your headers, your Docker hostnames, and your service account's role. That covered nearly everything that stood between a working agent and me, and I hope it saves you the three hours it cost me.&lt;/p&gt;

</description>
      <category>signoz</category>
      <category>wemakedev</category>
      <category>observability</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
