apple-numbers-mcp 1.1.9 → 1.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -77,9 +77,24 @@ numbers-parser`); see [Requirements](#requirements).
77
77
 
78
78
  ### Other Hosts (Hermes, Antigravity)
79
79
 
80
- Configuration for two more hosts is included — each registers the same `apple-numbers` MCP server (`npx -y apple-numbers-mcp`). All paths still need the `numbers-parser` Python sidecar available; see [Requirements](#requirements).
80
+ Two more hosts can run the same `apple-numbers` MCP server (`npx -y apple-numbers-mcp`). All paths still need the `numbers-parser` Python sidecar available; see [Requirements](#requirements).
81
81
 
82
- - **[Hermes Agent](https://hermes-agent.nousresearch.com/)** (NousResearch) — Hermes has no plugin/marketplace drop-in. Add the server with `hermes mcp add apple-numbers --command npx --args -y apple-numbers-mcp`, or merge [`.hermes-plugin/config.yaml`](https://github.com/sweetrb/apple-numbers-mcp/blob/main/.hermes-plugin/config.yaml) into `~/.hermes/config.yaml`. Details: [`.hermes-plugin/README.md`](https://github.com/sweetrb/apple-numbers-mcp/blob/main/.hermes-plugin/README.md).
82
+ - **[Hermes Agent](https://hermes-agent.nousresearch.com/)** (NousResearch) — Hermes has no plugin/marketplace drop-in, so there is nothing in this repo to install from. Register the server with the CLI:
83
+
84
+ ```bash
85
+ hermes mcp add apple-numbers --command npx --args -y apple-numbers-mcp
86
+ ```
87
+
88
+ Or add it to `~/.hermes/config.yaml` by hand:
89
+
90
+ ```yaml
91
+ mcp_servers:
92
+ apple-numbers:
93
+ command: npx
94
+ args: ["-y", "apple-numbers-mcp"]
95
+ ```
96
+
97
+ Restart your Hermes session afterward so the tools load.
83
98
  - **[Antigravity](https://antigravity.google/)** (Google) — add the server entry from [`.antigravity-plugin/mcp_config.json`](https://github.com/sweetrb/apple-numbers-mcp/blob/main/.antigravity-plugin/mcp_config.json) to `~/.gemini/config/mcp_config.json` (or via Antigravity's MCP settings).
84
99
 
85
100
  ### Manual Installation
@@ -138,10 +153,10 @@ The entrypoint is written as:
138
153
 
139
154
  ## Requirements
140
155
 
141
- - **macOS** — the npm package is macOS-only (it declares `os: ["darwin"]`). Reads go through `numbers-parser` directly (no Numbers.app needed); **formatting and formula tools additionally require Numbers.app.**
156
+ - **macOS** — the npm package is macOS-only (it declares `os: ["darwin"]`). Reads **and value/structure writes** go through `numbers-parser` directly (no Numbers.app needed); **formatting and formula tools additionally require Numbers.app.**
142
157
  - **Node.js 20+** — Required for the MCP server
143
158
  - **Python 3.11+** — the `numbers-parser` library installs automatically into a project-local venv on first use (or pre-warm with `pnpm run setup`). numbers-parser requires Python ≥ 3.10; macOS ships 3.9, so install a newer Python first (e.g. `brew install python@3.12`).
144
- - **Automation permission (writes only)** — Reads and exports need no special permission, but write/format tools drive Numbers.app via AppleScript and require the host app to have Automation permission for Numbers, granted on first use. See the [Automation Permission guide](https://github.com/sweetrb/apple-numbers-mcp/blob/main/docs/AUTOMATION-PERMISSION.md). Run the **`doctor`** tool to verify your setup.
159
+ - **Automation permission (formulas & formatting only)** — Reads, exports and ordinary value/structure writes (`set-cell(s)`, `add`/`update`/`delete-rows`, `add-sheet`/`add-table`, `rename-*`, `create-spreadsheet`, `import-csv`) need no special permission and no Numbers.app. Only the eight formula/format tools — `set-formula(s)`, `set-cell(s)-style`, `set-column-width`/`set-row-height`, `merge-cells`/`unmerge-cells` — drive Numbers.app via AppleScript and require the host app to have Automation permission for Numbers, granted on first use. See the [Automation Permission guide](https://github.com/sweetrb/apple-numbers-mcp/blob/main/docs/AUTOMATION-PERMISSION.md). Run the **`doctor`** tool to verify your setup.
145
160
 
146
161
  ## Features
147
162
 
@@ -218,11 +233,11 @@ Verify Python 3 and `numbers-parser` are installed and reachable from the server
218
233
 
219
234
  #### `doctor`
220
235
 
221
- Run a full setup diagnostic with four separate checks: `python_interpreter` (the resolved Python's path and version — warns when it's older than 3.11, the most common setup failure since stock macOS ships 3.9), `numbers_parser` (the read sidecar — required for all reads/exports), `numbers_app` (Numbers.app present — required for write/format tools), and `automation_permission` (an informational reminder that write tools need Automation permission for Numbers.app). Each is reported as ok / warn / fail with actionable advice. This is the richer counterpart to `health-check`; reach for it first when a tool returns a permission or setup error.
236
+ Run a full setup diagnostic with four separate checks: `python_interpreter` (the resolved Python's path and version — warns when it's older than 3.11, the most common setup failure since stock macOS ships 3.9), `numbers_parser` (the read sidecar — required for all reads/exports), `numbers_app` (Numbers.app present — required for the formula/format tools only), and `automation_permission` (an informational reminder that those tools need Automation permission for Numbers.app). Each is reported as ok / warn / fail with actionable advice. This is the richer counterpart to `health-check`; reach for it first when a tool returns a permission or setup error.
222
237
 
223
238
  **Parameters:** None
224
239
 
225
- **Returns:** A per-check report. The `structuredContent` carries the raw `{ healthy, checks[] }`, where each check has `name`, `status` (`ok`/`warn`/`fail`), and `detail`. Reads don't need Automation permission, but writes do — see [docs/AUTOMATION-PERMISSION.md](https://github.com/sweetrb/apple-numbers-mcp/blob/main/docs/AUTOMATION-PERMISSION.md).
240
+ **Returns:** A per-check report. The `structuredContent` carries the raw `{ healthy, checks[] }`, where each check has `name`, `status` (`ok`/`warn`/`fail`), and `detail`. Only the formula/format tools need Automation permission — see [docs/AUTOMATION-PERMISSION.md](https://github.com/sweetrb/apple-numbers-mcp/blob/main/docs/AUTOMATION-PERMISSION.md).
226
241
 
227
242
  ---
228
243
 
@@ -251,6 +266,8 @@ Read data from a table. Returns headers and rows. Defaults to the first sheet an
251
266
  | `endRow` | number | No | 0-based end row, inclusive (default: last row) |
252
267
  | `columns` | (string \| number)[] | No | Column filter: header names or 0-based indices |
253
268
 
269
+ **Returns:** Sheet name, table name, the headers and rows selected, and `numRows` / `numCols` describing **the selection** — not the table. When you pass `startRow`/`endRow`/`columns`, those counts describe what came back; use [`get-file-info`](#get-file-info) for the table's real dimensions.
270
+
254
271
  ---
255
272
 
256
273
  #### `get-cell`
@@ -286,6 +303,8 @@ Case-insensitive partial match across every cell in a `.numbers` file.
286
303
 
287
304
  Export a table to CSV, TSV, or JSON.
288
305
 
306
+ **⚠️ Safety:** Writes a file to disk at `outputPath`. The path is written unconditionally — any existing file there is **overwritten** — so confirm the destination before calling.
307
+
289
308
  | Parameter | Type | Required | Description |
290
309
  |-----------|------|----------|-------------|
291
310
  | `path` | string | Yes | Path to the `.numbers` file |
@@ -404,6 +423,8 @@ Add a new sheet, optionally with headers for the default table.
404
423
  | `tableName` | string | No | Name for the default table |
405
424
  | `headers` | string[] | No | Headers for the default table |
406
425
 
426
+ **Table geometry:** pass `headers` and the new table is created **1 row × `headers.length` columns**; omit them and you get a **12 × 8** grid of empty cells. There is no way to override the size through MCP — delete the extra rows with `delete-rows` if you don't want them. The response reports the dimensions that were created.
427
+
407
428
  ---
408
429
 
409
430
  #### `add-table`
@@ -417,6 +438,8 @@ Add a new table to an existing sheet.
417
438
  | `tableName` | string | No | Name for the new table |
418
439
  | `headers` | string[] | No | Column headers |
419
440
 
441
+ **Table geometry:** same rule as [`add-sheet`](#add-sheet) — with `headers` the table is **1 × `headers.length`**, without them **12 × 8**.
442
+
420
443
  ---
421
444
 
422
445
  #### `rename-sheet` / `rename-table`
@@ -468,7 +491,7 @@ Write multiple formulas in a single operation.
468
491
  | `path` | string | Yes | Path to the `.numbers` file |
469
492
  | `sheet` | string | Yes | Sheet name |
470
493
  | `table` | string | Yes | Table name |
471
- | `entries` | array | Yes | Array of `{row, col, formula}` objects |
494
+ | `formulas` | array | Yes | Array of `{row, col, formula}` objects |
472
495
 
473
496
  ---
474
497
 
@@ -560,6 +583,12 @@ Import a CSV, TSV, or JSON file into a new `.numbers` spreadsheet. Auto-detects
560
583
  | `sheetName` | string | No | Default: `"Sheet 1"` |
561
584
  | `tableName` | string | No | Default: `"Table 1"` |
562
585
 
586
+ **CSV/TSV fields are auto-typed.** Every field from a `csv`/`tsv` source is converted before it is written: `""` → empty cell, `true`/`false` → boolean, and anything Python's `int()`/`float()` parses → number. So a zip code `01234` becomes the number `1234`, `007` becomes `7`, and `1e5` becomes the number `100000`. This is deliberately less conservative than `set-cell`/`add-rows`, which keep leading-zero strings intact, and there is no per-column type control on import. For zero-padded identifiers, import from JSON instead (JSON values are passed through untouched) or repair the column afterwards with `set-cells-batch` using `type: "string"`.
587
+
588
+ **JSON column sets come from the first object.** For an array of objects, the columns are the keys of `data[0]`; a key that appears only in a later object is dropped and objects missing an early key get blank cells — normalize the keys first. For an array of arrays, columns are named `Column_0`, `Column_1`, … and the first row is kept as data.
589
+
590
+ **`format: "auto"` falls back to CSV.** Any extension that isn't `.csv`, `.tsv` or `.json` is parsed as comma-separated rather than erroring, so pass `format` explicitly for unusual extensions. The format actually used is echoed back in the result.
591
+
563
592
  ---
564
593
 
565
594
  ## Usage Patterns
@@ -584,7 +613,7 @@ AI: [calls search with query='Acme Corp']
584
613
 
585
614
  ```
586
615
  User: "Set B5 to =SUM(B2:B4) and bold the header row"
587
- AI: [calls set-formula] [calls set-cells-style-batch with bold:true on row 0]
616
+ AI: [calls set-formula] [calls set-cells-style-batch with style {fontName: "Helvetica-Bold"} on row 0]
588
617
  "Done — B5 is =SUM(B2:B4) and the header row is bold."
589
618
  ```
590
619
 
@@ -648,6 +677,15 @@ All configuration is optional — the server works out of the box.
648
677
  | `APPLE_NUMBERS_MCP_NO_AUTO_SETUP` | unset (auto-setup on) | Set to a truthy value (`1`, `true`) to disable the one-time automatic creation of the Python venv. When set, the server will not run `setup.sh` on its own — you must provide `numbers-parser` yourself (`pip3 install numbers-parser` or `pnpm run setup`). |
649
678
  | `APPLE_NUMBERS_MCP_SETUP_TIMEOUT` | `300000` (5 minutes) | Timeout in milliseconds for the automatic venv bootstrap (`setup.sh`). Raise it on a slow network where the `numbers-parser` pip install would otherwise time out. |
650
679
 
680
+ > **Per-call timeouts are not configurable.** Every `numbers-parser` sidecar call
681
+ > runs under a fixed **30-second** timeout and every AppleScript call under a fixed
682
+ > **60-second** one; there is no environment variable for either.
683
+ > `APPLE_NUMBERS_MCP_SETUP_TIMEOUT` above governs only the one-time venv bootstrap,
684
+ > and `APPLE_NUMBERS_MCP_MAX_BUFFER` caps stdout size, not wall-clock time. If you
685
+ > hit `Operation timed out after 30000ms`, narrow the request — use `read-table`'s
686
+ > `startRow`/`endRow` and `columns`, split large batches — rather than looking for
687
+ > a knob.
688
+
651
689
  ### Configuration file (when the host strips `env`)
652
690
 
653
691
  Some host apps (e.g. Claude Desktop) launch the MCP server with a scrubbed
@@ -686,19 +724,22 @@ This is the same Python-sidecar pattern used by [apple-photos-mcp](https://githu
686
724
  - **Local only** — All operations happen on the local machine. No data is sent to external servers.
687
725
  - **No credential storage** — The server doesn't store any passwords or authentication tokens.
688
726
  - **File-system access** — Tools take explicit paths; the server only reads or writes files you name.
689
- - **Numbers.app automation** — Formatting and formula tools drive Numbers.app via AppleScript. macOS will prompt for automation permission on first use.
727
+ - **Numbers.app automation** — Formatting and formula tools drive Numbers.app via AppleScript. macOS will prompt for automation permission on first use. These tools **open the file in Numbers.app** (launching it if it isn't running), **save the whole document** — including any unsaved edits you have open in it — and **leave it open** afterwards. Everything else, including all value and structure writes, runs entirely in the Python sidecar and never touches the app.
690
728
 
691
729
  ---
692
730
 
693
731
  ## Known Limitations
694
732
 
695
- For the full rundown — the read-vs-write split, AppleScript-only formulas/styles,
696
- indexing, dates, format lag, and concurrent edits — see **[docs/LIMITATIONS.md](https://github.com/sweetrb/apple-numbers-mcp/blob/main/docs/LIMITATIONS.md)**.
733
+ For the full rundown — the values-vs-formatting backend split, AppleScript-only
734
+ formulas/styles, `import-csv` auto-typing, fixed per-call timeouts, indexing,
735
+ dates, format lag, and concurrent edits — see **[docs/LIMITATIONS.md](https://github.com/sweetrb/apple-numbers-mcp/blob/main/docs/LIMITATIONS.md)**.
697
736
  The summary below is the quick version.
698
737
 
699
738
  | Limitation | Reason |
700
739
  |------------|--------|
701
- | Formatting / formulas / dimensions need Numbers.app | `numbers-parser` doesn't write styles or formulas; the AppleScript layer fills that gap (macOS only) |
740
+ | Formatting / formulas / dimensions need Numbers.app | `numbers-parser` doesn't write styles or formulas; the AppleScript layer fills that gap (macOS only). Value and structure writes do **not** need it |
741
+ | `import-csv` auto-types CSV/TSV fields | Leading zeros are lost (`01234` → `1234`); JSON input is passed through untouched |
742
+ | Per-call timeouts are fixed | 30 s for sidecar calls, 60 s for AppleScript; neither is configurable — narrow the request instead |
702
743
  | No conditional formatting | Not exposed by `numbers-parser` |
703
744
  | No charts or images | Not exposed by `numbers-parser` |
704
745
  | Sheet deletion not supported | Not exposed by `numbers-parser` |
package/build/index.js CHANGED
@@ -3104,9 +3104,9 @@ var require_data = __commonJS({
3104
3104
  }
3105
3105
  });
3106
3106
 
3107
- // node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/utils.js
3107
+ // node_modules/.pnpm/fast-uri@3.1.4/node_modules/fast-uri/lib/utils.js
3108
3108
  var require_utils = __commonJS({
3109
- "node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/utils.js"(exports, module) {
3109
+ "node_modules/.pnpm/fast-uri@3.1.4/node_modules/fast-uri/lib/utils.js"(exports, module) {
3110
3110
  "use strict";
3111
3111
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3112
3112
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
@@ -3417,9 +3417,9 @@ var require_utils = __commonJS({
3417
3417
  }
3418
3418
  });
3419
3419
 
3420
- // node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/schemes.js
3420
+ // node_modules/.pnpm/fast-uri@3.1.4/node_modules/fast-uri/lib/schemes.js
3421
3421
  var require_schemes = __commonJS({
3422
- "node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/schemes.js"(exports, module) {
3422
+ "node_modules/.pnpm/fast-uri@3.1.4/node_modules/fast-uri/lib/schemes.js"(exports, module) {
3423
3423
  "use strict";
3424
3424
  var { isUUID } = require_utils();
3425
3425
  var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
@@ -3627,9 +3627,9 @@ var require_schemes = __commonJS({
3627
3627
  }
3628
3628
  });
3629
3629
 
3630
- // node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/index.js
3630
+ // node_modules/.pnpm/fast-uri@3.1.4/node_modules/fast-uri/index.js
3631
3631
  var require_fast_uri = __commonJS({
3632
- "node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/index.js"(exports, module) {
3632
+ "node_modules/.pnpm/fast-uri@3.1.4/node_modules/fast-uri/index.js"(exports, module) {
3633
3633
  "use strict";
3634
3634
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3635
3635
  var { SCHEMES, getSchemeHandler } = require_schemes();
@@ -3770,6 +3770,7 @@ var require_fast_uri = __commonJS({
3770
3770
  return uriTokens.join("");
3771
3771
  }
3772
3772
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3773
+ var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
3773
3774
  function getParseError(parsed, matches) {
3774
3775
  if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
3775
3776
  return 'URI path must start with "/" when authority is present.';
@@ -3799,6 +3800,11 @@ var require_fast_uri = __commonJS({
3799
3800
  uri = "//" + uri;
3800
3801
  }
3801
3802
  }
3803
+ const authorityMatch = uri.match(AUTHORITY_PREFIX);
3804
+ if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
3805
+ parsed.error = "URI authority must not contain a literal backslash.";
3806
+ malformedAuthorityOrPort = true;
3807
+ }
3802
3808
  const matches = uri.match(URI_PARSE);
3803
3809
  if (matches) {
3804
3810
  parsed.scheme = matches[1];
@@ -3842,7 +3848,7 @@ var require_fast_uri = __commonJS({
3842
3848
  if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3843
3849
  if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3844
3850
  try {
3845
- parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
3851
+ parsed.host = new URL("http://" + parsed.host).hostname;
3846
3852
  } catch (e) {
3847
3853
  parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
3848
3854
  }
@@ -21421,7 +21427,7 @@ function findSystemPython() {
21421
21427
  }
21422
21428
  }
21423
21429
  throw new Error(
21424
- "Python 3 not found on PATH. Install Python >= 3.11 (stock macOS ships 3.9 - brew install python@3.12), or run scripts/setup.sh from a repo checkout. See https://github.com/sweetrb/apple-numbers-mcp#troubleshooting"
21430
+ "Python 3 not found on PATH. Install Python >= 3.11 (stock macOS ships 3.9 - brew install python@3.12), or run scripts/setup.sh from a repo checkout. Run the doctor tool to diagnose, and see https://github.com/sweetrb/apple-numbers-mcp#troubleshooting"
21425
21431
  );
21426
21432
  }
21427
21433
  function resolvePython() {
@@ -22138,27 +22144,27 @@ function runDoctor(_manager) {
22138
22144
  checks.push({
22139
22145
  name: "numbers_app",
22140
22146
  status: "ok",
22141
- detail: `Numbers.app present \u2014 write operations available (${found})`
22147
+ detail: `Numbers.app present \u2014 formula and formatting tools available (${found})`
22142
22148
  });
22143
22149
  } else {
22144
22150
  checks.push({
22145
22151
  name: "numbers_app",
22146
22152
  status: "warn",
22147
- detail: "Numbers.app not found. Reads/exports via numbers-parser still work, but write, formatting, and other AppleScript tools need Numbers.app installed."
22153
+ detail: "Numbers.app not found. Reads, exports and value/structure writes via numbers-parser still work; only the formula and formatting tools (set-formula(s), set-cell(s)-style, set-column-width/set-row-height, merge-cells/unmerge-cells) need Numbers.app installed."
22148
22154
  });
22149
22155
  }
22150
22156
  } catch (e) {
22151
22157
  checks.push({
22152
22158
  name: "numbers_app",
22153
22159
  status: "warn",
22154
- detail: `could not verify Numbers.app: ${String(e)}. Reads/exports still work; write/formatting tools need Numbers.app.`
22160
+ detail: `could not verify Numbers.app: ${String(e)}. Reads, exports and value/structure writes still work; only the formula/formatting tools need Numbers.app.`
22155
22161
  });
22156
22162
  }
22157
22163
  try {
22158
22164
  checks.push({
22159
22165
  name: "automation_permission",
22160
22166
  status: "ok",
22161
- detail: "AppleScript write tools require Automation permission for Numbers.app, granted on first use (System Settings > Privacy & Security > Automation)."
22167
+ detail: "The AppleScript formula/formatting tools require Automation permission for Numbers.app, granted on first use (System Settings > Privacy & Security > Automation). Reads and value/structure writes do not."
22162
22168
  });
22163
22169
  } catch (e) {
22164
22170
  checks.push({
@@ -22308,7 +22314,7 @@ var headerCellSchema = external_exports.string().max(MAX_HEADER_LEN);
22308
22314
  var server = new McpServer({
22309
22315
  name: "apple-numbers",
22310
22316
  version: version2,
22311
- description: "MCP server for reading and writing Apple Numbers (.numbers) spreadsheet files. Provides tools to inspect file structure, read table data, search cells, export to CSV/JSON/TSV, read individual cell values, create new spreadsheets, update cells, and append rows."
22317
+ description: "MCP server for reading and writing Apple Numbers (.numbers) spreadsheet files. Inspect file structure, read/search/export table data and individual cells, create spreadsheets, write cells and add/update/delete rows, manage sheets and tables, import CSV/TSV/JSON \u2014 all offline via the numbers-parser sidecar \u2014 and, via Numbers.app, write live formulas, cell styling, column/row dimensions, and merges."
22312
22318
  });
22313
22319
  server.registerTool(
22314
22320
  "health-check",
@@ -22330,7 +22336,7 @@ server.registerTool(
22330
22336
  server.registerTool(
22331
22337
  "doctor",
22332
22338
  {
22333
- description: "Use when: a tool returns a permission or setup error, or you want the full setup diagnostic before writing/formatting.\nReturns: four checks \u2014 the resolved Python interpreter (path + version), numbers-parser (read sidecar), Numbers.app (needed for writes), and Automation permission \u2014 each as ok/warn/fail with actionable advice.\nDo not use when: you only need the lightweight read-sidecar version check \u2014 use health-check instead.",
22339
+ description: "Use when: a tool returns a permission or setup error, or you want the full setup diagnostic before writing/formatting.\nReturns: four checks \u2014 the resolved Python interpreter (path + version), numbers-parser (the read/write sidecar), Numbers.app (needed only for the formula/formatting tools), and Automation permission \u2014 each as ok/warn/fail with actionable advice.\nDo not use when: you only need the lightweight read-sidecar version check \u2014 use health-check instead.",
22334
22340
  inputSchema: {},
22335
22341
  outputSchema: {
22336
22342
  healthy: external_exports.boolean().optional(),
@@ -22395,7 +22401,7 @@ ${summary}`,
22395
22401
  server.registerTool(
22396
22402
  "read-table",
22397
22403
  {
22398
- description: "Use when: you want the data from a table \u2014 headers plus rows, optionally limited to a row range (0-based inclusive indices; header is row 0, so data starts at row 1) and/or a subset of columns. Defaults to the first sheet and first table if sheet/table are omitted.\nReturns: sheet name, table name, dimensions, headers, and the selected rows.\nDo not use when: you only need one cell \u2014 use get-cell; you need the file's structure/names \u2014 use get-file-info; or you want to find a value's location \u2014 use search.",
22404
+ description: "Use when: you want the data from a table \u2014 headers plus rows, optionally limited to a row range (0-based inclusive indices; header is row 0, so data starts at row 1) and/or a subset of columns. Defaults to the first sheet and first table if sheet/table are omitted.\nReturns: sheet name, table name, the headers and rows selected, and numRows/numCols describing the SELECTION \u2014 not the table. Use get-file-info for the table's full dimensions.\nDo not use when: you only need one cell \u2014 use get-cell; you need the file's structure/names \u2014 use get-file-info; or you want to find a value's location \u2014 use search.",
22399
22405
  inputSchema: {
22400
22406
  path: external_exports.string().describe("Path to the .numbers file"),
22401
22407
  sheet: external_exports.string().optional().describe("Sheet name (default: first sheet)"),
@@ -22470,7 +22476,7 @@ ${lines.join("\n")}`, {
22470
22476
  server.registerTool(
22471
22477
  "export-table",
22472
22478
  {
22473
- description: "Use when: you want to write a table's data out to a CSV, TSV, or JSON file on disk. Defaults to the first sheet and first table if sheet/table are omitted.\nReturns: the exported row count, format, and output path.\nDo not use when: you want the data inline in the response rather than a file \u2014 use read-table; or you want to build a new .numbers file from a CSV/TSV/JSON source \u2014 use import-csv.",
22479
+ description: "Use when: you want to write a table's data out to a CSV, TSV, or JSON file on disk. Defaults to the first sheet and first table if sheet/table are omitted.\nReturns: the exported row count, format, and output path.\nDo not use when: you want the data inline in the response rather than a file \u2014 use read-table; or you want to build a new .numbers file from a CSV/TSV/JSON source \u2014 use import-csv.\nSafety: writes a file to disk at outputPath. The path is written unconditionally \u2014 any existing file there is OVERWRITTEN \u2014 so confirm the destination before calling.",
22474
22480
  inputSchema: {
22475
22481
  path: external_exports.string().describe("Path to the .numbers file"),
22476
22482
  format: external_exports.enum(["csv", "tsv", "json"]).describe("Output format"),
@@ -22566,7 +22572,7 @@ server.registerTool(
22566
22572
  description: `Use when: you need to write a computed value to one cell in an existing .numbers file, addressed by 0-based row and column (header is row 0). Defaults to the first sheet/table if omitted.
22567
22573
  Returns: the written coordinates, value, and the sheet/table affected.
22568
22574
  Do not use when: you're writing many cells \u2014 use set-cells-batch; replacing whole rows \u2014 use update-rows; or writing a live formula \u2014 use set-formula (passing "=SUM(...)" here writes literal text, not a formula).
22569
- Safety: modifies the .numbers file in place and OVERWRITES any existing data in the target cell.`,
22575
+ Safety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app) and OVERWRITES any existing data in the target cell.`,
22570
22576
  inputSchema: {
22571
22577
  path: external_exports.string().describe("Path to the .numbers file"),
22572
22578
  row: external_exports.number().int().min(0).max(MAX_INDEX).describe("Row index (0-based)"),
@@ -22598,7 +22604,7 @@ Safety: modifies the .numbers file in place and OVERWRITES any existing data in
22598
22604
  server.registerTool(
22599
22605
  "set-cells-batch",
22600
22606
  {
22601
- description: "Use when: you need to write computed values to multiple cells at once (each with 0-based row/col); more efficient than many set-cell calls. Defaults to the first sheet/table if omitted.\nReturns: the number of cells written and the sheet/table affected.\nDo not use when: replacing whole rows by index \u2014 use update-rows; appending new rows \u2014 use add-rows; or writing live formulas \u2014 use set-formulas-batch.\nSafety: modifies the .numbers file in place and OVERWRITES any existing data in the targeted cells.",
22607
+ description: "Use when: you need to write computed values to multiple cells at once (each with 0-based row/col); more efficient than many set-cell calls. Defaults to the first sheet/table if omitted.\nReturns: the number of cells written and the sheet/table affected.\nDo not use when: replacing whole rows by index \u2014 use update-rows; appending new rows \u2014 use add-rows; or writing live formulas \u2014 use set-formulas-batch.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app) and OVERWRITES any existing data in the targeted cells.",
22602
22608
  inputSchema: {
22603
22609
  path: external_exports.string().describe("Path to the .numbers file"),
22604
22610
  updates: external_exports.array(
@@ -22632,7 +22638,7 @@ server.registerTool(
22632
22638
  server.registerTool(
22633
22639
  "add-rows",
22634
22640
  {
22635
- description: "Use when: you want to append new rows of data to the end of an existing table; rows are added after the last existing row. Defaults to the first sheet/table if omitted.\nReturns: the number of rows added, the starting row index, and the new total row count.\nDo not use when: overwriting existing rows by index \u2014 use update-rows; setting individual cells \u2014 use set-cell / set-cells-batch.\nSafety: modifies the .numbers file in place. Additive (appends rows) and does not overwrite existing data, but still mutates the file.",
22641
+ description: "Use when: you want to append new rows of data to the end of an existing table; rows are added after the last existing row. Defaults to the first sheet/table if omitted.\nReturns: the number of rows added, the starting row index, and the new total row count.\nDo not use when: overwriting existing rows by index \u2014 use update-rows; setting individual cells \u2014 use set-cell / set-cells-batch.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app). Additive (appends rows) and does not overwrite existing data, but still mutates the file.",
22636
22642
  inputSchema: {
22637
22643
  path: external_exports.string().describe("Path to the .numbers file"),
22638
22644
  rows: external_exports.array(external_exports.array(external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]))).min(1).max(MAX_BATCH).describe(
@@ -22661,7 +22667,7 @@ server.registerTool(
22661
22667
  server.registerTool(
22662
22668
  "delete-rows",
22663
22669
  {
22664
- description: "Use when: you need to permanently remove a contiguous range of rows from a table; both startRow and endRow are 0-based inclusive indices (header is row 0). Defaults to the first sheet/table if omitted.\nReturns: the number of rows deleted and the new total row count.\nDo not use when: you only want to clear values while keeping the rows \u2014 use set-cells-batch with empty values; or overwrite rows in place \u2014 use update-rows.\nSafety: DESTRUCTIVE \u2014 requires explicit user confirmation; not undoable; the .numbers file is modified in place and the deleted rows cannot be recovered. Double-check the 0-based inclusive range before calling.",
22670
+ description: "Use when: you need to permanently remove a contiguous range of rows from a table; both startRow and endRow are 0-based inclusive indices (header is row 0). Defaults to the first sheet/table if omitted.\nReturns: the number of rows deleted and the new total row count.\nDo not use when: you only want to clear values while keeping the rows \u2014 use set-cells-batch with empty values; or overwrite rows in place \u2014 use update-rows.\nSafety: DESTRUCTIVE \u2014 requires explicit user confirmation; not undoable; the .numbers file is modified in place via the numbers-parser sidecar (does not require Numbers.app) and the deleted rows cannot be recovered. Double-check the 0-based inclusive range before calling.",
22665
22671
  inputSchema: {
22666
22672
  path: external_exports.string().describe("Path to the .numbers file"),
22667
22673
  startRow: external_exports.number().int().min(0).max(MAX_INDEX).describe("First row to delete (0-based, inclusive)"),
@@ -22688,7 +22694,7 @@ server.registerTool(
22688
22694
  server.registerTool(
22689
22695
  "add-sheet",
22690
22696
  {
22691
- description: "Use when: you want to add a new sheet to an existing .numbers file, optionally naming its default table and providing its headers.\nReturns: the new sheet name, its default table name, and the table's dimensions.\nDo not use when: adding a table to an existing sheet \u2014 use add-table; or creating a whole new file \u2014 use create-spreadsheet.\nSafety: modifies the .numbers file in place. Additive (adds a new sheet) and does not overwrite existing data, but still mutates the file.",
22697
+ description: "Use when: you want to add a new sheet to an existing .numbers file, optionally naming its default table and providing its headers. Geometry depends on headers: with headers the table is created 1 row x headers.length columns; without them it is a 12 x 8 grid of empty cells (not overridable through MCP).\nReturns: the new sheet name, its default table name, and the table's dimensions.\nDo not use when: adding a table to an existing sheet \u2014 use add-table; or creating a whole new file \u2014 use create-spreadsheet.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app). Additive (adds a new sheet) and does not overwrite existing data, but still mutates the file.",
22692
22698
  inputSchema: {
22693
22699
  path: external_exports.string().describe("Path to the .numbers file"),
22694
22700
  sheetName: external_exports.string().max(MAX_NAME_LEN).describe("Name for the new sheet"),
@@ -22714,7 +22720,7 @@ server.registerTool(
22714
22720
  server.registerTool(
22715
22721
  "add-table",
22716
22722
  {
22717
- description: "Use when: you want to add a new table to an existing sheet, optionally naming it and providing its headers. Defaults to the first sheet if omitted.\nReturns: the new table name, the sheet it was added to, and the table's dimensions.\nDo not use when: adding a whole new sheet \u2014 use add-sheet; or creating a new file \u2014 use create-spreadsheet.\nSafety: modifies the .numbers file in place. Additive (adds a new table) and does not overwrite existing data, but still mutates the file.",
22723
+ description: "Use when: you want to add a new table to an existing sheet, optionally naming it and providing its headers. Defaults to the first sheet if omitted. Geometry depends on headers: with headers the table is created 1 row x headers.length columns; without them it is a 12 x 8 grid of empty cells (not overridable through MCP).\nReturns: the new table name, the sheet it was added to, and the table's dimensions.\nDo not use when: adding a whole new sheet \u2014 use add-sheet; or creating a new file \u2014 use create-spreadsheet.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app). Additive (adds a new table) and does not overwrite existing data, but still mutates the file.",
22718
22724
  inputSchema: {
22719
22725
  path: external_exports.string().describe("Path to the .numbers file"),
22720
22726
  sheet: external_exports.string().optional().describe("Sheet name (default: first sheet)"),
@@ -22740,7 +22746,7 @@ server.registerTool(
22740
22746
  server.registerTool(
22741
22747
  "import-csv",
22742
22748
  {
22743
- description: "Use when: you want to convert an existing CSV, TSV, or JSON file into a new .numbers spreadsheet. Format is auto-detected from the input file extension unless you specify it explicitly.\nReturns: the imported row and column counts, detected format, and the input/output paths plus sheet/table names.\nDo not use when: building a file from headers/rows you already have in hand \u2014 use create-spreadsheet; or exporting a .numbers table out to CSV/TSV/JSON \u2014 use export-table.\nSafety: writes the output .numbers file via the numbers-parser sidecar (does not require Numbers.app). The output path is written unconditionally \u2014 if a file already exists there it is OVERWRITTEN in place; choose a fresh output path or confirm overwrite first.",
22749
+ description: 'Use when: you want to convert an existing CSV, TSV, or JSON file into a new .numbers spreadsheet. Format is auto-detected from the input file extension unless you specify it explicitly; any unrecognized extension falls back to CSV. CSV/TSV fields are auto-typed (numbers, booleans, empty cells), so zero-padded identifiers LOSE their leading zeros \u2014 "01234" becomes 1234. JSON values are passed through untouched, but an array of objects takes its column set from the FIRST object only, silently dropping keys that appear later.\nReturns: the imported row and column counts, detected format, and the input/output paths plus sheet/table names.\nDo not use when: building a file from headers/rows you already have in hand \u2014 use create-spreadsheet; or exporting a .numbers table out to CSV/TSV/JSON \u2014 use export-table.\nSafety: writes the output .numbers file via the numbers-parser sidecar (does not require Numbers.app). The output path is written unconditionally \u2014 if a file already exists there it is OVERWRITTEN in place; choose a fresh output path or confirm overwrite first.',
22744
22750
  inputSchema: {
22745
22751
  inputPath: external_exports.string().describe("Path to the CSV/TSV/JSON input file"),
22746
22752
  outputPath: external_exports.string().describe("Path for the output .numbers file"),
@@ -22772,7 +22778,7 @@ Sheet: "${result.sheetName}" | Table: "${result.tableName}"`,
22772
22778
  server.registerTool(
22773
22779
  "update-rows",
22774
22780
  {
22775
- description: "Use when: you need to replace whole rows by 0-based index in an existing file; each update carries a row index and a complete array of column values. Carrying multiple {row, values} entries is more efficient than per-row writes. Defaults to the first sheet/table if omitted.\nReturns: the number of rows updated and the sheet/table affected.\nDo not use when: appending new rows \u2014 use add-rows; writing individual cells \u2014 use set-cell / set-cells-batch; or writing live formulas \u2014 use set-formulas-batch.\nSafety: modifies the .numbers file in place and OVERWRITES the entire contents of each targeted row.",
22781
+ description: "Use when: you need to replace whole rows by 0-based index in an existing file; each update carries a row index and a complete array of column values. Carrying multiple {row, values} entries is more efficient than per-row writes. Defaults to the first sheet/table if omitted.\nReturns: the number of rows updated and the sheet/table affected.\nDo not use when: appending new rows \u2014 use add-rows; writing individual cells \u2014 use set-cell / set-cells-batch; or writing live formulas \u2014 use set-formulas-batch.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app) and OVERWRITES the entire contents of each targeted row.",
22776
22782
  inputSchema: {
22777
22783
  path: external_exports.string().describe("Path to the .numbers file"),
22778
22784
  updates: external_exports.array(
@@ -22852,7 +22858,7 @@ server.registerTool(
22852
22858
  server.registerTool(
22853
22859
  "set-formula",
22854
22860
  {
22855
- description: 'Use when: you need to write a live formula (e.g. "=SUM(A2:A10)") into a cell so it computes in Numbers, addressed by 0-based row/col. Requires explicit sheet and table.\nReturns: the cell, the formula set, and its computed value.\nDo not use when: writing a plain computed value \u2014 use set-cell (this tool is for live formulas); or setting many formulas \u2014 use set-formulas-batch.\nSafety: drives Numbers.app via AppleScript and requires Numbers.app to be running plus Automation permission. The file is opened if not already open, the formula OVERWRITES any existing content in the target cell, and the file is saved in place.',
22861
+ description: 'Use when: you need to write a live formula (e.g. "=SUM(A2:A10)") into a cell so it computes in Numbers, addressed by 0-based row/col. Requires explicit sheet and table.\nReturns: the cell, the formula set, and its computed value.\nDo not use when: writing a plain computed value \u2014 use set-cell (this tool is for live formulas); or setting many formulas \u2014 use set-formulas-batch.\nSafety: drives Numbers.app via AppleScript and requires Numbers.app INSTALLED (it need not already be running) plus Automation permission. The file is opened in Numbers.app if not already open, launching the app; the formula OVERWRITES any existing content in the target cell; the WHOLE document is saved, which commits any unsaved edits the user has open in it; and the document is left open afterwards.',
22856
22862
  inputSchema: {
22857
22863
  path: external_exports.string().describe("Path to the .numbers file"),
22858
22864
  sheet: external_exports.string().describe("Sheet name"),
@@ -22883,7 +22889,7 @@ Computed value: ${result.computedValue}`,
22883
22889
  server.registerTool(
22884
22890
  "set-formulas-batch",
22885
22891
  {
22886
- description: "Use when: you need to write live formulas to multiple cells at once (each with 0-based row/col); more efficient than many set-formula calls. Requires explicit sheet and table.\nReturns: the number of formulas set and the sheet/table affected.\nDo not use when: writing plain computed values \u2014 use set-cells-batch; or setting a single formula \u2014 use set-formula.\nSafety: drives Numbers.app via AppleScript and requires Numbers.app to be running plus Automation permission. Modifies the file in place and OVERWRITES any existing content in the targeted cells.",
22892
+ description: "Use when: you need to write live formulas to multiple cells at once (each with 0-based row/col); more efficient than many set-formula calls. Requires explicit sheet and table.\nReturns: the number of formulas set and the sheet/table affected.\nDo not use when: writing plain computed values \u2014 use set-cells-batch; or setting a single formula \u2014 use set-formula.\nSafety: drives Numbers.app via AppleScript and requires Numbers.app INSTALLED (it need not already be running) plus Automation permission. Opens the file in Numbers.app if it isn't open already, launching the app; OVERWRITES any existing content in the targeted cells; saves the WHOLE document, which commits any unsaved edits the user has open in it; and leaves the document open afterwards.",
22887
22893
  inputSchema: {
22888
22894
  path: external_exports.string().describe("Path to the .numbers file"),
22889
22895
  sheet: external_exports.string().describe("Sheet name"),
@@ -22941,7 +22947,7 @@ var cellStyleSchema = external_exports.object({
22941
22947
  server.registerTool(
22942
22948
  "set-cell-style",
22943
22949
  {
22944
- description: "Use when: you need to format one cell \u2014 font, text/background color, number format, alignment, text wrap \u2014 addressed by 0-based row/col. Requires explicit sheet and table.\nReturns: the styled cell and the sheet/table affected.\nDo not use when: styling many cells \u2014 use set-cells-style-batch; or writing a value/formula rather than formatting \u2014 use set-cell / set-formula.\nSafety: drives Numbers.app via AppleScript \u2014 modifies the file in place and requires Numbers.app to be running plus Automation permission.",
22950
+ description: "Use when: you need to format one cell \u2014 font, text/background color, number format, alignment, text wrap \u2014 addressed by 0-based row/col. Requires explicit sheet and table.\nReturns: the styled cell and the sheet/table affected.\nDo not use when: styling many cells \u2014 use set-cells-style-batch; or writing a value/formula rather than formatting \u2014 use set-cell / set-formula.\nSafety: drives Numbers.app via AppleScript \u2014 requires Numbers.app INSTALLED (it need not already be running) plus Automation permission. Opens the file in Numbers.app if it isn't open already, launching the app; modifies it in place; saves the WHOLE document, which commits any unsaved edits the user has open in it; and leaves the document open afterwards.",
22945
22951
  inputSchema: {
22946
22952
  path: external_exports.string().describe("Path to the .numbers file"),
22947
22953
  sheet: external_exports.string().describe("Sheet name"),
@@ -22968,7 +22974,7 @@ server.registerTool(
22968
22974
  server.registerTool(
22969
22975
  "set-cells-style-batch",
22970
22976
  {
22971
- description: "Use when: you need to format multiple cells at once (each with 0-based row/col); more efficient than many set-cell-style calls. Requires explicit sheet and table.\nReturns: the number of cells styled and the sheet/table affected.\nDo not use when: styling a single cell \u2014 use set-cell-style; or writing values/formulas rather than formatting \u2014 use set-cells-batch / set-formulas-batch.\nSafety: drives Numbers.app via AppleScript \u2014 modifies the file in place and requires Numbers.app to be running plus Automation permission.",
22977
+ description: "Use when: you need to format multiple cells at once (each with 0-based row/col); more efficient than many set-cell-style calls. Requires explicit sheet and table.\nReturns: the number of cells styled and the sheet/table affected.\nDo not use when: styling a single cell \u2014 use set-cell-style; or writing values/formulas rather than formatting \u2014 use set-cells-batch / set-formulas-batch.\nSafety: drives Numbers.app via AppleScript \u2014 requires Numbers.app INSTALLED (it need not already be running) plus Automation permission. Opens the file in Numbers.app if it isn't open already, launching the app; modifies it in place; saves the WHOLE document, which commits any unsaved edits the user has open in it; and leaves the document open afterwards.",
22972
22978
  inputSchema: {
22973
22979
  path: external_exports.string().describe("Path to the .numbers file"),
22974
22980
  sheet: external_exports.string().describe("Sheet name"),
@@ -22999,7 +23005,7 @@ server.registerTool(
22999
23005
  server.registerTool(
23000
23006
  "set-column-width",
23001
23007
  {
23002
- description: "Use when: you need to set a column's width in pixels, addressed by 0-based column index. Requires explicit sheet and table.\nReturns: the column index and the width applied.\nDo not use when: setting a row's height \u2014 use set-row-height.\nSafety: drives Numbers.app via AppleScript \u2014 modifies the file in place and requires Numbers.app to be running plus Automation permission.",
23008
+ description: "Use when: you need to set a column's width in pixels, addressed by 0-based column index. Requires explicit sheet and table.\nReturns: the column index and the width applied.\nDo not use when: setting a row's height \u2014 use set-row-height.\nSafety: drives Numbers.app via AppleScript \u2014 requires Numbers.app INSTALLED (it need not already be running) plus Automation permission. Opens the file in Numbers.app if it isn't open already, launching the app; modifies it in place; saves the WHOLE document, which commits any unsaved edits the user has open in it; and leaves the document open afterwards.",
23003
23009
  inputSchema: {
23004
23010
  path: external_exports.string().describe("Path to the .numbers file"),
23005
23011
  sheet: external_exports.string().describe("Sheet name"),
@@ -23029,7 +23035,7 @@ server.registerTool(
23029
23035
  server.registerTool(
23030
23036
  "set-row-height",
23031
23037
  {
23032
- description: "Use when: you need to set a row's height in pixels, addressed by 0-based row index. Requires explicit sheet and table.\nReturns: the row index and the height applied.\nDo not use when: setting a column's width \u2014 use set-column-width.\nSafety: drives Numbers.app via AppleScript \u2014 modifies the file in place and requires Numbers.app to be running plus Automation permission.",
23038
+ description: "Use when: you need to set a row's height in pixels, addressed by 0-based row index. Requires explicit sheet and table.\nReturns: the row index and the height applied.\nDo not use when: setting a column's width \u2014 use set-column-width.\nSafety: drives Numbers.app via AppleScript \u2014 requires Numbers.app INSTALLED (it need not already be running) plus Automation permission. Opens the file in Numbers.app if it isn't open already, launching the app; modifies it in place; saves the WHOLE document, which commits any unsaved edits the user has open in it; and leaves the document open afterwards.",
23033
23039
  inputSchema: {
23034
23040
  path: external_exports.string().describe("Path to the .numbers file"),
23035
23041
  sheet: external_exports.string().describe("Sheet name"),
@@ -23059,7 +23065,7 @@ server.registerTool(
23059
23065
  server.registerTool(
23060
23066
  "merge-cells",
23061
23067
  {
23062
- description: "Use when: you need to merge a rectangular range of cells into one, given 0-based inclusive top-left and bottom-right coordinates. Requires explicit sheet and table.\nReturns: the merged range and the sheet/table affected.\nDo not use when: undoing a merge \u2014 use unmerge-cells.\nSafety: drives Numbers.app via AppleScript \u2014 modifies the file in place and requires Numbers.app to be running plus Automation permission. Merging keeps only the top-left cell's content and can DROP the values in the other cells of the range.",
23068
+ description: "Use when: you need to merge a rectangular range of cells into one, given 0-based inclusive top-left and bottom-right coordinates. Requires explicit sheet and table.\nReturns: the merged range and the sheet/table affected.\nDo not use when: undoing a merge \u2014 use unmerge-cells.\nSafety: drives Numbers.app via AppleScript \u2014 requires Numbers.app INSTALLED (it need not already be running) plus Automation permission. Opens the file in Numbers.app if it isn't open already, launching the app; modifies it in place; saves the WHOLE document, which commits any unsaved edits the user has open in it; and leaves the document open afterwards. Merging keeps only the top-left cell's content and can DROP the values in the other cells of the range.",
23063
23069
  inputSchema: {
23064
23070
  path: external_exports.string().describe("Path to the .numbers file"),
23065
23071
  sheet: external_exports.string().describe("Sheet name"),
@@ -23086,7 +23092,7 @@ server.registerTool(
23086
23092
  server.registerTool(
23087
23093
  "unmerge-cells",
23088
23094
  {
23089
- description: "Use when: you need to split a previously merged range back into individual cells, given 0-based inclusive top-left and bottom-right coordinates. Requires explicit sheet and table.\nReturns: the unmerged range and the sheet/table affected.\nDo not use when: creating a merge \u2014 use merge-cells.\nSafety: drives Numbers.app via AppleScript \u2014 modifies the file in place and requires Numbers.app to be running plus Automation permission. Previously merged cells stay empty (the original non-top-left values are not restored).",
23095
+ description: "Use when: you need to split a previously merged range back into individual cells, given 0-based inclusive top-left and bottom-right coordinates. Requires explicit sheet and table.\nReturns: the unmerged range and the sheet/table affected.\nDo not use when: creating a merge \u2014 use merge-cells.\nSafety: drives Numbers.app via AppleScript \u2014 requires Numbers.app INSTALLED (it need not already be running) plus Automation permission. Opens the file in Numbers.app if it isn't open already, launching the app; modifies it in place; saves the WHOLE document, which commits any unsaved edits the user has open in it; and leaves the document open afterwards. Previously merged cells stay empty (the original non-top-left values are not restored).",
23090
23096
  inputSchema: {
23091
23097
  path: external_exports.string().describe("Path to the .numbers file"),
23092
23098
  sheet: external_exports.string().describe("Sheet name"),
@@ -1,12 +1,15 @@
1
- # Automation Permission for Write & Format Operations
1
+ # Automation Permission for Formula & Formatting Operations
2
2
 
3
3
  Apple Numbers MCP has **two distinct backends**, and only one of them needs a
4
- special macOS permission:
5
-
6
- - **Reads** go through [numbers-parser](https://pypi.org/project/numbers-parser/)
7
- (Python). They open the `.numbers` file **directly** off disk and need **no**
8
- special permission — just normal file access.
9
- - **Writes and formatting** go through **AppleScript driving Numbers.app**.
4
+ special macOS permission. The split is **values vs. formatting**, not read vs.
5
+ write:
6
+
7
+ - **Reads and value/structure writes** go through
8
+ [numbers-parser](https://pypi.org/project/numbers-parser/) (Python). They open
9
+ the `.numbers` file **directly** off disk and need **no** special permission —
10
+ just normal file access. No Apple event is ever sent, so Numbers.app doesn't
11
+ even have to be installed.
12
+ - **Formulas and formatting** go through **AppleScript driving Numbers.app**.
10
13
  numbers-parser can't write styles or formulas, so the server scripts Numbers.app
11
14
  to do it. macOS gates one app controlling another behind the **Automation**
12
15
  permission, so these tools require it.
@@ -16,51 +19,50 @@ how to grant or reset it, what failure looks like, and how to verify.
16
19
 
17
20
  ## Which tools need Automation permission
18
21
 
19
- **Need it** (AppleScript → Numbers.app — writes, formulas, formatting, structure
20
- changes, CSV import, file creation):
22
+ **Need it** (AppleScript → Numbers.app — formulas, styles, dimensions, merges):
21
23
 
22
- - `set-cell`, `set-cells-batch`
23
- - `add-rows`, `update-rows`, `delete-rows`
24
- - `add-sheet`, `add-table`, `rename-sheet`, `rename-table`
25
24
  - `set-formula`, `set-formulas-batch`
26
25
  - `set-cell-style`, `set-cells-style-batch`
27
26
  - `set-column-width`, `set-row-height`
28
27
  - `merge-cells`, `unmerge-cells`
29
- - `create-spreadsheet`
30
- - `import-csv`
31
28
 
32
- **Do NOT need it** (pure numbers-parser, file read directly off disk):
29
+ **Do NOT need it** (pure numbers-parser, the file opened directly off disk):
33
30
 
34
- - `get-file-info`
35
- - `read-table`
36
- - `get-cell`
37
- - `search`
38
- - `export-table`
31
+ - Reads — `get-file-info`, `read-table`, `get-cell`, `search`, `export-table`
32
+ - Value writes — `set-cell`, `set-cells-batch`, `add-rows`, `update-rows`,
33
+ `delete-rows`
34
+ - Structure writes — `add-sheet`, `add-table`, `rename-sheet`, `rename-table`
35
+ - File creation — `create-spreadsheet`, `import-csv`
39
36
  - `health-check` / `doctor` (these only probe the environment)
40
37
 
41
- So you can fully inspect, search, and export a spreadsheet with no Automation
42
- permission at all. The permission only matters the moment you try to **change or
43
- format** a file.
38
+ So you can inspect, search, export **and edit** a spreadsheet — cell values, whole
39
+ rows, sheets and tables, new files from CSV — with no Automation permission at all,
40
+ and with Numbers.app not installed. The permission only matters the moment you
41
+ write a **live formula** or change **formatting** (font, color, number format,
42
+ alignment, column width, row height, merges).
44
43
 
45
- > **Numbers.app must be installed.** The write/format path scripts Numbers.app, so
46
- > the app has to be present at `/Applications/Numbers.app` (or
47
- > `/System/Applications/Numbers.app`). Reads don't need it. If Numbers.app is
48
- > missing, `doctor` reports `numbers_app: warn` and the write tools fail.
44
+ > **Numbers.app must be installed for the formula/format tools.** Only that path
45
+ > scripts Numbers.app, so the app has to be present at `/Applications/Numbers.app`
46
+ > (or `/System/Applications/Numbers.app`). Reads and the numbers-parser writes
47
+ > don't need it. If Numbers.app is missing, `doctor` reports `numbers_app: warn`
48
+ > and only the formula/format tools fail.
49
49
 
50
50
  ## How the permission prompt appears
51
51
 
52
- The **first time** a write/format tool runs, macOS shows a one-time dialog:
52
+ The **first time** a formula or formatting tool runs, macOS shows a one-time
53
+ dialog:
53
54
 
54
55
  > **"<Host App>" wants access to control "Numbers". Allowing control will provide
55
56
  > access to documents and data in "Numbers", and to perform actions within that
56
57
  > app.**
57
58
 
58
- Click **OK / Allow**. The grant is remembered, so subsequent writes don't prompt
59
+ Click **OK / Allow**. The grant is remembered, so subsequent calls don't prompt
59
60
  again. The "host app" is whatever process launched the MCP server — Claude
60
61
  Desktop, Terminal, iTerm, or VS Code — **not** `node` and **not** Numbers.app.
61
62
 
62
- If the host app runs headless or the dialog is dismissed/denied, the write fails
63
- (see below) and you must grant the permission manually.
63
+ If the host app runs headless or the dialog is dismissed/denied, the
64
+ formula/format call fails (see below) and you must grant the permission manually.
65
+ Value and structure writes are unaffected — they never send an Apple event.
64
66
 
65
67
  ## How to grant it manually
66
68
 
@@ -85,7 +87,7 @@ If the host app runs headless or the dialog is dismissed/denied, the write fails
85
87
  ## What failure looks like
86
88
 
87
89
  When the host app lacks Automation permission for Numbers, the AppleScript layer
88
- fails and the write/format tool returns an error containing:
90
+ fails and the formula/format tool returns an error containing:
89
91
 
90
92
  ```
91
93
  Not authorized to send Apple events to Numbers.
@@ -95,9 +97,10 @@ Not authorized to send Apple events to Numbers.
95
97
  `"Numbers got an error: ..."` style messages if Numbers.app itself can't be
96
98
  driven. In all of these cases the underlying `.numbers` file is **not** modified.
97
99
 
98
- If you instead see **"Numbers.app not running"** or a message that Numbers.app
99
- can't be found, the problem is that the app isn't installed/openable — open
100
- Numbers.app once and retry.
100
+ If you instead see an error saying Numbers can't be found or isn't running, the
101
+ problem is that the app isn't installed/openable — install Numbers.app and retry.
102
+ (The tools do **not** require Numbers.app to already be running: the AppleScript
103
+ layer launches it and opens the file itself. It does have to be *installed*.)
101
104
 
102
105
  ## Resetting the permission
103
106
 
@@ -117,8 +120,10 @@ e.g.:
117
120
  tccutil reset AppleEvents com.apple.Terminal
118
121
  ```
119
122
 
120
- After resetting, **fully quit and reopen the host app**, then run any write tool
121
- (e.g. `set-cell`) and click **OK** when the dialog appears.
123
+ After resetting, **fully quit and reopen the host app**, then run any
124
+ formula/format tool (e.g. `set-cell-style`) and click **OK** when the dialog
125
+ appears. A value write such as `set-cell` will *not* re-trigger the prompt — it
126
+ never crosses the AppleScript boundary.
122
127
 
123
128
  ## Verifying
124
129
 
@@ -128,20 +133,26 @@ as `ok` / `warn` / `fail`:
128
133
  - **`python_interpreter`** — the resolved Python's path and version (warns when
129
134
  older than 3.11; stock macOS ships 3.9).
130
135
  - **`numbers_parser`** — is the Python read sidecar installed (powers all reads).
131
- - **`numbers_app`** — is Numbers.app present (required for any write/format tool).
132
- - **`automation_permission`** — an informational reminder that write tools need
133
- Automation permission, granted on first use.
136
+ - **`numbers_app`** — is Numbers.app present (required for the formula/format
137
+ tools; everything else works without it).
138
+ - **`automation_permission`** — an informational reminder that the formula/format
139
+ tools need Automation permission, granted on first use.
134
140
 
135
141
  > **Note:** Automation permission can't be probed without actually trying to
136
142
  > control Numbers (that would itself trigger the prompt), so `doctor` reports it
137
143
  > **informationally** rather than testing it live. The definitive test is to run a
138
- > real write — e.g. `set-cell` on a scratch file. If it succeeds, the permission
139
- > is in place; if it returns *"Not authorized to send Apple events to Numbers,"*
140
- > grant or reset it as above.
144
+ > tool that actually crosses the AppleScript boundary — e.g. `set-cell-style` (or
145
+ > `set-formula`) on a scratch file. If it succeeds, the permission is in place; if
146
+ > it returns *"Not authorized to send Apple events to Numbers,"* grant or reset it
147
+ > as above.
148
+ >
149
+ > **Don't test with `set-cell`** (or any other value/structure write). Those run
150
+ > on the numbers-parser sidecar and succeed whether or not the grant exists, so
151
+ > they always "pass" and tell you nothing about the permission.
141
152
 
142
153
  ## See also
143
154
 
144
- - [docs/LIMITATIONS.md](./LIMITATIONS.md) — the full read-vs-write split and other
155
+ - [docs/LIMITATIONS.md](./LIMITATIONS.md) — the full backend split and other
145
156
  limits.
146
157
  - [Known Limitations](../README.md#known-limitations) and
147
158
  [Security and Privacy](../README.md#security-and-privacy) in the README.
@@ -1,32 +1,38 @@
1
1
  # Limitations
2
2
 
3
3
  Apple Numbers MCP is a **hybrid** bridge to `.numbers` spreadsheets: it **reads**
4
- them offline with [numbers-parser](https://pypi.org/project/numbers-parser/)
5
- (Python, no app required), and **writes/formats** them by scripting **Numbers.app**
6
- via AppleScript (because numbers-parser can't write styles or formulas). That split
7
- is the source of most of the limitations below.
4
+ them *and writes their values and structure* offline with
5
+ [numbers-parser](https://pypi.org/project/numbers-parser/) (Python, no app
6
+ required), and **formats** them — styles, live formulas, column/row dimensions,
7
+ merges — by scripting **Numbers.app** via AppleScript (because numbers-parser
8
+ can't write those). That split is the source of most of the limitations below.
9
+ Note it is a **values-vs-formatting** split, not a read-vs-write one.
8
10
 
9
11
  This page documents the real limitations — what the server can't do and why — so
10
12
  they aren't re-investigated every release. These agree with the README's
11
13
  [Known Limitations](../README.md#known-limitations); this page adds the *why* and
12
14
  *what to do* for each.
13
15
 
14
- ## Writes & formatting require Numbers.app + Automation permission (reads don't)
15
-
16
- **Why:** numbers-parser can read a `.numbers` file directly off disk, but it does
17
- **not** write styles, formulas, or cell-dimension changes reliably. To do those,
18
- the server scripts **Numbers.app** through AppleScript. That means every
19
- write/format tool needs (a) **Numbers.app installed** and (b) the host app to have
20
- **Automation** permission to control Numbers. Read tools (`get-file-info`,
21
- `read-table`, `get-cell`, `search`, `export-table`) use only numbers-parser and
22
- need **neither** — they work on macOS or Linux with no Automation grant.
23
-
24
- **What to do:** For inspection/search/export, nothing special is needed. For any
25
- write — `set-cell(s)`, `add/update/delete-rows`, `add-sheet`/`add-table`,
26
- `rename-*`, `set-formula(s)`, `set-*-style`, `set-column-width`/`set-row-height`,
27
- `merge`/`unmerge`, `create-spreadsheet`, `import-csv` — install Numbers.app and
28
- grant Automation permission. Full steps in
29
- [AUTOMATION-PERMISSION.md](./AUTOMATION-PERMISSION.md). Verify with `doctor`.
16
+ ## Formulas & formatting require Numbers.app + Automation permission (nothing else does)
17
+
18
+ **Why:** numbers-parser can read a `.numbers` file directly off disk *and* write
19
+ values, rows, sheets and tables into it — but it does **not** write styles,
20
+ formulas, or cell-dimension changes reliably. To do those, the server scripts
21
+ **Numbers.app** through AppleScript. That means the eight formula/format tools
22
+ need (a) **Numbers.app installed** and (b) the host app to have **Automation**
23
+ permission to control Numbers. Every other tool — the reads (`get-file-info`,
24
+ `read-table`, `get-cell`, `search`, `export-table`) *and* the value/structure
25
+ writes (`set-cell(s)`, `add/update/delete-rows`, `add-sheet`/`add-table`,
26
+ `rename-*`, `create-spreadsheet`, `import-csv`) — uses only numbers-parser and
27
+ needs **neither**: no Numbers.app, no Automation grant, no Apple event.
28
+
29
+ **What to do:** For inspection/search/export, and for ordinary value and structure
30
+ edits, nothing special is needed. Only for `set-formula(s)`, `set-*-style`,
31
+ `set-column-width`/`set-row-height` and `merge`/`unmerge` do you have to install
32
+ Numbers.app and grant Automation permission. Full steps in
33
+ [AUTOMATION-PERMISSION.md](./AUTOMATION-PERMISSION.md). Verify with `doctor` — and
34
+ test the grant with a *formula/format* tool, since a `set-cell` succeeds without
35
+ it and so proves nothing.
30
36
 
31
37
  ## Formulas and styling are AppleScript-only
32
38
 
@@ -37,9 +43,11 @@ numbers-parser doesn't expose reliable writes for these. They go exclusively
37
43
  through Numbers.app via AppleScript, so they inherit the macOS-only + Automation
38
44
  requirements above and can't run on Linux.
39
45
 
40
- **What to do:** Run formula/format tools on macOS with Numbers.app. If you only
41
- have the Python path available (e.g. Linux), you can still read formulas and
42
- formatted values (via `get-cell verbose: true`) — you just can't set them.
46
+ **What to do:** Run formula/format tools on macOS with Numbers.app installed.
47
+ Where only the Python path is available, you can still *read* formulas and
48
+ formatted values (via `get-cell verbose: true`) — you just can't set them. Note
49
+ the npm package declares `os: ["darwin"]`, so Linux isn't a supported install
50
+ target even for that read-only subset (see "macOS only" below).
43
51
 
44
52
  ## Values vs. formulas
45
53
 
@@ -50,6 +58,40 @@ value writes the literal string, not a live formula.
50
58
  **What to do:** Use `set-formula` / `set-formulas-batch` to write actual formulas
51
59
  (these require Numbers.app). Use the value tools for plain data.
52
60
 
61
+ ## `import-csv` auto-types CSV/TSV fields, so leading zeros are lost
62
+
63
+ **Why:** For a `csv` or `tsv` source, every field is passed through the sidecar's
64
+ `_auto_convert()` before it is written: `""` becomes an empty cell, `true`/`false`
65
+ become booleans, and anything Python's `int()` or `float()` accepts becomes a
66
+ number. That is deliberately *less* conservative than the value-write tools
67
+ (`set-cell`, `set-cells-batch`, `add-rows`, `update-rows`), which reject
68
+ leading-zero integers so identifier-like strings keep their exact text. So a zip
69
+ code `01234` imports as the number `1234`, `007` imports as `7`, and a part number
70
+ `1e5` imports as the number `100000`. There is no per-column type control on import, and the
71
+ conversion isn't reversible once the `.numbers` file is written.
72
+
73
+ A `json` source does **not** do this — those values go straight through, so a JSON
74
+ string `"01234"` survives intact.
75
+
76
+ **What to do:** For CSV/TSV columns holding zero-padded identifiers, import from
77
+ JSON instead, or repair the affected cells after import with `set-cells-batch`
78
+ using `type: "string"`. Spot-check with `get-cell` after importing anything
79
+ identifier-like.
80
+
81
+ ## `import-csv` takes its JSON columns from the first object only
82
+
83
+ **Why:** For an array-of-objects JSON source the column set is the keys of the
84
+ **first** object. A key that appears only in a later object is dropped silently,
85
+ and objects missing an early key get blank cells. An array-of-arrays source has no
86
+ header names at all: columns are named `Column_0`, `Column_1`, … and the first row
87
+ is kept as data. Separately, when `format` is `auto`, any extension that isn't
88
+ `.csv`, `.tsv` or `.json` falls back to **CSV** rather than erroring.
89
+
90
+ **What to do:** Normalize a heterogeneous JSON export to a common key set before
91
+ importing, and pass `format` explicitly for inputs whose extension isn't one of the
92
+ three recognized ones. The tool's response reports the format it actually used and
93
+ the column count it produced — check both when the input is unusual.
94
+
53
95
  ## No charts, images, or conditional formatting
54
96
 
55
97
  **Why:** numbers-parser doesn't expose charts, embedded images, or
@@ -105,24 +147,41 @@ upgrade numbers-parser first. Check the installed version with `health-check` /
105
147
 
106
148
  ## Concurrent edits while Numbers.app has the file open
107
149
 
108
- **Why:** Two backends can touch the same file. The read path (numbers-parser) reads
109
- whatever is on disk, and the write path drives Numbers.app. If Numbers.app has the
110
- file **open with unsaved changes**, what numbers-parser reads off disk can be
111
- stale, and a write through AppleScript targets the app's in-memory copy — the two
112
- can disagree, and saves can race.
150
+ **Why:** Two backends can touch the same file. The numbers-parser path (reads and
151
+ value/structure writes) sees whatever is on disk; the formula/format path drives
152
+ Numbers.app. If Numbers.app has the file **open with unsaved changes**, what
153
+ numbers-parser reads off disk can be stale, and an AppleScript call targets the
154
+ app's in-memory copy — the two can disagree, and saves can race.
155
+
156
+ Three side effects of the AppleScript path matter here, because they apply to
157
+ **every** formula/format tool:
158
+
159
+ 1. **It opens the file itself**, launching Numbers.app if it isn't running. These
160
+ tools don't require Numbers.app to already be running — only *installed* — so a
161
+ single `set-column-width` on an idle Mac can pop a window and, the first time,
162
+ the Automation prompt.
163
+ 2. **It saves the whole document, not just the change.** If you have the workbook
164
+ open in Numbers with unsaved hand edits, one formula/format call commits *all*
165
+ of them to disk.
166
+ 3. **It leaves the document open** afterwards — nothing ever closes it — so every
167
+ later numbers-parser read or write on that file races the app's in-memory copy,
168
+ which is exactly the hazard this section describes.
113
169
 
114
170
  **What to do:** Prefer to **close the file in Numbers.app** before reading or doing
115
- bulk writes through the server, so disk and app agree. If you must keep it open,
171
+ bulk writes through the server, so disk and app agree — and close it again after a
172
+ run of formula/format calls, which will have reopened it. If you must keep it open,
116
173
  read back with `read-table` / `get-cell` after writes to confirm the result landed,
117
174
  and avoid editing the same cells by hand in Numbers.app at the same time.
118
175
 
119
176
  ## Concurrent writes to the same file are last-writer-wins
120
177
 
121
178
  **Why:** Each write tool opens the `.numbers` file, applies its change to an
122
- in-memory document, and saves the **whole document** back. The save itself is
123
- **atomic** — every mutating command writes to a sibling temp file and `os.replace()`s
124
- it onto the target — so an interrupted or crashed write can never leave a torn or
125
- half-written file. But atomicity protects against *corruption*, **not** against
179
+ in-memory document, and saves the **whole document** back. On the numbers-parser
180
+ path the save is **atomic** — every mutating sidecar command writes to a sibling
181
+ temp file and `os.replace()`s it onto the target — so an interrupted or crashed
182
+ write can never leave a torn or half-written file. (The formula/format tools save
183
+ through Numbers.app instead, so that guarantee is the app's, not this server's.)
184
+ But atomicity protects against *corruption*, **not** against
126
185
  *lost updates*: if two writes to the same file overlap (two agents, two server
127
186
  instances, or a server write racing a hand edit in Numbers.app), each loaded the
128
187
  document independently, and whichever saves **last** overwrites the other's change
@@ -135,11 +194,33 @@ related edits into a single call (`set-cells-batch`, `update-rows`,
135
194
  starting the next on the same file, and after a burst of writes read back with
136
195
  `read-table` / `get-cell` to confirm the final state is what you intended.
137
196
 
138
- ## macOS-only for writes; reads are cross-platform
139
-
140
- **Why:** Reads run anywhere numbers-parser runs (macOS or Linux). Writes/formatting
141
- script Numbers.app, which only exists on macOS.
142
-
143
- **What to do:** Run write/format workloads on a Mac with Numbers.app installed and
144
- Automation permission granted. See
197
+ ## Per-call timeouts are fixed (30 s sidecar, 60 s AppleScript)
198
+
199
+ **Why:** Every numbers-parser sidecar call runs under a **30-second** process
200
+ timeout, and every AppleScript call under **60 seconds** (the generated script also
201
+ carries an in-app `with timeout` block set 5 s lower, so Numbers.app aborts cleanly
202
+ before the process is killed). Neither is configurable — there is no
203
+ `APPLE_NUMBERS_MCP_*` variable for either one.
204
+ `APPLE_NUMBERS_MCP_SETUP_TIMEOUT` governs only the one-time venv bootstrap, and
205
+ `APPLE_NUMBERS_MCP_MAX_BUFFER` caps stdout size, not wall-clock time. A very large
206
+ workbook, or a wedged Numbers.app, surfaces as `Operation timed out after
207
+ 30000ms. File may be very large.`
208
+
209
+ **What to do:** Narrow the request rather than hunting for a knob: read in slices
210
+ with `read-table`'s `startRow`/`endRow` and `columns`, split large
211
+ `set-cells-batch` / `update-rows` calls into smaller batches, and close the file in
212
+ Numbers.app so the AppleScript layer isn't waiting on the app.
213
+
214
+ ## macOS only (the package declares `os: ["darwin"]`)
215
+
216
+ **Why:** The published npm package declares `"os": ["darwin"]`, so `npm`/`pnpm`
217
+ refuse to install it on any other platform (`EBADPLATFORM`). The numbers-parser
218
+ path — reads plus value/structure writes — is platform-independent in principle
219
+ (nothing in the sidecar branches on the platform), but the formula/format tools
220
+ script Numbers.app, which exists only on macOS, so the package as a whole is
221
+ pinned to darwin deliberately.
222
+
223
+ **What to do:** Run this server on a Mac. Add Numbers.app plus Automation
224
+ permission only if you need formulas or formatting; reads and value/structure
225
+ writes need neither. See
145
226
  [AUTOMATION-PERMISSION.md](./AUTOMATION-PERMISSION.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-numbers-mcp",
3
- "version": "1.1.9",
3
+ "version": "1.1.11",
4
4
  "description": "MCP server for Apple Numbers - read, write, search, and format .numbers spreadsheets via Claude and other AI assistants",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -87,6 +87,6 @@
87
87
  "test:integration": "vitest run --config vitest.integration.config.ts",
88
88
  "test:all": "vitest run && vitest run --config vitest.integration.config.ts",
89
89
  "test:watch": "vitest",
90
- "version": "node scripts/sync-plugin-version.mjs && git add .claude-plugin .agents/plugins codex .hermes-plugin .antigravity-plugin"
90
+ "version": "node scripts/sync-plugin-version.mjs && git add .claude-plugin .agents/plugins codex .antigravity-plugin"
91
91
  }
92
92
  }