Skip to main content
Glama
WormAlien

opencode-openai-vision-mcp

by WormAlien
README.md
# opencode-openai-vision-mcp

A tiny [MCP](https://modelcontextprotocol.io) server that gives **OpenCode** (and any
other MCP client) working image/vision support through **any OpenAI-compatible
endpoint** — for example an [OmniRoute](https://github.com/diegosouzapw/OmniRoute) or
[LiteLLM](https://github.com/BerriAI/litellm) gateway, or OpenAI itself.

It reads an image file from disk, sends it to a vision-capable model as an
`image_url` content block, and returns the model's text description.

## Why this exists

OpenCode currently cannot send image attachments to vision models served through a
**custom OpenAI-compatible provider** (`@ai-sdk/openai-compatible`). The attachment is
dropped before it reaches the model, and the assistant replies with something like:

> this model does not support image input

This is an OpenCode adapter bug, tracked upstream in
[anomalyco/opencode#20802](https://github.com/anomalyco/opencode/issues/20802)
(fix PRs [#26826](https://github.com/anomalyco/opencode/pull/26826) /
[#21627](https://github.com/anomalyco/opencode/pull/21627) were not merged at the time
of writing). The underlying model and gateway are usually fine — a direct
`/chat/completions` call with `image_url` works; only OpenCode's conversion is broken.

This project is a **workaround**: instead of relying on OpenCode's native image path,
it routes images through an MCP tool that you fully control.

## How it works

```
OpenCode (you paste a screenshot)
  └─ opencode-vision plugin            intercepts the image inside OpenCode,
     (separate project, see below)     saves it to a temp file, and rewrites the
                                        message to "call local_vision with this path"
        └─ local_vision (THIS server)  reads the file, base64-encodes it, and POSTs
                                        it as image_url to an OpenAI-compatible endpoint
              └─ your gateway/model     e.g. OmniRoute -> any vision model
                                        returns a text description back to OpenCode
```

This is a **describe-then-reason** approach: a vision model looks at the image and
returns text; your main model works from that text. It is not native multimodality,
but it restores the "paste a screenshot and ask" workflow.

## Prerequisites

1. **Node.js >= 18**
2. **A vision-capable model** reachable via an OpenAI-compatible `/chat/completions`
   endpoint (OmniRoute, LiteLLM, OpenAI, etc.).
3. **The [opencode-vision](https://github.com/DavidEasden/opencode-vision) plugin**
   (AGPL-3.0) — this is what intercepts the pasted image inside OpenCode and calls the
   `local_vision` tool. This MCP server is the *tool* it calls. You need both.

## Install

```bash
git clone https://github.com/WormAlien/opencode-openai-vision-mcp.git
cd opencode-openai-vision-mcp
npm install
```

Quick self-test (optional) — point it at your gateway and it should print a color word:

```bash
VISION_BASE_URL=http://localhost:20128/v1 \
VISION_API_KEY=your-key \
VISION_MODEL=your-vision-model \
node server.js
# then drive it with any MCP client, or just confirm it starts without error
```

## Configure OpenCode

Add the MCP server to your `opencode.json` (see `opencode.example.json`):

```json
{
  "mcp": {
    "local": {
      "type": "local",
      "command": ["node", "/absolute/path/to/opencode-openai-vision-mcp/server.js"],
      "enabled": true,
      "environment": {
        "VISION_BASE_URL": "http://localhost:20128/v1",
        "VISION_API_KEY": "YOUR_GATEWAY_API_KEY",
        "VISION_MODEL": "your-vision-model-or-alias"
      }
    }
  }
}
```

> Naming the server `local` makes its tool resolve to `local_vision`, which matches the
> default `imageAnalysisTool` of the opencode-vision plugin — so no extra wiring needed.

Then enable the plugin for your models via `opencode-vision.json`
(see `opencode-vision.example.json`):

```json
{
  "models": ["your-provider/*"],
  "imageAnalysisTool": "local_vision"
}
```

Restart OpenCode, select a model under that provider, paste an image, and ask away.

## Environment variables

| Variable           | Default                        | Description                                            |
| ------------------ | ------------------------------ | ------------------------------------------------------ |
| `VISION_BASE_URL`  | `http://localhost:20128/v1`    | OpenAI-compatible base URL (must end in `/v1`).         |
| `VISION_API_KEY`   | *(empty)*                      | Bearer token for the endpoint. Omitted if empty.       |
| `VISION_MODEL`     | `gpt-4o`                       | Vision model name, or a gateway alias.                  |
| `VISION_MAX_TOKENS`| `1024`                         | Max tokens for the description.                         |

**Tip:** point `VISION_MODEL` at a gateway *alias* (e.g. a `vision` alias in OmniRoute).
Then you can swap the real model in your gateway dashboard without editing any config.

## The `vision` tool

- **Input:** `path` (absolute path to a PNG/JPEG/WebP/GIF), optional `question`.
- **Output:** a text description (transcribes visible text by default).
- Handles both plain JSON and SSE/streamed responses from the endpoint.

## Notes / limitations

- The image must be readable on the **same machine** the server runs on (it reads from
  the local filesystem path the plugin saved).
- Quality depends entirely on the vision model you point it at.
- Once OpenCode merges native image support for openai-compatible providers
  ([#20802](https://github.com/anomalyco/opencode/issues/20802)), you may not need this.

## Credits

- [opencode-vision](https://github.com/DavidEasden/opencode-vision) (AGPL-3.0) — the
  OpenCode plugin that intercepts images and calls the MCP tool. **A separate project**;
  not bundled here.
- [Model Context Protocol](https://modelcontextprotocol.io) and its TypeScript SDK.

## License

MIT — see [LICENSE](./LICENSE). (This applies to this MCP server only; the
opencode-vision plugin has its own AGPL-3.0 license.)

TDQS

A3.9/5.0

Scored across 1 tool

Disambiguation5/5

Only one tool exists, so there is no possibility of confusion between tools.

Naming Consistency3/5

The single tool is named 'vision', which is a noun rather than a verb_noun pattern. While there is no inconsistency, the naming lacks a clear action-oriented convention.

Tool Count3/5

With only one tool, the server feels minimal. For a focused purpose like image analysis it may be acceptable, but it is on the low end of the scale.

Completeness3/5

The tool covers the stated purpose of analyzing images, but there are no additional capabilities like OCR or object detection, limiting completeness for broader vision tasks.

Maintenance

ActivityInactive
ResponsivenessNo issues