nxopen-mcp
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 serveRestart 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 |
| 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. |
| Full member list for a class, including members inherited from its ancestor chain. Use when you know the class name. |
| Exact signature, parameters, return value, NX version, and license requirement for one member. |
| Given a CAM operation name (e.g. "cavity milling", "hole drilling"), finds the matching |
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 indexat 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_classto show inherited members) are extracted by reflecting the NXOpen DLLs withpythonnet([reflect]extra) at index time. If the extra isn't installed or DLLs aren't found alongside the XML docs, indexing still succeeds —get_classsimply 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.dbconfig | 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

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:
Install nxopen-mcp (the
[reflect]extra is not needed — inheritance chains are already baked into the index).Copy the index file to
~/.nxopen-mcp/index.db(or keep it elsewhere and pass--db <path>toserve).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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mingfeng6684/nxopen-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server