Skip to main content

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 ten tools:

Tool What it does
search_spec Full-text search over symbol names, qnames, doc comments, enum/bitmask member names, and field names + docs. Porter-stemmed, so natural-language queries match ("subdivision" finds "subdivisions"). 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; the field list (each with type_qname, optional, is_array, its own doc, and any constraint/condition); enum/bitmask members (values + docs); and the verbatim .zs declaration as source.
get_rule Resolve an NDS rule id → rule text, its rule group, and module / version / source file.
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.
  • Non-blocking: the check runs in the background at startup (and on an explicit update_index tool call), so launching the server never waits on a download — the new index is hot-swapped in when ready.

Updating

The package and the spec bundle update independently:

  • Packagepipx upgrade ndslive-mcp gets new server code and tools.
  • Bundle (the spec data) — refreshed automatically: each time your MCP host launches the server, it checks Artifactory and, if a newer bundle is published, downloads and hot-swaps it. You normally run nothing.

pipx upgrade does not fetch a new bundle by itself — the next server launch does. To refresh on demand (or immediately, e.g. right after upgrading) use the CLI:

ndslive-mcp update            # download a newer bundle if one is published
ndslive-mcp update --force    # re-download even if the local version matches

Bundle versions are unique UTC timestamps, so a freshly published bundle is always detected as newer.

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-extension alongside 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.zip from the NDS compatibility-build pipeline; unpacks to ndslive/ with all.zs at 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 a UTC timestamp.
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)])
"

Releasing

Maintainer steps. The package (PyPI) and the spec bundle (Artifactory) release independently.

Cut a package release vX.Y.Z:

  1. In CHANGELOG.md, move the ## [Unreleased] entries under a new ## [X.Y.Z] - YYYY-MM-DD heading (leave a fresh empty ## [Unreleased] above).
  2. Bump version in pyproject.toml.
  3. Commit, then tag and push:
    git tag vX.Y.Z && git push origin main --tags
    

The tag triggers release.yml, which publishes to PyPI (OIDC Trusted Publishing) and creates the GitHub Release using that version's CHANGELOG.md section as the notes. No manual PyPI upload or release step.

Rebuild the spec bundle — only when the indexer, the build pipeline, or the upstream spec changed (the weekly job already rebuilds automatically when the spec zip's checksum changes):

gh workflow run release.yml --ref main -f force=true

This rebuilds the index from the indexer pinned in pyproject.toml ([tool.ndslive-mcp.build] indexer-extension-ref) and deploys via the step below. Adopting a new indexer is deliberate: release an indexer tag, bump that pin, then force a rebuild.

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.

Release files for ndslive-mcp 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ndslive-mcp 0.3.1
File Size Uploaded
ndslive_mcp-0.3.1.tar.gz 60.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ndslive-mcp 0.3.1
File Interpreter ABI Platform
ndslive_mcp-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 101.0 kB

Release files / ndslive_mcp-0.3.1.tar.gz

Download URL ndslive_mcp-0.3.1.tar.gz
Size 60.4 kB
Tags Source
SHA-256 checksum
How to use checksums
7818b4e168b999654aa4205027699529b484a14ecd2d108d0324179ad5c22914
BLAKE2b-256 checksum
How to use checksums
9ab73273f935847dcaa92091130ca4663eab9a46b80a204777c6c00aa57e50da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release files / ndslive_mcp-0.3.1-py3-none-any.whl

Download URL ndslive_mcp-0.3.1-py3-none-any.whl
Size 40.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f8e722ed6084ca39f84d26d415bfa585332e22f6bf9d003196f131bffdc4bdbb
BLAKE2b-256 checksum
How to use checksums
05dfd4b403eafd384922b3b77d213d120e1ab6ce268d9ec376aa8b03c89841b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page