MCP给大模型提供了基础的数据能力。
现在开始讲如何使用MCP。
MCP分成mcp client,mcp server。
Mcp Server分成2种,一种是stdio,一种是http。
区别
stdio,就是要求mcp server在本地执行,而http就是运行在云端上。
简单来说,我想将美国的气象局API封装成MCP,一个方案是我自己本地写个python,然后本地运行,这样本地的claude code、codex就可以直接调本地的mcp了;
但假如现在火山引擎官方提供了一个现成的mcp,我就不用自己写了,直接读取远程http的mcp就行了。
看场景使用。
一、示例
1.1、新建stdio的server
# -*- coding: utf-8 -*-
"""stdio 版 MCP server —— 查航班示例,走 stdio 传输(本地子进程,标准输入/输出通信)。
【这是什么】
MCP 有两种标准传输:
1) stdio —— 本地子进程,靠标准输入/输出通信。Claude Code / Cursor 这类**本地**宿主常用这种。
2) Streamable HTTP —— 一个 HTTP 端点(见 server-http.py)。火山方舟 / 扣子这类“云端远程接入”用 HTTP。
本文件是 **stdio** 这一种,提供一组查航班工具(search_flights、get_flight_status、
get_baggage_allowance、get_lowest_price_calendar,全部返回**假数据**),
和 server-http.py(查天气)配成两个不同领域的演示。
【谁来调它 / 怎么跑(stdio 和 HTTP 的关键区别)】
stdio server **不需要、也不能用 `uv run server-stdio.py` 直接“跑起来等连接”**——它没有监听端口,
而是由“宿主(Host)”把它当**子进程拉起**,然后通过这个子进程的 stdin/stdout 收发 JSON-RPC。
所以你不是“启动它”,而是把“怎么启动它”这条命令配置给宿主,宿主需要时自己拉起。
以 Claude Code / Cursor 为例,在 MCP 配置(mcpServers)里这样写:
{
"mcpServers": {
"flight-stdio": {
"command": "uv",
"args": ["run", "server-stdio.py"],
"cwd": "c:\\\\projects\\\\myai\\\\marktech\\\\mcp_helloworld"
}
}
}
(也可把 "command" 换成本机绝对路径的 python,"args" 写 ["server-stdio.py"]。)
⚠️ stdio 模式下:stdout 是协议专用通道,**不要往 stdout 打印任何普通日志**(会污染 JSON-RPC 报文导致解析失败)。
需要打日志请写到 **stderr**(本文件的提示就走 stderr)。这也是它和 server-http.py 的一个重要区别。
【和 server-http.py 的差异一览】
- 传输:mcp.run(transport="stdio") ← HTTP 版是 "streamable-http"
- 不需要 host / port / stateless_http / json_response 这些 HTTP 专属参数。
- 启动提示打到 stderr,避免污染 stdout 上的协议流。
"""
import sys
from mcp.server.fastmcp import FastMCP
# stdio 传输不监听端口,因此不需要 host/port,也不需要 stateless_http / json_response 这些 HTTP 专属开关。
mcp = FastMCP("flight")
@mcp.tool()
def search_flights(from_city: str, to_city: str, date: str = "2026-07-05") -> dict:
"""查询某天从出发城市到到达城市的航班。返回固定的**假**航班数据,仅用于演示 MCP 调用链路。
参数:
from_city: 出发城市,如 "北京"
to_city: 到达城市,如 "上海"
date: 出发日期,格式 YYYY-MM-DD
"""
return {
"from_city": from_city,
"to_city": to_city,
"date": date,
"flights": [
{
"flight_no": "CA1501",
"airline": "中国国航",
"depart_time": "07:30",
"arrive_time": "09:45",
"aircraft": "A350-900",
"price_cny": 1280,
"seats_left": 12,
"on_time_rate": "92%",
},
{
"flight_no": "MU5106",
"airline": "东方航空",
"depart_time": "10:20",
"arrive_time": "12:30",
"aircraft": "B787-9",
"price_cny": 980,
"seats_left": 5,
"on_time_rate": "88%",
},
{
"flight_no": "HU7603",
"airline": "海南航空",
"depart_time": "18:55",
"arrive_time": "21:10",
"aircraft": "B737-800",
"price_cny": 760,
"seats_left": 23,
"on_time_rate": "85%",
},
],
"note": "⚠️ 这是写死的假数据,不是真实航班,仅用于 MCP demo。",
}
@mcp.tool()
def get_flight_status(flight_no: str, date: str = "2026-07-05") -> dict:
"""查询某航班号在某天的实时动态(计划/延误/登机口等)。返回固定的**假**数据,仅用于演示 MCP 调用链路。
参数:
flight_no: 航班号,如 "MU5106"
date: 查询日期,格式 YYYY-MM-DD
"""
return {
"flight_no": flight_no,
"date": date,
"status": "延误",
"scheduled_depart": "10:20",
"estimated_depart": "11:05",
"terminal": "T2",
"gate": "C23",
"check_in_counter": "F 岛 01-08",
"reason": "前序航班晚到",
"note": "⚠️ 这是写死的假数据,不是真实航班动态,仅用于 MCP demo。",
}
@mcp.tool()
def get_baggage_allowance(flight_no: str, cabin_class: str = "经济舱") -> dict:
"""查询某航班某舱位的免费行李额度。返回固定的**假**数据,仅用于演示 MCP 调用链路。
参数:
flight_no: 航班号,如 "CA1501"
cabin_class: 舱位,如 "经济舱" / "公务舱" / "头等舱"
"""
return {
"flight_no": flight_no,
"cabin_class": cabin_class,
"free_checked_baggage": "1 件,每件不超过 23kg",
"carry_on": "1 件,不超过 8kg,尺寸 55×40×20cm 以内",
"extra_fee_hint": "超额行李约 ¥60/kg(以航司柜台报价为准)。",
"note": "⚠️ 这是写死的假数据,不是真实行李政策,仅用于 MCP demo。",
}
@mcp.tool()
def get_lowest_price_calendar(from_city: str, to_city: str) -> dict:
"""查询两城市之间未来一周每天的最低票价(低价日历)。返回固定的**假**数据,仅用于演示 MCP 调用链路。
参数:
from_city: 出发城市,如 "北京"
to_city: 到达城市,如 "上海"
"""
return {
"from_city": from_city,
"to_city": to_city,
"currency": "CNY",
"calendar": [
{"date": "2026-07-05", "lowest_price": 760},
{"date": "2026-07-06", "lowest_price": 690},
{"date": "2026-07-07", "lowest_price": 620},
{"date": "2026-07-08", "lowest_price": 580},
{"date": "2026-07-09", "lowest_price": 830},
{"date": "2026-07-10", "lowest_price": 1080},
{"date": "2026-07-11", "lowest_price": 990},
],
"cheapest_date": "2026-07-08",
"note": "⚠️ 这是写死的假数据,不是真实票价,仅用于 MCP demo。",
}
def main() -> None:
# 关键:stdout 被 stdio 传输占用来收发 JSON-RPC,普通日志必须打到 stderr,否则会污染协议流。
print("[flight-mcp] stdio 传输已就绪,等待宿主通过 stdin/stdout 通信。", file=sys.stderr)
print("[flight-mcp] 工具: search_flights, get_flight_status, get_baggage_allowance, get_lowest_price_calendar", file=sys.stderr)
# 关键:transport 设为 "stdio"(HTTP 版是 "streamable-http")。
mcp.run(transport="stdio")
if __name__ == "__main__":
main()
运行MCP server
注:这里用uv管理python,就是自动激活虚拟环境、依赖包这些,省得自己再手动管理、激活虚拟环境了。
uv run .\server-stdio.py
注意:首次需要安装uv,安装完后需要添加依赖:
uv init --bare # 只生成 pyproject.toml(不加 --bare 会附送 main.py、README 等示例文件)
uv add "mcp[cli]" # 写入依赖 + 解析生成 uv.lock + 自动创建 .venv,一步到位
uv run .\server-http.py
运行的效果如下图所示:

看到上图,表明stdio版的mcp已经启动了。
配置mcp server
这里我们用cline(vscode插件,自行安装)来演示

{
"mcpServers": {
"flight-stdio": {
"disabled": false,
"timeout": 60,
"type": "stdio",
"command": "uv",
"args": [
"run",
"server-stdio.py"
],
"cwd": "c:\\projects\\myai\\marktech\\mcp_helloworld"
}
}
}
使用MCP,效果如下:
查询成都到上海的航班

1.2、新建http版本的mpc server
# -*- coding: utf-8 -*-
"""第一个 HTTP MCP server —— 用官方 MCP Python SDK 的 FastMCP,走 Streamable HTTP 传输。
【这是什么】
MCP 有两种标准传输:
1) stdio —— 本地子进程,靠标准输入/输出通信(Claude Code 里常见的本地 server 就是这种)。
2) Streamable HTTP —— 一个 HTTP 端点(这里是 http://127.0.0.1:8000/mcp),用 JSON-RPC over HTTP。
火山方舟 / 扣子 这类“远程接入别人写的 server”用的就是 HTTP 这种。
本文件就是 HTTP 这一种。里面的工具返回**假数据**,方便你先把链路跑通、感受一下。
【谁来调它(解你的“客户端”困惑)】
你不用自己写客户端来用它。MCP 的角色分三层:
Host(AI 应用,如 火山方舟对话、扣子、Claude Code) → 内含 Client(连接器,替你说 MCP 协议)→ Server(就是本文件)。
把本 server 的 URL 填进火山方舟/扣子,**平台自己就是那个 client**,会自动握手、列工具、调工具。
所谓“官方 client”只是本地自测用的小工具(见 client.py),不是上线必需品。
【怎么跑】见同目录 README.md。一句话: uv run server.py
"""
import os
from mcp.server.fastmcp import FastMCP
# host/port 允许用环境变量覆盖,默认就是官方默认值 127.0.0.1:8000,端点路径默认 /mcp。
HOST = os.environ.get("MCP_HOST", "127.0.0.1")
PORT = int(os.environ.get("MCP_PORT", "8000"))
# 两个让“第一次玩”更顺手的开关:
# stateless_http=True:无状态模式。每个 HTTP 请求都自带完整上下文,**不需要** 先 initialize 拿
# session-id 再带着它请求。curl 一行就能调用工具。也最适合用 API 网关/Serverless
# 转发(火山方舟的 HTTP 接入正是这类)。把它改成 False 就是“有状态握手”模式。
# json_response=True:HTTP 响应直接回**普通 JSON**,而不是 SSE 的 "data: ..." 分帧,curl 能直接看懂。
# 生产里平台(火山方舟/扣子)自带的 client 两种模式都能处理,所以这里怎么选都不影响接入。
mcp = FastMCP("helloworld", host=HOST, port=PORT, stateless_http=True, json_response=True)
@mcp.tool()
def get_weather(city: str) -> dict:
"""查询某城市的天气。返回固定的**假**天气数据,仅用于演示 MCP 调用链路。"""
return {
"city": city,
"date": "2026-06-17",
"weather": "晴",
"temperature_c": 26,
"humidity": "45%",
"wind": "东南风 2 级",
"note": "⚠️ 这是写死的假数据,不是真实天气,仅用于 MCP demo。",
}
@mcp.tool()
def get_humidity(city: str) -> dict:
"""查询某城市当前的湿度详情。返回固定的**假**湿度数据,仅用于演示 MCP 调用链路。"""
return {
"city": city,
"date": "2026-06-17",
"humidity": "45%",
"dew_point_c": 13,
"comfort": "干爽舒适",
"indoor_advice": "无需除湿/加湿,正常通风即可。",
"note": "⚠️ 这是写死的假数据,不是真实湿度,仅用于 MCP demo。",
}
@mcp.tool()
def get_sandstorm_alert(city: str) -> dict:
"""查询某城市的沙尘暴预警信息。返回固定的**假**预警数据,仅用于演示 MCP 调用链路。"""
return {
"city": city,
"date": "2026-06-17",
"alert_level": "蓝色预警",
"pm10_ug_m3": 350,
"visibility_km": 3.5,
"peak_hours": "14:00 - 18:00",
"advice": "老人儿童减少外出,出门佩戴口罩和护目镜,门窗保持关闭。",
"note": "⚠️ 这是写死的假数据,不是真实预警,仅用于 MCP demo。",
}
@mcp.tool()
def get_forecast_3days(city: str) -> dict:
"""查询某城市未来 3 天的天气预报。返回固定的**假**预报数据,仅用于演示 MCP 调用链路。"""
return {
"city": city,
"forecast": [
{"date": "2026-06-18", "weather": "晴", "high_c": 28, "low_c": 17, "wind": "南风 2 级"},
{"date": "2026-06-19", "weather": "多云", "high_c": 26, "low_c": 16, "wind": "东南风 3 级"},
{"date": "2026-06-20", "weather": "小雨", "high_c": 22, "low_c": 15, "wind": "东风 3 级"},
],
"note": "⚠️ 这是写死的假数据,不是真实预报,仅用于 MCP demo。",
}
def main() -> None:
print(f"[helloworld-mcp] Streamable HTTP 已启动 -> http://{HOST}:{PORT}/mcp")
print("[helloworld-mcp] 工具: get_weather, get_humidity, get_sandstorm_alert, get_forecast_3days (按 Ctrl+C 停止)")
# 关键:transport 必须是带连字符的 "streamable-http"(不是下划线)。
mcp.run(transport="streamable-http")
if __name__ == "__main__":
main()
运行server
uv run .\server-http.py
运行后效果如图

