Skip to main content
Glama

Nexidion MCP 1.4.0

MCP server that exposes the Nexidion knowledge base as tools. Wraps the REST API — no raw SQL — so Nexidion's own auth and per-vault permissions still apply.

It runs in two modes, with two different identity models:

stdio (Claude Code)

--http (claude.ai)

Where

your machine, over the LAN

nexidion-mcp container on the Pi

Reached at

subprocess on stdin/stdout

https://mcp.nexidion.org/mcp

Who it acts as

the configured Nexidion user

the user who logged in, individually

Auth

login followed by an MCP actor-token exchange

OAuth 2.1 + PKCE (see below)

Tools

all 23

22 — no delete_node

delete_node is deliberately not reachable from the public connector: it is irreversible, and it is still one Claude Code session away.

IMPORTANT

The AI application you use must itself support MCP and local stdio servers. Running a local LLM API server (for example an OpenAI-compatible model endpoint) is not enough: an LLM API serves model inference, but it does not launch this process, speak MCP, or present MCP tools to the model. Use an MCP-capable client as the application around the model, and configure that client as shown below.

Files

  • server.py — the tools, plus both transports.

  • oauth.py — OAuth authorization server for the HTTP mode (state in SQLite).

  • Dockerfile, requirements.txt — the image the Pi runs.

  • .env — stdio config: NEXIDION_BASE_URL, NEXIDION_USER, NEXIDION_PASSWORD (chmod 600, git-ignored).

Related MCP server: Arc Todo MCP

Fresh stdio installation

These instructions start with a machine that has no checkout or Python environment. They configure the local stdio transport, which is the usual choice for desktop MCP clients. The Nexidion web application must already be running at a URL this machine can reach.

1. Prerequisites

Install:

  • Git.

  • Python 3.11 or newer, including venv and pip.

  • An MCP-capable AI client that supports local stdio servers. Claude Code is one example. A client that supports only remote HTTP MCP cannot use this local setup.

Check the installations:

git --version
python3 --version

On Windows PowerShell, use py --version for Python instead.

2. Clone and install on Linux

Choose any permanent directory; the paths used later must match it exactly.

git clone https://github.com/HabermannR/nexidion-mcp.git
cd nexidion-mcp
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

If python3 -m venv is unavailable on Debian or Ubuntu, install the distribution's python3-venv package and repeat the command.

3. Clone and install on Windows

In PowerShell:

git clone https://github.com/HabermannR/nexidion-mcp.git
Set-Location nexidion-mcp
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

If PowerShell blocks Activate.ps1, either permit locally created scripts for your user (Set-ExecutionPolicy -Scope CurrentUser RemoteSigned) or skip activation and run .\.venv\Scripts\python.exe explicitly in every command. Activation is only a shell convenience; the MCP client configuration below always uses the environment's Python executable directly.

4. Create and authorize the Nexidion MCP user

The stdio server logs in to Nexidion with a dedicated account. In the Nexidion web application:

  1. Sign in as an administrator and open Admin → Users.

  2. Create a normal, non-administrator user, for example username mcp, with a unique password of at least eight characters. Do not use or create the special llm_assistant account; that account belongs to Nexidion's task runner and cannot log in through the normal authentication endpoint.

  3. Open Admin → Vaults, expand Access for every vault this connector should use, select the new user, and choose Add. Do not grant access to vaults the client should not see.

The account's Nexidion vault permissions remain authoritative. MCP-specific node visibility and write policies are also enforced after login.

5. Create .env from scratch

Create a file named .env in the repository root, beside server.py:

NEXIDION_BASE_URL=https://your-nexidion.example.com
NEXIDION_USER=mcp
NEXIDION_PASSWORD=replace-with-the-dedicated-users-password

Use the origin of the Nexidion web application/API, with no trailing /api. For example, a LAN install might use http://192.168.1.20:5001. Do not put the MCP connector URL here. The parser treats everything after = literally, so do not add quotes or inline comments. Protect the file because it contains a password:

chmod 600 .env

On Windows, ensure the file is really named .env, not .env.txt. It is already ignored by Git. You may instead set NEXIDION_MCP_ENV to the absolute path of a credential file stored elsewhere.

6. Verify before configuring a client

From the repository root, with the virtual environment active:

python server.py --selftest

A working clean install prints output similar to:

selftest OK: 3 vaults, user=mcp, base=https://your-nexidion.example.com

