目录

前言

MCP Web Chat

功能概要说明

MCP Web Chat代码调用结构说明

api动态生成MCP Server

方法一(之前的方法)

方法二(现在的方法)

做个比较

相关代码

相关问题解决说明

稳定性

由此引申而来的异步任务问题

MCP周期问题(待讨论)

结语


前言

前面三篇文章,循序渐进的从MCP概念、初步使用到多tool调用到现有api动态生成mcp server,逐步像实际项目迈进。

本篇是MCP终极篇。将介绍如何实现MCP的Web Chat,以及过程中遇到的一些问题与解决。相关代码,我已经在Github开源。【github地址:loli0123456789/MCPWebChat: 基于MCP的WebChat】

本篇主要内容:

  • 功能概要说明

  • MCP Web Chat主体架构设计说明

通过该部分内容,你可以更好的理解为何如此规划设计

  • api动态生成MCP Server 功能说明

这块会说下动态生成MCP Server的两种思路与比较

  • 相关问题解决说明

MCP系列文章,可通过如下链接快速回看:

全网少有-通过Python调用MCP_python mcp-CSDN博客

MCP第二弹,支持Webapi调用与动态MCP【附完整代码】-CSDN博客

MCP 第三波升级!Function Call 多步调用 + 流式输出详解-CSDN博客

MCP Web Chat

功能概要说明

1、通过数据库或配置文件管理MCP Server

2、内置通用MCP Server,如获取当前时间、访问网页内容等,可自行根据需要添加

3、内置根据api信息自动生成tool的MCP Server

4、可以根据获取的MCP tools进行Web界面对话,对话可流式显示工具调用过程、最终结果

MCP Web Chat代码调用结构说明

在网上的多数MCP代码案例中,多数MCP的类是Server、Client,其中Client负责连接Server,同时负责通过大模型关联tools进行chat对话。这里Server只有在需要自己创建MCP Server的情况才需要,因此主要就是Client。

但是这里有两个问题:

1)把大模型放到Client里,有多个Server需要连接怎么办?

2)有多个用户要进行chat对话,消息如何管理?

基于以上问题,我对此的规划是:MCP_Client、MCP_Host、MCP_Chat。

MCP_Client:用于定义MCP Client

注意: 后续为了解决MCP Client长时间会挂掉的问题,同时为了降低自己写代码的复杂性,Client直接使用了百度appbuilder实现的MCP_Client功能。

MCP_Host:负责连接多个MCP_Client,全局单例

MCP_Chat:负责关联MCP_Host,并对接用户Chat,每次对话会实例化一个MCP_Chat对象,实现每个对话话的消息记录单独管理

一些MCP相关的通用方法,会放到mcp_utils文件。

在chat api层面,就是直接和MCP_Chat实例化对象交互,同时会把最新消息记录传递到对象,从而实现多轮对话。

对于流式输出,就是在MCP_Chat调用过程中,把相关信息都通过yeild的方式给流式输出,api层面再传出去就可以了。前端流式输出的效果,对content消息增加type区分,就可以知道是思考过程,还是最终结果。

api动态生成MCP Server

之前文章里这块内容也写过,但是当时实现方式不够理想,后来又换了一个更底层,更可控的方法。正好两个方法可以对比说明下。

方法一(之前的方法)

1)根据api信息,动态生成调用方法

2)通过mcp.add_tool添加1)中方法

方法二(现在的方法)

使用mcp官方的底层server方法,可以自定义list_tool和call_tool方法,这样灵活性、可控性会更高。也就是说只要把列出tool的方法和调用tool的方法实现,就可以了。

做个比较

1)传递给大模型的tool信息完善度

通过自定义list_tool,可以自定义传递给大模型的tools信息,这样你可以设置更完善的function信息,包括参数信息、必填信息等。而通过mcp.add_tool()添加的工具,tool信息是底层自己设置,可能信息不够全。

对于大模型来说,只有给的信息足够全,那么最终的效果才会更好。

2)调用tool的可控性

通过自定义call_tool,可以更好的控制如何调用工具,对于需要传递鉴权信息,或者有些api调用特殊的情况,都可以很好的自我控制。同样的,通过mcp.add_tool()添加的工具,调用的底层自己去调用,灵活度就比较低了。

相关代码
async def init_tools():tools = []for api in api_configs:# 编码,可作为tool名称code = api.get("name", "")# 方法描述description = api.get("discription", "")# 实际调用接口地址api_url = api.get("api_url", "")# 入参input_params = api.get("input_params", "")tool = types.Tool(name=code,description=description,inputSchema=process_input_param(input_params),)tools.append(tool)return tools@mcp.list_tools()
async def list_tools() -> list[types.Tool]:tools = await init_tools()return tools

如上代码,你可以自己控制tool的详细参数信息,给尽可能完整的信息,让大模型可以更好的理解上下文。

async def request_api(name: str, arguments: dict):"""请求api"""api_url = ""for api in api_configs:if api["name"] == name:api_url = api["api_url"]breakelse:raise ValueError(f"Unknown tool: {name}")result = await post_form_data(api_url, arguments, token=None)return result@mcp.call_tool()
async def fetch_tool(name: str, arguments: dict) -> list[types.Content]:print(f"name: {name}")print(f"arguments: {arguments}")result = await request_api(name, arguments)return [types.TextContent(type="text", text=result)]

如上代码,你可以更好的控制如何去调用api,传递token等参数。

相关问题解决说明

过程中遇到的一个主要问题就是MCP连接的稳定性与周期问题。

稳定性

高德MCP Server(amap)在连接一段时间后会不可用:

1)几分钟不调用,再调用的时候会报aclose错误(它自动关闭连接了,但是我自己开发的MCP Server不会如此)

解决 增加心跳检测,每2分钟ping一次,MCP有相应的方法send_ping()(设置3分钟不行,这个时候已经失效…)

2)虽然有了心跳检测,但是过了几个小时候后,再次调用会报错(大意是验证信息过期)

解决 每2个小时,重启一次MCP Server(自己开发的MCP Server没这个问题,因为也没做鉴权信息,当然我也没针对某个MCP Server这么控制重启)

由此引申而来的异步任务问题

MCP Client底层使用了sse_client用到了task_group,然后FastAPI框架也是异步的。基于task_group的设计,在它下面发起的任务,只能在它这里取消,就很容易报各种cancelscope错误。

比如底层创建了一个sse_client,结果在系统重启MCP Server(需要先关闭再启动连接)的时候,就报错了;心跳同样是在关闭MCP Client的时候需要关闭,也会有一样的问题。

解决的办法,就是套娃,在创建心跳任务、重启任务之外,都加一个task_group或cancel_scope,这样就不会导致一直向上传递导致报错的问题。

async def initialize(self):"""延迟初始化(避免启动时阻塞)"""if not self._initialized:self._task_group = anyio.create_task_group()await self._task_group.__aenter__()self._task_group.start_soon(self.connect_mcp_servers)# await self.connect_mcp_servers()self._initialized = True# 使用 anyio 创建取消范围self._heartbeat_cancel_scope = anyio.CancelScope()self._heartbeat_task = asyncio.create_task(self._run_with_cancel_scope())async def _run_with_cancel_scope(self):"""在取消范围内运行心跳任务"""with self._heartbeat_cancel_scope:await self._start_heartbeat()

如上代码,对于心跳任务,主要是取消,所以创建一个cancelScope就好。对于connect_mcp_servers给他创建一个rask_group。

在disconnect_mcp_servers的时候,还需要每个Client调用各自的task_group去clean_up

for i, client in enumerate(self.mcp_clients):try:log.info(f"开始清理客户端 {i}")# client._session_context._task_groupclient._session_context._task_group.start_soon(client.cleanup)# self._task_group.start_soon(client.cleanup)log.info(f"已启动清理客户端 {i}")except AttributeError as e:log.error(f"清理客户端 {i} 时出现属性错误: {e}")traceback.print_exc()except Exception as e:log.error(f"清理客户端 {i} 时出现异常: {type(e).__name__}: {e}")traceback.print_exc()
MCP周期问题(待讨论)

这个问题,我目前也不确定哪种方式就是合理

1)程序启动的时候,MCP Server就都启动连接,一直到程序关闭的时候关闭连接或定时重启

好处:用到的时候调用速度会快

问题:可能用不到,造成资源浪费

2)在对话用到了某个MCP的时候,才去启动连接

好处:节省资源,但是多个对话用到了某个MCP要不要创建多个Client?

问题:响应可能会慢,需要先去连接,获取tools

3)有一个MCP连接池?在2)的基础上启动,不断放到池子里,通过1)的方式维护

这个问题,欢迎各位讨论交流

结语

今天主要讲了如何将MCP 打造为一个项目实用的Web Chat应用。项目不仅仅是调用MCP进行chat,还支持直接创建通用方法MCPServer,以及基于现有api创建动态tool的MCPServer。

在文章的后半部分,讲了下项目过程中遇到的一些问题以及如何解决,还有一些问题留待各位交流探讨。

相关代码已经开源到Github, 【github地址:loli0123456789/MCPWebChat: 基于MCP的WebChat】。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/89216.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/89216.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/89216.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

破解VMware迁移难题

理解VMware迁移的常见挑战 VMware迁移过程中可能遇到的难题包括兼容性问题、性能瓶颈、数据完整性风险以及网络配置复杂性。识别这些问题是制定有效迁移策略的基础。 评估当前环境与目标环境 详细分析源VMware环境的配置、虚拟机数量、存储类型和网络拓扑。对比目标环境的硬件和…

15-STM32F103RCT6的FLASH写入

STM32F103RCT6的FLASH写入 1.//*******************************固件升级地址信息******************************// #define STM32_FLASH_BASE 0x08000000 //固件起始地址 #define FLASH_APP_ADDR 0x08005000 //APP开始地址 #define FLASH_PARA_ADDR 0x0803C000 //固件关…

PPO:近端策略优化算法

温馨提示: 本篇文章已同步至"AI专题精讲" PPO:近端策略优化算法 摘要 我们提出了一类新的用于强化学习的 policy gradient 方法,该方法在与环境交互以采样数据和使用随机梯度上升优化一个“代理”目标函数之间交替进行。与标准的…

数据结构的算法分析与线性表<1>

一、算法分析: 由于语句执行一次的实际所需时间与机器的软硬件有关,则算法分析是针对语句执行次数,而非执行时间。 时间复杂度 计算时间复杂度: 常量阶 如果算法中的n是固定的,或者说n是常数,或者时间复杂…

esp32使用ESP-IDF在Linux下的升级步骤,和遇到的坑Traceback (most recent call last):,及解决

因为之前使用的是ESP-IDF5.3版本。而5.3版本又不支持ESP32P4。而V5.4版本开始正式对P4的支持。所以我把ESP-IDF 升级到V5.4.2的release版本。 一、升级版本:【根据乐鑫官方的方式升级】ESP-IDF 版本简介 - ESP32-P4 - — ESP-IDF 编程指南 v5.4.2 文档 更新至一个稳…

【算法】贪心算法:最大数C++

文章目录前言题目解析算法原理字典序代码示例策略证明前言 题目的链接,大家可以先试着去做一下再来看一下思路。179. 最大数 - 力扣(LeetCode) 题目解析 还是老样子,把题目读懂,画出有用信息。 认真看示例&#xff0…

网络安全职业指南:探索网络安全领域的各种角色

本文旨在为对网络安全领域感兴趣的粉丝读者提供一份全面的职业指南。我们将探讨网络安全领域中各种不同的职业角色,包括其职责、所需技能以及职业发展路径,帮助你了解网络安全领域的职业选择,并为你的职业规划提供参考。网络安全职业概览 身处…

Design Vision:显示扇入/扇出逻辑

相关阅读 Design Visionhttps://blog.csdn.net/weixin_45791458/category_13006970.html?spm1001.2014.3001.5482 在使用Design Vision中查看示意图时,可以在示意图中查看所选单元(Cell)、引脚(Pin)、端口(Port)或线网(Net)的扇入/扇出逻辑。用户可以在当前激活的…

13.7 Meta LLaMA2-Chat核心技术突破:三重强化学习实现91.4%安全评分,超越ChatGPT的对话模型架构全解析

Meta LLaMA2-Chat核心技术突破:三重强化学习实现91.4%安全评分,超越ChatGPT的对话模型架构全解析 指令微调模型:LLaMA2-Chat 技术深度解析 LLaMA2-Chat 作为 Meta 推出的对话优化大模型,其技术实现展现了大模型对齐(Alignment)领域的前沿突破。与基础版 LLaMA2 相比,该…

二维仿射变换笔记

二维仿射变换笔记 1. 数学基础 1.1 变换矩阵 二维仿射变换使用3x3的齐次坐标矩阵表示: [a b tx] [c d ty] [0 0 1 ]其中: [a b; c d] 是线性变换部分,表示旋转、缩放和错切[tx; ty] 是平移部分最后一行 [0 0 1] 是齐次坐标的固定形式1.2 基本变换 1.2.1 平移变换 将点…

创建自定义Dataset类与多分类问题实战

codes 文章目录🌟 6 多分类问题与卷积模型的优化🧩 6.1 创建自定义Dataset类⚠️ 数据集特点:🔑 关键实现步骤:🛠️ 自定义Dataset类实现📊 数据集划分与可视化🧠 6.2 基础卷积模型&…

用vue自定义指令设置页面权限

1.按钮权限处理/*** v-hasPermi 按钮权限处理*/import store from /storeexport default {inserted(el, binding, vnode) {const { value } bindingconst all_permission "*:*:*";const permissions store.getters && store.getters.permissionsif (value…

JPA / Hibernate

1. JPA 和 Hibernate 有什么区别?JPA 是 Java 官方提出的一套 ORM 规范,它只定义了实体映射、关系管理、查询接口等标准,不包含具体实现。Hibernate 是对 JPA 规范的最常用实现,提供了完整的 ORM 功能,并扩展了许多 JP…

kibana显示未准备就绪

kibana显示未准备就绪 最近在研究新版本的ELK(Elasticsearch, Logstash, Kibana)栈时遇到了一个问题:虽然服务器本身能够访问ELK服务,但通过浏览器尝试访问时却无法成功。这里我将分享一些可能的排查步骤和解决方案。连接es的地址…

语音对话秒译 + 视频悬浮字 + 相机即拍即译:ViiTor 如何破局跨语言场景?

在跨语言信息获取场景中,语言壁垒常导致效率降低。ViiTor Translate 试图通过 “场景化功能布局” ,覆盖 语音、视频、图像、文本 四大维度翻译需求。以下基于产品功能展示,拆解其核心能力: 1. 实时语音对话翻译:打破交…

国内第一梯队终端安全产品解析:技术与场景实践

国内终端安全市场的第一梯队产品,通常具备技术领先性、场景覆盖度和规模化落地能力。结合 2025 年最新行业动态与实战案例,以下从技术架构、核心能力和典型应用三个维度,解析当前市场的头部产品及其差异化价值。一、技术架构与市场格局国内终…

FTP 备份,一种更安全的备份方式

备份数据后最重要的任务是确保备份安全存储,最好是异地存储。您可以通过物理方式将备份介质(例如磁带和 CD/DVD)移动到异地位置。这是一个乏味、耗时、不方便且不可靠的方式。更简单的解决方案是通过 FTP 备份到保存在异地的服务器。什么是 F…

理解 HTTP POST 请求中的 json 和 data 参数

在使用 Python 发送 HTTP POST 请求时(无论是使用 requests 还是 aiohttp),json 和 data 参数有明确的区别和使用场景。理解这些区别对正确构建请求至关重要。关键区别特性json 参数data 参数内容类型自动设置为 application/json需要手动设置…

C#反射机制与Activator.CreateInstance

本文仅作为参考大佬们文章的总结。 反射是C#和.NET框架中一项强大的功能,允许程序在运行时检查、创建和操作类型、方法、属性等元数据。作为反射机制的核心组件,Activator.CreateInstance提供了动态实例化对象的灵活方式。本文将全面剖析C#反射的原理、…

Linux的用户和用户组与权限解析、环境变量说明与配置、sudo配置解析和使用

一、Linux的用户及用户组与权限 1.1、Linux的用户和用户组内容介绍 Linux的用户角色分类序号Linux的用户角色说明1超级用户拥有对系统的最高管理权限,可执行任意操作,默认是root用户2普通用户只能对自己目录下的文件进行访问和修改,具有登录系…