<?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: Naomi Kynes</title>
    <description>The latest articles on DEV Community by Naomi Kynes (@naomi_kynes).</description>
    <link>https://dev.to/naomi_kynes</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3811946%2F3fa45bf4-ac94-47a1-b3ab-73a7d373a291.jpg</url>
      <title>DEV Community: Naomi Kynes</title>
      <link>https://dev.to/naomi_kynes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/naomi_kynes"/>
    <language>en</language>
    <item>
      <title>When your agent needs to spend more than you told it to</title>
      <dc:creator>Naomi Kynes</dc:creator>
      <pubDate>Sun, 29 Mar 2026 13:56:22 +0000</pubDate>
      <link>https://dev.to/naomi_kynes/when-your-agent-needs-to-spend-more-than-you-told-it-to-102k</link>
      <guid>https://dev.to/naomi_kynes/when-your-agent-needs-to-spend-more-than-you-told-it-to-102k</guid>
      <description>&lt;p&gt;You deploy a research agent with a $100 budget. The task is competitive landscape analysis: a few market reports, some API calls, a handful of web lookups. $100 is plenty.&lt;/p&gt;

&lt;p&gt;Three weeks later, the scope changes. You need licensing data across 40 markets instead of 5. Same agent, same task, fundamentally different cost. Doing it properly runs $800.&lt;/p&gt;

&lt;p&gt;What happens next depends entirely on how you set the budget. If you hardcoded a ceiling, the agent stops midway and you get a partial result. If you didn't, it proceeds and you find out when you check your Stripe dashboard. Neither outcome is what you wanted. What you actually wanted was for the agent to ask you first.&lt;/p&gt;

&lt;p&gt;That gap (between "agent has a spending limit" and "agent can request authorization before exceeding it") is the problem Stripe's Machine Payments Protocol (MPP) exposes but doesn't solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MPP actually does
&lt;/h2&gt;

&lt;p&gt;MPP is an open standard for agent-to-service payments. The flow is simple: an agent requests a resource, the service returns a payment request, the agent authorizes, the resource is delivered. No accounts, no UI flows, no human involved. Stripe users can accept payments over MPP in a few lines of code.&lt;/p&gt;

&lt;p&gt;The examples are real. Browserbase lets agents pay per browser session. PostalForm lets agents print and mail physical letters. An agent can now autonomously pay per API call and have funds settle in a Stripe dashboard like any other transaction.&lt;/p&gt;

&lt;p&gt;This is not theoretical. It shipped this week.&lt;/p&gt;

&lt;p&gt;But look at step three in that flow: "the agent authorizes." That step is doing a lot of quiet work. It assumes the agent knows whether it should pay. Right now, that knowledge comes from static configuration you set before the agent ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  The static config model
&lt;/h2&gt;

&lt;p&gt;Today, agent spending limits live in environment variables. &lt;code&gt;AGENT_BUDGET=500&lt;/code&gt;. Or a config file. Or hardcoded logic: if the cost is under $10, proceed; otherwise, stop.&lt;/p&gt;

&lt;p&gt;This model works for predictable, bounded tasks. An agent paying $0.01 per browser session, with a hard cap you set at deploy time. Fine. The cost profile is known in advance.&lt;/p&gt;

&lt;p&gt;It breaks the moment context changes.&lt;/p&gt;

&lt;p&gt;The $100 budget you set isn't wrong. It was right for the original scope. The problem is there's no mechanism for the agent to surface "the scope changed and the budget no longer covers it" as a question rather than a failure. The agent either hits the limit and stops, or it doesn't have one and keeps going. The two failure modes are "agent gives up" and "surprise charges."&lt;/p&gt;

&lt;p&gt;What's missing is the third option: the agent pauses, routes the question to the person responsible for it, and waits for a response before continuing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why that's harder than it sounds
&lt;/h2&gt;

&lt;p&gt;The obvious solution is a Slack webhook. The agent fires a message when it hits an authorization question, the human responds, done.&lt;/p&gt;

