FastMCP 实战:30 行 Python 给 AI 造一个数据库查询工具

FastMCP 实战:30 行 Python 给 AI 造一个数据库查询工具

上周帮团队搞了个内部知识库检索的需求------产品经理想在 Claude 里直接查数据库里的用户反馈数据。我一开始想的是写个 API,再套个 function calling 的 wrapper。折腾了半天发现 MCP(Model Context Protocol)+ FastMCP 这条路简单得多。30 来行代码,Claude Desktop 里直接就能查 SQLite 了。

这篇把我从零搭建的过程记下来,包括踩的坑。

MCP 是什么,一句话说清楚

MCP 是 Anthropic 搞的一个开放协议,干的事情很简单:让 AI 应用和外部工具之间有个统一的通信标准。你可以把它理解成 AI 世界的 USB-C 接口------不管你是数据库、文件系统还是 API 服务,只要实现了 MCP 协议,Claude、VS Code、Cursor 这些客户端都能直接连上。

协议本身基于 JSON-RPC 2.0,分两层:数据层管消息格式和语义,传输层管通信方式(本地 stdio 或远程 HTTP)。

FastMCP 是目前最主流的 Python MCP 框架。2024 年 FastMCP 1.0 被合并进了官方 Python SDK,现在独立维护的版本日下载量破百万,大概 70% 的 MCP Server 底层都在跑它。

环境准备

bash 复制代码
# 用 uv 安装(推荐)
uv pip install fastmcp

# 或者 pip
pip install fastmcp

# 验证安装
python -c "import fastmcp; print(fastmcp.__version__)"

我本地环境是 Python 3.11 + macOS,FastMCP 3.x 版本。Windows 和 Linux 一样能跑。

第一个 MCP Server:5 分钟跑通

先从最简单的开始,造一个能算数的工具:

python 复制代码
# calc_server.py
from fastmcp import FastMCP

mcp = FastMCP("计算器")

@mcp.tool
def add(a: float, b: float) -> float:
    """两个数相加"""
    return a + b

@mcp.tool
def multiply(a: float, b: float) -> float:
    """两个数相乘"""
    return a * b

if __name__ == "__main__":
    mcp.run()

跑起来:

bash 复制代码
python calc_server.py

默认走 stdio 传输,这时候 Server 在等客户端连接。想用 HTTP 的话:

bash 复制代码
python calc_server.py  # 或者在代码里改成 mcp.run(transport="http", port=8000)

FastMCP 做了三件事:把函数名当工具名,把 docstring 当工具描述,把参数类型标注转成 JSON Schema。你什么都不用手动配。

正经项目:给 AI 接一个 SQLite 数据库

下面是我在实际项目里用的代码,做了简化。需求是让 AI 能查一个用户反馈数据库。

python 复制代码
# feedback_server.py
import sqlite3
from fastmcp import FastMCP

DB_PATH = "feedback.db"

mcp = FastMCP(
    "用户反馈查询",
    instructions="这个服务器提供用户反馈数据的查询功能。"
                 "用 search_feedback 按关键词搜索,用 get_stats 看整体统计。"
)

def get_conn():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn

@mcp.tool
def search_feedback(keyword: str, limit: int = 20) -> list[dict]:
    """按关键词搜索用户反馈。keyword 是搜索词,limit 控制返回数量,默认 20 条。"""
    conn = get_conn()
    rows = conn.execute(
        "SELECT id, user_id, content, created_at, rating "
        "FROM feedback WHERE content LIKE ? ORDER BY created_at DESC LIMIT ?",
        (f"%{keyword}%", limit)
    ).fetchall()
    conn.close()
    return [dict(r) for r in rows]

@mcp.tool
def get_stats() -> dict:
    """获取反馈数据的整体统计:总数、平均评分、最近 7 天数量。"""
    conn = get_conn()
    total = conn.execute("SELECT COUNT(*) FROM feedback").fetchone()[0]
    avg_rating = conn.execute("SELECT AVG(rating) FROM feedback").fetchone()[0]
    recent = conn.execute(
        "SELECT COUNT(*) FROM feedback "
        "WHERE created_at > datetime('now', '-7 days')"
    ).fetchone()[0]
    conn.close()
    return {
        "total": total,
        "avg_rating": round(avg_rating, 2) if avg_rating else 0,
        "recent_7d": recent
    }

@mcp.resource("data://schema")
def get_schema() -> str:
    """返回 feedback 表的建表语句,方便 AI 理解数据结构。"""
    conn = get_conn()
    schema = conn.execute(
        "SELECT sql FROM sqlite_master WHERE type='table' AND name='feedback'"
    ).fetchone()
    conn.close()
    return schema[0] if schema else "表不存在"

if __name__ == "__main__":
    mcp.run(transport="http", port=8000)

这里用了三个 MCP 概念:

  • Tool (工具):search_feedbackget_stats,AI 可以主动调用,执行查询操作
  • Resource (资源):get_schema,被动数据源,AI 可以读取但不触发副作用
  • instructions 参数:告诉 AI 这个 Server 是干什么的、该怎么用

客户端连接:两种方式

方式一:配置到 Claude Desktop

编辑 ~/Library/Application Support/Claude/claude_desktop_config.json

json 复制代码
{
  "mcpServers": {
    "feedback": {
      "command": "python",
      "args": ["/path/to/feedback_server.py"]
    }
  }
}

重启 Claude Desktop,聊天界面右上角会多出一个锤子图标,点开能看到你注册的工具。直接在对话里问"最近有哪些关于登录问题的反馈?",Claude 会自动调用 search_feedback

方式二:用 FastMCP Client 写代码连

python 复制代码
# client.py
import asyncio
from fastmcp import Client

client = Client("http://localhost:8000/mcp")

async def main():
    async with client:
        # 调用工具
        result = await client.call_tool("search_feedback", {"keyword": "登录"})
        print(result)
        
        # 读取资源
        schema = await client.read_resource("data://schema")
        print(schema)

asyncio.run(main())

FastMCP Client 是异步的,必须在 async with 上下文里用。

进阶:加个 Prompt 模板

MCP 还支持 Prompt(提示词模板),让 AI 知道面对特定任务该怎么组织对话:

python 复制代码
@mcp.prompt
def analyze_negative(min_rating: int = 3) -> str:
    """分析低评分反馈的提示词模板。"""
    return (
        f"请帮我分析评分低于 {min_rating} 的用户反馈。"
        "步骤:1. 先用 get_stats 看整体情况;"
        "2. 搜索评分低的反馈内容;"
        "3. 归纳主要问题类别;"
        "4. 给出改进建议。"
    )

Prompt 不是工具,它不执行操作。它给 AI 一个"操作手册",告诉它面对某类任务时应该按什么顺序调用哪些工具。

踩坑记录

坑 1:sync 函数会阻塞吗?

不会。FastMCP 3.x 默认把同步函数扔到线程池里跑(run_in_thread=True),所以 sqlite3 这种同步库可以直接用,不需要改成 aiosqlite。多个请求同时来也不会互相卡住。

但如果你的函数涉及线程亲和性(比如 Windows 上的 COM 组件),需要设 @mcp.tool(run_in_thread=False) 让它在事件循环线程里跑。

坑 2:*args**kwargs 不能用

FastMCP 需要完整的参数签名来生成 JSON Schema,所以工具函数不能有 *args**kwargs。所有参数必须明确声明类型。

这个限制一开始没注意,我写了个 def query(sql: str, **params) 结果启动直接报错。

坑 3:HTTP 传输的 URL 路径

用 HTTP 传输时,MCP 端点的路径是 /mcp,不是根路径。Client 连接地址要写 http://localhost:8000/mcp,不是 http://localhost:8000/。我第一次连的时候 404 了好一会儿才反应过来。

坑 4:Resource URI 格式

Resource 的 URI 必须带 scheme,比如 data://schemafile:///path。写成 schema 会报 invalid URI。这个在官方文档里有说,但很容易忽略。

stdio vs HTTP:什么时候用哪个

场景 传输方式 原因
Claude Desktop 本地连接 stdio 无网络开销,启动快
多个客户端共享一个 Server HTTP stdio 只支持单客户端
远程部署 HTTP 必须走网络
开发调试 stdio 简单,不需要管端口

stdio 是默认值,适合本地场景。HTTP 用的是 Streamable HTTP 协议(不是普通的 REST API),支持 Server-Sent Events 做流式响应。

生产环境补充

真要上生产,还需要考虑几件事:

认证:FastMCP 支持 OAuth 和 Token 验证。HTTP 传输时建议加上,不然谁都能连:

python 复制代码
from fastmcp.server.auth import BearerTokenVerifier

mcp = FastMCP(
    "生产服务",
    auth=BearerTokenVerifier(token="your-secret-token")
)

超时控制:给工具加超时,防止慢查询拖垮整个 Server:

python 复制代码
@mcp.tool(timeout=10.0)  # 10 秒超时
def slow_query(sql: str) -> list[dict]:
    ...

中间件:FastMCP 3.x 支持 Middleware,可以做日志、限流、错误处理这些横切关注点。跟 Web 框架的中间件概念一样。

和直接写 function calling 比,MCP 好在哪

我之前用 OpenAI 的 function calling 也做过类似的事。对比下来 MCP 的优势在于:

  1. 协议标准化。function calling 每家 API 格式不一样,MCP 是开放协议,写一次 Server 所有支持 MCP 的客户端都能用
  2. Server 独立部署。工具逻辑和 AI 应用解耦,改数据库查询不用动 AI 那边的代码
  3. 发现机制。客户端可以自动发现 Server 提供了哪些工具、资源、提示词,不需要手动维护 schema 列表
  4. 传输灵活。同一套代码可以跑在本地(stdio)也可以跑在远端(HTTP),不用改业务逻辑

当然 MCP 也不是万能的。如果你只用 OpenAI 一家的 API、工具逻辑很简单,function calling 直接搞反而更快。MCP 适合工具多、客户端多、需要复用的场景。

小结

FastMCP 把 MCP 协议的复杂度基本屏蔽掉了。写个 Python 函数加个装饰器,就是一个 MCP 工具。对于想给 AI 接外部数据源的场景,这比自己封 API + function calling 省事不少。

代码都能直接跑。想试的话先装个 FastMCP,照着上面的例子改改数据库路径就行。

GitHub 地址:github.com/PrefectHQ/f... 官方文档:gofastmcp.com

相关推荐
imbackneverdie2 小时前
AI写文献综述,自动引用100篇真实参考文献
人工智能·ai·aigc·论文·ai写作·文献综述·ai工具
用户5191495848453 小时前
WP NssUser Register 权限提升漏洞利用工具 (CVE-2024-54363)
人工智能·aigc
手写码匠6 小时前
从零手写 SQL 查询引擎:解析器、优化器与执行器实战
人工智能·深度学习·算法·aigc
小快说网安7 小时前
当GPT Image 2遇见企业级AI大模型聚合平台:快快云云安全的接入逻辑与价值重构
人工智能·gpt·ai·chatgpt·aigc
寻道码路10 小时前
LangChain4j Java AI 应用开发实战(四):提示词工程进阶 - 模板化与结构化 Prompt 设计
java·人工智能·ai·prompt·aigc
灵感__idea16 小时前
《AI工程》:大语言模型,到底是什么?
aigc·openai·ai编程
后端小肥肠1 天前
文章没人看?多半是标题的锅:我用 Codex + Obsidian 做了个爆款标题 Skill
人工智能·aigc·agent
倔强的石头_1 天前
AI 客服像复读机?我给它装了个 3D 身体,对话直接变活了
aigc
用户5191495848451 天前
SSH漏洞批量扫描器 - 多线程域名单漏洞检测工具
人工智能·aigc