Generic Image Provider MCP
README.md
# Generic Image Provider MCP
A small stdio MCP server that maps a stable image-tool contract to an
OpenAI-compatible image provider while keeping all file access inside configured
Windows directories. Node.js 22 or newer is required.
## Tools
`tools/list` is the discovery source. `generate_image` and `provider_status` are
always present. `edit_image` appears only when `IMAGE_PROVIDER_EDIT_MODEL` is
configured, and `generate_hd_image` appears only when
`IMAGE_PROVIDER_HD_MODEL` is configured. Clients must not assume optional tools
exist before discovery.
`generate_image` accepts:
```json
{
"prompt": "A paper-cut mountain landscape",
"output_dir": "C:\\work\\images",
"filename": "mountain.png",
"size": "1024x1024",
"quality": "standard"
}
```
`edit_image` accepts the same fields plus one to the configured maximum number
of `reference_images`:
```json
{
"prompt": "Use a warmer palette",
"reference_images": ["C:\\work\\images\\source.jpg"],
"output_dir": "C:\\work\\images",
"filename": "warm.jpg",
"size": "1024x1024",
"quality": "high"
}
```
`generate_hd_image` uses the `generate_image` input shape.
`provider_status` accepts `{}` and returns configured capabilities and limits
without credentials. `quality` is optional and defaults to `standard`; all
other fields shown for generation are required. Prompts are limited to 8,000
characters and both size dimensions must be from 256 to 4096.
Successful image tools return one MCP text item containing JSON:
```json
{
"path": "C:\\work\\images\\mountain.png",
"mime_type": "image/png",
"width": 1024,
"height": 1024,
"bytes": 123456,
"sha256": "64 lowercase hexadecimal characters",
"model": "configured-model-name",
"capability": "standard_generation"
}
```
The stable public error codes are `INVALID_ARGUMENT`,
`CAPABILITY_UNAVAILABLE`, `PATH_NOT_ALLOWED`, `REFERENCE_INVALID`,
`OUTPUT_EXISTS`, `WRITE_FAILED`, `UNSUPPORTED_IMAGE`, `IMAGE_INVALID`,
`IMAGE_TOO_LARGE`, `PROVIDER_TIMEOUT`, `PROVIDER_AUTH_FAILED`,
`PROVIDER_RATE_LIMITED`, `PROVIDER_BAD_RESPONSE`, and `INTERNAL_ERROR`.
For `tools/call`, an `INVALID_ARGUMENT` failure is returned as JSON-RPC error
`-32602` with `error.data.code` set to `INVALID_ARGUMENT`. Provider,
persistence, path, capability, and other execution-time failures are returned
inside a successful JSON-RPC envelope as an MCP tool result with `isError: true`;
its text item contains `{"code":"...","message":"..."}`. Neither form includes
credentials.
## Windows installation
Start from a release checkout whose `origin` is
`https://github.com/huiku8177-rgb/generic-image-provider-mcp.git`. Check out a
specific release tag, create the installation parent directory, ensure Git can
reach public GitHub over HTTPS, and run:
```powershell
.\scripts\install-windows.ps1 `
-InstallDirectory 'C:\Tools\generic-image-provider-mcp' `
-ReleaseDirectory 'C:\Releases\generic-image-provider-mcp' `
-ExpectedRef 'v1.0.3'
```
`ExpectedRef` is mandatory and accepts only an explicit release tag such as
`v1.0.3`; a branch or full commit ID is rejected. The checkout's `HEAD` must
match the local tag. The local `origin` must be the exact canonical HTTPS URL
above, and the installer uses `git ls-remote` to require the GitHub tag,
including an annotated tag's peeled commit, to match the local commit. A
network or remote-verification failure stops before staging or switching.
Official tag lookup requires a Git for Windows build with the Schannel TLS
backend. The installer forces `http.sslBackend=schannel`,
`http.schannelUseSSLCAInfo=false`, and certificate verification. It temporarily
removes ambient Git, CA-bundle, OpenSSL, and Requests trust overrides, then
restores the parent process environment exactly. Proxy variables remain
available only for routing; Schannel still validates GitHub against the Windows
trust store. If Schannel is unavailable, verification fails safely before
staging. The updater delegates staging and verification to this same installer
path.
The installer requires an existing destination parent and refuses an existing
destination. It resolves paths, rejects broad or overlapping targets, and
requires Node.js 22+. The verified commit is governed by a positive allowlist:
the root files `.env.example`, `.gitignore`, `LICENSE`, `README.md`,
`package.json`, and `package-lock.json`; the two Windows scripts
`scripts/install-windows.ps1` and `scripts/update-windows.ps1`; `src/*.mjs`;
`test/*.test.mjs`; and `test/fixtures.mjs`. Every other path is rejected,
including submodules, symlinks, credential files, secret material, and media.
Windows device names, alternate data streams, control characters, trailing
dots/spaces, traversal, absolute paths, and case/trim-normalized collisions are
also rejected before extraction.
The installer exports the verified commit with `git archive`. Working-tree
edits, index-only changes, untracked files, and `.git` metadata therefore never
enter the installation. After extraction, the installer requires the file set
to equal the official tag's verified tree exactly and hashes every extracted
file as a raw, unfiltered Git blob; each path and blob ID must match before npm
is allowed to run. Local or environment-provided Git attributes therefore
cannot silently omit files or alter `export-subst` bytes.
It removes credential-bearing environment variables
from the `npm ci --omit=dev --ignore-scripts` and `npm test` subprocesses,
restores the parent PowerShell environment afterward, and performs both in a
sibling stage before a fail-if-exists directory rename.
The release must contain a tracked `package-lock.json`. Installation does not
fetch a checkout, trust a branch name, copy `.git`, or modify an existing
installation.
## Codex configuration
Set environment-variable values in the process that launches Codex. The
installer never reads or writes an API-key value and prints only this
name-based configuration:
```toml
[mcp_servers.generic_image_provider]
command = "node"
args = ["C:\\Tools\\generic-image-provider-mcp\\src\\server.mjs"]
env_vars = [
"IMAGE_PROVIDER_ALLOWED_ROOTS",
"IMAGE_PROVIDER_BASE_URL",
"IMAGE_PROVIDER_API_KEY",
"IMAGE_PROVIDER_IMAGE_MODEL",
"IMAGE_PROVIDER_EDIT_MODEL",
"IMAGE_PROVIDER_HD_MODEL",
"IMAGE_PROVIDER_TIMEOUT_MS",
"IMAGE_PROVIDER_MAX_RESPONSE_BYTES",
"IMAGE_PROVIDER_MAX_REFERENCE_BYTES",
"IMAGE_PROVIDER_MAX_REFERENCE_IMAGES"
]
```
Environment variables:
| Name | Required | Meaning/default |
| --- | --- | --- |
| `IMAGE_PROVIDER_ALLOWED_ROOTS` | yes | Semicolon-separated absolute local Windows directories |
| `IMAGE_PROVIDER_BASE_URL` | yes | Provider HTTP(S) base URL |
| `IMAGE_PROVIDER_API_KEY` | yes | Provider credential; never logged |
| `IMAGE_PROVIDER_IMAGE_MODEL` | yes | Standard generation model |
| `IMAGE_PROVIDER_EDIT_MODEL` | no | Enables `edit_image` |
| `IMAGE_PROVIDER_HD_MODEL` | no | Enables `generate_hd_image` |
| `IMAGE_PROVIDER_TIMEOUT_MS` | no | `120000`; range 1,000–300,000 |
| `IMAGE_PROVIDER_MAX_RESPONSE_BYTES` | no | `26214400`; range 1,024–104,857,600 |
| `IMAGE_PROVIDER_MAX_REFERENCE_BYTES` | no | `20971520`; range 1,024–52,428,800 |
| `IMAGE_PROVIDER_MAX_REFERENCE_IMAGES` | no | `4`; range 1–8 |
## Allowed roots
Every allowed root must be an existing absolute local-drive directory below a
drive root. Drive roots, relative paths, UNC paths, and Windows device paths are
rejected. `output_dir` and every reference must resolve within an allowed root
on the same drive.
Output directories must already exist. `filename` is a single safe basename,
not a path, and must end in `.png`, `.jpg`, or `.jpeg`. Existing outputs are
never overwritten: publication uses a no-replace hard link and returns
`OUTPUT_EXISTS` if another file already owns the name. References must be
bounded regular files and are checked before and after opening.
## Provider compatibility
Version 1.0.3 accepts PNG and 8-bit baseline sequential JPEG (SOF0) only.
The supported baseline subset is either one all-component interleaved scan in
SOF component order or one ordered non-interleaved scan per component. Mixed
component grouping is unsupported. Quantization tables must use 8-bit precision,
and both defined Huffman table identifiers and SOS DC/AC selectors are limited
to tables 0 and 1. An interleaved scan may contain at most ten sampling blocks
per MCU. Extended sequential SOF1, progressive SOF2, 12-bit JPEG, out-of-order,
duplicate, incomplete, or mixed-group component scans are rejected as
`IMAGE_INVALID`. WebP remains intentionally unsupported until a trustworthy
decoder is available.
The standard model maps to `POST /images/generations` with an
OpenAI-compatible JSON body. Editing maps to multipart
`POST /images/edits`. HD generation maps to `POST /chat/completions`. Standard
and editing responses may contain one `data[0].b64_json` image or one approved
image URL; HD chat content must identify exactly one approved image URL.
Returned bytes are decoded and structurally validated, and their dimensions
and extension must match the request. JSON envelopes and decoded/streamed image
bytes are bounded by `IMAGE_PROVIDER_MAX_RESPONSE_BYTES` (with only fixed JSON
encoding overhead), and the configured request timeout remains active through
body consumption or cancellation.
Remote image downloads require HTTPS, globally routable unicast DNS results,
one pinned address, bounded streaming, and at most one same-origin HTTPS
redirect. IPv6 is restricted to 2000::/3 with IANA special-purpose ranges
excluded; IPv4 private, shared, loopback, link-local, documentation, benchmark,
multicast, and reserved ranges are excluded. HTTP image downloads are allowed
only when the configured provider is loopback, so offline tests can use
loopback mock servers. For every non-success Fetch response, the response body
is cancelled or destroyed before the stable auth, rate-limit, or bad-response
error is returned; cancellation failure never replaces that primary error.
## Offline tests
No provider credential is needed:
```powershell
npm ci --ignore-scripts
npm test
npm run test:coverage
```
The suite makes no external network requests. Provider tests use loopback mock
servers, and release-authenticity tests redirect the production
canonical-URL `git ls-remote` invocation to a temporary local bare Git remote
through a test-only executable shim. Coverage includes configuration, path
policy, provider mapping, PNG/JPEG parsing, atomic cleanup, tools,
JSON-RPC/session handling, and the Windows installation/update behavior.
## Manual smoke test
A real-provider smoke test spends provider quota and is never part of the
offline suite or installer. Run one only after the account owner gives explicit
quota authorization for that exact test. First call `provider_status`, discover
the available tools, choose a new filename inside an allowed root, and request
one small standard image. Confirm MIME type, dimensions, SHA-256, and local
path, then account for the provider charge. Do not retry a quota-consuming
request automatically.
## Update and rollback
Stop clients using the server, prepare a separate official release checkout,
ensure Git can reach public GitHub over HTTPS for tag verification, and run:
```powershell
.\scripts\update-windows.ps1 `
-CurrentDirectory 'C:\Tools\generic-image-provider-mcp' `
-ReleaseDirectory 'C:\Releases\generic-image-provider-mcp' `
-ExpectedRef 'v1.1.0'
```
The updater rejects a source checkout as `CurrentDirectory`, verifies the
canonical official origin and explicit release tag against GitHub, exports the
verified commit into a unique sibling stage, scrubs credential environment
variables, runs `npm ci --omit=dev --ignore-scripts` and `npm test`, and only
then starts switching. It uses fail-if-exists sibling directory renames to move
the current installation to `backup-<name>-<UTC timestamp>` and activate the
stage, retaining the backup after success.
If the final rename fails, the updater reports the old installation as restored
only after the backup-to-current rename completes and the resulting namespace
state is verified. If recovery is prevented by a concurrent target or another
namespace change, it reports `Recovery uncertain` with the absolute current,
backup, and stage paths and deliberately preserves both stage and backup.
Stop and inspect those paths; do not delete either directory. For a later manual
rollback, stop the server, verify the absolute current and backup paths are
direct siblings, move the current directory aside, and move the selected backup
to the original name. Backups are never removed automatically.
## Threat model
The server trusts the configured provider endpoint, provider credential, the
administrator-selected allowed roots, and a release commit/tag the installer
operator has independently verified. It does not trust prompts, filenames,
reference paths, provider image bytes, redirects, or concurrent output-tree
changes. Credentials stay in the parent environment and public failures are
redacted.
Output revalidation is mandatory and executes twice: immediately before
temporary creation and immediately before the final no-replace link.
With Node built-in Windows filesystem APIs, an instruction-level namespace race
remains between the final revalidation and the no-replace link. The temporary
pathname/content binding and the final pathname binding can be changed by any
actor whose Windows ACL grants write or rename rights in the configured output
tree; this is not inherently limited to an actor logged in as the same account.
Allowed-root ACLs must not grant other accounts write or rename rights. Even
with that ACL boundary, a malicious process already running as the same account
can race the pure-Node sequence. This residual same-account race is an accepted
threat-model limitation; it does not permit omitting either revalidation and
does not expand trust to other local users.
The no-replace link still prevents overwriting an existing target. Temporary
files are uniquely named and removed on validation or publication failure when
the namespace remains controlled. The returned SHA-256 describes the provider
bytes validated by the server; it is not a durable binding to the final pathname
after a concurrent malicious namespace writer can act. Update staging and
backups are direct siblings of the validated current installation. A failed
final switch restores the prior installation only when the recovery rename and
postcondition checks succeed.
## License
MIT. See [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues