I Built mitmproxy for MCP

MCP has zero observability. When something breaks, you get silence. I built mcptools — six commands (doctor, inspect, call, proxy, record, replay) that give you actual visibility into what your MCP servers are doing. One pip install, works with any IDE.

Last month I lost 45 minutes to a typo. Not a subtle bug. Not a race condition. A typo in an environment variable name. GITHUB_TOKE instead of GITHUB_TOKEN.

I couldn't tell because MCP gives you absolutely nothing when things go wrong. No error message. No log line. Just a tool that should exist and doesn't. Your IDE won't mention it. The server won't complain. You're on your own with a JSON config file and your best guess.

Forty-five minutes of my life, gone. Over a missing N.

The spiral

I was setting up three MCP servers for a side project. Two worked immediately. The third? Silent failure. And my debugging process was... not great:

1. Re-read the config JSON for the 12th time
2. Restart Claude Desktop
3. Wait 30 seconds for it to boot
4. Still broken
5. Add print() to the server code
6. Remember that prints go to stdout and break the JSON-RPC stream
7. Redirect to stderr, restart again
8. Stare at logs, see nothing useful
9. Finally spot the typo in the env var name
10. 45 minutes gone

That last step is what did it. Not the bug itself — the fact that I had zero tools to find it. If I'd been debugging an HTTP API, I'd open devtools and see the failing request in two seconds. With MCP? You're blind.

I built the thing I kept wishing existed. It took a weekend. Honestly should have done it sooner.

mcptools doctor

This is the one that would have saved me those 45 minutes. It reads your MCP config, spins up every server, does the full handshake, and tells you what's healthy and what's broken.

Here's a config with three servers — two that work, one that's intentionally busted:

{
  "mcpServers": {
    "fetch": {
      "command": "uvx",
      "args": ["mcp-server-fetch"]
    },
    "time": {
      "command": "uvx",
      "args": ["mcp-server-time"]
    },
    "broken": {
      "command": "nonexistent-mcp-server-xyz",
      "args": []
    }
  }
}
$ mcptools doctor --config examples/demo_config.json

Config: examples/demo_config.json

  Checking fetch...           1 tool, 1 prompt
  Checking time...            2 tools
  Checking broken-server...   Command not found: nonexistent-mcp-server-xyz
    → Ensure 'nonexistent-mcp-server-xyz' is installed and in your PATH

Summary: 2 healthy, 1 error

Two seconds. Found the problem, told me what's wrong, suggested a fix. Under the hood it actually starts each server process, does the full MCP handshake, queries capabilities, and measures latency. So it catches the stuff that's hard to spot by staring at JSON — typos in commands, missing env vars, servers that start but don't speak MCP, slow startups, init crashes.

Try it on your setup. Claude Desktop: mcptools doctor --config ~/.claude/claude_desktop_config.json. Cursor: mcptools doctor --config ~/.cursor/mcp.json. Or just mcptools doctor with no args — it auto-detects configs for Claude Desktop, Cursor, VS Code, and Windsurf.

mcptools inspect

You found an MCP server on GitHub. The README looks promising but is three months out of date. What tools does it actually expose? What are the parameter types? Is that field required or optional?

$ mcptools inspect uvx mcp-server-fetch

┌───────────────────────── MCP Server ─────────────────────────┐
 mcp-fetch v1.26.0                                    
└──────────────────────────────────────────────────────────────┘
                           Tools (1)
  Name    Description                    Parameters
  fetch   Fetches a URL from the         url: string*,
          internet and extracts its      max_length: integer,
          contents as markdown.          start_index: integer,
                                         raw: boolean
                          Prompts (1)
  Name    Description                    Arguments
  fetch   Fetch a URL and extract         url*
          its contents

The * means required. One tool, one prompt, all the types laid out. No guessing from README examples that were last updated when the server had half as many features.

Works with anything that speaks MCP over stdio:

$ mcptools inspect uvx mcp-server-time
$ mcptools inspect python my_server.py
$ mcptools inspect npx @modelcontextprotocol/server-filesystem /tmp
$ mcptools inspect node build/index.js

mcptools call

Sometimes you just want to call a tool and see what it returns. No IDE, no config, no proxy — just a quick sanity check from the terminal.

$ mcptools call uvx mcp-server-fetch --tool fetch --args '{"url": "https://example.com"}'

┌──── fetch ────┐
 # Example     
 Domain        
 This domain   
 is for use in 
 illustrative  
 examples...   
└───────────────┘

It handles the handshake, verifies the tool exists, calls it, and pretty-prints the result. If the response is JSON, you get syntax highlighting. Pass --json for machine-readable output you can pipe to jq.

This is the fastest way to test whether a tool actually works before you wire it up in your IDE. No guessing, no restarting Claude Desktop, just run it and see.

mcptools proxy

