How Are Tools Assembled in OpenClaude: The Tool Pool Architecture

OpenClaude assembles executable toolsets through a six-stage pipeline that aggregates built-in utilities, MCP server plugins, and dynamically discovered tools, then applies coordinator-mode filtering and deduplication before serializing them into API requests.

Understanding how tools are assembled in OpenClaude is essential for developers extending the Gitlawb/openclaude codebase or debugging permission issues. The repository implements a sophisticated tool pool architecture orchestrated primarily in src/utils/toolPool.ts and src/entrypoints/cli/REPL.tsx. This system dynamically constructs context-aware toolsets while respecting security policies and model token limits.

The Six-Stage Tool Assembly Pipeline

Stage 1: Loading Built-in Tools

The assembly process begins in src/entrypoints/cli/REPL.tsx where the system calls getBuiltInTools() from src/tools/builtInTools.js. These static tools include core utilities such as Bash, FileEdit, FileWrite, SendMessage, and TaskCreate. Each tool exports a constants.ts file defining its name and metadata, alongside a prompt.ts file specifying its input schema for the model.

Stage 2: Discovering MCP Tools

After loading built-ins, the pipeline queries connected MCP (Model-Controlled Plugins) servers via getMcpTools(context.mcpClient). These runtime-discovered tools extend capabilities beyond the static set, allowing integration with external services without modifying the core codebase. The MCP client returns tool definitions that conform to the same schema standards as built-in utilities.

When the Tool Search feature is enabled, the system invokes discoverDeferredTools() from src/utils/toolSearch.ts. This mechanism sends a special ToolSearchTool request to the model, asking it to enumerate additional tools required for the specific query context. The server responds with a curated list of tool names, which OpenClaude then loads on-demand. This deferred loading strategy avoids pre-declaring hundreds of potentially unused MCP tools, significantly reducing API payload size.

Stage 4: Coordinator Mode Filtering

For multi-agent or remote-worker deployments, the pipeline applies applyCoordinatorToolFilter() defined in src/utils/toolPool.ts. This function filters the aggregated tool array against the COORDINATOR_MODE_ALLOWED_TOOLS constant exported from src/constants/tools.ts. This security layer ensures coordinator nodes restrict worker processes to an explicit whitelist of authorized functions, preventing unauthorized file system or network access in distributed scenarios.

Stage 5: Deduplication by Tool Name

The combined array—containing built-ins, MCP tools, and deferred discoveries—frequently contains duplicate entries when MCP servers expose functions matching built-in names. The pipeline applies uniqBy to deduplicate the collection based on tool name, ensuring each executable capability appears exactly once in the final context.

Stage 6: Serialization for the API Request

Finally, the deduplicated tool list passes to the query engine in src/query/*, which serializes the tools into the Anthropic API request payload. Depending on the model version and capabilities, this serialization may include structured tool_reference blocks or inline tool descriptions with JSON schemas.

Implementation Deep Dive

The following examples demonstrate the actual implementation patterns found in the OpenClaude source code.

Assembling the Tool Set in the REPL

The REPL.tsx entry point orchestrates the aggregation and filtering sequence:

// src/entrypoints/cli/REPL.tsx
import { getBuiltInTools } from '../../tools/builtInTools.js';
import { getMcpTools } from '../../tools/mcpTools.js';
import { discoverDeferredTools } from '../../utils/toolSearch.js';
import { applyCoordinatorToolFilter } from '../../utils/toolPool.js';

async function assembleTools(context) {
  // Stage 1: Static built-ins
  const builtIns = await getBuiltInTools();
  
  // Stage 2: Runtime MCP discovery
  const mcp = await getMcpTools(context.mcpClient);
  
  // Stage 3: Dynamic deferred loading
  const deferred = await discoverDeferredTools(context);

  // Combine all sources
  const allTools = [...builtIns, ...mcp, ...deferred];
  
  // Stage 4: Coordinator security filter
  const filtered = applyCoordinatorToolFilter(allTools);
  
  // Stage 5: Deduplication (uniqBy implementation detail)
  const uniqueTools = uniqBy(filtered, 'name');
  
  return uniqueTools; // Passed to query engine
}

Coordinator Mode Security Filtering

The filtering logic resides in toolPool.ts and uses a whitelist approach:

// src/utils/toolPool.ts
import { COORDINATOR_MODE_ALLOWED_TOOLS } from '../constants/tools.js';

export function applyCoordinatorToolFilter(tools: Tool[]): Tool[] {
  return tools.filter(tool =>
    COORDINATOR_MODE_ALLOWED_TOOLS.has(tool.name)
  );
}

Dynamic Tool Discovery Implementation

The Tool Search feature defers loading until the model explicitly requests specific capabilities:

// src/utils/toolSearch.ts
export async function discoverDeferredTools(ctx) {
  if (!ctx.enableToolSearch) return [];

  // Sends ToolSearchTool to model
  const { toolNames } = await ctx.client.sendToolSearchRequest();
  
  // Load only requested tools
  return toolNames.map(name => loadToolByName(name));
}

Summary

  • Built-in tools provide core filesystem and process utilities through static imports in src/tools/.
  • MCP tools extend capabilities at runtime via server discovery without code changes.
  • Tool Search enables deferred loading that minimizes token usage by only declaring tools the model explicitly requests.
  • Coordinator filtering enforces security policies via COORDINATOR_MODE_ALLOWED_TOOLS when operating in distributed modes.
  • Deduplication ensures clean toolsets by removing name collisions between built-ins and MCP providers.
  • Serialization in src/query/* converts the final array into Anthropic API-compatible tool definitions.

Frequently Asked Questions

What is the difference between built-in and MCP tools in OpenClaude?

Built-in tools are static TypeScript modules shipped with the repository (located in src/tools/) that provide essential capabilities like Bash execution and file editing. MCP tools are discovered at runtime from external Model-Controlled Plugins servers, allowing dynamic extension of capabilities without modifying the OpenClaude source code or redeploying the application.

How does Tool Search optimize the tool assembly process?

Tool Search optimizes assembly by implementing deferred loading: instead of declaring all available MCP tools in the initial system prompt (which consumes significant context window tokens), the model first receives a minimal toolset plus a ToolSearchTool. If the model determines it needs additional capabilities, it explicitly requests them via the Tool Search endpoint, and only those specific tools are loaded via discoverDeferredTools() in src/utils/toolSearch.ts.

What happens when coordinator mode filters remove a requested tool?

When applyCoordinatorToolFilter() processes the tool array, any tool not present in the COORDINATOR_MODE_ALLOWED_TOOLS whitelist defined in src/constants/tools.ts is removed from the collection before the API request is constructed. If the model subsequently attempts to invoke a removed tool, the request will fail at the validation layer, requiring the coordinator to either adjust its whitelist or the worker to request alternative approaches using available tools.

Where does deduplication occur in the assembly pipeline?

Deduplication occurs after coordinator filtering but before serialization to the API. The uniqBy function processes the filtered array in src/utils/toolPool.ts (or the assembly function in REPL.tsx), comparing tool names to ensure that duplicates—such as a FileEdit tool provided by both the built-in set and an MCP server—appear only once in the final payload sent to src/query/*.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →