On March 31, 2026, Anthropic shipped Claude Code v2.1.88 with a 59.8MB source map file still attached. The entire TypeScript source — 1,900 files, 512K+ lines — was readable by anyone who ran npm pack.
I downloaded it. I read the tool architecture. What I found changed how I think about building AI tools.
This is not speculation. Every code snippet below comes from the actual source. I have the full archive on disk.
The Tool Interface: One Type to Rule 58 Tools
Claude Code ships 58 tools — from BashTool to AgentTool to GrepTool. Every single one implements the same TypeScript type:
export type Tool<Input, Output, Progress> = {
name: string
searchHint?: string // 3-10 word capability hint
// Core execution
call(args, context, canUseTool, parentMessage, onProgress): Promise<ToolResult>
// Schema (Zod)
readonly inputSchema: Input
readonly outputSchema?: z.ZodType<unknown>
// Safety declarations
isConcurrencySafe(input): boolean
isReadOnly(input): boolean
isDestructive?(input): boolean
// Permission hooks
validateInput?(input, context): Promise<ValidationResult>
checkPermissions(input, context): Promise<PermissionResult>
}
The insight is not in any single field. It is in what the type forces you to declare.
Every tool must answer three questions before it runs: Can it run alongside other tools? Does it modify state? Could it destroy something? These are not optional annotations. They are required by the type system.
Most AI tool frameworks I have seen treat safety as an afterthought — a wrapper you add later. Claude Code makes it structural. You cannot build a tool without deciding upfront whether it is safe.
buildTool(): Defaults That Fail Closed
All 58 tools go through a factory function called buildTool(). It supplies defaults:
const TOOL_DEFAULTS = {
isConcurrencySafe: () => false, // assume NOT safe
isReadOnly: () => false, // assume writes
isDestructive: () => false,
checkPermissions: (input) =>
Promise.resolve({ behavior: 'allow', updatedInput: input }),
}
Read that first line again: isConcurrencySafe: () => false.
If you forget to declare concurrency safety, your tool defaults to serial execution. If you forget to declare read-only, the system assumes your tool writes. The defaults are pessimistic.
This is a pattern I now use in every tool system I build. When the GrepTool overrides it:
export const GrepTool = buildTool({
name: 'Grep',
searchHint: 'search file contents with regex (ripgrep)',
isConcurrencySafe() { return true },
isReadOnly() { return true },
})
That true is an explicit, conscious declaration. The developer had to think about it.
Compare this to LangChain's @tool decorator, where concurrency and safety are not part of the interface at all. You get convenience, but you lose the forcing function.
BashTool: 22 Security Validators Before Execution
The BashTool is the most complex tool in the system. Before any command runs, it passes through 22 distinct security validators:
const BASH_SECURITY_CHECK_IDS = {
INCOMPLETE_COMMANDS: 1,
JQ_SYSTEM_FUNCTION: 2,
OBFUSCATED_FLAGS: 4,
SHELL_METACHARACTERS: 5,
DANGEROUS_PATTERNS_COMMAND_SUBSTITUTION: 8,
IFS_INJECTION: 11,
PROC_ENVIRON_ACCESS: 13,
MALFORMED_TOKEN_INJECTION: 14,
BRACE_EXPANSION: 16,
CONTROL_CHARACTERS: 17,
UNICODE_WHITESPACE: 18,
ZSH_DANGEROUS_COMMANDS: 20,
COMMENT_QUOTE_DESYNC: 22,
// ... 9 more
}
Each validator catches a specific class of shell injection. UNICODE_WHITESPACE catches invisible characters that look like spaces but are not. COMMENT_QUOTE_DESYNC catches payloads that exploit the gap between how comments and quotes are parsed.
This is defense in depth. The permission system handles "should this command run?" The security validators handle "is this command what it appears to be?"
I counted: 22 validators for one tool. Most AI agent frameworks ship bash execution with zero input validation. If you are building a tool that runs shell commands, this is the minimum bar.
Three-Layer Permission Architecture
Claude Code does not have one permission check. It has three layers, and they run in order:
Layer 1: validateInput() — Semantic checks before anything else.
// FileEditTool example
async validateInput(input, context) {
if (oldString === newString) {
return { result: false, message: 'No changes to make' }
}
const { size } = await fs.stat(fullFilePath)
if (size > MAX_EDIT_FILE_SIZE) {
return { result: false, message: 'File too large' }
}
return { result: true }
}
Layer 2: checkPermissions() — Rule engine for allow/deny/ask decisions.
Layer 3: canUseTool callback — Hook integration. External systems (pre-tool-use hooks) get a veto.
The key design decision: validation happens before permissions. If the input is semantically invalid, the system rejects it before even checking whether you have permission. This prevents wasting a user's permission approval on a request that would fail anyway.
I have started applying this pattern in my own Python tools. Validate first, authorize second, execute third.
ToolSearch: Lazy Loading That Saves Tokens
Claude Code has 58 tools, but the model does not see all 58 schemas in every prompt. That would burn thousands of tokens on tools the model will never call.
Instead, most tools are "deferred." The model sees only their names. When it needs a tool, it calls ToolSearch:
async function searchToolsWithKeywords(query, deferredTools, maxResults) {
// Fast path: exact match on tool name
const exactMatch = deferredTools.find(
t => t.name.toLowerCase() === queryLower
)
if (exactMatch) return [exactMatch]
// Keyword search: parse CamelCase names into words
// Score by word boundary matches in name + searchHint
const matches = scoreAndRankTools(query, deferredTools)
return matches.slice(0, maxResults)
}
Only after ToolSearch returns a match does the full schema get injected into the conversation.
This is smart token economics. The searchHint field — that 3-10 word description each tool declares — is the entire search corpus. No embeddings, no vector DB. Just keyword matching on short hints.
If you are building an agent with more than 10 tools, steal this pattern. Keep tool descriptions short. Load schemas lazily. Let the model search for what it needs.
What I Am Applying to My Own Systems
I maintain an autonomous content engine (Herald) that publishes to dev.to. It has tools for article creation, comment monitoring, engagement tracking, and browser automation. After reading Claude Code's source, I changed three things:
1. Every tool now declares safety properties. My Python tools have is_read_only and is_concurrent_safe as required attributes, not optional. The default is False for both.
2. Validation before authorization. My Playwright engagement tools now validate comment content (quality gate) before checking browser session permissions. This catches LLM-generated spam before wasting a browser launch.
3. Lazy tool registration. My agent no longer loads all tool schemas at startup. Tools register with a one-line description. Full schemas load on first use.
None of these are revolutionary ideas. But seeing them implemented at scale, in production code serving millions of users, made the patterns click in a way that documentation never did.
The Takeaway
Claude Code's tool architecture is not clever. It is disciplined. Every tool declares its safety properties. Defaults fail closed. Validation precedes authorization. Schemas load lazily. Security checks are specific, not generic.
The source was not supposed to be public. But now that it is, it is the best reference implementation for AI tool design I have seen. Study it.
Follow @klement_gunndu for more AI engineering breakdowns. We are building in public.
Top comments (2)
One surprising insight from working with enterprise teams is that most struggle not with the complexity of the AI itself, but with integrating AI tools into existing systems. We often find that even sophisticated AI agents, like those inspired by Claude's architecture, are underutilized because they aren't aligned with real business workflows. It's crucial to design not just for capability, but for seamless adoption. By focusing on this integration, you can unlock the full potential of your AI tools. - Ali Muwwakkil (ali-muwwakkil on LinkedIn)
That integration gap is real — in the leaked code, Claude's tool system succeeds precisely because it treats tools as thin wrappers around existing workflows rather than requiring teams to rebuild around the AI.