Icon Composer MCP

Local MCP tools for editable Apple Icon Composer documents and native appearance previews.

Documentation

Icon Composer MCP

Icon Composer MCP (icon-composer-kit) is a local MCP server for creating, inspecting, revising, and rendering editable Apple Icon Composer .icon documents. It keeps the workspace explicit and local, validates artwork before it is written, and delegates previews to Apple’s signed Icon Composer renderer when it is available.

It is intentionally an icon composer. It accepts static SVG or PNG artwork and arranges that artwork into editable groups, layers, materials, fills, positions, and appearance specializations. It does not generate artwork, vectorize raster images, download assets, or call a network service.

Preview gallery

These previews were produced from the checked-in recipes with Apple Icon Composer’s native renderer. Each example has a corresponding editable bundle under examples/.

OrbitBloomPrism
OrbitBloomPrism

Each recipe can be rendered in all six supported appearances:

ExampleDefaultDarkTinted LightTinted DarkClear LightClear Dark
Orbit512512512512512512
Bloom512512512512512512
Prism512512512512512512
DefaultDarkTinted LightTinted DarkClear LightClear Dark
DefaultDarkTinted LightTinted DarkClear LightClear Dark

The small previews show the 32 px check used by the examples workflow: Orbit, Bloom, and Prism.

Requirements

  • Node.js 24 or newer.
  • npm and the checked-in package-lock.json.
  • A dedicated, writable absolute directory for ICON_WORKSPACE.
  • macOS with Apple Icon Composer for composer_status to report available and for render_icon to produce native previews. Document operations support local macOS and Linux filesystems. Windows is not supported by the ownership checks. The verified native tool currently reports version 1.6 in the development environment; availability is checked at runtime.

The server verifies the Apple signing requirement and the expected com.apple.IconComposerTool identifier before invoking ictool. ICON_COMPOSER_APP may point to an absolute .app bundle when the renderer is installed outside its default location; malformed values disable native rendering.

Install and run locally

Clone the repository, then build the local package:

git clone https://github.com/TheNaubit/icon-composer-mcp.git icon-composer-kit
cd icon-composer-kit
npm ci
npm run build
mkdir -p /absolute/path/icon-workspace
ICON_WORKSPACE=/absolute/path/icon-workspace node "$PWD/dist/server.js"

The server speaks MCP over stdio. Keep ICON_WORKSPACE dedicated to icons created by this server. The workspace root is an operator setting, not a tool argument, and must be an absolute real directory below the filesystem root.

Install in your MCP host

Build once with npm ci and npm run build, then choose your host below. These are local installations: run the host on the Mac that has Icon Composer to render previews. Linux supports document operations only. Browser-only and remote coding sessions cannot launch the renderer on your Mac through this stdio configuration.

Replace /absolute/path/icon-composer-kit with your checkout and /absolute/path/icon-workspace with a dedicated output directory. For desktop apps, replace /absolute/path/node with the result of command -v node (Node 24+). JSON and TOML paths are literal: do not put ~ or $PWD in them. Merge entries into existing configuration rather than replacing other servers.

HostSetupVerify
Codex CLIcodex mcp add or TOMLcodex mcp get and /mcp
Codex appShared local Codex TOMLRestart app, start a new local task
Claude Codeclaude mcp addclaude mcp get and /mcp
Claude Desktop — ChatDesktop JSON configurationRestart app, start a new Chat
Claude CoworkManual local plugin wrapperEnable plugin and check its tools
CursorUser or project mcp.jsonEnable server in MCP settings
VS Code.vscode/mcp.jsonStart server, select chat tools

No published npm package or one-click extension is assumed by these instructions. Keep machine-specific configuration and any plugin ZIP you customize outside this public repository.

Codex CLI

Register for your user:

codex mcp add icon-composer-kit \
  --env ICON_WORKSPACE=/absolute/path/icon-workspace \
  -- /absolute/path/node /absolute/path/icon-composer-kit/dist/server.js
codex mcp get icon-composer-kit

Start a new codex session and use /mcp to check the connection. To remove the registration, run codex mcp remove icon-composer-kit; icon files remain on disk.

