MCP 环境变量配置实战:从安装到生产部署的完整指南

主题: mcp-server-environment-variables-config更新于: 2026/6/29作者:AgentFactory 技术团队

作为开发者,当你尝试将 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+,创建并激活虚拟环境后执行:

BASH
pip 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())

启动方式

正确方式(推荐):

BASH
mcp run server.py

mcp run 会自动设置正确的 stdin/stdout 传输协议,避免手动初始化问题。

错误方式(会导致传输警告):

BASH
python server.py

核心配置 / 参数说明

环境变量文件(.env)

.env 文件格式为 KEY=VALUE,每行一个:

ENV
BRAVE_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 容器隔离:
DOCKERFILE
FROM 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 中固定版本:

TXT
mcp==0.2.0
python-dotenv==1.0.1

检查当前版本:

BASH
pip show mcp

如果版本高于 0.2.0,降级:

BASH
pip install mcp==0.2.0

5. 路径问题

在 Docker 或云环境中,.env 文件的路径必须是绝对路径:

PYTHON
import 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 文件路径错误。

解决步骤

  1. 确认 load_dotenv()server.py 的第一行可执行代码
  2. 检查 .env 文件是否与 server.py 在同一目录
  3. 验证文件内容格式:cat .env 确保没有多余空格或引号
  4. 临时添加调试代码:
PYTHON
from 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-composeenvironment 字段传递环境变量。例如:

BASH
docker run --env-file .env my-mcp-image

或者使用密钥管理服务(如 AWS Secrets Manager)在容器启动时动态注入环境变量。确保 load_dotenv() 可以读取挂载的密钥文件路径。

Q: 如果我的团队使用 Windows,环境变量加载会有什么问题?

A: python-dotenv 在 Windows 上工作正常,但需要注意文件路径分隔符。在 .env 文件中,如果包含文件路径,应使用正斜杠(/)或 pathlib 库处理跨平台路径。例如:

ENV
DATA_DIR=/data/windows/path

建议在代码中使用 os.path.joinpathlib.Path 来构建路径,确保跨平台兼容性。

Q: MCP 服务器需要处理 OAuth2 认证,如何管理令牌?

A: 对于 OAuth2,环境变量应存储客户端 ID 和客户端密钥。在服务器启动时,执行令牌交换并缓存访问令牌。使用 httpx.AsyncClientauth 参数或自定义令牌刷新逻辑。避免在每个工具调用时重新获取令牌,以减少延迟和 API 调用次数。示例:

PYTHON
async 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, ...)

相关深度解决方案

在配置当前服务时,如果您遇到了数据库锁死或需要更高并发的读写控制,建议配合参考我们整理的 SQLite MCP 服务的高级缓存配置指南 来提升响应速度。