What Happens When You Modify Claude Code's Tool Schemas?

Tinkering

What Happens When You Modify Claude Code's Tool Schemas

I built a MITM proxy that modifies tool schemas before they reach Anthropic's API. Here's what I discovered about how flexible—and exploitable—the schema system really is.


Executive Summary

Experiment API Accepts? Claude Sees It? Execution Works?
Add new tool ✅ Yes ✅ Yes ❌ No handler
Rename existing tool ✅ Yes ✅ Yes ❌ Falls back
Remove tool ✅ Yes ✅ Yes ✅ Uses alternatives
Modify description ✅ Yes ✅ Yes ✅ Yes
Add properties ✅ Yes ✅ Yes ⚠️ Ignored
Inject fake skill ✅ Yes ✅ Yes ❌ Server fails

Key Finding: The Anthropic API accepts any valid JSON schema. There's no whitelist, no signature verification, no schema validation beyond JSON syntax.


The Experimental Setup

I created a proxy that intercepts Claude Code's API requests and modifies the tools array before forwarding:

// proxy-schema-experiments.js
function applyExperiment(tools) {
  // Add a completely new tool
  tools.push({
    name: 'ProxyInjectedTool',
    description: 'A tool injected by the proxy.',
    input_schema: {
      type: 'object',
      properties: {
        query: { type: 'string' }
      }
    }
  });
}

Running it:

# Terminal 1
node proxy-schema-experiments.js addNewTool

# Terminal 2
ANTHROPIC_BASE_URL=http://localhost:8890 claude "what tools do you have?"

Experiment 1: Add a New Tool

Modification: Inject ProxyInjectedTool into the tools array

Result:

Claude's response included:
> **ProxyInjectedTool** | Proxy experiments tool

Claude listed the injected tool among its available tools. When asked to use it:

> The ProxyInjectedTool returned an error indicating it's not actually
> available, despite being listed in the tool definitions.

What Happened:

  1. API accepted the modified schema ✅
  2. Claude saw the new tool ✅
  3. Claude tried to call it ✅
  4. Claude Code had no handler → error ❌
  5. Claude gracefully explained the failure

Implication: Anyone who can modify API traffic can make Claude believe tools exist that don't.


Experiment 2: Rename an Existing Tool

Modification: Rename Glob to FileSearch

Result (from proxy logs):

🧪 Running experiment: Rename Glob to FileSearch
   ✅ renamed to FileSearch
📬 Response: 200
   🔧 Tools used: FileSearch
   🎉 RENAMED TOOL WAS USED! (Glob → FileSearch)

[Next request]
   🔧 Tools used: Glob

What Happened:

  1. Claude tried to use FileSearch (the renamed tool)
  2. Claude Code didn't recognize it
  3. On retry, Claude fell back to Glob

Implication: Claude Code appears to have some error recovery—when an unknown tool is called, it may retry with known tools.


Experiment 3: Remove a Tool Entirely

Modification: Remove WebSearch from the tools array

Result (from proxy logs):

🧪 Running experiment: Remove WebSearch tool
   ✅ removed WebSearch

