MCP服务器

一个基于Python 3.11和Starlette的最小化MCP服务器,支持流式HTTP传输和Bearer令牌认证,提供工具如获取本地日期时间和维基百科页面JSON。

  • by diversen
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 diversen/minimal-mcp-server

使用说明

简单MCP服务器(Starlette,可流式传输http)

Python 3.11中使用Starlette的最小MCP服务器:

  • streamable-http 运输(POST /mcp)
  • 承载令牌身份验证(Authorization: Bearer <token>)使用OAuth风格的挑战元数据
  • 工具:
    • get_locale_date_time
    • get_wikipedia_pages_json

需求

  • python 3.11+

设置

git clone https://github.com/diversen/minimal-mcp-server.git
cd minimal-mcp-server
uv sync

设置环境变量:

cp .env.example .env
export MCP_AUTH_TOKEN="your-very-secret-token"
export MCP_AUTHORIZATION_SERVERS="https://auth.example.com"
export MCP_ALLOWED_ORIGINS="http://localhost:3000,http://127.0.0.1:3000"
export HOST="0.0.0.0"
export PORT="5000"

.env 变量引用

  • MCP_AUTH_TOKEN (必填):服务器用于验证请求的固定承载令牌。必须设置。
  • HOST (可选):绑定Uvicorn的地址。默认值可以是 0.0.0.0 用于本地网络接入。
  • PORT (可选):服务器端口。此项目中建议的默认端口为 5000.
  • MCP_ALLOWED_ORIGINS (可选):浏览器客户端的逗号分隔的源代码列表。保留未设置以禁用源过滤。
  • MCP_AUTHORIZATION_SERVERS (可选):发现元数据返回的逗号分隔的OAuth授权服务器URL。仅当您使用真正的OAuth发现时才设置此项。
  • MCP_REQUIRED_SCOPE (可选):在元数据/挑战中公布的作用域名称。适用于OAuth设置;为固定令牌模式留空。
  • MCP_RESOURCE_DOCUMENTATION (可选):受保护MCP资源的文档URL。

对于简单的固定令牌模式,仅 MCP_AUTH_TOKEN 是必需的。

跑

uv run uvicorn server:app --host "${HOST}" --port "${PORT}"

用于开发(自动重新加载 .py 更改):

uv run uvicorn server:app --host "${HOST}" --port "${PORT}" --reload

MCP流程示例

1) initialize

curl -s -X POST "http://127.0.0.1:5000/mcp" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-very-secret-token" \
  -d '{
    "jsonrpc":"2.0",
    "id":1,
    "method":"initialize",
    "params":{
      "protocolVersion":"2025-06-18",
      "capabilities":{},
      "clientInfo":{"name":"demo-client","version":"0.1.0"}
    }
  }'

2) tools/list

curl -s -X POST "http://127.0.0.1:5000/mcp" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-very-secret-token" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

3) tools/call

curl -s -X POST "http://127.0.0.1:5000/mcp" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-very-secret-token" \
  -d '{
    "jsonrpc":"2.0",
    "id":3,
    "method":"tools/call",
    "params":{
      "name":"get_locale_date_time",
      "arguments":{"locale":"Copenhagen"}
    }
  }'

维基百科页面JSON(仅返回 query.pages 在 structuredContent):

curl -s -X POST "http://127.0.0.1:5000/mcp" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-very-secret-token" \
  -d '{
    "jsonrpc":"2.0",
    "id":4,
    "method":"tools/call",
    "params":{
      "name":"get_wikipedia_pages_json",
      "arguments":{"title":"Earth","language":"en"}
    }
  }'

身份验证行为

  • 缺少/无效的身份验证标头-> 401
  • 令牌错误-> 401
  • 缺少服务器令牌配置(MCP_AUTH_TOKEN) -> 500
  • 401 包括 WWW-Authenticate: Bearer ... resource_metadata=".../.well-known/oauth-protected-resource/mcp"

OAuth发现元数据

这些未经身份验证的端点可用于OAuth资源发现:

  • GET /.well-known/oauth-protected-resource
  • GET /.well-known/oauth-protected-resource/mcp

它们返回资源元数据JSON,包括:

  • resource (您的MCP端点URL)
  • authorization_servers (从 MCP_AUTHORIZATION_SERVERS)
  • bearer_methods_supported
  • 可选的 scopes_supported (从 MCP_REQUIRED_SCOPE)
  • 可选的 resource_documentation (从 MCP_RESOURCE_DOCUMENTATION)

注意:此项目现在实现了OAuth风格的发现/挑战约定,但访问令牌验证仍然是使用 MCP_AUTH_TOKEN.

原产地验证

  • 如果 MCP_ALLOWED_ORIGINS 已设置,请求具有 Origin 标头必须与其中一个值匹配。
  • 不匹配的来源被拒绝 403.

备注

  • 使用 POST /mcp 用于流式传输http请求。
  • 这个启动器故意保持小范围(tools 仅)。

添加新工具

工具分为以下模块 tools/ 并已注册 @register_tool(...).

  1. 在下创建新文件 tools/ (例如: tools/echo.py).
  2. 定义 name, description,以及 input_schema.
  3. 返回MCP工具输出 content 可选 structuredContent.
  4. 将模块导入 tools/__init__.py 因此,注册在启动时运行。

例子:

from typing import Any

from tools.registry import register_tool

@register_tool(
    name="echo",
    description="Echo a string.",
    input_schema={
        "type": "object",
        "properties": {"text": {"type": "string"}},
        "required": ["text"],
        "additionalProperties": False,
    },
)
def echo(arguments: dict[str, Any]) -> dict[str, Any]:
    text = arguments["text"]
    return {
        "content": [{"type": "text", "text": text}],
        "structuredContent": {"text": text},
        "isError": False,
    }

无需更改 server.py 当通过此注册表流添加工具时。