SQL 执行 MCP 工具开发(一)

效果展示

调用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 配置

相关推荐
LTD营销SaaS5 小时前
22站点智能成功申请“AI 创建业务型网站”发明专利
人工智能·ai建站·站点智能·22集团·ai创建业务型网站
EQUINOX15 小时前
【论文精读】| LLaVA
论文阅读·人工智能·深度学习
2601_957879335 小时前
电商在用什么 AI 视频创作工具?2026年从“做视频”到“做素材库”的工具变化
大数据·人工智能
yangdaxiageo5 小时前
AI搜索广告的商业化底座:GEO技术架构的三层模型详解
人工智能·架构
谢白羽5 小时前
SGLang的AWQ量化笔记
笔记·python·sglang
QZSJTR6 小时前
2026年AI搜索引擎技术原理与GEO优化实战:从RAG到AI Agent
人工智能·搜索引擎·chatgpt
小白羊丨6 小时前
如何诊断 Prompt 模板导致的效果下降?
人工智能·算法·prompt
水獭比特6 小时前
Vercel AI SDK Files V4:把文件交付做成可验收闭环
人工智能
迷迭香yy6 小时前
基金档案数据工程实战从收入分析到持仓穿透的Python解析 IG50免费开源股票数据API接口
开发语言·python
用户7565061786116 小时前
开发 Nemu 的第四天:真实 Beta 如何把一帧判断改造成状态机
aigc·ai编程