一、导读
环境:kylin v10 sp1、Python 3.10 、bisheng 2.2.0
背景:希望实现一个镜像,通过挂载代码的方式实现扩展mcp工具
时间:20260106
说明:最终实现compose.yml配置实现多工具并行
二、功能实现
1、代码实现
使用fastmcp实现stream流式工具样例
# tools/mcp_test.py
from fastmcp import FastMCP
from typing import Annotated
from pydantic import Field
mcp = FastMCP("数据类型验证")
@mcp.tool(
name="greet",
description="验证字符串功能"
)
def greet(name: Annotated[str, "需要打招呼的用户名字"]) -> str:
# 字符串验证
return f"Hello, {name}!"
@mcp.tool(
name="add",
description="整数验证"
)
def add(
a: Annotated[int, Field(default=3, description="数字1", ge=1, le=20)],
b: Annotated[int, Field(default=2, description="数字2", ge=1, le=20)],
) -> int:
# 标注mcp中默认值、参数描述、参数控制说明
return a + b
@mcp.tool(
name="subtract",
description="字典、整数验证",
)
def subtract(a: dict, b: int) -> dict:
a.update({"subtract": b})
return a
@mcp.tool(
name="multiply",
description="集合、布尔值验证",
)
def multiply(a: set, b: bool) -> str:
a.add(b)
return str(a)
@mcp.tool(
name="divide",
description="列表、浮点数验证",
)
def divide(a: list, b: float) -> str:
a.append(b)
return str(a)
if __name__ == "__main__":
mcp.run(
transport="http",
host="0.0.0.0",
port=8001,
log_level="DEBUG",
)
2、脚本执行
python mcp_test.py
3、bisheng测试
我在本地搭建了bisheng的项目,网络本身是互通的。

正常情况,MCP服务配置后,点击刷新,可出现下列工具
三、构建镜像
1、拉取镜像
docker pull docker.1ms.run/library/python:3.10-slim
2、编辑Dockerfile文件
利用dockerfile构建新的镜像
# Dockerfile
# 使用指定基础镜像
FROM docker.1ms.run/library/python:3.10-slim
# 设置时区和编码(可选但推荐)
ENV TZ=Asia/Shanghai \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
# 配置 pip 使用阿里云镜像源(构建时生效)
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ && \
pip config set install.trusted-host mirrors.aliyun.com
# 安装依赖(此时需有网络)
RUN pip install --no-cache-dir --disable-pip-version-check fastmcp==2.14.2
# 清理 pip 缓存(减小镜像体积)
RUN rm -rf /root/.cache
# 设置工作目录
WORKDIR /app
# 复制mcp的工具文件
COPY tools/ tools/
EXPOSE 8000
# 默认启动命令可留空,由 compose 覆盖
CMD ["python", "tools/mcp_test.py"]
3、目录结构
[xxxxxxxxxxx ~]$tree fastmcp-offline-image/
fastmcp-offline-image/
├── Dockerfile # dockerfile文件
└── tools # 所有的工具种类目录
└── mcp_test.py # 某一个测试工具
1 directory, 2 files
4、构建镜像
docker build -t py_mcp_tools:v0.0.1 .
其中,py_mcp_tools为镜像名称,v0.0.1为标签
5、查询是否构建成功
查询如下即成功了
[xxxxxx ~]$docker images | grep py_m
py_mcp_tools v0.0.1 e77d3daa0e9a 2 hours ago 214MB
6、运行测试
docker run -d --name mcp-test -p 8001:8001 py_mcp_tools:v0.0.1
查看日志
[xxxxxxxxx ~]$docker logs -f mcp-test
╭──────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ ▄▀▀ ▄▀█ █▀▀ ▀█▀ █▀▄▀█ █▀▀ █▀█ │
│ █▀ █▀█ ▄▄█ █ █ ▀ █ █▄▄ █▀▀ │
│ │
│ │
│ FastMCP 2.14.2 │
│ https://gofastmcp.com │
│ │
│ 🖥 Server: 数据类型验证 │
│ 🚀 Deploy free: https://fastmcp.cloud │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────╮
│ ✨ FastMCP 3.0 is coming! │
│ Pin fastmcp<3 in production, then upgrade when you're ready. │
╰──────────────────────────────────────────────────────────────────────────────╯
[01/06/26 13:42:43] INFO Starting MCP server '数据类型验证' server.py:2582
with transport 'http' on
http://0.0.0.0:8000/mcp
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit)
7、bisheng添加测试

