Skip to content

MCP tools report errors as success: ~30 handlers return "Error: …" text without isError (CLI exits 0) #378

Description

@kpmmmurphy

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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions