MCP Server模板工程:一套可复用的Server开发脚手架 摘要一套可复用的MCP Server开发脚手架包含项目模板、CI/CD配置、测试框架、文档生成和发布流程让你5分钟启动一个新的MCP Server项目。MCP Server模板工程一套可复用的Server开发脚手架我写了十几个MCP Server后发现一个问题每个项目都要重复搭一遍架子。配日志、写配置管理、搞测试、写Dockerfile、配CI/CD这些活每个项目都差不多但每次都得从头来。后来我花了一个周末把公共部分抽出来做了一套脚手架新项目基于它创建五分钟就能跑起来。这篇把这套脚手架完整分享出来包括项目结构、配置管理、日志体系、测试框架、Docker和CI/CD。为什么需要脚手架先看一个对比。从零开始搭一个生产级MCP Server我统计过自己花的时间。任务从零开始用脚手架项目结构设计30分钟0日志配置20分钟0配置管理40分钟0单元测试框架30分钟0Docker化25分钟5分钟CI/CD流水线60分钟5分钟README和文档20分钟10分钟总计约3.5小时20分钟省下来的三个多小时全花在写业务逻辑上。而且脚手架经过多次项目验证踩过的坑都填了比每次临时搭的靠谱得多。项目结构脚手架的目录结构长这样。mcp-server-template/ src/ __init__.py server.py # Server入口创建FastMCP实例 config.py # 配置管理从环境变量读取 logging_setup.py # 日志配置 tools/ # 工具目录每个文件一组工具 __init__.py example_tool.py resources/ # 资源目录 __init__.py example_resource.py prompts/ # 提示模板目录 __init__.py example_prompt.py tests/ __init__.py test_tools.py # 工具单元测试 test_resources.py # 资源单元测试 conftest.py # pytest fixtures Dockerfile docker-compose.yml .env.example # 环境变量模板 pyproject.toml # 项目配置和依赖 Makefile # 常用命令快捷方式 .github/ workflows/ ci.yml # GitHub Actions CI配置 README.md这个结构有几个设计考量。tools、resources、prompts分开目录对应MCP的三类原语加新功能时一眼知道放哪。tests目录跟src平级保持独立性。配置相关的文件放根目录方便查找。配置管理配置管理是脚手架里最先要搞好的部分。我见过太多MCP Server把配置散落在代码各处改一个配置要翻好几个文件。我的方案是用一个Settings类集中管理所有配置从环境变量读取有默认值有类型校验。日志体系MCP Server的日志有个铁律stdio模式下只能写stderr。我在第19篇和第35篇都提过这里不重复原因直接看脚手架怎么处理。脚手架的日志配置支持两种模式。stdio模式下日志写stderrHTTP模式下日志写文件。通过环境变量自动切换不用改代码。测试框架测试是最容易被忽略的部分。很多人写完MCP Server直接扔给Claude Desktop测出了问题根本不知道是协议层的问题还是业务逻辑的问题。脚手架内置了pytest测试框架能单独测工具函数也能测整个Server的协议交互。Docker和CI/CDDocker化让MCP Server能一键部署。CI/CD让每次提交代码自动跑测试保证质量。脚手架里都配好了改个项目名就能用。完整代码项目配置文件 pyproject.toml# pyproject.toml # MCP Server 模板项目的依赖和构建配置 [project] name mcp-server-template version 0.1.0 description MCP Server 开发脚手架模板 requires-python 3.10 dependencies [ mcp[cli]1.2.0, # MCP Python SDK httpx0.27.0, # 异步HTTP客户端 pydantic2.0.0, # 数据验证和配置管理 python-dotenv1.0.0, # .env 文件支持 ] [project.optional-dependencies] dev [ pytest8.0.0, # 测试框架 pytest-asyncio0.23.0, # 异步测试支持 pytest-cov4.1.0, # 覆盖率统计 ] [project.scripts] mcp-server src.server:main [tool.pytest.ini_options] asyncio_mode auto testpaths [tests]配置管理 config.py# src/config.py# 集中管理所有配置项从环境变量读取# 用 pydantic 做类型校验和默认值importosfromfunctoolsimportlru_cachefrompydanticimportFieldfrompydantic_settingsimportBaseSettingsclassSettings(BaseSettings):MCP Server 全局配置。 所有配置通过环境变量注入 代码里不硬编码任何敏感信息。 # ---------- Server 基础配置 ----------server_name:strField(defaultmcp-template,descriptionServer 名称显示在 MCP 客户端,)server_version:strField(default0.1.0,descriptionServer 版本号,)server_instructions:strField(defaultMCP Server 模板请替换为你的服务说明,description给客户端和 LLM 看的服务说明,)# ---------- 传输配置 ----------transport:strField(defaultstdio,description传输方式: stdio 或 streamable-http,)host:strField(default0.0.0.0,descriptionHTTP 模式监听地址,)port:intField(default8000,descriptionHTTP 模式监听端口,)# ---------- 日志配置 ----------log_level:strField(defaultINFO,description日志等级: DEBUG/INFO/WARNING/ERROR,)log_file:strField(default,description日志文件路径为空则写 stderr,)# ---------- 业务配置示例 ----------# 这里放你的业务相关配置比如 API Key、数据库连接串example_api_key:strField(default,description示例 API Key生产环境必须通过环境变量设置,)max_results:intField(default20,description工具返回结果的最大数量,)model_config{env_file:.env,env_file_encoding:utf-8,extra:ignore,# 忽略未定义的环境变量}lru_cachedefget_settings()-Settings:获取全局配置单例。 用 lru_cache 缓存整个进程只创建一次。 测试时可以用 get_settings.cache_clear() 重置。 returnSettings()日志配置 logging_setup.py# src/logging_setup.py# 日志配置自动适配 stdio 和 HTTP 两种传输模式importloggingimportsysfromsrc.configimportget_settingsdefsetup_logging()-None:配置日志系统。 stdio 模式: 日志写 stderr绝不写 stdout HTTP 模式: 日志写文件或 stderr 这个函数在 Server 启动时调用一次。 settingsget_settings()# 构建日志格式fmt(%(asctime)s [%(name)s] %(levelname)s %(message)s)datefmt%Y-%m-%d %H:%M:%S# 根据传输模式选择日志输出目标ifsettings.transportstdio:# stdio 模式: 必须写 stderr# 写 stdout 会破坏 JSON-RPC 消息handlerlogging.StreamHandler(sys.stderr)elifsettings.log_file:# HTTP 模式且配置了日志文件handlerlogging.FileHandler(settings.log_file)else:# HTTP 模式没有配置文件写 stderrhandlerlogging.StreamHandler(sys.stderr)handler.setFormatter(logging.Formatter(fmt,datefmt))# 配置根 loggerroot_loggerlogging.getLogger()root_logger.setLevel(settings.log_level)root_logger.addHandler(handler)# 降低第三方库的日志等级logging.getLogger(httpx).setLevel(logging.WARNING)logging.getLogger(mcp).setLevel(logging.INFO)Server入口 server.py# src/server.py# MCP Server 入口组装配置、日志、工具、资源、提示importloggingfromsrc.configimportget_settingsfromsrc.logging_setupimportsetup_loggingfromsrc.tools.example_toolimportregister_example_toolsfromsrc.resources.example_resourceimportregister_example_resourcesfromsrc.prompts.example_promptimportregister_example_prompts loggerlogging.getLogger(__name__)defcreate_server():创建并配置 MCP Server 实例。 负责组装所有组件: 1. 读取配置 2. 配置日志 3. 创建 FastMCP 实例 4. 注册工具、资源、提示 frommcp.server.fastmcpimportFastMCP# 读取配置settingsget_settings()# 配置日志setup_logging()logger.info(f启动{settings.server_name}v{settings.server_version})# 创建 FastMCP 实例mcpFastMCP(settings.server_name,instructionssettings.server_instructions,)# 注册各类原语register_example_tools(mcp)register_example_resources(mcp)register_example_prompts(mcp)logger.info(所有组件注册完成)returnmcpdefmain():主入口函数。 根据配置选择传输方式启动 Server。 settingsget_settings()mcpcreate_server()logger.info(f传输方式:{settings.transport})ifsettings.transportstdio:mcp.run(transportstdio)elifsettings.transportstreamable-http:mcp.run(transportstreamable-http,hostsettings.host,portsettings.port,)else:raiseValueError(f不支持的传输方式:{settings.transport})if__name____main__:main()示例工具 example_tool.py# src/tools/example_tool.py# 示例工具演示脚手架中的工具开发规范importloggingfrommcp.server.fastmcpimportFastMCP,Contextfromsrc.configimportget_settings loggerlogging.getLogger(__name__)defregister_example_tools(mcp:FastMCP)-None:注册示例工具到 MCP 实例。 每个工具模块都提供一个 register 函数 server.py 调用它完成注册。 这种模式让工具可以按模块拆分。 settingsget_settings()mcp.tool()asyncdeftemplate_search(query:str,limit:int10)-str:搜索模板内容。 这是一个示例工具实际项目中替换成你的业务逻辑。 Args: query: 搜索关键词 limit: 返回结果数量上限默认10 Returns: 搜索结果字符串 logger.info(f搜索: query{query}, limit{limit})# 限制最大返回数量用配置中的 max_resultsactual_limitmin(limit,settings.max_results)# 示例: 返回模拟结果# 真实项目这里调数据库或外部 APIresults[f结果{i}: 匹配 {query} 的内容foriinrange(1,actual_limit1)]return\n.join(results)mcp.tool()asyncdeftemplate_health_check(ctx:Context)-str:检查 Server 健康状态。 返回 Server 的运行信息包括版本和配置概要。 可用于客户端验证连接是否正常。 logger.info(执行健康检查)# 通过 Context 获取会话信息session_idid(ctx)# 简化示例return(fServer:{settings.server_name}\nfVersion:{settings.server_version}\nfTransport:{settings.transport}\nfStatus: healthy\nfSession:{session_id})logger.info(示例工具注册完成: template_search, template_health_check)示例资源 example_resource.py# src/resources/example_resource.py# 示例资源演示脚手架中的资源开发规范importloggingfrommcp.server.fastmcpimportFastMCPfromsrc.configimportget_settings loggerlogging.getLogger(__name__)defregister_example_resources(mcp:FastMCP)-None:注册示例资源到 MCP 实例。settingsget_settings()mcp.resource(config://server-info)asyncdefget_server_info()-str:返回 Server 配置信息。 客户端可以读取这个资源了解 Server 的配置。 注意不要在资源里暴露敏感信息。 return(fServer Name:{settings.server_name}\nfVersion:{settings.server_version}\nfTransport:{settings.transport}\nfMax Results:{settings.max_results}\nfAPI Key Configured:{yesifsettings.example_api_keyelseno})logger.info(示例资源注册完成: config://server-info)示例提示 example_prompt.py# src/prompts/example_prompt.py# 示例提示模板演示脚手架中的提示开发规范importloggingfrommcp.server.fastmcpimportFastMCP loggerlogging.getLogger(__name__)defregister_example_prompts(mcp:FastMCP)-None:注册示例提示模板到 MCP 实例。mcp.prompt()deftemplate_code_review(code:str)-str:生成代码审查提示。 Args: code: 要审查的代码片段 Returns: 格式化的代码审查提示 return(请审查以下代码关注:\n1. 代码风格和可读性\n2. 潜在的 bug\n3. 性能问题\n4. 安全隐患\n\nf代码:\n\n{code}\n)logger.info(示例提示模板注册完成: template_code_review)测试文件 conftest.py 和 test_tools.py# tests/conftest.py# pytest 全局 fixturesimportpytestfromsrc.configimportget_settingspytest.fixture(autouseTrue)defreset_settings():每个测试前重置配置缓存。 确保测试之间配置不互相干扰。 get_settings.cache_clear()yieldget_settings.cache_clear()pytest.fixturedefmcp_instance():创建测试用的 MCP 实例。fromsrc.serverimportcreate_serverreturncreate_server()# tests/test_tools.py# 工具单元测试importpytestfrommcp.server.fastmcpimportFastMCPfromsrc.tools.example_toolimportregister_example_toolspytest.fixturedefmcp():创建一个只注册了工具的测试 MCP 实例。instanceFastMCP(test-server)register_example_tools(instance)returninstancepytest.mark.asyncioasyncdeftest_template_search_basic(mcp):测试基本搜索功能。# 通过 MCP 实例调用工具resultawaitmcp.call_tool(template_search,{query:test,limit:5})# 验证返回结果包含预期内容asserttestinstr(result)assert结果 1instr(result)pytest.mark.asyncioasyncdeftest_template_search_limit(mcp):测试结果数量限制。resultawaitmcp.call_tool(template_search,{query:hello,limit:3})# 验证返回了 3 条结果result_strstr(result)assert结果 3inresult_strassert结果 4notinresult_strpytest.mark.asyncioasyncdeftest_template_health_check(mcp):测试健康检查工具。resultawaitmcp.call_tool(template_health_check,{})result_strstr(result)asserthealthyinresult_strassertmcp-templateinresult_strDockerfile# Dockerfile # MCP Server 容器化配置 FROM python:3.12-slim # 设置工作目录 WORKDIR /app # 安装系统依赖 RUN apt-get update apt-get install -y --no-install-recommends \ curl \ rm -rf /var/lib/apt/lists/* # 复制依赖文件并安装 COPY pyproject.toml ./ RUN pip install --no-cache-dir mcp[cli] httpx pydantic python-dotenv # 复制源代码 COPY src/ ./src/ COPY .env.example ./.env.example # 设置环境变量默认值 ENV MCP_TRANSPORTstreamable-http ENV MCP_HOST0.0.0.0 ENV MCP_PORT8000 ENV LOG_LEVELINFO # 暴露端口HTTP 模式用stdio 模式不需要 EXPOSE 8000 # 健康检查仅 HTTP 模式有效 HEALTHCHECK --interval30s --timeout5s --retries3 \ CMD curl -f http://localhost:8000/health || exit 1 # 启动命令 CMD [python, -m, src.server]docker-compose.yml# docker-compose.yml# 用 docker-compose 一键启动 MCP Serverversion:3.9services:mcp-server:build:.ports:-8000:8000environment:-MCP_TRANSPORTstreamable-http-MCP_HOST0.0.0.0-MCP_PORT8000-LOG_LEVELINFO-EXAMPLE_API_KEY${EXAMPLE_API_KEY}restart:unless-stoppedvolumes:-./logs:/app/logsGitHub Actions CI配置# .github/workflows/ci.yml# CI 流水线: 代码推送时自动跑测试name:CIon:push:branches:[main,develop]pull_request:branches:[main]jobs:test:runs-on:ubuntu-lateststrategy:matrix:python-version:[3.10,3.11,3.12]steps:# 检出代码-uses:actions/checkoutv4# 安装 Python-name:Set up Python ${{matrix.python-version}}uses:actions/setup-pythonv5with:python-version:${{matrix.python-version}}# 安装依赖-name:Install dependenciesrun:|pip install -e .[dev]# 跑测试-name:Run testsrun:|pytest --covsrc --cov-reportxml# 上传覆盖率报告-name:Upload coverageuses:codecov/codecov-actionv4if:matrix.python-version 3.12Makefile# Makefile # 常用命令快捷方式 .PHONY: install dev test run run-http docker-build docker-run clean # 安装生产依赖 install: pip install -e . # 安装开发依赖 dev: pip install -e .[dev] # 跑测试 test: pytest --covsrc --cov-reportterm-missing # stdio 模式运行 run: python -m src.server # HTTP 模式运行 run-http: MCP_TRANSPORTstreamable-http python -m src.server # 构建 Docker 镜像 docker-build: docker build -t mcp-server-template . # Docker 运行 docker-run: docker-compose up -d # 清理 clean: find . -type d -name __pycache__ -exec rm -rf {} find . -type f -name *.pyc -delete rm -rf .pytest_cache htmlcov环境变量模板 .env.example# .env.example# 复制为 .env 并填入实际值# Server 配置SERVER_NAMEmcp-templateSERVER_VERSION0.1.0SERVER_instructionsMCP Server 模板# 传输配置TRANSPORTstdioHOST0.0.0.0PORT8000# 日志配置LOG_LEVELINFOLOG_FILE# 业务配置EXAMPLE_API_KEYMAX_RESULTS20效果验证用脚手架创建新项目的步骤。# 1. 复制模板目录cp-rmcp-server-template my-new-servercdmy-new-server# 2. 安装依赖makedev# 3. 复制环境变量模板cp.env.example .env# 编辑 .env 填入你的配置# 4. 跑测试maketest# 应该看到 3 个测试全部通过# 5. stdio 模式启动makerun# 或用 MCP Inspector 调试mcp dev src/server.py# 6. HTTP 模式启动makerun-http# 浏览器访问 http://localhost:8000# 7. Docker 部署makedocker-buildmakedocker-run开发新工具只需要三步。在src/tools/下创建新文件写工具函数在server.py里加一行register_xxx_tools(mcp)。在tests/下写测试跑make test验证。常见问题与避坑坑一pydantic-settings 版本冲突。脚手架用 pydantic v2 的 BaseSettings但有些老教程还在用 pydantic v1 的写法。如果你看到from pydantic import BaseSettings报错说明装的是 v2。v2 的正确导入是from pydantic_settings import BaseSettings需要额外装pip install pydantic-settings。我第一次用的时候卡了半小时。坑二pytest-asyncio 配置不生效。测试异步工具函数时如果报RuntimeError: asyncio.run() cannot be called from a running event loop检查pyproject.toml里的asyncio_mode有没有设成auto。设成auto后所有async测试函数自动加event loop不用手动标pytest.mark.asyncio。坑三Docker镜像太大。用python:3.12基础镜像构建出来的镜像有800多MB。换成python:3.12-slim后降到200MB左右。如果还想更小用多阶段构建构建阶段装编译依赖运行阶段只复制编译好的包。我最后压到了120MB。坑四CI里Docker构建超时。GitHub Actions的免费runner构建Docker镜像有时会超时特别是第一次要拉基础镜像。解决办法是在CI里加Docker layer缓存用actions/cachev4缓存/var/lib/docker目录。加了缓存后构建时间从5分钟降到1分钟。坑五.env文件被提交到Git。这是个低级但致命的错误。.env里有API Key等敏感信息提交到Git后公开仓库就泄露了。脚手架的.gitignore里已经加了.env但有一次我手动加了一个.env.production忘了加到gitignore差点出事。建议在.gitignore里直接写.env*把所有env开头的文件都忽略掉。小结这套脚手架把MCP Server开发中重复性的工作全封装好了。项目结构清晰工具、资源、提示各归各位。配置管理集中环境变量驱动不硬编码。日志自动适配传输模式。测试框架开箱即用。Docker和CI/CD配好即用。从零开发到用脚手架效率提升十倍以上。更重要的是脚手架经过多项目验证坑都填过了稳定性有保障。你拿去改个项目名就能开始写业务逻辑。接下来三篇进入Client集成篇讲怎么把你的MCP Server接到Claude Desktop、Cursor和TRAE里。相关推荐Server最佳实践我从20个MCP Server中总结的经验Python MCP SDK入门FastMCP快速开发部署上线Docker容器化与云端部署