资讯动态

mcp 学习第二篇:用 uv + Python + SQLite 给通义灵码搭一个本地 MCP 服务

发布时间:2026/10/8 18:05:54 来源:尧图企业网站定制
1. 从零理解本地 MCP 服务uv Python SQLite 到底能做什么如果你已经看过 MCP 协议的基础介绍也照着 demo 写过一版能跑通的代码接下来最容易卡住的地方往往不是协议本身而是「怎么把它变成一个真正能被编辑器调用的本地服务」。这篇就聚焦这个动手环节用 uv 管理 Python 环境用 SQLite 存数据给通义灵码接入一个本地 MCP 服务让它在对话里直接查表结构、执行 SQL。先说清楚这套组合各自负责什么。MCP 是模型和外部工具之间的通信协议你可以把它理解成「模型世界的 USB 接口」——只要服务端按协议暴露工具客户端就能发现并调用。uv 是 Rust 写的 Python 项目和包管理工具装依赖、建虚拟环境、跑脚本一条命令搞定比传统 pip venv 少很多手动步骤。SQLite 则是单文件数据库不需要额外起服务特别适合本地做实验。通义灵码作为客户端负责把自然语言转成对工具的调用。适合谁看写过一点 Python、想在本地把 MCP 跑通、又不想折腾复杂环境的人。整篇的节奏是先备好环境再写服务端代码然后配置到通义灵码里最后发一次真实请求验证结果。每一步都给可复制的命令和配置你跟着敲就能复现。我试过在 Windows 11 VS Code 上走完整流程中间踩过路径和依赖的坑后面会单独用一节讲常见报错。先把环境搭起来。2. 前置准备uv 安装与 SQLite MCP 项目初始化这一节把地基打好。MCP 官方推荐的 Python 运行工具就是 uv所以第一步是把它装上。Windows 下用 PowerShell 执行官方安装脚本powershell -ExecutionPolicy ByPass -c irm https://astral.sh/uv/install.ps1 | iex装完之后有个容易忽略的点务必重启终端否则uv命令不会被识别。我第一次装完直接在当前窗口敲uv --version提示找不到命令重启后才正常。接着创建项目目录并初始化uv init sqlite_mcp cd sqlite_mcp uv venv .venv\Scripts\activate uv add mcp[cli]uv init会生成pyproject.tomluv venv建虚拟环境uv add把依赖写进配置并安装。装完后你的pyproject.toml里应该有类似这样的依赖声明[project] name sqlite-mcp version 0.1.0 requires-python 3.10 dependencies [ mcp[cli]1.2.0, ]这里mcp[cli]的方括号表示额外安装 CLI 相关依赖别漏掉否则后面调试工具会缺东西。Python 版本建议 3.10 以上FastMCP 用到的类型标注在低版本上会有兼容问题。数据库这边准备一个测试用的mcp_test.db。如果你上一篇已经建过直接拷到项目目录没有的话用下面的脚本建一张示例表import sqlite3 conn sqlite3.connect(mcp_test.db) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, city TEXT ) ) cursor.execute(INSERT INTO users (name, age, city) VALUES (张三, 28, 杭州)) cursor.execute(INSERT INTO users (name, age, city) VALUES (李四, 32, 成都)) conn.commit() cursor.close() conn.close() print(数据库初始化完成)把这段存成init_db.py用uv run init_db.py跑一次目录下就会出现mcp_test.db。到这里环境就绪可以写服务端了。3. 编写 sqlite_mcp.pyFastMCP 工具定义与可复制配置服务端代码是整个流程的核心。在项目目录下新建sqlite_mcp.py用 FastMCP 把三个函数暴露成工具查所有表、查表结构、执行 SQL。装饰器写法很像 Flask理解成本低。import sqlite3 from mcp.server.fastmcp import FastMCP mcp FastMCP(sqlite_mcp) DB_PATH mcp_test.db def get_all_tables(db_pathDB_PATH): 获取SQLite数据库中所有表的名称 conn sqlite3.connect(db_path) cursor conn.cursor() cursor.execute(SELECT name FROM sqlite_master WHERE typetable;) tables [row[0] for row in cursor.fetchall()] cursor.close() conn.close() return tables mcp.tool() def get_all_table_structures(): 获取SQLite数据库中所有表的结构信息 tables get_all_tables(DB_PATH) all_structures {} conn sqlite3.connect(DB_PATH) cursor conn.cursor() for table in tables: cursor.execute(fPRAGMA table_info({table})) all_structures[table] cursor.fetchall() cursor.close() conn.close() return all_structures mcp.tool() def execute_query(sql: str, params: tuple None): 执行SQL查询并返回结果 conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row cursor conn.cursor() try: cursor.execute(sql, params or ()) if sql.strip().upper().startswith(SELECT): return [dict(row) for row in cursor.fetchall()] conn.commit() return {affected_rows: cursor.rowcount} except sqlite3.Error as e: conn.rollback() return f数据库错误: {e} finally: cursor.close() conn.close() if __name__ __main__: mcp.run(transportstdio)几个关键点值得说明。mcp.tool()装饰器会把函数签名和 docstring 一起注册成工具描述模型就是靠这些描述判断该调哪个工具所以 docstring 要写清楚用途。transportstdio表示用标准输入输出通信这是本地 MCP 最常用的方式客户端启动子进程后通过管道收发消息。execute_query里做了两件事用row_factory sqlite3.Row让结果能转成字典方便模型理解用startswith(SELECT)区分查询和写操作写操作才 commit。异常里先 rollback 再返回错误字符串避免连接处于脏状态。写完先本地自测uv run sqlite_mcp.py如果没有任何报错、进程挂起等待输入说明服务端正常。stdio 模式下它不会打印东西这是预期行为按 CtrlC 退出即可。接下来是通义灵码的配置。在 VS Code 里打开通义灵码的智能体对话窗口模型选 qwen3点 MCP 工具再点小图标打开 MCP 配置文件加入下面这段{ mcpServers: { sqlite_mcp: { command: uv, args: [ --directory, D:\\博客\\mcp1\\sqlite_mcp, run, sqlite_mcp.py ] } } }这里的--directory必须指向你实际的项目绝对路径Windows 下反斜杠要写成双反斜杠。保存后通义灵码会自动识别出代码里的三个工具。三件套对齐一下Base URL 走本地 stdio 不需要填Key 也不需要Model ID 在客户端侧选 qwen3 即可——本地 MCP 的鉴权发生在客户端和模型之间服务端只负责执行工具。4. 验证请求在通义灵码里完成一次真实工具调用配置保存后回到通义灵码对话窗口先确认 MCP 工具列表里出现了sqlite_mcp并且展开能看到get_all_table_structures和execute_query。如果没出现多半是路径写错或 uv 不在系统 PATH 里下一节会细讲。现在发一条自然语言请求比如帮我看看 mcp_test.db 里有哪些表users 表的结构是什么正常情况下通义灵码会先调用get_all_table_structures拿到返回后组织成人类可读的回答。你会看到类似这样的结果{ users: [ [0, id, INTEGER, 0, null, 1], [1, name, TEXT, 1, null, 0], [2, age, INTEGER, 0, null, 0], [3, city, TEXT, 0, null, 0] ] }再发一条带查询的查一下 users 表里所有年龄大于 30 的人模型会生成SELECT * FROM users WHERE age 30并调用execute_query返回[{id: 2, name: 李四, age: 32, city: 成都}]到这一步一次完整的「自然语言 → 工具调用 → 结果校验」就闭环了。校验结果时重点看两处一是工具是否被正确选中对话里通常会显示调用了哪个工具二是返回的数据是否和数据库真实内容一致。你可以手动uv run一个查询脚本对比确认没有偏差。如果想验证写操作发一条「把张三的年龄改成 30」模型会调用execute_query执行 UPDATE返回{affected_rows: 1}。再查一次确认数据变了说明读写都通。5. 常见报错排查401、local proxy failed 与 reading choices 怎么解这一节把实际会撞到的坑列出来对照报错找原因。报错一command not found: uv或客户端启动服务失败。这是最常见的问题。原因是 uv 装完后没重启终端或者 uv 的安装路径没进系统 PATH。解决方式是重启终端后uv --version确认能识别如果还不行找到 uv 安装目录通常在%USERPROFILE%\.local\bin手动加进环境变量。客户端调用时用的是系统 PATH所以这一步必须过。报错二local proxy failed或连接被拒绝。本地 stdio 模式下一般不会出现网络代理问题如果看到这类提示先检查配置文件里的--directory路径是否存在、sqlite_mcp.py文件名是否拼对。路径里有中文或空格时确保 JSON 里正确转义。另外确认mcp_test.db就在项目目录下代码里用的是相对路径工作目录不对会找不到库。报错三401 Unauthorized。本地 MCP 服务本身不做鉴权出现 401 通常是客户端侧的模型 API Key 没配好。检查通义灵码里登录状态是否正常或者你用的模型服务 Key 是否过期。把 Key 重新填一次重启对话窗口。报错四Error reading choices或返回结构解析失败。这类错误多半是工具返回的数据格式模型无法解析。检查execute_query的返回SELECT 返回的是字典列表写操作返回的是{affected_rows: n}都是可序列化的。如果你改过代码返回了自定义对象模型就解析不了。保持返回纯 dict / list / str。报错五OAuth 相关提示。本地 stdio 服务不涉及 OAuth如果看到这类字样说明你可能误配了远程 MCP 的配置项。把配置改回commandargs的 stdio 形式即可。排查顺序建议先确认 uv 可用 → 再确认路径和文件存在 → 然后本地uv run能跑通 → 最后看客户端配置。逐层排除比一上来就怀疑协议问题高效得多。6. 把本地 MCP 用起来从调试到日常查询的实用建议服务跑通之后有几个习惯能让它更好用。第一工具函数的 docstring 写具体比如「查询 users 表中指定城市的用户」比「查询数据」更能帮模型选对工具。第二SQLite 路径尽量用绝对路径或基于项目根目录解析避免换工作目录后找不到库。第三写操作前可以先让模型查一次确认目标数据减少误改。如果你想把服务接到更多客户端思路是一样的任何支持 MCP 的编辑器都只需要一份command args配置指向同一个sqlite_mcp.py。想深入看协议细节和更多示例可以翻官方文档想快速验证模型对工具的理解能力直接在对话里发几条不同措辞的请求观察它是否稳定选中正确的工具。需要生成和管理调用凭证时可以到 TaoToken API Keys 处理接入细节参考 TaoToken 接入文档。想先直观感受模型对话效果用 模型对话 试几条如果打算长期做编码类 AgentCoding Plan 更合适。控制台在 console官网入口是 taotoken.net。最后留一个实操建议把sqlite_mcp.py里的DB_PATH改成从环境变量读取这样同一份代码能切换测试库和生产库不用每次改代码。改完记得重启客户端让新配置生效。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