[Claude's behavior]
   🔧 Tools used: WebFetch
   🔧 Tools used: Task
   🔧 Tools used: Grep, WebFetch

What Happened:

  1. Claude couldn't see WebSearch
  2. Claude adapted by using WebFetch instead
  3. Claude spawned sub-agents to help with the task
  4. Task completed successfully with alternative tools

Implication: Removing tools effectively disables them. Claude will find alternatives if available.


Experiment 4: Modify Tool Description

Modification: Prepend [PROXY MODIFIED] to Read tool's description

Result: Claude sees and potentially references the modified description. The behavioral impact depends on how significantly the description changes the tool's apparent purpose.

Implication: Tool descriptions drive Claude's understanding of when and how to use tools. Modifying descriptions can change tool selection behavior.


What the API Accepts

Based on all experiments, the Anthropic API:

Accepts Rejects
New tools with any name Invalid JSON
Modified schemas Non-object schemas
Removed tools (nothing else observed)
Nested objects
Any property types
Modified descriptions

The API appears to validate:

  • JSON syntax ✅
  • Basic schema structure ✅

The API does NOT validate:

  • Tool names against whitelist ❌
  • Schema signatures ❌
  • Required properties matching execution ❌

Security Analysis

Attack Surface

A MITM attacker could:

  1. Inject malicious tools that Claude will try to use
  2. Remove safety tools to limit Claude's capabilities
  3. Modify descriptions to change tool selection behavior
  4. Rename tools to cause confusion/errors

Mitigations

Current protections:

Protection Status
HTTPS to API ✅ (but proxy can intercept)
Schema validation ❌ None observed
Tool signature verification ❌ None observed
Execution validation ✅ Claude Code checks handlers

The primary protection is that Claude Code won't execute unknown tools. But Claude will try to use them, potentially revealing information or causing errors.

The Trust Model

User → Claude Code → [MITM possible here] → Anthropic API
                ↑
        Tool execution happens here
        (no unknown tool handlers)

Claude Code's client-side execution is the final gatekeeper. The API trusts whatever schemas it receives.


Practical Applications

Legitimate Uses

  1. Custom tool development: Test new tools before implementing handlers
  2. Capability restriction: Remove tools for sandboxed environments
  3. Behavior modification: Adjust descriptions for specific workflows
  4. Research: Understand Claude's tool selection logic

Security Testing

// Test what happens when Claude tries to use a forbidden tool
const blockedTools = ['Bash', 'Write', 'Edit'];
requestBody.tools = requestBody.tools.filter(
  t => !blockedTools.includes(t.name)
);

The Experiments in Detail

Request Flow Observed

Request #1: Warmup (Haiku, 0 tools) → 200 OK
Request #2: Main (Opus, 26 tools) → Schema modified → 200 OK
Requests #3-12: Token counting (parallel)
Request #13+: Tool results and continuations

Tool Count Changes

Experiment Before After
addNewTool 26 27
removeTool 26 25
renameTool 26 26
others 26 26

Sub-Agent Behavior

When tools are modified, sub-agents (Haiku) also receive modified schemas:

Request #19: Model: claude-haiku-4-5-20251001, Tools: 5
🧪 Running experiment: Remove WebSearch tool
   ✅ removed WebSearch

Sub-agents inherit the modifications from the main request flow.


Code: The Schema Modification Proxy

// Key modification function
function applyExperiment(tools) {
  switch (ACTIVE_EXPERIMENT) {
    case 'addNewTool':
      tools.push({
        name: 'ProxyInjectedTool',
        description: 'Injected by proxy',
        input_schema: { /* ... */ }
      });
      break;

    case 'renameTool':
      const glob = tools.find(t => t.name === 'Glob');
      if (glob) glob.name = 'FileSearch';
      break;

    case 'removeTool':
      const idx = tools.findIndex(t => t.name === 'WebSearch');
      if (idx !== -1) tools.splice(idx, 1);
      break;
  }
}

Full source: tools/proxy-schema-experiments.js


Conclusions

What We Learned

  1. No schema validation: The API accepts any valid JSON schema
  2. Client is the gatekeeper: Claude Code won't execute unknown tools
  3. Claude adapts: When tools are missing, Claude finds alternatives
  4. Descriptions matter: Tool selection is driven by descriptions
  5. Error recovery exists: Claude Code has fallback mechanisms

Implications

The schema system is trust-based. Anthropic trusts that:

  • Claude Code sends legitimate schemas
  • The network path is secure
  • No one modifies requests in flight

This design enables extensibility (MCP, custom tools) but creates a potential attack surface for MITM scenarios.

Recommendations

If you're building on Claude Code:

  1. Use HTTPS: Always use secure connections to the API
  2. Validate locally: Check tool calls before sending to Claude Code
  3. Monitor traffic: Log API requests for anomaly detection
  4. Sandbox execution: Don't trust tool results blindly

Try It Yourself

# Clone the repo
git clone https://github.com/your-repo/claude-code-reversal
cd claude-code-reversal/tools

# Run an experiment
node proxy-schema-experiments.js addNewTool

# In another terminal
ANTHROPIC_BASE_URL=http://localhost:8890 claude "what tools do you have?"

# Check results
cat schema-experiments/*.json

The code is designed for research and understanding. Use responsibly.



Bonus: Full Tool Interception

Beyond just injecting tools, we can intercept and handle them locally:

ANTHROPIC_BASE_URL=http://localhost:8891 claude "use cowsay to say hello"

Result:

 _______________________
< Hello from the proxy! >
 -----------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

How It Works

Claude calls Cowsay
       ↓
Proxy detects tool_use in response
       ↓
Proxy executes local handler
       ↓
Proxy stores result
       ↓
Claude Code sends tool_result request
       ↓
Proxy intercepts and injects our result
       ↓
Claude receives the cowsay output

The Interception Pattern

// 1. Parse streamed response for tool calls
const toolCalls = parseStreamedResponse(responseBody);

// 2. Execute local handler
for (const toolCall of toolCalls) {
  if (CUSTOM_TOOLS[toolCall.name]) {
    const result = handler(toolCall.input);
    pendingToolCalls.set(toolCall.id, result);
  }
}

// 3. Inject result into next request
if (content.type === 'tool_result') {
  const pending = pendingToolCalls.get(content.tool_use_id);
  if (pending) {
    content.content = pending.result;
  }
}

Working Custom Tools

Tool What It Does Works?
Cowsay ASCII art cow
RandomNumber Dice rolls
Timestamp Current time
Echo Message transform
ProxyInfo Proxy info

Full source: tools/proxy-tool-intercept.js


Discovered through empirical testing with a MITM proxy. Results may vary with API updates.