更新时间:2026-07-20 GMT+08:00
分享

制作MCP Server镜像

一个最小化的MCP Streamable HTTP计算器服务器示例,基于官方mcp SDK的FastMCP实现。适合作为学习MCP协议,快速搭建Streamable HTTP服务的起点。

MCP协议介绍

Model Context Protocol (MCP) 是由Anthropic提出并开源的协议标准,用于标准化大型语言模型(LLM)应用与外部工具、数据源之间的通信方式。它定义了一套统一的JSON-RPC 2.0消息格式,让AI客户端(如 Claude Desktop、Cursor、Claude Code 等)能够发现、调用外部工具,而无需关心底层实现细节。

Streamable HTTP 核心特性

  • 单端点:同一个 URL(如 /mcp)处理所有通信,简化路由和负载均衡配置。
  • POST 请求:客户端向服务器发送 JSON-RPC 请求消息。
  • GET 请求(可选):客户端建立 SSE 流,用于接收服务器主动推送的消息(如日志、资源变更通知)。
  • 内容协商:客户端必须发送 Accept: application/json, text/event-stream,服务器根据场景选择返回同步 JSON 或 SSE 流。
  • 会话管理:通过 Mcp-Session-Id 头部实现有状态会话,支持断线重连和消息重传。
  • 无状态模式:对于简单的工具调用场景,可以直接返回 application/json,无需维护 SSE 连接,更适合 Serverless 和水平扩展。

制作Docker镜像

构建镜像:

#进入mcp代码开发路径
cd mcp_test/

# 生成 arm64 镜像
docker build --platform linux/arm64 -t mcp-calculator:latest .

本地Docker调测

本地启动Docker容器:

docker run -d \
  --name mcp-calculator1 \
  -p 8080:8080 \
  mcp-calculator:latest

本地调测Docker:

# 方式 1:curl 直接调用 initialize
curl -X POST http://127.0.0.1:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 0,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-03-26",
      "capabilities": {},
      "clientInfo": {"name": "test", "version": "1.0.0"}
    }
  }'
#调测结果
event: message
data: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"calculator-mcp-server","version":"1.28.1"}}}

# 方式 2:curl 调用工具
curl -X POST http://127.0.0.1:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "add",
      "arguments": {"a": 3, "b": 5}
    }
  }'
#调测结果
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"8.0"}],"structuredContent":{"result":8.0},"isError":false}}

调测完成后停止并删除容器:

docker stop mcp-calculator1
docker rm mcp-calculator1

上传镜像

将构建的镜像上传至容器镜像服务。

  1. 登录容器镜像服务控制台,选择区域,要和AgentArts区域保持一致,否则无法选择到镜像。
  2. 单击右上角“创建组织”,输入组织名称完成组织创建。请自定义组织名称,本示例使用“demo-886633”,下面的命令中涉及到组织名称“demo-886633”也请替换为自定义的值。
  3. 单击右上角“登录指令”,获取登录访问指令,本文选择复制“通用型登录指令”登录指令。

    以root用户登录本地环境,输入复制的SWR临时登录指令。

  4. 上传镜像至容器镜像服务镜像仓库。

    1. 使用docker tag命令给上传镜像打标签。
      #region和domain信息请替换为实际值,组织名称deep-learning也请替换为自定义的值。
      sudo docker tag mcp-calculator:latest swr.{region-id}.{domain}/demo-886633/mcp-calculator:latest 
      #此处以华为云cn-southwest-2为例 
      sudo docker tag mcp-calculator:latest swr.cn-southwest-2.myhuaweicloud.com/demo-886633/mcp-calculator:latest
    2. 使用docker push命令上传镜像。
      #region和domain信息请替换为实际值,组织名称deep-learning也请替换为自定义的值。 
      sudo docker push swr.{region-id}.{domain}/demo-886633/mcp-calculator:latest
      #此处以华为云cn-southwest-2为例 
      sudo docker push swr.cn-southwest-2.myhuaweicloud.com/demo-886633/mcp-calculator:latest

  5. 完成镜像上传后,在容器镜像服务控制台的“我的镜像”页面可查看已上传的自定义镜像。

部署到华为云AgentArts

  1. 部署智能体运行时,部署时“入栈协议”请选择为“MCP”,具体请参考通过控制台部署智能体运行时

    图1 部署智能体运行时

  2. 云上AgentArts调测。

    1. 列出工具
      curl -X POST https://${RUNTIME_ENDPOINT} \         # RUNTIME_ENDPOINT的获取方式:在智能体运行时列表单击名称,在详情页面的"访问方式"中的"访问地址"
        -H "Content-Type: application/json" \
        -H 'X-Hw-Agentarts-Session-Id: {xxxxxxxxxx}' 
        -H 'Authorization: Bearer {apikey}' 
        -H "Accept: application/json, text/event-stream" \
        -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
    2. 调用工具
      curl -X POST https://${RUNTIME_ENDPOINT} \
        -H "Content-Type: application/json" \
        -H "Accept: application/json, text/event-stream" \
        -H 'Authorization: Bearer {apikey}' 
        -H "Accept: application/json, text/event-stream" \
        -d '{
          "jsonrpc": "2.0",
          "id": 2,
          "method": "tools/call",
          "params": {
            "name": "add",
            "arguments": {"a": 3, "b": 5}
          }
        }'

      如果返回了正确的 JSON-RPC 结果,说明云上部署和调测成功。

代码开发示例

项目结构

  1. 创建项目文件夹:
    mkdir mcp_test
  1. 项目下的工程目录文件:
    mcp_test/
    ├── mcp_server.py          # 主服务器实现
    ├── requirements.txt       # Python 依赖
    ├── Dockerfile             # 自定义镜像构建

完整服务器代码

mcp_server.py:

import logging
import os
import uvicorn
from mcp.server import transport_security
from mcp.server.fastmcp import FastMCP
from mcp.server.streamable_http_manager import TransportSecuritySettings
from starlette.requests import Request
from starlette.responses import JSONResponse
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
transport_security.TransportSecurityMiddleware._validate_host = lambda _self, _host: True
logger.info("MCP Host header validation patched to allow all hosts")
# 创建 MCP 服务器实例
mcp = FastMCP(
    "calculator-mcp-server",
    streamable_http_path="/mcp",
    stateless_http=True,
    transport_security=TransportSecuritySettings(
        enable_dns_rebinding_protection=False,
    ),
)
logger.info(f"MCP transport_security settings: {mcp.settings.transport_security}")
@mcp.tool()
def add(a: float, b: float) -> float:
    """两个数字相加"""
    logger.info(f"add called: {a} + {b}")
    return a + b
@mcp.tool()
def subtract(a: float, b: float) -> float:
    """两个数字相减"""
    logger.info(f"subtract called: {a} - {b}")
    return a - b
@mcp.tool()
def multiply(a: float, b: float) -> float:
    """两个数字相乘"""
    logger.info(f"multiply called: {a} * {b}")
    return a * b
@mcp.tool()
def divide(a: float, b: float) -> float:
    """两个数字相除,除数不能为0"""
    logger.info(f"divide called: {a} / {b}")
    if b == 0:
        raise ValueError("除数不能为0")
    return a / b
@mcp.custom_route("/ping", methods=["GET"])
async def ping(_request: Request) -> JSONResponse:
    """网关探活 ping 接口"""
    return JSONResponse({"status": "ok", "message": "pong"})
# 导出 Streamable HTTP ASGI 应用
app = mcp.streamable_http_app()
def main() -> None:
    """命令行入口,供 uvicorn / 控制台脚本调用。"""
    port = int(os.getenv("PORT", 8080))
    uvicorn.run(app, host="0.0.0.0", port=port)
if __name__ == "__main__":
    main()

Dockerfile:

# 先配置docker内置镜像加速拉取基础镜像,构建阶段pip用华为云源
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
# 华为云pip临时源
RUN pip install --no-cache-dir --upgrade pip \
    -i https://mirrors.huaweicloud.com/repository/pypi/simple \
    --trusted-host mirrors.huaweicloud.com \
    && pip install --no-cache-dir -r requirements.txt \
    -i https://mirrors.huaweicloud.com/repository/pypi/simple \
    --trusted-host mirrors.huaweicloud.com
COPY mcp_server.py .
EXPOSE 8080
CMD ["uvicorn", "mcp_server:app", "--host", "0.0.0.0", "--port", "8080", "--proxy-headers", "--forwarded-allow-ips", "*"]

requirements.txt:

mcp==1.28.1
uvicorn==0.46.0

本地验证

参考环境准备做好环境准备工作。

# 1. 安装依赖
pip install -r requirements.txt -i https://mirrors.huaweicloud.com/repository/pypi/simple --trusted-host mirrors.huaweicloud.com 

# 2. 启动服务器
python mcp_server.py

# 3. 测试 initialize
curl -X POST http://127.0.0.1:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}'
# 测试结果
event: message
data: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"calculator-mcp-server","version":"1.28.1"}}}

# 4. 测试工具调用
curl -X POST http://127.0.0.1:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"add","arguments":{"a":3,"b":5}}}'
# 测试结果
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"8.0"}],"structuredContent":{"result":8.0},"isError":false}}

#停止服务器
ps -ef |grep mcp_server.py
root       12692    5936  0 15:29 pts/0    00:00:00 python mcp_server.py
root       13674    5936  0 15:37 pts/0    00:00:00 grep --color=auto mcp_server.py
(venv) root@ecs-tmp-ggq:~/mcp_test# kill -9 12692

相关文档