news 2026/4/17 20:56:55

把你的MCP Server部署到公网,让阿里云上的应用来访问和使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
把你的MCP Server部署到公网,让阿里云上的应用来访问和使用

一、前言

前前后后的给小落同学加了许多的MCP。

但是这些功能之前一直在我本地的小落同学上跑,部署在阿里云ECS上的小落同学因为买的ECS配置太低(99元一年的2H2G特惠主机)跑不动,这个周末在家没事做,想想是不是干脆用frp让公网上的小落同学也可以把这些MCP也都给支持起来。

所以这个周末的任务就是:把原先一直在我本地电脑上跑的小落同学的MCP Server部署到公网,并让阿里云上的小落同学来访问和使用。

目前小落同学支持的MCP包括:

  • fill_time_cost: 每天自动填TAPD的项目的工时。
  • weekly_report: 每周自动写周报并发到我的钉钉。
  • weather_query: 天气查询。
  • content_summary: 文档内容总结。
  • smart_home: 智能家居控制(家里的小米灯光、海信电视、格力空调等)。
  • odd_notifier: 消息通知(将指定的消息发给指定的钉钉群、企业微信群、邮箱等等)。
  • odd_bookmark: 书签收藏(将微信公众号、博客园、知乎、CSDN等网站上的文章收藏并保存到本地的markdown文件,支持图片表格等)。
  • odd_translate: 内容、文章翻译功能。
  • odd_swissknife: 获取IP地址,获取地理坐标等。
  • 其它一些杂七杂八的功能,暂不在此介绍。

既然想了,那不管有没有人用小落同学,咱先给它配上去再说。

二、MCP Server可配置化

1. 新增ODDMCP配置

在小落同学的.env环境变量里新增mcp相关环境变量配置

########################################### ODDMCP 服务配置# 1. 访问 ODDMCP 服务的地址和端口, 以及访问令牌# 2. ODDMCP服务的Redis配置#########################################ODDMCP_SERVER_ADDR="oddmcp.oddmeta.net"# ODDMCP_SERVER_ADDR = "localhost"ODDMCP_SERVER_PORT=9600ODDMCP_SERVER_TOKEN="your_oddmcp_server_token"ODDMCP_REDIS_ADDR="47.116.14.194"# ODDMCP_REDIS_ADDR = "localhost"ODDMCP_REDIS_PORT=63579ODDMCP_REDIS_DB=0ODDMCP_REDIS_PASSWORD=""

2. 同步调整MCP Server和MCP Client中与MCP相关的配置

把原先固定的localhost的地址,改成从环境变量中获取。

1)代码:oddmcp_server.py
defStartOddMCPServerTask():""" 主函数 - 优化后版本,直接在主线程运行 """logger.info("启动MCP服务器...")load_dotenv()oddmcp_server_addr=os.environ.get("ODDMCP_SERVER_ADDR","0.0.0.0")oddmcp_server_port=os.environ.get("ODDMCP_SERVER_PORT",9600)print(f"MCP服务器正在启动 ({oddmcp_server_addr}:{oddmcp_server_port})...")print("按 Ctrl+C 停止")try:# FastMCP (Uvicorn) 会自动处理 Ctrl+C 信号并优雅关闭server.run(transport="http",host=oddmcp_server_addr,port=int(oddmcp_server_port),path="/mcp")exceptKeyboardInterrupt:# 这一步其实通常不需要,因为 Uvicorn 会捕获,但为了保险可以加上print("\n服务器已停止")exceptExceptionase:print(f"服务器运行出错:{e}")importtraceback traceback.print_exc()
2)代码:oddmcp_client.py
classOddmcpClient():def__init__(self)->None:load_dotenv()oddmcp_server_addr=os.environ.get("ODDMCP_SERVER_ADDR","localhost")# 读取Keyoddmcp_server_port=os.environ.get("ODDMCP_SERVER_PORT",9600)# 读取 modelself.uri=f"http://{oddmcp_server_addr}:{oddmcp_server_port}/mcp/"self.tools=Noneself._connected=Falseself.client=Client(StreamableHttpTransport(url=self.uri))
3)代码:oddmcp_status_callback.py
defStartOddmcpRedis():load_dotenv()oddmcp_redis_addr=os.environ.get("ODDMCP_REDIS_ADDR","127.0.0.1")oddmcp_redis_port=os.environ.get("ODDMCP_REDIS_PORT",6379)redis_db=os.environ.get("ODDMCP_REDIS_DB",0)redis_password=os.environ.get("ODDMCP_REDIS_PASSWORD","")logger.info(f"Starting Redis client with address{oddmcp_redis_addr}, port{oddmcp_redis_port}, db{redis_db}")ifredis_password=="":oddmcp_redis_client=redis.Redis(host=oddmcp_redis_addr,port=oddmcp_redis_port,db=redis_db)else:oddmcp_redis_client=redis.Redis(host=oddmcp_redis_addr,port=oddmcp_redis_port,db=redis_db,password=redis_password)returnoddmcp_redis_client oddmcp_redis_client=StartOddmcpRedis()
4)oddagent

同步的时候发现几个新的MCP Server功能还没同步到小落同学上的oddagent,也顺手改了一下。

{"tool_name":"content_sumary","name":"内容摘要,内容总结,会议总结","description":"对会议内容进行摘要总结服务。如:总结会议内容,会议总结,会议内容总结。","parameters":[{"name":"content","desc":"会议内容","type":"string","required":True},],"enabled":True},{"tool_name":"odd_notify","name":"通知","description":"将输入的内容利用odd_notify服务进行通知服务。如:转发一下上面的内容到我的钉钉/企业微信。","parameters":[{"name":"content","desc":"待转发的内容","type":"string","required":True},{"name":"bot","desc":"机器人名称","type":"string","required":True},],"enabled":True},{"tool_name":"odd_translate","name":"翻译","description":"将输入的内容利用odd_translate服务进行翻译服务。如:将英文翻译成中文,将中文翻译成英文。","parameters":[{"name":"content","desc":"待翻译的内容","type":"string","required":True},{"name":"method","desc":"翻译方法。中译英:zh2en,英译中:en2zh,中译日:zh2ja,日译中:ja2zh,中译韩:zh2ko,韩译中:ko2zh","type":"string","required":True},],"enabled":True},{"tool_name":"odd_swissknife","name":"瑞士工具","description":"瑞士工具服务。如:获取IP地址,获取地理坐标。","parameters":[{"name":"method","desc":"处理方法。获取IP地址:get_ip,获取地理坐标:get_geo","type":"string","required":True},],"enabled":True},

三、利用frp来做跳转

ODDMCP用了两个端口,一个是MCP Server所绑定的9600端口,另一个是每个在MCP运行过程中的一些实时进展状态回调时所使用的redis。

1. 客户端配置

代码:frpc-https.toml

################################### 服务5:oddmcp##################################[[proxies]]name="oddmcp"type="tcp"localPort=9600remotePort=9600################################### 服务6:oddmcp-redis##################################[[proxies]]name="oddmcp-redis"type="tcp"localPort=6379remotePort=63579

杀掉并重新启动 frpc

pkillfrpc ./frpc -c frpc-https.toml&

2. 服务端口配置

客户端修改并新增了这两个端口,并且重启了frpc之后,先到ECS服务器端查看一下,端口状态是否都正常。

  • MCP Server端口
ss -tuln|grep:9600# 或netstat-tulnp|grep:9600
  • Redis端口
ss -tuln|grep:9600# 或netstat-tulnp|grep:9600

如果都有正常绑定了,说明frp已经可以工作了。

需要注意的是:服务绑定地址应该是 0.0.0.0:9600,而不是 127.0.0.1:9600(后者只允许本地访问)。

四、阿里云ECS配置

配置好frp后,还需要让阿里云ECS放行这两个端口。

1. 修改ECS安全组配置

打开浏览器,登录**阿里云控制台,进入安全组**配置,并在其中新增、放行9600和63579这两个TCP的端口。

阿里云控制台上的功能比较多,不常用的话,可能要找地址找半天。由于忘记功能名字了,搜索也不好搜索,呵呵。

为方便记录,特把安全组的链接地址也贴一下:https://ecs.console.aliyun.com/securityGroup/region/cn-shanghai

2. 放行防火墙

打开xshell,ssh登录上ECS服务器,查看是否放行 9600/tcp

如果是centos/openEuler操作系统:

sudofirewall-cmd --list-all

若未放行,添加规则:

sudofirewall-cmd --add-port=9600/tcp --permanentsudofirewall-cmd --reload

如果是ubuntu操作系统:

sudoufw status verbose# 若启用,需放行端口sudoufw allow9600/tcp

查看Linux操作系统发行版命令:cat /etc/os-release

五、测试验证

1. 端口开放测试

在命令行里,telnet oddmcp.oddmeta.net 9600,看看能不能连接上。
如果能连接上,说明前面的frp的配置,以及阿里云ECS的配置都已经成功了。

2. 小落同学测试

重启一下小落同学,然后测试一下任意一个mcp的功能:智能体项目给填8小时的工时。然后看一下小落同学的日志输出。

如果看到类似下面这样的日志,说明有问题。

2026-02-0215:09:52 DEBUG oddagent.py:301(60024-33232)- 【意图识别】响应:code=0,purpose_options={'1':'query_weather','2':'weekly_report','3':'set_iot_status','4':'get_iot_status','5':'load_iot_list','6':'bookmark_it','7':'search_web','8':'fill_time_cost','9':'play_music','10':'content_sumary','11':'odd_translate','12':'odd_notify','13':'odd_swissknife'},user_choice=8, 耗时=0.38319110870361332026-02-0215:09:52 INFO oddagent.py:319(60024-33232)- 【意图识别】用户选择了工具:fill_time_cost[DEBUG][2026-02-0215:09:52,521][streamable_http.py:649]Connecting to StreamableHTTP endpoint: http://oddmcp.oddmeta.net:9443/mcp/[DEBUG][2026-02-0215:09:52,523][streamable_http.py:547]Sending client message:root=JSONRPCRequest(method='initialize',params={'protocolVersion':'2025-11-25','capabilities':{},'clientInfo':{'name':'mcp','version':'0.1.0'}},jsonrpc='2.0',id=0)[DEBUG][2026-02-0215:09:52,524][_trace.py:87]connect_tcp.startedhost='oddmcp.oddmeta.net'port=9443local_address=Nonetimeout=5.0socket_options=None[DEBUG][2026-02-0215:09:52,575][_trace.py:87]connect_tcp.failedexception=ConnectError(gaierror(11001,'getaddrinfo failed'))[ERROR][2026-02-0215:09:52,578][oddmcp_client.py:57]连接MCP服务器失败: Client failed to connect:[Errno11001]getaddrinfo failed[ERROR][2026-02-0215:09:52,579][openai_compatible.py:64][OddMCP]获取工具失败,mcp为空或tools为空!MCP服务器未启动?[ERROR][2026-02-0215:09:52,580][openai_compatible.py:113][OddMCP]获取工具失败: fill_time_cost

如果正常的话,日志应该是类似这样的。

