Two ways to give agents capabilities
MCP servers
Remote servers you connect by URL. Tools are discovered automatically at startup. The server can be in any language.
Python tools
Local Python functions. You write the implementation; the engine binds them to agents that declare them.
MCP servers
Declare a server by URL. The engine connects and discovers its tools automatically at startup.MCP connections are created once at startup, not per request. If a server is unreachable, the engine logs a warning and continues — local tools still work.
Tool tags
If your MCP server groups tools into categories, you can limit which tools an agent sees:X-MCP-Tool-Tag header during tool discovery. The server filters — the engine binds only what comes back. The LLM never sees the tags.
Multiple tags are sent comma-joined: X-MCP-Tool-Tag: orders,returns
Custom transport — if your server expects a different header or a query param, tool_tag_transport supports two types:
MCP auth
To add auth headers to outgoing MCP requests, use abefore_mcp_request hook. The token never appears in the YAML or in prompts. See Runtime Hooks.
Python tools
Declare a tool with a description. That description is what the LLM uses to decide whether to call it.Implementing a tool
Rungenerate (see Quickstart) to create stubs. Then fill in the logic:
Calling a real system as the user
A tool that reaches your own API should act as the person who asked, so your API applies their permissions. The credential the request arrived with is on the run context — not a parameter, since anything in the signature becomes part of the schema the LLM sees:Authorization header (see Identity), and never reaches the model.
Resolvers vs. tools
Both are Python code. The difference is when they run and who calls them.
Use a resolver for context the prompt always needs: current date, user name, account info.
Use a tool for actions the user may or may not need: look up an order, create a return, send a message.
Tool call tracing
Every tool call is tracked in the run result — agent, tool name, provider (local or MCP), success or failure, latency. You can see this in logs and in Langfuse if tracing is enabled.Shared tool usage across agents
Tool usage is stored in a repository, not carried in graph state, so every agent and orchestrator working on the same conversation shares it. Each one is privately told what has already been run — including in earlier turns of the same conversation, so an agent can answer “which tools have you used?”:list_executed_tools tool that returns the executed tools straight from the repository. The passive context is useful for reasoning but a model can still mistake a bound child agent for a tool when explicitly reporting. This deterministic lookup trades one reserved engine-tool name and an extra model tool call for an answer grounded in repository data. It is not recorded, does not consume tool/child-call limits, and a configured child with the same name wins.
That block is internal execution context: it is given to the model as a system message, never as a conversation turn, and never persisted as chat history. Only agent, tool name, and status are included — never arguments, results, or errors. It is refreshed before every model turn, so a supervisor’s final answer reflects what the agents it just called actually ran.
The store behind it is a repository port. The in-memory adapter ships with the engine; a shared backend (Redis, PostgreSQL) is an adapter swap in the composition layer, with no change to agents or tools. Bounded reads return the newest matching records in chronological order, and context requests one extra record only to detect truncation.