Skip to content

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

@younaman

Description

@younaman

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)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds investigationstatus unclear, requires more work and discussion

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions