☰
mcp 学习第二篇:用 uv + Python + SQLite 给通义灵码搭一个本地 MCP 服务
2026/10/8 18:05:42 网站建设 项目流程

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.toml,uv 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.py:FastMCP 工具定义与可复制配置

服务端代码是整个流程的核心。在项目目录下新建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_path=DB_PATH): """获取SQLite数据库中所有表的名称""" conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") 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(f"PRAGMA 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(transport="stdio")

几个关键点值得说明。@mcp.tool()装饰器会把函数签名和 docstring 一起注册成工具描述,模型就是靠这些描述判断该调哪个工具,所以 docstring 要写清楚用途。transport="stdio"表示用标准输入输出通信,这是本地 MCP 最常用的方式,客户端启动子进程后通过管道收发消息。

execute_query里做了两件事:用row_factory = sqlite3.Row让结果能转成字典,方便模型理解;用startswith("SELECT")区分查询和写操作,写操作才 commit。异常里先 rollback 再返回错误字符串,避免连接处于脏状态。

写完先本地自测:

uv run sqlite_mcp.py

如果没有任何报错、进程挂起等待输入,说明服务端正常。stdio 模式下它不会打印东西,这是预期行为,按 Ctrl+C 退出即可。

接下来是通义灵码的配置。在 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 的配置项。把配置改回command+args的 stdio 形式即可。

排查顺序建议:先确认 uv 可用 → 再确认路径和文件存在 → 然后本地uv run能跑通 → 最后看客户端配置。逐层排除,比一上来就怀疑协议问题高效得多。

6. 把本地 MCP 用起来:从调试到日常查询的实用建议

服务跑通之后,有几个习惯能让它更好用。第一,工具函数的 docstring 写具体,比如「查询 users 表中指定城市的用户」比「查询数据」更能帮模型选对工具。第二,SQLite 路径尽量用绝对路径或基于项目根目录解析,避免换工作目录后找不到库。第三,写操作前可以先让模型查一次确认目标数据,减少误改。

如果你想把服务接到更多客户端,思路是一样的:任何支持 MCP 的编辑器都只需要一份command + args配置,指向同一个sqlite_mcp.py。想深入看协议细节和更多示例,可以翻官方文档;想快速验证模型对工具的理解能力,直接在对话里发几条不同措辞的请求,观察它是否稳定选中正确的工具。

需要生成和管理调用凭证时,可以到 TaoToken API Keys 处理;接入细节参考 TaoToken 接入文档。想先直观感受模型对话效果,用 模型对话 试几条;如果打算长期做编码类 Agent,Coding Plan 更合适。控制台在 console,官网入口是 taotoken.net。

最后留一个实操建议:把sqlite_mcp.py里的DB_PATH改成从环境变量读取,这样同一份代码能切换测试库和生产库,不用每次改代码。改完记得重启客户端,让新配置生效。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询