<?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: Bronto</title>
    <description>The latest articles on DEV Community by Bronto (bronto_io).</description>
    <link>https://dev.to/bronto_io</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%2Forganization%2Fprofile_image%2F13363%2F11c79796-59d3-403d-a79e-6998bd36f406.png</url>
      <title>DEV Community: Bronto</title>
      <link>https://dev.to/bronto_io</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bronto_io"/>
    <language>en</language>
    <item>
      <title>Fixing Observability Pricing for the AI Era</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Wed, 29 Jul 2026 18:57:51 +0000</pubDate>
      <link>https://dev.to/bronto_io/fixing-observability-pricing-for-the-ai-era-3gjk</link>
      <guid>https://dev.to/bronto_io/fixing-observability-pricing-for-the-ai-era-3gjk</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Trevor Parsons&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today, we're announcing new pricing for Bronto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$0.10 per GB ingested, $1 per TB searched — for any signal: logs, traces, or metrics. No per-host or per-metric charges. Retained for 12 months, always hot, with sub-second search.&lt;/li&gt;
&lt;li&gt;It's designed to be disruptive, simple, predictable, and to align cost with customer value. It's over 100x more efficient than legacy pricing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're addressing a foundational issue our industry has failed to tackle: current pricing models aren't fit for purpose. They're out of whack from an overall cost perspective, don't align cost with value, and are designed to favor the vendor, not the customer. They're also hard to understand and wildly unpredictable, with teams regularly getting hit with nasty overage surprises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost has been the biggest issue for observability customers for the past 15+ years&lt;/strong&gt;, and vendors continue to willfully ignore it.&lt;/p&gt;

&lt;p&gt;Observability costs routinely run at 20–30% of total infra spend. That leads to a familiar set of workarounds: dropping high-volume logs, cutting retention to 3, 7, or 15 days, sampling, archiving and rehydrating, building pipelines just to throw data away before it lands, or rolling your own observability on open source and inheriting all the management overhead.&lt;/p&gt;

&lt;p&gt;One example that stuck with us: a company resized its hosts onto bigger AWS instances just to fight per-host pricing — even though that wasn't the right architecture for their system. &lt;strong&gt;They changed their infrastructure to suit their observability bill.&lt;/strong&gt; That's how distorted this pricing has become.&lt;/p&gt;

&lt;p&gt;Instead of tackling the problem head-on, vendors keep adding "features" on top of datastores that aren't fit for purpose. The latest wave is AI agents and automated workflows. Those capabilities are genuinely powerful and will help teams manage complex systems, reduce MTTR, and improve root-cause analysis — but if you build AI capabilities on top of fundamentally broken foundations, the cost problem only gets worse, especially as AI workloads drive even higher data volumes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Legacy Pricing Is Broken
&lt;/h2&gt;

&lt;p&gt;There are two core problems with legacy pricing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, it's roughly 10x too expensive no matter how you slice it. Vendors charge dollars per GB ingested and stored, when per-GB pricing needs to be at the level of cents per GB — so teams stop architecting around the cost. Observability spend should drop from ~30% of infra spend to under 5%, low enough that you stop engineering around it. On top of that, dollar-per-GB pricing is typically for only days of hot retention, meaning you're paying dearly for access to a sliver of your data — again, at least an order of magnitude off for an AI world where historical analysis over months or years should be the norm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt;, it's a value problem. You pay to ingest and store, so the vendor gets paid whether or not you ever get value from the data. You may never log in, search, or add an alert — the vendor still gets paid. In logging especially, people describe their provider as an expensive datastore they never actually use. The model was built for the vendor, not the customer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Bronto — Pricing Built for the Customer, Not the Vendor
&lt;/h2&gt;

&lt;p&gt;Built on BrontoDB, our custom-built observability datastore, Bronto drives massive efficiency in ingesting, storing, and analyzing observability data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bronto pricing: cents per GB, not dollars per GB.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$0.10 per GB ingested — any signal, logs, traces, or metrics. No per-host or per-metric charges. Retained for 12 months, always hot, with sub-second search.&lt;/li&gt;
&lt;li&gt;$1 per TB searched — 5x cheaper than scanning the same data on AWS Athena, which runs $5 per TB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you ingest data and never search it, you pay very little. You pay more only when you get more value by searching across more data — incentives that line up with yours, not against them.&lt;/p&gt;

&lt;p&gt;For enterprise plans, typical DevOps usage at scale comes in under a combined ingest-and-search cost of about $0.20 per GB (roughly $0.10 for ingest, ~$0.10 for search at the $1/TB rate). A free trial lets you verify exactly where you'd land with your own data.&lt;/p&gt;

&lt;h2&gt;
  
  
  100x–1000x More Efficient
&lt;/h2&gt;

&lt;p&gt;Bronto's entry plan is built for startups, solo builders, and teams building something new: $25/month for 1TB ingested with 20x search, at 12-month retention — roughly $0.025 per GB for any signal, with no per-host costs.&lt;/p&gt;

&lt;p&gt;Datadog runs about $2.60 per GB for 30-day retention. A team ingesting 1TB/month might pay around $2,600 with Datadog for 30 days of retention versus $25 with Bronto — 100x cheaper, with over 10x the retention on top, which works out to roughly &lt;strong&gt;1000x more efficient&lt;/strong&gt;. That's before even factoring in cost explosions from things like high-cardinality metrics.&lt;/p&gt;

&lt;p&gt;At larger volumes, Bronto's $0.10/GB ingested plus $1/TB searched comes out to around $0.20 per GB all-in for a typical DevOps profile, with 12 months of retention — versus Datadog's $2.60 per GB at 30 days (assuming ~1KB events and 100% log indexing). Roughly 100x more efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple. Predictable. No surprises.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple&lt;/strong&gt; — one per-GB cost for any signal ingested, one per-TB cost for what you search, with 12-month retention by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable&lt;/strong&gt; — entry plans have generous built-in headroom; larger plans get a usage dashboard or direct support from the team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No surprises&lt;/strong&gt; — built-in usage tracking and alerting mean no end-of-month shocks, and data never stops flowing when you hit a limit — you'll just get a heads-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Bronto Delivers
&lt;/h2&gt;

&lt;p&gt;All your data in one place, with full coverage and no blind spots. Always-hot data at 12-month default retention. Seamless cross-correlation across logs, traces, and metrics without hopping between separate datastores (think Prometheus, Tempo, and Loki, each limited and disjointed in its own way). And AI on top, via &lt;a href="https://docs.bronto.io/ai-features/hosted-mcp" rel="noopener noreferrer"&gt;Bronto's MCP server&lt;/a&gt;, &lt;a href="https://docs.bronto.io/ai-features/vibe-building#vibe-building-with-bronto" rel="noopener noreferrer"&gt;Bronto Vibe&lt;/a&gt;, or Bronto's built-in &lt;a href="https://docs.bronto.io/ai-features/ai-investigation-reports" rel="noopener noreferrer"&gt;investigation capabilities&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try Bronto Today
&lt;/h2&gt;

&lt;p&gt;Spin up a &lt;a href="https://app.eu.bronto.io/signup" rel="noopener noreferrer"&gt;free trial&lt;/a&gt; and run the numbers against your own data, or read the full details at &lt;a href="https://bronto.io/pricing" rel="noopener noreferrer"&gt;bronto.io/pricing&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>pricing</category>
      <category>devops</category>
      <category>ai</category>
    </item>
    <item>
      <title>Trust, Discovery, Context, Consistency: What AWS DevOps Agent Looks For in a Telemetry Layer</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Wed, 29 Jul 2026 18:49:57 +0000</pubDate>
      <link>https://dev.to/bronto_io/trust-discovery-context-consistency-what-aws-devops-agent-looks-for-in-a-telemetry-layer-7e4</link>
      <guid>https://dev.to/bronto_io/trust-discovery-context-consistency-what-aws-devops-agent-looks-for-in-a-telemetry-layer-7e4</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Stephen Crowley&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AWS DevOps Agent was built for modern software environments: engineering teams running heterogeneous infrastructure, juggling multiple observability tools, and without enough bandwidth to manually investigate every service incident. Its GA announcement highlighted reductions in mean time to resolution from two hours to thirty minutes — but that kind of improvement only happens when the underlying telemetry data is clean, structured, and fast to query.&lt;/p&gt;

&lt;p&gt;This is where Bronto comes in. Not as a point integration built specifically for AWS DevOps Agent, but as a telemetry layer designed from the ground up around the principles that make any AI-driven investigation successful: trust in every result, discovery before every query, full context in every response, and consistent behaviour across every dataset.&lt;/p&gt;

&lt;p&gt;We put this to the test directly. In a side-by-side evaluation, the same system — the OpenTelemetry demo application — sent data simultaneously to Bronto, Elasticsearch/Lucene (via OpenSearch), and Grafana Loki, all exposed via their respective MCP integrations. AWS DevOps Agent ran the same incident investigation against all three, then we asked it to judge.&lt;/p&gt;

&lt;p&gt;We gave it a deliberately neutral prompt, designed to let the agent reach its own conclusions based on what it had just experienced:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Compare Bronto, Elasticsearch/OpenSearch, and Grafana Loki for use by AI agents (like AWS DevOps Agent) performing incident investigation. Focus the comparison on what matters for AI agents, NOT human usability. Include Recommendations for AWS DevOps Agent. Do NOT focus on: Query syntax complexity (AI handles nested JSON fine), Human readability of responses, Nesting depth.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The prompt explicitly told the agent to ignore the things Bronto is often praised for by humans — simple query syntax, flat response structure, readable output. We wanted to know what mattered to the agent itself, working from its own experience querying all three systems during a live investigation.&lt;/p&gt;

&lt;p&gt;Its verdict was unambiguous. For incident investigation, Bronto is the clear winner. The evaluation identified four factors that actually determine whether an AI agent can investigate reliably:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Silent failure risk&lt;/strong&gt; — Does the system fail quietly with wrong results?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data discovery&lt;/strong&gt; — Can the agent explore before querying?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response completeness&lt;/strong&gt; — Does one query return enough context?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic behavior&lt;/strong&gt; — Does the same query always produce the same behavior?&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;AI Agent Suitability&lt;/th&gt;
&lt;th&gt;Primary Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bronto&lt;/td&gt;
&lt;td&gt;High (9.4/10)&lt;/td&gt;
&lt;td&gt;None significant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elasticsearch/Lucene&lt;/td&gt;
&lt;td&gt;Medium (5.3/10)&lt;/td&gt;
&lt;td&gt;Silent failures from case/mapping issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grafana Loki&lt;/td&gt;
&lt;td&gt;Low (4.6/10)&lt;/td&gt;
&lt;td&gt;Limited discovery, minimal context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  1. Silent Failure Risk
&lt;/h2&gt;

&lt;p&gt;The most critical factor for AI agents is not whether they can express a query — it's whether they can trust the result. When a query returns empty results, the agent must decide: is there genuinely no matching data, or did the query fail silently for some other reason? Unlike a human analyst who might notice something feels off and try a different approach, an AI agent treats an empty result as ground truth and continues its investigation on that basis.&lt;/p&gt;

&lt;p&gt;Elasticsearch/Lucene uses case-sensitive keyword field matching by default. A filter written as &lt;code&gt;{ "term": { "service.name": "Checkout" } }&lt;/code&gt; returns zero results if the indexed value is &lt;code&gt;checkout&lt;/code&gt; — and it does it silently, with no error. The same silent failure applies to wrong field types (&lt;code&gt;.keyword&lt;/code&gt; vs analyzed), fields that exist in the data but weren't mapped at index setup time, and any variation in field naming between indices. The agent concludes "no data" when the reality is "query did not match."&lt;/p&gt;

&lt;p&gt;Loki has fewer mapping complexities, but labels are case-sensitive and log content grep is case-sensitive by default. An agent querying &lt;code&gt;{service_name="Checkout"}&lt;/code&gt; when the label value is &lt;code&gt;checkout&lt;/code&gt; gets zero results with no signal that the label exists with a different casing.&lt;/p&gt;

&lt;p&gt;Bronto returns a clear error for unknown field names. Its &lt;code&gt;ILIKE&lt;/code&gt; operator handles any casing — &lt;code&gt;$service.name = 'Checkout'&lt;/code&gt; matches &lt;code&gt;checkout&lt;/code&gt;, &lt;code&gt;CHECKOUT&lt;/code&gt;, and any other variant. When Bronto returns empty results, the agent can treat that as a trustworthy signal that no matching data exists.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure Type&lt;/th&gt;
&lt;th&gt;Bronto&lt;/th&gt;
&lt;th&gt;Elasticsearch/Lucene&lt;/th&gt;
&lt;th&gt;Loki&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wrong case in query value&lt;/td&gt;
&lt;td&gt;Still works (ILIKE)&lt;/td&gt;
&lt;td&gt;Silent empty result&lt;/td&gt;
&lt;td&gt;Silent empty result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong case in field name&lt;/td&gt;
&lt;td&gt;Clear error&lt;/td&gt;
&lt;td&gt;Silent empty result&lt;/td&gt;
&lt;td&gt;Silent empty result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Field doesn't exist&lt;/td&gt;
&lt;td&gt;Clear error&lt;/td&gt;
&lt;td&gt;Silent empty result&lt;/td&gt;
&lt;td&gt;Silent empty result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong field type (&lt;code&gt;.keyword&lt;/code&gt; vs analyzed)&lt;/td&gt;
&lt;td&gt;N/A (consistent)&lt;/td&gt;
&lt;td&gt;Silent wrong results&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typo in field name&lt;/td&gt;
&lt;td&gt;Clear error&lt;/td&gt;
&lt;td&gt;Silent empty result&lt;/td&gt;
&lt;td&gt;Silent empty result&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In our evaluation, Elasticsearch/Lucene returned zero events for every error and warning query across cart, payment, and ad services — even though the events existed in the index. An agent relying on those results would have proceeded under the conclusion that no active errors existed, while multiple services were actively failing. Bronto returned all of them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;When Query Returns Empty&lt;/th&gt;
&lt;th&gt;Agent Concludes&lt;/th&gt;
&lt;th&gt;Actually Correct?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bronto&lt;/td&gt;
&lt;td&gt;No matching data exists&lt;/td&gt;
&lt;td&gt;"No errors found"&lt;/td&gt;
&lt;td&gt;Likely correct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elasticsearch/Lucene&lt;/td&gt;
&lt;td&gt;Unknown reason&lt;/td&gt;
&lt;td&gt;"No errors found"&lt;/td&gt;
&lt;td&gt;May be wrong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loki&lt;/td&gt;
&lt;td&gt;Unknown reason&lt;/td&gt;
&lt;td&gt;"No errors found"&lt;/td&gt;
&lt;td&gt;May be wrong&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  2. Data Discovery
&lt;/h2&gt;

