# MCP Tool Mirroring Guide Auto-generate mock implementations from MCP server tool schemas. ToolMirror discovers tools from an MCP server and creates mock implementations without manual coding. --- ## Installation Install the optional MCP support before using live mirroring: ```bash pip install "stuntdouble[mcp]" ``` --- ## Overview MCP (Model Context Protocol) Tool Mirroring allows you to: 1. **Discover** tools from any MCP server (stdio or HTTP) 2. **Generate** mock implementations automatically from schemas 3. **Use** the mocked tools in your AI agent tests ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ MCP Mirroring Flow │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. Discovery 2. Generation 3. Usage │ │ ──────────── ───────────── ────── │ │ │ │ ┌─────────────┐ ┌─────────────────┐ ┌──────────────┐ │ │ │ MCP Server │ │ MockGenerator │ │ AI Agent │ │ │ │ │ │ │ │ │ │ │ │ tools/list │──────▶ │ Schema analysis │──────▶ │ bind_tools() │ │ │ │ │ │ Type inference │ │ │ │ │ │ - tool_a │ │ Mock creation │ │ or │ │ │ │ - tool_b │ │ │ │ ToolNode() │ │ │ │ - tool_c │ │ Optional: LLM │ │ │ │ │ └─────────────┘ └─────────────────┘ └──────────────┘ │ │ │ │ Supported transports: stdio (subprocess), HTTP (remote server) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### When to Use Mirroring | Scenario | Recommendation | |----------|----------------| | Testing agent with MCP tools | ✅ Mirror tools for fast, offline testing | | CI/CD pipelines | ✅ Use `ToolMirror.for_ci()` for deterministic mocks | | Development without MCP server | ✅ Mirror once, develop offline | | Need realistic mock data | ✅ Use `ToolMirror.with_llm()` | | Production | ❌ Use real MCP tools | --- ## Quick Start ### Basic Mirroring (stdio) ```python from stuntdouble.mirroring import ToolMirror # Create mirror instance mirror = ToolMirror() # Mirror tools from MCP server (subprocess) mirror.mirror(["python", "-m", "my_mcp_server"]) # Get LangChain-compatible tools tools = mirror.to_langchain_tools() # Use with agent agent = llm.bind_tools(tools) response = agent.invoke("Create an invoice for customer CUST-001") ``` ### HTTP Server Mirroring ```python from stuntdouble.mirroring import ToolMirror mirror = ToolMirror() # Mirror from HTTP MCP server mirror.mirror(http_url="http://localhost:8080/mcp") tools = mirror.to_langchain_tools() ``` ### One-Line Convenience Functions ```python from stuntdouble.mirroring import mirror, mirror_for_agent # One-line mirroring (returns result dict) result = mirror(["python", "-m", "my_server"]) print(f"Mirrored {result['mirrored_count']} tools: {result['tools']}") # Mirror and get tools ready for agent tools = mirror_for_agent(llm, ["python", "-m", "my_server"]) agent = llm.bind_tools(tools) ``` --- ## LLM-Powered Generation For realistic, contextual mock data, use an LLM to generate responses: ```python from langchain_openai import ChatOpenAI from stuntdouble.mirroring import ToolMirror llm = ChatOpenAI(model="gpt-4o") mirror = ToolMirror.with_llm(llm) mirror.mirror(["python", "-m", "financial_mcp"]) tools = mirror.to_langchain_tools() # Tools return realistic, contextual mock data # e.g., create_invoice returns realistic invoice data ``` ### Quality Presets ```python from stuntdouble.mirroring import ToolMirror, QualityPreset # High quality - uses LLM for all responses mirror = ToolMirror.with_llm(llm, quality=QualityPreset.HIGH) # Balanced quality - parameter-aware static generation mirror = ToolMirror.with_llm(llm, quality=QualityPreset.BALANCED) # Fast - static responses only (no LLM calls) mirror = ToolMirror.for_ci() ``` --- ## HTTP Authentication Mirror tools from remote MCP servers that require authentication: ```python from stuntdouble.mirroring import ToolMirror mirror = ToolMirror() # Bearer token authentication result = mirror.mirror( http_url="https://api.example.com/mcp", headers={"Authorization": "Bearer your-token-here"} ) # API key authentication result = mirror.mirror( http_url="http://localhost:8080", headers={ "X-API-Key": "abc123", "X-Client-ID": "my-app" } ) # Multiple custom headers result = mirror.mirror( http_url="http://internal.company.com/mcp", headers={ "Authorization": "Bearer token", "X-Request-ID": "unique-id", "X-Environment": "production" } ) tools = mirror.to_langchain_tools() ``` ### Security Features Headers are automatically included in all HTTP requests: - SSE connection establishment (`GET /sse`) - JSON-RPC message calls (`POST /messages`) StuntDouble provides built-in security: | Feature | Description | |---------|-------------| | **Header Validation** | Headers must be a dict with string keys/values | | **Sensitive Data Protection** | Headers are redacted in logs and errors | | **Protocol Compliance** | `Content-Type: application/json` auto-enforced | | **Transport Validation** | Warning if headers specified for stdio | --- ## Customizing Mock Data Override generated mock data for specific tools: ```python from stuntdouble.mirroring import ToolMirror mirror = ToolMirror() mirror.mirror(["python", "-m", "my_server"]) # Customize specific tool responses mirror.customize("get_customer", { "id": "CUST-001", "name": "Custom Test Corp", "tier": "platinum" }) # List all mirrored tools print(mirror.list_mirrors()) # [MirrorInfo(...), MirrorInfo(...), ...] tools = mirror.to_langchain_tools() ``` --- ## CI/CD Optimization For CI/CD pipelines, use the optimized factory: ```python from stuntdouble.mirroring import ToolMirror # Optimized for CI: deterministic, no LLM calls, fast mirror = ToolMirror.for_ci() mirror.mirror(["python", "-m", "my_server"]) tools = mirror.to_langchain_tools() ``` ### Caching Enable caching for faster subsequent runs: ```python mirror = ToolMirror().enable_caching(ttl_minutes=30) mirror.mirror(["python", "-m", "my_server"]) # Second call uses cached tool definitions mirror.mirror(["python", "-m", "my_server"]) ``` --- ## API Reference ### ToolMirror Class | Method | Description | |--------|-------------| | `ToolMirror()` | Create mirror with default settings | | `ToolMirror.with_llm(llm, quality=)` | Create with LLM-powered generation | | `ToolMirror.for_ci()` | Create mirror optimized for CI/CD | | `ToolMirror.for_langgraph(registry=None, quality=)` | Create mirror that also registers mirrored mocks in a LangGraph registry | | `mirror.enable_caching(ttl_minutes=)` | Enable response caching | | `mirror.enable_llm(llm)` | Add LLM generation to existing mirror | ### Mirroring Methods | Method | Description | |--------|-------------| | `mirror.mirror(server_command=, http_url=, headers=)` | Mirror tools from MCP server | | `mirror.to_langchain_tools()` | Convert to LangChain StructuredTool format | | `mirror.customize(tool_name, data)` | Override mock data for a tool | | `mirror.list_mirrors()` | List mirrored tools as `MirrorInfo` objects | | `mirror.list_mirrors_by_server(server_name)` | Filter mirrored tools by source server | | `mirror.get_stats()` | Return cache and generation statistics | | `mirror.unregister(tool_name)` | Remove a mirrored tool | ### Convenience Functions ```python from stuntdouble.mirroring import mirror, mirror_for_agent # Mirror and get result dict result = mirror(server_command=["python", "-m", "server"]) # Returns: {'mirrored_count': 5, 'tools': [...], 'server_name': '...'} # Mirror and get tools ready for agent tools = mirror_for_agent(llm, server_command=["python", "-m", "server"]) ``` --- ## Integration with Approaches MCP Tool Mirroring works with both StuntDouble approaches: - **[LangGraph Approach](langgraph-integration.md#using-mirrored-tools)** — Use mirrored tools with LangGraph's per-invocation mocking --- ## See Also - [LangGraph Approach](langgraph-integration.md) — Per-invocation mocking - [Quickstart Guide](quickstart.md) — Getting started