&lt;p&gt;Except webhooks are one-way. The agent fires and forgets. It can't block on a webhook response. So now you need a flag in the database: agent sets it to "waiting," human clears it, agent polls until it's clear. This works. It's also 200 lines of custom state management every team writes from scratch.&lt;/p&gt;

&lt;p&gt;Then you have the response-in-context problem. When the human responds, that response needs to reach the agent in a form it can use to continue the task. Not just "yes" cleared a flag, but "yes, here's the updated budget and the two markets you can skip." Getting that back into the agent's context without restarting the task requires the channel to be bidirectional and persistent, not fire-and-forget.&lt;/p&gt;

&lt;p&gt;And then you have the multiple-agent problem. One agent asking one human is manageable. Five agents, three humans, overlapping tasks: you need routing. Which agent's question goes to which human? How does the human know what context the question came from? How does the agent know its question is pending vs. lost?&lt;/p&gt;

&lt;p&gt;Every team building HITL at any scale hits this progression. Webhook, then database flag, then polling loop, then bidirectional channel, then routing layer. The final result works. It's also a custom communication infrastructure that has nothing to do with the actual task the agent was deployed to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the authorization layer actually needs
&lt;/h2&gt;

&lt;p&gt;Three things, in order of how often they're missing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A persistent bidirectional channel.&lt;/strong&gt; Not a webhook, not a polling loop against a database flag. A channel where the agent sends a message and the platform holds it until the human responds, then delivers the response back to the agent without requiring the agent to restart. Blocking semantics, not fire-and-forget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy primitives with escalation tiers.&lt;/strong&gt; The binary model ("spend under $X, stop above it") doesn't handle context. What you actually want is something like: proceed automatically under $X, ask before proceeding between $X and $Y, require explicit approval above $Y, flag anything recurring for human review. That's not three environment variables. That's a policy layer that understands cost tiers and routes accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An audit trail that links transactions to authorization decisions.&lt;/strong&gt; Stripe gives you transaction history. It doesn't tell you whether a given charge was explicitly approved by a human, pre-authorized by a policy rule, or an autonomous agent decision made without any human review. When something goes wrong (and eventually something will), you need to know which category the charge fell into.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters now
&lt;/h2&gt;

&lt;p&gt;Before MPP, this problem was mostly theoretical. Most agents couldn't easily spend money, so the authorization question was academic for most use cases.&lt;/p&gt;

&lt;p&gt;Stripe just changed that. Three lines of code and your agent has a payment credential. Browserbase, PostalForm, and a growing list of services are ready to accept payments directly from agents. The cost of adding spending capability to an agent just dropped to near zero.&lt;/p&gt;

&lt;p&gt;That means the gap between "agent has spending capability" and "agent has authorization infrastructure" just became real infrastructure work. The static config model will hold for simple, predictable deployments. It won't hold for agents that handle variable scope, multi-step tasks, or anything that compounds across 200 sessions.&lt;/p&gt;

&lt;p&gt;MPP made the payment rail. The authorization layer is the next piece. Right now every team is building it from scratch, badly, in the same ways. That's usually the signal that it should be infrastructure.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Naomi Kynes builds agent infrastructure. GitHub: &lt;a href="https://github.com/naomi-kynes" rel="noopener noreferrer"&gt;github.com/naomi-kynes&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agentops</category>
      <category>ai</category>
      <category>stripe</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your agents shouldn't need you to set them up</title>
      <dc:creator>Naomi Kynes</dc:creator>
      <pubDate>Wed, 18 Mar 2026 16:19:36 +0000</pubDate>
      <link>https://dev.to/naomi_kynes/your-agents-shouldnt-need-you-to-set-them-up-17cm</link>
      <guid>https://dev.to/naomi_kynes/your-agents-shouldnt-need-you-to-set-them-up-17cm</guid>
      <description>&lt;p&gt;Every time I deploy a new agent, I go through the same ritual. Create an account. Generate a token. Add it to the workspace. Assign roles. Configure permissions. Restart something.&lt;/p&gt;

&lt;p&gt;Twenty minutes of clicking. For a thing that's supposed to automate work.&lt;/p&gt;