[INFO][2026-02-0216:38:13,691][openai_compatible.py:365][OpenAI]工具调用chunk: ChoiceDelta(content=None,function_call=None,refusal=None,role='assistant',tool_calls=[ChoiceDeltaToolCall(index=0,id='call_01e050c62592430f8653d 5',function=ChoiceDeltaToolCallFunction(arguments='{"project_name',name='fill_time_cost'),type='function')],reasoning_content=None)[INFO][2026-02-0216:38:13,691][openai_compatible.py:391][OpenAI]工具调用名称: fill_time_cost[INFO][2026-02-0216:38:13,692][openai_compatible.py:395][OpenAI]工具参数增加:'{"project_name'[INFO][2026-02-0216:38:13,715][openai_compatible.py:365][OpenAI]工具调用chunk: ChoiceDelta(content=None,function_call=None,refusal=None,role=None,tool_calls=[ChoiceDeltaToolCall(index=0,id='',function=ChoiceDeltaToolCallFu nction(arguments='": "智能体',name=None),type='function')],reasoning_content=None)[INFO][2026-02-0216:38:13,715][openai_compatible.py:373][OpenAI]使用最后一个有效工具调用ID: call_01e050c62592430f8653d5[INFO][2026-02-0216:38:13,716][openai_compatible.py:395][OpenAI]工具参数增加:'": "智能体'[INFO][2026-02-0216:38:13,762][openai_compatible.py:365][OpenAI]工具调用chunk: ChoiceDelta(content=None,function_call=None,refusal=None,role=None,tool_calls=[ChoiceDeltaToolCall(index=0,id='',function=ChoiceDeltaToolCallFu nction(arguments='项目", "hours',name=None),type='function')],reasoning_content=None)[INFO][2026-02-0216:38:13,769][openai_compatible.py:373][OpenAI]使用最后一个有效工具调用ID: call_01e050c62592430f8653d5[INFO][2026-02-0216:38:13,769][openai_compatible.py:395][OpenAI]工具参数增加:'项目", "hours'[INFO][2026-02-0216:38:13,772][openai_compatible.py:365][OpenAI]工具调用chunk: ChoiceDelta(content=None,function_call=None,refusal=None,role=None,tool_calls=[ChoiceDeltaToolCall(index=0,id='',function=ChoiceDeltaToolCallFu nction(arguments='": 10',name=None),type='function')],reasoning_content=None)[INFO][2026-02-0216:38:13,772][openai_compatible.py:373][OpenAI]使用最后一个有效工具调用ID: call_01e050c62592430f8653d5[INFO][2026-02-0216:38:13,773][openai_compatible.py:395][OpenAI]工具参数增加:'": 10'[INFO][2026-02-0216:38:13,811][openai_compatible.py:365][OpenAI]工具调用chunk: ChoiceDelta(content=None,function_call=None,refusal=None,role=None,tool_calls=[ChoiceDeltaToolCall(index=0,id='',function=ChoiceDeltaToolCallFu nction(arguments='}',name=None),type='function')])[INFO][2026-02-0216:38:13,811][openai_compatible.py:373][OpenAI]使用最后一个有效工具调用ID: call_01e050c62592430f8653d5[INFO][2026-02-0216:38:13,814][openai_compatible.py:395][OpenAI]工具参数增加:'}'

六、后话

最后,测试是成功了,但是发现的一个问题是我让位于阿里云ECS上的小落同学去访问一个远端的、部署在我自己电脑上的MCP Server,可能是由于我家里网络速度的原因,亦或可能是其他什么原因,总是会特别慢,导致查询MCP Server上任务的运行状态可能会超时,从而拿不到任务运行过程中的步骤列表及每一个步骤的结果。

查询任务状态超时

[INFO][2026-02-0216:38:14,335][oddmcp_client.py:142]Result:{"success":true,"description":"工时填写任务已提交,正在处理中...","data":{"task_id":"f3ffd5c4-1d34-4919-9eb1-7900837ca0af","project_name":"智能体项目","task_name":"time_cost","user_id":"2","hours":10,"status":"pending"}}[INFO][2026-02-0216:38:14,336][openai_compatible.py:442][OddMCP]工具返回: CallToolResult(content=[TextContent(type='text',text='{"success":true,"description":"工时填写任务已提交,正在处理中...","data":{"task_id":"f3ffd5c4-1d34-4919-9eb1-7900837ca0af","project_name":"智能体项目","task_name":"time_cost","user_id":"2","hours":10,"status":"pending"}}',annotations=None,meta=None)],structured_content={'success':True,'description':'工时填写任务已提交,正在处理 中...','data':{'task_id':'f3ffd5c4-1d34-4919-9eb1-7900837ca0af','project_name':'智能体项目','task_name':'time_cost','user_id':'2','hours':10,'status':'pending'}},meta=None,data={'success':True,'description':'工时填写任务已提交,正在处理中...','data':{'task_id':'f3ffd5c4-1d34-4919-9eb1-7900837ca0af','project_name':'智能体项目','task_name':'time_cost','user_id':'2','hours':10,'status':'pending'}},is_error=False)[INFO][2026-02-0216:38:14,337][openai_compatible.py:476][OddMCP]获取到任务ID: f3ffd5c4-1d34-4919-9eb1-7900837ca0af,开始轮询任务状态。[INFO][2026-02-0216:38:14,349][oddmcp_status_callback.py:23]Starting Redis client with address47.116.14.194, port63579, db0[ERROR][2026-02-0216:38:35,396][openai_compatible.py:509][OddMCP]轮询任务状态失败: Timeout connecting to server

家里用的中国移动送的1G的宽带,这网络咋就这么垃圾呢???
除了网络的问题外,初步想了一下可能的优化方向:将Redis客户端做成一个singleton的实例,不要每次都去重新连接redis数据库。后面有空了我再统一再来改一下吧。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/8 12:38:11

深入研究大数据领域 Hadoop 的 Oozie 工作流调度系统

深入研究大数据领域 Hadoop 的 Oozie 工作流调度系统 关键词:Oozie 工作流调度、Hadoop 生态、DAG 任务编排、工作流定义语言、分布式任务调度、ETL 流程管理、批处理作业协调 摘要:本文深入剖析 Hadoop 生态中的核心组件 Oozie,系统讲解其工作流调度原理、架构设计、核心功…

作者头像 李华
网站建设 2026/4/15 14:54:31

FinRobot金融AI代理平台实战指南:从入门到精通大模型在金融领域的应用

FinRobot是一个专为金融领域设计的AI代理平台,整合多种AI技术,提供自动化股票分析、财务评估和报告生成功能。平台采用四层架构,通过感知、大脑和行动模块实现智能决策,配有智能调度器优化任务分配。开发者可利用FinRobot构建金融…

作者头像 李华
网站建设 2026/4/8 15:58:55

**AI漫剧制作工具2025推荐,新手也能快速上手的创作利器

AI漫剧制作工具2025推荐,新手也能快速上手的创作利器 据《2025年中国数字内容产业白皮书》显示,2025年AI视频内容市场规模预计突破800亿元,其中AI漫剧因其制作门槛相对较低、内容形式新颖,成为个人创作者与中小团队入局的热门赛道…

作者头像 李华
网站建设 2026/4/17 2:37:07

英伟达 数字孪生 AODT 下载

登陆 docker login nvcr.ioUsername: $oauthtoken Password: 下载镜像 docker pull nvcr.io/nvidia/aerial/aodt-sim:1.4.0_devel docker pull nvcr.io/nvidia/aerial/aodt-sim:1.4.0_runtime docker pull nvcr.io/nvidia/aerial/aodt-gis:1.4.0 docker pull nvcr.io/nvidia/ae…

作者头像 李华
网站建设 2026/4/16 14:24:57

认知突围:练就看透本质的能力

打破信息茧房,跳出思维定式:不是刷更多信息,而是练会“看透本质”的认知能力 目录 打破信息茧房,跳出思维定式:不是刷更多信息,而是练会“看透本质”的认知能力 一、先破外部茧房:驯化算法,刺破“过滤气泡” 核心实操(依据:过滤气泡的算法驱动特性) 矫正误区 二、再…

作者头像 李华