This is the one I reach for most. It sits between your IDE and the MCP server — a transparent man-in-the-middle. Every JSON-RPC message, both directions, with timing.

Your IDE (Claude Code / Cursor / Windsurf)
     stdin/stdout
mcptools proxyintercepts everything
     stdin/stdout
MCP Server
$ mcptools proxy --config ~/.claude/claude_desktop_config.json --server fetch --no-tui

>>> initialize                        120ms
<<< initialize
>>> notifications/initialized
>>> tools/list                        45ms
<<< tools/list
>>> tools/call                        1205ms
<<< tools/call

That's the --no-tui log mode. Drop the flag and you get a full TUI dashboard — live message table, latency per call, error highlighting, stats, and a detail panel where you can click any row to see the raw JSON-RPC payload.

Honestly, just seeing latency numbers next to each call changed how I think about MCP servers. Some of them are way slower than you'd expect. The GitHub MCP server takes 3+ seconds just to initialize — it has 35 tools, and the handshake gets heavy. Good to know before you wonder why Claude is taking forever to respond.

mcptools record + replay

Sometimes you need to show someone else what happened. Or you're chasing a bug that only shows up when the moon is full and you're not looking at the logs.

$ mcptools record --config ~/.claude/claude_desktop_config.json -o debug_session.json

Recording: fetch
Output: debug_session.json
Ctrl+C to stop and save

>>> initialize                        120ms
<<< initialize
>>> tools/list                        45ms
<<< tools/list
>>> tools/call                        1205ms
<<< tools/call
^C
Session saved: debug_session.json
6 messages recorded
$ mcptools replay debug_session.json

┌──────────────────── MCP Session Replay ────────────────────┐
 Session: debug_session.json                        
 Messages: 24 | Duration: 12.3s | Speed: 1x          
└────────────────────────────────────────────────────────────┘

+0.0s >>> initialize
+0.1s <<< initialize                     120ms
+0.1s >>> notifications/initialized
+0.2s >>> tools/list                      45ms
+0.3s <<< tools/list
+2.1s >>> tools/call                      1205ms
+3.3s <<< tools/call

You can also filter and speed up: mcptools replay debug_session.json --speed 2 --filter "tools/*"

The JSON has timestamps, directions, full payloads, latency — everything you'd want in a bug report. Attach it to a GitHub issue instead of "it doesn't work" with a screenshot.

A real debugging session

Last week a GitHub MCP server kept timing out. Here's the actual workflow I used to pin it down.

$ mcptools doctor --server github

  Checking github...   35 tools — Slow response (3200ms)

OK so it connects, but 3.2 seconds for an init is a lot. 35 tools. Noted.

$ mcptools proxy --server github --no-tui

>>> initialize                           3205ms
<<< initialize
>>> tools/list                           89ms
<<< tools/list
>>> tools/call                           timeout
  ERROR: Request timed out after 30s

There it is. Starts fine, lists tools fine, but chokes on the actual tools/call. So it's not a config issue — it's the server failing at execution time. Probably a token scope thing or a rate limit.

$ mcptools record --server github -o github_timeout.json
# reproduce the timeout, Ctrl+C

Session saved: github_timeout.json
8 messages recorded

Attached the JSON to the issue. The maintainer gets the full trace — every message, every response, exact timing. Turned out to be a missing repo scope on the token. Five-minute fix once you can actually see what's happening.

The pattern is always the same: doctor to check if it starts, inspect to see what's exposed, proxy to watch the traffic. Between those three I've never hit an MCP issue I couldn't diagnose.

Under the hood

MCP uses JSON-RPC 2.0 over stdio. mcptools speaks the same protocol. inspect starts a server subprocess, runs the handshake, queries capabilities, formats the result. proxy reads from stdin (your IDE), forwards to the server, reads responses, forwards back. Your IDE has no idea it's there — just a pipe in the middle that logs everything passing through.

src/mcptools/
├── cli.py              # Click CLI — 6 commands
├── jsonrpc.py          # Shared JSON-RPC helpers
├── handshake.py        # MCP init handshake & error helpers
├── config/parser.py    # Auto-detects IDE configs & server selection
├── doctor/checks.py    # Concurrent health checks
├── inspect/
│   ├── server.py       # Server introspection
│   └── caller.py       # Direct tool invocation
├── proxy/
│   ├── interceptor.py  # Traffic interception
│   └── transport.py    # Stdio transport layer
├── record/
│   ├── recorder.py     # Session capture
│   └── replayer.py     # Session replay
└── tui/dashboard.py    # Real-time TUI

About 2,500 lines of Python. Textual for the TUI, Rich for tables, Click for CLI, Pydantic for validation. Nothing exotic.

Go try it

$ pip install git+https://github.com/jannik-cas/mcptools.git
$ mcptools doctor

Either everything's green and you stop worrying, or you find the thing that's been silently wasting your time.

Star mcptools on GitHub