Alternatively, merge this into ~/.codex/config.toml. For a trusted project only, use that project's .codex/config.toml instead:

[mcp_servers.icon-composer-kit]
command = "/absolute/path/node"
args = ["/absolute/path/icon-composer-kit/dist/server.js"]

[mcp_servers.icon-composer-kit.env]
ICON_WORKSPACE = "/absolute/path/icon-workspace"

Official Codex MCP documentation.

Codex app

The app and CLI share MCP configuration on the same local Codex host. Use the CLI registration above, or add the same TOML block to ~/.codex/config.toml if you do not use the CLI. Restart the app and open a new local task. Ask it to call composer_status, then list_icons.

Use the configuration on the Mac running the task, not a separate remote host. To uninstall, remove the TOML server block and its environment subtable, then restart the app. Official shared-configuration guidance.

Claude Code

Use user scope for access across your projects:

claude mcp add --transport stdio --scope user \
  --env ICON_WORKSPACE=/absolute/path/icon-workspace \
  icon-composer-kit -- /absolute/path/node /absolute/path/icon-composer-kit/dist/server.js
claude mcp get icon-composer-kit

Start a new Claude Code session and run /mcp. All six tools should be available. To restrict installation to your current project without sharing its paths, replace --scope user with --scope local. --scope project writes shared .mcp.json configuration; avoid committing personal absolute paths there.

Remove it with claude mcp remove --scope user icon-composer-kit, using the same scope you installed. Official Claude Code MCP guide.

Claude Desktop — Chat

On macOS, open Claude Desktop's Settings → Developer → Edit Config, or edit ~/Library/Application Support/Claude/claude_desktop_config.json. Merge this entry:

{
  "mcpServers": {
    "icon-composer-kit": {
      "command": "/absolute/path/node",
      "args": ["/absolute/path/icon-composer-kit/dist/server.js"],
      "env": { "ICON_WORKSPACE": "/absolute/path/icon-workspace" }
    }
  }
}

Fully quit and reopen Claude Desktop. In a new Chat, check the available tools and request composer_status. Remove the entry and restart to uninstall. This is manual stdio configuration, not an .mcpb desktop-extension installation. Official local-server setup.

Claude Cowork

Manual local plugin installation. Claude documents local MCP servers in desktop plugins, but explicitly says legacy claude_desktop_config.json servers are not available in Cowork. Use the plugin mechanism rather than assuming the Chat setup transfers. Local plugin execution requires the desktop app and can be disabled by organization policy. See Cowork architecture and the legacy-configuration limitation.

To prepare a machine-local wrapper, create this folder outside the checkout:

icon-composer-local/
├── .claude-plugin/
│   └── plugin.json
└── .mcp.json

Put this in .claude-plugin/plugin.json:

{
  "name": "icon-composer-local",
  "version": "1.0.1",
  "description": "Local Apple Icon Composer tools"
}

Put the JSON from the Claude Desktop — Chat section in .mcp.json, with your actual absolute paths. The wrapper references your existing built checkout and dependencies; it does not bundle or download them. This layout follows the official plugin MCP reference.

From the wrapper directory, include both hidden entries in the archive:

zip -r ../icon-composer-local.zip .claude-plugin .mcp.json

In Claude Desktop, open Cowork → Customize → Plugins and use the custom-plugin upload option to install the ZIP. Enable it and start a new task. Ask for composer_status and list_icons; plugin tool names may have a namespace prefix. Official plugin installation guide.

If custom uploads/local MCP execution are unavailable, use Claude Code or Claude Desktop Chat instead. Do not enter a filesystem path in the remote-connector URL field. To uninstall, remove the wrapper from Plugins; retain or archive the separate icon workspace as desired. The customized ZIP contains local paths, so keep it private.

Cursor

For your user, merge the following into ~/.cursor/mcp.json. For one project, use .cursor/mcp.json in that project instead:

{
  "mcpServers": {
    "icon-composer-kit": {
      "type": "stdio",
      "command": "/absolute/path/node",
      "args": ["/absolute/path/icon-composer-kit/dist/server.js"],
      "env": { "ICON_WORKSPACE": "/absolute/path/icon-workspace" }
    }
  }
}

