bug(mcp): tool results omit resultType required by protocol revision 2026-07-28 #740
Labels
No labels
breaking
bug
documentation
enhancement
epic
good first issue
help wanted
refactoring
resolution/duplicate
resolution/invalid
resolution/wontfix
security
severity/critical
severity/high
severity/low
severity/medium
status/abandoned
status/blocked
status/needs-decision
status/needs-info
status/needs-verification
testing
upstream
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Reference
sixfold-space/madtea#740
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Some madtea MCP tool calls fail inside the client with a protocol error instead of a result. The client rejects the server's result envelope because it carries no
resultTypefield. Protocol revision 2026-07-28 maderesultTyperequired on everytools/callresult. madtea does not always send it.The error
The client returns this on the failing calls:
The bridge that treats an absent
resultTypeas "complete" applies only to servers on an earlier revision. A server on revision 2026-07-28 must sendresultType.What I saw
The error above came back on these calls:
madt_issueswith thesearchaction,madt_finish,madt_branch_delete.These calls returned normal results:
madt_server_info,madt_issueswith thelistaction,madt_prswithlist,create, andmerge,madt_pull,madt_fetch.So the server sends
resultTypeon some tool results and omits it on others. The failure is per handler or per code path, not server-wide.Impact
A failing call looks like a tool error to the caller, even when the operation on the forge has partly or fully succeeded. The caller cannot read the real state from the envelope.
madt_finishreturned this error after it had already pushed the branch, which left the branch pushed with no pull request and forced a manual check of the forge to find the true state. An error that does not match the real outcome is a trap: a caller can retry a step that is not safe to repeat, or report a real success as a failure.What I want
The MCP server sends a spec-conformant
tools/callresult for every tool and every action, withresultTypeset, under protocol revision 2026-07-28. If the result envelope comes from the MCP SDK, update the SDK to the revision the server negotiates. Do not pin the negotiated revision backwards to dodge the field.Reopening. v1.8.0 of the SDK fixed the handler path. One result path still omits
resultType, so the acceptance ("every tool and every action") is not met yet.Still failing
A call with an unknown argument returns the protocol error again. Example:
madt_reposwith aqueryargument (the search argument isq).I called the server directly and captured the raw result for two calls.
Failing (unknown argument on
madt_repos):Working (
madt_issueslist):The success result carries
resultType. The error result carries noresultTypeand nostructuredContent.Root cause
The gap is the unknown-argument rejection in
internal/mcp/arg_suggest.go.installUnknownArgumentRejectionis a receiving middleware. On an unknown top-level argument it returns a bareCallToolResultwithIsError: trueatarg_suggest.go:268. This return is short of the SDK layer that stampsresultType, so the envelope leaves without the field.A known argument used on the wrong action takes the normal route: it reaches the handler and returns through the path that stamps
resultType. That is whymadt_issueswith a misusedlabelsargument comes back clean, andmadt_reposwith an unknownqueryargument does not.Why the guard missed it
TestToolResultCarriesResultTypeininternal/mcp/result_type_test.gochecks the success path only. It fails on any error result (result_type_test.go:42) and never calls the unknown-argument path. So the residual does not show in the gate.Impact
Every unknown-argument rejection is a non-conformant result. The caller sees an opaque "malformed result" instead of the "did you mean" text the rejection carries. The redirect that is built to help the caller self-correct never reaches the caller.
Acceptance
resultTypeunder revision 2026-07-28.This blocks v0.20.0.
Resolved on main. The unknown-argument rejection now carries resultType under
protocol 2026-07-28:
93edff67removed the pre-empting middleware, so the SDKinput-schema validator rejects an unknown top-level argument in band and
callTool stamps resultType on the result; the did-you-mean enrichment mutates
only the content and preserves the stamp. This is not a backward revision pin.
The guard test now covers the error path: result_type_rejection_test.go checks
a focused madt_repos case and every tool's rejection (39 tools, 0 skipped),
alongside the existing success-path test. Fixed by
93edff67and8e85f20f.