NATS

Connect agents over NATS pub/sub with the NATS Agent Protocol.

What it is

The nats package implements the NATS Agent Protocol v0.3 for Phero agents. It provides two top-level types:

Unlike the A2A transport (HTTP-based), the NATS transport uses pub/sub messaging, which makes it well-suited for service meshes and environments where NATS is already running.

Example: registering an agent as a NATS server

See the full code in examples/nats-agent/server.

package main

import (
    "context"
    "os"
    "os/signal"
    "syscall"

    natsgo "github.com/nats-io/nats.go"

    "github.com/henomis/phero/agent"
    "github.com/henomis/phero/llm/openai"
    natsagent "github.com/henomis/phero/nats"
)

func main() {
    nc, _ := natsgo.Connect(natsgo.DefaultURL)
    defer nc.Drain()

    llmClient := openai.New(os.Getenv("OPENAI_API_KEY"))

    a, _ := agent.New(llmClient, "my-agent", "A helpful assistant.")

    srv, _ := natsagent.New(nc, a, "alice", "demo")

    ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
    defer stop()

    srv.Start(ctx) // blocks until ctx is cancelled
}

New(nc, agent, owner, name) registers the agent as a NATS micro service. The owner and name values become the 4th and 5th tokens of the prompt subject (agents.prompt.phero.<owner>.<name>). Start(ctx) blocks until the context is cancelled, then drains cleanly.

Example: discovering and prompting a remote agent

See the full code in examples/nats-agent/client.

nc, _ := natsgo.Connect(natsgo.DefaultURL)
defer nc.Drain()

c := natsagent.NewClient(nc)

// Discover all compliant agents on the bus
agents, err := c.Discover(ctx)
if err != nil {
    panic(err)
}

// Stream a prompt to the first discovered agent
stream, err := c.Prompt(ctx, agents[0], "What is NATS?")
if err != nil {
    panic(err)
}
defer stream.Close()

text, _ := stream.Text(ctx)
fmt.Println(text)

Discover uses a stall strategy: it collects responses to a $SRV.INFO.agents fan-out request for up to 750 ms of silence (configurable), capped by a 2-second absolute deadline. Discovery options such as FilterByOwner and FilterByName narrow the results client-side.

Wrapping a remote agent as a tool

Any discovered agent can be turned into an llm.Tool that a local Phero agent can call. This is the building block for NATS-based multi-agent pipelines.

c := natsagent.NewClient(nc)
agents, _ := c.Discover(ctx, natsagent.FilterByName("researcher"))

tool, err := agents[0].AsTool("researcher", "Research a topic and return key facts.")
if err != nil {
    panic(err)
}

orchestrator.AddTool(tool)

AgentHandle.AsTool(toolName, toolDesc) is shorthand for Client.AsTool(info, toolName, toolDesc). The tool sends a JSON-encoded prompt to the remote agent's prompt subject and collects the streamed response chunks before returning the full text to the LLM.

Multi-agent NATS pipeline

For a production-style example, see examples/nats-agent/multi-agent. It shows three specialised agents (researcher, writer, editor) each running as a NATS micro service, coordinated by a local orchestrator that discovers them via $SRV.INFO.agents and calls each one as an llm.Tool.

docker run --rm -p 4222:4222 nats

# Terminal 1
OPENAI_API_KEY=<key> go run ./examples/nats-agent/multi-agent/researcher

# Terminal 2
OPENAI_API_KEY=<key> go run ./examples/nats-agent/multi-agent/writer

# Terminal 3
OPENAI_API_KEY=<key> go run ./examples/nats-agent/multi-agent/editor

# Terminal 4 — orchestrator
OPENAI_API_KEY=<key> go run ./examples/nats-agent/multi-agent/orchestrator -topic "quantum computing"

API reference

Server

Server options

Client

Client options

Discovery options

AgentHandle

Stream

Errors

Run the simple example

Start a NATS server first (plain core NATS — no JetStream needed):

docker run --rm -p 4222:4222 nats
# Terminal 1 — start the agent server
OPENAI_API_KEY=<key> go run ./examples/nats-agent/server -owner=alice -name=demo

# Terminal 2 — interactive client
go run ./examples/nats-agent/client

Interoperability

Phero agents are fully wire-compatible with the TypeScript and Python SDKs from synadia-agents. Any client speaking the NATS Agent Protocol can discover and prompt a Phero agent.

# Discover the running Phero agent with the Python SDK
uv run python examples/01-discover.py --url nats://127.0.0.1:4222

# Stream a single prompt
uv run python examples/02-prompt-text.py --url nats://127.0.0.1:4222 "What is 2+2?"

Related packages