# 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()) ```