Skip to main content
Glama
cheezcake

Nexus MCP

by cheezcake
README.md
# Nexus MCP

Knowledge-graph task operating system, exposed as an MCP server for Claude Code.

Nexus maintains a temporal knowledge graph (nodes, directed edges, FTS5 search, recursive CTE traversal) in SQLite. This package provides:

1. **MCP server** (stdio) -- 24 tools for graph CRUD, view management, search, and traversal
2. **Graph context hook** -- Claude Code UserPromptSubmit hook that auto-resolves entity references in your prompts
3. **Web UI** (optional) -- Quart companion app for browsing the graph, D3 visualization, and desktop-metaphor view management

## Quick start

```bash
cd ~/Dropbox/code/python/nexus_mcp

# Create venv (check architecture first)
python3 -m venv venv          # x86_64 (niviniel)
# python3 -m venv venv_laptop # arm64 (nivinielicus)

# Install
venv/bin/pip install -e .
```

## Installation

### 1. MCP server

Add to your Claude Code settings (`~/.claude/settings.json` or project `.claude/settings.json`):

```json
{
  "mcpServers": {
    "nexus-mcp": {
      "type": "stdio",
      "command": "/Users/cheezcake/Dropbox/code/python/nexus_mcp/venv/bin/python",
      "args": ["-m", "nexus_mcp"]
    }
  }
}
```

For arm64 (nivinielicus), replace `venv` with `venv_laptop`.

**Environment variables** (optional):

| Variable | Default | Description |
|----------|---------|-------------|
| `NEXUS_DB_PATH` | `../nexus.db` (relative to package) | Path to the SQLite database |
| `NEXUS_WEB_URL` | `http://localhost:6969` | Quart web UI URL for HTTP notify |

### 2. Graph context hook

Add to your Claude Code settings alongside the MCP server:

```json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "/Users/cheezcake/Dropbox/code/python/nexus_mcp/venv/bin/python -m nexus_mcp.hook"
          }
        ]
      }
    ]
  }
}
```

For arm64 (nivinielicus), replace `venv` with `venv_laptop`.

The hook runs on every prompt (<500ms). It reads `nexus.db` in read-only mode (SQLite WAL concurrent reader). When your prompt mentions graph entities, a `<graph-context>` block is injected with matched nodes and their 1-hop neighborhoods.

**Reference syntax** the hook recognizes:

| Pattern | Example | Resolution |
|---------|---------|------------|
| `#concept` | `#SIP Registration` | FTS5 search, type=concept |
| `@person` | `@Kevin` | FTS5 search, type=person |
| `!ticket` | `!CT-931` | external_id lookup, type=ticket |
| `$system` | `$PIAB` | FTS5 search, type=system |
| `~document` | `~PRD` | FTS5 search, type=document |
| `XX-NNN` | `CT-931`, `AR-990` | Ticket ID regex, general search |
| keywords | `CTFaxSource` | 4+ char non-stopword tokens |

### 3. Stop hook (conscience)

A `Stop` hook that keeps the graph in sync with your work. After each turn it reads the current turn from the transcript and **blocks the stop** when the turn persisted something graph-worthy — a Total Recall `memory_set`, or a ticket ID with no node — while making **zero** Nexus writes (an entity was touched but never wired). The block names exactly what it caught and asks you to create/update the node(s) and wire edges, or dismiss in one line.

Add alongside the graph-context hook (same settings file):

```json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/Users/cheezcake/Dropbox/code/python/nexus_mcp/venv/bin/python -m nexus_mcp.hook_stop"
          }
        ]
      }
    ]
  }
}
```

For arm64 (nivinielicus), replace `venv` with `venv_laptop`.

It blocks **at most once per turn** (honors the `stop_hook_active` loop guard, so it can never loop), reads `nexus.db` read-only, and tails only the last ~1500 transcript lines so it stays fast. A false positive — a prose-only `memory_set`, or a passing ticket mention — is dismissable in a single line.

| Aspect | Detail |
|--------|--------|
| **Triggers** | a `memory_set` tool call, or a `CT-NNN`/`AR-NNN`-style ticket ID in the turn text with no matching `ticket` node |
| **Suppressed by** | any `nexus_node_create*` / `nexus_node_update` / `nexus_edge_create` in the same turn |
| **Disable** | `NEXUS_STOP_HOOK=0` |
| **SubagentStop** | `--subagent` flag changes only the wording; wiring to subagents is optional, not recommended by default |

### 4. Web UI (optional)

The Quart web app provides a browser-based companion for browsing the graph. It reads/writes the same `nexus.db`.

```bash
./run.sh
# Starts on http://localhost:6969
```

The MCP server pushes real-time events to the web UI (view opens, label changes, etc.) via HTTP notify. The web UI does not need to be running for the MCP server to work.

## Tools

### Graph operations (11 tools)

