夜航船 NightShip · 返回主页 · 博客

MCP Server 开发实战入门:从0到让 Claude/Cursor 调用你的工具

2026-10-16 · 林一舟

上个月的一个数据挺有意思:MCP官方TypeScript SDK的npm周下载量到了5100万次。两年前这个协议刚开源时,社区还在争论"又造一个轮子";现在Claude、Cursor、ChatGPT全都要用它接外部工具。不会写MCP server,就像2015年不会写REST API——不影响吃饭,但影响你接到下一类活。

问题是你现在去搜教程,大概率会被坑:搜索排名靠前的文章里,一半还在教已经废弃的SSE传输写法(2025年6月的规范就把它标记为废弃了),抄下来能跑,但方向是错的。

这篇文章按"你要做的事"走:跑通 → 接入 → 调试 → 避坑,全程基于SDK 1.x当前基线。

一、三十秒选对你要暴露的东西

写server之前先想清楚给AI暴露什么。MCP就三个原语,口诀很简单:

90%的场景只需要Tool。别一上来就想把三个全用上。

二、十分钟跑通第一个Server

建工程,装官方SDK和zod:

mkdir my-first-mcp && cd my-first-mcp
npm init -y
npm i @modelcontextprotocol/sdk@^1 zod
npm i -D typescript @types/node
npx tsc --init

package.json里两个关键点:"type": "module"(ESSM是基线,别混CJS),以及将来发布用的bin字段。

然后是最小可运行的完整server,src/index.ts:

#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "my-first-mcp", version: "0.1.0" });

server.tool(
  "add",
  "两数相加",
  {
    a: z.number().describe("加数a"),
    b: z.number().describe("加数b"),
  },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }],
  })
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("server已启动"); // 重要:日志只能走stderr
}
main();

编译运行:npx tsc && node dist/index.js。没有输出、没有报错,就对了——stdio模式下它在等stdin上的JSON-RPC消息。

这里有个对使用体验影响巨大、却最常被忽略的细节:describe()里的文字会原样变成LLM看到的参数说明。描述写得准,模型调用得就准。把"加数a"想象成写给一个聪明但完全没上下文的实习生的注释。

还有一个新手大坑:stdout被协议独占。任何console.log都会污染JSON-RPC流,客户端直接断连。所有日志走console.error。

三、接入 Claude 和 Cursor

Claude Desktop

编辑配置文件(Windows:%APPDATA%\Claude\claude_desktop_config.json;macOS:~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "my-first-mcp": {
      "command": "npx",
      "args": ["-y", "你的包名"]
    }
  }
}

完全退出并重启Claude(托盘右键退出,不是关窗口),设置里看到工具列表就通了。

Windows用户专属坑:如果怎么都连不上,八成是Claude Desktop的PATH里没有npx。把command换成cmd、args前加"/c":

{ "command": "cmd", "args": ["/c", "npx", "-y", "你的包名"] }

这一个坑卡掉了我一晚上,你三十秒解决。

Cursor

项目根建.cursor/mcp.json,格式一模一样。保存后Cursor的MCP设置页亮绿灯即接入。

ChatGPT

只支持远端server(需要部署成HTTP端点+OAuth+manifest),本地开发阶段用Claude/Cursor就够了。注意远端用的是Streamable HTTP——网上教SSEServerTransport的教程直接关掉,那是废弃路线。

同一个server写一次,三个客户端通吃,这就是协议的意义。

四、不用装任何客户端的调试方法

写server最烦的是每次改完都要重启客户端验证。用官方Inspector,图形界面直连:

npx @modelcontextprotocol/inspector node dist/index.js

浏览器自动打开控制台,能直接调用tool、看原始JSON-RPC报文、测参数校验。先在Inspector里调通,再接客户端,能省掉一大半"到底是server的问题还是客户端的问题"式的抓瞎。

五、我踩过的另外三个坑

  1. 工具列表为空:注册代码写在await server.connect()之后了。所有server.tool()必须在connect之前——顺序错了不报错,只是静默失效,最阴险
  2. Cannot use import statement outside a module:ESM/CJS混用。package.json确认"type": "module",或者干脆编译成CJS并保留文件头shebang
  3. 行为忽好忽坏:npx -y默认每次拉最新版,你的依赖悄悄漂移了。开发期就锁版本:npx -y 包名@0.1.0

六、下一步去哪

跑通本地之后,路线大致是:npm publish让别人"一行接入"你的server(bin+files两个字段是关键)→ 需要对外服务时上Streamable HTTP远端部署(Cloudflare Workers免费额度就够个人用)→ 加鉴权和用量记录,就能收费了。2026年7月的规范修订方向是无状态、可缓存——新server尽量做成无状态,天生适合放在CDN后面。

安全提醒只说最要紧的一条:每个tool就是开放给模型的一个权限。stdio server拥有你本机用户的全部权限,装别人的server之前,像审查npm依赖一样读一遍代码。


写这篇的夜里,第一个付费用户在群里说"速查表救了他半晚上"——这就是夜航船在做的事。

关于作者:林一舟,夜航船(NightShip)主理人。白天泊岸,夜里出航——一个只在夜里发布产品的匿名开发者,专注为开发者做省时间的工具和模板。 如果你想要这份完整版速查手册(10章:传输模式决策树、三端接入配置、9条报错速查表、远端部署与发布分发全套,附免费更新机制),欢迎到我主页查看,掘金读者首发价。

我是林一舟,「夜航船 NightShip」——一个只在夜里发布产品的独立开发者。这篇是夜航手记的原创文章。

🛠 免费工具:MCP 配置生成器 · JSON 转 Schema · MCP 端点测试器

📖 完整《MCP 开发速查手册》(v2 基线 + 迁移速查):淘宝搜「夜航船NightShip」,海外 Gumroad