# Efecto MCP Server

Model Context Protocol (MCP) server for programmatic poster creation with [Efecto](https://efecto.app).

Create stunning visual designs with ASCII effects, glitch art, halftone patterns, animated text, and more—all from your CLI or AI agent.

## Quick Start

**Install and set up:**

```bash
npx @efectoapp/mcp-server install
```

Then restart Claude Code and you're ready to go!

### Verify It Works

Ask Claude:
> "Use efecto to list available effects"

### Alternative: Using npx (no global install)

```bash
claude mcp add efecto -- npx @efectoapp/mcp-server
```

## Example Workflow

> "Make a cyberpunk image that says DOPE for Instagram"

```
1. search_images({ query: "cyberpunk", orientation: "vertical" })
   → Returns images from Lummi

2. create_poster({ aspectRatio: "9:16" })
   → Creates Instagram story format

3. set_background({ type: "image", imageUrl: "<url from step 1>" })
   → Sets the cyberpunk image as background

4. apply_effect({ effectId: "glitch-digital" })
   → Applies digital glitch effect

5. add_postprocess({ type: "chromatic-aberration", strength: 0.02 })
   → Adds RGB split effect

6. add_postprocess({ type: "scanlines", intensity: 0.3 })
   → Adds CRT scanlines

7. add_layer({ type: "text", content: "DOPE", fontSize: 120, fontFamily: "Space Grotesk", color: "#ffffff", animationType: "glitch", animationSpeed: 2 })
   → Adds animated glitch title text

8. generate_url()
   → Returns shareable URL like https://efecto.app/canvas?lm=1&...

9. render_image({ savePath: "~/Downloads/cyberpunk-poster.png" })
   → Renders to image, saves to disk, and shows inline preview
```

## Available Tools

### Discovery Tools

| Tool | Description |
|------|-------------|
| `list_effects` | List all available main effects |
| `list_fonts` | List available fonts for text layers |
| `list_palettes` | List color palettes |
| `list_materials` | List 3D materials |
| `list_postprocess` | List post-processing effects |
| `list_text_animations` | List text animation types and parameters |

### Image Tools

#### `search_images`

Search for free stock images from Lummi.

| Parameter | Type | Description |
|-----------|------|-------------|
| `query` | string | Search query (e.g., "cyberpunk", "nature"). Empty = featured |
| `orientation` | string | `"vertical"` (9:16), `"horizontal"` (16:9), `"square"` (1:1) |
| `type` | string | `"photo"`, `"illustration"`, `"3d"` |
| `luminance` | string | `"dark"`, `"neutral"`, `"bright"` |
| `limit` | number | Max results (default: 10, max: 50) |

### State Building Tools

#### `create_poster`

Create a new poster.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `aspectRatio` | string | `"16:9"` | `"16:9"`, `"9:16"`, `"1:1"`, `"4:3"`, `"full"` |
| `backgroundColor` | string | `"#1a1a1a"` | Background color (hex) |

#### `set_background`

Set the background layer (always at index 0).

| Parameter | Type | Description |
|-----------|------|-------------|
| `type` | string | `"solid"`, `"gradient"`, `"image"`, or `"video"` |
| `color` | string | Hex color (for solid type; writes `fill` + legacy `solidColor`) |
| `gradientStyle` | string | `"linear"` or `"radial"` (for gradient type) |
| `gradientStartColor` | string | Hex start color for gradient fill |
| `gradientEndColor` | string | Hex end color for gradient fill |
| `gradientStops` | array | Optional stop list (`2-8`) with `{ color, opacity, position }`; overrides start/end |
| `gradientAngle` | number | Linear gradient angle in degrees (linear only) |
| `gradientCenterX` | number | Radial center X, `0-1` (radial only) |
| `gradientCenterY` | number | Radial center Y, `0-1` (radial only) |
| `gradientRadius` | number | Radial radius scale (radial only) |
| `imageUrl` | string | Image URL (for image type) |
| `videoUrl` | string | Video URL (for video type) |
| `objectFit` | string | `"cover"` or `"contain"` for image/video |

#### `add_layer`

Add a layer to the poster.

| Parameter | Type | Description |
|-----------|------|-------------|
| `type` | string | `"text"`, `"image"`, `"video"`, `"3d"` |
| `name` | string | Layer name |

**Text layer parameters:**
- `content`: Text content
- `fontFamily`: Font name (e.g., "Space Grotesk", "Inter")
- `fontSize`: Size in pixels
- `fontWeight`: `"normal"`, `"medium"`, `"semibold"`, `"bold"`
- `color`: Hex color
- `textAlign`: `"left"`, `"center"`, `"right"`

**Text animation parameters (optional):**
- `animationType`: Animation type (see below)
- `animationSpeed`: Speed multiplier (0.1-5.0)
- `animationAmplitude`: Intensity (0-100)
- `animationStaggerDelay`: Delay between characters in ms (0-500)
- `animationStaggerMode`: `"sequential"`, `"random"`, `"center-out"`, `"edges-in"`
- `animationLoop`: Loop the animation (default: true)

**Animation types:** `wave`, `rotate`, `scale`, `orbit`, `bounce`, `shake`, `spiral`, `typewriter`, `glitch`, `elastic`, `cascade`, `neon`

Each animation type has additional specific parameters. Use `list_text_animations` to see all options.

**Image/Video layer parameters:**
- `mediaUrl`: Media URL
- `objectFit`: `"cover"`, `"contain"`

**Transform parameters (all layers):**
- `x`, `y`: Position (-1 to 1, 0 = center)
- `width`, `height`: Size (0 to 1+, 1 = full canvas)
- `rotation`: Degrees
- `opacity`: 0-1

#### `modify_layer`

Modify an existing layer by ID.

#### `remove_layer`

Remove a layer by ID (cannot remove background).

#### `apply_effect`

Apply the main effect. Only one can be active.

| Parameter | Type | Description |
|-----------|------|-------------|
| `effectId` | string | Effect ID (see below) |
| `enabled` | boolean | Enable/disable (default: true) |

**Available Effect IDs:**

| Category | Effect IDs |
|----------|------------|
| ASCII | `ascii-standard`, `ascii-blocks`, `ascii-braille`, `ascii-hatching`, `ascii-matrix`, `ascii-technical`, `ascii-dense`, `ascii-minimal` |
| Dither | `dither-floyd-steinberg`, `dither-atkinson`, `dither-jarvis-judice-ninke`, `dither-stucki`, `dither-burkes`, `dither-sierra`, `dither-two-row-sierra`, `dither-sierra-lite` |
| Halftone | `halftone-mono`, `halftone-cmyk` |
| Glitch | `glitch-chromatic`, `glitch-digital`, `glitch-vhs`, `glitch-weird` |
| Art | `art-kuwahara`, `art-crosshatch`, `art-lineart`, `art-engraving`, `art-stipple` |

**Effect-specific parameters:**
- ASCII: `cellSize` (4-32), `color` (bool), `invert` (bool)
- Dither: `pixelation` (1-10), `colors` (array)
- Halftone: `dotSize`
- Glitch: `speed`

#### `add_postprocess`

Add a stackable post-processing effect.

| Parameter | Type | Description |
|-----------|------|-------------|
| `type` | string | Post-process type (see below) |
| `enabled` | boolean | Enable/disable |

**Post-process types:** `scanlines`, `vignette`, `chromatic-aberration`, `curvature`, `grain`, `noise`, `pixelate`, `wave`, `rgb-glitch`, `brightness-contrast`, `color-tint`, `palette`, `jitter`, `bloom`, `dot-screen`, `sepia`, `grid`, `light-beams`, `motion-blur`

**Common parameters by type:**
- `scanlines`: `intensity`, `count`
- `vignette`: `intensity`, `radius`
- `chromatic-aberration`: `strength`, `angle`
- `grain`: `intensity`, `size`, `speed`
- `brightness-contrast`: `brightness`, `contrast`, `saturation`, `hue`
- `palette`: `colors` (array), `intensity`

#### `get_state`

Get the current poster state as JSON.

### Output Tools

#### `generate_url`

Generate a shareable URL for the current poster.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `baseUrl` | string | `"https://efecto.app"` | Base URL |

Returns a URL that opens in any browser with all effects applied.

#### `render_image`

Render the current poster to an image using local Puppeteer (free, no API key needed). Returns the image inline so the agent can see it. Optionally saves to disk.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `format` | string | `"png"` | `"png"`, `"jpeg"`, `"webp"` |
| `width` | number | auto | Output width |
| `height` | number | auto | Output height |
| `quality` | number | 90 | JPEG/WebP quality |
| `savePath` | string | — | File path to save the image (e.g. `"~/Downloads/poster.png"`). If omitted, image is only returned inline. |

Returns the image inline via MCP image content. If `savePath` is provided, also writes the file to disk and returns the resolved path.

## Development

```bash
pnpm install    # Install dependencies
pnpm build      # Build TypeScript
pnpm dev        # Watch mode
pnpm start      # Run server
```

## API Endpoints

The MCP server calls these Efecto API endpoints:

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/v1/images/search` | GET | Search Lummi images |
| `/api/v1/state/encode` | POST | Encode state to URL |
| `/api/v1/state/decode` | POST | Decode URL to state |
| `/api/v1/state/validate` | POST | Validate state |
| `/api/v1/render` | POST | Render to image (deprecated — use `render_image` tool instead) |

## License

MIT
