用一个小项目,学会开发 MCP

0 阅读3分钟

想自己写 MCP 的话,可以先看这个小项目

我用一个小功能来演示怎么开发 MCP。

在 AI 工具里说「在地图里搜索西湖景区」,这个服务会打开 macOS 自带的「地图」,并发起搜索。

功能就这一个,下面看它是怎么工作的。

模型和 MCP 的关系

模型负责听懂你的话,再决定调哪个工具。工具登记在 MCP 服务里,模型只根据名称、说明和参数来选。MCP 是两者之间的协议:AI 工具启动服务,把这次选择发过去;服务执行完,把结果交还模型。

MCP 是怎么工作的

MCP 服务内部就做这几件事。它和 AI 工具用标准输入、标准输出通信,这叫 stdio。标准输出只放协议消息,日志写到标准错误。服务先登记工具,写上名称、说明和参数。调用到达后,按参数执行,再把结果送回去。

这个仓库叫 local-maps-mcp。要看的就是三个文件。

  • package.json 是项目说明书。bin 和 npm start 都指向入口 src/index.js。
  • src/index.js 用官方 SDK @modelcontextprotocol/server 创建服务,用 Zod 描述参数,登记唯一的工具 search_map。参数 query 是必填字符串,说明里写着:用户要求在地图里搜索、查找或打开某个地点时使用。
  • src/maps.js 只做地图这一段:检查参数、拼出链接、调用系统的 open。协议消息它不碰。

过程分析

  1. 平时由 AI 工具启动 MCP 服务里的 src/index.js。
  2. 调用到达后,index.js 不做地图的事,只把 query 交给 maps.js 里的 openMapSearch()。
  3. maps.js 把地点拼成一条 Apple 地图链接,搜索词放在 q 参数里,例如 https://maps.apple.com/?q=西湖景区。再用系统命令 open 打开「地图」,不需要申请地图开放平台的 API Key
/usr/bin/open -b com.apple.Maps <地图链接>

-b com.apple.Maps 指定由系统「地图」打开这个链接。open 返回,表示请求已经交给系统。

怎么配置 MCP 服务

AI 工具要有一份配置,写明服务名、用来启动的 Node.js,以及入口 src/index.js。

因为我使用的 AI 工具是 Cursor,所以这里以 Cursor 为例。

Cursor 会合并项目级和用户级配置,同名时以项目级为准。

项目级放在 .cursor/mcp.json,打开这个项目才生效。

用户级放在 ~/.cursor/mcp.json,这台电脑上的项目都能用。

写进用户级时,保留文件里原有的其他服务。

{
  "mcpServers": {
    "local-maps-mcp": {
      "command": "node",
      "args": ["${workspaceFolder}/src/index.js"]
    }
  }
}

在 Agent 对话里输入:

在地图里搜索西湖景区

模型会调用 search_map,参数是 { "query": "西湖景区" }。系统地图来到前台并接收这次搜索,对话里出现:

已向系统地图发送搜索请求:西湖景区

怎么发布 MCP 服务

希望别人也能用,就发到 npm。登录和发布都指定官方源。登录时 npm 会要动态验证码,发布时可能再要一次。

// 在命令行里进入到项目目录
npm login --registry https://registry.npmjs.org
npm publish --registry https://registry.npmjs.org

看到 + local-maps-mcp@1.0.0,就是发布成功。

别人装的是发布后的包,入口不再是仓库里的源文件。配置可以写成:

{
  "mcpServers": {
    "local-maps-mcp": {
      "command": "npx",
      "args": ["-y", "local-maps-mcp"]
    }
  }
}

也可以全局安装,再把启动命令写成 local-maps-mcp:

npm install -g local-maps-mcp --registry https://registry.npmjs.org
command -v local-maps-mcp

command -v 打印出可执行文件路径,就表示当前终端找得到它。

下面是完整代码,这个服务的仓库:

仓库:github.com/liyufengrex…