#
tokens: 1358/50000 5/5 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── pyproject.toml
├── README.md
├── src
│   └── illustrator
│       ├── __init__.py
│       └── server.py
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Illustrator MCP Server
Adobe Illustrator is compatible with JavaScript. In fact, some super big stuff you need to programmatically generate with these scripts. Bots are good at JavaScript.

This MCP server let's bots send scripts straight to Illustrator and look at the result.

Since it depends on AppleScript, it's only compatible with MacOS. and I've only tested it with Claude Desktop.
`~/Library/Application\ Support/Claude/claude_desktop_config.json`

```
{
    "mcpServers": {
        "illustrator": {
            "command": "uv",
            "args": [
                "--directory",
                "/Users/you/code/mcp/illustrator-mcp-server",
                "run",
                "illustrator"
            ]
        }
    }
}
```

```

--------------------------------------------------------------------------------
/src/illustrator/__init__.py:
--------------------------------------------------------------------------------

```python
from . import server
import asyncio


def main():
    """Main entry point for the package."""
    asyncio.run(server.main())


# Optionally expose other important items at package level
__all__ = ["main", "server"]

```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "illustrator"
version = "0.1.0"
dependencies = [
    "httpx>=0.28.1",
    "mcp>=1.1.1",
    "pillow>=11.0.0",
]

[project.scripts]
illustrator = "illustrator:main"

```

--------------------------------------------------------------------------------
/src/illustrator/server.py:
--------------------------------------------------------------------------------

```python
import subprocess
import tempfile
import os
import asyncio
import mcp.types as types
from mcp.server.models import InitializationOptions
from mcp.server import NotificationOptions, Server
import mcp.server.stdio
import base64
from PIL import Image
import io


server = Server("illustrator")


@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="view",
            description="View a screenshot of the Adobe Ullustrator window",
            inputSchema={
                "type": "object",
                "properties": {},
            },
        ),
        types.Tool(
            name="run",
            description="Run ExtendScript code in Illustrator",
            inputSchema={
                "type": "object",
                "properties": {
                    "code": {
                        "type": "string",
                        "description": "ExtendScript/JavaScript code to execute in Illustrator. It will run on the current document. you only need to make the document once",
                    }
                },
                "required": ["code"],
            },
        ),
    ]


def captureIllustrator() -> list[types.TextContent | types.ImageContent]:
    with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
        screenshot_path = f.name

    try:
        activate_script = """
            tell application "Adobe Illustrator" to activate
            delay 1
            tell application "Claude" to activate
        """
        subprocess.run(["osascript", "-e", activate_script])

        result = subprocess.run(
            [
                "screencapture",
                "-R",
                "0,0,960,1080",
                "-C",
                "-T",
                "2",
                "-x",
                screenshot_path,
            ]
        )

        if result.returncode != 0:
            return [types.TextContent(type="text", text="Failed to capture screenshot")]

        with Image.open(screenshot_path) as img:
            if img.mode in ("RGBA", "LA"):
                img = img.convert("RGB")
            buffer = io.BytesIO()
            img.save(buffer, format="JPEG", quality=50, optimize=True)
            compressed_data = buffer.getvalue()
            screenshot_data = base64.b64encode(compressed_data).decode("utf-8")

        return [
            types.ImageContent(
                type="image",
                mimeType="image/jpeg",
                data=screenshot_data,
            )
        ]

    finally:
        if os.path.exists(screenshot_path):
            os.unlink(screenshot_path)


def runIllustratorScript(code: str) -> list[types.TextContent]:
    script = code.replace('"', '\\"').replace("\n", "\\n")

    applescript = f"""
        tell application "Adobe Illustrator"
            do javascript "{script}"
        end tell
    """

    result = subprocess.run(
        ["osascript", "-e", applescript], capture_output=True, text=True
    )

    if result.returncode != 0:
        return [
            types.TextContent(
                type="text", text=f"Error executing script: {result.stderr}"
            )
        ]

    success_message = "Script executed successfully"
    if result.stdout:
        success_message += f"\nOutput: {result.stdout}"

    return [types.TextContent(type="text", text=success_message)]


@server.call_tool()
async def handleCallTool(
    name: str, arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
    if name == "view":
        return captureIllustrator()
    elif name == "run":
        if not arguments or "code" not in arguments:
            return [types.TextContent(type="text", text="No code provided")]
        return runIllustratorScript(arguments["code"])
    else:
        raise ValueError(f"Unknown tool: {name}")


async def main():
    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name="illustrator",
                server_version="0.1.0",
                capabilities=server.get_capabilities(
                    notification_options=NotificationOptions(),
                    experimental_capabilities={},
                ),
            ),
        )


if __name__ == "__main__":
    asyncio.run(main())

```