Skip to main content

Module tools

Module tools 

Source
Expand description

CODE-REGISTERED tools — the embedder seam.

An embedder building its own binary on the agentd-core library can register native Rust tools the agent calls alongside MCP tools:

agentd::tools::register(agentd::tools::CodeTool::new(
    "shout",
    "Uppercase the input text.",
    serde_json::json!({"type": "object", "properties": {"text": {"type": "string"}},
                       "required": ["text"]}),
    |args| {
        let text = args.get("text").and_then(serde_json::Value::as_str).unwrap_or("");
        Ok(serde_json::json!({ "text": text.to_uppercase() }))
    },
))
.expect("unique tool name");

Design constraints (binding):

  • The registry is process-global and must be populated in main, BEFORE the subagent dispatch. Subagents re-exec current_exe(); the child runs the embedder’s main again, which re-registers the same tools — that is how a tool registered “by code” is visible in every process of the tree (the exact pattern the stock CLI uses for nothing, preserving its no-local-code posture: agentd-cli registers zero tools, so this registry is empty in every stock binary).
  • Dispatch priority is self-tools → code tools → MCP. A registered tool cannot shadow agentd’s own orchestration primitives (SELF_CONTROL_TOOLS — registration refuses those names), and a remote MCP server cannot steal a code tool’s calls by publishing a colliding name (the code tool wins the catalogue slot and the dispatch).
  • Workflows address code tools as the reserved server name code ({"kind": "tool", "server": "code", "tool": "shout", …}); config validation refuses an --mcp server named code.
  • Handlers are Fn(&Value) -> Result<Value, String> + Send + Sync: they may be called from the agent loop, from workflow tool nodes, and from parallel foreach/parallel lanes (threads of the same process) concurrently. Keep them reentrant; hold no lock across a call into agentd.
  • Trust: a code tool is the embedder’s own compiled code — it is first-party by definition, like the binary itself. It sits OUTSIDE the --mcp-tags trifecta accounting; an embedder whose tool does egress or touches secrets owns that risk the way it owns the rest of its binary.

Structs§

CodeTool
One registered native tool: an MCP-shaped definition (name + description + input JSON Schema) plus the Rust handler.

Functions§

call
Call a registered code tool directly — the PUBLIC entry the runtime’s tool executor (registry route code) and an embedder’s dispatcher use. None = unregistered; Some(Ok(v)) / Some(Err(reason)) mirror the handler. The handler runs outside the registry lock.
count
How many tools are registered (the capabilities manifest surfaces this).
register
Register a tool. Refuses (Err) an empty name, a duplicate, or a name that collides with agentd’s own self/control primitives — a code tool may shadow a remote MCP tool (first-party wins) but never the orchestration surface.
unregister
Remove a registered tool (dynamic embedders). Returns whether it existed. Prefer registering once in main — see the module doc’s re-exec rule.

Type Aliases§

CodeToolFn
The handler signature: JSON arguments in, JSON result (or a refusal string) out. Err takes the tool-error path (the model sees it as a failed call; a workflow tool node takes its error edge).