纯Python实现的轻量级MCP服务器。
这是一个轻量级的手工实现的Model Context Protocol,专注于大多数用户实际需要的功能:通过清晰的Python类型注解来暴露工具。
pip install zeromcp
或者使用uv:
uv add zeromcp
from typing import Annotated
from zeromcp import McpServer
mcp = McpServer("my-server")
@mcp.tool
def greet(
name: Annotated[str, "要问候的名字"],
age: Annotated[int | None, "人的年龄"] = None
) -> str:
"""生成问候消息"""
if age:
return f"你好,{name}!你今年{age}岁了。"
return f"你好,{name}!"
if __name__ == "__main__":
mcp.serve("127.0.0.1", 8000)
然后手动使用inspector测试你的MCP服务器:
npx -y @modelcontextprotocol/inspector
一旦一切正常工作,你可以配置mcp.json:
{
"mcpServers": {
"my-server": {
"type": "http",
"url": "http://127.0.0.1/mcp"
}
}
}
对于只支持stdio传输的MCP客户端:
from zeromcp import McpServer
mcp = McpServer("my-server")
@mcp.tool
def greet(name: str) -> str:
"""生成问候"""
return f"你好,{name}!"
if __name__ == "__main__":
mcp.stdio()
然后在mcp.json中进行配置(每个客户端不同):
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["path/to/server.py"]
}
}
}
zeromcp使用原生Python Annotated类型进行模式生成:
from typing import Annotated, Optional, TypedDict, NotRequired
class GreetingResponse(TypedDict):
message: Annotated[str, "问候消息"]
name: Annotated[str, "被问候的名字"]
age: Annotated[NotRequired[int], "如果提供的话,年龄"]
@mcp.tool
def greet(
name: Annotated[str, "要问候的名字"],
age: Annotated[Optional[int], "人的年龄"] = None
) -> GreetingResponse:
"""生成问候消息"""
if age is not None:
return {
"message": f"你好,{name}!你今年{age}岁了。",
"name": name,
"age": age
}
return {
"message": f"你好,{name}!",
"name": name
}
工具可以接受多种输入类型:
from typing import Annotated, TypedDict
class StructInfo(TypedDict):
name: Annotated[str, "结构名称"]
size: Annotated[int, "结构大小(字节)"]
fields: Annotated[list[str], "字段名称列表"]
@mcp.tool
def struct_get(
names: Annotated[list[str], "结构名称数组"]
| Annotated[str, "单个结构名称"]
) -> list[StructInfo]:
"""按名称检索结构信息"""
return [
{
"name": name,
"size": 128,
"fields": ["field1", "field2", "field3"]
}
for name in (names if isinstance(names, list) else [names])
]
from zeromcp import McpToolError
@mcp.tool
def divide(
numerator: Annotated[float, "分子"],
denominator: Annotated[float, "分母"]
) -> float:
"""两个数相除"""
if denominator == 0:
raise McpToolError("除以零")
return numerator / denominator
通过URI模式暴露只读数据。资源序列化为JSON。
from typing import Annotated
@mcp.resource("file://data.txt")
def read_file() -> dict:
"""获取关于data.txt的信息"""
return {"name": "data.txt", "size": 1024}
@mcp.resource("file://{filename}")
def read_any_file(
filename: Annotated[str, "要读取的文件名"]
) -> dict:
"""获取关于任何文件的信息"""
return {"name": filename, "size": 2048}
以下客户端已测试:
注意:通常推荐使用/mcp端点,但并非所有客户端都正确支持它。
<a name="ai-usage"></a> <sup>1</sup>README和部分测试由Claude编写