Skip to main content

MCP server that gives AI coding agents accurate NXOpen .NET API knowledge via hybrid retrieval

Project description

nxopen-mcp

English | 繁體中文

MCP server that gives AI coding agents (Claude Code, Codex, Cursor) accurate knowledge of the Siemens NXOpen .NET API — eliminating hallucinated API calls via hybrid retrieval over your own NX installation's official documentation.

Why

LLMs hallucinate NXOpen APIs: it's a niche domain (Siemens NX CAM/CAD automation) with sparse public training data, so models confidently invent classes, methods, and parameters that don't exist. This server grounds agents in the real docs instead of guesses:

  • Semantic search (BGE-M3 dense + sparse embeddings) so natural-language queries in English or 中文 find the right API even without exact names.
  • Exact-name channel: a literal CamelCase name in your query (e.g. CavityMillingBuilder) is looked up directly and pinned to the top (types first) — never left to approximate matching.
  • RRF fusion available to combine channels — though evaluation made dense + exact the default (see Evaluation).

Everything runs locally and offline against an index built from your own licensed NX installation — no Siemens files are ever bundled with this repo or sent anywhere.

Quick start

Requires Python 3.11+.

# 1. Install from PyPI (or run without installing: uvx nxopen-mcp)
pip install "nxopen-mcp[embed,reflect]"

# 2. Build the index from YOUR NX installation (one-time — see time note below)
nxopen-mcp index --nx-path "D:\Siemens\NX12.0"

# 3. Register with Claude Code (user scope: available in every project)
claude mcp add -s user nxopen -- nxopen-mcp serve

# 4. Ask Claude Code to write NXOpen code — it now queries real APIs.

index looks for NXOpen*.xml doc files under <nx-path>\UGII\managed (falling back to <nx-path> itself), and for NXOpen*.dll assemblies in the same folder.

Extras: [embed] pulls in FlagEmbedding (downloads the ~2GB BGE-M3 model on first use) — required for indexing and semantic search. [reflect] pulls in pythonnet so get_class can show inherited members; skip it and indexing still works, just without inheritance chains.

How long does indexing take? Honest numbers: a full NX 12 doc set is ~100k members; on an 8-core laptop CPU that's several hours of embedding (memory-bandwidth-bound — --workers N helps mainly on machines with more memory channels; a CUDA GPU helps a lot). Plan to run it overnight, or copy a teammate's index (see Sharing a pre-built index).

First semantic query is slow by design. serve starts instantly, and get_class / get_member respond immediately, but the first search_api / find_builder call loads the BGE-M3 model (~1–2 min). After that, semantic queries take seconds. If your MCP client shows the first search "hanging", it's the one-time model load — let it finish.

By default the index is written to ~/.nxopen-mcp/index.db; override with --db <path> on both index and serve.

If nxopen-mcp isn't on your PATH (e.g. installed into a venv), use the full path to the executable (Windows: <venv>\Scripts\nxopen-mcp.exe) in the commands and configs below.

.mcp.json (Claude Code / other MCP-aware clients)

{
  "mcpServers": {
    "nxopen": {
      "command": "nxopen-mcp",
      "args": ["serve"]
    }
  }
}

If you built the index at a non-default path, pass it explicitly:

{
  "mcpServers": {
    "nxopen": {
      "command": "nxopen-mcp",
      "args": ["serve", "--db", "D:\\path\\to\\index.db"]
    }
  }
}

Codex

