nx_mcp_call.nx
buildroot/runtime/nx_mcp_call.nx
about
nx_mcp_call.nx -- the SOVEREIGN MCP client: POST a JSON-RPC tools/call to /mcp over our own TLS-1.3 and PRINT
the response body. Fills the gap surfaced 2026-07-17 ([[reference-sovereign-mcp-call-client-gap-2026-07-17]]):
nx_mgmt_client's `call` sends x-www-form-urlencoded (built for /api/login) so it mis-frames a JSON tools/call
(status=0); this client sends Content-Type: application/json + Accept + X-Nishi-Cap and echoes the body.
✅PROVEN 2026-07-17: `tools/call` WORKS end-to-end -- nishi_search returned real search hits, and
nx_dataplane_census returned 655 flat-file-debt rows (56KB, HTTP 200, isError:false) over sovereign TLS /mcp
with a least-authority cap. `initialize`/`tools/list`/`tools/call` all work; the sovereign JSON POST /mcp +
response-parse + body-print are complete. nx_mgmt_client CANNOT do this (it sends x-www-form-urlencoded -> the
daemon mis-frames the JSON -> status=0). That form-encoding -- NOT a daemon bug -- is why the doctrine's
"raw tools/call via nx_mgmt_client" path always failed. ⚠fork-exec tools are SLOW (census ~16s) so a client
needs patience; ⚠tools whose ELF FORKS CHILDREN (e.g. nx_status -> nx_hostctl sub-forks) return empty because
tr_run_capture (nx_tool_run.nx:21) dups the pipe to stdout/stderr but does NOT close the inherited request
SOCKET fd before execve -> forked grandchildren hold the connection open -> no clean close (a SEPARATE daemon
fix: add a close-fds-3..N loop in the child). Non-forking tools (search, census) work today.
tool_name "__initialize__" sends an initialize (open, no cap) probe. [[reference-sovereign-mcp-call-client-gap-2026-07-17]]
nx_mcp_call <base_url> <tool_name> <cap_file> [args_json_fragment]
e.g. nx_mcp_call https://nishifamily.com nx_dataplane_census /tmp/capfile
args_json_fragment (optional) is spliced into arguments after _cap, e.g. "argv":["a","b"]
REUSE (compose, don't rebuild TLS): copies nx_mgmt_client's PROVEN transport (build_request/send_drain/req)
verbatim -- renamed mc_* -- flipping ONLY the content-type. DRY debt: extract the shared transport into a
no-main lib later (nx_mgmt_client has a main(), so it can't be imported today). license_tier: ORIGINAL
dependencies 10 imports · 0 importers
imports: nx_acme_http.nxnx_itoa_lib.nxnx_https_url_for_fetch.nxnx_https_url_connect.nxnx_tls13_client_session_run.nxnx_tls13_client_session.nxnx_tls13.nxnx_tls13_record.nxnx_tls13_read_record_from_fd.nxnx_csprng.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 32 | const MC_MAGIC_16384: i64 = 16384 |
| 33 | const MC_MAGIC_16645: i64 = 16645 |
| 34 | const MC_MAGIC_4194304: i64 = 4194304 |
| 35 | const MC_MAGIC_8192: i64 = 8192 |
| 36 | const MC_MAGIC_8191: i64 = 8191 |
| 37 | const MC_MAGIC_65536: i64 = 65536 |
| 38 | const MC_MAGIC_4096: i64 = 4096 |
| 39 | const MC_MAGIC_131072: i64 = 131072 |
| 40 | const MC_MAGIC_1048576: i64 = 1048576 |
| 42 | const MC_CONNECT_FAIL: i64 = 0 - 11 |
| 43 | const MC_HANDSHAKE_FAIL: i64 = 0 - 12 |
functions
| 45 | func mc_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } called by 1: main |
| 50 | func mc_putn(v: i64) -> i64 { nxi_out(v); return 0 } |
| 51 | func mc_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } called by 1: main |
| 52 | func mc_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o + i] = s[i]; i = i + 1 } return o + i } |
| 53 | func mc_catb(d: *u8, o: i64, s: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { d[o + i] = s[i]; i = i + 1 } return o + i } |
| 54 | func mc_catn(d: *u8, o: i64, v: i64) -> i64 called by 1: mc_build_request |
| 63 | func mc_write_n(fd: i64, buf: *u8, n: i64) -> i64 called by 1: mc_send_drain |
| 72 | func mc_read_file(path: *u8, out: *u8, cap: i64) -> i64 |
| 84 | func mc_rtrim_nl(buf: *u8, n: i64) -> i64 called by 1: main |
| 93 | func mc_join_url(base_url: *u8, suffix: *u8, out: *u8) -> i64 |
| 103 | func mc_build_request(method: *u8, method_len: i64, path: *u8, path_len: i64, host: *u8, host_len: i64, cap: *u8, cap_len: i64, ct: *u8, ct_len: i64, body: *u8, body_len: i64, out: *u8) -> i64 |
| 131 | func mc_send_drain(s: *Tls13ClientSession, fd: i64, req: *u8, req_len: i64, out: *u8, out_cap: i64) -> i64 |
| 177 | func mc_req(store: *TrustStore, full_url: *u8, target: *NxHttpsTarget, req: *u8, req_len: i64, out: *u8, out_cap: i64) -> i64 |
| 201 | func main(argc: i64, argv: *i64) -> i64 |