You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
chodak166 99578fe985 Make build network host-optional and enable hybrid code indexing with local ollama embeddings 2 days ago
.roo Modernize MCP stack: streamable HTTP, multi-project code index 5 days ago
.crush.json Modernize MCP stack: streamable HTTP, multi-project code index 5 days ago
.env.example Added BUILD_NETWORK env var for no docker egress hosts 5 days ago
.gitignore Modernize MCP stack: streamable HTTP, multi-project code index 5 days ago
AGENTS.md Make build network host-optional and enable hybrid code indexing with local ollama embeddings 2 days ago
Dockerfile Modernize MCP stack: streamable HTTP, multi-project code index 5 days ago
LICENSE Initial commit 2 years ago
README.md Make build network host-optional and enable hybrid code indexing with local ollama embeddings 2 days ago
add-project.sh Modernize MCP stack: streamable HTTP, multi-project code index 5 days ago
codebase-index.config.json Make build network host-optional and enable hybrid code indexing with local ollama embeddings 2 days ago
docker-compose.yml Make build network host-optional and enable hybrid code indexing with local ollama embeddings 2 days ago
opencode.json Modernize MCP stack: streamable HTTP, multi-project code index 5 days ago
supergateway.config.jsonc Modernize MCP stack: streamable HTTP, multi-project code index 5 days ago

README.md

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", 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/<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:

  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.

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

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 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):

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

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/<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_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

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:

    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: "<uid>:<gid>" on the mcp-codebase-index service in docker-compose.yml.