# logosdb-mcp-server

MCP server that exposes [LogosDB](https://github.com/jose-compu/logosdb) semantic search to
**Claude Code**, **Google Antigravity**, and any other [Model Context Protocol](https://modelcontextprotocol.io/) client.

## Install

```bash
npm install -g logosdb-mcp-server
```

Or run without installing via `npx -y logosdb-mcp-server`.

## Claude Code: complete recipe

These steps assume your **Claude Code project root** is the directory where you want `./.logosdb` (or your chosen `LOGOSDB_PATH`) to live. The MCP child process **`cwd`** is normally that same folder, so relative paths in `.claude/mcp.json` resolve there.

### 1. Prerequisites

- **Node.js** 18+ (`node -v`), **`npm`**, and for the recipes below **`npx`** on `PATH`.
- **Claude Code** installed and authenticated.
- Add **`.logosdb`** (or whatever you set in `LOGOSDB_PATH`) to **`.gitignore`** if you do not want vector data in version control.

### 2. Install the package (pick one)

**Published server (typical):** no project `package.json` is required if Claude Code will run `npx` for you. Optional registry check:

```bash
npm view logosdb-mcp-server version
```

Alternatively add a dependency:

```bash
npm install logosdb-mcp-server
```

**Develop from a LogosDB git clone:** from the monorepo root run `npm install` so `mcp/dist/index.js` is built; then point `mcp.json` at that file with `node` (see the main [README — Claude Code recipe](https://github.com/jose-compu/logosdb/blob/main/README.md#claude-code-complete-recipe)).

### 3. Create `.claude/mcp.json`

In your **project root**, create `.claude/mcp.json` (or merge into **`~/.claude.json`** for all projects) with a `logosdb` entry.

**Recommended (always run the published server):**

```json
{
  "mcpServers": {
    "logosdb": {
      "command": "npx",
      "args": ["-y", "logosdb-mcp-server"],
      "env": {
        "LOGOSDB_PATH": "./.logosdb"
      }
    }
  }
}
```

**Project-local install (pin a version in `package.json`):** same `mcpServers` block, or point `node` at `./node_modules/logosdb-mcp-server/dist/index.js` with empty `args` — see [Configure](#configure).

**Optional env:** Ollama, OpenAI, Voyage, chunk size, and index root are in [Configure](#configure) and [Environment variables](#environment-variables).

### 4. Path confinement for indexing

`logosdb_index_file` only accepts paths under **`process.cwd()`** or **`LOGOSDB_INDEX_ROOT`**. Read [Path confinement (`logosdb_index_file`)](#path-confinement-logosdb_index_file) before indexing parent directories or using symlinks.

### 5. Restart and verify

- Restart **Claude Code** or reload MCP after editing JSON.
- Ask the agent to run **`logosdb_list`**. If the server fails to spawn, run the same `command` + `args` in a shell from your project root and fix errors (missing Node, network for `npx`, etc.).

### 6. Use it

- Ask in natural language to index `src/` (or a file), search semantically, or store a short decision via **`logosdb_index`**.
- **Slash commands:** copy [`.claude/commands/`](https://github.com/jose-compu/logosdb/tree/main/.claude/commands) from the LogosDB repo into your project for `/index`, `/search`, `/forget` (optional).
- **Agent habits:** add a `CLAUDE.md` snippet so the agent indexes and searches without reminders — see the main [README — Agent instructions](https://github.com/jose-compu/logosdb/blob/main/README.md#agent-instructions-claudemd-and-similar).

### 7. Troubleshooting

| Issue | Action |
|-------|--------|
| Native `logosdb` fails (`Could not locate the bindings file`, install exits 1) | Use **`logosdb` ≥ 1.0.0** (N-API prebuild downloader + vendored C++ fallback). From a terminal: `npm view logosdb version` then reinstall; if prebuilds are missing, ensure Python + C++17 toolchain so `node-gyp rebuild` can run. |
| `npx` cannot download or run the package | Check network, Node version, and corporate proxy; try `npm install logosdb-mcp-server` + `node ./node_modules/logosdb-mcp-server/dist/index.js` in `args` via `node` command. |
| Wrong embedding size / garbage search | Use one backend and dimension per namespace; use a fresh `LOGOSDB_PATH` or new namespace when changing models ([Environment variables](#environment-variables)). |
| Path rejected on index | Stay inside cwd or set `LOGOSDB_INDEX_ROOT` to an absolute allowed directory. |

## Configure

### Default: local embeddings (Transformers.js)

If you omit `EMBEDDING_PROVIDER`, the server uses **[Transformers.js](https://github.com/xenova/transformers.js)** (`@xenova/transformers`) with a small on-device model. **No API keys.** The first run may download weights (cache under the standard Transformers.js cache directory).

Add to `.claude/mcp.json` in your project (or `~/.claude.json` for global use):

```json
{
  "mcpServers": {
    "logosdb": {
      "command": "npx",
      "args": ["-y", "logosdb-mcp-server"],
      "env": {
        "LOGOSDB_PATH": "./.logosdb"
      }
    }
  }
}
```

Default model: `Xenova/all-MiniLM-L6-v2` (384 dimensions). Override:

```json
"env": {
  "LOGOSDB_PATH": "./.logosdb",
  "TRANSFORMERS_MODEL": "Xenova/all-MiniLM-L6-v2",
  "TRANSFORMERS_EMBEDDING_DIM": "384"
}
```

If you switch models, set `TRANSFORMERS_EMBEDDING_DIM` / `EMBEDDING_DIM` to the **exact** output size of that model.

### Path confinement (`logosdb_index_file`)

Indexing resolves paths with `realpath` and only allows files under **`process.cwd()`** or, if set, **`LOGOSDB_INDEX_ROOT`** (absolute directory). Symlinks that escape those roots are rejected. Optional: set **`EMBEDDING_FETCH_TIMEOUT_MS`** (milliseconds, capped at 600000) for Ollama/OpenAI/Voyage HTTP calls; default is 120000.

### `.gitignore`-aware walking (0.9.1)

When `logosdb_index_file` walks a directory that lives inside a Git working tree, it honours `.gitignore` rules **in addition to** the legacy `SKIP_DIRS` / hidden / extension filters. Sources, in Git order:

- the working tree's root **`.gitignore`**,
- every **nested `.gitignore`** discovered during the walk (per-directory scope),
- **`.git/info/exclude`**,
- the **global excludes file** at `$XDG_CONFIG_HOME/git/ignore` (or `~/.config/git/ignore`). Reading `core.excludesFile` from `~/.gitconfig` is **not** parsed; symlink that file into `~/.config/git/ignore` if you need it.

Pattern semantics (leading `/`, trailing `/`, `**`, `!pattern` negations) match Git via the [`ignore`](https://www.npmjs.com/package/ignore) npm package.

**Default:** on when a `.git/` dir is found by walking up from the indexed path (and staying inside `process.cwd()` / `LOGOSDB_INDEX_ROOT`). **No-op** when no Git working tree is detected — non-Git projects see no change.

**Disable** in either of:

- per call: `logosdb_index_file({ path, namespace, respect_gitignore: false })`
- globally: `LOGOSDB_RESPECT_GITIGNORE=0` (`false`, `no`, `off` all accepted)

The tool response echoes `respect_gitignore: <bool>` so the agent can confirm what was applied.

### Local HTTP: Ollama

Run [Ollama](https://ollama.com) with an embedding model (e.g. `nomic-embed-text`), then:

```json
"env": {
  "LOGOSDB_PATH": "./.logosdb",
  "EMBEDDING_PROVIDER": "ollama",
  "OLLAMA_HOST": "http://127.0.0.1:11434",
  "OLLAMA_EMBED_MODEL": "nomic-embed-text",
  "OLLAMA_EMBEDDING_DIM": "768"
}
```

### Cloud (opt-in): OpenAI

```json
"env": {
  "LOGOSDB_PATH": "./.logosdb",
  "EMBEDDING_PROVIDER": "openai",
  "OPENAI_API_KEY": "<your-openai-api-key>"
}
```

### Cloud (opt-in): Voyage AI (dim=1024)

```json
"env": {
  "LOGOSDB_PATH": "./.logosdb",
  "EMBEDDING_PROVIDER": "voyage",
  "VOYAGE_API_KEY": "<your-voyage-api-key>"
}
```

## Google Antigravity

[Google Antigravity](https://codelabs.developers.google.com/google-workspace-mcp-antigravity) is an agentic IDE stack that can load **MCP servers** over **stdio** — the same pattern as Claude Code. **`logosdb-mcp-server`** is published as [`logosdb-mcp-server`](https://www.npmjs.com/package/logosdb-mcp-server) on npm and speaks standard MCP tools (`logosdb_index`, `logosdb_index_file`, `logosdb_search`, `logosdb_list`, `logosdb_info`, `logosdb_delete`). See the [MCP specification](https://modelcontextprotocol.io).

### Where to configure

Exact menu labels change between Antigravity builds. In general:

1. Open the **Agent** (or AI) panel.
2. Use **Manage MCP servers**, **MCP settings**, or **View raw config** (wording may differ).
3. Add a stdio server whose **command** is `npx` and **args** include `-y` and `logosdb-mcp-server`, with **environment variables** for `LOGOSDB_PATH` and embedding options.

If Antigravity exposes a raw JSON file (`mcpServers` or project-level MCP config), use the same JSON shape as below. Confirm the file path in your Antigravity version if the agent cannot start the server.

**Requirements:** [Node.js](https://nodejs.org/) on your `PATH` (for `npx`) and a writable `LOGOSDB_PATH` directory.

### Recommended: local embeddings (no API keys)

Matches the default elsewhere in this README — no `OPENAI_API_KEY` required:

```json
{
  "mcpServers": {
    "logosdb": {
      "command": "npx",
      "args": ["-y", "logosdb-mcp-server"],
      "env": {
        "LOGOSDB_PATH": "./.logosdb"
      }
    }
  }
}
```

### Optional: cloud embeddings

**OpenAI:**

```json
{
  "mcpServers": {
    "logosdb": {
      "command": "npx",
      "args": ["-y", "logosdb-mcp-server"],
      "env": {
        "LOGOSDB_PATH": "./.logosdb",
        "EMBEDDING_PROVIDER": "openai",
        "OPENAI_API_KEY": "<your-openai-api-key>"
      }
    }
  }
}
```

**Voyage AI** (`EMBEDDING_PROVIDER=voyage`, `VOYAGE_API_KEY`): see the [Configure](#configure) section above.

**Ollama** (local HTTP): set `EMBEDDING_PROVIDER=ollama` and the `OLLAMA_*` variables from [Local HTTP: Ollama](#local-http-ollama).

### Further reading

- Google codelab — [Google Workspace MCP servers in Antigravity](https://codelabs.developers.google.com/google-workspace-mcp-antigravity)
- Community walkthrough — [How to use MCP servers in Antigravity](https://antigravity.codes/blog/antigravity-mcp-tutorial)

## Slash commands

Three custom slash commands are included in `.claude/commands/` and are available as soon as you open Claude Code in this project:

| Command | Example |
|---|---|
| `/index` | `/index ./src --namespace=backend` |
| `/search` | `/search "JWT validation" --namespace=backend` |
| `/forget` | `/forget --namespace=backend --id=42` |

## Example session

Once configured, start Claude Code in your project:

```
$ cd myproject && claude

> /index ./src --namespace=backend
Indexed 42 files into 'backend' collection

> Find where we handle JWT validation
Searching... Found 3 matches:
  1. src/auth/jwt.ts (score: 0.94)
  2. src/middleware/auth.ts (score: 0.87)
  3. src/utils/token.ts (score: 0.72)

> Show me the first one
[Claude displays src/auth/jwt.ts with explanation]

---

> Index the src/ directory so you can search it semantically

● logosdb_index_file({ path: "./src/auth/jwt.ts", namespace: "code" })
  ✓ { indexed: 4, namespace: "code", file: "./src/auth/jwt.ts" }
● logosdb_index_file({ path: "./src/middleware/auth.ts", namespace: "code" })
  ✓ { indexed: 2, namespace: "code", file: "./src/middleware/auth.ts" }
  … (42 files indexed)

I've indexed the src/ directory into the "code" namespace — 42 files, 187 chunks.

> Find where we validate JWT tokens

● logosdb_search({ query: "JWT token validation", namespace: "code", top_k: 5 })
  ✓ {
      results: [
        { score: 0.9431, file: "src/auth/jwt.ts",         text: "export function verifyToken(token) {…" },
        { score: 0.8712, file: "src/middleware/auth.ts",   text: "const decoded = jwt.verify(req.headers…" },
        { score: 0.8204, file: "src/utils/token.ts",       text: "function decodePayload(token: string)…"  },
      ]
    }

The JWT validation logic lives in three places:
1. **src/auth/jwt.ts** — `verifyToken()` is the main entry point (score 0.94)
2. **src/middleware/auth.ts** — Express middleware that calls `verifyToken` on every request
3. **src/utils/token.ts** — low-level payload decoder used by both

> Remember that we decided to use RS256 instead of HS256 going forward

● logosdb_index({ text: "Decision: use RS256 (asymmetric) instead of HS256 for JWT signing. Decided 2026-05-02.", namespace: "decisions" })
  ✓ { id: 0, indexed: true, namespace: "decisions" }

Noted. I'll keep that in the "decisions" namespace for future sessions.
```

## Environment variables

| Variable | Default | Description |
|---|---|---|
| `LOGOSDB_PATH` | `./.logosdb` | Root directory for all namespace databases |
| `EMBEDDING_PROVIDER` | *(unset → local)* | `transformers` / `local` / `auto` (default), `ollama`, `openai`, `voyage` |
| `TRANSFORMERS_MODEL` | `Xenova/all-MiniLM-L6-v2` | Hugging Face id for Transformers.js |
| `TRANSFORMERS_EMBEDDING_DIM` | `384` | Must match model output (or set `EMBEDDING_DIM`) |
| `EMBEDDING_DIM` | — | Optional global override for expected vector length |
| `OLLAMA_HOST` | `http://127.0.0.1:11434` | When `EMBEDDING_PROVIDER=ollama` |
| `OLLAMA_EMBED_MODEL` | `nomic-embed-text` | Ollama embedding model name |
| `OLLAMA_EMBEDDING_DIM` | `768` | Must match the model’s embedding size |
| `OPENAI_API_KEY` | — | Required when `EMBEDDING_PROVIDER=openai` |
| `VOYAGE_API_KEY` | — | Required when `EMBEDDING_PROVIDER=voyage` |
| `LOGOSDB_CHUNK_SIZE` | `800` | Target characters per chunk for `"legacy"` and `"section"` modes (ignored in `"line"` mode) |
| `LOGOSDB_CHUNK_MODE` | `auto` | Default chunking strategy: `auto` \| `line` \| `section` \| `legacy`. See [Chunking strategies](#chunking-strategies). |
| `LOGOSDB_RESPECT_GITIGNORE` | `1` (auto-on inside a Git working tree) | `0` / `false` / `no` / `off` disables `.gitignore`-aware filtering globally (0.9.1+) |

**Important:** Use one embedding backend consistently for a given namespace on disk. Mixing dimensions or models on the same `LOGOSDB_PATH` namespace will produce invalid search results.

## Tools

| Tool | Inputs | Description |
|---|---|---|
| `logosdb_index` | `text`, `namespace`, `metadata?` | Embed and store a text snippet |
| `logosdb_index_file` | `path`, `namespace`, `chunk_size?`, `chunking?`, `incremental?`, `respect_gitignore?` | Chunk, embed, and store a file or tree; **`incremental: true`** skips unchanged files, replaces chunks for changed files, and prunes removed files under a directory (state in `LOGOSDB_PATH/_logosdb_mcp_manifests/`). When the path lives inside a Git working tree, `.gitignore` rules are applied by default (override per call with `respect_gitignore: false` or globally with `LOGOSDB_RESPECT_GITIGNORE=0`; see [`.gitignore`-aware walking](#gitignore-aware-walking-091)). `chunking` selects the splitting strategy — see [Chunking strategies](#chunking-strategies). |
| `logosdb_search` | `query`, `namespace`, `top_k?`, `ts_from?`, `ts_to?`, `candidate_k?` | Semantic search; optional inclusive ISO 8601 time window (maps to `search_ts_range`) |
| `logosdb_list` | — | List all namespaces |
| `logosdb_info` | `namespace` | Stats: count, live count, dimension |
| `logosdb_delete` | `namespace`, `id?` **or** `query?`, `search_top_k?`, `match_rank?` | Delete by row id, or embed `query` and delete the `match_rank` hit (default 0) among `search_top_k` neighbors |

Timestamp-filtered search matches the core library: when `ts_from` and/or `ts_to` are set, results are drawn from that window; `candidate_k` defaults to `10 × top_k` if omitted.

## Chunking strategies

`logosdb_index_file` splits each file into chunks before embedding. The strategy is chosen automatically per file type (`"auto"`, the default) or can be forced per-call or globally.

| Mode | Files (auto) | How it splits | When to use |
|------|-------------|---------------|-------------|
| `auto` | — | Delegates to `line`, `section`, or `legacy` based on extension | Default — best choice for mixed repositories |
| `line` | `.ts` `.tsx` `.js` `.jsx` `.mjs` `.cjs` `.py` `.go` `.rs` `.java` `.c` `.cpp` `.h` `.cs` `.rb` `.php` `.swift` `.kt` `.scala` `.sh` `.sql` | Sliding **~50-line windows**, **~10-line overlap**. Prefers blank-line boundaries at window edges to avoid cutting mid-function. | Code; improves recall on function/class-level queries |
| `section` | `.md` `.rst` | **Heading-aware**: splits on ATX (`# … ######`) and Setext (`=== / ---`) boundaries. Small consecutive sections are merged up to `chunk_size`. Large sections are sub-split with the paragraph chunker. | Documentation, READMEs, RST manuals |
| `legacy` | `.json` `.yaml` `.toml` `.txt` `.cfg` `.ini` `.graphql` `.proto` and anything else | **Paragraph / character merge**: splits on blank lines, merges short paragraphs up to `chunk_size` (default 800 chars), carries `overlapChars` (100 chars) into the next chunk. Long single-paragraph blocks are sub-split by character window. | Config files, plain text, and **compatibility with namespaces indexed before the smart chunker was introduced** |

### Selecting a strategy

**Per call** — pass `chunking` to `logosdb_index_file`:

```
logosdb_index_file(path="src/", namespace="code", chunking="line")
logosdb_index_file(path="docs/", namespace="docs", chunking="section")
logosdb_index_file(path="configs/", namespace="cfg", chunking="legacy")
```

**Globally** — set `LOGOSDB_CHUNK_MODE` in the MCP server env:

```json
"env": {
  "LOGOSDB_PATH": "./.logosdb",
  "LOGOSDB_CHUNK_MODE": "legacy"
}
```

Valid values: `auto` (default), `line`, `section`, `legacy`.

### Using `"legacy"` mode for compatibility

If you have an **existing namespace** that was indexed before the smart chunker was introduced (pre-1.0.0 or with `LOGOSDB_CHUNK_MODE` unset on an older server), its chunks were produced by the legacy paragraph/char chunker. Continuing to use `"legacy"` for that namespace keeps results consistent — or reindex it fresh with `incremental: false` to switch strategies.

The `chunking` setting is part of the **incremental cache fingerprint**: changing the mode on an existing incremental namespace causes all previously-indexed files to be re-chunked automatically on the next run.

### `chunk_size` and overlap

`chunk_size` controls the target character count for the `"legacy"` and `"section"` modes (default `800`; env `LOGOSDB_CHUNK_SIZE`). It is ignored in `"line"` mode (which uses `linesPerChunk` / `lineOverlap`, not exposed as tool parameters but configurable programmatically via the `ChunkOptions` API).

## Development

```bash
npm install --ignore-scripts   # skip native addon build (use linked logosdb or published wheel)
npm run build                  # tsc → dist/
npm test                       # unit tests + MCP stdio smoke (initialize + tools/list; catches startup crashes)
npm start                      # run server directly
```

## License

MIT