Open Cursor's MCP controls in Settings / Customize, enable the server, and reload the window if it has not connected. In Agent chat, ask it to call composer_status. Remove the JSON entry to uninstall. Official Cursor configuration guide.

VS Code

VS Code uses a servers object rather than mcpServers. Merge this into .vscode/mcp.json, or run MCP: Open User Configuration for a user-wide installation:

{
  "servers": {
    "icon-composer-kit": {
      "type": "stdio",
      "command": "/absolute/path/node",
      "args": ["/absolute/path/icon-composer-kit/dist/server.js"],
      "env": { "ICON_WORKSPACE": "/absolute/path/icon-workspace" }
    }
  }
}

Use the editor's Start action for the server and accept its trust prompt. Select its tools in agent chat and request composer_status. To uninstall, stop the server and remove its entry. In a remote VS Code window, ensure the server is configured to run on your local Mac if you need native rendering. Official VS Code MCP guide.

Verify any installation

Ask the host: “Use Icon Composer Kit to call composer_status and list_icons.” A successful tool response confirms the connection. composer_status returning available: false means MCP is connected but Apple's renderer could not be verified; document creation can still work.

If no tools appear, run the included client independently:

ICON_WORKSPACE=/absolute/path/icon-workspace \
  /absolute/path/node /absolute/path/icon-composer-kit/dist/client.js tools

This should list six tools. Then check your host's logs, literal absolute paths, Node version and workspace ownership. If Icon Composer is installed separately from Xcode, add ICON_COMPOSER_APP to the same server environment configuration with the absolute .app path.

These recipes were checked against official documentation on September 5, 2026, and the installed CLI help. Use the linked host documentation if menu labels change.

Command-line MCP client

The built client is a small diagnostic caller. It starts a child server with the current ICON_WORKSPACE, lists the registered tools, or calls one tool using a JSON file.

export ICON_WORKSPACE=/absolute/path/icon-workspace
node dist/client.js tools
node dist/client.js call list_icons

Calls without arguments can omit the JSON file:

node dist/client.js call composer_status

The client accepts JSON input up to 2 MiB and prints the MCP result as JSON. Tool failures return a non-zero exit status. ICON_WORKSPACE is required even for tools because the client always starts the local server.

A first icon

The checked-in examples/orbit.json is a complete create_icon request. It uses a two-stop background, two groups, static SVG layers, glass material, and a dark background specialization.

export ICON_WORKSPACE=/absolute/path/icon-workspace
node dist/client.js call create_icon examples/orbit.json

For a dark preview, save this as render.json in the checkout (or any local file explicitly passed to the client):

{ "name": "orbit", "rendition": "Dark", "size": 512, "inline": false }
node dist/client.js call render_icon render.json

The PNG path in the result is relative to ICON_WORKSPACE. Open orbit.icon in Apple's Icon Composer to inspect its layers interactively. Save manual editor experiments as a separate copy; they are not imported into the managed specification.

The response contains the logical bundle path (orbit.icon) and a SHA-256 revision. Creation never overwrites an existing icon name. To revise an icon, call read_icon, copy its complete spec and revision, then call update_icon with a new outputName. The original bundle remains unchanged.

MCP surface

The server registers six tools and one prompt. Tool responses are JSON text, except render_icon, which may also include a PNG image when inline is true.

ToolPurposeWrites workspace?
composer_statusCheck whether the verified Apple renderer is available; may return its numeric short version.No
list_iconsList valid icon bundle names in the configured workspace.No
create_iconValidate a complete specification and create a new editable .icon bundle.Yes
read_iconRead the managed specification and its revision.No
update_iconVerify an expected revision and create a revised specification under a new name.Yes
render_iconRender one native PNG appearance, save it with a unique filename, and optionally return it inline.Yes

The compose-icon prompt provides a composition checklist: a 1024 by 1024 canvas, front-to-back groups and layers, separate glass shapes, dark artwork where useful, six appearance renders, and 32 px/1024 px inspection. Prompt text is guidance; the tool schemas remain authoritative.

