效果展示



调用MCP执行SQL时会自动弹出消息框,倒计时结束前未审批会自动拒绝该SQL的执行,此外可以通过备注介入,给AI合理的建议。
构建 MCP 服务
导入的包
python
import json
import os
import subprocess
import time
from functools import wraps
from pathlib import Path
from mcp.server import FastMCP
MIN_WINDOW_SIZE = (500, 300)
MAX_WINDOW_SIZE = (800, 550)
DATA_SAVE_PATH = "E:/Workspace/MCP/tools/execute_sql/data"
WAILS_EXE_PATH = "E:/Workspace/MCP/tools/execute_sql/build/bin/execute-sql.exe"
mcp = FastMCP("ExecuteSQLReview", host="127.0.0.1", port=50050)
if __name__ == "__main__":
mcp.run(transport="streamable-http")
stdio 无法正常弹窗,请使用 streamable-http 模式。
装饰器
python
def mcp_tool(enable=True, max_length=2000):
def decorator(func):
original_func = func
@wraps(func)
def wrapper(*args, **kwargs):
if not enable:
return original_func(*args, **kwargs)
raw_response = original_func(*args, **kwargs)
if len(str(raw_response)) > max_length:
file_name = DATA_SAVE_PATH + f"/result-{time.time()}.json"
with open(file_name, "w", encoding="utf-8") as f:
f.write(json.dumps(raw_response, ensure_ascii=False, indent=4))
return {
"code": 200,
"msg": f"响应结果过长,请调用文件工具查看",
"file_path": os.path.abspath(file_name)
}
return raw_response
if enable:
return mcp.tool()(wrapper)
else:
return wrapper
return decorator
装饰器的作用有两方面,一是方便快速启用停用MCP工具,二是保存SQL执行结果,因为执行SQL返回的结果往往较多,因此这里新增了返回长度判断,如果过长则自动保存至文件中并提示AI使用文本工具查看。
计算窗口尺寸
python
def calc_window_size(content, min_window_size=MIN_WINDOW_SIZE, max_window_size=MAX_WINDOW_SIZE):
if not content:
return min_window_size
# 计算行数和最大行长度
lines = content.split('/n')
line_count = len(lines)
max_char_count = max(len(line) for line in lines) if lines else 0
# 每个字符的预估宽度
CHAR_WIDTH = 13
# 每行的预估高度
LINE_HEIGHT = 28
# 基础边距
BASE_PADDING = 20
# 计算尺寸(考虑边距)
window_width = max_char_count * CHAR_WIDTH + BASE_PADDING * 2
window_height = line_count * LINE_HEIGHT + BASE_PADDING * 2
# 边界处理
def clamp(value, min_val, max_val):
return max(min_val, min(value, max_val))
width = clamp(window_width, min_window_size[0], max_window_size[0])
height = clamp(window_height, min_window_size[1], max_window_size[1])
return width, height
执行SQL
python
@mcp_tool(enable=True)
def execute_sql_review(sql: str, tags: list[dict]) -> dict:
"""
执行SQL语句
sql: 执行的SQL语句
tags: 标签列表, 执行SQL标签, 查询, 更新, 删除.... 例: [{"name": "更新语句", "color": "#ffcb6b"}]
"""
data = {
"sql": sql,
"options": [
{"name": "通过", "value": "accept"},
{"name": "拒绝", "value": "reject", "is_default": True},
],
"tags": tags,
"auto_close": 50
}
data_save_path = DATA_SAVE_PATH + f"/data-{time.time()}.json"
result_save_path = DATA_SAVE_PATH + f"/result-{time.time()}.json"
with open(data_save_path, "w", encoding="utf-8") as f:
f.write(json.dumps(data, ensure_ascii=False, indent=4))
width, height = calc_window_size(sql)
cmd = [
str(Path(WAILS_EXE_PATH).resolve()),
"-data_path", str(Path(data_save_path).resolve()),
"-result_path", str(Path(result_save_path).resolve()),
"-title", "ExecuteSQLReview",
"-width", str(width),
"-height", str(height),
"-min_width", str(MIN_WINDOW_SIZE[0]),
"-min_height", str(MIN_WINDOW_SIZE[1]),
"-on_top", "true",
"-disable_resize", "true",
"-frameless", "false",
"-close_confirm", "false"
]
# 运行
subprocess.run(
cmd, capture_output=False, text=True, encoding='utf-8', errors='ignore', shell=False
)
# 获取执行结果
with open(result_save_path, "r", encoding="utf-8") as f:
result = json.load(f)
# 判断执行结果
if result["select"] == "reject":
return result
# 模拟执行SQL并返回
return {
"run_ti": 566,
"run_result": [{"name": "a", "age": 15}, {"name": "b", "age": 16}]
}
这里执行SQL的逻辑可以自定义实现。
MCP 配置
