BackDocumentation
Documentation v1.0

MCP-Spy Docs

A complete guide to inspecting, debugging, and understanding everything that happens between your AI assistant and its tools β€” whether you built those tools or not.

What is MCP-Spy?

The Model Context Protocol (MCP) is the standard that lets AI assistants like Claude call external tools β€” reading files, querying databases, searching the web, running code, and much more. Every time Claude uses a tool, it sends a structured message behind the scenes. MCP-Spy lets you see those messages.

Think of it like browser DevTools, but for AI tool calls. You see the exact request the AI made, the exact response it got back, how long it took, and whether it succeeded or failed. No guessing, no black boxes.

Why does this matter?

When an AI tool call fails or gives wrong results, it is nearly impossible to diagnose without seeing the raw data. Was the AI's request malformed? Did the tool return bad data? Did it time out? MCP-Spy answers all of these questions instantly.

Who is this for?

MCP-Spy is useful in three different situations, even if you have never written a line of code in your life.

AI Power Users

You use Claude Desktop or Cursor with MCP servers like the filesystem MCP, GitHub MCP, or Slack MCP β€” but you don't write code. MCP-Spy lets you see exactly what your AI assistant is doing with those tools in real time.

MCP Developers

You are building an MCP server and need to inspect payloads, reproduce bugs, test edge cases, and iterate fast. MCP-Spy is your primary debugging tool during development.

Teams & Auditors

You need a log of every AI tool call for compliance, security review, or team debugging. Cloud Sync gives you a persistent, shareable record of all MCP activity.

How it works

MCP-Spy is a transparent proxy. It sits between your AI client (Claude, Cursor, etc.) and the MCP server, intercepting every message in both directions without changing anything about how either side works.

You do not need to modify the MCP server. You do not need to change the AI client. You just insert MCP-Spy in the middle by telling your AI client to talk to MCP-Spy's port instead of the server's port directly.

πŸ€–

AI Client

Claude, Cursor, Windsurf, or any MCP-compatible app sends a tool call.

πŸ”

MCP-Spy

Intercepts the message, logs it, then forwards it unchanged to the real server.

πŸ› οΈ

MCP Server

Any MCP server β€” filesystem, GitHub, Slack, or one you built yourself. It never knows MCP-Spy is there.

MCP-Spy is invisible to both sides. It only observes.

Quick Start

No installation needed. Run MCP-Spy with npx directly:

npx mcp-spy --target 3000

This starts MCP-Spy on port 4000 (its default listen port) and forwards traffic to port 3000 where your MCP server is running. Then tell your AI client to connect to http://localhost:4000 instead.

  • 1
    Your MCP server is already running on some port (e.g. 3000).
  • 2
    Run npx mcp-spy --target 3000 in your terminal. MCP-Spy starts listening on 4000.
  • 3
    Update your AI client config to point to http://localhost:4000 instead of 3000.
  • 4
    The TUI opens in your terminal. Every tool call appears in real time β€” request, response, duration, status code.

Using with any MCP server

You do not need to build an MCP server to use MCP-Spy. It works with any existing MCP server β€” including all the popular ones that the community and Anthropic provide out of the box.

Common examples include the filesystem MCP (lets Claude read and write your local files), the GitHub MCP (lets Claude interact with your repos), the Slack MCP (lets Claude read and send messages), and many others.

The core idea

Instead of telling Claude Desktop to run npx @modelcontextprotocol/server-filesystem /Users/youdirectly, you tell it to run MCP-Spy, and MCP-Spy wraps that command for you. You don't change the MCP server at all β€” you just add MCP-Spy in front of it.

Example: Wrapping the Filesystem MCP

Normally your claude_desktop_config.json might look like this:

Before β€” without MCP-Spy
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"]
    }
  }
}

To add MCP-Spy, you wrap that command with mcp-spy --target, but since this MCP uses stdio (not HTTP), you use the --wrap mode by passing the original command as the target:

After β€” with MCP-Spy wrapping it
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "mcp-spy",
        "--target", "3000",
        "--",
        "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"
      ]
    }
  }
}

MCP-Spy starts the real MCP server as a child process and intercepts all stdio communication. Nothing changes for Claude β€” it still gets the same responses, but now you can see every call.

Other popular MCP servers you can wrap

GitHub MCP
@modelcontextprotocol/server-github
Read repos, issues, PRs, and code.
Filesystem MCP
@modelcontextprotocol/server-filesystem
Read and write local files.
Slack MCP
@modelcontextprotocol/server-slack
Read channels, send messages.
Brave Search MCP
@modelcontextprotocol/server-brave-search
Web search results via Brave.
PostgreSQL MCP
@modelcontextprotocol/server-postgres
Query your database directly.
Puppeteer MCP
@modelcontextprotocol/server-puppeteer
Browser automation and screenshots.

Any MCP server that follows the standard protocol can be wrapped with MCP-Spy β€” these are just the most common ones.

Debugging common errors

MCP failures can be confusing because the AI assistant usually gives a vague error message. Here is how to use MCP-Spy to diagnose the most common problems.

Problem: Claude says β€œI wasn't able to use that tool”

Open MCP-Spy TUI and look at the status code column for the failed call.

  • 500 β€” The server crashed. Check the response payload for a stack trace.
  • 404 β€” Claude called a tool that doesn't exist (wrong tool name or version mismatch).
  • 401 / 403 β€” Missing or expired API key in the server's environment.
  • No entry at all β€” MCP-Spy never received the request. Your client config is still pointing to the old port.
Problem: The tool returns wrong or incomplete results

Click on the log entry in the TUI to expand the full request and response payloads. Check:

  • Did Claude send the right arguments? (Check Request Payload)
  • What did the server actually return? (Check Response Payload)
  • Is the data truncated, malformed, or missing fields the AI expected?

With Cloud Pro you can use Edit & Replay to modify the request and resend it to isolate exactly what causes the problem.

Problem: Everything is slow

The duration column in the TUI shows how long each call takes. If specific tool calls are slow (e.g. >2 seconds), inspect the response to see if the server is returning large payloads that could be paginated or filtered.

Building your own MCP server

If you are developing an MCP server, MCP-Spy is even more valuable. You get live feedback on every tool call during development without adding any logging code to your server.

The setup is the same β€” run your server, start MCP-Spy pointing at it, and connect your AI client to MCP-Spy's port. As you make changes to your server, MCP-Spy keeps logging. No restarts needed.

The development loop

  1. Start your MCP server (e.g. node server.js on port 3000)
  2. Start MCP-Spy (npx mcp-spy --target 3000)
  3. Ask Claude to call your tool
  4. See the exact JSON-RPC in the TUI
  5. Fix the bug, restart your server, repeat

Language-specific examples are below in the Server Examples section.

Claude Desktop

Claude Desktop reads its MCP server config from claude_desktop_config.json. The file lives at:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

To spy on any MCP server in Claude Desktop, change its command and args to go through MCP-Spy. Here is a complete example wrapping an HTTP-based custom server:

claude_desktop_config.json
{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["mcp-spy", "--target", "3000"],
      "env": {}
    }
  }
}

Claude Desktop launches this command when it starts. MCP-Spy listens on port 4000, forwards to your server on 3000, and logs everything. Restart Claude Desktop after editing the config file.

Cursor

Go to Cursor Settings β†’ Features β†’ MCP and add a new server entry. Set the command to launch MCP-Spy instead of your server directly.

Cursor MCP config (JSON)
{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["mcp-spy", "--target", "3000"]
    }
  }
}

Cursor will launch MCP-Spy on startup and route all tool calls through it.

Windsurf

Windsurf (by Codeium) also supports MCP servers. Edit the ~/.codeium/windsurf/mcp_config.json file and add MCP-Spy as a wrapper the same way as Claude Desktop.

~/.codeium/windsurf/mcp_config.json
{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["mcp-spy", "--target", "3000"]
    }
  }
}

Other MCP clients

