Skip to main content
Glama
indesign-nutria-mcp

indesign-nutria-mcp

by nutriandrea

Published to npm as indesign-nutria-mcp. Your agent starts it with npx -y indesign-nutria-mcp — see Quick Start. InDesign still runs on your machine: the server reaches it over a local bridge, and nothing leaves the box.


šŸš€ Why This Exists

InDesign automation has been broken for 20 years. ExtendScript is ancient. CEP panels are over-engineered. UXP scripting requires a CS degree. You just want AI to do the layout.

This MCP server fixes that. Clone it, point your agent at it, and InDesign is yours to drive like a puppeteer.

šŸ†š MCP vs The Old Ways

MCP (this)

ExtendScript

CEP Panels

UXP Scripts

Manual

Learn in

30 seconds

3 weeks

2 months

1 month

0 (slow)

AI-native

āœ… Any MCP agent

āŒ

āŒ

āŒ

āŒ

Tool count

199

Unlimited (you write them)

5-20 typical

5-20 typical

āˆž (manual)

Error messages

Human-readable

Opaque crashes

Varies

Better

N/A

Setup time

5 minutes

10 minutes

2 hours

30 minutes

Instant

Undo support

āœ… Built-in

āŒ

Varies

āŒ

āœ…

Search text

text_search("pattern")

Write 50 lines

Maybe

Maybe

Ctrl+F

Debug mode

āœ… Stack traces

āŒ

Varies

Limited

N/A

Remote control

āœ… Anywhere STDIO works

āŒ InDesign-only

āŒ

āŒ

N/A

Maintenance

Zero (we handle 199 tools)

You write+test everything

Fragile

Fragile

N/A


Related MCP server: InDesign UXP MCP Server

✨ What It Can Do

šŸ“ Text & Typography (27 tools)

Create frames, set content, apply paragraph/character styles, control formatting, search with GREP, find/replace, apply fonts, set columns, insets, auto-size, vertical justification, drop caps, keep options, hyphenation, tabs, paragraph rules, text wrap, baseline, linking/unlinking.

šŸŽØ Shapes & Objects (20 tools)

Rectangles, ellipses, polygons, lines, groups, anchored objects — create, modify, delete, list, arrange.

šŸ–¼ļø Images (5 tools)

Place from disk, query metadata, fit/proportional, adjust brightness/contrast, relink.

šŸŽ­ Colors & Swatches (6 tools)

CMYK/RGB/LAB/spot swatches, ink list, gradients, apply to fill/stroke.

šŸ“„ Pages & Documents (15 tools)

Create, open, save, close, getInfo, listOpen — plus page add/delete/duplicate/move/getInfo/listAll/applyMaster, sections.

šŸ“Š Tables (20 tools)

Create, set cells, add/delete rows/columns, merge/split, alignment, fills, strokes, insets, header/footer, row/column sizing, table styles, cell styles.

🧬 Styles (10 tools)

Paragraph, character, object styles — create, list, duplicate, delete, apply.

šŸ—ļø Masters & Books (10 tools)

Master spreads — create, duplicate, apply, delete, list, getPages. Books — list, open, getDocuments, synchronize.

šŸ“¤ Export (9 tools)

PDF, EPUB, HTML, JPG, PNG, package — plus preflight, font/swatch/table lists, and script_run() for custom ExtendScript.

šŸ” Find & Replace (5 tools)

GREP find/replace, format-aware find/replace.

šŸ”„ Transform (5 tools)

Align, distribute, rotate, scale, flip.

šŸ”— Interactive (5 tools)

Hyperlinks, buttons, cross-reference anchors.

šŸ“š References (10 tools)

Footnotes, endnotes, cross-references, index entries/topics/generation.

šŸ“‹ Lists (6 tools)

Define, apply to paragraph/selection, remove, restart numbering.

🧩 XML & Data Merge (11 tools)

XML tags, import/export, data merge with CSV/TSV/XML sources.

šŸ”§ Other

Layers, effects (drop shadow, feather, transparency), TOC, sections, undo/redo, undo groups.


🧠 Included AI Skills

Ten skills ship with the repo, auto-loaded by trigger keywords when you use any AI agent:

#

Skill

What it does for you

1

Aesthetic Preference

Asks 8 questions before any creative work — font, palette, style, margins. Builds a persistent JSON profile.

2

Layout Readability

Validates overlays, contrast, orphans/widows, hierarchy, spacing, overflow before delivery.

3

Export & Verify

Mandatory modify → export JPG → analyze pixels → fix → repeat cycle.

4

Import Word

Imports .docx, maps Word styles to InDesign paragraph styles.

5

Batch Operations

Applies the same modification across N pages.

6

Image Optimize

Place, resize, DPI check, relink. Profiles for print (300dpi CMYK) vs web (72dpi RGB).

7

Table Format

Creates and styles tables — columns, rows, borders, fills, merge cells.

8

Template Manager

Save/load reusable page templates.

9

Export Batch

Export same document to multiple formats at once with different profiles.

10

Style Extractor

Scans .indd files, extracts full style profile (fonts, colors, styles, masters, margins) as JSON, replicates on new layout.


šŸ—ļø Architecture

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”       STDIO        ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”     WebSocket     ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Any AI Agent       │ ◄────────────────► │  indesign-nutria-mcp │ ◄───────────────► │  Adobe InDesign   │
│  (Claude, OpenCode, │                    │  (Node.js MCP Server) │     port 8120     │  + UXP Plugin     │
│   Cursor, etc.)     │                    │  35 handlers         │                    │  + ExtendScript    │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜                    │  199 tools           │                    ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
                                           │  10 AI skills        │
                                           ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Two ways to reach InDesign

The MCP protocol always speaks stdio. Only the last hop differs, and you pick one per platform:

macOS

Windows

With plugin

UXP panel in plugin/

UXP panel in plugin/

Without plugin

node bridge-proxy.mjs (JXA/osascript)

COM_BRIDGE_ENABLED=true (in-process COM)

Both are supported. The plugin is the richer path (it also emits change events); the proxy exists for setups where you cannot or do not want to load a UXP panel. See WINDOWS.md for the Windows bridge and start-bridge.sh for the macOS one.

Full breakdown: the four macOS processes, the request lifecycle, the error contract and the trust boundaries are in docs/architecture.md.

Full Handler Catalog

Handler

Tools

Key Features

AnchoredObject

5

create, getSettings, release, setPosition, setProperties

Book

4

list, open, getDocuments, synchronize

Changes

1

getStatus — what changed since T?, without touching InDesign

Color

6

swatch CRUD, inks, gradients, apply

DataMerge

5

selectDataSource, listFields, mergeRecords, export, removeDataSource

Document

9

create/open/save/close, getInfo, listOpen, getState, getPageStories, getStoryPages

Effect

4

drop shadow, feather, transparency, gradient feather

Export

11