日志变化
[xxxxxxx ~]$docker logs -f mcp-test
╭──────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ ▄▀▀ ▄▀█ █▀▀ ▀█▀ █▀▄▀█ █▀▀ █▀█ │
│ █▀ █▀█ ▄▄█ █ █ ▀ █ █▄▄ █▀▀ │
│ │
│ │
│ FastMCP 2.14.2 │
│ https://gofastmcp.com │
│ │
│ 🖥 Server: 数据类型验证 │
│ 🚀 Deploy free: https://fastmcp.cloud │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────╮
│ ✨ FastMCP 3.0 is coming! │
│ Pin fastmcp<3 in production, then upgrade when you're ready. │
╰──────────────────────────────────────────────────────────────────────────────╯
[01/06/26 13:42:43] INFO Starting MCP server '数据类型验证' server.py:2582
with transport 'http' on
http://0.0.0.0:8000/mcp
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: 10.62.79.13:58838 - "POST /mcp HTTP/1.1" 200 OK
INFO: 10.62.79.13:58864 - "GET /mcp HTTP/1.1" 200 OK
INFO: 10.62.79.13:58852 - "POST /mcp HTTP/1.1" 202 Accepted
INFO: 10.62.79.13:58874 - "POST /mcp HTTP/1.1" 200 OK
四、compose配置多工具
再配置一个MySQL工具,占用8002端口,实现docker compose启动多个工具
1、MySQL工具集
# tools/mcp_mysql.py
import pymysql
from fastmcp import FastMCP
from typing import Dict, List, Any, Optional
# 创建 FastMCP 实例
mcp = FastMCP("MySQL Management Tool")
# 数据库连接配置 (这些是默认值,实际连接时会使用用户提供的参数)
DB_CONFIG = {
'host': 'localhost',
'port': 3306,
'user': 'your_username',
'password': 'your_password',
'database': 'your_database',
'charset': 'utf8mb4', # 推荐使用 utf8mb4 支持完整的 UTF-8
}
@mcp.tool(
name="test_connection",
description="测试与 MySQL 数据库的连接。需要提供主机、端口、用户名和密码。"
)
def test_connection(host: str, port: int, username: str, password: str, database: Optional[str] = None) -> Dict[str, Any]:
"""
测试数据库连接。
"""
try:
config = {
'host': host,
'port': port,
'user': username,
'password': password,
'charset': 'utf8mb4',
}
# 如果提供了数据库名,则加入配置,用于测试连接到特定数据库
if database:
config['database'] = database
connection = pymysql.connect(**config)
connection.close()
return {
"status": "success",
"message": f"连接到数据库 {host}:{port} (database: {database or 'not specified'}) 成功!"
}
except Exception as e:
return {
"status": "error",
"message": f"连接失败: {str(e)}"
}
@mcp.tool(
name="list_tables",
description="查询当前数据库下的所有表名。"
)
def list_tables(host: str, port: int, username: str, password: str, database: str) -> Dict[str, Any]:
"""
查询数据库中的所有表。
"""
try:
config = {**DB_CONFIG, 'host': host, 'port': port,
'user': username, 'password': password, 'database': database}
connection = pymysql.connect(**config)
cursor = connection.cursor()
cursor.execute("SHOW TABLES;")
tables = [row[0]
for row in cursor.fetchall()] # row[0] 因为SHOW TABLES返回单列
cursor.close()
connection.close()
return {
"status": "success",
"database": database,
"tables": tables
}
except Exception as e:
return {
"status": "error",
"message": f"查询表列表失败: {str(e)}"
}
@mcp.tool(
name="describe_table",
description="查询指定表的结构(列信息)。"
)
def describe_table(host: str, port: int, username: str, password: str, database: str, table_name: str) -> Dict[str, Any]:
"""
查询表结构。
"""
try:
config = {**DB_CONFIG, 'host': host, 'port': port,
'user': username, 'password': password, 'database': database}
connection = pymysql.connect(**config)
cursor = connection.cursor()
# 使用 DESCRIBE 语句获取表结构
cursor.execute(f"DESCRIBE `{table_name}`;")
# 或者使用 SHOW COLUMNS 语句,结果类似
# cursor.execute(f"SHOW COLUMNS FROM `{table_name}`;")
columns_info = cursor.fetchall()
# 将结果转换为更易读的字典列表格式
column_descriptions = []
for col in columns_info:
# DESCRIBE 返回的每一行是一个元组 (Field, Type, Null, Key, Default, Extra)
column_descriptions.append({
"Field": col[0],
"Type": col[1],
"Null": col[2],
"Key": col[3],
"Default": col[4],
"Extra": col[5]
})
cursor.close()
connection.close()
return {
"status": "success",
"database": database,
"table": table_name,
"structure": column_descriptions
}
except Exception as e:
return {
"status": "error",
"message": f"查询表 {table_name} 结构失败: {str(e)}"
}
@mcp.tool(
name="fetch_all_data",
description="查询指定表的所有数据。"
)
def fetch_all_data(host: str, port: int, username: str, password: str, database: str, table_name: str) -> Dict[str, Any]:
"""
查询表的所有数据。
"""
try:
config = {**DB_CONFIG, 'host': host, 'port': port,
'user': username, 'password': password, 'database': database}
connection = pymysql.connect(**config)
cursor = connection.cursor(
pymysql.cursors.DictCursor) # 使用字典游标,返回结果为字典格式
# 构建并执行查询语句
query = f"SELECT * FROM `{table_name}`;"
cursor.execute(query)
rows = cursor.fetchall()
cursor.close()
connection.close()
return {
"status": "success",
"database": database,
"table": table_name,
"data": rows
}
except Exception as e:
return {
"status": "error",
"message": f"查询表 {table_name} 数据失败: {str(e)}"
}
@mcp.tool(
name="show_user_privileges",
description="查看当前登录用户的权限。"
)
def show_user_privileges(host: str, port: int, username: str, password: str, database: str) -> Dict[str, Any]:
"""
查看用户权限。
"""
try:
config = {**DB_CONFIG, 'host': host, 'port': port,
'user': username, 'password': password, 'database': database}
connection = pymysql.connect(**config)
cursor = connection.cursor()
# 执行 SHOW GRANTS 语句
cursor.execute("SHOW GRANTS;")
grants = [row[0]
for row in cursor.fetchall()] # row[0] 因为SHOW GRANTS返回单列
cursor.close()
connection.close()
return {
"status": "success",
"user": username,
"grants": grants
}
except Exception as e:
return {
"status": "error",
"message": f"查询用户 {username} 权限失败: {str(e)}"
}
if __name__ == "__main__":
mcp.run(
transport="http",
host="0.0.0.0", # 绑定到所有网络接口
port=8002, # 指定端口
log_level="INFO", # 设置日志级别
)
该代码已测试,此处不再复现。可按照第一个进行复测
2、构建新镜像
dockerfile内容需要增加安装pymysql的内容,如下:
# Dockerfile
# 安装依赖(此时需有网络)
RUN pip install --no-cache-dir --disable-pip-version-check fastmcp==2.14.2 pymysql==1.1.2
按照上述构建镜像的方法构建新镜像,命令如下:
docker build -t py_mcp_tools:v0.0.2 .
3、构建compose文件
通过在compose中实现两个(多个)服务,同时启动
# compose.yml
version: '3.10'
x-service-template: &service-template
image: 10.64.7.121:30085/agentops/py_mcp_tools:v0.0.3
volumes:
- ./tools:/app/tools
working_dir: /app/tools
restart: unless-stopped
services:
mcp-test:
<<: *service-template
container_name: mcp-test
command: python mcp_test.py
ports:
- "8001:8001"
mcp-mysql:
<<: *service-template
container_name: mcp-mysql
command: python mcp_mysql.py
ports:
- "8002:8002"
4、目录结构
[xxxxxxxxxx fastmcp-offline-image]$tree
.
├── compose.yml
├── Dockerfile
├── readme.md
└── tools
├── mcp_mysql.py
└── mcp_test.py
1 directory, 5 files
5、启动
在dockerfile同级目录下执行:
[xxxxxxxx fastmcp-offline-image]$docker-compose up -d
6、日志查看
[xxxxxxx fastmcp-offline-image]$docker-compose logs -f
mcp-mysql |
mcp-mysql |
mcp-mysql | ╭──────────────────────────────────────────────────────────────────────────────╮
mcp-mysql | │ │
mcp-mysql | │ │
mcp-mysql | │ ▄▀▀ ▄▀█ █▀▀ ▀█▀ █▀▄▀█ █▀▀ █▀█ │
mcp-mysql | │ █▀ █▀█ ▄▄█ █ █ ▀ █ █▄▄ █▀▀ │
mcp-mysql | │ │
mcp-mysql | │ │
mcp-mysql | │ FastMCP 2.14.2 │
mcp-mysql | │ https://gofastmcp.com │
mcp-mysql | │ │
mcp-test |
mcp-test |
mcp-mysql | │ 🖥 Server: MySQL Management Tool │
mcp-mysql | │ 🚀 Deploy free: https://fastmcp.cloud │
mcp-test | ╭──────────────────────────────────────────────────────────────────────────────╮
mcp-test | │ │
mcp-test | │ │
mcp-mysql | │ │
mcp-test | │ ▄▀▀ ▄▀█ █▀▀ ▀█▀ █▀▄▀█ █▀▀ █▀█ │
mcp-test | │ █▀ █▀█ ▄▄█ █ █ ▀ █ █▄▄ █▀▀ │
mcp-test | │ │
mcp-test | │ │
mcp-test | │ FastMCP 2.14.2 │
mcp-test | │ https://gofastmcp.com │
mcp-test | │ │
mcp-mysql | ╰──────────────────────────────────────────────────────────────────────────────╯
mcp-mysql | ╭──────────────────────────────────────────────────────────────────────────────╮
mcp-mysql | │ ✨ FastMCP 3.0 is coming! │
mcp-mysql | │ Pin fastmcp<3 in production, then upgrade when you're ready. │
mcp-mysql | ╰──────────────────────────────────────────────────────────────────────────────╯
mcp-mysql |
mcp-mysql |
mcp-mysql | [01/07/26 15:51:41] INFO Starting MCP server 'MySQL server.py:2582
mcp-mysql | Management Tool' with transport
mcp-mysql | 'http' on http://0.0.0.0:8002/mcp
mcp-mysql | INFO: Started server process [1]
mcp-mysql | INFO: Waiting for application startup.
mcp-mysql | INFO: Application startup complete.
mcp-test | │ 🖥 Server: 数据类型验证 │
mcp-test | │ 🚀 Deploy free: https://fastmcp.cloud │
mcp-test | │ │
mcp-test | ╰──────────────────────────────────────────────────────────────────────────────╯
mcp-test | ╭──────────────────────────────────────────────────────────────────────────────╮
mcp-test | │ ✨ FastMCP 3.0 is coming! │
mcp-test | │ Pin fastmcp<3 in production, then upgrade when you're ready. │
mcp-test | ╰──────────────────────────────────────────────────────────────────────────────╯
mcp-test |
mcp-test |
mcp-test | [01/07/26 15:51:41] INFO Starting MCP server '数据类型验证' server.py:2582
mcp-test | with transport 'http' on
mcp-test | http://0.0.0.0:8001/mcp
mcp-test | INFO: Started server process [1]
mcp-mysql | INFO: Uvicorn running on http://0.0.0.0:8002 (Press CTRL+C to quit)
mcp-mysql | INFO: 10.62.79.13:33960 - "POST /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:33980 - "GET /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:33972 - "POST /mcp HTTP/1.1" 202 Accepted
mcp-mysql | INFO: 10.62.79.13:33982 - "POST /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:33990 - "DELETE /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:41548 - "POST /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:41572 - "GET /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:41556 - "POST /mcp HTTP/1.1" 202 Accepted
mcp-mysql | INFO: 10.62.79.13:41580 - "POST /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:41588 - "DELETE /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:51588 - "POST /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:51600 - "GET /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:51594 - "POST /mcp HTTP/1.1" 202 Accepted
mcp-mysql | INFO: 10.62.79.13:51614 - "POST /mcp HTTP/1.1" 200 OK
mcp-mysql | INFO: 10.62.79.13:51620 - "DELETE /mcp HTTP/1.1" 200 OK
mcp-test | INFO: Waiting for application startup.
mcp-test | INFO: Application startup complete.
mcp-test | INFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit)
mcp-test | INFO: 10.62.79.13:54932 - "POST /mcp HTTP/1.1" 200 OK
mcp-test | INFO: 10.62.79.13:54956 - "GET /mcp HTTP/1.1" 200 OK
mcp-test | INFO: 10.62.79.13:54942 - "POST /mcp HTTP/1.1" 202 Accepted
mcp-test | INFO: 10.62.79.13:54962 - "POST /mcp HTTP/1.1" 200 OK
mcp-test | [01/07/26 15:53:54] DEBUG [数据类型验证] Handler called: server.py:1155
mcp-test | list_tools
mcp-test | INFO: 10.62.79.13:54972 - "DELETE /mcp HTTP/1.1" 200 OK
mcp-test | INFO: 10.62.79.13:49088 - "POST /mcp HTTP/1.1" 200 OK
mcp-test | INFO: 10.62.79.13:49114 - "GET /mcp HTTP/1.1" 200 OK
mcp-test | INFO: 10.62.79.13:49102 - "POST /mcp HTTP/1.1" 202 Accepted
7、bisheng验证

OK,到此结束
162

被折叠的 条评论
为什么被折叠?



