mcp: typed tool handler adds zero structuredContent to an IsError result
Maintainers usually reply within 1 day
A pull request for this has already been merged.
- #1397 by @Ilyuha888 — merged
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 74/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- go
- Domain
- backend-api-design
Research direction
Start in mcp/server.go at toolForErr, where typed handler output is converted and validated; compare its handling of input requests and error results. The report includes a reproducer and says a test already passes with the proposed change. Confirm that IsError results omit generated structured content and skip output validation, while explicitly supplied StructuredContent remains available.
Written by the indexing model from the issue text.
Description
What did you do?
A typed tool handler reports a tool execution error the way the spec describes: it returns a result with IsError: true and text content, the zero Out, and a nil error. (Returning a Go error avoids the problem below, but then the SDK discards the handler's own result.)
type Out struct {
Items []string `json:"items"`
}
func search(ctx context.Context, _ *mcp.CallToolRequest, in In) (*mcp.CallToolResult, Out, error) {
return &mcp.CallToolResult{
IsError: true,
Content: []mcp.Content{&mcp.TextContent{Text: "no shop with this id"}},
}, Out{}, nil
}
What did you see?
The result carries a zero structuredContent next to the error:
{"content":[{"type":"text","text":"no shop with this id"}],"structuredContent":{"items":null},"isError":true}
A client that reads structuredContent first gets {"items":null}, which looks like an empty successful result.
When the output schema rejects the zero value, the call fails with a protocol error instead of returning the tool error. With an OutputSchema whose required status property has "enum": ["ok", "partial"]:
calling "tools/call": validating tool output: validating root: validating /properties/status: enum: does not equal any of: [ok partial]
What did you expect to see?
The error result as the handler built it: no structuredContent and no output validation, as in the spec's tool execution error example. The err != nil path already behaves this way.
Versions
go-sdk v1.8.0; same code on main at 452ac5a. Go 1.27.1, darwin/arm64.
Possible fix
In toolForErr (mcp/server.go), skip the output step for an error result, the same way it is skipped when the handler asks for input:
if asksForInput(res) || res.IsError {
outval = nil
}
A handler that wants structured data on an error result can still set res.StructuredContent itself. I have this change with a test (go test ./... passes) and can send a PR if the approach works for you.
Full reproduction
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/google/jsonschema-go/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type In struct {
Query string `json:"query"`
}
type Out struct {
Items []string `json:"items"`
}
type Report struct {
Status string `json:"status"`
}
func failing[O any](ctx context.Context, _ *mcp.CallToolRequest, _ In) (*mcp.CallToolResult, O, error) {
var zero O
return &mcp.CallToolResult{
IsError: true,
Content: []mcp.Content{&mcp.TextContent{Text: "no shop with this id"}},
}, zero, nil
}
func main() {
ctx := context.Background()
server := mcp.NewServer(&mcp.Implementation{Name: "repro", Version: "v0"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "search"}, failing[Out])
mcp.AddTool(server, &mcp.Tool{
Name: "report",
OutputSchema: &jsonschema.Schema{
Type: "object",
Required: []string{"status"},
Properties: map[string]*jsonschema.Schema{"status": {Type: "string", Enum: []any{"ok", "partial"}}},
},
}, failing[Report])
ct, st := mcp.NewInMemoryTransports()
if _, err := server.Connect(ctx, st, nil); err != nil {
panic(err)
}
cs, err := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0"}, nil).Connect(ctx, ct, nil)
if err != nil {
panic(err)
}
defer cs.Close()
for _, name := range []string{"search", "report"} {
res, err := cs.CallTool(ctx, &mcp.CallToolParams{Name: name, Arguments: map[string]any{"query": "x"}})
if err != nil {
fmt.Printf("%s: error: %v\n", name, err)
continue
}
b, _ := json.Marshal(res)
fmt.Printf("%s: %s\n", name, b)
}
}
Output on v1.8.0 (_meta and resultType trimmed from the first line):
search: {"content":[{"type":"text","text":"no shop with this id"}],"structuredContent":{"items":null},"isError":true}
report: error: calling "tools/call": validating tool output: validating root: validating /properties/status: enum: does not equal any of: [ok partial]
- Dominant language
- Go
- Stars
- 5.2k
- Forks
- 583
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 53
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/go-sdk
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
modelcontextprotocol/go-sdk#1372 ·
Maintainers usually reply within 1 day
-
Session-lifecycle bookkeeping logs at info, spamming multiple log lines per request on stateless streamable HTTPPossibly taken @anneheartrecord claimed this 48 days ago. OpenP3
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
modelcontextprotocol/go-sdk#1204 · 1 reaction ·
Maintainers usually reply within 1 day
-
Expose generic `SendNotification` on `ServerSession` for custom protocol extensionsPossibly taken @ajuijas claimed this 214 days ago. Openneeds investigation
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
modelcontextprotocol/go-sdk#745 · 13 comments · 1 reaction ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
modelcontextprotocol/go-sdk#1394 · 1 comment ·
Maintainers usually reply within 1 day
-
mcp: rejected request IDs receive no JSON-RPC error envelopePossibly taken @skosovsky claimed this 3 days ago. Open
Difficulty 4/5 1-2 days Newbie friendliness 68/100
modelcontextprotocol/go-sdk#1391 ·
Maintainers usually reply within 1 day
All issues in modelcontextprotocol/go-sdk
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Gentleman-Programming/engram#1899 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
OpenTollGate/tollgate-module-basic-go#949 ·
Maintainers usually reply within 1 day