StuntDouble Documentation
Tool Mocking Framework for AI Agent Testing
What is StuntDouble?
StuntDouble is a Python framework for mocking AI agent tool calls. Just like a stunt double performs risky scenes in place of an actor, StuntDouble lets you test your AI agents without the risk, cost, and unpredictability of production APIs.
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ Production Testing with StuntDouble │
│ ────────── ──────────────────────── │
│ │
│ Agent ──▶ Real API ──▶ $$$ Agent ──▶ StuntDouble ──▶ Mock │
│ ↓ ↓ │
│ Slow, Flaky, Fast, Reliable, │
│ Unpredictable Deterministic │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Motivation
The Problem
AI agents rely heavily on external tools—APIs for customer data, billing systems, communication services, and more. Testing these agents presents unique challenges:
Challenge |
Impact |
|---|---|
API Costs |
Every test run hits real APIs, accumulating costs |
Slow Execution |
Network latency makes test suites painfully slow |
Non-Deterministic |
Real data changes, causing flaky tests |
Environment Dependencies |
Tests require production-like environments |
Risk of Side Effects |
Tests might accidentally send emails, create invoices |
The Solution
StuntDouble provides transparent tool mocking that intercepts tool calls and returns controlled responses:
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ Before StuntDouble │
│ ───────────────── │
│ │
│ ┌─────────┐ ┌──────────────┐ ┌────────────────┐ │
│ │ Agent │────▶│ list_bills() │────▶│ Real API │ │
│ └─────────┘ └──────────────┘ │ • Slow │ │
│ │ • Costly │ │
│ │ • Flaky │ │
│ └────────────────┘ │
│ │
│ After StuntDouble │
│ ───────────────── │
│ │
│ ┌─────────┐ ┌──────────────┐ ┌────────────────┐ │
│ │ Agent │────▶│ StuntDouble │────▶│ Mock Function │ │
│ └─────────┘ └──────────────┘ │ • Instant │ │
│ │ │ • Free │ │
│ │ │ • Controlled │ │
│ ▼ └────────────────┘ │
│ scenario_metadata? │
│ ├── YES ──▶ Return mock │
│ └── NO ──▶ Call real tool │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Key Features
Feature |
Description |
|---|---|
🚀 LangGraph Native |
Per-invocation mocking via |
✨ Transparent Mocking |
Wrap tools once, toggle between real and mock |
🔄 Zero Code Changes |
Agent code remains unchanged, production-ready |
🔍 Smart Input Matching |
Operators like |
⏰ Dynamic Outputs |
Placeholders for |
🔗 Fluent Builder API |
Chainable mock registration: |
📝 Call Recording |
Capture and assert on tool calls with |
🔐 Signature Validation |
Catch mock/tool signature mismatches at registration or runtime |
🪞 MCP Tool Mirroring |
Auto-generate mocks from MCP server schemas |
🔑 Context-Aware Mocks |
Access runtime config (user ID, headers) in mock factories |
Getting Started
Quick Example
Best for LangGraph agents. Per-invocation mocking via RunnableConfig.
from langgraph.prebuilt import ToolNode
from stuntdouble import (
mockable_tool_wrapper,
default_registry,
inject_scenario_metadata,
)
mock = default_registry.mock # Fluent builder on default registry
# Register mocks on the default registry (fluent API)
mock("list_bills").returns({"bills": [{"id": "B001", "amount": 500}]})
# Build graph with native ToolNode + mockable wrapper
builder.add_node("tools", ToolNode(tools, awrap_tool_call=mockable_tool_wrapper))
# Invoke with mocks via config
config = inject_scenario_metadata({}, {
"scenario_id": "landing-page-demo"
})
result = await graph.ainvoke(state, config=config)
Benefits:
✅ Fully concurrent—each request gets its own mocks
✅ No global state or environment variables
✅ Native
ToolNodeintegration—minimal code changes✅ Call recording for test assertions
✅ Signature validation to catch config errors early
Auto-Generate Mocks from MCP Servers
Already using an MCP server? StuntDouble can auto-discover its tools and generate mock implementations, which you can then use in your LangGraph agent workflow:
from stuntdouble.mirroring import ToolMirror
mirror = ToolMirror()
mirror.mirror(["python", "-m", "my_mcp_server"])
tools = mirror.to_langchain_tools()
Installation
# Using uv (recommended)
uv add stuntdouble
# Using pip
pip install stuntdouble
# Using Poetry
poetry add stuntdouble
For MCP mirroring support, install the optional extra:
pip install "stuntdouble[mcp]"
Documentation Structure
Getting Started
Guides
Support
For questions or feedback, please open an issue on the repository.