Skip to main content

Tool System Lab [Core]

Experiment: experiments/exp_04_tool_system/main.py

Objective​

Show the Tool protocol, Pydantic validation, registry filtering, built-in + MCP pool merge, and batch partitioning for concurrent vs serial execution—matching how Claude Code defines and runs tools safely at scale.

Source mapping (Claude Code)​

PieceTypeScript
Tool shape, buildTool, result mappingsrc/Tool.ts, src/tools.ts
Partition batches, executesrc/services/tools/toolOrchestration.ts

Architecture​

Key code walkthrough​

Protocol + factory (build_tool mirrors buildTool):

@runtime_checkable
class Tool(Protocol):
name: str
description: str
is_concurrency_safe: bool
is_read_only: bool
is_enabled: bool

def validate_input(self, raw_input: dict[str, Any]) -> Any: ...
async def call(self, validated_input: Any, context: dict[str, Any]) -> ToolResult: ...
def build_tool(
*,
name: str,
description: str,
input_model: type[BaseModel],
call_fn: Any,
...
) -> Tool:
"""Factory that builds a Tool with sensible defaults."""
class BuiltTool:
def validate_input(self, raw_input: dict[str, Any]) -> Any:
return self._input_model.model_validate(raw_input)
# map_result spills large results to disk when over max_result_chars

Partitioning consecutive safe calls into one concurrent batch:

def partition_tool_calls(
calls: list[ToolCall],
pool: list[Tool],
) -> list[list[ToolCall]]:
"""
Partition tool calls into batches.
Consecutive concurrency-safe tools form one concurrent batch.
Non-safe tools each get their own serial batch.
Mirrors partitionToolCalls in toolOrchestration.ts.
"""

Pool merge (built-ins win on name collision):

def assemble_tool_pool(
built_ins: list[Tool],
mcp_tools: list[Tool],
) -> list[Tool]:
"""Merge built-in and MCP tools (built-ins win on name collision)."""

How to run​

cd experiments
python -m exp_04_tool_system.main --mock
python -m exp_04_tool_system.main --provider anthropic
python -m exp_04_tool_system.main --provider openai

Exercises​

  1. Add a tool with is_enabled=False and confirm it disappears from get_tools().
  2. Implement true filesystem read_file / grep behind the existing handlers (keep validation).
  3. Extend partition_tool_calls to cap parallelism (e.g. max 3 concurrent safe calls per batch).

Next experiment​

Permission Engine Lab — gate call() with decide() before execution.