
# 制作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镜像
- 参考[环境准备](https://support.huaweicloud.com/highcode-agentarts/agentarts_10_040.html#agentarts_10_040__section147911114188)做好环境准备工作。
- 已完成MCP Server代码开发任务，代码示例可参考[代码开发示例]。
**构建镜像：**
```
#进入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信息请替换为实际值，组织名称demo-886633也请替换为自定义的值。
      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信息请替换为实际值，组织名称demo-886633也请替换为自定义的值。 
      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"，具体请参考[通过控制台部署智能体运行时](https://support.huaweicloud.com/highcode-agentarts/agentarts_10_031.html)。
   
   图1部署智能体运行时   
   ![](https://support.huaweicloud.com/highcode-agentarts/zh-cn_image_0000002643362304.png "点击放大")
   
   
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
   ```
   

2. 项目下的工程目录文件：
   ```
   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
```
**本地验证**
参考[环境准备](https://support.huaweicloud.com/highcode-agentarts/agentarts_10_040.html#agentarts_10_040__section147911114188)做好环境准备工作。
```
# 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
```
