Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

mcp: typed tool handler adds zero structuredContent to an IsError result

クローズ 初心者向け
#1,387 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 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

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

modelcontextprotocol/go-sdk のほかの issue

modelcontextprotocol/go-sdk の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。