How ToolRegistry Facilitates Agent Tool Dispatch and Execution in AI Systems
The ToolRegistry bridges abstract agent requests with concrete code execution by maintaining a searchable catalog of ToolDescriptor metadata and a mapped executor factory, enabling validated routing across distributed MCP servers.
The rohitg00/ai-engineering-from-scratch repository implements a robust Tool Registry pattern that decouples an agent's intent from implementation details. This architecture allows language models to discover capabilities dynamically, validate arguments against JSON Schema, and execute tools through a governed dispatch pipeline. By centralizing tool metadata in a searchable registry, the system supports secure multi-server deployments without hard-coding tool names or endpoints.
ToolRegistry Architecture Components
The registry consists of two primary abstractions: metadata descriptors that advertise capabilities, and executor mappings that bind names to runnable code.
ToolDescriptor Metadata Schema
Each capability is defined by a ToolDescriptor object declared in [tools.ts](phases/19-capstone-projects/13-mcp-server-with-registry/code/ts/src/tools.ts). These descriptors provide the contract between the agent and the tool:
- name – The canonical identifier the LLM invokes (e.g.,
incidents_list) - description – Human-readable purpose for model-based tool selection
- inputSchema – JSON Schema validating runtime arguments
- annotations – Policy hints like
readOnlyHintordestructiveHintfor governance layers
Executable Mapping via makeExecutors
Concrete implementations are bound to names through the makeExecutors factory function (lines 45–71 in [tools.ts](phases/19-capstone-projects/13-mcp-server-with-registry/code/ts/src/tools.ts)). This function returns a dictionary mapping each ToolDescriptor.name to a ToolExecutor function that receives validated ToolArgs and returns ContentBlock arrays.
Registry Discovery and Search Capabilities
The Python implementation in [main.py](phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py) provides a Registry class (lines 50–64) that enables platform-wide capability discovery.
Capability Manifest Registration
MCP servers expose their tools via .well-known/mcp-capabilities documents. The Registry.register(server) method ingests these manifests and stores the server-to-tool mappings. This allows multiple servers—such as read-only and destructive instances—to coexist without namespace conflicts.
Cross-Server Tool Discovery
The Registry.search(query) method performs case-insensitive lookups across all registered servers, returning tuples of (server_name, tool_name) that match the agent's intent. This eliminates hard-coding and enables dynamic tool availability.
The Agent Tool Dispatch Flow
The ToolRegistry orchestrates a five-stage pipeline that transforms an LLM's abstract intent into validated execution:
1. Intent Resolution – The agent consults tool descriptions and selects a matching name.
2. Argument Validation – Model-generated arguments are validated against the inputSchema defined in the descriptor.
3. Policy Enforcement – An OPA (Open Policy Agent) rule inspects annotations (e.g., destructiveHint) and may require human approval before proceeding.
4. Execution Routing – The dispatch layer retrieves the executor from the map returned by makeExecutors and invokes it with validated arguments.
5. Response Streaming – The executor returns ContentBlock objects (text, markdown, etc.) that are formatted as the tool's response to the LLM.
Implementation Walkthrough
The following TypeScript excerpt from [tools.ts](phases/19-capstone-projects/13-mcp-server-with-registry/code/ts/src/tools.ts) demonstrates defining tool descriptors and their corresponding executors:
// Tool descriptors declare the contract
export const TOOL_DESCRIPTORS: ToolDescriptor[] = [
{
name: "incidents_list",
description: "List recent incidents, optionally filtered by severity.",
inputSchema: {
type: "object",
properties: {
severity: { type: "string", enum: ["p0","p1","p2"] }
},
required: []
},
annotations: { readOnlyHint: true },
},
];
// Factory builds the runtime execution map
export function makeExecutors(store: Record<string, Incident>): Record<string, ToolExecutor> {
const execList = (args: ToolArgs) => {
const sev = typeof args.severity === "string" ? args.severity : undefined;
const items = Object.values(store).filter(i => !sev || i.severity === sev);
return [{ type: "text", text: JSON.stringify(items) }];
};
return { incidents_list: execList };
}
The Python registry implementation in [main.py](phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py) handles multi-server registration and search:
# Registry instantiation and server registration
registry = Registry()
registry.register(ro) # Read-only MCP server
registry.register(rw) # Destructive MCP server
# Search returns matching server/tool tuples
print(registry.search("jira")) # [('rw', 'incidents_list'), ...]
print(registry.search("postgres")) # [('ro', 'incidents_get'), ...]
Governance and Security Integration
The registry enables fine-grained control through annotations centralization. By storing destructiveHint and readOnlyHint flags within the ToolDescriptor, policy engines can intercept dispatch requests before execution. This architecture supports OAuth 2.1 scope enforcement, audit logging, and human-in-the-loop approval gates, all while keeping individual server implementations lightweight and focused on business logic.
Summary
- ToolDescriptor objects define the interface contract, including JSON Schema validation rules and security annotations.
makeExecutorscreates a runtime map linking tool names to concrete functions that returnContentBlockarrays.- The Registry class enables cross-server discovery via
.well-known/mcp-capabilitiesmanifests and searchable registration. - Dispatch follows a five-stage pipeline: intent resolution, schema validation, policy checking, executor invocation, and response streaming.
- Security integration leverages annotation hints to trigger OPA policy gates without modifying tool implementations.
Frequently Asked Questions
What is the difference between ToolDescriptor and ToolExecutor?
ToolDescriptor is static metadata that describes what a tool does, what arguments it accepts, and what policies apply to it. ToolExecutor is the runtime function that actually performs the work, receiving validated arguments and returning content blocks. The descriptor lives in the registry for discovery, while the executor lives in the server's runtime map.
How does the Registry handle tool name collisions across servers?
The Registry stores tools with their server provenance, returning tuples of (server_name, tool_name) from search() results. This namespacing allows multiple servers to expose identically named tools (e.g., get_user) without conflict, letting the dispatch layer select the appropriate server based on context or policy.
Can the ToolRegistry enforce rate limiting or authentication?
Yes, because the Registry centralizes capability manifests, it can intercept dispatch calls to enforce OAuth 2.1 scopes, API rate limits, or audit logging before routing to the executor. The annotations field provides hooks for policy engines like OPA to require additional authentication or Human-in-the-Loop (HITL) approval for destructive operations.
Where is the ToolRegistry pattern implemented in the codebase?
The core implementation resides in [phases/19-capstone-projects/13-mcp-server-with-registry/code/ts/src/tools.ts](phases/19-capstone-projects/13-mcp-server-with-registry/code/ts/src/tools.ts) for TypeScript tool definitions and [phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py](phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py) for the Python registry and discovery service. Additional context exists in phases/14-agent-engineering/06-tool-use-and-function-calling/assets/tool-stack.svg.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →