mcp: whether MaxLineLength rejects a message depends on how the stream is read

Abierto
#1,509 0 comentarios 0 reacciones 1 asignado Ver en GitHub

Los mantenedores suelen responder en 1 día

@Tangerg ya está trabajando en esto.

Desde el 6/11/2026.

Evaluación

Este issue todavía no se ha evaluado.

Descripción

Checklist
  • I searched the existing issues, and this has not been reported before.
  • This is a real problem that I ran into myself, using the latest release of the SDK.
  • I ran into it in a production system, not in a toy example, a benchmark, or an automated or AI-driven scan of the code.
  • I (a human) examined the relevant SDK code, and wrote or reviewed every line of this report. It is not unreviewed AI output.
  • This report is brief and concise, and sticks to the facts needed to understand the problem.
What happened?

Whether MaxLineLength rejects an inbound message on IOTransport, StdioTransport or CommandTransport depends on how the reader splits the stream. In the program below, with a cap of 128 bytes, the same 153-byte message is accepted when the stream arrives in one read and rejected when it arrives one byte at a time.

frameLimitReader resets its count after each message, but json.Decoder reads ahead. Bytes of the next message that the decoder read while finishing the previous one are counted toward the previous one and then forgotten, so the next message can exceed the cap by that many bytes.

Expected: whether a message fits depends only on the message.

I'd like to fix this myself, by starting each message's count at the decoder's InputOffset instead of resetting it, so that bytes read ahead count toward the message they belong to.

AI assistance: the investigation was done with Claude; I reviewed the report and the SDK code.

How to reproduce
package main

import (
	"context"
	"fmt"
	"io"
	"strings"
	"testing/iotest"

	"github.com/modelcontextprotocol/go-sdk/mcp"
)

type nopCloser struct{ io.Writer }

func (nopCloser) Close() error { return nil }

func main() {
	// With MaxLineLength 128, the second message (153 bytes) should be rejected.
	stream := `{"jsonrpc":"2.0","method":"first"}` + "\n" +
		`{"jsonrpc":"2.0","method":"second","params":{"p":"` + strings.Repeat("x", 100) + `"}}` + "\n"
	for _, r := range []struct {
		name string
		r    io.Reader
	}{
		{"whole stream per read", strings.NewReader(stream)},
		{"one byte per read", iotest.OneByteReader(strings.NewReader(stream))},
	} {
		t := &mcp.IOTransport{Reader: io.NopCloser(r.r), Writer: nopCloser{io.Discard}, MaxLineLength: 128}
		conn, err := t.Connect(context.Background())
		if err != nil {
			panic(err)
		}
		if _, err := conn.Read(context.Background()); err != nil {
			panic(err)
		}
		_, err = conn.Read(context.Background())
		fmt.Printf("%s: second message: %v\n", r.name, err)
	}
}

Output with v1.8.0:

whole stream per read: second message: <nil>
one byte per read: second message: inbound JSON-RPC frame exceeded the configured maximum line length
Where did you hit this?

Flame's plugin runtime talks MCP to plugin processes over pipes with IOTransport, setting MaxLineLength to its process protocol's frame budget; Frame, the plugin framework under it, sets MaxLineLength to -1 and admits whole frames in its own reader instead.

Go MCP SDK version

v1.8.0 (also on main at 449d945)

Go version

go version go1.27.3 darwin/arm64

Logs

No response

Lenguaje dominante
Go
Estrellas
5.3k
Forks
607
Merge medio
1 d 11 h
PR fusionados (30 d)
82

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.