一句话结论
搭一个 MCP Server 没你想的难:本质上就是「起一个服务 + 用 SDK 注册几个带输入描述的工具 + 选一种传输方式」。本文用 TypeScript 从零写一个天气查询 MCP Server,不到 10 分钟就能被 Claude Desktop / Cursor 等客户端调用。核心难点不在代码,而在把工具的输入 schema 写清楚——写模糊了,模型就不会用或乱用你的工具。
MCP 解决了什么问题
在 MCP(Model Context Protocol,模型上下文协议)出现前,每个 AI 应用想接数据库、API、文件系统,都要自己写一套私有适配。MCP 提出一个统一协议:你按规范暴露「工具 / 资源 / 提示词」,任何兼容 MCP 的客户端(Claude Code、Cursor、 Claude Desktop 等)都能即插即用地发现并调用。关于协议本身,先看《MCP 模型上下文协议是什么》。
MCP Server 的两种主流传输:stdio(客户端把你的服务当子进程拉起,走标准输入输出,本地最常用)和 HTTP/SSE(远程部署用)。新手先吃透 stdio,再考虑上 HTTP。
从零搭建天气 MCP Server
- 建目录并初始化:mkdir my-mcp-server && cd my-mcp-server && npm init -y
- 装依赖:npm install @modelcontextprotocol/sdk zod,开发依赖 npm install -D typescript @types/node
- 在 src/index.ts 里创建 McpServer 实例,用 registerTool 注册工具,输入用 zod 描述。
- 把工具 handler 写成真正的异步函数:这里去 wttr.in 免费接口取天气,返回文本块。
- 接 StdioServerTransport 并 connect,console.error 打日志(stdio 下千万别用 console.log,会污染 JSON-RPC)。
- 编译(npm run build)后,在客户端配置里把服务加进 mcpServers,重启客户端即可对话调用。
核心代码(TypeScript)
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: "weather-server", version: "1.0.0" });
server.registerTool(
"get_weather",
{
title: "Get Weather",
description: "Get current weather for a city",
inputSchema: { city: z.string().describe("City name") },
},
async ({ city }) => {
const res = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`);
const data = await res.json();
const c = data.current_condition[0];
return { content: [{ type: "text", text: `${city}: ${c.temp_C}°C, ${c.weatherDesc[0].value}` }] };
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP server running on stdio");
为什么 STDIO 模式下不能用 console.log?
MCP 的 stdio 传输把 JSON-RPC 消息走标准输出(stdout)。如果你在代码里 console.log,日志会和协议消息混在一起,直接破坏 JSON-RPC 帧、让服务起不来。正确做法是写 console.error(走标准错误 stderr),或用一个写 stderr/文件的日志库。这是新手踩坑第一名。
工具的 description 不是装饰。LangGraph、OpenAI Agents SDK 这类框架会把这段描述直接喂给模型来判断「要不要调这个工具」。写「Get weather data」远不如「Returns current temperature and condition for a given city」——后者让模型在合适的时候才调用,避免误用。
怎么接进客户端
编译后,在客户端配置里加上你的服务即可(以 Claude Desktop 为例):
{
"mcpServers": {
"weather": {
"command": "npx",
"args": ["tsx", "/full/path/to/my-mcp-server/server.ts"]
}
}
}
重启客户端后问「Tokyo 现在天气怎样」,它就会调用你的 get_weather 工具。想不依赖客户端先自测,可用官方 MCP Inspector:npx @modelcontextprotocol/inspector npx tsx server.ts,打开网页 UI 手动调工具、排查问题。
常见问题
MCP Server 一定要用 TypeScript 写吗?
一个 Server 能注册多个工具吗?
本地 stdio 卡住怎么办?
搭好的 MCP 怎么和 Claude Code 配合?
小结
一个 MCP Server = 实例化服务器 + 注册带 schema 的工具 + 选传输 + 挂客户端。先把天气这个最小例子跑通,再把它换成你的数据库查询、内部 API、文件检索——每个新能力都只是一次 registerTool。延伸阅读:MCP 模型上下文协议是什么。






评论 (0)