1. 为什么要在本地跑一个 MCP 客户端连 SQLite
MCP(Model Context Protocol)是让 AI 智能体标准化调用外部工具与数据源的协议,你可以把它理解成智能体的“工具箱接口”:模型负责思考,MCP 负责把“查数据库、写数据库”这类动作变成可被调用的工具。SQLite 则是本地最省事的结构化存储,一个文件就是一个库,不需要额外起服务。把这两者拼起来,再配一条统一的 API 通道,就能做出一个完全本地、数据不出机器的数据库对话智能体。
这套方案适合谁?适合手里有一堆本地数据(订单、日志、设备台账、测试数据)想用自然语言查询的开发者;适合在做 Agent 应用、需要给智能体接一个真实数据源的同学;也适合想先跑通 MCP 全链路、再迁移到 MySQL/PostgreSQL 的工程团队。整条链路里,模型侧走本地推理,工具侧走本地 MCP 服务,而模型请求的统一出口用 TaoToken 的 API 通道来承接,这样你既保留了本地数据的私密性,又不用为每个模型供应商单独写一套鉴权和重试逻辑。
我试过的坑是:一开始把数据库路径写成相对路径,MCP 服务被智能体以子进程方式拉起时工作目录变了,结果连到了一个空的 test.db,查出来永远是 0 行。后面统一改成绝对路径才稳定。所以下面所有配置里的路径,建议你都用绝对路径。
2. TaoToken 前置准备:拿到统一 API 通道
TaoToken 在这里扮演的角色是“模型请求的统一入口”。你的 MCP 客户端本身不直接关心底层是哪个模型,只把请求发到 TaoToken 的 API 地址,由它来路由。这样做的好处是:本地 MCP 服务只管工具调用,模型调用集中在一处配置,换模型、加模型都不用动业务代码。
第一步,去控制台创建 API Key。打开 https://taotoken.net/console ,登录后在 API Keys 页面新建一个密钥,复制出来保存好,后面配置里要用。密钥只显示一次,丢了就重新建一个。
第二步,确认你要用的模型名。在模型对话页面可以先手动试一句,确认这个模型在你的账号下可用:https://taotoken.net/models 。把模型名记下来,比如常见的对话模型或代码模型,填到后面的 config.toml 里。
第三步,如果你后面要做长期编码或 Agent 任务,建议顺手了解一下 Coding Plan,它更适合高频、长上下文的场景:https://taotoken.net/coding-plan 。接入文档在 https://taotoken.net/doc ,遇到参数不确定时以文档为准。
这里要强调一点:TaoToken 是合规的 API 服务入口,你只需要按文档填 base_url 和 api_key,不要自己拼任何非官方的转发地址。API 根地址统一用 https://taotoken.net/api ,注意这个地址后面不加任何查询参数。
3. 可复制的 config.toml 骨架
下面这份 config.toml 是给 MCP 客户端用的骨架,核心是三块:模型通道(指向 TaoToken)、MCP 服务定义(SQLite)、以及运行参数。你可以直接复制,把 api_key、model、数据库路径换成自己的。
# config.toml —— 本地 MCP 客户端配置骨架 [llm] # 统一走 TaoToken 的 API 通道 provider = "openai_compatible" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoToken密钥" model = "你的模型名" temperature = 0.2 max_tokens = 2048 timeout = 60 [agent] name = "sqlite-local-agent" system_prompt = """ 你是一个本地数据库助手。你可以调用 SQLite 工具来查询和修改数据。 规则: 1. 只操作被授权的数据库文件,不要尝试访问其他路径。 2. 写操作前先用 SELECT 确认影响范围。 3. 返回结果时用简洁的中文说明你执行了什么、影响了几行。 """ [mcp_servers.sqlite] # 用绝对路径,避免子进程工作目录变化导致连错库 command = "python" args = ["-m", "mcp_server_sqlite", "--db", "/Users/you/data/app.db"] transport = "stdio" enabled = true [runtime] log_level = "info" max_tool_rounds = 8几个参数说明一下。base_url 固定为 https://taotoken.net/api ,不要加斜杠后缀之外的路径。transport 用 stdio 是最省事的本地方式,MCP 服务作为子进程被拉起,通过标准输入输出通信,不需要开端口。max_tool_rounds 控制一次对话里最多调用几轮工具,太小会导致复杂查询被截断,太大又可能让智能体反复试错,8 是个比较稳的起点。
如果你用的是 Claude Code 这类客户端,配置文件的字段名会不一样,但本质相同:把模型通道指向 TaoToken,把 MCP 服务注册进去。Claude Code 的接入方式可以参考 https://taotoken.net/claude-code-anthropic ,里面给了对应的字段映射。
4. settings.json 配置片段与 MCP 服务注册
有些客户端(尤其是 VS Code 系插件和部分 Agent 框架)读的是 settings.json,而不是 config.toml。下面给一份等价的 settings.json 片段,字段按常见约定命名,你按自己客户端的实际 schema 微调即可。
{ "mcp": { "servers": { "sqlite": { "command": "python", "args": ["-m", "mcp_server_sqlite", "--db", "/Users/you/data/app.db"], "transport": "stdio", "env": { "SQLITE_READONLY": "0" } } } }, "llm": { "baseUrl": "https://taotoken.net/api", "apiKey": "sk-你的TaoToken密钥", "model": "你的模型名" }, "agent": { "maxToolRounds": 8, "logLevel": "info" } }注意 env 里的 SQLITE_READONLY,如果你只想让智能体查数据、不允许改数据,把它设成 "1",这样即使模型生成了 INSERT/UPDATE,工具层也会拒绝执行。这是本地场景里非常实用的一道闸。
MCP 服务本身可以用现成的 SQLite MCP server,也可以自己写一个最小实现。自己写的好处是可控,下面是一个精简版,暴露两个工具:查询和写入。
# sqlite_mcp_server.py —— 最小 SQLite MCP 服务 import sqlite3 import json from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent DB_PATH = "/Users/you/data/app.db" app = Server("sqlite-local") def get_conn(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn @app.list_tools() async def list_tools(): return [ Tool( name="query", description="执行 SELECT 查询,返回 JSON 数组", inputSchema={ "type": "object", "properties": {"sql": {"type": "string"}}, "required": ["sql"], }, ), Tool( name="execute", description="执行 INSERT/UPDATE/DELETE,返回影响行数", inputSchema={ "type": "object", "properties": {"sql": {"type": "string"}}, "required": ["sql"], }, ), ] @app.call_tool() async def call_tool(name: str, arguments: dict): conn = get_conn() try: cur = conn.cursor() if name == "query": cur.execute(arguments["sql"]) rows = [dict(r) for r in cur.fetchall()] return [TextContent(type="text", text=json.dumps(rows, ensure_ascii=False))] if name == "execute": cur.execute(arguments["sql"]) conn.commit() return [TextContent(type="text", text=f"affected_rows={cur.rowcount}")] return [TextContent(type="text", text=f"unknown tool: {name}")] finally: conn.close() async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ == "__main__": import asyncio asyncio.run(main())把 DB_PATH 换成你的绝对路径,然后用 python sqlite_mcp_server.py 就能作为 stdio 服务被客户端拉起。config.toml 里的 args 相应改成 ["/绝对路径/sqlite_mcp_server.py"]。
5. 连接验证与对话测试:从 0 行到正确结果
配置写完,先别急着让智能体自由发挥,按下面三步验证,能快速定位问题出在哪一层。
第一步,单独验证 MCP 服务能起来。在终端直接跑:
python -m mcp_server_sqlite --db /Users/you/data/app.db如果它没有立刻报错退出,说明服务本身没问题。如果报 ModuleNotFoundError,先装依赖:
pip install mcp mcp-server-sqlite第二步,验证 TaoToken 通道能通。用 curl 打一次模型列表或对话接口,确认密钥和地址正确:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的TaoToken密钥" \ -H "Content-Type: application/json" \ -d '{ "model": "你的模型名", "messages": [{"role": "user", "content": "只回复两个字:通了"}] }'返回里能看到模型输出,说明通道没问题。如果返回 401,检查密钥;返回 404,检查 base_url 是不是写成了带多余路径的形式。
第三步,跑端到端对话。启动你的 MCP 客户端,输入一句自然语言,比如“帮我看看 users 表里有多少条记录”。正常的话,你会看到客户端先调用 query 工具,拿到结果,再用中文回答你。整个过程在日志里能看到工具调用记录,形如 tool_call: query, args: {"sql": "SELECT COUNT(*) FROM users"}。
为了确认写入链路也通,再试一句“往 users 表插入一条 name 为 test 的记录”,然后自己用 sqlite3 命令行查一下:
sqlite3 /Users/you/data/app.db "SELECT * FROM users WHERE name='test';"能看到这条记录,说明从自然语言到数据库落盘的完整链路已经打通。
6. 本篇常见错排查
报错一:no such table: xxx。九成是数据库路径不对,MCP 服务连到了另一个空库。把 config.toml 和 settings.json 里的路径都改成绝对路径,再用ls -l /你的路径/app.db确认文件真实存在。
报错二:Connection refused 或超时。这是模型通道的问题,不是 MCP 的问题。检查 base_url 是否为 https://taotoken.net/api ,密钥是否过期,以及本机网络是否能正常访问该地址。注意不要在 base_url 后面拼 /v1/chat/completions 之外的奇怪路径。
报错三:智能体反复调用同一个工具、停不下来。把 max_tool_rounds 调小到 5 左右,同时在 system_prompt 里明确“拿到结果后直接回答,不要重复查询”。提示词对工具调用轮次的影响比想象中大。
报错四:写操作被拒绝。检查 env 里的 SQLITE_READONLY 是不是设成了 "1"。这个开关是故意设计的保护,要写入就改成 "0"。
报错五:中文返回乱码。在 MCP 服务里返回 JSON 时加 ensure_ascii=False,客户端侧统一按 UTF-8 解析。SQLite 本身对 UTF-8 支持很好,乱码基本都出在序列化环节。
排查顺序建议固定为:先单独起 MCP 服务 → 再 curl 模型通道 → 最后跑端到端。这样任何一层出问题都能立刻定位,不用在整条链路上瞎猜。
7. 下一步:把通道和工具都管起来
链路跑通之后,日常维护其实就两件事:管好 API Key,管好工具权限。密钥建议按用途分开建,本地调试一个、长期任务一个,出问题能单独吊销。工具权限上,读多写少的场景直接把 SQLITE_READONLY 打开,需要写入时再临时放开,比事后审计省心得多。
如果你准备把这个 MCP 客户端用到长期编码或 Agent 任务里,建议把模型通道切到 Coding Plan,长上下文和高频调用下更稳:https://taotoken.net/coding-plan 。密钥管理统一在 https://taotoken.net/api-keys 处理,接入细节以 https://taotoken.net/doc 为准。想先手动验证某个模型的表现,可以直接在 https://taotoken.net/models 里对话测试,确认没问题再写进 config.toml。整套配置里唯一需要记住的地址就是 https://taotoken.net/api ,其余都是围绕它的参数调整。