&lt;p&gt;Can the agent explore what data exists and verify a query will work before it runs it? This determines whether the agent enters an investigation with confidence or proceeds on assumptions that may silently be wrong.&lt;/p&gt;

&lt;p&gt;Bronto treats discovery as a first-class workflow — and critically, it works across all data in Bronto, not just OpenTelemetry. The agent calls &lt;code&gt;get_datasets()&lt;/code&gt; to get a named catalogue of every dataset. It calls &lt;code&gt;get_keys(log_id="checkout")&lt;/code&gt; to list all searchable fields. It calls &lt;code&gt;get_key_values(log_id="checkout", key="$severity_text")&lt;/code&gt; to enumerate the actual values present — &lt;code&gt;["INFO", "ERROR", "WARN"]&lt;/code&gt;. By the time the agent issues its first search query, it knows the field exists, knows what values are in it, and can form that query with confidence.&lt;/p&gt;

&lt;p&gt;Elasticsearch/Lucene's discovery path is complex and unreliable. &lt;code&gt;GET _cat/indices&lt;/code&gt; returns a raw index list. &lt;code&gt;GET logs-checkout-*/_mapping&lt;/code&gt; returns hundreds of lines of nested JSON that describe field types but not values. To discover what values actually exist in a field, the agent must run a separate aggregation query — and must already know whether the field is &lt;code&gt;.keyword&lt;/code&gt; or analyzed to construct it correctly.&lt;/p&gt;

&lt;p&gt;Loki's discovery is limited to labels. &lt;code&gt;list_loki_label_names()&lt;/code&gt; typically returns three or four labels: &lt;code&gt;pod&lt;/code&gt;, &lt;code&gt;service_name&lt;/code&gt;, &lt;code&gt;stream&lt;/code&gt;. There is no way to discover the fields inside log content.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Bronto&lt;/th&gt;
&lt;th&gt;Elasticsearch/Lucene&lt;/th&gt;
&lt;th&gt;Loki&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;List available datasets&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;get_datasets&lt;/code&gt; (all sources)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_cat/indices&lt;/code&gt; (raw list)&lt;/td&gt;
&lt;td&gt;Via labels only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List searchable fields&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;get_keys&lt;/code&gt; (simple list)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_mapping&lt;/code&gt; (complex nested JSON)&lt;/td&gt;
&lt;td&gt;Labels only (3–5 fields)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List field values&lt;/td&gt;
&lt;td&gt;&lt;code&gt;get_key_values&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Terms aggregation query&lt;/td&gt;
&lt;td&gt;Label values only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify field exists before query&lt;/td&gt;
&lt;td&gt;Easy, explicit&lt;/td&gt;
&lt;td&gt;Must parse mapping&lt;/td&gt;
&lt;td&gt;Cannot for log content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works across non-OTEL data sources&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Varies by index config&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero-knowledge start&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  3. Response Completeness
&lt;/h2&gt;

&lt;p&gt;Does a single query return enough context for the agent to understand what happened, identify impact, and decide what to investigate next?&lt;/p&gt;

&lt;p&gt;Bronto returns row-based dense JSON where every event is a self-describing object. A single error event reliably includes service name, service version, pod name, node name, namespace, trace ID, and user ID:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@raw"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exporter export timeout: rpc error: code = Unavailable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-04 11:36:56.845 UTC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&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;"$service.name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"checkout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$service.version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$k8s.pod.name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"checkout-f86478f-xgclj"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$k8s.node.name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ip-192-168-46-190.ec2.internal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$trace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"367379916186c4759c7c226a0350408f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dea7494c-4867-11f1-ab4e-6edbf8c21e43"&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="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;In our live test, Bronto found 15 real error events in 3 queries, with root cause visible immediately: high memory usage causing exporter timeouts. Trace IDs were present on every event, allowing the agent to pivot to trace correlation without an additional query.&lt;/p&gt;

&lt;p&gt;Elasticsearch/Lucene's response completeness depends entirely on what was mapped when the index was created. The response also wraps every hit in metadata (&lt;code&gt;_index&lt;/code&gt;, &lt;code&gt;_id&lt;/code&gt;, &lt;code&gt;_score&lt;/code&gt;, &lt;code&gt;_shards&lt;/code&gt;) that the agent must parse past before reaching the actual log data.&lt;/p&gt;

&lt;p&gt;Loki returned zero application errors in the same test — only synthetic canary logs. Where it does return results, all structured context is embedded inside the raw log line string.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Context Needed&lt;/th&gt;
&lt;th&gt;Bronto&lt;/th&gt;
&lt;th&gt;Elasticsearch/Lucene&lt;/th&gt;
&lt;th&gt;Loki&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Error message&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@raw&lt;/code&gt; field&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;message&lt;/code&gt; field&lt;/td&gt;
&lt;td&gt;Log line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$service.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If mapped&lt;/td&gt;
&lt;td&gt;If label exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$service.version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If mapped&lt;/td&gt;
&lt;td&gt;Rarely available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pod / node name&lt;/td&gt;
&lt;td&gt;Indexed&lt;/td&gt;
&lt;td&gt;If mapped&lt;/td&gt;
&lt;td&gt;Rarely available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trace ID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$trace_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If mapped&lt;/td&gt;
&lt;td&gt;Must parse log line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User ID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$user_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If mapped&lt;/td&gt;
&lt;td&gt;Must parse log line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timestamp&lt;/td&gt;
&lt;td&gt;Human-readable&lt;/td&gt;
&lt;td&gt;ISO format&lt;/td&gt;
&lt;td&gt;Nanoseconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Investigation Task&lt;/th&gt;
&lt;th&gt;Bronto&lt;/th&gt;
&lt;th&gt;Elasticsearch/Lucene&lt;/th&gt;
&lt;th&gt;Loki&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Find error + full context&lt;/td&gt;
&lt;td&gt;1 query&lt;/td&gt;
&lt;td&gt;1–3 queries&lt;/td&gt;
&lt;td&gt;3–5+ queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correlate with trace&lt;/td&gt;
&lt;td&gt;0 additional (trace_id included)&lt;/td&gt;
&lt;td&gt;0–1 additional&lt;/td&gt;
&lt;td&gt;2+ additional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identify user impact&lt;/td&gt;
&lt;td&gt;0 additional (user_id included)&lt;/td&gt;
&lt;td&gt;0–1 additional&lt;/td&gt;
&lt;td&gt;2+ additional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find related logs by trace&lt;/td&gt;
&lt;td&gt;1 query&lt;/td&gt;
&lt;td&gt;1 query&lt;/td&gt;
&lt;td&gt;5+ queries (per service)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Deterministic Behavior
&lt;/h2&gt;

&lt;p&gt;Does the agent's investigative workflow work consistently across different services and datasets, or must it learn a different approach for each index it encounters?&lt;/p&gt;

&lt;p&gt;What is consistent in Bronto is the &lt;em&gt;workflow&lt;/em&gt; the agent uses to deal with variation. Discovery is the deterministic part: &lt;code&gt;get_datasets()&lt;/code&gt; always returns the catalogue, &lt;code&gt;get_keys(log_id)&lt;/code&gt; always returns the actual fields present in that dataset, and &lt;code&gt;get_key_values(log_id, key)&lt;/code&gt; always returns the actual values. That same three-call pattern works identically for an OTEL dataset, a syslog dataset, or a custom JSON ingest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# OTEL service
get_keys("checkout") → [$service.name, $severity_text, $trace_id, ...]
search: "$severity_text ILIKE 'error'"

# Custom JSON ingest with a different schema
get_keys("legacy-app") → [app_name, log_level, request_id, ...]
search: "log_level ILIKE 'error'"

# Syslog
get_keys("network-devices") → [host, facility, severity, message, ...]
search: "severity ILIKE 'err' OR severity ILIKE 'crit'"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fields differ but the workflow does not — so the agent never has to guess.&lt;/p&gt;

&lt;p&gt;Elasticsearch/Lucene cannot make this guarantee. There is no enforced standard for field naming — one index might store service name as &lt;code&gt;service.name.keyword&lt;/code&gt;, another as &lt;code&gt;serviceName&lt;/code&gt; (analyzed), another as &lt;code&gt;svc&lt;/code&gt;. Two indices with the same conceptual data can require materially different agent logic.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Bronto&lt;/th&gt;
&lt;th&gt;Elasticsearch/Lucene&lt;/th&gt;
&lt;th&gt;Loki&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Discovery workflow&lt;/td&gt;
&lt;td&gt;Same 3 calls for any dataset&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_mapping&lt;/code&gt; shape varies&lt;/td&gt;
&lt;td&gt;Labels-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Field types&lt;/td&gt;
&lt;td&gt;Consistent within a dataset&lt;/td&gt;
&lt;td&gt;Varies by mapping&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query behavior&lt;/td&gt;
&lt;td&gt;Predictable&lt;/td&gt;
&lt;td&gt;Depends on analyzers&lt;/td&gt;
&lt;td&gt;Depends on labels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-dataset patterns&lt;/td&gt;
&lt;td&gt;Discover then query — works for any source&lt;/td&gt;
&lt;td&gt;Per-index logic likely needed&lt;/td&gt;
&lt;td&gt;Per-stream logic likely needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What Each Platform Was Built For
&lt;/h2&gt;

&lt;p&gt;Each of these platforms reflects the use case it was designed for — and none of them, except Bronto, were designed with AI agents in mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Elasticsearch/Lucene&lt;/strong&gt; was built around a human at a dashboard. The Query DSL is JSON because it was meant to be assembled by Kibana on behalf of an analyst clicking through visualisations. Hand the same API to an agent, and the assumptions invert: silent case-sensitivity, mapping-dependent matching, and relevance ranking that buries rare events become failure modes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grafana Loki&lt;/strong&gt; was built around a single bet on cost — index only labels, store everything else as opaque log lines, charge less than indexed alternatives. The same minimalism that keeps Loki cheap keeps it opaque to an agent that needs to discover fields, correlate by trace ID, or filter on high-cardinality attributes. The cost argument has weakened independently: Loki Cloud lists $0.50/GB ingested versus Bronto's $0.10/GB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bronto&lt;/strong&gt; was built for AI agent investigation from the ground up. Discovery as a first-class workflow, case-insensitive matching by default, dense self-describing JSON responses, deterministic discovery patterns across heterogeneous data sources.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;th&gt;Bronto&lt;/th&gt;
&lt;th&gt;Elasticsearch/Lucene&lt;/th&gt;
&lt;th&gt;Loki&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Silent failure risk&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;4/10&lt;/td&gt;
&lt;td&gt;5/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data discovery&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;td&gt;5/10&lt;/td&gt;
&lt;td&gt;4/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response completeness&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;td&gt;7/10&lt;/td&gt;
&lt;td&gt;3/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deterministic behavior&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;5/10&lt;/td&gt;
&lt;td&gt;5/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query simplicity&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;6/10&lt;/td&gt;
&lt;td&gt;7/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Weighted total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9.4/10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.3/10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.6/10&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Connecting Bronto to AWS DevOps Agent is straightforward: deploy Bronto's hosted MCP server, route your telemetry to it, and authorise the agent. Both are built on the same open standards, so it works the first time, with no custom skills or pre-configuration.&lt;/p&gt;




&lt;h2&gt;
  
  
  See It in Action
&lt;/h2&gt;

&lt;p&gt;We recorded AWS DevOps Agent running a live incident investigation against Bronto — no pre-configuration, no custom skills, just the hosted MCP server connected and pointed at the OpenTelemetry demo.&lt;/p&gt;

&lt;p&gt;The investigation completed in under a minute. Once started, the agent generated its investigation plan within eight seconds. Within sixty seconds, it had queried Bronto, identified that the ad service was experiencing a high error rate, followed the statement IDs to the specific log lines causing failures, and surfaced the symptom: ads were failing to load. After further analysis — scanning the full codebase and correlating what Bronto's logs revealed — the agent returned its root cause: the &lt;code&gt;adFailure&lt;/code&gt; feature flag was controlled entirely by an external flagd service.&lt;/p&gt;

&lt;p&gt;From alert to root cause, with the data layer doing exactly what it was designed to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.bronto.io/ai-features/aws-devops-agent#aws-devops-agent-integration" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Set Up AWS DevOps Agent with Bronto&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>devops</category>
      <category>ai</category>
      <category>observability</category>
      <category>aws</category>
    </item>
    <item>
      <title>Building on Vercel? Your Logs Now Have a Better Home</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Wed, 22 Jul 2026 18:50:39 +0000</pubDate>
      <link>https://dev.to/bronto_io/building-on-vercel-your-logs-now-have-a-better-home-235g</link>
      <guid>https://dev.to/bronto_io/building-on-vercel-your-logs-now-have-a-better-home-235g</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Adam Ogórek&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Vercel is one of the breakout stories of the AI boom. From being a scrappy deployment tool on launch ten years ago, it is now the platform of choice for over 6 million developers, with 100,000 new signups every month. No surprise that it is now touching on Decacorn status at a $9.3B valuation.&lt;/p&gt;

&lt;p&gt;That kind of adoption doesn't happen by accident — it's the result of a genuinely exceptional developer experience. Instant deployments, per-branch preview environments, a global CDN that just works, and a framework-defined model that deploys your app without you writing a line of config.&lt;/p&gt;

&lt;p&gt;And the scope keeps widening. What started as the home for Next.js now covers most of the modern frontend ecosystem, zero-config backends for popular Python and TypeScript frameworks, and a full AI Cloud on top. If you're building a web or agentic app, Vercel is the obvious home for it.&lt;/p&gt;

&lt;p&gt;Vercel's built-in observability is purpose-built for the deployment workflow — real-time, per-request, fast. &lt;a href="https://vercel.com/docs/observability/observability-plus" rel="noopener noreferrer"&gt;Observability Plus&lt;/a&gt; includes up to 30 days of retention (with a 14-day query window) and richer performance breakdowns. What it doesn't cover is everything else telemetry data gets pulled into: investigating an incident that started three releases ago, running analytical queries over months of traffic, or letting an AI agent dig through raw events on your behalf.&lt;/p&gt;

&lt;p&gt;That's where Bronto comes in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bronto + Vercel
&lt;/h2&gt;

&lt;p&gt;Bronto now integrates directly with Vercel. Once your drain is configured, your logs and traces land alongside every other source in your stack, with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;12 months of hot retention, no sampling&lt;/strong&gt; — every log fully indexed, queryable the same way at day one or day 300&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-second search over terabytes&lt;/strong&gt; — the difference between running the query and deciding it's not worth the wait&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboards built for scale&lt;/strong&gt; — long-range comparisons, percentile breakdowns, and an AI widget creator that builds charts from a plain-English prompt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI investigation built in&lt;/strong&gt; — when a monitor fires, Bronto can investigate autonomously and deliver a root-cause report as a follow-up; BrontoScope does the same for any error event, on demand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A richer exploration UI&lt;/strong&gt; — SQL search, auto-parsing, automatic error detection, and live tail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native tracing at scale&lt;/strong&gt; — ingest traces from Vercel and other external sources alongside your logs, then pivot between them in a click&lt;/li&gt;
&lt;/ul&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%2F08lnkh5hfc6z3v1ojv7s.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%2F08lnkh5hfc6z3v1ojv7s.png" alt="Bronto dashboard showing Vercel logs" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting It Up
&lt;/h2&gt;

&lt;p&gt;Vercel exposes log data through &lt;strong&gt;log drains&lt;/strong&gt; — a webhook-style forward of runtime logs, build logs, edge function output, and more to a custom endpoint. You can also drain web analytics data (page views, custom events, real user performance metrics) using the same setup.&lt;/p&gt;

&lt;p&gt;Full setup instructions are on the &lt;a href="https://docs.bronto.io/integrations/vercel" rel="noopener noreferrer"&gt;Bronto Vercel docs page&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Vercel log drains require a Pro or Enterprise plan.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What You Can Do with Bronto
&lt;/h2&gt;

&lt;p&gt;Once your Vercel logs are in Bronto, they behave like any other log source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long-Term Performance Tracking
&lt;/h3&gt;

&lt;p&gt;With web analytics data flowing in alongside your logs, you can track Core Web Vitals — TTFB, FCP, LCP, INP, CLS — over months rather than days. That's the difference between reacting to a spike and actually understanding whether your performance is trending in the right direction across releases.&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%2Famzozcl9h282yymmzqdj.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%2Famzozcl9h282yymmzqdj.png" alt="Core Web Vitals dashboard" width="800" height="542"&gt;&lt;/a&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%2Fd1vz3i102mx35vdj6a8h.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%2Fd1vz3i102mx35vdj6a8h.png" alt="Performance trend charts" width="799" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Deployment Debugging
&lt;/h3&gt;

&lt;p&gt;Some bugs don't show up in the last 30 minutes — they emerge gradually across a series of deployments. With a year of log history, you can search across deployment boundaries, correlate error rate changes with specific releases, and build a clearer picture of how your system has evolved over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Headless Access via MCP
&lt;/h3&gt;

&lt;p&gt;Connect Claude (or any MCP-compatible agent) to your Bronto data and run natural-language analysis directly against your Vercel logs. Bronto's &lt;a href="https://www.bronto.io/blog/bronto-hosted-mcp-server" rel="noopener noreferrer"&gt;hosted MCP server&lt;/a&gt; streamlines common DevOps tasks like investigating unusual traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Instrumentation
&lt;/h3&gt;

&lt;p&gt;You can also generate your own logs from within your Vercel application and ship them to Bronto alongside the platform logs — giving you far more flexibility than Vercel's built-in observability. If you're running an AI feature, you can track token usage, model costs, and recent prompts directly in a Bronto dashboard.&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%2Fnvlhc09a2ubrwpa79si6.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%2Fnvlhc09a2ubrwpa79si6.png" alt="Custom instrumentation dashboard" width="799" height="538"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Build the Interface You Actually Want
&lt;/h2&gt;

&lt;p&gt;When your data is in Bronto, you're not locked into a single UI. The Bronto REST API exposes everything our own interface is built on, which means you can build custom views on top of your Vercel data using whatever tool fits your workflow.&lt;/p&gt;

&lt;p&gt;It's the same idea as with our &lt;a href="https://www.bronto.io/blog/brontovibe" rel="noopener noreferrer"&gt;Lovable integration&lt;/a&gt;: remix the baseline project, point it at your Vercel datasets, and prompt your way to a dashboard specific to your application. Want a scheduled weekly report of your Core Web Vitals? A view that correlates deployment events with error rate changes? Build it — the API is there.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Note on Cost
&lt;/h2&gt;

&lt;p&gt;Vercel charges $0.50/GB for data leaving through log drains — a platform cost regardless of where the logs end up. The good news: Bronto is priced as low as &lt;strong&gt;$0.10/GB at scale&lt;/strong&gt;, which is 50–90% cheaper than incumbent solutions. So even with Vercel's egress factored in, you're still coming out well ahead of what you'd pay to keep the same data in a traditional log platform.&lt;/p&gt;

&lt;p&gt;And because Bronto doesn't sample, index-select, or shunt old data into cold tiers, the cost you see is the cost of keeping everything hot, searchable, and retained for a year.&lt;/p&gt;




&lt;p&gt;Connect your Vercel logs to Bronto in just a few minutes — no agents, no sampling, no surprises.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.bronto.io/integrations/vercel" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Read the Integration Guide&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>logging</category>
      <category>devops</category>
      <category>observability</category>
    </item>
    <item>
      <title>Bronto Hosted MCP Server</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Mon, 08 Jun 2026 13:46:19 +0000</pubDate>
      <link>https://dev.to/bronto_io/bronto-hosted-mcp-server-21p6</link>
      <guid>https://dev.to/bronto_io/bronto-hosted-mcp-server-21p6</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Ciaran McGauran&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An earlier post, &lt;a href="https://www.bronto.io/blog/bronto-mcp-server" rel="noopener noreferrer"&gt;Bronto MCP Server&lt;/a&gt;, introduced Bronto's local MCP server and showed how AI clients can use log data to answer operational questions. That post focused on the developer-managed local setup: run the server yourself, connect it to Bronto with an API key, and let clients like Claude Code query logs through MCP tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We now support a hosted MCP experience.&lt;/strong&gt; Instead of running the MCP server yourself, you enable MCP access in the Bronto UI, sign in with your existing Bronto login method (OAuth, SSO, or Google Social), and connect supported MCP clients directly to Bronto's hosted MCP endpoint.&lt;/p&gt;

&lt;p&gt;The result is a much simpler setup for teams that want the benefits of MCP without managing a local server process or distributing API keys to individual developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why a Hosted MCP Server?
&lt;/h2&gt;

&lt;p&gt;The local MCP server is still a good fit for individual developers — it's flexible, easy to inspect, and quick to wire up locally. But a hosted model solves a different set of problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No local server repos to clone, install, run, or keep up to date&lt;/li&gt;
&lt;li&gt;No user-managed API keys for routine MCP access&lt;/li&gt;
&lt;li&gt;Sign-in uses the same login methods already managed in Bronto&lt;/li&gt;
&lt;li&gt;Access can be governed centrally in the Bronto UI&lt;/li&gt;
&lt;li&gt;Teams can standardize on one managed MCP endpoint&lt;/li&gt;
&lt;li&gt;Enables Bronto in environments and platforms that only support remote MCP (e.g. AWS DevOps Agent)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short: the local version is better for local experimentation. The hosted version is better when you want Bronto to manage authentication and access, and when administrators need clear control over who can connect.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changes Compared to the Local Version?
&lt;/h2&gt;

&lt;p&gt;At a tool level, the experience is familiar. Clients still get access to Bronto datasets, keys, values, log search, and metrics. What changes is how access is granted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Administrators enable MCP login and control which sign-in methods are allowed&lt;/li&gt;
&lt;li&gt;Users sign in through Bronto's normal authentication flow&lt;/li&gt;
&lt;li&gt;MCP clients complete a browser-based OAuth flow&lt;/li&gt;
&lt;li&gt;Bronto issues and validates the MCP access tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps the user experience close to a normal SaaS sign-in flow, while still exposing Bronto data through MCP.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up the Hosted MCP Server
&lt;/h2&gt;

&lt;p&gt;To enable MCP for your Bronto account and connect an MCP client, follow the instructions at &lt;a href="https://docs.bronto.io/ai-features/hosted-mcp" rel="noopener noreferrer"&gt;docs.bronto.io/ai-features/hosted-mcp&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Can You Ask It? Five CDN Log Examples
&lt;/h2&gt;

&lt;p&gt;CDN logs are a particularly good example for the hosted MCP workflow: high volume, operationally important, and often the first place teams look when users report latency, origin instability, or unusual traffic. The examples below use &lt;strong&gt;Claude Opus 4.6&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: Find the Right CDN Datasets
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Which datasets contain production CDN logs for our edge or delivery layer?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Teams often have multiple CDN-related datasets with non-obvious names. Before asking for regressions or cache behavior, you first want to know which datasets are actually carrying the production traffic.&lt;/p&gt;

&lt;p&gt;The hosted MCP flow surfaced two production CDN and edge-layer datasets immediately:&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.amazonaws.com%2Fuploads%2Farticles%2Fau7v10q26kt2aic4ctd6.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.amazonaws.com%2Fuploads%2Farticles%2Fau7v10q26kt2aic4ctd6.png" alt="Dataset discovery result" width="623" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Look for Elevated Error Responses
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In the last 30 minutes, look through our CDN logs for elevated 5xx responses and group them by host, path, and status code.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is one of the fastest ways to distinguish between a widespread origin issue, a bad deploy affecting one route, a customer-specific path problem, and noisy but low-impact isolated errors.&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.amazonaws.com%2Fuploads%2Farticles%2Fynxhr27pfbaxfdl5mcuv.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.amazonaws.com%2Fuploads%2Farticles%2Fynxhr27pfbaxfdl5mcuv.png" alt="CDN 5xx regression result" width="800" height="749"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 3: Investigate Latency Regressions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Compare CDN response-time metrics for the last hour against the previous hour and identify the hosts or paths with the largest regression.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Especially useful when users report "the site feels slower" but there's no obvious outage. Compare time windows, identify the worst regressions, and narrow quickly to the hosts and paths driving the slowdown.&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.amazonaws.com%2Fuploads%2Farticles%2F7362apkhuri9okm4epoj.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.amazonaws.com%2Fuploads%2Farticles%2F7362apkhuri9okm4epoj.png" alt="Response time regression result" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 4: Look for Cache Effectiveness Problems
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Search our CDN logs for cache-related fields and show whether cache miss rates increased in the last 24 hours.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This kind of question is often hard to answer quickly if you don't already know the exact field names in the dataset. MCP helps by discovering the relevant keys first, then narrowing the search. Identify where cache misses are increasing, see which paths are driving origin load, and determine whether the issue is broad or concentrated.&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.amazonaws.com%2Fuploads%2Farticles%2Fm50oizz6jj2htd1fpthp.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.amazonaws.com%2Fuploads%2Farticles%2Fm50oizz6jj2htd1fpthp.png" alt="Cache miss analysis result" width="799" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 5: Investigate Unusual Traffic
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Use CDN logs from the last 15 minutes to find unusual request-volume spikes by client IP, user agent, host, and path.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Helps with bot traffic, scraper spikes, attack reconnaissance, and accidental client retry storms. Isolate sudden spikes, see which IPs or paths dominate, and decide quickly whether you're looking at bots, scrapers, or retry storms.&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.amazonaws.com%2Fuploads%2Farticles%2Faviwv5lm38mi904sziuf.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.amazonaws.com%2Fuploads%2Farticles%2Faviwv5lm38mi904sziuf.png" alt="Traffic spike analysis result" width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;The original Bronto MCP Server post showed that log data is a strong MCP use case. The hosted version makes that workflow much easier to operationalize.&lt;/p&gt;

&lt;p&gt;Instead of asking users to install and run infrastructure locally, Bronto now provides the MCP endpoint directly — with sign-in and access managed through the product itself, just like the UI or direct API.&lt;/p&gt;

&lt;p&gt;CDN logs are exactly the kind of data where this shines: large volumes, fast-moving operational questions, and investigations that benefit from moving fluidly from dataset discovery to raw search to concrete explanations.&lt;/p&gt;

