modelcontextprotocol / modelcontextprotocol/go-sdk

MCP go-sdk implementation Gap-2: SDK does not enforce MCP spec "MUST NOT" rule for stdio transport stdout writes

Open
#572 8 comments 0 reactions 1 assignee View on GitHub

@guglielmo-san is already working on this.

Since Apr 14, 2026.

breaking change needs investigation v2
Dominant language
Go
Stars
5.1k
Forks
543
Avg merge
1d 17h
Merged PRs (30d)
37

Description

Describe the bug
The MCP Go SDK does not enforce the MCP specification requirement that "In the stdio transport, the server MUST NOT write anything to its stdout that is not a valid MCP message." Applications using the SDK can easily bypass the transport layer and write arbitrary content directly to os.Stdout, violating the MCP protocol.

To Reproduce

Current stdio transport Implementation (How it fails to ensure compliance)

1. Transport Layer Implementation:

// mcp/transport.go:92-94
func (*StdioTransport) Connect(context.Context) (Connection, error) {
    return newIOConn(rwc{os.Stdin, os.Stdout}), nil
}

2. rwc Wrapper:

// mcp/transport.go:273-288
type rwc struct {
    rc io.ReadCloser
    wc io.WriteCloser
}

func (r rwc) Write(p []byte) (n int, err error) {
    return r.wc.Write(p)  // Direct passthrough to os.Stdout
}

3. ioConn Write Method:

// mcp/transport.go:541-590
func (t *ioConn) Write(ctx context.Context, msg jsonrpc.Message) error {
    data, err := jsonrpc2.EncodeMessage(msg)  // Only validates JSON-RPC format
    if err != nil {
        return err
    }
    data = append(data, '\n')
    _, err = t.rwc.Write(data)  // Writes to wrapped stdout
    return err
}

Expected behavior

From MCP Spec (stdio transport):

"The server MUST NOT write anything to its stdout that is not a valid MCP message."

Key Points:

  • This is a MUST NOT requirement (mandatory compliance)
  • Applies specifically to stdout writes
  • Requires prevention of non-MCP content, not just correctness validation

How MCP Server Can Violate the Rule:

1. In the MCP Server, direct os.Stdout Access:

// This bypasses the SDK entirely
fmt.Fprintf(os.Stdout, "DEBUG: Server started\n")
os.Stdout.WriteString("Hello World!\n")
fmt.Print("Another violation!\n")

2. No Prevention Mechanisms:

  • os.Stdout remains globally accessible
  • No wrapper or interceptor around stdout
  • No runtime validation of stdout content
  • No warnings or errors when non-MCP content is written

Logs
If applicable, add logs to help explain your problem.

Additional context

Possible Fix

Option 1: Wrapper-Based Prevention (Recommended)

type mcpStdout struct {
    original *os.File
    validator func([]byte) error
}

func (m *mcpStdout) Write(p []byte) (n int, err error) {
    if err := m.validator(p); err != nil {
        return 0, fmt.Errorf("MCP compliance violation: %w", err)
    }
    return m.original.Write(p)
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.