indesign-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@indesign-mcpshow me the paragraph styles"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
InDesign MCP Server
A custom MCP server that lets Claude Code read, search, and manipulate Adobe InDesign documents. macOS only.
Claude connects to InDesign via osascript → ExtendScript. No plugins, no UXP — just a single Python file.
What it can do
Read documents — pages overview, stories (threaded text flows), paragraph styles, character styles
Search & replace — find text across the entire document, bulk replace
Inspect structure — text frames per page with bounds/overflow info, tables with contents
Run arbitrary scripts — raw ExtendScript escape hatch for anything the built-in tools don't cover
Related MCP server: Claude-LMStudio-Bridge
Prerequisites
macOS (uses
osascript)Adobe InDesign 2025 or 2026 (tested with 2026 v21.4.1)
uv — Python package runner (
brew install uvorcurl -LsSf https://astral.sh/uv/install.sh | sh)Claude Code — claude.ai/code (CLI, desktop app, or IDE extension)
You do NOT need to install Python separately — uv handles it.
Setup
Clone this repo somewhere on your Mac:
git clone <repo-url> ~/indesign-mcp cd ~/indesign-mcpOpen InDesign and open the document you want to work with.
Start Claude Code in the project directory:
claudeThe MCP server starts automatically (configured in
.mcp.json).Try it out — ask Claude to read your document:
"What's in this InDesign document?" "Find all instances of 'Chapter' in the document" "Show me the paragraph styles"
That's it. No manual server launch needed.
If your InDesign version is different
Edit server.py line 13 and change the app name:
APP_NAME = "Adobe InDesign 2026" # Change to match your versionAdjusting the .mcp.json path
The .mcp.json file contains an absolute path to server.py. After cloning, update it to match your location:
{
"mcpServers": {
"indesign": {
"command": "uv",
"args": ["run", "--python", "3.13", "--with", "mcp[cli]", "mcp", "run", "/YOUR/PATH/HERE/server.py"]
}
}
}Available tools
Tool | Description |
| Document overview: name, pages, layers, story count |
| All pages with dimensions, item counts, master pages |
| All text stories with content previews |
| Full text of a story by index |
| Paragraphs with applied styles (paginated) |
| Text frames on a page with position, size, overflow status |
| Search for text across the document (max 100 results) |
| Find and replace text document-wide |
| List all paragraph styles |
| List all character styles |
| Find all tables in the document |
| Read a table as a 2D array |
| Execute arbitrary ExtendScript — the escape hatch |
Hard-won lessons (read before extending)
These are things we learned the painful way. Save yourself the debugging.
ExtendScript has no JSON
InDesign's ExtendScript engine (even in 2026) does not have JSON.stringify or JSON.parse. The server includes a polyfill that gets prepended to every script automatically. If you write standalone .jsx files or use run_script, the polyfill is already included.
Units are document units, not points
All geometry values (column widths, cell insets, bounds, positions) use the document's ruler units — usually inches. Setting table.columns[0].width = 48 means 48 inches, not 48 points. Check with doc.viewPreferences.horizontalMeasurementUnits. Font sizes and leading remain in points (they're typographic units).
findChangeTextOptions, not findTextPreferences
wholeWord and caseSensitive go on app.findChangeTextOptions, NOT on app.findTextPreferences. The API splits what to find (findTextPreferences) from how to find (findChangeTextOptions).
Tables: use bodyRowCount for everything
Create tables with insertionPoint.tables.add({columnCount: N, bodyRowCount: M, headerRowCount: 0}). Use bodyRowCount for all rows including visual headers — ExtendScript's headerRows accessor causes errors. Don't prefix frame contents with \r before inserting a table.
Table styling pattern
// Dark header row
cell.fillColor = doc.swatches.itemByName("Black");
cell.texts[0].fillColor = doc.swatches.itemByName("Paper");
// Alternating row shading
cell.fillColor = doc.swatches.itemByName("Black");
cell.fillTint = 10;
// Cell insets (in inches!)
cell.topInset = 0.03;Text wrap with hand-made contour paths
For wrapping text around an image's actual shape (not its bounding box):
Create a polygon that traces the figure silhouette (~60+ anchor points)
Place the image INTO the polygon (
polygon.place(File))Fit proportionally (
polygon.fit(FitOptions.PROPORTIONALLY))Set contour wrap (
polygon.textWrapPreferences.textWrapMode = TextWrapModes.CONTOUR)
The polygon does double duty: clips the image AND serves as the text wrap contour. Don't use a separate rectangle for the image — its white background will cover text behind it.
Detect Edges doesn't work on white-background images. You need transparent PNGs (alpha channel) or hand-made paths.
Auto-sizing text frames
Set textFramePreferences.autoSizingType = AutoSizingTypeEnum.HEIGHT_ONLY on content frames, then read geometricBounds[2] to find actual bottom — use that to position subsequent elements.
Adding new tools
Add a function decorated with
@mcp.tool()inserver.pyBuild the ExtendScript as a Python f-string — use
{{/}}for JS bracesEnd the script with a
JSON.stringify(...)expression (last expression's value is returned)Use
_escape_jsx()to sanitize any user-provided strings before embeddingCall
run_jsx(script)to execute
Architecture
Claude Code → MCP tool call → server.py builds ExtendScript string
↓
run_jsx() prepends JSON polyfill
↓
writes temp .jsx file
↓
osascript tells InDesign to execute it
↓
InDesign returns result via stdoutSingle file, no build step, no dependencies beyond mcp[cli].
License
Do what you want with it.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/matthijs1000/indesign-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server