# How ToolRegistry Facilitates Agent Tool Dispatch and Execution in AI Systems

> Discover how ToolRegistry in ai-engineering-from-scratch streamlines AI agent tool dispatch and execution. It maps requests to concrete code via metadata for efficient routing.

- Repository: [Rohit Ghumare/ai-engineering-from-scratch](https://github.com/rohitg00/ai-engineering-from-scratch)
- Tags: internals
- Published: 2026-07-30

---

**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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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 `readOnlyHint` or `destructiveHint` for governance layers

### Executable Mapping via makeExecutors

Concrete implementations are bound to names through the **`makeExecutors`** factory function (lines 45–71 in [[`tools.ts`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/tools.ts)](phases/19-capstone-projects/13-mcp-server-with-registry/code/ts/src/tools.ts) demonstrates defining tool descriptors and their corresponding executors:

```typescript
// 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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/main.py)](phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py) handles multi-server registration and search:

```python

# 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.
- **`makeExecutors`** creates a runtime map linking tool names to concrete functions that return `ContentBlock` arrays.
- **The Registry class** enables cross-server discovery via `.well-known/mcp-capabilities` manifests 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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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`](phases/14-agent-engineering/06-tool-use-and-function-calling/assets/tool-stack.svg).