[FEATURE] Synthetic Response Injection for Testing and Caching
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:
- Testing hooks and automation: Developers building Claude Code integrations cannot test their workflows without consuming API credits and waiting for real responses
- Response caching: Cannot implement a caching layer to avoid redundant API calls for repeated questions
- Offline development: Cannot work on Claude Code integrations when offline or when the API is unavailable
- Deterministic testing: Cannot create reproducible test scenarios with known responses
- 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
- 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.
- 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.
- 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
- Developer frequently asks similar questions during development
BeforeModelhook hashes the request messages- Checks a local SQLite database for matching hash
- If found, returns cached response as
syntheticResponse - If not found, allows API call and caches response in
AfterModelhook - 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
- CI pipeline tests Claude Code automation scripts
BeforeModelhook loads fixtures fromtest_fixtures/directory- Returns deterministic responses based on request patterns
- Tests run quickly without API calls
- Results are reproducible across runs
Scenario: Offline development
- Developer is on a plane without internet
BeforeModelhook detects offline status- Returns helpful message: "I'm currently offline. Here's what I can tell you from my cached knowledge..."
- Developer can continue working on prompts and workflows
Scenario: Rate limit handling
- API returns 429 rate limit error
BeforeModelhook detects rate limit condition- Returns cached response or queues request for later
- 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
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