The count may differ. Zero vaults normally means the account still needs vault access. An authentication error means the username/password is wrong; a connection error means NEXIDION_BASE_URL, DNS, TLS, a firewall, or the Nexidion service needs attention. This test verifies Python, dependencies, .env, login, actor-token exchange, and API reachability without involving an MCP client.

Do not use python server.py as a visual test: a healthy stdio server waits silently for MCP protocol messages on standard input.

7. Register with Claude Code

Use absolute paths. On Linux:

claude mcp add --scope user nexidion -- \
  /absolute/path/to/nexidion-mcp/.venv/bin/python \
  /absolute/path/to/nexidion-mcp/server.py

On Windows PowerShell (quote paths that may contain spaces):

claude mcp add --scope user nexidion -- `
  "C:\absolute\path\to\nexidion-mcp\.venv\Scripts\python.exe" `
  "C:\absolute\path\to\nexidion-mcp\server.py"

Then run claude mcp list and claude mcp get nexidion. Start a new Claude Code session because MCP tools are loaded at session startup, run /mcp, and ask it to call whoami and list_vaults. The reported user should be mcp, and only the vaults granted above should appear.

8. Generic MCP client JSON

Clients commonly call the configuration property mcpServers, although the file name and settings location vary by application. Consult your client's documentation and confirm that it supports local stdio MCP servers. A Linux entry is:

{
  "mcpServers": {
    "nexidion": {
      "command": "/absolute/path/to/nexidion-mcp/.venv/bin/python",
      "args": ["/absolute/path/to/nexidion-mcp/server.py"]
    }
  }
}

The Windows equivalent requires doubled backslashes because this is JSON:

{
  "mcpServers": {
    "nexidion": {
      "command": "C:\\absolute\\path\\to\\nexidion-mcp\\.venv\\Scripts\\python.exe",
      "args": ["C:\\absolute\\path\\to\\nexidion-mcp\\server.py"]
    }
  }
}

Restart the client completely after editing its configuration, then inspect its MCP server/tool view and call whoami followed by list_vaults. Some clients use a different schema or require an env/working-directory entry; the explicit Python and script paths above avoid depending on activation or the working directory. The server finds .env beside server.py.

The login token is immediately exchanged at /api/auth/actor-token; all subsequent requests carry actor_type=mcp, so stdio cannot bypass AI visibility or write policy.

HTTP (claude.ai custom connector)

Add it in claude.ai under Settings → Connectors → Add custom connector, URL:

https://mcp.nexidion.org/mcp

Claude then registers itself (RFC 7591 dynamic client registration), redirects you to a login page served by this server, and you sign in with your own Nexidion username and password. Every tool call afterwards runs as that user.

How the auth actually works

claude.ai will only talk to a remote MCP server over OAuth, so this server is an OAuth authorization server — the MCP SDK supplies the /authorize, /token and /register handlers, oauth.py supplies the storage, and /login supplies the one thing OAuth can't know: who the human is. That is answered by posting the credentials to Nexidion's existing /api/auth/login.

The catch: Nexidion's own JWTs expire after 8 hours, and we never store your password, so we cannot re-login for you later. Instead the container is given the app's JWT_SECRET_KEY and mints a short-lived (5 min) Nexidion JWT per request for the user bound to the access token. That is why the connector stays connected indefinitely, and why this container is as security-critical as the app itself: holding that key, it could forge a token for any user. It only ever does so after that user has proved their password.

Tokens: access 1 h, refresh 90 days and rotated on every use. Only SHA-256 hashes are stored. whoami reports which Nexidion user Claude is currently acting as.

Every short-lived Nexidion JWT used for an HTTP or stdio tool call includes the trusted actor_type=mcp claim. Nexidion therefore retains the user identity for vault permissions and auditing while applying inherited node AI-access policies to the request. Relevant read tools default to include_quarantined=false; explicit opt-in never bypasses an AI-invisible policy.

Tests

Install the pinned runtime and test dependencies in a clean environment, then run:

python -m pip install -r requirements-dev.txt
python -m pytest -q

The committed tests cover stdio actor-token exchange, HTTP MCP claims, and policy option forwarding. local_smoke_test.py exercises initialization and authentication against an explicitly selected local Nexidion instance.

Config (set by compose on the Pi, not by .env)

var

value

NEXIDION_BASE_URL

http://nexidion:5001 (compose network)

NEXIDION_PUBLIC_URL