Codex CLI reads MCP servers from ~/.codex/config.toml (Windows: C:\Users\<you>\.codex\config.toml; create the file if it doesn't exist). Add:

[mcp_servers.nxopen]
command = "nxopen-mcp"
args = ["serve"]

If nxopen-mcp isn't on PATH, use the full executable path (double the backslashes on Windows):

[mcp_servers.nxopen]
command = "D:\\path\\to\\venv\\Scripts\\nxopen-mcp.exe"
args = ["serve", "--db", "D:\\path\\to\\index.db"]

Alternatively, one CLI command does the same thing:

codex mcp add nxopen -- nxopen-mcp serve

Restart Codex, then verify with codex mcp list (or just ask it to look up an NXOpen class — you'll see the nxopen tool calls). Note the same first-semantic-query model load (~1–2 min) applies here.

Cursor

Add the same JSON block as .mcp.json above to Cursor's MCP settings (project .cursor/mcp.json, or global via Settings → MCP).

Tools

tool purpose
search_api Hybrid semantic search over the API (dense + exact-name by default, sparse optional). Accepts English or 中文 queries; use when you don't know the exact class/member name.
get_class Full member list for a class, including members inherited from its ancestor chain. Use when you know the class name.
get_member Exact signature, parameters, return value, NX version, and license requirement for one member.
find_builder Given a CAM operation name (e.g. "cavity milling", "hole drilling"), finds the matching *Builder class, its creator method, and a Builder → Commit → Destroy code skeleton.

Architecture

NXOpen*.xml / *.dll  (your NX install)
        │
        ▼
  indexer/parser.py        one XML doc-comment member -> one MemberRecord
  indexer/inheritance.py   optional: reflect DLLs (pythonnet) for base-class chains
        │
        ▼
  indexer/embedder.py      BGE-M3 dense vector + sparse token weights per record
        │
        ▼
  indexer/build.py         writes members, dense_vec (sqlite-vec), sparse_postings
        │                  into a single SQLite file (index.db)
        ▼
  retrieval/store.py       exact-name lookup, class/member/inheritance queries
  retrieval/hybrid.py      dense ANN + exact CamelCase match (default),
                           optional sparse channel, RRF fusion
        │
        ▼
  server.py                4 MCP tools (FastMCP, stdio transport)
  cli.py                   `nxopen-mcp index` / `nxopen-mcp serve`

Design decisions:

  • BYO-Docs licensing. This repository contains no Siemens XML/DLL files. Users point nxopen-mcp index at their own licensed NX installation; the resulting index is a local SQLite file that never leaves the machine and is never committed (see .gitignore).
  • One-member-one-chunk. Each indexed unit is a single API member (type, property, method, field, or event) rather than an arbitrary text window, so retrieval results map 1:1 onto something an agent can act on (a class, a method signature) instead of a fragment of a doc page.
  • RRF fusion, not score blending. When the sparse channel is enabled, dense and sparse rankings are combined with Reciprocal Rank Fusion, which is scale-free and doesn't require calibrating dense-vs-sparse score magnitudes against each other (the evaluated default runs dense + exact only). Exact CamelCase name matches are promoted ahead of the fused list outright, since a literal name in the query is a much stronger signal than similarity.
  • Inheritance via reflection, with graceful degradation. Ancestor chains (needed by get_class to show inherited members) are extracted by reflecting the NXOpen DLLs with pythonnet ([reflect] extra) at index time. If the extra isn't installed or DLLs aren't found alongside the XML docs, indexing still succeeds — get_class simply has no inherited members to show.

Evaluation

Measured on a real index built from an NX 12 installation (97,913 API members) against a 73-query golden set (eval/golden.jsonl, mixed English / Traditional Chinese, four query styles — semantic description, exact class name, member lookup, builder idiom — spanning CAM, Features, Sketch, Assemblies, Drawings, UF, and BlockStyler):

python eval/run_eval.py --db ~/.nxopen-mcp/index.db
config Recall@5 Recall@10 MRR
dense-only 58.90% 68.49% 0.420
sparse-only 34.25% 38.36% 0.193
dense+exact (default) 58.90% 68.49% 0.445
dense+sparse+exact 49.32% 61.64% 0.409

With vs. without the tool: hallucination test

Same model (Claude Haiku), the original 33 golden questions, one variable — whether the nxopen-mcp tools are available. Answers were graded against the golden set; "hallucinated" means the proposed member does not exist anywhere in the real 97,913-member index:

metric closed-book (no tool) with nxopen-mcp
exactly correct 13/33 (39.4%) 31/33 (93.9%)
wrong but real API 13/33 (39.4%) 2/33 (6.1%)
hallucinated (API does not exist) 7/33 (21.2%) 0/33 (0%)
time to answer all 33 84 s 321 s (44 tool calls)

The closed-book hallucinations are the dangerous kind — plausible names like NXOpen.CAM.MillGeometryBuilder (real name: MillGeomBuilder) or NXOpen.Session.Parts.Open (real: NXOpen.PartCollection.Open) that read fine and fail at compile time. Tool-assisted answering costs ~7 s per question and eliminated hallucinations entirely.

Evaluation-driven default. The original design fused dense, sparse and exact-name channels with uniform RRF. Measurement showed BGE-M3's sparse channel hurt on this corpus — on the initial 33-query set a weight sweep (w_sparse ∈ {0.5, 0.3, 0.15}) never recovered the dense-only baseline, and expanding the golden set to 73 queries replicated the finding (Recall@5 58.9% → 49.3% when sparse is fused). The exact-name channel — after reordering its matches (types first, shortest name first, capped at 3) — matches dense recall while improving MRR (0.420 → 0.445) and guaranteeing literal-name hits. The default is therefore dense + exact; the sparse channel remains available via the channels parameter of search().

Demo

Claude Code querying the real NXOpen API through nxopen-mcp

A real session: asked to write NXOpen code that sets the spindle speed, Claude Code calls search_api (semantic search, English or Chinese) and get_class (members + inheritance chain), then writes code in which every member — FeedsBuilder, SpindleRpmToggle, SpindleRpmBuilder.Value — exists in the real API, with NX version info to prove it.

Sharing a pre-built index

The index is a single SQLite file (~500 MB for a full NX 12 doc set), so teammates can skip the hours-long build:

  1. Install nxopen-mcp (the [reflect] extra is not needed — inheritance chains are already baked into the index).
  2. Copy the index file to ~/.nxopen-mcp/index.db (or keep it elsewhere and pass --db <path> to serve).
  3. Register the server: claude mcp add -s user nxopen -- nxopen-mcp serve

A full step-by-step onboarding guide (with troubleshooting): docs/setup-prebuilt-index.md (繁體中文版).

The BGE-M3 model (~2 GB) still downloads on the first semantic query — it encodes the query text, independent of the index. Exact lookups (get_class / get_member) never need the model.

Licensing boundary: the index embeds text from Siemens' API documentation. Sharing it within an organization whose seats are licensed for NX is reasonable; do not redistribute index files publicly — anyone outside your license should build their own with nxopen-mcp index.

License & IP

Code: MIT (see LICENSE). This repository contains no Siemens files — no NXOpen XML docs, no DLLs. The index is built locally from your own licensed NX installation's documentation via nxopen-mcp index and never leaves your machine.

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

nxopen_mcp-0.2.0.tar.gz (398.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nxopen_mcp-0.2.0-py3-none-any.whl (23.0 kB view details)

Uploaded Python 3

File details

Details for the file nxopen_mcp-0.2.0.tar.gz.

File metadata

  • Download URL: nxopen_mcp-0.2.0.tar.gz
  • Upload date:
  • Size: 398.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nxopen_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ac06a5e17032ec33e6b74e8dec21534e46fde592ff3307d373e427ba1f3e7ce9
MD5 4158173e19a107434e88a34e7d3381a6
BLAKE2b-256 8f332f208fc1183dba1c4974f4da3b90ea145523dc5d25cfd8de4c2d7c805559

See more details on using hashes here.

Provenance

The following attestation bundles were made for nxopen_mcp-0.2.0.tar.gz:

Publisher: publish.yml on mingfeng6684/nxopen-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nxopen_mcp-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: nxopen_mcp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 23.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nxopen_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c8702a9b943993bd5996abf8f229dd74829c5214d78d5c91b25dfe88dd7fa5dc
MD5 195769fa0903959d4fb0c2073e05e02b
BLAKE2b-256 d4f35fd9f32941eab724acf40c77d735a3cd6821bf95b02b5aef73626da7ddea

See more details on using hashes here.

Provenance

The following attestation bundles were made for nxopen_mcp-0.2.0-py3-none-any.whl:

Publisher: publish.yml on mingfeng6684/nxopen-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page