Local MCP server for searching and exploring the NDS.Live specification
Project description
ndslive-mcp
A locally-installable MCP server giving agents — Claude Code, IDE assistants, scripts — structured search and lookup over the NDS.Live specification.
What it gives you
Once installed and authenticated, your MCP host gains nine tools:
| Tool | What it does |
|---|---|
search_spec |
Full-text search over symbol names, qnames, doc comments. Filter by kind / module / version. |
search_docs |
Full-text search over the bundled documentation.nds.live and best-practices.nds.live markdown. |
get_type |
Resolve a fully-qualified name → kind, module, version, source file, line, doc, fields. |
find_references |
Every place a type is referenced, by field name and source location. |
list_modules |
Modules in the bundle, optionally filtered by category (common / feature / attribute / service / reference). |
get_module |
Module metadata: category, deps, top-level types. |
get_module_versions |
Every version of a module the bundle has indexed. |
compare_versions |
Diff between two versions of the same module: added / removed / changed types. |
update_index |
Force a refresh against Artifactory. Live-swap; no server restart. |
Install
pipx install ndslive-mcp # public PyPI; no NDS gate on the code itself
ndslive-mcp install # guided setup: verify your Artifactory PAT, save it, pre-fetch the bundle
Then register the server with your MCP host. You don't run the server yourself — the host launches ndslive-mcp on demand over stdio (and kills it when it's done). For Claude Code:
claude mcp add ndslive -- ndslive-mcp
Other hosts (Codex, Gemini, IDE assistants): configure a stdio MCP server whose command is ndslive-mcp (no arguments).
ndslive-mcp install is a wizard around the lower-level commands, which you can also run individually: ndslive-mcp auth (save/verify PAT) and ndslive-mcp update (fetch or refresh the bundle).
How auth works
The Python package is public on PyPI — anyone can install. The bundle (the actual NDS.Live spec content: SQLite index, raw schemas, docs) is gated behind NDS Artifactory PAT auth. On first run, the server tries to download the bundle; if no PAT is saved it logs a warning and refuses to answer queries until you run ndslive-mcp auth.
PATs are stored in the OS keyring (macOS Keychain / Linux Secret Service / Windows Credential Locker). They never live in plaintext on disk.
For headless / CI usage:
NDS_ARTIFACTORY_USER=u NDS_ARTIFACTORY_PAT=p ndslive-mcp serve
These env vars take precedence over the keyring.
How updates work
server start ──► HEAD ndslive-mcp.json on Artifactory (~100 ms)
│
┌──────┴──────┐
same version newer version
│ │
│ ▼
│ GET bundle.zip
│ verify sha256
│ extract → ~/.cache/ndslive-mcp/versions/<v>/
│ atomic-swap `current` symlink
│ │
└──────┬───────┘
▼
open index.sqlite (read-only)
- Atomic: a half-downloaded bundle never becomes the live one — the symlink only flips after sha256 verification.
- Rollback-friendly: previous version dirs stay on disk.
- No background polling: check on startup and on explicit
update_indextool call only.
Manual refresh:
update_index(force=true)
How it's built
The bundle is built off-band by CI and published to Artifactory:
spec sources ──► zserio.jar + indexer-extension ──► symbols.jsonl
│
+ nds.live.compatibility/*.yaml (categories)
+ documentation.nds.live + best-practices (markdown)
│
▼
build_index.py
│
▼
index.sqlite (FTS5)
│
▼
ndslive-mcp.zip + ndslive-mcp.json
│
▼
NDS Artifactory — same folder as the spec zip (gated)
Java only runs at build time. Clients are pure Python — no JRE required.
See docs/architecture.md for the full design and docs/jsonl-schema.md for the JSONL contract.
Build and test locally
Prerequisites
- Python 3.10+ — for the server itself, the test suite, and the index build step.
- Java 11+ — only needed if you want to build a fresh bundle end-to-end (the indexer extension runs the zserio compiler). The installed server does not need a JRE.
- A checkout of
nds-live-indexer-extensionalongside this repo, if you want to rebuild the indexer. - The zserio compiler jar via
pip install zserio==2.18.1(the prod spec zip does not bundle it). The jar lands at…/site-packages/zserio/compiler/zserio.jar. - A spec bundle zip for the indexer to consume (e.g.
ndslive.zipfrom the NDS compatibility-build pipeline; unpacks tondslive/withall.zsat its root).
Install and run tests
pip install -e '.[dev]'
pytest # hermetic — no Java, no network, no Artifactory
ruff check .
The test suite uses synthesized JSONL fixtures and httpx.MockTransport so it has no external dependencies. CI runs the same commands across Python 3.10 / 3.11 / 3.12.
Run the server against an existing bundle
If you already have a ndslive-mcp.zip on disk (e.g. from CI or a colleague), point the cache at it and serve in --offline mode so it skips the startup Artifactory check:
# Extract the bundle into a versioned cache dir
mkdir -p ~/.cache/ndslive-mcp/versions/local
unzip -q <path-to-bundle>.zip -d ~/.cache/ndslive-mcp/versions/local/
# Point `current` at it
ln -sfn ~/.cache/ndslive-mcp/versions/local ~/.cache/ndslive-mcp/current
# Serve — no auth required, no network call
ndslive-mcp serve --offline
You can iterate on tool definitions in src/ndslive_mcp/tools/, restart, and the new code picks up the existing index.
Build a bundle end-to-end
Useful for testing the full pipeline before pushing. Requires Java 11+ and a built indexer JAR.
# 0. Get the zserio compiler jar (shared by the indexer build and the index run)
pip install zserio==2.18.1
ZSERIO_JAR=$(python -c 'import zserio, os; print(os.path.join(os.path.dirname(zserio.__file__), "compiler", "zserio.jar"))')
# 1. Build the indexer JAR once (or after extension changes)
cd ../nds-live-indexer-extension
mkdir -p libs && cp "$ZSERIO_JAR" "libs/zserio-2.18.1.jar" # satisfies compileOnly fileTree('libs')
gradle shadowJar
# 2. Build a bundle in this repo using a local spec zip
cd ../ndslive-mcp
ZSERIO_JAR=$ZSERIO_JAR \
INDEXER_JAR=../nds-live-indexer-extension/build/libs/nds-live-indexer-extension-*-all.jar \
LOCAL_SPEC_ZIP=../_ext/ndslive.zip \
SKIP_DOCS=1 \
bash scripts/build_bundle.sh
# → .work/ndslive-mcp.zip
# → .work/ndslive-mcp.json
Then run ndslive-mcp serve --offline against the produced bundle (see previous section).
Env-var overrides that short-circuit external fetches for offline iteration:
| Variable | Effect |
|---|---|
LOCAL_SPEC_ZIP |
Use a local spec zip instead of fetching from Artifactory. |
SKIP_DOCS |
Skip cloning documentation.nds.live + best-practices.nds.live + nds.live.compatibility (faster, but no doc FTS and no module categories). |
BUNDLE_VERSION |
Override the version string in ndslive-mcp.json; defaults to today UTC. |
WORK |
Work directory; defaults to ./.work. |
Without these overrides, build_bundle.sh needs NDS_ARTIFACTORY_USER / NDS_ARTIFACTORY_PAT to download the spec zip from the NDS compatibility-build pipeline.
Smoke-check what landed in the bundle
The SQLite index inside the bundle is queryable directly. A quick sanity check after a build:
python -c "
from pathlib import Path
from ndslive_mcp.store import Store
s = Store(Path('.work/out/index.sqlite'))
print('modules:', len(s.list_modules()))
print('lane versions:', s.get_module_versions('lane'))
print('sample search:', [r.qname for r in s.search('LaneGroup', limit=3)])
"
Deploy
CI runs scripts/deploy_bundle.sh automatically as the final step of the release workflow. To deploy by hand (emergency push, or to test the deploy path without merging to main):
# 1. Build the bundle, embedding the production publish URL
NDS_BUNDLE_PUBLISH_URL=https://artifactory.nds-association.org/artifactory/<repo>/<path> \
INDEXER_JAR=../nds-live-indexer-extension/build/libs/nds-live-indexer-extension-*-all.jar \
NDS_ARTIFACTORY_USER=$USER \
NDS_ARTIFACTORY_PAT=$ART_PAT \
bash scripts/build_bundle.sh
# 2. Dry-run to confirm the upload destinations
DRY_RUN=1 bash scripts/deploy_bundle.sh
# 3. Real upload — bundle first, then ndslive-mcp.json (order matters: keeps clients consistent)
NDS_ARTIFACTORY_USER=$USER NDS_ARTIFACTORY_PAT=$ART_PAT \
NDS_BUNDLE_PUBLISH_URL=https://artifactory.nds-association.org/artifactory/<repo>/<path> \
bash scripts/deploy_bundle.sh
The deploy script refuses to run if ndslive-mcp.json's embedded url doesn't match NDS_BUNDLE_PUBLISH_URL — that mismatch means the build was done with a stale URL and clients would 404.
License
The ndslive-mcp software is licensed under BSD-3-Clause — the same as the ndslive-setup installer. The license covers this software (the "hull") only. The NDS.Live specification content delivered as the bundle is NDS Protected Material, not part of this package, and remains gated behind NDS Artifactory authentication and governed by your NDS Member Agreement or NDS.Live Evaluation License.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ndslive_mcp-0.1.2.tar.gz.
File metadata
- Download URL: ndslive_mcp-0.1.2.tar.gz
- Upload date:
- Size: 36.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f24433b3969e9c03928502f1b18e16253ace6aae0112335a7463b70480984ef3
|
|
| MD5 |
55a698896faa8f12705f617491f5fb39
|
|
| BLAKE2b-256 |
aa35a67a870464997fde5e4f287f649eef2512fdb39f6a58acd565059098639a
|
Provenance
The following attestation bundles were made for ndslive_mcp-0.1.2.tar.gz:
Publisher:
release.yml on ndsev/nds-live-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ndslive_mcp-0.1.2.tar.gz -
Subject digest:
f24433b3969e9c03928502f1b18e16253ace6aae0112335a7463b70480984ef3 - Sigstore transparency entry: 1771152279
- Sigstore integration time:
-
Permalink:
ndsev/nds-live-mcp@4e26fcce0e8c83c768b3cca5a009fd397ec97edd -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/ndsev
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4e26fcce0e8c83c768b3cca5a009fd397ec97edd -
Trigger Event:
push
-
Statement type:
File details
Details for the file ndslive_mcp-0.1.2-py3-none-any.whl.
File metadata
- Download URL: ndslive_mcp-0.1.2-py3-none-any.whl
- Upload date:
- Size: 31.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5fbb8c13f5a7dcf1d2b6debb27856746c390537ba2d16668c6a9974d467c2be6
|
|
| MD5 |
2975803a668d844b43b153dae79a4e08
|
|
| BLAKE2b-256 |
100e732391e22eb3be70b46eaf1e9a81b104411103be4f16afe83d96e3aad76a
|
Provenance
The following attestation bundles were made for ndslive_mcp-0.1.2-py3-none-any.whl:
Publisher:
release.yml on ndsev/nds-live-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ndslive_mcp-0.1.2-py3-none-any.whl -
Subject digest:
5fbb8c13f5a7dcf1d2b6debb27856746c390537ba2d16668c6a9974d467c2be6 - Sigstore transparency entry: 1771152437
- Sigstore integration time:
-
Permalink:
ndsev/nds-live-mcp@4e26fcce0e8c83c768b3cca5a009fd397ec97edd -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/ndsev
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4e26fcce0e8c83c768b3cca5a009fd397ec97edd -
Trigger Event:
push
-
Statement type: