Tool System
ContextRouter’s tool system is built on two complementary patterns: platform tools for internal LLM capabilities, and federated tools for secure bi-directional execution on consumer projects.
Platform Tools
Platform tools are atomic, domain-agnostic LLM capabilities registered in the PlatformToolRegistry. They are the building blocks that YAML graph templates compose into pipelines.
Each platform tool:
- Has a Pydantic config schema (
frozen=True,extra="forbid") - Requires
router:executetoken scope - Wraps exceptions in
PlatformServiceError - Has zero domain imports — pure LLM capability
Registry Architecture
All 36 platform tool bindings are registered at startup via register_all_platform_tools():
def register_all_platform_tools(registry: PlatformToolRegistry) -> None: """Wire all platform tool modules into the registry.""" # RAG pipeline (11): extract_query, detect_intent, retrieve, ground, # generate, reflect, suggest, format_output, synthesize_results, # web_search, no_results register_tool_specs(registry, _GENERIC_RAG + [no_results_spec])
# SQL analytics (4): sql_plan, sql_execute, sql_verify, sql_visualizer register_tool_specs(registry, _SQL_TOOLS)
# Content / news (6): classify, generate_content, review_content, # filter_content, plan_content, match_semantic register_router_content_tools(registry)
# Brain service (7): search, memory_read/write, blackboard_read/write, # kg_query, upsert register_brain_tools(registry)
# Worker service (4): start_workflow, get_status, execute_code, # register_schedules register_worker_tools(registry)
# Other (4): register_router_rlm_tools(registry) # 1 RLM processing tool register_language_tools(registry) # 1 grammar/spelling tool register_shield_tools(registry) # 1 Shield scanning tool register_ingest_tools(registry) # 1 file download toolTool Categories
See Graph Compiler — Platform Tools for the complete binding reference.
Federated Tools (Bi-Directional Execution)
In typical LLM applications, the orchestrator needs direct access to databases or APIs to execute tools. This creates a massive security liability — the central Mind would need credentials for every connected project (the Confused Deputy problem).
ContextRouter solves this with Bi-Directional Tool Binding via the ToolExecutorStream. External projects connect to Router which acts purely as a “Cognition Provider”. When the LLM decides to run a tool like export_products, Router does not execute it. Instead, it streams the tool request back down the persistent connection to the client project. The client executes locally inside its own VPC, and streams the result back up to the LLM.
See also: ContextUnity Security Scope, BiDi Execution
How it works
Consumer Project Router (Cognition Provider) │ │ │ RegisterManifest(yaml) │ │ ──────────────────────────────▶ │ Compile graphs + validate tools │ │ │ ToolExecutorStream() │ │ ◀─────────────────────────────── │ Graph node needs federated tool │ │ │ Execute locally (own DB/API) │ │ ──────────────────────────────▶ │ Return result to graph │ │Declaring Federated Tools in Manifest
Consumer projects do not declare a graph-level federated_tools map. Federated BiDi tools are discovered from:
toolkits:on the router or per-graph — toolkit classes register@federated_toolhandlers.- Template nodes — YAML templates list
type: toolnodes withtool_binding: federated:<tool_name>. - Inline graphs — each project tool node uses
type: toolor inferred tool type with a namespaced binding, e.g.tool_binding: federated:medical_sql, with optionalmeta(handler,source,toolkit).
router: toolkits: - GardenerTools graph: gardener: template: "yaml:gardener" config: taxonomy_version: "v2"Tool isolation: Tools attached via a graph’s template + toolkits apply only to that compiled graph. Router derives the BiDi tool list from federated:* node bindings for registration.
Implementing a Federated Tool
from contextunity.core.sdk.tools import federated_tool
@federated_tool("export_products_for_normalization")async def export_products_for_normalization(state: dict) -> dict: """Export products from the consumer's catalogue for normalisation.""" products = await fetch_products_from_local_db(state.get("filters", {})) return {"products": products}The @federated_tool decorator registers the handler in the project ToolRegistry. At RegisterManifest time, Router validates that every template type: tool node with tool_binding: federated:* is satisfied by generated bundle tools from explicit graph bindings or toolkits.
Tool Registration Flow
1. Consumer sends RegisterManifest(yaml) via gRPC2. Router parses manifest → loads template → validates: a. All `federated:*` tool bindings have handlers from explicit bindings or toolkits ✓ b. All platform tool_bindings exist in PlatformToolRegistry ✓ c. DAG is valid (no cycles, no phantom nodes) ✓3. Graph compiled and cached by (tenant_id, graph_name)4. Consumer opens ToolExecutorStream() — persistent BiDi connection5. On ExecuteAgent(graph="gardener"): - Platform nodes execute locally in Router - Federated nodes stream requests to consumer via BiDi6. Both kinds emit a unified `tool_result` trace event (`status`, `duration_ms`, `handler`, `source`, `toolkit`, `error`)