numbersmcp 0.1.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/dist/index.d.ts +2 -0
- package/dist/index.js +95 -0
- package/dist/tools/addRow.d.ts +7 -0
- package/dist/tools/addRow.js +31 -0
- package/dist/tools/getFormula.d.ts +7 -0
- package/dist/tools/getFormula.js +25 -0
- package/dist/tools/listSheets.d.ts +1 -0
- package/dist/tools/listSheets.js +19 -0
- package/dist/tools/listSpreadsheets.d.ts +1 -0
- package/dist/tools/listSpreadsheets.js +17 -0
- package/dist/tools/readRange.d.ts +11 -0
- package/dist/tools/readRange.js +60 -0
- package/dist/tools/readTable.d.ts +1 -0
- package/dist/tools/readTable.js +56 -0
- package/dist/tools/setFormula.d.ts +8 -0
- package/dist/tools/setFormula.js +20 -0
- package/dist/tools/writeCell.d.ts +8 -0
- package/dist/tools/writeCell.js +20 -0
- package/dist/tools/writeRange.d.ts +8 -0
- package/dist/tools/writeRange.js +34 -0
- package/package.json +18 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { listSpreadsheets } from "./tools/listSpreadsheets.js";
|
|
6
|
+
import { listSheets } from "./tools/listSheets.js";
|
|
7
|
+
import { readRange } from "./tools/readRange.js";
|
|
8
|
+
import { writeCell } from "./tools/writeCell.js";
|
|
9
|
+
import { writeRange } from "./tools/writeRange.js";
|
|
10
|
+
import { addRow } from "./tools/addRow.js";
|
|
11
|
+
import { readTable } from "./tools/readTable.js";
|
|
12
|
+
import { getFormula } from "./tools/getFormula.js";
|
|
13
|
+
import { setFormula } from "./tools/setFormula.js";
|
|
14
|
+
const server = new McpServer({
|
|
15
|
+
name: "numbersmcp",
|
|
16
|
+
version: "0.1.0",
|
|
17
|
+
});
|
|
18
|
+
server.tool("list-spreadsheets", "List all open Numbers documents", {}, async () => {
|
|
19
|
+
const docs = await listSpreadsheets();
|
|
20
|
+
return { content: [{ type: "text", text: JSON.stringify(docs, null, 2) }] };
|
|
21
|
+
});
|
|
22
|
+
server.tool("list-sheets", "List all sheets and tables in an open Numbers document", {
|
|
23
|
+
document: z.string().describe("Name of the open Numbers document"),
|
|
24
|
+
}, async ({ document }) => {
|
|
25
|
+
const sheets = await listSheets(document);
|
|
26
|
+
return { content: [{ type: "text", text: JSON.stringify(sheets, null, 2) }] };
|
|
27
|
+
});
|
|
28
|
+
server.tool("read-range", "Read cell values from a range in a Numbers table (e.g. 'A1:C10')", {
|
|
29
|
+
document: z.string().describe("Name of the open Numbers document"),
|
|
30
|
+
range: z.string().describe("Cell range in A1 notation, e.g. 'A1:C10'"),
|
|
31
|
+
sheet: z.string().optional().describe("Sheet name (defaults to first sheet)"),
|
|
32
|
+
table: z.string().optional().describe("Table name (defaults to first table)"),
|
|
33
|
+
}, async ({ document, range, sheet, table }) => {
|
|
34
|
+
const rows = await readRange(document, range, sheet, table);
|
|
35
|
+
return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
|
|
36
|
+
});
|
|
37
|
+
server.tool("write-cell", "Write a value to a specific cell in a Numbers table", {
|
|
38
|
+
document: z.string().describe("Name of the open Numbers document"),
|
|
39
|
+
cell: z.string().describe("Cell reference in A1 notation, e.g. 'B3'"),
|
|
40
|
+
value: z.string().describe("Value to write to the cell"),
|
|
41
|
+
sheet: z.string().optional().describe("Sheet name (defaults to first sheet)"),
|
|
42
|
+
table: z.string().optional().describe("Table name (defaults to first table)"),
|
|
43
|
+
}, async ({ document, cell, value, sheet, table }) => {
|
|
44
|
+
const result = await writeCell(document, cell, value, sheet, table);
|
|
45
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
46
|
+
});
|
|
47
|
+
server.tool("write-range", "Write multiple values to a range starting at a cell in a Numbers table", {
|
|
48
|
+
document: z.string().describe("Name of the open Numbers document"),
|
|
49
|
+
startCell: z.string().describe("Top-left cell reference in A1 notation, e.g. 'A1'"),
|
|
50
|
+
values: z.array(z.array(z.string())).describe("2D array of string values to write (rows × columns)"),
|
|
51
|
+
sheet: z.string().optional().describe("Sheet name (defaults to first sheet)"),
|
|
52
|
+
table: z.string().optional().describe("Table name (defaults to first table)"),
|
|
53
|
+
}, async ({ document, startCell, values, sheet, table }) => {
|
|
54
|
+
const result = await writeRange(document, startCell, values, sheet, table);
|
|
55
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
56
|
+
});
|
|
57
|
+
server.tool("add-row", "Add a new row at the bottom of a Numbers table", {
|
|
58
|
+
document: z.string().describe("Name of the open Numbers document"),
|
|
59
|
+
values: z.array(z.string()).describe("Array of string values for each cell in the new row"),
|
|
60
|
+
sheet: z.string().optional().describe("Sheet name (defaults to first sheet)"),
|
|
61
|
+
table: z.string().optional().describe("Table name (defaults to first table)"),
|
|
62
|
+
}, async ({ document, values, sheet, table }) => {
|
|
63
|
+
const result = await addRow(document, values, sheet, table);
|
|
64
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
65
|
+
});
|
|
66
|
+
server.tool("read-table", "Read an entire Numbers table as structured data", {
|
|
67
|
+
document: z.string().describe("Name of the open Numbers document"),
|
|
68
|
+
sheet: z.string().optional().describe("Sheet name (defaults to first sheet)"),
|
|
69
|
+
table: z.string().optional().describe("Table name (defaults to first table)"),
|
|
70
|
+
headerRow: z.boolean().optional().default(true).describe("If true, first row is used as keys and rows are returned as objects"),
|
|
71
|
+
}, async ({ document, sheet, table, headerRow }) => {
|
|
72
|
+
const data = await readTable(document, sheet, table, headerRow);
|
|
73
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
74
|
+
});
|
|
75
|
+
server.tool("get-formula", "Get the formula from a specific cell in a Numbers table", {
|
|
76
|
+
document: z.string().describe("Name of the open Numbers document"),
|
|
77
|
+
cell: z.string().describe("Cell reference in A1 notation, e.g. 'A1'"),
|
|
78
|
+
sheet: z.string().optional().describe("Sheet name (defaults to first sheet)"),
|
|
79
|
+
table: z.string().optional().describe("Table name (defaults to first table)"),
|
|
80
|
+
}, async ({ document, cell, sheet, table }) => {
|
|
81
|
+
const result = await getFormula(document, cell, sheet, table);
|
|
82
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
83
|
+
});
|
|
84
|
+
server.tool("set-formula", "Set a formula on a specific cell in a Numbers table (prefix with '=' e.g. '=SUM(B1:B10)')", {
|
|
85
|
+
document: z.string().describe("Name of the open Numbers document"),
|
|
86
|
+
cell: z.string().describe("Cell reference in A1 notation, e.g. 'A1'"),
|
|
87
|
+
formula: z.string().describe("Formula string starting with '=', e.g. '=SUM(B1:B10)'"),
|
|
88
|
+
sheet: z.string().optional().describe("Sheet name (defaults to first sheet)"),
|
|
89
|
+
table: z.string().optional().describe("Table name (defaults to first table)"),
|
|
90
|
+
}, async ({ document, cell, formula, sheet, table }) => {
|
|
91
|
+
const result = await setFormula(document, cell, formula, sheet, table);
|
|
92
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
93
|
+
});
|
|
94
|
+
const transport = new StdioServerTransport();
|
|
95
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function addRow(document, values, sheet, table) {
|
|
3
|
+
const docEsc = escapeForAppleScript(document);
|
|
4
|
+
const tableRef = table
|
|
5
|
+
? `table "${escapeForAppleScript(table)}"`
|
|
6
|
+
: "table 1";
|
|
7
|
+
const sheetRef = sheet
|
|
8
|
+
? `sheet "${escapeForAppleScript(sheet)}"`
|
|
9
|
+
: "sheet 1";
|
|
10
|
+
const setCmds = values.map((v, i) => {
|
|
11
|
+
const valEsc = escapeForAppleScript(v);
|
|
12
|
+
return `set value of cell ${i + 1} of row newRowIndex to "${valEsc}"`;
|
|
13
|
+
});
|
|
14
|
+
const script = `
|
|
15
|
+
tell application "Numbers"
|
|
16
|
+
tell ${tableRef} of ${sheetRef} of document "${docEsc}"
|
|
17
|
+
set rowCount to row count
|
|
18
|
+
add row below row rowCount
|
|
19
|
+
set newRowIndex to rowCount + 1
|
|
20
|
+
${setCmds.join("\n ")}
|
|
21
|
+
end tell
|
|
22
|
+
end tell`;
|
|
23
|
+
await runAppleScript(script);
|
|
24
|
+
return {
|
|
25
|
+
success: true,
|
|
26
|
+
document,
|
|
27
|
+
sheet: sheet ?? "sheet 1",
|
|
28
|
+
table: table ?? "table 1",
|
|
29
|
+
valuesWritten: values.length,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function getFormula(document, cell, sheet, table) {
|
|
3
|
+
const docEsc = escapeForAppleScript(document);
|
|
4
|
+
const cellEsc = escapeForAppleScript(cell);
|
|
5
|
+
const tableRef = table
|
|
6
|
+
? `table "${escapeForAppleScript(table)}"`
|
|
7
|
+
: "table 1";
|
|
8
|
+
const sheetRef = sheet
|
|
9
|
+
? `sheet "${escapeForAppleScript(sheet)}"`
|
|
10
|
+
: "sheet 1";
|
|
11
|
+
const script = `
|
|
12
|
+
tell application "Numbers"
|
|
13
|
+
tell ${tableRef} of ${sheetRef} of document "${docEsc}"
|
|
14
|
+
set theCell to cell "${cellEsc}"
|
|
15
|
+
set theFormula to ""
|
|
16
|
+
try
|
|
17
|
+
set theFormula to formula of theCell
|
|
18
|
+
if theFormula is missing value then set theFormula to ""
|
|
19
|
+
end try
|
|
20
|
+
return theFormula
|
|
21
|
+
end tell
|
|
22
|
+
end tell`;
|
|
23
|
+
const raw = await runAppleScript(script);
|
|
24
|
+
return { document, sheet: sheet ?? "sheet 1", table: table ?? "table 1", cell, formula: raw };
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function listSheets(document: string): Promise<Record<string, string>[]>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript, parseRecords } from "@mailappmcp/shared";
|
|
2
|
+
export async function listSheets(document) {
|
|
3
|
+
const docEsc = escapeForAppleScript(document);
|
|
4
|
+
const script = `
|
|
5
|
+
tell application "Numbers"
|
|
6
|
+
tell document "${docEsc}"
|
|
7
|
+
set output to ""
|
|
8
|
+
repeat with s in every sheet
|
|
9
|
+
set sheetName to name of s
|
|
10
|
+
repeat with t in every table of s
|
|
11
|
+
set output to output & sheetName & "|||" & (name of t) & "|||" & (row count of t) & "|||" & (column count of t) & "~~~"
|
|
12
|
+
end repeat
|
|
13
|
+
end repeat
|
|
14
|
+
end tell
|
|
15
|
+
return output
|
|
16
|
+
end tell`;
|
|
17
|
+
const raw = await runAppleScript(script);
|
|
18
|
+
return parseRecords(raw, ["sheet", "table", "rowCount", "columnCount"]);
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function listSpreadsheets(): Promise<Record<string, string>[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { runAppleScript, parseRecords } from "@mailappmcp/shared";
|
|
2
|
+
export async function listSpreadsheets() {
|
|
3
|
+
const script = `
|
|
4
|
+
tell application "Numbers"
|
|
5
|
+
set output to ""
|
|
6
|
+
repeat with doc in every document
|
|
7
|
+
set docPath to ""
|
|
8
|
+
try
|
|
9
|
+
set docPath to path of doc
|
|
10
|
+
end try
|
|
11
|
+
set output to output & (name of doc) & "|||" & docPath & "~~~"
|
|
12
|
+
end repeat
|
|
13
|
+
return output
|
|
14
|
+
end tell`;
|
|
15
|
+
const raw = await runAppleScript(script);
|
|
16
|
+
return parseRecords(raw, ["name", "path"]);
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function parseCellRef(ref: string): {
|
|
2
|
+
row: number;
|
|
3
|
+
col: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function parseRange(range: string): {
|
|
6
|
+
startRow: number;
|
|
7
|
+
startCol: number;
|
|
8
|
+
endRow: number;
|
|
9
|
+
endCol: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function readRange(document: string, range: string, sheet?: string, table?: string): Promise<string[][]>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
function colLetterToNum(col) {
|
|
3
|
+
let n = 0;
|
|
4
|
+
for (const ch of col.toUpperCase()) {
|
|
5
|
+
n = n * 26 + (ch.charCodeAt(0) - 64);
|
|
6
|
+
}
|
|
7
|
+
return n;
|
|
8
|
+
}
|
|
9
|
+
export function parseCellRef(ref) {
|
|
10
|
+
const match = ref.toUpperCase().match(/^([A-Z]+)(\d+)$/);
|
|
11
|
+
if (!match)
|
|
12
|
+
throw new Error(`Invalid cell reference: ${ref}`);
|
|
13
|
+
return { col: colLetterToNum(match[1]), row: parseInt(match[2], 10) };
|
|
14
|
+
}
|
|
15
|
+
export function parseRange(range) {
|
|
16
|
+
const parts = range.split(":");
|
|
17
|
+
if (parts.length !== 2)
|
|
18
|
+
throw new Error(`Invalid range: ${range}`);
|
|
19
|
+
const start = parseCellRef(parts[0]);
|
|
20
|
+
const end = parseCellRef(parts[1]);
|
|
21
|
+
return { startRow: start.row, startCol: start.col, endRow: end.row, endCol: end.col };
|
|
22
|
+
}
|
|
23
|
+
export async function readRange(document, range, sheet, table) {
|
|
24
|
+
const { startRow, startCol, endRow, endCol } = parseRange(range);
|
|
25
|
+
const docEsc = escapeForAppleScript(document);
|
|
26
|
+
const tableRef = table
|
|
27
|
+
? `table "${escapeForAppleScript(table)}"`
|
|
28
|
+
: "table 1";
|
|
29
|
+
const sheetRef = sheet
|
|
30
|
+
? `sheet "${escapeForAppleScript(sheet)}"`
|
|
31
|
+
: "sheet 1";
|
|
32
|
+
const script = `
|
|
33
|
+
tell application "Numbers"
|
|
34
|
+
tell ${tableRef} of ${sheetRef} of document "${docEsc}"
|
|
35
|
+
set output to ""
|
|
36
|
+
repeat with r from ${startRow} to ${endRow}
|
|
37
|
+
repeat with c from ${startCol} to ${endCol}
|
|
38
|
+
set cellVal to value of cell c of row r
|
|
39
|
+
if cellVal is missing value then
|
|
40
|
+
set cellVal to ""
|
|
41
|
+
end if
|
|
42
|
+
set output to output & (cellVal as text) & "|||"
|
|
43
|
+
end repeat
|
|
44
|
+
set output to output & "~~~"
|
|
45
|
+
end repeat
|
|
46
|
+
end tell
|
|
47
|
+
return output
|
|
48
|
+
end tell`;
|
|
49
|
+
const raw = await runAppleScript(script);
|
|
50
|
+
if (!raw)
|
|
51
|
+
return [];
|
|
52
|
+
const rows = raw
|
|
53
|
+
.split("~~~")
|
|
54
|
+
.filter((r) => r.trim())
|
|
55
|
+
.map((row) => row
|
|
56
|
+
.split("|||")
|
|
57
|
+
.slice(0, endCol - startCol + 1)
|
|
58
|
+
.map((v) => v.trim()));
|
|
59
|
+
return rows;
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function readTable(document: string, sheet?: string, table?: string, headerRow?: boolean): Promise<Record<string, string>[] | string[][]>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function readTable(document, sheet, table, headerRow = true) {
|
|
3
|
+
const docEsc = escapeForAppleScript(document);
|
|
4
|
+
const tableRef = table
|
|
5
|
+
? `table "${escapeForAppleScript(table)}"`
|
|
6
|
+
: "table 1";
|
|
7
|
+
const sheetRef = sheet
|
|
8
|
+
? `sheet "${escapeForAppleScript(sheet)}"`
|
|
9
|
+
: "sheet 1";
|
|
10
|
+
const script = `
|
|
11
|
+
tell application "Numbers"
|
|
12
|
+
tell ${tableRef} of ${sheetRef} of document "${docEsc}"
|
|
13
|
+
set numRows to row count
|
|
14
|
+
set numCols to column count
|
|
15
|
+
set output to ""
|
|
16
|
+
repeat with r from 1 to numRows
|
|
17
|
+
repeat with c from 1 to numCols
|
|
18
|
+
set cellVal to value of cell c of row r
|
|
19
|
+
if cellVal is missing value then
|
|
20
|
+
set cellVal to ""
|
|
21
|
+
end if
|
|
22
|
+
set output to output & (cellVal as text) & "|||"
|
|
23
|
+
end repeat
|
|
24
|
+
set output to output & "~~~"
|
|
25
|
+
end repeat
|
|
26
|
+
return output
|
|
27
|
+
end tell
|
|
28
|
+
end tell`;
|
|
29
|
+
const raw = await runAppleScript(script);
|
|
30
|
+
if (!raw)
|
|
31
|
+
return headerRow ? [] : [];
|
|
32
|
+
const rows = raw
|
|
33
|
+
.split("~~~")
|
|
34
|
+
.filter((r) => r.trim())
|
|
35
|
+
.map((row) => {
|
|
36
|
+
const cells = row.split("|||");
|
|
37
|
+
// Remove trailing empty cell from the trailing ||| before ~~~
|
|
38
|
+
if (cells[cells.length - 1] === "" || cells[cells.length - 1].trim() === "") {
|
|
39
|
+
cells.pop();
|
|
40
|
+
}
|
|
41
|
+
return cells.map((v) => v.trim());
|
|
42
|
+
});
|
|
43
|
+
if (!headerRow) {
|
|
44
|
+
return rows;
|
|
45
|
+
}
|
|
46
|
+
if (rows.length === 0)
|
|
47
|
+
return [];
|
|
48
|
+
const headers = rows[0];
|
|
49
|
+
return rows.slice(1).map((row) => {
|
|
50
|
+
const obj = {};
|
|
51
|
+
headers.forEach((h, i) => {
|
|
52
|
+
obj[h] = row[i] ?? "";
|
|
53
|
+
});
|
|
54
|
+
return obj;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function setFormula(document, cell, formula, sheet, table) {
|
|
3
|
+
const docEsc = escapeForAppleScript(document);
|
|
4
|
+
const cellEsc = escapeForAppleScript(cell);
|
|
5
|
+
const formulaEsc = escapeForAppleScript(formula);
|
|
6
|
+
const tableRef = table
|
|
7
|
+
? `table "${escapeForAppleScript(table)}"`
|
|
8
|
+
: "table 1";
|
|
9
|
+
const sheetRef = sheet
|
|
10
|
+
? `sheet "${escapeForAppleScript(sheet)}"`
|
|
11
|
+
: "sheet 1";
|
|
12
|
+
const script = `
|
|
13
|
+
tell application "Numbers"
|
|
14
|
+
tell ${tableRef} of ${sheetRef} of document "${docEsc}"
|
|
15
|
+
set value of cell "${cellEsc}" to "${formulaEsc}"
|
|
16
|
+
end tell
|
|
17
|
+
end tell`;
|
|
18
|
+
await runAppleScript(script);
|
|
19
|
+
return { success: true, document, sheet: sheet ?? "sheet 1", table: table ?? "table 1", cell, formula };
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function writeCell(document, cell, value, sheet, table) {
|
|
3
|
+
const docEsc = escapeForAppleScript(document);
|
|
4
|
+
const cellEsc = escapeForAppleScript(cell);
|
|
5
|
+
const valueEsc = escapeForAppleScript(value);
|
|
6
|
+
const tableRef = table
|
|
7
|
+
? `table "${escapeForAppleScript(table)}"`
|
|
8
|
+
: "table 1";
|
|
9
|
+
const sheetRef = sheet
|
|
10
|
+
? `sheet "${escapeForAppleScript(sheet)}"`
|
|
11
|
+
: "sheet 1";
|
|
12
|
+
const script = `
|
|
13
|
+
tell application "Numbers"
|
|
14
|
+
tell ${tableRef} of ${sheetRef} of document "${docEsc}"
|
|
15
|
+
set value of cell "${cellEsc}" to "${valueEsc}"
|
|
16
|
+
end tell
|
|
17
|
+
end tell`;
|
|
18
|
+
await runAppleScript(script);
|
|
19
|
+
return { success: true, document, sheet: sheet ?? "sheet 1", table: table ?? "table 1", cell, value };
|
|
20
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
import { parseCellRef } from "./readRange.js";
|
|
3
|
+
export async function writeRange(document, startCell, values, sheet, table) {
|
|
4
|
+
const docEsc = escapeForAppleScript(document);
|
|
5
|
+
const { row: startRow, col: startCol } = parseCellRef(startCell);
|
|
6
|
+
const tableRef = table
|
|
7
|
+
? `table "${escapeForAppleScript(table)}"`
|
|
8
|
+
: "table 1";
|
|
9
|
+
const sheetRef = sheet
|
|
10
|
+
? `sheet "${escapeForAppleScript(sheet)}"`
|
|
11
|
+
: "sheet 1";
|
|
12
|
+
const setCmds = [];
|
|
13
|
+
for (let r = 0; r < values.length; r++) {
|
|
14
|
+
for (let c = 0; c < values[r].length; c++) {
|
|
15
|
+
const valEsc = escapeForAppleScript(values[r][c]);
|
|
16
|
+
setCmds.push(`set value of cell ${startCol + c} of row ${startRow + r} to "${valEsc}"`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const script = `
|
|
20
|
+
tell application "Numbers"
|
|
21
|
+
tell ${tableRef} of ${sheetRef} of document "${docEsc}"
|
|
22
|
+
${setCmds.join("\n ")}
|
|
23
|
+
end tell
|
|
24
|
+
end tell`;
|
|
25
|
+
await runAppleScript(script);
|
|
26
|
+
return {
|
|
27
|
+
success: true,
|
|
28
|
+
document,
|
|
29
|
+
sheet: sheet ?? "sheet 1",
|
|
30
|
+
table: table ?? "table 1",
|
|
31
|
+
startCell,
|
|
32
|
+
rowsWritten: values.length,
|
|
33
|
+
};
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "numbersmcp",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "MCP server for macOS Numbers.app — spreadsheet access via AppleScript",
|
|
5
|
+
"mcpName": "io.github.aernouddekker/numbersmcp",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": { "numbersmcp": "./dist/index.js" },
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"scripts": { "build": "tsc", "start": "node dist/index.js", "prepublishOnly": "npm run build" },
|
|
10
|
+
"keywords": ["mcp", "mcp-server", "numbers", "spreadsheet", "macos", "applescript", "claude"],
|
|
11
|
+
"author": "Aernoud Dekker",
|
|
12
|
+
"repository": { "type": "git", "url": "https://github.com/aernouddekker/macos-mcp.git", "directory": "packages/numbers" },
|
|
13
|
+
"dependencies": { "@mailappmcp/shared": "*", "@modelcontextprotocol/sdk": "^1.12.0", "zod": "^3.23.0" },
|
|
14
|
+
"devDependencies": { "typescript": "^5.5.0", "@types/node": "^22.0.0" },
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"engines": { "node": ">=18" },
|
|
17
|
+
"os": ["darwin"]
|
|
18
|
+
}
|