midjourney-mcp
README.md
# midjourney-mcp
FastMCP server for Midjourney image generation via [PiAPI](https://piapi.ai/),
with a daily-USD cost cap, per-call audit log, and SSRF-safe image URL inputs.
Midjourney has no broadly available official API as of May 2026 (Enterprise
application-only). This MCP wraps PiAPI's PAYG Midjourney surface — the most
stable third-party wrapper at the time of build — behind one consistent tool
surface that fits the same audit + safety patterns as the rest of the family
([cloudflare-dns-mcp](https://github.com/adelaidasofia/cloudflare-dns-mcp),
[godaddy-mcp](https://github.com/adelaidasofia/godaddy-mcp),
[parse-mcp](https://github.com/adelaidasofia/parse-mcp), etc.).
## Why a peer MCP
Other Midjourney MCPs exist (AceDataCloud, z23cc, PiAPI's own TypeScript MCP).
This one ships:
1. **Daily USD cap.** A single env var (`MIDJOURNEY_MCP_DAILY_USD_CAP`) sets a
hard ceiling on per-day image-gen spend. Cost-incurring tools refuse with a
stable `error_class="rate_cap"` once the projected day total exceeds cap.
2. **Per-call 4-field audit log** (`execution_time_ms` / `io` / `token_usage` /
`error_class`) at `~/.claude/midjourney-mcp/audit.log.jsonl`, with actual
PiAPI credit consume rolled in on completion.
3. **SSRF-safe image URL inputs** on `describe` + `blend` via
[mycelium-security](https://github.com/adelaidasofia/mycelium-security)
`sanitize_or_raise` + `assert_public_ip`. Defense in depth — refuses
private / link-local / cloud metadata service hosts before forwarding.
4. **`sanitize_error()` strip patterns** on every error payload — X-API-Key,
Bearer tokens, api_key / secret / password / token patterns get redacted
before crossing into model context.
5. **`admin.env` auto-load.** Secrets live at `~/.claude/midjourney-mcp/admin.env`
(chmod 600, gitignored), never inline in `.mcp.json`.
## Install
```bash
cd ~/.claude/midjourney-mcp
python3 -m pip install --break-system-packages -r requirements.txt
```
Then drop the API key into `admin.env`:
```bash
printf 'PIAPI_API_KEY=your-piapi-key-here\n' > ~/.claude/midjourney-mcp/admin.env
chmod 600 ~/.claude/midjourney-mcp/admin.env
```
Sign up at <https://piapi.ai/> if you need a key. PAYG starts at ~$0.01 per
imagine task. Host-Your-Account ($8/seat/mo) is also supported — set the
daily cap to 0 (`MIDJOURNEY_MCP_DAILY_USD_CAP=0`) to disable cost tracking in
HYA mode.
Register in your project `.mcp.json`:
```json
{
"mcpServers": {
"midjourney": {
"command": "python3",
"args": ["/Users/YOU/.claude/midjourney-mcp/server.py"],
"env": {}
}
}
}
```
Then restart Claude Code.
## Tools (11 total)
### Read (no cost, no cap)
| Tool | What it does |
|---|---|
| `healthcheck` | Verify the PiAPI key is set + return today's cap snapshot |
| `account_info` | Today's USD cap + per-tool cost estimates + default process_mode |
| `get_task(task_id)` | Single poll for task status + output URLs |
| `wait_for_task(task_id, timeout_seconds, poll_interval_seconds)` | Block until terminal state |
| `list_recent_tasks(limit)` | Scan the local audit log for recent task_ids |
### Cost-incurring (cap-gated)
| Tool | Estimated USD (fast mode) | What it does |
|---|---:|---|
| `imagine(prompt, aspect_ratio, ...)` | $0.040 | 4-up grid from a text prompt |
| `variation(origin_task_id, index, prompt, ...)` | $0.040 | Regenerate variations off a grid (index 1-4 / high_variation / low_variation) |
| `upscale(origin_task_id, index, ...)` | $0.010 | Isolate + upscale one grid image (index 1-4 / light / beta / 2x / 4x / subtle / creative) |
| `describe(image_url, ...)` | $0.005 | 4 prompts from an input image (image-to-prompt) |
| `blend(image_urls, dimension, ...)` | $0.040 | Merge 2-5 images into a new 4-up grid |
### Lifecycle
| Tool | What it does |
|---|---|
| `cancel_task(task_id)` | Cancel a non-terminal task |
## Daily USD cap
Image generation is creative iteration. Draft+confirm on every call breaks the
loop. Instead, every cost-incurring tool runs a cap check BEFORE the API call:
```
projected = spent_today_usd + estimated_call_usd
if projected > MIDJOURNEY_MCP_DAILY_USD_CAP:
refuse with error_class="rate_cap"
```
Spent-today tracks the calendar day at a configurable UTC offset (default
`-5`; override via `MIDJOURNEY_MCP_TZ_OFFSET_HOURS=-8` for US Pacific
Standard, `1` for Central European, etc.). Cap resets at midnight in that
offset. Estimates drive the cap (conservative pre-flight); actual PiAPI
credit consume is backfilled from `meta.usage.consume` on task completion
and lands in the audit log.
Default cap: `$5.00/day`. Override via env: `MIDJOURNEY_MCP_DAILY_USD_CAP=20.00`.
Set to `0` to disable (e.g. HYA mode with flat $8/mo billing).
## Typical flow
```python
# 1. Submit an imagine
imagine(prompt="a quiet kitchen at dawn, soft window light, film grain --ar 3:2", aspect_ratio="3:2")
# -> {"task_id": "tk_abc...", "status": "pending", ...}
# 2. Wait for it
wait_for_task(task_id="tk_abc...", timeout_seconds=180)
# -> {"status": "completed", "task": {"output": {"image_url": "...", "image_urls": [...]}}}
# 3. Upscale the best one (say grid position 2)
upscale(origin_task_id="tk_abc...", index="2")
# -> {"task_id": "tk_xyz...", ...}
wait_for_task(task_id="tk_xyz...")
# Or vary instead of upscale:
variation(origin_task_id="tk_abc...", index="3", prompt="<same prompt, tweak>")
```
## Audit log
Every tool call writes one JSONL line at `~/.claude/midjourney-mcp/audit.log.jsonl`:
```json
{
"ts": 1737842400,
"tool": "imagine",
"execution_time_ms": 1230,
"io": {"input": {"prompt_chars": 47, "aspect_ratio": "3:2"}, "output": {"task_id": "tk_abc...", "status": "pending"}},
"token_usage": {},
"error_class": "none",
"extra": {"task_id": "tk_abc...", "usd_estimate": 0.04}
}
```
Search by tool, date, error_class, task_id. Useful for cost attribution + bug
triage. Override the path via `MIDJOURNEY_MCP_AUDIT_LOG`.
## Process modes
PiAPI translates `process_mode` to Midjourney's plan-level modes:
- `relax` — slowest, cheapest, no GPU minutes consumed on official MJ plans
- `fast` — default, normal-quality GPU time
- `turbo` — fastest, premium GPU time, ~2× cost
Override default via env: `MIDJOURNEY_MCP_PROCESS_MODE=fast` (default).
Override per-call via the `process_mode` argument on any tool.
## Aspect ratios
Pass `aspect_ratio` to `imagine` / `variation`. Common values:
- `1:1` (square, default)
- `3:2`, `2:3` (classic photo)
- `16:9`, `9:16` (cinema / portrait phone)
- `4:3`, `3:4` (older monitor / portrait)
- `21:9` (ultra-wide)
PiAPI also accepts Midjourney's `--ar W:H` flag in the prompt itself; either
works, but `aspect_ratio` is cleaner.
## Configuration reference
| Env var | Default | Purpose |
|---|---|---|
| `PIAPI_API_KEY` | (required) | PiAPI API key. Auto-loaded from `admin.env`. |
| `PIAPI_API_BASE` | `https://api.piapi.ai/api/v1` | Override for mocking / testing. |
| `MIDJOURNEY_MCP_DAILY_USD_CAP` | `5.00` | Daily USD spend cap. `0` disables. |
| `MIDJOURNEY_MCP_PROCESS_MODE` | `fast` | Default mode if not specified per-call. |
| `MIDJOURNEY_MCP_AUDIT_LOG` | `~/.claude/midjourney-mcp/audit.log.jsonl` | Audit log path. |
| `MIDJOURNEY_MCP_SPEND_FILE` | `~/.claude/midjourney-mcp/spend.json` | Daily spend tracker path. |
## Related MCPs
Same author, same install path (`~/.claude/<name>-mcp`), same safety patterns:
- [whatsapp-mcp](https://github.com/adelaidasofia/whatsapp-mcp) — WhatsApp Web bridge with draft+confirm sends
- [imessage-mcp](https://github.com/adelaidasofia/imessage-mcp) — iMessage with Whisper voice transcription
- [slack-mcp](https://github.com/adelaidasofia/slack-mcp) — Multi-workspace Slack with draft+confirm
- [substack-mcp](https://github.com/adelaidasofia/substack-mcp) — Notes + drafts + post management
- [parse-mcp](https://github.com/adelaidasofia/parse-mcp) — Multi-backend document parser
- [godaddy-mcp](https://github.com/adelaidasofia/godaddy-mcp) — GoDaddy DNS with draft+confirm
- [cloudflare-dns-mcp](https://github.com/adelaidasofia/cloudflare-dns-mcp) — Cloudflare DNS with draft+confirm
- [finance-mcp](https://github.com/adelaidasofia/finance-mcp) — Plaid-backed personal finance
## License
MIT.
---
Built by Adelaida Diaz-Roa. Full install or team version at diazroa.com.
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessNo issues