配置http版的mcp server
{
"mcpServers": {
"weather-http": {
"autoApprove": [],
"disabled": false,
"timeout": 60,
"type": "streamableHttp",
"url": "http://127.0.0.1:8000/mcp"
}
}
}
使用mcp,测试效果
查询上海的天气

二、原理
上面第一部分掌握了,就可以达到60分的水准,就可以使用了。但是这还远远不够,因为还不太清楚MCP内部的运作逻辑。
单从通信协议上面看,它和调用接口没有本质区别,都是http。但是它在上面封装了一层叫json-rpc的规约,包括入参、出参这些,所有的通信都必须遵循这个规约,和a2a非常像,这样如果所有人都按照这个规约来开发mcp,那就可以接到全球所有标准的mcp client
有点像当年的usb或者显卡的hd接口,标准统一就可以无缝衔接。
2.1、完整流程演示

2.2、Mcp Client向Mcp Server注册过程
向mcp server的注册过程中,会依次向服务器发起3次调用:

initialize:就是初步打招呼
tools/list:这个就是最重要的,将mcp包含的所有方法全部列出来
tools/call:前期是不调用的,只有在大模型真正聊天时才发起调用
时序图如下:

2.3、示例
用http的mcp server举例
2.3.1、监听本地loopback网卡
http server运行端口是8000,我们用wireshark监听8000端口
监听本地回环loopback接口

