一个全面的 Cookiecutter 模板,用于使用 Python 创建 Model Context Protocol (MCP) 服务器。
Model Context Protocol (MCP) 是一个开放标准,允许应用程序以标准化的方式为大型语言模型(如 Claude)提供上下文。MCP 服务器可以暴露:
# 如果需要,请安装 cookiecutter
pip install cookiecutter
# 从模板生成一个新的 MCP 服务器项目
cookiecutter gh:shubhamgupta-dat/mcp-server-cookiecutter
# 或者从本地副本生成
cookiecutter path/to/mcp-server-cookiecutter
# 导航到你的新项目
cd your-project-name
# 设置环境(底层使用 uv)
make setup
# 激活虚拟环境
source .venv/bin/activate # 在 Unix/MacOS 上
# 或者
.venv\Scripts\activate # 在 Windows 上
# 使用 MCP Inspector 在开发模式下运行服务器
make dev
# 在 Claude Desktop 中安装服务器(用于本地测试)
make install
mcp-server-template/
├── cookiecutter.json
└── {{cookiecutter.project_name}}/
├── README.md
├── Makefile
├── Dockerfile
├── .gitignore
├── pyproject.toml
└── {{cookiecutter.module_name}}/
├── __init__.py
├── server.py
├── config.py
├── tools/
│ ├── __init__.py
│ └── sample_tools.py
├── resources/
│ ├── __init__.py
│ └── sample_resources.py
└── prompts/
├── __init__.py
└── sample_prompts.py
{
"project_name": "mcp-server",
"module_name": "{{ cookiecutter.project_name.lower().replace('-', '_') }}",
"project_description": "一个 Model Context Protocol (MCP) 服务器",
"author_name": "你的名字",
"author_email": "your.email@example.com",
"version": "0.1.0",
"python_version": "3.10"
}
# {{cookiecutter.project_name}}
{{cookiecutter.project_description}}
## 概述
这是一个 Model Context Protocol (MCP) 服务器,它暴露了工具、资源和提示,用于与像 Claude 这样的 LLM 应用程序交互。MCP 服务器让你能够通过自定义功能、数据源和模板化的交互来扩展 AI 应用程序。
## 快速开始
### 使用 uv 设置(推荐)
```bash
# 设置环境
make setup
# 激活虚拟环境
source .venv/bin/activate # 在 Unix/MacOS 上
# 或者
.venv\Scripts\activate # 在 Windows 上
# 使用 MCP Inspector 在开发模式下运行服务器
make dev
# 在 Claude Desktop 中安装服务器
make install
# 如果没有安装 uv,请先安装
pip install uv
# 创建虚拟环境
uv venv
# 激活虚拟环境
source .venv/bin/activate # 在 Unix/MacOS 上
# 或者
.venv\Scripts\activate # 在 Windows 上
# 以开发模式安装包
uv pip install -e .
# 在开发模式下运行
mcp dev {{cookiecutter.module_name}}.server
# 在 Claude Desktop 中安装
mcp install {{cookiecutter.module_name}}.server
使用 Docker 构建和运行:
# 构建 Docker 镜像
make docker-build
# 或者
docker build -t {{cookiecutter.project_name}} .
# 运行容器
make docker-run
# 或者
docker run -p 8000:8000 {{cookiecutter.project_name}}
服务器组织成几个组件:
server.py:主要的 MCP 服务器设置和配置config.py:配置管理tools/:工具实现(LLM 可执行的功能)resources/:资源实现(LLM 可访问的数据)prompts/:提示模板实现(可重复使用的对话模板)此服务器实现了所有三个 MCP 原语:
工具:LLM 可调用的功能来执行操作
calculate,fetch_data,long_task资源:提供上下文给 LLM 的数据源
static://example,dynamic://{parameter},config://{section}提示:用于 LLM 交互的可重复使用模板
simple_prompt,structured_prompt,data_analysis_prompt在 tools/ 目录中创建或修改文件:
@mcp.tool()
def my_custom_tool(param1: str, param2: int = 42) -> str:
"""
一个有用的自定义工具。
参数:
param1: 第一个参数的描述
param2: 第二个参数的描述,默认值为 42
返回:
返回值的描述
"""
# 你的实现代码
return f"结果: {param1}, {param2}"
在 resources/ 目录中创建或修改文件:
@mcp.resource("my-custom-resource://{param}")
def my_custom_resource(param: str) -> str:
"""
提供有用数据的自定义资源。
参数:
param: 参数的描述
返回:
资源内容
"""
# 你的实现代码
return f"资源内容为: {param}"
在 prompts/ 目录中创建或修改文件:
@mcp.prompt()
def my_custom_prompt(param: str) -> str:
"""
自定义提示模板。
参数:
param: 参数的描述
返回:
格式化的提示
"""
return f"""
# 自定义提示模板
上下文: {param}
请根据上述上下文进行分析并回复。
"""
服务器支持通过以下方式配置:
环境变量:前缀为 MCP_(例如,MCP_API_KEY=xyz123)
MCP_DATABASE__HOST=localhost)配置文件:通过 MCP_CONFIG_FILE 环境变量指定
示例配置:
{
"api": {
"key": "xyz123",
"url": "https://api.example.com"
},
"database": {
"host": "localhost",
"port": 5432
}
}
# 运行测试
make test
# 格式化代码
make format
# 类型检查
make type-check
# 清理构建工件
make clean
[在此处包含你的许可证信息]
### {{cookiecutter.project_name}}/Makefile
```makefile
.PHONY: setup run dev install deploy test clean format type-check
# 从 cookiecutter 或默认为 3.10 设置 Python 版本
PYTHON_VERSION := {{cookiecutter.python_version}}
# 使用 uv 设置
setup:
# 检查是否已安装 uv,如果没有则安装
@which uv >/dev/null || pip install uv
# 创建虚拟环境
uv venv
# 安装带有开发额外项的依赖
uv pip install -e ".[dev]"
@echo "✅ 环境设置完成。激活它使用 'source .venv/bin/activate' (Unix/macOS) 或 '.venv\\Scripts\\activate' (Windows)"
# 直接运行服务器
run:
python -m {{cookiecutter.module_name}}.server
# 使用 MCP inspector 在开发模式下运行
dev:
mcp dev {{cookiecutter.module_name}}.server
# 在 Claude Desktop 中安装
install:
mcp install {{cookiecutter.module_name}}.server
# 运行测试
test:
pytest
# 使用 black 和 isort 格式化代码
format:
black {{cookiecutter.module_name}}
isort {{cookiecutter.module_name}}
# 使用 mypy 进行类型检查
type-check:
mypy {{cookiecutter.module_name}}
# 清理构建工件
clean:
rm -rf build/
rm -rf dist/
rm -rf *.egg-info/
find . -type d -name __pycache__ -exec rm -rf {} +
find . -type f -name "*.pyc" -delete
# Docker 构建
docker-build:
docker build -t {{cookiecutter.project_name}}:latest .
# 使用 Docker 运行
docker-run:
docker run -p 8000:8000 {{cookiecutter.project_name}}:latest
# 使用特定版本的 Python
FROM python:{{cookiecutter.python_version}}-slim AS builder
# 设置构建参数
ARG APP_USER=mcp
ARG APP_UID=1000
ARG APP_GID=1000
# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# 设置应用用户
RUN groupadd -g $APP_GID $APP_USER && \
useradd -m -u $APP_UID -g $APP_GID -s /bin/bash $APP_USER
# 设置工作目录
WORKDIR /app
# 安装 uv 用于依赖管理
RUN pip install --no-cache-dir uv
# 复制项目文件
COPY pyproject.toml README.md ./
# 复制应用代码
COPY {{cookiecutter.module_name}} ./{{cookiecutter.module_name}}
# 以开发模式安装应用
RUN uv pip install --no-cache-dir -e .
# 切换到更小的最终镜像
FROM python:{{cookiecutter.python_version}}-slim
# 从构建器复制
COPY --from=builder /usr/local/lib/python{{cookiecutter.python_version}}/site-packages /usr/local/lib/python{{cookiecutter.python_version}}/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY --from=builder /app /app
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV MCP_ENV=production
# 暴露端口用于基于 HTTP 的传输(SSE)
EXPOSE 8000
# 使用生产传输(SSE)运行服务器
CMD ["python", "-m", "{{cookiecutter.module_name}}.server"]
# Python 字节码
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
# 分发/打包
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# 虚拟环境
venv/
env/
ENV/
.venv/
.env/
# 单元测试/覆盖率报告
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
pytest_cache/
# 编辑器文件
.idea/
.vscode/
*.swp
*.swo
*~
# 操作系统特定
.DS_Store
Thumbs.db
# 项目特定
*.log
.env
.env.*
!.env.example
# MCP 特定
claude_desktop_config.json
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "{{cookiecutter.project_name}}"
version = "{{cookiecutter.version}}"
description = "{{cookiecutter.project_description}}"
authors = [
{name = "{{cookiecutter.author_name}}", email = "{{cookiecutter.author_email}}"},
]
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"mcp>=1.0",
"httpx>=0.24.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
"isort>=5.0",
"mypy>=1.0",
"ruff>=0.2.0",
]
[tool.setuptools]
packages = ["{{cookiecutter.module_name}}"]
[tool.black]
line-length = 88
target-version = ["py310"]
[tool.isort]
profile = "black"
line_length = 88
[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
[tool.ruff]
line-length = 88
target-version = "py310"
select = ["E", "F", "I"]
"""{{cookiecutter.project_description}}"""
__version__ = "{{cookiecutter.version}}"
"""
主要的 MCP 服务器实现。
此文件初始化 FastMCP 服务器,并导入所有工具、资源和提示。
"""
from contextlib import asynccontextmanager
from collections.abc import AsyncIterator
from dataclasses import dataclass
from mcp.server.fastmcp import Context, FastMCP
# 导入配置管理
from .config import load_config
@dataclass
class AppContext:
"""
类型安全的应用程序上下文容器。
在这里存储任何全局状态或连接。
"""
config: dict
@asynccontextmanager
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
"""
应用程序生命周期管理器。
处理启动和关闭操作,具有正确的资源管理。
参数:
server: FastMCP 服务器实例
产生:
初始化资源的应用程序上下文
"""
# 加载配置
config = load_config()
# 初始化连接和资源
print("🚀 服务器正在启动...")
try:
# 创建并产生应用程序上下文
yield AppContext(config=config)
finally:
# 关闭时清理资源
print("🛑 服务器正在关闭...")
# 创建具有生命周期支持的 MCP 服务器
mcp = FastMCP(
"{{cookiecutter.project_name}}", # 服务器名称
lifespan=app_lifespan, # 生命周期管理器
dependencies=["mcp>=1.0"], # 必需的依赖项
)
# 导入所有工具、资源和提示
# 这些导入必须在初始化 MCP 服务器之后进行
from .tools.sample_tools import *
from .resources.sample_resources import *
from .prompts.sample_prompts import *
# 将服务器实例提供给其他模块
server = mcp
if __name__ == "__main__":
# 当直接执行时,运行服务器
mcp.run()
"""
MCP 服务器的配置管理。
处理从环境变量和/或配置文件加载配置。
"""
import os
import json
from pathlib import Path
from typing import Dict, Any, Optional
def load_config(config_path: Optional[str] = None) -> Dict[str, Any]:
"""
从环境变量和/或配置文件加载配置。
前缀为 MCP_ 的环境变量会自动包含在配置中(移除前缀并转换为小写)。
参数:
config_path: 可选的 JSON 配置文件路径
返回:
包含配置值的字典
"""
# 从空配置开始
config = {
"server": {
"name": "{{cookiecutter.project_name}}",
"version": "{{cookiecutter.version}}"
}
}
# 如果提供了文件路径,则加载
if config_path:
config_file = Path(config_path)
if config_file.exists():
try:
with open(config_file, 'r') as f:
file_config = json.load(f)
# 深度合并配置
_deep_merge(config, file_config)
except Exception as e:
print(f"警告:无法加载配置文件:{e}")
# 还检查环境中的配置文件路径
env_config_path = os.environ.get("MCP_CONFIG_FILE")
if env_config_path and env_config_path != config_path:
try:
with open