My agent kept calling the wrong tool. The LLM was correct — I could see it reasoning about which tool to use, and its logic was sound. The MCP server was correct — I had tested the tool function in isolation and it worked. But something between the two was wrong, and I had absolutely no way to see what it was.

No logs. No request trace. No response body. Just a failed agent run and a vague error message that told me nothing about which layer had broken. If this had been a REST API, I would have had Postman, Swagger, middleware logging, and an OpenTelemetry trace within the hour. With an MCP server, I was effectively debugging blind.

This is the problem that most engineers hit within their first week of building with the Model Context Protocol. MCP is a powerful standard — it gives AI agents a clean, structured way to interact with tools, data sources, and services. But the debugging story has been, until recently, far behind what we expect from modern backend infrastructure.

That is changing. This article is a complete walkthrough of every tool and approach that exists today for debugging MCP servers — from your first local test all the way to production observability.

Why MCP Servers Are Harder to Debug Than REST APIs

To understand the debugging gap, you need to understand what MCP actually does under the hood. When your AI agent talks to an MCP server, it is exchanging JSON-RPC messages — typically over stdio (standard input/output) or HTTP/SSE. This is invisible by default. Unlike an HTTP request that you can intercept with a proxy or inspect in your browser's network tab, stdio communication happens between processes, in memory, with no built-in way to observe it.

For example, when Claude calls a tool on your MCP server, the actual exchange looks like this at the protocol level:

// What Claude sends to your MCP server (JSON-RPC over stdio)
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_service_status",
    "arguments": { "service_name": "auth-service" }
  }
}

// What your server responds
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [{ "type": "text", "text": "auth-service is healthy" }]
  }
}

When this works, you never see it. When it breaks, you also never see it — unless you have deliberately set up the tooling to intercept and surface these messages. That is the debugging gap.

Capability REST API debugging MCP server debugging (today)
Interactive test client Postman / Insomnia MCP Inspector, MCPJam
Schema browser Swagger / OpenAPI UI MCP Inspector tool list
Terminal quick-test curl MCP Inspector CLI (limited)
Request/response logging Middleware (Express, FastAPI) FastMCP logging, custom middleware
Distributed tracing OpenTelemetry (mature) FastMCP OTel, early stage
Observe real client traffic Proxy / network tab Requires proxy layer (mcps-logger)
CI contract testing Newman / Dredd / Pact Bellwether, MCPSpec (early)
Schema drift detection openapi-diff, Optic Bellwether (growing)

The gap is real, but it is closing faster than you might think. Let us walk through every tool that exists today, what it actually does, and when to reach for it.

· · ·

The Tools That Exist Today

1. MCP Inspector — Your Postman for MCP

🔍
MCP Inspector
Official tool from Anthropic — browser-based interactive testing and debugging
The closest thing MCP has to Postman. Opens a web UI where you can browse all tools your server exposes, call them with custom parameters, see raw JSON-RPC request and response payloads, and watch the full message exchange in real time. Supports stdio, SSE, and Streamable HTTP transports.
Official Browser UI No install CLI mode CI-friendly

Getting started is one command — no installation needed:

# Launch against a Node.js MCP server
npx @modelcontextprotocol/inspector node my-mcp-server.js

# Launch against a Python MCP server
npx @modelcontextprotocol/inspector python server.py

# Launch against a running HTTP server
npx @modelcontextprotocol/inspector --url http://localhost:3000/mcp

# Custom ports if 6274/6277 are in use
PORT=8080 PROXY_PORT=8081 npx @modelcontextprotocol/inspector node server.js

The UI runs at http://localhost:6274. The proxy that connects to your server runs at port 6277. You will immediately see your server's full capability list — every tool, its input schema, its description, and whether it is responding correctly to tools/list.

CLI mode for CI pipelines: This is the part most people miss. MCP Inspector also has a headless CLI mode that you can use in automated pipelines without a browser:

# List all tools (CI-friendly, returns JSON)
npx @modelcontextprotocol/inspector --cli node server.js \
  --method tools/list

# Call a specific tool and assert on the response
npx @modelcontextprotocol/inspector --cli node server.js \
  --method tools/call \
  --tool-name get_service_status \
  --tool-arg service_name=auth-service
⚠️ Security Warning — Update Immediately CVE-2025-49596 is a critical remote code execution vulnerability in older versions of MCP Inspector. A malicious MCP server can execute arbitrary code on the machine running Inspector. Always run npx @modelcontextprotocol/inspector@latest to ensure you are on the patched version. If you are testing untrusted third-party MCP servers, run Inspector inside a container or VM.

One important limitation to understand: MCP Inspector is not a traffic proxy. It acts as its own client — it shows you Inspector-to-server traffic, not Claude-to-server traffic or Cursor-to-server traffic. If your agent behaves differently than the Inspector does, you are seeing a different code path. To observe real client traffic, you need a proxy layer — more on that below.

2. MCPJam Inspector — When You Need an LLM Playground

🎮
MCPJam Inspector
Community tool with LLM playground + full conformance testing CLI
Extends the official Inspector concept with an actual LLM chat interface inside the testing UI — so you can simulate how an agent would select and call your tools in a real multi-turn conversation, not just call them manually one by one. The CLI is particularly powerful with mcpjam server doctor for one-shot health checks and OAuth validation.
LLM playground Conformance testing OAuth support CLI
# Install MCPJam CLI
npm i -g @mcpjam/cli

# One-shot health check — connectivity, auth, tool sweep
mcpjam server doctor --url https://your-mcp-server.com/mcp

# List tools
mcpjam tools list --url https://your-mcp-server.com/mcp

# Call a specific tool with a JSON params file
mcpjam tools call \
  --url https://your-mcp-server.com/mcp \
  --tool-name get_service_status \
  --tool-args @params.json \
  --format json

# Run protocol conformance validation
mcpjam server conformance --url https://your-mcp-server.com/mcp

The conformance command is particularly valuable before going to production — it validates your server against the MCP protocol specification across all protocol domains, including tool discovery, resource management, and session lifecycle. Think of it as an automated contract test that verifies your server is actually spec-compliant, not just "works on my machine."

Choose MCP Inspector for protocol-level debugging and CI pipelines. Choose MCPJam when you want to simulate how an LLM agent will actually behave — tool selection, multi-turn conversation flow, and edge cases in how your tools get called in context.

3. FastMCP — The Framework That Brings Observability In

FastMCP
Python-first MCP framework with built-in OpenTelemetry and structured logging
FastMCP is to MCP what FastAPI is to REST APIs. You define tools with Python decorators, the framework handles all JSON-RPC protocol plumbing, and it ships with built-in OpenTelemetry instrumentation — so you get distributed traces, spans, and metrics out of the box without setting up your own observability stack from scratch.
Python OpenTelemetry built-in Structured logging FastAPI-like DX
# Install FastMCP
pip install fastmcp

# A FastMCP server with built-in observability
import logging
from fastmcp import FastMCP, Context

logger = logging.getLogger(__name__)
mcp = FastMCP("platform-tools-server")

@mcp.tool()
async def get_service_status(service_name: str, ctx: Context) -> str:
    """Check the operational status of a platform service"""
    logger.info(
        "Tool called: get_service_status",
        extra={"service": service_name, "trace_id": ctx.request_id}
    )
    try:
        result = check_service(service_name)
        logger.info("Tool succeeded", extra={"service": service_name, "status": result})
        return result
    except Exception as e:
        logger.error("Tool failed", extra={"service": service_name}, exc_info=True)
        raise

The key observability insight with FastMCP is correlation. Every tool invocation has a request_id available via the context object. Log that ID on every line and you can trace the full lifecycle of a single agent call through your logs — which tool was called, with what parameters, how long it took, whether it succeeded, and what it returned. Without this correlation, your logs are a flat list of events with no way to connect them.

For example, when you add OpenTelemetry spans to your FastMCP tools, you can see in Jaeger or Grafana Tempo exactly where time is being spent: is the latency in the MCP protocol layer, in your business logic, or in the downstream API call? That distinction is impossible to make without distributed tracing.

4. OpenTelemetry — Production-Grade Observability

📡
OpenTelemetry for MCP
Vendor-neutral traces, metrics, and logs for production MCP server infrastructure
Once your MCP server moves beyond local development, you need production observability — not just logs, but distributed traces that show you the full call chain from agent decision to tool execution to downstream API response. OpenTelemetry's GenAI semantic conventions now include specific attributes for MCP tool invocations, making this a first-class citizen in your observability stack.
Traces Metrics Logs Jaeger / Tempo / Datadog GenAI conventions
// Node.js MCP server with OpenTelemetry (SDK setup)
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";

const sdk = new NodeSDK({
  resource: resourceFromAttributes({
    "service.name":       "platform-tools-mcp-server",
    "mcp.server.name":    "platform-tools",
    "deployment.env":     process.env.ENV,
  }),
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT + "/v1/traces",
  }),
});
sdk.start();

// Instrument your tool calls with spans
import { trace } from "@opentelemetry/api";
const tracer = trace.getTracer("mcp-tools");

async function getServiceStatus(serviceName: string) {
  return tracer.startActiveSpan("tool.get_service_status", async (span) => {
    span.setAttributes({
      "mcp.tool.name": "get_service_status",
      "mcp.tool.input.service_name": serviceName,
    });
    try {
      const result = await checkService(serviceName);
      span.setStatus({ code: SpanStatusCode.OK });
      return result;
    } catch (err) {
      span.recordException(err);
      span.setStatus({ code: SpanStatusCode.ERROR });
      throw err;
    } finally {
      span.end();
    }
  });
}
💡 Critical insight on trace correlation Always correlate trace IDs between your MCP server logs and whatever LLM client or agent framework you are using. Without this, you will "see" traces but still struggle to connect a specific failed agent run to the exact tool execution that caused it. Log the trace ID on every line of your tool handler — it is the thread that connects everything together at 3am during an incident.

The key metrics to instrument on every MCP server in production:

5. mcps-logger / Proxy Approach — Seeing Real Agent Traffic

🔀
mcps-logger / Traffic Proxy
Intercept and log real Claude-to-server or Cursor-to-server MCP traffic
MCP Inspector can only show you Inspector-to-server traffic — not what Claude or your agent actually sends. To see real production-like traffic from an LLM client to your server, you need a proxy layer that sits in the actual data path. Tools like mcps-logger and emceepee intercept the stdio stream between your agent client and your MCP server, logging every JSON-RPC message without modifying them.
Real traffic stdio intercept Claude-to-server Non-invasive

This matters more than it sounds. In practice, an LLM client calls your tools differently than you do manually in Inspector. The LLM may call tools in a different sequence, pass different argument shapes, or trigger edge cases in your tool schema that you never thought to test manually. A proxy logger surfaces exactly what is happening in the real call path.

· · ·

CI/CD for MCP Servers — The Part Most Teams Skip

Manual testing is how you build confidence in development. Automated testing in CI is how you stop regressions from shipping to production on a Friday afternoon. The MCP tooling for CI is early but growing fast.

Schema drift is the most underrated failure mode. As one engineering team found out: "So many MCP servers silently change tool signatures between versions, and you never know if something broke until a user complains." Your tools are a contract with every agent that calls them. If you rename a parameter, change a return type, or remove a tool — anything that depends on your MCP server breaks silently.

Here is the three-gate testing approach that is emerging as a best practice for MCP servers in CI:

1
Protocol compliance check
Use MCP Inspector CLI or MCPJam conformance to verify your server correctly implements the MCP spec — initialize handshake, tools/list response format, error handling. This catches protocol-level bugs before anything else.
2
Schema snapshot test
Take a snapshot of your server's complete tool schema — every tool name, parameter name, type, and description. On every PR, compare against the previous snapshot. Any change to the public schema fails the build with a clear diff. Bellwether automates this. A simpler approach: write a test that calls tools/list and asserts on the exact JSON output.
3
Tool call assertions
Call each critical tool with known inputs and assert on the response structure, not just the status code. Use MCP Inspector CLI, mcp-test, or MCPSpec for this. The key: test the response contract, not the business logic. Business logic belongs in unit tests. Here you are testing that the MCP layer correctly wraps and returns your tool's output.
#!/bin/bash
# Example CI script using MCP Inspector CLI

# Gate 1: Protocol compliance
npx @modelcontextprotocol/inspector@latest --cli node server.js \
  --method tools/list > tools_output.json

# Gate 2: Schema snapshot comparison
if ! diff tools_output.json tools_snapshot.json; then
  echo "❌ Tool schema changed — review diff before merging"
  exit 1
fi

# Gate 3: Tool call assertions
npx @modelcontextprotocol/inspector@latest --cli node server.js \
  --method tools/call \
  --tool-name get_service_status \
  --tool-arg service_name=test-service \
  | jq -e '.result.content[0].text | length > 0' \
  || (echo "❌ Tool returned empty response" && exit 1)

echo "✅ All MCP gates passed"
· · ·

The Right Approach for Engineers Building Agents That Connect to MCP

If you are not just building an MCP server but also building an agent that consumes MCP servers — or using an existing MCP server as a tool source — the debugging approach shifts. You are no longer just validating that your server implements the protocol correctly. You are debugging why your agent is selecting or not selecting certain tools, why it is passing arguments in unexpected ways, and why a multi-step tool chain is failing partway through.

Here is the recommended debugging workflow from day one to production:

1
Local: Start with MCP Inspector
Before writing your agent, validate that every tool on the MCP server you are connecting to works correctly in isolation. Call each tool manually with edge-case inputs. Confirm the response schemas match what you expect. Never assume — always verify the contract before you build on top of it.
2
Development: Add structured logging and trace IDs from day one
Every tool call in your agent code should log the tool name, inputs (sanitised), and a trace ID that correlates to the agent run. Do not wait until debugging to add this — it is 10 lines of code upfront and saves hours when something breaks.
3
Testing: Use MCPJam for realistic agent simulation
Before deploying, run your actual use-case queries through MCPJam's LLM playground with your connected MCP servers. Watch how the LLM selects tools, what arguments it passes, and whether the response is being used correctly. This surfaces LLM-level bugs that unit tests cannot catch.
4
Pre-production: Schema snapshot + conformance in CI
Lock the schema of every MCP server your agent depends on. Any change to that schema is a potential breaking change for your agent. Fail the build when it changes, review the diff deliberately, and update your agent's expectations accordingly. This is dependency management for AI tools.
5
Production: OpenTelemetry traces on every tool call
Instrument every tool invocation with a span. Export to your existing observability backend — Jaeger, Grafana Tempo, Datadog, or whatever your platform already uses. Alert on tool error rate spikes and latency p95 regressions. Treat your MCP server like the production service it is — because it is.
🔑 The most important mental shift MCP servers are infrastructure, not scripts. The moment an AI agent in production depends on your MCP server, it is a production service that needs logging, tracing, alerting, versioning, and deployment pipelines. Treat it exactly as you would treat a microservice. The debugging tools may be younger, but the operational principles are identical.
· · ·

Going back to that agent that kept calling the wrong tool — the fix was not in the LLM and it was not in the tool function. It was in the tool description. The MCP tool schema I had written for get_service_health was ambiguous — two tools had overlapping descriptions, and the LLM was consistently picking the wrong one. MCP Inspector's schema browser made this instantly visible the moment I looked at both tool descriptions side by side. Thirty seconds of debugging with the right tool versus hours of guessing without it.

The MCP debugging ecosystem is maturing fast. Six months ago, MCP Inspector was the only real option. Today you have a layered stack — Inspector for protocol testing, MCPJam for agent simulation, FastMCP for framework-level observability, OpenTelemetry for production traces, and a growing set of CI tools for schema governance. Use each layer at the right stage of your development lifecycle.

The engineers who will build reliable AI agents are not the ones who get lucky — they are the ones who instrument first, assume nothing, and treat their MCP servers with the same operational rigour they give to the rest of their stack.

Happy coding!! If you are building MCP servers or AI agents and hitting debugging walls, drop a comment or reach me on LinkedIn — happy to dig into specific scenarios. And if you are using a tool or approach I have not covered here, I would genuinely like to hear about it.

In my next article, I will go into sandboxing AI agents — how to ensure an agent with MCP tool access cannot do more than it should, and what the current isolation primitives look like across different deployment environments. Stay connected.