NURLNURL registrynurl-lang.org →

← nurl-mcp

nurl-mcp 0.5.0 API

main.nu

nurl-mcp — a local MCP (Model Context Protocol) server for the NURL toolchain. The LLM-facing counterpart of nurl-lsp: where nurl-lsp serves editors over LSP, nurl-mcp serves an LLM agent over MCP so it can drive the locally installed compiler — build, run, type-check, format NURL, compile it to wasm32-wasi (via the wasmbuilder package, no build service), and read the installed standard library — against the host's real filesystem.

Transport: newline-delimited JSON-RPC 2.0 over stdin/stdout (the transport every MCP client supports). Logs go to stderr; stdout is protocol-only. Wire it into an MCP client by pointing its command at the built binary, e.g. claude mcp add nurl -- nurl-mcp.

The build/run/check/fmt tools shell out to nurl / nurlc / nurlfmt (found on $PATH — the installed toolchain shims export $NURL_STDLIB so the compiler resolves stdlib imports). Running NURL is full code execution (libc FFI, no sandbox); over stdio that is safe because only the process that spawned this server talks to it. The --http transport serves the MCP Streamable-HTTP protocol via the stdlib facade (ext/mcp_http.nu: POST single/batch, GET SSE probe, Mcp-Session-Id echo, DELETE, CORS) with bearer-token auth layered on top; nurl_run stays off over HTTP unless --allow-run is given.

API

: ~ i g_read_only 0

── Policy (set once in main, read by the dispatcher) ───────────────

g_read_only : 1 ⇒ expose only the read/analysis tools (check, fmt, list_stdlib, read_stdlib) — no build, no run. g_run_allowed : 1 ⇒ nurl_run is available. Defaults to 1 over stdio (only the spawning process can reach the server); over HTTP it is 0 unless --allow-run is passed, because run is unsandboxed code execution. g_token : non-empty ⇒ HTTP requests must carry Authorization: Bearer <g_token>.

: ~ i g_run_allowed 1

: ~ s g_token ``

@ nm_version → s

@ nm_log_banner s suffix → v

Log a startup banner "nurl-mcp <version> <suffix>" through mcp_log, building the line from the single-source version so the banners can never drift from the handshake version again.

: ~ i nm_seq 0

@ nm_is_temp s path → b

A resolved input path is a temp we own iff it sits under the "<tmpdir>/nurl-mcp-" prefix nm_tmp_path mints. Path-based (not a shared mutable flag) so it stays correct if requests are ever served on concurrent fibers.

@ nm_tmp_path s suffix → String

@ nm_unlink s path → v

@ nm_unlink_artifacts s base → v

Remove a build's artifacts: the binary base and the base.ll IR that the build driver leaves beside it.

@ nm_input_path Json args → ?String

Resolve a tool's input to a .nu file path (owned String) the compiler can read. Prefers an explicit path; otherwise writes inline source to a temp file. Returns None when neither argument is present.

@ nm_fail_from_output s prefix Output o b with_stdout → Json

Turn a non-zero-exit Output into an MCP error result carrying the exit code and captured diagnostics. with_stdout folds stdout in too (the build driver prints some diagnostics there; the front-end check prints IR there, so check passes F).

@ nm_proc_err ProcessErr e → Json

@ nm_missing_input → Json

@ nm_tool_check Json args → Json

@ nm_tool_build Json args → Json

@ nm_tool_build_wasm Json args → Json

── Tool: nurl_build_wasm (compile to wasm32-wasi, fully local) ──────

Drives the wasmbuilder library in-process: nurlc → wasm32 IR rewrite → the toolchain's bundled zig cc. No build service, no network (a machine with no zig anywhere downloads it once — see the wasmbuilder README).

Output shape:

<input minus .nu>.wasm

(mirrors the playground MCP tool)

@ nm_run_result Output o → Json

@ nm_tool_run Json args → Json

@ nm_tool_fmt Json args → Json

@ nm_tool_list_stdlib Json args → Json

@ nm_has_dotdot s p → b

@ nm_tool_read_stdlib Json args → Json

@ nm_stdlib_root → String

@ nm_tool_api Json args → Json

@ nm_tool_grep Json args → Json

@ nm_schema_api → Json

@ nm_schema_grep → Json

@ nm_prop Json props s name s desc → v

@ nm_schema_src_path → Json

@ nm_schema_name → Json

@ nm_schema_empty → Json

@ nm_build_ok → b

Policy predicates (read the globals set in main).

@ nm_run_ok → b

@ build_tools_list → ( Vec Json )

@ dispatch_tool s name Json args → Json

@ handle_initialize Json id → Json

@ handle_ping Json id → Json

@ handle_tools_list Json id → Json

@ handle_tools_call Json id Json params → Json

@ handle_unknown_method Json id s method → Json

@ dispatch Json req → ?Json

Transport-agnostic dispatcher: request Json → Some(response) for a request, or None for a notification (the caller drops it / replies 202).

@ stdio_loop → v

@ nm_cors HttpResponse r → v

@ nm_authed HttpRequest req → b

@ nm_is_loopback s host → b

@ run_http s host i port → i

@ main → i