Any MCP-compatible client works with MCP-Spy as long as you can configure it to use a custom server address. The pattern is always the same:

  1. Find where the client defines its MCP server (usually a JSON config file)
  2. Replace the direct server command or URL with MCP-Spy's address (http://localhost:4000)
  3. Make sure MCP-Spy is running with --target pointing at the original server

Compatible clients include: Claude Desktop, Cursor, Windsurf, VS Code (Continue extension), Zed, and any custom application using the official MCP SDK.

Pro Feature

Cloud Sync Dashboard

By default, MCP-Spy stores logs locally in a SQLite database on your machine. With Cloud Sync (Pro), every intercepted call is also uploaded to your personal dashboard at mcpspy.dev in real time.

Persistent history

All your logs are saved in the cloud. Close your terminal and come back later β€” everything is still there.

Share with your team

Generate a public link for any trace. Share it with a teammate or paste it in a GitHub issue.

Edit & Replay

Modify any request payload in the browser and resend it to your local MCP server instantly.

Visual inspector

Syntax-highlighted JSON diff view, filter by status, method, or duration β€” all in the browser.

Activate in CLI:

npx mcp-spy --target 3000 --sync mcp_live_XXXXXXXX

Get your API key from the dashboard after subscribing at mcpspy.dev/pricing.

Edit & Replay Pro

Edit & Replay lets you take any logged request, modify its JSON payload in the browser, and resend it to your locally running MCP server β€” without triggering Claude again.

