Skip to main content
Glama
saurabhav88

envious-canvas

by saurabhav88
README.md
<p align="center">
  <h1 align="center">envious-canvas</h1>
  <p align="center">
    Give Claude the power to create, edit, and resize images.<br/>
    One prompt. Every channel. On brand.
  </p>
</p>

<p align="center">
  <a href="#license"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License"></a>
  <a href="#requirements"><img src="https://img.shields.io/badge/python-3.11+-3776AB.svg?logo=python&logoColor=white" alt="Python 3.11+"></a>
  <a href="#testing"><img src="https://img.shields.io/badge/tests-255%20passing-brightgreen.svg" alt="Tests: 22 passing"></a>
  <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-compatible-blueviolet.svg" alt="MCP Compatible"></a>
</p>

---

## What is this?

**envious-canvas** is an [MCP server](https://modelcontextprotocol.io) that turns Claude into a full image production studio. Ask Claude to generate images, edit photos, remove backgrounds, upscale resolution, and resize to any platform's specs — all from natural language.

It is not just an API wrapper. It is a **workflow tool** with brand kits for visual consistency, smart provider routing across 4 AI services, and 69 channel presets so you never have to look up image dimensions again.

### What you can do

| You say | What happens |
|---------|-------------|
| *"Create a hero image for my blog post about sustainable architecture"* | Generates a high-quality image, saves it to your desktop |
| *"Edit this photo to remove the background clutter and make it cleaner"* | AI editing that preserves the subject while transforming the scene |
| *"Remove the background from product-shot.png"* | Subject isolation with transparent PNG output |
| *"Upscale this image to 4x resolution"* | AI-powered super-resolution using Real-ESRGAN |
| *"Resize this for Instagram, LinkedIn, and Facebook"* | Creates correctly sized versions for each platform |
| *"Generate a campaign pack for our summer sale"* | One prompt produces 10+ channel-ready assets: blog hero, OG image, social cards, display ads |
| *"Create a marketing banner with headline 'Launch Day' and our brand colors"* | Renders pixel-perfect HTML/CSS to PNG — precise text, gradients, layouts |

---

## Quick start

### 1. Clone and install

```bash
git clone https://github.com/saurabhav88/envious-canvas.git
cd envious-canvas
uv sync
```

> Don't have `uv`? Install it: `curl -LsSf https://astral.sh/uv/install.sh | sh`

### 2. Add at least one API key

You only need **one** provider to get started. Set the key as an environment variable, or place it in a file at `~/.enviouswispr-keys/` (one key per file, e.g. `openai-api-key`).

| Provider | Env variable | What it unlocks | Get a key |
|----------|-------------|-----------------|-----------|
| **OpenAI** | `OPENAI_API_KEY` | Generation + editing (recommended default) | [platform.openai.com](https://platform.openai.com/api-keys) |
| **Google Gemini** | `GEMINI_API_KEY` | Generation, editing, vision/describe | [aistudio.google.com](https://aistudio.google.com/apikey) |
| **Replicate** | `REPLICATE_API_TOKEN` | FLUX models, upscale, background removal | [replicate.com](https://replicate.com/account/api-tokens) |
| **fal.ai** | `FAL_KEY` | Fast FLUX generation (fallback) | [fal.ai](https://fal.ai/dashboard/keys) |

### 3. Connect to Claude

**Claude Code** — add to `~/.claude.json`:

```json
{
  "mcpServers": {
    "envious-canvas": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/envious-canvas", "python", "main.py"]
    }
  }
}
```

**Claude Desktop** — add to your config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "envious-canvas": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/envious-canvas", "python", "main.py"],
      "env": {
        "OPENAI_API_KEY": "sk-...",
        "GEMINI_API_KEY": "AI..."
      }
    }
  }
}
```

Replace `/path/to/envious-canvas` with the actual path where you cloned the repo.

### 4. Verify it works

```bash
# Run smoke tests (no API keys needed)
uv run pytest tests/test_smoke.py -v
```

That's it. Open Claude and start asking for images. Chromium (for HTML rendering) auto-installs on first server start.

---

## The 6 MCP tools

| Tool | What it does |
|------|-------------|
| **canvas_plan_image** | Plan a creative asset — auto-detects fidelity mode, resolves brand kit, estimates cost. Approve before generating. |
| **canvas_create_image** | Generate marketing images — plan-based or direct mode. Supports brand kits, multi-channel campaign packs, style control, and provider selection. |
| **canvas_render_layout** | Render HTML/CSS templates to pixel-perfect PNG. Supports brand kit CSS variables, Google Fonts, and responsive viewports. |
| **canvas_edit_image** | Edit existing images: text-directed edits, remove background, upscale (2-4x), apply color grading. |
| **canvas_manage_library** | CRUD for brand kits and generated assets. Create/list/search/delete brand kits, search generated assets. |
| **canvas_inspect_asset** | Analyze any image — get descriptions, extract attributes, run QA checks. Powered by Gemini vision. |

---

## 69 channel presets, 8 platforms

Stop guessing image dimensions. Every preset follows the latest platform specs (sourced from Hootsuite, Sprout Social, Buffer, and HubSpot, 2025-2026).

| Platform | Presets | Includes |
|----------|---------|----------|
| **Facebook** | 13 | Feed (square, landscape, portrait), Stories, cover photos, event/group covers, link preview, ads (square, portrait, landscape, Stories, carousel) |
| **Instagram** | 11 | Feed (square, landscape, portrait, grid), Stories, Reels covers, ads (feed, Stories, Reels, carousel) |
| **LinkedIn** | 11 | Personal/company covers, feed (square, landscape, portrait), article cover, link preview, ads (landscape, square, portrait, carousel) |
| **Twitter/X** | 7 | Header, feed (landscape, square, portrait), summary card, ads (landscape, square) |
| **TikTok** | 5 | Video cover, carousel, ads (vertical, landscape, square) |
| **YouTube** | 4 | Channel banner, video thumbnail, Shorts cover, community post |
| **Pinterest** | 4 | Standard pin, Idea Pin, promoted pin, carousel ad |
| **Google My Business** | 3 | Logo, cover photo, general photo |
| **Web + Google Ads** | 7 | OG image, blog hero (standard + wide), Google Display (landscape, square, portrait), Demand Gen |

Plus 4 backward-compatibility aliases for common shorthand (`meta_feed_square`, `linkedin_single`, etc.).

---

## Campaign packs

The most powerful feature. One prompt generates a complete, channel-ready asset bundle.

**What you say:**
> "Generate a campaign pack for 'Acme Spring Collection — New Arrivals' using the acme brand kit."

**What you get — 10 assets, 10 unique aspect ratios, 8 platforms:**

| Asset | Aspect | Size | Covers |
|-------|--------|------|--------|
| Square | 1:1 | 1080 x 1080 | Facebook, Instagram, TikTok, Google Display |
| Portrait | 4:5 | 1080 x 1350 | Facebook, Instagram, LinkedIn, Google Display |
| Instagram grid | 3:4 | 1080 x 1440 | Instagram profile grid |
| Pinterest pin | 2:3 | 1000 x 1500 | Pinterest |
| Stories/vertical | 9:16 | 1080 x 1920 | Instagram/Facebook Stories, TikTok, Google Demand Gen |
| YouTube banner | 16:9 | 2560 x 1440 | YouTube channel art, blog hero, video thumbnail |
| OG image | 1.91:1 | 1200 x 630 | Link previews, Google Display landscape |
| Wide banner | 2:1 | 1600 x 800 | Email headers, wide banners |
| X/Twitter header | 3:1 | 1500 x 500 | X/Twitter banner |
| LinkedIn cover | 4:1 | 1584 x 396 | LinkedIn personal banner |

All from a single generation, cropped to each channel's exact spec. Saved to your desktop, ready to use.

---

## Brand kits

Keep every image on brand, across every provider and channel.

```yaml
name: acme
color_palette: ["#1a1a2e", "#e94560", "#f5f5f5"]
typography: ["Inter", "Playfair Display"]
visual_vibe: "modern minimalist, warm, approachable"
positive_prompt: "clean, professional, modern design"
negative_prompt: "cluttered, busy, text, watermark, low quality"
safe_use_rules: ["no people", "no text in image"]
preferred_aspects: ["16:9", "1:1", "4:5"]
```

**Create one:**
> "Create a brand kit called 'acme' with deep navy and coral red colors, modern minimalist style, and a rule to never include text in images."

**Use it everywhere:**
> "Generate a blog hero for our product launch using the acme brand kit."

Brand kits are saved as YAML files and persist across sessions.

---

## Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `OPENAI_API_KEY` | — | OpenAI API key |
| `GEMINI_API_KEY` | — | Google Gemini API key |
| `REPLICATE_API_TOKEN` | — | Replicate API token |
| `FAL_KEY` | — | fal.ai API key |
| `IMAGE_GEN_OUTPUT_DIR` | `~/Desktop/envious-canvas` | Where generated images are saved |
| `IMAGE_GEN_BRAND_KITS_DIR` | `./brand-kits` | Where brand kit YAML files live |

---

## Known limitations

- **OpenAI size constraints** — `gpt-image-1` only supports 1024x1024, 1536x1024, and 1024x1536. Other sizes fall back to 1024x1024. Use `export_asset_preset` to resize after generation.
- **Campaign pack cropping** — The hero image is center-cropped to each channel spec. Works well for abstract/gradient art; may clip subjects in photographic content. Per-channel regeneration is planned.
- **Gemini model churn** — Google frequently deprecates preview models. If you get 404 errors, check [Google's model list](https://ai.google.dev/gemini-api/docs/models) for current model IDs.
- **fal.ai is fallback-only** — fal activates only when OpenAI and Gemini are both unavailable.

---

<details>
<summary><strong>Technical details</strong> — architecture, provider routing, model catalog</summary>

### Architecture

```
envious-canvas/
├── main.py                # FastMCP server entry point
├── providers/
│   ├── base.py            # Canonical types, Provider protocol, Capability enum
│   ├── registry.py        # Tier-based model catalog (27 models, 11 tiers)
│   ├── router.py          # Capability-validated provider routing
│   ├── renderer.py        # HTML-to-image rendering via Playwright
│   ├── openai.py          # OpenAI provider (GPT Image 1/1.5 + DALL-E)
│   ├── gemini.py          # Google provider (Gemini Image + Imagen 4)
│   ├── replicate.py       # Replicate provider (FLUX models, Real-ESRGAN, RemBG)
│   └── fal.py             # fal.ai provider (FLUX models)
├── brand/
│   └── kit.py             # Brand kit CRUD + prompt transformation
├── presets/
│   └── channels.py        # 69 channel export specs across 8 platforms
├── brand-kits/            # YAML brand kit storage (persists across sessions)
├── tests/
│   ├── test_smoke.py      # 22 smoke tests (no API keys needed)
│   └── eval.xml           # 10 MCP evaluation questions
├── pyproject.toml
└── README.md
```

### Provider routing

Each provider declares its capabilities. When you request an action, the router:

1. Matches the requested capability to available providers
2. Validates the request is compatible (you cannot send an upscale request to OpenAI)
3. Selects the best provider based on tier priority and availability
4. Falls back gracefully if the primary provider is unavailable

### Model catalog — 27 models across 11 tiers

| Tier | Purpose | Models |
|------|---------|--------|
| fast_generate | Cheapest/fastest | GPT Image 1 Mini, Gemini Flash, Imagen 4 Fast, FLUX Schnell |
| quality_generate | Best balance | GPT Image 1, Imagen 4, FLUX.2 Pro, FLUX.2 Flex (fal) |
| premium_generate | Highest fidelity | GPT Image 1.5, Imagen 4 Ultra, FLUX.2 Flex, FLUX.2 Max (fal) |
| edit | Standard editing | GPT Image 1, Gemini Flash, Kontext Pro |
| premium_edit | Best editing | GPT Image 1.5, Kontext Max |
| fill_expand | Inpainting/outpainting | FLUX Fill Pro |
| describe | Vision/analysis | Gemini Flash (Vision) |
| upscale | Resolution enhancement | Real-ESRGAN (2x-4x) |
| remove_background | Subject isolation | RemBG |
| brand_finetuned | Brand-locked generation | FLUX Pro Finetuned |
| legacy | Backward compatibility | DALL-E 3, DALL-E 2 |

</details>

---

## Testing

```bash
# Smoke tests — no API keys, no network calls
uv run pytest tests/test_smoke.py -v

# Verify server starts and responds to MCP handshake
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}' | uv run python main.py
```

---

## Requirements

- Python 3.11+
- [uv](https://github.com/astral-sh/uv) package manager
- At least one API key (OpenAI, Gemini, Replicate, or fal.ai)
- Claude Code or Claude Desktop

---

## License

MIT

TDQS

A4.4/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a distinct purpose: create, edit, inspect, manage library, plan, and render. The 'Do not use when' hints in descriptions further eliminate ambiguity.

Naming Consistency5/5

All tool names follow the consistent pattern 'canvas_<verb>_<noun>' in snake_case, making the action and target clear.

Tool Count5/5

With 6 tools, the server is well-scoped for marketing image generation and management, covering the core workflow without unnecessary clutter.

Completeness4/5

The tool set covers planning, creation, editing, inspection, library management, and layout rendering. Minor gaps like direct cropping could exist but are addressable via editing.

Maintenance

ActivityInactive
ResponsivenessNo issues