How to debug the Claude Model Context Protocol?
What is the Model Context Protocol?
Anthropic announced an interesting new specification this week: the Model Context Protocol (MCP). With this new specification, developers can create an MCP server that connects to the Claude app locally on their machine.
This MCP server allows users to provide local data to Claude under their own control.
For example, using the Filesystem MCP Server, you can expose parts of your local file system to Claude and ask questions about files in that directory.
To illustrate, I configured the Filesystem MCP server to access a directory in one of my TypeScript projects and was then able to ask questions about the files there.
Pretty cool right?
time to build my own MCP server
The best way to understand a new technology is to build an example.
I went to the Your First MCP Server section of the documentation and decided to follow the tutorial to build a Weather sample MCP server in typescript.
I configured Claude to access my brand new server and…
Ouch.
I opened the “View Result from get_forecast” section and I got:
The question becomes: How to I debug that thing?
Debugging the MCP protocol
setup of a MCP server
The way you configure the connection between Claude and your server is though a json configuration file at: ~/Library/Application\ Support/Claude/claude_desktop_config.json
In that file you put the command to run your MCP server, the arguments to use when starting the server and optionally the environment variables to set before the Claude application spawns the process.
For my Weather application I put a shell script as the command to run my typescript server:
The script was :
capturing the messages
The way that the Claude interact with a MCP server is pretty simple:
- it follows the JSON-RPC 2.0 Specification
- Claude sends JSON message to the standard input of the MCP process
- Claude reads the response, and the notification from the standard output of the MCP process
In order to intercept those messages, you need to remember your Unix IPC classes from college and in particular the Named pipe aka FIFO (available on a MAC).
The script to start the server was changed to that:
As you can see the command is now piping 3 processes:
- the first process is a tee command that forks its stdin to the named pipe
/tmp/mcp.fifo
- the second process is the usual process for the MCP process
- the third process is a tee again that takes the stdout of the MCP process (though the pipe) and forks that output to the same named pipe
Using those tee
command and a fifo
IPC file, you can now see the message exchanged between Claude and the MCP server.
Before restarting Claude, you do first in your terminal:
The tail -f
command will print the data in real time being pushed to the named pipe.
Optional
Instead of starting Claude though the Finder, I started it from the command line. Why? faster to start and stop, and could see potential errors too:
The grep -v
is required to filter out this EGL error that is constantly being sent to stdout. That seems to be a classic but not a problem for Electron applications.
After a restart, I asked again about the weather in San Diego and saw that messages between my test server and the Claude application.
protocol debugging
My gut feeling was that something was wrong in the message sent by my MCP server to Claude.
I narrowed it down to that message, that show how Claude queries my server for the weather in San Diego:
The error in Claude is still: n.content.map
is not a function.
I can see that my backend is returning a content
property that is an object
and that could explain the reason the error: an object
does not have a map
method, but an array does.
To confirm my gut feeling, I added the same tooling around the Filesystem MCP server, as I know this server is working:
This confirmed that the Filesystem MCP server is returning content
as an array.
The first change in the weather app was to change the content to be an array:
Claude gave me another error.
let’s stop the guess - check the spec
The documentation site has a link to the Model Control JSON RPC specification: https://spec.modelcontextprotocol.io/specification/server/tools/#tool-result
It seems that the content type is type
, not mimeType
and that type can be either text
or image
The final change was:
And this time, that works:
and the output was:
Conclusion
The good thing about encountering a bug like this is that it forces you to dig deeper: had the sample worked out of the box, I might have stopped my exploration right there.
Instead, I had to delve into the specification and discover that the protocol is quite impressive but easy to grasp, and the debugging process wasn’t particularly complicated either.
That gave me an idea to develop a “real” MCP server for a real problem I have daily.
Note: I suspect that most LLM/AI developers are using Python, which might explain why there is less scrutiny on the TypeScript code. I took a look at the Python samples, and the bug is not present there.
Updates
- I submitted a PR to the Anthropic doc repo for that issue: https://github.com/modelcontextprotocol/docs/pull/51
- and a few hours later, the PR was merged. That was fast.
Feel like leaving a comment?
The easiest way is to use bluesky: https://bsky.app/profile/pcarion.com/post/3lbvnqeugpk22