Multi-Agent Orchestration
Route messages between agents, call tools on remote agents via MCP, and compose complex pipelines from independent Durable Object agents.
How it works
Each Honi agent is a Durable Object — addressable by binding name and thread ID. The multi-agent helpers let you communicate between agents directly over DO stubs, with no HTTP round-trips leaving your Cloudflare account.
Routing Messages
Use routeToAgent to send a chat message to another agent and get back its full response:
TypeScript
1import { routeToAgent } from 'honidev'
2
3const result = await routeToAgent(
4 env,
5 { binding: 'RESEARCHER_AGENT', threadId: 'thread-1' },
6 'Summarise the latest earnings report for AAPL'
7)
8
9console.log(result.response) // final assistant reply
Calling Tools on Remote Agents
Use callAgentTool to invoke a specific tool on another agent via its MCP endpoint — useful for composing agent capabilities without full chat:
TypeScript
1import { callAgentTool, listAgentTools } from 'honidev'
2
3// See what tools another agent exposes
4const tools = await listAgentTools(env, { binding: 'DATA_AGENT' })
5
6// Call a specific tool directly
7const data = await callAgentTool(
8 env,
9 { binding: 'DATA_AGENT' },
10 'fetch_metrics',
11 { metricId: 'revenue-q4' }
12)
wrangler.toml Setup
Each agent needs its own Durable Object binding. Agents that call each other need both bindings declared:
TOML — wrangler.toml
1# Orchestrator agent
2[[durable_objects.bindings]]
3name = "ORCHESTRATOR"
4class_name = "OrchestratorDO"
5
6# Sub-agents
7[[durable_objects.bindings]]
8name = "RESEARCHER_AGENT"
9class_name = "ResearcherDO"
10
11[[durable_objects.bindings]]
12name = "DATA_AGENT"
13class_name = "DataDO"
Full Example — Orchestrator Pattern
TypeScript — src/index.ts
1import { createAgent, tool, routeToAgent, z } from 'honidev'
2
3// Sub-agent: handles research tasks
4const researcher = createAgent({
5 name: 'researcher',
6 model: 'claude-sonnet-4-5',
7 binding: 'RESEARCHER_AGENT',
8 system: 'You are a research specialist.',
9})
10
11// Orchestrator: routes tasks to sub-agents
12const delegateResearch = tool({
13 name: 'delegate_research',
14 description: 'Send a research task to the researcher agent',
15 input: z.object({ task: z.string() }),
16 handler: async ({ task }, { env }) => {
17 const res = await routeToAgent(env, { binding: 'RESEARCHER_AGENT' }, task)
18 return res.response
19 }
20})
21
22const orchestrator = createAgent({
23 name: 'orchestrator',
24 model: 'claude-sonnet-4-5',
25 binding: 'ORCHESTRATOR',
26 tools: [delegateResearch],
27 system: 'You coordinate tasks across specialist agents.'
28})
29
30export default { fetch: orchestrator.fetch }
31export const OrchestratorDO = orchestrator.DurableObject
32export const ResearcherDO = researcher.DurableObject
API Reference
| Function | Description |
|---|---|
routeToAgent(env, agent, message) | Send a chat message to another agent. Returns { response, messages } |
callAgentTool(env, agent, toolName, args) | Call a specific tool on another agent via MCP |
listAgentTools(env, agent) | List tools exposed by another agent's MCP endpoint |
getAgentHistory(env, agent) | Fetch conversation history from another agent |
clearAgentHistory(env, agent) | Clear another agent's conversation history |
AgentReference
| Field | Type | Description |
|---|---|---|
binding | string | Durable Object namespace binding name from wrangler.toml |
threadId | string? | Thread/instance ID (defaults to 'default') |