mcpField
G
GridPath

GridPath

本地服务
gridpath.dev生产力91 天前更新

关于 GridPath

GridPath 是可编辑真实 Excel 工作簿的表格引擎,供应用开发者和编码 Agent 使用。它保留公式并将修改写回原始 .xlsx,Agent 的每项改动都可先审阅再保存。

主要功能

  1. 01编辑真实 .xlsx 工作簿
  2. 02实时计算公式
  3. 03审阅并接受或拒绝 Agent 修改
  4. 04将改动写回原始文件
  5. 05提供嵌入式表格界面
  6. 06导入 CSV 和分隔文本

适用场景

  1. 01在编码 Agent 中修改工作簿并逐项审阅
  2. 02在 Web 应用中嵌入电子表格
  3. 03将 CSV 数据转换为可编辑工作簿

MCP 相关

GridPath 提供 MCP 服务,供 Claude Code、Claude Desktop、Cursor 和 Codex 等客户端编辑磁盘上的工作簿,并在保存前审阅改动。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"pixelsmasher13-gridpath": {
"command": "npx",
"args": [
"-y",
"gridpath"
]
}
}
}
启动命令npx -y gridpath

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmgridpath0.5.0

工具

32

收录的工具列表 · 更新于 1 天前

参数类型必填说明
cellstring是A1 地址。示例:"A1"、"B17"、"AA42"。
pathstring是要操作的 .xlsx 文件路径(绝对路径,或用户主目录下的 ~/…)。
sheetstring是工作表名称(区分大小写)。
valueany否字面值(字符串、数字、布尔值或 null)。使用 `formula` 时留空为 null。
formulastring|null否以 '=' 开头的 Excel 样式公式。引用使用 A1 表示法。示例:"=SUM(A1:A10)"。

详细介绍

gridpath

npm license node

在你的 Web 应用和 Agent 中使用真实的 Excel 文件。 打开用户已有的 .xlsx 文件,以完整尺寸保留所有实时公式,让 AI 在审阅后进行编辑,并保存回同一个文件。

在你的 Web 应用中在你的编码 Agent 中
安装npm install gridpathclaude 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 应用中

快速开始
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,工作簿将以空白状态启动。

切换工作簿有两种方式:

  1. **为每个文档重新挂载组件。**为组件设置 key={book.id},就像 Next.js 示例中为每个工作簿使用一个页面一样。
  2. 在同一个电子表格上调用 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。

当无法连接后端或后端返回错误时,用户的编辑不会丢失:

  1. 状态显示“仅保存在此浏览器中 · 重试”,且 keptLocally: true。
  2. 自动保存会在 2、5、15 和 30 秒后重试,之后每分钟重试一次。
  3. 编辑内容的副本会保存在此浏览器中。
  4. 如果先关闭页面,下次打开时会重新载入该副本;它比后端中的版本更新,并会保存到后端。状态行会显示“已恢复未保存到你账户的更改”。

打开工作簿时,如果无法连接后端,电子表格会显示错误和“重试”选项。它不会打开空白工作簿,导致自动保存覆盖用户已保存的工作簿。

其他任何方式都可以作为存储实现。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-timeoutgetCell()(等待 30 s 后)、whenCalculated(ms) 或 save()(等待 60 s 后)在重新计算仍在运行时超时。等待计算完成后再试一次。
pending-changesAI 更改待审核时调用了 save() 或 open()(PendingChangesError,包含 pending)。接受或拒绝这些更改(sheet.ai.accept() / reject()),或调用 save({ includePending: true })。
unsaved-changesopen() 将会丢弃无法保存的编辑内容(UnsavedChangesError)。询问用户,然后使用 { discardUnsaved: true } 重试。
not-foundopenStored(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。
autosavetrue(默认值:编辑暂停两秒后自动保存)、以毫秒为单位的延迟时间,或 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…"),用于显示加载指示器。
toolbarfalse 可隐藏工具栏。
fileMenufalse 可从「文件」菜单中移除「打开」、「新建」和「最近」。
reviewPanelfalse 可关闭「AI 更改」面板。你可以使用 sheet.ai.list()、accept() 和 reject() 自行构建面板。
engine.workerfalse 可让引擎在页面上运行,而不是在 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 中,请卸载组件)。
aiAgent 工具;见下文。
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 中使用真实模型进行测试(工作簿、方法和完整结果):

GridPathSpreadJS 19.2Handsontable 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 URL worker 和资源会自动打包。

  • 其他情况:将 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,并将结果作为一个可审核批次提交。

GridPath 审核页面:工作簿中突出显示了一个已更改单元格,旁边的面板列出了待处理的更改批次

安装
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-requiredAgent 无法保存。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 中进行
需要安装NodePythonWindows + ExcelWeb 应用 + 账户
宏、数据透视表和加载项数据能否保留字节级一致通常会被丢弃是不保证

基准测试和方法说明(包括普通编码 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. 的商标。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。