MCP 环境变量配置实战:从安装到生产部署的完整指南
作为开发者,当你尝试将 LLM(如 Claude、GPT-4)连接到外部 API 时,最头疼的问题莫过于 API 密钥管理。硬编码不安全,手动设置环境变量又容易遗漏。本文基于 MCP SDK 0.2.0 版本,完整演示如何通过 python-dotenv 实现零配置的环境变量加载,并解决生产环境中的常见坑。
它解决什么问题 / 适用场景
MCP(Model Context Protocol)的核心价值在于让 LLM 安全地与外部服务交互。具体来说,它解决的是以下场景中的密钥管理痛点:
- 多 API 密钥管理:一个 MCP 服务器可能需要同时连接 Brave Search、OpenAI、数据库等多个服务,每个都需要独立的密钥。
- 跨平台部署:开发环境在 macOS,CI 在 Linux,生产环境在 Docker,环境变量加载方式各不相同。
- 安全合规:密钥不能硬编码在代码中,也不能出现在日志或错误信息里。
- 与 MCP Host 集成:Claude Desktop、Cursor 等 Host 通过
mcpServers配置传递环境变量,需要确保服务器端正确接收。
安装与快速上手
基础安装
确保使用 Python 3.10+,创建并激活虚拟环境后执行:
BASHpip install mcp==0.2.0 python-dotenv==1.0.1
重要:必须固定版本号。MCP SDK 仍处于快速迭代期,pip install mcp 可能拉取不兼容的版本。
项目结构
my-mcp-server/
├── .env # 敏感配置,不要提交到 Git
├── .env.example # 模板文件,提交到 Git
├── server.py # MCP 服务器代码
└── requirements.txt # 依赖锁定
最小化服务器代码
PYTHON# server.py import os from dotenv import load_dotenv from mcp.server import Server, NotificationOptions from mcp.server.models import InitializationOptions import mcp.server.stdio # 必须在任何工具注册前加载环境变量 load_dotenv() # 验证关键环境变量 BRAVE_API_KEY = os.getenv("BRAVE_API_KEY") if not BRAVE_API_KEY: raise ValueError("BRAVE_API_KEY environment variable is not set") app = Server("my-mcp-server") @app.list_tools() async def list_tools(): return [ { "name": "search_brave", "description": "Search the web using Brave Search", "inputSchema": { "type": "object", "properties": { "query": {"type": "string"} } } } ] @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "search_brave": # 使用 BRAVE_API_KEY 调用 API return [{"type": "text", "text": f"Searching for {arguments['query']}"}] async def main(): async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): await app.run( read_stream, write_stream, InitializationOptions( server_name="my-mcp-server", server_version="0.1.0" ) ) if __name__ == "__main__": import asyncio asyncio.run(main())
启动方式
正确方式(推荐):
BASHmcp run server.py
mcp run 会自动设置正确的 stdin/stdout 传输协议,避免手动初始化问题。
错误方式(会导致传输警告):
BASHpython server.py
核心配置 / 参数说明
环境变量文件(.env)
.env 文件格式为 KEY=VALUE,每行一个:
ENVBRAVE_API_KEY=your-brave-search-key-here OPENAI_API_KEY=sk-your-openai-api-key DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
关键规则:
- 不要使用引号包裹值(除非值本身包含空格)
- 不要有多余的空格(
KEY = VALUE会解析失败) - 敏感信息不要提交到版本控制
与 MCP Host 集成
当在 Claude Desktop 或 Cursor 中配置 MCP 服务器时,使用以下 JSON 模板:
JSON{ "mcpServers": { "my-mcp-server": { "command": "python", "args": [ "-m", "mcp", "run", "/path/to/your/server.py" ], "env": { "BRAVE_API_KEY": "your-brave-search-key", "OPENAI_API_KEY": "your-openai-api-key" } } } }
注意:Host 配置中的 env 字段会覆盖 .env 文件中的同名变量。如果 Host 已经传递了环境变量,load_dotenv() 不会覆盖已存在的值。
与同类方案对比
| 对比维度 | 环境变量 + python-dotenv | 硬编码密钥 | 密钥管理服务(如 AWS Secrets Manager) |
|---|---|---|---|
| 安全性 | 高:密钥不在代码中,可通过文件权限控制 | 极低:密钥暴露在代码仓库中 | 最高:加密存储,访问审计 |
| 可移植性 | 高:跨平台(Linux/macOS/Windows)一致 | 低:需要为每个环境修改代码 | 中:依赖云服务 SDK |
| 集成复杂度 | 低:两行代码即可集成 | 无:直接使用字符串 | 高:需要配置 IAM 权限和 SDK |
| 部署灵活性 | 高:支持 Docker、云环境、本地开发 | 低:无法动态切换环境 | 中:需要网络访问密钥服务 |
| 调试便利性 | 高:MCP Inspector 可直接验证 | 低:密钥变更需要重新部署 | 中:需要额外日志配置 |
结论:对于大多数 MCP 服务器项目,python-dotenv 是最佳平衡点。只有在需要严格审计和自动轮换密钥的生产环境,才考虑密钥管理服务。
生产环境实践与注意事项
1. 并发冲突
问题:多个 MCP Host 实例同时启动同一个服务器脚本,可能导致文件锁定或资源竞争。
解决方案:
- 为每个 Host 实例使用独立的虚拟环境
- 或使用 Docker 容器隔离:
DOCKERFILEFROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["mcp", "run", "server.py"]
2. 文件权限控制
.env 文件包含敏感信息,必须设置严格权限:
BASH# Linux/macOS chmod 600 .env # 验证权限 ls -la .env # 输出应为:-rw------- 1 user staff 123 Mar 15 10:00 .env
3. Docker 环境变量传递
错误做法:将 .env 文件复制到镜像中。
正确做法:
BASH# 使用 --env-file docker run --env-file .env my-mcp-image # 或 docker-compose.yml version: '3.8' services: mcp-server: image: my-mcp-image env_file: - .env
4. 版本锁定
始终在 requirements.txt 中固定版本:
TXTmcp==0.2.0 python-dotenv==1.0.1
检查当前版本:
BASHpip show mcp
如果版本高于 0.2.0,降级:
BASHpip install mcp==0.2.0
5. 路径问题
在 Docker 或云环境中,.env 文件的路径必须是绝对路径:
PYTHONimport os from pathlib import Path from dotenv import load_dotenv # 推荐:使用绝对路径 env_path = Path(__file__).parent / ".env" load_dotenv(dotenv_path=env_path)
常见报错与排查
错误 1:BRAVE_API_KEY environment variable is not set
根因:load_dotenv() 未在工具注册前调用,或 .env 文件路径错误。
解决步骤:
- 确认
load_dotenv()是server.py的第一行可执行代码 - 检查
.env文件是否与server.py在同一目录 - 验证文件内容格式:
cat .env确保没有多余空格或引号 - 临时添加调试代码:
PYTHONfrom dotenv import load_dotenv import os load_dotenv() print(f"Current directory: {os.getcwd()}") print(f"Files in directory: {os.listdir('.')}") print(f"BRAVE_API_KEY: {os.getenv('BRAVE_API_KEY')}")
错误 2:ModuleNotFoundError: No module named 'mcp'
根因:虚拟环境未激活或依赖未安装。
解决步骤:
BASH# 激活虚拟环境 source .venv/bin/activate # Linux/macOS .venv\Scripts\activate # Windows # 重新安装 pip install mcp==0.2.0 python-dotenv==1.0.1
错误 3:Transport initialization warnings
根因:使用 python server.py 而非 mcp run server.py。
解决步骤:
BASH# 正确启动方式 mcp run server.py # 如果仍出现警告,检查 MCP SDK 版本 pip show mcp
错误 4:ImportError after an SDK update
根因:MCP SDK 自动升级到不兼容版本。
解决步骤:
BASH# 检查当前版本 pip show mcp # 降级到固定版本 pip install mcp==0.2.0 # 永久解决:在 requirements.txt 中固定版本 echo "mcp==0.2.0" >> requirements.txt
常见问题 FAQ
Q: 如何在 Docker 容器中安全地传递环境变量给 MCP 服务器?
A: 不要将 .env 文件复制到镜像中。使用 Docker 的 --env-file 选项或 docker-compose 的 environment 字段传递环境变量。例如:
BASHdocker run --env-file .env my-mcp-image
或者使用密钥管理服务(如 AWS Secrets Manager)在容器启动时动态注入环境变量。确保 load_dotenv() 可以读取挂载的密钥文件路径。
Q: 如果我的团队使用 Windows,环境变量加载会有什么问题?
A: python-dotenv 在 Windows 上工作正常,但需要注意文件路径分隔符。在 .env 文件中,如果包含文件路径,应使用正斜杠(/)或 pathlib 库处理跨平台路径。例如:
ENVDATA_DIR=/data/windows/path
建议在代码中使用 os.path.join 或 pathlib.Path 来构建路径,确保跨平台兼容性。
Q: MCP 服务器需要处理 OAuth2 认证,如何管理令牌?
A: 对于 OAuth2,环境变量应存储客户端 ID 和客户端密钥。在服务器启动时,执行令牌交换并缓存访问令牌。使用 httpx.AsyncClient 的 auth 参数或自定义令牌刷新逻辑。避免在每个工具调用时重新获取令牌,以减少延迟和 API 调用次数。示例:
PYTHONasync def main(): # 预获取令牌 token = await get_oauth_token( client_id=os.getenv("OAUTH_CLIENT_ID"), client_secret=os.getenv("OAUTH_CLIENT_SECRET") ) # 将令牌传递给工具函数 async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): await app.run(read_stream, write_stream, ...)