Skip to main content
This guide answers three questions, in order:
  1. Which setup do I want? — local vs remote, SQLite vs OpenSearch
  2. How do I run it — local, remote without Docker, remote with Docker
  3. How do I get the MCP details into my agent? — the part that is easy to get wrong
There is a second, unrelated use of “MCP” in Open Index — connectors, which pull data from someone else’s MCP server into your brain. That’s Connectors.

1. Which setup do I want?

Two independent choices. Pick one from each column. Where does the brain run? Which search backend? Rule of thumb: local + SQLite to start. Move to remote + OpenSearch when a second writer appears — that’s the line SQLite can’t cross, not entity count.
OpenSearch is only set up for you in the Docker path. Running it outside Docker means operating a cluster yourself; Open Index will happily connect to one (config below), but this repo only ships a ready-made cluster in docker-compose.yml.
Switching backends does not require editing brain.yaml — set OPEN_INDEX_SEARCH_BACKEND=sqlite|opensearch in the environment and it wins over the file. That’s what the compose profiles do.

2. Local brain (stdio)

Nothing to host. The agent starts the server itself over stdio.
open-index init already writes a .mcp.json into the brain directory, so if you open Claude Code in that folder, it connects automatically — no further setup. To connect from somewhere else (a different repo, Claude Desktop, Cursor), get the config block:
The path is absolutized deliberately: your agent’s working directory is usually not the brain directory, and a relative --brain . quietly opens the wrong place (or an empty one). See Connecting your agent for where this block goes.

3. Remote brain, without Docker

Use this when you have a VM and don’t want containers. The brain becomes an HTTP MCP endpoint that any agent can register by URL.

Install and run

serve prints exactly what to connect to:
0.0.0.0 is not an address. It’s the bind address — “listen on every interface”. It is never what you paste into an agent. That’s why the banner prints the reachable addresses separately.

The token is not optional

serve exposes put_entity and create_doc_type. Without a token, anyone who can reach the port can rewrite your brain. Either set OPEN_INDEX_TOKEN, or pass --read-only to drop the write tools:
A common shape is two endpoints: a read-only one on an open port, and an authenticated read+write one for the agents allowed to author.

Keep it running (systemd)

ExecStartPre matters: file-backed entities live in git, not in the index. After a git pull or a fresh machine, the index is empty until open-index index runs and the brain answers every query with nothing.

TLS / behind a proxy

serve speaks plain HTTP. For TLS, terminate at nginx/Caddy and forward to it. Streamable HTTP uses long-lived responses, so disable response buffering:
Then tell serve its public name so the banner and mcp-config print the URL agents should actually use, rather than the internal one:

OpenSearch without Docker

Point brain.yaml at your existing cluster. Secrets stay as ${ENV} refs, resolved when the connection is opened:
Or leave brain.yaml on SQLite and override per-environment:

4. Remote brain, with Docker

The shortest path to a shared brain, and the only path where OpenSearch is set up for you.
No external services:
Both serve http://localhost:8080/mcp. The only difference is OPEN_INDEX_SEARCH_BACKEND; your brain.yaml is identical either way, so you can switch by changing the profile and nothing else. Add the explorer UI on :8501 alongside either:

What the container does on start

The brain directory is mounted, not baked in (BRAIN_DIR:/brain) — doc_types and entities stay in your git repo. On start the entrypoint:
  1. fails fast with a clear message if /brain/brain.yaml isn’t there,
  2. waits for OpenSearch to report healthy when that backend is selected,
  3. runs open-index index so file-backed entities are loaded (skip with OPEN_INDEX_SKIP_INDEX=1),
  4. execs open-index serve.

Permissions on the mounted brain directory

The container runs as uid 10001 (not root), so a bind-mounted brain directory owned by your user is not writable by it — indexing fails on the first write. Give the container ownership and keep group access for yourself:
You can still read and edit the files; writes from inside the container land as uid 10001 with your group.

Many brains from one process

serve --brain <dir> runs one brain. For more than a handful, --brains <root> serves every brain under a directory from a single process, each at /<name>/mcp:
This matters because of what is not duplicated. A process per brain re-loads the Python runtime and a ~250MB resident embedding model each time, so a modest host tops out at a handful. In one process the model is loaded once and a brain costs only its config and doc_types. Measured on the bundled example brain, SQLite-backed: That is 1.8MB of marginal cost per brain, and 200 mount in ~4 seconds. Each brain keeps its own storage, its own read/write policy and its own token — OPEN_INDEX_TOKEN_<NAME> gates one brain (OPEN_INDEX_TOKEN_SALES_EU for sales-eu/), and --token covers any without one. Nothing is shared between brains except the process and the model. Two extras come with it: GET / lists every brain with its URL, entity count and doc_types, and GET /healthz is an unauthenticated probe for a load balancer.
Prefer SQLite here. With OpenSearch, every brain is a separate cluster index and therefore a shard; the working guidance is ~20 shards per GB of heap, so hundreds of brains would hit that ceiling long before RAM. SQLite gives each brain its own file and no shard cost at all. Use OpenSearch for the few brains that genuinely need concurrent writers or >10k entities.

Running one-off commands

--brain /brain is added for you.

Plain docker run, no compose

Notes for real deployments

  • Persistence. SQLite: brain.db is inside your mounted brain dir — back that up. OpenSearch: the opensearch-data named volume. Index-backed entities (storage: index) exist only there; they are not in git and are not recreated by open-index index. Back it up or be able to re-ingest.
  • The OpenSearch cluster here has security disabled (DISABLE_SECURITY_PLUGIN=true) and binds to loopback. That’s fine for a single host where only the brain container talks to it; enable the security plugin and set search.username/password before putting it on a shared network.
  • Behind a proxy, set OPEN_INDEX_PUBLIC_URL in .env so the printed connection details are the ones agents can actually use.

5. Connecting your agent

You need two things: the URL (remote) or brain path (local), and the token (remote only). mcp-config assembles both into the right block.
It writes to stdout, so it pipes straight where it belongs:

Where the block goes

A remote block looks like this:
Committing a token into .mcp.json puts it in git history. For a shared repo, prefer a per-developer user-scoped entry, or a read-only endpoint with no token for the committed config.

Check it works before blaming the agent

Once connected, the agent should call navigation_guidelines() first — it reports the doc_types, fields, and relationship vocabulary of your brain.

Troubleshooting