https://mcp.nexidion.org

JWT_SECRET_KEY

same secret the Flask app signs with

MCP_STATE_DB

/state/oauth_state.db (volume nexidion_mcp-state)

Deploy

Cross-build on WSL, push to Hub, pin by digest — same rule as the app, so there is always a digest to roll back to:

docker buildx build --builder nxbuilder --platform linux/arm64 \
  -f Dockerfile -t rhabermann/nexidion-mcp:vN --push .
docker buildx imagetools inspect rhabermann/nexidion-mcp:vN --format '{{.Manifest.Digest}}'
# then on the Pi: put that digest in ~/nexidion/docker-compose.yml
ssh rhab@192.168.178.63 'cd ~/nexidion && docker compose pull mcp && docker compose up -d --no-deps mcp'

Only clients whose redirect URIs are on allowed hosts may register (MCP_ALLOWED_REDIRECT_HOSTS, default claude.ai,claude.com,chatgpt.com; loopback is always allowed; * disables the check). Add a host there before connecting another web client.

Wiping nexidion_mcp-state forces every connector to re-authorize — that is the kill switch if a token ever leaks:

docker compose rm -sf mcp && docker volume rm nexidion_mcp-state && docker compose up -d --no-deps mcp

Tools

Reads: whoami, list_vaults, get_vault, list_nodes, get_node, search (full-text), find_node_by_title, get_node_versions, get_version, bulk_get_nodes, list_tasks, get_task.

Agent-native retrieval (needs a Nexidion build that has the matching endpoints):

  • search returns verbatim matches from each hit's current content (offsets, heading path, version) and flags summary_only hits; full content is omitted unless include_content=true.

  • list_node_assets / get_node_asset list and return the images a node embeds, as real image content, under that node's access policy.

  • get_context_bundle loads a node with its parent, children, outlinks and backlinks in one bounded call, each with relation, distance and link context.

  • search_context_bundle does the same starting from a search query's top hits. Writes: create_node, update_node, patch_node, move_node, set_summary, create_task (queues the Nexidion AI agent). delete_node (destructive) — stdio only.

Targeted content patches

patch_node(vault_id, node_id, expected_version, replacements, dry_run=false) requires the matching backend PATCH /api/vaults/<vault_id>/nodes/<node_id>/patch. Read get_node first and pass its version as expected_version. For example:

{
  "vault_id": 7,
  "node_id": "27cc5022-0370-4543-9cdd-43fafb8d1282",
  "expected_version": 26,
  "replacements": [
    {"old_text": "Example", "new_text": "[[Example]]", "expected_matches": 1}
  ],
  "dry_run": true
}

All replacements use case-sensitive literal matches in the original content, with non-overlapping occurrences counted per replacement. Inserted text is never matched again. Cross-replacement overlaps, duplicate targets, a changed version, or any unexpected match count abort the entire call with HTTP 409. Counts and versions must be positive integers; old_text cannot be empty. An empty new_text deletes matches. Regex and fuzzy matching are not supported.

Dry-run returns diff, matches (index, expected and actual counts), version, changed, and dry_run, without writing. Re-submit with dry_run=false and the same expected version to save. A changed patch creates exactly one historical version, preserves the title, and invalidates the AI summary. Saving returns the new version and counts without the full content or diff; a no-op returns the existing version. Vault permissions, inherited write locks, and protected source restrictions apply equally to dry-run and saving.

Requires Nexidion 4.3.3 or later. Upgrade the application before the connector. Published images do not update an already running connector; the public MCP requires an explicitly authorized deployment.

Claude Code permissions (optional)

Claude Code can apply separate tool-approval rules in its settings. Tool names use the mcp__nexidion__<tool> form. If you customize those rules, consider allowing the read and non-destructive write tools while keeping mcp__nexidion__delete_node in ask because deletion is irreversible. Client-side approval rules supplement, but do not replace, Nexidion's account and vault access controls.

Admin

  • stdio base URL points at the Pi over the LAN (http://192.168.178.63:5001). For off-LAN use, switch NEXIDION_BASE_URL to https://caddy.nexidion.org in .env.

  • The stdio account has only the vault access assigned in Nexidion. Revoke access through the Admin UI rather than relying on a hard-coded user ID.

  • To rotate the stdio password, reset the configured account and update .env.

  • Rotating JWT_SECRET_KEY logs everyone out of the app and invalidates the connector's minted JWTs — restart both containers together.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers