MCP开发应用,使用python部署sse模式
<h1>一、概述</h1><p>MCP服务端当前支持两种与客户端的数据通信方式:标准输入输出(stdio) 和 基于Http的服务器推送事件(http sse)</p>
<div>
<div>
<h2 data-id="heading-1">1.1 标准输入输出(stdio)</h2>
<p><strong>原理:</strong> 标准输入输出是一种用于本地通信的传输方式。在这种模式下,MCP 客户端会将服务器程序作为子进程启动,双方通过约定的标准输入和标准输出(可能是通过共享文件等方法)进行数据交换。具体而言,客户端通过标准输入发送请求,服务器通过标准输出返回响应。。</p>
<p><strong>适用场景:</strong> 标准输入输出方式适用于客户端和服务器在同一台机器上运行的场景(本地自行编写服务端或将别人编写的服务端代码pull到本地执行),确保了高效、低延迟的通信。这种直接的数据传输方式减少了网络延迟和传输开销,适合需要快速响应的本地应用。</p>
<h2 data-id="heading-2">1.2 基于Http的服务器推送事件(http sse)</h2>
<p><strong>原理:</strong> 客户端和服务端通过 HTTP 协议进行通信,利用 SSE 实现服务端向客户端的实时数据推送,服务端定义了/see与/messages接口用于推送与接收数据。这里要注意SSE协议和WebSocket协议的区别,SSE协议是单向的,客户端和服务端建立连接后,只能由服务端向客户端进行消息推送。而WebSocket协议客户端和服务端建立连接后,客户端可以通过send向服务端发送数据,并通过onmessage事件接收服务端传过来的数据。</p>
<p><strong>适用场景:</strong> 适用于客户端和服务端位于不同物理位置的场景,尤其是对于分布式或远程部署的场景,基于 HTTP 和 SSE 的传输方式更合适。</p>
</div>
<br>
</div>
<h1>二、MCP开发应用</h1>
<h2>MCP Server应用平台</h2>
<div>主要有以下这些:</div>
<div>
<p>1.GitHub - modelcontextprotocol/servers: Model Context Protocol Servers</p>
</div>
<div>
<p>2.PulseMCP | Keep up-to-date with MCP</p>
</div>
<div>
<p>3.Awesome MCP Servers</p>
</div>
<div>
<p>4.Smithery - Model Context Protocol Registry</p>
</div>
<div>
<p>5.Open-Source MCP servers | Glama</p>
</div>
<div>
<p>6.Cursor Directory - Cursor Rules & MCP Servers</p>
</div>
<div>
<p>7.MCP.SO</p>
</div>
<div>
<p>8.List of all MCP Servers (42) | Portkey</p>
</div>
<div>
<p>9.Cline/MCP-MarketPlace</p>
</div>
<div>
<p>10.Reddit/MCP</p>
</div>
<div> </div>
<div>这里面有很多已经开发好的mcp应用,可以拿来直接使用即可。</div>
<div>Cherry Studio客户端推荐的mcp平台为:https://mcp.so</div>
<div> </div>
<div> </div>
<div>但是大家可以发现一个问题,就是MCP Server应用平台,发布的应用,80%以上,运行方式都是stdio,这种方式适合本地开发。</div>
<div>但是想在dify里面使用,一般都是sse方式,因为适合远程调用。即使是不同命名空间,不同的工作流依然可以调用,只要网络能联通,就没问题。</div>
<div>而且sse方式,只需要运行一次,就可以让成千上万个dify应用调用,还是很方便的。</div>
<div> </div>
<h2>MCP应用开发</h2>
<div>以mysql为例子,进入网站https://mcp.so,搜索mysql,找到Mysql_mcp_server_pro,链接如下:https://mcp.so/server/mysql_mcp_server_pro/wenb1n-dev(xwb602625136)?tab=content</div>
<div>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411180651560-428682466.png" alt="" loading="lazy"></p>
<p>github地址如下:https://github.com/wenb1n-dev/mysql_mcp_server_pro</p>
</div>
<div> </div>
<div>这个应用只支持mysql 5.6,由于我的mysql版本是8.0,需要修改对应的源码才行。</div>
<h3>mysql表数据</h3>
<p>新建表score</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">CREATE</span> <span style="color: rgba(0, 0, 255, 1)">TABLE</span><span style="color: rgba(0, 0, 0, 1)"> `score` (
`id` </span><span style="color: rgba(0, 0, 255, 1)">bigint</span> <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span> AUTO_INCREMENT COMMENT <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
`username` </span><span style="color: rgba(0, 0, 255, 1)">varchar</span>(<span style="color: rgba(128, 0, 0, 1); font-weight: bold">255</span>) <span style="color: rgba(0, 0, 255, 1)">CHARACTER</span> <span style="color: rgba(0, 0, 255, 1)">SET</span> utf8mb4 COLLATE utf8mb4_unicode_ci <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span> COMMENT <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">用户名</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
`chinese` </span><span style="color: rgba(0, 0, 255, 1)">int</span> <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">0</span><span style="color: rgba(255, 0, 0, 1)">'</span> COMMENT <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">语文</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
`mathematics` </span><span style="color: rgba(0, 0, 255, 1)">int</span> <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">0</span><span style="color: rgba(255, 0, 0, 1)">'</span> COMMENT <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">数学</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
`english` </span><span style="color: rgba(0, 0, 255, 1)">int</span> <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">0</span><span style="color: rgba(255, 0, 0, 1)">'</span> COMMENT <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">英语</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
`total` </span><span style="color: rgba(0, 0, 255, 1)">int</span> <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">0</span><span style="color: rgba(255, 0, 0, 1)">'</span> COMMENT <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">总分</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 0, 255, 1)">PRIMARY</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span><span style="color: rgba(0, 0, 0, 1)"> (`id`)
) ENGINE</span><span style="color: rgba(128, 128, 128, 1)">=</span>InnoDB AUTO_INCREMENT<span style="color: rgba(128, 128, 128, 1)">=</span><span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span> <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> CHARSET<span style="color: rgba(128, 128, 128, 1)">=</span>utf8mb4 COLLATE<span style="color: rgba(128, 128, 128, 1)">=</span>utf8mb4_unicode_ci COMMENT<span style="color: rgba(128, 128, 128, 1)">=</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">成绩表</span><span style="color: rgba(255, 0, 0, 1)">'</span>;</pre>
</div>
<p> </p>
<p>插入数据</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">INSERT</span> <span style="color: rgba(0, 0, 255, 1)">INTO</span> `score` <span style="color: rgba(0, 0, 255, 1)">VALUES</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>, <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">张三</span><span style="color: rgba(255, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">95</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">97</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">99</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">291</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">INSERT</span> <span style="color: rgba(0, 0, 255, 1)">INTO</span> `score` <span style="color: rgba(0, 0, 255, 1)">VALUES</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">2</span>, <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">李四</span><span style="color: rgba(255, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">73</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">85</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">95</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">253</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">INSERT</span> <span style="color: rgba(0, 0, 255, 1)">INTO</span> `score` <span style="color: rgba(0, 0, 255, 1)">VALUES</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">3</span>, <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">王二</span><span style="color: rgba(255, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">88</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">83</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">96</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">267</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">INSERT</span> <span style="color: rgba(0, 0, 255, 1)">INTO</span> `score` <span style="color: rgba(0, 0, 255, 1)">VALUES</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">4</span>, <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">周五</span><span style="color: rgba(255, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">92</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">88</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">93</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">273</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">INSERT</span> <span style="color: rgba(0, 0, 255, 1)">INTO</span> `score` <span style="color: rgba(0, 0, 255, 1)">VALUES</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">5</span>, <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">马六</span><span style="color: rgba(255, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">85</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">91</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">97</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">273</span>);</pre>
</div>
<p> </p>
<h3>python代码</h3>
<div>新建空目录Mysql_mcp_server_pro,新建2个文件.evn,server.py</div>
<div>.env文件内容如下:</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># MySQL数据库配置
MYSQL_HOST</span>=192.168.20.128<span style="color: rgba(0, 0, 0, 1)">
MYSQL_PORT</span>=<span style="color: rgba(128, 0, 128, 1)">3306</span><span style="color: rgba(0, 0, 0, 1)">
MYSQL_USER</span>=<span style="color: rgba(0, 0, 0, 1)">root
MYSQL_PASSWORD</span>=abcd@1234<span style="color: rgba(0, 0, 0, 1)">
MYSQL_DATABASE</span>=test</pre>
</div>
<p>这个是mysql连接信息</p>
<p> </p>
<p>server.py</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> os
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> uvicorn
</span><span style="color: rgba(0, 0, 255, 1)">from</span> mcp.server.sse <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> SseServerTransport
</span><span style="color: rgba(0, 0, 255, 1)">from</span> mysql.connector <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> connect, Error
</span><span style="color: rgba(0, 0, 255, 1)">from</span> mcp.server <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> Server
</span><span style="color: rgba(0, 0, 255, 1)">from</span> mcp.types <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> Tool, TextContent
</span><span style="color: rgba(0, 0, 255, 1)">from</span> starlette.applications <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> Starlette
</span><span style="color: rgba(0, 0, 255, 1)">from</span> starlette.routing <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> Route, Mount
</span><span style="color: rgba(0, 0, 255, 1)">from</span> dotenv <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> load_dotenv
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> get_db_config():
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">从环境变量获取数据库配置信息
返回:
dict: 包含数据库连接所需的配置信息
- host: 数据库主机地址
- port: 数据库端口
- user: 数据库用户名
- password: 数据库密码
- database: 数据库名称
异常:
ValueError: 当必需的配置信息缺失时抛出
</span><span style="color: rgba(128, 0, 0, 1)">"""</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 加载.env文件</span>
<span style="color: rgba(0, 0, 0, 1)"> load_dotenv()
config </span>=<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">host</span><span style="color: rgba(128, 0, 0, 1)">"</span>: os.getenv(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MYSQL_HOST</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">localhost</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">),
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">port</span><span style="color: rgba(128, 0, 0, 1)">"</span>: int(os.getenv(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MYSQL_PORT</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">3306</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)),
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">user</span><span style="color: rgba(128, 0, 0, 1)">"</span>: os.getenv(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MYSQL_USER</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">),
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">password</span><span style="color: rgba(128, 0, 0, 1)">"</span>: os.getenv(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MYSQL_PASSWORD</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">),
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">database</span><span style="color: rgba(128, 0, 0, 1)">"</span>: os.getenv(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MYSQL_DATABASE</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">),
}
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(config)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span> all(, config[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">password</span><span style="color: rgba(128, 0, 0, 1)">"</span>], config[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">database</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">]]):
</span><span style="color: rgba(0, 0, 255, 1)">raise</span> ValueError(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">缺少必需的数据库配置</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> config
</span><span style="color: rgba(0, 0, 255, 1)">def</span> execute_sql(query: str) -><span style="color: rgba(0, 0, 0, 1)"> list:
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">执行SQL查询语句
参数:
query (str): 要执行的SQL语句,支持多条语句以分号分隔
返回:
list: 包含查询结果的TextContent列表
- 对于SELECT查询:返回CSV格式的结果,包含列名和数据
- 对于SHOW TABLES:返回数据库中的所有表名
- 对于其他查询:返回执行状态和影响行数
- 多条语句的结果以"---"分隔
异常:
Error: 当数据库连接或查询执行失败时抛出
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
config </span>=<span style="color: rgba(0, 0, 0, 1)"> get_db_config()
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
with connect(</span>**<span style="color: rgba(0, 0, 0, 1)">config) as conn:
with conn.cursor() as cursor:
statements </span>=
results </span>=<span style="color: rgba(0, 0, 0, 1)"> []
</span><span style="color: rgba(0, 0, 255, 1)">for</span> statement <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> statements:
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
cursor.execute(statement)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 检查语句是否返回了结果集 (SELECT, SHOW, EXPLAIN, etc.)</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> cursor.description:
columns </span>= <span style="color: rgba(0, 0, 255, 1)">for</span> desc <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> cursor.description]
rows </span>=<span style="color: rgba(0, 0, 0, 1)"> cursor.fetchall()
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 将每一行的数据转换为字符串,特殊处理None值</span>
formatted_rows =<span style="color: rgba(0, 0, 0, 1)"> []
</span><span style="color: rgba(0, 0, 255, 1)">for</span> row <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> rows:
formatted_row </span>=<span style="color: rgba(0, 0, 0, 1)"> [
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">NULL</span><span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(0, 0, 255, 1)">if</span> value <span style="color: rgba(0, 0, 255, 1)">is</span> None <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> str(value)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> value <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> row
]
formatted_rows.append(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">,</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">.join(formatted_row))
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 将列名和数据合并为CSV格式</span>
<span style="color: rgba(0, 0, 0, 1)"> results.append(
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\n</span><span style="color: rgba(128, 0, 0, 1)">"</span>.join([<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">,</span><span style="color: rgba(128, 0, 0, 1)">"</span>.join(columns)] +<span style="color: rgba(0, 0, 0, 1)"> formatted_rows)
)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果语句没有返回结果集 (INSERT, UPDATE, DELETE, etc.)</span>
<span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">:
conn.commit()</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 只有在非查询语句时才提交</span>
results.append(f<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">查询执行成功。影响行数: {cursor.rowcount}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> Error as stmt_error:
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 单条语句执行出错时,记录错误并继续执行</span>
<span style="color: rgba(0, 0, 0, 1)"> results.append(
f</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">执行语句 '{statement}' 出错: {str(stmt_error)}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 可以在这里选择是否继续执行后续语句,目前是继续</span>
<span style="color: rgba(0, 0, 255, 1)">return</span>
</span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> Error as e:
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(f<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">执行SQL '{query}' 时出错: {e}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span>
</span><span style="color: rgba(0, 0, 255, 1)">def</span> get_table_name(text: str) -><span style="color: rgba(0, 0, 0, 1)"> list:
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">根据表的中文注释搜索数据库中的表名
参数:
text (str): 要搜索的表中文注释关键词
返回:
list: 包含查询结果的TextContent列表
- 返回匹配的表名、数据库名和表注释信息
- 结果以CSV格式返回,包含列名和数据
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
config </span>=<span style="color: rgba(0, 0, 0, 1)"> get_db_config()
sql </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COMMENT </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
sql </span>+= f<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{config['database']}' AND TABLE_COMMENT LIKE '%{text}%';</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> execute_sql(sql)
</span><span style="color: rgba(0, 0, 255, 1)">def</span> get_table_desc(text: str) -><span style="color: rgba(0, 0, 0, 1)"> list:
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">获取指定表的字段结构信息
参数:
text (str): 要查询的表名,多个表名以逗号分隔
返回:
list: 包含查询结果的TextContent列表
- 返回表的字段名、字段注释等信息
- 结果按表名和字段顺序排序
- 结果以CSV格式返回,包含列名和数据
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
config </span>=<span style="color: rgba(0, 0, 0, 1)"> get_db_config()
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 将输入的表名按逗号分割成列表</span>
table_names =
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 构建IN条件</span>
table_condition = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">','</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">.join(table_names)
sql </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">SELECT TABLE_NAME, COLUMN_NAME, COLUMN_COMMENT </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
sql </span>+=<span style="color: rgba(0, 0, 0, 1)"> (
f</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '{config['database']}' </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
)
sql </span>+= f<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">AND TABLE_NAME IN ('{table_condition}') ORDER BY TABLE_NAME, ORDINAL_POSITION;</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> execute_sql(sql)
</span><span style="color: rgba(0, 0, 255, 1)">def</span> get_lock_tables() -><span style="color: rgba(0, 0, 0, 1)"> list:
sql </span>= <span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">SELECT
p2.`HOST` AS 被阻塞方host,
p2.`USER` AS 被阻塞方用户,
r.trx_id AS 被阻塞方事务id,
r.trx_mysql_thread_id AS 被阻塞方线程号,
TIMESTAMPDIFF(SECOND, r.trx_wait_started, CURRENT_TIMESTAMP) AS 等待时间,
r.trx_query AS 被阻塞的查询,
l.OBJECT_NAME AS 阻塞方锁住的表,
m.LOCK_MODE AS 被阻塞方的锁模式,
m.LOCK_TYPE AS '被阻塞方的锁类型(表锁还是行锁)',
m.INDEX_NAME AS 被阻塞方锁住的索引,
m.OBJECT_SCHEMA AS 被阻塞方锁对象的数据库名,
m.OBJECT_NAME AS 被阻塞方锁对象的表名,
m.LOCK_DATA AS 被阻塞方事务锁定记录的主键值,
p.`HOST` AS 阻塞方主机,
p.`USER` AS 阻塞方用户,
b.trx_id AS 阻塞方事务id,
b.trx_mysql_thread_id AS 阻塞方线程号,
b.trx_query AS 阻塞方查询,
l.LOCK_MODE AS 阻塞方的锁模式,
l.LOCK_TYPE AS '阻塞方的锁类型(表锁还是行锁)',
l.INDEX_NAME AS 阻塞方锁住的索引,
l.OBJECT_SCHEMA AS 阻塞方锁对象的数据库名,
l.OBJECT_NAME AS 阻塞方锁对象的表名,
l.LOCK_DATA AS 阻塞方事务锁定记录的主键值,
IF(p.COMMAND = 'Sleep', CONCAT(p.TIME, ' 秒'), 0) AS 阻塞方事务空闲的时间
FROM performance_schema.data_lock_waits w
INNER JOIN performance_schema.data_locks l ON w.BLOCKING_ENGINE_LOCK_ID = l.ENGINE_LOCK_ID
INNER JOIN performance_schema.data_locks m ON w.REQUESTING_ENGINE_LOCK_ID = m.ENGINE_LOCK_ID
INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.BLOCKING_ENGINE_TRANSACTION_ID
INNER JOIN information_schema.INNODB_TRX r ON r.trx_id = w.REQUESTING_ENGINE_TRANSACTION_ID
INNER JOIN information_schema.PROCESSLIST p ON p.ID = b.trx_mysql_thread_id
INNER JOIN information_schema.PROCESSLIST p2 ON p2.ID = r.trx_mysql_thread_id
ORDER BY 等待时间 DESC;</span><span style="color: rgba(128, 0, 0, 1)">"""</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> execute_sql(sql)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 初始化服务器</span>
app = Server(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">operateMysql</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
@app.list_tools()
async </span><span style="color: rgba(0, 0, 255, 1)">def</span> list_tools() -><span style="color: rgba(0, 0, 0, 1)"> list:
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">列出可用的MySQL工具
返回:
list: 工具列表,当前仅包含execute_sql工具
</span><span style="color: rgba(128, 0, 0, 1)">"""</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
Tool(
name</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">execute_sql</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
description</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">在MySQL8.0数据库上执行SQL</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
inputSchema</span>=<span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">type</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">properties</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">query</span><span style="color: rgba(128, 0, 0, 1)">"</span>: {<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">type</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">string</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">description</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">要执行的SQL语句</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">}
},
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">required</span><span style="color: rgba(128, 0, 0, 1)">"</span>: [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">query</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
},
),
Tool(
name</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">get_table_name</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
description</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">根据表中文名搜索数据库中对应的表名</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
inputSchema</span>=<span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">type</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">properties</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">text</span><span style="color: rgba(128, 0, 0, 1)">"</span>: {<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">type</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">string</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">description</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">要搜索的表中文名</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">}
},
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">required</span><span style="color: rgba(128, 0, 0, 1)">"</span>: [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">text</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
},
),
Tool(
name</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">get_table_desc</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
description</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">根据表名搜索数据库中对应的表结构,支持多表查询</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
inputSchema</span>=<span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">type</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">properties</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">text</span><span style="color: rgba(128, 0, 0, 1)">"</span>: {<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">type</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">string</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">description</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">要搜索的表名</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">}
},
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">required</span><span style="color: rgba(128, 0, 0, 1)">"</span>: [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">text</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
},
),
Tool(
name</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">get_lock_tables</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
description</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">获取当前mysql服务器InnoDB 的行级锁</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
inputSchema</span>={<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">type</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">properties</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {}},
),
]
@app.call_tool()
async </span><span style="color: rgba(0, 0, 255, 1)">def</span> call_tool(name: str, arguments: dict) -><span style="color: rgba(0, 0, 0, 1)"> list:
</span><span style="color: rgba(0, 0, 255, 1)">if</span> name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">execute_sql</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
query </span>= arguments.get(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">query</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span><span style="color: rgba(0, 0, 0, 1)"> query:
</span><span style="color: rgba(0, 0, 255, 1)">raise</span> ValueError(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">缺少查询语句</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> execute_sql(query)
</span><span style="color: rgba(0, 0, 255, 1)">elif</span> name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">get_table_name</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
text </span>= arguments.get(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">text</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span><span style="color: rgba(0, 0, 0, 1)"> text:
</span><span style="color: rgba(0, 0, 255, 1)">raise</span> ValueError(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">缺少表信息</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> get_table_name(text)
</span><span style="color: rgba(0, 0, 255, 1)">elif</span> name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">get_table_desc</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
text </span>= arguments.get(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">text</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span><span style="color: rgba(0, 0, 0, 1)"> text:
</span><span style="color: rgba(0, 0, 255, 1)">raise</span> ValueError(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">缺少表信息</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> get_table_desc(text)
</span><span style="color: rgba(0, 0, 255, 1)">elif</span> name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">get_lock_tables</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> get_lock_tables()
</span><span style="color: rgba(0, 0, 255, 1)">raise</span> ValueError(f<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">未知的工具: {name}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
sse </span>= SseServerTransport(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/messages/</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Handler for SSE connections</span>
async <span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> handle_sse(request):
async with sse.connect_sse(
request.scope, request.receive, request._send
) as streams:
await app.run(streams, streams[</span>1<span style="color: rgba(0, 0, 0, 1)">], app.create_initialization_options())
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Create Starlette app with routes</span>
starlette_app =<span style="color: rgba(0, 0, 0, 1)"> Starlette(
debug</span>=<span style="color: rgba(0, 0, 0, 1)">True,
routes</span>=<span style="color: rgba(0, 0, 0, 1)">[
Route(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/sse</span><span style="color: rgba(128, 0, 0, 1)">"</span>, endpoint=<span style="color: rgba(0, 0, 0, 1)">handle_sse),
Mount(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/messages/</span><span style="color: rgba(128, 0, 0, 1)">"</span>, app=<span style="color: rgba(0, 0, 0, 1)">sse.handle_post_message),
],
)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
uvicorn.run(starlette_app, host</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">0.0.0.0</span><span style="color: rgba(128, 0, 0, 1)">"</span>, port=9000)</pre>
</div>
<p>这里面,主要提供了4个工具方法,分别是:</p>
</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">execute_sql
get_table_name
get_table_desc
get_lock_tables</span></pre>
</div>
<p> </p>
<p>安装python依赖</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">pip install mcp
pip install mysql</span>-connector-<span style="color: rgba(0, 0, 0, 1)">python
pip install uvicorn
pip install python</span>-<span style="color: rgba(0, 0, 0, 1)">dotenv
pip install starlette</span></pre>
</div>
<p> </p>
<p>启动应用</p>
</div>
<div>
<div class="cnblogs_code">
<pre>python server.py</pre>
</div>
<p>输出:</p>
<div class="cnblogs_code">
<pre>INFO: Started server process [<span style="color: rgba(128, 0, 128, 1)">23756</span><span style="color: rgba(0, 0, 0, 1)">]
INFO: Waiting </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">0.0.0.0:9000 (Press CTRL+C to quit)</span>
INFO: <span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span>:<span style="color: rgba(128, 0, 128, 1)">60896</span> - <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">GET /sse HTTP/1.1</span><span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 128, 1)">200</span> OK</pre>
</div>
<p> </p>
<p>访问页面:http://127.0.0.1:9000/sse</p>
<p>效果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411182228381-561588116.png" alt="" loading="lazy"></p>
<p> </p>
<h1>三、测试MCP应用</h1>
</div>
<h2>客户端添加MCP</h2>
<div>以Cherry Studio客户端为例子,注意:必须是Cherry Studio最新版本,才有MCP设置。</div>
<div> </div>
<div>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411182526483-684455862.png" alt="" loading="lazy"></p>
<p> 添加MCP服务器</p>
<p>输入名称:mysql_mcp_server_pro</p>
<p>类型:sse</p>
<p>URL:http://127.0.0.1:9000/sse</p>
<p> </p>
<p>点击保存</p>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411182641192-1257661192.png" alt="" loading="lazy"></p>
<p> </p>
<p>保存成功后,就可以看到工具列表了,只有4个</p>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411182716159-4330916.png" alt="" loading="lazy"></p>
<h2>测试MCP应用</h2>
<p> 返回主页,点击新建助手,选择模型</p>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411182829911-1587484556.png" alt="" loading="lazy"></p>
<p> 在输入框,找到MCP服务器</p>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411182903995-825336037.png" alt="" loading="lazy"></p>
<p> 开启MCP</p>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411182925913-1542022116.png" alt="" loading="lazy"></p>
<p> </p>
<p>先来看mysql的数据表score,内容如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411183012769-324659737.png" alt="" loading="lazy"></p>
<p> </p>
<h3> 查询成绩表score的内容</h3>
</div>
<div>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411183146398-1992254574.png" alt="" loading="lazy"></p>
<h3> 查询成绩表的表名是什么</h3>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411183415881-286035921.png" alt="" loading="lazy"></p>
<p> </p>
<h3>查询成绩表的表结构</h3>
</div>
<div>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411183448297-795619178.png" alt="" loading="lazy"></p>
<p> </p>
<h3>获取当前mysql的锁</h3>
<p><img src="https://img2024.cnblogs.com/blog/1341090/202504/1341090-20250411183739130-1568045308.png" alt="" loading="lazy"></p>
<p> </p>
<p>总结一下,AI模型调用MCP,还是很方便的。</p>
<p>有些时候AI模型做不到的,你可以自己写一个MCP应用。比如上面提到的查询mysql表数据,还有很多呢。</p>
<p>比如查询内部CRM系统,gitlab信息,内部业务系统,处理特定格式excel文件等等,都可以的。</p>
<p> </p>
</div><br><br>
来源:https://www.cnblogs.com/xiao987334176/p/18821197
頁:
[1]