apple-numbers-mcp 1.1.19 → 1.2.1
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 +4 -2
- package/build/index.js +58 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -675,6 +675,7 @@ All configuration is optional — the server works out of the box.
|
|
|
675
675
|
| `APPLE_NUMBERS_MCP_MAX_BUFFER` | 50 MB (Python reader) / 64 MB (AppleScript) | Max bytes captured from a subprocess's stdout, applied to both the Python reader and the AppleScript layer. Raise it if a very large spreadsheet is truncated; lower it to cap memory. |
|
|
676
676
|
| `APPLE_NUMBERS_MCP_CONFIG_FILE` | `~/Library/Application Support/apple-numbers-mcp/config.json` | Path to the JSON config file (see below). |
|
|
677
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`). |
|
|
678
|
+
| `APPLE_NUMBERS_MCP_EXTRA_ROOTS` | unset | Colon-separated **absolute** directories to add to the allowed-path roots, e.g. `/Data/Finance:/srv/shared`. The built-in roots already cover your home directory, the temp dirs and `/Volumes` (where external and network mounts appear), so this is for genuinely unusual layouts rather than routine configuration. Entries are canonicalized like the built-ins, so a symlinked entry cannot smuggle in a wider parent; relative and empty entries are ignored. **Widening a security boundary — set it deliberately.** |
|
|
678
679
|
| `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. |
|
|
679
680
|
|
|
680
681
|
> **Per-call timeouts are not configurable.** Every `numbers-parser` sidecar call
|
|
@@ -775,9 +776,10 @@ These are tracked for future releases. The underlying `numbers-parser` library h
|
|
|
775
776
|
- Ensure the file extension is `.numbers`.
|
|
776
777
|
|
|
777
778
|
### "Output path … is outside the allowed roots" / "Input path … is outside the allowed roots"
|
|
778
|
-
-
|
|
779
|
+
- Every tool that touches a file on disk is bounded to your **home directory**, `/tmp`, `/private/tmp`, and `/Volumes` — the server will not write into `/etc`, `/Library`, an app bundle, or any other system location, and will not read from one.
|
|
780
|
+
- **As of 1.2.0 this covers the `.numbers` file itself**, not just export/import destinations. `get-file-info`, `read-table`, `search` and the in-place writes (`set-cell`, `add-rows`, `set-formula`, …) previously accepted a path anywhere on disk.
|
|
779
781
|
- Symlinks are resolved **before** the check, so a link inside an allowed directory that points outside it is rejected too; the error reports the resolved path, which is usually the quickest way to see where a path really went.
|
|
780
|
-
- Fix: choose a path under one of those roots (`~/Documents/report.
|
|
782
|
+
- Fix: choose a path under one of those roots (`~/Documents/report.numbers`, `/tmp/report.csv`). If your spreadsheets genuinely live somewhere else, add that directory to `APPLE_NUMBERS_MCP_EXTRA_ROOTS` — see [Environment variables](#environment-variables).
|
|
781
783
|
|
|
782
784
|
### Formatting / formula tools fail with "Numbers.app not running" or "Not authorized to send Apple events to Numbers"
|
|
783
785
|
- Open Numbers.app at least once. macOS will prompt for automation permission — accept it.
|
package/build/index.js
CHANGED
|
@@ -21813,8 +21813,7 @@ function unmergeCells(filePath, sheet, table, startRow, startCol, endRow, endCol
|
|
|
21813
21813
|
|
|
21814
21814
|
// src/services/numbersManager.ts
|
|
21815
21815
|
import { existsSync as existsSync3 } from "node:fs";
|
|
21816
|
-
import {
|
|
21817
|
-
import { homedir as homedir2 } from "node:os";
|
|
21816
|
+
import { extname } from "node:path";
|
|
21818
21817
|
|
|
21819
21818
|
// src/utils/exportPath.ts
|
|
21820
21819
|
import { lstatSync, readlinkSync, realpathSync } from "node:fs";
|
|
@@ -21833,12 +21832,18 @@ var ALLOWED_EXPORT_ROOTS = [
|
|
|
21833
21832
|
"/Volumes"
|
|
21834
21833
|
];
|
|
21835
21834
|
var ALLOWED_EXPORT_ROOTS_TEXT = "your home directory, /tmp, /private/tmp, or /Volumes";
|
|
21835
|
+
var EXTRA_ROOTS_ENV = "APPLE_NUMBERS_MCP_EXTRA_ROOTS";
|
|
21836
|
+
function extraRoots() {
|
|
21837
|
+
const raw = process.env[EXTRA_ROOTS_ENV];
|
|
21838
|
+
if (!raw) return [];
|
|
21839
|
+
return raw.split(":").map((s) => s.trim()).filter((s) => s.length > 0 && isAbsolute(s));
|
|
21840
|
+
}
|
|
21836
21841
|
function canonicalize(path) {
|
|
21837
21842
|
return realpathSync.native(path);
|
|
21838
21843
|
}
|
|
21839
21844
|
function allowedRoots() {
|
|
21840
21845
|
const roots = [];
|
|
21841
|
-
for (const root of ALLOWED_EXPORT_ROOTS) {
|
|
21846
|
+
for (const root of [...ALLOWED_EXPORT_ROOTS, ...extraRoots()]) {
|
|
21842
21847
|
if (!roots.includes(root)) roots.push(root);
|
|
21843
21848
|
try {
|
|
21844
21849
|
const canonical = canonicalize(root);
|
|
@@ -21912,9 +21917,32 @@ function resolveWithinAllowedRoots(path, label) {
|
|
|
21912
21917
|
|
|
21913
21918
|
// src/services/numbersManager.ts
|
|
21914
21919
|
var NumbersManager = class {
|
|
21920
|
+
/**
|
|
21921
|
+
* Resolve and validate the path of an EXISTING `.numbers` file.
|
|
21922
|
+
*
|
|
21923
|
+
* This is the resolver behind `get-file-info`, `read-table`, `search` and
|
|
21924
|
+
* every in-place write (`set-cell`, `add-rows`, `set-formula`, …). It used to
|
|
21925
|
+
* expand `~`, `resolve()` and check the extension — and nothing else — so
|
|
21926
|
+
* those tools could read and modify a `.numbers` file **anywhere on disk**,
|
|
21927
|
+
* while its sibling `validateOutputPath` had enforced the allowed-roots
|
|
21928
|
+
* boundary since 1.1.19. That asymmetry was the largest remaining gap here.
|
|
21929
|
+
*
|
|
21930
|
+
* It now goes through the same `resolveWithinAllowedRoots` as the write side,
|
|
21931
|
+
* which also brings the hardening that came with it: canonicalization via
|
|
21932
|
+
* `realpathSync.native` (so a case-respelled segment cannot defeat the check
|
|
21933
|
+
* on case-insensitive APFS) and symlink resolution BEFORE the comparison (so
|
|
21934
|
+
* a link inside an allowed directory cannot point out of it).
|
|
21935
|
+
*
|
|
21936
|
+
* The extension is checked on the CANONICAL path for the same reason it is on
|
|
21937
|
+
* the output side: a symlink must not be able to present a `.numbers` name
|
|
21938
|
+
* for a target that is something else entirely.
|
|
21939
|
+
*
|
|
21940
|
+
* The built-in roots cover home, the temp dirs and `/Volumes`, so a file the
|
|
21941
|
+
* user could plausibly want to open is almost always already inside them.
|
|
21942
|
+
* `APPLE_NUMBERS_MCP_EXTRA_ROOTS` exists for the layouts they are not.
|
|
21943
|
+
*/
|
|
21915
21944
|
validatePath(filePath) {
|
|
21916
|
-
const
|
|
21917
|
-
const resolved = resolve2(expanded);
|
|
21945
|
+
const resolved = resolveWithinAllowedRoots(filePath, "Input path");
|
|
21918
21946
|
if (!existsSync3(resolved)) {
|
|
21919
21947
|
throw new Error(`File not found: ${resolved}`);
|
|
21920
21948
|
}
|
|
@@ -22444,11 +22472,11 @@ First use get-file-info and read-table to read the relevant table(s) so you know
|
|
|
22444
22472
|
// src/services/fileConfig.ts
|
|
22445
22473
|
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
|
|
22446
22474
|
import { join as join3 } from "node:path";
|
|
22447
|
-
import { homedir as
|
|
22475
|
+
import { homedir as homedir2 } from "node:os";
|
|
22448
22476
|
function fileConfigPath(env = process.env) {
|
|
22449
22477
|
const override = env.APPLE_NUMBERS_MCP_CONFIG_FILE;
|
|
22450
22478
|
if (override && override.trim()) return override.trim();
|
|
22451
|
-
return join3(
|
|
22479
|
+
return join3(homedir2(), "Library", "Application Support", "apple-numbers-mcp", "config.json");
|
|
22452
22480
|
}
|
|
22453
22481
|
function loadFileConfig(env = process.env, path = fileConfigPath(env)) {
|
|
22454
22482
|
const applied = [];
|
|
@@ -22595,6 +22623,8 @@ var MAX_NAME_LEN = 1024;
|
|
|
22595
22623
|
var MAX_QUERY_LEN = 1e4;
|
|
22596
22624
|
var MAX_HEADER_LEN = 1024;
|
|
22597
22625
|
var headerCellSchema = external_exports.string().max(MAX_HEADER_LEN);
|
|
22626
|
+
var PATH_BOUNDARY_NOTE = `
|
|
22627
|
+
Paths: the file must resolve \u2014 after ~ expansion and symlink resolution \u2014 to a location under ${ALLOWED_EXPORT_ROOTS_TEXT}; anything else is rejected with an error naming those roots. Add directories with the ${EXTRA_ROOTS_ENV} environment variable.`;
|
|
22598
22628
|
var server = new McpServer({
|
|
22599
22629
|
name: "apple-numbers",
|
|
22600
22630
|
version: version2,
|
|
@@ -22649,7 +22679,7 @@ registerTool(
|
|
|
22649
22679
|
registerTool(
|
|
22650
22680
|
"get-file-info",
|
|
22651
22681
|
{
|
|
22652
|
-
description: "Use when: you need to discover the structure of a .numbers file before reading or writing \u2014 the exact sheet and table names, their dimensions, and header rows. Call this first when sheet/table names are unknown.\nReturns: each sheet with its tables, per-table row\xD7col counts, and the header row (row 0) cells.\nDo not use when: you want the actual cell data \u2014 use read-table; or a single cell \u2014 use get-cell.",
|
|
22682
|
+
description: "Use when: you need to discover the structure of a .numbers file before reading or writing \u2014 the exact sheet and table names, their dimensions, and header rows. Call this first when sheet/table names are unknown.\nReturns: each sheet with its tables, per-table row\xD7col counts, and the header row (row 0) cells.\nDo not use when: you want the actual cell data \u2014 use read-table; or a single cell \u2014 use get-cell." + PATH_BOUNDARY_NOTE,
|
|
22653
22683
|
inputSchema: {
|
|
22654
22684
|
path: external_exports.string().describe("Absolute or ~-relative path to the .numbers file")
|
|
22655
22685
|
},
|
|
@@ -22693,7 +22723,7 @@ ${summary}`,
|
|
|
22693
22723
|
registerTool(
|
|
22694
22724
|
"read-table",
|
|
22695
22725
|
{
|
|
22696
|
-
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.",
|
|
22726
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
22697
22727
|
inputSchema: {
|
|
22698
22728
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
22699
22729
|
sheet: external_exports.string().optional().describe("Sheet name (default: first sheet)"),
|
|
@@ -22729,7 +22759,7 @@ ${dataRows}`,
|
|
|
22729
22759
|
registerTool(
|
|
22730
22760
|
"search",
|
|
22731
22761
|
{
|
|
22732
|
-
description: "Use when: you need to locate where a text value appears across every cell in a .numbers file (case-insensitive partial match), optionally limited to one sheet.\nReturns: a match count and each match's sheet, table, row, column header, and value.\nDo not use when: you already know the cell's coordinates \u2014 use get-cell; or you want a whole table's contents \u2014 use read-table.",
|
|
22762
|
+
description: "Use when: you need to locate where a text value appears across every cell in a .numbers file (case-insensitive partial match), optionally limited to one sheet.\nReturns: a match count and each match's sheet, table, row, column header, and value.\nDo not use when: you already know the cell's coordinates \u2014 use get-cell; or you want a whole table's contents \u2014 use read-table." + PATH_BOUNDARY_NOTE,
|
|
22733
22763
|
inputSchema: {
|
|
22734
22764
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
22735
22765
|
query: external_exports.string().max(MAX_QUERY_LEN).describe("Text to search for (case-insensitive)"),
|
|
@@ -22795,7 +22825,7 @@ registerTool(
|
|
|
22795
22825
|
registerTool(
|
|
22796
22826
|
"get-cell",
|
|
22797
22827
|
{
|
|
22798
|
-
description: "Use when: you need a single cell's value by 0-based row and column index (header is row 0). Set verbose=true to also get the formula, formatted value, and merge info.\nReturns: the cell's value and type, plus formula/formatted-value/merge details when verbose.\nDo not use when: you want many cells or whole rows \u2014 use read-table; or you don't know the coordinates \u2014 use search.",
|
|
22828
|
+
description: "Use when: you need a single cell's value by 0-based row and column index (header is row 0). Set verbose=true to also get the formula, formatted value, and merge info.\nReturns: the cell's value and type, plus formula/formatted-value/merge details when verbose.\nDo not use when: you want many cells or whole rows \u2014 use read-table; or you don't know the coordinates \u2014 use search." + PATH_BOUNDARY_NOTE,
|
|
22799
22829
|
inputSchema: {
|
|
22800
22830
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
22801
22831
|
sheet: external_exports.string().describe("Sheet name"),
|
|
@@ -22864,7 +22894,7 @@ registerTool(
|
|
|
22864
22894
|
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.
|
|
22865
22895
|
Returns: the written coordinates, value, and the sheet/table affected.
|
|
22866
22896
|
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).
|
|
22867
|
-
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
|
|
22897
|
+
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.` + PATH_BOUNDARY_NOTE,
|
|
22868
22898
|
inputSchema: {
|
|
22869
22899
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
22870
22900
|
row: external_exports.number().int().min(0).max(MAX_INDEX).describe("Row index (0-based)"),
|
|
@@ -22896,7 +22926,7 @@ Safety: modifies the .numbers file in place via the numbers-parser sidecar (does
|
|
|
22896
22926
|
registerTool(
|
|
22897
22927
|
"set-cells-batch",
|
|
22898
22928
|
{
|
|
22899
|
-
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.",
|
|
22929
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
22900
22930
|
inputSchema: {
|
|
22901
22931
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
22902
22932
|
updates: external_exports.array(
|
|
@@ -22930,7 +22960,7 @@ registerTool(
|
|
|
22930
22960
|
registerTool(
|
|
22931
22961
|
"add-rows",
|
|
22932
22962
|
{
|
|
22933
|
-
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.",
|
|
22963
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
22934
22964
|
inputSchema: {
|
|
22935
22965
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
22936
22966
|
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(
|
|
@@ -22959,7 +22989,7 @@ registerTool(
|
|
|
22959
22989
|
registerTool(
|
|
22960
22990
|
"delete-rows",
|
|
22961
22991
|
{
|
|
22962
|
-
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.",
|
|
22992
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
22963
22993
|
inputSchema: {
|
|
22964
22994
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
22965
22995
|
startRow: external_exports.number().int().min(0).max(MAX_INDEX).describe("First row to delete (0-based, inclusive)"),
|
|
@@ -22986,7 +23016,7 @@ registerTool(
|
|
|
22986
23016
|
registerTool(
|
|
22987
23017
|
"add-sheet",
|
|
22988
23018
|
{
|
|
22989
|
-
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.",
|
|
23019
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
22990
23020
|
inputSchema: {
|
|
22991
23021
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
22992
23022
|
sheetName: external_exports.string().max(MAX_NAME_LEN).describe("Name for the new sheet"),
|
|
@@ -23012,7 +23042,7 @@ registerTool(
|
|
|
23012
23042
|
registerTool(
|
|
23013
23043
|
"add-table",
|
|
23014
23044
|
{
|
|
23015
|
-
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.",
|
|
23045
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
23016
23046
|
inputSchema: {
|
|
23017
23047
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23018
23048
|
sheet: external_exports.string().optional().describe("Sheet name (default: first sheet)"),
|
|
@@ -23070,7 +23100,7 @@ Sheet: "${result.sheetName}" | Table: "${result.tableName}"`,
|
|
|
23070
23100
|
registerTool(
|
|
23071
23101
|
"update-rows",
|
|
23072
23102
|
{
|
|
23073
|
-
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.",
|
|
23103
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
23074
23104
|
inputSchema: {
|
|
23075
23105
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23076
23106
|
updates: external_exports.array(
|
|
@@ -23102,7 +23132,7 @@ registerTool(
|
|
|
23102
23132
|
registerTool(
|
|
23103
23133
|
"rename-sheet",
|
|
23104
23134
|
{
|
|
23105
|
-
description: "Use when: you want to change a sheet's name in a .numbers file. Defaults to the first sheet if the current name is omitted.\nReturns: the old and new sheet names.\nDo not use when: renaming a table \u2014 use rename-table.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app).",
|
|
23135
|
+
description: "Use when: you want to change a sheet's name in a .numbers file. Defaults to the first sheet if the current name is omitted.\nReturns: the old and new sheet names.\nDo not use when: renaming a table \u2014 use rename-table.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app)." + PATH_BOUNDARY_NOTE,
|
|
23106
23136
|
inputSchema: {
|
|
23107
23137
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23108
23138
|
newName: external_exports.string().max(MAX_NAME_LEN).describe("New name for the sheet"),
|
|
@@ -23125,7 +23155,7 @@ registerTool(
|
|
|
23125
23155
|
registerTool(
|
|
23126
23156
|
"rename-table",
|
|
23127
23157
|
{
|
|
23128
|
-
description: "Use when: you want to change a table's name in a .numbers file. Defaults to the first sheet/table if omitted.\nReturns: the old and new table names and the sheet it belongs to.\nDo not use when: renaming a sheet \u2014 use rename-sheet.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app).",
|
|
23158
|
+
description: "Use when: you want to change a table's name in a .numbers file. Defaults to the first sheet/table if omitted.\nReturns: the old and new table names and the sheet it belongs to.\nDo not use when: renaming a sheet \u2014 use rename-sheet.\nSafety: modifies the .numbers file in place via the numbers-parser sidecar (does not require Numbers.app)." + PATH_BOUNDARY_NOTE,
|
|
23129
23159
|
inputSchema: {
|
|
23130
23160
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23131
23161
|
newName: external_exports.string().max(MAX_NAME_LEN).describe("New name for the table"),
|
|
@@ -23150,7 +23180,7 @@ registerTool(
|
|
|
23150
23180
|
registerTool(
|
|
23151
23181
|
"set-formula",
|
|
23152
23182
|
{
|
|
23153
|
-
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.',
|
|
23183
|
+
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.' + PATH_BOUNDARY_NOTE,
|
|
23154
23184
|
inputSchema: {
|
|
23155
23185
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23156
23186
|
sheet: external_exports.string().describe("Sheet name"),
|
|
@@ -23181,7 +23211,7 @@ Computed value: ${result.computedValue}`,
|
|
|
23181
23211
|
registerTool(
|
|
23182
23212
|
"set-formulas-batch",
|
|
23183
23213
|
{
|
|
23184
|
-
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.",
|
|
23214
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
23185
23215
|
inputSchema: {
|
|
23186
23216
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23187
23217
|
sheet: external_exports.string().describe("Sheet name"),
|
|
@@ -23239,7 +23269,7 @@ var cellStyleSchema = external_exports.object({
|
|
|
23239
23269
|
registerTool(
|
|
23240
23270
|
"set-cell-style",
|
|
23241
23271
|
{
|
|
23242
|
-
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.",
|
|
23272
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
23243
23273
|
inputSchema: {
|
|
23244
23274
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23245
23275
|
sheet: external_exports.string().describe("Sheet name"),
|
|
@@ -23266,7 +23296,7 @@ registerTool(
|
|
|
23266
23296
|
registerTool(
|
|
23267
23297
|
"set-cells-style-batch",
|
|
23268
23298
|
{
|
|
23269
|
-
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.",
|
|
23299
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
23270
23300
|
inputSchema: {
|
|
23271
23301
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23272
23302
|
sheet: external_exports.string().describe("Sheet name"),
|
|
@@ -23297,7 +23327,7 @@ registerTool(
|
|
|
23297
23327
|
registerTool(
|
|
23298
23328
|
"set-column-width",
|
|
23299
23329
|
{
|
|
23300
|
-
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.",
|
|
23330
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
23301
23331
|
inputSchema: {
|
|
23302
23332
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23303
23333
|
sheet: external_exports.string().describe("Sheet name"),
|
|
@@ -23327,7 +23357,7 @@ registerTool(
|
|
|
23327
23357
|
registerTool(
|
|
23328
23358
|
"set-row-height",
|
|
23329
23359
|
{
|
|
23330
|
-
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.",
|
|
23360
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
23331
23361
|
inputSchema: {
|
|
23332
23362
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23333
23363
|
sheet: external_exports.string().describe("Sheet name"),
|
|
@@ -23357,7 +23387,7 @@ registerTool(
|
|
|
23357
23387
|
registerTool(
|
|
23358
23388
|
"merge-cells",
|
|
23359
23389
|
{
|
|
23360
|
-
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.",
|
|
23390
|
+
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." + PATH_BOUNDARY_NOTE,
|
|
23361
23391
|
inputSchema: {
|
|
23362
23392
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23363
23393
|
sheet: external_exports.string().describe("Sheet name"),
|
|
@@ -23384,7 +23414,7 @@ registerTool(
|
|
|
23384
23414
|
registerTool(
|
|
23385
23415
|
"unmerge-cells",
|
|
23386
23416
|
{
|
|
23387
|
-
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).",
|
|
23417
|
+
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)." + PATH_BOUNDARY_NOTE,
|
|
23388
23418
|
inputSchema: {
|
|
23389
23419
|
path: external_exports.string().describe("Path to the .numbers file"),
|
|
23390
23420
|
sheet: external_exports.string().describe("Sheet name"),
|
package/package.json
CHANGED