← 返回资讯
苏晴
资深编辑
已审核

3个通宵、2次放弃,那些文档没写的坑

**Experience: I built an MCP server from scratch and here's what the docs don't tell you**

3个通宵、2次放弃,那些文档没写的坑

3个通宵、2次放弃,那些文档没写的坑


Experience: I built an MCP server from scratch and here's what the docs don't tell you

TIL that implementing the Model Context Protocol isn't the "weekend project" every YouTube tutorial makes it out to be. Three all-nighters, two ragequits, and one 3 AM existential crisis where I seriously considered becoming a farmer later, my MCP server finally responds without randomly dropping connections. Here's the real talk.

I've been seeing MCP pop up everywhere since Anthropic released it back in November 2024, and like many of you, I thought "cool, another protocol to add to my resume." Spoiler: it's actually useful, but the learning curve is steeper than the hello-world examples suggest. Way steeper.

What actually is MCP? (The non-marketing version)

For those who haven't fallen down this rabbit hole yet, MCP is basically a standardized way for LLMs to talk to external tools and data sources. Think of it as USB-C for AI integrations - one protocol to rule them all, instead of writing custom integrations for every single tool.

Actually, wait—I should clarify that comparison. It's more like USB-C if USB-C also handled discovery and capability negotiation. Which real USB-C kinda does now I guess? Whatever, you get the point.

"It's like if REST and WebSocket had a baby that speaks JSON-RPC" - some comment I saw on r/MachineLearning that's surprisingly accurate

The architecture is client-server. Your MCP server exposes "tools" (functions the AI can call), "resources" (data the AI can read), and "prompts" (pre-written templates). The client—usually Claude Desktop or some VS Code extension—discovers these and uses them. Simple enough on paper.

My implementation journey (the ugly parts)

I decided to build an MCP server in TypeScript. Version 0.5.0 of the SDK, specifically. Why? Because I hate myself apparently. And because all the Python examples I found were using 0.3.2 and half the APIs had changed.

The official SDK helps. It does. But here's where things got real:

1. The transport layer isn't as plug-and-play as advertised

The docs make it seem like you just pick stdio or SSE and go. Reality check: stdio works great for local development—like, genuinely great, no complaints. But if you're building anything that needs to handle multiple concurrent clients, you'll want SSE.

The problem?

Error handling in SSE is... let's call it "minimal." Charitably.

I spent 4 hours debugging why my server would randomly stop sending events. Not crashing, just... stopping. Silent. Turns out, if your SSE connection drops mid-stream, the default implementation just shrugs and walks away. No reconnection logic, no buffering, nothing. The client sits there waiting for events that ain't coming.

TYPESCRIPT
// What the tutorial shows:
const server = new McpServer({ name: 'my-server' });
server.connect(transport);

// What you actually need:
server.on('error', (err) => {
 // Hope you like debugging race conditions
 console.error('Something broke:', err);
 // Spoiler: this fires approximately never when you need it
});

I think there's a GitHub issue about this somewhere. Probably has 80+ thumbs up by now.

2. Tool definitions are deceptively simple

Defining a tool looks straightforward. Name, description, input schema. Three fields. How hard could it be?

Well.

The description field is doing heavy lifting. Like, all the lifting. The AI uses your description to decide when to call the tool, and vague descriptions lead to the AI either never calling your tool or calling it for literally everything.

Real example from my project: I built a database query tool. First description: "Query the database." Result: Claude called it to answer "what's your name?" I wish I was joking. The logs were just Claude repeatedly trying to SELECT username FROM somewhere.

Updated description: "Execute read-only SQL queries against the PostgreSQL database containing user analytics data. Use for questions about user counts, engagement metrics, or historical trends. Do NOT use for system configuration or metadata queries."

Night and day difference. Like, completely different tool behavior.

But here's the thing nobody mentions—you gotta iterate on these descriptions. I went through 7 versions before it felt right. And what works for Claude might make GPT-4o call your tool way too aggressively or not at all. There's no standard for this yet.

3. The "tools/list" discovery is cool until it's not

When your MCP server starts, the client calls tools/list to discover what's available. Makes sense. But if you have 50+ tools (I may have gone overboard... okay I definitely went overboard with 73 tools), that initial handshake gets chunky.