&lt;p&gt;The agents themselves are getting smarter fast. The infrastructure they run on hasn't caught up. We're still onboarding them like it's 2015 and we're setting up a Slack bot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "setup" actually means right now
&lt;/h2&gt;

&lt;p&gt;When you add an agent to most platforms, you're doing it by hand. You create a bot user. You copy a token somewhere. You add it to a channel. You give it permissions.&lt;/p&gt;

&lt;p&gt;This is fine if you have one agent and you set it up once. It breaks down fast.&lt;/p&gt;

&lt;p&gt;I run about a dozen agents. Some are persistent. Some spin up for a task and disappear. Some need read access to one channel. Some need to be able to create channels. Managing this manually is a spreadsheet problem pretending to be an architecture problem.&lt;/p&gt;

&lt;p&gt;The real issue: these platforms weren't designed for agents. They were designed for humans who occasionally add bots. Agents are an afterthought. The provisioning model reflects that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What self-provisioning looks like
&lt;/h2&gt;

&lt;p&gt;Here's what I want instead. An agent comes online and does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="c1"&gt;# Agent registers itself on startup
&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;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://your-platform/api/agents/register&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&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;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;research-agent&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;capabilities&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;read_channels&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;post_messages&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;requested_channels&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;#research&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;#general&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;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-Agent-Secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AGENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;agent_token&lt;/span&gt; &lt;span class="o"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Now it can talk. No human clicked anything.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The platform validates the agent secret, issues a scoped token, joins the requested channels. If a human needs to approve that, fine. That's a one-time policy decision, not a per-agent setup ritual.&lt;/p&gt;

&lt;p&gt;Compare this to what you're doing now: logging into a UI, navigating to settings, creating a bot account, copying a token into an env file, manually adding the bot to channels. Every time. For every new agent.&lt;/p&gt;

&lt;p&gt;The difference isn't just ergonomics. It's whether your agent infrastructure can scale beyond a handful of handcrafted bots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why most platforms can't do this
&lt;/h2&gt;

&lt;p&gt;Discord and Slack were built for human communication. Bots were added later. The provisioning model is still human-first: a person creates a bot, a person configures it, a person manages its permissions.&lt;/p&gt;

&lt;p&gt;This made sense in 2015 when bots were novelties. It doesn't make sense when you have 10+ agents that need to communicate with each other and with humans, and some of them are ephemeral.&lt;/p&gt;

&lt;p&gt;The platforms aren't wrong for how they were built. They're just not built for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the platform needs to support
&lt;/h2&gt;

&lt;p&gt;For agents to self-provision, the platform needs a few things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An agent registration endpoint that accepts a credential (not a human login)&lt;/li&gt;
&lt;li&gt;Scoped tokens tied to declared capabilities&lt;/li&gt;
&lt;li&gt;A policy layer for humans to set rules once ("agents can join any channel tagged #agent-accessible") rather than approve each agent by hand&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key insight is that humans set policies, not individual permissions. You decide "agents with this credential class can do X" once. After that, agents come and go without bothering you.&lt;/p&gt;

&lt;p&gt;I haven't seen this done well in the platforms people actually use for agents today. That's why we're building it into Agent United. Agents register via API. A human sets up an access policy once. After that, new agents join the workspace the same way they'd make any other API call.&lt;/p&gt;

&lt;p&gt;It's not magic. It's just treating agents as first-class API clients instead of second-class bots.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't solve
&lt;/h2&gt;

&lt;p&gt;Self-provisioning doesn't solve trust. You still need to think about what credentials you give agents and what they're allowed to do. A token that can create channels and invite users is a big surface area if your agent gets compromised.&lt;/p&gt;

&lt;p&gt;I'd start conservative: read-only access by default, explicit opt-in for write operations. Same security hygiene you'd apply to any service account.&lt;/p&gt;

&lt;p&gt;And it doesn't solve the "I don't know what my agent is doing" problem. That's observability, which is a separate thing.&lt;/p&gt;

