1. Why an MCP inspector is essential
Imagine debugging a frontend while being forbidden to open DevTools. That’s roughly what life without an MCP inspector looks like. The MCP protocol runs “under the hood” of ChatGPT and the Apps SDK; if you only look at the chat response and think, “Well, why doesn’t it see my tool?”, you’re essentially shooting in the dark.
Inspectors like MCP Inspector (official) or MCP Jam are special MCP clients for developers. They can:
- connect to your MCP server the same way ChatGPT does;
- perform the handshake / capabilities;
- request the list of tools/resources/prompts;
- invoke any tool manually with arbitrary arguments;
- display raw JSON messages (requests / replies / errors).
It’s basically “Postman for MCP, but with brains.” Unlike a plain REST client, the inspector knows MCP specifics: it understands tools/list, tools/call, can display argument schemas, and sometimes even supports an OAuth flow for protected servers.
Without an inspector, debugging looks like this: you launch ChatGPT, try to call the App, see “Error talking to app” or notice that a tool doesn’t get called at all, and start guessing: did the model decide not to call the tool, did your MCP fail to start, or is it a JSON error? With an inspector you can check each layer independently: first the MCP server one-on-one with the inspector, then the ChatGPT ↔ MCP link.
2. A quick tour of inspectors: MCP Inspector, Jam, and friends
In practice, you’ll most often use two kinds of inspectors for MCP.
First, there is the official MCP Inspector from the Model Context Protocol repository. It’s a web app (typically a React SPA) that you run either locally or via npx/Docker and it can connect to your MCP server over HTTP/SSE.
Second, there are MCP Jam-like inspectors that often add OAuth conveniences. They can read .well-known/oauth-protected-resource, extract authorization_endpoint and token_endpoint, complete a PKCE flow, and then hit the MCP server in an authorized state.
MCP Jam is built by developers on top of MCP Inspector. If MCP Inspector implements a minimal set of debugging tools, then MCP Jam implements everything a developer needs for day-to-day MCP work. Personally, I recommend using MCP Jam right away so you don’t have to relearn later.
For our course, the difference is:
- the basic Inspector is always useful, even for the simplest unprotected MCP server;
- MCP Jam (or similar) becomes useful once you reach the modules on authentication and authorization.
But the core idea is the same: it’s a regular MCP client that simply shows, nicely, what ChatGPT does “silently.”
3. Typical workflow with an MCP inspector
Let’s walk through a typical scenario: you wrote a new tool in your MCP server and want to make sure it actually works.
In the previous lecture you already spun up a minimal MCP server. Now let’s add a systematic approach: run the full cycle “server → inspector → JSON logic” step by step.
Step 1 — start the MCP server
You already did this in the previous lecture: suppose you have a script npm run mcp-dev:
# example of starting an MCP server
npm run mcp-dev
# under the hood something like: ts-node src/mcp-server.ts
It’s important that the server listens on your chosen transport: in this course it’s usually an HTTP endpoint at /mcp on some port, for example http://localhost:4001/mcp.
Step 2 — start MCP Jam
Second terminal:
# one way to start MCP Jam
npx @mcpjam/inspector@latest
# you can add --port 4002 etc. if needed
After this, the inspector opens in the browser, most often at http://localhost:6274 or a similar port.
On the MCP Jam start screen, you’ll be asked to specify the MCP server URL. You enter:
http://localhost:4001/mcp
or your tunneled URL if you’re already running everything through ngrok.
Step 3 — handshake / capabilities
As soon as MCP Jam connects, it automatically does what ChatGPT does:
- Sends an initialization request (initialize) with client data.
- Receives a response with the protocol version and your server’s capabilities.
- Based on capabilities, determines whether the server supports tools, resources, prompts, and other features.
In the UI this is usually displayed as something like:
Connected
Protocol: mcp/2025-06-18
Capabilities:
- tools: list, call
- resources: list, read
- prompts: list, get
If at this step the inspector can’t connect (connection refused, CORS, 500, etc.), you immediately see the error and understand: the problem is definitely not in the model or ChatGPT, but in your server side or the network.
Step 4 — discovery: looking at tools/resources/prompts
After a successful handshake, the inspector typically calls methods like tools/list, resources/list, prompts/list to populate the sidebar. You’ll see:
- a list of tools with descriptions and JSON Schema for input arguments;
- a list of resources grouped by collections/paths;
- a list of prompts with brief descriptions.
If you just added a new tool but it doesn’t appear in the list, then it’s incorrectly registered on the server or the server didn’t start with the updated code. It’s much easier to spot here than to guess why ChatGPT “doesn’t want” to call your tool.
4. Manually calling tools via MCP Jam
The most useful MCP Jam feature is manually invoking tools. It’s your personal UI for tools/call.
Choose a tool and fill in the arguments
Suppose in the previous module you wrote a tool suggest_gifts:
// somewhere in src/mcp/tools/suggestGifts.ts
export const suggestGiftsTool = {
name: "suggest_gifts",
description: "Picks gift ideas based on age, budget, and interests",
inputSchema: {
type: "object",
properties: {
age: { type: "number" },
budget: { type: "number" },
interests: {
type: "array",
items: { type: "string" }
}
},
required: ["age", "budget"]
},
// handler is defined separately
};
In MCP Jam you click suggest_gifts. On the right a form opens, generated from inputSchema. There you fill in something like:
{
"age": 30,
"budget": 100,
"interests": ["games", "books"]
}
and click “Call” or a similar button.
The inspector sends the MCP request tools/call, and you immediately see:
- the raw JSON of the request (what exactly is sent to the server);
- the raw JSON of the response (result or error);
- possibly a convenient preview format of the result.
Reading JSON logs in the inspector
The inspector usually shows something like:
// Request
{
"id": "1",
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "suggest_gifts",
"arguments": {
"age": 30,
"budget": 100,
"interests": ["games", "books"]
}
}
}
// Reply
{
"id": "1",
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "1) Board game ... 2) Gift card to a bookstore ..."
}
]
}
}
If your handler crashes with an exception, you’ll see an error in JSON-RPC style:
{
"id": "1",
"jsonrpc": "2.0",
"error": {
"code": -32603,
"message": "Internal error",
"data": "TypeError: Cannot read properties of undefined ..."
}
}
This is crucially where you see the protocol level. If the response isn’t in the format that Apps SDK/ChatGPT expects, you can catch it here before you start blaming “GPT bugs.”
5. Debugging resources and prompts
Tools aren’t everything MCP can do. You already know there are also resources and prompts.
With the inspector you can:
- open the list of resources (resources/list) and inspect their metadata;
- read a specific resource (resources/read) and ensure the returned data is correct;
- perform a search across resources (if you implemented such a feature);
- view prepared prompts and their text.
For example, if you have a resource gift_catalog:
// resource registration pseudocode
registerResource({
uri: "resource://giftgenius/catalog",
name: "Gift catalog",
mimeType: "application/json",
handler: async () => {
return JSON.stringify(giftCatalogData);
}
});
In the inspector you’ll see this resource, click it, and immediately view the JSON. If it turns out the JSON is invalid or the MIME type is odd, you’ll catch that before ChatGPT starts stumbling trying to read it or render it in a widget.
6. MCP server logs: what, where, and how to log
MCP Jam is great, but it’s not enough: you need the MCP server’s own logs. Without them any production turns into a lottery.
What to log
The useful minimum:
- every incoming MCP message (request/notification) with:
- timestamp;
- method (tools/call, tools/list, etc.);
- tool name (if any);
- truncated arguments (without sensitive data);
- every outgoing response:
- status (success / error);
- execution time;
- a trimmed version of the result or at least the type;
- technical errors:
- JSON parsing;
- unexpected exceptions in handlers.
At the same time, it’s very important not to log PII and secrets in full: tokens, passwords, full texts of confidential requests. Production logging recommendations usually explicitly advise logging data with truncated PII.
Where to write logs: stdout / stderr
MCP has an important requirement: JSON messages must go through the “correct channel”, and all debug logs through another. For example, if you use a transport over stdout/stderr, then:
- JSON-RPC messages must go to stdout;
- all console.log, console.error, etc. must go to stderr.
If you mix JSON and text logs in the same stream, the client (MCP Jam or ChatGPT) simply can’t parse the messages because in the middle of JSON there will suddenly be a line like Server started at http://localhost:4001. This is one of the common mistakes in MCP servers.
In the HTTP scenario, the problem is simpler, but the principle is the same: the HTTP response must contain clean JSON, and all logs go to the console/file, not into the response body.
A simple logger for a TypeScript MCP server
Let’s add a small logger to our hypothetical MCP server:
// src/logger.ts
export function logRequest(method: string, details: unknown) {
console.error(
JSON.stringify({
level: "info",
type: "request",
method,
details,
ts: new Date().toISOString(),
})
);
}
export function logError(method: string, error: unknown) {
console.error(
JSON.stringify({
level: "error",
type: "error",
method,
error: String(error),
ts: new Date().toISOString(),
})
);
}
And in the tools handler:
// src/mcp-server.ts (snippet)
server.setRequestHandler("tools/call", async (req) => {
logRequest("tools/call", {
name: req.params?.name,
// it's better not to log the whole payload here, only safe fields
});
try {
const result = await handleToolCall(req);
return result;
} catch (e) {
logError("tools/call", e);
throw e;
}
});
This way you’ll see structured JSON logs in the console, which you can then easily correlate by ts or an additional requestId.
7. The combo: MCP Jam + logs
The right MCP debugging strategy almost always looks like this:
- You reproduce the problem in the inspector: you see that tools/list returns an empty list, tools/call fails, the JSON response looks odd, etc.
- At the same time you look at the MCP server logs: what it prints on startup, what errors it outputs for each message, whether there’s a stack trace.
- Correlate id, method, ts in the logs with what the inspector shows.
For example, you see in the inspector:
{
"error": {
"code": -32603,
"message": "Internal error"
}
}
And in the logs at the same time:
{
"level": "error",
"type": "error",
"method": "tools/call",
"error": "TypeError: Cannot read properties of undefined (reading 'age')",
"ts": "2025-11-21T10:15:12.345Z"
}
That’s it, diagnosis is clear: somewhere in the handler you expect age, but the schema/arguments differ.
8. Mini‑checklist “is the MCP server ready to integrate with the App”
Before wiring your MCP server to a real ChatGPT App, it’s convenient to run through a short checklist with the inspector.
First, the handshake and capabilities must succeed without errors. MCP Jam should show that the server supports the entities you need: at least tools and, if used, resources / prompts.
Second, the list of tools/resources/prompts in the inspector should match the set of tools, resources, and prompts you believe you’ve implemented. Typos in a name, forgotten registrations, etc. are caught here instantly.
Third, tool calls with valid arguments should consistently return a correct result. Ideally try several typical cases (requests you actually expect in production).
Fourth, calls with invalid arguments should return clear error responses in JSON-RPC style, not crash with a 500. For example, if a required parameter is missing, it’s better to return a structured error that ChatGPT can then turn into a user-friendly message.
Fifth, the server logs should not flood the console with gigabytes of stack traces for every minor hiccup. Errors should be structured, and sensitive data carefully filtered out.
If all of this passes in the inspector, you can connect the MCP server to the Apps SDK and play with widgets in Dev Mode with much more confidence.
9. Common MCP server bugs and how to catch them via the inspector
Now let’s go through the fun part — what most often breaks and how to spot it.
Configuration and connectivity
Sometimes it seems that “the server isn’t working,” but the problem is that it doesn’t listen on the required port or endpoint at all. The inspector will honestly say connection refused or simply fail to connect. Common causes: incorrect URL (for example, /mcp instead of /api/mcp), the port is occupied by another process, the tunnel isn’t up, or CORS blocks requests.
Invalid JSON / mixing logs and protocol
One of the most painful stories is when you print console.log("Server started") to stdout while JSON-RPC messages are supposed to go over it. The client expects clean JSON but receives text + JSON, tries to parse it, and fails with a format error.
The solution is simple: strictly separate what goes into the protocol stream (stdout or the HTTP response body) and what goes into logs (stderr or a separate log file).
Schema and implementation mismatch
Another popular error: your inputSchema declares one thing while the code expects another. For example, the schema says age is a number and interests is an optional array of strings, but the code tries to do arguments.interests.toLowerCase(). The model (and the inspector) dutifully send interests as null or omit the field — and everything breaks.
The inspector lets you clearly see what JSON is actually sent in tools/call and match that to your code.
Incorrect tool/resource names
If in capabilities / tools/list you export the tool as suggest_gifts_v2, but in the Apps manifest or the widget you expect suggest_gifts, “tool not found” will haunt you to the end of the project. In the inspector, the list of tools and their name fields makes this obvious at a glance, without trying to guess what GPT thinks.
Slow or hanging tools
If a tool call in the inspector takes 30 seconds and then times out, don’t expect ChatGPT to react any better. An MCP inspector helps you understand exactly where you’re slow: network call, DB, external API. It’s helpful to log the start and end time of each request to immediately see outliers.
10. Common mistakes when inspecting and debugging MCP
Mistake #1: trying to debug MCP only via ChatGPT.
Many developers first connect MCP to an App, see that “something doesn’t work,” and start tweaking prompts, tool descriptions, and even the model version. Meanwhile, the MCP server doesn’t start at all or tools/list is empty. Always start with the inspector: if things are broken there, the model is not the culprit.
Mistake #2: mixing JSON-RPC and logs in the same stream.
When an MCP client expects clean JSON and you print debug strings to stdout, the result is predictable — parsing breaks, the Inspector shows strange errors. Logs must go separately (stderr, files, external logging systems), and protocol messages strictly in their own channel.
Mistake #3: ignoring capabilities and the tool list.
Often a tool “disappears” simply because you forgot to register it or enable the corresponding capability. If you don’t look at capabilities and tools/list in the inspector, you may spend a long time thinking the model is to blame, not your registration code.
Mistake #4: ignoring schema errors and JSON mismatches.
When inputSchema and the actual JSON diverge, the model and the inspector predictably start behaving strangely. If you don’t look at raw JSON messages in the inspector and don’t validate the schema, these errors will surface in the most unexpected places.
Mistake #5: logging everything, including PII and tokens.
In the heat of debugging, it’s easy to print the full request body to logs, including potential personal data or secrets. In production this becomes a time bomb: leaks, compliance issues, etc. Log only what’s truly needed for diagnosis, with truncated/anonimized data.
Mistake #6: not reproducing the problem with minimal cases.
Sometimes a bug appears in a complex ChatGPT dialog, and the developer tries to debug it as-is. It’s much more effective to reproduce the same scenario in the inspector with one or two MCP requests, eliminating the influence of prompts, dialog history, and the model’s “mood.”
GO TO FULL VERSION