12 KiB
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/<project>/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, library docs via context7, web search via brave (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. It lets the AI find things in a large codebase quickly (keyword search, "where is this defined", "who calls this") instead of reading files one by one. Every project gets its own address:http://localhost:8001/<project>/mcp.
Quick start (once per machine)
git clone <this repo> && 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:
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:
./add-project.sh ~/src/git/my-project
That single command:
- remembers the project in
projects.env(the list of indexed repositories), - gives it its own address:
http://localhost:8001/my-project/mcp, - builds its search index (stored inside the project at
.opencode/index/— disposable, rebuilt anytime, gitignored), - 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),
- 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.
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:
{
"$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:
{
"$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 |
{
"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
By default the index runs in structural mode: keyword (BM25) search, symbol definitions, call graphs, branch awareness — entirely offline, no AI provider needed. This is usually plenty and it works out of the box.
To add semantic search ("find code by meaning"), plug in an embedding provider:
-
Start local Ollama embeddings:
docker compose --profile embeddings up -d ollama, thendocker compose exec ollama ollama pull nomic-embed-text. -
In
codebase-index.config.jsonswitch"mode": "structural"to"hybrid"and add:{ "embeddingProvider": "custom", "customProvider": { "baseUrl": "http://ollama:11434/v1", "model": "nomic-embed-text", "dimensions": 768 } } -
Rebuild each project's index:
docker compose exec mcp-codebase-index ocbi index --project ~/src/git/my-project --config /etc/codebase-index/config.json --force
(Or ask the AI to run the index_codebase / index_status tools — rebuilding and
refreshing indexes is something the assistants can do themselves through MCP.)
Everyday commands
docker compose ps # status
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 down # stop everything (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/<name>/mcp. Any stdio MCP server works:
"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 v4 plus the bundled MCP servers, and 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_ROOTinto 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
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 listsays "No MCP servers configured" right afteropencode reload— the background service reconnects asynchronously; wait 2–3 seconds and run it again. -
Project lives outside
PROJECTS_ROOT— create adocker-compose.override.ymlnext todocker-compose.ymlin this repo, mounting the extra root at its own path:services: mcp-codebase-index: volumes: - /opt/code:/opt/codethen run
docker compose up -d mcp-codebase-indexand register the project with./add-project.sh /opt/code/whatever. -
Docker build fails with
Temporary failure resolving ...— on this host the default docker bridge has no internet access; builds already run withnetwork: hostin the compose file. If it still fails, check your firewall. -
brave-searchtools fail — missing/invalidBRAVE_API_KEYin.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 runindex_codebase. -
Index data owned by root / permission errors — the index container runs as uid 1000. If your user has a different uid, set
user: "<uid>:<gid>"on themcp-codebase-indexservice indocker-compose.yml.