Serving tools
The a2a endpoint exposes the wrapped application’s commands to other agents over NATS as callable tools. A peer reads the agent’s card and invokes one of them. No prompt is sent and the agent loop does not run, so the model, budget and session settings do not apply.
Note
The endpoint is opt-in. The configuration must set expose.agent.a2a.serve_tools: true, otherwise fisk serve exposes
no tools over a2a.
Serving tools is a endpoint of fisk serve since
Version0.0.5. The fisk a2a command is gone, and a configuration
carrying the old expose.agent.agent_to_agent key is refused at startup.
Serving a set of tools
A worker serving only tools needs no system_prompt and no llm.model. It does need application_path: built-in
tools are never served, so an agent with no wrapped application serves nothing.
Reading the card
fisk discover fetches the card the worker answers discovery with:
Importing those tools into another agent is remote_tools in that agent’s configuration, covered in the
Reference.
Tool selection
expose.agent.tools applies on top of the agent’s include and exclude. One file can run every stream_ tool in a
job and serve two of them to peers.
A tool carrying ai:confirm or a configured confirm tag is left off the card, because no operator is behind a served
call to approve it. Use ai:deny to keep a command out entirely.
Built-in tools are never served. Knowledge, memory and the human-in-the-loop tools declare no a2a exposure, and the startup banner lists them as withheld when the configuration enables them.
Limits
| Setting | Description |
|---|---|
max_concurrent_tools (int) | tool calls run at once; default is the CPU count clamped to 2 to 8 |
tool_timeout (duration) | limit on one call this agent answers; default 30s |
request_timeout (duration) | wait for a peer’s next message; default 2m, minimum 30s |
In a container the concurrency default reads the container’s CPU limit, not the host’s, and the banner prints the
concurrency in use. --workers sizes the queued-jobs intake and does not reach these. harness.tool_timeout limits a
tool call inside the agent loop.
request_timeout limits a call this agent makes to a peer. The peer answers with a set of messages: an
acknowledgement, a keepalive every ten seconds while the tool runs, then the reply. The timeout applies to the gap
between those messages, so this agent waits for as long as the keepalives arrive, and harness.tool_timeout ends the
call. A card fetch is a single message, so the same value covers a whole card fetch.
The same value is how long the prompts endpoint holds a question it put to a caller, see Answering questions. Raising it for a slow peer raises that window too.
An agent that only calls other agents still needs request_timeout, so a block holding nothing else is valid:
Any other expose.agent.a2a block must set serve_tools: true or a prompts block, or fisk serve exits with an
error.
The server refuses a call that arrives with every slot in use rather than queueing it. The ack carries
accepted: false, and the tool.reply carries is_error: true with code: capacity. The identity is a NATS queue
group, so a retry is delivered to whichever member is free next.
Running with other endpoints
A single fisk serve process runs a channel and this endpoint on one NATS connection:
Adding a prompts block to the same a2a block also answers prompts, covered in
Answering prompts.
Shutdown and faults
A drain stops the endpoint answering and removes the identity from its queue group, so the worker takes no further tool calls. Prompts stop with it, both endpoints using one transport and one identity.
A drain does not wait for a call that is already running. The call runs to completion with nowhere to reply to, and a
command it started may outlive the worker. tool_timeout stops a call that does not finish.
An error on any of the service’s subscriptions stops the whole micro service, taking discovery, tools and prompts for
that identity down together. fisk serve logs it, drains the runs in flight and exits non-zero, so a supervisor
restarts the worker. A drain stops the service by the same path, and is logged rather than reported as a fault.
Safety
Whoever can publish to choria.fisk-ai.tool.<identity> can run every tool on the card. NATS permissions are the whole
of the access control: a served call carries no verified caller, so nothing can distinguish one peer from another.
The tool safety rules described in the Reference hold here as everywhere else: commands run as an argument vector rather than through a shell, each argument is checked against the command’s schema, and credentials are stripped from tool environments.