&lt;p&gt;If the first phase of MCP was proving that AI clients can be useful on top of log data, the hosted phase is about making that capability practical for more teams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.bronto.io/ai-features/hosted-mcp" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Set Up the Hosted MCP Server&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>devops</category>
      <category>logging</category>
    </item>
    <item>
      <title>How to Use A2A to Access Multiple Data Stores</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Fri, 05 Jun 2026 17:44:21 +0000</pubDate>
      <link>https://dev.to/bronto_io/how-to-use-a2a-to-access-multiple-data-stores-23a5</link>
      <guid>https://dev.to/bronto_io/how-to-use-a2a-to-access-multiple-data-stores-23a5</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by David Tracey&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is Part 3 of a three-part series on the Agent2Agent (A2A) protocol. &lt;a href="https://www.bronto.io/blog/introduction-to-a2a" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; introduced A2A concepts. &lt;a href="https://www.bronto.io/blog/how-to-use-mcp-agents-to-access-multiple-data-stores" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt; showed how to use MCP servers with Claude to query multiple data sources. This post extends that scenario by replacing Claude as the coordinator with a proper A2A "SuperAgent" that autonomously discovers and orchestrates specialist agents.&lt;/p&gt;

&lt;p&gt;The scenario is the same: an employee leaves a company, and IT needs to verify their details have been removed from all systems — an HR database and a logging platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&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.amazonaws.com%2Fuploads%2Farticles%2F1bpldw15d4g22y1mos1w.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.amazonaws.com%2Fuploads%2Farticles%2F1bpldw15d4g22y1mos1w.png" alt="A2A SuperAgent architecture" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll build:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An A2A agent wrapping the Bronto MCP server (&lt;code&gt;GetUsersAgent&lt;/code&gt;, port 9998)&lt;/li&gt;
&lt;li&gt;An A2A agent wrapping the SQLite MCP server (&lt;code&gt;GetEmployeesAgent&lt;/code&gt;, port 9999)&lt;/li&gt;
&lt;li&gt;A "SuperAgent" (port 9000) that discovers both agents via their Agent Cards and routes requests&lt;/li&gt;
&lt;li&gt;An A2A client that sends queries to the SuperAgent&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Create the A2A Bronto Agent
&lt;/h2&gt;

&lt;p&gt;Each A2A agent has two files: &lt;code&gt;__main__.py&lt;/code&gt; (Agent Card + server setup) and &lt;code&gt;agent_executor.py&lt;/code&gt; (the actual logic).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;__main__.py&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uvicorn&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.server.apps&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;A2AStarletteApplication&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.server.request_handlers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DefaultRequestHandler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.server.tasks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InMemoryTaskStore&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AgentCapabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentSkill&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BrontoAgentExecutor&lt;/span&gt;

&lt;span class="k"&gt;if&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;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;skill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AgentSkill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bronto_user_info&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;GetUsersAgent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Returns Information on Bronto Users&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tags&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;bronto users&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;examples&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;David&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;Gary&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="n"&gt;public_agent_card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AgentCard&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;GetUsersAgent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Uses Bronto REST API to get all users in an org&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://localhost:9998/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;default_input_modes&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;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;default_output_modes&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;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;AgentCapabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;streaming&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;skills&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;supports_authenticated_extended_card&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;request_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;agent_executor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;BrontoAgentExecutor&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;task_store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;InMemoryTaskStore&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;A2AStarletteApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;agent_card&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;public_agent_card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;http_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request_handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;9998&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;agent_executor.py&lt;/code&gt;
&lt;/h3&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;a2a.server.agent_execution&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.server.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EventQueue&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;new_agent_text_message&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BrontoAgent&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mcp_server_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8080/sse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mcp_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcp_server_url&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;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;str&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mcp_client&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;mcp_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_bronto_users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&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;A2A Server received: MCP Server replied: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mcp_response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&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;Error calling MCP server: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BrontoAgentExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AgentExecutor&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BrontoAgent&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;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventQueue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;new_agent_text_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;cancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventQueue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cancel not supported&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;The Employee DB A2A agent uses the same structure — just updating the MCP URL to port 8081, changing the tool name to &lt;code&gt;get_employees&lt;/code&gt;, and updating the Agent Card name/description accordingly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Create the A2A SuperAgent
&lt;/h2&gt;

&lt;p&gt;The SuperAgent discovers sub-agents via their Agent Cards and routes requests based on the query it receives.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;agent_executor.py&lt;/code&gt; (key parts)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid4&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.server.agent_execution&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.server.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EventQueue&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;new_agent_text_message&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;A2ACardResolver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A2AClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MessageSendParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SendMessageRequest&lt;/span&gt;

&lt;span class="c1"&gt;# Sub-agent URLs
&lt;/span&gt;&lt;span class="n"&gt;sub_agents&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;http://localhost:9999&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# bronto get users
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:9998&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# get employees db
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SuperAgent&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subordinate_urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sub_agents&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_registry&lt;/span&gt; &lt;span class="o"&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;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_discover_agents&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Agent Discovery Complete&lt;/span&gt;&lt;span class="sh"&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;_discover_agents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&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;httpx_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subordinate_urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;resolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;A2ACardResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpx_client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;public_card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_agent_card&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_registry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;public_card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;card&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;public_card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;skills&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="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;public_card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;]&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Discovered: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;public_card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to discover agent at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&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;bronto_agents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bronto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;skills&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;def&lt;/span&gt; &lt;span class="nf"&gt;a2a_get_text_from_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="o"&gt;-&amp;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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&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;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parts&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;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
        &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;IndexError&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;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&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;Could not extract text: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SuperAgentExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AgentExecutor&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SuperAgent&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;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventQueue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&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="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&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;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;agent_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parts&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;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GetAllUserDetails&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;agent_cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;merged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_all_bronto_agents&lt;/span&gt;&lt;span class="p"&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;event_queue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;new_agent_text_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GetUsers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;agent_cmd&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_named_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GetUsersAgent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;new_agent_text_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&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="k"&gt;elif&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GetEmployees&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;agent_cmd&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_named_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GetEmployeesAgent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;new_agent_text_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&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="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nf"&gt;new_agent_text_message&lt;/span&gt;&lt;span class="p"&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;Agent not found for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;agent_cmd&lt;/span&gt;&lt;span class="si"&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_named_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&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;agent_name&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;target_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;target_agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&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;Unable to find &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;agent_name&lt;/span&gt;&lt;span class="si"&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="n"&gt;a2a_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;A2AClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpx_client&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;agent_card&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_agent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;card&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SendMessageRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;MessageSendParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&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;parts&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;kind&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;text&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;text&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;bronto_user_info&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;messageId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nb"&gt;hex&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="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;a2a_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a2a_get_text_from_response&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="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_all_bronto_agents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&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;event_queue&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bronto_agents&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No Bronto Agents found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="n"&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="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_named_agent&lt;/span&gt;&lt;span class="p"&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;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Report from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&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;cancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventQueue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cancel not supported&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;h2&gt;
  
  
  Step 3: Create the A2A Client
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# client.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid4&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;A2ACardResolver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A2AClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a2a.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MessageSendParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SendMessageRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SendStreamingMessageRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TextPart&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://localhost:9000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;query_param&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GetAllUserInfo&lt;/span&gt;&lt;span class="sh"&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="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&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;httpx_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;A2ACardResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpx_client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;agent_card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_agent_card&lt;/span&gt;&lt;span class="p"&gt;()&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;A2AClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpx_client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;httpx_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_card&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;agent_card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;user_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&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="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;TextPart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query_param&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
            &lt;span class="n"&gt;message_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SendMessageRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;MessageSendParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&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="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="nf"&gt;model_dump_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exclude_none&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;__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;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Running the Full Demo
&lt;/h2&gt;

&lt;p&gt;With the MCP servers running (from Part 2), start the A2A agents:&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;# Terminal 1: Bronto A2A agent (wraps Bronto MCP server)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;bronto-get-users &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv run &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Terminal 2: Employee DB A2A agent (wraps SQLite MCP server)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;bronto-get-employees &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv run &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Terminal 3: SuperAgent&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;superagent &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv run &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Terminal 4: A2A Client&lt;/span&gt;
uv run &lt;span class="nb"&gt;.&lt;/span&gt; GetEmployees      &lt;span class="c"&gt;# routes to Employee DB agent&lt;/span&gt;
uv run &lt;span class="nb"&gt;.&lt;/span&gt; GetUsers          &lt;span class="c"&gt;# routes to Bronto agent&lt;/span&gt;
uv run &lt;span class="nb"&gt;.&lt;/span&gt; GetAllUserDetails &lt;span class="c"&gt;# calls all agents and merges responses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SuperAgent discovers available agents via their Agent Cards, then routes requests accordingly — without any hardcoded knowledge of what each agent does.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This three-part series has demonstrated that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A2A standardizes agent communication&lt;/strong&gt; — any A2A agent can discover and call any other A2A agent, regardless of who built it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP and A2A work together&lt;/strong&gt; — MCP handles tool access, A2A handles agent-to-agent orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data doesn't need to move&lt;/strong&gt; — logs stay in Bronto (optimized for high-volume fast search), HR data stays in SQL, each accessed by purpose-built agents&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A more advanced SuperAgent could pass all Agent Card descriptions to an LLM for smarter routing, or filter/aggregate data from sub-agents before sending it to an LLM — reducing token usage and avoiding LLM hallucination from large raw datasets. The &lt;a href="https://posthog.com/blog/optimizing-agent-cost" rel="noopener noreferrer"&gt;PostHog blog on optimizing agent costs&lt;/a&gt; illustrates why this matters.&lt;/p&gt;

&lt;p&gt;Whether A2A becomes the universal standard for agent collaboration or the ecosystem remains a patchwork of custom MCP integrations remains to be seen — but Bronto's high-performance logging platform will be a first-class citizen in whichever world emerges.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bronto.io/bronto-labs" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Explore Bronto's AI Features&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Use MCP Agents to Access Multiple Data Stores</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Fri, 05 Jun 2026 17:43:54 +0000</pubDate>
      <link>https://dev.to/bronto_io/how-to-use-mcp-agents-to-access-multiple-data-stores-29g5</link>
      <guid>https://dev.to/bronto_io/how-to-use-mcp-agents-to-access-multiple-data-stores-29g5</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by David Tracey&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is Part 2 of a three-part series on the Agent2Agent (A2A) protocol and agentic data querying. &lt;a href="https://www.bronto.io/blog/introduction-to-a2a" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; introduced A2A concepts and showed how to build a basic agent. This post demonstrates how easy it is to create two simple MCP agents to query different data sources and then analyse their outputs using Claude.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scenario:&lt;/strong&gt; when an employee leaves a company, the IT department needs to verify their details and access have been removed from all systems — in this case, an HR database and a logging platform. Traditionally, an IT person would check each system separately using custom tools. We'll show how MCP makes this dramatically simpler.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;We'll create:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Bronto REST API client to fetch users from Bronto&lt;/li&gt;
&lt;li&gt;A SQLite database server representing HR data&lt;/li&gt;
&lt;li&gt;Two MCP servers wrapping each data source&lt;/li&gt;
&lt;li&gt;A demonstration of Claude using both MCP servers to answer a query&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2F9wqrc432uniwcl94luow.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.amazonaws.com%2Fuploads%2Farticles%2F9wqrc432uniwcl94luow.png" alt="MCP architecture diagram" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can set up a &lt;a href="https://app.bronto.io/signup" rel="noopener noreferrer"&gt;14-day free trial&lt;/a&gt; to test the Bronto REST API client used here.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Create a Bronto REST API Client
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# bronto_client.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests.adapters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTTPAdapter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib3.util.retry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Retry&lt;/span&gt;

&lt;span class="n"&gt;ADMIN_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SomeKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DATA_STORAGE_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;us&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;# swap for your region
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&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;https://api.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DATA_STORAGE_REGION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.brontobytes.io/users&lt;/span&gt;&lt;span class="sh"&gt;"&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;X-BRONTO-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;ADMIN_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&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;__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;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;session&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://api.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DATA_STORAGE_REGION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.brontobytes.io&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;HTTPAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backoff_factor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="n"&gt;status_forcelist&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;get_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv ./env &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; ./env/bin/activate &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
python bronto_client.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns a JSON response with all users in your Bronto account:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"users"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00000ffff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Paul"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"McCartney"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"paul@beatles.io"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"roles"&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="s2"&gt;"1111"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Admin"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tags"&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;"team"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fabfour"&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="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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Create a Simple Employee Database
&lt;/h2&gt;

&lt;p&gt;Install SQLite (or use &lt;code&gt;brew install sqlite&lt;/code&gt; on Mac), then create the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlite3 bronto_employee.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;Employee&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;FirstName&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;LastName&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;HireDate&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;Employee&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HireDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Paul'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'McCartney'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-07-22'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ringo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Starr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-10-22'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'George'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Harrison'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-06-01'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Lennon'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-11-05'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Wrap Both Sources as MCP Servers
&lt;/h2&gt;

&lt;p&gt;We'll use &lt;a href="https://apidog.com/blog/fastmcp/" rel="noopener noreferrer"&gt;FastMCP&lt;/a&gt; to expose each data source as an MCP tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bronto MCP Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mcp-bronto-user-get.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests.adapters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTTPAdapter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib3.util.retry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Retry&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;

&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bronto Users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ADMIN_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SomeKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DATA_STORAGE_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;us&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&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_bronto_users&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;session&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://api.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DATA_STORAGE_REGION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.brontobytes.io&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;HTTPAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backoff_factor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="n"&gt;status_forcelist&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;]))&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&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;https://api.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DATA_STORAGE_REGION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.brontobytes.io/users&lt;/span&gt;&lt;span class="sh"&gt;"&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;X-BRONTO-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;ADMIN_API_KEY&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="n"&gt;response&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="k"&gt;if&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;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv ./env &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; ./env/bin/activate &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;fastmcp
fastmcp run mcp-bronto-user-get-server.py:mcp &lt;span class="nt"&gt;--transport&lt;/span&gt; sse &lt;span class="nt"&gt;--port&lt;/span&gt; 8080 &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SQLite Employee MCP Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# sqlite-server.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Employee DB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&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_employees&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/your_path/bronto_employee.db&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT LastName, FirstName FROM Employee ORDER BY HireDate DESC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;LastName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ln&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FirstName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ln&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&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;__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;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the server on port 8081:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FastMCP run &lt;span class="nt"&gt;-p&lt;/span&gt; 8081 &lt;span class="nt"&gt;-t&lt;/span&gt; sse sqlite-test-server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inspecting MCP Servers
&lt;/h3&gt;

