Docker 安装 MCPO
Open WebUI 集成 MCP 服务,需要使用 MCPO,记录下 Docker 安装 MCPO 的过程。
MCPO 简介
MCPO:MCP(模型上下文协议)到 OpenAPI 代理服务器,可以实现将标准的 MCP 服务转换为 OpenAPI 服务。
json
{
"mcpServers": {
"time": {
"command": "uvx",
"args": ["mcp-server-time", "--local-timezone=Asia/Shanghai"]
}
}
}
json
{
"openapi": "3.1.0",
"info": {
"title": "mcp-time",
"description": "mcp-time MCP Server",
"version": "1.13.1"
},
"servers": [
{
"url": "/time"
}
],
"paths": {
"/get_current_time": {
"post": {
"summary": "Get Current Time",
"description": "Get current time in a specific timezones",
"operationId": "tool_get_current_time_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/get_current_time_form_model"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response Tool Get Current Time Post"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
},
"security": [
{
"HTTPBearer": []
}
]
}
},
"/convert_time": {
"post": {
"summary": "Convert Time",
"description": "Convert time between timezones",
"operationId": "tool_convert_time_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/convert_time_form_model"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response Tool Convert Time Post"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
},
"security": [
{
"HTTPBearer": []
}
]
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"convert_time_form_model": {
"properties": {
"source_timezone": {
"type": "string",
"title": "Source Timezone",
"description": "Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'Asia/Shanghai' as local timezone if no source timezone provided by the user."
},
"time": {
"type": "string",
"title": "Time",
"description": "Time to convert in 24-hour format (HH:MM)"
},
"target_timezone": {
"type": "string",
"title": "Target Timezone",
"description": "Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use 'Asia/Shanghai' as local timezone if no target timezone provided by the user."
}
},
"type": "object",
"required": [
"source_timezone",
"time",
"target_timezone"
],
"title": "convert_time_form_model"
},
"get_current_time_form_model": {
"properties": {
"timezone": {
"type": "string",
"title": "Timezone",
"description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'Asia/Shanghai' as local timezone if no timezone provided by the user."
}
},
"type": "object",
"required": [
"timezone"
],
"title": "get_current_time_form_model"
}
},
"securitySchemes": {
"HTTPBearer": {
"type": "http",
"scheme": "bearer"
}
}
}
}
部署准备
官方原版的 Dockerfile 太简陋,翻阅 B 站时意外找到一位大佬封装的 Docker 部署物料 mcpo_docker_use,很适合快速部署。
创建 mcpo
目录,上传下方物料,根据需要修改 .env
和 config.json
文件。
text
.
├─ docker-compose.yml
├─ Dockerfile
├─ start.sh(容器入口点脚本,读取 config.json 并动态安装 MCP 工具)
├─ .env(用于存放敏感信息和配置)
└─ config.json(配置需要启动的 MCP 服务器)
yaml
services:
mcpo:
build:
context: .
args:
# 从 .env 文件读取 PIP_SOURCE 并传递给 Dockerfile
- PIP_SOURCE=${PIP_SOURCE:-} # 使用 :-} 避免未设置时出错
container_name: mcpo
restart: unless-stopped
ports:
- 8000:8000
volumes:
- ./config.json:/app/config/config.json
- ./logs:/app/logs
- ./data:/app/data
- ./node_modules:/app/node_modules # 持久化 node_modules
- ./.npm:/app/.npm # 持久化 npm 缓存
- ./.uv_cache:/app/.cache/uv # 持久化 uv 缓存 (注意路径改为 /app/.cache/uv)
# 使用 env_file 或 environment 传递运行时环境变量
env_file:
- .env # 加载 .env 文件中的所有变量作为运行时环境变量
# 或者只传递需要的变量
# environment:
# - MCPO_API_KEY=${MCPO_API_KEY}
healthcheck:
test: CMD curl -f http://localhost:8000/docs
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
dockerfile
FROM python:3.13-slim
LABEL org.opencontainers.image.title="mcpo"
LABEL org.opencontainers.image.description="Docker image for mcpo (Model Context Protocol OpenAPI Proxy)"
LABEL org.opencontainers.image.licenses="MIT"
LABEL maintainer="flyfox666@gmail.com, biguncle2017@gmail.com"
# Install base dependencies, Node.js, and Git in a single layer (as root)
RUN set -eux; \
# Configure apt sources (use Aliyun mirror)
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/debian.sources; do \
[ -f "$f" ] && sed -i 's|deb.debian.org|mirrors.aliyun.com|g; s|security.debian.org|mirrors.aliyun.com|g' "$f" || true; \
done && \
# Install curl and ca-certificates first for NodeSource script
apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && \
# Add NodeSource repo for Node.js 22.x
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
# Update again after adding new source and install remaining packages, including git
&& apt-get update && apt-get install -y --no-install-recommends \
bash \
jq \
nodejs \
git \
# Clean up apt cache
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
# Verify npm/npx and git installation path
&& echo "--- Debug: Verifying npm/npx/git path ---" \
&& which npm \
&& which npx \
&& which git || echo "npm, npx, or git not found immediately after install"
# Create application directories (as root)
RUN mkdir -p /app/config /app/logs /app/data /app/node_modules /app/.npm /app/.cache/uv
# Copy start script (as root)
COPY start.sh /app/start.sh
RUN chmod +x /app/start.sh
# Create non-root user and grant ownership (as root)
RUN useradd -m -d /app -s /bin/bash appuser && chown -R appuser:appuser /app
# Switch to non-root user
USER appuser
WORKDIR /app
# Set environment variables for the non-root user
# Ensure user's local bin and Node's/Git's global bin are in PATH
ENV HOME=/app \
PATH=/app/.local/bin:/usr/bin:/usr/local/bin:$PATH \
UV_CACHE_DIR=/app/.cache/uv \
NPM_CONFIG_CACHE=/app/.npm \
MCPO_LOG_DIR="/app/logs"
# Accept PIP_SOURCE build argument
ARG PIP_SOURCE=""
# Make PIP_SOURCE available as an environment variable for the RUN layer below
ENV PIP_SOURCE=${PIP_SOURCE}
# Install uv using pip, respecting PIP_SOURCE via environment variable (as non-root user)
RUN set -eux; \
echo "--- Debug: Checking PIP_SOURCE value ---"; \
echo "PIP_SOURCE Env Var: ${PIP_SOURCE:-<not set or empty>}"; \
# Set PIP_INDEX_URL environment variable if PIP_SOURCE is valid
if [ -n "$PIP_SOURCE" ] && echo "$PIP_SOURCE" | grep -q '^https://'; then \
export PIP_INDEX_URL="$PIP_SOURCE"; \
echo "已设置 PIP_INDEX_URL 环境变量为: $PIP_INDEX_URL"; \
else \
echo "未设置自定义 pip 源,将使用默认源"; \
# Unset PIP_INDEX_URL just in case it was inherited
unset PIP_INDEX_URL; \
fi; \
# Install/upgrade pip and install uv for the user (pin version for reproducibility)
echo "--- Debug: Upgrading pip ---"; \
python -m pip install --upgrade pip --user; \
echo "--- Debug: Installing uv (using source: ${PIP_INDEX_URL:-<default>}) ---"; \
python -m pip install --user uv==0.6.14; \
# Verify uv installation
echo "--- Debug: Verifying uv installation ---"; \
uv --version
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8000/docs || exit 1
ENTRYPOINT ["/app/start.sh"]
shell
#!/bin/bash
set -e
# 设置所有缓存和 HOME 目录到 /app,防止权限问题
export HOME=/app
export UV_CACHE_DIR=/app/.cache/uv
export NPM_CONFIG_CACHE=/app/.npm
date_str=$(date +"%Y%m%d_%H%M%S")
log_file="/app/logs/mcpo_${date_str}.log"
# 确保持久化目录存在(全部在 /app 下)
mkdir -p /app/data /app/node_modules /app/.npm /app/.cache/uv
# 动态安装 config.json 中声明的 mcp 工具
if [ -f /app/config/config.json ]; then
echo "检测到 /app/config/config.json,准备动态安装 MCP 工具..."
jq -r '.mcpServers | keys[]' /app/config/config.json | while read -r key; do
(
command=$(jq -r ".mcpServers[\"$key\"].command" /app/config/config.json)
args=$(jq -r ".mcpServers[\"$key\"].args | @sh" /app/config/config.json)
envs=$(jq -r ".mcpServers[\"$key\"].env // {} | to_entries[]? | \"export \(.key)=\\\"\(.value)\\\"\"" /app/config/config.json)
# 设置环境变量
if [ -n "$envs" ]; then
eval "$envs"
fi
# 动态安装
if [ "$command" = "uvx" ]; then
echo "使用 uvx 安装: $args"
eval uvx $args
elif [ "$command" = "npx" ]; then
echo "使用 npx 安装: $args"
eval npx $args
else
echo "未知 command: $command,跳过 $key"
fi
)
done
else
echo "未检测到 /app/config/config.json,跳过 MCP 工具动态安装。"
fi
# 启动主服务
if [ ! -z "$MCPO_API_KEY" ]; then
uvx mcpo --host 0.0.0.0 --port 8000 --config /app/config/config.json --api-key "$MCPO_API_KEY" 2>&1 | tee -a "$log_file"
else
uvx mcpo --host 0.0.0.0 --port 8000 --config /app/config/config.json 2>&1 | tee -a "$log_file"
fi
properties
# Docker 构建时使用的 pip 源 (可选, 留空则使用默认源)
# 例如: 阿里云 PyPI 镜像
PIP_SOURCE=https://mirrors.aliyun.com/pypi/simple/
# 例如: 清华大学 PyPI 镜像
# PIP_SOURCE=https://pypi.tuna.tsinghua.edu.cn/simple
# mcpo 运行时需要的 API Key (必需)
MCPO_API_KEY=123456
json
{
"mcpServers": {
"time": {
"command": "uvx",
"args": ["mcp-server-time", "--local-timezone=Asia/Shanghai"]
},
"fetch": {
"command": "uvx",
"args": ["mcp-server-fetch"]
}
}
}
运行容器
运行下方命令构建镜像并运行容器,成功后访问 http://ip:8000/docs
查看生成的 Swagger 文档。
shell
docker-compose up -d
运行容器后,如果需要更新 MCP Server,在修改完 config.json
后重启容器即可。
shell
docker restart mcpo
参考资料
- mcpo的docker封装,后台稳定运行:https://www.bilibili.com/video/BV1QtRmYhEqx/
- mcpo_docker_use:https://github.com/flyfox666/mcpo_docker_use