There's no pagination in the spec. Not yet anyway. I've seen Claude Desktop timeout on the discovery call—just spin forever then silently fail. Nothing in the logs. Took me 2 hours to figure out what was happening.

Pro tip: batch your tool registrations and consider lazy-loading tools that aren't commonly used. Yes, this defeats some of the auto-discovery magic, but your users will thank you when the connection initializes in 2 seconds instead of 15.

I'm currently running about 12 "core" tools at startup and loading the rest on demand. It's hacky. I don't love it.

What actually worked well

It's not all complaints. Once I got past the initial hurdles:

My "production-ready" checklist

After this experience, here's what I'd recommend before calling your MCP server done:

1. Implement proper logging - The protocol has a logging system built in. Use it. Future you debugging at 2 AM will want those timestamps.

2. Add rate limiting - Your tools will get called in rapid succession. Claude doesn't know about your API limits. Or care, apparently.

3. Validate inputs with Zod - The AI will send malformed JSON sometimes. Not often, but when it does, you want a proper validation layer, not a cryptic "undefined is not a function" at 3 AM. Ask me how I know.

4. Test with multiple clients - Claude Desktop, Continue.dev (v0.9.5), and custom clients all behave slightly differently. Claude is the most forgiving, honestly.

5. Monitor tool call frequency - You'll discover which tools are actually useful vs. which ones seemed like a good idea after your third coffee. Spoiler: half my tools have never been called once.

The elephant in the room

Is MCP going to be the standard, or will it get Google Wave'd?

I think—and this is just my read after living in this codebase for two weeks—the protocol is solid enough to survive. But the real test is whether major players beyond Anthropic adopt it. OpenAI has their function calling, Google has their thing, and nobody likes being told their baby is ugly.

The protocol itself is open and well-designed, which gives me hope. But I've been burned by "standards" before. Looking at you, SOAP. And GraphQL kinda-sorta-not-really delivering on the dream.

I will say, I've seen more community tools and servers pop up in the last month than I expected. There's an MCP server for Brave Search now, for GitHub, for PostgreSQL. That ecosystem matters.

TL;DR: Building an MCP server is worth the effort if you need AI-tool integration, but budget 2-3x the time you think it'll take. Maybe 4x if you're doing SSE transport. The SDK is good, the docs are okay, and the edge cases will eat your lunch. Start with stdio transport, define your tool descriptions like you're explaining to a brilliant but extremely literal-minded intern, and for the love of all that is holy, add error handling before you need it. Not after. Before.

Edit: Thanks for the gold, kind stranger! Since people are asking—yes, I'll open-source my implementation once I clean up the embarrassing parts. Give me a week. Maybe two. There's a comment block in my SSE handler that just says "// I don't know why this works but don't touch it" and I need to figure that out first.

Edit 2: Several people asked about Python vs TypeScript for MCP servers. I've used both now (Python with the 0.4.1 SDK, TypeScript with 0.5.0), and honestly? Python's async story makes the SSE transport slightly less painful, but TypeScript's type system catches schema validation bugs at compile time. Pick your poison. I went with TypeScript because I'd rather debug types at 2 PM than runtime errors at 2 AM.

Edit 3: Someone in the comments mentioned that MCP Inspector tool. If you're building an MCP server and haven't used it yet, stop reading this and go install it. npx @anthropic-ai/mcp-inspector. It'll save you so much time debugging tool calls. Can't believe I didn't find it until day 6 of this project.

What's your experience been with MCP? Anyone found a clean way to handle tool versioning, or are we all just YOLOing it with breaking changes and hoping nobody notices?

635
10590 阅读
4 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年06月27日 16:25

苏晴

资深编辑

科技媒体从业 8 年,曾就职于多家科技媒体。关注 AI 创业和投资赛道,采访过 50+ 位行业从业者。

读者评论 4

张工 3天前
写得很实在,特别是实测对比那部分,跟我自己的使用感受一致。
回复 点赞 (12)
前端工程师 6天前
代码示例很清晰,直接用到项目里了。
回复 点赞 (6)
技术小白 1周前
作为非技术人员也看懂了,感谢作者的通俗讲解。
回复 点赞 (3)
Dev小王 1周前
终于有人把这个说清楚了,收藏了。
回复 点赞 (8)