Skip to main content
Glama
README.md
# dsh-media-mcp

[中文](README.zh.md) | English

Turn a sentence into an image or a short video on your machine.

`dsh-media-mcp` is a small [MCP](https://modelcontextprotocol.io) server. Your AI client calls it,
it calls your media provider, and the result lands on disk as a normal file you can open. It also
exposes its own settings as tools, so changing the model, the size or the output folder is something
you say in chat — no settings screen to hunt for.

Works with any MCP client. Built for and tested with
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`).

## What you can do

Just ask:

- **"Draw a corgi surfing at sunset."** — you get back an absolute path to a saved JPEG.
- **"Make a five second clip of a paper boat drifting down a rainy street."** — a video, in a few
  minutes (three to four, measured).
- **"What models are you using?"** — `get_config` answers.
- **"Switch to Seedream 4.0, 2048x2048."** — `set_config` saves it; the next image uses it.

| Tool | What it does |
| --- | --- |
| `generate_image` | Generate from a prompt, save it, return the paths. Optional per-call `model`, `size`, `outputDir`. |
| `generate_video` | Generate a short clip. Text to video only. Optional per-call `model`, `ratio`, `duration`, `outputDir`. Slow and paid — ask for it explicitly. |
| `get_config` | Show the settings in effect, whether a key is configured, and which models are known. |
| `set_config` | Change a setting and save it. Applies to the next call. |

It knows a size each image model will refuse, and says so **before** spending your money:

```
model doubao-seedream-4-5-251128 requires at least 3686400 pixels, but 1024x1024 is only
1048576. Retry with a larger size such as 1920x1920.
```

## Requirements

Node.js `^22.19.0 || >=24.0.0`. No dependencies, no build step: clone it and run it.

## Setup

### 1. Get an API key

The built-in preset is Volcengine Ark (Doubao / Seedream / Seedance). The server speaks the OpenAI
images API for pictures and Ark's task API for video, so a different vendor works too — it needs one
entry in `src/providers.mjs`.

### 2. Write the config file

This is where your key lives. `~/.config/media-gen/config.yml` on macOS and Linux, or
`%APPDATA%\media-gen\config.yml` on Windows:

```yaml
provider: ark
apiKey: your-api-key
imageModel: doubao-seedream-5-0-flash-260915
imageSize: 1024x1024
videoModel: doubao-seedance-2-5-260628
# outputDir: "~/Pictures/generated"
```

Then `chmod 600` it. [`config.example.yml`](config.example.yml) is the same thing to copy from, and
`MEDIA_GEN_CONFIG` points somewhere else if you prefer.

Quote a value that starts with `~`, otherwise YAML reads it as nothing.

### 3. Point your client at the server

Any MCP client:

```jsonc
{
  "mcpServers": {
    "media": {
      "command": "node",
      "args": ["/absolute/path/to/dsh-media-mcp/src/server.mjs"],
      "cwd": "/absolute/path/to/your/workspace"
    }
  }
}
```

DeepSeek Harness — append this row to `$DSH_HOME/profiles/<profile>/cordis.patch.yml`.
`@deepseek-ai/dsh-mcp-client` already ships with `dsh`, so nothing needs installing:

```yaml
- insert:
    - id: mcp-media
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        transport: stdio
        serverName: media
        command: node
        args:
          - /absolute/path/to/dsh-media-mcp/src/server.mjs
        cwd: /absolute/path/to/your/workspace
        toolCallTimeoutMs: 900000   # a video waits minutes; the 60s default is far too short
        failOnStartupError: true
```

The tools appear as `mcp__media__generate_image`, `mcp__media__generate_video`,
`mcp__media__get_config` and `mcp__media__set_config`.

**No app restart needed:** a patched profile picks the row up in the same session, and generated
files follow `cwd`, so point `cwd` at the workspace you want them in.

## Where your files go

| What | Default | Override |
| --- | --- | --- |
| Images | `<workspace>/image_output/` — one `img-YYYYMMDD-HHMMSS.jpg` per result | `outputDir` (config or per call) |
| Videos | `<workspace>/video_output/` — one `vid-YYYYMMDD-HHMMSS.mp4` per result | `outputDir` (config or per call) |

`<workspace>` is the `cwd` you gave the server. Setting `outputDir` sends **both** kinds to that one
folder; leave it unset to keep them apart. `media-gen outdir` prints both directories, and `~` is
expanded in a configured path.

Tools return **paths**, not file data, so a large image or clip never floods the conversation. Open
the file with any viewer; in DeepSeek Harness you can also hand an image path to the `read_image`
tool.

## Settings

Change any of these with `set_config`, or by editing the file — it is read on every call, so nothing
needs restarting either way.

| Setting | In the config file | Environment variable | Notes |
| --- | --- | --- | --- |
| Provider | `provider:` | `MEDIA_GEN_PROVIDER` | Which preset to use. Default `ark`. |
| API key | `apiKey:` | `MEDIA_GEN_API_KEY` | Required. Never printed by any tool. |
| API URL | `baseUrl:` | `MEDIA_GEN_BASE_URL` | Overrides the preset's URL. |
| Image model | `imageModel:` | `MEDIA_GEN_IMAGE_MODEL` | Default model for pictures. |
| Image size | `imageSize:` | `MEDIA_GEN_IMAGE_SIZE` | Default size, `WxH`. |
| Video model | `videoModel:` | `MEDIA_GEN_VIDEO_MODEL` | Default model for clips. |
| Image timeout | `imageTimeoutMs:` | `MEDIA_GEN_IMAGE_TIMEOUT_MS` | Milliseconds one image call may take. Default `180000`. |
| Video timeout | `videoTimeoutMs:` | `MEDIA_GEN_VIDEO_TIMEOUT_MS` | Milliseconds the whole video flow may take. Default `720000`. |
| Output folder | `outputDir:` | `MEDIA_GEN_OUTPUT_DIR` | Overrides both defaults above. `~` is expanded. |

An environment variable beats the config file. For the model lists, their size floors and their
notes, run `media-gen models` or ask `get_config`.

Aspect ratio and clip length are per call (`ratio`, `duration`; default `16:9` and `5` seconds),
because they change what a clip costs.

### Timeouts

A five second clip measured about four minutes, so both waits are yours to set rather than constants
baked into the code:

```yaml
imageTimeoutMs: 300000     # a slow image provider
videoTimeoutMs: 1200000    # longer clips
```

`set_config` writes the same two keys, `media-gen config set videoTimeoutMs=1200000` does it from a
shell, and either can be overridden for one call: `generate_image` and `generate_video` take
`timeoutMs`, the CLI takes `--timeout-ms 1200000`. `get_config` reports what is in effect.

The ceiling is `3600000` (one hour). A larger value is refused rather than shortened, because a call
that hangs for an hour is a mistake worth seeing.

**Keep your client's tool timeout above ours.** The server can only stop its own wait; if your MCP
client gives up first you get its error instead of a message naming the video task id. The DSH row
above uses `toolCallTimeoutMs: 900000` for that reason.

A config file from an older release, written as `MEDIA_GEN_API_KEY=...` lines, still works. Renaming
it to `config.yml` is all it takes to move on.

## Command line

The same server comes with a small CLI. Symlink `bin/media-gen.mjs` onto your `PATH`:

```bash
media-gen env                            # what is in effect right now
media-gen models                         # image models and their size floors
media-gen outdir                         # where images and videos will be written
media-gen img "a corgi surfing at sunset"
media-gen img "cyberpunk alley" -s 2048x2048 -o alley.jpg
media-gen video "a paper boat drifting down a rainy street" --duration 5
media-gen config set imageModel=doubao-seedream-4-0-250828
media-gen config set videoModel=doubao-seedance-1-0-pro-250528
media-gen key                            # redacted; the raw key is never printed
media-gen serve                          # run the MCP server on stdio
```

## Troubleshooting

| What you see | What it means |
| --- | --- |
| `no API key configured` | `apiKey:` is missing or empty in the config file. |
| `model … requires at least N pixels` | That image model has a size floor — use the size the message suggests. |
| `media API error [InvalidEndpointOrModel.NotFound]` | The model is not available to your account or region. `get_config` lists the models this version knows. |
| `media API error [SetLimitExceeded]` | Ark's "Safe Experience Mode" usage cap for that model. Raise or close it on the Ark console's model-activation page. |
| `video task … did not finish within Ns` | The clip was still rendering. The message carries the task id, so you can query it instead of paying for a second render — or raise `videoTimeoutMs`. |
| `video task … reported a status this version does not know` | A state the server will not guess at. Update the server rather than retrying blindly. |
| A video tool call is cut off by your client | Raise its tool timeout. The clip legitimately takes minutes; the row above uses `900000`. |
| `ignored: <key>` in `get_config` | A config key that is not a setting — usually a typo, so it changed nothing. |
| `format: env assignments — legacy` | The file is in the old `KEY=value` style. It still works; rename it to `config.yml` whenever you like. |

## What video can and cannot do

Text to video, one clip per call. Reference images and videos (image-to-video, extend, edit) are not
supported: each of those changes the request contract, and what is not implemented should not look
like it is.

## Development

Tests are offline — no network, no API key, no spend:

```bash
node --test tests/*.test.mjs
```

## License

MIT — see [LICENSE](LICENSE).

TDQS

A4.4/5.0

Scored across 4 tools

Disambiguation5/5

Each tool targets a distinct operation: image generation, video generation, config retrieval, and config modification. No overlap exists, making selection unambiguous.

Naming Consistency5/5

All tools follow the verb_noun pattern: generate_image, generate_video, get_config, set_config. The naming is consistent and predictable.

Tool Count5/5

With 4 tools, the server is well-scoped for its purpose—media generation plus configuration management. Each tool is essential and there are no redundant or missing core operations.

Completeness4/5

The core generation and configuration workflows are covered, but the reference to a read_image tool in generate_image's description suggests a potential gap for viewing outputs. No explicit file listing or deletion tools are present, though these may be outside the server's intended scope.

Maintenance

ActivityMaintained
ResponsivenessNo issues