# mcp-supergateway A local toolbox for AI coding assistants, running in Docker. AI coding tools — **opencode**, **crush**, VS Code's **Cline**, **Roo Code**, **Kilo Code** — can all use extra abilities (fetch a web page, search the web, read library docs, search a codebase) through a standard plug system called **MCP**. Every ability is a small server program that normally has to be installed and run separately for each tool. This repo runs those servers **once per machine, in Docker**, and gives each one a simple local web address. Every AI tool on your computer then shares the same services — no Node.js, no Python, nothing to install on the host. ## The idea in one picture ``` your AI tools this docker-compose stack ┌─────────────┐ │ opencode │───┐ │ crush │ │ http://localhost:8000/fetch/mcp ┌────────────────────┐ │ Cline │ ├───►http://localhost:8000/context7/mcp ──► │ mcp-gateway │ │ Roo / Kilo │ │ http://localhost:8000/brave-search/mcp │ (shared tools) │ └─────────────┘ │ └────────────────────┘ │ http://localhost:8001/supergateway/mcp └───►http://localhost:8001/yazi/mcp ──► ┌────────────────────┐ http://localhost:8001//mcp │ mcp-codebase-index│ │ (one endpoint per │ │ code project) │ └────────────────────┘ ``` Two containers do all the work: - **`mcp-gateway`** (port 8000) — always-useful helpers, shared by every tool: [web fetch](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch), [library docs via context7](https://github.com/upstash/context7), [web search via brave](https://github.com/brave/brave-search-mcp-server) (needs a free API key). - **`mcp-codebase-index`** (port 8001) — a private search index for each of your code projects, built with [open-codebase-index](https://github.com/Helweg/open-codebase-index). It lets the AI find things in a large codebase quickly (keyword search, "where is this defined", "who calls this", and — with the bundled ollama embeddings — "find this by meaning") instead of reading files one by one. Every project gets its own address: `http://localhost:8001//mcp`. ## Quick start (once per machine) ```bash git clone && cd supergateway cp .env.example .env # put your BRAVE_API_KEY in it (https://brave.com/search/api/) ./add-project.sh . # registers this repo as the first indexed project and starts everything ``` `./add-project.sh .` builds the Docker image, starts both containers, indexes this repository, and writes a few small config files into it (see below). Check that it worked: ```bash docker compose ps # both containers should be "Up" curl http://localhost:8000/healthz # -> ok curl http://localhost:8001/healthz # -> ok ``` ## Indexing your own projects — the one command you'll actually use From this repo, run: ```bash ./add-project.sh ~/src/git/my-project ``` That single command: 1. remembers the project in `projects.env` (the list of indexed repositories), 2. gives it its own address: `http://localhost:8001/my-project/mcp`, 3. builds its search index (stored inside the project at `.opencode/index/` — disposable, rebuilt anytime, gitignored), 4. writes three tiny config files **into the project**, each pointing at that address: - `opencode.json` — opencode reads this automatically when started in that directory - `.crush.json` — crush reads this automatically when started in that directory - `.roo/mcp.json` — Roo Code / Cline / Kilo Code workspace config (commit them — teammates who run this stack get the same behavior for free), 5. adds `.opencode/` to the project's `.gitignore`. **Why this is nice:** you never re-configure your tools when switching projects. Open a project → the AI tool automatically finds *that project's* index. Work on three projects at the same time — each has its own address and its own index, nothing interferes. Rules of the game: - the project must be a **git repository** (the index is branch-aware and needs git), - it must live under `PROJECTS_ROOT` (default `~/src`, set in `.env` — the script configures this for you). Projects elsewhere need one extra volume mount, see [Troubleshooting](#troubleshooting). To stop indexing a project: `./add-project.sh --remove ~/src/git/my-project` (index data and the small config files are left in the project; delete them if you want). For "how does repo X use repo Y" questions you can also attach neighboring repositories to the current project as **knowledge bases** (the `add_knowledge_base` tool) — they get searched alongside the project's own code. ## Pointing your tools at the stack (once per tool) The shared tools (port 8000) are configured once, globally. Per-project indexes (port 8001) need **no global configuration at all** — each project carries its own config files, written by `add-project.sh`. ### opencode `~/.config/opencode/opencode.jsonc`: ```jsonc { "$schema": "https://opencode.ai/config.json", "mcp": { "fetch": { "type": "remote", "url": "http://localhost:8000/fetch/mcp", "enabled": true }, "context7": { "type": "remote", "url": "http://localhost:8000/context7/mcp", "enabled": true }, "brave-search": { "type": "remote", "url": "http://localhost:8000/brave-search/mcp", "enabled": true } } } ``` Verify from any indexed project directory: `opencode mcp list` → four servers connected (three above + `codebase-index` from the project's own `opencode.json`). If opencode was already running there, run `opencode reload` first and give it a few seconds. ### crush `~/.config/crush/crush.json`: ```json { "$schema": "https://charm.land/crush.json", "mcp": { "fetch": { "type": "http", "url": "http://localhost:8000/fetch/mcp" }, "context7": { "type": "http", "url": "http://localhost:8000/context7/mcp" }, "brave-search": { "type": "http", "url": "http://localhost:8000/brave-search/mcp" } } } ``` crush picks up each project's `.crush.json` automatically (restart crush in that directory). ### Cline / Roo Code / Kilo Code (VS Code) Each indexed project already contains a workspace `.roo/mcp.json` (written by `add-project.sh`), so opening the project in Roo Code just works. To also get the shared tools everywhere, add them once to your plugin's global settings file: | Plugin | Global config file (Linux) | | --------- | --------------------------------------------------------------------------------------------------------- | | Cline | `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` | | Roo Code | `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json` | | Kilo Code | `~/.config/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json` | ```json { "mcpServers": { "fetch": { "type": "streamableHttp", "url": "http://localhost:8000/fetch/mcp", "disabled": false }, "context7": { "type": "streamableHttp", "url": "http://localhost:8000/context7/mcp", "disabled": false }, "brave-search": { "type": "streamableHttp", "url": "http://localhost:8000/brave-search/mcp", "disabled": false } } } ``` ## What the code index actually searches The index runs in **hybrid mode**: keyword (BM25) search, symbol definitions, call graphs, branch awareness — plus **semantic search** ("find code by meaning") via local [ollama](https://ollama.com) embeddings. The `ollama` container ships with this stack but stays dormant (it lives behind the `embeddings` profile), so everything already works fully offline without it: while ollama is off, semantic ranking just falls back to keyword ranking. Enable semantic search (once per machine): ```bash docker compose --profile embeddings up -d # start the ollama container docker compose exec ollama ollama pull nomic-embed-text # one-time, ~274 MB, kept in a volume # build vectors for your projects (safe to repeat): docker compose exec mcp-codebase-index ocbi index --project ~/src/git/my-project \ --config /etc/codebase-index/config.json --force ``` Projects registered later with `./add-project.sh` pick embeddings up automatically. Check a project's mode: `docker compose exec mcp-codebase-index ocbi status --project ~/src/git/my-project --config /etc/codebase-index/config.json` — semantic search is live when it reports `Provider: ollama` and the index shows embedded chunks. (You can also just ask the AI to run `index_status` / `codebase_search`; semantic hits carry similarity scores near 1.0.) Gotchas: - Plain `docker compose up -d` / `down` / `ps` do **not** manage the ollama container. Pass `--profile embeddings` whenever a command should include it; `docker compose --profile embeddings down` stops everything. - If indexing logs `INDEXING WARNING: N chunks failed to embed`, ollama was unreachable or too slow (CPU). Start it, make sure the model is pulled, re-run the index — failed chunks are simply rebuilt on the next run. Search keeps working keyword-only meanwhile. - Batch sizes for the index container are pre-tuned small (`OLLAMA_MAX_BATCH_TOKENS` / `OLLAMA_MAX_BATCH_ITEMS` env in `docker-compose.yml`) so CPU-only ollama stays under the embedding API's 30s timeout. Raise them if you have a GPU. - Embeddings don't have to be local: any OpenAI-compatible endpoint works. Switch `embeddingProvider` to `"custom"` in `codebase-index.config.json` and set `customProvider: { "baseUrl", "model", "apiKey" }`. The chat model provider (z.ai, OpenRouter, cortecs, …) is configured in your AI tool and is completely independent of this stack — the index never talks to it. ## Everyday commands ```bash docker compose ps # status docker compose --profile embeddings ps # status, including ollama docker compose logs -f mcp # shared-tools gateway logs docker compose logs -f mcp-codebase-index # index gateway logs (per-project lines prefixed) docker compose restart mcp mcp-codebase-index # after editing supergateway.config.jsonc / codebase-index.config.json docker compose --profile embeddings down # stop everything incl. ollama (index data survives, it lives in the projects) ``` ## Adding or removing shared tools (port 8000) Edit `supergateway.config.jsonc` — each entry becomes `http://localhost:8000//mcp`. Any stdio MCP server works: ```jsonc "memory": { "stdio": "npx -y @modelcontextprotocol/server-memory" } ``` Then `docker compose restart mcp`. Servers started via `npx` need outbound network from the container on first use. ## How it works under the hood (curious readers) - One image (built from `Dockerfile`, `node:24-slim`) runs both services: [supergateway](https://github.com/supercorp-ai/supergateway) v4 plus the bundled MCP servers, and [open-codebase-index](https://github.com/Helweg/open-codebase-index). - Both gateways speak **Streamable HTTP** (the current MCP transport) with sessions, so any modern client can connect. - The index gateway mounts your `PROJECTS_ROOT` into the container **at the same path it has on the host** — so every file path the index reports is a real path you (and your AI tool) can open. - Each project's index server process is started **per client session** and dies when the client disconnects; the index data on disk persists, so reconnects are incremental. Registering 20 projects does not mean 20 idle processes. - Index data is stored per project in `.opencode/index/` — the same layout the native open-codebase-index uses — and is safe to delete at any time (it gets rebuilt). ## Troubleshooting ```bash curl http://localhost:8000/healthz # -> ok (shared tools gateway) curl http://localhost:8001/healthz # -> ok (index gateway) docker compose ps # both services "Up" ``` - **`opencode mcp list` says "No MCP servers configured" right after `opencode reload`** — the background service reconnects asynchronously; wait 2–3 seconds and run it again. - **Project lives outside `PROJECTS_ROOT`** — create a `docker-compose.override.yml` next to `docker-compose.yml` in this repo, mounting the extra root at its own path: ```yaml services: mcp-codebase-index: volumes: - /opt/code:/opt/code ``` then run `docker compose up -d mcp-codebase-index` and register the project with `./add-project.sh /opt/code/whatever`. - **Docker build fails with `Temporary failure resolving ...`** — the default docker bridge has no internet access on that host. Add `BUILD_NETWORK=host` to `.env` and rebuild. Note that recent `docker compose` versions delegate builds to buildx bake, which refuses `network: host` builds with "Pass `--allow=network.host`..." unless the host really needs it — on machines with working bridge networking leave `BUILD_NETWORK` unset (or set it to `default`). If it still fails, check your firewall. - **Indexing logs `INDEXING WARNING: N chunks failed to embed` (no semantic scores)** — the ollama embeddings container isn't running or the model isn't pulled yet. Fix with `docker compose --profile embeddings up -d` + `docker compose exec ollama ollama pull nomic-embed-text`, then re-run the index. Search still works meanwhile, just keyword-ranked (see "What the code index actually searches"). - **`brave-search` tools fail** — missing/invalid `BRAVE_API_KEY` in `.env`. - **Index seems stale** — re-run the project's index: `./add-project.sh ~/src/git/my-project` (safe to repeat), or ask the AI to run `index_codebase`. - **Index data owned by root / permission errors** — the index container runs as uid 1000. If your user has a different uid, set `user: ":"` on the `mcp-codebase-index` service in `docker-compose.yml`.