MCP server for Zig that connects AI coding assistants to ZLS via the Language Server Protocol.
Works with Claude Code, Cursor, Windsurf, and any MCP-compatible client.
AI assistant <--(MCP stdio)--> zig-mcp <--(LSP pipes)--> ZLS
|
zig build / test / check
Install directly from the Claude Code interface β no manual build needed:
# 1. Add the marketplace
/plugin marketplace add nzrsky/zig-mcp
# 2. Install the plugin
/plugin install zig-mcp@zigOr as a one-liner from the terminal:
claude plugin marketplace add nzrsky/zig-mcp && claude plugin install zig-mcp@zigThe binary is built automatically on first use. Just make sure zig and zls are in your PATH.
git clone https://github.com/nzrsky/zig-mcp.git
cd zig-mcp
zig build -Doptimize=ReleaseFastBinary is at zig-out/bin/zig-mcp.
If you installed via the plugin system, skip this section β everything is configured automatically.
# add globally
claude mcp add zig-mcp -- /absolute/path/to/zig-mcp --workspace /path/to/your/zig/project
# add for current project only
claude mcp add --scope project zig-mcp -- /absolute/path/to/zig-mcp --workspace /path/to/your/zig/projectOr edit ~/.claude/mcp_servers.json:
{
"mcpServers": {
"zig-mcp": {
"command": "/absolute/path/to/zig-mcp",
"args": ["--workspace", "/path/to/your/zig/project"]
}
}
}If you omit
--workspace, zig-mcp uses the current working directory.
Add to .cursor/mcp.json in your project:
{
"mcpServers": {
"zig-mcp": {
"command": "/absolute/path/to/zig-mcp",
"args": ["--workspace", "/path/to/your/zig/project"]
}
}
}Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"zig-mcp": {
"command": "/absolute/path/to/zig-mcp",
"args": ["--workspace", "/path/to/your/zig/project"]
}
}
}--workspace, -w <path> Project root directory (default: cwd)
--zls-path <path> Path to ZLS binary (default: auto-detect from PATH)
--help, -h Show help
--version Show version
All of these answer from ZLS's semantic model β the part a shell and a text search cannot reach.
| Tool | What it knows that grep does not |
|---|---|
zig_definition |
The one true declaration, followed through imports and aliases. Takes symbol or file+line+character |
zig_references |
Real usages, scope-aware; skips same-named identifiers, comments and strings. symbol mode also searches through re-exports |
zig_hover |
The type after comptime evaluation and inference β invisible in the source text |
zig_diagnostics |
Errors for one file without building the project, re-synced against disk first |
zig_workspace_symbols |
Declarations by name, not every line mentioning it |
zig_document_symbols |
A file's outline: declarations, kinds, nesting |
zig_completion |
What can legally follow at a position, with types |
zig_signature_help |
The real signature, comptime and generic parameters included |
zig_rename |
Which files a rename touches, scope-aware |
zig_code_action |
Quick fixes ZLS offers for a range |
zig_inlay_hints |
Every inferred type in a file at once β nothing of this is in the source text |
zig_type_definition |
The declaration of a value's type, not of the value |
zig_ast_query |
Code by shape: empty catch {}, catch unreachable, undefined initializers, unreachable, @panic. Matched over the syntax tree, so comments and string literals never match and multi-line forms always do |
zig_unused_private |
Private declarations nothing refers to β exact, because a non-pub name cannot escape its file |
There is no zig_build, zig_test, zig_format, zig_version, zig_check
or zig_manage. They used to exist and wrapped zig build, zig test,
zig fmt, zig version, zig ast-check and zvm β and a wrapper loses to
the shell it wraps: no pipes, no redirection, no working
directory of its own. Session transcripts settle it: 526 zig build
invocations through the shell, zero calls to the tool. Run those with your
shell.
zig-mcp spawns ZLS as a child process and talks to it over stdin/stdout using the LSP protocol (Content-Length framing). On the other side, it speaks MCP (newline-delimited JSON-RPC) to the AI assistant.
Three threads:
- main -- reads MCP requests, dispatches tool calls, writes responses
- reader -- reads LSP responses from ZLS, correlates by request ID
- stderr -- forwards ZLS stderr to the server log
If ZLS crashes, zig-mcp automatically restarts it and re-opens all tracked documents.
Files are opened in ZLS lazily on first access, and re-synced (didChange) whenever their contents change on disk -- no need to manage document state manually.
# build
zig build
# run tests (162 unit tests, including a fake-ZLS harness)
zig build test
# quality gates: lint, coverage, dead code, mutants
make lint cov deadcode mutants
# run manually
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}' | \
zig-out/bin/zig-mcp --workspace . 2>/dev/nullMIT