&lt;p&gt;The FastMCP CLI has a useful &lt;code&gt;inspect&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FastMCP inspect sqlite-test-server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows available tools, prompts, resources, and a full inspection log.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Connect Both Servers to Claude
&lt;/h2&gt;

&lt;p&gt;Install Claude locally, then add both MCP servers to Claude's config file. On Mac: &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&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;"EmployeeDB"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/your_path/mcp-env/bin/python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"/your_path/mcp-sqlite-server.py"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A simple MCP server to query employee data from a database"&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;"BrontoUsers"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/your_path/mcp-env/bin/python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"/your_path/mcp-bronto-user-get.py"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A simple MCP server to query Bronto Users"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the servers are loaded in Claude under &lt;strong&gt;Settings → Developer → Local MCP Servers&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Query Both Data Sources with One Prompt
&lt;/h2&gt;

&lt;p&gt;Enter this prompt in Claude:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Show me Bronto employees, check if they have Bronto user accounts and summarise what you find&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude uses both MCP servers and returns something like:&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Summary of Bronto Employees and User Accounts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Employee Database:&lt;/em&gt; 4 employees total&lt;br&gt;&lt;br&gt;
&lt;em&gt;Bronto User Accounts:&lt;/em&gt; 8 active user accounts&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Employees WITH Bronto accounts (3 out of 4):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paul McCartney — 2 accounts (@beatles.io, &lt;a class="mentioned-user" href="https://dev.to/wings"&gt;@wings&lt;/a&gt;.io) — Admin role&lt;/li&gt;
&lt;li&gt;George Harrison — 2 accounts (@beatles.io, @wilburys.io) — Admin&lt;/li&gt;
&lt;li&gt;John Lennon — 4 accounts (various domains) — Admin/ReadOnly roles&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Employee WITHOUT Bronto account:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ringo Starr — No user account found&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;75% coverage — 3 of 4 employees have Bronto accounts&lt;/li&gt;
&lt;li&gt;Most employees have multiple accounts (likely testing/different environments)&lt;/li&gt;
&lt;li&gt;Additional users not in the employee list suggest external contractors or an incomplete employee list&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;As this simple demo shows, MCP makes it easy to query multiple, disparate data sources from a single LLM prompt — &lt;strong&gt;without moving any data&lt;/strong&gt;. Logs stay in Bronto (optimized for fast search at scale) and HR data stays in SQL (optimized for tabular queries). Each data source is accessed by an agent purpose-built for it.&lt;/p&gt;

&lt;p&gt;This benefit compounds as the number of data stores and agents grows.&lt;/p&gt;

&lt;p&gt;Part 3 will extend this scenario by adding A2A, giving us a "SuperAgent" that can autonomously discover and orchestrate these data source agents without relying on Claude as the coordinator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.bronto.io/signup" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Start a Free Bronto Trial&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>python</category>
    </item>
    <item>
      <title>Introduction to A2A and Agent Search</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Tue, 26 May 2026 18:32:08 +0000</pubDate>
      <link>https://dev.to/bronto_io/introduction-to-a2a-and-agent-search-2lj2</link>
      <guid>https://dev.to/bronto_io/introduction-to-a2a-and-agent-search-2lj2</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by David Tracey&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AI is rapidly evolving from simple tools to increasingly complex agents capable of reasoning and decision making. As agents are used for more tasks, the ability to use multiple co-operating agents will become increasingly important — particularly for tasks requiring specialised knowledge or access across domains.&lt;/p&gt;

&lt;p&gt;Large-scale, flexible solutions will require a common means for agents to communicate capabilities to each other using a trusted means of collaboration. That's what the &lt;strong&gt;Agent2Agent (A2A) protocol&lt;/strong&gt; is designed to provide.&lt;/p&gt;

&lt;p&gt;This is the first post in a three-part series:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 1 (this post):&lt;/strong&gt; Introduction to A2A concepts and a "Hello World" agent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2:&lt;/strong&gt; Using MCP to query multiple data sources including Bronto's logging platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3:&lt;/strong&gt; Combining A2A and MCP with a "SuperAgent" orchestrating multiple agents&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Why A2A?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://a2a-protocol.org/latest/" rel="noopener noreferrer"&gt;Agent2Agent protocol&lt;/a&gt; — originally developed by Google and now under the &lt;a href="https://www.linuxfoundation.org/press/linux-foundation-launches-the-agent2agent-protocol-project-to-enable-secure-intelligent-communication-between-ai-agents" rel="noopener noreferrer"&gt;Linux Foundation&lt;/a&gt; as an open source project — gives agents an open protocol for interaction built on HTTP. It standardizes how agents exchange messages, requests, and data, allowing agents to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover each other's capabilities without exposing internal state, memory, or implementation&lt;/li&gt;
&lt;li&gt;Negotiate interaction details (text, forms, media, etc.)&lt;/li&gt;
&lt;li&gt;Collaborate on running tasks securely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IBM's definition of agentic AI captures it well: &lt;em&gt;"Agentic AI is an artificial intelligence system that can accomplish a specific goal with limited supervision"&lt;/em&gt;, with each agent in a multi-agent system performing a specific subtask coordinated through AI orchestration.&lt;/p&gt;




&lt;h2&gt;
  
  
  A2A and MCP: Complementary Protocols
&lt;/h2&gt;

&lt;p&gt;MCP and A2A serve different but complementary roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP&lt;/strong&gt; exposes tools and structured data sources to an agent. It extends an agent's capabilities by standardizing how an agent accesses databases or product APIs. It focuses on a single agent accessing tools, and lacks built-in agent authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A&lt;/strong&gt; provides structured agent-to-agent communication — allowing multiple autonomous agents to collaborate, delegate tasks, and exchange information using capabilities exposed via Agent Cards. It enables multi-agent interactions with secure communication and capability discovery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MCP Tools are generally basic operations with defined inputs/outputs. A2A is designed for autonomous agents that reason, use tools, and collaborate on complex problems. The two protocols are not mutually exclusive — agents can use MCP for specific tool access and A2A for broader agent collaboration.&lt;/p&gt;

&lt;p&gt;Part 3 of this series will show an emerging pattern where A2A agents sit in front of MCP servers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key A2A Concepts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agent Card&lt;/strong&gt; — public metadata describing an agent's capabilities, skills, URL, and authentication requirements. Other agents retrieve this card to discover what an agent can do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A2A Server&lt;/strong&gt; — exposes an HTTP API endpoint implementing A2A methods and executes tasks on behalf of other agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A2A Client&lt;/strong&gt; — an application or agent that sends requests to an A2A server to initiate tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task&lt;/strong&gt; — initiated by a client sending an A2A message (role: &lt;code&gt;user&lt;/code&gt;). Each task has a unique ID and states: &lt;code&gt;submitted&lt;/code&gt;, &lt;code&gt;working&lt;/code&gt;, &lt;code&gt;input-required&lt;/code&gt;, &lt;code&gt;completed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Message&lt;/strong&gt; — contains a role (&lt;code&gt;user&lt;/code&gt; or &lt;code&gt;agent&lt;/code&gt;), optional metadata, and an array of parts (&lt;code&gt;TextPart&lt;/code&gt;, &lt;code&gt;FilePart&lt;/code&gt;, or structured JSON data).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Communication flows:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Discovery&lt;/strong&gt; — client fetches the Agent Card via &lt;code&gt;GET /.well-known/agent-card.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initiation&lt;/strong&gt; — client sends a request with a task ID to the server agent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processing&lt;/strong&gt; — server processes the task, streaming intermediate updates via Server-Sent Events if needed&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2Fyqnk3yejx0nb66f0uwjj.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.amazonaws.com%2Fuploads%2Farticles%2Fyqnk3yejx0nb66f0uwjj.png" alt="A2A protocol diagram" width="636" height="377"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Hello World A2A Agent
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation and Setup
&lt;/h3&gt;

&lt;p&gt;These examples use the official &lt;a href="https://github.com/a2aproject/a2a-samples" rel="noopener noreferrer"&gt;A2A samples repository&lt;/a&gt;. Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.12 or higher&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;UV&lt;/a&gt; (recommended Python package manager)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running the Sample
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone the repo&lt;/span&gt;
git clone https://github.com/a2aproject/a2a-samples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;a2a-samples/samples/python/agents/helloworld

&lt;span class="c"&gt;# Install UV and run the server&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;uv
uv run &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs &lt;code&gt;__main__.py&lt;/code&gt; in the helloworld directory, which defines an &lt;code&gt;AgentCard&lt;/code&gt;, &lt;code&gt;AgentSkill&lt;/code&gt;, and extended card, then calls uvicorn to run a server on &lt;code&gt;localhost:9999&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In a separate terminal, run the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run test_client.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Agent Card Response
&lt;/h3&gt;

&lt;p&gt;The server returns its public Agent Card:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"capabilities"&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;"streaming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;"defaultInputModes"&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="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"defaultOutputModes"&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="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Just a hello world agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello World Agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"preferredTransport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JSONRPC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"protocolVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"just returns hello world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"examples"&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="s2"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello_world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Returns hello world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tags"&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="s2"&gt;"hello world"&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="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"supportsAuthenticatedExtendedCard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&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:9999/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&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.0.0"&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;Authenticated clients additionally receive an extended card with a &lt;code&gt;super_hello_world&lt;/code&gt; skill — demonstrating how A2A supports capability tiers based on authentication level.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Coming in Parts 2 and 3
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 2&lt;/strong&gt; will show how to build simple MCP servers for Bronto's REST API and a SQLite database, then use Claude to query both simultaneously with a natural language prompt — no data movement required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 3&lt;/strong&gt; will extend this into a full A2A scenario with a "SuperAgent" orchestrating two A2A agents (one per data store), demonstrating how A2A and MCP work together.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Picture: Challenges A2A Still Needs to Solve
&lt;/h2&gt;

&lt;p&gt;A2A is an evolving protocol. Current areas requiring further development include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-agent orchestration&lt;/strong&gt; — standardized support for conflict handling and failure recovery across agents from different organizations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared vocabulary&lt;/strong&gt; — no agreed-upon standard definitions for common items like invoices, policies, or receipts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust&lt;/strong&gt; — establishing trust between agents across organizational boundaries is key to adoption&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; — authentication is provided, but richer methods are needed for privacy across organizational/national boundaries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise readiness&lt;/strong&gt; — standardized usage management, SLOs, SLAs, and automated negotiation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The separate open source project &lt;a href="https://agntcy.org/" rel="noopener noreferrer"&gt;AGNTCY&lt;/a&gt;, backed by Cisco, LangChain, Galileo and others, provides the Open Agent Schema Framework (OASF) as another approach to standardizing agent capability descriptions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bronto.io/bronto-labs" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Explore Bronto's AI Features&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>devops</category>
    </item>
    <item>
      <title>Bronto for Fastly: Real-Time CDN Logging That Actually Scales</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Tue, 26 May 2026 17:04:31 +0000</pubDate>
      <link>https://dev.to/bronto_io/bronto-for-fastly-real-time-cdn-logging-that-actually-scales-b1k</link>
      <guid>https://dev.to/bronto_io/bronto-for-fastly-real-time-cdn-logging-that-actually-scales-b1k</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Mike Neville-O'Neill&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A company processing tens of TBs of Fastly logs daily through traditional logging vendors faces an impossible choice: pay enterprise prices for comprehensive logging, or fly blind with minimal retention.&lt;/p&gt;

&lt;p&gt;That's why most teams default to a 7-day retention window. Not because they don't need historical data — because the economics don't work.&lt;/p&gt;

&lt;p&gt;That was the exact situation for &lt;a href="https://www.contentstack.com/" rel="noopener noreferrer"&gt;Contentstack&lt;/a&gt;, a leading global platform for digital experiences. They now keep a full year of logs, run unlimited queries, and report 50% cost savings versus their previous solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Cost of Restricted Logging
&lt;/h2&gt;

&lt;p&gt;Processing 10TB of Fastly logs daily can cost ~$30,000/month for ingestion alone on traditional platforms. To reduce costs, teams resort to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abbreviating field names (&lt;code&gt;EdgeResponseStatus&lt;/code&gt; → &lt;code&gt;ers&lt;/code&gt;) to save bytes&lt;/li&gt;
&lt;li&gt;Sampling 1 in 100 requests and hoping issues appear in that sample&lt;/li&gt;
&lt;li&gt;Keeping only 7 days of data when seasonal patterns span months&lt;/li&gt;
&lt;li&gt;Dropping entire log categories to keep storage costs down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When ingestion and storage are expensive, comprehensive logging becomes a luxury instead of standard practice. But what if the economics actually worked?&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changes When You Can Keep Everything
&lt;/h2&gt;

&lt;p&gt;Bronto's architectural efficiency — 90% compression, optimized storage, purpose-built for logs — means you pay 50–90% less to ingest the same data. This changes everything.&lt;/p&gt;

&lt;p&gt;Consider tracking API performance across enterprise customers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Monitoring performance across multiple API customers&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;count&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="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'customer_key_1'&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'customer_key_2'&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'customer_key_3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;resTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'MISS, MISS'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;shield&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;cache&lt;/span&gt;
&lt;span class="c1"&gt;-- Time range: 30 days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When ingestion is affordable, you can actually keep the 30 days of high-volume logs this investigation requires. No sampling, no abbreviations, no compromises.&lt;/p&gt;

