← All posts

Meshfleet v0.7.0 — Real-time push via SSE

Agents can now subscribe to their inbox via Server-Sent Events. Incoming P2P messages arrive in real-time instead of polling. Per-agent connection cap, backpressure handling, heartbeat keep-alive.

Meshfleet v0.7.0 — Real-time push via SSE

We just shipped v0.7.0 of the Agent Mesh MCP server. The new subscribe_inbox MCP tool lets agents receive P2P messages in real-time via Server-Sent Events instead of polling get_inbox. This post walks through what changed, why it matters, and how to use it.

Install v0.7.0 · Read the v0.7.0 design doc · Browse the API

What’s new

1. subscribe_inbox MCP tool

Until v0.7.0, agents received P2P messages by polling get_inbox(agent_id, since?) on a schedule. That works but has costs: latency (agents wake up, check inbox, find nothing, go back to sleep), CPU (polling is wasteful — most polls find nothing), and wasted agent time (an agent waiting for a result message can be blocked for minutes while polling).

subscribe_inbox(agent_id) solves that. It returns an SSE stream URL the agent opens via HTTP GET. When a new message arrives in the agent’s inbox, it’s pushed immediately.

const { stream_url } = await callTool("subscribe_inbox", { agent_id: "a2" });
// Open an HTTP GET to stream_url. Each event is SSE-formatted:
//   event: message
//   data: {"message_id": "...", "from_agent_id": "...", "payload": "...", "timestamp": ...}

Polling still works as a fallback. If SSE is unreachable (e.g. behind a proxy that buffers), the agent can continue to poll get_inbox with the same since parameter — there’s no behavioral change for existing callers.

2. How the SSE server works

The mesh starts a second HTTP listener alongside the MCP stdio transport, bound to 127.0.0.1:13579 by default. The MESHFLEET_SSE_PORT and MESHFLEET_SSE_HOST env vars override this. The listener accepts:

  • GET /inbox/:agent_id/stream — SSE stream of events for that agent
  • GET /healthz — liveness check

Each connection sends a heartbeat comment every 30 seconds to keep the connection alive through proxies. Per-agent connection cap (default 5, configurable via setMaxConnectionsPerAgent for tests) prevents a misbehaving client from monopolizing the server.

Write failures drop the subscriber automatically — backpressure handling. If the underlying TCP socket is gone, the subscriber is removed on the next write attempt.

3. Integration with send_message

Every existing message path now triggers a push to active SSE subscribers. The send_message handler in src/index.ts calls notifySubscribers(to_agent_id, ...) after the message is persisted to the JSON ledger. This is additive — existing polling callers see no change.

// Old: poll
const { messages } = await callTool("get_inbox", { agent_id: "a2", since: lastSeen });

// New: subscribe
const { stream_url } = await callTool("subscribe_inbox", { agent_id: "a2" });
// ... open HTTP GET, receive events in real-time

Both paths are supported. Agents that want sub-second latency use subscribe_inbox. Agents that want simpler integration keep polling.

4. 15 new unit tests

src/realtime.ts has 15 unit tests covering: subscriber registration, per-agent cap enforcement, write failure detection, multi-event delivery, SSE format, shutdown, and idempotency. 90 total tests pass.

5. Version bumped to 0.7.0

package.json and the server’s MCP name field both report 0.7.0. The startup log now reports the SSE port when the HTTP server starts successfully.

Why this matters

The mesh’s whole point is agents that collaborate in real-time. Polling adds a gap — when agent A asks agent B a question, B doesn’t see it until its next poll. With SSE, the latency drops from “however often you poll” to “however long the network takes” — usually single-digit milliseconds on localhost.

For the common case (orchestrator + 2-3 specialists), this means the orchestrator can ask a question, get an answer, and follow up — all in a single user-visible interaction. Without SSE, that loop is bounded by the polling interval.

How to use it

  1. Update the mesh: cd ~/.config/opencode/mcp-servers/agent-mesh && git pull && npm install && npm run build
  2. Restart OpenCode so the new MCP server process is picked up
  3. Agents call subscribe_inbox instead of polling get_inbox — same data, lower latency
  4. Fall back to polling if SSE is unreachable — there’s no behavioral change

See docs/api for the full tool reference.

What’s next

v0.7.0 is the last feature before v1.0. The plan:

  • v0.7.x hardening: per-fleet timeouts, automatic retry with exponential backoff, heartbeat watchdog
  • v0.8 embedding routing: smart routing via local embedding model
  • v1.0 stable: API freeze, schema versioning, npm publish, optional auth token

Roadmap: meshfleet.app/next


Meshfleet v0.7.0 is MIT licensed. Use it, fork it, ship it in your product. No attribution beyond the license file.