1extern crate std;
5
6#[path = "mcp_format_impls.rs"]
7mod mcp_format_impls;
8#[path = "mcp_stub_impls.rs"]
9mod mcp_stub_impls;
10#[path = "mcp_tool_impls/mod.rs"]
11mod mcp_tool_impls;
12
13use crate::wal::append_mutation;
14use crate::NQuin;
15use core::ptr::write_volatile;
16use serde_json::{json, Value};
17use std::string::String;
18
19#[derive(Copy, Clone, PartialEq, Eq)]
21pub enum McpRuntimeState {
22 HandshakePhase,
23 AllocationFirewallActive,
24 SanctuaryGated,
25}
26
27#[derive(Debug)]
28pub enum McpSystemError {
29 SanctuaryGateTriggered,
30 ToolNotFound,
31 ToolNotReady,
32 ParseError,
33 IntentFrameViolation,
34 FeatureNotEnabled,
35 InvalidParameters,
36}
37
38#[derive(Debug, Clone)]
39pub struct McpIntentFrame {
40 pub purpose_hash: u64,
41 pub active_deontic_constraints: Vec<u64>,
42 pub active_profile_id: Option<u64>,
43 pub session_nonce: u64,
44 pub sanctuary_override: Option<[u8; 32]>,
49 pub qpu_enabled: bool,
50 pub llm_enabled: bool,
51}
52
53pub struct RawToolPayload<'a> {
55 pub tool_name: &'a [u8],
56 pub arguments_raw: &'a [u8],
57}
58
59#[derive(Clone, Copy)]
60struct McpToolDescriptor {
61 name: &'static str,
62 description: &'static str,
63 input_schema: &'static str,
64}
65
66fn extract_raw_json_string<'a>(payload: &'a [u8], key: &[u8]) -> Option<&'a [u8]> {
69 let mut i = 0;
71 while i < payload.len() {
72 if payload[i..].starts_with(key) {
73 i += key.len();
74 while i < payload.len() && (payload[i] == b' ' || payload[i] == b':') {
76 i += 1;
77 }
78 if i < payload.len() && payload[i] == b'"' {
79 i += 1;
80 let start = i;
81 while i < payload.len() && payload[i] != b'"' {
82 i += 1;
83 }
84 return Some(&payload[start..i]);
85 }
86 }
87 i += 1;
88 }
89 None
90}
91
92fn stable_mcp_tools() -> &'static [McpToolDescriptor] {
93 &[
94 McpToolDescriptor {
95 name: "query_graph",
96 description: "Run guarded graph traversal against the in-memory daemon graph.",
97 input_schema: r#"{"type":"object","properties":{"sanctuary_override":{"type":"string"}}}"#,
98 },
99 McpToolDescriptor {
100 name: "query_sparql",
101 description: "Execute an N-Triples pattern query against the in-process daemon graph.",
102 input_schema: r#"{"type":"object","required":["query"],"properties":{"query":{"type":"string"},"limit":{"type":"integer"}}}"#,
103 },
104 McpToolDescriptor {
105 name: "get_graph_stats",
106 description: "Return quin count and capacity for the resident daemon graph.",
107 input_schema: r#"{"type":"object","properties":{}}"#,
108 },
109 McpToolDescriptor {
110 name: "list_ontologies",
111 description: "List startup ontology catalog entries and on-disk presence.",
112 input_schema: r#"{"type":"object","properties":{}}"#,
113 },
114 McpToolDescriptor {
115 name: "llm_infer",
116 description: "Run local GGUF inference. Greedy by default. Optional exact seeded sampling: pass `sampler_cbor`, a hex-encoded CBOR map of SamplerConfig {temperature, top_k, top_p, repeat_penalty, freq_penalty, presence_penalty, penalty_window, seed}; temperature<=0 or absent ⇒ greedy.",
117 input_schema: r#"{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string"},"model_path":{"type":"string"},"graph_context":{"type":"string"},"sampler_cbor":{"type":"string","description":"hex-encoded CBOR map of SamplerConfig"}}}"#,
118 },
119 McpToolDescriptor {
120 name: "llm_chat",
121 description: "Multi-turn chat completion via the local GGUF inference stack.",
122 input_schema: r#"{"type":"object","required":["messages"],"properties":{"messages":{"type":"array"},"model_path":{"type":"string"},"graph_context":{"type":"string"}}}"#,
123 },
124 McpToolDescriptor {
125 name: "list_models",
126 description: "Discover GGUF models under storage and any resident mounted model.",
127 input_schema: r#"{"type":"object","properties":{}}"#,
128 },
129 McpToolDescriptor {
130 name: "qpu_optimize",
131 description: "Formulate a QUBO/circuit job from a problem description and classical solve.",
132 input_schema: r#"{"type":"object","required":["problem"],"properties":{"problem":{"type":"object"}}}"#,
133 },
134 McpToolDescriptor {
135 name: "qpu_dft",
136 description: "Bounded Thomas-Fermi DFT ground-state energy from quins or grid resolution.",
137 input_schema: r#"{"type":"object","properties":{"grid_resolution":{"type":"integer"},"quins":{"type":"array"}}}"#,
138 },
139 McpToolDescriptor {
140 name: "qpu_status",
141 description: "Return QPU bridge connection and job-queue status.",
142 input_schema: r#"{"type":"object","properties":{}}"#,
143 },
144 McpToolDescriptor {
145 name: "get_wallet_status",
146 description: "Inspect queued ILP micropayments from pending_payments.ndjson.",
147 input_schema: r#"{"type":"object","properties":{}}"#,
148 },
149 McpToolDescriptor {
150 name: "get_did_info",
151 description: "Parse a did:q42 identifier into its topological pointer hash.",
152 input_schema: r#"{"type":"object","required":["did"],"properties":{"did":{"type":"string"}}}"#,
153 },
154 McpToolDescriptor {
155 name: "ingest_ontology",
156 description: "Parse TTL/N3/q42 ontology file and extend the daemon graph.",
157 input_schema: r#"{"type":"object","required":["path"],"properties":{"path":{"type":"string"},"context_hash":{"type":"integer"}}}"#,
158 },
159 McpToolDescriptor {
160 name: "validate_shacl",
161 description: "Validate quins for a target subject/property against SHACL constraints.",
162 input_schema: r#"{"type":"object","required":["quins","target_subject","target_property","constraints"],"properties":{"quins":{"type":"array"},"target_subject":{"type":"integer"},"target_property":{"type":"integer"},"constraints":{"type":"array"}}}"#,
163 },
164 McpToolDescriptor {
165 name: "validate_enumerated_identity",
166 description: "SHACL identity extension: validate an identity as an enumerated state over multiple cryptographically-attested identifiers. A binding asserting certainty (confidence>=1.0) is rejected as a DefinitiveCollapse — identity must stay a confidence-relation (the out-of-band remainder). Returns verdict + distinct/attested counts + noisy-OR aggregate confidence.",
167 input_schema: r#"{"type":"object","required":["bindings"],"properties":{"bindings":{"type":"array","items":{"type":"object","properties":{"identifier":{"type":"integer"},"scheme":{"type":"string"},"attested":{"type":"boolean"},"confidence":{"type":"number"}}}},"min_distinct":{"type":"integer"},"min_attested":{"type":"integer"}}}"#,
168 },
169 McpToolDescriptor {
170 name: "shacl_credential_gate",
171 description: "SHACL identity extension: decide whether a verified W3C Verifiable Credential gates a SHACL target node — applies only if the credential's subject is the focus node, the issuer is accepted, and it carries the required claim. (Verify the VC signature/expiry via the identity layer first.)",
172 input_schema: r#"{"type":"object","required":["focus_node","gate","credential"],"properties":{"focus_node":{"type":"integer"},"gate":{"type":"object"},"credential":{"type":"object"}}}"#,
173 },
174 McpToolDescriptor {
175 name: "shacl_degrade_violations",
176 description: "SHACL identity extension: real-time severity degradation — off-grid, non-Critical violations degrade to non-blocking so a partial subgraph stays usable; Critical fails closed. Returns blocking/degraded counts and whether the subgraph is usable.",
177 input_schema: r#"{"type":"object","required":["violations"],"properties":{"violations":{"type":"array"},"mode":{"type":"string"}}}"#,
178 },
179 McpToolDescriptor {
180 name: "shacl_route",
181 description: "SHACL identity extension: decentralized shape-target routing — given shape->locus routes, enumerate the shapes that apply at a query_locus, or the loci a query_shape must be dispatched to (validation goes to the data, no central aggregation).",
182 input_schema: r#"{"type":"object","required":["routes"],"properties":{"routes":{"type":"array"},"query_locus":{"type":"integer"},"query_shape":{"type":"integer"}}}"#,
183 },
184 McpToolDescriptor {
185 name: "list_qapps",
186 description: "List installed qapps from storage Qapps directory.",
187 input_schema: r#"{"type":"object","properties":{}}"#,
188 },
189 McpToolDescriptor {
190 name: "get_qapp_manifest",
191 description: "Load qapp.json manifest for an installed or bundled qapp.",
192 input_schema: r#"{"type":"object","required":["qapp_name"],"properties":{"qapp_name":{"type":"string"}}}"#,
193 },
194 McpToolDescriptor {
195 name: "inspect_qapp_readiness",
196 description: "Check manifest, entrypoints, and ontology readiness for a qapp.",
197 input_schema: r#"{"type":"object","required":["qapp_name"],"properties":{"qapp_name":{"type":"string"}}}"#,
198 },
199 McpToolDescriptor {
200 name: "list_qapp_updates",
201 description: "Compare installed qapp versions against bundled update offers.",
202 input_schema: r#"{"type":"object","properties":{}}"#,
203 },
204 McpToolDescriptor {
205 name: "get_system_status",
206 description: "Return runtime status for the Qualia MCP surface.",
207 input_schema: r#"{"type":"object","properties":{}}"#,
208 },
209 McpToolDescriptor {
210 name: "list_capabilities",
211 description: "List the canonical Qualia capability catalogue with concrete operations, maturity, runtime surfaces, and executable MCP routes.",
212 input_schema: r#"{"type":"object","properties":{"domain":{"type":"string"},"maturity":{"type":"string","enum":["stable","partial","experimental","fail-closed"]},"surface":{"type":"string"}}}"#,
213 },
214 McpToolDescriptor {
215 name: "describe_qapp_surface_schema",
216 description: "Describe the current Qapp host surface schema exposed by Qualia.",
217 input_schema: r#"{"type":"object","properties":{}}"#,
218 },
219 McpToolDescriptor {
220 name: "inject_test_quin",
221 description: "Inject a deterministic test Quin through the paraconsistent router.",
222 input_schema: r#"{"type":"object","properties":{}}"#,
223 },
224 McpToolDescriptor {
225 name: "evaluate_modality",
226 description: "Evaluate logic modalities: ltl, asp, deontic, epistemic, dl, paraconsistent, probabilistic.",
227 input_schema: r#"{"type":"object","required":["modality"],"properties":{"modality":{"type":"string"},"quins":{"type":"array"},"trace":{"type":"array"},"formula":{"type":"object"},"now_unix":{"type":"integer"},"agent_did_hash":{"type":"integer"},"world_hash":{"type":"integer"}}}"#,
228 },
229 McpToolDescriptor {
230 name: "evaluate_logic_rules",
231 description: "Load N3 rules into the RuleEngine, fire them in the Webizen VM, and evaluate a Quin against the fired conclusions. Emits WAL audit events. Returns per-rule pass/fail verdicts.",
232 input_schema: r#"{"type":"object","required":["n3_source","quin"],"properties":{"n3_source":{"type":"string","description":"N3 rule text to parse and fire"},"quin":{"type":"object","description":"The Quin to evaluate","properties":{"subject":{"type":"integer"},"predicate":{"type":"integer"},"object":{"type":"integer"},"context":{"type":"integer"}}},"ruleset_name":{"type":"string","default":"default"},"contract_hash":{"type":"integer","default":0}}}"#,
233 },
234 McpToolDescriptor {
235 name: "matrix_operation",
236 description: "Linear algebra: multiply, transpose, solve, or inverse with caller-supplied matrices.",
237 input_schema: r#"{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["multiply","transpose","solve","inverse"]},"left":{"type":"object","properties":{"id":{"type":"string"},"rows":{"type":"integer"},"cols":{"type":"integer"},"data":{"type":"array","items":{"type":"number"}}}},"right":{"type":"object"},"matrices":{"type":"array"},"result_id":{"type":"string"}}}"#,
238 },
239 McpToolDescriptor {
240 name: "algebra_solve_polynomial",
241 description: "Find all (real + complex) roots of a polynomial given descending coefficients.",
242 input_schema: r#"{"type":"object","required":["coeffs"],"properties":{"coeffs":{"type":"array","items":{"type":"number"},"description":"descending coefficients [c_n, ..., c_1, c_0]"}}}"#,
243 },
244 McpToolDescriptor {
245 name: "algebra_matrix_analyze",
246 description: "Determinant, eigenvalues (general), symmetric eigensystem, or SVD of a row-major matrix.",
247 input_schema: r#"{"type":"object","required":["op","rows","data"],"properties":{"op":{"type":"string","enum":["determinant","eigenvalues","eigen_symmetric","svd"]},"rows":{"type":"integer"},"cols":{"type":"integer"},"data":{"type":"array","items":{"type":"number"}}}}"#,
248 },
249 McpToolDescriptor {
250 name: "cas",
251 description: "Symbolic algebra: differentiate/simplify/expand/evaluate a text expression (e.g. 'x^3 - 2*x^2'), or solve/factor a quadratic symbolically.",
252 input_schema: r#"{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["differentiate","simplify","expand","evaluate","solve_quadratic","factor"]},"expr":{"type":"string"},"var":{"type":"string"},"env":{"type":"object"},"a":{"type":"number"},"b":{"type":"number"},"c":{"type":"number"}}}"#,
253 },
254 McpToolDescriptor {
255 name: "ode_solve",
256 description: "Run a configurable CFD or molecular-dynamics simulation step.",
257 input_schema: r#"{"type":"object","properties":{"type":{"type":"string","enum":["cfd","distributed","molecular_dynamics"]},"nx":{"type":"integer"},"ny":{"type":"integer"},"dx":{"type":"number"},"time_step":{"type":"number"},"total_time":{"type":"number"},"num_threads":{"type":"integer"}}}"#,
258 },
259 McpToolDescriptor {
260 name: "chemical_analysis",
261 description: "Predict molecular properties from SMILES or formula via ChemistryModelingLibrary.",
262 input_schema: r#"{"type":"object","properties":{"smiles":{"type":"string"},"formula":{"type":"string"},"molecular_weight":{"type":"number"},"prop":{"type":"string"},"properties":{"type":"array","items":{"type":"string"}}}}"#,
263 },
264 McpToolDescriptor {
265 name: "statistical_analysis",
266 description: "Descriptive statistics on caller-supplied tabular data.",
267 input_schema: r#"{"type":"object","required":["rows"],"properties":{"stat":{"type":"string","enum":["mean","variance","correlation"]},"rows":{"type":"array"},"columns":{"type":"array","items":{"type":"string"}},"column":{"type":"string"},"column_y":{"type":"string"},"method":{"type":"string"}}}"#,
268 },
269 McpToolDescriptor {
270 name: "ml_inference",
271 description: "Load a model by id and run inference on caller-supplied input bytes.",
272 input_schema: r#"{"type":"object","properties":{"model_id":{"type":"string"},"model_path":{"type":"string"},"input_data":{"type":"array","items":{"type":"integer"}},"input_hex":{"type":"string"},"batch_size":{"type":"integer"},"temperature":{"type":"number"},"max_tokens":{"type":"integer"}}}"#,
273 },
274 McpToolDescriptor {
275 name: "financial_model",
276 description: "Black-Scholes option pricing or portfolio risk with caller parameters.",
277 input_schema: r#"{"type":"object","properties":{"op":{"type":"string","enum":["option","risk"]},"underlying_price":{"type":"number"},"strike":{"type":"number"},"volatility":{"type":"number"},"assets":{"type":"array"},"cash_balance":{"type":"number"}}}"#,
278 },
279 McpToolDescriptor {
280 name: "medical_score",
281 description: "Clinical analysis for a caller-supplied patient record.",
282 input_schema: r#"{"type":"object","properties":{"patient_id":{"type":"string"},"score":{"type":"string","enum":["diagnosis","treatment","prognosis","prevention"]},"patient":{"type":"object"}}}"#,
283 },
284 McpToolDescriptor {
285 name: "engineering_analysis_op",
286 description: "Structural, thermal, or dynamic FEA with caller model geometry and loads.",
287 input_schema: r#"{"type":"object","properties":{"analysis":{"type":"string","enum":["structural","thermal","dynamic"]},"model":{"type":"object"},"dimensions":{"type":"array"},"youngs_modulus":{"type":"number"}}}"#,
288 },
289 McpToolDescriptor {
290 name: "computational_geometry",
291 description: "Native Rust/WASM computational geometry over caller data: robust orientation, 2D convex hull, triangle half-edge topology, Delaunay, Voronoi, nearest site, and primitive generation (box, sphere, cylinder, plane).",
292 input_schema: r#"{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["orientation_2","convex_hull_2","triangle_topology","delaunay_2","voronoi_2","nearest_site","mesh_topology","create_box","create_sphere","create_cylinder","create_plane"]},"points":{"type":"array","items":{"type":"array","items":{"type":"number"}}},"vertex_count":{"type":"integer"},"triangles":{"type":"array","items":{"type":"array","items":{"type":"integer"}}},"width":{"type":"number"},"height":{"type":"number"},"depth":{"type":"number"},"radius":{"type":"number"},"size":{"type":"number"},"lat_segments":{"type":"integer"},"lon_segments":{"type":"integer"},"segments":{"type":"integer"},"query":{"type":"array","items":{"type":"number"}}}}"#,
293 },
294 McpToolDescriptor {
295 name: "computer_vision",
296 description: "Native computer_vision specialized lib (MIG-V2): list ops, rgb_to_gray, classical super_resolve (nearest/bilinear/bicubic/lanczos), mesh_quality_cleanup, class_score_to_sigma. Caller-supplied buffers; generative=false classical SR; GPU path used when Cool for nearest/bicubic.",
297 input_schema: r#"{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["list","capability_summary","rgb_to_gray","super_resolve","mesh_quality_cleanup","class_score_to_sigma"]},"width":{"type":"integer"},"height":{"type":"integer"},"scale":{"type":"integer"},"kernel":{"type":"string","enum":["nearest","bilinear","bicubic","lanczos3"]},"rgb":{"type":"array","items":{"type":"integer"}},"class_hash":{"type":"integer"},"score":{"type":"number"},"positions":{"type":"array","items":{"type":"array","items":{"type":"number"}}},"indices":{"type":"array","items":{"type":"integer"}},"weld_epsilon":{"type":"number"}}}"#,
298 },
299 McpToolDescriptor {
300 name: "audio_features",
301 description: "Native qualia-audio features + honest capability registry: list (all capabilities with Present/Partial/Missing/NeedsWeights status), capability_summary (counts), log_mel, pitch_yin (real YIN F0+confidence), loudness_r128 (EBU R128 LUFS). Caller-supplied PCM samples; CPU; learned heads fail closed.",
302 input_schema: r#"{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["list","capability_summary","log_mel","pitch_yin","loudness_r128"]},"samples":{"type":"array","items":{"type":"number"}},"sample_rate":{"type":"integer"},"n_mel":{"type":"integer"}}}"#,
303 },
304 McpToolDescriptor {
305 name: "geometry_manifests",
306 description: "List per-op capability manifests (backends, determinism class, resource limits) and run Reserve-mode budget queries for computational-geometry ops.",
307 input_schema: r#"{"type":"object","properties":{"op":{"type":"string","description":"Op name for a Reserve-mode budget query (omit to list all manifests)"},"device":{"type":"object","properties":{"cpu":{"type":"boolean"},"simd":{"type":"boolean"},"wgpu":{"type":"boolean"},"cuda":{"type":"boolean"},"wasm":{"type":"boolean"},"exact":{"type":"boolean"}}}}}"#,
308 },
309 McpToolDescriptor {
310 name: "bioinformatics_align",
311 description: "Pairwise nucleotide or protein alignment on caller query/target sequences.",
312 input_schema: r#"{"type":"object","required":["query","target"],"properties":{"query":{"type":"string"},"target":{"type":"string"},"mode":{"type":"string","enum":["dna","protein"]}}}"#,
313 },
314 McpToolDescriptor {
315 name: "chemical_descriptors",
316 description: "Compute Lipinski/Veber descriptors from a SMILES string.",
317 input_schema: r#"{"type":"object","required":["smiles"],"properties":{"smiles":{"type":"string"}}}"#,
318 },
319 McpToolDescriptor {
320 name: "clinical_risk",
321 description: "Clinical risk scores: framingham, cha2ds2_vasc, sofa, egfr.",
322 input_schema: r#"{"type":"object","properties":{"score":{"type":"string","enum":["framingham","cha2ds2","cha2ds2_vasc","sofa","egfr","renal"]},"age":{"type":"integer"},"input":{"type":"object"}}}"#,
323 },
324 McpToolDescriptor {
325 name: "parse_csv",
326 description: "Stream CSV into Quins via zero-heap parser. Requires field_mappings; accepts csv_data or file_path.",
327 input_schema: r#"{"type":"object","required":["field_mappings"],"properties":{"csv_data":{"type":"string"},"file_path":{"type":"string"},"field_mappings":{"type":"array","items":{"type":"object","required":["source_key"],"properties":{"source_key":{"type":"string"},"predicate":{"type":"string"},"predicate_hash":{"type":"integer"},"datatype":{"type":"string","enum":["integer","float","datetime","string"]}}}},"base_class_hash":{"type":"integer"},"context_hash":{"type":"integer"},"ingest_to_graph":{"type":"boolean"}}}"#,
328 },
329 McpToolDescriptor {
330 name: "parse_rdf",
331 description: "Parse RDF/RDF-Star (nt, turtle, nquads, trig, n3, jsonld, cbor) into quins via zero-heap streaming parsers.",
332 input_schema: r#"{"type":"object","required":["format"],"properties":{"format":{"type":"string"},"rdf_data":{"type":"string"},"file_path":{"type":"string"},"context_hash":{"type":"integer"},"ingest_to_graph":{"type":"boolean"}}}"#,
333 },
334 McpToolDescriptor {
335 name: "parse_json",
336 description: "Stream JSON objects into Quins via zero-heap parser. Requires field_mappings; accepts json_data or file_path.",
337 input_schema: r#"{"type":"object","required":["field_mappings"],"properties":{"json_data":{"type":"string"},"file_path":{"type":"string"},"field_mappings":{"type":"array","items":{"type":"object","required":["source_key"],"properties":{"source_key":{"type":"string"},"predicate":{"type":"string"},"predicate_hash":{"type":"integer"},"datatype":{"type":"string","enum":["integer","float","datetime","string"]}}}},"base_class_hash":{"type":"integer"},"context_hash":{"type":"integer"},"ingest_to_graph":{"type":"boolean"}}}"#,
338 },
339 McpToolDescriptor {
340 name: "serialize_csv",
341 description: "Serialize quins or daemon graph slice to CSV. Returns inline csv_data or writes file_path when output=file.",
342 input_schema: r#"{"type":"object","required":["headers","predicate_hashes"],"properties":{"quins":{"type":"array"},"use_graph":{"type":"boolean"},"context_hash":{"type":"integer"},"headers":{"type":"array","items":{"type":"string"}},"predicate_hashes":{"type":"array","items":{"type":"integer"}},"datatypes":{"type":"array","items":{"type":"string"}},"output":{"type":"string","enum":["inline","file"]},"file_path":{"type":"string"}}}"#,
343 },
344 McpToolDescriptor {
345 name: "serialize_json",
346 description: "Serialize quins or daemon graph slice to JSON array. Returns inline json_data or writes file_path when output=file.",
347 input_schema: r#"{"type":"object","required":["field_names","predicate_hashes"],"properties":{"quins":{"type":"array"},"use_graph":{"type":"boolean"},"context_hash":{"type":"integer"},"field_names":{"type":"array","items":{"type":"string"}},"predicate_hashes":{"type":"array","items":{"type":"integer"}},"datatypes":{"type":"array","items":{"type":"string"}},"output":{"type":"string","enum":["inline","file"]},"file_path":{"type":"string"}}}"#,
348 },
349 McpToolDescriptor {
350 name: "serialize_rdf",
351 description: "Serialize quins or graph slice to RDF/RDF-Star via resolver-backed zero-heap dispatch.",
352 input_schema: r#"{"type":"object","properties":{"quins":{"type":"array"},"use_graph":{"type":"boolean"},"context_hash":{"type":"integer"},"format":{"type":"string"},"rdf_star":{"type":"boolean"},"star":{"type":"boolean"},"output":{"type":"string","enum":["inline","file"]},"file_path":{"type":"string"}}}"#,
353 },
354 McpToolDescriptor {
355 name: "symbolic_logic_infer",
356 description: "Defeasible forward-chaining or bounded SAT with caller facts, rules, and clauses.",
357 input_schema: r#"{"type":"object","properties":{"solver":{"type":"string","enum":["defeasible","sat"]},"facts":{"type":"array"},"rules":{"type":"array"},"clauses":{"type":"array"},"max_iterations":{"type":"integer"}}}"#,
358 },
359 McpToolDescriptor {
360 name: "geometric_algebra_op",
361 description: "3D vector cross product, dot product, or angle between caller vectors.",
362 input_schema: r#"{"type":"object","required":["a","b"],"properties":{"op":{"type":"string","enum":["cross","angle","dot"]},"a":{"type":"array","items":{"type":"number"},"minItems":3,"maxItems":3},"b":{"type":"array","items":{"type":"number"},"minItems":3,"maxItems":3}}}"#,
363 },
364 McpToolDescriptor {
365 name: "run_docs_tests",
366 description: "Run docs/tests headless suites (logic, wasm, native, or both). Native/both require daemon on localhost:4242.",
367 input_schema: r#"{"type":"object","properties":{"mode":{"type":"string","enum":["logic","wasm","native","both"]}}}"#,
368 },
369 McpToolDescriptor {
370 name: "get_pending_tasks",
371 description: "Get tasks pending in the ambient orchestrator.",
372 input_schema: r#"{"type":"object","properties":{}}"#,
373 },
374 McpToolDescriptor {
375 name: "values_check",
376 description: "Values abuse-check (human-rights guard): does an agent claiming a natural-person-only dignity right trip the inverse rights-guard? Runs the real agency.n3 G1/G1' lane. agentType is a webcivics values class (CorporatePerson, ArtificialAgent, NaturalPerson, ...).",
377 input_schema: r#"{"type":"object","required":["agentType"],"properties":{"agentType":{"type":"string"},"claimsDignityRight":{"type":"boolean"}}}"#,
378 },
379 McpToolDescriptor {
380 name: "values_evaluate",
381 description: "Deontic-contract reasoner in values terms: is a norm (forbid/oblige/permit) bound to a party+action currently in force? Runs the native deontic VM and returns Active / Defeated (by an 'unless' exception) / Expired (past its window) / Malformed.",
382 input_schema: r#"{"type":"object","required":["modality","party","action"],"properties":{"modality":{"type":"string","enum":["forbid","oblige","permit"]},"party":{"type":"string"},"action":{"type":"string"},"object":{"type":"string"},"now":{"type":"integer"},"expiry":{"type":"integer"},"unless":{"type":"string"}}}"#,
383 },
384 McpToolDescriptor {
385 name: "graph_resolve",
386 description: "Resolve an identifier (IRI) against the live daemon graph: returns its modal identifier-KIND (open kind fabric — WebizenId / DidQ42 / ContentHash / ... or none for a plain dictionary reference) and its out-degree. Composes the hybrid-modality resolver (zero-alloc QuinIndex / slice scan + modal_kind) over one identity space.",
387 input_schema: r#"{"type":"object","required":["iri"],"properties":{"iri":{"type":"string"}}}"#,
388 },
389 McpToolDescriptor {
390 name: "jural_correlate",
391 description: "Hohfeldian jural relations: given one of the 8 positions (claim, duty, privilege, no-right, power, liability, immunity, disability) return the correlative the counterparty necessarily bears, the jural opposite, and its order (first-order conduct vs second-order control). Runs modalities/jural.rs.",
392 input_schema: r#"{"type":"object","required":["position"],"properties":{"position":{"type":"string","enum":["claim","duty","privilege","no-right","power","liability","immunity","disability"]}}}"#,
393 },
394 McpToolDescriptor {
395 name: "deontic_govern",
396 description: "Interaction governance: map a deontic verdict status (active/violated/defeated/expired/pending/discharged/malformed) + classification (nonDerogable/humanitarian/ambiguous) to the runtime PolicyMode the Webizen VM enacts — PreventiveBlock(DenyRollback) | PermissiveAudit(WAL) | Prioritize | Interactive | Allow. Runs interaction_governance::map_policy.",
397 input_schema: r#"{"type":"object","required":["status"],"properties":{"status":{"type":"string"},"nonDerogable":{"type":"boolean"},"humanitarian":{"type":"boolean"},"ambiguous":{"type":"boolean"}}}"#,
398 },
399 McpToolDescriptor {
400 name: "mcp_cooperate",
401 description: "Agent-cooperation gate (Track M): decide whether a VERIFIED, TYPED, GROUNDED calling agent's request passes the deontic gate before execution. Composes mcp_cooperation::authorize — an asserted-not-verified identity is DeniedUnverified; an ungrounded artificial agent (no human Principal, agency.n3 G1') is DeniedUngrounded; else the request runs the Phase-6 policy gate. Trust is behaviourally derived, not self-asserted.",
402 input_schema: r#"{"type":"object","required":["caller","verified"],"properties":{"caller":{"type":"string"},"role":{"type":"string"},"verified":{"type":"boolean"},"grounded":{"type":"boolean"},"requestStatus":{"type":"string"},"nonDerogable":{"type":"boolean"},"humanitarian":{"type":"boolean"},"ambiguous":{"type":"boolean"}}}"#,
403 },
404 ]
405}
406
407pub unsafe fn enforce_fiduciary_tool_dispatch(
409 payload: RawToolPayload,
410 intent_frame: &McpIntentFrame,
411) -> Result<String, McpSystemError> {
412 mcp_tool_impls::cooperation_gate(payload.arguments_raw)?;
416
417 match payload.tool_name {
418 b"query_graph" => {
420 if intent_frame.sanctuary_override.is_none() {
424 let violation_quin = NQuin::new_conduct_violation(
425 b"EgressViolation: Invalid or Missing Cryptographic Sanctuary Override",
426 );
427 let _ = append_mutation(&violation_quin);
428 return Err(McpSystemError::SanctuaryGateTriggered);
429 }
430 execute_bare_metal_graph_traversal(payload.arguments_raw, intent_frame)
431 }
432
433 b"query_sparql" => execute_sparql_query(payload.arguments_raw, intent_frame),
434
435 b"get_graph_stats" => execute_graph_stats(payload.arguments_raw, intent_frame),
436
437 b"list_ontologies" => execute_list_ontologies(payload.arguments_raw, intent_frame),
438
439 b"llm_infer" => {
441 if !intent_frame.llm_enabled {
442 return Err(McpSystemError::FeatureNotEnabled);
443 }
444 execute_llm_infer(payload.arguments_raw, intent_frame)
445 }
446
447 b"llm_chat" => {
448 if !intent_frame.llm_enabled {
449 return Err(McpSystemError::FeatureNotEnabled);
450 }
451 execute_llm_chat(payload.arguments_raw, intent_frame)
452 }
453
454 b"list_models" => execute_list_models(payload.arguments_raw, intent_frame),
455
456 b"qpu_optimize" => {
458 if !intent_frame.qpu_enabled {
459 return Err(McpSystemError::FeatureNotEnabled);
460 }
461 execute_qpu_optimize(payload.arguments_raw, intent_frame)
462 }
463
464 b"qpu_dft" => {
465 if !intent_frame.qpu_enabled {
466 return Err(McpSystemError::FeatureNotEnabled);
467 }
468 execute_qpu_dft(payload.arguments_raw, intent_frame)
469 }
470
471 b"qpu_status" => execute_qpu_status(payload.arguments_raw, intent_frame),
472
473 b"matrix_operation" => execute_matrix_operation(payload.arguments_raw, intent_frame),
475 b"algebra_solve_polynomial" => {
476 mcp_tool_impls::algebra_solve_polynomial(payload.arguments_raw)
477 }
478 b"algebra_matrix_analyze" => mcp_tool_impls::algebra_matrix_analyze(payload.arguments_raw),
479 b"cas" => mcp_tool_impls::cas(payload.arguments_raw),
480
481 b"ode_solve" => execute_ode_solve(payload.arguments_raw, intent_frame),
482
483 b"chemical_analysis" => execute_chemical_analysis(payload.arguments_raw, intent_frame),
484
485 b"statistical_analysis" => {
486 execute_statistical_analysis(payload.arguments_raw, intent_frame)
487 }
488
489 b"ml_inference" => execute_ml_inference(payload.arguments_raw, intent_frame),
490
491 b"financial_model" => execute_financial_model(payload.arguments_raw, intent_frame),
492
493 b"medical_score" => execute_medical_score(payload.arguments_raw, intent_frame),
494
495 b"engineering_analysis_op" => {
496 execute_engineering_analysis(payload.arguments_raw, intent_frame)
497 }
498 b"computational_geometry" => mcp_tool_impls::computational_geometry(payload.arguments_raw),
499 b"geometry_manifests" => mcp_tool_impls::geometry_manifests(payload.arguments_raw),
500 b"computer_vision" => mcp_tool_impls::computer_vision(payload.arguments_raw),
501 #[cfg(not(target_arch = "wasm32"))]
502 b"audio_features" => mcp_tool_impls::audio_features(payload.arguments_raw),
503
504 b"get_wallet_status" => execute_wallet_status(payload.arguments_raw, intent_frame),
506
507 b"get_did_info" => execute_did_info(payload.arguments_raw, intent_frame),
508
509 b"ingest_ontology" => execute_ingest_ontology(payload.arguments_raw, intent_frame),
511
512 b"validate_shacl" => execute_shacl_validation(payload.arguments_raw, intent_frame),
513
514 b"validate_enumerated_identity" => {
516 execute_validate_enumerated_identity(payload.arguments_raw, intent_frame)
517 }
518 b"shacl_credential_gate" => {
519 execute_shacl_credential_gate(payload.arguments_raw, intent_frame)
520 }
521 b"shacl_degrade_violations" => {
522 execute_shacl_degrade_violations(payload.arguments_raw, intent_frame)
523 }
524 b"shacl_route" => execute_shacl_route(payload.arguments_raw, intent_frame),
525
526 b"inject_test_quin" => {
528 execute_paraconsistent_injection(payload.arguments_raw, intent_frame)
529 }
530
531 b"list_qapps" => execute_list_qapps(payload.arguments_raw, intent_frame),
532
533 b"get_qapp_manifest" => execute_get_qapp_manifest(payload.arguments_raw, intent_frame),
534
535 b"inspect_qapp_readiness" => {
536 execute_inspect_qapp_readiness(payload.arguments_raw, intent_frame)
537 }
538
539 b"list_qapp_updates" => execute_list_qapp_updates(payload.arguments_raw, intent_frame),
540
541 b"describe_qapp_surface_schema" => {
542 execute_describe_qapp_surface_schema(payload.arguments_raw, intent_frame)
543 }
544
545 b"get_system_status" => execute_system_status(payload.arguments_raw, intent_frame),
546 b"list_capabilities" => mcp_tool_impls::list_capabilities(payload.arguments_raw),
547
548 b"evaluate_modality" => execute_evaluate_modality(payload.arguments_raw, intent_frame),
550
551 b"evaluate_logic_rules" => mcp_tool_impls::evaluate_logic_rules(payload.arguments_raw),
552
553 b"bioinformatics_align" => {
554 execute_bioinformatics_align(payload.arguments_raw, intent_frame)
555 }
556
557 b"chemical_descriptors" => {
558 execute_chemical_descriptors(payload.arguments_raw, intent_frame)
559 }
560
561 b"clinical_risk" => execute_clinical_risk(payload.arguments_raw, intent_frame),
562
563 b"symbolic_logic_infer" => {
564 execute_symbolic_logic_infer(payload.arguments_raw, intent_frame)
565 }
566
567 b"geometric_algebra_op" => {
568 execute_geometric_algebra_op(payload.arguments_raw, intent_frame)
569 }
570
571 b"parse_csv" => execute_parse_csv(payload.arguments_raw, intent_frame),
573 b"parse_rdf" => execute_parse_rdf(payload.arguments_raw, intent_frame),
574 b"parse_json" => execute_parse_json(payload.arguments_raw, intent_frame),
575 b"serialize_csv" => execute_serialize_csv(payload.arguments_raw, intent_frame),
576 b"serialize_json" => execute_serialize_json(payload.arguments_raw, intent_frame),
577 b"serialize_rdf" => execute_serialize_rdf(payload.arguments_raw, intent_frame),
578
579 b"run_docs_tests" => execute_run_docs_tests(payload.arguments_raw, intent_frame),
580 b"get_pending_tasks" => execute_get_pending_tasks(payload.arguments_raw, intent_frame),
581
582 b"values_check" => mcp_tool_impls::values_check(payload.arguments_raw),
584 b"values_evaluate" => mcp_tool_impls::values_evaluate(payload.arguments_raw),
585 b"jural_correlate" => mcp_tool_impls::jural_correlate(payload.arguments_raw),
586 b"deontic_govern" => mcp_tool_impls::deontic_govern(payload.arguments_raw),
587 b"mcp_cooperate" => mcp_tool_impls::mcp_cooperate(payload.arguments_raw),
588 b"graph_resolve" => mcp_tool_impls::graph_resolve(payload.arguments_raw),
589
590 _ => Err(McpSystemError::ToolNotFound),
591 }
592}
593
594unsafe fn execute_get_pending_tasks(
595 args: &[u8],
596 _intent: &McpIntentFrame,
597) -> Result<String, McpSystemError> {
598 mcp_stub_impls::get_pending_tasks(args)
599}
600
601unsafe fn execute_sparql_query(
604 args: &[u8],
605 _intent: &McpIntentFrame,
606) -> Result<String, McpSystemError> {
607 mcp_stub_impls::query_sparql(args)
608}
609
610unsafe fn execute_bare_metal_graph_traversal(
611 _args: &[u8],
612 intent: &McpIntentFrame,
613) -> Result<String, McpSystemError> {
614 let mut arena = crate::webizen::SlgArena::new();
615 let contract = if intent
616 .active_deontic_constraints
617 .first()
618 .copied()
619 .unwrap_or(0)
620 != 0
621 {
622 intent.active_deontic_constraints[0]
623 } else {
624 intent.purpose_hash
625 };
626 let fired = arena.fire_registered_rules(contract);
627 Ok(fired.max(1).to_string())
628}
629
630unsafe fn execute_graph_stats(
631 args: &[u8],
632 _intent: &McpIntentFrame,
633) -> Result<String, McpSystemError> {
634 mcp_stub_impls::get_graph_stats(args)
635}
636
637unsafe fn execute_list_ontologies(
638 args: &[u8],
639 _intent: &McpIntentFrame,
640) -> Result<String, McpSystemError> {
641 mcp_stub_impls::list_ontologies(args)
642}
643
644unsafe fn execute_llm_infer(
647 args: &[u8],
648 _intent: &McpIntentFrame,
649) -> Result<String, McpSystemError> {
650 mcp_stub_impls::llm_infer(args)
651}
652
653unsafe fn execute_llm_chat(
654 args: &[u8],
655 _intent: &McpIntentFrame,
656) -> Result<String, McpSystemError> {
657 mcp_stub_impls::llm_chat(args)
658}
659
660unsafe fn execute_list_models(
661 args: &[u8],
662 _intent: &McpIntentFrame,
663) -> Result<String, McpSystemError> {
664 mcp_stub_impls::list_models(args)
665}
666
667unsafe fn execute_qpu_optimize(
670 args: &[u8],
671 _intent: &McpIntentFrame,
672) -> Result<String, McpSystemError> {
673 mcp_stub_impls::qpu_optimize(args)
674}
675
676unsafe fn execute_qpu_dft(args: &[u8], _intent: &McpIntentFrame) -> Result<String, McpSystemError> {
677 mcp_stub_impls::qpu_dft(args)
678}
679
680unsafe fn execute_qpu_status(
681 args: &[u8],
682 _intent: &McpIntentFrame,
683) -> Result<String, McpSystemError> {
684 mcp_stub_impls::qpu_status(args)
685}
686
687unsafe fn execute_matrix_operation(
690 args: &[u8],
691 _intent: &McpIntentFrame,
692) -> Result<String, McpSystemError> {
693 mcp_tool_impls::matrix_operation(args)
694}
695
696unsafe fn execute_ode_solve(
697 args: &[u8],
698 _intent: &McpIntentFrame,
699) -> Result<String, McpSystemError> {
700 mcp_tool_impls::ode_solve(args)
701}
702
703unsafe fn execute_chemical_analysis(
704 args: &[u8],
705 _intent: &McpIntentFrame,
706) -> Result<String, McpSystemError> {
707 mcp_tool_impls::chemical_analysis(args)
708}
709
710unsafe fn execute_statistical_analysis(
711 args: &[u8],
712 _intent: &McpIntentFrame,
713) -> Result<String, McpSystemError> {
714 mcp_tool_impls::statistical_analysis(args)
715}
716
717unsafe fn execute_ml_inference(
718 args: &[u8],
719 _intent: &McpIntentFrame,
720) -> Result<String, McpSystemError> {
721 mcp_tool_impls::ml_inference(args)
722}
723
724unsafe fn execute_financial_model(
725 args: &[u8],
726 _intent: &McpIntentFrame,
727) -> Result<String, McpSystemError> {
728 mcp_tool_impls::financial_model(args)
729}
730
731unsafe fn execute_medical_score(
732 args: &[u8],
733 _intent: &McpIntentFrame,
734) -> Result<String, McpSystemError> {
735 mcp_tool_impls::medical_score(args)
736}
737
738unsafe fn execute_engineering_analysis(
739 args: &[u8],
740 _intent: &McpIntentFrame,
741) -> Result<String, McpSystemError> {
742 mcp_tool_impls::engineering_analysis(args)
743}
744
745unsafe fn execute_wallet_status(
748 args: &[u8],
749 _intent: &McpIntentFrame,
750) -> Result<String, McpSystemError> {
751 mcp_stub_impls::get_wallet_status(args)
752}
753
754unsafe fn execute_did_info(
755 args: &[u8],
756 _intent: &McpIntentFrame,
757) -> Result<String, McpSystemError> {
758 mcp_stub_impls::get_did_info(args)
759}
760
761unsafe fn execute_ingest_ontology(
764 args: &[u8],
765 _intent: &McpIntentFrame,
766) -> Result<String, McpSystemError> {
767 mcp_stub_impls::ingest_ontology(args)
768}
769
770unsafe fn execute_shacl_validation(
771 args: &[u8],
772 _intent: &McpIntentFrame,
773) -> Result<String, McpSystemError> {
774 mcp_stub_impls::validate_shacl(args)
775}
776
777unsafe fn execute_validate_enumerated_identity(
778 args: &[u8],
779 _intent: &McpIntentFrame,
780) -> Result<String, McpSystemError> {
781 mcp_stub_impls::validate_enumerated_identity_tool(args)
782}
783
784unsafe fn execute_shacl_credential_gate(
785 args: &[u8],
786 _intent: &McpIntentFrame,
787) -> Result<String, McpSystemError> {
788 mcp_stub_impls::shacl_credential_gate(args)
789}
790
791unsafe fn execute_shacl_degrade_violations(
792 args: &[u8],
793 _intent: &McpIntentFrame,
794) -> Result<String, McpSystemError> {
795 mcp_stub_impls::shacl_degrade_violations(args)
796}
797
798unsafe fn execute_shacl_route(
799 args: &[u8],
800 _intent: &McpIntentFrame,
801) -> Result<String, McpSystemError> {
802 mcp_stub_impls::shacl_route(args)
803}
804
805unsafe fn execute_paraconsistent_injection(
808 _args: &[u8],
809 intent: &McpIntentFrame,
810) -> Result<String, McpSystemError> {
811 let candidate = NQuin {
812 subject: intent.purpose_hash,
813 predicate: crate::q_hash("q42:testClaim"),
814 object: intent.session_nonce,
815 context: intent.purpose_hash,
816 metadata: 0,
817 parity: 0,
818 };
819 let mut q = candidate;
820 q.parity = q.subject ^ q.predicate ^ q.object ^ q.context;
821
822 let mut consistent = [NQuin::default(); 8];
823 let mut isolated = [NQuin::default(); 8];
824 let (c, i) = crate::modalities::paraconsistent::route_paraconsistent(
825 &[q],
826 &mut consistent,
827 &mut isolated,
828 )
829 .map_err(|_| McpSystemError::ParseError)?;
830
831 for idx in 0..i {
832 let _ = append_mutation(&isolated[idx]);
833 }
834 Ok((c + i).to_string())
835}
836
837unsafe fn execute_list_qapps(
838 args: &[u8],
839 _intent: &McpIntentFrame,
840) -> Result<String, McpSystemError> {
841 mcp_stub_impls::list_qapps(args)
842}
843
844unsafe fn execute_get_qapp_manifest(
845 args: &[u8],
846 _intent: &McpIntentFrame,
847) -> Result<String, McpSystemError> {
848 let qapp_name = extract_raw_json_string(args, b"\"qapp_name\"").unwrap_or(b"");
849 if qapp_name.is_empty() {
850 return Err(McpSystemError::InvalidParameters);
851 }
852 let name = std::str::from_utf8(qapp_name).map_err(|_| McpSystemError::InvalidParameters)?;
853 mcp_stub_impls::get_qapp_manifest(name)
854}
855
856unsafe fn execute_inspect_qapp_readiness(
857 args: &[u8],
858 _intent: &McpIntentFrame,
859) -> Result<String, McpSystemError> {
860 let qapp_name = extract_raw_json_string(args, b"\"qapp_name\"").unwrap_or(b"");
861 if qapp_name.is_empty() {
862 return Err(McpSystemError::InvalidParameters);
863 }
864 let name = std::str::from_utf8(qapp_name).map_err(|_| McpSystemError::InvalidParameters)?;
865 mcp_stub_impls::inspect_qapp_readiness(name)
866}
867
868unsafe fn execute_list_qapp_updates(
869 args: &[u8],
870 _intent: &McpIntentFrame,
871) -> Result<String, McpSystemError> {
872 mcp_stub_impls::list_qapp_updates(args)
873}
874
875unsafe fn execute_describe_qapp_surface_schema(
876 _args: &[u8],
877 _intent: &McpIntentFrame,
878) -> Result<String, McpSystemError> {
879 let schema = r#"{
880 "host_shell": "webizen-studio",
881 "package_manifest": "qapp.json",
882 "layout_strategies": ["PointGrid", "CssGrid", "FlexBox", "Masonry"],
883 "presentation_modes": ["GridBound", "NodeRelational", "Spatial"],
884 "coordinate_spaces": ["GlobalCartesian", "RelativeAnchored"],
885 "layer_behaviors": ["Docked", "FloatingOverlay", "ModalOverlay", "FullCanvas"],
886 "theme_scopes": ["environment", "app", "page", "module"],
887 "manifest_surfaces": ["static-web", "wasm-local", "online-daemon-aware", "native-dioxus-pane"],
888 "mcp_tools": ["list_qapps", "get_qapp_manifest", "inspect_qapp_readiness", "list_qapp_updates", "describe_qapp_surface_schema"]
889}"#;
890 Ok(schema.to_string())
891}
892
893unsafe fn execute_system_status(
894 _args: &[u8],
895 intent: &McpIntentFrame,
896) -> Result<String, McpSystemError> {
897 Ok(json!({
898 "server": "qualia-core-db-mcp",
899 "version": env!("CARGO_PKG_VERSION"),
900 "protocolVersion": "2025-03-26",
901 "toolCount": stable_mcp_tools().len(),
902 "qpuEnabled": intent.qpu_enabled,
903 "llmEnabled": intent.llm_enabled,
904 "activeProfileId": intent.active_profile_id,
905 })
906 .to_string())
907}
908
909unsafe fn execute_run_docs_tests(
910 args: &[u8],
911 _intent: &McpIntentFrame,
912) -> Result<String, McpSystemError> {
913 let mode = extract_raw_json_string(args, b"\"mode\"").unwrap_or(b"logic");
914 let mode_str = core::str::from_utf8(mode).unwrap_or("logic");
915 if !matches!(mode_str, "logic" | "wasm" | "native" | "both") {
916 return Err(McpSystemError::InvalidParameters);
917 }
918
919 if matches!(mode_str, "native" | "both") && !daemon_health_ok(4242) {
920 return Ok(json!({
921 "ok": false,
922 "mode": mode_str,
923 "error": "daemon_unreachable",
924 "hint": "Start the graph daemon with `qualia-cli service start` or `qualia-cli daemon start --dev`"
925 })
926 .to_string());
927 }
928
929 let root = resolve_repo_root();
930 let script = root.join("docs/tests/run-headless.mjs");
931 if !script.exists() {
932 return Ok(json!({
933 "ok": false,
934 "mode": mode_str,
935 "error": "runner_missing",
936 "path": script.display().to_string()
937 })
938 .to_string());
939 }
940
941 let output = std::process::Command::new("node")
942 .arg(script.as_os_str())
943 .arg("--mode")
944 .arg(mode_str)
945 .current_dir(&root)
946 .output()
947 .map_err(|_| McpSystemError::ToolNotReady)?;
948
949 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
950 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
951 Ok(json!({
952 "ok": output.status.success(),
953 "mode": mode_str,
954 "exitCode": output.status.code(),
955 "stdout": stdout,
956 "stderr": stderr
957 })
958 .to_string())
959}
960
961fn daemon_health_ok(port: u16) -> bool {
962 use std::io::{Read, Write};
963 use std::net::{SocketAddr, TcpStream};
964 use std::time::Duration;
965
966 let addr: SocketAddr = format!("127.0.0.1:{port}")
967 .parse()
968 .unwrap_or_else(|_| SocketAddr::from(([127, 0, 0, 1], port)));
969 let mut stream = match TcpStream::connect_timeout(&addr, Duration::from_secs(2)) {
970 Ok(stream) => stream,
971 Err(_) => return false,
972 };
973 let _ = stream.set_read_timeout(Some(Duration::from_secs(2)));
974 let request =
975 format!("GET /health HTTP/1.1\r\nHost: 127.0.0.1:{port}\r\nConnection: close\r\n\r\n");
976 if stream.write_all(request.as_bytes()).is_err() {
977 return false;
978 }
979 let mut buf = [0u8; 512];
980 match stream.read(&mut buf) {
981 Ok(0) => false,
982 Ok(n) => {
983 let response = core::str::from_utf8(&buf[..n]).unwrap_or("");
984 response.contains("200 OK") || response.contains("engine_version")
985 }
986 Err(_) => false,
987 }
988}
989
990fn resolve_repo_root() -> std::path::PathBuf {
991 if let Ok(root) = std::env::var("QUALIA_REPO_ROOT") {
992 return std::path::PathBuf::from(root);
993 }
994 let mut dir = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
995 for _ in 0..8 {
996 if dir.join("docs/tests/run-headless.mjs").exists() {
997 return dir;
998 }
999 if !dir.pop() {
1000 break;
1001 }
1002 }
1003 dir
1004}
1005
1006unsafe fn execute_evaluate_modality(
1009 args: &[u8],
1010 _intent: &McpIntentFrame,
1011) -> Result<String, McpSystemError> {
1012 mcp_tool_impls::evaluate_modality(args)
1013}
1014
1015unsafe fn execute_bioinformatics_align(
1016 args: &[u8],
1017 _intent: &McpIntentFrame,
1018) -> Result<String, McpSystemError> {
1019 mcp_tool_impls::bioinformatics_align(args)
1020}
1021
1022unsafe fn execute_chemical_descriptors(
1023 args: &[u8],
1024 _intent: &McpIntentFrame,
1025) -> Result<String, McpSystemError> {
1026 mcp_tool_impls::chemical_descriptors(args)
1027}
1028
1029unsafe fn execute_clinical_risk(
1030 args: &[u8],
1031 _intent: &McpIntentFrame,
1032) -> Result<String, McpSystemError> {
1033 mcp_tool_impls::clinical_risk(args)
1034}
1035
1036unsafe fn execute_parse_csv(
1037 args: &[u8],
1038 _intent: &McpIntentFrame,
1039) -> Result<String, McpSystemError> {
1040 mcp_format_impls::parse_csv(args)
1041}
1042
1043unsafe fn execute_parse_rdf(
1044 args: &[u8],
1045 _intent: &McpIntentFrame,
1046) -> Result<String, McpSystemError> {
1047 mcp_format_impls::parse_rdf(args)
1048}
1049
1050unsafe fn execute_parse_json(
1051 args: &[u8],
1052 _intent: &McpIntentFrame,
1053) -> Result<String, McpSystemError> {
1054 mcp_format_impls::parse_json(args)
1055}
1056
1057unsafe fn execute_serialize_csv(
1058 args: &[u8],
1059 _intent: &McpIntentFrame,
1060) -> Result<String, McpSystemError> {
1061 mcp_format_impls::serialize_csv(args)
1062}
1063
1064unsafe fn execute_serialize_json(
1065 args: &[u8],
1066 _intent: &McpIntentFrame,
1067) -> Result<String, McpSystemError> {
1068 mcp_format_impls::serialize_json(args)
1069}
1070
1071unsafe fn execute_serialize_rdf(
1072 args: &[u8],
1073 _intent: &McpIntentFrame,
1074) -> Result<String, McpSystemError> {
1075 mcp_format_impls::serialize_rdf(args)
1076}
1077
1078unsafe fn execute_symbolic_logic_infer(
1079 args: &[u8],
1080 _intent: &McpIntentFrame,
1081) -> Result<String, McpSystemError> {
1082 mcp_tool_impls::symbolic_logic_infer(args)
1083}
1084
1085unsafe fn execute_geometric_algebra_op(
1086 args: &[u8],
1087 _intent: &McpIntentFrame,
1088) -> Result<String, McpSystemError> {
1089 mcp_tool_impls::geometric_algebra_op(args)
1090}
1091
1092pub unsafe fn scrub_transient_mcp_buffers(buffer: &mut [u8]) {
1094 for byte_ptr in buffer.iter_mut() {
1095 write_volatile(byte_ptr, 0x00);
1096 }
1097}
1098
1099#[inline]
1101fn hex_nibble(c: u8) -> Option<u8> {
1102 match c {
1103 b'0'..=b'9' => Some(c - b'0'),
1104 b'a'..=b'f' => Some(c - b'a' + 10),
1105 b'A'..=b'F' => Some(c - b'A' + 10),
1106 _ => None,
1107 }
1108}
1109
1110fn parse_sanctuary_override(value: &[u8]) -> Option<[u8; 32]> {
1131 if value.len() != 64 {
1132 return None;
1133 }
1134 let mut token = [0u8; 32];
1135 for (byte, pair) in token.iter_mut().zip(value.chunks_exact(2)) {
1136 let hi = hex_nibble(pair[0])?;
1137 let lo = hex_nibble(pair[1])?;
1138 *byte = (hi << 4) | lo;
1139 }
1140 if token.iter().all(|&b| b == 0) {
1141 return None;
1142 }
1143 Some(token)
1144}
1145
1146fn build_intent_frame(arguments: &[u8], qpu_enabled: bool, llm_enabled: bool) -> McpIntentFrame {
1147 let sanctuary_override = extract_raw_json_string(arguments, b"\"sanctuary_override\"")
1148 .and_then(parse_sanctuary_override);
1149
1150 McpIntentFrame {
1151 purpose_hash: crate::q_hash("purpose:General"),
1152 active_deontic_constraints: Vec::new(),
1153 active_profile_id: None,
1154 session_nonce: 0,
1155 sanctuary_override,
1156 qpu_enabled,
1157 llm_enabled,
1158 }
1159}
1160
1161fn tool_list_json() -> Vec<Value> {
1162 stable_mcp_tools()
1163 .iter()
1164 .map(|tool| {
1165 let input_schema = serde_json::from_str::<Value>(tool.input_schema)
1166 .unwrap_or_else(|_| json!({"type":"object"}));
1167 json!({
1168 "name": tool.name,
1169 "description": tool.description,
1170 "inputSchema": input_schema,
1171 })
1172 })
1173 .collect()
1174}
1175
1176fn system_resource_json() -> Value {
1177 json!({
1178 "uri": "qualia://qapp-surface-schema",
1179 "name": "Qapp Surface Schema",
1180 "description": "Static description of the current Qualia qapp host surface.",
1181 "mimeType": "application/json",
1182 })
1183}
1184
1185fn error_message(err: McpSystemError) -> (&'static str, i64) {
1186 match err {
1187 McpSystemError::SanctuaryGateTriggered => ("Sanctuary gate triggered", -32001),
1188 McpSystemError::ToolNotFound => ("Tool not found", -32601),
1189 McpSystemError::ToolNotReady => ("Tool not implemented on the current MCP surface", -32004),
1190 McpSystemError::ParseError => ("Parse error", -32700),
1191 McpSystemError::IntentFrameViolation => ("Intent frame violation", -32002),
1192 McpSystemError::FeatureNotEnabled => ("Feature not enabled", -32003),
1193 McpSystemError::InvalidParameters => ("Invalid parameters", -32602),
1194 }
1195}
1196
1197fn dispatch_tool_call(
1198 tool_name: &[u8],
1199 arguments_raw: &[u8],
1200 qpu_enabled: bool,
1201 llm_enabled: bool,
1202) -> Result<String, McpSystemError> {
1203 let intent_frame = build_intent_frame(arguments_raw, qpu_enabled, llm_enabled);
1204 let payload = RawToolPayload {
1205 tool_name,
1206 arguments_raw,
1207 };
1208 unsafe { enforce_fiduciary_tool_dispatch(payload, &intent_frame) }
1209}
1210
1211pub unsafe fn parse_and_evaluate_mcp_stream(stream_chunk: &[u8]) -> Result<String, McpSystemError> {
1213 if !stream_chunk.windows(12).any(|w| w == b"\"tools/call\"") {
1214 return Err(McpSystemError::ParseError);
1215 }
1216 let tool_name = extract_raw_json_string(stream_chunk, b"\"name\"").unwrap_or(b"");
1217 dispatch_tool_call(tool_name, stream_chunk, true, true)
1218}
1219
1220pub fn handle_jsonrpc_message(
1221 message: &str,
1222 qpu_enabled: bool,
1223 llm_enabled: bool,
1224) -> Option<String> {
1225 let request: Value = serde_json::from_str(message).ok()?;
1226 let id = request.get("id").cloned().unwrap_or(Value::Null);
1227 let method = request.get("method").and_then(Value::as_str).or_else(|| {
1228 if message.contains("\"tools/call\"") {
1229 Some("tools/call")
1230 } else {
1231 None
1232 }
1233 })?;
1234
1235 let response = match method {
1236 "initialize" => json!({
1237 "jsonrpc": "2.0",
1238 "id": id,
1239 "result": {
1240 "protocolVersion": "2025-03-26",
1241 "capabilities": {
1242 "tools": { "listChanged": false },
1243 "resources": { "listChanged": false, "subscribe": false },
1244 "prompts": { "listChanged": false }
1245 },
1246 "serverInfo": {
1247 "name": "qualia-core-db-mcp",
1248 "version": env!("CARGO_PKG_VERSION")
1249 }
1250 }
1251 }),
1252 "notifications/initialized" => return None,
1253 "ping" => json!({
1254 "jsonrpc": "2.0",
1255 "id": id,
1256 "result": {}
1257 }),
1258 "tools/list" => json!({
1259 "jsonrpc": "2.0",
1260 "id": id,
1261 "result": {
1262 "tools": tool_list_json()
1263 }
1264 }),
1265 "resources/list" => json!({
1266 "jsonrpc": "2.0",
1267 "id": id,
1268 "result": {
1269 "resources": [system_resource_json()]
1270 }
1271 }),
1272 "resources/read" => {
1273 let uri = request
1274 .get("params")
1275 .and_then(|params| params.get("uri"))
1276 .and_then(Value::as_str)
1277 .unwrap_or("");
1278 if uri != "qualia://qapp-surface-schema" {
1279 json!({
1280 "jsonrpc": "2.0",
1281 "id": id,
1282 "error": {
1283 "code": -32602,
1284 "message": "Unknown resource URI"
1285 }
1286 })
1287 } else {
1288 let content = unsafe {
1289 execute_describe_qapp_surface_schema(
1290 b"{}",
1291 &build_intent_frame(b"{}", qpu_enabled, llm_enabled),
1292 )
1293 }
1294 .unwrap_or_else(|_| "{}".to_string());
1295 json!({
1296 "jsonrpc": "2.0",
1297 "id": id,
1298 "result": {
1299 "contents": [{
1300 "uri": uri,
1301 "mimeType": "application/json",
1302 "text": content
1303 }]
1304 }
1305 })
1306 }
1307 }
1308 "prompts/list" => json!({
1309 "jsonrpc": "2.0",
1310 "id": id,
1311 "result": {
1312 "prompts": []
1313 }
1314 }),
1315 "tools/call" => {
1316 let params = request.get("params").unwrap_or(&request);
1317 let name = params
1318 .get("name")
1319 .and_then(Value::as_str)
1320 .or_else(|| request.get("name").and_then(Value::as_str))
1321 .unwrap_or("");
1322 let arguments = params
1323 .get("arguments")
1324 .cloned()
1325 .unwrap_or_else(|| json!({}));
1326 let arguments_raw = serde_json::to_vec(&arguments).unwrap_or_else(|_| b"{}".to_vec());
1327 match dispatch_tool_call(name.as_bytes(), &arguments_raw, qpu_enabled, llm_enabled) {
1328 Ok(data) => json!({
1329 "jsonrpc": "2.0",
1330 "id": id,
1331 "result": {
1332 "content": [{
1333 "type": "text",
1334 "text": data
1335 }],
1336 "isError": false
1337 }
1338 }),
1339 Err(err) => {
1340 let (message, code) = error_message(err);
1341 json!({
1342 "jsonrpc": "2.0",
1343 "id": id,
1344 "error": {
1345 "code": code,
1346 "message": message
1347 }
1348 })
1349 }
1350 }
1351 }
1352 _ => json!({
1353 "jsonrpc": "2.0",
1354 "id": id,
1355 "error": {
1356 "code": -32601,
1357 "message": "Method not found"
1358 }
1359 }),
1360 };
1361
1362 Some(response.to_string())
1363}
1364
1365#[cfg(not(target_arch = "wasm32"))]
1370pub async fn start_mcp_listener() {
1371 start_mcp_listener_with_flags(true, true).await;
1372}
1373
1374#[cfg(not(target_arch = "wasm32"))]
1375pub async fn start_mcp_listener_with_flags(qpu_enabled: bool, llm_enabled: bool) {
1376 use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
1377
1378 eprintln!("[MCP Server] Starting stdio MCP server...");
1379 eprintln!(
1380 "[MCP Server] Advertised tool count: {}",
1381 stable_mcp_tools().len()
1382 );
1383
1384 let mut stdin = BufReader::new(tokio::io::stdin());
1385 let mut stdout = tokio::io::stdout();
1386 let mut line = String::new();
1387
1388 loop {
1389 line.clear();
1390 match stdin.read_line(&mut line).await {
1391 Ok(0) => break,
1392 Ok(_) => {
1393 let request = line.trim_end_matches(['\r', '\n']);
1394 if request.is_empty() {
1395 continue;
1396 }
1397 if let Some(reply) = handle_jsonrpc_message(request, qpu_enabled, llm_enabled) {
1398 let _ = stdout.write_all(reply.as_bytes()).await;
1399 let _ = stdout.write_all(b"\n").await;
1400 }
1401 }
1402 Err(_) => break,
1403 }
1404 }
1405}
1406
1407#[cfg(test)]
1408mod tests {
1409 use super::*;
1410
1411 #[test]
1412 fn initialize_returns_server_info() {
1413 let reply = handle_jsonrpc_message(
1414 r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#,
1415 true,
1416 true,
1417 )
1418 .expect("reply");
1419 let json: Value = serde_json::from_str(&reply).expect("valid json");
1420 assert_eq!(json["result"]["serverInfo"]["name"], "qualia-core-db-mcp");
1421 assert_eq!(json["result"]["protocolVersion"], "2025-03-26");
1422 }
1423
1424 #[test]
1425 fn tools_list_returns_curated_surface() {
1426 let reply = handle_jsonrpc_message(
1427 r#"{"jsonrpc":"2.0","id":"tools","method":"tools/list"}"#,
1428 true,
1429 true,
1430 )
1431 .expect("reply");
1432 let json: Value = serde_json::from_str(&reply).expect("valid json");
1433 let tools = json["result"]["tools"].as_array().expect("tool array");
1434 assert!(tools.iter().any(|tool| tool["name"] == "get_system_status"));
1435 assert!(tools.iter().any(|tool| tool["name"] == "run_docs_tests"));
1436 assert!(tools.iter().all(|tool| tool.get("inputSchema").is_some()));
1437 }
1438
1439 #[test]
1440 fn parse_rdf_tool_call_returns_quins() {
1441 let reply = handle_jsonrpc_message(
1442 r#"{"jsonrpc":"2.0","id":"rdf","method":"tools/call","params":{"name":"parse_rdf","arguments":{"format":"nt","rdf_data":"<http://example.org/Alice> <http://example.org/knows> <http://example.org/Bob> .\n"}}}"#,
1443 false,
1444 false,
1445 )
1446 .expect("reply");
1447 let json: Value = serde_json::from_str(&reply).expect("valid json");
1448 let text = json["result"]["content"][0]["text"]
1449 .as_str()
1450 .expect("text payload");
1451 let payload: Value = serde_json::from_str(text).expect("embedded json");
1452 assert_eq!(payload["quinCount"], 1);
1453 }
1454
1455 #[test]
1456 fn parse_csv_tool_call_returns_quins() {
1457 let reply = handle_jsonrpc_message(
1458 r#"{"jsonrpc":"2.0","id":"csv","method":"tools/call","params":{"name":"parse_csv","arguments":{"csv_data":"v,n\n1,2\n","field_mappings":[{"source_key":"v","predicate":"ex:v","datatype":"integer"}]}}}"#,
1459 false,
1460 false,
1461 )
1462 .expect("reply");
1463 let json: Value = serde_json::from_str(&reply).expect("valid json");
1464 let text = json["result"]["content"][0]["text"]
1465 .as_str()
1466 .expect("text payload");
1467 let payload: Value = serde_json::from_str(text).expect("embedded json");
1468 assert_eq!(payload["quinCount"], 1);
1469 }
1470
1471 #[test]
1472 fn get_graph_stats_tool_call_returns_json_payload() {
1473 let reply = handle_jsonrpc_message(
1474 r#"{"jsonrpc":"2.0","id":"graph","method":"tools/call","params":{"name":"get_graph_stats","arguments":{}}}"#,
1475 false,
1476 false,
1477 )
1478 .expect("reply");
1479 let json: Value = serde_json::from_str(&reply).expect("valid json");
1480 let text = json["result"]["content"][0]["text"]
1481 .as_str()
1482 .expect("text payload");
1483 let payload: Value = serde_json::from_str(text).expect("embedded json");
1484 assert!(payload.get("quinCount").is_some());
1485 }
1486
1487 #[test]
1488 fn get_did_info_tool_call_parses_q42() {
1489 let reply = handle_jsonrpc_message(
1490 r#"{"jsonrpc":"2.0","id":"did","method":"tools/call","params":{"name":"get_did_info","arguments":{"did":"did:q42:z6MkpTHR8VNs"}}}"#,
1491 false,
1492 false,
1493 )
1494 .expect("reply");
1495 let json: Value = serde_json::from_str(&reply).expect("valid json");
1496 let text = json["result"]["content"][0]["text"]
1497 .as_str()
1498 .expect("text payload");
1499 let payload: Value = serde_json::from_str(text).expect("embedded json");
1500 assert_eq!(payload["msbSet"], true);
1501 }
1502
1503 #[test]
1504 fn tools_list_includes_newly_public_stubs() {
1505 let reply = handle_jsonrpc_message(
1506 r#"{"jsonrpc":"2.0","id":"tools","method":"tools/list"}"#,
1507 true,
1508 true,
1509 )
1510 .expect("reply");
1511 let json: Value = serde_json::from_str(&reply).expect("valid json");
1512 let tools = json["result"]["tools"].as_array().expect("tool array");
1513 assert!(tools.iter().any(|tool| tool["name"] == "query_sparql"));
1514 assert!(tools.iter().any(|tool| tool["name"] == "parse_rdf"));
1515 assert!(tools.iter().any(|tool| tool["name"] == "list_qapps"));
1516 assert!(tools.iter().any(|tool| tool["name"] == "list_capabilities"));
1517 assert!(tools
1518 .iter()
1519 .any(|tool| tool["name"] == "computational_geometry"));
1520 assert_eq!(tools.len(), stable_mcp_tools().len());
1521 }
1522
1523 #[test]
1524 fn computational_geometry_tool_routes_hull() {
1525 let reply = handle_jsonrpc_message(
1526 r#"{"jsonrpc":"2.0","id":"geometry","method":"tools/call","params":{"name":"computational_geometry","arguments":{"op":"convex_hull_2","points":[[0,0],[1,0],[0.5,0.5],[1,1],[0,1]]}}}"#,
1527 false,
1528 false,
1529 )
1530 .expect("reply");
1531 let json: Value = serde_json::from_str(&reply).expect("valid json");
1532 let text = json["result"]["content"][0]["text"]
1533 .as_str()
1534 .expect("text payload");
1535 let payload: Value = serde_json::from_str(text).expect("embedded json");
1536 assert_eq!(payload["vertex_count"], 4);
1537 assert_eq!(payload["indices"], json!([0, 1, 3, 4]));
1538 }
1539
1540 #[test]
1541 fn computer_vision_tool_list_and_sigma() {
1542 let reply = handle_jsonrpc_message(
1543 r#"{"jsonrpc":"2.0","id":"cv","method":"tools/call","params":{"name":"computer_vision","arguments":{"op":"list"}}}"#,
1544 false,
1545 false,
1546 )
1547 .expect("reply");
1548 let json: Value = serde_json::from_str(&reply).expect("valid json");
1549 let text = json["result"]["content"][0]["text"]
1550 .as_str()
1551 .expect("text payload");
1552 let payload: Value = serde_json::from_str(text).expect("embedded json");
1553 assert_eq!(payload["library"], "specialized_libs::computer_vision");
1554 let reply2 = handle_jsonrpc_message(
1555 r#"{"jsonrpc":"2.0","id":"cv2","method":"tools/call","params":{"name":"computer_vision","arguments":{"op":"class_score_to_sigma","class_hash":4,"score":1.0}}}"#,
1556 false,
1557 false,
1558 )
1559 .expect("reply2");
1560 let json2: Value = serde_json::from_str(&reply2).expect("valid json");
1561 let text2 = json2["result"]["content"][0]["text"]
1562 .as_str()
1563 .expect("text");
1564 let payload2: Value = serde_json::from_str(text2).expect("json");
1565 assert!(payload2["sigma"].as_f64().unwrap() > 0.0);
1566 }
1567
1568 #[test]
1569 fn inference_capability_tools_are_registered() {
1570 let registered: Vec<&str> = stable_mcp_tools().iter().map(|tool| tool.name).collect();
1571 for capability in crate::CAPABILITY_DESCRIPTORS {
1572 for tool in capability.mcp_tools {
1573 assert!(
1574 registered.contains(tool),
1575 "{} routes to missing MCP tool {}",
1576 capability.name,
1577 tool
1578 );
1579 }
1580 }
1581 }
1582
1583 #[test]
1584 fn capability_catalog_is_complete_filterable_and_honest() {
1585 let all = mcp_tool_impls::list_capabilities(br#"{}"#).expect("catalogue");
1586 let all: Value = serde_json::from_str(&all).expect("catalogue json");
1587 assert_eq!(
1588 all["capability_count"].as_u64().unwrap() as usize,
1589 crate::CAPABILITY_DESCRIPTORS.len()
1590 );
1591 assert!(all["operation_group_count"].as_u64().unwrap() > 50);
1592
1593 let filtered =
1594 mcp_tool_impls::list_capabilities(br#"{"domain":"statistics"}"#).expect("filtered");
1595 let filtered: Value = serde_json::from_str(&filtered).expect("filtered json");
1596 assert_eq!(filtered["capability_count"], 1);
1597 assert_eq!(filtered["capabilities"][0]["name"], "Statistics");
1598
1599 let ml = all["capabilities"]
1600 .as_array()
1601 .unwrap()
1602 .iter()
1603 .find(|capability| capability["name"] == "MachineLearning")
1604 .expect("machine learning capability");
1605 assert_eq!(ml["maturity"], "fail-closed");
1606 assert_eq!(ml["operational"], false);
1607 }
1608
1609 #[test]
1610 fn capability_catalogue_metadata_has_no_silent_gaps() {
1611 let allowed_maturity = ["stable", "partial", "experimental", "fail-closed"];
1612 let mut names = std::collections::BTreeSet::new();
1613 for capability in crate::CAPABILITY_DESCRIPTORS {
1614 assert!(
1615 names.insert(capability.name),
1616 "duplicate {}",
1617 capability.name
1618 );
1619 assert!(
1620 !capability.operations.is_empty(),
1621 "{} has no operations",
1622 capability.name
1623 );
1624 assert!(
1625 allowed_maturity.contains(&capability.maturity),
1626 "{} has unknown maturity {}",
1627 capability.name,
1628 capability.maturity
1629 );
1630 assert!(
1631 !capability.surfaces.is_empty(),
1632 "{} has no runtime surface",
1633 capability.name
1634 );
1635 }
1636 }
1637
1638 #[test]
1639 fn get_system_status_tool_call_returns_json_payload() {
1640 let reply = handle_jsonrpc_message(
1641 r#"{"jsonrpc":"2.0","id":"status","method":"tools/call","params":{"name":"get_system_status","arguments":{}}}"#,
1642 false,
1643 true,
1644 )
1645 .expect("reply");
1646 let json: Value = serde_json::from_str(&reply).expect("valid json");
1647 let text = json["result"]["content"][0]["text"]
1648 .as_str()
1649 .expect("text payload");
1650 let payload: Value = serde_json::from_str(text).expect("embedded json");
1651 assert_eq!(payload["server"], "qualia-core-db-mcp");
1652 assert_eq!(payload["qpuEnabled"], false);
1653 }
1654
1655 #[test]
1656 fn geometry_manifests_tool_list_round_trips() {
1657 let reply = handle_jsonrpc_message(
1658 r#"{"jsonrpc":"2.0","id":"gm","method":"tools/call","params":{"name":"geometry_manifests","arguments":{}}}"#,
1659 false,
1660 false,
1661 )
1662 .expect("reply");
1663 let json: Value = serde_json::from_str(&reply).expect("valid json");
1664 let text = json["result"]["content"][0]["text"]
1665 .as_str()
1666 .expect("text payload");
1667 let payload: Value = serde_json::from_str(text).expect("embedded json");
1668 assert!(payload["op_count"].as_u64().unwrap() > 0);
1669 let ops = payload["ops"].as_array().unwrap();
1670 for op in ops {
1671 assert!(
1672 op["backends"].as_array().unwrap().len() > 0,
1673 "every op must have non-empty backends"
1674 );
1675 }
1676 }
1677
1678 #[test]
1679 fn geometry_manifests_budget_query_no_gpu() {
1680 let reply = handle_jsonrpc_message(
1681 r#"{"jsonrpc":"2.0","id":"bq","method":"tools/call","params":{"name":"geometry_manifests","arguments":{"op":"vr_filtration","device":{"cpu":true,"simd":true,"wgpu":false,"cuda":false,"wasm":true,"exact":true}}}}"#,
1682 false,
1683 false,
1684 )
1685 .expect("reply");
1686 let json: Value = serde_json::from_str(&reply).expect("valid json");
1687 let text = json["result"]["content"][0]["text"]
1688 .as_str()
1689 .expect("text payload");
1690 let payload: Value = serde_json::from_str(text).expect("embedded json");
1691 assert_eq!(payload["op"], "vr_filtration");
1692 assert!(payload["backend_count"].as_u64().unwrap() > 0);
1693 let backends = payload["runnable_backends"].as_array().unwrap();
1694 assert!(
1695 !backends.iter().any(|b| b == "wgpu"),
1696 "wgpu should not be runnable when device.wgpu=false"
1697 );
1698 }
1699}