Skip to main content

Tool System — Framework Comparison

Every agentic framework needs tools — but they define, validate, permission-check, and execute them very differently. This page compares Claude Code's tool system against five popular frameworks, with real code from their source repos.

Defining a Tool

The most fundamental question: how do you tell the framework "here's a thing the model can call"?

// buildTool() factory — you provide name, schema, call()
const ReadTool = buildTool({
name: 'Read',
inputSchema: z.object({
file_path: z.string().describe('Absolute path to the file'),
offset: z.number().optional(),
limit: z.number().optional(),
}),

async call(args, context, canUseTool, parentMsg, onProgress) {
const { allowed } = await canUseTool(args)
if (!allowed) return { data: null }
const content = await readFile(args.file_path)
return { data: content }
},

isReadOnly() { return true },
isConcurrencySafe() { return true },
})

Pattern: Factory function with rich interface (45+ methods). Zod schema internally, converted to JSON Schema for the API. Each tool is a directory under src/tools/.

Input Validation

How does each framework ensure the model sends valid arguments?

FrameworkSchema SystemValidationStrict Mode
Claude CodeZod (internal) → JSON Schema (API)Zod .parse() wrapping call()JSON Schema sent to model
Google ADKPydantic create_model() → JSON Schemamodel_validate() + mandatory arg checksFunctionDeclaration schema
OpenAI AgentsPydantic from signature → JSON SchemaPydantic parse + ensure_strict_json_schema()Strict by default
LangChainPydantic v2 BaseModelPydantic model validationOptional args_schema
LangGraphSame as LangChainSame + injected arg filteringSame
Claude SDKPython types → JSON SchemaType conversion at schema levelJSON Schema pass-through
Key difference

Claude Code validates at execution time with Zod inside buildTool(). The Python frameworks also generate schemas up front, but they typically validate again at invocation time with Pydantic or equivalent runtime parsing.

Permission & Safety

This is where frameworks diverge most. Claude Code has the most layered permission system of any framework:

Claude Code
Permission modes
Bash classifier
Pre/post hooks
isDestructive()
OpenAI Agents
Input guardrails
Output guardrails
needs_approval
Google ADK
require_confirmation
BashToolPolicy
Before/after callbacks
LangChain / LangGraph
handle_tool_errors
Error type filtering
Claude SDK
can_use_tool callback
Permission modes
allowed/disallowed lists
// 4-layer permission system:
// 1. Permission mode (default, auto, plan, bypassPermissions)
// 2. Per-tool rules (allow Bash(git *), deny Bash(rm *))
// 3. Bash safety classifier (auto mode only)
// 4. Pre-tool hooks (user shell commands that can veto)

// Each tool declares its own safety profile:
isReadOnly(input) { return true } // No side effects
isDestructive(input) { return false } // Not dangerous
isConcurrencySafe(input) { return true } // Can run in parallel
checkPermissions(input, ctx) { return { allowed: true } }

Parallel Execution

Can the framework run multiple tool calls at the same time?

FrameworkParallel?MechanismSafety Partitioning
Claude CodeYesPromise.all for safe tools, serial for unsafeisConcurrencySafe() per tool
Google ADKYesasyncio.gather() for all toolsNo partitioning — all parallel
OpenAI AgentsYesasyncio.wait() with batch executorSibling cancellation on failure
LangChainYesThreadPoolExecutor (sync) / asyncio.gather (async)No partitioning
LangGraphYesSame as LangChain via ToolNodeNo partitioning
Claude SDKVia Claude CodeDelegates to Claude Code's executorInherits Claude Code's partitioning
Key difference

Claude Code is the only framework that partitions tools into safe (parallel) and unsafe (serial) groups based on each tool's isConcurrencySafe() declaration. All other frameworks run everything in parallel and rely on the model to avoid conflicting calls.

Comparison Matrix

FeatureClaude CodeGoogle ADKOpenAI AgentsLangChainLangGraphClaude SDK
Schema systemZodPydanticPydanticPydanticPydanticJSON Schema
Auto-infer schemaNo (explicit)Yes (type hints)Yes (signature)Yes (signature)Yes (signature)No (explicit)
Permission system4-layerConfirmation + policyGuardrails + approvalError handling onlyError handling + middlewareCallback + modes
Bash safetyClassifierPrefix allowlistVia guardrailsNoneNoneVia callback
Pre-execution hooksShell hooksBefore callbacksInput guardrailsNonewrap_tool_callNone
Post-execution hooksShell hooksAfter callbacksOutput guardrailsNonewrap_tool_callNone
Parallel executionSafe/unsafe splitAll parallelAll parallelAll parallelAll parallelInherited
Progress streamingonProgress()Async generatorsNoneNoneStreamWriterNone
Tool count40+ built-in~10 built-in~10 built-inCommunity toolsVia LangChainVia MCP
Read-only flagisReadOnly()NoneNoneNoneNonereadOnly annotation
Destructive flagisDestructive()NoneNoneNoneNonedestructive annotation
State injectionVia ToolContextVia ToolContextVia ToolContextToolRuntime / injected argsInjectedState/StoreVia MCP context

The Takeaway

Claude Code's tool system is uniquely deep compared to other frameworks:

  • It's the only one with built-in safety partitioning (read-only vs. write tools)
  • It has the richest permission model (4 layers vs. 1-2 in others)
  • It ships with 40+ production-grade tools vs. a handful in other frameworks
  • It's the only one with streaming progress from individual tool executions

Other frameworks optimize for developer ergonomics — auto-infer schemas from type hints, minimal boilerplate, decorator-based. Claude Code optimizes for production safety — explicit schemas, layered permissions, concurrency partitioning.