自己动手写一个MCP Server,你就知道MCP是怎么个事了!
是不是总刷到各种高大上的 MCP 解读,名词满天飞,听着高深莫测,转头就一脸茫然?概念背了不少,却压根不知道这东西能干啥、怎么用?别再云里雾里看热闹了,跟着动手写个天气服务,瞬间吃透 MCP 全貌!
一、摘要
Model Context Protocol(MCP,模型上下文协议)是 Anthropic 推出的开源标准化协议,旨在统一 AI 客户端与外部服务的通信规范,解决大模型上下文传递、工具调用、资源读取、提示词复用等场景的生态碎片化问题。当前主流本地 AI 客户端(Claude Code、Trea、Cursor 等)均已原生支持 MCP。
本文从原理 + 实战双维度切入:首先基于 Python 实现一个本地天气模拟 MCP Server,完整落地 MCP 五大核心组件(传输层、Tools 工具、Resources 资源、Prompts 提示词模板、Roots 根目录、Logging 日志);其次讲解 Claude Code、Trea 两大主流客户端的本地 MCP 服务配置流程;最后结合 Claude Code 独有 Skill 能力,演示 MCP + Skill 联动 的综合配置、业务编排与落地使用,帮助读者从零到一吃透 MCP 的设计思想与工程实践。
二、引言
2.1 什么是 MCP?
传统 AI 客户端扩展存在明显短板:不同工具的插件、外部能力、上下文交互逻辑互不兼容,开发者需要为每一款客户端单独开发适配插件,维护成本极高。
MCP 定义了一套基于 JSON-RPC 2.0 的标准化通信协议,架构分为 MCP Client(AI 客户端,如 Claude Code)和 MCP Server(外部能力服务)两层:
- Client:大模型交互入口,负责接收用户指令、调用远端 MCP 服务、整合结果返回给用户;
- Server:能力提供方,对外暴露工具、本地资源、预设提示词、目录权限等能力;
- 通信基础:统一使用 JSON-RPC 2.0 编解码,支持 Stdio、HTTP、SSE、WebSocket 多种传输方式。
2.2 MCP 核心价值
- 标准化:一套 MCP Server 可对接所有兼容 MCP 的 AI 客户端;
- 能力解耦:大模型逻辑、外部工具、本地文件资源完全拆分;
- 权限可控:通过 Roots 目录隔离,限制服务端可访问的本地路径;
- 能力丰富:不止工具调用,还支持资源读取、提示词模板、日志推送等全场景。
2.3 本文实战目标
- 手写本地天气 MCP Server,全覆盖 MCP 所有核心组件;
- 完成 Claude Code、Trea 客户端接入配置;
- 实现
MCP 能力 + Claude Code Skill联动编排,落地真实业务场景。
三、MCP 整体架构与核心组件
在动手编码前,先理清 MCP 标准定义的核心模块,这是理解后续代码的关键。
3.1 整体架构
用户指令 → MCP Client(Claude Code/Trea)
↓ JSON-RPC 2.0 + 传输层(Stdio/HTTP)
MCP Server(能力服务)
↓
返回工具结果/资源内容/日志 → Client 整合输出给用户
传输层选择:本地客户端(Claude Code/Trea)优先使用 Stdio 标准输入输出,无需端口、网络配置,通过子进程直接通信,是本地 MCP 服务的首选方案。
3.2 MCP 六大核心组件(本文重点演示)
| 组件 | 英文名称 | 核心作用 | 应用场景 |
|---|---|---|---|
| 传输层 | Transport | 定义 Client/Server 通信方式 | Stdio(本地)、HTTP/SSE(远程) |
| 工具调用 | Tools | 对外暴露可被大模型调用的外部函数 | 查天气、执行命令、数据库查询 |
| 资源读取 | Resources | 向模型提供本地 / 远程静态 / 动态资源 | 读取本地文档、配置文件、动态文本 |
| 提示词模板 | Prompts | 可复用的预设 Prompt 模板,支持参数渲染 | 固定话术、场景化提示词 |
| 根目录权限 | Roots | 声明服务端允许访问的本地根路径,做权限隔离 | 限制文件访问范围,保障本地安全 |
| 日志推送 | Logging | Server 主动向 Client 推送分级日志 | 服务调试、运行状态监控 |
本文天气 Demo 会逐个实现以上所有组件,不做简化,完整还原标准 MCP 服务形态。
四、实战:从零编写本地天气 MCP Server
4.1 环境准备
1. 基础环境
- Python 版本:
3.10+(MCP SDK 最低要求) - 操作系统:Windows /macOS/ Linux 全支持
2. 安装依赖
使用 MCP 官方 Python SDK(封装了 JSON-RPC、传输层、协议解析,无需手写底层通信):
pip install mcp
3. 项目结构
创建独立文件夹 weather-mcp-server,目录结构极简:
weather-mcp-server/
├── main.py # MCP 服务主程序(核心代码)
└── weather_doc.txt # 静态资源文件(用于演示 Resources 组件)
4.2 模拟业务数据
我们纯本地模拟天气数据(不调用第三方外网接口),保证服务离线可用。 预设城市天气字典:北京、上海、广州、深圳,包含温度、天气状况、风力。
4.3 完整 MCP Server 代码(main.py)
实现全部六大核心组件:
from mcp.server import Server
from mcp.server.stdio import StdioServerTransport
from mcp.types import (
Tool, TextContent,
Resource, ResourceTemplate,
Prompt, PromptArgument,
LoggingLevel
)
import asyncio
# ===================== 1. 初始化MCP服务与基础数据 =====================
# 创建MCP Server实例,服务名称自定义
app = Server("local-weather-mcp-server")
# 本地模拟天气数据库(离线可用,无外网依赖)
WEATHER_DATA = {
"北京": {"temp": "22℃", "status": "晴", "wind": "微风2级"},
"上海": {"temp": "25℃", "status": "多云", "wind": "东风3级"},
"广州": {"temp": "30℃", "status": "雷阵雨", "wind": "南风4级"},
"深圳": {"temp": "29℃", "status": "阴", "wind": "无风"}
}
# 静态资源文件路径(用于Resources组件演示)
DOC_FILE_PATH = "./weather_doc.txt"
# ===================== 2. 核心组件1:Roots 根目录权限声明 =====================
# 声明服务端可访问的本地根目录,客户端会校验权限
@app.list_roots()
async def list_roots():
return {
"roots": [
{
"uri": f"file://{__file__.rsplit('/', 1)[0]}",
"name": "天气服务根目录"
}
]
}
# ===================== 3. 核心组件2:Logging 分级日志推送 =====================
# 封装日志工具,向客户端推送日志(info/warn/error)
async def send_log(level: LoggingLevel, message: str):
await app.send_log_message(
level=level,
logger_name="weather-mcp",
message=message
)
# ===================== 4. 核心组件3:Tools 工具调用(核心能力) =====================
# 注册工具列表:告诉客户端当前服务提供哪些可调用工具
@app.list_tools()
async def list_tools():
return [
Tool(
name="query_weather", # 工具名称(客户端调用标识)
description="查询指定城市的实时天气,支持城市:北京、上海、广州、深圳",
inputSchema={ # 入参JSON Schema(大模型自动解析参数)
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "需要查询天气的城市名称"
}
},
"required": ["city"] # 必传参数
}
)
]
# 工具执行逻辑:客户端触发工具调用时执行
@app.call_tool()
async def call_tool(name: str, arguments: dict):
await send_log(LoggingLevel.INFO, f"收到工具调用请求:工具={name}, 参数={arguments}")
if name != "query_weather":
await send_log(LoggingLevel.ERROR, f"未知工具:{name}")
return [TextContent(type="text", text="错误:不存在该工具")]
city = arguments.get("city", "")
if city not in WEATHER_DATA:
await send_log(LoggingLevel.WARNING, f"查询未知城市:{city}")
return [TextContent(type="text", text=f"暂不支持查询【{city}】的天气,仅支持:北京、上海、广州、深圳")]
# 拼接天气结果
weather = WEATHER_DATA[city]
result = f"【{city}】今日天气:{weather['status']},气温{weather['temp']},风力{weather['wind']}"
await send_log(LoggingLevel.INFO, f"天气查询成功:{result}")
return [TextContent(type="text", text=result)]
# ===================== 5. 核心组件4:Resources 资源读取(静态+动态) =====================
# 1. 静态资源:读取本地 weather_doc.txt 文件
# 2. 动态资源:根据城市动态生成天气简介(ResourceTemplate)
@app.list_resources()
async def list_resources():
return [
# 静态文件资源
Resource(
uri=f"file://{DOC_FILE_PATH}",
name="天气服务说明文档",
description="MCP天气服务使用规则说明"
),
# 动态资源模板(支持参数渲染)
ResourceTemplate(
uriTemplate="mcp://weather/intro/{city}",
name="城市天气动态简介",
description="根据城市动态生成天气温馨提示"
)
]
# 读取资源内容
@app.read_resource()
async def read_resource(uri: str):
await send_log(LoggingLevel.INFO, f"读取资源请求:{uri}")
# 处理静态文件资源
if uri.startswith("file://"):
file_path = uri.replace("file://", "")
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
return content
except Exception as e:
return f"读取文件失败:{str(e)}"
# 处理动态资源 mcp://weather/intro/{city}
if uri.startswith("mcp://weather/intro/"):
city = uri.split("/")[-1]
if city in WEATHER_DATA:
return f"温馨提示:{city}今日{WEATHER_DATA[city]['status']},出行注意合理穿搭。"
else:
return f"暂无【{city}】的天气提示"
return "未知资源URI"
# ===================== 6. 核心组件5:Prompts 提示词模板 =====================
# 注册预设提示词模板(客户端可直接拉取使用,支持参数)
@app.list_prompts()
async def list_prompts():
return [
Prompt(
name="weather_ask_template",
description="通用天气查询话术模板",
arguments=[
PromptArgument(
name="user_question",
description="用户原始提问",
required=True
)
]
)
]
# 渲染提示词模板
@app.get_prompt()
async def get_prompt(name: str, arguments: dict | None = None):
if name == "weather_ask_template" and arguments:
user_q = arguments.get("user_question", "")
prompt_text = f"""
你是专业天气助手,请根据用户提问调用天气工具并友好回答。
用户提问:{user_q}
要求:语言简洁、通俗易懂,补充出行建议。
""".strip()
return {"description": "天气查询模板", "messages": [{"role": "user", "content": prompt_text}]}
return {"description": "无效模板", "messages": []}
# ===================== 7. 传输层:Stdio 启动服务(本地通信核心) =====================
async def main():
# 使用Stdio传输层:标准输入输出,本地客户端默认通信方式
transport = StdioServerTransport()
await app.connect(transport)
await send_log(LoggingLevel.INFO, "天气MCP Server 已启动,等待客户端连接...")
if __name__ == "__main__":
asyncio.run(main())
4.4 补充静态资源文件
在同目录创建 weather_doc.txt,写入内容(用于测试 Resources 静态文件读取):
===== 本地天气MCP服务使用说明 =====
1. 支持查询城市:北京、上海、广州、深圳
2. 调用工具:query_weather
3. 可读取资源:服务说明文档、城市动态天气提示
4. 可使用预设提示词模板:weather_ask_template
4.5 核心组件逐模块解析
结合代码,对应前文的 MCP 组件,讲清作用 + 协议逻辑:
-
Transport 传输层 代码中
StdioServerTransport()是本地 MCP 标配,服务启动后通过标准输入 / 输出和客户端通信,无需开放端口,安全性高、配置简单。 -
Roots 根目录
list_roots声明服务端仅能访问当前项目目录,客户端会做路径校验,防止 MCP 服务越权读取本地文件,是 MCP 的安全机制。 -
Logging 日志 通过
send_log_message向客户端推送分级日志(INFO/WARN/ERROR),在 Claude Code/Trea 中可直接看到服务运行日志,方便调试。 -
Tools 工具调用
list_tools:向客户端声明工具列表 + 参数规则(JSON Schema),大模型会自动识别工具能力;call_tool:客户端触发工具后执行业务逻辑,返回结构化文本结果; 这是 MCP 最常用的能力,也是大模型 “联网 / 调用外部能力” 的核心。
-
Resources 资源 分为静态资源(本地文件) 和动态资源(模板 URI),作用是为大模型补充上下文信息,比如读取配置、文档、动态生成提示文本。
-
Prompts 提示词模板 复用固定提示词,支持动态参数渲染,避免客户端重复编写 Prompt,统一交互话术。
4.6 本地单独测试服务
直接运行 main.py:
python main.py
服务会进入阻塞状态(等待客户端连接),说明 MCP Server 启动正常。
五、主流本地 AI 客户端配置 MCP Server
我们已经完成 MCP 服务开发,接下来分别配置 Claude Code 和 Trea 两大客户端,接入本地天气 MCP。
5.1 配置 Claude Code(Anthropic 官方终端 / VS Code 客户端)
Claude Code 支持全局 MCP 配置和项目局部配置,优先级:项目配置 > 全局配置。
步骤 1:找到配置文件路径
- 全局配置(所有项目生效): macOS/Linux:
~/.claude/mcp.jsonWindows:C:\Users\你的用户名\.claude\mcp.json - 项目局部配置(仅当前文件夹生效): 在
weather-mcp-server目录下创建.claude/mcp.json
若目录 / 文件不存在,手动新建即可。
步骤 2:编写 MCP 配置文件(mcp.json)
核心字段说明:
command:Python 解释器绝对路径(必须写绝对路径,防止环境变量问题);args:MCP 服务脚本的绝对路径;env:可选,环境变量配置。
配置模板(通用):
{
"mcpServers": {
"local-weather-server": {
"command": "/usr/bin/python3", # macOS/Linux 绝对路径
"args": ["/你的项目路径/weather-mcp-server/main.py"],
"env": {}
}
}
}
Windows 示例:
{
"mcpServers": {
"local-weather-server": {
"command": "D:\\Python311\\python.exe",
"args": ["D:\\weather-mcp-server\\main.py"],
"env": {}
}
}
}
步骤 3:验证接入
- 打开终端,进入任意目录,执行
claude启动 Claude Code; - 输入指令:
帮我查北京的天气; - Claude Code 会自动发现 MCP 工具,调用
query_weather并返回结果,同时终端会打印 MCP 服务推送的日志。
5.2 配置 Trea 客户端
Trea 是轻量化本地 AI 客户端,原生兼容 MCP 协议,配置方式分为图形界面配置和配置文件配置。
方式 1:图形界面配置(推荐新手)
- 打开 Trea,进入 设置 → MCP 服务;
- 点击「添加服务」,服务名称填写
weather-mcp; - 传输类型选择 Stdio;
- 执行命令:填写 Python 绝对路径;
- 启动参数:填写
main.py绝对路径; - 保存并启用服务,重启 Trea。
方式 2:配置文件配置
Trea MCP 配置文件路径:
- macOS/Linux:
~/.trea/config.json配置内容和 Claude Code 格式基本一致,填入mcpServers节点即可,保存后重启客户端生效。
验证
在 Trea 对话框输入:查询上海天气,客户端会自动调用 MCP 工具,返回结果。
六、进阶:Claude Code 中 MCP + Skill 综合配置与实战
6.1 先理解:Claude Code Skill 是什么
Skill 是 Claude Code 独有的自定义技能 / 工作流编排能力,本质是可复用的指令脚本 + 交互逻辑:
- MCP:负责底层能力(工具、资源、提示词、日志);
- Skill:负责上层业务编排(指令拦截、流程串联、话术封装、多能力组合);
二者组合可以实现:用户指令 → Skill 预处理 → 调用 MCP 工具/读取 MCP 资源/使用 MCP 模板 → 整合结果输出。
6.2 Skill 目录规范
Claude Code Skill 存放路径:
- 全局 Skill(所有项目生效):
~/.claude/skills/ - 项目局部 Skill:项目目录下
.claude/skills/
新建文件 weather_helper.md(Skill 以 Markdown 格式编写)。
6.3 编写联动 Skill(MCP + Skill 完整编排)
该 Skill 实现逻辑:
- 拦截用户「查天气」相关指令;
- 加载 MCP 预设 Prompt 模板;
- 调用 MCP 天气工具;
- 读取 MCP 静态说明文档 + 动态资源;
- 整合所有内容,输出格式化回答。
weather_helper.md 完整代码:
---
name: 天气助手
description: 联动本地MCP服务查询天气,整合资源与提示词
trigger: ["查天气", "天气怎么样", "今日天气"]
---
## 执行流程
1. 调用MCP服务的 weather_ask_template 提示词模板,优化回答话术;
2. 识别用户查询的城市,调用MCP工具 query_weather 获取天气数据;
3. 读取MCP静态资源【天气服务说明文档】;
4. 读取对应城市的MCP动态资源【天气温馨提示】;
5. 整合所有信息,分模块友好输出。
## 输出格式
### 天气查询结果
{工具返回的天气数据}
### 出行提示
{动态资源内容}
### 服务说明
{静态文档内容}
6.4 联合使用实操演示
- 确保 MCP Server 已在 Claude Code 中正常配置、Skill 文件放入对应目录;
- 终端执行
claude启动客户端; - 输入触发指令:
帮我查广州天气;
完整执行链路拆解
- Skill 拦截:检测到触发词「查天气」,启动预设工作流;
- 加载 MCP Prompt:拉取
weather_ask_template模板,规范回答风格; - 调用 MCP Tool:执行
query_weather,获取广州天气数据; - 读取 MCP Resources:
- 读取静态文件
weather_doc.txt; - 读取动态资源
mcp://weather/intro/广州;
- 读取静态文件
- 结果整合:Skill 按照预设格式拼接所有内容,输出给用户;
- 日志回显:MCP 服务推送的 INFO/WARN 日志在终端同步展示。
6.5 最佳实践总结
- 能力分层:底层通用能力(工具、资源)交给 MCP,上层业务流程、交互逻辑交给 Skill;
- 权限隔离:通过 MCP Roots 限制文件访问范围,避免安全风险;
- 复用优先:高频话术、固定规则抽离为 MCP Prompts,减少 Skill 冗余代码;
- 日志调试:利用 MCP Logging 排查工具调用、资源读取异常。
七、总结与拓展
7.1 本文核心收获
- MCP 本质:一套基于 JSON-RPC + 多传输层的能力标准化协议,核心是解耦 AI 客户端与外部服务;
- 核心组件全覆盖:掌握 Transport、Tools、Resources、Prompts、Roots、Logging 六大模块的设计与编码;
- 落地流程:MCP Server 开发 → 客户端配置(Claude Code/Trea)→ MCP + Skill 业务编排,完整闭环。
7.2 拓展方向
- 传输层升级:将 Stdio 改为 HTTP/SSE,实现远程 MCP 服务,多客户端共享;
- 复杂工具开发:基于 MCP 实现代码执行、数据库操作、本地文件管理等高级工具;
- 生态对接:对接 Cursor、IDE 插件、企业内部 AI 平台,复用同一套 MCP 服务;
- 权限增强:结合 MCP Roots + 身份校验,实现多用户权限管控。
7.3 学习建议
MCP 现在是本地大模型扩展的事实标准,从简单 Demo 入手理解组件,再结合业务场景做复杂服务开发,是快速掌握该协议的最优路径。本文的天气 Demo 可直接改造为文件管理、接口测试、运维工具等通用 MCP 服务。
互动话题:
上手 MCP 之后,你第一个想基于它开发什么自定义工具?我是阿宇,欢迎大家在评论区留言讨论!
更多推荐


所有评论(0)