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进入交互 ...
随机推荐
- DICOM 生成dcm文件
using Dicom; using Dicom.Imaging; using Dicom.IO.Buffer; using System; using System.Collections.Gene ...
- Nginx设置HTTPS监听
原文链接:https://blog.liuzijian.com/post/03708e47-03bd-4efe-a569-16f0eedc6158.html 1. 获取 SSL 证书 首先,你需要获取 ...
- delphi中实现http请求和提交
在对接本地的一个药械监管系统使用实现的代码,编译环境delphi Xe7 ,使用类TIdHTTP实现网络的get请求和post提交 //设置组件的属性 procedure TfrmMain.SetHt ...
- react声明周期详解
react的生命周期,分为3三个阶段, 挂载阶段 constructor(){} UNSAFE_componentWillMount(){} == componentWillMount(在17版本中将 ...
- [AI/GPT] 硅基流动(SiliconFlow) : AI大模型时代的基础设施
概述:硅基流动(SilliconFlow) 简介 硅基流动(SiliconFlow) 是一家专注于人工智能(AI)基础设施的公司,致力于通过技术创新降低大模型(如生成式AI和大语言模型)的部署和推理成 ...
- 用python做时间序列预测七:时间序列复杂度量化
本文介绍一种方法,帮助我们了解一个时间序列是否可以预测,或者说了解可预测能力有多强. Sample Entropy (样本熵) Sample Entropy是Approximate Entropy(近 ...
- 关于DateTime的自定义转换
关于DateTime的自定义转换.把字符串时间转换成可以供DateTime类型识别的字符串类型的粗略实现. /// <summary> /// 把从数据库中读取的字符串的CurrentTi ...
- 越“挖”越有料,天翼云“息壤”助攻DeepSeek变身万能搭子!
还在为DeepSeek服务器繁忙而抓狂? 还在为API调用费用涨价而头疼? 还在为数据安全而担忧? 别急! 天翼云"息壤"算力互联调度平台出马 全面解锁DeepSeek新玩法 带你 ...
- [BZOJ3600] 没有人的算术 题解
妙不可言!妙绝人寰! 单点修,区间查,包是线段树的.考虑如何比较两节点大小. 考虑二叉搜索树,我们只要再给每个节点附一个权值,就可以比较了! 注意力相当惊人的注意到,假如给每个点一个区间 \([l_x ...
- KUKA库卡机器人维修
KUKA库卡机器人作为生产线上的核心设备,一旦出现KUKA机械手故障,将直接影响整个生产线的运行效率.及时的库卡机器人维修工作不仅能够迅速恢复机器人的工作状态,减少生产停滞时间,还能通过预防性维护降低 ...
