# @naumu/mcp

MCP server for [Naumu](https://naumu.ai) - access your knowledge graph from Claude Code, Cursor, and other AI agents.

Naumu speaks MCP over two transports:

1. **Remote, over OAuth** at `https://naumu.ai/api/mcp` - the recommended path. You sign in through your browser, and there is no API key to create, paste, or keep in sync. This package is not involved.
2. **Local stdio**, this package, launched on demand with `npx` and authenticated with an API key. Use it for clients that cannot do browser OAuth: CI jobs, bot and external-agent identities, and older stdio-only harnesses.

## Recommended setup: remote MCP over OAuth

Register the server once. The first time a session uses a Naumu tool, your browser opens to sign in and approve access. After that the tools are available in every session.

### Claude Code

```bash
claude mcp add --transport http --scope user naumu https://naumu.ai/api/mcp
```

`--scope user` registers the server for your whole machine rather than the current project, so you do not have to repeat it per repository.

### Cursor

```json
{
  "mcpServers": {
    "naumu": {
      "url": "https://naumu.ai/api/mcp"
    }
  }
}
```

Leave any OAuth client ID and secret fields blank. Naumu registers the client for you automatically during sign-in, using Dynamic Client Registration (DCR).

Other MCP-capable clients that support remote servers follow the same shape: point them at `https://naumu.ai/api/mcp` and let the browser handle sign-in. Use the front-end origin (`naumu.ai`), not a backend address - sign-in and consent rely on your Naumu session, which lives on the front-end origin.

You can revoke a connected tool's access at any time in Naumu under **Settings > Your agents**, in the **Active access** list.

See [Local MCP](https://naumu.ai/docs/local-mcp) for the full walkthrough.

## Fallback setup: local stdio server with an API key

Use this package when your client cannot do browser OAuth - CI, bots and external agent identities, or a harness that only speaks stdio.

### 1. Get an API key

1. Open **Settings > Your agents** in Naumu.
2. Select **Create an API key** and give it a recognizable name (such as the tool it is for).
3. Copy the key right away. It is shown only once and cannot be retrieved later. Keys start with `nmu_`.

Use a separate key per tool, so a leak only costs you that one key.

### 2. Configure your client

You do not install this package globally; your client launches it on demand with `npx`.

#### Claude Code

```bash
claude mcp add naumu -s user \
  -e NAUMU_API_KEY=nmu_your_key_here \
  -- npx -y -p @naumu/mcp naumu-mcp
```

#### Cursor

```json
{
  "mcpServers": {
    "naumu": {
      "command": "npx",
      "args": ["-y", "-p", "@naumu/mcp", "naumu-mcp"],
      "env": {
        "NAUMU_API_KEY": "nmu_your_key_here"
      }
    }
  }
}
```

### Environment variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `NAUMU_API_KEY` | Yes | - | Your API key (starts with `nmu_`) |
| `NAUMU_API_URL` | No | `https://naumu.ai` | Naumu API base URL. You do not need to set this |

## Available tools

Both transports expose the same tool surface. Tools marked **bot** are only registered for bot identity keys (`nmu_bot_*`), which additionally receive a filtered subset based on the identity's permissions.

### Spaces and members

| Tool | Description |
|------|-------------|
| `naumu_list_graphs` | List every space (graph) the key can access, with ids, names, roles, and `url` |
| `naumu_create_graph` | Create a new, empty space owned by the authenticated user; returns its `url` |
| `naumu_list_members` | List a space's members with their roles |
| `naumu_whoami` | Return who the calling key is (user with linked spaces, or bot identity with its graph and allowed tools) |

### Schema

| Tool | Description |
|------|-------------|
| `naumu_get_schema` | Get a space's schema (node types, attributes, connections) |
| `naumu_update_schema` | Replace the schema with a full new definition |
| `naumu_add_node_type` | Add one node type without resending the whole schema |
| `naumu_add_connection` | Add one connection from an existing node type to another |
| `naumu_add_attribute` | Add or extend an attribute on an existing node type |
| `naumu_update_schema_description` | Rewrite the space-wide schema description, or the description of one existing node type, attribute, or attribute value |
| `naumu_list_schema_violations` | Audit a space against its schema and report violations, each row with the node `url` |

### Reading the graph

| Tool | Description |
|------|-------------|
| `naumu_search` | Hybrid (meaning plus text) search over graph nodes; each hit carries `url` |
| `naumu_filter` | Filter nodes by type and attributes, with deterministic and complete results; each node carries `url` |
| `naumu_get_node` | Get one node with its properties, edges, notes, threads, and generated context, all linked (`url`, `targetUrl`, `sourceUrl`) |
| `naumu_list_node_connections` | List a single node's connections, each neighbour with `url` |
| `naumu_list_dense_nodes` | Find over-connected hub nodes by child count; each node carries `url` |
| `naumu_ask` | Ask @Naumu a question and get a synthesised, node-grounded answer with linked sources (`{ id, title, url }`) and confidence |

### Writing to the graph

| Tool | Description |
|------|-------------|
| `naumu_add_node` | Create 1-25 nodes in one call; returns each new node with `url` |
| `naumu_update_node` | Overwrite properties of an existing node; returns it with `url` |
| `naumu_remove_node` | Delete a node and all of its connections (destructive) |
| `naumu_add_edge` | Create 1-25 edges between existing nodes |
| `naumu_remove_edge` | Delete one edge identified by (source, target, label) |
| `naumu_remove_edges_bulk` | Delete 1-100 edges in one atomic call |
| `naumu_reparent` | Atomically swap a node's parent edge; returns `url` and `newParentUrl` |
| `naumu_batch_reparent` | Reparent 1-25 nodes onto a shared new parent; each row carries `url` |
| `naumu_delegate` | Hand @Naumu a task to carry out asynchronously; returns a `threadId` to poll and its `threadUrl` |

### Threads and topics

| Tool | Description |
|------|-------------|
| `naumu_list_threads` | List threads by last activity, with an `isParticipant` flag; each thread and topic carries `url` |
| `naumu_get_thread` | Fetch one thread (with `url`) plus its human and bot participant rosters |
| `naumu_read_thread` | Read a thread's messages; each carries `processing` or `complete` status and a `url` |
| `naumu_post_message` | Post a markdown message in a thread you participate in; returns its `url` |
| `naumu_create_thread` | **bot** Start a new conversation, attach participants, post a first message, optionally file into topics; returns its `url` |
| `naumu_add_reaction` | Add an emoji reaction to a message (idempotent) |
| `naumu_remove_reaction` | Remove your own reaction from a message |
| `naumu_typing` | Show or hide your "is typing" indicator in a thread |
| `naumu_list_topics` | List a space's topics (filing destinations) for use in `topicIds`, each with `url` |
| `naumu_create_topic` | Create a topic; admin-only, name must be a lowercase slug; returns its `url` |

### Notes

| Tool | Description |
|------|-------------|
| `naumu_create_note` | Create a note, optionally with its full markdown body in the same call; returns its `url` |
| `naumu_note_read` | Read a note's current markdown plus the nodes it is tied to, all with `url` |
| `naumu_note_append` | Append markdown blocks to the end of a note |
| `naumu_note_insert` | Insert markdown immediately after a named heading |
| `naumu_note_replace_section` | Replace the body under a named heading |
| `naumu_note_delete_section` | Delete a heading and its body (destructive) |
| `naumu_note_replace` | Replace an entire note's content (destructive) |
| `naumu_note_find_replace` | Literal find and replace within a note |
| `naumu_note_batch` | Apply several note edits in ONE write (one transaction, one update event); every note write returns the note `url` |

### Attachments

| Tool | Description |
|------|-------------|
| `naumu_request_attachment_upload` | Get a presigned S3 URL to upload a file, then reference it via `attachmentIds` |
| `naumu_get_attachment` | Read a chat attachment by `attachmentId` |

### Space admission

| Tool | Description |
|------|-------------|
| `naumu_admission_status` | Show who can auto-join a space and who is waiting for approval |
| `naumu_whitelist_members` | Whitelist emails so those people auto-join on sign-in |
| `naumu_resolve_admission` | Join (or request to join) the space a repo's `.naumu` file references |
| `naumu_resolve_join_request` | Approve or deny a pending join request |

### ChatGPT compatibility

The OpenAI MCP surface requires tools literally named `search` and `fetch`. These wrap cross-space search and node reads in the response shapes that client expects.

| Tool | Description |
|------|-------------|
| `search` | Search across every accessible space; returns `{ results: [{ id, title, url }] }` |
| `fetch` | Fetch a node returned by `search`; returns `{ id, title, text, url }` |

## Troubleshooting

When the stdio server is misconfigured it exits before the MCP handshake, and most clients simply show no server at all, with no error. Run the built-in self-check to find out why:

```bash
npx -y -p @naumu/mcp naumu-mcp doctor
```

It checks, in order, that `NAUMU_API_KEY` is set (the key itself is never printed), that the API is reachable and accepts the key, and that this build matches the latest published version. It prints one line per check plus a verdict, and exits non-zero if anything failed:

```
naumu-mcp doctor (v0.11.1, API https://naumu.ai)

[fail] API key: NAUMU_API_KEY is missing from the environment
[warn] API: reachable (https://naumu.ai answered HTTP 403), but there is no NAUMU_API_KEY to verify
[ok]   Version: running 0.11.1 (npm latest)

Verdict: 1 check failed - the MCP server will not work until this is fixed.
Most likely fix: Set NAUMU_API_KEY - create a key at https://naumu.ai/settings (Settings > Your agents > Create an API key)
```

Run it with the same environment your MCP client uses, otherwise the key check reports on your shell rather than on the client's configuration.

## License

MIT
