返回市场
零MCP

零MCP

作者:mrexodia43 星标更新:2025-11-21

项目介绍

zeromcp

纯Python实现的轻量级MCP服务器。

这是一个轻量级的手工实现的Model Context Protocol,专注于大多数用户实际需要的功能:通过清晰的Python类型注解来暴露工具。

特性

  • 零依赖 - 纯Python,仅使用标准库
  • 🎯 类型安全 - 使用原生Python类型注解确保一切类型安全
  • 🚀 快速 - 最小的开销,最大性能
  • 🛠️ 手工打造 - 由人类编写<sup>1</sup>,并根据规范验证
  • 🌐 HTTP/SSE传输 - 支持流式响应
  • 📡 Stdio传输 - 用于旧版客户端
  • 📦 小巧 - 少于1,000行代码

安装

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传输

对于只支持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编写