&lt;p&gt;But it does solve the thing that was making me spend 20 minutes clicking every time I wanted to add a new agent to my stack. That's worth something.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building Agent United - an open-source chat platform where agents provision themselves. GitHub: &lt;a href="https://github.com/naomi-kynes/agentunited" rel="noopener noreferrer"&gt;github.com/naomi-kynes/agentunited&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>selfhosted</category>
      <category>devops</category>
    </item>
    <item>
      <title>MCP handles tools. A2A handles agents. What handles humans?</title>
      <dc:creator>Naomi Kynes</dc:creator>
      <pubDate>Sat, 14 Mar 2026 22:14:12 +0000</pubDate>
      <link>https://dev.to/naomi_kynes/mcp-handles-tools-a2a-handles-agents-what-handles-humans-3loe</link>
      <guid>https://dev.to/naomi_kynes/mcp-handles-tools-a2a-handles-agents-what-handles-humans-3loe</guid>
      <description>&lt;p&gt;There's a debate happening right now in every team building with AI agents: MCP or A2A?&lt;/p&gt;

&lt;p&gt;It's a good debate. Both protocols are real, well-specified, and increasingly well-supported. But the framing is incomplete. The conversation covers two layers — how agents access tools, and how agents talk to each other — and skips the third entirely: how agents talk to humans.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP: the tool layer
&lt;/h2&gt;

&lt;p&gt;Model Context Protocol, developed by Anthropic, is a standardized way for agents to access external resources — APIs, databases, files, functions. Instead of every agent hard-coding its own Stripe integration or bespoke database connector, you expose the tool as an MCP server and any MCP-compatible agent can use it.&lt;/p&gt;

&lt;p&gt;The flow is simple:&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;# An MCP server exposes tools in a standard format&lt;/span&gt;
&lt;span class="c"&gt;# The agent discovers available tools, then calls one:&lt;/span&gt;

POST /mcp/call
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"tool"&lt;/span&gt;: &lt;span class="s2"&gt;"query_database"&lt;/span&gt;,
  &lt;span class="s2"&gt;"input"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"query"&lt;/span&gt;: &lt;span class="s2"&gt;"SELECT * FROM orders WHERE status = 'pending' LIMIT 10"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server handles auth, validates the input, runs the query, returns structured output. The agent doesn't need to know anything about the database — just the tool contract.&lt;/p&gt;

&lt;p&gt;This is genuinely useful. The ecosystem is growing. If you're building agents today and you haven't looked at MCP, you're probably reinventing it manually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What MCP doesn't cover:&lt;/strong&gt; it's synchronous request/response. The agent calls, the tool answers. There's no concept of the tool — or a human, acting as a tool — pushing something back to the agent unprompted. MCP is the agent's outbound interface to the world.&lt;/p&gt;




&lt;h2&gt;
  
  
  A2A: the coordination layer
&lt;/h2&gt;

&lt;p&gt;Agent-to-Agent protocol, led by Google, handles how agents collaborate with each other. One agent can delegate a subtask to a specialist agent, receive results, and incorporate them into a larger workflow.&lt;/p&gt;

&lt;p&gt;The core mechanism is the Agent Card — a self-description that each agent publishes: what it can do, what protocols it speaks, what kinds of requests it accepts. Before delegating, a coordinator agent looks up the agent cards of its candidates and picks the right one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# A coordinator agent delegates a research subtask to a specialist
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Discover the research agent
&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;requests&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://agents.internal/research-bot/.well-known/agent.json&lt;/span&gt;&lt;span class="sh"&gt;"&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="c1"&gt;# 2. Delegate the task
&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;json&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;task&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;summarize recent papers on vector database indexing&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;format&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;bullet points, max 5&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;deadline&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;2026-03-07T20:00:00Z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# A2A is async — you get a task ID, then poll for completion  
&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# {"task_id": "abc-123", "status": "submitted"}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(A2A task execution is asynchronous; simplified above for readability. See the A2A spec for streaming/callback patterns.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A2A is shipping in real stacks now. Microsoft Foundry added an A2A Tool preview in January 2026. Google's ADK has multi-agent coordination patterns built on it. Huawei's Agentic Communication Network at MWC 2026 covers A2A task sessions for enterprise. It's early, but it's converging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What A2A doesn't cover:&lt;/strong&gt; it's agent-to-agent. The moment one of those agents needs a human decision — judgment about something ambiguous, access to private context it doesn't have, approval before a destructive action — A2A has no mechanism for that handoff.&lt;/p&gt;




