New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

muse-image-mcp

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

muse-image-mcp

MCP server for Meta Muse image generation

latest
Source
npmnpm
Version
0.1.3
Version published
Maintainers
1
Created
Source

muse-image-mcp

CI npm License: MIT

English · 繁體中文 · 简体中文

An MCP server that gives any MCP-capable agent — Claude Code, Claude Desktop, Cursor — image generation powered by the Meta Muse image model.

What it does

Three tools, covering the full loop of working with images in a conversation:

ToolWhat it's for
generate_imageText to image. 1–10 images per call.
edit_imageEdit from reference images — local files or URLs.
iterate_imageConversational refinement. Keep saying "make it warmer" and it remembers.

Why this server

Your context window survives. Every tool writes images to disk and returns an absolute file path — never the image bytes. Generating a dozen images costs you a dozen lines of context instead of a dozen megabytes of base64. When you actually want to look at an image, open the path with a file-reading tool.

Multi-turn refinement without state. iterate_image returns a response_id; feed it back as previous_response_id and the next turn continues the same conversation. The server itself stores nothing — conversation state lives on Meta's side, so the server stays restartable and stateless.

New models don't require a new release. Switch models with MUSE_MODEL or a per-call model parameter, and pass parameters this server has never heard of through extra_params. Fields that determine request structure are protected from being overwritten; everything else is a deliberate escape hatch.

You always know what it cost. Every response ends with the estimated cost of that call.

Requirements

  • Node.js >= 20.12.0
  • A Meta Muse API key

Getting an API key

Muse Image runs on the Meta Model API, so you register with Meta — not with this project:

  • Go to https://dev.meta.ai and sign in to the Meta Model API dashboard.
  • Open API keys.
  • Click Create API key and copy the value.

That value is what you pass as MUSE_API_KEY below. Meta's own documentation calls this variable MODEL_API_KEY; this server reads it as MUSE_API_KEY, talks to https://api.meta.ai/v1, and defaults to the model muse-image-1.0.

Reference: Model API docs · Image generation · Muse Image announcement

Keep the key out of source control — use the MCP config's env block or a .env file, both described below.

Install

claude mcp add muse-image --scope user --env MUSE_API_KEY=your-key -- npx -y muse-image-mcp

npx fetches and runs the latest version on demand — nothing to install first.

  • --scope user applies it to every project; use --scope local for the current project only.
  • -y skips npx's install prompt. Without it the server hangs on an interactive question and the handshake fails.
  • Everything after -- is the launch command; --env before it belongs to claude mcp add.

For other MCP clients (Claude Desktop, Cursor), write the config by hand:

{
  "mcpServers": {
    "muse-image": {
      "command": "npx",
      "args": ["-y", "muse-image-mcp"],
      "env": { "MUSE_API_KEY": "your-key" }
    }
  }
}

On Windows, if npx can't be found, use "command": "cmd" with "args": ["/c", "npx", "-y", "muse-image-mcp"].

Option 2: Local clone (when you want to change the code)

git clone https://github.com/kevintsai1202/muse-image-mcp.git
cd muse-image-mcp
npm install
npm run build
claude mcp add muse-image --scope user --env MUSE_API_KEY=your-key -- node <your-clone-path>/dist/index.js

After installing

Start a new session — MCP servers are loaded at session start, so an existing session won't pick it up. Then confirm with claude mcp list, which should show muse-image: ... - Connected, and check that the three mcp__muse-image__* tools are available.

Configuring the API key

Precedence is the env block in your MCP config > a .env file.

Using the MCP config env block (the only route when installed via npx)

See --env MUSE_API_KEY=your-key above.

Using a .env file

The server searches these locations in order and uses the first one that exists:

  • <package root>/.env — convenient for a local clone
  • ~/.muse-image-mcp/.env — the only location you control when installed via npx
MUSE_API_KEY=your-key

Note that .env is not read from the directory you launched Claude Code in — an MCP server's working directory is decided by the client, which makes it a poor place for configuration. When installed via npx the package itself lives in a hashed npm cache directory that gets cleaned up, so a .env there would be pointless.

