Skill 07 · Adobe Illustrator Integrations
Subchapter 7.1
references/automation-and-mcp.mdMarkdown11 KBView on GitHub
Every route from outside Illustrator ends in the same place: ExtendScript running inside Illustrator. This file covers starting that script from an OS script or another language, passing it parameters, reading its result, wrapping it as an MCP server for AI tools, and the rules for computer-use sessions when no scripting route exists.
Illustrator registers the COM class Illustrator.Application (version-specific Illustrator.Application.<major>). Its scripting methods, from the type library Illustrator installs:
| Method | Parameters |
|---|---|
DoJavaScriptFile | JavaScriptFile, optional Arguments (array), optional ExecutionMode |
DoJavaScript | JavaScriptCode (a string of ExtendScript), optional Arguments, optional ExecutionMode |
DoScript | Action, From (action set), optional Dialogs: plays a recorded action |
ExecutionMode values: 1 aiNeverShowDebugger, 2 aiDebuggerOnError, 3 aiBeforeRunning. Use 1 for unattended runs. The call returns when the script finishes, with the value of the script’s last expression as a string.
PowerShell:
$ai = New-Object -ComObject Illustrator.Application # starts Illustrator if it is not running
$result = $ai.DoJavaScriptFile('C:\Users\<user>\Documents\AcmeTools\export-artboards.jsx', @(), 1)
Write-Output $resultPython (pywin32):
import win32com.client
app = win32com.client.Dispatch("Illustrator.Application")
result = app.DoJavaScriptFile(r"C:\Users\<user>\Documents\AcmeTools\export-artboards.jsx", [], 1)
print(result)If the call fails with “JavaScript code was missing”, the .jsx path does not exist or cannot be read.
osascript - "/Users/<user>/Documents/AcmeTools/export-artboards.jsx" <<'APPLESCRIPT'
on run argv
tell application "Adobe Illustrator"
do javascript "$.evalFile(new File(arguments[0]))" with arguments {item 1 of argv}
end tell
end run
APPLESCRIPTThe script path travels as an osascript argument and reaches ExtendScript through with arguments, so it never becomes part of either script’s source: a path with quotes cannot break the call or inject code. do javascript also accepts a file reference, but some versions have rejected file arguments; evaluating the file with $.evalFile from a fixed code string works across versions. Check the application name for your install if tell application cannot find it.
Adobe.extendscript-debug): run and debug .jsx against a running Illustrator. This is Adobe’s replacement for ExtendScript Toolkit.-run script.jsx): 32-bit, will not run on macOS 10.15 or later, and needs a workaround for debugger error #1116. Keep it only for old pipelines that already depend on it.arguments handling differs between launchers (ExtendScript Toolkit -run and BridgeTalk do not pass them through), so use arguments only to hand over the script path, as in the AppleScript launcher above, and pass the job’s parameters in a sidecar file that the script reads, with the result written to another file. Folder.temp in ExtendScript is the OS temp folder, which both sides can find. For the script side of external runs (arguments[], $.getenv, $.fileName, logging), see the adobe-illustrator-scripting skill’s External Invocation & Argument Passing section.
Parameter file written by the caller as key=value lines, %TEMP%\acme-export-params.txt on Windows or $TMPDIR/acme-export-params.txt on macOS:
outputFolder=C:\Users\<user>\Documents\AcmeExports
scale=200ExtendScript has no JSON parser, and a key=value file needs none, so nothing in the file is ever run as code. For nested data, bundle json2.js and use its JSON.parse, never eval.
export-artboards.jsx (ES3):
(function () {
var paramsFile = new File(Folder.temp + '/acme-export-params.txt');
var resultFile = new File(Folder.temp + '/acme-export-result.json');
// key=value lines; values stay plain text, so nothing in the file is evaluated.
function readParams(file) {
var params = {};
file.encoding = 'UTF-8';
if (!file.open('r')) { return null; }
while (!file.eof) {
var line = file.readln().replace(/\r$/, '');
var at = line.indexOf('=');
if (at > 0) { params[line.substring(0, at)] = line.substring(at + 1); }
}
file.close();
return params;
}
// JSON string literal: escapes backslash, quote, and every control character,
// so multi-line error messages still parse.
function quote(value) {
return '"' + String(value).replace(/[\\"\u0000-\u001f]/g, function (ch) {
if (ch === '\\' || ch === '"') { return '\\' + ch; }
return '\\u' + ('000' + ch.charCodeAt(0).toString(16)).slice(-4);
}) + '"';
}
function writeResult(ok, message) {
resultFile.encoding = 'UTF-8';
resultFile.open('w');
resultFile.write('{"ok":' + (ok ? 'true' : 'false') + ',"message":' + quote(message) + '}');
resultFile.close();
}
var previous = app.userInteractionLevel;
app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
try {
if (!paramsFile.exists) { writeResult(false, 'Parameter file not found.'); return; }
var params = readParams(paramsFile);
if (!params) { writeResult(false, 'Cannot read the parameter file.'); return; }
var scale = Number(params.scale);
if (!params.outputFolder || !isFinite(scale) || scale <= 0) {
writeResult(false, 'Parameters need outputFolder and a positive scale.');
return;
}
if (!app.documents.length) { writeResult(false, 'No document is open.'); return; }
// Export work goes here (see the adobe-illustrator-scripting skill).
writeResult(true, 'Exported at ' + scale + '% to ' + params.outputFolder);
} catch (e) {
writeResult(false, 'Line ' + e.line + ': ' + e.message);
} finally {
app.userInteractionLevel = previous;
}
}());Batch caller (Windows):
@echo off
setlocal
set "_script=%~dp0export-artboards.jsx"
set "_params=%TEMP%\acme-export-params.txt"
set "_result=%TEMP%\acme-export-result.json"
if exist "%_result%" del /Q "%_result%"
powershell -NoProfile -Command "[System.IO.File]::WriteAllLines($env:_params, @(('outputFolder=' + $env:USERPROFILE + '\Documents\AcmeExports'), 'scale=200'), (New-Object System.Text.UTF8Encoding))"
powershell -NoProfile -Command "$ai = New-Object -ComObject Illustrator.Application; [void]$ai.DoJavaScriptFile($env:_script, @(), 1)"
if not exist "%_result%" (
echo Illustrator did not write a result file.
exit /b 1
)
type "%_result%"
findstr /c:"\"ok\":true" "%_result%" >nul || exit /b 1
exit /b 0bash caller (macOS):
#!/usr/bin/env bash
set -euo pipefail
script="$(cd "$(dirname "$0")" && pwd)/export-artboards.jsx"
params="${TMPDIR%/}/acme-export-params.txt"
result="${TMPDIR%/}/acme-export-result.json"
rm -f "$result"
printf 'outputFolder=%s\nscale=200\n' "$HOME/Documents/AcmeExports" > "$params"
# The path is an osascript argument, passed on with "with arguments", so it is never part of any script source.
osascript - "$script" <<'APPLESCRIPT'
on run argv
tell application "Adobe Illustrator"
do javascript "$.evalFile(new File(arguments[0]))" with arguments {item 1 of argv}
end tell
end run
APPLESCRIPT
[[ -f "$result" ]] || { echo "Illustrator did not write a result file." >&2; exit 1; }
cat "$result"
grep -q '"ok":true' "$result"Pipeline tips:
saveAs or export calls into an output folder; never save over the source document.C:\Users\<user>\AppData\Local\AcmeTools\logs\ or ~/Library/Logs/AcmeTools/.userInteractionLevel as above.An Illustrator MCP server is a process an AI tool starts (usually over stdio) that exposes tools; each tool runs ExtendScript in Illustrator through COM or AppleScript and returns the result.
AI tool <-> MCP server (stdio) <-> COM / AppleScript <-> Illustrator (ExtendScript)Keep the tool set small and specific:
| Tool | Input | Output |
|---|---|---|
document_info | none | Open documents, active document, artboards, color mode, units |
list_layers | document index | Layer names, visibility, lock state, item counts |
run_script | script name from an allowlisted folder, JSON parameters | The script’s JSON result |
export_artboard_png | artboard index, scale | Path to the PNG (or the image itself) so the model can see the result |
save_copy_as | output path inside the output folder, format | Saved path |
try/catch and return { ok, message }.Registering a local server in VS Code (.vscode/mcp.json):
{
"servers": {
"illustrator": {
"type": "stdio",
"command": "node",
"args": ["${workspaceFolder}/tools/illustrator-mcp/server.js"],
"env": { "ACME_SCRIPTS_DIR": "${workspaceFolder}/tools/illustrator-mcp/scripts" }
}
}
}Before installing one, read its source and check:
Use a computer-use tool only for what has no scripting route: some panels and dialogs, generative features, and visual checks a script cannot make.