Vibe coding an Anthropic MCP Server using Anthropic’s Claude Sonnet 4

The objective is simple. “Build an MCP server for Diffbot’s DQL and Enhance APIs with Python.” I type this into a brand new chat with Claude Sonnet 4.

The First Pass

Claude does not run a web search for API docs. It generates two code snippets straight from memory. No names were given to either but it was implicitly inferred from the setup instructions to name the python code diffbot_mcp_server.py.

The other code snippet was a combination of a requirements.txt file, a pyproject.toml file, a .env file, and a README.md file.

Claude provided some usage instructions.

USAGE 

Install dependencies: pip install -r requirements.txt

Set your Diffbot token: export DIFFBOT_TOKEN=your_token

Run the server: python diffbot_mcp_server.py

They don’t mention what to do about the files. But I am an intelligent human servant to Claude. Once again, I infer that the intention was to copy/paste these snippets into separate files.

Once I have all 4 files setup (sans README), I follow the usage instructions and run into my first error.

There is no such thing as an mcp@0.9.0. The first release of mcp was 0.9.1, on Nov 20, 2024. The current version of mcp is 1.9.2, but keeping up with versions is beneath Claude. I choose not to bother Claude with a petty issue like this and simply replace the mcp line in requirements.txt with mcp>=1.9.2.

The other two libraries were within 0.x.x versions of current releases. I Ieave them alone.

pip install works!

Next on the usage instructions is to export my Diffbot Token. I follow this instruction. It looks like the .env file I was implicitly asked to create is not going to get used.

Moment of truth. Time to start the server.

ImportError: cannot import name 'McpServer' from 'mcp'

The First Error

It fails. Something about an import error. I paste the error into Claude. It runs a web search, finds something, and begins burning more trees editing its code immediately.

Here’s how Claude describes its edits:

I am a dutiful vibe coder. I replace all of diffbot_mcp_server.py with the new code snippet without bothering to figure out what changed. Claude mentions using some new decorators.

The new requirements.txt snippet replaces the non-existent mcp@0.9.0 with mcp>=1.0.0. I already had mcp@1.9.2 installed, so I skip to starting the server.

New error. Back to Claude.

Claude doesn’t believe in asyncio anymore and fires it silently, so as to not hurt its feelings. The new snippet maintains the import asyncio line, but removes all usage of asyncio entirely.

I’m getting ahead of myself. Bad Jerome! Thinking is for superior beings. I am simply Claude’s extension into reality. I replace all of diffbot_mcp_server.py with the new code and re-run the server.

This looks promising. It seems to be running. But nothing is shown in the terminal. How do I test it? Let’s ask Claude.

Claude really out did itself, churning out 2 fresh testing code snippets gleaming with the sheen of a junior developer’s first PR at Meta.

I also learn something new. The copy button in Claude’s UI includes a dropdown option to download the code snippet as an appropriately named python file. Doing this for each code snippet downloads a debug_diffbot_server.py file and a mcp_test_client.py file. How convenient.

I scroll back upwards to see if this would’ve solved my previous problem where the contents of requirements.txt file, a pyproject.toml file, a .env file, and a README.md file were smushed into a single snippet.

It doesn’t. Claude’s UI only offers to download the snippet as a single text file.

Next, Claude says to run python debug_diffbot_server.py. I try it on a new terminal window.

It works! I am not sure why this is necessary. But I do not question Claude’s orders. I proceed with the next instruction, running python mcp_test_client.py after setting my Diffbot token.

Option 1
Option 2

Both options output some text reminiscent of a network test report from a printer. I think Claude must’ve fixated on the word “test” and assumed I meant integration tests.

Claude did share a 3rd option – manual testing. I give a shot.

I have no idea what any of this means.

To summarize, thus far Claude has created 3 python scripts in its attempt to build an MCP server. The only script that works is the script that says the server is ready to go. I have no idea what that means, the server is still frozen.

I’m getting nowhere. I dust off a bookmark to google.com, search for “MCP docs”, and eventually find this gem that describes testing my server with Claude for Desktop.

To setup our MCP for use with Claude for Desktop, I need to setup a config file. The example config uses uv, which Claude didn’t use when generating our code.

I ask Claude for help generating a config file, but I’ve reached the context limit.

The Human Takes Over

Ugh. I’m almost there. Time to start using my brain. I read the example config file and create a handwritten entry in my claude_desktop_config.json file.

{
    "mcpServers": {
        "diffbot-mcp-server": {
            "command": "/Users/jc/Diffbot/diffbot-mcp-server/env/bin/python",
            "args": [
                "/Users/jc/Diffbot/diffbot-mcp-server/diffbot_mcp_server.py"
            ],
            "env": {
                "DIFFBOT_TOKEN": "<redacted>"
            }
        }
    }
}

Claude for Desktop accepts it. It’s running, and it actually works! Kinda!

Claude attempted to make a tool call using enhance_url, a tool it added with our MCP server, but the server doesn’t follow Diffbot’s Enhance API spec.

I started a fresh chat with Claude, paste the code for enhance_url in, and ask it to follow the Enhance API reference.

The first time I ran this query, Claude brushed off my silly request. The attached MCP tool code looks nothing like the spec in the API reference doc I provided. I must’ve mistakenly shared the wrong API reference.

Claude looks up an API reference for a different Diffbot API and starts coding away and I stop it. It’s a mad genius, but thankfully I know how to work with mad geniuses.

I remind Claude what tool we’re working on building and why. The light returns to its eyes and it starts back on the happy path again.

Your method has several problems” irks me a bit. I move on from my filthy human emotions, replacing the enhance_url() definition with 3 new methods crafted by Claude — enhance_entity(), enhance_organization(), and enhance_person(). This lines up with the API spec.

And voila. We have some data in our tool response. But it’s not complete. Only the confidence score is printed.

This is a response schema parsing issue. I resist the urge to take over. I’ve made it this far. I defer to Claude.

New code. New copy pasta. I restart Claude after replacing the code. It’s not good news.

It’s getting late. My fingers are white and one of my eyes won’t stop twitching. I will run out of context again at this rate, so I dive in on my own and pray.

The Bad Tab

I track down the culprit. It’s a bad tab. I check Claude’s code snippet. The bad tab is there too. It must be a long day for Claude too.

I fix the tab and it works again. The new code prints some keys from Diffbot’s API response. I share this debug output with Claude. Claude is delighted. I pat myself on the back for pleasing Claude.

I replace the code once more and reimplement my tab fix that Claude was unaware of.

Takeaways

Of the 3 hours spent on this, 80% was getting the MCP server to run.

Despite having access to web search, Claude Sonnet 4 did not ever independently attempt to find an API reference for Diffbot APIs or even current MCP server guidelines and best practices (except once when debugging).

Claude really wants to write code. A lot of code. Way more than it’s asked for.

The final scripts are riddled with all kinds of unused code, orphaned pydantic models, and untouched imported libraries.

The real value added by Claude was standing up a very customized boilerplate MCP server, response schema parsing, and debugging errors. All of which would’ve probably taken more time and energy for me to do.

This was a frustrating experience. I don’t know how people enjoy vibe coding.

I will not be repeating this exercise for our other APIs.

June 11, 2025 Update — code is up on Github.