Assets Generation MCP Server
by ayaka209
README.md
# Assets Generation MCP Server
[English](README.md) | [简体中文](README.zh-CN.md)
An MCP server for AI image generation with dual-provider support for OpenAI-compatible models and Google Gemini. It returns standard MCP `ImageContent` blocks.
## Features
- Automatically selects the provider from the model name
- When both OpenAI and Gemini are configured, provider selection is still based on the requested model; if no model is provided, `DEFAULT_MODEL` is used
- Returns images as MCP-standard `ImageContent` (`{ type: "image", data, mimeType }`)
- Supports three transports: stdio (default), SSE, and HTTP
- Supports custom API proxy endpoints (`OPENAI_BASE_URL` / `GEMINI_BASE_URL`)
- Automatically loads `.env`, and also supports CLI arguments for MCP clients that cannot pass `env`
- Providers without valid API keys are disabled automatically without affecting the other provider
## Quick Start
```bash
npm install
npm run build
cp .env.example .env
```
## Environment Variables
| Variable | Required | Default | Description |
|------|------|--------|------|
| `GEMINI_API_KEY` | One provider required | - | Google Gemini API key |
| `OPENAI_API_KEY` | One provider required | - | OpenAI-compatible API key |
| `DEFAULT_MODEL` | No | `gemini-2.5-flash-image` | Default model when the tool call does not provide `model` |
| `GEMINI_BASE_URL` | No | - | Gemini API proxy endpoint |
| `OPENAI_BASE_URL` | No | - | OpenAI-compatible API proxy endpoint |
| `OPENAI_IMAGE_MODEL` | No | `gpt-image-2` | OpenAI-compatible image model used in integration tests |
| `MCP_TRANSPORT` | No | `stdio` | Transport mode: `stdio` / `sse` / `http` |
| `MCP_STDIO_LOGS` | No | `false` | Enable startup/runtime logs in stdio mode (set `true` to re-enable for debugging) |
| `MCP_HOST` | No | `localhost` | Host for SSE/HTTP mode |
| `MCP_PORT` | No | `3000` | Port for SSE/HTTP mode |
> Configure at least one of `GEMINI_API_KEY` or `OPENAI_API_KEY`.
>
> Placeholder values in `.env` such as `your-gemini-api-key` are ignored automatically.
>
> Configuration precedence is: **CLI arguments > process environment variables > `.env` in the current working directory > built-in defaults**.
## Tool: `generate_image`
| Parameter | Required | Default | Description |
|------|------|--------|------|
| `prompt` | Yes | - | Detailed image description |
| `model` | No | `DEFAULT_MODEL` | Model name — see supported models below |
| `size` | No | `auto` / `1024x1024` | Image dimensions for OpenAI-compatible models |
| `quality` | No | `standard` | `high`/`medium`/`low`/`standard` (gpt-image-*) or `hd`/`standard` (dall-e-3) |
| `n` | No | `1` | Number of images (gpt-image-*: 1–10; dall-e-3: 1; Gemini: 1) |
| `aspect_ratio` | No | `1:1` | Gemini-only: `1:1` `3:4` `4:3` `9:16` `16:9` |
| `response_format` | No | `auto` | `url` / `base64` / `auto` — see [Response Format](#response-format) below |
| `timeout` | No | `120` | Max wait time in **seconds**. Increase for slow proxies or high-quality models |
Supported model families:
- **OpenAI / OpenAI-compatible**: `gpt-image-2`, `gpt-image-1`, `dall-e-3`, `dall-e-2`, `doubao-*`, `volcengine/doubao-*`
- **Gemini**: `gemini-2.5-flash-image`, `gemini-2.0-flash-exp`, `imagen-3.0-generate-001`
### Response Format
The `response_format` parameter controls how image URLs and file paths are returned alongside the base64 `ImageContent` blocks:
| Value | Behavior |
|-------|----------|
| `auto` (default) | **Always returns a local file path** in the response text (`Saved to: /tmp/abc123.png`) alongside the base64 `ImageContent` block. Images are saved to `os.tmpdir()` with a random filename. This mode is the most compatible and ensures the image is always accessible regardless of the provider's default response format. |
| `base64` | Returns `ImageContent` blocks only (forces `b64_json` for OpenAI). |
| `url` | **Always returns a file path or URL** in the response text: <br>• If the API returns a `url` → `Image URL: https://...` <br>• If the API returns only base64 → the image is saved to `os.tmpdir()` with a random filename → `Saved to: /tmp/abc123.png` |
> **Security**: files saved to the temp directory use `crypto.randomBytes(16)` for filenames with `wx` (exclusive-create) and `0o600` (owner-only) flags — no path-traversal risk, no file-overwrite collisions.
### Usage
### Claude Desktop / Kiro (stdio mode)
```json
{
"mcpServers": {
"assets-gen": {
"command": "node",
"args": ["/path/to/assets-gen-mcp/dist/index.js"],
"env": {
"GEMINI_API_KEY": "your-key",
"GEMINI_BASE_URL": "https://your-proxy.com"
}
}
}
}
```
If your MCP client cannot pass `env`, use CLI arguments instead:
```json
{
"mcpServers": {
"assets-gen": {
"command": "npx",
"args": [
"-y",
"@ayaka209/assets-gen-mcp",
"--openai-api-key",
"your-key",
"--openai-base-url",
"https://your-proxy.com/gptapi",
"--default-model",
"gpt-image-2"
]
}
}
}
```
macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`
### MCP SDK Client
```typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["dist/index.js"],
env: { GEMINI_API_KEY: "your-key" },
});
const client = new Client({ name: "my-app", version: "1.0.0" }, { capabilities: {} });
await client.connect(transport);
const result = await client.callTool({
name: "generate_image",
arguments: { prompt: "A cat in space" },
});
// result.content -> [{ type: "image", data: "<base64>", mimeType: "image/png" }]
```
### Provider Selection Rules
1. If `model` is provided, the provider is chosen from the model prefix
2. If `model` is omitted, `DEFAULT_MODEL` is used
3. `gpt-image-*` / `dall-e-*` / `doubao-*` / `volcengine/doubao-*` go to the OpenAI-compatible path
4. `gemini-*` / `imagen-*` go to the Gemini path
### SSE / HTTP Mode
```bash
MCP_TRANSPORT=sse MCP_PORT=3000 node dist/index.js
```
Endpoints:
- `GET /sse` - SSE connection
- `POST /message?sessionId=xxx` - Send messages
- `GET /` - Health check
### OpenAI-Compatible Proxy Testing
Run the full integration tests:
```bash
OPENAI_API_KEY=your-key
OPENAI_BASE_URL=https://your-openai-compatible-endpoint
OPENAI_IMAGE_MODEL=gpt-image-2
npm run test:integration
```
For a single end-to-end smoke test:
```bash
OPENAI_API_KEY=your-key
OPENAI_BASE_URL=https://your-openai-compatible-endpoint
OPENAI_IMAGE_MODEL=gpt-image-2
npm run test:openai-proxy
```
This script verifies:
1. A direct `images.generate` call against `OPENAI_BASE_URL`
2. A stdio MCP round-trip through this repository's `generate_image` tool
To inspect which OpenAI-compatible models your endpoint exposes:
```bash
OPENAI_API_KEY=your-key
OPENAI_BASE_URL=https://your-openai-compatible-endpoint
npm run models:openai
```
If your MCP client cannot pass `env`, you can launch it directly with CLI arguments:
```bash
npx -y @ayaka209/assets-gen-mcp --openai-api-key sk-... --openai-base-url https://your-openai-compatible-endpoint --default-model gpt-image-2
```
Show all supported CLI options:
```bash
npx -y @ayaka209/assets-gen-mcp --help
```
## Development
```bash
npm run build # Build
npm run watch # Watch TypeScript
npm test # Unit tests
npm run test:integration # Integration tests (requires API keys)
npm run test:openai-proxy # One-command OpenAI-compatible smoke test
npm run models:openai # List OpenAI-compatible models visible to the endpoint
npm run models # List available Gemini models
```
## Tech Stack
- [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk) - MCP server/client SDK
- [`@google/genai`](https://www.npmjs.com/package/@google/genai) - Google Gemini SDK
- [`openai`](https://www.npmjs.com/package/openai) - OpenAI SDK
## License
MIT
Maintenance
ActivityInactive
ResponsivenessNo issues