This is useful for:

  • Testing how your server handles edge cases (empty strings, large numbers, missing fields)
  • Reproducing a specific failure with slightly different inputs
  • Confirming that a bug is in the request (Claude's side) vs. the response (server side)

How to use it

  1. Open the dashboard (Pro required)
  2. Click any log entry to select it
  3. Click Edit & Replay β€” a modal opens with the original JSON request
  4. Modify the payload as needed
  5. Click Send β€” the response appears immediately below

Requires the CLI to be running locally (npx mcp-spy --target ...) since Edit & Replay sends the request through your machine's local proxy.

Share Trace Pro

Any log entry in your dashboard can be turned into a permanent, public link that anyone can view β€” no login required.

The shared trace page shows the full request and response payloads with syntax highlighting, the HTTP method, status code, and duration. It is read-only and works from any device.

How to share a trace

  1. Open the dashboard and click any log entry
  2. Click the Share button (chain icon)
  3. The link is copied to your clipboard automatically
  4. The URL looks like: mcpspy.dev/trace/abc123...

Auto-Redaction

Pro

Add --redact-pii to automatically mask secrets from all payloads before they are saved locally or synced to the cloud. Nothing sensitive ever touches the database.

npx mcp-spy --target 3000 --redact-pii --sync YOUR_KEY

The following patterns are automatically redacted:

AWS Access Keys
AKIA… patterns
Bearer tokens
Authorization headers in payloads
Private keys
-----BEGIN … blocks
JSON secrets
"password", "token", "api_key" fields
Email addresses
user@example.com
High-entropy tokens
32–64 char hex strings

Redacted logs are tagged with a πŸ”’ badge in both the TUI and dashboard. The original bytes are forwarded to the MCP server unmodified β€” redaction only affects storage.

Token Profiling

Pro

Every intercepted request and response is automatically profiled for token count. This tells you how much of an LLM's context window each tool call consumes β€” and gives you a rough cost estimate.

Token counts appear as ~1.2k badges on every log row in the TUI and dashboard. The TUI stats bar shows the session total. No extra flags needed β€” it's always on.

Estimation method

Uses a blended heuristic (word count Γ— 1.3 vs. char count Γ· 4, take the higher). Accurate to Β±15% for JSON/code payloads β€” adequate for spotting expensive calls without any heavy dependencies.

Export (cURL / Postman)

Pro

Turn any saved log into a runnable shell command or a Postman collection with one click.

Copy as cURL

In the dashboard: select a log β†’ Copy cURL button.
In the TUI: select a log β†’ press c.

curl -s -X POST http://localhost:4000 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"tools/call",...}'

Postman Collection

In the dashboard: click the Postman button in the top-right. Downloads a .json file you can import directly into Postman.

The collection includes all currently visible logs as individual requests, respecting any active server filter.

Auto-Mock Mode

Pro

Add --mock to make MCP-Spy intercept all requests and return saved responses from your local database β€” without forwarding anything to the real server.

npx mcp-spy --target 3000 --mock

For each incoming method (e.g. tools/call), MCP-Spy looks up the most recent successful response for that method in SQLite and returns it immediately. If no saved response exists for a method, it returns a 404 JSON-RPC error.

When is this useful?

  • Testing AI agent behaviour without hitting real APIs or incurring costs
  • Reproducing a specific scenario deterministically
  • Running the agent while the real MCP server is offline
  • Frontend development where you don't need live data

CI/CD Test Runner

Pro

Use the test subcommand to replay saved requests against your MCP server and assert valid JSON-RPC responses. Exits 0 on all pass, 1 on any failure β€” works natively in GitHub Actions, GitLab CI, and any shell pipeline.

# Replay last 10 saved requests against the server on port 3000
npx mcp-spy test --target 3000

# Only replay a specific method
npx mcp-spy test --target 3000 --method tools/call

# Only replay from a specific server label
npx mcp-spy test --target 3000 --name filesystem --count 20
.github/workflows/mcp-test.yml
name: MCP Server Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Start MCP server
        run: node server.js &
      - name: Run MCP-Spy tests
        run: npx mcp-spy test --target 3000 --count 20
FlagDescriptionDefault
--targetPort of the MCP server to testrequired
--methodFilter to only replay this JSON-RPC methodall
--nameFilter to only replay from this server labelall
--countMax requests to replay10
--timeoutPer-request timeout in ms5000

TUI β€” Invoking & Keyboard Shortcuts

How to launch the TUI

The Terminal UI (TUI) is a full-screen interface that shows a live list of intercepted MCP calls on the left and the selected payload on the right. It opens automatically whenever you start the proxy with a --target port.

Terminal β€” start the TUI alongside the proxy
# Basic β€” proxy + TUI in one command
npx mcp-spy -t 3001

# With a label and cloud sync (Pro)
npx mcp-spy -t 3001 --name filesystem --sync mcp_live_XXXX...

# Disable TUI, keep plain console output (useful in CI)
npx mcp-spy -t 3001 --no-tui

Standalone welcome & guided setup

Running npx mcp-spy without --target opens an interactive welcome screen instead of the proxy. You will see:

  1. Subscription prompt β€” a summary of what Pro unlocks and the price. Press Y to open mcpspy.dev/pricing in your browser, or N to skip.
  2. Guided setup wizard β€” a 4-step walkthrough that shows you how to connect the official @modelcontextprotocol/server-filesystem server to MCP-SPY and your MCP client. Navigate with β†’ / N and go back with ← / P.

Quick demo with the official filesystem server:

# Terminal 1 β€” start the MCP server on port 3001
npx -y @modelcontextprotocol/server-filesystem \
    --transport sse --port 3001 ~/Documents

# Terminal 2 β€” start MCP-SPY proxy (TUI opens automatically)
npx mcp-spy -t 3001 --name filesystem

# Claude Desktop config β€” point at the PROXY (4000), not the server (3001)
# Edit: ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "filesystem": {
      "url": "http://localhost:4000"
    }
  }
}
# Use "url" β€” not "command"/"args" β€” otherwise Claude Desktop
# spawns the server directly and bypasses the proxy entirely.

Keyboard shortcuts

These shortcuts work once the TUI is running with an active proxy.

KeyAction
↑ / ↓Navigate between log entries
sCycle server filter β€” shows all, then each server label in turn
cToggle cURL export view for the selected log
qQuit MCP-Spy
Ctrl+CForce quit

If you prefer plain terminal output without the TUI (useful in CI pipelines), add the --no-tui flag when starting MCP-Spy.

Node.js / TypeScript

MCP-Spy works with any MCP server. You do not need to change your server code at all. If you are building a Node.js MCP server using the official @modelcontextprotocol/sdk:

