Found while black-box testing the m1k3 CLI (2026-09-18).
What happens
$ m1k3 call search_knowledge '{}' → stdout "Error: empty query." exit 0
$ m1k3 call search_knowledge '{"query":42}' → stdout "Error: empty query." exit 0
$ m1k3 call get_answer '{"job_id":"nope"}' → stderr "m1k3: Error: No such job…" exit 3
The CLI is faithful — the difference is server-side. MCPToolRegistry sets isError only when a handler throws; a handler that returns the string "Error: …" is a success to every MCP client. About 30 sites do that (rg '"Error: ' under Sources/M1K3MCPKit, M1K3KnowledgeTools, M1K3AgentTools, M1K3App/AppEnvironment+DeepDelegation.swift). ToolResult.error(…) already exists and ExecuteScriptTool uses it; almost nothing else does.
Why it matters
A script or visiting agent checking $? / isError reads a failed call as success. m1k3 search "" && next-step runs the next step.
The design question (why this is an issue, not a drive-by)
Inside the local agent loop, "Error: …" as an observation is the right shape — the model reads it and recovers. Over MCP it should be isError: true. Options: (a) migrate sites to ToolResult.error and carry the flag through the MCP adapter; (b) one seam in the registry keyed on the uniform Error: prefix (cheap, convention-coupled). (a) is the honest one. Also: a wrong-typed argument ("query": 42) is silently coerced to empty rather than refused.
From the CLI critical-test session of 2026-09-18 (the launch trap itself is #376).
Found while black-box testing the
m1k3CLI (2026-09-18).What happens
The CLI is faithful — the difference is server-side.
MCPToolRegistrysetsisErroronly when a handler throws; a handler that returns the string"Error: …"is a success to every MCP client. About 30 sites do that (rg '"Error: 'underSources/M1K3MCPKit,M1K3KnowledgeTools,M1K3AgentTools,M1K3App/AppEnvironment+DeepDelegation.swift).ToolResult.error(…)already exists andExecuteScriptTooluses it; almost nothing else does.Why it matters
A script or visiting agent checking
$?/isErrorreads a failed call as success.m1k3 search "" && next-stepruns the next step.The design question (why this is an issue, not a drive-by)
Inside the local agent loop,
"Error: …"as an observation is the right shape — the model reads it and recovers. Over MCP it should beisError: true. Options: (a) migrate sites toToolResult.errorand carry the flag through the MCP adapter; (b) one seam in the registry keyed on the uniformError:prefix (cheap, convention-coupled). (a) is the honest one. Also: a wrong-typed argument ("query": 42) is silently coerced to empty rather than refused.From the CLI critical-test session of 2026-09-18 (the launch trap itself is #376).