Skip to main content
Glama
README.md
# OpenRouter GenImage Kote for Claude Code

`openrouter-genimage-kote` is a native Claude Code plugin that generates and
edits raster images through OpenRouter. Image-to-image is the primary workflow:
the plugin can preserve a character or object while changing pose, clothing,
background, lighting, or style, and it can use up to 16 ordered references.
Text-to-image is available for new assets that do not need a visual reference.

The plugin connects directly to the fixed OpenRouter API origin. It has no
proxy mode, KoteGateway, configurable API origin, route selection, or dependency
on an external image broker.

```text
Claude Code
    -> bundled local stdio MCP server
    -> direct HTTPS
    -> https://openrouter.ai/api/v1/
    -> generated file inside the primary project root
```

## Requirements

- Claude Code 2.1.216 or newer. Version 2.1.216 is the minimum supported
  release because this plugin relies on the corrected plugin-skill namespace
  behavior as well as secure `userConfig` handling. Development validation is
  pinned to Claude Code 2.1.241.
- Node.js 20 or newer available as `node`.
- A personal OpenRouter API key.
- An exact OpenRouter image-model ID that supports the requested operation and
  parameters.
- Optional: an OpenRouter management key for full account-credit information.

The MCP roots refresh behavior itself requires Claude Code 2.1.203+, but this
plugin intentionally sets the higher 2.1.216 minimum for the reasons above.

## Install from the marketplace

In Claude Code, add the GitHub marketplace:

```text
/plugin marketplace add koteyye/openrouter-genimage-kote-claude-code
```

Install the plugin:

```text
/plugin install openrouter-genimage-kote@kote-plugins
```

The plugin is disabled by default because image generation can incur charges.
Open `/plugin`, configure the four options described below, and explicitly
enable the plugin. Enter secrets in the interactive plugin UI: do not place a
real API key in a shell command, where it can be retained in shell history.

Reload plugin components after configuration:

```text
/reload-plugins
```

Then inspect MCP status:

```text
/mcp
```

The `openrouter-images` server should be connected and expose three tools.

## Configuration

Claude Code prompts for these `userConfig` values when the plugin is enabled:

| Setting | Required | Sensitive | Purpose |
| --- | --- | --- | --- |
| `openrouter_api_key` | Yes | Yes | Personal key used for model discovery, generation, and `/key` usage data |
| `openrouter_management_api_key` | No | Yes | Used only for `/credits`; leave empty if unavailable |
| `image_model` | Yes | No | Exact OpenRouter image-model ID; the tool cannot override it |
| `output_directory` | Yes | No | Project-relative default output directory; defaults to `assets/generated` |

Sensitive values are masked and stored through Claude Code secure storage.
The plugin receives them only as environment variables in its local MCP
process. An empty management key is treated as absent.

There is deliberately no `baseUrl`, endpoint, routing, provider, or proxy
setting. The model cannot choose a network route or silently select another
image model.

## Tools

| MCP tool | Use | Confirmation |
| --- | --- | --- |
| `openrouter_edit_image` | Edit, transform, vary, or match one or more images | Required for every call |
| `openrouter_generate_image` | Create a new image without references | Required for every call |
| `openrouter_balance` | Read key usage/limits and optional account credits | No paid-tool confirmation annotation |

Claude Code tool names are namespaced at runtime. The balance skill is scoped
to the expected name
`mcp__plugin_openrouter-genimage-kote_openrouter-images__openrouter_balance`,
pinned in the skill and static checks for Claude Code 2.1.241. Its appearance in
the live tool list remains an interactive release-gate check.

Both paid tools publish
`_meta["anthropic/requiresUserInteraction"] = true`, so Claude Code must ask
for explicit approval on every generation or edit. The MCP server does not
implement its own prompt and never reads stdin outside the MCP transport.

## Image-to-image: primary workflow

Ask Claude to edit a path inside the workspace:

```text
Make the background transparent in assets/character.png and save the result as
assets/character-transparent.png. Preserve the character, clothing, proportions,
and illustration style.
```

Claude should call `openrouter_edit_image` with a `project_file` reference. A
local PNG, JPEG, or WebP is checked by real path, size, and magic bytes, then
sent in memory as a base64 data URL. The source file is never modified.

For an iterative edit, refer to the path returned by the preceding tool call:

```text
Create another variation of assets/generated/character-in-tavern.png with
evening lighting. Keep everything else as close as possible.
```

### Public HTTPS reference

```text
Use https://example.com/reference.webp as the visual reference and create a
matching inventory icon with a transparent background.
```

Only public `https://` URLs without credentials are accepted. Localhost,
loopback, link-local, metadata addresses, `http:`, `file:`, `data:`, `blob:`,
and `javascript:` inputs are rejected. An explicit HTTPS reference is passed to
OpenRouter as supplied; the plugin does not attach OpenRouter authorization to
that URL.

### Multiple ordered references

```text
Use assets/references/girl.png for identity and face,
assets/references/outfit.png for clothing only, and
https://example.com/style.webp for palette and lighting. Preserve that order.
```

Claude describes the role of each reference in the prompt and passes them in
the same order. The plugin does not sort, merge, remove, or reinterpret them.
The first reference is normally the primary subject.

### Images pasted into chat

The public MCP interface accepts only an accessible workspace path or a public
HTTPS URL. If an image is pasted into Claude Code but no concrete usable path
is exposed to the model, Claude must not invent a path or search undocumented
Claude Code caches. Save the attachment in the project, for example as
`assets/references/source.png`, and provide that path, or provide a public HTTPS
URL.

If a supported Claude Code release provides a concrete path, it is treated as
a normal `project_file` only after it passes MCP-root validation.

## Text-to-image

Use text-to-image only when no existing visual must influence the result:

```text
Create a 1:1 inventory icon of one healing potion, centered with generous
padding, readable at small size, no text, transparent background, PNG.
```

One result is generated by default. Ask for multiple alternatives explicitly;
`n` may be 1 through 10 and additional images may increase cost. Aspect ratio,
background, format, resolution, quality, compression, and seed are passed as
tool parameters when requested and supported by the configured model.

Before the paid request, the server discovers model capabilities. It rejects
unsupported image input/output or parameters locally and never falls back to a
different model. A paid `POST /images` is never retried automatically because
the upstream billing status may be unknown.

## Outputs

Results are written only inside `CLAUDE_PROJECT_DIR`, even when a source comes
from an additional MCP root. Paths are project-relative. Existing files and
source files are never overwritten; conflicts receive a numeric suffix. Writes
use a complete, fsynced temporary file followed by atomic no-clobber
publication in the same directory; an existing destination is never replaced.

The MCP result contains concise text and structured content with operation,
model, requested/returned counts, reference count, optional cost, saved paths,
MIME types, byte sizes, and warnings. It never returns full image bytes, base64,
the prompt, or credentials. If some returned items are invalid, valid images
are saved and the result reports partial success. If OpenRouter omits
`usage.cost`, the plugin reports that cost was not provided rather than showing
zero.

After generation, Claude should report the exact saved path and inspect the
file before describing its visual contents. That saved path can be used as the
next image-to-image reference.

## Workspace roots

Relative input paths resolve from `CLAUDE_PROJECT_DIR`. Absolute input paths
are allowed only when their real paths are regular files inside the current MCP
roots. Roots include the primary project and directories added with `--add-dir`,
`/add-dir`, or Claude Code additional-directory settings. The server refreshes
its in-memory roots after `notifications/roots/list_changed`.

Additional roots are read-only from the plugin's perspective: every generated
output remains inside the primary project. Lexical traversal and symlink escape
are rejected for both input and output paths.

## Balance and credits

Run the native namespaced skill:

```text
/openrouter-genimage-kote:openrouter-balance
```

You can also ask, “Check my OpenRouter balance.” The skill calls
`openrouter_balance` exactly once and does not start a generation.

`GET /key` reports usage and the spending limit for the current API key. Its
`limit_remaining` value is the **remaining API-key spending limit**, not the
full OpenRouter account balance. If a management key is configured—or the
current key explicitly identifies itself as a management key—the plugin also
calls `GET /credits` and reports total credits, total account usage, and their
arithmetic difference. A credits failure is reported as unavailable while
successful key usage remains visible. Balance responses are not cached.

## Security and network behavior

- All authenticated production requests go directly to
  `https://openrouter.ai/api/v1/` over HTTPS.
- Redirects are rejected, preventing authorization from reaching a redirect
  target.
- Proxy environment variables are not read automatically; no global `fetch`
  interception, proxy dispatcher, gateway bootstrap, or route marker exists.
- API keys are passed to the bundled local process through environment
  substitution, never command-line arguments or plugin files.
- Logs go to stderr as redacted JSON lines. They exclude full prompts, keys,
  authorization headers, base64, binary images, and full upstream bodies.
- Local references are limited to 20 MiB each, 50 MiB total, and 16 images.
  Response and decoded-image totals are bounded at 128 MiB by default.
- Public HTTPS references are disclosed to OpenRouter as part of the paid
  request. Use only URLs you are authorized to share with that service.

See [SECURITY.md](SECURITY.md) for reporting and the complete threat model.

## Version 0.1.0 limitations

Version 0.1.0 does not support masks, interactive inpainting, pixel-perfect
edits, internal attachment identifiers, undocumented Claude image caches, SVG
output, PDF/GIF/HEIC/TIFF/BMP/ICO input, video, LoRA training, streaming image
generation, a plugin UI, object storage, an external image broker, automatic
input resizing, or automatic credit purchases. Provider-specific passthrough
and automatic model switching are intentionally absent.

Image models are generative: preservation and seeds do not guarantee identical
pixels or absolute reproducibility.

## Troubleshooting

### MCP server is disconnected

1. Confirm `node --version` is 20 or newer.
2. Confirm Claude Code is 2.1.216 or newer.
3. Open `/plugin` and verify the plugin is enabled and all required options are
   non-empty.
4. Run `/reload-plugins`, then `/mcp`.
5. Inspect Claude Code debug output and MCP stderr. Never paste unredacted keys
   into an issue.

### Configuration error

Open `/plugin` and correct the API key, exact image-model ID, and relative
output directory. The server registers all tools even when startup config is
invalid; calls return `GENIMAGE_CONFIG_INVALID` rather than crashing the MCP
transport.

### Reference is rejected

Use PNG, JPEG, or WebP; verify the file is inside a current MCP root and under
20 MiB. Add another directory through Claude Code rather than copying an
absolute path from outside the roots. For a pasted image, save it in the
project first.

### Model or parameter is unsupported

Select an exact OpenRouter model that advertises image output; image-to-image
also requires image input and `input_references`. Remove unsupported optional
parameters or configure a suitable model yourself. The plugin will not select
a substitute.

### Output already exists

Choose a new project-relative path. The plugin intentionally does not
overwrite existing files.

## Update and remove

Refresh the marketplace and update the plugin:

```text
/plugin marketplace update
/plugin update openrouter-genimage-kote@kote-plugins
/reload-plugins
```

Remove it with:

```text
/plugin uninstall openrouter-genimage-kote@kote-plugins
```

The plugin does not store API keys itself. Generated project files remain after
uninstall and must be removed by the user if no longer wanted.

## Development and verification

```bash
npm ci
npm run ci
```

The build uses local esbuild only, performs no network request, and produces a
single Node 20 ESM file at
`plugins/openrouter-genimage-kote/dist/mcp-server.mjs` with no source map. The
plugin runtime does not need a neighboring `node_modules` directory.

Run the current pinned Claude validator explicitly:

```bash
npx --yes @anthropic-ai/claude-code@2.1.241 plugin validate ./plugins/openrouter-genimage-kote --strict
npx --yes @anthropic-ai/claude-code@2.1.241 plugin validate . --strict
```

`npm run validate:plugin` always performs static manifest, skill, prohibited
routing, and secret checks. It also runs the pinned CLI when that package is
already available in the npm cache; this automatic path is offline. CI invokes
the pinned validator explicitly.

`npm run smoke:marketplace` copies the marketplace and plugin into a temporary
cache layout and does not touch the real Claude configuration. Set
`RUN_CLAUDE_MARKETPLACE_SMOKE=1` to add/install/list/uninstall the local
marketplace with Claude Code 2.1.241 in isolated temporary config and plugin
directories.

Real OpenRouter tests are paid and disabled by default:

```bash
RUN_OPENROUTER_E2E=1 \
OPENROUTER_API_KEY="<enter through a secure CI secret or local environment>" \
OPENROUTER_IMAGE_MODEL="provider/model-id" \
npm run test:e2e
```

An optional management-key credits test additionally reads
`OPENROUTER_MANAGEMENT_API_KEY`. Do not place real credentials in committed
files or command history.

CI is configured for Ubuntu, macOS, and Windows. Automated checks cover source,
protocol, integration, bundle, manifest, and isolated marketplace behavior.
Interactive `/mcp`, slash-skill discovery, the per-call confirmation dialog,
and paid image quality remain release-gate checks; do not infer that they were
performed from a successful source build alone.

## License and attribution

Licensed under MIT. Platform-independent portions were adapted at source level
from `openrouter-genimage-kote-opencode` at commit
`fd1e29d4a740d1765ce69b80ace722cec0a9258b`, also MIT licensed. See
[THIRD_PARTY_NOTICES.md](plugins/openrouter-genimage-kote/THIRD_PARTY_NOTICES.md)
for dependency and reuse notices.