multi-format export, print packaging, batch folder, preflight, fonts/swatches/tables/masters/XML, executeScript, script_run

Font

5

list, find, change, missing check, glyph insert

Grep

4

find, replace, findFormat, replaceFormat

Image

5

place, info, adjust, fit, relink

Index

4

addEntry, createTopic, generate, listTopics

Interactive

5

hyperlinks, buttons, anchors

Layer

5

create, delete, list, reorder, setProperties

List

6

define, apply, remove, restart numbering

Master

6

create, duplicate, apply, delete, list, getPages

Note

4

footnotes, endnotes

Object

6

shapes, groups, image links

Page

7

add, delete, duplicate, move, getInfo, listAll, applyMaster

Preview

1

document — render a page as an image the agent can see

Recipe

2

flow a placed text story across pages, export with a named PDF preset — each in one undo group

Resources

6

list/update/embed/unembed links, getLinkInfo

Section

4

create, list, setNumbering, delete

Shape

6

rectangle/ellipse/line/polygon create, delete, modify

Style

7

paragraph/character/object styles CRUD

Table

16

create, cells, rows/columns, merge/split, alignment, fills, strokes, header/footer

TableStyle

4

tableStyle, cellStyle CRUD

Text

7

frames, content, stories, findReplace, BOM filtering, timeout

TextAdvanced

20

columns, wrap, links, drop caps, rules, tabs, formatting read/write, search

Typography

1

read-only audit of fonts in use, size/leading pairs, style usage and overrides

Toc

4

createStyle, generate, update, listStyles

Transform

5

align, distribute, rotate, scale, flip

Undo

5

undo, redo, history, beginGroup, endGroup

Xml

6

tags CRUD, tag/untag items, import/export

Xref

3

create, list, updateFormat

199

35 handlers, no duplicates

tests/contract/toolInventory.test.ts pins this number, so the table cannot drift from the code without failing CI.

Verified end-to-end against InDesign 2026 (21.3) on the plugin-free macOS path: MCP stdio → WebSocket bridge → JXA proxy → InDesign, creating a document, adding a text frame, reading it back, and closing.

Closing documents from an agent? document_close defaults to saveOptions: "no" — the only value that cannot raise a modal dialog. "ask" opens a save prompt, and "yes" opens a Save As dialog for a document that was never saved; both block the script until the bridge times out. Save first with document_save { filePath }, then close.

MCP Resources

URI

What it gives you

mcp://session/status

Active document session state

mcp://bridge/status

WebSocket bridge health + queue depth

mcp://tools/inventory

Full 199-tool catalog for agent self-discovery

mcp://document/active

Currently active document info


šŸŽÆ Use Cases

šŸ¢ Agency Production

"Create a 200-page catalog from this CSV. Product name in 14pt Arial Bold, price in 12pt Arial Regular, description in 10pt. Add a red 'SALE' badge on page 1, 50, 100, 150. Export as PDF."

šŸ“š Book Publishing

"Import this Word document. Map Heading 1 to 'Chapter Title' style, Normal to 'Body Text'. Add running headers with page numbers. Generate a TOC. Export to PDF with crop marks."

šŸŽØ Creative Automation

"Take this InDesign template and generate 50 variations with different colors and text from this spreadsheet. Export each as a separate PDF."

šŸ­ Print Production

"Check all 200 files for missing fonts and broken links. Fix them. Export as PDF/X-1a."

šŸ”„ Continuous Publishing

"Every morning at 8am, update the daily newspaper template with fresh content from our CMS, preflight, and upload to the web server."


⚔ Quick Start (30 seconds)

Two halves: the server your agent runs — one line from npm, no clone and no build — and the bridge that reaches InDesign on this machine.

1ļøāƒ£ Connect InDesign

The server listens on 127.0.0.1:8120; something still has to drive InDesign. Pick by platform:

macOS — with the UXP panel (also emits change events)

  1. Open UXP Developer Tool

  2. Load the plugin/ directory — it ships in this repository, so clone it once: git clone https://github.com/nutriandrea/adobe-indesign-mcp (no build step)

  3. Open the MCP Bridge panel — it connects on its own

macOS — without the panel

git clone https://github.com/nutriandrea/adobe-indesign-mcp
cd adobe-indesign-mcp
npm install            # gives the proxy its `ws` dependency; nothing to build
./start-bridge.sh      # launches InDesign, then the JXA proxy

Windows — no bridge process. The COM bridge drives InDesign in-process, so Windows needs neither the clone nor a second process; it is enabled by the env var in the config you use in step 2 (see examples/claude-desktop.windows.json). See WINDOWS.md for the details and caveats.

2ļøāƒ£ Connect your AI agent

Both hosts start the same published package — nothing to clone, nothing to build.

Claude Desktop:

{
  "mcpServers": {
    "indesign": {
      "command": "npx",
      "args": ["-y", "indesign-nutria-mcp"]
    }
  }
}

OpenCode (project opencode.json, or the same block in ~/.config/opencode/opencode.json):

{
  "mcp": {
    "servers": {
      "indesign": {
        "type": "local",
        "command": ["npx", "-y", "indesign-nutria-mcp"]
      }
    }
  }
}

On OpenCode the servers live under mcp.servers, not mcpServers, and the command is an array — that is the documented V2 shape.

Ready-made files for Claude Desktop, Cursor and Claude Code — including the Windows COM variant — are in examples/. Each one is copy-paste.

3ļøāƒ£ Start creating

šŸ¤– "Create a landscape A3 document with 3 pages."
šŸ¤– "Add a blue rectangle covering the top half of page 1."
šŸ¤– "In the rectangle, add text 'Hello World' in white, Arial Bold 72pt."
šŸ¤– "Export page 1 as JPG."

šŸ“ Notes for MCP client developers

Building a custom client (script, harness, test runner) against this server? Two lessons from real-world integration:

Pitfall

Fix

Large responses arrive split across stdout chunks. preview_document at 150ppi returns ~100+ KB of base64 — more than one pipe chunk. Parsing each data event with JSON.parse(chunk) silently drops the pieces and the call appears to hang forever.

Accumulate stdin into a buffer and only JSON.parse complete newline-delimited lines: buf += chunk; while ((i = buf.indexOf('\n')) >= 0) { parse(buf.slice(0, i)); buf = buf.slice(i + 1); }

Image tools return image content, not text. preview_document responds with { type: 'image', mimeType: 'image/png', data: '<base64>' } — reading .text yields undefined.

Read the base64 payload from content[0].data (check content[0].type).

Every message is a single-line JSON-RPC object terminated by \n; there is no framing beyond the newline.


šŸ†• What's New in v1.6.0

Fixed

āš ļø

Read this before you script

The layout skill named tools that do not exist

skills/indesign/indesign-mcp-layout/SKILL.md advertised an mcp_indesign_* prefix the registry never used, and pointed connectivity checks at health_check and list_ai_skills — neither is registered by any handler. A skill is instruction, so naming a tool that does not resolve sends a model into a second failure on top of the one it was recovering from.