server.ts β€” no changes needed
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new Server({ name: "my-tool", version: "1.0.0" });

// Your tool definitions...

const transport = new StdioServerTransport();
await server.connect(transport);

Run your server: node server.js on port 3000. Then start MCP-Spy: npx mcp-spy --target 3000.

Python

Using the official mcp Python package. MCP-Spy intercepts the stdio stream transparently.

server.py
import asyncio
from mcp.server.stdio import stdio_server
from mcp.server import Server

app = Server("my-tool")

@app.tool()
async def get_weather(location: str):
    """Returns current weather for a location."""
    return f"It is 72Β°F and sunny in {location}"

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await app.run(
            read_stream,
            write_stream,
            app.create_initialization_options()
        )

if __name__ == "__main__":
    asyncio.run(main())

Go

Using the mark3labs/mcp-go package. Compile your server, run it on a port, and point MCP-Spy at it.

main.go
package main

import (
	"context"
	"fmt"
	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

func main() {
	s := server.NewMCPServer("my-tool", "1.0.0",
		server.WithToolCapabilities(true),
	)

	tool := mcp.NewTool("get_weather",
		mcp.WithDescription("Get weather for a location"),
		mcp.WithString("location", mcp.Required()),
	)

	s.AddTool(tool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
		loc := req.Params.Arguments["location"].(string)
		return mcp.NewToolResultText(fmt.Sprintf("Sunny in %s", loc)), nil
	})

	if err := server.ServeStdio(s); err != nil {
		panic(err)
	}
}

Rust

Using the mcp-rs crate. Compile your binary and point MCP-Spy at the running process.

src/main.rs
use mcp_rs::server::Server;
use mcp_rs::transport::stdio::StdioTransport;
use tokio;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let transport = StdioTransport::new();
    let mut server = Server::new(transport);

    // Add tools here...

    server.listen().await?;
    Ok(())
}

CLI Reference

FlagDescriptionDefault
--target <port>Required. The local port where your actual MCP server is running.β€”
--name <label>Human-readable label for this server. Shows in TUI and dashboard for multi-MCP setups. E.g. --name filesystem.port-{n}
--sync <token>Your Pro API key. Uploads logs to your cloud dashboard in real time.β€”
--redact-piiAuto-redact secrets (AWS keys, tokens, emails, passwords) from payloads before saving or syncing. Proxy traffic is unaffected.off
--mockMock mode β€” return saved responses from the local database instead of forwarding to the real server.off
--no-tuiDisables the interactive TUI. Useful in CI, scripts, or piped output.off
Test subcommand: mcp-spy test --target <port> β€” see the CI/CD Test Runner section for full options.

Running multiple MCP servers simultaneously

Use --name to label each instance. Run each in a separate terminal:

# Terminal 1 β€” filesystem MCP
npx mcp-spy --target 3000 --name filesystem --sync YOUR_KEY

# Terminal 2 β€” GitHub MCP
npx mcp-spy --target 3001 --name github --sync YOUR_KEY

In the dashboard, each log shows its server label. Click a label to filter the list to that server only.

Where your data is stored

Local (free tier)

Every intercepted request is saved to a SQLite database on your machine immediately β€” no network needed.

~/.mcp-spy/mcp_logs.db
  • βœ“ Persists across terminal restarts
  • βœ“ No size limit beyond disk space
  • βœ“ Read by the TUI in real time
  • βœ— Lost if you wipe your machine

Cloud (Pro β€” --sync)

Each log is also sent to your personal Convex database in real time. Available from any device via the dashboard.

mcpspy.dev/dashboard
  • βœ“ Survives machine wipe
  • βœ“ Accessible from browser anywhere
  • βœ“ Shareable trace links
  • βœ“ Real-time live updates

Activating Premium Features

After subscribing at mcpspy.dev/pricing, go to your dashboard to find your API key. Pass it to the CLI with the --sync flag to activate Cloud Sync.

npx mcp-spy --target 3000 --sync mcp_live_your_api_key_here

Without the token, MCP-Spy runs in local mode β€” free, fully offline, with the TUI only.