@stabgan/openrouter-mcp-multimodal 4.5.0 → 4.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +380 -242
- package/dist/index.js +22 -6
- package/dist/model-cache.d.ts +35 -12
- package/dist/model-cache.js +79 -22
- package/dist/tool-descriptions.d.ts +19 -0
- package/dist/tool-descriptions.js +423 -0
- package/dist/tool-handlers/analyze-audio.js +5 -1
- package/dist/tool-handlers/analyze-image.js +6 -5
- package/dist/tool-handlers/analyze-video.js +6 -5
- package/dist/tool-handlers/audio-utils.js +4 -2
- package/dist/tool-handlers/chat-completion.js +1 -1
- package/dist/tool-handlers/fetch-utils.js +16 -2
- package/dist/tool-handlers/generate-audio.js +2 -4
- package/dist/tool-handlers/generate-image-input.d.ts +3 -0
- package/dist/tool-handlers/generate-image-input.js +38 -0
- package/dist/tool-handlers/generate-image.d.ts +13 -51
- package/dist/tool-handlers/generate-image.js +32 -119
- package/dist/tool-handlers/generate-video.js +28 -24
- package/dist/tool-handlers/health-check.js +4 -1
- package/dist/tool-handlers/image-utils.d.ts +1 -0
- package/dist/tool-handlers/image-utils.js +26 -16
- package/dist/tool-handlers/openrouter-errors.d.ts +5 -1
- package/dist/tool-handlers/openrouter-errors.js +78 -13
- package/dist/tool-handlers/provider-routing.js +7 -2
- package/dist/tool-handlers/rerank.js +2 -5
- package/dist/tool-handlers/search-models.d.ts +2 -2
- package/dist/tool-handlers/search-models.js +2 -6
- package/dist/tool-handlers/structured-output.d.ts +8 -0
- package/dist/tool-handlers/structured-output.js +11 -0
- package/dist/tool-handlers/video-utils.js +6 -9
- package/dist/tool-handlers.js +43 -123
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +26 -14
package/README.md
CHANGED
|
@@ -1,148 +1,155 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="assets/logo.
|
|
2
|
+
<img src="assets/logo.svg" alt="OpenRouter MCP Multimodal — MCP server for chat, vision, audio, and video AI tools" width="128" height="128" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h1 align="center">OpenRouter MCP Multimodal
|
|
5
|
+
<h1 align="center">OpenRouter MCP Multimodal</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong>The
|
|
8
|
+
<strong>The MCP server for multimodal AI agents.</strong><br/>
|
|
9
|
+
One install · 14 tools · 300+ OpenRouter models · text, vision, audio & video — analysis and generation.
|
|
9
10
|
</p>
|
|
10
11
|
|
|
11
12
|
<p align="center">
|
|
12
13
|
<a href="https://www.npmjs.com/package/@stabgan/openrouter-mcp-multimodal"><img src="https://img.shields.io/npm/v/@stabgan/openrouter-mcp-multimodal.svg?label=npm&color=cb3837&logo=npm" alt="npm version" /></a>
|
|
14
|
+
<a href="https://pypi.org/project/mcp-server-openrouter-multimodal/"><img src="https://img.shields.io/pypi/v/mcp-server-openrouter-multimodal.svg?label=pypi&color=3775A9&logo=pypi&logoColor=white" alt="PyPI version" /></a>
|
|
15
|
+
<a href="https://github.com/stabgan/openrouter-mcp-multimodal/releases"><img src="https://img.shields.io/github/v/release/stabgan/openrouter-mcp-multimodal?label=release&color=6366f1" alt="GitHub release" /></a>
|
|
13
16
|
<a href="https://hub.docker.com/r/stabgan/openrouter-mcp-multimodal"><img src="https://img.shields.io/docker/v/stabgan/openrouter-mcp-multimodal/latest?label=docker&color=2496ed&logo=docker&logoColor=white" alt="Docker version" /></a>
|
|
14
|
-
<a href="https://github.com/stabgan/openrouter-mcp-multimodal/actions/workflows/
|
|
15
|
-
<a href="https://www.apache.org/licenses/LICENSE-2.0"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="Apache 2.0" /></a>
|
|
16
|
-
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%E2%89%
|
|
17
|
+
<a href="https://github.com/stabgan/openrouter-mcp-multimodal/actions/workflows/ci.yml"><img src="https://github.com/stabgan/openrouter-mcp-multimodal/actions/workflows/ci.yml/badge.svg" alt="CI status" /></a>
|
|
18
|
+
<a href="https://www.apache.org/licenses/LICENSE-2.0"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="Apache 2.0 license" /></a>
|
|
19
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%E2%89%A520-43853d?logo=node.js&logoColor=white" alt="Node.js 20+" /></a>
|
|
17
20
|
</p>
|
|
18
21
|
|
|
19
22
|
<p align="center">
|
|
20
23
|
<a href="https://www.npmjs.com/package/@stabgan/openrouter-mcp-multimodal"><img src="https://img.shields.io/npm/dt/@stabgan/openrouter-mcp-multimodal.svg?label=npm%20downloads&color=cb3837&logo=npm" alt="npm downloads" /></a>
|
|
21
|
-
<a href="https://www.npmjs.com/package/@stabgan/openrouter-mcp-multimodal"><img src="https://img.shields.io/npm/dm/@stabgan/openrouter-mcp-multimodal.svg?label=monthly&color=cb3837&logo=npm" alt="npm monthly" /></a>
|
|
22
24
|
<a href="https://hub.docker.com/r/stabgan/openrouter-mcp-multimodal"><img src="https://img.shields.io/docker/pulls/stabgan/openrouter-mcp-multimodal.svg?label=docker%20pulls&color=2496ed&logo=docker&logoColor=white" alt="Docker pulls" /></a>
|
|
23
|
-
<a href="https://
|
|
24
|
-
<a href="https://
|
|
25
|
-
<a href="https://github.com/stabgan/openrouter-mcp-multimodal/network/members"><img src="https://img.shields.io/github/forks/stabgan/openrouter-mcp-multimodal.svg?style=social" alt="GitHub forks" /></a>
|
|
26
|
-
</p>
|
|
27
|
-
<p align="center">
|
|
28
|
-
<sub>4,700+ installs across npm + Docker Hub · ~950 npm installs/month and accelerating</sub>
|
|
25
|
+
<a href="https://registry.modelcontextprotocol.io/servers/io.github.stabgan/openrouter-multimodal"><img src="https://img.shields.io/badge/MCP_Registry-listed-6366f1" alt="MCP Registry" /></a>
|
|
26
|
+
<a href="https://smithery.ai/server/@stabgan/openrouter-mcp-multimodal"><img src="https://img.shields.io/badge/Smithery-Install-6366f1" alt="Smithery MCP registry" /></a>
|
|
29
27
|
</p>
|
|
30
28
|
|
|
31
29
|
<p align="center">
|
|
32
|
-
<a href="#
|
|
33
|
-
<a href="#tools">Tools</a>
|
|
34
|
-
<a href="#
|
|
35
|
-
<a href="#
|
|
36
|
-
<a href="#
|
|
37
|
-
<a href="#
|
|
38
|
-
<a href="./CHANGELOG.md">Changelog</a>
|
|
30
|
+
<a href="#quick-start">Quick start</a> ·
|
|
31
|
+
<a href="#tools">Tools</a> ·
|
|
32
|
+
<a href="#examples">Examples</a> ·
|
|
33
|
+
<a href="#security">Security</a> ·
|
|
34
|
+
<a href="#development">Development</a> ·
|
|
35
|
+
<a href="#faq">FAQ</a>
|
|
39
36
|
</p>
|
|
40
37
|
|
|
41
38
|
---
|
|
42
|
-
[](https://mseep.ai/app/8f27d6d4-0877-4b86-b377-8a33f451e755)
|
|
43
39
|
|
|
44
|
-
|
|
40
|
+
## What is this?
|
|
41
|
+
|
|
42
|
+
**OpenRouter MCP Multimodal** is a production-grade [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server — listed on the [official MCP Registry](https://registry.modelcontextprotocol.io/servers/io.github.stabgan/openrouter-multimodal) as `io.github.stabgan/openrouter-multimodal`. It connects AI coding agents ([Cursor](https://cursor.com), [Claude Desktop](https://claude.ai/download), [VS Code](https://code.visualstudio.com), [Windsurf](https://codeium.com/windsurf), [Cline](https://github.com/cline/cline), and others) to [OpenRouter](https://openrouter.ai)'s unified LLM API over stdio.
|
|
43
|
+
|
|
44
|
+
Unlike text-only MCP servers, one install covers the **full multimodal surface**:
|
|
45
|
+
|
|
46
|
+
| Capability | Tools | Highlights |
|
|
47
|
+
| :---------- | :-------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| **Chat** | `chat_completion` | 300+ models, `:nitro` / `:exacto` suffixes, provider routing, web search, response caching, reasoning tokens |
|
|
49
|
+
| **Vision** | `analyze_image`, `generate_image` | OCR, captioning, VQA, image generation with reference inputs |
|
|
50
|
+
| **Audio** | `analyze_audio`, `generate_audio` | Transcription, speech/music generation |
|
|
51
|
+
| **Video** | `analyze_video`, `generate_video`, `generate_video_from_image`, `get_video_status` | Clip understanding, Veo / Sora / Seedance / Wan generation with progress notifications |
|
|
52
|
+
| **Catalog** | `search_models`, `get_model_info`, `validate_model`, `rerank_documents`, `health_check` | Model discovery, validation, reranking, ops health |
|
|
53
|
+
|
|
54
|
+
**Production hardening:** input/output path sandboxes (including analyze\_\* local files as of v4.5.2), SSRF guards, structured errors with `_meta.code`, MCP 2025-06-18 structured outputs, async video progress notifications, and **650+** automated tests (unit, mock, regression, and live integration).
|
|
55
|
+
|
|
56
|
+
## Quick start
|
|
57
|
+
|
|
58
|
+
**1. Get an API key** (free tier works) → [openrouter.ai/keys](https://openrouter.ai/keys)
|
|
59
|
+
|
|
60
|
+
**2. Run the server**
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export OPENROUTER_API_KEY=sk-or-v1-...
|
|
64
|
+
npx -y @stabgan/openrouter-mcp-multimodal
|
|
65
|
+
```
|
|
45
66
|
|
|
67
|
+
**3. Add to your MCP client** (Cursor, Claude Desktop, VS Code, etc.) — see [Install](#install) below.
|
|
46
68
|
|
|
69
|
+
> **No credits required to start.** Free models such as `google/gemma-4-26b-a4b-it:free` work for chat and vision. Video/audio generation typically needs credits.
|
|
47
70
|
|
|
48
|
-
##
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
MCP servers are distributed through several packaging models. **This server is implemented in Node.js/TypeScript**; the table below maps each ecosystem method to how you run it here.
|
|
74
|
+
|
|
75
|
+
| Method | Runtime | Best for | This server |
|
|
76
|
+
| :--- | :--- | :--- | :--- |
|
|
77
|
+
| **[npx](#npx-recommended)** | Node.js 20+ | Most MCP clients (default) | ✅ `@stabgan/openrouter-mcp-multimodal` |
|
|
78
|
+
| **[uvx / pipx](#uvx--pipx-python-launcher)** | Python 3.10+ **and** Node.js 20+ | Python-first workflows, same pattern as PyPI MCP servers | ✅ [`mcp-server-openrouter-multimodal`](https://pypi.org/project/mcp-server-openrouter-multimodal/) |
|
|
79
|
+
| **[npm global](#npm-global)** | Node.js 20+ | Pin a version without re-downloading | ✅ |
|
|
80
|
+
| **[node (local)](#node-local-clone)** | Node.js 20+ | Contributors / air-gapped builds | ✅ |
|
|
81
|
+
| **[Docker Hub](#docker)** | Docker | Isolation, no Node on host | ✅ `stabgan/openrouter-mcp-multimodal` |
|
|
82
|
+
| **[GHCR](#ghcr-github-container-registry)** | Docker | GitHub-native OCI pulls | ✅ `ghcr.io/stabgan/openrouter-mcp-multimodal` |
|
|
83
|
+
| **[Smithery CLI](#smithery)** | Node.js (via installer) | Interactive install into Claude/Cursor/etc. | ✅ |
|
|
84
|
+
| **[MCP Registry](#mcp-registry)** | npm or OCI | Official discovery (`io.github.stabgan/openrouter-multimodal`) | ✅ [listing](https://registry.modelcontextprotocol.io/servers/io.github.stabgan/openrouter-multimodal) |
|
|
85
|
+
| **[One-click deeplinks](#one-click)** | Node.js | Cursor, VS Code, Kiro | ✅ |
|
|
86
|
+
| **[Claude Code CLI](#claude-code-cli)** | Node.js | Terminal-first Claude Code users | ✅ |
|
|
87
|
+
| **[MCP Inspector](#mcp-inspector)** | Node.js | Debug / list tools locally | ✅ |
|
|
88
|
+
| **Windows `cmd /c npx`** | Node.js | Claude Desktop / Cursor when `npx` not on GUI PATH | ✅ [see below](#windows-npx) |
|
|
89
|
+
| pip / uv (direct) | — | Native Python MCP servers only | — use **uvx** row above |
|
|
90
|
+
| DXT desktop extensions | — | Bundled Claude Desktop `.dxt` | not yet |
|
|
91
|
+
| Remote HTTP / SSE | — | Hosted Smithery / Cloudflare endpoints | via [Smithery](https://smithery.ai/server/@stabgan/openrouter-mcp-multimodal) |
|
|
92
|
+
|
|
93
|
+
> **uvx vs npx:** In the MCP ecosystem, **`npx` runs npm (Node) packages** and **`uvx` runs PyPI (Python) packages**. Because this server is Node-based, `uvx` uses a thin [Python launcher](./python/) that execs `npx -y @stabgan/openrouter-mcp-multimodal` — you still need Node installed.
|
|
94
|
+
|
|
95
|
+
### One-click
|
|
49
96
|
|
|
50
97
|
<table>
|
|
51
|
-
<tr><td><strong>
|
|
52
|
-
<tr><td><strong>Cursor</strong></td><td><a href="https://cursor.com/en/install-mcp?name=openrouter&config=eyJ0eXBlIjoic3RkaW8iLCJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzdGFiZ2FuL29wZW5yb3V0ZXItbWNwLW11bHRpbW9kYWwiXSwiZW52Ijp7Ik9QRU5ST1VURVJfQVBJX0tFWSI6InNrLW9yLXYxLS4uLiJ9fQ%3D%3D"><img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Add to Cursor" /></a></td></tr>
|
|
98
|
+
<tr><td><strong>Cursor</strong></td><td><a href="https://cursor.com/en/install-mcp?name=openrouter&config=eyJ0eXBlIjoic3RkaW8iLCJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzdGFiZ2FuL29wZW5yb3V0ZXItbWNwLW11bHRpbW9kYWwiXSwiZW52Ijp7Ik9QRU5ST1VURVJfQVBJX0tFWSI6InNrLW9yLXYxLS4uLiJ9fQ%3D%3D"><img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Add OpenRouter MCP to Cursor" /></a></td></tr>
|
|
53
99
|
<tr><td><strong>VS Code</strong></td><td><a href="https://insiders.vscode.dev/redirect/mcp/install?name=openrouter&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40stabgan%2Fopenrouter-mcp-multimodal%22%5D%2C%22env%22%3A%7B%22OPENROUTER_API_KEY%22%3A%22sk-or-v1-...%22%7D%7D"><img src="https://img.shields.io/badge/Add_to-VS_Code-007ACC?style=for-the-badge&logo=visualstudiocode&logoColor=white" alt="Add to VS Code" /></a></td></tr>
|
|
54
|
-
<tr><td><strong>
|
|
55
|
-
<tr><td><strong>Claude Desktop</strong></td><td><a href="#
|
|
56
|
-
<tr><td><strong>
|
|
57
|
-
<tr><td><strong>
|
|
58
|
-
<tr><td><strong>Smithery</strong></td><td><code>npx -y @smithery/cli install @stabgan/openrouter-mcp-multimodal --client claude</code></td></tr>
|
|
100
|
+
<tr><td><strong>Kiro</strong></td><td><a href="https://kiro.dev/launch/mcp/add?name=openrouter&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40stabgan%2Fopenrouter-mcp-multimodal%22%5D%2C%22env%22%3A%7B%22OPENROUTER_API_KEY%22%3A%22sk-or-v1-...%22%7D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D"><img src="https://img.shields.io/badge/Add_to-Kiro-232F3E?style=for-the-badge&logo=amazonaws&logoColor=white" alt="Add to Kiro" /></a></td></tr>
|
|
101
|
+
<tr><td><strong>Claude Desktop / Windsurf / Cline</strong></td><td><a href="#manual-config">Manual JSON config</a> (pick any method below)</td></tr>
|
|
102
|
+
<tr><td><strong>Smithery</strong></td><td><a href="https://smithery.ai/server/@stabgan/openrouter-mcp-multimodal"><code>npx -y @smithery/cli install @stabgan/openrouter-mcp-multimodal --client claude</code></a></td></tr>
|
|
103
|
+
<tr><td><strong>MCP Registry</strong></td><td><a href="https://registry.modelcontextprotocol.io/servers/io.github.stabgan/openrouter-multimodal">Official registry page</a> — npm + OCI packages</td></tr>
|
|
59
104
|
</table>
|
|
60
105
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<!--
|
|
64
|
-
Install-link audit (2026-04-20, round 4 — HTTPS redirectors only):
|
|
65
|
-
GitHub Markdown strips non-HTTPS schemes (cursor://, vscode:) from href attributes
|
|
66
|
-
and rewrites them to cursor.sh / nothing. The only deeplinks that survive GitHub's
|
|
67
|
-
sanitizer are HTTPS URLs. Fortunately both Cursor and VS Code publish official
|
|
68
|
-
HTTPS redirector endpoints that hand off to the IDE's native protocol handler:
|
|
69
|
-
|
|
70
|
-
- Kiro: https://kiro.dev/launch/mcp/add?name=<name>&config=<url-encoded JSON>
|
|
71
|
-
- Cursor: https://cursor.com/en/install-mcp?name=<name>&config=<base64>
|
|
72
|
-
(config excludes `name`; badge is cursor.com/deeplink/mcp-install-dark.svg)
|
|
73
|
-
- VS Code: https://insiders.vscode.dev/redirect/mcp/install?name=<name>&config=<urlenc JSON>
|
|
74
|
-
(config excludes `name`; append &quality=insiders for Insiders)
|
|
75
|
-
|
|
76
|
-
Earlier rounds used cursor://anysphere.cursor-deeplink/... and vscode:mcp/install?...
|
|
77
|
-
— both got stripped by GitHub and rendered as broken redirects to cursor.sh or a
|
|
78
|
-
camo.githubusercontent.com image-proxy URL. Pattern confirmed against
|
|
79
|
-
github/github-mcp-server and modelcontextprotocol/servers READMEs.
|
|
80
|
-
|
|
81
|
-
Regenerate with: node scripts/make-install-links.mjs
|
|
82
|
-
-->
|
|
83
|
-
|
|
84
|
-
## Why This One?
|
|
85
|
-
|
|
86
|
-
| Feature | Status |
|
|
87
|
-
| :--- | :--- |
|
|
88
|
-
| Text chat with 300+ models | ✅ |
|
|
89
|
-
| Image analysis (vision) | ✅ Native with sharp optimization |
|
|
90
|
-
| Audio analysis | ✅ Transcription + analysis, base64 auto-encoded |
|
|
91
|
-
| Audio generation | ✅ Conversational, speech, and music with format auto-detection |
|
|
92
|
-
| Image generation | ✅ Path-sandboxed disk output |
|
|
93
|
-
| **Video understanding** | ✅ **v3** — mp4, mpeg, mov, webm from files, URLs, or data URLs |
|
|
94
|
-
| **Video generation** | ✅ **v3** — Veo 3.1 / Sora 2 Pro / Seedance / Wan via async API with progress notifications |
|
|
95
|
-
| **Response caching** | ✅ **v4.5** — `X-OpenRouter-Cache` passthrough, zero tokens billed on hit, 80–300ms latency |
|
|
96
|
-
| **Web search plugin** | ✅ **v4.5** — `online: true` on `chat_completion` injects OpenRouter's Exa-backed plugin |
|
|
97
|
-
| **Rerank** | ✅ **v4.5** — `rerank_documents` tool against `/rerank` (Cohere, Fireworks) |
|
|
98
|
-
| **Health check** | ✅ **v4.5** — `health_check` verifies API key + OpenRouter reachability |
|
|
99
|
-
| **Reasoning tokens** | ✅ **v4.5** — passthrough of DeepSeek R1 / Gemini Thinking / Opus 4.7 traces on `_meta.reasoning` |
|
|
100
|
-
| **MCP 2025-06-18 spec** | ✅ **v4.5** — structured outputs (`outputSchema`), progress notifications, `title` + `openWorldHint` |
|
|
101
|
-
| Auto image resize + compress | ✅ Configurable (defaults 800px max, JPEG 80%) |
|
|
102
|
-
| Model search + validation | ✅ Filter by vision / audio / video modality |
|
|
103
|
-
| Free model support | ✅ Default: free Nemotron VL |
|
|
104
|
-
| Docker support | ✅ Multi-arch (amd64 + arm64), ~345 MB Alpine |
|
|
105
|
-
| Retry-After + jitter | ✅ Honors `Retry-After` header, avoids thundering herd |
|
|
106
|
-
| IPv4 + IPv6 SSRF blocklist | ✅ Covers mapped, compat, multicast, 6to4, Teredo, ORCHID |
|
|
107
|
-
| Structured error taxonomy | ✅ Closed `_meta.code` so clients can switch on failure modes |
|
|
108
|
-
| Reasoning-model awareness | ✅ Detects `max_tokens` cutoff during CoT, guides the caller |
|
|
109
|
-
| MCP 2025 tool annotations | ✅ `readOnlyHint` / `destructiveHint` / `idempotentHint` on every tool |
|
|
106
|
+
Paste your `OPENROUTER_API_KEY` when prompted — deeplinks use placeholders so secrets never appear in URLs.
|
|
110
107
|
|
|
111
|
-
|
|
108
|
+
### Manual config
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
| `chat_completion` | Send messages to any OpenRouter model. Detects reasoning-model cutoffs. Supports **provider routing** (`quantizations`, `ignore`, `sort`, `order`, `require_parameters`, `data_collection`, `allow_fallbacks`), **model suffixes** (`:nitro` for fastest, `:floor` for cheapest, `:exacto` for Auto Exacto tool-calling), **response caching** (`cache`, `cache_ttl`, `cache_clear`), **reasoning passthrough** (`include_reasoning`), and **web search** (`online`, `web_max_results`). |
|
|
116
|
-
| `analyze_image` | Analyze images from local files, URLs, or data URIs. Auto-optimized with sharp. Optional `cache_input: true` attaches `cache_control: ephemeral` for Anthropic / Gemini 2.5+ prompt caching. |
|
|
117
|
-
| `analyze_audio` | Analyze/transcribe audio (WAV, MP3, FLAC, OGG, etc.) from files, URLs, or data URIs. Optional `cache_input: true` for prompt caching. |
|
|
118
|
-
| `analyze_video` | Analyze/transcribe video (mp4, mpeg, mov, webm) from files, URLs, or data URIs. Optional `cache_input: true` for prompt caching. |
|
|
119
|
-
| `generate_image` | Generate images from text prompts. Supports `aspect_ratio` (14 values), `image_size` (0.5K–4K), and `max_tokens`. Optional path-sandboxed disk save. |
|
|
120
|
-
| `generate_audio` | Generate audio from text. Auto-detects format, wraps raw PCM in WAV. |
|
|
121
|
-
| `generate_video` | Generate video via OpenRouter's async API (Veo 3.1 / Sora 2 Pro / Seedance / Wan). Submits, polls, downloads, saves. Emits MCP `notifications/progress` when the client sends a `progressToken`. |
|
|
122
|
-
| `generate_video_from_image` | Image-to-video wrapper around `generate_video`. Narrower schema, higher tool-call hit rate. |
|
|
123
|
-
| `get_video_status` | Resume polling a `generate_video` job by id. Download + save when complete. |
|
|
124
|
-
| `rerank_documents` | Rerank candidate documents against a query via OpenRouter's `/rerank` endpoint. Supports Cohere and Fireworks rerankers. |
|
|
125
|
-
| `search_models` | Search/filter models by name, provider, or capabilities (vision / audio / video). Paginated via `offset` / `next_offset` / `has_more` / `total`. |
|
|
126
|
-
| `get_model_info` | Get pricing, context length, and capabilities for any model. |
|
|
127
|
-
| `validate_model` | Check if a model ID exists on OpenRouter. |
|
|
128
|
-
| `health_check` | Verify API-key validity, OpenRouter reachability, and return server + protocol versions. |
|
|
110
|
+
<details open>
|
|
111
|
+
<summary><strong>npx (recommended)</strong></summary>
|
|
129
112
|
|
|
130
|
-
|
|
113
|
+
```bash
|
|
114
|
+
export OPENROUTER_API_KEY=sk-or-v1-...
|
|
115
|
+
npx -y @stabgan/openrouter-mcp-multimodal
|
|
116
|
+
```
|
|
131
117
|
|
|
132
|
-
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"openrouter": {
|
|
122
|
+
"command": "npx",
|
|
123
|
+
"args": ["-y", "@stabgan/openrouter-mcp-multimodal"],
|
|
124
|
+
"env": {
|
|
125
|
+
"OPENROUTER_API_KEY": "sk-or-v1-..."
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
133
131
|
|
|
134
|
-
|
|
132
|
+
Pin a release: `"args": ["-y", "@stabgan/openrouter-mcp-multimodal@4.5.3"]`
|
|
135
133
|
|
|
136
|
-
|
|
134
|
+
</details>
|
|
135
|
+
|
|
136
|
+
<details>
|
|
137
|
+
<summary><strong>uvx / pipx (Python launcher)</strong></summary>
|
|
137
138
|
|
|
138
|
-
|
|
139
|
+
Install [uv](https://docs.astral.sh/uv/getting-started/installation/) (includes `uvx`), ensure **Node.js 20+** is also on your `PATH`, then:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
export OPENROUTER_API_KEY=sk-or-v1-...
|
|
143
|
+
uvx mcp-server-openrouter-multimodal
|
|
144
|
+
# pin npm version: OPENROUTER_MCP_NPM_VERSION=4.5.3 uvx mcp-server-openrouter-multimodal
|
|
145
|
+
```
|
|
139
146
|
|
|
140
147
|
```json
|
|
141
148
|
{
|
|
142
149
|
"mcpServers": {
|
|
143
150
|
"openrouter": {
|
|
144
|
-
"command": "
|
|
145
|
-
"args": ["-
|
|
151
|
+
"command": "uvx",
|
|
152
|
+
"args": ["mcp-server-openrouter-multimodal"],
|
|
146
153
|
"env": {
|
|
147
154
|
"OPENROUTER_API_KEY": "sk-or-v1-..."
|
|
148
155
|
}
|
|
@@ -151,7 +158,61 @@ Get a free API key from [openrouter.ai/keys](https://openrouter.ai/keys).
|
|
|
151
158
|
}
|
|
152
159
|
```
|
|
153
160
|
|
|
154
|
-
|
|
161
|
+
**pipx equivalent:** `pipx run mcp-server-openrouter-multimodal`
|
|
162
|
+
|
|
163
|
+
Optional: `OPENROUTER_MCP_NPM_VERSION=4.5.3` pins the underlying npm package.
|
|
164
|
+
|
|
165
|
+
</details>
|
|
166
|
+
|
|
167
|
+
<details>
|
|
168
|
+
<summary><strong>npm global</strong></summary>
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm install -g @stabgan/openrouter-mcp-multimodal
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"mcpServers": {
|
|
177
|
+
"openrouter": {
|
|
178
|
+
"command": "openrouter-multimodal",
|
|
179
|
+
"env": { "OPENROUTER_API_KEY": "sk-or-v1-..." }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
</details>
|
|
186
|
+
|
|
187
|
+
<details>
|
|
188
|
+
<summary><strong>node (local clone)</strong></summary>
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
git clone https://github.com/stabgan/openrouter-mcp-multimodal.git
|
|
192
|
+
cd openrouter-mcp-multimodal
|
|
193
|
+
npm ci && npm run build
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"mcpServers": {
|
|
199
|
+
"openrouter": {
|
|
200
|
+
"command": "node",
|
|
201
|
+
"args": ["/absolute/path/to/openrouter-mcp-multimodal/dist/index.js"],
|
|
202
|
+
"env": { "OPENROUTER_API_KEY": "sk-or-v1-..." }
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
</details>
|
|
209
|
+
|
|
210
|
+
<details>
|
|
211
|
+
<summary><strong>Docker</strong></summary>
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
docker run --rm -i -e OPENROUTER_API_KEY=sk-or-v1-... stabgan/openrouter-mcp-multimodal:latest
|
|
215
|
+
```
|
|
155
216
|
|
|
156
217
|
```json
|
|
157
218
|
{
|
|
@@ -159,8 +220,11 @@ Get a free API key from [openrouter.ai/keys](https://openrouter.ai/keys).
|
|
|
159
220
|
"openrouter": {
|
|
160
221
|
"command": "docker",
|
|
161
222
|
"args": [
|
|
162
|
-
"run",
|
|
163
|
-
"
|
|
223
|
+
"run",
|
|
224
|
+
"--rm",
|
|
225
|
+
"-i",
|
|
226
|
+
"-e",
|
|
227
|
+
"OPENROUTER_API_KEY=sk-or-v1-...",
|
|
164
228
|
"stabgan/openrouter-mcp-multimodal:latest"
|
|
165
229
|
]
|
|
166
230
|
}
|
|
@@ -168,212 +232,286 @@ Get a free API key from [openrouter.ai/keys](https://openrouter.ai/keys).
|
|
|
168
232
|
}
|
|
169
233
|
```
|
|
170
234
|
|
|
171
|
-
|
|
235
|
+
Use `-i` (interactive stdio). Avoid `-t` (TTY corrupts MCP framing on some hosts).
|
|
236
|
+
|
|
237
|
+
</details>
|
|
238
|
+
|
|
239
|
+
<details>
|
|
240
|
+
<summary><strong>GHCR (GitHub Container Registry)</strong></summary>
|
|
172
241
|
|
|
173
242
|
```bash
|
|
174
|
-
|
|
243
|
+
docker run --rm -i -e OPENROUTER_API_KEY=sk-or-v1-... \
|
|
244
|
+
ghcr.io/stabgan/openrouter-mcp-multimodal:4.5.3
|
|
175
245
|
```
|
|
176
246
|
|
|
177
247
|
```json
|
|
178
248
|
{
|
|
179
249
|
"mcpServers": {
|
|
180
250
|
"openrouter": {
|
|
181
|
-
"command": "
|
|
182
|
-
"
|
|
251
|
+
"command": "docker",
|
|
252
|
+
"args": [
|
|
253
|
+
"run", "--rm", "-i",
|
|
254
|
+
"-e", "OPENROUTER_API_KEY=sk-or-v1-...",
|
|
255
|
+
"ghcr.io/stabgan/openrouter-mcp-multimodal:latest"
|
|
256
|
+
]
|
|
183
257
|
}
|
|
184
258
|
}
|
|
185
259
|
}
|
|
186
260
|
```
|
|
187
261
|
|
|
188
|
-
|
|
262
|
+
</details>
|
|
263
|
+
|
|
264
|
+
<details>
|
|
265
|
+
<summary><strong>Smithery</strong></summary>
|
|
266
|
+
|
|
267
|
+
Interactive install (writes config for your client):
|
|
189
268
|
|
|
190
269
|
```bash
|
|
191
270
|
npx -y @smithery/cli install @stabgan/openrouter-mcp-multimodal --client claude
|
|
271
|
+
# or: --client cursor | vscode | windsurf | ...
|
|
192
272
|
```
|
|
193
273
|
|
|
194
|
-
|
|
274
|
+
Listing: [smithery.ai/server/@stabgan/openrouter-mcp-multimodal](https://smithery.ai/server/@stabgan/openrouter-mcp-multimodal)
|
|
275
|
+
|
|
276
|
+
</details>
|
|
195
277
|
|
|
196
278
|
<details>
|
|
197
|
-
<summary><strong>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
| `OPENROUTER_PROVIDER_IGNORE` | No | — | CSV. Exclude these provider slugs (e.g. `openai,anthropic`). |
|
|
207
|
-
| `OPENROUTER_PROVIDER_SORT` | No | — | `price` / `throughput` / `latency`. |
|
|
208
|
-
| `OPENROUTER_PROVIDER_ORDER` | No | — | JSON array or CSV of provider IDs (e.g. `["meta-llama","google"]`). |
|
|
209
|
-
| `OPENROUTER_PROVIDER_REQUIRE_PARAMETERS` | No | — | `true` / `false`. Only use providers supporting every request parameter. |
|
|
210
|
-
| `OPENROUTER_PROVIDER_DATA_COLLECTION` | No | — | `allow` / `deny`. Opt out of providers that log request data. |
|
|
211
|
-
| `OPENROUTER_PROVIDER_ALLOW_FALLBACKS` | No | — | `true` / `false`. |
|
|
212
|
-
| `OPENROUTER_CACHE_RESPONSES` | No | — | `1` / `true`. Enable response caching server-wide. Sends `X-OpenRouter-Cache: true` on chat + analyze_* calls unless overridden per-request with `cache: false`. Zero tokens billed on hits. |
|
|
213
|
-
| `OPENROUTER_INCLUDE_REASONING` | No | — | `1` / `true`. Enable reasoning tokens passthrough server-wide for DeepSeek R1 / Gemini Thinking / Opus 4.7. Adds `_meta.reasoning` to `chat_completion` responses. |
|
|
214
|
-
| `OPENROUTER_MODEL_CACHE_TTL_MS` | No | `3600000` | Model cache TTL (ms) |
|
|
215
|
-
| `OPENROUTER_IMAGE_MAX_DIMENSION` | No | `800` | Longest edge for resize (px) |
|
|
216
|
-
| `OPENROUTER_IMAGE_JPEG_QUALITY` | No | `80` | JPEG quality (1–100) |
|
|
217
|
-
| `OPENROUTER_IMAGE_FETCH_TIMEOUT_MS` | No | `30000` | Image URL timeout |
|
|
218
|
-
| `OPENROUTER_IMAGE_MAX_DOWNLOAD_BYTES` | No | `26214400` | Image URL size cap (~25 MB) |
|
|
219
|
-
| `OPENROUTER_IMAGE_MAX_REDIRECTS` | No | `8` | Image URL redirect cap |
|
|
220
|
-
| `OPENROUTER_IMAGE_MAX_DATA_URL_BYTES` | No | `20971520` | Image data URL size cap (~20 MB) |
|
|
221
|
-
| `OPENROUTER_AUDIO_FETCH_TIMEOUT_MS` | No | `30000` | Audio URL timeout |
|
|
222
|
-
| `OPENROUTER_AUDIO_MAX_DOWNLOAD_BYTES` | No | `26214400` | Audio URL size cap (~25 MB) |
|
|
223
|
-
| `OPENROUTER_AUDIO_MAX_REDIRECTS` | No | `8` | Audio URL redirect cap |
|
|
224
|
-
| `OPENROUTER_AUDIO_MAX_DATA_URL_BYTES` | No | `20971520` | Audio data URL size cap |
|
|
225
|
-
| `OPENROUTER_DEFAULT_VIDEO_MODEL` | No | `google/gemini-2.5-flash` | Default for `analyze_video` |
|
|
226
|
-
| `OPENROUTER_DEFAULT_VIDEO_GEN_MODEL` | No | `google/veo-3.1` | Default for `generate_video` |
|
|
227
|
-
| `OPENROUTER_VIDEO_FETCH_TIMEOUT_MS` | No | `60000` | Video URL timeout |
|
|
228
|
-
| `OPENROUTER_VIDEO_MAX_DOWNLOAD_BYTES` | No | `104857600` | Video URL size cap (~100 MB) |
|
|
229
|
-
| `OPENROUTER_VIDEO_MAX_REDIRECTS` | No | `8` | Video URL redirect cap |
|
|
230
|
-
| `OPENROUTER_VIDEO_MAX_DATA_URL_BYTES` | No | `104857600` | Video data URL size cap |
|
|
231
|
-
| `OPENROUTER_VIDEO_POLL_INTERVAL_MS` | No | `15000` | Async video poll cadence |
|
|
232
|
-
| `OPENROUTER_VIDEO_MAX_WAIT_MS` | No | `600000` | Max wait before returning a resumable handle |
|
|
233
|
-
| `OPENROUTER_VIDEO_GEN_MAX_BYTES` | No | `268435456` | Generated video download cap (~256 MB) |
|
|
234
|
-
| `OPENROUTER_VIDEO_INLINE_MAX_BYTES` | No | `10485760` | Inline video ceiling (~10 MB) |
|
|
235
|
-
| `OPENROUTER_OUTPUT_DIR` | No | `process.cwd()` | Sandbox root for `save_path` |
|
|
236
|
-
| `OPENROUTER_ALLOW_UNSAFE_PATHS` | No | — | `1` disables the sandbox |
|
|
237
|
-
| `OPENROUTER_LOG_LEVEL` | No | `info` | `error` / `warn` / `info` / `debug` |
|
|
279
|
+
<summary><strong>MCP Registry</strong></summary>
|
|
280
|
+
|
|
281
|
+
Official name: `io.github.stabgan/openrouter-multimodal`
|
|
282
|
+
|
|
283
|
+
- Registry: [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io/servers/io.github.stabgan/openrouter-multimodal)
|
|
284
|
+
- npm package: `@stabgan/openrouter-mcp-multimodal`
|
|
285
|
+
- OCI image: `docker.io/stabgan/openrouter-mcp-multimodal`
|
|
286
|
+
|
|
287
|
+
Clients that support registry-driven install will offer npm or Docker; otherwise use the JSON blocks above.
|
|
238
288
|
|
|
239
289
|
</details>
|
|
240
290
|
|
|
241
|
-
|
|
291
|
+
<details>
|
|
292
|
+
<summary><strong>Claude Code CLI</strong></summary>
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
claude mcp add openrouter -- npx -y @stabgan/openrouter-mcp-multimodal
|
|
296
|
+
# project scope:
|
|
297
|
+
claude mcp add --scope project openrouter -- npx -y @stabgan/openrouter-mcp-multimodal
|
|
298
|
+
```
|
|
242
299
|
|
|
243
|
-
|
|
244
|
-
- **Generate tools** write to disk through a path sandbox: `save_path` is resolved against `OPENROUTER_OUTPUT_DIR` and any traversal attempt is rejected. Override with `OPENROUTER_ALLOW_UNSAFE_PATHS=1`.
|
|
245
|
-
- **IPv6 SSRF blocklist** covers loopback, unspecified, IPv4-mapped, IPv4-compatible, link-local, site-local, ULA, multicast, documentation, Teredo, ORCHID, and 6to4 of private IPv4.
|
|
300
|
+
Set `OPENROUTER_API_KEY` in your shell or client env before starting Claude Code.
|
|
246
301
|
|
|
247
|
-
|
|
302
|
+
</details>
|
|
248
303
|
|
|
304
|
+
<details>
|
|
305
|
+
<summary><strong>MCP Inspector</strong></summary>
|
|
306
|
+
|
|
307
|
+
Debug tools/list and tool calls against a live OpenRouter key:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
export OPENROUTER_API_KEY=sk-or-v1-...
|
|
311
|
+
npx -y @modelcontextprotocol/inspector npx -y @stabgan/openrouter-mcp-multimodal
|
|
249
312
|
```
|
|
250
|
-
# Chat
|
|
251
|
-
Use chat_completion to explain quantum computing in simple terms.
|
|
252
313
|
|
|
253
|
-
|
|
254
|
-
Use chat_completion with model "anthropic/claude-3.5-sonnet", prompt "Summarize this",
|
|
255
|
-
provider { sort: "price", ignore: ["openai"], data_collection: "deny" }
|
|
314
|
+
</details>
|
|
256
315
|
|
|
257
|
-
|
|
258
|
-
|
|
316
|
+
<details>
|
|
317
|
+
<summary><strong>Windows npx</strong></summary>
|
|
259
318
|
|
|
260
|
-
|
|
261
|
-
Use chat_completion with model "mistralai/mistral-7b-instruct:floor", prompt "Quick check"
|
|
319
|
+
When Claude Desktop or Cursor cannot find `npx` (GUI apps often miss shell `PATH`), wrap with `cmd`:
|
|
262
320
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"mcpServers": {
|
|
324
|
+
"openrouter": {
|
|
325
|
+
"command": "cmd",
|
|
326
|
+
"args": ["/c", "npx", "-y", "@stabgan/openrouter-mcp-multimodal"],
|
|
327
|
+
"env": { "OPENROUTER_API_KEY": "sk-or-v1-..." }
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
268
332
|
|
|
269
|
-
|
|
270
|
-
Use chat_completion with model "openai/gpt-4o", prompt "What shipped in OpenRouter last week?",
|
|
271
|
-
online: true, web_max_results: 5
|
|
333
|
+
If still failing, use the full path from `where npx` as the command.
|
|
272
334
|
|
|
273
|
-
|
|
274
|
-
Use rerank_documents with query "best practices for MCP server auth",
|
|
275
|
-
documents: ["doc A text...", "doc B text...", "doc C text..."], top_n: 3
|
|
335
|
+
</details>
|
|
276
336
|
|
|
277
|
-
|
|
278
|
-
Use generate_video_from_image with image "./frame.png", prompt "zoom out slowly",
|
|
279
|
-
model "google/veo-3.1", save to ./clip.mp4
|
|
337
|
+
## Why this server?
|
|
280
338
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
339
|
+
| Capability | This server | Typical MCP LLM servers |
|
|
340
|
+
| :----------------------------------- | :---------: | :---------------------: |
|
|
341
|
+
| Text chat (300+ models) | ✅ | ✅ |
|
|
342
|
+
| Image analysis + generation | ✅ | partial |
|
|
343
|
+
| Audio analysis + TTS | ✅ | ❌ |
|
|
344
|
+
| Video analysis + generation | ✅ | ❌ |
|
|
345
|
+
| Model search / validate / rerank | ✅ | ❌ |
|
|
346
|
+
| Path sandbox + SSRF protection | ✅ | rare |
|
|
347
|
+
| MCP 2025 structured outputs | ✅ | rare |
|
|
348
|
+
| Async video + progress notifications | ✅ | ❌ |
|
|
284
349
|
|
|
285
|
-
|
|
286
|
-
Use analyze_image on /path/to/photo.jpg and tell me what you see.
|
|
350
|
+
## Tools
|
|
287
351
|
|
|
288
|
-
|
|
289
|
-
Use analyze_audio on /path/to/recording.mp3 to transcribe it.
|
|
352
|
+
14 MCP tools. Each description includes **Use when**, **Good/Bad examples**, **Fails when**, and **Works with** so agents pick the right tool and recover from errors.
|
|
290
353
|
|
|
291
|
-
|
|
292
|
-
|
|
354
|
+
| Tool | Purpose |
|
|
355
|
+
| :-------------------------- | :---------------------------------------------------------- |
|
|
356
|
+
| `chat_completion` | Text chat, web search, provider routing, caching, reasoning |
|
|
357
|
+
| `analyze_image` | Vision — local path, URL, or data URL + `question` |
|
|
358
|
+
| `analyze_audio` | Transcribe / analyze audio files |
|
|
359
|
+
| `analyze_video` | Describe / Q&A over video files |
|
|
360
|
+
| `generate_image` | Text-to-image with optional reference images |
|
|
361
|
+
| `generate_audio` | Text-to-speech / music |
|
|
362
|
+
| `generate_video` | Text-to-video (async, resumable) |
|
|
363
|
+
| `generate_video_from_image` | Image-to-video (narrower schema) |
|
|
364
|
+
| `get_video_status` | Poll / resume video jobs |
|
|
365
|
+
| `search_models` | Paginated model catalog search |
|
|
366
|
+
| `get_model_info` | Pricing, context, modalities |
|
|
367
|
+
| `validate_model` | Cheap model ID existence check |
|
|
368
|
+
| `rerank_documents` | Relevance ranking for RAG |
|
|
369
|
+
| `health_check` | API key + reachability probe |
|
|
293
370
|
|
|
294
|
-
|
|
295
|
-
Use generate_audio with prompt "Explain neural networks" and voice "alloy", save to ./response.wav
|
|
371
|
+
Errors use a closed `_meta.code` taxonomy: `INVALID_INPUT` · `UNSAFE_PATH` · `UPSTREAM_*` · `MODEL_NOT_FOUND` · `JOB_STILL_RUNNING` · and more.
|
|
296
372
|
|
|
297
|
-
|
|
298
|
-
Use generate_audio with model "google/lyria-3-clip-preview" and prompt "upbeat jazz piano trio"
|
|
373
|
+
## Examples
|
|
299
374
|
|
|
300
|
-
|
|
301
|
-
Use generate_image with prompt "a cat astronaut on mars", aspect_ratio "16:9", image_size "1K", save to ./cat.png
|
|
375
|
+
### Chat (free model)
|
|
302
376
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"tool": "chat_completion",
|
|
380
|
+
"arguments": {
|
|
381
|
+
"model": "google/gemma-4-26b-a4b-it:free",
|
|
382
|
+
"messages": [{ "role": "user", "content": "Summarize MCP in one sentence." }]
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Analyze an image
|
|
306
388
|
|
|
307
|
-
|
|
308
|
-
|
|
389
|
+
```json
|
|
390
|
+
{
|
|
391
|
+
"tool": "analyze_image",
|
|
392
|
+
"arguments": {
|
|
393
|
+
"image_path": "diagram.png",
|
|
394
|
+
"question": "List every label in this diagram."
|
|
395
|
+
}
|
|
396
|
+
}
|
|
309
397
|
```
|
|
310
398
|
|
|
311
|
-
|
|
399
|
+
> Use `image_path` and `question` — not `image` / `prompt`.
|
|
400
|
+
|
|
401
|
+
### Search models (vision + free)
|
|
312
402
|
|
|
403
|
+
```json
|
|
404
|
+
{
|
|
405
|
+
"tool": "search_models",
|
|
406
|
+
"arguments": {
|
|
407
|
+
"query": "gemma",
|
|
408
|
+
"capabilities": { "vision": true },
|
|
409
|
+
"limit": 10,
|
|
410
|
+
"offset": 0
|
|
411
|
+
}
|
|
412
|
+
}
|
|
313
413
|
```
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
├── analyze-image.ts # Vision analysis
|
|
328
|
-
├── analyze-audio.ts # Audio transcription
|
|
329
|
-
├── analyze-video.ts # Video understanding
|
|
330
|
-
├── generate-image.ts # Image generation
|
|
331
|
-
├── generate-audio.ts # Audio generation + streaming
|
|
332
|
-
├── generate-video.ts # Video generation (async)
|
|
333
|
-
├── image-utils.ts # Sharp optimization, MIME sniffing
|
|
334
|
-
├── audio-utils.ts # Audio format detection
|
|
335
|
-
├── video-utils.ts # Video format detection
|
|
336
|
-
├── search-models.ts # Model search
|
|
337
|
-
├── get-model-info.ts # Model detail lookup
|
|
338
|
-
└── validate-model.ts # Model existence check
|
|
414
|
+
|
|
415
|
+
### Generate video (async)
|
|
416
|
+
|
|
417
|
+
```json
|
|
418
|
+
{
|
|
419
|
+
"tool": "generate_video",
|
|
420
|
+
"arguments": {
|
|
421
|
+
"model": "google/veo-3.1",
|
|
422
|
+
"prompt": "Ocean waves at sunrise, cinematic drone shot",
|
|
423
|
+
"duration": 4,
|
|
424
|
+
"save_path": "river.mp4"
|
|
425
|
+
}
|
|
426
|
+
}
|
|
339
427
|
```
|
|
340
428
|
|
|
429
|
+
If the job is still running when `max_wait_ms` elapses, the response succeeds with `_meta.code: JOB_STILL_RUNNING` and a `video_id` — call `get_video_status` to resume. **This is not an error.**
|
|
430
|
+
|
|
431
|
+
More examples: [docs/plans/tool-description-improvement.md](./docs/plans/tool-description-improvement.md)
|
|
432
|
+
|
|
433
|
+
## Security
|
|
434
|
+
|
|
435
|
+
- **Input path sandbox** — `analyze_*` and reference images must stay inside `OPENROUTER_INPUT_DIR`
|
|
436
|
+
- **Output path sandbox** — `save_path` must stay inside `OPENROUTER_OUTPUT_DIR`
|
|
437
|
+
- **SSRF protection** — private/reserved IPs blocked on URL fetches
|
|
438
|
+
- **Untrusted content** — analyze outputs tagged `_meta.content_is_untrusted: true`
|
|
439
|
+
|
|
440
|
+
Override sandboxes only with `OPENROUTER_ALLOW_UNSAFE_PATHS=1` (discouraged).
|
|
441
|
+
|
|
442
|
+
## Configuration
|
|
443
|
+
|
|
444
|
+
<details>
|
|
445
|
+
<summary><strong>Environment variables</strong></summary>
|
|
446
|
+
|
|
447
|
+
| Variable | Required | Default | Description |
|
|
448
|
+
| :----------------------------- | :------: | :------------------------------------ | :----------------------------------- |
|
|
449
|
+
| `OPENROUTER_API_KEY` | **Yes** | — | OpenRouter API key |
|
|
450
|
+
| `OPENROUTER_DEFAULT_MODEL` | No | `nvidia/nemotron-nano-12b-v2-vl:free` | Default when tools omit `model` |
|
|
451
|
+
| `OPENROUTER_INTEGRATION_MODEL` | No | `google/gemma-4-26b-a4b-it:free` | Model used by live integration tests |
|
|
452
|
+
| `OPENROUTER_OUTPUT_DIR` | No | `cwd` | Sandbox root for `save_path` |
|
|
453
|
+
| `OPENROUTER_INPUT_DIR` | No | — | Sandbox root for local input files |
|
|
454
|
+
| `OPENROUTER_LOG_LEVEL` | No | `info` | `error` / `warn` / `info` / `debug` |
|
|
455
|
+
|
|
456
|
+
See [`.env.example`](./.env.example) for the full list (provider routing, image/audio/video limits, caching, video polling).
|
|
457
|
+
|
|
458
|
+
</details>
|
|
459
|
+
|
|
341
460
|
## Development
|
|
342
461
|
|
|
343
462
|
```bash
|
|
344
463
|
git clone https://github.com/stabgan/openrouter-mcp-multimodal.git
|
|
345
464
|
cd openrouter-mcp-multimodal
|
|
346
465
|
npm install
|
|
347
|
-
cp .env.example .env
|
|
466
|
+
cp .env.example .env # add OPENROUTER_API_KEY
|
|
348
467
|
npm run build
|
|
349
|
-
npm start
|
|
350
468
|
```
|
|
351
469
|
|
|
470
|
+
### Testing
|
|
471
|
+
|
|
472
|
+
| Command | What it runs |
|
|
473
|
+
| :------------------------- | :--------------------------------------------------------- |
|
|
474
|
+
| `npm test` | **652** unit + mock tests (no API key, <2s) |
|
|
475
|
+
| `npm run test:regression` | Security + schema regression guards |
|
|
476
|
+
| `npm run test:integration` | **16** live OpenRouter scenarios (**requires** `.env` key) |
|
|
477
|
+
| `npm run test:e2e` | Full MCP stdio smoke (`scripts/live-e2e.mjs`) |
|
|
478
|
+
| `npm run ci` | lint + format + build + **all** of the above except e2e |
|
|
479
|
+
|
|
480
|
+
**Free models for CI / zero-credit accounts:** integration tests default to `google/gemma-4-26b-a4b-it:free` (override with `OPENROUTER_INTEGRATION_MODEL`). GitHub Actions requires the `OPENROUTER_API_KEY` repository secret.
|
|
481
|
+
|
|
482
|
+
Mock tests live under `src/__tests__/mock/` and cover handlers, path sandboxes, SSRF blocks, model-cache pagination, tool descriptions, and structured outputs — **330+** additional cases beyond the core suite.
|
|
483
|
+
|
|
352
484
|
```bash
|
|
353
|
-
npm test # 163 unit tests, <1s
|
|
354
|
-
npm run test:integration # Live API tests
|
|
355
485
|
npm run lint
|
|
356
|
-
|
|
486
|
+
npm run format:check
|
|
357
487
|
```
|
|
358
488
|
|
|
359
|
-
##
|
|
489
|
+
## FAQ
|
|
490
|
+
|
|
491
|
+
### Do I need paid OpenRouter credits?
|
|
492
|
+
|
|
493
|
+
No, to get started. Free models work for chat and vision. Audio/video **generation** usually requires credits; analysis may return `402` on some models — the server surfaces that as a structured error.
|
|
494
|
+
|
|
495
|
+
### Which MCP clients are supported?
|
|
496
|
+
|
|
497
|
+
Any MCP-compatible client over stdio: Cursor, Claude Desktop, VS Code Copilot, Windsurf, Cline, Kiro, and custom agents.
|
|
498
|
+
|
|
499
|
+
### How is this different from calling OpenRouter directly?
|
|
500
|
+
|
|
501
|
+
This server adds MCP tool schemas, security sandboxes, error taxonomy, model caching, async video polling with progress notifications, and agent-oriented tool descriptions — so LLMs invoke the right capability without custom HTTP glue.
|
|
360
502
|
|
|
361
|
-
|
|
503
|
+
### Where is the security advisory for path traversal?
|
|
362
504
|
|
|
363
|
-
|
|
364
|
-
- Structured `_meta.code` on every error response (text messages preserved)
|
|
365
|
-
- `save_path` sandboxed by default — set `OPENROUTER_OUTPUT_DIR` or `OPENROUTER_ALLOW_UNSAFE_PATHS=1`
|
|
366
|
-
- Reasoning-model awareness: `content: null` + `finish_reason: length` now returns `INVALID_INPUT` with a preview instead of empty string
|
|
367
|
-
- IPv6 SSRF coverage extended to mapped, compat, multicast, 6to4, Teredo, ORCHID
|
|
505
|
+
Fixed in 4.5.2+ — see [GHSA-3q7p-736f-x44v](https://github.com/stabgan/openrouter-mcp-multimodal/security/advisories/GHSA-3q7p-736f-x44v) and `docs/solutions/security-issues/`.
|
|
368
506
|
|
|
369
507
|
## Compatibility
|
|
370
508
|
|
|
371
|
-
Works with any MCP client:
|
|
509
|
+
Works with any MCP client. Protocol: **MCP 2025-06-18**. Node **≥ 20** (Docker image uses Node 22).
|
|
372
510
|
|
|
373
511
|
## License
|
|
374
512
|
|
|
375
|
-
Apache 2.0
|
|
513
|
+
Apache 2.0 — see [LICENSE](./LICENSE).
|
|
376
514
|
|
|
377
515
|
## Contributing
|
|
378
516
|
|
|
379
|
-
Issues and PRs welcome.
|
|
517
|
+
Issues and PRs welcome. For large changes, open an issue first. Run `npm run ci` before submitting.
|