maginary-mcp
OfficialThis server is an MCP server for Maginary, an OpenRouter-like image/video generation service that uses a Midjourney-style --flag prompt DSL.
Catalog tools: list all
--flagparameters, search them by text, or fetch a single parameter's full record (works without auth, uses live docs or a bundled snapshot).Image/video generation: submit a prompt via
generate(supports img2img, multiple image URLs, style transfer with--sref, callback URLs, and x402 pay-per-use for wallet agents).Generation management: fetch a generation by UUID (
get_generation), poll until it finishes (wait_for_generation), and run follow-up actions like upscale/vary/pan/zoom/img2vid/reroll (execute_action).Image upload: upload a local image file to get a CDN URL for use in prompts (stdio connections).
Account & auth: create accounts via email, check verification/credits/API key status, manage API keys (create/list/revoke), and activate a key locally or per-request.
Billing & products: list available products/plans with pricing, create Stripe checkout sessions, and query account credit/upload balances.
Multiple authentication paths: API key, OAuth-based hosted connection, wallet-signed headers, or x402 payments inside tool calls.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@maginary-mcp generate an image of a cat astronaut --style retro --ar 1:1"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
maginary-mcp
Model Context Protocol server for Maginary — an abstracted OpenRouter for images and video: ~20 model families (GPT-image-2, Seedance 2, Sora 2, Nano Banana Pro, Flux…) behind one Midjourney-style --flag prompt. 16 tools: full flag catalog, generate / upscale / vary / animate, in-chat signup, billing, and x402 pay-per-use for agents with a wallet.
why
Maginary uses a Midjourney-style --flag prompt DSL over an async HTTP API. This server:
surfaces the full parameter catalog to your LLM so it can pick the right flags
offers a one-shot
generatetool that hitsPOST /api/gens/offers
get_generation+wait_for_generationfor polling to a terminal stateworks offline for the catalog tools (ships a bundled snapshot; refreshed from the live docs endpoint at startup when reachable)
Related MCP server: BudgetPixel MCP Server
connect
This is an MCP server — you don't run it directly; your AI client (Claude Desktop, Cursor, etc.) launches and talks to it behind the scenes. Just add one config block and start chatting.
Claude Desktop
In Claude Desktop: settings → developer → edit config. That opens claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\). Add:
{
"mcpServers": {
"maginary": {
"command": "uvx",
"args": ["--upgrade", "maginary-mcp"]
}
}
}Restart Claude Desktop. Ask it to generate an image — it will see Maginary's tools automatically.
No account yet? No problem — Claude will walk you through signup (just give it your email). Already have an API key? Add it to skip that step:
"env": { "MAGINARY_API_KEY": "sk-mag-…" }Requires Python 3.10+ and uv. Alternatively: pip install maginary-mcp.
configuration
Nothing is required. For the stdio server you'll at most set one variable:
var | default | meaning |
| — | Bearer token from app.maginary.ai/dashboard#api-keys. Skips the in-chat signup flow. Catalog tools work without it. |
|
| Override for staging or self-hosted. |
|
| Standard Python log level; goes to stderr (stdout is reserved for MCP JSON-RPC). |
The rest only apply when you run the hosted server yourself (maginary-mcp-http, see below). Directory pages that scan the code list them too; ignore them for local use.
var (hosted only) | default | meaning |
|
| Bind address of the HTTP server. |
|
| Sent to the backend as |
| off | On: every |
|
| The authorization server named in the protected-resource metadata (the backend, django-oauth-toolkit). |
|
| This server's canonical resource identifier (RFC 8707 audience). Also what |
hosted (no-install) — Streamable HTTP
Connect a client straight to the hosted server at https://mcp.maginary.ai/mcp.
Zero install — the server is multi-tenant, so each request is scoped to
whatever credential it arrives with. Two ways to authenticate, pick whichever
fits the client:
Connect (OAuth) — for Claude Desktop, claude.ai, and any other client that speaks MCP's OAuth spec. Add the server with no headers at all:
{
"mcpServers": {
"maginary": { "url": "https://mcp.maginary.ai/mcp" }
}
}Click "Connect" in the client. It opens a login page on app.maginary.ai,
you sign in and approve the requested scopes, and the client holds the token
from then on — no key to generate or paste. Requires the server to be running
with MAGINARY_MCP_REQUIRE_AUTH=1; without it, no login is asked for at all.
API key — for any client that doesn't do the OAuth dance (or if you'd rather not click through a login), generate a key at app.maginary.ai/dashboard#api-keys and send it yourself:
{
"mcpServers": {
"maginary": {
"url": "https://mcp.maginary.ai/mcp",
"headers": { "Authorization": "Bearer sk-mag-…" }
}
}
}Both are equivalent once connected — same tools, same account. Catalog tools
work with no credential either way; generate / get_generation /
wait_for_generation need one. Run the hosted server yourself with:
paying inside the tool call (x402 over MCP)
No key at all? Call generate anyway. Out of credits (or no account), the
result is isError: true with the x402 PaymentRequired at the top level
(accepts, resource, …) plus error: "payment_required". An x402-capable
MCP client — the x402 SDK's x402MCPSession — signs accepts[0] and calls
the same tool again with the payment in _meta["x402/payment"]. The server
forwards it to the backend as PAYMENT-SIGNATURE; the backend verifies,
settles on Base and, for a wallet with no account, creates one. The settled
result carries the on-chain receipt in _meta["x402/payment-response"] and
x402_receipt. No API key is returned — subsequent requests use wallet-signed
auth headers (X-Wallet-Address, X-Wallet-Signature, X-Wallet-Timestamp)
instead.
The server holds no payment logic; everything is decided by the backend's
/api/gens/ contract.
wallet-signed authentication
After the first x402 payment creates the wallet's account, all subsequent requests are authenticated by signing a short message with the wallet's private key. Three headers on every request:
Header | Value |
| Lowercased 0x EVM address (42 chars) |
| EIP-191 |
| Unix seconds (integer) |
The challenge string is:
Maginary: authenticate <address> at <timestamp>. This does not move funds.with <address> lowercased and <timestamp> the same unix seconds sent in
the header. The timestamp must be within 5 minutes of the server's clock
(30 s of future skew tolerated). No API key management needed — the wallet
is the credential.
pip install "maginary-mcp[http]"
maginary-mcp-http # serves /mcp on 0.0.0.0:8642 (MAGINARY_MCP_PORT to change)
# — or —
docker build -t maginary-mcp . && docker run -p 8642:8642 maginary-mcpThe hosted server sets no MAGINARY_API_KEY (keys come per-request). It also
serves /health, a human page at GET /, the OAuth protected-resource metadata
and an MCP server card at /.well-known/mcp/server-card.json (live tool list,
auth posture) for directories that scan a bare URL.
Claude Skill
The server ships an Agent Skill that
teaches the --flag DSL, model selection, and the async generate→poll flow:
maginary-mcp --install-skill # -> ~/.claude/skills/maginary-image-gen/SKILL.mdThe skill stands on its own — hosts without MCP get the DSL plus the raw REST
calls (POST /gens/ → poll). With the server connected, Claude instead calls
search_parameters for the authoritative flag list and generate/wait_for_generation
natively. Re-running updates it; local edits are protected unless you pass --force.
Source: src/maginary_mcp/SKILL.md.
tools
catalog (no auth)
list_parameters(category?, status?, include_reserved=false)— enumerate the catalogsearch_parameters(query, category?, include_reserved=false)— text search over names / aliases / desc / examplesget_parameter(name)— full record for one flag (canonical name or alias)
list_parameters responses include the categories / statuses taxonomy, and both
list/search responses carry source (live vs bundled-snapshot).
generation (auth required)
generate(prompt, callback_url?)—POST /api/gens/. Supports img2img: place image URLs in the prompt. Multiple URLs = multi-input compositing. Use--sref <url>for style-only transfer (not img2img).upload_image(file_path, filename?)— reads a local image file and uploads viaPOST /api/images/upload/. Returns a CDN URL for use in img2img prompts or--sref. Stdio connections only (hosted: use a URL directly or the REST endpoint).execute_action(generation_uuid, action_type, parent_image_index?, prompt?, callback_url?)—POST /api/gens/{uuid}/actions/. Run a follow-up on a completed generation's image (upscale, vary, pan, zoom, img2vid, reroll).get_generation(uuid)—GET /api/gens/{uuid}/. Response includesprocessing_result.available_actionsmapping slots to valid action types.wait_for_generation(uuid, timeout_s=45)— poll todone/failed; atimeoutresult means still running — call again
worked example
Inside an MCP-capable client, once configured:
"Search the maginary catalog for anything about aspect ratio."
The LLM calls search_parameters("aspect") and gets back the --ar entry with values, examples, and supported models.
"Now generate a cinematic portrait 16:9 with the flagship model."
The LLM calls generate("a cinematic portrait --ar 16:9 --flagship"), gets a uuid, then wait_for_generation(uuid) and reads image_urls[] out of the terminal record.
"Upscale the first image."
The LLM checks processing_result.available_actions["0"], sees "upscale_2x", calls execute_action(uuid, "upscale_2x", 0), gets a new uuid, then wait_for_generation(new_uuid).
"Edit this photo to look like a watercolor." (user provides a local image)
The LLM calls upload_image("/tmp/photo.png") → gets a CDN URL, then generate("https://cdn.maginary.ai/…/photo.webp reimagine as watercolor painting"). (stdio only — on hosted, the user provides a URL instead.)
catalog freshness
Live fetch on startup from
https://maginary.ai/docs/parameters.json, 5-second timeout.Bundled snapshot at
src/maginary_mcp/parameters_snapshot.jsonused as a fallback whenever live fetch fails (no network, docs site down, etc.).The snapshot is refreshed manually by the maintainer via
python scripts/refresh_snapshot.py— deliberately not baked into the wheel build so a new snapshot always corresponds to a reviewed commit.
The source field on list_parameters / search_parameters responses tells you which one is active.
development
cd mcp
python -m venv venv && source venv/bin/activate
pip install -e .
maginary-mcp # runs on stdio; kill with Ctrl+Dlicense
MIT.
Available Tools
13 toolscheck_account_statusAInspect
Check account verification status, credit balance, and API key count.
Use this after ``create_account`` to poll whether the user has clicked the
verification link. Pass ``email`` + ``password`` (from ``create_account``)
for Basic auth, or omit both to use the configured API key.
Args:
email: Account email (for Basic auth).
password: Account password (for Basic auth).
Returns:
Dict with ``verified`` (bool), ``email``, ``api_key_count``,
``credits_remaining``, ``uploads_remaining``.
Inputs: email, password. Returns: object
checkoutAInspect
Create a Stripe checkout session for purchasing a product.
Returns a ``checkout_url`` — the user must open it in a browser to
complete payment. After payment, credits are provisioned automatically
via webhook.
If the agent has a USDC wallet, skip this entirely — just call
``generate`` and the x402 protocol handles payment on-chain.
Args:
product_id: Product ID from ``get_products``.
email: Account email (for Basic auth during onboarding).
password: Account password (for Basic auth during onboarding).
Returns:
Dict with ``checkout_url``. On failure, an ``isError`` result — e.g.
``error: "email_not_verified"`` until the user clicks the
verification link, or ``"auth"`` / ``"failed"``.
Inputs: product_id (required), email, password. Returns: object
configure_api_keyAInspect
Activate an API key. Local (stdio) servers persist it; hosted does not.
Call this after ``manage_api_key(action='create')`` returns a ``raw_key``.
On a local server the key is saved to ``~/.config/maginary/api_key``
(chmod 600) and survives restarts. On the hosted server
(mcp.maginary.ai) nothing can be stored — auth is per-request: the
response will say ``persisted: false`` and the key must be sent as an
``Authorization: Bearer <key>`` header on every request (set it in the
MCP client's connection config).
Args:
api_key: The full API key string returned by ``manage_api_key``.
Returns:
Confirmation dict.
Inputs: api_key (required). Returns: object
create_accountAInspect
Create a new Maginary account for the given email address.
Returns the auto-generated password — display it to the user ONCE so they
can save it. A verification email is sent; the user must click the link
before the account can generate images.
After verification, use ``manage_api_key(action='create')`` with
``email`` + ``password`` to get an API key, then ``configure_api_key``
to activate it.
Args:
email: The user's email address.
Returns:
Dict with ``email``, ``password``, and ``message``. On failure, an
``isError`` result — e.g. ``error: "already_exists"`` (email taken:
ask the user for their password or a different email),
``"rate_limited"``, or ``"failed"``.
Inputs: email (required). Returns: object
generateAInspect
Kick off a generation via POST /api/gens/.
Args:
prompt: The full prompt string, including any ``--flag`` parameters.
E.g. ``"a fox in autumn foliage --ar 16:9 --flagship"``.
Flags go at the END of the prompt. The ones people need most:
``--1`` / ``--2`` / ``--3`` / ``--4`` = how many images (default 4;
``--1`` for a single image, cheapest), ``--ar 16:9`` = aspect ratio,
``--v <model>`` = model. Anything else: call ``get_parameter(name)``
or ``search_parameters`` first — never guess a flag.
callback_url: Optional HTTPS URL that will receive a webhook when the
generation reaches done / failed. See
https://maginary.ai/blog/webhooks-guide for signature verification.
Returns:
On success, the created generation record. Key fields: ``uuid`` (use
to poll), ``action_type``, ``processing_state``,
``expected_output_count``.
On failure, an ``isError`` result instead (nothing is raised), with a
JSON body whose ``error`` field is one of:
- ``"auth"`` — no/invalid API key. Surface the message directly to
the human.
- ``"payment_required"`` — out of credits. The body carries
``billing_url`` and ``challenge``: either send the human to
``billing_url`` to top up, or pay programmatically via x402 —
``challenge`` is the standard x402 payment-required payload (USDC
on Base); settle it and retry this call.
- ``"failed"`` — anything else (invalid prompt, rate limit, backend
or network error); see ``message``.
x402 over MCP: a ``payment_required`` result also carries the x402
fields at the top level (``accepts``, ``resource``); an x402-capable
client signs ``accepts[0]`` and calls this tool again with the payment
in ``_meta["x402/payment"]``. The settled call returns the generation
with ``x402_receipt`` (and ``_meta["x402/payment-response"]``); a
wallet's first settlement also returns ``x402_account`` with an API
key — pass it as ``_meta["maginary/api_key"]`` on later calls (or as
the Authorization header of a new connection).Every flag that exists, and its state: Flags, live (35): --ar, --output-count (--1/--2/--3/--4), --seed, --transparent, --sref, --sw, --png, --jpg, --webp, --svg, --2k, --4k, --upscale, --vary, --varysubtle, --varystrong, --panleft, --panright, --panup, --pandown, --zoomout, --mp4, --video-resolution (--480p/--540p/--720p/--1024p/--1080p/--2160p / --4k (4k, Seedance 2 Pro)/--480p24 / --480p24fps/--540p24 / --540p24fps/--720p24 / --720p24fps/--1024p30 / --1024p30fps/--1080p24 / --1080p24fps), --video-fps (--24fps/--30fps/--50fps/--60fps), --video-duration (--4s / --4sec/--5s / --5sec/--6s / --6sec/--8s / --8sec/--10s / --10sec/--12s / --12sec), --flagship, --sora, --soralite, --nanobananapro, --nb2, --gpt2, --gpt2high, --seedance2, --seedance2pro, --demo. Partial (4, only some models honour them): --no, --zoomout2x, --zoomoutexpand, --zoomoutexpand2x. Reserved (2, the parser rejects them): --cref, --cw. Any other --flag is rejected with Unrecognized parameter. Details: get_parameter(name).
Inputs: prompt (required), callback_url. Returns: object
get_balanceAInspect
Check remaining credits and uploads for the authenticated account.
Args:
email: Account email (for Basic auth).
password: Account password (for Basic auth).
Returns:
Dict with ``credits_remaining`` and ``uploads_remaining``.
Inputs: email, password. Returns: object
get_generationAInspect
Fetch a generation by UUID (GET /api/gens/{uuid}/).
Args:
uuid: The UUID returned by ``generate``.
Returns:
The full generation record. If terminal, ``image_urls[]`` holds the
finished outputs and ``processing_result.slots[]`` the per-slot detail.
NOTE: a generation that failed server-side is a SUCCESSFUL tool call
returning ``processing_state: "failed"`` — always check the state,
never infer success from the absence of a tool error.
Hosted: a key obtained mid-session may be passed as
``_meta["maginary/api_key"]``.
Inputs: uuid (required). Returns: object
get_parameterAInspect
Return the full record for a single parameter (canonical name or alias).
Args:
name: Parameter name with or without leading ``--`` (e.g. ``ar``,
``--ar``, ``aspect``). Case-insensitive.
Returns:
The parameter dict. Not-found is an ``isError`` result — surface it
rather than fabricating a param.
Inputs: name (required). Returns: object
get_productsAInspect
List available Maginary products/plans with pricing.
No authentication required. Use this to present purchase options to the
user. The ``novice_pack`` ($10, 150 credits) is the recommended starting
point.
Returns:
Dict with ``count`` and ``products`` — each product carries ``id``,
``short_name``, ``title``, ``description``, ``price_cents``,
``credits``, ``uploads``, ``is_subscription``. (The backend sends a
bare array; it is wrapped here because FastMCP validates tool output
against the dict annotation and rejects a top-level list.)
Inputs: no named parameters. Returns: object
list_parametersAInspect
List Maginary prompt-DSL parameters.
Args:
category: Restrict to one category (e.g. ``composition``, ``video``,
``model``, ``outpaint``). Call with no filters once — the response's
``categories`` / ``statuses`` maps are the full taxonomy.
status: Restrict to one status (``live``, ``mostly-dead``,
``unimplemented``).
include_reserved: When False (default) drop ``unimplemented``
(recognized-but-blocked) parameters from the result.
Returns:
A dict with ``count``, ``source`` (``live`` vs. ``bundled-snapshot``),
``categories`` / ``statuses`` (the filter taxonomy), and ``parameters``
(the array of matching entries).
Inputs: status, category, include_reserved. Returns: object
manage_api_keyAInspect
Create, list, or revoke Maginary API keys (up to 10 per account).
Auth: pass ``email`` + ``password`` for Basic auth (onboarding), or omit
both to use the configured API key (normal operation).
Args:
action: One of ``create``, ``list``, ``revoke``.
name: Key name (required for ``create``).
key_prefix: 8-char prefix of the key to revoke (required for ``revoke``).
email: Account email (for Basic auth).
password: Account password (for Basic auth).
Returns:
For ``create``: dict with ``raw_key`` (the full key — show once, then
use ``configure_api_key`` to activate it), ``key_prefix``, ``name``.
For ``list``: dict with ``keys`` array.
For ``revoke``: success/error message.
Inputs: action (required), name, email, password, +1 more. Returns: object
search_parametersAInspect
Text-search over parameter names, aliases, descriptions, values, examples.
Args:
query: Substring match, case-insensitive.
category: Optional single-category restriction.
include_reserved: Whether to include ``unimplemented`` parameters.
Returns:
Dict with ``count``, ``source`` (``live`` vs. ``bundled-snapshot``),
and ``parameters`` (ordered as they appear in the catalog).
Inputs: query (required), category, include_reserved. Returns: object
wait_for_generationAInspect
Poll get_generation on a backoff until it reaches done / failed.
Args:
uuid: The UUID returned by ``generate``.
timeout_s: Return after this many seconds even if still running.
Default 45 stays under the 60 s per-call limit most MCP clients
enforce; a ``timeout`` result just means "call again". Only raise
it (e.g. for video) on clients you know allow long tool calls.
Returns:
The terminal generation record — which includes generations that
failed server-side: those are SUCCESSFUL tool calls returning
``processing_state: "failed"`` with empty ``image_urls``, so always
check the state. On tool failure, an ``isError`` result whose
``error`` field is ``"timeout"`` (``message`` names the last
observed state — the generation keeps running server-side and can be
re-fetched with ``get_generation`` later), ``"auth"``, or
``"failed"``.
Inputs: uuid (required), timeout_s. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Tools are mostly distinct: parameter discovery, generation lifecycle, account management, and billing each form clear clusters. Minor overlap exists between check_account_status and get_balance, which both report credits_remaining and uploads_remaining, and generate/get_generation/wait_for_generation require careful reading but are sufficiently differentiated.
Most tools follow a clear verb_noun snake_case pattern: list_parameters, get_parameter, create_account, manage_api_key, get_products. The exceptions are the bare verbs generate and checkout, which are still readable but deviate slightly from the otherwise consistent convention.
13 tools is well-scoped for a service that covers prompt-DSL parameter discovery, generation submission and polling, account onboarding, API key management, and purchasing. Each tool serves a distinct functional area without excessive fragmentation or redundancy.
The core workflow is covered end-to-end: discover parameters, generate, poll for completion, fetch results, create an account, obtain an API key, check balance, and purchase credits. Minor gaps include no way to list past generations or cancel an in-progress generation, but these are not fatal to the primary use case.
Maintenance
Related MCP Connectors
OAuth-protected ImagineVid MCP for image, video, and music generation.
Create and manage AI image and video generations through Quriov's fixed public MCP tools.
Generate AI images and videos from any compatible MCP client.
Generate AI images and videos, quote credit costs, manage media and track batch jobs with OAuth.
Related MCP Servers
AlicenseAqualityDmaintenanceEnables image and video generation through the Magicly API using any MCP client. Supports listing models, generating media, and polling predictions.36 npmMIT- AlicenseNot gradedqualityCmaintenanceEnables MCP-compatible clients to generate and upscale AI images, videos, music, and sound effects, manage generation jobs, access model catalogs, and track credits.3MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP-compatible clients to generate images and videos, synthesize speech, clone and design voices, and create related media outputs.493 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP-compatible clients to generate images and videos, synthesize speech, clone and design voices, and dynamically configure API interactions.493 npmMIT