Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Cerrado Apto para principiantes
#1,387 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Ya se ha fusionado un pull request relacionado.

  • #1397 de @Ilyuha888 — fusionado

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
74/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
go

Línea de trabajo

Empieza en mcp/server.go, en toolForErr, donde se convierten y validan las salidas tipadas de los handlers; compara su tratamiento de las solicitudes de entrada y los resultados de error. El informe incluye un reproductor y afirma que una prueba ya pasa con el cambio propuesto. Confirma que los resultados IsError omiten el contenido estructurado generado y se saltan la validación de salida, mientras que StructuredContent proporcionado explícitamente sigue estando disponible.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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]
Lenguaje dominante
Go
Estrellas
5.2k
Forks
583
Merge medio
1 d 12 h
PR fusionados (30 d)
52

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de modelcontextprotocol/go-sdk

Todos los issues de modelcontextprotocol/go-sdk

Issues similares

Más issues de Go

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.