MCP开发应用,使用python部署sse模式
一、概述
MCP服务端当前支持两种与客户端的数据通信方式:标准输入输出(stdio) 和 基于Http的服务器推送事件(http sse)
1.1 标准输入输出(stdio)
原理: 标准输入输出是一种用于本地通信的传输方式。在这种模式下,MCP 客户端会将服务器程序作为子进程启动,双方通过约定的标准输入和标准输出(可能是通过共享文件等方法)进行数据交换。具体而言,客户端通过标准输入发送请求,服务器通过标准输出返回响应。。
适用场景: 标准输入输出方式适用于客户端和服务器在同一台机器上运行的场景(本地自行编写服务端或将别人编写的服务端代码pull到本地执行),确保了高效、低延迟的通信。这种直接的数据传输方式减少了网络延迟和传输开销,适合需要快速响应的本地应用。
1.2 基于Http的服务器推送事件(http sse)
原理: 客户端和服务端通过 HTTP 协议进行通信,利用 SSE 实现服务端向客户端的实时数据推送,服务端定义了/see与/messages接口用于推送与接收数据。这里要注意SSE协议和WebSocket协议的区别,SSE协议是单向的,客户端和服务端建立连接后,只能由服务端向客户端进行消息推送。而WebSocket协议客户端和服务端建立连接后,客户端可以通过send向服务端发送数据,并通过onmessage事件接收服务端传过来的数据。
适用场景: 适用于客户端和服务端位于不同物理位置的场景,尤其是对于分布式或远程部署的场景,基于 HTTP 和 SSE 的传输方式更合适。
二、MCP开发应用
MCP Server应用平台
7.MCP.SO
10.Reddit/MCP
MCP应用开发
# MySQL数据库配置
MYSQL_HOST=192.168.20.128
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=abcd@1234
MYSQL_DATABASE=test
这个是mysql连接信息
server.py
import os import uvicorn
from mcp.server.sse import SseServerTransport
from mysql.connector import connect, Error
from mcp.server import Server
from mcp.types import Tool, TextContent
from starlette.applications import Starlette
from starlette.routing import Route, Mount
from dotenv import load_dotenv def get_db_config():
"""从环境变量获取数据库配置信息 返回:
dict: 包含数据库连接所需的配置信息
- host: 数据库主机地址
- port: 数据库端口
- user: 数据库用户名
- password: 数据库密码
- database: 数据库名称 异常:
ValueError: 当必需的配置信息缺失时抛出
""" # 加载.env文件
load_dotenv() config = {
"host": os.getenv("MYSQL_HOST", "localhost"),
"port": int(os.getenv("MYSQL_PORT", "3306")),
"user": os.getenv("MYSQL_USER"),
"password": os.getenv("MYSQL_PASSWORD"),
"database": os.getenv("MYSQL_DATABASE"),
}
print(config)
if not all([config["user"], config["password"], config["database"]]):
raise ValueError("缺少必需的数据库配置") return config def execute_sql(query: str) -> list[TextContent]:
"""执行SQL查询语句 参数:
query (str): 要执行的SQL语句,支持多条语句以分号分隔 返回:
list[TextContent]: 包含查询结果的TextContent列表
- 对于SELECT查询:返回CSV格式的结果,包含列名和数据
- 对于SHOW TABLES:返回数据库中的所有表名
- 对于其他查询:返回执行状态和影响行数
- 多条语句的结果以"---"分隔 异常:
Error: 当数据库连接或查询执行失败时抛出
"""
config = get_db_config()
try:
with connect(**config) as conn:
with conn.cursor() as cursor:
statements = [stmt.strip() for stmt in query.split(";") if stmt.strip()]
results = [] for statement in statements:
try:
cursor.execute(statement) # 检查语句是否返回了结果集 (SELECT, SHOW, EXPLAIN, etc.)
if cursor.description:
columns = [desc[0] for desc in cursor.description]
rows = cursor.fetchall() # 将每一行的数据转换为字符串,特殊处理None值
formatted_rows = []
for row in rows:
formatted_row = [
"NULL" if value is None else str(value)
for value in row
]
formatted_rows.append(",".join(formatted_row)) # 将列名和数据合并为CSV格式
results.append(
"\n".join([",".join(columns)] + formatted_rows)
) # 如果语句没有返回结果集 (INSERT, UPDATE, DELETE, etc.)
else:
conn.commit() # 只有在非查询语句时才提交
results.append(f"查询执行成功。影响行数: {cursor.rowcount}") except Error as stmt_error:
# 单条语句执行出错时,记录错误并继续执行
results.append(
f"执行语句 '{statement}' 出错: {str(stmt_error)}"
)
# 可以在这里选择是否继续执行后续语句,目前是继续 return [TextContent(type="text", text="\n---\n".join(results))] except Error as e:
print(f"执行SQL '{query}' 时出错: {e}")
return [TextContent(type="text", text=f"执行查询时出错: {str(e)}")] def get_table_name(text: str) -> list[TextContent]:
"""根据表的中文注释搜索数据库中的表名 参数:
text (str): 要搜索的表中文注释关键词 返回:
list[TextContent]: 包含查询结果的TextContent列表
- 返回匹配的表名、数据库名和表注释信息
- 结果以CSV格式返回,包含列名和数据
"""
config = get_db_config()
sql = "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COMMENT "
sql += f"FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{config['database']}' AND TABLE_COMMENT LIKE '%{text}%';"
return execute_sql(sql) def get_table_desc(text: str) -> list[TextContent]:
"""获取指定表的字段结构信息 参数:
text (str): 要查询的表名,多个表名以逗号分隔 返回:
list[TextContent]: 包含查询结果的TextContent列表
- 返回表的字段名、字段注释等信息
- 结果按表名和字段顺序排序
- 结果以CSV格式返回,包含列名和数据
"""
config = get_db_config()
# 将输入的表名按逗号分割成列表
table_names = [name.strip() for name in text.split(",")]
# 构建IN条件
table_condition = "','".join(table_names)
sql = "SELECT TABLE_NAME, COLUMN_NAME, COLUMN_COMMENT "
sql += (
f"FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '{config['database']}' "
)
sql += f"AND TABLE_NAME IN ('{table_condition}') ORDER BY TABLE_NAME, ORDINAL_POSITION;"
return execute_sql(sql) def get_lock_tables() -> list[TextContent]:
sql = """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;""" return execute_sql(sql) # 初始化服务器
app = Server("operateMysql") @app.list_tools()
async def list_tools() -> list[Tool]:
"""列出可用的MySQL工具 返回:
list[Tool]: 工具列表,当前仅包含execute_sql工具
"""
return [
Tool(
name="execute_sql",
description="在MySQL8.0数据库上执行SQL",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string", "description": "要执行的SQL语句"}
},
"required": ["query"],
},
),
Tool(
name="get_table_name",
description="根据表中文名搜索数据库中对应的表名",
inputSchema={
"type": "object",
"properties": {
"text": {"type": "string", "description": "要搜索的表中文名"}
},
"required": ["text"],
},
),
Tool(
name="get_table_desc",
description="根据表名搜索数据库中对应的表结构,支持多表查询",
inputSchema={
"type": "object",
"properties": {
"text": {"type": "string", "description": "要搜索的表名"}
},
"required": ["text"],
},
),
Tool(
name="get_lock_tables",
description="获取当前mysql服务器InnoDB 的行级锁",
inputSchema={"type": "object", "properties": {}},
),
] @app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]: if name == "execute_sql":
query = arguments.get("query")
if not query:
raise ValueError("缺少查询语句")
return execute_sql(query)
elif name == "get_table_name":
text = arguments.get("text")
if not text:
raise ValueError("缺少表信息")
return get_table_name(text)
elif name == "get_table_desc":
text = arguments.get("text")
if not text:
raise ValueError("缺少表信息")
return get_table_desc(text)
elif name == "get_lock_tables":
return get_lock_tables() raise ValueError(f"未知的工具: {name}") sse = SseServerTransport("/messages/") # Handler for SSE connections
async def handle_sse(request):
async with sse.connect_sse(
request.scope, request.receive, request._send
) as streams:
await app.run(streams[0], streams[1], app.create_initialization_options()) # Create Starlette app with routes
starlette_app = Starlette(
debug=True,
routes=[
Route("/sse", endpoint=handle_sse),
Mount("/messages/", app=sse.handle_post_message),
],
) if __name__ == "__main__":
uvicorn.run(starlette_app, host="0.0.0.0", port=9000)
这里面,主要提供了3个工具方法,分别是:
execute_sql
get_table_name
get_table_desc
get_lock_tables
安装python依赖
pip install mcp
pip install mysql-connector-python
pip install uvicorn
pip install python-dotenv
pip install starlette
启动应用
python server.py
输出:
INFO: Started server process [23756]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)
INFO: 127.0.0.1:60896 - "GET /sse HTTP/1.1" 200 OK
访问页面:http://127.0.0.1:9000/sse
效果如下:

三、测试MCP应用
客户端添加MCP

添加MCP服务器
输入名称:mysql_mcp_server_pro
类型:sse
URL:http://127.0.0.1:9000/sse
点击保存

保存成功后,就可以看到工具列表了,只有4个

测试MCP应用
返回主页,点击新建助手,选择模型

在输入框,找到MCP服务器

开启MCP

先来看mysql的数据表score,内容如下:

查询成绩表score的内容

查询成绩表的表名是什么

查询成绩表的表结构

获取当前mysql的锁

MCP开发应用,使用python部署sse模式的更多相关文章
- Flink部署-standalone模式
Flink部署-standalone模式 2018年11月30日 00:07:41 Xlucas 阅读数:74 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.cs ...
- [转]页游开发中的 Python 组件与模式Presentation Transcript
转: 页游开发中的 Python 组件与模式Presentation Transcript 1. 页游开发中的 Python 组件与模式 赖勇浩( http://laiyonghao.com ) 20 ...
- Python的交互模式和命令行模式
Pyhton的交互模式 在终端输入Python3命令就会进入家Python的交互模式,在交互模式下,输入一行代码,回车,就会执行这行代码. Python的命令行模式 在终端输入Python3 1.py ...
- windows下apache + mod_wsgi + python部署flask接口服务
windows下apache + mod_wsgi + python部署flask接口服务 用python3安装虚拟环境 为啥要装虚拟环境? 原因1:安装虚拟环境是为了使项目的环境和全局环境隔离开,在 ...
- 浅谈Python设计模式 - 外观模式
声明:本系列文章主要参考<精通Python设计模式>一书,并且参考一些资料,结合自己的一些看法来总结而来. 外观模式 外观模式的核心在于将复杂的内部实现包装起来,只向外界提供简单的调用接口 ...
- Python的组合模式与责任链模式编程示例
Python的组合模式与责任链模式编程示例 这篇文章主要介绍了Python的组合模式与责任链模式编程示例,组合模式与责任链模式都属于Python的设计模式,需要的朋友可以参考下 组合模式 我们把Com ...
- python实现建造者模式
python实现建造者模式 前言 无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮.方向盘.发送机等各种部件.而对于大多数用户而言,无须知道这些部件的 ...
- python实现策略模式
python实现策略模式 原文地址 1.策略模式概述 策略模式:定义一系列算法,把它们一一封装起来,并且使它们之间可以相互替换.此模式让算法的变化不会影响到使用算法的客户. 电商领域有个使用“策略”模 ...
- python部署到服务器
python部署到服务器 linux下的CentOS 7 自带python2.7.5, 使用 python --version 命令查看,因系统需要python2.7.5,因此我们并不卸载,另外安装p ...
- Python的交互模式和直接运行.py文件有什么区别
使用文本编辑器 - 廖雪峰的官方网站 https://www.liaoxuefeng.com/wiki/1016959663602400/1017024645952992 直接输入python进入交互 ...
随机推荐
- 云辅助隐私集合求交(Server-Aided PSI)协议介绍:学习
原文来自:云辅助隐私集合求交(Server-Aided PSI)协议介绍,下面学习一波,并记录一些笔记. 背景 总结: 1.PSI-CA和PSI相比,前者在乎的是交集的大小,后者在乎的是交集本身.另外 ...
- Luogu P1930 亚瑟王的宫殿 题解 [ 蓝 ] [ 分层图最短路 ] [ 枚举 ]
亚瑟王的宫殿:比较 tricky 的图论. 图论做法 思路 因为是无向图,所以考虑一个经典 trick,把所有点到集合点的距离之和化为集合点到其他所有点的位置之和,就可以从集合点做单源最短路了. 于是 ...
- FreeSql学习笔记——6.修改
前言 FreeSql 提供丰富的数据库更新功能,支持单条或批量更新,支持更新指定的字段,在特定的数据库执行还可以返回更新后的记录.与删除一样,没有条件的话不会执行,避免全表修改到全表: 指 ...
- PKUWC2025 游记
哈哈哈哈哈,我糖完了,哈哈哈哈哈. Day \(-998244353\) 被波波抓到机房充军集训去了,听到了很多新奇的算法,然后拼尽全力仍难以 \(AC\)--然后被各种巨佬疯狂单调队列. Day \ ...
- [SDOI2008] 洞穴勘测 题解
似乎所有的线段树分治题都能被 \(LCT\) 平替掉? 一眼动态树,直接 \(LCT\). Connect x y 操作用 \(link(x,y)\) 实现,Destroy x y 操作用 \(cut ...
- 光猫路由器设置-改桥接、开启UPNP和DMZ的教程
疑难解答加微信机器人,给它发:进群,会拉你进入八米交流群 机器人微信号:bamibot 简洁版教程访问:https://bbs.8miyun.cn 以电信光猫举例,其他网络百度都有对应教程. 电信光猫 ...
- 标题Spark Mavem项目在本地测试报错 : Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/spark/SparkConf
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/spark/SparkConf at S ...
- MySQL数据库datetime类型不能为空值的问题
修改mysql的配置文件:my.ini 将其只的: sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUT ...
- springboot項目打jar/war包
一.打jar包 (1)不打入项目引用的第三方jar <build> <plugins> <plugin> <groupId>org.apache.mav ...
- Linux 通过docker安装nginx,.net core sdk或运行时安装到Linux
1.Linux docker通过yum安装 https://blog.csdn.net/GMingZhou/article/details/94024453 https://qizhanming.co ...
