An MCP (Model Context Protocol) server that gives AI coding assistants
real, structured introspection into Zephyr RTOS and nRF Connect SDK
workspaces — not just shell wrappers around west.
It's designed to be invoked by AI clients (Claude Desktop, Claude Code,
Cursor, etc.) over stdio. Once registered, you can ask in plain language
"why isn't CONFIG_BT_PERIPHERAL enabled in my nrf5340 build?" and the
assistant walks the resolved Kconfig, finds provenance from the
.config-trace.pickle, and points you at the .conf file to edit.
A tool earns its place here when one of these is true:
- It returns structured, agent-consumable data that would otherwise force the LLM to parse pages of CLI output (build errors, devicetree, Kconfig dependency graphs, ELF size reports).
- It encapsulates a multi-step workflow that's error-prone to chain by hand (build + parse errors + suggest fix; query DT + cross-ref ELF).
- It uses Zephyr's own build artifacts rather than re-deriving
them:
zephyr/edt.pickle,zephyr/.config,zephyr/.config-trace.pickle(when available),build_info.yml,CMakeCache.txt.
We deliberately do not ship one tool per west flag. Agents already
have a shell. We expose the interesting commands (build) with
structured output, plus a run_west escape hatch for everything else.
| Tool | Purpose |
|---|---|
workspace_info |
One-call workspace introspection: Zephyr version, SDK, manifest, counts. |
open_build |
Inspect a build directory; sysbuild-aware (multi-image trees). |
query_devicetree |
Filter the resolved DT by compatible / label / status / chosen / alias / path. |
list_dt_chosen_and_aliases |
Enumerate /chosen and /aliases — first step for "why isn't this driver picked up." |
get_dt_memory_regions |
List zephyr,memory-region nodes; optionally cross-reference ELF allocation. |
find_kconfig_resolved |
Symbol value + provenance (assigned / default / select / imply / unset). |
find_kconfig_definition |
Cheap "does this symbol exist" — no build needed. |
verify_kconfigs |
Bulk-check CONFIG_* names, sorted into enabled / disabled / missing. |
build_project |
west build with parsed, categorized errors, sysbuild-attributed. |
search_workspace |
Content search across the manifest (west grep); tracked source only, structured {project, file, line, text} hits. |
find_workspace_files |
Filename search across the manifest (west forall + git ls-files); tracked files, {project, path}. |
run_west |
Escape hatch for west subcommands without dedicated tools. |
Domain-aware. For nRF53/54/91 (and any board using MCUboot, TF-M, or
multi-core), the build tree is sysbuild: domains.yaml indexes per-image
build dirs. Every tool takes an optional domain parameter. The same
tools handle single-image and multi-image trees uniformly.
Capability honesty. Kconfig provenance depends on
.config-trace.pickle, which is produced by the Kconfig traceconfig
feature — present in NCS ≥ 3.2.1 and recent upstream Zephyr (4.x with
the traceconfig contribution), but not in earlier trees (e.g. NCS 3.1.0
and older, Zephyr 3.7 LTS). build_info.yml is likewise recent. Tools
detect what's available and degrade gracefully: on a tree without the
trace, find_kconfig_resolved still returns the authoritative value
read from .config, but with provenance_available: false and no "why"
(provenance/set_at) data — rather than silently producing partial output.
No CMake env reconstruction. Older approaches tried to fake the
~20 environment variables Zephyr's kconfig.cmake sets, then re-run
Kconfig. That's unsound — generated paths, board-revision configs,
shield Kconfigs all rely on CMake having run. We read the resolved
.config instead. The build already did the work.
West Python API. Workspace discovery, manifest parsing, and config
reading all go through west.util, west.manifest, and
west.configuration rather than shelling out to west or hand-rolling
INI parsers.
The intended distribution channel is the
vfox-zephyr-sdk mise plugin,
which installs zephyr-mcp as a single uv-managed script alongside the
Zephyr SDK and west itself:
mise install vfox-zephyr-sdk:mcp@<version>
For development on this repo:
git clone https://github.com/ale-alfaro/zephyr-rtos-mcp
cd zephyr-rtos-mcp
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
After mise install vfox-zephyr-sdk:mcp@<version>:
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"zephyr": {
"command": "mise",
"args": ["x", "vfox-zephyr-sdk:mcp@<version>", "--", "zephyr-mcp"],
"env": {
"ZEPHYR_BASE": "/path/to/your/zephyrproject/zephyr"
}
}
}
}ZEPHYR_BASE is how the server finds your workspace — it runs west topdir
from it. Every Zephyr environment sets this; the server errors if it's missing
and no workspace_path is passed. (You can still override per call with the
workspace_path argument to any tool.)
Claude Code:
claude mcp add zephyr --env ZEPHYR_BASE=/path/to/your/zephyrproject/zephyr \
-- mise x vfox-zephyr-sdk:mcp@<version> -- zephyr-mcp
Running via mise x inherits the full Zephyr toolchain environment, so
when the MCP server's build_project tool shells out to west, it
finds the right SDK and toolchain.
Before wiring to an LLM, exercise the tools directly with the MCP Inspector — it's a browser UI that lists tools and lets you call them by hand:
npx @modelcontextprotocol/inspector mise x vfox-zephyr-sdk:mcp@<version> -- zephyr-mcp
When the LLM gives a wrong answer, work through:
- Did it call any tools? (If not, improve the tool docstring.)
- Does the same call in the Inspector return the right data? (If no, bug in Python.)
- If both are fine, the LLM may have misread the response — usually means the summary line + JSON payload could be clearer.
Server logs go to stderr; on macOS, Claude Desktop captures them at
~/Library/Logs/Claude/mcp-server-zephyr.log.
- Python ≥ 3.10
- Zephyr ~3.7 through 4.x / NCS 2.6 through 3.x (we test against the artifact shapes from this range)
- Kconfig provenance ("why is CONFIG_X set?") additionally requires the
Kconfig trace artifact: NCS ≥ 3.2.1 or recent upstream Zephyr 4.x.
On older trees every tool still works, but
find_kconfig_resolvedreports value-only (provenance_available: false). - Linux, macOS, Windows (via uv-managed environment)
Alpha. The tool list and response shapes are stable but may evolve
based on real-world use. The artifact-reading foundation
(zephyr_mcp/artifacts.py) and the domain/sysbuild model are unlikely
to change.
zephyr_mcp/
├── artifacts.py ← BuildArtifacts + BuildTree; sysbuild detection.
│ The keystone — everything else reads through this.
├── workspace.py ← Workspace discovery via west.util; manifest via
│ west.manifest; config via west.configuration.
├── dts_tools.py ← edt.pickle loader; query_devicetree;
│ get_dt_memory_regions with ELF cross-reference.
├── kconfig_tools.py ← Two-tier Kconfig: definition-only (cheap) and
│ resolved-with-provenance (needs build context).
├── build_tools.py ← west build wrapper with structured error parsing,
│ sysbuild domain attribution.
├── server.py ← FastMCP entry point; tool registration.
├── models.py ← Pydantic models for all responses.
├── cache.py ← Mtime-keyed cache for expensive parses.
├── errors.py ← Domain exceptions.
└── __init__.py
Apache-2.0