如何使用 FastMCP 搞一个 MySQL MCP Server,让 AI 写 SQL并调优

IT 文章42秒前更新 小编
1 0 0

日常开发中,你是不是也经常遇到这种情况:想让 AI 帮你写一段 SQL,或者优化一条慢查询,但光靠嘴描述业务逻辑,AI 给出来的结果总是“差点意思”?问题出在哪?缺上下文

尤其是数据库相关的任务,最关键的上下文就是表结构。如果你不告诉 AI 表里有哪些字段、主键外键怎么设的,它只能凭空猜——那结果自然没法直接用。

手动复制建表语句粘贴过去?太麻烦了。有没有更聪明的办法?有!这就是 MCP(Model Context Protocol) 要解决的问题。

ad

程序员导航

优网导航旗下整合全网优质开发资源,一站式IT编程学习与工具大全网站


什么是 MCP?简单说就是给大模型“接外挂”

MCP 是一种协议,能让大语言模型(LLM)安全地调用外部工具或服务。你可以把它理解成 LLM 的“插件系统”。通过 MCP Server,AI 助手就能动态获取数据库结构、执行查询、分析执行计划等,而不需要你手动喂数据。

本文就手把手教你用 Python + FastMCP 框架,写一个 MySQL MCP Server,让它在 AI 助手(比如 Windsurf)里跑起来,真正实现“懂你库”的智能 SQL 辅助。


为什么选 FastMCP?

FastMCP 是 Python 社区里一个轻量又实用的 MCP 实现框架。它:

  • 写起来简单,几行装饰器就能暴露工具;
  • 支持生产级部署;
  • 内置鉴权、日志、传输模式切换等功能。

对我们这种想快速验证想法的开发者来说,再合适不过。

ad

AI 工具导航

优网导航旗下AI工具导航,精选全球千款优质 AI 工具集


核心代码:四个工具搞定数据库交互

下面这个 mysql_mcp_server.py 文件,就是我们今天的主角。它通过四个工具函数,把 MySQL 的关键能力暴露给 AI:

#! /usr/bin/env python3
import os
import traceback
from typing import Any, Dict, List
import pymysql
from fastmcp import FastMCP
from pymysql import Connection

# 创建 FastMCP 实例,名字叫 "mysql-mcp-server"
mcp = FastMCP("mysql-mcp-server")

def get_db_config() -> Dict[str, Any]:
    """从环境变量读取数据库连接配置"""
    return {
        "host": os.getenv("MYSQL_HOST", "localhost"),
        "port": int(os.getenv("MYSQL_PORT", "3306")),
        "user": os.getenv("MYSQL_USER", "root"),
        "password": os.getenv("MYSQL_PASSWORD", ""),
        "database": os.getenv("MYSQL_DATABASE", ""),
        "cursorclass": pymysql.cursors.DictCursor  # 返回字典格式结果,方便处理
    }

def get_connection() -> Connection[Any]:
    """根据配置建立 MySQL 连接"""
    return pymysql.connect(**get_db_config())

💡 小贴士:这里所有数据库参数都从环境变量读取,既安全又灵活,适合本地调试和部署。

接下来是四个核心工具,每个都用 @mcp.tool 装饰器注册,AI 就能自动发现并调用它们:

1. 列出所有表(list_tables)

@mcp.tool("list_tables", description="List all the tables in the database")
def list_tables() -> List[str]:
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute("SHOW TABLES")
            # SHOW TABLES 返回的列名是动态的(如 Tables_in_test),所以取第一个 key 的值
            tables = [row[next(iter(row))] for row in cur.fetchall()]
            return tables
    except Exception as e:
        print("Error in list tables:", e)
        traceback.print_exc()
        raise RuntimeError(f"list tables failed: {e}")
    finally:
        conn.close()

2. 查看表结构(describe_table)

@mcp.tool("describe_table", description="Query the table structure for a specified table")
def describe_table(table_name: str) -> List[Dict[str, Any]]:
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            # 注意:这里没做 SQL 注入防护,仅限可信环境使用
            cur.execute(f"DESCRIBE {table_name}")
            columns = cur.fetchall()
            return list(columns) if columns else []
    except Exception as e:
        print("Error in describe table:", e)
        traceback.print_exc()
        raise RuntimeError(f"describe table failed: {e}")
    finally:
        conn.close()

⚠️ 注意f"DESCRIBE {table_name}" 这种拼接方式在生产环境有风险。如果要上线,建议加白名单校验或改用参数化方式(虽然 MySQL 的 DESCRIBE 不支持参数化)。

3. 分析 SQL 执行计划(explain_sql)

@mcp.tool("explain_sql", description="Explain a SQL query")
def explain_sql(query: str) -> List[Dict[str, Any]]:
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(f"EXPLAIN {query}")
            result = cur.fetchall()
            return result
    except Exception as e:
        print("Error in explain sql:", e)
        traceback.print_exc()
        raise RuntimeError(f"explain sql failed: {e}")
    finally:
        conn.close()

这个对优化慢 SQL 特别有用。AI 可以先让你提供原始 SQL,然后主动调用 explain_sql 看执行计划,再给出索引建议或重写方案。

4. 安全执行 SELECT 查询(run_sql)

@mcp.tool("run_sql", description="Run SELECT SQL query")
def run_sql(query: str, limit: int = 1000) -> List[Dict[str, Any]]:
    # 限制只允许 SELECT,防止误删改数据
    if not query.strip().lower().startswith("select"):
        raise RuntimeError("Only SELECT queries are supported")
    
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(f"{query} LIMIT {limit}")  # 自动加 LIMIT 防止查爆
            result = cur.fetchall()
            return result
    except Exception as e:
        print("Error in run sql:", e)
        traceback.print_exc()
        raise RuntimeError(f"run sql failed: {e}")
    finally:
        conn.close()

🔒 安全设计:只允许 SELECT,且自动加 LIMIT 1000,避免拖垮数据库。

ad

免费在线工具导航

优网导航旗下整合全网优质免费、免注册的在线工具导航大全

最后,启动服务:

if __name__ == "__main__":
    mcp.run()  # 默认使用 Stdio 模式(适合本地集成)
    # 如果要部署成 HTTP 服务,可改为:
    # mcp.run(transport="http", host="0.0.0.0", port=8000)

在 Windsurf 里配置并使用

Windsurf(或其他支持 MCP 的 AI 编辑器)需要一个配置文件来知道怎么调你的 Server。

~/.codeium/windsurf/mcp_config.json 里填入:

{
  "mcpServers": {
    "mysql-mcp-server": {
      "command": "python3",
      "args": ["/path/to/your/mysql_mcp_server.py"],
      "env": {
        "MYSQL_HOST": "localhost",
        "MYSQL_PORT": "3306",
        "MYSQL_USER": "root",
        "MYSQL_PASSWORD": "root",
        "MYSQL_DATABASE": "test"
      }
    }
  }
}

配置完重启 Windsurf,就可以直接对话了。

实测效果

  • :“用 mysql-mcp-server 查询我的数据库里有哪些表?”
    → AI 自动调 list_tables,返回表名列表。
  • 如何使用 FastMCP 搞一个 MySQL MCP Server,让 AI 写 SQL并调优:“查一下 moment 表的结构。”
    → AI 调 describe_table("moment"),返回字段详情。如何使用 FastMCP 搞一个 MySQL MCP Server,让 AI 写 SQL并调优
  • :“下面这段 SQL 很慢,帮我优化……”
    → AI 先分析逻辑,再主动问:“要不要我跑个 EXPLAIN 看看执行计划?”
    → 你回“要”,它就调 explain_sql,结合结果给出具体优化建议。如何使用 FastMCP 搞一个 MySQL MCP Server,让 AI 写 SQL并调优

实测下来,有了真实表结构和执行计划,AI 的建议靠谱多了,不再是泛泛而谈。


想部署到服务器?改两行就行

上面默认是 Stdio 模式,适合本地 IDE 集成。如果你想把 MCP Server 部署成独立服务,只需:

  1. 修改启动代码:
    if __name__ == "__main__":
        mcp.run(transport="http", host="0.0.0.0", port=8000)
    
  2. 更新 mcp_config.json,改成 URL 方式:
    {
      "mcpServers": {
        "mysql-mcp-server": {
          "url": "http://your-server-ip:8000/mcp"
        }
      }
    }
    

这样,任何地方的 AI 助手只要能访问这个 URL,就能用你的 MySQL 上下文了。


总结

通过 FastMCP 写一个 MySQL MCP Server,成本极低,但收益明显:

  • 让 AI 写 SQL 时“看得见”你的表结构;
  • 优化慢查询时能结合真实执行计划;
  • 所有操作可控、安全、可审计。

这比你每次手动粘贴 DDL 或 EXPLAIN 结果高效太多。尤其适合团队共享数据库上下文,或者集成到内部开发工具链中。

© 版权声明

相关文章

暂无评论

暂无评论...