iwork-mcp 0.2.0 → 0.3.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.
@@ -104,6 +104,80 @@ export function registerKeynoteTools(server) {
104
104
  return { index: i, slideNumber: i + 1, title: title, skipped: s.skipped() };
105
105
  }));
106
106
  `, { documentName })));
107
+ server.tool("keynote_get_slide_content", "Read all content from a slide: title, body, presenter notes, and list of items", {
108
+ documentName: z.string().describe("Name of the open presentation"),
109
+ slideNumber: z.number().describe("Slide number (1-based)"),
110
+ }, async ({ documentName, slideNumber }) => handleJXA(() => runJXA(`
111
+ const app = Application("Keynote");
112
+ const doc = app.documents.byName(params.documentName);
113
+ const slide = doc.slides[params.slideNumber - 1];
114
+
115
+ let title = "";
116
+ try { const t = slide.defaultTitleItem(); if (t) title = t.objectText(); } catch(e) {}
117
+ let body = "";
118
+ try { const b = slide.defaultBodyItem(); if (b) body = b.objectText(); } catch(e) {}
119
+ let notes = "";
120
+ try { notes = slide.presenterNotes(); } catch(e) {}
121
+
122
+ const textItems = [];
123
+ try {
124
+ const items = slide.textItems();
125
+ for (let i = 0; i < items.length; i++) {
126
+ textItems.push({ index: i, text: items[i].objectText(), width: items[i].width(), height: items[i].height() });
127
+ }
128
+ } catch(e) {}
129
+
130
+ const images = [];
131
+ try {
132
+ const imgs = slide.images();
133
+ for (let i = 0; i < imgs.length; i++) {
134
+ images.push({ index: i, width: imgs[i].width(), height: imgs[i].height() });
135
+ }
136
+ } catch(e) {}
137
+
138
+ const shapes = [];
139
+ try {
140
+ const shps = slide.shapes();
141
+ for (let i = 0; i < shps.length; i++) {
142
+ shapes.push({ index: i, text: shps[i].objectText(), width: shps[i].width(), height: shps[i].height() });
143
+ }
144
+ } catch(e) {}
145
+
146
+ return JSON.stringify({ slideNumber: params.slideNumber, title, body, presenterNotes: notes, textItems, images, shapes });
147
+ `, { documentName, slideNumber })));
148
+ server.tool("keynote_delete_slide", "Delete a slide from the presentation", {
149
+ documentName: z.string().describe("Name of the open presentation"),
150
+ slideNumber: z.number().describe("Slide number to delete (1-based)"),
151
+ }, async ({ documentName, slideNumber }) => handleJXA(() => runJXA(`
152
+ const app = Application("Keynote");
153
+ const doc = app.documents.byName(params.documentName);
154
+ app.delete(doc.slides[params.slideNumber - 1]);
155
+ return JSON.stringify({ deleted: true, slideNumber: params.slideNumber, remainingSlides: doc.slides.length });
156
+ `, { documentName, slideNumber })));
157
+ server.tool("keynote_duplicate_slide", "Duplicate an existing slide", {
158
+ documentName: z.string().describe("Name of the open presentation"),
159
+ slideNumber: z.number().describe("Slide number to duplicate (1-based)"),
160
+ }, async ({ documentName, slideNumber }) => handleJXA(() => runJXA(`
161
+ const app = Application("Keynote");
162
+ const doc = app.documents.byName(params.documentName);
163
+ app.duplicate(doc.slides[params.slideNumber - 1]);
164
+ return JSON.stringify({ duplicated: true, totalSlides: doc.slides.length });
165
+ `, { documentName, slideNumber })));
166
+ server.tool("keynote_list_master_slides", "List all available master slide layouts in the current theme", {
167
+ documentName: z.string().describe("Name of the open presentation"),
168
+ }, async ({ documentName }) => handleJXA(() => runJXA(`
169
+ const app = Application("Keynote");
170
+ const doc = app.documents.byName(params.documentName);
171
+ const masters = doc.masterSlides();
172
+ return JSON.stringify(masters.map((m, i) => ({ index: i, name: m.name() })));
173
+ `, { documentName })));
174
+ server.tool("keynote_stop_slideshow", "Stop a running slideshow", {
175
+ documentName: z.string().describe("Name of the open presentation"),
176
+ }, async ({ documentName }) => handleJXA(() => runJXA(`
177
+ const app = Application("Keynote");
178
+ app.stop(app.documents.byName(params.documentName));
179
+ return JSON.stringify({ stopped: true });
180
+ `, { documentName })));
107
181
  server.tool("keynote_add_slide", "Add a new slide to the presentation", {
108
182
  documentName: z.string().describe("Name of the open presentation"),
109
183
  masterSlideName: z.string().optional().describe("Master slide / layout name (e.g. 'Title & Subtitle', 'Blank')"),
@@ -154,6 +154,92 @@ export function registerNumbersTools(server) {
154
154
  columnCount: table.columnCount(),
155
155
  });
156
156
  `, { documentName, sheetName: sheetName ?? null, tableName: tableName ?? null, rows: rows ?? null, columns: columns ?? null })));
157
+ server.tool("numbers_rename_sheet", "Rename a sheet in a Numbers document", {
158
+ documentName: z.string().describe("Name of the open document"),
159
+ sheetName: z.string().describe("Current sheet name"),
160
+ newName: z.string().describe("New name for the sheet"),
161
+ }, async ({ documentName, sheetName, newName }) => handleJXA(() => runJXA(`
162
+ const app = Application("Numbers");
163
+ const doc = app.documents.byName(params.documentName);
164
+ const sheet = doc.sheets.byName(params.sheetName);
165
+ sheet.name = params.newName;
166
+ return JSON.stringify({ renamed: true, oldName: params.sheetName, newName: params.newName });
167
+ `, { documentName, sheetName, newName })));
168
+ server.tool("numbers_delete_sheet", "Delete a sheet from a Numbers document", {
169
+ documentName: z.string().describe("Name of the open document"),
170
+ sheetName: z.string().describe("Name of the sheet to delete"),
171
+ }, async ({ documentName, sheetName }) => handleJXA(() => runJXA(`
172
+ const app = Application("Numbers");
173
+ const doc = app.documents.byName(params.documentName);
174
+ const sheet = doc.sheets.byName(params.sheetName);
175
+ app.delete(sheet);
176
+ return JSON.stringify({ deleted: true, sheetName: params.sheetName });
177
+ `, { documentName, sheetName })));
178
+ server.tool("numbers_rename_table", "Rename a table in a Numbers document", {
179
+ documentName: z.string().describe("Name of the open document"),
180
+ tableName: z.string().describe("Current table name"),
181
+ newName: z.string().describe("New name for the table"),
182
+ sheetName: z.string().optional().describe("Sheet name (defaults to first sheet)"),
183
+ }, async ({ documentName, tableName, newName, sheetName }) => handleJXA(() => runJXA(`
184
+ const app = Application("Numbers");
185
+ const doc = app.documents.byName(params.documentName);
186
+ const sheet = params.sheetName ? doc.sheets.byName(params.sheetName) : doc.sheets[0];
187
+ const table = sheet.tables.byName(params.tableName);
188
+ table.name = params.newName;
189
+ return JSON.stringify({ renamed: true, oldName: params.tableName, newName: params.newName });
190
+ `, { documentName, tableName, newName, sheetName: sheetName ?? null })));
191
+ server.tool("numbers_delete_table", "Delete a table from a sheet", {
192
+ documentName: z.string().describe("Name of the open document"),
193
+ tableName: z.string().describe("Name of the table to delete"),
194
+ sheetName: z.string().optional().describe("Sheet name (defaults to first sheet)"),
195
+ }, async ({ documentName, tableName, sheetName }) => handleJXA(() => runJXA(`
196
+ const app = Application("Numbers");
197
+ const doc = app.documents.byName(params.documentName);
198
+ const sheet = params.sheetName ? doc.sheets.byName(params.sheetName) : doc.sheets[0];
199
+ const table = sheet.tables.byName(params.tableName);
200
+ app.delete(table);
201
+ return JSON.stringify({ deleted: true, tableName: params.tableName });
202
+ `, { documentName, tableName, sheetName: sheetName ?? null })));
203
+ server.tool("numbers_delete_row", "Delete one or more rows from a table", {
204
+ documentName: z.string().describe("Name of the open document"),
205
+ rowIndex: z.number().describe("Row number to delete (1-based)"),
206
+ count: z.number().optional().describe("Number of rows to delete (default: 1)"),
207
+ sheetName: z.string().optional().describe("Sheet name (defaults to first sheet)"),
208
+ tableName: z.string().optional().describe("Table name (defaults to first table)"),
209
+ }, async ({ documentName, rowIndex, count, sheetName, tableName }) => handleJXA(() => runJXA(`
210
+ const app = Application("Numbers");
211
+ const doc = app.documents.byName(params.documentName);
212
+ const sheet = params.sheetName ? doc.sheets.byName(params.sheetName) : doc.sheets[0];
213
+ const table = params.tableName ? sheet.tables.byName(params.tableName) : sheet.tables[0];
214
+ const n = params.count || 1;
215
+ for (let i = 0; i < n; i++) {
216
+ app.delete(table.rows[params.rowIndex - 1]);
217
+ }
218
+ return JSON.stringify({ deleted: n, newRowCount: table.rowCount() });
219
+ `, { documentName, rowIndex, count: count ?? null, sheetName: sheetName ?? null, tableName: tableName ?? null })));
220
+ server.tool("numbers_delete_column", "Delete one or more columns from a table", {
221
+ documentName: z.string().describe("Name of the open document"),
222
+ column: z.string().describe("Column letter to delete, e.g. 'A', 'B'"),
223
+ count: z.number().optional().describe("Number of columns to delete (default: 1)"),
224
+ sheetName: z.string().optional().describe("Sheet name (defaults to first sheet)"),
225
+ tableName: z.string().optional().describe("Table name (defaults to first table)"),
226
+ }, async ({ documentName, column, count, sheetName, tableName }) => handleJXA(() => runJXA(`
227
+ const app = Application("Numbers");
228
+ const doc = app.documents.byName(params.documentName);
229
+ const sheet = params.sheetName ? doc.sheets.byName(params.sheetName) : doc.sheets[0];
230
+ const table = params.tableName ? sheet.tables.byName(params.tableName) : sheet.tables[0];
231
+ const colStr = params.column.toUpperCase();
232
+ let colIndex = 0;
233
+ for (let i = 0; i < colStr.length; i++) {
234
+ colIndex = colIndex * 26 + (colStr.charCodeAt(i) - 64);
235
+ }
236
+ colIndex -= 1;
237
+ const n = params.count || 1;
238
+ for (let i = 0; i < n; i++) {
239
+ app.delete(table.columns[colIndex]);
240
+ }
241
+ return JSON.stringify({ deleted: n, newColumnCount: table.columnCount() });
242
+ `, { documentName, column, count: count ?? null, sheetName: sheetName ?? null, tableName: tableName ?? null })));
157
243
  // ── Data Reading Tools ──
158
244
  server.tool("numbers_read_table", "Read all data from a table as a 2D array. Returns rows of cell values.", {
159
245
  documentName: z.string().describe("Name of the open document"),
@@ -166,14 +252,11 @@ export function registerNumbersTools(server) {
166
252
  const table = params.tableName ? sheet.tables.byName(params.tableName) : sheet.tables[0];
167
253
  const rowCount = table.rowCount();
168
254
  const colCount = table.columnCount();
255
+ // Batch read all cell values in one IPC call
256
+ const allValues = table.cells.value();
169
257
  const data = [];
170
258
  for (let r = 0; r < rowCount; r++) {
171
- const row = [];
172
- for (let c = 0; c < colCount; c++) {
173
- const cell = table.cells[r * colCount + c];
174
- row.push(cell.value());
175
- }
176
- data.push(row);
259
+ data.push(allValues.slice(r * colCount, (r + 1) * colCount));
177
260
  }
178
261
  return JSON.stringify(data);
179
262
  `, { documentName, sheetName: sheetName ?? null, tableName: tableName ?? null })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iwork-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for Apple iWork (Numbers, Pages, Keynote) automation",
5
5
  "license": "MIT",
6
6
  "type": "module",