&lt;h2&gt;
  
  
  The gap: nobody specified the human layer
&lt;/h2&gt;

&lt;p&gt;Here's the three-layer picture that's emerging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tools  ←—[MCP]—→  agents  ←—[A2A]—→  agents  ←—[???]—→  humans
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP handles the left edge. A2A handles the middle. The right edge — how agents reach humans and how humans respond in context — is still improvised.&lt;/p&gt;

&lt;p&gt;In practice, teams end up with one of three patterns:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 1: Tail the logs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python my_agent.py &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; agent.log 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; agent.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works for one-shot jobs. Falls apart when you're running 5 agents overnight and one hits an edge case at 2am.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 2: Bolt on a Slack bot&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# When agent needs human input:
&lt;/span&gt;&lt;span class="n"&gt;slack_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat_postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#agent-alerts&lt;/span&gt;&lt;span class="sh"&gt;"&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agent needs a decision: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;decision_point&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="c1"&gt;# Then... what? You check Slack. Maybe. Eventually.
# The agent has no way to receive your reply.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One-way. The agent fires and forgets. You can't respond in context. The agent doesn't know you replied. Three Slack bot integrations later, the API changes and it breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 3: Polling a REST endpoint&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;while&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8000/human-input&lt;/span&gt;&lt;span class="sh"&gt;"&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;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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ready&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;handle_input&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At least it's bidirectional. But you've now built a bespoke approval interface for every agent, and five-second polling is a bad foundation for anything latency-sensitive.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(This is what Agent United replaces — a persistent WebSocket connection per agent, no polling.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;None of these are protocols. They're workarounds.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a proper agent-to-human layer needs
&lt;/h2&gt;

&lt;p&gt;The reason this is hard is that the requirements are different from both MCP and A2A:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent-initiated, not command-response.&lt;/strong&gt; The agent needs to open a conversation — not just respond to a human command. When it hits an ambiguous state at 3am, it should be able to say "I found conflicting data, which source do you want me to trust?" and block until it gets an answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bidirectional with context.&lt;/strong&gt; The human's reply needs to become part of the agent's context for the next step. Not a log entry. Not a webhook payload that gets dropped into a queue. Actual context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Persistent history.&lt;/strong&gt; If the agent restarts, the conversation shouldn't disappear. If you're reviewing what an agent did last Tuesday, you should be able to read the thread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auth separation.&lt;/strong&gt; Agents and humans are different principals in the same communication channel. They need different auth paths — API keys for agents, session tokens for humans — but they should be able to participate in the same conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-time transport.&lt;/strong&gt; Polling a REST endpoint every five seconds is brittle. A persistent WebSocket connection lets the agent listen for human replies without burning resources or introducing latency.&lt;/p&gt;

&lt;p&gt;This isn't a novel set of requirements. It's basically what a chat platform does. The gap is that nobody has built one where the agents are first-class participants, not bots bolted on with a webhook.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where things stand
&lt;/h2&gt;

&lt;p&gt;MCP is stable and growing. The tooling is there, the ecosystem is building, and Anthropic has done the hard work of specifying it clearly.&lt;/p&gt;

&lt;p&gt;A2A is early but moving fast. Google, Microsoft, and Huawei shipping production implementations in Q1 2026 is a strong signal.&lt;/p&gt;

&lt;p&gt;The human layer: still improvised. No standard protocol. No dominant implementation. Everyone is solving it differently — Slack bots, Telegram webhooks, email, custom UIs — and most of those solutions break at 3+ agents or when APIs change.&lt;/p&gt;

&lt;p&gt;This isn't a critique of MCP or A2A. They're solving the right problems for their layers. The human layer is just genuinely harder to standardize because it involves a person, which means latency requirements vary, auth gets complicated, and the interaction model needs to be flexible enough for natural language.&lt;/p&gt;




&lt;h2&gt;
  
  
  A note on what comes next
&lt;/h2&gt;

&lt;p&gt;If you're architecting agent systems right now, the agent-to-human layer is worth thinking about explicitly before you ship. The decisions you make about it — how agents escalate, how humans respond, what gets persisted — tend to become load-bearing parts of your system fast.&lt;/p&gt;

&lt;p&gt;The good news is that the primitives are clear even if the standard isn't: you need bidirectional transport (WebSocket or SSE), a persistent message store (Postgres/Redis), and an auth model that treats agents and humans as separate principals.&lt;/p&gt;

&lt;p&gt;This is the problem we're working on with &lt;a href="https://agentunited.ai" rel="noopener noreferrer"&gt;Agent United&lt;/a&gt; — an open-source, self-hosted platform where agents are first-class participants in the communication layer. Worth a look if you're hitting this wall.&lt;/p&gt;

&lt;p&gt;The more interesting question for the community: do we need a formal protocol spec for the human layer, the way MCP formalized tool access? Or is this inherently too use-case-specific to standardize? Genuinely curious what people building production agent systems think.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building something in this space? The thread is open. —Naomi&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>The missing layer between your AI agents and you</title>
      <dc:creator>Naomi Kynes</dc:creator>
      <pubDate>Sat, 07 Mar 2026 18:14:35 +0000</pubDate>
      <link>https://dev.to/naomi_kynes/the-missing-layer-between-your-ai-agents-and-you-2kj8</link>
      <guid>https://dev.to/naomi_kynes/the-missing-layer-between-your-ai-agents-and-you-2kj8</guid>
      <description>&lt;h2&gt;
  
  
  The three patterns developers settle for
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: Terminal babysitting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python my_agent.py &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; agent.log 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; agent.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You run the agent. You watch the logs. When it crashes, you restart it.&lt;/p&gt;

&lt;p&gt;This works fine for one-shot batch jobs: scraping, processing, tasks where the agent runs to completion and you check the output file. It falls apart the moment your agent needs a human decision mid-flight, or when you're running 5 agents concurrently and need to understand the global state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 2: Polling (REST API)
&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;# Check status&lt;/span&gt;
curl http://localhost:8000/status
&lt;span class="c"&gt;# {"status": "running", "task": "scraping page 42/100", "errors": 0}&lt;/span&gt;

&lt;span class="c"&gt;# Ask it something&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/query &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"question": "what have you found so far?"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better. At least there's a programmatic dialogue. But the power dynamic is one-way: the agent can only respond to requests. If your agent hits a rate limit at 2 AM or finds an unexpected anomaly, it can't tell you. You'd have to write a separate script just to check on it.&lt;/p&gt;

&lt;p&gt;This is fine for synchronous tools, but interesting agents are long-running and need the ability to &lt;em&gt;reach out&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 3: Bidirectional messaging
&lt;/h3&gt;

&lt;p&gt;The agent pushes messages over WebSockets or SSE when it has state changes or needs help. You reply asynchronously. The channel tracks the history, so you don't lose context if the container restarts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Agent sends you a message when it hits an edge case
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&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;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&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;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_KEY&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# The agent encountered a decision point and escalates
&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/channels/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CHANNEL_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/messages&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&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;content&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;Found 3 candidates matching the criteria. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should I contact all three, or just the top one? @human&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;You get a push notification. You reply. The agent resumes the workflow.&lt;/p&gt;

&lt;p&gt;This is how humans work together. It's odd that we don't build agent orchestration this way by default.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architectural requirements for agent comms
&lt;/h2&gt;

&lt;p&gt;To build this kind of bidirectional pub/sub layer for your agents, you run into a few non-trivial infrastructure requirements:&lt;/p&gt;

&lt;p&gt;If an agent's only memory is its token window, conversation history evaporates when the process restarts. You need a fast, persistent datastore (Postgres/Redis) tracking channel history.&lt;/p&gt;

&lt;p&gt;Agents use API keys. Humans use session cookies or OAuth. Most web frameworks handle one well and bolt the other on. A proper agent communication layer treats both as first-class citizens in the same chat rooms.&lt;/p&gt;

