MCP服务器写好了怎么分享?上传发布的完整指南
写在前面
上一篇文章我们聊了怎么从零写一个MCP服务器。但写出来只是第一步——如果你写的数据库查询MCP服务器只有你自己能用,那价值就大打折扣了。怎么让团队里的其他人、甚至全世界的Agent都能用上你的工具?这就是今天要聊的话题:MCP服务器的上传和发布。
说实话,MCP服务器的发布方式比我想象的要多。从最简单的"丢个GitHub链接",到打包成PyPI包,到上传到MCP市场,再到企业内部私有部署,不同场景有不同的最佳实践。我自己在发布第一个MCP服务器的时候,走了不少弯路——一开始直接把代码丢给同事,结果人家环境不一样、依赖装不上,折腾了半天才跑起来。后来慢慢摸索出了几种靠谱的发布方式。
这篇文章我会把MCP服务器的发布方式从头到尾讲一遍,每种方式的优缺点、适用场景、具体操作步骤,以及我踩过的坑。希望能帮你少走弯路。
一、为什么要花心思做发布?
可能有人觉得:"不就是个Python脚本吗?直接发给别人不就行了?"我一开始也是这么想的,直到现实给了我一记耳光。
我第一次把写好的MCP服务器发给同事,他运行的时候报了一堆错:
- Python版本不对,他用的3.9,我用的3.11
- 依赖包版本冲突,他环境里已经装了旧版的某个库
- 数据库路径写死了,他本地没有那个数据库
- 环境变量没配置,API Key不知道在哪设
前前后后折腾了快一个小时才跑起来。从那以后我就意识到:发布不是可选项,是MCP服务器开发的必要环节。 一个别人跑不起来的工具,等于没有。
好的发布应该做到:
- 一键启动:别人拿到之后,最少的步骤就能跑起来
- 环境隔离:不污染使用者的本地环境
- 配置清晰:需要改什么、怎么改,写得明明白白
- 版本可追溯:出了问题能知道是哪个版本,能回滚
带着这几个标准,我们来看看具体的发布方式。
二、方式一:直接分享代码(最简单,但最不靠谱)
最简单的方式就是把代码文件直接发给别人,或者丢到GitHub上。
my-mcp-server/
├── server.py # MCP服务器主文件
├── requirements.txt # 依赖列表
├── .env.example # 环境变量示例
└── README.md # 使用说明
使用者需要:
git clone https://github.com/yourname/my-mcp-server.git
cd my-mcp-server
pip install -r requirements.txt
cp .env.example .env
# 编辑.env填入自己的配置
python server.py
优点:
- 零成本,不需要任何额外工具
- 代码透明,别人可以审查和修改
- 适合个人项目和快速原型
缺点:
- 对使用者有技术要求,得会装Python、配环境
- 依赖冲突问题解决不了
- 没有版本管理,别人不知道用的是哪个版本
- 出了问题排查困难
适用场景:自己用、给技术团队内部用、快速验证想法。
如果用这种方式,README一定要写清楚:
- Python版本要求
- 安装步骤
- 环境变量配置
- 启动命令
- 常见问题
我后来给自己的所有MCP服务器都加了一个标准的README模板,省了很多答疑的时间。
三、方式二:打包成PyPI包(Python生态的标准做法)
如果你的MCP服务器是Python写的,打包成PyPI包是最自然的方式。使用者只需要pip install就能装上,然后一条命令启动。
打包步骤
1. 整理项目结构
my-mcp-server/
├── pyproject.toml # 项目配置
├── README.md # 项目说明
├── LICENSE # 开源协议
└── src/
└── my_mcp_server/
├── __init__.py
├── server.py # MCP服务器主文件
└── cli.py # 命令行入口
2. 编写pyproject.toml
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-mcp-server"
version = "0.1.0"
description = "一个数据库查询MCP服务器"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [{name = "你的名字", email = "[email protected]"}]
dependencies = [
"mcp>=1.0.0",
"httpx>=0.25.0",
]
[project.scripts]
my-mcp-server = "my_mcp_server.cli:main"
[project.urls]
Homepage = "https://github.com/yourname/my-mcp-server"
Repository = "https://github.com/yourname/my-mcp-server"
3. 编写CLI入口
# src/my_mcp_server/cli.py
import asyncio
import click
from .server import server
@click.command()
@click.option("--host", default="0.0.0.0", help="监听地址")
@click.option("--port", default=8000, type=int, help="监听端口")
@click.option("--transport", type=click.Choice(["stdio", "sse"]), default="stdio")
def main(host, port, transport):
"""启动MCP服务器"""
if transport == "stdio":
asyncio.run(server.run_stdio())
else:
asyncio.run(server.run_sse(host=host, port=port))
if __name__ == "__main__":
main()
4. 构建和上传
# 安装构建工具
pip install build twine
# 构建
python -m build
# 上传到PyPI(需要先注册账号)
twine upload dist/*
上传成功后,别人就可以这样用了:
pip install my-mcp-server
my-mcp-server --transport stdio
在Agent的MCP配置里:
{
"mcpServers": {
"my-server": {
"command": "my-mcp-server",
"args": ["--transport", "stdio"]
}
}
}
优点:
- Python生态标准,开发者熟悉
- 自动处理依赖安装
- 版本管理清晰,
pip install my-mcp-server==0.1.0 - 一条命令启动,使用简单
缺点:
- 只适合Python写的MCP服务器
- 还是需要使用者有Python环境
- PyPI上的包名可能被占用
适用场景:Python写的MCP服务器、面向开发者群体、需要版本管理的项目。
我自己的MCP服务器现在都是用这种方式发布的,团队里的人pip install一下就能用,比之前发代码文件省心多了。
四、方式三:上传到MCP市场(最符合MCP生态的方式)
随着MCP生态的发展,出现了专门的MCP服务器市场和目录。比较知名的有:
- Smithery(smithery.ai):MCP服务器的应用商店
- MCP Hub:社区维护的MCP服务器目录
- Awesome MCP:GitHub上的精选列表
在这些平台上发布MCP服务器,能让更多人发现和使用你的工具。
以Smithery为例,发布流程大致是:
- 注册账号,连接GitHub
- 选择你的MCP服务器仓库
- 填写基本信息(名称、描述、图标、分类)
- 配置启动命令和环境变量
- 提交审核,通过后上架
上架后,用户可以在Smithery上一键安装你的MCP服务器到支持的客户端(比如Claude Desktop、Cursor等)。
// 用户在Claude Desktop的配置里,只需要加一行
{
"mcpServers": {
"my-server": {
"url": "https://smithery.ai/server/@yourname/my-mcp-server"
}
}
}
Smithery会帮你处理远程运行,用户不需要本地安装任何东西。
优点:
- 曝光度高,能被更多人发现
- 用户使用门槛低,不需要本地安装
- 平台帮你处理运行环境
- 有评分和评论,能获得反馈
缺点:
- 依赖第三方平台,平台政策变化会影响你
- 可能需要付费(平台抽成或运行费用)
- 审核流程,不是即时上线
- 不适合包含敏感数据的私有工具
适用场景:公开的通用工具、想积累用户和口碑、不涉及敏感数据。
我有一个天气查询的MCP服务器上传到了Smithery,目前有几百个用户在用,还挺有成就感的。
五、方式四:HelloAgents平台上传
如果你用的是HelloAgents框架,它内置了MCP服务器的上传和管理功能。这是最省心的方式,尤其是对HelloAgents生态内的用户。
HelloAgents的MCP服务器上传流程:
1. 准备MCP服务器
确保你的MCP服务器符合HelloAgents的规范:
- 支持stdio传输
- 有明确的工具描述和参数schema
- 环境变量通过配置传入,不要硬编码
2. 打包
HelloAgents支持两种打包方式:
# 方式A:直接上传Python项目
helloagents mcp pack ./my-mcp-server --output my-server.zip
# 方式B:Docker镜像(推荐,环境隔离最好)
docker build -t my-mcp-server:0.1.0 .
docker save my-mcp-server:0.1.0 -o my-server.tar
Dockerfile参考:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "server.py"]
3. 上传到HelloAgents平台
# 登录
helloagents login
# 上传
helloagents mcp upload ./my-server.zip \
--name "数据库查询助手" \
--description "支持订单查询和统计的MCP服务器" \
--version "0.1.0" \
--env "DB_HOST=请输入数据库地址" \
--env "DB_PORT=5432"
上传成功后,会得到一个服务器ID。其他HelloAgents用户可以直接通过ID安装:
helloagents mcp install server_abc123
4. 管理和更新
# 查看已上传的服务器
helloagents mcp list
# 发布新版本
helloagents mcp upload ./my-server-v2.zip --version "0.2.0"
# 查看使用统计
helloagents mcp stats server_abc123
优点:
- 跟HelloAgents生态无缝集成
- 平台帮你处理运行和扩缩容
- 有使用统计和用户管理
- 环境变量配置友好
- 支持私有和公开两种模式
缺点:
- 绑定HelloAgents生态,非HelloAgents用户用不了
- 可能有平台费用
适用场景:HelloAgents生态内的工具、企业内部工具分发、需要托管运行的场景。
六、方式五:Docker私有部署(企业内部首选)
如果你的MCP服务器涉及内部数据、不能放到公网上,那Docker私有部署是最佳选择。
1. 编写Dockerfile
FROM python:3.11-slim
WORKDIR /app
# 先装依赖,利用Docker缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制代码
COPY . .
# 非root用户运行,更安全
RUN useradd -m appuser
USER appuser
EXPOSE 8000
CMD ["python", "server.py", "--transport", "sse", "--host", "0.0.0.0", "--port", "8000"]
2. 构建和推送镜像
# 构建
docker build -t registry.mycompany.com/mcp/db-query:0.1.0 .
# 推送到私有镜像仓库
docker push registry.mycompany.com/mcp/db-query:0.1.0
3. 部署到K8s或服务器
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: db-query-mcp
spec:
replicas: 2
selector:
matchLabels:
app: db-query-mcp
template:
metadata:
labels:
app: db-query-mcp
spec:
containers:
- name: server
image: registry.mycompany.com/mcp/db-query:0.1.0
ports:
- containerPort: 8000
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-credentials
key: host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
name: db-query-mcp
spec:
selector:
app: db-query-mcp
ports:
- port: 8000
targetPort: 8000
kubectl apply -f k8s-deployment.yaml
部署后,公司内部的Agent通过SSE方式连接:
from mcp.client.sse import sse_client
from mcp.client.session import ClientSession
async with sse_client("http://db-query-mcp.internal:8000/sse") as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("query_orders", {"start_date": "2026-01-01"})
优点:
- 完全私有,数据不出内网
- 环境隔离,不依赖宿主机
- 可扩缩容,高可用
- 跟企业现有DevOps流程集成
缺点:
- 需要有Docker和K8s基础设施
- 运维成本较高
- 对小团队来说可能过重
适用场景:企业内部工具、涉及敏感数据、需要高可用和扩缩容。
我们公司内部的MCP服务器都是这种方式部署的,跑在K8s上,配合内部服务发现,用起来很稳。
七、版本管理和更新
不管用哪种发布方式,版本管理都很重要。我踩过的最大的坑就是"改了代码但没更新版本号",结果用户反馈的bug我这边已经修了,但他用的还是旧版本,排查了半天才发现是版本不对。
我的版本管理规范:
1. 语义化版本号
用主版本.次版本.修订号的格式:
- 主版本:不兼容的API变更
- 次版本:向下兼容的功能性新增
- 修订号:向下兼容的问题修正
比如0.1.0 → 0.1.1(修bug)→ 0.2.0(加功能)→ 1.0.0(正式发布)
2. 更新日志(CHANGELOG)
每个版本都记录改了什么:
# Changelog
## [0.2.0] - 2026-10-11
### Added
- 新增query_order_stats工具,支持订单统计
- 支持按客户ID筛选订单
### Fixed
- 修复日期格式校验不严格的问题
- 修复大查询结果导致内存溢出的问题
## [0.1.0] - 2026-09-25
### Added
- 初始版本,支持list_tables和query_orders工具
3. 废弃提示
如果要移除某个工具或参数,先在新版本里加废弃警告,至少保留一个版本再移除:
@server.call_tool()
async def call_tool(name, arguments):
if name == "old_query":
logger.warning("old_query已废弃,请使用query_orders")
# ... 仍然正常工作,但提示用户迁移
八、安全注意事项
发布MCP服务器的时候,安全是最容易被忽略但又最重要的环节。我见过太多MCP服务器把API Key硬编码在代码里、或者没有做输入校验导致SQL注入。
几个必须注意的安全点:
1. 绝对不要把密钥硬编码在代码里
# 绝对不要这样!
API_KEY = "sk-abc123def456"
DB_PASSWORD = "mypassword123"
# 应该这样
import os
API_KEY = os.environ.get("API_KEY")
DB_PASSWORD = os.environ.get("DB_PASSWORD")
代码仓库(即使是私有的)也可能泄露,密钥一定要通过环境变量或配置文件传入。
2. 输入校验,防止注入攻击
MCP服务器的参数来自Agent,而Agent的输入来自用户——这意味着参数可能是恶意的。一定要做校验:
# 不好的做法:直接拼接SQL
query = f"SELECT * FROM orders WHERE status = '{status}'"
# 好的做法:参数化查询
query = "SELECT * FROM orders WHERE status = ?"
cursor.execute(query, (status,))
3. 限制资源使用
加超时、加返回条数限制、加速率限制,防止恶意调用把你的服务器打挂:
# 超时
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.get(url)
# 返回条数限制
limit = min(arguments.get("limit", 50), 100) # 最多100条
4. SSE模式加认证
如果用SSE模式部署在公网上,一定要加认证,不然任何人都能调用你的工具:
# 简单的API Key认证
from fastapi import Header, HTTPException
async def verify_auth(authorization: str = Header(None)):
if authorization != f"Bearer {EXPECTED_KEY}":
raise HTTPException(status_code=401, detail="Unauthorized")
九、我踩过的坑
最后说几个发布过程中踩的坑:
坑1:依赖没写全,别人装完跑不起来
有一次我本地能跑,发给同事就报ModuleNotFoundError。查了半天发现是我本地环境里装了某个库,但没写进requirements.txt。解决办法:用pip freeze或者pipreqs生成依赖列表,并且在干净的虚拟环境里测试一遍。
坑2:路径写死了
代码里写了/Users/myname/data/db.sqlite,别人电脑上当然没有这个路径。所有路径都要可配置,或者用相对路径。
坑3:版本号忘了更新
改了代码重新上传,但版本号还是旧的,用户那边缓存了旧版本。每次发布一定要更新版本号,并且在更新日志里写清楚改了什么。
坑4:SSE模式没加认证被人扫到了
有一次我把一个测试用的MCP服务器部署到公网,没加认证,结果被端口扫描器扫到了,有人调用我的工具刷了一堆请求。后来加了认证和速率限制才解决。公网部署一定要加认证!
坑5:日志里打印了敏感信息
调试的时候把API Key和数据库密码打到日志里了,日志文件又不小心提交到了GitHub。虽然很快删掉了,但已经被人fork了。从那以后我加了一个代码检查步骤,提交前自动扫描敏感信息。
写在最后
MCP服务器的发布,说简单也简单——丢个代码文件就行;说复杂也复杂——要考虑环境隔离、版本管理、安全认证、高可用部署。选择哪种方式,取决于你的使用场景和用户群体。
我的建议是:
- 自己用 → 直接跑代码就行
- 给技术同事用 → 打PyPI包
- 给大众用 → 上传MCP市场
- 企业内部用 → Docker私有部署
- HelloAgents用户 → 用HelloAgents平台上传
不用追求一步到位,可以先从最简单的方式开始,用户多了再逐步升级发布方式。
这篇文章是10.5节的第二篇,也是构建自定义MCP服务器的收尾。下一篇我们进入10.6本章总结,把整个通信协议章节的内容串起来回顾一遍。
你发布过MCP服务器吗?用的什么方式?遇到过什么问题?欢迎在评论区交流。
下一篇预告:本章总结。我们会把第十章智能体通信协议的全部内容做一个系统性的回顾,梳理MCP、A2A、ANP三个协议的知识体系,以及它们在实际项目中的选型和组合策略。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2401_89160889/article/details/168251080