Specification reference

create_icon accepts {name, spec}. update_icon requires {name, outputName, expectedRevision, spec}. expectedRevision is the 64-character hexadecimal revision returned by read_icon. Both accept strict JSON objects. Unknown fields are rejected. The current managed specification is stored as source.json inside each bundle and is the authority used by read_icon and later updates.

Icon fields

FieldTypeConstraints and default
backgroundstring or {top,bottom}Required. Colors are six-digit #RRGGBB; an object is a vertical gradient.
darkBackgroundSame as backgroundOptional dark appearance fill.
platform"iOS" | "macOS"Defaults to "iOS".
groupsArrayRequired; 1–4 groups.

Group fields

FieldTypeConstraints and default
namestringRequired, 1–80 characters; no control characters.
layersArrayRequired; 1–32 layers. Groups and layers are front-to-back.
specularbooleanDefaults to false.
blurnumber0–1, defaults to 0.
translucencynumber0–1, defaults to 0.
shadow"none" | "neutral" | "chromatic"Defaults to "none".
shadowOpacitynumber0–1, defaults to 0.
lighting"combined" | "individual"Defaults to "combined".

Layer fields

FieldTypeConstraints and default
namestringRequired, 1–80 characters; no control characters.
svgstringExactly one primary source is required: static SVG up to 256 KiB.
pngBase64stringAlternative primary source: base64 non-interlaced PNG, at most 1 MiB and 1024 px per side. Pixels are re-encoded without metadata.
darkSvgstringOptional dark artwork specialization, with the same SVG rules.
darkPngBase64stringOptional dark PNG specialization, with the same PNG rules.
glassbooleanDefaults to false.
opacitynumber0–1, defaults to 1.
scalenumber0.01–4, defaults to 1.
x, ynumber−1024–1024 points, each defaults to 0.

The complete icon JSON must be at most 2 MiB and contain at most 64 artwork assets, including dark specializations. SVG accepts a deliberately small static language of geometry, local gradients, masks, and clip paths, with raw input capped at 256 KiB, 4096 elements, and depth 32. External URLs, data URLs, scripts, event attributes, styles, declarations, and unsupported elements are rejected. PNG input must be a valid non-animated, non-interlaced PNG with intact CRCs; ancillary metadata is removed. The MCP transport reserves up to 3 MiB per frame.

Background and untagged SVG colors are emitted as Display P3 values in the native document. PNG pixels are not color-converted and embedded profiles are removed; export PNG artwork as sRGB before importing it.

Rendering

render_icon takes name, rendition, size, inline, optional tintColor, optional tintStrength, and optional opaqueBackground.

FieldTypeConstraints and default
renditionDefault, Dark, TintedLight, TintedDark, ClearLight, ClearDarkDefaults to Default.
sizeinteger16–1024 px, defaults to 512.
inlinebooleanDefaults to true; include the PNG as an MCP image when true.
tintColornumberOptional, 0–1. Passed to Apple’s renderer.
tintStrengthnumberOptional, 0–1. Passed to Apple’s renderer.
opaqueBackground#RRGGBBOptional. Composites the rendered alpha onto an opaque RGB PNG. This is a flattened preview, not an App Store source icon.

After checking that the native files still match the managed specification, the renderer creates a temporary bundle from source.json, then invokes Apple ictool with the icon’s configured platform. The output is checked for PNG signature and requested dimensions before it is saved under a generated filename in the workspace. Native rendering has no approximation or network fallback.

Data, revisions, and safety boundaries

  • Names are lowercase identifiers: a letter followed by up to 63 lowercase letters, digits, or hyphens.
  • Creation and rendering write with restrictive file modes and never overwrite an existing icon or preview filename.
  • update_icon is copy-on-write: it requires the current SHA-256 revision and a new output name. A mismatch stops the update.
  • Workspace paths are controlled by the process environment. Tool calls cannot select arbitrary filesystem paths.
  • The server serializes operations, excludes concurrent workspace writers, limits new writes to a workspace with fewer than 100 top-level entries, and limits valid tool operations to 120 per minute per process. Errors returned through MCP are deliberately generic and do not disclose local paths or native process details.
  • The server does not import edits made in the native Icon Composer GUI. If icon.json or an expected asset is edited outside this tool, the next read/update rejects the bundle rather than silently reverting it. There is no arbitrary existing .icon import.

See SECURITY.md for the project’s reporting policy.

Examples and verification

The gallery generator reads the three JSON recipes, creates editable documents through MCP, renders all six appearances plus a 32 px preview, and refreshes the checked-in assets. It requires macOS and a verified Apple Icon Composer installation:

npm run examples

The regular checks do not require native rendering:

npm run format:check
npm run typecheck
npm run coverage
npm run build

Continuous integration runs those host-independent checks on Ubuntu. The native gallery is a manual macOS verification because Apple Icon Composer is not available in that CI environment. Run npm run test:native on macOS for a separate real MCP verification of PNG layers, solid fills, chromatic shadows, both platforms, all appearances, and opaque exports.

Design tips

  • Start with a simple silhouette that remains legible at 32 px.
  • Keep disconnected glass shapes in separate layers so material effects can be controlled independently.
  • Use at most four groups and keep layer order explicit.
  • Give dark mode its own background and artwork when a light mark loses contrast.
  • Use flat groups with specular: false, blur: 0, translucency: 0, shadow: "none", and glass: false when the artwork should stay visually stable.
  • Inspect both the smallest preview and a large native render before distributing an icon.

Troubleshooting

SymptomResolution
Server exits before tools appearSet an absolute ICON_WORKSPACE; use Node 24+ and run npm ci then npm run build. The directory and managed files must be owned by your account and not group/world writable.
composer_status reports unavailableInstall Apple Icon Composer. The default is the copy inside /Applications/Xcode.app. For a standalone copy set ICON_COMPOSER_APP=/Applications/Icon\ Composer.app in your shell, or the equivalent literal value in the host's env object. OS sandbox/trust restrictions can also prevent verification.
Icon name already existsChoose a new name; use read_icon and update_icon to preserve revision history.
Native document was modified / unexpected filesPreserve the manual copy. Create a new managed icon using your desired specification. Do not remove source.json or store unrelated files inside a managed bundle.
Workspace busy after a crashStop every process using that workspace. Inspect and remove only its stale .operation-lock directory; archive leftover .render-* temporary directories before restarting. Do not remove an active writer's lock.
Workspace limit reachedArchive older icons/previews outside the active workspace, or configure a new dedicated workspace. There is no deletion tool.
SVG rejectedExport plain paths/shapes with inline presentation attributes. Convert text to paths in your artwork editor; remove CSS, embedded images, external references and filters.
PNG rejectedExport a static, non-interlaced sRGB PNG at up to 1024 px per side and 1 MiB.
Opaque export fails on a detailed renderFlattening currently accepts native PNGs up to 1 MiB; the ordinary preview path allows 6 MiB. Try a smaller preview.
Glass looks joined or unexpectedPut disconnected shapes in separate layers and compare all six native renditions.

The native document format is not a versioned public Apple API. Compatibility is checked against the installed renderer; a future Apple update may require a mapping change. The package does not automate Xcode project integration, compile asset catalogs, or export an unmasked App Store source icon. opaqueBackground flattens the rendered preview, including its rounded silhouette.

Architecture

MCP host or included CLI client
  → stdio transport and strict schemas
  → workspace ownership, revision and file checks
  → validated SVG/PNG assets + editable native .icon
  → verified Apple ictool → bounded PNG response

The build type-checks with TypeScript and bundles the server and CLI with esbuild into dist/server.js and dist/client.js. Source imports use the extensionless @/ alias for src/; the build resolves these imports. Runtime dependencies remain installed through npm.

The runtime uses the official MCP server/client packages, Zod, Saxes, and PNGJS. There is no shell command builder, HTTP server, database, background service, or bundled Apple binary. Source modules, tests and reproducible examples are included in this repository; AGENTS.md maps their responsibilities.

Contributing

Read CONTRIBUTING.md before opening a change. The repository is licensed under the MIT License.