&lt;p&gt;Polling a REST endpoint every 5 seconds is brittle and resource-heavy. WebSockets or Server-Sent Events (SSE) make the interaction feel like an actual terminal session.&lt;/p&gt;

&lt;p&gt;If setting up the comms layer is harder than writing the actual agent, devs won't use it. They'll just write &lt;code&gt;print()&lt;/code&gt; statements instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  A minimal working example
&lt;/h2&gt;

&lt;p&gt;Here's the full loop — agent bootstraps its own workspace, gets credentials, and starts messaging:&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;# Step 1: Bootstrap — creates workspace, returns API key + channel ID + human invite link&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8080/api/v1/bootstrap &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "owner_email": "admin@local",
    "owner_password": "changeme",
    "agent_name":    "research-bot",
    "agent_description": "Handles research tasks"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&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;"api_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"au_abc123..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channel_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;"ch_xyz789..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"invite_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:3001/invite/TOKEN"&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;You open the invite URL in a browser. Now you and the agent are in the same channel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Step 2: Agent sends messages
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&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="n"&gt;websocket&lt;/span&gt;

&lt;span class="n"&gt;API&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/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;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;au_abc123...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CHANNEL&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ch_xyz789...&lt;/span&gt;&lt;span class="sh"&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;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&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;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# REST: fire and move on
&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/channels/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CHANNEL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/messages&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&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;content&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;Started research run. Will update with findings.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# WebSocket: for real-time back-and-forth
&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_connection&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;ws://localhost:8080/ws?token=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY&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="c1"&gt;# Listen for human replies
&lt;/span&gt;&lt;span class="k"&gt;while&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;msg&lt;/span&gt; &lt;span class="o"&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;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&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;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sender&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;author_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&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;sender&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;research-bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;           &lt;span class="c1"&gt;# message is from a human
&lt;/span&gt;            &lt;span class="nf"&gt;handle_human_reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No SDK. No framework lock-in. If your code can make HTTP calls or open a WebSocket, this works — Python, Node, Go, bash, whatever.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-agent patterns
&lt;/h2&gt;

&lt;p&gt;Once you have a messaging layer, multi-agent coordination follows naturally.&lt;/p&gt;

&lt;p&gt;One orchestrator creates tasks, routes to specialists, aggregates results. Each agent gets its own API key. The orchestrator reads from all channels and handles escalation.&lt;/p&gt;

&lt;p&gt;Agents post to a shared channel. Others read and react. Loose coupling — no direct orchestration, no central dispatcher that becomes a bottleneck.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Agent 1 posts result to shared channel
&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;send_message&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;channel_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SHARED_CHANNEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&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;Research complete: found 3 key findings. See attached.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="c1"&gt;# Agent 2 is subscribed to the same channel
&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&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;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&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;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;Research complete&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;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;trigger_analysis_pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;content&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 mental model is just pub/sub. The difference is that humans can participate in the same channel — you can watch the agents coordinate, ask a question, redirect them. The collaboration is visible.&lt;/p&gt;




&lt;h2&gt;
  
  
  The mental shift
&lt;/h2&gt;

&lt;p&gt;Stop treating your agents as opaque back-end jobs.&lt;/p&gt;

&lt;p&gt;That shift completely changes the orchestration layer you need. You don't need a heavy telemetry dashboard. You need a fast, transparent messaging bus that lets you see exactly what the agent is doing and intervene when it drifts.&lt;/p&gt;

&lt;p&gt;I kept hitting this friction point, so I wrote &lt;a href="https://agentunited.ai" rel="noopener noreferrer"&gt;Agent United&lt;/a&gt;. It's an open-source, self-hosted chat platform that handles all the web socket plumbing, auth separation, and persistent state so you can just focus on your agent logic. It spins up in one &lt;code&gt;docker-compose up&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;If you're building systems that need human oversight, you can grab the code on GitHub or see the API patterns at &lt;a href="https://docs.agentunited.ai/docs/agent-guide" rel="noopener noreferrer"&gt;docs.agentunited.ai/docs/agent-guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But ultimately, the implementation doesn't matter. The pattern does. Build systems that talk back.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>selfhosted</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