Environment variables

All of these work in either .env or your MCP config's env block.

VariableRequiredDefaultDescription
MUSE_API_KEYYesAPI key. Without it the server exits immediately and explains itself on stderr
MUSE_MODELNomuse-image-1.0Global default model ID
MUSE_EXTRA_PARAMSNo{}JSON object string — global default extra parameters
MUSE_OUTPUT_DIRNo<cwd>/generated-imagesOutput directory, created if missing
MUSE_BASE_URLNohttps://api.meta.ai/v1API base URL
MUSE_TIMEOUT_MSNo120000Per-request timeout in milliseconds

The cwd in MUSE_OUTPUT_DIR's default is the working directory the MCP client launched the server from. In Claude Code that's the project root of your session, so images land in that project's generated-images/. If your client behaves differently, or you want a fixed location, set MUSE_OUTPUT_DIR to an absolute path.

Since v0.1.0 the default output directory changed from muse-output/ to generated-images/. The old directory is not deleted or migrated automatically.

Switching models and passing new parameters

When a new model ships you don't have to wait for this project to update — switch models with an environment variable, send new parameters through extra_params.

Switching models

Globally, in .env or your MCP config:

MUSE_MODEL=muse-image-2.0

Per call, just ask for it in conversation and the agent will pass model:

{ "prompt": "a red fox", "model": "muse-image-2.0" }

Passing new parameters

Global defaults as a JSON object string:

MUSE_EXTRA_PARAMS={"quality":"ultra"}

Per-call overrides via extra_params, merged with the global setting — the per-call value wins:

{ "prompt": "a red fox", "extra_params": { "style_preset": "anime" } }

Protected core fields

model, prompt, response_format, images, input, store, and previous_response_id determine the structure of the request and cannot be overwritten by extra_params. Setting them there has no effect, and the response will end with a warning listing the ignored keys.

To change models, use the model parameter or MUSE_MODEL — not extra_params.

Outside those core fields, extra_params does override same-named regular parameters, including n, size, output_format, and reasoning_strength. The tool schema's validation for these (for example n being limited to 1–10) does not apply on this path — that's a deliberate escape hatch so a future model that changes parameter semantics isn't blocked by today's limits. When overriding n this way, watch your image count and cost.

Tools

Every tool saves images locally and returns absolute paths, never the image content itself — this keeps base64 out of your conversation context. Open the path with a file-reading tool when you want to see the image.

generate_image — text to image

ParameterRequiredDefaultDescription
promptYesImage description
nNo1Number of images, 1–10
sizeNoAspect ratio string such as 1792x1024 — not an exact pixel resolution
output_formatNopngpng / webp / jpeg
reasoning_strengthNohighhigh / low — priced the same
filename_prefixNomuseOutput filename prefix
modelNoModel ID; omit to use the server default (see MUSE_MODEL)
extra_paramsNoObject of extra parameters, merged with MUSE_EXTRA_PARAMS with per-call priority; core fields are protected (see above)

edit_image — edit from reference images

ParameterRequiredDefaultDescription
promptYesImage description
imagesYesArray of local file paths (png/jpg/jpeg/webp/gif) or http(s) URLs. Local files are base64-encoded automatically
nNo1Number of images, 1–10
sizeNoAspect ratio string such as 1792x1024 — not an exact pixel resolution
output_formatNopngpng / webp / jpeg
reasoning_strengthNohighhigh / low — priced the same
filename_prefixNomuse-editOutput filename prefix
modelNoModel ID; omit to use the server default
extra_paramsNoSame merge and protection rules as above

iterate_image — conversational refinement

ParameterRequiredDescription
promptYesThis turn's instruction
previous_response_idNoThe id returned by the previous turn; omit to start a new conversation
imagesNoReference images for the first turn
reasoning_strengthNoDefaults to high
sizeNoAspect ratio string such as 1024x1536
output_formatNopng / webp / jpeg. Unlike the other two tools, omitting it yields webp — that's Meta's default for this endpoint
filename_prefixNoDefaults to muse-iter
modelNoModel ID; omit to use the server default
extra_paramsNoSame merge and protection rules as above

This tool has no n parameter — the /v1/responses endpoint returns one image per turn. It does support size and output_format, but they travel inside the request's tools entry rather than at the top level; see the findings below.

The response always includes a response_id. Pass it as previous_response_id on the next call to continue the same conversation. This server stores no conversation state; Meta does.

Findings from /v1/responses

Request body: per-image settings live inside tools

reasoning_strength, size and output_format are not top-level parameters on this endpoint — they belong to the image_generation tool entry. Sending reasoning_strength at the top level is not merely ignored; the API rejects the whole request:

HTTP 400 — unknown parameter `reasoning_strength`

The correct shape:

{
  "model": "muse-image-1.0",
  "input": "now make the background deep navy",
  "store": true,
  "previous_response_id": "resp_abc123",
  "tools": [
    { "type": "image_generation", "reasoning_strength": "low", "size": "1024x1536", "output_format": "png" }
  ]
}

Versions up to 0.1.0 sent reasoning_strength at the top level, which made iterate_image fail on every call. generate_image and edit_image were never affected — they use /images/generations and /images/edits, where these are legitimate top-level parameters.

This also corrects an earlier claim in this README: size and output_format are supported here. Specifying output_format: "png" returns a genuine PNG (verified by the base64 header) rather than the endpoint's webp default. Only n is genuinely unavailable — one image per turn.

scripts/probe-responses-api.mjs reproduces all of this against the live API, one hypothesis per case.

Response shape

Meta doesn't publish the response schema for /v1/responses. The original implementation was an educated guess modeled on OpenAI's Responses convention; it was later verified against real API calls. The actual structure:

{
  "model": "muse-image-1.0",
  "id": "resp_6aa4c23f99592ae0ac454928",
  "object": "response",
  "status": "completed",
  "output": [
    { "type": "reasoning", "summary": [{ "type": "summary_text", "text": "..." }] },
    { "type": "message", "role": "assistant", "content": [{ "type": "output_text", "text": "" }] },
    { "type": "image_generation_call", "id": "ig_...", "status": "completed", "result": "<base64 image data>" }
  ]
}

Differences from the guessed shape:

  • id location: raw.id was correct, confirmed by testing. No change needed.
  • Image data location: not in any b64_json field, but in the result field of the output[] entry where type === "image_generation_call", as raw base64 with no data URL prefix. src/muse-client.ts's extractB64Images now recognizes both b64_json (kept for other possible shapes) and this verified shape.
  • No output_format field: the response has none. The original fallback default of "png" was wrong — since the iterate request doesn't send an output_format parameter, Meta applies the same default as /images/generations, which is webp. The returned base64 decodes to a genuine WebP file (RIFF/WEBP header). The fallback is now "webp".

Multi-turn conversation is verified

Two real API calls were made in sequence: one iterate_image to obtain a response_id, then a second call using that id as previous_response_id. The second turn returned only the one image generated in that turn — it did not re-send the first turn's image. extractB64Images's deep-traversal logic needed no changes for this.

(The test tooling in that run didn't capture the second turn's raw response byte-for-byte, so this conclusion rests on filesystem evidence — exactly one output file, with no -2/-3 suffixes — rather than a byte-level comparison. The pinning test added in tests/muse-client.test.ts uses the real response shape verified earlier.)

Pricing

US$0.01 per generated image, regardless of reasoning_strength. Every tool response discloses the estimated cost of that call.

This server is free and MIT-licensed — you pay Meta for API usage, nothing else.

Development

npm test          # Unit tests (never hits the real API)
npm run build     # Compile to dist/
npm run smoke     # Real-API smoke test; needs MUSE_E2E=1 and a real key. Generates 6 images, about US$0.06

License

MIT © Kevin Tsai

Keywords

mcp

FAQs

Package last updated on 12 Sep 2026

Related posts