本地手写MCP服务教程
背景
最近AI比较火爆,那就不得不提及AI的左膀右臂,MCP。简单介绍一下MCP是什么。
MCP是什么
不知大家在用deepseek、Kimi、gpt的时候,有没有这种感觉,你问一些问题,AI只能提供解决办法,不能直接把结果告诉你。
比如,你问,帮我创建一个excel,在excel中写上xxx数据。它会告诉你操作流程:打开WPS,点击“新建”,…,却不能真正的创建文件,它没有执行能力。
你问,今天北京的天气怎么样,它可能会告诉你:【打开浏览器,输入北京天气】,而它却不能搜索浏览器将结果告知你。即:它只能回答你问题,不能帮你干活,它只能给你提供方案,不能真正的帮你执行。
因为,AI大部分都是基于知识库进行回答,AI并没有联网搜索功能,没有读写磁盘的能力等等,在这种情况下,AI只是一个工具,与计算器、字典没有什么区别。
那怎么才能让AI为我们干活呢,答案就是MCP!mcp可以让AI长出手和脚,可以让AI真正的帮你做事,mcp可以让AI连接数据库,执行sql;可以在电脑上创建文件,读取文件,执行linux命令;可以搜索浏览器,获取结果。
一个大家最熟悉的例子就是,千问帮我们点奶茶!千问直接对接了淘宝闪购的商品选购接口、高德的定位服务、支付的支付接口。实现了无需通过传统页面点击模拟用户操作。千问是真真正正的帮我实现了选品、下单、支付流程。使用千问下单奶茶,AI正在创造一个新时代!
MCP定义:Model Context Protocol,模型上下文协议,是由 Anthropic 公司于 2024 年 11 月开源的一种标准化通信协议,旨在统一大语言模型(LLM)与外部工具、数据源和服务之间的交互方式,被广泛称为 “AI 的 USB-C 接口” 。
简而言之,MCP是一种标准协议,标准化了AI工具与外部工具/服务的交互方式,像一个插座一样连接了AI与外部服务。
MCP本质:说到底,mcp还是一个服务,AI工具引入mcp包,AI就是mcp的客户端,提供外部服务/工具/数据源的、引入了mcp的服务就是mcp服务端,客户端与服务端之间通过mcp协议交互,本质还是服务的调用。是mcp让AI 从工具变成了小助手。
工具说明
本次要手动实现一个天气查询的mcp工具。
基础准备
1、python3.9+、mac环境
我是mac电脑,python需要是3.9版本以上的,太低的会不支持。
我的情况是:电脑上有3.8和3.10两个版本的python,而环境变量中配置的是3.8版本的,但mcp又不支持3.9以下的,我又不想修改环境变量,因为,我的其他项目在用3.8。 这时,python提供了一个方式,python支持创建虚拟环境,在虚拟环境中可以使用指定的Python版本。
命令实操
1、创建虚拟环境
cd /User/xxx # 进入一个目录
mkdir MyMcp # 创建一个文件夹
cd MyMcp # 进入到该文件夹
ls /usr/local/bin/python* # 先查看本地安装的所有Python版本,如果这个命令不好使,可以在搜索下其他的命令
# 我本地是有3.10版本的Python,但不是环境变量中配置的Python,所以在创建虚拟环境时我需要手动指定Python,但我需要先查看3.10版本的路径
which python3.10 # 打印出的路径是:/Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
python3 -m venv -p /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10 venv # 使用指定版本Python创建虚拟环境venv -p代表指定Python版本
source venv/bin/activate # 激活虚拟环境 在MyMcp目录下执行即可。当前目录结构应该是MyMcp/venv/...
# 激活成功之后,命令行最前面应该会出现(venv)
deactivate # 退出虚拟环境,如果要删除的话,直接删除当前文件夹即可
2、安装核心库
虚拟环境创建好后,开始安装核心包
pip install mcp aiohttp anyio fastmcp # 这个命令就是默认安装mcp、aiohttp、anyio、fastmcp 最新版本
这些库中,mcp是核心库,aiohttp用于处理HTTP请求,anyio用于管理异步并发。
安装好之后,可以查看所有安装的包的版本
pip list # 查看所有
pip show mcp # 查看mcp包的详细信息
mcp版本是:1.26.0
fastmcp版本是:3.0.2
aiohttp版本是:3.13.3
anyio版本是:4.12.1
brew版本是:3.6.2
3、创建mcp服务器
1、创建脚本
touch server.py # 创建一个名为server.py的脚本
2、写代码(stadio方式交互)
import asyncio
import sys
import aiohttp
from mcp.server.fastmcp import FastMCP
from mcp.server.stdio import stdio_server
# 创建一个FastMCP服务,参数是服务名,可自定义
mcp = FastMCP("weather-server")
# 建议从环境变量读取 API Key
API_KEY = "" # 从官网上免费获取一个自己的key
# 定义为mcp的tool
@mcp.tool()
async def get_weather(city: str) -> str:
"""根据城市名称查询当前天气情况"""
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}&units=metric"
timeout = aiohttp.ClientTimeout(total=10)
try:
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.get(url) as response:
response.raise_for_status()
data = await response.json()
temperature = data['main']['temp']
description = data['weather'][0]['description']
return f"{city}的当前天气:温度{temperature}℃,{description}"
except Exception as e:
return f"获取天气信息失败: {str(e)}"
if __name__ == "__main__":
mcp.run() # 这种方式默认是以stadio方式交互的
3、写代码(http方式交互)
import asyncio
import contextlib
import aiohttp
from mcp.server.fastmcp import FastMCP
from mcp.server.stdio import stdio_server
from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.middleware.cors import CORSMiddleware
# 创建Server实例,名字叫"weather-server"
mcp = FastMCP("weater-server", stateless_http=True,json_response=True)
print(type(mcp)) # 打印mcp类型
# 你的OpenWeatherMap API Key,建议从环境变量读取
API_KEY = ""
@mcp.tool()
async def get_weather(city: str) -> str:
"""根据城市名称查询当前天气情况
Args:
city: 城市名称,例如"Beijing"或"上海"
Returns:
返回该城市的当前天气信息,包括温度和天气状况。
"""
# 构建请求URL
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}&units=metric"
timeout = aiohttp.ClientTimeout(total=10) # 设置10秒超时
# 错误处理与超时控制
try:
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.get(url) as response:
# 检查HTTP状态码是否成功
response.raise_for_status()
data = await response.json()
# 解析返回的JSON数据
temperature = data['main']['temp']
description = data['weather'][0]['description']
result_text = f"{city}的当前天气:温度{temperature}℃,{description}"
return result_text
except asyncio.TimeoutError:
return "天气请求超时,请稍后重试"
except aiohttp.ClientResponseError as e:
return f"HTTP错误: {e.status} - {e.message}"
except Exception as e:
return f"获取天气信息时发生错误: {str(e)}"
# Create a combined lifespan to manage both session managers
@contextlib.asynccontextmanager
async def lifespan(app: Starlette):
async with contextlib.AsyncExitStack() as stack:
await stack.enter_async_context(mcp.session_manager.run())
yield
# Create your Starlette app first
starlette_app = Starlette(routes=[Mount("/", mcp.streamable_http_app())],lifespan=lifespan,)
# Then wrap it with CORS middleware
starlette_app = CORSMiddleware(
starlette_app,
allow_origins=["*"], # Configure appropriately for production
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
expose_headers=["Mcp-Session-Id"],
)
if __name__ == "__main__":
mcp.run(transport="streamable-http")
4、说明
- 工具声明:@mcp.tool()装饰器将函数注册为MCP工具。函数的参数和文档字符串会自动成为工具Schema的一部分,帮助AI模型理解如何调用它。
- mcp.run() 这种方式默认是以stadio方式交互的,与Claude等客户端通信的标准方式。claude也可以设置使用http方式交互
- mcp.run(transport=“streamable-http”) 这种方式使用http访问,可以使用postman访问。
4、启动服务(服务端)
- 执行python命令,启动服务
python server.py
- 以stadio方式交互,执行启动命令后,控制台应该是没有任何输出的,能看到光标在闪
- 以http方式交互,控制台输出如下:

5、配置mcp工具(客户端配置)
1、claude配置
我本地安装了claude客户端(命令行方式,非app),找到.claude.json文件,我的是在~目录下,编辑.claude.json文件,配置mcp
"mcpServers": {
"weather-server": { # server.py脚本中的名字
"command": "/Users/xxxx/MyMCP/venv/bin/python3.10", # 这个是虚拟环境中使用的python路径
"args": [
"/Users/xxxx/MyMCP/server.py" # 自定义的python脚本
]
}
}

在虚拟环境中使用which python3.10 (如果是其他版本,把命令中的3.10替换为自己实际使用的版本)命令查看虚拟环境中使用的python路径,填写到.claude.json配置文件的。
这里有一个小坑:我刚开始把mcp配置写在了projects的项目目录下,当我在非项目目录下启动claude时,会发现claude找不到我配置的mcp工具,这是因为mcp的配置有针对全局的,也有针对项目,我如果配置在项目目录下,那就只在该目录下使用;所以,最好是配置在全局,这样无论在哪儿启动claude,都可以使用mcp
6、使用mcp
1、在claude中使用
-
stadio方式与claude交互时,不需要手动启动mcp服务端,claude会自动启动mcp服务。配置完claude配置文件后,启动claude,如果启动claude之后,右下角没有提示“1
mcp failed”,那就成功了一半(如果有,检查配置文件)。 -
启动claude成功之后,输入/mcp,可以看到刚才自己写的mcp

-
向claude提问:查询北京今天的天气(claude会自动推断是否要使用mcp工具),也可以明确告诉AI,使用xxx工具

2、在postman中使用
- 在postman中使用mcp时,需要手动将mcp服务启动,mcp启动成功后会有如下输出:

ip:127.0.0.1
port:8000
访问路径:http://127.0.0.1:8000/mcp(默认会带着mcp路径)
- postman请求配置:
1、请求头中添加:Accept = application/json
2、body中配置
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_weather", // server.py中定义的方法名
"arguments": {
"city": "Beijing" // 参数 要查询的城市
}
}
}

- 获取响应结果

总结
我在自己写脚本的过程中遇到很多坑,我本身是学java的,python接触不多,遇到了很多问题,但是今天写的时候,有些bug已经想不起来了。遇到的最难解决的两个问题就是:
1、跟着其他博主博客写代码,博主博客不写清楚包的版本号;
2、我本地可能是工具版本的问题,在下载工具包时,总是会报包不存在的问题,根本原因是我本地的brew有问题、pip命令也有问题(使用了/Users/xxx/MyMCP/venv/bin/python -m pip install --upgrade pip)命令升级pip就好了
有问题欢迎留言讨论~
更多推荐


所有评论(0)