过滤8000端口,如下面图所示:

2.3.2、查看数据包
查看initialize数据
选中ID为88的这条,右键>Follow>HTTP Stream


可以清晰看到method:initialize。
查看tools/list数据
查看ID为120的,同样查看HTTP Stream,如下图,可以清晰的看到当前MCP有哪些工具

就可以看到method:toos/list,将mcp里面所有tools全部吐出来了

查看tools/call数据
在真正有用户发起提问时,才会真正调用MCP
查看id为7715的数据

至此,一切都真相大白。
三、Claude code如何使用
claude code中使用mcp比较麻烦,不太智能,稍微绕点小弯子。
1是因为claude code的语法和cline不太一样,但是大同小异。
2 是因为claude code的mcp配置文件路径也是非常奇葩。一个是全局配置,路径在当前用户目录的.claude.json

另一个就是在当前项目的根目录下,有一个.mcp.json文件:

配置文件具体长啥样:
{
"mcpServers": {
"weather-http": {
"type": "http",
"url": "http://127.0.0.1:8000/mcp"
},
"flight-stdio": {
"type": "stdio",
"command": "uv",
"args": [
"run",
"--directory",
"c:\\projects\\myai\\marktech\\mcp_helloworld",
"server-stdio.py"
]
}
}
}
下面在vscode插件中,输入/mcp,在弹出来的MCP servers,点击它即可。


1841

被折叠的 条评论
为什么被折叠?