| Tool | Description |
|------|-------------|
| `nexus_search` | Full-text search across node labels and properties |
| `nexus_traverse` | Bidirectional recursive walk (N hops, type/relation/temporal filters) |
| `nexus_get_edges` | Edges connected to a node (direction + relation filter) |
| `nexus_node_read` | Read a node by ULID |
| `nexus_node_create` | Create a node with duplicate detection |
| `nexus_node_create_linked` | Atomic create + link to existing node |
| `nexus_node_update` | Merge properties, optionally rename |
| `nexus_node_archive` | Soft delete with cascading edge expiry |
| `nexus_edge_create` | Create directed temporal edge |
| `nexus_edge_expire` | Logical edge delete (set valid_to) |
| `nexus_query_temporal` | Point-in-time subgraph snapshot |

### View operations (10 tools)

| Tool | Description |
|------|-------------|
| `nexus_view_open` | Create materialized view from query, open on desktop |
| `nexus_view_list` | List open views with cursor state |
| `nexus_view_read` | Read view contents |
| `nexus_view_iterate` | Advance keyset cursor, return node + 1-hop subgraph |
| `nexus_view_iterate_reset` | Reset cursor to beginning |
| `nexus_view_filter` | Derived child view with additional filters |
| `nexus_view_annotate` | Write findings to view annotations |
| `nexus_view_sort` | Change sort order (created_at, updated_at, label, id) |
| `nexus_view_snapshot` | Freeze membership (immune to cascading archive) |
| `nexus_view_close` | Archive view and close UI window |

### Context tools (3 tools)

| Tool | Description |
|------|-------------|
| `nexus_task_context` | 2-hop subgraph around a task (nodes + edges + summary) |
| `nexus_property_conventions` | Live property-key-per-type usage from graph |
| `nexus_well_known_nodes` | Nodes with `tr_keys` property (TR prefetch guidance) |

## Query language

Views accept a sigil-based query language:

```
type:task status:active              # property filter
#SIP Registration                    # concept by label
@Kevin                               # person by label
!CT-931                              # ticket by external_id
$PIAB                                # system by label
type:ticket from:2026-03-01          # temporal filter
01KM4HZ3DFJC1F3RVVRB3KTA3N          # ULID traversal
```

## CLAUDE.md integration

Add the following to your CLAUDE.md (project or global) for Nexus-aware sessions:

```markdown
## Nexus Knowledge Graph

You have access to a knowledge graph via nexus-mcp tools (mcp__nexus-mcp__*).

**Graph tools**: nexus_search, nexus_traverse, nexus_get_edges, nexus_node_read,
nexus_node_create, nexus_node_create_linked, nexus_node_update, nexus_node_archive,
nexus_edge_create, nexus_edge_expire, nexus_query_temporal

**View tools**: nexus_view_open, nexus_view_list, nexus_view_read, nexus_view_iterate,
nexus_view_iterate_reset, nexus_view_filter, nexus_view_annotate, nexus_view_sort,
nexus_view_snapshot, nexus_view_close

**Context tools**: nexus_task_context (re-orient on a task), nexus_property_conventions
(learn existing property keys), nexus_well_known_nodes (systems with TR documentation)

### Workflow discipline

- **Search before creating** -- always nexus_search before nexus_node_create to avoid duplicates.
- **Be opportunistic** -- when you encounter entities (people, systems, tickets, concepts),
  create or update nodes. When you see relationships, wire edges. The graph should grow
  richer with every conversation.
- **Well-known nodes** -- if a graph entity has `tr_keys` in its properties, fetch those
  Total Recall keys via memory_get before acting on that system. Do not guess paths or
  commands for these systems.
- **After compaction** -- call nexus_task_context with your current task ID to re-orient
  against the graph. The graph reflects all mutations you've made and survives compaction.

### Node types

task, ticket, concept, person, system, document

### Edge relations

relates_to, blocks, caused_by, assigned_to, references, tagged_with,
escalated_to, part_of, message, spawned, contains, has_window, derived_from

### Attribution

NEVER use Claude, Anthropic, or AI attribution in any public-facing operation --
git commits, Jira comments, Confluence edits, emails, or any external system.
```

## Architecture

```
Claude Code session (subscription)
  |
  +-- nexus-mcp (stdio, FastMCP)
  |     +-- GraphEngine (SQLite WAL)
  |     +-- ViewEngine
  |     +-- HTTP notify --> Quart web UI
  |
  +-- nexus.hook (UserPromptSubmit)
  |     +-- reads nexus.db (read-only)
  |     +-- injects <graph-context>
  |
  +-- nexus.hook_stop (Stop)
        +-- reads nexus.db (read-only)
        +-- blocks the stop if an entity was touched but not graphed

Quart web UI (:6969, optional)
  +-- reads/writes nexus.db
  +-- receives POST /notify from MCP
  +-- pushes SSE to browser
```

## Database

`nexus.db` -- SQLite with WAL mode, FTS5, JSON1 extension.

Multiple concurrent readers (MCP server, web UI, hook) are safe under WAL. Writes serialize through SQLite's WAL lock. The MCP server runs a WAL checkpoint on shutdown.

## Keyboard shortcuts (web UI)

| Shortcut | Action |
|----------|--------|
| `Ctrl+N` | New task |
| `Ctrl+K` | Focus search bar |
| `Ctrl+G` | Open graph explorer |
| `Ctrl+T` | Open timeline view |
| `Ctrl+Shift+T` | Tile all windows |
| `Escape` | Close topmost window |

Maintenance

ActivitySlowing
ResponsivenessSyncing