&lt;h3&gt;
  
  
  30-Day Performance Archaeology
&lt;/h3&gt;

&lt;p&gt;A customer report of intermittent slowdowns triggered a deep investigation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resTime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resTime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;count&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="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'customer_12345'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;shield&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;cache&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="c1"&gt;-- Analyzing response times week-over-week for the past month&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analyzing max and average response times over 30 days revealed certain endpoints were gradually slowing down — a pattern invisible in a 7-day window but clear with month-long data.&lt;/p&gt;

&lt;p&gt;The team spotted week-over-week degradation that would have caused a critical outage within weeks. With 7-day retention, they never would have seen it coming.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Month Error Forensics
&lt;/h3&gt;

&lt;p&gt;When 404 errors started appearing randomly across the CDN, the team needed to know: new issue or recurring pattern?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;count&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="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="k"&gt;ILIKE&lt;/span&gt; &lt;span class="s1"&gt;'%/api/v2/content/%'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;response_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'404'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shield&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pop&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="c1"&gt;-- 90-day investigation of 404 errors for content API endpoints&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filtering 90 days of complete, unsampled logs revealed what looked like random errors was actually a cache invalidation race condition — one that only occurred under specific geographic and timing conditions. With 7-day windows and aggressive sampling, this would have remained an unsolved mystery.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Intelligence Layer: From Logs to Insights
&lt;/h2&gt;

&lt;p&gt;Long-term retention unlocks intelligence — but only if you can query it. Bronto's analytics engine lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run SQL queries across months of data in seconds&lt;/li&gt;
&lt;li&gt;Set up change detection that automatically flags when cache hit rates drop&lt;/li&gt;
&lt;li&gt;Build filterable dashboards to click into any spike or anomaly&lt;/li&gt;
&lt;li&gt;Use natural language with the AI dashboard builder: &lt;em&gt;"Show me API response times by customer and region for the last 90 days"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Use BrontoScope to interrogate logs with context, correlating events and surfacing root causes that would take hours to piece together manually&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Becomes Possible
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;API customer journey mapping&lt;/strong&gt; — track the entire API consumer lifecycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which endpoints do new users test first?&lt;/li&gt;
&lt;li&gt;How does usage scale over months?&lt;/li&gt;
&lt;li&gt;What usage patterns precede account downgrades?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Intelligent cache optimization&lt;/strong&gt; — with a year of data you can finally answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do cache hit ratios vary by season, not just by day?&lt;/li&gt;
&lt;li&gt;Which content genuinely needs global distribution vs. regional?&lt;/li&gt;
&lt;li&gt;How do different shield configurations perform under various traffic patterns?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security pattern recognition&lt;/strong&gt; — long-term retention enables sophisticated threat detection:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bot networks that rotate through IP ranges over weeks&lt;/li&gt;
&lt;li&gt;Credential stuffing campaigns that evolve over months&lt;/li&gt;
&lt;li&gt;DDoS reconnaissance that occurs weeks before an attack&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fcx7txc6kwq0re3ooo5yi.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Fcx7txc6kwq0re3ooo5yi.gif" alt="API performance dashboard in Bronto" width="720" height="392"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Before and After Bronto
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Field names abbreviated to save bytes&lt;/td&gt;
&lt;td&gt;Full field names: readable and searchable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aggressive sampling: keeping 1% of logs&lt;/td&gt;
&lt;td&gt;100% coverage: every request, response, error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Short retention: 7 days max&lt;/td&gt;
&lt;td&gt;365-day retention: full seasonal patterns visible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blind spots: log categories dropped to save costs&lt;/td&gt;
&lt;td&gt;Complete visibility: CDN and compute logs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Technical Foundation: Built for Fastly's Scale
&lt;/h2&gt;

&lt;p&gt;Bronto integrates with Fastly through a &lt;a href="https://www.fastly.com/documentation/guides/integrations/logging-endpoints/log-streaming-bronto/" rel="noopener noreferrer"&gt;built-in logging endpoint&lt;/a&gt;. Configure it once in your Fastly service settings and logs start streaming in minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simple Fastly Integration&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;Bronto&lt;/span&gt;
&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JSON&lt;/span&gt;
&lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://logs.bronto.io/fastly&lt;/span&gt;
&lt;span class="na"&gt;compression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zstd&lt;/span&gt;  &lt;span class="c1"&gt;# 10:1 automatic compression&lt;/span&gt;
&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
&lt;span class="na"&gt;header_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;X-Bronto-Token&lt;/span&gt;
&lt;span class="na"&gt;header_value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${BRONTO_TOKEN}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it scales:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elastic ingestion that grows with traffic&lt;/li&gt;
&lt;li&gt;90% data compression&lt;/li&gt;
&lt;li&gt;Time-series optimization for CDN access patterns&lt;/li&gt;
&lt;li&gt;Intelligent tiering keeps recent data hot, historical data accessible&lt;/li&gt;
&lt;li&gt;Column-oriented storage for efficient aggregations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Bronto Express: From Ingestion to Insights in Minutes
&lt;/h2&gt;

&lt;p&gt;Setting up this level of insight shouldn't require weeks of work. &lt;strong&gt;Bronto Express for Fastly&lt;/strong&gt; uses AI to take raw log streams and dynamically build Fastly-specific queries, monitors, and dashboards personalized for your organization.&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.amazonaws.com%2Fuploads%2Farticles%2Fp4xpfu8jadu3byixkwxv.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.amazonaws.com%2Fuploads%2Farticles%2Fp4xpfu8jadu3byixkwxv.png" alt="Bronto Express Fastly preview" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It includes pre-built dashboards for real-time performance, geographic traffic, API usage, and security monitoring; a saved searches library for common investigations; and intelligent monitors for performance degradation and error rate thresholds.&lt;/p&gt;

&lt;p&gt;Setup is four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create your Bronto instance (selecting the Fastly Express pack)&lt;/li&gt;
&lt;li&gt;Add Bronto as a logging endpoint in Fastly (5-minute configuration)&lt;/li&gt;
&lt;li&gt;Watch pre-built dashboards populate with your data&lt;/li&gt;
&lt;li&gt;Customize searches and alerts for your specific needs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No agents. No forwarders. No complex pipelines.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Platform Effect: When Every Team Has Access
&lt;/h2&gt;

&lt;p&gt;When CDN logs become affordable and accessible, every team benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Engineering&lt;/strong&gt; — debugs issues with complete historical context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; — investigates threats across meaningful timeframes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product&lt;/strong&gt; — understands real usage patterns, not samples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sales&lt;/strong&gt; — identifies expansion opportunities from actual usage data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finance&lt;/strong&gt; — accurately allocates costs to business units&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support&lt;/strong&gt; — resolves tickets with full forensic capability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't just about saving money on logging (though 50–90% savings helps). It's about removing the artificial constraints that prevent teams from fully understanding their infrastructure.&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690a0d4891fc665398f7fccf_Bronto%2520CDN%2520Fastly%2520with%2520Maps.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690a0d4891fc665398f7fccf_Bronto%2520CDN%2520Fastly%2520with%2520Maps.png" alt="Geographic visualization of CDN traffic" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bronto.io/book-a-demo" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;See Sub-Second Search Across Your Fastly Logs&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>devops</category>
      <category>logging</category>
      <category>cdn</category>
      <category>observability</category>
    </item>
    <item>
      <title>Investigation Reports: When Monitors Get Smarter</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Fri, 22 May 2026 18:04:01 +0000</pubDate>
      <link>https://dev.to/bronto_io/investigation-reports-when-monitors-get-smarter-51ei</link>
      <guid>https://dev.to/bronto_io/investigation-reports-when-monitors-get-smarter-51ei</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Marco Aquilanti&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a monitor fires, there's a familiar sequence of checks required to find the root cause. The engineers who set up the monitor usually know these steps by heart — they know the dependencies, the error codes, what to check and where. But for the on-call responder, these steps aren't always obvious. Historically, the solution was to force engineering teams to document the checks in a playbook and hope the responder would read it under pressure.&lt;/p&gt;

&lt;p&gt;Today, we can offload these checks to an LLM, shifting the responder's role from &lt;strong&gt;gathering evidence&lt;/strong&gt; to &lt;strong&gt;reviewing a diagnosis&lt;/strong&gt; — significantly reducing MTTR.&lt;/p&gt;

&lt;p&gt;Our new &lt;strong&gt;Investigation Reports&lt;/strong&gt; feature does exactly this: an LLM completes the investigation and delivers a detailed report before the human even acknowledges the alert.&lt;/p&gt;

&lt;p&gt;Investigation Reports builds on &lt;a href="https://www.bronto.io/blog/error-investigations-with-brontoscope" rel="noopener noreferrer"&gt;BrontoScope&lt;/a&gt;, our first AI-powered investigation feature — and on the positive customer feedback it generated.&lt;/p&gt;




&lt;h2&gt;
  
  
  BrontoScope vs. Investigation Reports
&lt;/h2&gt;

&lt;p&gt;Both BrontoScope and Investigation Reports perform automated investigations and provide reports, but they work differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BrontoScope&lt;/strong&gt; starts with a user request to investigate a specific error event in the logs. The investigation follows a defined workflow aimed at establishing when and where an error is occurring. The LLM guides the process and summarizes findings synchronously — the user is waiting for a response and gets it in seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Investigation Reports&lt;/strong&gt; is triggered by a system event (a monitor firing), with no user waiting for a synchronous response. This gives the LLM more time — not seconds but minutes — to query data and analyze results. Investigating an alert is also a more generic task than BrontoScope's focused error investigation, making it harder to define a single fixed workflow that succeeds in every scenario.&lt;/p&gt;

&lt;p&gt;For these reasons, Investigation Reports lets the LLM operate more freely — giving it &lt;strong&gt;tools&lt;/strong&gt; and &lt;strong&gt;context&lt;/strong&gt; rather than a coded workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tools Are Easy, Context Is Hard
&lt;/h2&gt;

&lt;p&gt;The tools side is straightforward: the LLM can call Bronto's APIs to perform &lt;a href="https://www.bronto.io/blog/why-is-bronto-so-fast" rel="noopener noreferrer"&gt;lightning-fast log search&lt;/a&gt;, query key-value dictionaries, check monitor history, retrieve precomputed metrics, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt; is the harder problem.&lt;/p&gt;

&lt;p&gt;LLMs make good logical decisions when provided with relevant, well-explained context. But the context window is limited — and it's been demonstrated repeatedly that longer context leads to worse answers and higher hallucination rates. This is known as "context rot" (see &lt;a href="https://research.trychroma.com/context-rot" rel="noopener noreferrer"&gt;research from Chroma&lt;/a&gt; and &lt;a href="https://arxiv.org/pdf/2509.21361" rel="noopener noreferrer"&gt;this arXiv paper&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For an effective investigation, the LLM needs more than just the monitor that fired. It needs historical context and an understanding of the monitored system. But dumping thousands of tokens of documentation into the prompt backfires — it drastically increases hallucination risk and degrades report accuracy.&lt;/p&gt;




&lt;h2&gt;
  
  
  User-Defined Investigation Context
&lt;/h2&gt;

&lt;p&gt;The precise knowledge needed for a good investigation is hard for an LLM to infer autonomously — but it can be provided by the engineer who owns the monitor.&lt;/p&gt;

&lt;p&gt;In a dedicated "Investigation Prompt" text area, the user instructs the LLM on what to check and what to do when the monitor fires. Free-form text makes the feature highly flexible, effectively letting users define an ad-hoc workflow for each specific use case.&lt;/p&gt;

&lt;p&gt;Engineers and SREs commonly include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List of dependencies of the affected service&lt;/li&gt;
&lt;li&gt;Related log datasets and how to correlate/query them&lt;/li&gt;
&lt;li&gt;Relevant keys and metrics to check&lt;/li&gt;
&lt;li&gt;What to include in the report — affected components, customers, or users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The screenshot below shows an example investigation prompt telling the LLM to check datasets in a collection named "booking system":&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.amazonaws.com%2Fuploads%2Farticles%2Fte4por6ohzd2dlbx3x1f.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.amazonaws.com%2Fuploads%2Farticles%2Fte4por6ohzd2dlbx3x1f.png" alt="Investigation prompt configuration" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's the Investigation Report generated when that monitor fired — the LLM followed the instructions, ran multiple queries, and produced a report with a potential root cause, diagnosis, and timeline:&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.amazonaws.com%2Fuploads%2Farticles%2Fysaqs1s1qt78m983721s.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.amazonaws.com%2Fuploads%2Farticles%2Fysaqs1s1qt78m983721s.png" alt="Generated investigation report" width="583" height="699"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Investigation Reports Beyond Incident Response
&lt;/h2&gt;

&lt;p&gt;Bronto's own customer support and sales teams found an unexpected use case. They set up monitors to be notified when new organizations are created or contracts are updated in the system — keeping the team up to date on new sign-ups and customer onboarding.&lt;/p&gt;

&lt;p&gt;Investigation Reports automates the task of fetching context about each event. Details like contract type, retention plan, company size, and location are queried across multiple logs and assembled into a report that arrives within a minute of the monitor notification. This lets the team quickly identify relevant events among routine ones.&lt;/p&gt;

&lt;p&gt;Below is the investigation prompt used by the customer support team, and an example of the automatically generated report:&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.amazonaws.com%2Fuploads%2Farticles%2Fx8bdmis1nhcvfg6xuqmv.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.amazonaws.com%2Fuploads%2Farticles%2Fx8bdmis1nhcvfg6xuqmv.png" alt="Customer support investigation prompt" width="521" height="250"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2F2s62bhpw73lk5lqxui35.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.amazonaws.com%2Fuploads%2Farticles%2F2s62bhpw73lk5lqxui35.png" alt="Customer support investigation report" width="486" height="540"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Investigation Reports are a great illustration of what LLMs are genuinely good at: taking a well-framed task with relevant context and producing a structured, actionable summary faster than any human could. Every monitor notification now comes with relevant information to speed up resolution.&lt;/p&gt;

&lt;p&gt;We'll be building further on this capability in the coming months — using AI alongside Bronto's logging platform to help teams reduce toil, resolve issues faster, and extract more value from their data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bronto.io/bronto-labs" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Explore Bronto Labs&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>observability</category>
      <category>devops</category>
      <category>logging</category>
    </item>
    <item>
      <title>Using Dashboard Filtering to Get Customer Usage in Seconds from TBs of Data</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Fri, 22 May 2026 13:53:30 +0000</pubDate>
      <link>https://dev.to/bronto_io/using-dashboard-filtering-to-get-customer-usage-in-seconds-from-tbs-of-data-3b0d</link>
      <guid>https://dev.to/bronto_io/using-dashboard-filtering-to-get-customer-usage-in-seconds-from-tbs-of-data-3b0d</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Conall Heffernan&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As the Customer Success lead at Bronto, I need fast, reliable insights into customer health and product usage — but I don't have time to constantly update indexes, schemas, or individual widgets just to answer new questions. I need to spot patterns, explore trends, and get answers in real time without manual overhead.&lt;/p&gt;

&lt;p&gt;That's why Bronto's dashboards are so critical to my work. We recently added a new query filtering feature that lets me use SQL to look for any pattern across all widgets in a dashboard simultaneously — and update everything at lightning speed.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Bronto Dashboards Provide
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rich visualisation options&lt;/strong&gt; — time-series charts, geomaps, numeric value widgets (with units like bytes and time), top lists, treemaps, and log event lists for drilling into raw data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI widget builder&lt;/strong&gt; — describe what you want in natural language; an LLM builds the query and creates the widget without you needing to know the query language or which datasets to select (&lt;a href="https://www.bronto.io/blog/building-dashboards-with-ai" rel="noopener noreferrer"&gt;see the full post on this feature&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full screen widget mode&lt;/strong&gt; — compare different timeframes for the same query (e.g. vs. previous day, week, month)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy filtering&lt;/strong&gt; — use the query builder with a searchable dropdown of top keys and values, or write SQL filters directly in the filter bar&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2F53u81j0o04n96irxymdu.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.amazonaws.com%2Fuploads%2Farticles%2F53u81j0o04n96irxymdu.png" alt="Bronto dashboard overview" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Power of Filtering at Scale
&lt;/h2&gt;

&lt;p&gt;In many logging or observability tools, applying a filter means updating every single chart, table, or widget individually. Tedious. Time-consuming. It doesn't scale.&lt;/p&gt;

&lt;p&gt;With Bronto, applying a filter in the main query bar &lt;strong&gt;instantly updates every single widget on the dashboard&lt;/strong&gt; for your given timeframe. With a default retention period of one year, you don't need to worry about missing long-term trends — all your data is fully searchable and visualisable. Concerned about when an issue started? You can analyze trends over months, not days.&lt;/p&gt;

&lt;p&gt;Our widgets use pre-computed log-based metrics (LBMs) for rapid responses, but the new dashboard filtering goes further — running raw log queries to filter the data in your dashboard in real time. Results come back in seconds, and you can drill down across all widgets simultaneously using SQL or by clicking from a dropdown of top keys and values.&lt;/p&gt;

&lt;p&gt;One thing worth noting: &lt;strong&gt;there's no initial configuration of keys required&lt;/strong&gt;. I can use any key I want in the filter, with no setup.&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.amazonaws.com%2Fuploads%2Farticles%2Fhfox04m9gccw8zexp8vy.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Fhfox04m9gccw8zexp8vy.gif" alt="Dashboard filtering GIF — all widgets update simultaneously" width="720" height="349"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Log-Based Filtering vs. Log-Based Metrics
&lt;/h2&gt;

&lt;p&gt;Log-based metrics (LBMs) deliver millisecond responses — perfect for real-time dashboards, trend analysis, and high-level views of system behavior.&lt;/p&gt;

&lt;p&gt;When something unexpected happens or a new question comes in, we seamlessly switch to log-based filtering to investigate deeper, explore raw data, and uncover answers without being constrained by pre-defined fields or aggregations.&lt;/p&gt;

&lt;p&gt;Log-based filtering excels because it lets you query and visualize raw logs immediately — no upfront configuration like index definitions or field extraction required. Bronto combines this with structured parsing and indexing, using the right approach for the right job.&lt;/p&gt;

&lt;p&gt;The result: both fast, flexible investigation and high-performance queries on known fields, without forcing you to predefine every key or build parsing pipelines before you can search effectively.&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.amazonaws.com%2Fuploads%2Farticles%2Fm5jyxzhz2yx8fj5wbo5b.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.amazonaws.com%2Fuploads%2Farticles%2Fm5jyxzhz2yx8fj5wbo5b.png" alt="Log-based filtering in action" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Use Dashboard Filtering for Customer Usage
&lt;/h2&gt;

&lt;p&gt;My primary use for this feature is gathering and presenting product usage data to our leadership team. Questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"How much data did Org ID 54321 send over the last 6 months?"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"How much did company ACME search last month?"&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of building 10 custom dashboards (which doesn't scale as your customer base grows), I use dashboard filtering:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to our main Usage Dashboard&lt;/li&gt;
&lt;li&gt;Enter the specific &lt;code&gt;org_id&lt;/code&gt; in the main query filter (e.g. &lt;code&gt;org_id: 54321&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Every widget updates instantly to reflect only that organization's data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. A complex, multi-step data lookup becomes a quick and easy process.&lt;/p&gt;

&lt;p&gt;The first time I tried filtering across the dashboard for an &lt;code&gt;org_id&lt;/code&gt;, I thought something wasn't working right — the results were rendered so fast across terabytes of data. It was a genuine "wow" moment. As a customer support lead, it's great to see the under-the-hood changes we're building for customers also improving my own day-to-day.&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.amazonaws.com%2Fuploads%2Farticles%2F8dt7b0dzmrz9a3x1kaky.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.amazonaws.com%2Fuploads%2Farticles%2F8dt7b0dzmrz9a3x1kaky.png" alt="Dashboard filtered by org_id" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How Bronto Dashboards Compare
&lt;/h2&gt;

&lt;p&gt;Most dashboard tools are optimized for known questions: predefined fields, fixed widgets, metrics decided on ahead of time. That works for stable monitoring but breaks down when you need to explore new questions, investigate unexpected behavior, or quickly slice data in different ways.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Bronto Dashboards&lt;/th&gt;
&lt;th&gt;Traditional Competitors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Query Scope&lt;/td&gt;
&lt;td&gt;SQL filtering on any log value&lt;/td&gt;
&lt;td&gt;Often restricted to predefined or indexed fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update Speed&lt;/td&gt;
&lt;td&gt;Instant — all widgets update simultaneously&lt;/td&gt;
&lt;td&gt;Manual, per-widget updates required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Terabytes rendered in seconds across all datasets&lt;/td&gt;
&lt;td&gt;Latency issues with large datasets; often minutes to render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Schema-less — no upfront definitions or configuration&lt;/td&gt;
&lt;td&gt;Parsing and indexing pipelines required first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Filtering included in monthly quota; faster MTTR&lt;/td&gt;
&lt;td&gt;Slow dashboards can inflate costs by consuming more resources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Modern teams need dashboards that are fast, flexible, and easy to adapt as questions change. Bronto dashboards combine high-performance log-based metrics with instant, dashboard-wide filtering to help you explore usage, investigate issues, answer leadership questions, and quickly re-run reports as requirements evolve.&lt;/p&gt;

&lt;p&gt;Questions like "what does this customer's usage look like over the last six months?" are answered in seconds — without upfront schemas, per-widget reconfiguration, or slow refresh cycles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bronto.io/book-a-demo" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;See Dashboard Filtering in Action&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>logging</category>
      <category>devops</category>
      <category>observability</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Your LLM Logs Deserve Better — Send Claude Code Events to Bronto</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Thu, 21 May 2026 18:47:19 +0000</pubDate>
      <link>https://dev.to/bronto_io/your-llm-logs-deserve-better-send-claude-code-events-to-bronto-1nmk</link>
      <guid>https://dev.to/bronto_io/your-llm-logs-deserve-better-send-claude-code-events-to-bronto-1nmk</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Conall Heffernan&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this post, I'll walk through how to integrate Anthropic's Claude Code monitoring with Bronto so you can get a clear, unified view of your AI tool usage — all in one place.&lt;/p&gt;

&lt;p&gt;This project came out of our recent company AI sprint — a chance for me, as a technical support manager, to jump into an engineering sprint and brush up on my Python and Bash skills. After years of helping customers set up and troubleshoot their environments, it was exciting to get hands-on and demo my findings to the wider team over pizza and beers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Can Send from Claude Code to Bronto
&lt;/h2&gt;

&lt;p&gt;Claude Code has a built-in monitoring feature that provides a detailed view into how your teams use AI-assisted coding — from productivity metrics to cost visibility. When paired with Bronto, this data becomes even more useful.&lt;/p&gt;

&lt;p&gt;Here's what you can do with the exported data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Understand usage patterns&lt;/strong&gt; — analyze how different users and teams engage with Claude Code over time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure productivity&lt;/strong&gt; — track lines of code modified and models used&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor costs&lt;/strong&gt; — get a clear picture of spend by team, user, or session&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit prompts&lt;/strong&gt; — see what users are actually asking, building, or automating; identify common use cases, detect misuse, and refine prompts based on real-world usage&lt;/li&gt;
&lt;/ul&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690be8e11777bd82c81936fc_Frame%2520709.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690be8e11777bd82c81936fc_Frame%2520709.png" alt="Claude Code usage dashboard in Bronto" width="799" height="287"&gt;&lt;/a&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690beba7d88bcdeba38bde40_Screenshot%25202025-11-06%2520at%252000.23.59%25201.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690beba7d88bcdeba38bde40_Screenshot%25202025-11-06%2520at%252000.23.59%25201.png" alt="Claude Code session view" width="799" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Bronto specifically:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralise&lt;/strong&gt; all event logging across your organisation for unified analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualise effortlessly&lt;/strong&gt; — create dashboards using the AI widget creator without writing queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up monitors&lt;/strong&gt; to alert on sudden cost spikes, unusual token usage, or high session volumes from specific teams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast, scalable search&lt;/strong&gt; — explore and investigate your LLM logs quickly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle high volume easily&lt;/strong&gt; — Claude Code can generate massive, detailed logs; Bronto's storage and query engine handles this without breaking your budget&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-term retention&lt;/strong&gt; — 12 months of hot logs by default, which is increasingly important for support tracking, debugging non-deterministic LLM behavior, and &lt;a href="https://artificialintelligenceact.eu/article/19/" rel="noopener noreferrer"&gt;EU AI Act compliance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Two Ways to Connect Claude Code to Bronto
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Direct (Simplest)
&lt;/h3&gt;

&lt;p&gt;Claude Code has built-in OTel support, so it can send telemetry straight to Bronto. Just set these environment variables (full details in &lt;a href="https://docs.claude.com/en/docs/claude-code/monitoring-usage" rel="noopener noreferrer"&gt;Claude Code's docs&lt;/a&gt;):&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLAUDE_CODE_ENABLE_TELEMETRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOGS_EXPORTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;otlp
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http/protobuf
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://ingestion.eu.bronto.io
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_HEADERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"x-bronto-api-key=&amp;lt;api_key_here&amp;gt;,x-bronto-collection=AISprint,x-bronto-dataset=claude-code"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOGS_EXPORT_INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2000
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOG_USER_PROMPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup, Claude Code sends structured logs directly to Bronto — no additional infrastructure to run or maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running Claude Code locally or in a single environment&lt;/li&gt;
&lt;li&gt;No existing centralised OTel collector&lt;/li&gt;
&lt;li&gt;Lightweight, zero-maintenance integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 2: Via an Existing OTel Collector
&lt;/h3&gt;

&lt;p&gt;If your organisation already runs an OTel Collector, route Claude Code's logs through that same pipeline before forwarding to Bronto. This is the approach I used — I spun up a small AWS EC2 instance running the OTel collector, configured Claude Code to send logs to it, and had the collector forward those logs to Bronto.&lt;/p&gt;

&lt;p&gt;Point Claude Code at your local collector:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLAUDE_CODE_ENABLE_TELEMETRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOGS_EXPORTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;otlp
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grpc
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4317
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOG_USER_PROMPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Persist these environment variables — otherwise starting a new terminal session will clear them and you'll wonder why nothing is flowing. Ask me how I know.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Collector receiver config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4317&lt;/span&gt;  &lt;span class="c1"&gt;# Default OTLP gRPC port&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4318&lt;/span&gt;  &lt;span class="c1"&gt;# Default OTLP HTTP port&lt;/span&gt;
        &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;allowed_origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aggregating logs from multiple sources before forwarding&lt;/li&gt;
&lt;li&gt;Applying processors like filters&lt;/li&gt;
&lt;li&gt;Keeping your Bronto exporter config in one place&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Generating Some Log Data
&lt;/h2&gt;

&lt;p&gt;To verify everything was working, I fired up Claude Code and asked it to create a simple Python script, then made a few tweaks — spin up a basic GUI, take an input value, save it locally. While I was doing this, Claude Code was generating log events in the background, flowing through my OTel collector and straight into Bronto.&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690bea21d87d6e7006211527_image%2520700.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690bea21d87d6e7006211527_image%2520700.png" alt="Log events flowing into Bronto" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Searching Claude Logs in Bronto
&lt;/h2&gt;

&lt;p&gt;During testing, Claude was hitting errors because I'd been using an old revoked API key from a previous project. While the UI showed an error, I could also see the API errors flowing into Bronto. A simple &lt;code&gt;error&lt;/code&gt; query surfaced the logs directly.&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690be95616906ce6b7a55a0c_image%2520699.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690be95616906ce6b7a55a0c_image%2520699.png" alt="API error logs in Bronto" width="799" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also created a tree map to instantly visualize which models were used across the project — making it easy to see usage patterns and identify which models were most heavily used.&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690bec82e2cb74fc78804c2d_Screenshot%25202025-10-24%2520at%252012.32.40%25E2%2580%25AFp.m.%25201.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690bec82e2cb74fc78804c2d_Screenshot%25202025-10-24%2520at%252012.32.40%25E2%2580%25AFp.m.%25201.png" alt="Tree map of model usage" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token usage analysis&lt;/strong&gt; — a multi group-by on &lt;code&gt;$input_tokens&lt;/code&gt; and &lt;code&gt;$output_tokens&lt;/code&gt; helps identify outliers and inefficient prompts, control API spend, and uncover latency trends tied to model size or context length.&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690becd8686f49d58caa4d4c_Screenshot%25202025-10-24%2520at%252012.35.21%25E2%2580%25AFp.m.%25201.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690becd8686f49d58caa4d4c_Screenshot%25202025-10-24%2520at%252012.35.21%25E2%2580%25AFp.m.%25201.png" alt="Token usage by group" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session duration&lt;/strong&gt; — tracking how long each session stayed active helps identify inefficient workflows, idle connections, or hanging requests that inflate costs. It's also a signal for user engagement: shorter sessions suggest quick completions, longer ones can point to complex prompts or slow model responses.&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690bedbb76c4988680f8437c_Screenshot%25202025-10-31%2520at%25202.30.09%25E2%2580%25AFp.m.%25201.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690bedbb76c4988680f8437c_Screenshot%25202025-10-31%2520at%25202.30.09%25E2%2580%25AFp.m.%25201.png" alt="Session duration over time" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt auditing&lt;/strong&gt; — prompts are redacted by default. To enable them, set &lt;code&gt;OTEL_LOG_USER_PROMPTS=1&lt;/code&gt; (already included in the config above).&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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690bed71d7567084d5dc4c63_image%2520701.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%2Fcdn.prod.website-files.com%2F67d2e1c8bd118640c72006cb%2F690bed71d7567084d5dc4c63_image%2520701.png" alt="Prompt audit log view" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;From Claude Code to Bronto, all connected through OpenTelemetry — a few environment variables, an open source agent, and you have logs and insights flowing in. Once it's set up, you get full visibility into how your AI tooling is being used, cost metrics, and what's actually happening under the hood.&lt;/p&gt;




&lt;h2&gt;
  
  
  Appendix: Full OTel Collector Config
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Receivers:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4317&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4318&lt;/span&gt;
        &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;allowed_origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Exporters:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlphttp/brontobytes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs_endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://ingestion.eu.bronto.io/v1/logs"&lt;/span&gt;
    &lt;span class="na"&gt;compression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gzip&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;x-bronto-api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INGESTION_API_KEY_GOES_HERE"&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
    &lt;span class="na"&gt;retry_on_failure&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="na"&gt;initial_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;max_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;max_elapsed_time&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;120s&lt;/span&gt;
    &lt;span class="na"&gt;sending_queue&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="na"&gt;num_consumers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;queue_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pipelines:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;memory_limiter&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;transform&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlphttp/brontobytes&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://app.eu.bronto.io/signup" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Try Bronto Free — Send Your First Claude Logs&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>logging</category>
      <category>devops</category>
      <category>claudecode</category>
    </item>
    <item>
      <title>The 4L's of Logging — The New Standard for the AI Era</title>
      <dc:creator>Patrick Londa</dc:creator>
      <pubDate>Thu, 21 May 2026 16:18:59 +0000</pubDate>
      <link>https://dev.to/bronto_io/the-4ls-of-logging-the-new-standard-for-the-ai-era-amc</link>
      <guid>https://dev.to/bronto_io/the-4ls-of-logging-the-new-standard-for-the-ai-era-amc</guid>
      <description>&lt;p&gt;&lt;em&gt;Authored by Noel Ruane &amp;amp; Trevor Parsons&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Traditionally, the world of logging had 3L's — &lt;strong&gt;low cost, low latency, and large scale&lt;/strong&gt; — and the accepted wisdom was that you'd have to compromise by choosing two of the three.&lt;/p&gt;

&lt;p&gt;Until recently, the logging market bore this out. Solutions fell into one of three categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expensive and fast&lt;/strong&gt; — Datadog, ELK-type solutions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slow and cheap(er)&lt;/strong&gt; — index-free solutions like Grafana Loki (still relatively expensive when you actually search)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tweeners&lt;/strong&gt; — expensive, relatively slow, not great at scale — think CloudWatch, Google Cloud Logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyone who's ever hired builders knows the iron rule: fast, cheap, or quality — pick two.&lt;/p&gt;

&lt;p&gt;But the world has changed with the arrival of AI. As everyone builds and innovates around LLMs and agents, achieving all three L's has become more essential than ever. And AI has added a fourth requirement to the list: &lt;strong&gt;long-term retention&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Becomes 4: What Long-Term Retention Opens Up
&lt;/h2&gt;

&lt;p&gt;With a short retention window — 3, 15, or 30 days, as most solutions provide — logs are accessible only for outages and system issues severe enough to warrant immediate attention.&lt;/p&gt;

&lt;p&gt;Long-term retention opens up an entirely new set of use cases.&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.amazonaws.com%2Fuploads%2Farticles%2Fb8wsq74b6biq4g2ba5fi.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.amazonaws.com%2Fuploads%2Farticles%2Fb8wsq74b6biq4g2ba5fi.png" alt="Use cases enabled by long-term retention" width="799" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A common example: comparing customer API request latencies or conversion metrics month-on-month, looking at seasonality effects, or preparing data for a customer QBR. You might want to analyze customers in aggregate to spot trends, or drill into a specific customer's history.&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.amazonaws.com%2Fuploads%2Farticles%2Fm3wxbcxs5m1zgxupc052.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.amazonaws.com%2Fuploads%2Farticles%2Fm3wxbcxs5m1zgxupc052.png" alt="Long-term data analysis example" width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Support issues can be recent or ongoing over months. Being able to look beyond a few days of data is often the difference between resolving a customer issue properly and leaving it half-answered.&lt;/p&gt;

&lt;p&gt;In an AI world where agents can interrogate your data and solve problems at a completely different scale, long-term &lt;strong&gt;hot&lt;/strong&gt; retention is essential — not the cumbersome, costly process of rehydrating logs from archives that most providers offer. The common workaround of dumping logs to S3 and spinning up Athena is another great example of the kind of toil that shouldn't exist in modern logging.&lt;/p&gt;

&lt;p&gt;The importance of long-term hot data is borne out by our own customers: the longer the data retention, the more use cases open up, the longer the time periods searched, and the more value customers extract from their data. This value will only increase as agents become more powerful.&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.amazonaws.com%2Fuploads%2Farticles%2Fwqh1ehixwoippfxnksta.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.amazonaws.com%2Fuploads%2Farticles%2Fwqh1ehixwoippfxnksta.png" alt="Retention vs. value curve" width="430" height="298"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The 4L's Set a New Standard for the AI Era
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://youtu.be/w87UvmMcmW4?si=Ob5CXZ4tEmjew8eI" rel="noopener noreferrer"&gt;Microsoft's Satya Nadella put it well&lt;/a&gt;: AI is at the intersection of where intelligence meets data. If you don't have the data, you can't point AI at it. The new requirements for data in the AI era map directly to the 4L's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Large Scale&lt;/strong&gt; — data that is always and easily accessible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-Term Retention&lt;/strong&gt; — data retained so AI can analyze it and take action on it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Cost&lt;/strong&gt; — storage and search must be cost-efficient as volumes see exponential growth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Latency&lt;/strong&gt; — fast search and querying&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As Bessemer Venture Partners notes in their &lt;a href="https://www.bvp.com/atlas/roadmap-ai-infrastructure" rel="noopener noreferrer"&gt;Roadmap for AI Infrastructure&lt;/a&gt;, the volume of unstructured data is projected to reach 612 zettabytes by 2030, driven by ML/AI and synthetic data generation. Log data is one specific type where this applies acutely.&lt;/p&gt;

&lt;p&gt;Here's why logging requirements are intensifying in the AI era:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Systems of action are replacing systems of record.&lt;/strong&gt; As AI-native apps don't just store data but act on it, having log data accessible and hot means AI models can actually do something meaningful with it. The era of retaining logs for 30 days and locking them in cold storage is ending.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-led Root Cause Analysis.&lt;/strong&gt; We're already seeing AIs perform RCA using log data to help teams solve problems faster. &lt;a href="https://www.bronto.io/blog/error-investigations-with-brontoscope" rel="noopener noreferrer"&gt;BrontoScope&lt;/a&gt;, for example, automatically runs an investigation that assesses how widespread an error is — figuring out what queries to run, assessing results, running further queries, and delivering an immediate assessment with recommendations. It can save anywhere from minutes to hours per incident. As MCP becomes standard, log data will be analyzable with additional context from other systems — replicating what engineers do manually today when piecing together evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLMs are non-deterministic.&lt;/strong&gt; Given the unpredictable nature of LLMs, logs will play an even more important role in understanding what happened — whether from a support, debugging, audit, security, or compliance perspective. The same action can deliver different results, making logs essential for showing exactly what occurred. (See our &lt;a href="https://www.bronto.io/blog/logging-events-from-ollama" rel="noopener noreferrer"&gt;guide to collecting Ollama logs&lt;/a&gt; to get started.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regulation.&lt;/strong&gt; &lt;a href="https://artificialintelligenceact.eu/article/19/" rel="noopener noreferrer"&gt;EU AI Act Article 19&lt;/a&gt;, for example, will require logs of certain higher-risk AI systems to be retained for at least 6 months from 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Purpose-Built Adaptive Technology for Log Data
&lt;/h2&gt;

&lt;p&gt;How do you actually achieve all 4L's? The short answer: treat logs as a first-class citizen and build a purpose-built data store for them.&lt;/p&gt;

&lt;p&gt;Until now, vendors have always repurposed existing datastores for logging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch&lt;/strong&gt; — at the core of most logging solutions for two decades. Super fast because it indexes everything, but super expensive due to data bloat and operational complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClickHouse&lt;/strong&gt; — more recently vendors have turned to ClickHouse. Great for metrics, good for some logs — but only if you know the shape of your data in advance, which is a non-starter for a general-purpose logging platform. (Full write-up: &lt;a href="https://www.bronto.io/blog/why-clickhouse-fails-as-a-general-purpose-logging-solution" rel="noopener noreferrer"&gt;Why ClickHouse fails as a general-purpose logging solution&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snowflake&lt;/strong&gt; — vendors like Observe have turned to Snowflake to address the cost problem, but customers regularly report search latency issues and burning through CPU credits when searching large data volumes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key learning from two decades in this space: &lt;strong&gt;not all log data should be treated equally&lt;/strong&gt;. Don't just "index everything" (inverted indexes) or at the other extreme "index nothing" (index-free solutions). Instead, take a more thoughtful approach — &lt;strong&gt;let the system adapt and optimize based on the shape of your data and the queries you run&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These optimizations operate at every layer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Indexing:&lt;/strong&gt; We combine multiple patent-pending adaptive compact indexes. The platform automatically decides which to apply based on data and query patterns. Techniques include Bloom filters, summary indexes, partition indexing, automatic log-to-metric conversion, and additional patent-pending technologies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; Our storage tier routes data to the most appropriate structure based on its shape and how it will be searched — columnar store, specialized data structures, high compression, partial decoding, and storage tier coordination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search:&lt;/strong&gt; Specialized algorithms applied depending on the query being run, highly parallelized to operate at any scale.&lt;/p&gt;

&lt;p&gt;For deeper reading: &lt;a href="https://www.bronto.io/blog/why-is-bronto-so-fast" rel="noopener noreferrer"&gt;Why Bronto is so fast at searching logs&lt;/a&gt;, or the Log Management Cost Trap series — &lt;a href="https://www.bronto.io/blog/cost-trap-ingestion" rel="noopener noreferrer"&gt;Part 1: Ingestion&lt;/a&gt;, &lt;a href="https://www.bronto.io/blog/cost-trap-storage" rel="noopener noreferrer"&gt;Part 2: Storage&lt;/a&gt;, &lt;a href="https://www.bronto.io/blog/cost-trap-search" rel="noopener noreferrer"&gt;Part 3: Search&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  No Maintenance Required
&lt;/h2&gt;

&lt;p&gt;One important point often overlooked: we see vendors trying to solve the cost problem by deploying within an organization's cloud (BYOC), using the customer's hardware to reduce costs. This requires deploying a logging platform into your environment and having the vendor manage it there. Unless you have tight data regulations, this feels like an overly complex backward step.&lt;/p&gt;

&lt;p&gt;Bronto is built on a modern cloud multi-tenanted architecture. No setup, no maintenance, no management required by the customer. We take advantage of compute/storage separation and independently scaling services.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI = the Intersection of Intelligence and Data
&lt;/h2&gt;

&lt;p&gt;If your current provider is building AI capabilities on top of an old, repurposed, or expensive logging platform — one with 2 L's instead of 4 — you won't be able to take full advantage of the AI revolution.&lt;/p&gt;

&lt;p&gt;The 4L's aren't a nice-to-have. In the AI era, they're the baseline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bronto.io/book-a-demo" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;See the 4L's in Action&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>logging</category>
      <category>ai</category>
      <category>devops</category>
      <category>observability</category>
    </item>
  </channel>
</rss>
