search2chart-mcp
v0.5.0
Published
跨客户端 ECharts 图表 MCP server:数据 → 自包含可交互 HTML + 对话框内联图片(data URI / localhost http / GitHub+jsDelivr CDN 公网 https)
Readme
echarts-chart-mcp
A cross-agent ECharts chart MCP server. Feed it data (from web search or a CSV/XLSX file), get back an interactive chart as a self-contained HTML file. Works with DeepSeek Harness (DSH), Codex, WorkBuddy, and Trae.
把「数据 → 图表」做成一次开发、多端通用的 MCP server。agent 用自带搜索 / 本地文件取数后调用本服务,即可在对话里出图。零运行时依赖(纯 Node 手写 MCP stdio 协议)。
特性
- 零运行时依赖:纯 Node 实现 MCP stdio 协议,不需要 Python 或其他运行时。
- 自包含、可交互图表:生成内嵌 ECharts 的 HTML 文件,支持类型切换 / 配色 / 宽高调整。
- 离线可用:
npm run fetch-echarts把 echarts 落到vendor/,沙箱禁外网也能渲染。 - 跨端通用:搜索流(
chart_from_data)+ 文件流(chart_from_file)。 - Excel 可选:
npm i xlsx后支持.xlsx/.xls;不装也能跑 CSV。
工具
| 工具 | 作用 |
|------|------|
| chart_from_data | 结构化数据 → 写入图表 HTML 文件,返回绝对路径 + 数据概要(完整 ECharts option 默认省略,includeOption: true 恢复) |
| chart_from_file | CSV/XLSX 路径 → 解析后写入图表 HTML,返回路径 + 概要(首列类别轴,其余列数值序列) |
| list_chart_types | 列出支持的类型 / 配色与字段约定 |
字段约定:第一列 = 类别轴;其余列 = 数值序列(多列即多序列);chartType: auto 按数据自动选 饼/柱。
为什么输出「文件 + 路径」而不是 HTML 文本
MCP 工具结果在多数宿主里走文本通道:宿主(如 DSH 的 mcp-client)会把非文本块折叠成纯文本,直接把 HTML 吐回去只会在工具结果里显示成一长段 / 调试视图,不会被当成网页内联渲染。
因此本 server 统一把图表写成 .html 文件并返回绝对路径,各宿主用自己擅长的方式呈现:
- DSH / Web:模型在终回复用反引号写出路径 → 自动变可点击链接 → 浏览器打开即交互式图表(无需改宿主本体/UI)。
- WorkBuddy:助手读文件后用 Visualizer 内联渲染。
- Codex / Trae:打开该 HTML 文件即可。
想让宿主直接拿到 HTML(而不是走文件链接),把工具参数
returnHtml: true即可额外返回 HTML 原文——前提是宿主能渲染 HTML(如 Trae 的预览、装了 genui 的 DSH)。
图表文件默认写入 os.tmpdir()/echarts-charts/,可用环境变量 ECHARTS_CHARTS_DIR 覆盖(例如设成你的工作区 charts/ 目录,产物就落在该目录)。产物自动清理:超过 3 天或目录内超过 500 个文件时删除最旧者,无需手动维护。
对话框直接内联出图
不同客户端对「工具结果里的图片」处理方式不一致:有的把 image content block 当多模态输入(纯文本模型会过滤掉),有的不渲染 file:// 图片。为此采用模型回写策略:
- 工具结果落盘一份
.svg,并在返回的文本里给出这一行 + 一句「请在最终回复中原样写回该行」的指示。 - 模型按指示在最终回复里写出该 markdown 图片 → 走客户端的 markdown 渲染通路(展示给人看),避开 MCP 多模态输入过滤。
- 同时仍附带 MCP
imagecontent block(base64 SVG),供 Claude Desktop / Cursor 等支持 image block 的客户端直接渲染。 - 都不支持时回退到
.html路径文本(可点击打开交互式图表)。
内联模式速查
通过环境变量 ECHARTS_INLINE_MODE 控制内联行为:
| 模式 | 行为 | 适用客户端 |
|---|---|---|
| inline(默认) | data URI → localhost http → file:// 自动 fallback | OpenCode / DSH / 通用 |
| file | 只输出 file:// 本地路径 | ZCode |
| cdn | 上传 GitHub+jsDelivr,输出公网 https(见下方隐私说明) | WorkBuddy |
| all | 测试模式:一次返回所有格式,让用户判断哪个能显示 | 首次接入时测试 |
| none | 纯文本(.html 路径 + 数据),无内联 | 纯文本模型 / 终端 |
隐私说明(cdn 模式):图表含你的数据,上传到公网 GitHub 仓库后经 jsDelivr 可被任何人访问。因此 CDN 仅在显式设置
ECHARTS_INLINE_MODE=cdn时启用,不会自动兜底上传;敏感数据请勿使用 cdn 模式。
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
| ECHARTS_INLINE_MODE | inline | 内联模式(inline / file / cdn / all / none) |
| ECHARTS_RETURN_IMAGE | true | 是否返回 MCP image content block |
| ECHARTS_RETURN_DATA | true | 是否附带清洗后完整数据 |
| ECHARTS_RETURN_OPTION | false | 是否附带完整 ECharts option JSON(体积较大,默认省略;工具参数 includeOption 可单次覆盖) |
| ECHARTS_DATA_MAX_ROWS | 60 | 返回数据的最大行数 |
| ECHARTS_DATA_URI_MAX | 49152 | data URI 最大字节数 |
| SEARCH2CHART_PORT | 18765 | 本地 HTTP 服务端口 |
| ECHARTS_CHARTS_DIR | os.tmpdir()/echarts-charts | 图表输出目录 |
| ECHARTS_CDN_TOKEN | — | GitHub PAT(cdn 模式必填) |
| ECHARTS_CDN_REPO | iqingyoung/search2chart-cdn | GitHub 仓库(cdn 模式) |
| ECHARTS_CDN_RETENTION_DAYS | 3 | CDN 图片保留天数(自动清理) |
| ECHARTS_CDN_BRANCH | main | GitHub 分支 |
首次接入测试
先用 all 模式一次性测试所有格式:
临时配置:
{ "env": { "ECHARTS_INLINE_MODE": "all" } }发给 Agent:
用 chart_from_data 生成一个简单柱状图。
数据:[["城市","销量"],["北京",120],["上海",200],["广州",150]]
标题:城市销量对比
chartType:barAgent 返回 3-4 行图片,分别标记为 【1·data URI】【2·localhost http】【3·file://】【4·CDN 公网 https】。哪个在对话框里真正渲染成了图片,你的客户端就支持哪种。然后把 ECHARTS_INLINE_MODE 固定为对应值。
工具结果里已内置 Agent 自动检测指令,告诉模型如何根据用户反馈自动设置模式:
| 用户反馈 | Agent 动作 |
|---|---|
| "1正常" | 提示设置 ECHARTS_INLINE_MODE=inline |
| "2正常" | 提示设置 ECHARTS_INLINE_MODE=inline |
| "3正常" | 提示设置 ECHARTS_INLINE_MODE=file |
| "4正常" | 提示设置 ECHARTS_INLINE_MODE=cdn |
| "都行" | 提示设置 ECHARTS_INLINE_MODE=inline(data URI 优先) |
| "都不行" | 提示设置 ECHARTS_INLINE_MODE=none |
已知实测:ZCode →
file,OpenCode →inline(data URI),WorkBuddy →cdn,DSH →inline(localhost http),Claude/Cursor →inline+returnImage:true。
视觉与数据:
- 统一米白底色
#fafaf7:SVG 与 HTML 都用米白底,避免透明背景在深色/灰白客户端不可见。 - 清洗数据留存:默认在结果里附带清洗后的完整数据(JSON 数组数组,代码块包裹),让纯文本模型(GLM-5.2 / DeepSeek 等)在上下文里继续做占比/趋势/对比分析,无需看图。
returnData: false或环境变量ECHARTS_RETURN_DATA=false可关;超 60 行(ECHARTS_DATA_MAX_ROWS可调)自动截断。 - 图表类型双语:summary 同时给出中文名与英文键(如「柱状图(bar)」)。
ZCode 实测:MCP 客户端把工具结果的 image block 当模型输入过滤掉,但模型回复里的
file://markdown 图片能被渲染器直接显示——因此走「模型回写」通路。
各客户端表现:
| 客户端 | image block 内联 | 模型回写 file:// 图片 | 路径文本兜底 |
|---|---|---|---|
| ZCode | ❌(过滤) | ✅(实测) | ✅ |
| Claude Desktop / Cursor | ✅ | 取决于客户端 | ✅ |
| Codex CLI / Claude Code(终端) | ❌(终端不渲染像素) | ❌ | ✅ 落盘 .html |
| DSH(MCP 通道) | ❌ | ❌(sanitizeUrl 拦截 file:) | ✅ 路径变可点击链接;或改用 dsh/ 原生插件 |
运行
node server.js # 由 MCP 客户端以 stdio 拉起,无需手动运行
npm run fetch-echarts # 可选:下载 echarts 到 vendor/,支持离线/沙箱渲染
npm test # 可选:端到端自检(initialize / tools/list / 两路出图)接入各客户端
所有客户端统一用 stdio 拉起。推荐用 npx(无需 clone):npx search2chart-mcp。或手动指定路径 node <绝对路径>/mcp/server.js(注意 mcp/ 子目录,server.js 在 mcp/ 下,不在仓库根)。
路径坑:本仓库根有
mcp/和dsh/两个子目录。MCP server 入口是mcp/server.js,不是根目录的server.js。所有接入配置里的 args 都要写完整路径.../search2chart-mcp/mcp/server.js。
ZCode
ZCode 的 MCP 配置在 .zcode 体系下,分 workspace 和 user 两个 scope。
在 <repo>/.zcode/config.json(workspace scope,仅当前项目生效)或 ~/.zcode/cli/config.json(user scope,全局生效)的 mcp.servers 下新增:
{
"mcp": {
"servers": {
"echarts-chart-mcp": {
"type": "stdio",
"command": "node",
"args": ["/abs/path/to/search2chart-mcp/mcp/server.js"],
"enabled": true
}
}
}
}接入踩坑(实测):
- 必须完全退出 ZCode 再重开:不是新建会话,是退出整个应用/进程再打开。MCP server 在会话启动时连接,当前会话无法热加载新配置。
- command 用
node依赖 PATH:若启动失败(设置 → MCP 显示 failed /spawn node ENOENT),用which node查绝对路径(如/usr/local/bin/node、~/.nvm/versions/node/v20.x.x/bin/node),填进command字段。 - schema 严格:配置里多余的未知字段会导致整个 server 被静默丢弃(不报错但不加载)。只保留
type/command/args/enabled/cwd/env/timeoutMs。 - 图片不走 MCP image block:ZCode 的 MCP 客户端会把工具结果的
imagecontent block 当作多模态模型输入处理,纯文本模型(如 GLM-5.2)会过滤掉。本 server 采用「模型回写」策略——工具结果里给出字面量并指示模型在最终回复中原样写回,走 ZCode 的 markdown 渲染通路(展示给人看),绕开 MCP 输入过滤。重启后调用工具,模型回复里会直接显示图表。 - 验证连接:重启后进入 设置 → MCP,应看到
echarts-chart-mcp显示为「已连接」。工具名形如mcp__echarts-chart-mcp__chart_from_data。
DeepSeek Harness (DSH)
DSH 用 Cordis 加载插件(不是 harness.yaml)。在 profile 的 patch 层新增一个 mcp-client 实例即可(每个实例只连一个 server)。
编辑 ~/.dsh/profiles/<profile>/cordis.patch.yml(如 web profile):
- insert:
- id: mcp-client-echarts-chart
name: '@deepseek-ai/dsh-mcp-client' # 随 dsh 包预装
config:
transport: stdio
serverName: echarts-chart
command: 'C:/abs/path/to/node.exe'
args:
- 'C:/abs/path/to/search2chart-mcp/mcp/server.js'
cwd: 'C:/abs/path/to/search2chart-mcp'
failOnStartupError: false # 务必 false,否则 server 启动失败会让 DSH 整体启动失败
reconnect: { enabled: true, initialDelayMs: 500, maxDelayMs: 30000, maxAttempts: 10 }- 务必用
- insert:包裹:写成顶层- id:会被 DSH 当成「覆盖已有插件」,因 id 在 bundle 层不存在而静默 skip,表现就是重启后找不到、且不报错。 id全局唯一;serverName须匹配[A-Za-z0-9_-]{1,32},决定工具前缀mcp__echarts-chart__*。- 路径用
C:/...正斜杠(Windows 下 Node 接受),避免重启后相对/PATH 丢失。 - DSH 的 MCP 通道不渲染图片:DSH 的 MCP 客户端会把非文本 content block 折叠成纯文本(
extractText丢弃),且 markdown 渲染器只放行http(s)协议(sanitizeUrl拦截file:/data:)。因此在 DSH 里只能走.html路径文本通路——终回复里用反引号包路径成可点击链接,浏览器打开即交互式图表。若要真正内联出图,改用本仓库dsh/目录下的原生 DSH 插件(走 localhost HTTP 服务 + http URL markdown 图片),见仓库根 README。 - 重启 DSH 后,会话里即可看到
mcp__echarts-chart__chart_from_data等工具。
完整示例见 examples/dsh-cordis.patch.yml。
WorkBuddy
写入 ~/.workbuddy/mcp.json 的 mcpServers:
{ "mcpServers": { "echarts-chart-mcp": { "command": "node", "args": ["/abs/path/to/search2chart-mcp/mcp/server.js"] } } }工具返回 HTML 文件路径后,助手用 HTML 预览 / Visualizer 内联展示。
Trae
在 Trae 的 MCP 设置中加入同上的 stdio 配置(命令 node,参数指向 mcp/server.js 的绝对路径),Web IDE 直接预览返回的 HTML(亦可设 returnHtml: true 直接拿到 HTML)。
Codex / Claude Code
claude mcp add echarts -- node /abs/path/to/search2chart-mcp/mcp/server.js终端无内联渲染:工具会落盘 .html,用浏览器打开即可。Codex 会把 image block 透传给支持图像的模型(模型不支持则降级为占位文本)。
示例
搜索流(agent 搜完把数据喂入):
{ "data": [["品牌","市占率"],["A",32.5],["B",27.8],["C",18.2]], "chartType": "pie", "title": "品牌市占率" }文件流:
{ "filePath": "/data/sales.csv", "chartType": "bar", "title": "月度销量" }目录结构
echarts-chart-mcp/
├── server.js # MCP stdio 协议 + 工具入口
├── lib/
│ ├── chart.js # 数据归一化 + 选图推断 + ECharts option
│ ├── html.js # 自包含可交互 HTML(类型/配色/宽高控件)
│ ├── svg.js # 零依赖 SVG 渲染器(bar/line/pie)→ image content block
│ └── parse.js # CSV 零依赖解析;XLSX 走可选 xlsx
├── scripts/
│ ├── fetch-echarts.js # 下载 echarts 到 vendor/(离线用)
│ └── selftest.js # 端到端自检
├── examples/
│ └── dsh-cordis.patch.yml # DSH 接入示例
├── sample.csv # 自测用样例
├── package.json
├── README.md
├── LICENSE
└── .gitignore可选:在 DSH 对话流里真正内联渲染
「写文件 + 可点击链接」是零 UI 改动的通用方案,四端都能用。若要在 DSH 对话流里直接内联(不点链接),可按 DSH「一切皆插件」的官方扩展路径,加一对「原生 dsh 插件 + 配对 UI 插件」:生成逻辑复用 lib/chart.js / lib/html.js,只是出口从 MCP 文本换成 Cordis 结构化事件 + ECharts UI 组件(参照 dsh-client-ui-tool 的 searchBody/card 渲染分支)。此方式不碰 DSH 本体。
License
MIT
