nx_mcp.nx source
↩ module page · 249 lines · 13017 B
1// nx_mcp.nx -- THE SOVEREIGN MCP CLIENT. Retires nishi-ops/nxmcp.py (Python) and nxfsedit.ps1
2// (PowerShell) from the operating path: composing a tools/call envelope is FORMATTING, and formatting
3// is an organ's job, not a script's (rule 29 -- NishiLang for everything, shell is break-glass only).
4//
5// nx_mcp <tool> <capfile> [argv...]
6// any argv element of the form @@FILE:<path> is replaced by that FILE'S CONTENT (positional, because
7// organs differ on where a payload goes -- nx_fs_write is `write <path> <content> [expect=]`, so a
8// "content is always last" helper silently passes the expect flag AS the content).
9//
10// WHY THIS EXISTS, MEASURED 2026-08-10: the Python client returned `{}` on a 36,616 B source push and
11// left the NAS file at 33,955 B -- a state matching NEITHER the new bytes NOR the previous ones, which
12// then COMPILED CLEAN and produced a plausible binary. A transport that can silently corrupt the thing
13// it is shipping is not a transport. ★★★★★★ A FOREIGN-RUNTIME DEPENDENCY IN THE SHIP PATH IS A DEFECT
14// YOU CANNOT DEBUG WITH YOUR OWN INSTRUMENTS -- I could not determine the cause because the failure was
15// inside somebody else's json encoder, and "undetermined" is the honest verdict a sovereign stack should
16// never have to write about its own ship lane.
17//
18// COMPOSITION, NOT REIMPLEMENTATION: the TLS-1.3 wire is nx_mgmt_client (proven, sovereign, already the
19// lane for /api/build|promote|deploy). This organ owns exactly ONE thing -- a correct RFC 8259 envelope.
20// license_tier: ORIGINAL
21import "nx_syscalls.nx"
22import "nx_json_lib.nx"
23
24const MC_MAGIC_8388608: i64 = 8388608
25const MC_MAGIC_2048: i64 = 2048
26const MC_MAGIC_512: i64 = 512
27
28const MC_BODY_CAP: i64 = 8388608 // request envelope
29const MC_RESP_CAP: i64 = 8388608 // response
30const MC_FILE_CAP: i64 = 4194304 // one @@FILE payload
31const MC_URL: *u8 = "https://nishifamily.com"
32const MC_PATH: *u8 = "/mcp"
33const MC_CLIENT: *u8 = "./_offc/nx_mgmt_client.elf"
34
35func 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 }
36func mc_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
37func mc_cat(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[o + i] = s[i]; i = i + 1 } return o + i }
38func mc_num(dst: *u8, o: i64, v: i64) -> i64 {
39 let t: *u8 = sys_mmap(32)
40 var m: i64 = v
41 var k: i64 = 0
42 if m == 0 { t[0] = 48 as u8; k = 1 }
43 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
44 var w: i64 = o
45 var i: i64 = 0
46 while i < k { dst[w] = t[k - 1 - i]; w = w + 1; i = i + 1 }
47 return w
48}
49func mc_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] == (0 as u8) { return 1 } return 0 }
50
51// ---------------------------------------------------------------------------------------------
52// RFC 8259 string escape, and NOTHING ELSE. Exactly: quote, backslash, and the C0 controls.
53// ★ UTF-8 BYTES PASS THROUGH UNTOUCHED. Escaping them to \uXXXX is what breaks this wire: the
54// sovereign JSON parser does not decode \uXXXX -- it DROPS THE BACKSLASH -- so an ascii-safe encoder
55// turns a star into the literal text u2605 and the write still answers OK. '>' and '/' need no escape.
56func mc_esc(dst: *u8, o0: i64, src: *u8, n: i64) -> i64 {
57 var o: i64 = o0
58 var i: i64 = 0
59 while i < n {
60 let c: i64 = src[i] as i64
61 if c == 34 { dst[o] = 92 as u8; o = o + 1; dst[o] = 34 as u8; o = o + 1 } else {
62 if c == 92 { dst[o] = 92 as u8; o = o + 1; dst[o] = 92 as u8; o = o + 1 } else {
63 if c == 8 { dst[o] = 92 as u8; o = o + 1; dst[o] = 98 as u8; o = o + 1 } else {
64 if c == 9 { dst[o] = 92 as u8; o = o + 1; dst[o] = 116 as u8; o = o + 1 } else {
65 if c == 10 { dst[o] = 92 as u8; o = o + 1; dst[o] = 110 as u8; o = o + 1 } else {
66 if c == 12 { dst[o] = 92 as u8; o = o + 1; dst[o] = 102 as u8; o = o + 1 } else {
67 if c == 13 { dst[o] = 92 as u8; o = o + 1; dst[o] = 114 as u8; o = o + 1 } else {
68 if c < 32 {
69 // the remaining C0 controls are rare and MUST be escaped; \u00XX is correct here because
70 // the value is pure ASCII hex, not a multi-byte character.
71 dst[o] = 92 as u8; o = o + 1
72 dst[o] = 117 as u8; o = o + 1
73 dst[o] = 48 as u8; o = o + 1
74 dst[o] = 48 as u8; o = o + 1
75 let hi: i64 = (c / 16) % 16
76 let lo: i64 = c % 16
77 if hi < 10 { dst[o] = (48 + hi) as u8 } else { dst[o] = (87 + hi) as u8 }
78 o = o + 1
79 if lo < 10 { dst[o] = (48 + lo) as u8 } else { dst[o] = (87 + lo) as u8 }
80 o = o + 1
81 } else { dst[o] = c as u8; o = o + 1 } } } } } } } }
82 i = i + 1
83 }
84 return o
85}
86
87// read a whole file into buf; returns bytes, or -1. Loops: one read() on a regular file may return short.
88func mc_read_file(path: *u8, buf: *u8, cap: i64) -> i64 {
89 let fd: i64 = sys_openat_rd(path)
90 if fd < 0 { return 0 - 1 }
91 var n: i64 = 0
92 var run: i64 = 1
93 while run == 1 {
94 let k: i64 = sys_read(fd, ((buf as i64) + n) as *u8, cap - n)
95 if k <= 0 { run = 0 } else { n = n + k; if n >= cap { run = 0 } }
96 }
97 sys_close(fd)
98 return n
99}
100
101// "@@FILE:<path>" -> that file's CONTENT into out; else a copy of the literal. Returns byte length,
102// or -1 when a named file cannot be read (REFUSE: shipping the literal string "@@FILE:/x" as if it
103// were the payload is the silent-corruption shape this organ exists to end).
104func mc_resolve(arg: *u8, out: *u8, cap: i64) -> i64 {
105 let pfx: *u8 = "@@FILE:" as *u8
106 var isf: i64 = 1
107 var i: i64 = 0
108 while i < 7 { if arg[i] != pfx[i] { isf = 0; i = 7 } else { i = i + 1 } }
109 if isf == 0 {
110 let n: i64 = mc_slen(arg)
111 var k: i64 = 0
112 while k < n { if k < cap { out[k] = arg[k] } k = k + 1 }
113 return n
114 }
115 return mc_read_file(((arg as i64) + 7) as *u8, out, cap)
116}
117
118func main(argc: i64, argv: *i64) -> i64 {
119 if argc < 3 {
120 mc_puts("usage: nx_mcp <tool> <capfile> [argv...] (an argv of @@FILE:<path> sends that file's CONTENT)\n" as *u8)
121 sys_exit(2); return 2
122 }
123 let tool: *u8 = argv[1] as *u8
124 let capf: *u8 = argv[2] as *u8
125
126 let cap: *u8 = sys_mmap(MC_MAGIC_2048)
127 let capn0: i64 = mc_read_file(capf, cap, MC_MAGIC_2048)
128 if capn0 <= 0 { mc_puts("nx_mcp: cannot read capfile\n" as *u8); sys_exit(3); return 3 }
129 // trim trailing whitespace/newline: a cap with a stray \n is rejected by the server as a bad token,
130 // and the error it returns talks about capabilities, not about your file's last byte.
131 var capn: i64 = capn0
132 while capn > 0 { let c: i64 = cap[capn - 1] as i64; if c == 10 { capn = capn - 1 } else { if c == 13 { capn = capn - 1 } else { if c == 32 { capn = capn - 1 } else { capn = 0 - capn } } } }
133 if capn < 0 { capn = 0 - capn }
134
135 let body: *u8 = sys_mmap(MC_BODY_CAP)
136 var o: i64 = mc_cat(body, 0, "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"" as *u8)
137 o = mc_esc(body, o, tool, mc_slen(tool))
138 o = mc_cat(body, o, "\",\"arguments\":{\"argv\":[" as *u8)
139 let scratch: *u8 = sys_mmap(MC_FILE_CAP)
140 var i: i64 = 3
141 var emitted: i64 = 0
142 while i < argc {
143 let raw: *u8 = argv[i] as *u8
144 let rn: i64 = mc_resolve(raw, scratch, MC_FILE_CAP)
145 if rn < 0 { mc_puts("nx_mcp: @@FILE payload unreadable -- REFUSING to send the literal\n" as *u8); sys_exit(4); return 4 }
146 if emitted > 0 { body[o] = 44 as u8; o = o + 1 }
147 body[o] = 34 as u8; o = o + 1
148 o = mc_esc(body, o, scratch, rn)
149 body[o] = 34 as u8; o = o + 1
150 emitted = emitted + 1
151 i = i + 1
152 }
153 o = mc_cat(body, o, "],\"_cap\":\"" as *u8)
154 o = mc_esc(body, o, cap, capn)
155 o = mc_cat(body, o, "\"}}}" as *u8)
156
157 // Unique scratch so concurrent invocations cannot read each other's envelope.
158 // ⚠ NOT getpid: raw __syscall(39) is an x86_64 number with no wrapper here, and it returned a
159 // NEGATIVE value -- which mc_num renders as NOTHING, so all three temp paths silently collapsed to
160 // the same name (/tmp/nx_mcp_resp_.json). The wall clock is portable and already proven this session.
161 let pid: i64 = sys_now_realtime_us()
162 let bpath: *u8 = sys_mmap(MC_MAGIC_512)
163 var bp: i64 = mc_cat(bpath, 0, "/tmp/nx_mcp_body_" as *u8); bp = mc_num(bpath, bp, pid); bp = mc_cat(bpath, bp, ".json" as *u8); bpath[bp] = 0 as u8
164 let tpath: *u8 = sys_mmap(MC_MAGIC_512)
165 var tp: i64 = mc_cat(tpath, 0, "/tmp/nx_mcp_tok_" as *u8); tp = mc_num(tpath, tp, pid); tp = mc_cat(tpath, tp, ".txt" as *u8); tpath[tp] = 0 as u8
166 let rpath: *u8 = sys_mmap(MC_MAGIC_512)
167 var rp: i64 = mc_cat(rpath, 0, "/tmp/nx_mcp_resp_" as *u8); rp = mc_num(rpath, rp, pid); rp = mc_cat(rpath, rp, ".json" as *u8); rpath[rp] = 0 as u8
168
169 let bfd: i64 = sys_openat_wr(bpath, 0x1a4)
170 if bfd < 0 { mc_puts("nx_mcp: cannot write envelope\n" as *u8); sys_exit(5); return 5 }
171 sys_write(bfd, body, o)
172 sys_close(bfd)
173 // /mcp authenticates from _cap IN THE BODY, so the token file is unused -- but the client's argv
174 // grammar requires one, and an ABSENT file makes it fail for a reason that has nothing to do with auth.
175 let tfd: i64 = sys_openat_wr(tpath, 0x1a4)
176 if tfd >= 0 { sys_write(tfd, "x\n" as *u8, 2); sys_close(tfd) }
177
178 // fork the PROVEN sovereign client; capture its stdout so we can unwrap the envelope.
179 let cargv: *i64 = sys_mmap(80) as *i64
180 cargv[0] = MC_CLIENT as i64
181 cargv[1] = MC_URL as i64
182 cargv[2] = "call" as i64
183 cargv[3] = "POST" as i64
184 cargv[4] = MC_PATH as i64
185 cargv[5] = tpath as i64
186 cargv[6] = bpath as i64
187 cargv[7] = 0
188 let envp: *i64 = sys_mmap(8) as *i64
189 envp[0] = 0
190 let child: i64 = sys_fork()
191 if child == 0 {
192 let rfd: i64 = sys_openat_wr(rpath, 0x1a4)
193 if rfd < 0 { sys_exit(126) }
194 // ★ sys_dup3, NOT a raw dup2. RV64 HAS NO dup2 -- the syscall was removed from the arch, and
195 // this estate builds for RV64 as well as x86_64 (SYS_DUP3 = 292 / 24 respectively). A raw
196 // __syscall(33) compiles fine, silently fails, and the ONLY symptom is a zero-byte capture that
197 // looks like "the server said nothing". ★★★★★ A HARDCODED SYSCALL NUMBER IS AN ARCHITECTURE
198 // ASSUMPTION WEARING THE COSTUME OF A CONSTANT.
199 sys_dup3(rfd, 1, 0)
200 sys_execve(MC_CLIENT, cargv, envp)
201 sys_exit(127)
202 }
203 let st: *i64 = sys_mmap(16) as *i64
204 sys_wait4(child, st, 0)
205 let rc: i64 = (st[0] >> 8) & 0xff
206
207 let resp: *u8 = sys_mmap(MC_RESP_CAP)
208 let rn2: i64 = mc_read_file(rpath, resp, MC_RESP_CAP)
209 __syscall(263, 0 - 100, bpath as i64, 0, 0, 0, 0)
210 __syscall(263, 0 - 100, tpath as i64, 0, 0, 0, 0)
211 // ⚠ KEEP the response file when capture failed: deleting the only evidence of a failure is how a
212 // transport bug stays unexplained. It is unlinked ONLY on the success path below.
213 if rn2 > 0 { __syscall(263, 0 - 100, rpath as i64, 0, 0, 0, 0) }
214 if rn2 <= 0 {
215 mc_puts("nx_mcp: TRANSPORT-SILENT rn2=" as *u8)
216 let nb0: *u8 = sys_mmap(16); let ne0: i64 = mc_num(nb0, 0, rn2); sys_write(1, nb0, ne0)
217 mc_puts(" kept=" as *u8); mc_puts(rpath)
218 mc_puts(" (client rc=" as *u8)
219 let nb: *u8 = sys_mmap(16); let ne: i64 = mc_num(nb, 0, rc); sys_write(1, nb, ne)
220 // ★ never print an empty success: a silent transport is an UNKNOWN outcome, and the call may
221 // well have LANDED. Say so, so the caller checks the artifact instead of blindly retrying.
222 mc_puts(") -- OUTCOME UNKNOWN, the call may have landed: VERIFY THE ARTIFACT before retrying\n" as *u8)
223 sys_exit(6); return 6
224 }
225
226 // unwrap {"result":{"content":[{"type":"text","text":"..."}]}} -> the organ's own bytes.
227 // Anchored on the LAST "text":" so a tool whose OUTPUT mentions the field name cannot hijack it.
228 let out: *u8 = sys_mmap(MC_RESP_CAP)
229 var at: i64 = 0 - 1
230 var scan: i64 = 0
231 var run3: i64 = 1
232 while run3 == 1 {
233 let f: i64 = jx_find(resp, rn2, scan, "\"text\":\"" as *u8)
234 if f < 0 { run3 = 0 } else { at = f; scan = f + 8 }
235 }
236 if at < 0 {
237 // no text field: hand back the raw response rather than inventing a summary of it
238 sys_write(1, resp, rn2)
239 if rc != 0 { sys_exit(rc); return rc }
240 sys_exit(0); return 0
241 }
242 jx_copy_str(resp, rn2, at + 8, out, MC_RESP_CAP)
243 let on: i64 = mc_slen(out)
244 sys_write(1, out, on)
245 if on > 0 { if out[on - 1] != (10 as u8) { sys_write(1, "\n" as *u8, 1) } }
246 // isError is the organ's verdict, not the transport's: surface it in the exit code.
247 if jx_find(resp, rn2, 0, "\"isError\":true" as *u8) >= 0 { sys_exit(1); return 1 }
248 sys_exit(0); return 0
249}