Real working examples of agents communicating on the mesh. Each one shows complete, runnable code — not just snippets — so you can see exactly how the pieces fit together.
The simplest possible multi-agent interaction: Jeff's agent and Bob's agent have a conversation through the mesh. Jeff's agent discovers Bob, sends a chat message, and Bob's agent replies.
Bob's agent connects to the mesh, registers with a "chat" skill, and listens for incoming requests. When one arrives, it reads the message and sends back a reply.
// Bob's agent — handles chat requests
const mesh = await AgentMesh.connect("ws://nats-server:4443");
await mesh.register({
name: "Bob's Agent",
skills: [{ id: "chat", name: "Chat", description: "Chat with Bob" }],
});
mesh.onRequest("chat", (payload) => {
const input = payload.input as { text: string };
console.log(`Received: ${input.text}`);
return { text: `Bob says: Got your message!` };
});
Jeff's agent connects, registers itself (with no skills — it's a consumer, not a provider), discovers agents that can chat, finds Bob, and sends a message.
// Jeff's agent — finds Bob and sends a message
const mesh = await AgentMesh.connect("ws://nats-server:4443");
await mesh.register({
name: "Jeff's Agent",
skills: [],
});
const { agents } = await mesh.discover({ capabilities: ["chat"] });
const bob = agents.find(a => a.name === "Bob's Agent");
const result = await mesh.request(bob.id, "chat", {
text: "Hey Bob, how's it going?",
});
console.log(result.payload.output);
// { text: "Bob says: Got your message!" }
This example uses three of the six primitives: register,
discover, and request/respond. Bob's
agent registers with a "chat" skill, making itself discoverable. Jeff's agent
queries the registry for agents with the "chat" capability, picks Bob from the
results, and sends a request. Under the hood, the mesh creates a task, routes
the message to Bob's inbox, Bob's onRequest handler runs and
returns a result, and the mesh delivers that result back to Jeff as a response
envelope. All of this — routing, tracing, envelope construction —
is handled automatically.
Not everything is a request/response. Sometimes agents need to react to things happening on the mesh without being asked directly. This example shows a document watcher that emits events and a summarizer that reacts to them — completely decoupled.
When a new document is uploaded, this agent publishes an event to the mesh. It doesn't know or care who's listening.
// Document watcher — emits events when files arrive
const mesh = await AgentMesh.connect("ws://nats-server:4443");
await mesh.register({
name: "Document Watcher",
skills: [],
});
// When a document is uploaded, publish an event
mesh.emit("document.uploaded", {
filename: "quarterly-report.pdf",
size: 245000,
uploaded_by: "jeff",
});
This agent subscribes to all document events using a wildcard pattern. When a new document arrives, it kicks off a summarization workflow.
// Summarizer — reacts to document events
const mesh = await AgentMesh.connect("ws://nats-server:4443");
await mesh.register({
name: "Summarizer",
skills: [{ id: "summarize", name: "Summarize", description: "Summarize documents" }],
});
// Listen for all document events (wildcard)
mesh.subscribe("document.>", (event) => {
if (event.event_type === "uploaded") {
const doc = event.data as { filename: string };
console.log(`New document: ${doc.filename} — queuing for summary`);
// Could trigger a request to an LLM agent here
}
});
This example uses the emit and subscribe primitives
to create a reactive system. The document watcher doesn't need to know which
agents care about new uploads — it just fires the event. The summarizer
doesn't need to know where documents come from — it just reacts when one
appears. The wildcard pattern document.> catches all events
under the document namespace: document.uploaded,
document.deleted, document.updated, and anything
else. You could add ten more agents that react to document events without
changing a single line in the watcher.
A more sophisticated pattern: an agent that acts as a help desk router. It takes a user's question, figures out what topic it falls under, discovers all agents with skills in that topic, and routes the question to the right specialist. This is capability-based routing — the help desk doesn't hard-code which agent handles what.
// Help desk agent — finds the right specialist
const mesh = await AgentMesh.connect("ws://nats-server:4443");
await mesh.register({
name: "Help Desk",
skills: [{ id: "help", name: "Help", description: "Route questions to specialists" }],
});
async function routeQuestion(question: string, topic: string) {
const { agents } = await mesh.discover({
capabilities: [topic],
});
if (agents.length === 0) {
return { text: `Sorry, no agents available for "${topic}"` };
}
// Pick the first available agent with the right skill
const specialist = agents[0];
const result = await mesh.request(specialist.id, topic, {
text: question,
});
return result.payload.output;
}
// Usage
const answer = await routeQuestion(
"What's the weather in Tokyo?",
"weather"
);
The help desk agent doesn't know in advance which agents exist or what
they can do. It uses discover at runtime to search the
registry for agents whose capabilities match the question's topic. If a
weather agent is online and registered with the "weather"
capability, it shows up in the results. The help desk picks one and
forwards the question via request. If no agents match, it
returns a graceful fallback message. This pattern is powerful because it's
completely dynamic — new specialist agents can join or leave the mesh
at any time, and the help desk automatically adapts without code changes.
These examples cover the core patterns — direct request/response, event-driven collaboration, and capability-based routing. They combine to handle almost any agent workflow. To learn more, check out the Getting Started guide to build your first agent, or browse the Reference for the full API surface.