Change

What it means for you

The skill names real tools now

It points at the bare tool names the registry actually exposes, and documents the read-only resources (mcp://bridge/status and friends), which never touch InDesign and cost no round-trip.

Skills are guarded like the instructions are

tests/unit/skillGuidance.test.ts walks every SKILL.md under skills/ and marketplace/ and fails on a tool name the registry does not know — and on a skill whose frontmatter name disagrees with its folder. Regression-proven against the bug above.

Marketplace counts are checked too

verify:tool-counts now reads the markdown under marketplace/, so the number quoted to a buyer is checked alongside the README, llms.txt and the package description.

Added

Change

What it means for you

Client configs

examples/ holds copy-paste configs for Claude Desktop (macOS, and the Windows COM variant), Cursor and Claude Code, plus examples/README.md.

Registry and bundle metadata

server.json — generated from package.json and gated by verify:server-json — and mcpb/manifest.json, so the MCP Registry and an MCP Bundle can be published from the same release.

Release automation

release.yml takes a tag through verify and verify:pack, publishes to npm with provenance, builds the .mcpb and attaches it to the GitHub Release with the CHANGELOG notes. publish-mcp-registry.yml chains off the release, so the registry never advertises a version npm does not have. validate-release-tag.sh refuses a tag that disagrees with package.json or has no CHANGELOG section.

A packaged companion skill

marketplace/agensi/ prepares the skill for a third-party marketplace, with npm run build:agensi asserting the archive shape the reviewer requires — one top-level folder, SKILL.md at its root, text files only. Like mcpb/, it does not ship to npm.

For AI agents

āš ļø

Read this before you script

Pass document_id — don't trust the front tab

Every tool takes an optional document_id: a document name (as document_listOpen reports it) or a 0-based index. With more than one document open the front tab is not reliably the intended target, and a call that lands on the wrong document edits the wrong file. Call document_getState, then pass document_id on everything that follows.

Change

What it means for you

document_id on every tool

199 tools, one extra optional parameter. Omit it and a tool acts on the front document — exactly the previous behaviour, so nothing you already send changes.

A target that is not open fails loudly

document_id 'Gone.indd' is not open. Open documents: A.indd, B.indd, classified document_not_found and suggesting document_listOpen. Previously the script silently edited whichever document happened to be frontmost.

One shared resolver

app.activeDocument was written inline in 197 places across 32 handlers; __resolveDocument() is now injected with every script and every document-scoped handler resolves through it, so the target holds for the whole call — even if the front tab changes mid-script.

Safe by construction

document_id reaches InDesign as a JSON string literal and is matched by name, so it is never executable. It is bound per call in AsyncLocalStorage, so a call that awaits before dispatching cannot be hijacked by a concurrent one.

Changed

Change

What it means for you

The "pin the document" advice is now executable

The connect-time contract used to tell the host to ask which document was meant. It now names document_id and how to pass it, and the shipped indesign-mcp-layout skill points at it too.

For AI agents

āš ļø

Read this before you script

Failures now tell you how to recover

A failing call returns a JSON envelope { ok: false, code, message, suggested_next_tool?, suggested_args? } instead of prose. Branch on code, and call suggested_next_tool when it is present rather than guessing.

State before action

document_getState returns the front document's geometry, its index, every open document, the active layer and the selection count in one round-trip. Call it before mutating instead of assembling that state from four separate calls.

A recipe is one call, not a chain

recipe_flowTextStory places a text file and flows it across pages; recipe_exportPreset applies a named PDF preset and exports. Measured live, the flow cost 1 call / 215 bytes against 6 calls / 786 bytes for the nearest atomic chain — which cannot place the text at all.

Change

What it means for you

Structured error envelopes

Sixteen failure classes, each with a suggested_next_tool checked against the live registry (a bad frame index suggests document_getInfo; a timeout suggests document_listOpen). ExtendScript errors carry line, file and stack.

A workflow contract on connect

The server advertises roughly 680 tokens of instructions — bridge bootstrap, state-before-action, the document_close save trap, the error-envelope shape and undo grouping.

document_getState

One round-trip replaces the document_getInfo + document_listOpen + layer + selection sequence.

preview_document takes documentIndex

Render any open document, not only the front one. Exports are capped at 4 MB and re-rendered once at a fitting resolution before failing with output_too_large.

Two new recipes

recipe_flowTextStory and recipe_exportPreset, bringing the catalog to 197.

Opt-in success envelope

RESPONSE_ENVELOPE_SUCCESS (or responseEnvelope.success) wraps successful results as { ok: true, ... }, so a single key covers success and failure. Off by default, because it changes the response shape.

A smaller, correct package

A files allowlist replaces the .npmignore denylist: 1.6 MB unpacked, down from 4.4 MB — and the bridge-proxy.mjs that the macOS launcher execs is now actually shipped.

Changed

Change

What it means for you

Error responses are JSON text

The failure message is preserved verbatim inside the envelope; isError: true is unchanged.

document_close defaults saveOptions to "no"

"ask" raised a modal save dialog that blocked until the bridge timed out and could leave InDesign wedged; "no" cannot prompt. Call document_save first when you want the work kept.

Fixed

Fix

What it means

Every call after undo_beginGroup failed

wrapExtendScript set app.scriptPreferences.undoMode, a property InDesign does not have. Undo groups now run through app.doScript(..., UndoModes.ENTIRE_SCRIPT).

undo, redo and undo_history threw on every call

They read document.undoable / document.redoable, which are not part of the DOM.

The server reported itself as v1.0.0

Four releases after v1.0.0 it advertised the old literal on initialize. SERVER_VERSION is now the single source of truth, and two gates keep it honest.

For AI agents

āš ļø

Read this before you script

Prefer the typed tools

export_executeScript and script_run have no cancellation. A hung script leaves the bridge busy and every later call queues behind it until the host restarts. Loops, font enumeration and multi-mutation probes belong in the typed tools, which validate arguments and time out. See skills/indesign/indesign-mcp-layout.

Change

What it means for you

The bridge protocol is now canonical

Responses are { type: 'result', id, result } or { type: 'error', id, error }. Legacy success/response types are still accepted on input, but an unknown type with a valid id is now an explicit error instead of a silently resolved promise.

Plugin-free macOS setup

start-bridge.sh launches InDesign and the JXA proxy — no UXP panel to load.

The JXA proxy no longer blocks or corrupts

Scripts reach osascript through a temp file instead of shell-quoted -e strings, and execution is asynchronous with a FIFO queue. A script containing a quote, backslash or newline can no longer be mangled, and the MCP server keeps answering while InDesign works.

Configurable InDesign version

INDESIGN_APP replaces the hardcoded "Adobe InDesign 2024" process name.

The panel connects itself

The MCP Bridge panel connects on open, so there is no Connect click to miss.

InDesign 2026 anchoring

Creating an anchored object no longer depends on move(), which 2026 rejects for insertion-point targets.

The shipped config starts everywhere

It declared the wrong MCP transport and would have aborted startup on macOS and Linux had it enabled the Windows COM bridge. Both are now covered by a test.

Change

What it means for you

Agent vision: preview_document

Render any page as an image the agent can actually see — layout mistakes get caught in the loop, not after export.

Batch PDF: export_batchFolder

Export every .indd in a folder to PDF in one call, with per-file results.

Windows COM bridge

Run with zero plugins via COM_BRIDGE_ENABLED=true. See WINDOWS.md.

Hardened file paths

Every file-touching tool validates paths up front — traversal attempts (..) and system directories are rejected before InDesign ever sees them (now 11 guarded call sites).

Change

What it means for you

InDesign 2026 ready

Color and anchored-object tools work on 2026's renamed enums — no more silent failures after upgrading.

Real env var config

Every setting in .env.example is now honored: BRIDGE_PORT, HTTP_BRIDGE_ENABLED, LOG_LEVEL, … Invalid values never block startup.

No more 30s hangs

If the plugin isn't connected, tool calls fail instantly with "Bridge is not connected" instead of timing out.

One escaping path

All handlers share a single battle-tested ExtendScript string escaper.

Hardened path validation

File-path checks now use true containment (no prefix-collision escapes).

Honest status

getStatus().connected reflects actual plugin connectivity, and CI enforces coverage gates.

Feature

Tools

What it solves

Text formatting read

text_getFormatting

Get font/size/style per text range. No more custom scripts.

Character style apply

text_applyCharStyle

Apply to a range in one call.

Font apply

text_applyFont

Apply font family/style/size to a range.

Text search

text_search, text_searchFormatting

GREP + format-aware search with paragraph-relative positions.

Story→Page map

document_getPageStories, document_getStoryPages

Which stories are on which pages.

Undo groups

undo_beginGroup, undo_endGroup

Group operations into one undo step.

Debug mode

script_run(code, debug=true)

Full ExtendScript stack traces.

BOM filtering

Text reads

\ufeff and \u0004 filtered by default.

Timeout/maxResults

text_getStories, etc.

Custom limits for large documents.

MCP resources

mcp://tools/inventory

Agent auto-discovers all tools.


āš™ļø Configuration

All settings have defaults — zero config required. To customize, copy .env.example to .env or pass a JSON file; environment variables win over the file:

BRIDGE_PORT=8120            # WebSocket port the UXP plugin connects to
HTTP_BRIDGE_ENABLED=false   # optional REST fallback bridge
HTTP_BRIDGE_PORT=3000
SERVER_TRANSPORT=stdio      # stdio | websocket
LOG_LEVEL=info              # debug | info | warn | error
COM_BRIDGE_ENABLED=false    # Windows only: drive InDesign over COM, no plugin
RESPONSE_ENVELOPE_SUCCESS=false  # wrap successful results as { ok: true, ... }

The plugin-free macOS proxy takes its own variables:

INDESIGN_APP="Adobe InDesign 2026"     # matches your installed version
BRIDGE_WS_URL=ws://127.0.0.1:8120
BRIDGE_TIMEOUT_MS=120000               # a long export is not a hang
BRIDGE_RECONNECT_DELAY_MS=3000

Precedence: environment variables > JSON config file > defaults. Invalid values are ignored, never fatal.


🪟 Windows

Two ways to run on Windows: the standard UXP plugin flow (identical to macOS) or the new opt-in COM bridge that needs no plugin at all. See WINDOWS.md.


šŸ“¦ Project Structure

ā”œā”€ā”€ src/
│   ā”œā”€ā”€ server/          # MCP server (STDIO transport)
│   ā”œā”€ā”€ bridge/          # WebSocket bridge, ScriptExecutor, protocol, COM executor
│   ā”œā”€ā”€ handlers/        # 35 handler modules (199 tools)
│   ā”œā”€ā”€ schemas/         # Zod parameter schemas
│   ā”œā”€ā”€ core/            # Session tracking
│   ā”œā”€ā”€ types/           # TypeScript definitions
│   └── utils/           # Config, logger, security, JSON polyfill
ā”œā”€ā”€ plugin/              # UXP panel (index.html, index.js, manifest.json)
ā”œā”€ā”€ examples/            # Copy-paste client configs (Claude Desktop, Cursor, Claude Code)
ā”œā”€ā”€ mcpb/                # MCP Bundle manifest (npm run build:mcpb packs the archive)
ā”œā”€ā”€ server.json          # MCP Registry manifest (generated from package.json)
ā”œā”€ā”€ llms.txt             # llms.txt index for agents and directories
ā”œā”€ā”€ bridge-proxy.mjs     # Plugin-free macOS proxy (JXA/osascript)
ā”œā”€ā”€ jxa-driver.js        # Runs one ExtendScript inside InDesign
ā”œā”€ā”€ start-bridge.sh      # Launches InDesign + the proxy
ā”œā”€ā”€ skills/              # Agent-facing skills shipped with the server
ā”œā”€ā”€ marketplace/         # Store listings that package a skill for third-party channels
ā”œā”€ā”€ tests/               # 1131 tests (vitest)
ā”œā”€ā”€ .opencode/skills/    # 10 AI agent skills
ā”œā”€ā”€ docs/                # Documentation and fork audits
ā”œā”€ā”€ media/               # Social preview, hero images
ā”œā”€ā”€ dist/                # Compiled output
└── opencode.json        # MCP configuration

šŸ“‹ Requirements

Requirement

Version

Adobe InDesign

2022+ (2024/2025/2026 recommended)

Node.js

18+

OS

macOS or Windows (Linux can run the MCP server, but no InDesign automation path exists there)


🧪 Development

npm test            # Run 1131 tests
npm run test:watch  # Watch mode
npm run build       # TypeScript compile
npm run lint        # ESLint

Live smoke test (macOS)

npm run smoke:macos

npm test covers the handlers, the protocol and the startup sequence, but it cannot see the seams between the four separate processes on the plugin-free macOS path. That gap is not hypothetical: 965 tests once passed against a build whose bridge never listened on its port, so every tool call failed in the field and nothing went red.

smoke:macos drives the real chain — MCP stdio → WebSocket bridge → JXA proxy → InDesign — against a running InDesign, creating a document, writing to it, reading it back, and closing it. It creates one unsaved document and cleans up after itself. It needs a desktop app, so it is not part of npm test and does not run on CI; it exits 0 with a SKIP notice if InDesign is not open.

Run it by hand before releasing a change to the bridge, the protocol, the proxy or the driver.

npm run smoke:recipes   # the live gate for the two recipes

smoke:recipes does what the unit tests cannot: it flows a long text file with recipe_flowTextStory, checks the document grew by exactly what the tool reported, calls undo once to confirm the whole flow is a single undo step, then exercises both preset paths of recipe_exportPreset. Same rules as above — needs a desktop app, exits 0 with a SKIP notice when InDesign is closed.

npm run smoke:target    # the live gate for document_id

smoke:target opens two documents and proves the thing document_id is for: a call that names the non-front document edits that document and leaves the front one alone. It then checks index targeting, that omitting document_id still acts on the front document (the additive guarantee), that an unopened target arrives as a document_not_found envelope suggesting document_listOpen, and that a hostile document_id is inert rather than executed. It closes only the documents it created, never saves, and leaves every pre-existing document untouched. Same rules as above — needs a desktop app, exits 0 with a SKIP notice when InDesign is closed.


šŸ—ŗļø Roadmap

  • InDesign Server — headless server support for CI/CD pipelines

  • Live preview — stream InDesign canvas to agent

  • Template marketplace — share and discover InDesign templates

  • Natural language → layout — describe a page, get a page

  • Batch PDF processing — apply changes across hundreds of files

  • VSCode extension — control InDesign from your editor


šŸ“š Deep Dives


šŸ¤ Contributing

PRs welcome! The handler pattern is designed to be simple:

  1. Create src/handlers/YourHandler.ts

  2. Define tools with Zod schemas

  3. Register in IndesignMcpServer.ts

  4. Add tests

Check CONTRIBUTING.md for details.


šŸ™ Acknowledgements

This server is a merge of what four people found in the field. Each of these contributions fixed something that was genuinely broken, not a cosmetic preference.

  • @graydini — built the Windows COM bridge that this project runs on today: a persistent cscript process holding one live COM connection, NEVER_INTERACT so export dialogs can never block, and the path-containment hardening in validateFilePath. See PR #1 and the PR #21 merge. Also reported the InDesign 2026 API incompatibilities that shaped official Windows support: renamed and read-only ColorModel and AnchorPoint enums, and move() no longer accepting InsertionPoint targets. Earlier fixes from the same line of work landed on main as safe basedOn/pointSize reads in style listing, rich style-creation parameters, \n → paragraph-break normalization in text content, and an eval-free JSON polyfill that survives the ExtendScript sanitizer. Their branch also carried COM-specific operational knowledge — that a hung script wedges the Windows bridge with no way out, that fontFamily, italic and weight throw under COM, and that InDesign must be visibly running — now documented in WINDOWS.md and the layout skill. A file-by-file audit of what was still unmerged is in docs/forks/graydini-windows-com-bridge-audit.md.

  • @advaitakelkar — diagnosed why the macOS proxy returned junk, in PR #22. Three real bugs in one report: osascript sends script results on stderr, so every successful call was read as a failure; the script was passed through shell -e quoting, which mangles any quote, backslash or newline; and the app name was hardcoded to "Adobe InDesign 2024", so every other version failed. The same work also moved the connection handshake out of app.doScript, where it was being parsed as ExtendScript. Adopted and extended — see What's New in v1.4.2.

  • @ijeetu — two fixes, both easy to miss and both real. The MCP Bridge panel now connects on open instead of waiting for a Connect click, so the panel no longer looks dead when the server is already running. Earlier, they traced a parse failure in the JSON polyfill to sanitizeCode() rewriting eval( into a comment and orphaning the parentheses — the shipped wrapExtendScript already reflects that fix.


šŸ“„ License

MIT Ā© Andrea Cacioppo


Available Tools

197 tools
anchoredObject_createC

Create an anchored object at an insertion point or attach to a text frame

Inputs: width (required), height (required), storyIndex (required), contentType (required), +7 more.

anchoredObject_getSettingsC

Get anchored object settings

Inputs: itemIndex (required), pageIndex (required), document_id.

anchoredObject_releaseC

Release anchored object to regular page item

Inputs: itemIndex (required), pageIndex (required), document_id.

anchoredObject_setPositionC

Reposition an anchored object

Inputs: itemIndex (required), pageIndex (required), xOffset, yOffset, +4 more.

anchoredObject_setPropertiesC

Set multiple anchored object properties

Inputs: itemIndex (required), pageIndex (required), properties (required), document_id.

book_getDocumentsC

List all documents in a book

Inputs: bookIndex (required), document_id.

book_listB

List all open books in InDesign

Inputs: document_id.

book_openC

Open an InDesign book file (.indb)

Inputs: filePath (required), document_id.

book_synchronizeC

Synchronize book documents with the style source

Inputs: bookIndex (required), document_id.

cellStyle_createC

Create a cell style in the active document

Inputs: name (required), basedOn, topInset, fillColor, +7 more.

cellStyle_listB

List all cell styles in the active document

Inputs: document_id.

changes_getStatusA

Report document change activity pushed by the plugin over the bridge — no InDesign round-trip. Pass since (epoch ms) to learn whether anything changed after that moment, e.g. to skip re-rendering a preview when nothing changed.

Inputs: since, document_id.

color_applyB

Apply a color swatch to selected item (fill or stroke)

Inputs: swatchName (required), target, document_id.

color_gradient_createC

Create a gradient swatch

Inputs: name (required), stops (required), type, document_id.

color_ink_listB

List all inks in the document

Inputs: document_id.

color_swatch_createC

Create a new color swatch

Inputs: name (required), model (required), red, blue, +7 more.

color_swatch_deleteC

Delete a color swatch by name

Inputs: name (required), document_id.

color_swatch_listB

List all color swatches in the document

Inputs: document_id.

dataMerge_exportC

Export the merged document to a file

Inputs: format (required), outputPath (required), document_id.

dataMerge_listFieldsB

List available data merge fields from the current data source

Inputs: document_id.

dataMerge_mergeRecordsC

Merge records into the document using the selected data source

Inputs: linkImages, document_id, recordsPerPage, generatePreview, +1 more.

dataMerge_removeDataSourceB

Remove the current data source from the document

Inputs: document_id.

dataMerge_selectDataSourceC

Select a data source (CSV/TSV/XML) file for data merge

Inputs: filePath (required), document_id.

document_closeC

Close the active InDesign document with save options

Inputs: document_id, saveOptions.

document_createC

Create a new InDesign document with specified dimensions, margins, and settings

Inputs: slug, bleed, pages, width, +5 more.

document_getInfoB

Get detailed information about the active document including page count, dimensions, and margins

Inputs: document_id.

document_getPageStoriesC

Get all stories that appear on a specific page

Inputs: pageIndex (required), document_id.

document_getStateA

Get the editing state in one call: the front document (name, path, page count, size, margins), the index of that document, the names of every open document, the active layer, and how many objects are selected. Prefer this over chaining document_getInfo and document_listOpen

Inputs: document_id.

document_getStoryPagesC

Get all pages covered by a specific story (for finding which pages a story lives on)

Inputs: storyIndex (required), document_id.

document_listOpenC

List all currently open InDesign documents

Inputs: document_id.

document_openC

Open an existing InDesign document from file path

Inputs: filePath (required), showWindow, document_id.

document_saveC

Save the active InDesign document, optionally to a specific path

Inputs: filePath, document_id, saveOptions.

effect_applyDropShadowC

Apply a drop shadow effect to an object

Inputs: itemIndex (required), pageIndex (required), blur, mode, +5 more.

effect_applyFeatherC

Apply a feather effect to an object

Inputs: itemIndex (required), pageIndex (required), type, noise, +3 more.

effect_applyGradientFeatherC

Apply a gradient feather effect to an object

Inputs: itemIndex (required), pageIndex (required), type, angle, +3 more.

effect_applyTransparencyC

Set transparency and blend mode for an object

Inputs: itemIndex (required), pageIndex (required), opacity, blendMode, +3 more.

export_batchFolderA

Export every InDesign (.indd) file in a folder to PDF in one run — opens each document invisibly and reports per-file results

Inputs: folderPath (required), outputDir, overwrite, timeoutMs, +1 more.

export_documentC

Export the active document to a specified format (PDF, JPG, PNG, EPUB, HTML, package)

Inputs: format (required), options, filePath, document_id.

export_executeScriptC

Execute arbitrary ExtendScript code in InDesign

Inputs: code (required), document_id.

export_getFontsB

Get all fonts used in the active document

Inputs: document_id.

export_getMasterSpreadsC

List all master spreads in the active document

Inputs: document_id.

export_getSwatchesB

Get all swatches (colors, gradients, tints) from the active document

Inputs: document_id.

export_getTablesC

List all tables in the active document

Inputs: pageIndex, document_id.

export_getXmlTagsC

List XML tags in the active document

Inputs: document_id.

export_preflightC

Run a preflight check on the active document

Inputs: profile, document_id, waitForCompletion.

font_changeC

Change font of selected text

Inputs: fontName (required), size, fontStyle, document_id.

font_findC

Search fonts by name

Inputs: query (required), document_id.

font_glyph_insertC

Insert a special glyph at the cursor position

Inputs: glyphId (required), fontName, document_id.

font_listC

List all available fonts

Inputs: document_id.

font_missing_checkC

Check for missing fonts in the document

Inputs: document_id.

grep_findC

Find text using GREP pattern in the active document

Inputs: grepWhat (required), scope, document_id, includeFootnotes, +1 more.

grep_findFormatC

Find text with specific formatting

Inputs: findWhat (required), scope, format, document_id.

grep_replaceC

Find and replace text using GREP pattern

Inputs: grepWhat (required), replaceWith (required), scope, document_id.

grep_replaceFormatC

Find and replace text while changing formatting

Inputs: findWhat (required), replaceWith (required), format, document_id.

group_listC

List all groups in a page

Inputs: pageIndex (required), document_id.

group_ungroupC

Ungroup a group by index

Inputs: pageIndex (required), groupIndex (required), document_id.

image_adjustC

Adjust image properties (brightness, contrast, etc.)

Inputs: itemIndex (required), pageIndex (required), contrast, brightness, +1 more.

image_fitC

Fit image content to its frame

Inputs: fitting (required), itemIndex (required), pageIndex (required), document_id.

image_infoC

Get metadata for an image on a page

Inputs: itemIndex (required), pageIndex (required), document_id.

image_listC

List all placed images in a page

Inputs: pageIndex (required), document_id.

image_placeC

Place an image from a file path onto a page

Inputs: x (required), y (required), filePath (required), pageIndex (required), +3 more.

index_addEntryC

Add an index entry for the current selection

Inputs: topic (required), sortBy, subTopic, pageRange, +2 more.

index_createTopicC

Create a new index topic

Inputs: name (required), parent, document_id.

index_generateC

Generate the index on a specified page

Inputs: pageIndex (required), title, bounds, document_id, +1 more.

index_listTopicsC

List all index topics in the active document

Inputs: document_id.

interactive_listAnchorsC

List all cross-reference anchors in the active document

Inputs: document_id.

interactive_listButtonsC

List all buttons in the active document

Inputs: document_id.

layer_createC

Create a new layer in the active document

Inputs: name, locked, visible, printable, +2 more.

layer_deleteD

Delete a layer

Inputs: layerIndex (required), document_id.

layer_listC

List all layers in the active document

Inputs: document_id.

layer_reorderC

Move a layer to a new position in the layer stack

Inputs: newIndex (required), layerIndex (required), document_id.

layer_setPropertiesC

Set layer properties (visibility, lock, etc.)

Inputs: layerIndex (required), locked, visible, printable, +1 more.

list_applyToParagraphC

Apply a list to a specific paragraph

Inputs: listName (required), pageIndex (required), frameIndex (required), paragraphIndex (required), +2 more.

list_applyToSelectionC

Apply a list to the current text selection

Inputs: listName (required), level, document_id.

list_defineC

Define a new numbered or bulleted list

Inputs: name (required), type (required), document_id, continueNumbering.

list_listB

List all defined lists in the document

Inputs: document_id.

list_removeFromParagraphC

Remove list formatting from a paragraph

Inputs: pageIndex (required), frameIndex (required), paragraphIndex (required), document_id.

list_restartNumberingC

Restart numbering at a specific paragraph

Inputs: pageIndex (required), frameIndex (required), startNumber (required), paragraphIndex (required), +1 more.

master_applyC

Apply a master spread to a specific page

Inputs: pageIndex (required), masterName (required), document_id.

master_createC

Create a new master spread with a given name and page count

Inputs: name (required), pageCount, document_id.

master_deleteC

Delete a master spread by name

Inputs: name (required), document_id.

master_duplicateC

Duplicate an existing master spread

Inputs: name (required), newName (required), document_id.

master_getPagesC

List all pages on a master spread

Inputs: name (required), document_id.

master_listB

List all master spreads with page count

Inputs: document_id.

note_addEndnoteC

Add an endnote to a story

Inputs: text (required), storyIndex (required), document_id, numberingType.

note_addFootnoteC

Add a footnote to a specific paragraph in a story

Inputs: text (required), storyIndex (required), paragraphIndex (required), prefix, +4 more.

note_footnoteOptionsC

Set footnote options for the active document

Inputs: layout, prefix, suffix, separator, +3 more.

note_listFootnotesC

List all footnotes in a story or the active document

Inputs: storyIndex, document_id.

page_addC

Add a new page to the active document at a specified position

Inputs: position, document_id, referencePage.

page_applyMasterC

Apply a master spread to a page

Inputs: pageIndex (required), masterName (required), document_id.

page_deleteC

Delete a page from the active document by index

Inputs: index (required), document_id.

page_duplicateC

Duplicate a page in the active document

Inputs: index (required), document_id.

page_getInfoC

Get detailed information about a specific page

Inputs: index (required), document_id.

page_listAllB

List all pages in the active document with their properties

Inputs: document_id.

page_moveC

Move a page to a new position in the active document

Inputs: index (required), toIndex (required), document_id.

preview_documentC

Render a page of the active document as an image (PNG or JPEG) so you can visually inspect layout, colors and typography

Inputs: format, pageIndex, document_id, documentIndex, +1 more.

recipe_exportPresetA

Export the active document to PDF using a named PDF export preset in one call — validates the preset first and, if it is wrong, fails with the list of valid preset names

Inputs: filePath (required), presetName (required), timeout, document_id, +1 more.

recipe_flowTextStoryA

Place a text file and flow it across as many pages as it needs in one call — creates the frames, links them, adds pages when it runs out, and reports where the story ended and whether it still overflows

Inputs: filePath (required), timeout, maxPages, pageIndex, +2 more.

redoC

Redo the last undone operation

Inputs: document_id.

resources_getLinkInfoC

Get detailed status for a specific link

Inputs: linkIndex (required), document_id.

script_runC

Execute arbitrary ExtendScript code with optional debug mode that returns line-level error stack traces

Inputs: code (required), debug, timeout, document_id.

section_createC

Create a new section starting at a specified page

Inputs: startPageIndex (required), name, document_id, includePrefix, +3 more.

section_deleteC

Delete a section by its index

Inputs: sectionIndex (required), document_id.

section_listB

List all sections in the active document

Inputs: document_id.

section_setNumberingC

Update page numbering for a section

Inputs: sectionIndex (required), pageNumberStart (required), document_id, numberingStyle.

shape_createC

Create a geometric shape (rectangle, ellipse, polygon, line)

Inputs: type (required), bounds (required), pageIndex (required), fillColor, +4 more.

shape_deleteC

Delete a shape from a page

Inputs: itemIndex (required), pageIndex (required), document_id.

shape_ellipse_createC

Create an ellipse or circle on a page

Inputs: x (required), y (required), width (required), height (required), +5 more.

shape_line_createC

Create a graphic line on a page

Inputs: x1 (required), x2 (required), y1 (required), y2 (required), +4 more.

shape_listC

List all shapes on a page

Inputs: pageIndex (required), document_id.

shape_modifyC

Modify properties of an existing shape

Inputs: itemIndex (required), pageIndex (required), x, y, +7 more.

shape_polygon_createC

Create a polygon on a page

Inputs: x (required), y (required), width (required), height (required), +6 more.

shape_rectangle_createC

Create a rectangle on a page

Inputs: x (required), y (required), width (required), height (required), +6 more.

style_createCharacterC

Create a new character style

Inputs: name (required), color, basedOn, fontStyle, +4 more.

style_createParagraphC

Create a new paragraph style

Inputs: name (required), color, basedOn, leading, +7 more.

style_deleteC

Delete a style

Inputs: name (required), type (required), document_id.

style_duplicateD

Duplicate a style

Inputs: name (required), type (required), newName (required), document_id.

style_listCharacterC

List all character styles in the active document

Inputs: document_id.

style_listObjectC

List all object styles in the active document

Inputs: document_id.

style_listParagraphB

List all paragraph styles in the active document

Inputs: document_id.

table_addColumnC

Add a column to a table at a specific index

Inputs: pageIndex (required), tableIndex (required), atIndex, document_id.

table_addRowC

Add a row to a table at a specific index

Inputs: pageIndex (required), tableIndex (required), atIndex, document_id.

table_createC

Create a table on a page at specified bounds with row/column count

Inputs: bounds (required), pageIndex (required), rows, columns, +1 more.

table_deleteColumnC

Delete a column from a table

Inputs: pageIndex (required), tableIndex (required), columnIndex (required), document_id.

table_deleteRowC

Delete a row from a table

Inputs: rowIndex (required), pageIndex (required), tableIndex (required), document_id.

table_getInfoC

Get detailed info about a table (rows, columns, cell contents)

Inputs: pageIndex (required), tableIndex (required), document_id.

table_listB

List all tables on a page or in the document

Inputs: pageIndex, document_id.

table_mergeCellsC

Merge a range of cells into one

Inputs: endRow (required), startRow (required), endColumn (required), pageIndex (required), +3 more.

table_setCellC

Set content of a specific cell

Inputs: row (required), column (required), content (required), pageIndex (required), +2 more.

table_setCellAlignmentC

Set cell text alignment

Inputs: rowIndex (required), pageIndex (required), tableIndex (required), columnIndex (required), +3 more.

table_setCellFillC

Set a cell's fill color and tint

Inputs: rowIndex (required), fillColor (required), pageIndex (required), tableIndex (required), +3 more.

table_setCellInsetC

Set cell inset spacing

Inputs: top (required), left (required), right (required), bottom (required), +5 more.

table_setCellStrokeC

Set cell edge stroke properties

Inputs: edge (required), rowIndex (required), pageIndex (required), tableIndex (required), +5 more.

table_setHeaderFooterC

Set header and footer row counts

Inputs: pageIndex (required), tableIndex (required), footerRows, headerRows, +2 more.

table_setRowColumnSizeC

Set row height or column width

Inputs: size (required), type (required), index (required), pageIndex (required), +3 more.

table_splitCellC

Split a merged cell

Inputs: rowIndex (required), pageIndex (required), tableIndex (required), columnIndex (required), +3 more.

tableStyle_createC

Create a table style in the active document

Inputs: name (required), basedOn, fillColor, spaceAfter, +6 more.

tableStyle_listB

List all table styles in the active document

Inputs: document_id.

text_addFrameC

Add a text frame to a specified page with content

Inputs: bounds (required), pageIndex (required), content, document_id.

text_applyCharStyleC

Apply a character style to a range of characters within a paragraph

Inputs: endChar (required), startChar (required), styleName (required), storyIndex (required), +2 more.

text_applyFontC

Apply font family, style, and/or size to a range of characters within a paragraph

Inputs: endChar (required), startChar (required), storyIndex (required), paragraphIndex (required), +4 more.

text_applyParagraphStyleC

Apply a paragraph style to a text range

Inputs: pageIndex (required), styleName (required), frameIndex (required), document_id, +1 more.

text_findReplaceC

Find and replace text content

Inputs: findWhat (required), replaceWith (required), scope, document_id.

text_getContentC

Get content from a text frame

Inputs: pageIndex (required), frameIndex (required), timeout, document_id, +1 more.

text_getFormattingB

Get detailed formatting (font, size, style, character style) for each text style range in a paragraph

Inputs: storyIndex (required), paragraphIndex (required), document_id, includeControlChars.

text_getStoriesC

List all stories in the active document with optional timeout and max results

Inputs: timeout, maxResults, document_id, includeControlChars.

text_getTextFramesC

List all text frames on a page with optional timeout

Inputs: pageIndex (required), timeout, document_id, includeControlChars.

text_linkFramesC

Link (thread) two text frames together

Inputs: sourcePageIndex (required), targetPageIndex (required), sourceFrameIndex (required), targetFrameIndex (required), +1 more.

text_searchFormattingC

Find text ranges with specific formatting (font family, style, or size) within a story

Inputs: storyIndex (required), timeout, fontStyle, pointSize, +3 more.

text_setAutoSizeC

Set auto-sizing type for a text frame

Inputs: pageIndex (required), frameIndex (required), autoSizingType (required), document_id, +1 more.

text_setColumnsC

Set column count and gutter for a text frame

Inputs: count (required), pageIndex (required), frameIndex (required), gutter, +1 more.

text_setContentC

Set content of a text frame

Inputs: content (required), pageIndex (required), frameIndex (required), document_id.

text_setDropCapC

Set drop cap options for a paragraph

Inputs: pageIndex (required), frameIndex (required), paragraphIndex (required), lines, +3 more.

text_setFirstBaselineC

Set first baseline options for a text frame

Inputs: pageIndex (required), frameIndex (required), offsetType (required), minOffset, +1 more.

text_setHyphenationC

Set hyphenation settings for a paragraph

Inputs: pageIndex (required), frameIndex (required), hyphenation (required), paragraphIndex (required), +5 more.

text_setIgnoreWrapC

Set whether text frame ignores text wrap

Inputs: pageIndex (required), frameIndex (required), ignoreWrap (required), document_id.

text_setInsetSpacingC

Set text frame inset spacing

Inputs: top (required), left (required), right (required), bottom (required), +3 more.

text_setKeepOptionsC

Set keep options (widow/orphan control) for a paragraph

Inputs: pageIndex (required), frameIndex (required), document_id, keepWithNext, +4 more.

text_setParagraphRuleAboveC

Add or modify rule above a paragraph

Inputs: ruleOn (required), pageIndex (required), ruleColor (required), ruleWidth (required), +8 more.

text_setParagraphRuleBelowC

Add or modify rule below a paragraph

Inputs: ruleOn (required), pageIndex (required), ruleColor (required), ruleWidth (required), +8 more.

text_setTabsC

Set tab stops for a paragraph

Inputs: tabStops (required), pageIndex (required), frameIndex (required), paragraphIndex (required), +1 more.

text_setTextWrapC

Set text wrap preferences for an object

Inputs: mode (required), itemIndex (required), pageIndex (required), side, +2 more.

text_setVerticalJustificationC

Set vertical justification for a text frame

Inputs: pageIndex (required), frameIndex (required), justification (required), document_id.

text_unlinkFramesC

Unlink a text frame from its thread

Inputs: pageIndex (required), frameIndex (required), document_id.

toc_createStyleC

Create or modify a Table of Contents style in the active document

Inputs: name (required), title, entryStyle, titleStyle, +5 more.

toc_generateC

Generate a Table of Contents on a specified page

Inputs: pageIndex (required), styleName (required), title, bounds, +2 more.

toc_listStylesB

List all Table of Contents styles in the active document

Inputs: document_id.

toc_updateC

Update (regenerate) an existing TOC from its story

Inputs: storyIndex (required), styleName, document_id.

transform_alignC

Align objects on a page relative to selection, margins, or page

Inputs: alignMode (required), pageIndex (required), itemIndices (required), relativeTo, +1 more.

transform_distributeC

Distribute objects evenly on a page

Inputs: pageIndex (required), itemIndices (required), distributeMode (required), bounds, +1 more.

transform_flipC

Flip an object horizontally or vertically

Inputs: axis (required), itemIndex (required), pageIndex (required), document_id.

transform_rotateC

Rotate an object by a specified angle

Inputs: angle (required), itemIndex (required), pageIndex (required), layerIndex, +1 more.

transform_scaleC

Scale an object by percentages

Inputs: itemIndex (required), pageIndex (required), scaleX, scaleY, +2 more.

undoC

Undo the last operation

Inputs: document_id.

undo_beginGroupA

Begin an undo group. While active, each subsequent script runs as a single undo step, so one Undo reverts a whole tool call (e.g. a multi-step recipe). It cannot merge separate calls into one step — each call is still its own undo entry. Call undo_endGroup() to restore the default.

Inputs: document_id.

undo_endGroupB

End the undo group and restore the default undo behavior

Inputs: document_id.

undo_historyC

Get undo/redo state information

Inputs: document_id.

xml_addTagC

Create a new XML tag

Inputs: name (required), document_id.

xml_deleteTagC

Delete an XML tag by name

Inputs: name (required), document_id.

xml_exportC

Export document structure as XML

Inputs: filePath (required), document_id.

xml_importC

Import XML file into the document

Inputs: filePath (required), document_id.

xml_listTagsC

List all XML tags in the active document

Inputs: document_id.

xml_tagPageItemC

Apply an XML tag to a page item

Inputs: tagName (required), itemIndex (required), pageIndex (required), document_id.

xref_createC

Create a cross-reference in the active document

Inputs: sourceText (required), targetName (required), targetType (required), document_id, +1 more.

xref_listB

List all cross-references in the active document

Inputs: document_id.

xref_updateFormatC

Update the format of a cross-reference

Inputs: index (required), appliedFormat (required), document_id.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.2/5.0

    Scored across 197 tools

    Disambiguation1/5

    Many tools duplicate one another (export_executeScript/script_run, page_applyMaster/master_apply, table_list/export_getTables, font_list/export_getFonts), making selection ambiguous. Although some descriptions clarify intent, the sheer overlap forces agents to guess.

    Naming Consistency2/5

    Names mix noun_verb and verb_noun patterns (page_add vs export_document) and use inconsistent camelCase (document_getStoryPages) alongside snake_case. Prefixes vary by domain (table_ vs tableStyle_, font_missing_check), preventing a predictable convention.

    Tool Count1/5

    197 tools is far beyond the 50+ threshold for extreme mismatch, even for a feature-rich domain like InDesign automation. Many tools exist only to duplicate or wrap others, bloating the surface unnecessarily.

    Completeness4/5

    The surface covers most InDesign operations—documents, pages, text, tables, images, styles, export, data merge, scripts—with few obvious gaps. Missing a group_create and book_create/book_close prevents a perfect score, but core workflows are workable.

    Maintenance

    ActivityActive
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      C
      quality
      C
      maintenance
      Enables AI assistants to automate Adobe InDesign publishing workflows, including document creation, text formatting, image placement, PDF export, and more via 35+ professional tools.
      36
      60 npm
      61
      MIT
    • A
      license
      C
      quality
      C
      maintenance
      A Model Context Protocol (MCP) server that gives AI assistants direct, native control over Adobe InDesign via a UXP plugin bridge, with ~130 tools covering documents, pages, text, graphics, styles, master spreads, books, and export.
      100
      60 npm
      110
      MIT