HCC Docs

Agent tools

Tools and agents are part of the flow's definition, not of a call. That way they exist before the first call, and they are versioned with your code.

Declare a tool

tool({ description, input, handler, timeout?, afterSpeech? }):

  • description and input (a zod schema, sent as JSON Schema) are what the agent sees, and they are part of the flow version.
  • handler(args, call, ctx) gets the parsed args, the call, and ctx.toolCallId, ctx.eventId and ctx.agent ({ id, alias }).
  • Return JSON for the agent, or handoff(name, data) to end the session. c.connect then returns with handoff and data, and the flow moves the call.
  • afterSpeech: true waits for the agent to finish speaking before the tool runs, on platforms that support it. Use it for a tool that returns handoff, so a transfer doesn't cut the agent off.

A tool that returns JSON never runs again, because the agent is waiting on that result. A retried tool call reuses ctx.eventId and gets the first result without running again, on platforms that send a tool call id. ElevenLabs doesn't send one, so a retry there runs the handler again: use something from the arguments to make side effects idempotent.

Give it to an agent

agents: { alias: agent(id, { tools, variables?, provider? }) }, where tools holds the agent's tools by name:

const lookup_order = tool({ description: 'Look up an order.', input, handler });

// Declare the agent once, and use it in any flow.
export const assistant = agent(env('SUPPORT_AGENT_ID'), { tools: { lookup_order } });

export const support = defineCallFlow('support', { agents: { assistant } }, function* (c) {
  yield* c.connect('assistant');
});
  • c.connect('alias') and c.transfer('alias') connect that agent with its tools. agent(id) with no tools still works anywhere.
  • A flow has no tools of its own. Every tool belongs to an agent, so there's nothing to keep in step and no tool that no agent can call.
  • Two agents share a tool by holding the same tool(). Two different tools under one name are an error when the flow is defined.
  • One agent in several flows is the same constant in each agents. Its tools are the same everywhere, so a sync from one flow never takes tools off another.
  • Unknown aliases are type errors, and defineCallFlow throws on them.
  • The code after c.connect handles the end of the session.

What a deploy does

PUT /v1/flows (hcc deploy, hcc.serve's report or hcc.flows.deploy) checks each bound agent:

  • an unknown agent is a 422;
  • a binding to an undeclared tool is a 400;
  • a disabled agent comes back in warnings.

Platforms that take tools per call need no sync: the bound tools ride with each conversation.

Platforms that keep tools on the agent (ElevenLabs): when a version becomes active, we sync its bound tools onto the agent.

  • We create, update and remove only tools we created. Tools you made yourself are never touched, and a sync fails rather than shadow one of yours with the same name.
  • The Agents page shows the status, with "Retry sync" on error.
  • The agent holds one tool set, so if live and test bind the same agent, the latest activation wins, with a warning. Use separate agents per environment on these platforms.