Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.q-uestionable.ai/llms.txt

Use this file to discover all available pages before exploring further.

Session replay sends previously captured client-to-server messages against a live MCP server, collecting responses for analysis, testing, and reproduction.

Basic Workflow

  1. Capture a session:
qai proxy start \
  --transport stdio \
  --target-command "python my_server.py" \
  --session-file session.json
Interact with your agent or client normally. The proxy saves all messages to session.json.
  1. Replay the captured messages:
qai proxy replay \
  --session-file session.json \
  --target-command "python my_server.py" \
  --output results.json
The replay engine resends client-to-server messages and captures new responses.

Replay Command Reference

qai proxy replay --session-file FILE [OPTIONS]
Options:
OptionRequiredTypeDescription
--session-fileYesstringPath to the captured session file
--target-commandYes*stringCommand to start the target server (stdio)
--target-urlYes*stringURL of the target server (SSE/HTTP)
--outputNostringSave replay results to JSON file
--timeoutNofloatPer-message response timeout in seconds (default: 10.0)
--no-handshakeNoflagSkip synthetic handshake (only if session already includes initialize)
* Either --target-command or --target-url is required.

Auto-Handshake

By default, replay sends a synthetic MCP handshake before replaying messages:
{
  "jsonrpc": "2.0",
  "id": "__handshake__",
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": {
      "name": "mcp-proxy-replay",
      "version": "0.1.0"
    }
  }
}
This ensures the server is ready to accept tool calls. Use --no-handshake only if:
  • Your captured session already starts with an initialize request
  • You want to replay the exact sequence as captured (including handshake)

Modifying Arguments Before Replay

Replay sends messages exactly as captured. To modify arguments:
  1. Export the session JSON or inspect it to find the target message
  2. Edit the message’s payload field directly in the JSON file
  3. Replay the modified session
Example:
{
  "proxy_id": "msg-001",
  "sequence": 5,
  "direction": "client_to_server",
  "payload": {
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "search",
      "arguments": {
        "query": "modified search term"
      }
    }
  }
}

Output Format

Replay results are written to JSON with a detailed record of each message:
{
  "results": [
    {
      "original_request": {
        "id": "msg-001",
        "method": "tools/call",
        "jsonrpc_id": 1
      },
      "sent_message": {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "tools/call",
        "params": { ... }
      },
      "response": {
        "jsonrpc": "2.0",
        "id": 1,
        "result": { ... }
      },
      "error": null,
      "duration_ms": 145.2
    }
  ]
}
Result fields:
FieldDescription
original_requestThe ProxyMessage from the captured session
sent_messageThe actual SessionMessage sent to the server
responseThe server’s response (null if notification or timeout)
errorError description if replay failed (null on success)
duration_msRound-trip time in milliseconds

Handling Timeouts

Use --timeout to adjust the per-message timeout:
qai proxy replay \
  --session-file session.json \
  --target-command "python slow_server.py" \
  --timeout 30.0 \
  --output results.json
Messages that exceed the timeout are recorded with a timeout error and continue to the next message.

Filtering Replayed Messages

Only client-to-server messages are replayed. Server-to-client messages in the session are skipped:
  • client_to_server messages (requests and notifications) are replayed in order
  • server_to_client messages (responses and notifications) are ignored
  • Notifications (no JSON-RPC id) are sent without waiting for a response

Use Cases

Reproduce Security Findings

Capture a vulnerable interaction, then replay it to confirm the finding:
# 1. Capture the attack sequence
qai proxy start \
  --transport stdio \
  --target-command "python app.py" \
  --session-file attack.json

# 2. Replay against the target to confirm
qai proxy replay \
  --session-file attack.json \
  --target-command "python app.py" \
  --output confirmation.json

# 3. Compare responses

Test Compatibility

Verify a new server version behaves the same as the old:
# Replay sessions against new server
qai proxy replay \
  --session-file v1_capture.json \
  --target-command "python server_v2.py" \
  --output v2_results.json

# Compare with original v1_results.json

Debug Intermittent Issues

Capture a failing interaction, then replay it repeatedly:
qai proxy replay \
  --session-file failing_session.json \
  --target-command "python server.py" \
  --output replay_1.json

qai proxy replay \
  --session-file failing_session.json \
  --target-command "python server.py" \
  --output replay_2.json

# Compare results to identify variability
Only client-to-server messages are replayed. Server responses are discarded from the original session and newly captured during replay. This allows testing the server against the same client sequence while isolating server-side changes.