mcp: typed tool handler adds zero structuredContent to an IsError result
メンテナーはふだん 1 日以内に返信
関連するプルリクエストがすでにマージされています。
- #1397 @Ilyuha888 による — マージ済み
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 74/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- go
調査の方向性
mcp/server.go の toolForErr から始めます。ここでは型付きのハンドラー出力が変換、検証されます。入力リクエストとエラー結果の処理を比較してください。レポートには再現コードが含まれており、提案された変更を適用するとテストがすでに通ると記載されています。IsError の結果では生成された構造化コンテンツが省略され、出力検証がスキップされる一方、明示的に指定された StructuredContent は引き続き利用できることを確認してください。
索引モデルが issue の本文から書いたものです。
説明
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]
- 主要言語
- Go
- スター
- 5.2k
- フォーク
- 583
- 平均マージ
- 1日 12時間
- マージ済み PR(30日)
- 52
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
modelcontextprotocol/go-sdk のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
modelcontextprotocol/go-sdk#1372 ·
メンテナーはふだん 1 日以内に返信
-
Session-lifecycle bookkeeping logs at info, spamming multiple log lines per request on stateless streamable HTTP対応中かも @anneheartrecord が 48 日前に担当しました。 オープンP3
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
modelcontextprotocol/go-sdk#1204 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
Expose generic `SendNotification` on `ServerSession` for custom protocol extensions対応中かも @ajuijas が 213 日前に担当しました。 オープンneeds investigation
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
modelcontextprotocol/go-sdk#745 · コメント 13 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 35/100
modelcontextprotocol/go-sdk#1394 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
mcp: rejected request IDs receive no JSON-RPC error envelope対応中かも @skosovsky が 2 日前に担当しました。 オープン
難易度 4/5 1〜2日 初心者へのやさしさ 68/100
modelcontextprotocol/go-sdk#1391 ·
メンテナーはふだん 1 日以内に返信
modelcontextprotocol/go-sdk の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
SAP/crossplane-provider-btp#1118 ·
メンテナーはふだん 1 日以内に返信
-
benchmark: `make setup` fails because audit-exporter builds with Go 1.25.0 while go.mod requires 1.26.0対応中かも @xsvm が今日担当しました。 オープン
難易度 1/5 1時間未満 初心者へのやさしさ 84/100
volcano-sh/volcano#6097 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
[Bug]: Bedrock invoke-with-response-stream reports `stop_reason: "end_turn"` for tool-use turnsオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信