关于 GridPath
GridPath 是可编辑真实 Excel 工作簿的表格引擎,供应用开发者和编码 Agent 使用。它保留公式并将修改写回原始 .xlsx,Agent 的每项改动都可先审阅再保存。
主要功能
- 01编辑真实 .xlsx 工作簿
- 02实时计算公式
- 03审阅并接受或拒绝 Agent 修改
- 04将改动写回原始文件
- 05提供嵌入式表格界面
- 06导入 CSV 和分隔文本
适用场景
- 01在编码 Agent 中修改工作簿并逐项审阅
- 02在 Web 应用中嵌入电子表格
- 03将 CSV 数据转换为可编辑工作簿
MCP 相关
GridPath 提供 MCP 服务,供 Claude Code、Claude Desktop、Cursor 和 Codex 等客户端编辑磁盘上的工作簿,并在保存前审阅改动。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
32收录的工具列表 · 更新于 1 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| cell | string | 是 | A1 地址。示例:"A1"、"B17"、"AA42"。 |
| path | string | 是 | 要操作的 .xlsx 文件路径(绝对路径,或用户主目录下的 ~/…)。 |
| sheet | string | 是 | 工作表名称(区分大小写)。 |
| value | any | 否 | 字面值(字符串、数字、布尔值或 null)。使用 `formula` 时留空为 null。 |
| formula | string|null | 否 | 以 '=' 开头的 Excel 样式公式。引用使用 A1 表示法。示例:"=SUM(A1:A10)"。 |
详细介绍
gridpath
在你的 Web 应用和 Agent 中使用真实的 Excel 文件。 打开用户已有的 .xlsx
文件,以完整尺寸保留所有实时公式,让 AI 在审阅后进行编辑,并保存回同一个文件。
| 在你的 Web 应用中 | 在你的编码 Agent 中 | |
|---|---|---|
| 安装 | npm install gridpath | claude mcp add gridpath -- npx -y gridpath mcp |
| 你将获得 | 可嵌入的电子表格:画布网格,基于 Web Worker 中的 Rust/WebAssembly 引擎、原位保存 .xlsx,以及供你自己的模型使用的 Agent 工具 | 适用于 Claude Code、Claude Desktop、Cursor、Codex 的 MCP 服务:Agent 编辑磁盘上的文件,你可以在保存前审阅每项更改 |
| 文档 | 在你的 Web 应用中 | 在你的编码 Agent 中 |
两者运行相同的引擎(IronCalc 的分支): 在进程内计算真实公式,并通过修补原始文件进行保存,因此未更改的部分会以字节级一致的形式保留。重新计算的公式和受支持的图表数据缓存会带上当前值;图表定义仍保留为原生 Excel XML。
在你的 Web 应用中
- 快速开始 · Next.js · 用户将获得什么
- 工作簿和文档 · CSV 和文本文件 · 保存 · 保存到你的后端
- 错误 · Excel 保存的值 · AI
- 选项 · 方法 · 打包工具 · 保存的工作原理
快速开始
npm install gridpath react react-dom
React 和 React DOM 是对等依赖。你的应用不必使用
React:createSpreadsheet 会将电子表格挂载到任意元素中。
import { Spreadsheet } from "gridpath";
<div style={{ height: 600 }}>
<Spreadsheet
file={bytes} // an .xlsx as ArrayBuffer or Uint8Array; omit for a blank workbook
onReady={(sheet) => (sheetRef.current = sheet)}
onError={(err) => console.error(err.code, err.message, err.hint)}
/>
</div>;
不使用 React:
import { createSpreadsheet } from "gridpath";
const sheet = await createSpreadsheet(document.getElementById("sheet")!, { file: await file.arrayBuffer() });
sheet.setCell("Sheet1", "B2", "=SUM(A1:A10)");
console.log(await sheet.getCell("Sheet1", "B2")); // { value: 55, formula: "=SUM(A1:A10)" }
const xlsx = await sheet.save(); // Uint8Array
工作簿打开后,createSpreadsheet 会完成解析。如果无法打开工作簿,它会以 GridPathError 拒绝,并保持元素为空。
Next.js
无需任何配置。已在 Next.js 16 和 React 19 上测试,
使用 Turbopack 和 webpack,并在 next dev 和生产构建中运行。
- 从客户端组件(
"use client")渲染电子表格。外围页面仍可使用服务端组件。 gridpath/agent可用于路由处理程序。
examples/nextjs-demo
是一个完整的应用。每位用户都有一份工作簿列表,保存在应用自己的 API 中:app/api/books/[[...path]]/route.ts 包含
httpStore 调用的 REST 路由,并带有身份验证。每个工作簿都有自己的 URL。此示例展示了:
- 上传和新建工作簿;
- 加载指示器;
- 针对每种错误代码的处理方式;
- 切换工作簿时保存正在离开的工作簿。
用户无需编写代码即可获得的功能
-
格式工具栏:
- 撤销和重做;
- 字体和字号;
- 粗体、斜体和下划线;
- 字体颜色和填充颜色;
- 对齐和自动换行;
- 货币、百分比、逗号格式,以及增加或减少小数位数;
- 数字格式;
- 插入和删除行与列;
- 排序、筛选、冻结窗格和清除。
键盘快捷键(⌘B、⌘Z、⌘S……)可在网格中使用。
toolbar={false}会隐藏工具栏。 -
显示保存位置的保存状态:“已保存在此浏览器中”、“已保存到你的账户”、“正在保存……”、“有未保存的更改”或“无法保存 · 重试”。
- 编辑暂停两秒后会自动保存。按 ⌘S 或点击“保存”按钮可立即保存。
-
从上次离开处继续:重新加载后,工作簿会恢复,光标也会回到用户之前所在的单元格。
-
“文件”菜单,包含“打开……”、“新建”和“最近的工作簿”。如果你的应用有自己的列表,可用
fileMenu={false}移除这些菜单项。 -
版本历史记录:恢复较早的版本或下载该版本。恢复时,被替换的状态也会作为一个版本保留。
-
下载为
.xlsx,与“保存”按钮分开。 -
Agent 通过
sheet.ai操作时显示的“AI 更改”面板。每项更改都会显示操作内容(“D2:空白 → =C2/B2-1”)和位置,并提供“接受”和“拒绝”按钮。点击某项更改可查看对应单元格。- 如果你的应用有自己的审核界面,可用
reviewPanel={false}关闭该面板。
- 如果你的应用有自己的审核界面,可用
-
无法打开文件时显示的通俗说明,例如“旧格式的 .xls 工作簿或受密码保护的 .xlsx”。
-
工具栏中显示使用 Excel 保存值的公式数量,并附有公式列表。
-
在任何内容丢失前主动询问:如果有无法保存的更改时打开另一个工作簿,会提示“放弃未保存的更改?”
工作簿和文档
电子表格一次显示一个工作簿。通过 documentId 标识工作簿,其默认值为 fileName。结合存储,该 ID 用于加载工作簿:
<Spreadsheet store={store} documentId={book.id} fileName={book.name} />
电子表格会打开该 ID 对应的已存储状态。如果还传入了 file,只有在已存储状态由同一个文件保存时,才会优先使用已存储状态;否则会打开 file。如果没有已存储状态,也没有 file,工作簿将以空白状态启动。
切换工作簿有两种方式:
- **为每个文档重新挂载组件。**为组件设置
key={book.id},就像 Next.js 示例中为每个工作簿使用一个页面一样。 - 在同一个电子表格上调用
open()或openStored()。两者遵循相同的规则:
| 打开另一个工作簿时…… | |
|---|---|
| 有待审核的 AI 更改 | 调用会以 PendingChangesError 拒绝。请先接受或拒绝这些更改。 |
有未保存的编辑,且已设置存储或 onSave | 会先保存这些编辑。 |
| 保存失败,或没有设置保存目标 | 调用会以 UnsavedChangesError 拒绝。传入 { discardUnsaved: true } 可放弃这些编辑。远程存储保存失败不算作此处的失败:这些编辑会保留在此浏览器中。 |
| 新文件无法打开 | 调用会以 unsupported-file 或 malformed-file 拒绝,之前打开的工作簿会保持不变。明显无效的文件(空文件、旧版 .xls、PDF 或图片)会在任何更改发生前被拒绝。 |
“文件”菜单遵循相同规则,并会在放弃任何内容前询问用户。
清理。
- 卸载组件或调用
destroy()(仅限createSpreadsheet)。如果有尚未保存的编辑,会先保存。这也适用于不会触发beforeunload的客户端导航。之后,引擎的 worker 会停止。从此以后,对旧sheet对象的所有调用都会以not-ready失败。 - 关闭页面时,如果有未保存的编辑,浏览器会显示“要离开网站吗?”警告。
CSV 和文本文件
file、open() 和 File 菜单也支持 CSV 和其他分隔文本。
文件会作为新工作簿打开,并保存为 .xlsx:sales.csv 会变成
sales.xlsx,其中包含一个名为 sales 的工作表,工具栏也会显示这一点。
- 分隔符:逗号、制表符、分号或竖线,以前几行一致使用的分隔符为准。Excel 的
sep=;首行设置会生效。在分号分隔的文件中,逗号作为小数点(1.234,5即 1234.5)。 - 编码:UTF-8(带或不带字节顺序标记)、带标记的 UTF-16(Excel 的“Unicode 文本”),其他情况使用 Windows-1252。
- 值会按输入单元格时的方式读取:数字、货币、百分比、ISO 和美国格式日期、
TRUE/FALSE以及=公式,公式会计算。无法解析的公式会保留为文本。 - 保留为文本,避免 Excel 丢失信息:带前导零的数字(邮政编码、ID)以及超过 15 位的数字字符串(银行卡号和账户号码)。
- 列宽会调整以适应文本,最多为 60 个字符。
非表格文本(.json、.xml、.html、.md 等)会被拒绝,并返回
unsupported-file。在较新的 Mac 上,Chromium 中打开一个 17 MB 的 CSV(200 万个单元格)需要 7 到 10 秒。
保存
保存有三种方式:
- 自动保存:编辑暂停两秒后保存。使用
autosave(毫秒)更改延迟,或设为false关闭自动保存。 - ⌘S 和 Save 按钮。
- 在代码中调用
sheet.saveNow()。
每次保存都会将工作簿写入 store 并调用 onSave。使用 onStatusChange 或 getStatus() 监听状态:
state | 工具栏显示 | |
|---|---|---|
"saved" | “已保存在此浏览器”/“已保存到你的账户” | savedAt,以及 where:store 的 label。 |
"unsaved" | “有未保存的更改” | 即将自动保存。 |
"saving" | “正在保存…” | |
"review" | “请审核 2 项 AI 更改以保存” | pending。保存会等待用户接受或拒绝 AI 的更改。 |
"error" | “无法保存 · 重试”或“仅保存在此浏览器中 · 重试” | code、message 和 hint。如果这些编辑已安全保存在此浏览器中,则会设置 keptLocally;自动保存将再次尝试的时间则记录在 retryAt 中。 |
"idle" | (无) | 新工作簿,或未设置保存功能。 |
save() 和 saveBlob() 会返回字节数据,但不会操作 store,
可用于自行上传。
保存到后端
默认情况下,所有内容都通过 browserStore() 保存在用户的浏览器(IndexedDB)中。五分钟内的保存会共用一个版本。最新的 20 个版本会逐个保留,之后保留每天一个版本,共 30 天。可通过 browserStore({ checkpointMinutes, keepRecent, keepDays }) 调整这些设置。
要将内容保存到已登录用户的账户,请将 httpStore 指向你的 API:
import { httpStore } from "gridpath";
const store = httpStore({
url: "/api/books",
// Called before every request, so a refreshed token is picked up. Leave it
// out with cookie sessions.
headers: async () => ({ Authorization: `Bearer ${await getToken()}` }),
});
<Spreadsheet store={store} documentId={book.id} />;
它调用的路由均相对于 url。ID 会进行 URL 编码:
| 请求 | 响应 |
|---|---|
PUT /{id}?name=…&source=…[&checkpoint=1],请求体为 .xlsx 文件 | 任意 2xx。source 用于标记打开工作簿所依据的文件。checkpoint 会单独创建一个版本(在恢复前后)。 |
GET /{id} | .xlsx 文件,包含以下标头:X-Workbook-Name(百分比编码)、X-Workbook-Saved-At(ISO 时间)、X-Workbook-Source。若不存在则返回 404。 |
GET / | [{ id, name, savedAt, size }],按最新到最旧排序(“最近”列表)。 |
GET /{id}/versions | [{ id, savedAt, size }],按最新到最旧排序。若未保留历史记录则返回 404。 |
GET /{id}/versions/{versionId} | 该版本的 .xlsx 文件。 |
DELETE /{id} | 任意 2xx。 |
当无法连接后端或后端返回错误时,用户的编辑不会丢失:
- 状态显示“仅保存在此浏览器中 · 重试”,且
keptLocally: true。 - 自动保存会在 2、5、15 和 30 秒后重试,之后每分钟重试一次。
- 编辑内容的副本会保存在此浏览器中。
- 如果先关闭页面,下次打开时会重新载入该副本;它比后端中的版本更新,并会保存到后端。状态行会显示“已恢复未保存到你账户的更改”。
打开工作簿时,如果无法连接后端,电子表格会显示错误和“重试”选项。它不会打开空白工作簿,导致自动保存覆盖用户已保存的工作簿。
其他任何方式都可以作为存储实现。httpStore 是此接口的一种实现:
interface WorkbookStore {
label?: string; // the status line's "Saved …" wording: "to your account"
local?: boolean; // true when it is in this browser (no extra drafts kept)
save(w: { id: string; name: string; bytes: Uint8Array; source?: string; checkpoint?: boolean }): Promise<void>;
load(id: string): Promise<{ id; name; bytes; savedAt: string; source?: string } | null>;
list(): Promise<Array<{ id; name; savedAt: string; size: number }>>;
versions(id: string): Promise<Array<{ id: string; savedAt: string; size: number }>>;
loadVersion(id: string, versionId: string): Promise<Uint8Array | null>;
remove?(id: string): Promise<void>;
}
还有两个更简单的选项:
onSave:每次保存时接收字节,并由你自行上传。Promise 被拒绝时会视为保存失败并重试。store={false}:完全不保存;用户仍可使用“下载”,你的代码仍可使用save()。
错误
电子表格报告的所有问题都是 GridPathError。每个错误都包含:
code,供你的代码执行相应操作;message,说明发生了什么;hint,一句可展示给用户的操作说明。
你会通过以下四种方式收到错误:
- 方法会因这些错误而拒绝。 例如,
open()、openStored()、save()和getCell()。 onError会收到引擎故障和无法打开的工作簿:包括第一个工作簿,或从“文件”菜单打开的工作簿。onStatusChange会将保存失败报告为{ state: "error", code, message, hint }。保存失败不会传递给onError,因为自动保存会重试。- 电子表格本身会在网格所在位置显示问题,因此用户不会面对空白框。
try {
await sheet.open(bytes, { name: file.name });
} catch (e) {
if (!(e instanceof GridPathError)) throw e;
if (e.code === "unsaved-changes" && confirm(`${e.message}. Discard them?`)) await sheet.open(bytes, { name: file.name, discardUnsaved: true });
else showToast(`${e.message}. ${e.hint}`);
}
code | 何时发生 | 该怎么做 |
|---|---|---|
unsupported-file | 文件不是电子表格:例如空文件、旧格式 .xls、受密码保护的工作簿、PDF、图像或 JSON 文件。 | 请用户提供 .xlsx 或 CSV 文件(提示中会说明如何获取)。 |
malformed-file | 引擎无法读取 .xlsx(文件已损坏或被截断),或 CSV 无法作为表格读取。 | 在 Excel 中打开并重新保存。 |
engine-failed | 引擎的 worker 或 .wasm 未能加载,或引擎随后停止运行。若引擎停止,横幅会提示计算已停止,并提供下载选项。 | 提供此软件包的 engine.worker.js 和 .wasm(参见打包工具);重新加载页面。 |
storage-failed | 存储失败。使用 httpStore 时,错误的 cause 是一个包含响应 status 的 HttpStoreError。若 URL 返回的是网页而非数据,错误会说明这一点。 | 对于 401/403,请让用户重新登录。其他情况会由自动保存重试。 |
save-failed | 无法将会话中的更改写入文件。 | 编辑内容仍显示在屏幕上;撤销上次更改并再次保存。 |
calculation-timeout | getCell()(等待 30 s 后)、whenCalculated(ms) 或 save()(等待 60 s 后)在重新计算仍在运行时超时。 | 等待计算完成后再试一次。 |
pending-changes | AI 更改待审核时调用了 save() 或 open()(PendingChangesError,包含 pending)。 | 接受或拒绝这些更改(sheet.ai.accept() / reject()),或调用 save({ includePending: true })。 |
unsaved-changes | open() 将会丢弃无法保存的编辑内容(UnsavedChangesError)。 | 询问用户,然后使用 { discardUnsaved: true } 重试。 |
not-found | openStored(id) 未找到任何内容。 | |
not-ready | 在 onReady 之前、另一个工作簿正在打开时,或卸载之后调用了方法。 | 等待 onReady(或等待 open() 返回的 Promise)。 |
GridPath 无法计算的公式
GridPath 无法计算某些公式:
- Excel 加载项函数(
_xll.…); - GridPath 尚不支持的函数;
- 指向其他工作簿的链接。
这些单元格会保留 Excel 保存到文件中的值,而读取这些单元格的公式会根据该值进行计算。用户会在工具栏中看到一个计数,以及一份说明各单元格情况的列表。你的代码也可以读取相同内容:
await sheet.getCell("Model", "C7");
// { value: 1250, formula: "=_xll.FDS(…)", fromFile: true,
// note: "Showing Excel's saved value because GridPath couldn't calculate this formula: it calls FDS, an Excel add-in function." }
await sheet.getCalcIssues(); // [{ sheet, ref, row, col, formula, value, calculated, note }]
选项
| 选项 | |
|---|---|
file | .xlsx 字节(ArrayBuffer 或 Uint8Array)。如果未提供,工作簿将为空白,或重新打开已存储的工作簿。组件挂载时读取;如需打开其他工作簿,请使用 open()。 |
sheets | 空白工作簿的工作表名称(默认值为 ["Sheet1"])。 |
fileName | 工作簿的文件名,用于下载和「最近」列表(默认值为 "Untitled.xlsx")。 |
documentId | 此工作簿对应的已存储工作簿(默认值:fileName)。请参阅工作簿和文档。 |
store | 保存内容、最近打开的工作簿和版本的存储位置。默认值为 browserStore()。你可以传入 httpStore({ url, headers })、自定义的 WorkbookStore 或 false。 |
autosave | true(默认值:编辑暂停两秒后自动保存)、以毫秒为单位的延迟时间,或 false。 |
onSave | ({ id, name, bytes }) => …,每次保存时调用。被拒绝的 Promise 视为保存失败。 |
onStatusChange | 保存状态变化时调用。 |
onReady | (sheet) => …,第一个工作簿打开时调用一次(React;createSpreadsheet 则会解析为该工作表)。 |
onError | (error: GridPathError) => …,用于处理引擎故障以及无法打开的工作簿。 |
onDocumentChange | ({ id, name }) => …,打开其他工作簿时调用,包括从「文件」菜单、「最近」列表、还原操作、open() 或 openStored() 打开。 |
onEdit | 网格中每次用户编辑后调用。 |
onProgress | 加载阶段("Loading workbook…", "Building grid…"),用于显示加载指示器。 |
toolbar | false 可隐藏工具栏。 |
fileMenu | false 可从「文件」菜单中移除「打开」、「新建」和「最近」。 |
reviewPanel | false 可关闭「AI 更改」面板。你可以使用 sheet.ai.list()、accept() 和 reject() 自行构建面板。 |
engine.worker | false 可让引擎在页面上运行,而不是在 Web Worker 中运行。 |
engine.workerUrl, engine.wasmUrl | 指定 worker 脚本和 .wasm 的加载位置,适用于打包工具不会在软件包旁提供这些文件的情况。 |
方法
| 方法 | 契约 |
|---|---|
getSheetNames() | 按顺序返回工作表名称。 |
getCell(sheet, "A1") | 返回 { value, display?, formula, fromFile?, note? };如果单元格为空,则返回 null。此方法会等待待处理的写入完成重新计算(最长 30 s,超时后会以 calculation-timeout 拒绝)。 |
getCalcIssues(limit?) | 返回显示 Excel 已保存值的公式(见上文)。 |
setCell(sheet, "A1", value) | 立即返回。写入会排队并按顺序重新计算,因此之后调用 getCell 会得到结果。以 = 开头的字符串会被视为公式。 |
setCells(sheet, [{ row, col, value }]) | 一次设置多个单元格(从 0 开始计数)。 |
whenCalculated(ms = 30000) | 待处理的写入完成重新计算后解析;超过 ms 后以 calculation-timeout 拒绝。 |
select(sheet, "A1")、focus() | 移动选区;使网格获得键盘焦点。 |
undo()、redo() | 撤销或重做用户或 Agent 的上一次更改。 |
save(options?)、saveBlob(options?) | 返回工作簿的 .xlsx 字节(或 Blob),不修改存储。会等待重新计算(最长 60 s)。如果 AI 更改尚待审核,则以 PendingChangesError 拒绝,除非传入 { includePending: true }。 |
saveNow() | 立即保存到存储并触发 onSave,与自动保存和 ⌘S 的操作相同。解析时返回新的状态,且永不拒绝:失败时会返回 { state: "error", … }。 |
hasUnsavedChanges() | 如果存在尚未保存到存储的编辑(包括待审核的 AI 更改),则返回 true。适用于离开路由前的提示。 |
download(fileName?) | 下载 .xlsx。 |
getStatus()、getDocument() | 返回保存状态;以及已打开工作簿的 { id, name }。 |
open(bytes, { name?, id?, discardUnsaved? }) | 在此处打开另一个工作簿,并遵循这些规则。工作簿打开后解析。 |
openStored(id, { discardUnsaved? }) | 按 ID 从存储中打开工作簿;如果不存在,则以 not-found 拒绝。适用相同规则。 |
destroy() | 保存尚未保存的内容、卸载并停止引擎(仅适用于 createSpreadsheet;在 React 中,请卸载组件)。 |
ai | Agent 工具;见下文。 |
AI:你的模型,你的密钥
sheet.ai 是 GridPath 的 Agent 工具集(与 MCP 服务 使用相同的工具和结果格式),作用于用户正在查看的网格。将这些工具提供给你的模型,使用 sheet.ai.run() 执行它的工具调用,然后让用户审核:每次写入都会立即生效,并高亮显示为待处理批次,用户可在“AI changes”面板(或你自己的 UI)中接受或拒绝。
GridPath 永远不会看到密钥。模型调用在你的服务器上进行;工具调用则在工作簿所在的浏览器中运行。
服务器(任意 Node 路由;此处使用的是演示项目的 server/agent.ts)。系统提示词和工具在此处固定;如果从请求中获取这些内容,该路由就会成为可使用你的密钥的通用代理。请添加你自己的身份验证和速率限制。
import Anthropic from "@anthropic-ai/sdk";
import { AGENT_INSTRUCTIONS, agentTools } from "gridpath/agent";
const client = new Anthropic(); // ANTHROPIC_API_KEY
const tools = agentTools() as Anthropic.Beta.BetaTool[];
export async function agentTurn(messages: Anthropic.Beta.BetaMessageParam[]) {
const response = await client.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 16000,
output_config: { effort: "high" },
system: AGENT_INSTRUCTIONS,
tools,
cache_control: { type: "ephemeral" }, // system + tools are identical every call
betas: ["server-side-fallback-2026-07-01"],
fallbacks: "default", // a declined request re-runs on a fallback model
messages,
});
return { content: response.content, stop_reason: response.stop_reason };
}
浏览器(演示项目的 src/agentLoop.ts,已缩略):
const messages = [{ role: "user", content: [
{ type: "text", text: `The open workbook:\n${await sheet.ai.context()}` },
{ type: "text", text: prompt },
] }];
for (;;) {
const out = await (await fetch("/api/agent", { method: "POST", body: JSON.stringify({ messages }) })).json();
messages.push({ role: "assistant", content: out.content }); // unchanged, thinking blocks included
const calls = out.content.filter((b) => b.type === "tool_use");
if (out.stop_reason !== "tool_use" || calls.length === 0) break;
const results = [];
for (const c of calls) {
const r = await sheet.ai.run(c.name, c.input);
results.push({ type: "tool_result", tool_use_id: c.id, content: JSON.stringify(r), ...(r?.ok === false ? { is_error: true } : {}) });
}
messages.push({ role: "user", content: results }); // all results in one message
}
已经在使用 Agent 框架?使用 sheet.ai.tools()(Anthropic 格式)或 sheet.ai.tools("openai"),并将工具调用路由到 sheet.ai.run()。
sheet.ai | |
|---|---|
tools(format?) | 工具定义:"anthropic"(默认)或 "openai"。也可从 gridpath/agent 导入 agentTools()。 |
instructions() | 如何使用这些工具,可用于系统提示词(AGENT_INSTRUCTIONS)。 |
context() | 第一轮对话所需的工作簿概览。 |
run(name, input) | 执行一次工具调用;返回解析后的 JSON,供模型使用。永不抛出异常。 |
list(), onChange(fn) | 待处理或已接受的批次。 |
accept(id?) | 保留一个批次,或所有待处理批次。 |
reject(id?) | 撤销一个批次及其之后的所有批次,按从新到旧的顺序撤销,确保后续批次移动的行和列保持正确。返回已撤销批次的 ID。 |
包含:describe_workbook、find_rows、query_rows、read_range、set_cell、set_range、copy_range、set_format、clear_range、run_script(在沙盒化的 worker 中运行)、行/列/工作表结构、冻结、隐藏、合并、备注、大小、define_name。不包含:需要桌面应用或网络的工具(fetch_web、edgar_lookup、参考工作簿)。
有待处理的更改时,save() 会拒绝保存:在用户接受或拒绝这些更改之前,它会抛出 PendingChangesError(包含 pending 计数),因此未经审核的内容不会意外写入文件。save({ includePending: true }) 则会连同这些更改一起保存。
保存机制
每次保存都会将会话的更改(单元格、格式、大小、行和列、工作表、筛选器)重新应用到打开工作簿时的原始字节上。会话未触及的部分会原样复制。公式会保留其结果:每个新公式,以及会话编辑后重新计算的每个公式,都会以当前结果作为缓存值保存(公式文本保持不变),因此从不进行计算的读取工具(pandas、使用 data_only=True 的 openpyxl、文件预览)也能看到当前数值。会话未影响的公式则保留 Excel 保存的值。文件还会标记为在打开时重新计算,Excel 会遵循这一标记。
图表
浏览器会在工作表锚点处渲染导入的标准折线图,使用单独加载的 Apache ECharts SVG 渲染器。源数据来自计算引擎,包括视口外的单元格;编辑和撤销会在重新计算后更新图表。await sheet.getCharts() 会返回显示描述信息和当前值。sheet.getLastEditTimings() 会报告最近一次编辑的计算、序列化和传输/应用耗时。
此首个版本支持直接的一维工作表引用,每个系列最多 10,000 个数据点,并支持基本标题、图例、RGB 线条颜色和数值坐标轴。其视觉样式近似 Excel。暂不支持其他图表类型、命名/外部数据引用、堆积/平滑折线、日期/对数/次坐标轴,以及图表创建或编辑控件。不支持的图表会显示占位符,并在保存时保留其原生定义。
浏览器和无头模式下的保存会根据已保存的工作表值,刷新受影响的直接引用数值/字符串图表缓存。源引用、样式和无关图表 XML 保持不变。不支持的引用会保留原始缓存;Excel 的重新计算标记仍会保留。
与其他可嵌入电子表格的比较
与 Web 应用通常会嵌入的电子表格组件相比,在 Chromium 中使用真实模型进行测试(工作簿、方法和完整结果):
| GridPath | SpreadJS 19.2 | Handsontable 18.1 + HyperFormula 3.5 + ExcelJS 4.4 | |
|---|---|---|---|
| 1.4 MB 分析师模型:打开、完全重新计算 | 0.6 秒,0.8 秒 | 1.5 秒,3.6 秒 | 1.9 秒 |
| … 与 Excel 的结果一致 | 95.5% | 94.0% | 56.9% |
| … 保存时丢失 | 无(76 个部件中有 75 个字节完全相同) | 自定义 XML、打印机设置 | 自定义 XML、打印机设置、批注、3,418 个名称中的 3,335 个 |
| 超过 10 MB 的数据密集型模型(230 万个单元格):打开、完全重新计算 | 4.2 秒,10.9 秒 | 标签页崩溃(惰性模式:6.5 秒,3.7 分钟) | 10.8 分钟 |
| … 编辑 → 依赖项更新 | 2.7 秒 | —(惰性模式下首次编辑时崩溃) | 64 秒 |
打包工具
该软件包使用 new URL("…", import.meta.url) 加载其 worker 和引擎,Next.js、Vite、webpack 5 和 Rollup 都可以自行打包。
-
Next.js:无需配置(参见 Next.js)。
-
Vite:生产构建可直接使用。对于开发服务器,请将该软件包排除在依赖预打包之外:
// vite.config.ts export default defineConfig({ optimizeDeps: { exclude: ["gridpath"] } }); -
webpack 5(Next.js 之外):无需配置;
new URLworker 和资源会自动打包。 -
其他情况:将
engine.worker.js、其代码块和gridpath_engine_bg.wasm从dist/复制到静态文件目录,并传入engine: { workerUrl, wasmUrl }。
浏览器中的体积:约 120 KB JavaScript 和 1.3 MB 引擎,均为 gzip 压缩后大小(使用 brotli 时为 0.9 MB)。引擎只下载一次,并由浏览器缓存。
从源代码构建
crates/engine-wasm/build.sh # needs rustup target wasm32-unknown-unknown and wasm-bindgen-cli
cd packages/gridpath && npm install && npm pack
examples/embed-demo 会像应用一样安装打包后的 tarball。
在你的编码 Agent 中(MCP)
让你的 Agent 编辑 Excel 文件。在保存任何内容之前先审核。
适用于 Claude Code、Claude Desktop、Cursor、Codex 或任何 MCP 宿主的 MCP 服务,可编辑你已有的 .xlsx / .xlsm 文件,并提供审核步骤:
- 保存前审核。 每次写入都会返回一个
review_url:一个本地页面,显示工作簿、突出显示已更改的单元格、逐个单元格显示更改前后内容,并可按批次或按单元格接受/拒绝、保存或另存为……(副本)。启用--review-required后,只有该页面可以写入文件;Agent 只会提供链接。 - 保留你的 Excel 文件原样。 不通过库重新导出,也不转换为其他格式。保存时会修补原始字节:只重写编辑涉及的部分。未触及的部分(包括数据透视表、VBA 宏、加载项数据和自定义 XML)都会以完全相同的字节写回。引用的图表缓存会随重新计算后的工作表值更新。
- 在进程内计算真实公式。 计算引擎(IronCalc 的一个分支,编译为 WebAssembly)在服务内运行。Agent 可以读取计算后的值,也可以写入在 Excel 中打开文件时有效的公式;插入和删除操作会更新引用。
- Node 可运行的地方都能运行。 无需 Python、openpyxl、LibreOffice 或 Excel 安装。支持 macOS、Windows、Linux。
- 沙盒化脚本。
run_script会在带硬性超时限制的工作线程中运行 Agent 针对工作簿编写的 JavaScript,并将结果作为一个可审核批次提交。

安装
Claude Desktop(无需终端)
下载 gridpath-0.5.1.mcpb 并打开。Claude Desktop 会显示安装对话框,让你选择 Claude 可以编辑的文件夹,以及保存是否需要审核。Node 已内置;无需安装其他内容。对于从文件安装的任何扩展,Claude Desktop 都会显示标准的“未验证”警告;源代码是公开的,并且在你于审核页面接受并保存之前,不会写入任何内容。
Claude Code
claude mcp add gridpath -- npx -y gridpath mcp --review-required
Cursor
添加到 Cursor(一键完成),或者将其添加到 mcp.json:
{ "mcpServers": { "gridpath": { "command": "npx", "args": ["-y", "gridpath", "mcp", "--review-required"] } } }
Codex(~/.codex/config.toml)
[mcp_servers.gridpath]
command = "npx"
args = ["-y", "gridpath", "mcp", "--review-required"]
Gemini CLI、Windsurf、VS Code 及其他
在该宿主的 MCP 设置中使用与 Cursor JSON 相同的 command / args。该服务通过 stdio 通信。
选项
| 标志 | 含义 |
|---|---|
--allow <dir> [<dir>...] | 将服务限制为这些文件夹。默认值:你的主目录。 |
--review-required | Agent 无法保存。save_workbook 会返回审核链接;你可以在那里接受并保存。也可设置 GRIDPATH_REVIEW_REQUIRED=true。 |
--port <n> | 本地审核服务的端口(默认:随机)。 |
gridpath review <file> | 稍后重新打开工作簿待处理批次的审核页面。 |
待处理的更改会保存在工作簿旁边(.gridpath/<file>.batches.json),直到保存或拒绝,因此即使 Agent 会话结束,也不会丢失内容。
工具
每个工具都需要 path。单元格和区域使用 A1 表示法。
| 工具 | 功能 |
|---|---|
describe_workbook (sheet?) | 结构图:工作表、已用区域、标题行、分区和命名区域。请首先调用。 |
find_rows (query, sheet?, max_results?) | 按标签在各工作表中查找行,并附带一条求值后的示例。 |
read_range (sheet, range) | 返回区域的求值结果、公式和显示文本(≤ 500 个单元格)。 |
set_cell (sheet, cell, value | formula) | 写入一个单元格。 |
set_range (sheet, top_left, values[][]) | 写入一个区域;以 = 开头的字符串会被视为公式。 |
copy_range (sheet, source, dest_sheet, dest, mode) | 类似 Excel 的复制,并调整引用;支持 all / values / formats。 |
run_script (script) | 在沙箱中运行 JS,可使用 sheet(name).get/set/values/setValues/format/clear;每次执行一个批次。 |
set_format (sheet, range | operations[], format) | 设置数字格式、粗体/斜体、颜色、对齐方式和自动换行。 |
clear_range (sheet, range) | 清除值和公式,保留格式。 |
insert_rows / delete_rows / insert_columns / delete_columns | 结构编辑;引用、CF 和数据验证区域会随之调整。 |
create_sheet / rename_sheet / delete_sheet | 工作表操作。 |
set_column_width / set_row_height / freeze_panes / unfreeze_panes / hide_rows / show_rows / hide_columns / show_columns | 布局。 |
merge_cells / unmerge_cells / set_note / delete_note / define_name | 合并单元格、批注和命名区域。 |
list_batches | 查看待处理批次和审核链接。 |
reject_batch (batch_id) | 丢弃待处理批次;其余批次会重新应用到一个新模型上。 |
save_workbook (as?) | 将待处理更改写入文件;或通过 as 写入修补后的副本,并保持原文件不变。唯一会写入文件的工具。原位保存受 --review-required 限制。 |
写入操作不会触碰文件。更改会累积为待处理批次,每次写入都会返回已应用内容的回读结果(值、公式、标签的 row_map),以及 review_url。
Agent 的说法
Claude 在 Cursor 中通过 GridPath 编辑了一个有 1,400 行、2,500 多个命名区域和隐藏工作表的卖方模型后,询问它与内置方法相比如何:
它会实时重新计算。 我修改第 58 行后,马上就能读回新的增长率(28.0%、28.0%、27.9%),并确认修改生效了。我常用的方法是一个叫 openpyxl 的 Python 库,它不会计算公式。我必须把文件交给 LibreOffice 才能获得数值,而某些函数的计算结果可能与 Excel 不同。
它能快速找到内容。 通过几次调用,它的行搜索和工作簿结构图就找到了“Product revenue”和 Assumptions 结构。否则我得逐个单元格扫描一个有 1,400 行的工作表。
它不会改动文件的其他部分。 它只会重写发生更改的单元格。openpyxl 打开并重新保存这样复杂的模型时,可能会悄悄丢弃或损坏图表、数据验证、数据透视表缓存或某些格式。这是我使用内置方法时最大的风险。
对比
| gridpath | 文件库 MCP(openpyxl 等) | 实时 Excel MCP(COM) | 浏览器电子表格 SDK | |
|---|---|---|---|---|
| 编辑现有文件 | 是,原位修补 | 重写整个文件 | 是,通过 Excel | 转换为其自有格式 |
| 公式求值 | 是,在进程内完成 | 否 | 是,需要打开 Excel | 是,在托管应用中完成 |
| 写入前审核 | 是 | 否 | 否 | 在供应商的 UI 中进行 |
| 需要安装 | Node | Python | Windows + Excel | Web 应用 + 账户 |
| 宏、数据透视表和加载项数据能否保留 | 字节级一致 | 通常会被丢弃 | 是 | 不保证 |
基准测试和方法说明(包括普通编码 Agent 表现与我们相当的测试)见 gridpath-evals 仓库。
支持的文件
.xlsx 和 .xlsm。已在真实分析师模型上验证:一个 1.4 MB、12 个工作表、55,000 个公式的工作簿(76 个包部件中,编辑后有 75 个与原文件逐字节相同),以及一个 13 个工作表的 .xlsm 银行模型,其 vbaProject.bin 保存后与原文件逐字节相同。
工作原理
引擎会将 .xlsx 解析为进程内模型;编辑会修改该模型,并读取重新计算后的结果,只重新计算受编辑影响的部分。保存时会将会话中的更改与加载时的文件进行比较,并在 ZIP 部件级别修补原始字节:未更改的部件会直接复制,不会重新生成整个包。在浏览器中,引擎运行于 Web Worker 中,网格只绘制当前可见区域,因此大型模型也能保持轻量。
无头模式(Node)
gridpath/headless 是不含网格的引擎:可从服务端或脚本打开工作簿、调用相同的工具、读取结果并保存。
import { WorkbookSession } from "gridpath/headless";
状态
v0.5。目前尚不支持:
- **在网格中:**折线图以外的图表类型、创建或编辑图表、数据验证下拉列表、实时协作。
- 面向 Agent:
fetch_web和edgar_lookup信息来源工具,以及对图表或数据透视表的编辑。
GridPath 也是一个桌面应用,采用相同的引擎和审阅功能,并内置了自己的 Agent。
许可证
Apache-2.0(LICENSE、NOTICE);软件包中的第三方软件列于 THIRD_PARTY_NOTICES.md。0.2.0 之前的版本采用 FSL-1.1-Apache-2.0。“GridPath”是 Heelix Technologies Inc. 的商标。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。