Problem
Two Neural Link MCP tools mis-pass their arguments to their handlers: check_namespace and get_namespace_tree. The call reaches the handler with the wrong argument shape, so the tool fails for every agent/harness. Surfaced by @neo-gpt (Euclid) as generic NL friction — not app-specific (a cross-repo harness start made it visible, but the defect is harness-independent).
Root cause
ToolService dispatch (ai/mcp/ToolService.mjs:99-104) branches on x-pass-as-object:
if (tool.passAsObject) {
return tool.handler(validatedArgs);
}
const handlerArgs = tool.argNames.map(name => validatedArgs[name]);
return tool.handler(...handlerArgs); Both tools bind to RuntimeService handlers that destructure a single object:
check_namespace → RuntimeService.checkNamespace({namespace, sessionId}) (ai/services/neural-link/RuntimeService.mjs:44)
get_namespace_tree → RuntimeService.getNamespaceTree({root, sessionId}) (ai/services/neural-link/RuntimeService.mjs:86)
…but their OpenAPI operations (ai/mcp/server/neural-link/openapi.yaml) are missing x-pass-as-object: true. So dispatch spreads positionally — checkNamespace(namespaceString) — and the {namespace, sessionId} destructure yields undefineds → the tool silently mis-behaves. Every sibling read-tier tool with an object requestBody carries the marker (40+ ops). These two are the only x-handler operations in the spec, and both omit it.
Audit — the "same handler-shape mismatch" check (all 6 marker-less ops)
| op |
handler |
verdict |
check_namespace |
checkNamespace({namespace, sessionId}) |
fix |
get_namespace_tree |
getNamespaceTree({root, sessionId}) |
fix |
get_worker_topology |
getWorkerTopology() (nullary, empty requestBody) |
correct as-is |
get_window_topology |
getWindowTopology() (nullary, empty requestBody) |
correct as-is |
healthcheck |
healthcheck() (nullary) |
correct as-is |
get_mcp_tool_handbook |
toolId => … (single positional arg) |
correct as-is |
Fix
Add x-pass-as-object: true to the check_namespace and get_namespace_tree operations in ai/mcp/server/neural-link/openapi.yaml.
Acceptance Criteria
Diagnosis credit: @neo-gpt (Euclid).
Problem
Two Neural Link MCP tools mis-pass their arguments to their handlers:
check_namespaceandget_namespace_tree. The call reaches the handler with the wrong argument shape, so the tool fails for every agent/harness. Surfaced by @neo-gpt (Euclid) as generic NL friction — not app-specific (a cross-repo harness start made it visible, but the defect is harness-independent).Root cause
ToolServicedispatch (ai/mcp/ToolService.mjs:99-104) branches onx-pass-as-object:if (tool.passAsObject) { return tool.handler(validatedArgs); // pass the single validated object } const handlerArgs = tool.argNames.map(name => validatedArgs[name]); return tool.handler(...handlerArgs); // else: spread positionallyBoth tools bind to
RuntimeServicehandlers that destructure a single object:check_namespace→RuntimeService.checkNamespace({namespace, sessionId})(ai/services/neural-link/RuntimeService.mjs:44)get_namespace_tree→RuntimeService.getNamespaceTree({root, sessionId})(ai/services/neural-link/RuntimeService.mjs:86)…but their OpenAPI operations (
ai/mcp/server/neural-link/openapi.yaml) are missingx-pass-as-object: true. So dispatch spreads positionally —checkNamespace(namespaceString)— and the{namespace, sessionId}destructure yieldsundefineds → the tool silently mis-behaves. Every sibling read-tier tool with an object requestBody carries the marker (40+ ops). These two are the onlyx-handleroperations in the spec, and both omit it.Audit — the "same handler-shape mismatch" check (all 6 marker-less ops)
check_namespacecheckNamespace({namespace, sessionId})get_namespace_treegetNamespaceTree({root, sessionId})get_worker_topologygetWorkerTopology()(nullary, empty requestBody)get_window_topologygetWindowTopology()(nullary, empty requestBody)healthcheckhealthcheck()(nullary)get_mcp_tool_handbooktoolId => …(single positional arg)Fix
Add
x-pass-as-object: trueto thecheck_namespaceandget_namespace_treeoperations inai/mcp/server/neural-link/openapi.yaml.Acceptance Criteria
check_namespace+get_namespace_treeoperations carryx-pass-as-object: true.ToolServicebuilds both tools withpassAsObject === true(the object reaches the destructuring handler intact).x-handleroperation in the NL OpenAPI hasx-pass-as-object: true(the current invariant — bothx-handlerops bind object-destructuring handlers).Diagnosis credit: @neo-gpt (Euclid).