1. 从一句自然语言到一张图表,中间到底缺了什么
MCP 智能体构建示例这件事,很多人卡在第一步:模型能聊天,但碰不到你的数据库。你问“哪个区域的企业利润最高”,它只能给你一段听起来很对、但跑不通的 SQL。原因不复杂——大模型本身没有连接你本地数据的能力,它需要一个标准化的通道去调用外部工具,这个通道就是 MCP(Model Context Protocol)。它由 Anthropic 主导提出,本质上是把 function calling 抽象成一套开放协议,让模型和外部数据源、工具之间用统一接口通信。你可以把它理解成“AI 世界的 USB-C”:不管对面是数据库、文件系统还是画图服务,插上就能用。
这篇要解决的是一个最小闭环:用 MCP 协议搭一个智能体服务端,通过 TaoToken 统一 Key 接入大模型,让它根据自然语言自动生成 SQL、执行查询、再把结果渲染成图表。适合谁?适合已经会一点 Python、想让大模型真正操作自己数据的开发者;也适合正在选型 MCP 接入方案、不想为每个模型单独维护一套 Key 的团队。我试过把模型调用、SQL 生成、可视化拆成三个 MCP 工具,串起来跑通之后,从提问到出图大概十几秒。下面把可复制的骨架、TaoToken 的接入参数、以及一次端到端验证动作完整给出来。
2. 为什么用 TaoToken 统一 Key 接 MCP 服务端
MCP 服务端里最绕的一环是模型调用。你写 SQL 生成要调一次模型,写可视化代码又要调一次模型,如果每个环节都单独配 Key、单独改 base_url,代码会迅速变成一团乱麻。更麻烦的是换模型:今天用这个,明天想换另一个,每个调用点都得动。
TaoToken 在这里的价值是提供一个统一的 API 通道。你只需要在环境变量里维护一份 Key 和一个 base_url,MCP 服务端里所有模型调用都走它。这样换模型时只改一个 model 名字,不用碰业务代码。对 MCP 这种“一个服务端里可能有多处模型调用”的场景,统一 Key 能省掉大量重复配置。
接入信息如下,建议直接写进.env,不要硬编码在代码里:
| 配置项 | 值 | 说明 |
|---|---|---|
| API Base | https://taotoken.net/api | OpenAI 兼容格式,langchain 可直接用 |
| API Key | 在控制台创建 | 形如sk-开头,注意保密 |
| 模型名 | 按需填写 | 例如对话/代码类模型,填控制台里可用的名字 |
| 官网入口 | https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= | 注册与文档入口 |
注意:API Base 用
https://taotoken.net/api,不要在后面拼多余的路径。langchain 的ChatOpenAI会自动补/chat/completions。
Key 的创建入口在控制台的 API Keys 页面,文档在接入文档里,两个地址都带上来源参数方便你回查:
- API Keys:
https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite - 接入文档:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
如果你后面要做长期编码或 Agent 类任务,可以了解下 Coding Plan,它更适合高频调用场景:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite。
3. 可复制的 MCP 服务端配置骨架
整个服务端分三块:模型调用模块、SQL 生成与执行模块、可视化模块。用FastMCP起服务,用 SSE 传输,这样客户端可以直接通过 URL 连接,不用自己写 stdio 进程管理。
先装依赖:
pip install mcp langchain langchain-openai sqlalchemy psycopg2-binary pandas matplotlib python-dotenv3.1 统一模型调用模块
把模型初始化抽成一个函数,所有工具共用。这样 TaoToken 的 Key 只读一次,换模型也只改一处。
import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain.prompts import PromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain.schema.runnable import RunnablePassthrough load_dotenv() def build_model(): return ChatOpenAI( model=os.getenv("TAOTOKEN_MODEL", "deepseek-chat"), openai_api_key=os.getenv("TAOTOKEN_API_KEY"), openai_api_base="https://taotoken.net/api", max_tokens=1024, temperature=0, ) model = build_model().env文件长这样:
TAOTOKEN_API_KEY=sk-你的key TAOTOKEN_MODEL=deepseek-chat DATABASE_URI=postgresql://postgres:123456@localhost:5432/demo3.2 SQL 生成工具
模型不知道你的表结构,所以要先喂元数据。把表结构写成一个 JSON 文件,运行时读进来拼进 prompt。这里用metadata.json描述两张表:企业表company和利润表profit。
import json def load_metadata(path="metadata.json"): with open(path, "r", encoding="utf-8") as f: return json.load(f) SQL_PROMPT = PromptTemplate( template=( "有如下表结构信息:{metadata}。" "请将自然语言查询 '{question}' 转换为 PostgreSQL SQL," "表名和字段名都要加双引号,仅返回 SQL 语句,不要解释。" ), input_variables=["metadata", "question"], ) sql_chain = ( {"metadata": RunnablePassthrough(), "question": RunnablePassthrough()} | SQL_PROMPT | model | StrOutputParser() ) async def generate_sql_query(question: str) -> str: metadata = load_metadata() result = sql_chain.invoke({"metadata": metadata, "question": question}) return result.replace("```sql", "").replace("```", "").strip()3.3 注册 MCP 工具
用FastMCP起服务,把三个能力注册成工具:生成 SQL、执行 SQL、生成图表。SSE 模式监听 8001 端口。
from mcp.server.fastmcp import FastMCP import pandas as pd from sqlalchemy import create_engine import psycopg2 mcp = FastMCP("DataBase Server", port=8001, request_timeout=30000) @mcp.tool() async def generate_sql(query: str) -> str: """根据自然语言生成 SQL 查询语句""" return await generate_sql_query(query) @mcp.tool() async def execute_sql(sql: str) -> str: """执行 SQL 并返回 JSON 结果""" engine = create_engine(os.getenv("DATABASE_URI")) try: conn = psycopg2.connect( dbname="demo", user="postgres", password="123456", host="localhost", port="5432", options="-c client_encoding=utf-8", ) df = pd.read_sql(sql, conn) conn.close() return df.to_json(orient="records", force_ascii=False) except Exception as e: return f"查询出错: {e}" @mcp.tool(name="generate_chart", description="根据数据生成图表") async def generate_chart(df_json: str) -> str: """根据传入的数据生成 matplotlib 图表并保存为 chart.png""" chart_prompt = PromptTemplate( template=( "你是数据可视化专家。有如下数据 {df}," "用 matplotlib 选择合适的图表,配色美观," "保存为 chart.png,支持中文显示,只给代码。" ), input_variables=["df"], ) chain = ( {"df": RunnablePassthrough()} | chart_prompt | model | StrOutputParser() ) code = chain.invoke({"df": df_json}) code = code.replace("```python", "").replace("```", "").strip() exec(code) return "chart.png" if __name__ == "__main__": mcp.run(transport="sse")启动后服务地址是http://127.0.0.1:8001/sse,这个 URL 后面客户端要用。
4. 端到端验证:从提问到出图
服务端跑起来后,用支持 MCP 的客户端连上去测。这里用 Cherry Studio,它内置了 MCP Client,不用自己写连接代码。在 MCP 设置里新增一个 SSE 类型的服务器,URL 填http://127.0.0.1:8001/sse,保存后能看到三个工具被识别出来。
然后直接在对话框里问:“帮我查一下各个区域的企业利润总和,并画个图。” 预期流程是这样的:
- 客户端把问题发给模型,模型决定调用
generate_sql; generate_sql走 TaoToken 通道生成 SQL,比如SELECT "district", SUM("profit") FROM "company" JOIN "profit" ON ... GROUP BY "district";- 模型拿到 SQL 后调用
execute_sql,返回 JSON 数据; - 模型再调用
generate_chart,把数据交给可视化工具,生成chart.png; - 客户端返回结果,你打开本地图片就能看到各区域利润对比图。
验证成功的标志是:chart.png出现在工作目录,且图表里的区域名和数值跟数据库对得上。如果只想先验证模型通道是否通,可以单独跑一段最小请求:
from langchain_openai import ChatOpenAI import os llm = ChatOpenAI( model=os.getenv("TAOTOKEN_MODEL"), openai_api_key=os.getenv("TAOTOKEN_API_KEY"), openai_api_base="https://taotoken.net/api", ) print(llm.invoke("只回复两个字:通了").content)返回“通了”就说明 TaoToken 的 Key 和 base_url 配置正确,可以继续排查 MCP 层的问题。想直接在网页里试模型对话,可以用模型对话入口:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite。
5. 本篇常见错排查
报错一:openai.AuthenticationError: Incorrect API key九成是 Key 没读到。检查.env是否被load_dotenv()加载,变量名是否和代码里一致。另外确认 base_url 是https://taotoken.net/api,不要写成带/v1或其他后缀的地址。
报错二:psycopg2.OperationalError: could not connect to server数据库没起或端口不对。先用psql或 DBeaver 手动连一次,确认DATABASE_URI里的库名、用户、密码、端口都对。注意execute_sql里我用了options="-c client_encoding=utf-8",中文表名或字段名乱码时这个参数很关键。
报错三:生成的 SQL 执行报column does not exist模型没按双引号规则来。PostgreSQL 对大小写敏感,字段名不加双引号会被转成小写。在 prompt 里强调“表名和字段名都要加双引号”,并且把metadata.json里的字段名写准确,模型才有依据。
报错四:generate_chart执行后没有图片exec(code)里的代码可能用了plt.show()而不是savefig。在 prompt 里明确“保存为 chart.png,不需要显示”。另外 matplotlib 默认不支持中文,代码里要加plt.rcParams['font.sans-serif'] = ['SimHei'],否则中文会变方块。
报错五:查询结果太大导致 token 超限如果execute_sql返回几千行,直接塞给模型会爆 token。在execute_sql里加个截断,比如df.head(50),或者在 prompt 里让模型先做聚合再返回。可视化场景通常只需要聚合后的几十行数据。
报错六:SSE 连接不上确认服务端mcp.run(transport="sse")已启动,端口 8001 没被占用。客户端 URL 要带/sse后缀。如果本机有防火墙,放行对应端口。
6. 把这条链路固定下来
跑通一次之后,建议把三个工具的参数和返回格式固定成约定:generate_sql只返回纯 SQL 字符串,execute_sql只返回 JSON,generate_chart只返回图片路径。这样客户端和模型之间的交互会稳定很多,不会因为返回格式飘忽而反复重试。
另外,元数据文件建议跟着数据库 schema 一起维护,表结构变了就更新metadata.json,比让模型去猜字段靠谱得多。如果后面要接更多数据源,比如 MySQL 或 SQLite,只需要改execute_sql里的连接方式,模型调用和 MCP 工具注册那两层不用动——这正是统一 Key 加 MCP 分层带来的好处。
需要长期跑编码或 Agent 任务的话,Coding Plan 的额度模型更适合高频调用:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite。接入过程中遇到 Key 或通道问题,先查接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite。