[FEATURE] Synthetic Response Injection for Testing and Caching

Resolved 💬 3 comments Opened Jan 28, 2026 by coygeek Closed Feb 28, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Claude Code currently has no mechanism to intercept an LLM request and return a pre-defined response without actually calling the API. This limits several important use cases:

  1. Testing hooks and automation: Developers building Claude Code integrations cannot test their workflows without consuming API credits and waiting for real responses
  2. Response caching: Cannot implement a caching layer to avoid redundant API calls for repeated questions
  3. Offline development: Cannot work on Claude Code integrations when offline or when the API is unavailable
  4. Deterministic testing: Cannot create reproducible test scenarios with known responses
  5. Cost optimization: Cannot skip API calls when the answer is already known or cached

Proposed Solution

Extend the BeforeModel hook (see related feature request) to support synthetic response injection.

How It Works

When a BeforeModel hook returns a syntheticResponse field, Claude Code skips the actual API call and uses the provided response instead:

{
  "hookSpecificOutput": {
    "hookEventName": "BeforeModel",
    "decision": "mock",
    "syntheticResponse": {
      "content": "The answer to your question is...",
      "stop_reason": "end_turn"
    }
  }
}

Full Response Schema

{
  "hookSpecificOutput": {
    "hookEventName": "BeforeModel",
    "decision": "mock",
    "syntheticResponse": {
      "content": "Response text here",
      "stop_reason": "end_turn",
      "tool_calls": [
        {
          "name": "Read",
          "input": {"file_path": "/path/to/file.txt"}
        }
      ]
    }
  }
}

Decision Values

| Decision | Behavior |
|----------|----------|
| allow | Proceed with actual API call (default) |
| modify | Modify request, then call API |
| mock | Use syntheticResponse, skip API call |
| block | Block request entirely with error |

Configuration Example

{
  "hooks": {
    "BeforeModel": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/response-cache.py"
          }
        ]
      }
    ]
  }
}

Alternative Solutions

  1. External proxy: Route Claude Code through a caching proxy that intercepts API calls. This requires significant infrastructure and doesn't integrate with the hooks system.
  1. Test accounts with credits: Use a separate test account for development. This still costs money and doesn't help with offline development or deterministic testing.
  1. Mock the entire CLI: Create a mock version of Claude Code for testing. This is a significant maintenance burden and doesn't test real integration.

Priority

High - Significant impact on productivity

This is essential for anyone building automation, integrations, or custom workflows on top of Claude Code.

Feature Category

Developer tools/SDK

Use Case Example

Scenario: Response caching for cost reduction

  1. Developer frequently asks similar questions during development
  2. BeforeModel hook hashes the request messages
  3. Checks a local SQLite database for matching hash
  4. If found, returns cached response as syntheticResponse
  5. If not found, allows API call and caches response in AfterModel hook
  6. Result: Repeated questions cost nothing and respond instantly
#!/usr/bin/env python3
import json
import sys
import hashlib
import sqlite3

input_data = json.load(sys.stdin)
request = input_data.get("llm_request", {})

# Hash the messages for cache lookup
cache_key = hashlib.sha256(
    json.dumps(request.get("messages", []), sort_keys=True).encode()
).hexdigest()

# Check cache
conn = sqlite3.connect("~/.claude/response_cache.db")
cursor = conn.execute("SELECT response FROM cache WHERE key = ?", (cache_key,))
row = cursor.fetchone()

if row:
    cached = json.loads(row[0])
    output = {
        "hookSpecificOutput": {
            "hookEventName": "BeforeModel",
            "decision": "mock",
            "syntheticResponse": cached
        }
    }
    print(json.dumps(output))
else:
    # No cache hit, allow normal API call
    pass

sys.exit(0)

Scenario: Integration testing

  1. CI pipeline tests Claude Code automation scripts
  2. BeforeModel hook loads fixtures from test_fixtures/ directory
  3. Returns deterministic responses based on request patterns
  4. Tests run quickly without API calls
  5. Results are reproducible across runs

Scenario: Offline development

  1. Developer is on a plane without internet
  2. BeforeModel hook detects offline status
  3. Returns helpful message: "I'm currently offline. Here's what I can tell you from my cached knowledge..."
  4. Developer can continue working on prompts and workflows

Scenario: Rate limit handling

  1. API returns 429 rate limit error
  2. BeforeModel hook detects rate limit condition
  3. Returns cached response or queues request for later
  4. User experience remains smooth during rate limiting

Additional Context

Prior Art

Gemini CLI's BeforeModel hook supports this:

"Control: Override request, inject synthetic response, block"

Their documentation explicitly mentions the ability to return a synthetic response to skip the actual LLM call.

Implementation Considerations

  • Response validation: Validate that synthetic responses match the expected schema
  • Tool call handling: If synthetic response includes tool calls, they should be executed normally
  • Logging: Log when synthetic responses are used (for debugging)
  • Metrics: Track synthetic vs real API calls for cost analysis
  • Streaming: Consider how synthetic responses interact with streaming output

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