apple-numbers-mcp 0.5.0
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/LICENSE +21 -0
- package/README.md +709 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +483 -0
- package/build/index.js.map +1 -0
- package/build/services/fileConfig.d.ts +7 -0
- package/build/services/fileConfig.d.ts.map +1 -0
- package/build/services/fileConfig.js +52 -0
- package/build/services/fileConfig.js.map +1 -0
- package/build/services/numbersManager.d.ts +175 -0
- package/build/services/numbersManager.d.ts.map +1 -0
- package/build/services/numbersManager.js +357 -0
- package/build/services/numbersManager.js.map +1 -0
- package/build/tools/doctor.d.ts +22 -0
- package/build/tools/doctor.d.ts.map +1 -0
- package/build/tools/doctor.js +96 -0
- package/build/tools/doctor.js.map +1 -0
- package/build/tools/resourcesAndPrompts.d.ts +15 -0
- package/build/tools/resourcesAndPrompts.d.ts.map +1 -0
- package/build/tools/resourcesAndPrompts.js +77 -0
- package/build/tools/resourcesAndPrompts.js.map +1 -0
- package/build/tools/respond.d.ts +29 -0
- package/build/tools/respond.d.ts.map +1 -0
- package/build/tools/respond.js +44 -0
- package/build/tools/respond.js.map +1 -0
- package/build/types.d.ts +128 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/applescript.d.ts +90 -0
- package/build/utils/applescript.d.ts.map +1 -0
- package/build/utils/applescript.js +248 -0
- package/build/utils/applescript.js.map +1 -0
- package/build/utils/python.d.ts +12 -0
- package/build/utils/python.d.ts.map +1 -0
- package/build/utils/python.js +101 -0
- package/build/utils/python.js.map +1 -0
- package/package.json +91 -0
- package/requirements.txt +1 -0
- package/scripts/setup.sh +46 -0
- package/src/utils/numbers_reader.py +779 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Output cap for osascript. Node's execFileSync defaults to ~1 MB, which a large
|
|
4
|
+
* table read (getCellStyle round-trips, big batch operations) can blow past —
|
|
5
|
+
* execFileSync then throws ENOBUFS and the failure surfaces with no useful detail.
|
|
6
|
+
* 64 MB headroom, overridable via APPLE_NUMBERS_MCP_MAX_BUFFER.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
9
|
+
function getMaxBuffer() {
|
|
10
|
+
const raw = process.env.APPLE_NUMBERS_MCP_MAX_BUFFER;
|
|
11
|
+
if (raw !== undefined) {
|
|
12
|
+
const n = Number(raw);
|
|
13
|
+
if (Number.isFinite(n) && n > 0)
|
|
14
|
+
return n;
|
|
15
|
+
}
|
|
16
|
+
return DEFAULT_MAX_BUFFER_BYTES;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Headroom (ms) between the in-AppleScript `with timeout` and the outer osascript
|
|
20
|
+
* process timeout. The script-level timeout should fire first so Numbers.app
|
|
21
|
+
* aborts from inside its own AppleScript dispatch before Node SIGKILLs osascript.
|
|
22
|
+
* Killing osascript alone does not stop work already dispatched into Numbers.app,
|
|
23
|
+
* which is what wedges it for subsequent calls.
|
|
24
|
+
*/
|
|
25
|
+
const SCRIPT_TIMEOUT_HEADROOM_MS = 5000;
|
|
26
|
+
/**
|
|
27
|
+
* Wrap a full `tell application "Numbers" ... end tell` script in an AppleScript
|
|
28
|
+
* `with timeout` block so an Apple Event that honors timeouts aborts cleanly
|
|
29
|
+
* rather than holding Numbers.app's single-threaded dispatch open. The seconds
|
|
30
|
+
* value is set below the process timeout so the in-app abort wins the race
|
|
31
|
+
* against the outer SIGKILL.
|
|
32
|
+
*/
|
|
33
|
+
function wrapWithTimeout(script, processTimeoutMs) {
|
|
34
|
+
const seconds = Math.max(1, Math.ceil((processTimeoutMs - SCRIPT_TIMEOUT_HEADROOM_MS) / 1000));
|
|
35
|
+
return `with timeout of ${seconds} seconds\n${script}\nend timeout`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Convert 0-based row/col indices to A1 notation (e.g., 0,0 → "A1", 2,3 → "D3").
|
|
39
|
+
*/
|
|
40
|
+
export function toA1(row, col) {
|
|
41
|
+
let letters = "";
|
|
42
|
+
let c = col;
|
|
43
|
+
while (true) {
|
|
44
|
+
letters = String.fromCharCode(65 + (c % 26)) + letters;
|
|
45
|
+
c = Math.floor(c / 26) - 1;
|
|
46
|
+
if (c < 0)
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
return `${letters}${row + 1}`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Run an AppleScript string via osascript and return stdout.
|
|
53
|
+
*/
|
|
54
|
+
export function runAppleScript(script, timeoutMs = 60000) {
|
|
55
|
+
try {
|
|
56
|
+
return execFileSync("osascript", ["-e", wrapWithTimeout(script, timeoutMs)], {
|
|
57
|
+
encoding: "utf-8",
|
|
58
|
+
timeout: timeoutMs,
|
|
59
|
+
// SIGKILL (not the default SIGTERM): a wedged osascript blocked on an
|
|
60
|
+
// unresponsive Numbers.app can ignore SIGTERM and leak. SIGKILL guarantees
|
|
61
|
+
// the process is reaped on timeout.
|
|
62
|
+
killSignal: "SIGKILL",
|
|
63
|
+
// Raise the output cap above Node's ~1 MB default so large reads aren't
|
|
64
|
+
// truncated into an ENOBUFS failure.
|
|
65
|
+
maxBuffer: getMaxBuffer(),
|
|
66
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
67
|
+
}).trim();
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
// osascript writes its real diagnostic to stderr; Node's default error
|
|
71
|
+
// message ("Command failed: osascript ...") buries it. Surface stderr so the
|
|
72
|
+
// thrown Error carries the actual AppleScript error.
|
|
73
|
+
const error = err;
|
|
74
|
+
const stderr = error.stderr?.toString().trim();
|
|
75
|
+
if (stderr) {
|
|
76
|
+
error.message = `${error.message}\n${stderr}`;
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Escape a string for use inside AppleScript double quotes.
|
|
83
|
+
*/
|
|
84
|
+
function escapeAS(s) {
|
|
85
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generate the AppleScript preamble that opens a document (or finds it if already open)
|
|
89
|
+
* and navigates to a sheet/table. The caller provides the body commands.
|
|
90
|
+
*/
|
|
91
|
+
function buildScript(filePath, sheet, table, body, returnExpr = '"done"') {
|
|
92
|
+
return `
|
|
93
|
+
tell application "Numbers"
|
|
94
|
+
set docFound to false
|
|
95
|
+
repeat with d in documents
|
|
96
|
+
set dPath to POSIX path of (file of d as text)
|
|
97
|
+
if dPath is "${escapeAS(filePath)}" then
|
|
98
|
+
set docFound to true
|
|
99
|
+
set targetDoc to d
|
|
100
|
+
exit repeat
|
|
101
|
+
end if
|
|
102
|
+
end repeat
|
|
103
|
+
if not docFound then
|
|
104
|
+
set targetDoc to open POSIX file "${escapeAS(filePath)}"
|
|
105
|
+
delay 1
|
|
106
|
+
end if
|
|
107
|
+
tell targetDoc
|
|
108
|
+
tell sheet "${escapeAS(sheet)}"
|
|
109
|
+
tell table "${escapeAS(table)}"
|
|
110
|
+
${body}
|
|
111
|
+
end tell
|
|
112
|
+
end tell
|
|
113
|
+
save
|
|
114
|
+
end tell
|
|
115
|
+
return ${returnExpr}
|
|
116
|
+
end tell`;
|
|
117
|
+
}
|
|
118
|
+
export function setFormula(filePath, sheet, table, row, col, formula) {
|
|
119
|
+
const cellRef = toA1(row, col);
|
|
120
|
+
const body = ` set the value of cell "${cellRef}" to "${escapeAS(formula)}"
|
|
121
|
+
delay 0.3
|
|
122
|
+
set computedVal to the value of cell "${cellRef}"`;
|
|
123
|
+
const computedValue = runAppleScript(buildScript(filePath, sheet, table, body, "(computedVal as text)"));
|
|
124
|
+
return {
|
|
125
|
+
path: filePath,
|
|
126
|
+
sheetName: sheet,
|
|
127
|
+
tableName: table,
|
|
128
|
+
cell: cellRef,
|
|
129
|
+
formula,
|
|
130
|
+
computedValue,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export function setFormulasBatch(filePath, sheet, table, formulas) {
|
|
134
|
+
const cmds = formulas
|
|
135
|
+
.map((f) => ` set the value of cell "${toA1(f.row, f.col)}" to "${escapeAS(f.formula)}"`)
|
|
136
|
+
.join("\n");
|
|
137
|
+
runAppleScript(buildScript(filePath, sheet, table, cmds));
|
|
138
|
+
return { path: filePath, sheetName: sheet, tableName: table, cellsSet: formulas.length };
|
|
139
|
+
}
|
|
140
|
+
function colorToAS(c) {
|
|
141
|
+
return `{${c.red}, ${c.green}, ${c.blue}}`;
|
|
142
|
+
}
|
|
143
|
+
function buildStyleCommands(cellRef, style) {
|
|
144
|
+
const cmds = [];
|
|
145
|
+
const c = `cell "${cellRef}"`;
|
|
146
|
+
if (style.fontName !== undefined)
|
|
147
|
+
cmds.push(` set font name of ${c} to "${escapeAS(style.fontName)}"`);
|
|
148
|
+
if (style.fontSize !== undefined)
|
|
149
|
+
cmds.push(` set font size of ${c} to ${style.fontSize}`);
|
|
150
|
+
if (style.textColor !== undefined)
|
|
151
|
+
cmds.push(` set text color of ${c} to ${colorToAS(style.textColor)}`);
|
|
152
|
+
if (style.backgroundColor !== undefined)
|
|
153
|
+
cmds.push(` set background color of ${c} to ${colorToAS(style.backgroundColor)}`);
|
|
154
|
+
if (style.format !== undefined)
|
|
155
|
+
cmds.push(` set format of ${c} to ${style.format}`);
|
|
156
|
+
if (style.alignment !== undefined)
|
|
157
|
+
cmds.push(` set alignment of ${c} to ${style.alignment}`);
|
|
158
|
+
if (style.verticalAlignment !== undefined)
|
|
159
|
+
cmds.push(` set vertical alignment of ${c} to ${style.verticalAlignment}`);
|
|
160
|
+
if (style.textWrap !== undefined)
|
|
161
|
+
cmds.push(` set text wrap of ${c} to ${style.textWrap}`);
|
|
162
|
+
return cmds;
|
|
163
|
+
}
|
|
164
|
+
export function setCellStyle(filePath, sheet, table, row, col, style) {
|
|
165
|
+
const cellRef = toA1(row, col);
|
|
166
|
+
const cmds = buildStyleCommands(cellRef, style);
|
|
167
|
+
if (cmds.length === 0)
|
|
168
|
+
return { path: filePath, sheetName: sheet, tableName: table, cell: cellRef };
|
|
169
|
+
runAppleScript(buildScript(filePath, sheet, table, cmds.join("\n")));
|
|
170
|
+
return { path: filePath, sheetName: sheet, tableName: table, cell: cellRef };
|
|
171
|
+
}
|
|
172
|
+
export function setCellsStyleBatch(filePath, sheet, table, entries) {
|
|
173
|
+
const allCmds = [];
|
|
174
|
+
for (const entry of entries) {
|
|
175
|
+
const cellRef = toA1(entry.row, entry.col);
|
|
176
|
+
allCmds.push(...buildStyleCommands(cellRef, entry.style));
|
|
177
|
+
}
|
|
178
|
+
if (allCmds.length > 0) {
|
|
179
|
+
runAppleScript(buildScript(filePath, sheet, table, allCmds.join("\n")));
|
|
180
|
+
}
|
|
181
|
+
return { path: filePath, sheetName: sheet, tableName: table, cellsStyled: entries.length };
|
|
182
|
+
}
|
|
183
|
+
export function setColumnWidth(filePath, sheet, table, col, width) {
|
|
184
|
+
// Column letters for AppleScript
|
|
185
|
+
const colLetter = toA1(0, col).replace("1", "");
|
|
186
|
+
const body = ` set width of column "${colLetter}" to ${width}`;
|
|
187
|
+
runAppleScript(buildScript(filePath, sheet, table, body));
|
|
188
|
+
return { path: filePath, sheetName: sheet, tableName: table };
|
|
189
|
+
}
|
|
190
|
+
export function setRowHeight(filePath, sheet, table, row, height) {
|
|
191
|
+
// AppleScript rows are 1-based
|
|
192
|
+
const body = ` set height of row ${row + 1} to ${height}`;
|
|
193
|
+
runAppleScript(buildScript(filePath, sheet, table, body));
|
|
194
|
+
return { path: filePath, sheetName: sheet, tableName: table };
|
|
195
|
+
}
|
|
196
|
+
export function mergeCells(filePath, sheet, table, startRow, startCol, endRow, endCol) {
|
|
197
|
+
const rangeStr = `${toA1(startRow, startCol)}:${toA1(endRow, endCol)}`;
|
|
198
|
+
const body = ` merge range "${rangeStr}"`;
|
|
199
|
+
runAppleScript(buildScript(filePath, sheet, table, body));
|
|
200
|
+
return { path: filePath, sheetName: sheet, tableName: table, range: rangeStr };
|
|
201
|
+
}
|
|
202
|
+
export function unmergeCells(filePath, sheet, table, startRow, startCol, endRow, endCol) {
|
|
203
|
+
const rangeStr = `${toA1(startRow, startCol)}:${toA1(endRow, endCol)}`;
|
|
204
|
+
const body = ` unmerge range "${rangeStr}"`;
|
|
205
|
+
runAppleScript(buildScript(filePath, sheet, table, body));
|
|
206
|
+
return { path: filePath, sheetName: sheet, tableName: table, range: rangeStr };
|
|
207
|
+
}
|
|
208
|
+
export function getCellStyle(filePath, sheet, table, row, col) {
|
|
209
|
+
const cellRef = toA1(row, col);
|
|
210
|
+
const body = ` set c to cell "${cellRef}"
|
|
211
|
+
set fn to font name of c
|
|
212
|
+
if fn is missing value then set fn to ""
|
|
213
|
+
set fs to font size of c
|
|
214
|
+
set tc to text color of c
|
|
215
|
+
if tc is missing value then
|
|
216
|
+
set tcStr to "0,0,0"
|
|
217
|
+
else
|
|
218
|
+
set tcStr to (item 1 of tc) & "," & (item 2 of tc) & "," & (item 3 of tc)
|
|
219
|
+
end if
|
|
220
|
+
set bg to background color of c
|
|
221
|
+
if bg is missing value then
|
|
222
|
+
set bgStr to "65535,65535,65535"
|
|
223
|
+
else
|
|
224
|
+
set bgStr to (item 1 of bg) & "," & (item 2 of bg) & "," & (item 3 of bg)
|
|
225
|
+
end if
|
|
226
|
+
set fmt to format of c
|
|
227
|
+
set al to alignment of c
|
|
228
|
+
set va to vertical alignment of c
|
|
229
|
+
set tw to text wrap of c`;
|
|
230
|
+
const returnExpr = `fn & "|" & fs & "|" & tcStr & "|" & bgStr & "|" & fmt & "|" & al & "|" & va & "|" & tw`;
|
|
231
|
+
const raw = runAppleScript(buildScript(filePath, sheet, table, body, returnExpr));
|
|
232
|
+
const parts = raw.split("|");
|
|
233
|
+
const parseColor = (s) => {
|
|
234
|
+
const [r, g, b] = s.split(",").map(Number);
|
|
235
|
+
return { red: r, green: g, blue: b };
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
fontName: parts[0],
|
|
239
|
+
fontSize: parseFloat(parts[1]),
|
|
240
|
+
textColor: parseColor(parts[2]),
|
|
241
|
+
backgroundColor: parseColor(parts[3]),
|
|
242
|
+
format: parts[4],
|
|
243
|
+
alignment: parts[5],
|
|
244
|
+
verticalAlignment: parts[6],
|
|
245
|
+
textWrap: parts[7] === "true",
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=applescript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applescript.js","sourceRoot":"","sources":["../../src/utils/applescript.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface PythonResult<T = unknown> {
|
|
2
|
+
data?: T;
|
|
3
|
+
error?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Reset the cached Python path (useful for testing). */
|
|
6
|
+
export declare function _resetPythonCache(): void;
|
|
7
|
+
export declare function runNumbersReader<T = unknown>(command: string, args: string[], timeoutMs?: number): PythonResult<T>;
|
|
8
|
+
export declare function checkDependencies(): {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=python.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../src/utils/python.ts"],"names":[],"mappings":"AA+CA,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,yDAAyD;AACzD,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAcD,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAC1C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,SAAQ,GAChB,YAAY,CAAC,CAAC,CAAC,CAiCjB;AAED,wBAAgB,iBAAiB,IAAI;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAcpE"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { execSync, execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
function getProjectRoot() {
|
|
8
|
+
// Walk up from build/utils/ or src/utils/ to project root
|
|
9
|
+
return join(__dirname, "..", "..");
|
|
10
|
+
}
|
|
11
|
+
// Resolve path to the Python helper script
|
|
12
|
+
function getScriptPath() {
|
|
13
|
+
return join(getProjectRoot(), "src", "utils", "numbers_reader.py");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Find the best Python executable. Preference order:
|
|
17
|
+
* 1. Project-local venv (./venv/bin/python3)
|
|
18
|
+
* 2. System python3
|
|
19
|
+
* 3. System python
|
|
20
|
+
*/
|
|
21
|
+
function findPython() {
|
|
22
|
+
const projectRoot = getProjectRoot();
|
|
23
|
+
const venvPython = join(projectRoot, "venv", "bin", "python3");
|
|
24
|
+
// Prefer the project venv — it has numbers-parser installed
|
|
25
|
+
if (existsSync(venvPython)) {
|
|
26
|
+
return venvPython;
|
|
27
|
+
}
|
|
28
|
+
// Fall back to system Python
|
|
29
|
+
for (const cmd of ["python3", "python"]) {
|
|
30
|
+
try {
|
|
31
|
+
execSync(`${cmd} --version`, { stdio: "pipe" });
|
|
32
|
+
return cmd;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new Error('Python 3 not found. Run "npm run setup" to create a venv, or ensure python3 is on PATH.');
|
|
39
|
+
}
|
|
40
|
+
let cachedPython = null;
|
|
41
|
+
/** Reset the cached Python path (useful for testing). */
|
|
42
|
+
export function _resetPythonCache() {
|
|
43
|
+
cachedPython = null;
|
|
44
|
+
}
|
|
45
|
+
const DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024; // 50MB for large spreadsheets
|
|
46
|
+
/** Max stdout bytes from the Python sidecar, overridable via env for huge files. */
|
|
47
|
+
function getMaxBuffer() {
|
|
48
|
+
const raw = process.env.APPLE_NUMBERS_MCP_MAX_BUFFER;
|
|
49
|
+
if (raw !== undefined) {
|
|
50
|
+
const n = Number(raw);
|
|
51
|
+
if (Number.isFinite(n) && n > 0)
|
|
52
|
+
return n;
|
|
53
|
+
}
|
|
54
|
+
return DEFAULT_MAX_BUFFER_BYTES;
|
|
55
|
+
}
|
|
56
|
+
export function runNumbersReader(command, args, timeoutMs = 30000) {
|
|
57
|
+
const python = cachedPython ?? (cachedPython = findPython());
|
|
58
|
+
const scriptPath = getScriptPath();
|
|
59
|
+
const fullArgs = [scriptPath, command, ...args];
|
|
60
|
+
const debug = process.env.DEBUG || process.env.VERBOSE;
|
|
61
|
+
if (debug) {
|
|
62
|
+
console.error(`[numbers-mcp] ${python} ${fullArgs.join(" ")}`);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const stdout = execFileSync(python, fullArgs, {
|
|
66
|
+
encoding: "utf-8",
|
|
67
|
+
timeout: timeoutMs,
|
|
68
|
+
maxBuffer: getMaxBuffer(),
|
|
69
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
70
|
+
});
|
|
71
|
+
const result = JSON.parse(stdout.trim());
|
|
72
|
+
if (result.error) {
|
|
73
|
+
return { error: result.error };
|
|
74
|
+
}
|
|
75
|
+
return { data: result };
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
const error = err;
|
|
79
|
+
if (error.stderr?.includes("numbers-parser not installed")) {
|
|
80
|
+
return { error: "numbers-parser not installed. Run: npm run setup" };
|
|
81
|
+
}
|
|
82
|
+
if (error.message?.includes("ETIMEDOUT") || error.message?.includes("timed out")) {
|
|
83
|
+
return { error: `Operation timed out after ${timeoutMs}ms. File may be very large.` };
|
|
84
|
+
}
|
|
85
|
+
return { error: error.message || "Unknown error executing Python script" };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function checkDependencies() {
|
|
89
|
+
try {
|
|
90
|
+
const python = cachedPython ?? (cachedPython = findPython());
|
|
91
|
+
const version = execSync(`${python} -c "import numbers_parser; print(numbers_parser.__version__)"`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
92
|
+
return { ok: true, message: `All dependencies available (numbers-parser ${version})` };
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
message: "numbers-parser not installed. Run: npm run setup",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=python.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python.js","sourceRoot":"","sources":["../../src/utils/python.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,SAAS,cAAc;IACrB,0DAA0D;IAC1D,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,2CAA2C;AAC3C,SAAS,aAAa;IACpB,OAAO,IAAI,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC;AACrE,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU;IACjB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAE/D,4DAA4D;IAC5D,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,6BAA6B;IAC7B,KAAK,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,QAAQ,CAAC,GAAG,GAAG,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAChD,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;AACJ,CAAC;AAOD,IAAI,YAAY,GAAkB,IAAI,CAAC;AAEvC,yDAAyD;AACzD,MAAM,UAAU,iBAAiB;IAC/B,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,MAAM,wBAAwB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,8BAA8B;AAEjF,oFAAoF;AACpF,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;IACrD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,wBAAwB,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,IAAc,EACd,SAAS,GAAG,KAAK;IAEjB,MAAM,MAAM,GAAG,YAAY,IAAI,CAAC,YAAY,GAAG,UAAU,EAAE,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IAEhD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IACvD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,iBAAiB,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE;YAC5C,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,SAAS;YAClB,SAAS,EAAE,YAAY,EAAE;YACzB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAW,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,GAAmD,CAAC;QAClE,IAAI,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,8BAA8B,CAAC,EAAE,CAAC;YAC3D,OAAO,EAAE,KAAK,EAAE,kDAAkD,EAAE,CAAC;QACvE,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACjF,OAAO,EAAE,KAAK,EAAE,6BAA6B,SAAS,6BAA6B,EAAE,CAAC;QACxF,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,IAAI,uCAAuC,EAAE,CAAC;IAC7E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,IAAI,CAAC,YAAY,GAAG,UAAU,EAAE,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,QAAQ,CACtB,GAAG,MAAM,gEAAgE,EACzE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CACvD,CAAC,IAAI,EAAE,CAAC;QACT,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,8CAA8C,OAAO,GAAG,EAAE,CAAC;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,kDAAkD;SAC5D,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "apple-numbers-mcp",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "MCP server for reading and writing Apple Numbers (.numbers) spreadsheet files",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "build/index.js",
|
|
7
|
+
"types": "build/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"apple-numbers-mcp": "build/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"build",
|
|
13
|
+
"src/utils/numbers_reader.py",
|
|
14
|
+
"scripts/setup.sh",
|
|
15
|
+
"requirements.txt",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"setup": "bash scripts/setup.sh",
|
|
21
|
+
"build": "tsc && tsc-alias",
|
|
22
|
+
"start": "node build/index.js",
|
|
23
|
+
"dev": "tsc --watch",
|
|
24
|
+
"lint": "eslint src",
|
|
25
|
+
"lint:fix": "eslint src --fix",
|
|
26
|
+
"format": "prettier --write src",
|
|
27
|
+
"format:check": "prettier --check src",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:coverage": "vitest run --coverage",
|
|
31
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
32
|
+
"test:all": "vitest run && vitest run --config vitest.integration.config.ts",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"version": "node scripts/sync-plugin-version.mjs && git add .claude-plugin/plugin.json .claude-plugin/marketplace.json",
|
|
35
|
+
"prepublishOnly": "npm run lint && npm run test && npm run build",
|
|
36
|
+
"prepare": "husky; npm run build"
|
|
37
|
+
},
|
|
38
|
+
"volta": {
|
|
39
|
+
"node": "22.13.1"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"mcp",
|
|
43
|
+
"apple-numbers",
|
|
44
|
+
"claude",
|
|
45
|
+
"ai",
|
|
46
|
+
"spreadsheet",
|
|
47
|
+
"macos",
|
|
48
|
+
"numbers-parser",
|
|
49
|
+
"model-context-protocol"
|
|
50
|
+
],
|
|
51
|
+
"author": "Rob Sweet <rob@superiortech.io>",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/sweetrb/apple-numbers-mcp.git"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/sweetrb/apple-numbers-mcp#readme",
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/sweetrb/apple-numbers-mcp/issues"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=20.0.0"
|
|
63
|
+
},
|
|
64
|
+
"os": [
|
|
65
|
+
"darwin"
|
|
66
|
+
],
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@modelcontextprotocol/sdk": "^1.4.1",
|
|
69
|
+
"zod": "^3.22.4"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@eslint/js": "^9.0.0",
|
|
73
|
+
"@types/node": "^22.0.0",
|
|
74
|
+
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
|
75
|
+
"@typescript-eslint/parser": "^8.57.2",
|
|
76
|
+
"@vitest/coverage-v8": "^2.1.9",
|
|
77
|
+
"eslint": "^9.0.0",
|
|
78
|
+
"husky": "^9.0.0",
|
|
79
|
+
"lint-staged": "^16.2.7",
|
|
80
|
+
"prettier": "^3.0.0",
|
|
81
|
+
"tsc-alias": "^1.8.0",
|
|
82
|
+
"typescript": "^5.5.0",
|
|
83
|
+
"vitest": "^2.0.0"
|
|
84
|
+
},
|
|
85
|
+
"lint-staged": {
|
|
86
|
+
"src/**/*.ts": [
|
|
87
|
+
"eslint --fix",
|
|
88
|
+
"prettier --write"
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
package/requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numbers-parser>=4.0.0
|
package/scripts/setup.sh
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Create a Python venv and install numbers-parser.
|
|
4
|
+
# Run via: npm run setup
|
|
5
|
+
#
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
10
|
+
VENV_DIR="$PROJECT_ROOT/venv"
|
|
11
|
+
|
|
12
|
+
echo "==> Setting up Python venv in $VENV_DIR"
|
|
13
|
+
|
|
14
|
+
# Find python3
|
|
15
|
+
PYTHON=""
|
|
16
|
+
for cmd in python3 python; do
|
|
17
|
+
if command -v "$cmd" &>/dev/null; then
|
|
18
|
+
PYTHON="$cmd"
|
|
19
|
+
break
|
|
20
|
+
fi
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
if [ -z "$PYTHON" ]; then
|
|
24
|
+
echo "ERROR: Python 3 not found. Install Python 3 first." >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
echo " Using: $PYTHON ($($PYTHON --version))"
|
|
29
|
+
|
|
30
|
+
# Create venv if it doesn't exist
|
|
31
|
+
if [ ! -d "$VENV_DIR" ]; then
|
|
32
|
+
echo "==> Creating virtual environment..."
|
|
33
|
+
"$PYTHON" -m venv "$VENV_DIR"
|
|
34
|
+
else
|
|
35
|
+
echo " Venv already exists, updating..."
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Install dependencies
|
|
39
|
+
echo "==> Installing numbers-parser..."
|
|
40
|
+
"$VENV_DIR/bin/pip" install --upgrade pip -q
|
|
41
|
+
"$VENV_DIR/bin/pip" install -r "$PROJECT_ROOT/requirements.txt" -q
|
|
42
|
+
|
|
43
|
+
echo "==> Verifying installation..."
|
|
44
|
+
"$VENV_DIR/bin/python3" -c "import numbers_parser; print(f' numbers-parser {numbers_parser.__version__} installed')"
|
|
45
|
+
|
|
46
|
+
echo "==> Done! Python venv ready."
|