# iWork Studio (pypi · iwork-studio)

Create, edit, design and export Apple Numbers, Keynote and Pages files. Verified and Arabic-safe.

- Trust score: 96/100 (high trust)
- Change this week: +18
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-11-04

## Components

- pypi · `iwork-studio`: 96/100 (this document), [markdown](https://verifymcp.io/servers/arkanji-iwork-studio/iwork-studio.md), [page](https://verifymcp.io/servers/arkanji-iwork-studio/iwork-studio)

## Channel facts

- Registry: `pypi`
- Package: `iwork-studio`
- Version: `2.6.1`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-11-04.

- **Supply Chain Security**: 99/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs setuptools.build_meta at install time, a recognised build step with no custom scripting around it.
  - 7 of 59 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 97/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to Arkanji/iwork-studio).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 21 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 81/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 13655 tokens (~210/item across 65 items; 65 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 100/100
  - No destabilizing schema changes in the last 30 days.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (98% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - All 4 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.
  - An AI judge read all 66 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

## Install

### How do I install the iWork Studio MCP server?

iWork Studio runs locally as a PyPI package, launched with uvx iwork-studio. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add arkanji-iwork-studio -- uvx iwork-studio
```

### Cursor

```json
{
  "mcpServers": {
    "arkanji-iwork-studio": {
      "command": "uvx",
      "args": [
        "iwork-studio"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "arkanji-iwork-studio": {
      "command": "uvx",
      "args": [
        "iwork-studio"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add arkanji-iwork-studio -- uvx iwork-studio
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "arkanji-iwork-studio": {
      "type": "local",
      "command": [
        "uvx",
        "iwork-studio"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add arkanji-iwork-studio --command uvx --arg iwork-studio
```

### Hermes

```yaml
mcp_servers:
  arkanji-iwork-studio:
    command: "uvx"
    args: ["iwork-studio"]
```

### Netclaw

```json
{
  "McpServers": {
    "arkanji-iwork-studio": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "iwork-studio"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add arkanji-iwork-studio -t stdio -c uvx -a iwork-studio
```

### Other

```json
{
  "mcpServers": {
    "arkanji-iwork-studio": {
      "command": "uvx",
      "args": [
        "iwork-studio"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-11-03 (score 96, +1)

- [security] Stability: 0.97 → pass

### 2026-11-01 (score 95, +1)

No change was recorded against any check on this day. Stability & Change Management went from 90 to 93. That category is still filling its 30-day observation window: 27 days of observed history at the previous scan, 28 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-30 (score 94, +16)

- [security improvement] Malware scan: unverified → pass

### 2026-10-28 (score 78, +1)

No change was recorded against any check on this day. Stability & Change Management went from 77 to 80. That category is still filling its 30-day observation window: 23 days of observed history at the previous scan, 24 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-26 (score 77, +1)

No change was recorded against any check on this day. Stability & Change Management went from 70 to 73. That category is still filling its 30-day observation window: 21 days of observed history at the previous scan, 22 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-23 (score 76, +1)

No change was recorded against any check on this day. Stability & Change Management went from 60 to 63. That category is still filling its 30-day observation window: 18 days of observed history at the previous scan, 19 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-21 (score 75, +1)

No change was recorded against any check on this day. Stability & Change Management went from 53 to 57. That category is still filling its 30-day observation window: 16 days of observed history at the previous scan, 17 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-19 (score 74, +1)

No change was recorded against any check on this day. Stability & Change Management went from 47 to 50. That category is still filling its 30-day observation window: 14 days of observed history at the previous scan, 15 at this one. The score rises as the window fills, whether or not the server changes.

## MCP tools (65)

### `iwork_capabilities` (~77 tokens)

What this Mac can do

What this machine can do right now: routes, GUI session, installed apps, Keynote slide-op status. Call first when unsure. Call first when unsure what this machine can do: which apps exist, whether a GUI session is available, which toolsets are loaded. Not needed before plain reads of .numbers or .key files.

### `iwork_samples` (~122 tokens)

Copy sample files to try

Copy sample files (sample-sales.numbers with English and Arabic sheets of synthetic sales data, and sample-outline.md for a deck) into a folder, so a first try needs none of the user's own files. Never overwrites. Returns try-it prompts. Use when the user wants to try iWork Studio but has no file handy, or asks for an example; then follow one of the returned try_it prompts. Not for the user's own files.

Input parameters:

- `folder` (string, required): Folder to copy the samples into (created if missing), e.g. ~/Desktop.

### `iwork_read` (~103 tokens)

Read an iWork file

Read a .numbers, .key or .pages file into JSON (.pages needs the Pages app and a GUI session). Use to see a file's content before editing it. Not for formatting details: use numbers_inspect_format (Numbers) or keynote_inspect_style (Keynote). For file facts only, iwork_metadata is cheaper.

Input parameters:

- `path` (string, required): Path to the .numbers, .key or .pages file (absolute, or starting with ~).

### `iwork_list_backups` (~100 tokens)

List backups

List the versioned backups (newest first) every write left for this file, plus the Keynote write manifest. Use when the user wants to undo or compare versions: it lists the backups, then iwork_restore_backup restores one. Not needed after a failed tool call: failed writes never change the file.

Input parameters:

- `path` (string, required): Path to the .numbers, .key or .pages file (absolute, or starting with ~).

### `pages_preflight` (~55 tokens)

Pages: check it can answer

Check Pages can answer AppleEvents. Run once before any Pages op; never loop on failure. Call once before other Pages tools. A -1712 error means a dialog in Pages needs a human; don't retry until it's dismissed.

### `iwork_verify_format` (~235 tokens)

Check rendered formatting

Independent check of formatting: export a PDF through the app and confirm `text` is drawn with the given font (name contains), size (pt), color (#RRGGBB), bold, and page size (pt). Needs macOS + the app. Use to prove how a piece of text is drawn (font, size, colour, page size). To check text is merely present use iwork_verify_render.

Input parameters:

- `bold`: Expected bold (true) or not bold (false).
- `color`: Expected text colour "#RRGGBB".
- `font`: Expected font; matches when the drawn font name contains it, e.g. "Avenir".
- `page_height`: Expected page height in points.
- `page_width`: Expected page width in points.
- `path` (string, required): Path to the .numbers, .key or .pages file (absolute, or starting with ~).
- `size`: Expected size in points (±0.6).
- `text` (string, required): Text that must appear in the rendered PDF; for Arabic, one word (multi-word RTL text is reordered).

### `iwork_verify_render` (~140 tokens)

Check text is rendered

Export the file to PDF through its app and assert `assert_text` is visibly rendered. For Arabic, use one word. Use after an important write to prove the text really shows when rendered. To check font, size or colour use iwork_verify_format; to check a deck's whole design use keynote_review_deck.

Input parameters:

- `assert_text` (string, required): Text that must be visibly rendered; for Arabic, one word.
- `expected_pages`: Expected page (or slide) count; omit to skip that check.
- `path` (string, required): Path to the .numbers, .key or .pages file (absolute, or starting with ~).

### `numbers_edit_cell` (~253 tokens)

Numbers: edit cell

Set one cell (e.g. ref "B2") in a .numbers file. Backed up, verified, atomic; every other cell is checked unchanged. dry_run=true previews the change on a copy without touching the file. Use to set one value. For a formula use numbers_set_formula; to add whole rows use numbers_insert with values; for how a number displays use numbers_set_number_format.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `ref` (string, required): One cell in A1 notation, e.g. "B2".
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.
- `value` (required): New value: a number stays a number, text stays text exactly (Arabic too); null clears. For a formula use numbers_set_formula.

### `numbers_inspect_format` (~142 tokens)

Numbers: inspect format

Current formatting of a Numbers table: column widths, row heights, header rows/cols, merges, and per-cell font/colour/fill/alignment, number format (shown_as) and borders. Read this before formatting. Use before formatting to see current styles, number formats, sizes, headers and merges. For values use iwork_read.

Input parameters:

- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.

### `numbers_set_dimensions` (~247 tokens)

Numbers: set dimensions

Set column widths and/or row heights in points, e.g. columns={"A": 160, "C": 90}, rows={"1": 32} (rows are 1-based). Nothing else changes. dry_run=true previews the change on a copy without touching the file. Use for column widths and row heights. To change which rows count as headers use numbers_set_headers.

Input parameters:

- `columns`: Column widths in points by letter, e.g. {"A": 160, "C": 90}.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `rows`: Row heights in points by 1-based row number, e.g. {"1": 32}.
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.

### `numbers_set_number_format` (~399 tokens)

Numbers: set number format

How numbers display in a range ("B2:B9"): format = number | currency | percentage | scientific | fraction | datetime | text. Options: decimal_places, thousands_separator, negative_style (minus|red|parentheses|red_parentheses), currency_code (ISO, e.g. SAR, USD), accounting, date_format (e.g. "d MMM yyyy"). Values are not changed; the result shows how each cell now displays. dry_run=true previews the change on a copy without touching the file. Use for how numbers display (currency, %, dates, decimals); the values don't change. For fonts and colours use numbers_set_cell_style.

Input parameters:

- `accounting`: true = accounting style (symbol aligned left) for currency.
- `cells` (string, required): A cell or range in A1 notation, e.g. "B2" or "A1:D1".
- `currency_code`: ISO currency code for format=currency, e.g. "SAR" or "USD".
- `date_format`: Date pattern for format=datetime, e.g. "d MMM yyyy".
- `decimal_places`: Digits after the decimal point.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `format` (string, required): number | currency | percentage | scientific | fraction | datetime | text.
- `negative_style`: minus | red | parentheses | red_parentheses.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.
- `thousands_separator`: true = group thousands (1,234).

### `numbers_set_cell_style` (~430 tokens)

Numbers: set cell style

Style a range ("A1:D1"): font_name, font_size, bold, italic, underline, strikethrough, font_color / fill_color as "#RRGGBB", align (left|center|right|justify|auto), valign (top|middle|bottom), wrap. Only the attributes you pass change; every other cell is verified untouched. dry_run=true previews the change on a copy without touching the file. Use for fonts, colours, fill or alignment on a few cells. For a whole table that should look designed use numbers_apply_design; for how numbers display use numbers_set_number_format.

Input parameters:

- `align`: Horizontal: left | center | right | justify | auto.
- `bold`: true/false; omit to leave as is.
- `cells` (string, required): A cell or range in A1 notation, e.g. "B2" or "A1:D1".
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `fill_color`: Cell fill "#RRGGBB".
- `font_color`: Text colour "#RRGGBB".
- `font_name`: Font family, e.g. "Helvetica Neue" or "Geeza Pro".
- `font_size`: Font size in points.
- `italic`: true/false; omit to leave as is.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `strikethrough`: true/false; omit to leave as is.
- `table`: Table name on that sheet. Omit when the sheet has one table.
- `underline`: true/false; omit to leave as is.
- `valign`: Vertical: top | middle | bottom.
- `wrap`: true = wrap text in the cell.

### `numbers_set_borders` (~291 tokens)

Numbers: set borders

Cell borders on a range: sides = all | outline | inner | top | right | bottom | left; width in points; color "#RRGGBB"; style = solid | dashes | dots | none. dry_run=true previews the change on a copy without touching the file. Use for lines around or inside a range. For fills and fonts use numbers_set_cell_style.

Input parameters:

- `cells` (string, required): A cell or range in A1 notation, e.g. "B2" or "A1:D1".
- `color` (string): Line colour "#RRGGBB".
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `sides` (string): all | outline | inner | top | right | bottom | left.
- `style` (string): solid | dashes | dots | none (none removes the border).
- `table`: Table name on that sheet. Omit when the sheet has one table.
- `width` (number): Line width in points.

### `numbers_set_headers` (~227 tokens)

Numbers: set headers

Set how many header rows / header columns a table has (0–5). dry_run=true previews the change on a copy without touching the file. Use when the user wants a different number of header rows or columns, e.g. to freeze a title row on top for sorting and styling. Not for sizes (numbers_set_dimensions) or header colours (numbers_set_cell_style or numbers_apply_design).

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `header_columns`: Number of header columns (0 for none).
- `header_rows`: Number of header rows (0 for none).
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.

### `numbers_merge_cells` (~227 tokens)

Numbers: merge cells

Merge a rectangular range ("A1:C1"). Refused if any cell other than the top-left holds data (it would be hidden) or the range crosses the header edge. dry_run=true previews the change on a copy without touching the file. Use to merge a range, e.g. a title across columns. Refused if it would hide values; avoid merging inside data you will sort.

Input parameters:

- `cells` (string, required): The range to merge, e.g. "A1:C1". Refused if it would hide values.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.

### `keynote_replace_text` (~185 tokens)

Keynote: replace text

Find/replace text across every slide of a .key deck (literal unless regex=true). Formatting and structure are verified unchanged. dry_run=true previews the change on a copy without touching the file. Use to change the same text everywhere in a deck (names, dates, numbers). To rewrite one slide's title or body use keynote_set_slide_text.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `find` (string, required): Text to find (literal unless regex=true).
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `regex` (boolean): true = treat find as a regular expression.
- `replace` (string, required): Replacement text.

### `pages_replace_all` (~168 tokens)

Pages: replace all

Replace every occurrence of `find` in a .pages body (needs Pages + GUI session). Rolled back if the readback disagrees. dry_run=true previews the change on a copy without touching the file. Use to change text everywhere while keeping formatting. To replace the whole body use pages_set_body; for template fields use pages_fill_placeholders.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `find` (string, required): Text to find, exactly.
- `path` (string, required): Path to the .pages document (absolute, or starting with ~).
- `replace` (string, required): Replacement text.

### `pages_set_body` (~163 tokens)

Pages: set body

Replace the entire body text of a .pages document (needs Pages + GUI session). Body formatting is reset. dry_run=true previews the change on a copy without touching the file. Use only to replace the entire body text; it resets body formatting, so warn the user. Prefer pages_replace_all or pages_fill_placeholders.

Input parameters:

- `body` (string, required): The new body text; paragraphs separated by newlines. Resets body formatting.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .pages document (absolute, or starting with ~).

### `iwork_restore_backup` (~109 tokens)

Undo: restore a backup

Undo: atomically restore a backup (a name from iwork_list_backups). The current version is backed up first. Use to undo a change, with a backup name from iwork_list_backups. The current version is backed up first, so a restore can itself be undone.

Input parameters:

- `backup` (string, required): Backup name from iwork_list_backups.
- `path` (string, required): Path to the .numbers, .key or .pages file (absolute, or starting with ~).

### `keynote_add_slide` (~171 tokens)

Keynote: add slide

Insert a slide after slide number `after` (0 = first, omit = end). Every other slide is verified unchanged. dry_run=true previews the change on a copy without touching the file. Use for a new blank slide in an existing deck. To copy a slide use keynote_duplicate_slide; for a whole new deck use keynote_build_deck.

Input parameters:

- `after`: Insert after this slide number; 0 = make it the first slide; omit = at the end.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).

### `keynote_duplicate_slide` (~148 tokens)

Keynote: duplicate slide

Duplicate slide number `slide` (1-based); the copy lands right after it. dry_run=true previews the change on a copy without touching the file. Use to copy an existing slide (layout and content). For a blank slide use keynote_add_slide.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).

### `keynote_delete_slide` (~158 tokens)

Keynote: delete slide

Delete slide number `slide` (1-based). Recoverable with iwork_restore_backup. dry_run=true previews the change on a copy without touching the file. Use to remove a slide for good (the last slide can't be deleted). To keep it but hide it in the slideshow use keynote_skip_slide.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).

### `keynote_move_slide` (~164 tokens)

Keynote: move slide

Move slide number `slide` so it ends up at position `to` (both 1-based). dry_run=true previews the change on a copy without touching the file. Use to reorder slides. To copy a slide to a new position, duplicate it first with keynote_duplicate_slide.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): The slide to move, 1-based.
- `to` (integer, required): Where it should end up, 1-based.

### `keynote_skip_slide` (~168 tokens)

Keynote: skip slide

Hide (skipped=true) or show a slide in the slideshow. dry_run=true previews the change on a copy without touching the file. Use to hide or show a slide in the slideshow without deleting it. To remove it use keynote_delete_slide.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `skipped` (boolean): true = hide the slide in the slideshow; false = show it again.
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).

### `keynote_set_presenter_notes` (~171 tokens)

Keynote: set presenter notes

Set the presenter notes of slide number `slide` (1-based). Arabic-safe. dry_run=true previews the change on a copy without touching the file. Use for speaker notes on an existing slide. For a new deck, put notes in keynote_build_deck's outline instead.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `notes` (string, required): The presenter notes text (replaces existing notes).
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).

### `iwork_export` (~335 tokens)

Export (PDF, Excel, Word, PowerPoint…)

Export to another format. Numbers: pdf | xlsx | csv. Pages: pdf | docx | epub | txt | rtf. Keynote: pdf | pptx | images | movie. Optional password (+hint) for pdf/xlsx/docx/pptx, image_quality (good|better|best), image_format for slide images (jpeg|png|tiff). Default output sits next to the source. The export is checked against the source with a second tool and the source is verified unchanged. Needs macOS + the app. Use to hand the result over in another format (PDF, Excel, Word, PowerPoint, images). Never export to docx/pptx/xlsx to edit and convert back: edit the iWork file directly.

Input parameters:

- `format` (string, required): Numbers: pdf | xlsx | csv. Pages: pdf | docx | epub | txt | rtf. Keynote: pdf | pptx | images | movie.
- `image_format`: Slide images only: jpeg | png | tiff.
- `image_quality`: good | better | best.
- `out`: Output path (a folder for images); omit to write next to the source.
- `overwrite` (boolean): true = replace an existing output (the old one is kept as a backup or returned). Default false.
- `password`: Optional password for pdf, xlsx, docx or pptx.
- `password_hint`: Optional hint shown with the password prompt.
- `path` (string, required): Path to the .numbers, .key or .pages file (absolute, or starting with ~).

### `iwork_metadata` (~104 tokens)

File details

What a file says about itself, without the app: kind, size, modified, template it was made from, app builds that saved it, file format version, slide count (Keynote), embedded media count. Use for file facts (template, app build, slide count) without reading content. For the content itself use iwork_read.

Input parameters:

- `path` (string, required): Path to the .numbers, .key or .pages file (absolute, or starting with ~).

### `iwork_thumbnail` (~133 tokens)

Preview image

Extract the preview image stored in the file (first page/slide) to a JPEG and return its path — a quick look without opening the app. Reflects the app's last save. Use for a quick look at the stored first-page preview with no app. It reflects the last save in the app, not your latest edits: to see a slide as it renders now, use keynote_slide_image.

Input parameters:

- `out_dir`: Folder for the extracted JPEG; omit for a temporary folder.
- `path` (string, required): Path to the .numbers, .key or .pages file (absolute, or starting with ~).

### `iwork_find` (~162 tokens)

Find iWork files

Find Numbers / Keynote / Pages files (newest first). kind: numbers | keynote | pages; name: part of the file name. Searches `folder`, else the allowed folders, else Documents/Desktop/Downloads. Uses Spotlight on macOS. Use when the user names a file loosely ("my sales deck") or you don't know its path; then read it with iwork_read. Not needed when you already have the path.

Input parameters:

- `folder`: Folder to search; omit to search the allowed folders, else Documents, Desktop and Downloads.
- `kind`: numbers | keynote | pages; omit for all three.
- `limit` (integer): Maximum results (1–500).
- `name`: Part of the file name to match (case-insensitive).

### `iwork_list_design_kits` (~126 tokens)

List design kits

Design kits for good-looking decks and tables: font pairs (Latin + Arabic, bundled with macOS), contrast-checked palettes and a type scale. Lists the presets and the kits you saved (saved: true). Use with keynote_build_deck(kit=…), keynote_apply_design, numbers_apply_design. Custom kits: pass {"fonts": {...}, "colors": {...}}. Use before any styling to pick a kit, and to see the user's saved brand kits. Pass the name as kit= to keynote_build_deck, keynote_apply_design or numbers_apply_design.

### `iwork_extract_design_kit` (~250 tokens)

Extract design kit

Make a design kit from the user's own deck or table: heading and body fonts (Latin and Arabic) and the title, body and brand colours. .numbers reads the table's header and body styles (no app); .key reads every slide's title and body (needs macOS + Keynote). Parts the file doesn't show come from the nearest preset and are listed in notes. save=true with a name keeps it for reuse by name (contrast must pass). The file is never changed. Use when the user has a deck or table that already has their brand look. If they give colours and fonts directly use iwork_save_design_kit instead.

Input parameters:

- `name`: Name for the kit, e.g. "Acme" (required with save=true).
- `overwrite` (boolean): true = replace a saved kit with the same name.
- `path` (string, required): A .key deck or .numbers table that already has the look to capture.
- `save` (boolean): true = save it under name for reuse; contrast must pass.
- `sheet`: For .numbers: the sheet holding the styled table.
- `table`: For .numbers: the styled table.

### `iwork_save_design_kit` (~225 tokens)

Save design kit

Save a design kit by name for reuse anywhere a kit is accepted. kit = {"fonts": {...}, "colors": {...}, "theme": "...", "background": "#RRGGBB"} (missing parts come from the "executive" preset, or from "base": "<preset>"), or a preset name to copy. Contrast is checked (4.5:1). Never replaces a saved kit unless overwrite=true; can't reuse a preset's name. Use when the user gives brand colours and fonts directly. To capture them from an existing file use iwork_extract_design_kit.

Input parameters:

- `kit` (required): {"fonts": {...}, "colors": {...}, "theme": "...", "background": "#RRGGBB", "base": "<preset>"} or a preset name to copy.
- `name` (string, required): Name to save under, e.g. "Acme" (letters, digits, spaces, - or _; not a preset name).
- `overwrite` (boolean): true = replace a saved kit with the same name.

### `iwork_delete_design_kit` (~89 tokens)

Delete design kit

Delete a saved design kit (presets can't be deleted). Returns the kit's contents, so it can be saved again with iwork_save_design_kit. Use only when the user asks to remove a saved kit. To change a kit, save it again with overwrite=true instead.

Input parameters:

- `name` (string, required): Name of the saved kit to delete (presets can't be deleted).

### `numbers_apply_design` (~304 tokens)

Numbers: apply design

Style a whole table from a design kit: header band (fill, bold, contrast-checked text), body font and colour (Arabic cells get the Arabic font), alternate-row banding, number columns right-aligned. Values never change; every other table is verified untouched. No app needed. dry_run=true previews the change on a copy without touching the file. Use to make a whole table look designed in one call (header band, fonts, banding, aligned numbers). For a few cells use numbers_set_cell_style. Preview with dry_run.

Input parameters:

- `banding` (boolean): true = tint alternate body rows.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `kit`: A design kit: a name from iwork_list_design_kits (preset or saved, e.g. "executive", "Acme") or your own {"fonts": {...}, "colors": {"title": "#RRGGBB", ...}}. Contrast is checked.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.

### `iwork_list_templates` (~80 tokens)

List templates

Built-in templates for Numbers or Pages, or themes for Keynote (app: numbers | pages | keynote). Needs macOS + the app. Use before iwork_create to get valid template or theme names. Not needed for iwork_create_from_template, which copies the user's own file.

Input parameters:

- `app` (string, required): numbers | pages | keynote.

### `numbers_create` (~193 tokens)

Numbers: create

Create a new .numbers file from data. sheets = [{"name": "Sales", "tables": [{"name": "Q1", "rows": [["Region", "Revenue"], ["Riyadh", 1200]], "header_rows": 1}]}]. Numbers stay numbers, text stays text exactly (Arabic included). Refuses to overwrite. No app needed. Use to make a new Numbers file when you have the rows. From a CSV file use numbers_import_csv; to add a table to an existing file use numbers_add_table.

Input parameters:

- `path` (string, required): Where to create the new .numbers file; it must not exist yet.
- `sheets` (array, required): Sheets with their tables: [{"name": "Sales", "tables": [{"name": "Q1", "rows": [["Region", "Revenue"], ["Riyadh", 1200]], "header_rows": 1}]}].

### `numbers_import_csv` (~216 tokens)

Numbers: import csv

Turn a CSV/TSV into a new .numbers file (UTF-8, delimiter auto-detected). Plain numbers become numbers (numbers=false keeps everything as text); dates, "$1,234" and Arabic-Indic digits stay text exactly as written. Refuses to overwrite. Use when the data is in a CSV/TSV file. When you already have the rows use numbers_create.

Input parameters:

- `csv_path` (string, required): The CSV or TSV file to read (UTF-8).
- `delimiter`: Column separator, e.g. "," or "\t"; omit to detect it.
- `header_rows` (integer): How many rows at the top are headers (styled and kept on top).
- `numbers` (boolean): true = plain numbers become numbers; false = keep every cell as text.
- `path` (string, required): Where to create the new .numbers file; it must not exist yet.
- `sheet` (string): Name for the new sheet.
- `table` (string): Name for the new table.

### `numbers_insert` (~307 tokens)

Numbers: insert

Insert rows or columns (what = rows | columns) before 1-based position `at`; omit `at` to append. Optional `values`: one list per new row (or column). Every existing cell is verified at its new position. In tables with formulas, only appending is allowed (shifting would break references). dry_run=true previews the change on a copy without touching the file. Use to add rows or columns to an existing table (optionally filled). For a separate table use numbers_add_table. In tables with formulas only appending works; do mid-table inserts in Numbers.

Input parameters:

- `at`: 1-based position to insert before; omit to append at the end.
- `count` (integer): How many to insert.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.
- `values`: Optional contents: one list per new row (or column), e.g. [["Riyadh", 1200]].
- `what` (string, required): rows | columns.

### `numbers_delete` (~250 tokens)

Numbers: delete

Delete `count` rows or columns (what = rows | columns) starting at 1-based position `at`. Remaining cells are verified. Refused in tables with formulas or merged cells. Undo with iwork_restore_backup. dry_run=true previews the change on a copy without touching the file. Use to remove rows or columns. Not for clearing values (use numbers_edit_cell with null). Refused in tables with formulas or merges; undo with iwork_restore_backup.

Input parameters:

- `at` (integer, required): 1-based position of the first one to delete.
- `count` (integer): How many to delete.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.
- `what` (string, required): rows | columns.

### `numbers_add_table` (~266 tokens)

Numbers: add table

Add a table with data to an existing sheet (`sheet`, default the first) or to a new sheet (`new_sheet`). Every existing table is verified unchanged. dry_run=true previews the change on a copy without touching the file. Use for a new, separate table on a sheet or a new sheet. To grow an existing table use numbers_insert.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `header_columns` (integer): How many columns on the left are headers.
- `header_rows` (integer): How many rows at the top are headers (styled and kept on top).
- `new_sheet`: Name of a new sheet to create for the table instead.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `rows` (array, required): The data, header first: [["Region", "Revenue"], ["Riyadh", 1200]].
- `sheet`: Existing sheet to add it to; omit for the first sheet.
- `table_name` (string, required): Name of the new table (must not exist on that sheet).

### `iwork_create_from_template` (~114 tokens)

New file from your own file

Start a new document from one of the user's own files (any .numbers / .key / .pages): copies it to `path` and checks it opens. Refuses to overwrite. Use when the user has their own template or past file to start from. For Apple's built-in templates use iwork_create.

Input parameters:

- `path` (string, required): Where to create the new file; it must not exist yet.
- `template` (string, required): The user's own .numbers, .key or .pages file to copy.

### `iwork_create` (~152 tokens)

New file from a built-in template

New .numbers / .key / .pages from Apple's built-in templates (Keynote: themes); names from iwork_list_templates. Omit template for Blank / Basic White. Refuses to overwrite. Needs macOS + the app. Use for a new, empty file from Apple's templates. Instead: iwork_create_from_template to copy the user's own file, numbers_create to make a Numbers file from data, keynote_build_deck for a finished deck from an outline.

Input parameters:

- `path` (string, required): Where to create the new file (.numbers, .key or .pages); it must not exist yet.
- `template`: Template or theme name from iwork_list_templates; omit for Blank / Basic White.

### `numbers_set_formula` (~257 tokens)

Numbers: set formula

Put a formula in one cell, e.g. ref "D10", formula "=SUM(D2:D9)"; Numbers computes it and the result is returned. Every other cell's input is verified unchanged. Needs macOS + Numbers, file closed. dry_run=true previews the change on a copy without touching the file. Use for a cell that calculates (=SUM…); Numbers computes the result. For a plain value use numbers_edit_cell. Needs the Numbers app.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `formula` (string, required): The formula, starting with "=", e.g. "=SUM(B2:B9)".
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `ref` (string, required): One cell in A1 notation, e.g. "B2".
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.

### `numbers_recalculate` (~192 tokens)

Numbers: recalculate

Make Numbers recompute every formula from the current values. Numbers keeps showing a formula's old result after edits made without it (numbers_edit_cell, numbers_insert…), so run this after those when the file has formulas — their result says so (formulas_need_recalc). Formulas and inputs are verified unchanged. Needs macOS + Numbers, file closed. dry_run=true previews the change on a copy without touching the file. Use only after a no-app edit returned formulas_need_recalc, so totals are current. Not needed after numbers_set_formula, which recalculates itself.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).

### `numbers_sort` (~412 tokens)

Numbers: sort

Sort a table's body rows by a column letter (header rows stay on top). Verified as a pure reorder. A table whose formulas read other rows (e.g. =B2*0.1 below a base-figure row) is refused, because Numbers' sort would break them; for it, to_new_table=true leaves the table, its formulas and its charts as they are and puts a sorted copy of its values (no formulas) in a new table on the same sheet (named new_table_name, default "<table> sorted"). Chart the copy with keynote_build_deck. Needs macOS + Numbers, file closed. dry_run=true previews the change on a copy without touching the file. Use to reorder body rows by one column; header rows stay on top. If it refuses because formulas read other rows, call it again with to_new_table=true for a sorted copy; never rewrite the formulas to force it. Needs the Numbers app.

Input parameters:

- `column` (string, required): Column letter to sort by, e.g. "C".
- `descending` (boolean): true = largest/last first.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `new_table_name`: Name for the new table (with to_new_table); default "<table> sorted".
- `path` (string, required): Path to the .numbers file (absolute, or starting with ~).
- `sheet`: Sheet name. Omit when the file has one sheet (iwork_read lists sheets and tables).
- `table`: Table name on that sheet. Omit when the sheet has one table.
- `to_new_table` (boolean): true = leave the table as it is and put a sorted copy of its values (no formulas) in a new table on the same sheet. Use it when the table's formulas read other rows.

### `pages_list_placeholders` (~78 tokens)

Pages: list placeholders

Template placeholders in a .pages document (tag + current text), e.g. a letter's "Name" or "Date" fields. Needs macOS + Pages. Use before pages_fill_placeholders to see the template's field tags.

Input parameters:

- `path` (string, required): Path to the .pages document (absolute, or starting with ~).

### `pages_fill_placeholders` (~196 tokens)

Pages: fill placeholders

Fill template placeholders by tag, e.g. {"Name": "Sara", "Date": "3 October"}. Formatting is kept; the body is verified to change only there. Needs macOS + Pages. dry_run=true previews the change on a copy without touching the file. Use for template fields (Name, Date). It's the only write for page-layout documents like letters and flyers, which have no body text.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .pages document (absolute, or starting with ~).
- `values` (object, required): Placeholder tag → text, e.g. {"Name": "Sara", "Date": "3 October"} (tags from pages_list_placeholders).

### `pages_read_tables` (~75 tokens)

Pages: read tables

Every table in a .pages document: name, size, and each cell's value, shown text and formula. Needs macOS + Pages. Use before pages_set_table_cells to see the tables, their names and cell references.

Input parameters:

- `path` (string, required): Path to the .pages document (absolute, or starting with ~).

### `pages_set_table_cells` (~282 tokens)

Pages: set table cells

Write cells of an existing table in a .pages document. table = its name or number (from pages_read_tables); cells = {"B2": 1200, "C3": "تم", "D9": "=SUM(D2:D8)"} — numbers stay numbers, "=…" makes a formula, null clears. Every other cell and the body text are verified unchanged. New tables can't be created (Pages 15 doesn't script it). Needs macOS + Pages, document closed. dry_run=true previews the change on a copy without touching the file. Use to write into an existing Pages table. New tables can't be created in Pages; for text outside tables use pages_replace_all.

Input parameters:

- `cells` (object, required): {"B2": 1200, "C3": "تم", "D9": "=SUM(D2:D8)"}: numbers stay numbers, "=…" is a formula, null clears.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .pages document (absolute, or starting with ~).
- `table` (string, required): The table's name or number (from pages_read_tables).

### `keynote_slideshow` (~108 tokens)

Keynote: slideshow

Present: action = start (needs path; from_slide 1-based) | stop | next | previous. Doesn't change the file. Needs macOS + Keynote. Use to present the deck live; it doesn't change the file. To share it instead use iwork_export.

Input parameters:

- `action` (string, required): start | stop | next | previous.
- `from_slide` (integer): Slide to start from, 1-based.
- `path`: The deck to present (needed for start).

### `keynote_list_slides` (~83 tokens)

Keynote: list slides

Every slide with its text, presenter notes and whether it is hidden (via Keynote). Use slide numbers from here for slide operations. Use before slide operations to see slide numbers, text, notes and hidden state. For fonts and layouts use keynote_inspect_style.

Input parameters:

- `path` (string, required): Path to the .key deck (absolute, or starting with ~).

### `keynote_list_themes` (~43 tokens)

Keynote: list themes

Themes available in Keynote on this Mac (names to pass to keynote_set_theme). Use before keynote_set_theme or keynote_build_deck to get valid theme names.

### `keynote_inspect_style` (~94 tokens)

Keynote: inspect style

A deck's styling: current theme, available slide layouts, and per slide its layout plus each text item's text, font, size and colour. Read this before theming. Use before formatting: theme, layouts, and each text item's font, size and colour. For slide text and notes use keynote_list_slides.

Input parameters:

- `path` (string, required): Path to the .key deck (absolute, or starting with ~).

### `keynote_set_theme` (~157 tokens)

Keynote: set theme

Apply a different Keynote theme to the whole deck. Rolled back if any slide loses text. dry_run=true previews the change on a copy without touching the file. Use to switch the deck's theme only. For a full designed look (fonts, sizes, colours) use keynote_apply_design, which can set the theme too.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `theme` (string, required): Theme name from keynote_list_themes.

### `keynote_set_slide_layout` (~178 tokens)

Keynote: set slide layout

Change one slide's layout (master), e.g. "Title & Bullets". Other slides are verified untouched. dry_run=true previews the change on a copy without touching the file. Use to change one slide's layout (names from keynote_inspect_style). Not for text styling: use keynote_format_text.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `layout` (string, required): Layout (master slide) name from keynote_inspect_style.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).

### `keynote_format_text` (~282 tokens)

Keynote: format text

Font (PostScript name, e.g. "HelveticaNeue-Bold"), size (pt) and/or colour ("#RRGGBB") of one text item on a slide — pick it by item index (from keynote_inspect_style) or by a unique piece of its text (match). Alignment and shape fill are not scriptable. dry_run=true previews the change on a copy without touching the file. Use for one text item's font, size or colour. To restyle the whole deck consistently use keynote_apply_design.

Input parameters:

- `color`: Text colour "#RRGGBB".
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `font`: PostScript font name, e.g. "HelveticaNeue-Bold".
- `item`: Text item index on the slide (from keynote_inspect_style); or use match.
- `match`: A unique piece of the item's text, to pick it instead of item.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `size`: Font size in points.
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).

### `keynote_set_transition` (~249 tokens)

Keynote: set transition

Transition into a slide: effect such as dissolve, push, wipe, magic move, cube, flip, move in, reveal, "none"; duration/delay in seconds; automatic=true advances on its own. Other slides verified untouched. dry_run=true previews the change on a copy without touching the file. Use for one slide's transition. For the same transition on every slide of a new deck, pass transition= to keynote_build_deck.

Input parameters:

- `automatic`: true = advance to the next slide on its own.
- `delay`: Delay before it starts, in seconds.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `duration`: Duration in seconds.
- `effect` (string, required): Transition effect, e.g. "dissolve", "push", "magic move", or "none".
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).

### `keynote_build_deck` (~524 tokens)

Keynote: build deck

Build a new Keynote deck from an outline. slides = [{"title": "…", "body": ["bullet", "bullet"], "layout": "Title & Bullets", "notes": "…", "image": "/path/pic.png"}, …]; the first slide defaults to a title layout, the rest to Title & Bullets. theme from keynote_list_themes; optional transition for every slide (e.g. dissolve). Every slide is read back and checked; on any mismatch the new file is removed. Never overwrites. kit = a design kit (iwork_list_design_kits) for a designed deck in one call. Chart slides: add "chart": {"type": "bar", "rows": ["2025", "2026"], "columns": ["Q1", "Q2"], "data": [[1, 2], [3, 4]]} or {"type": "line", "from": "/path/report.numbers", "columns": ["Q1", "Q2"]} (header row → column names, first column → row names); chart slides default to Title Only. Table slides: "table": {"rows": [["Region", "Q1"], ["Riyadh", 1200]]} or {"from": "/path/report.numbers", "columns": ["Q1"], "max_rows": 8}; with a kit the table is styled too. Needs macOS + Keynote. Use for a new deck from an outline, including chart and table slides; then run keynote_review_deck. To change an existing deck use the slide and text tools instead.

Input parameters:

- `kit`: A design kit: a name from iwork_list_design_kits (preset or saved, e.g. "executive", "Acme") or your own {"fonts": {...}, "colors": {"title": "#RRGGBB", ...}}. Contrast is checked.
- `path` (string, required): Where to create the new .key deck; it must not exist yet.
- `slides` (array, required): The outline: [{"title": "…", "body": ["bullet", …], "notes": "…", "layout": "…", "image": "/path.png", "chart": {…}, "table": {…}}, …]; see the tool description for chart and table.
- `theme`: Theme name from keynote_list_themes; the kit's theme is used when omitted.
- `transition`: Transition for every slide, e.g. "dissolve".

### `keynote_add_table` (~442 tokens)

Keynote: add table

Add a table to a slide (1-based). rows = [["Region", "Q1"], ["Riyadh", 1200], …]: text, numbers, or null; text starting with "=" is a formula. kit (iwork_list_design_kits name or your own) styles it: header band, fonts (Arabic-aware), banding, numbers right-aligned. Optional x, y (points from top-left) and width. Every cell, font and colour is read back; other slides and tables are checked untouched. At most 50 rows × 15 columns. dry_run=true previews the change on a copy without touching the file. Needs macOS + Keynote. Use for a table on an existing slide. In a new deck, put it in keynote_build_deck's outline; to show a trend use keynote_add_chart.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `header_rows` (integer): How many rows at the top are headers (styled and kept on top).
- `kit`: A design kit: a name from iwork_list_design_kits (preset or saved, e.g. "executive", "Acme") or your own {"fonts": {...}, "colors": {"title": "#RRGGBB", ...}}. Contrast is checked.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `rows` (array, required): The table, header first: [["Region", "Q1"], ["Riyadh", 1200]]; text, numbers or null; "=…" is a formula.
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).
- `width`: Table width in points; omit for Keynote's default.
- `x`: Left edge in points (give with y); omit to let Keynote place it.
- `y`: Top edge in points (give with x).

### `keynote_review_deck` (~169 tokens)

Keynote: review deck

Design review of a deck as Keynote actually draws it: renders to PDF and reports, per slide, text drawn off the slide or past the bottom of its box (errors), text boxes drawn on top of each other, text under 18 pt, and slides that are too dense (warnings). Run it after building or restyling a deck, fix the errors, then look at a flagged slide with keynote_slide_image. The file isn't changed. Needs macOS + Keynote. Use after building or restyling a deck, before saying it's done; fix every error. To look at a flagged slide use keynote_slide_image; to check one word renders use iwork_verify_render.

Input parameters:

- `path` (string, required): Path to the .key deck (absolute, or starting with ~).

### `keynote_slide_image` (~151 tokens)

Keynote: slide image

One slide as an image (JPEG, `width` px wide), exported by Keynote, so you can look at the design yourself. Skipped slides aren't exported. The file isn't changed. Needs macOS + Keynote. Use to look at a slide's real rendering, e.g. one keynote_review_deck flagged. For a quick preview without Keynote use iwork_thumbnail (first slide, last saved state).

Input parameters:

- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).
- `width` (integer): Image width in pixels (320–2560).

### `keynote_apply_design` (~270 tokens)

Keynote: apply design

Make a deck look designed: the kit's theme (optional), then every slide's title and body fonts (Arabic-aware), sizes from a type scale, and colours from a contrast-checked palette. kit = a name from iwork_list_design_kits or your own {"fonts": {...}, "colors": {...}}. Text is verified unchanged. dry_run=true previews the change on a copy without touching the file. Use to restyle a whole deck from a kit. For one text item use keynote_format_text. Preview with dry_run, then run keynote_review_deck.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `kit`: A design kit: a name from iwork_list_design_kits (preset or saved, e.g. "executive", "Acme") or your own {"fonts": {...}, "colors": {"title": "#RRGGBB", ...}}. Contrast is checked.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `set_theme` (boolean): true = also switch to the kit's theme first.

### `keynote_set_slide_text` (~226 tokens)

Keynote: set slide text

Fill a slide's title and/or body box (body may be a list of bullet lines). The title box is the top-most text box, the body the largest other one. Other slides and text boxes are verified untouched. dry_run=true previews the change on a copy without touching the file. Use to fill or rewrite one slide's title and body. To change the same words across the deck use keynote_replace_text; for speaker notes use keynote_set_presenter_notes.

Input parameters:

- `body`: New body: a string, or a list of bullets; omit to leave it.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).
- `title`: New title text; omit to leave it.

### `keynote_add_chart` (~349 tokens)

Keynote: add chart

Add a chart to a slide from data. rows = series names, columns = category names, data = one list of numbers per row (rows × columns). type: bar | stacked_bar | horizontal_bar | stacked_horizontal_bar | line | area | stacked_area | pie | scatter (or *_3d). group_by: row | column. Text and other charts are verified untouched. dry_run=true previews the change on a copy without touching the file. Use for a chart on an existing slide. In a new deck, put the chart in keynote_build_deck's outline instead; for exact numbers use keynote_add_table.

Input parameters:

- `columns` (array, required): Column names, e.g. ["Q1", "Q2", "Q3"].
- `data` (array, required): One list of numbers per row name, each as long as columns.
- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `group_by` (string): row (each row is a series) | column.
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `rows` (array, required): Row names, e.g. ["2025", "2026"] (series when group_by="row").
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).
- `type` (string): bar | stacked_bar | horizontal_bar | stacked_horizontal_bar | line | area | stacked_area | pie | scatter (or a *_3d variant).

### `keynote_add_image` (~250 tokens)

Keynote: add image

Place an image file (png, jpg, heic, pdf…) on a slide; optional x/y position and width in points. All text verified untouched. dry_run=true previews the change on a copy without touching the file. Use to place a picture or logo on a slide. For data, use keynote_add_chart or keynote_add_table rather than a picture of a chart.

Input parameters:

- `dry_run` (boolean): true = make the change on a throwaway copy, run every check, and return what would change; the file itself is not touched. Use it before broad or risky edits and show the user.
- `image` (string, required): Image file to place (png, jpg, heic, pdf…).
- `path` (string, required): Path to the .key deck (absolute, or starting with ~).
- `slide` (integer, required): Slide number, 1-based (keynote_list_slides lists them).
- `width`: Image width in points; height keeps the aspect ratio.
- `x`: Left edge in points (give with y); omit to let Keynote place it.
- `y`: Top edge in points from the slide's top-left corner (give with x).

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/arkanji-iwork-studio/iwork-studio#diagnostics

## Score history

- 2026-11-04: 96
- 2026-11-03: 96
- 2026-11-02: 95
- 2026-11-01: 95
- 2026-10-31: 94
- 2026-10-30: 94
- 2026-10-29: 78
- 2026-10-28: 78
- 2026-10-27: 77
- 2026-10-26: 77
- 2026-10-25: 76
- 2026-10-24: 76
- 2026-10-23: 76
- 2026-10-22: 75
- 2026-10-21: 75
- 2026-10-20: 74
- 2026-10-19: 74
- 2026-10-18: 73
- 2026-10-17: 73
- 2026-10-16: 72
- 2026-10-15: 72
- 2026-10-14: 71
- 2026-10-13: 49
- 2026-10-12: 56
- 2026-10-04: 82

## Common questions

### What is the iWork Studio MCP server?

iWork Studio is an MCP server listed in the public MCP registry as io.github.Arkanji/iwork-studio. Create, edit, design and export Apple Numbers, Keynote and Pages files. Verified and Arabic-safe. This page covers its PyPI package (iwork-studio).

### Is the iWork Studio MCP server safe to use?

iWork Studio scores 96 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 4 November 2026. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the iWork Studio MCP server expose?

iWork Studio exposes 65 tools: iwork_capabilities, iwork_samples, iwork_read, iwork_list_backups, pages_preflight, and 60 more. Their descriptions and schemas cost roughly 13,026 tokens of context every time the server is loaded.

### Is the iWork Studio MCP server still maintained?

iWork Studio is still listed as active in the MCP registry. We last reached this channel on 4 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the iWork Studio MCP server under?

iWork Studio declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- PyPI project: https://pypi.org/project/iwork-studio/
- Socket report: https://socket.dev/pypi/package/iwork-studio
- Repository: https://github.com/Arkanji/iwork-studio
- Changelog RSS feed: https://verifymcp.io/servers/arkanji-iwork-studio/iwork-studio.xml
- Changelog JSON feed: https://verifymcp.io/servers/arkanji-iwork-studio/iwork-studio.json
- HTML version of this page: https://verifymcp.io/servers/arkanji-iwork-studio/iwork-studio
