Skip to content

[go-fan] Go Module Review: modelcontextprotocol/go-sdk (v1.6.0 β€” silent cross-origin regression)Β #32992

@github-actions

Description

@github-actions

🐹 Go Fan Report: github.com/modelcontextprotocol/go-sdk

Module Overview

The official Go SDK for the Model Context Protocol (MCP), maintained by Anthropic in collaboration with Google. It provides typed MCP clients and servers, stdio/command/streamable-HTTP transports, OAuth helpers, JSON-RPC primitives, and middleware hooks.

  • Repository: https://github.com/modelcontextprotocol/go-sdk
  • License: Apache 2.0 / MIT (existing code)
  • Stars: ~4.5k Β· Last push: 2026-05-18 (today β€” highly active)
  • MCP spec versions supported (v1.4.0+): 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05

Current Usage in gh-aw

  • Files: 26 (.go imports across pkg/cli + pkg/parser)
  • Import count: 282 symbol references
  • Current version: v1.6.0
  • Roles: gh-aw both hosts an MCP server (gh aw mcp-server) and acts as an MCP client (gh aw mcp inspect)

Key APIs in use

API Where
mcp.NewServer / mcp.NewClient pkg/cli/mcp_server.go:47, pkg/cli/mcp_inspect_mcp.go:167,233
mcp.AddTool[In, Out] (generic typed tools) pkg/cli/mcp_tools_*.go (10 tools)
mcp.StdioTransport / CommandTransport / StreamableClientTransport pkg/cli/mcp_server_command.go:171, pkg/cli/mcp_inspect_mcp.go:170,240
mcp.NewStreamableHTTPHandler + StreamableHTTPOptions pkg/cli/mcp_server_http.go:68-73
server.AddReceivingMiddleware pkg/cli/mcp_server.go:90 (custom "Did you mean?" middleware)
mcp.ToolAnnotations{ReadOnlyHint, IdempotentHint, OpenWorldHint, DestructiveHint} all tool registrations
mcp.Icon{Source: "πŸ“Š"} every tool
jsonrpc.Error + Code{InternalError,InvalidParams,MethodNotFound} pkg/cli/mcp_error.go, mcp_argument_validation.go
jsonschema.For[T] + AddSchemaDefault (SEP-1024 elicitation) pkg/cli/mcp_schema.go

Research Findings

Recent Updates (v1.6.0 β€” 2026-05-08)

  • 🚨 Behavior change: Cross-Origin Protection no longer enabled by default when StreamableHTTPOptions.CrossOriginProtection is nil. Restore via MCPGODEBUG=enableoriginverification=1 or by setting the field explicitly.
  • ✨ New auth.ClientCredentialsHandler β€” OAuth 2.0 Client Credentials grant (RFC 6749 Β§4.4) for service-to-service authentication.
  • πŸ› SetError preserves existing Content (previously clobbered it). Revert via MCPGODEBUG=seterroroverwrite=1.
  • πŸ”’ DNS rebinding + cross-origin protections added to SSE transport (Rename --workflow-dir to --workflows-dirΒ #891).
  • πŸ› Race condition fix in ServerSession.startKeepalive (Add agentic workflow run information to step summaryΒ #856).
  • πŸ› Keepalive no longer closes the session when ping returns method-not-found.
  • ✨ Streamable transport now accepts parameterized Content-Type (e.g. application/json; charset=utf-8).
  • ✨ OAuth flow no longer re-prompts after a cancelled Authorize.
  • ➑️ CrossOriginProtection field is now deprecated β€” wrap the handler with http.NewCrossOriginProtection() middleware instead. Removed in v1.8.0.

Best Practices from the README

  • Tools are added with mcp.AddTool(server, &mcp.Tool{...}, handler) where the handler signature is func(ctx, *CallToolRequest, In) (*CallToolResult, Out, error).
  • Servers run via server.Run(ctx, transport) for stdio or NewStreamableHTTPHandler for HTTP.
  • Layer behavior via AddReceivingMiddleware / AddSendingMiddleware.

Improvement Opportunities

πŸƒ Quick Wins

1. 🚨 Restore cross-origin protection on the HTTP MCP server (security regression)

pkg/cli/mcp_server_http.go:64-74 passes CrossOriginProtection: nil in mcp.StreamableHTTPOptions. In v1.4.1–v1.5.0 this meant default protection was enabled. In v1.6.0 it now means NO protection. This regression arrived silently with the dependency bump.

Forward-compatible fix (recommended by the SDK, since the field is deprecated and removed in v1.8.0):

// Wrap the handler with cross-origin protection middleware
handler := mcp.NewStreamableHTTPHandler(getServer, &mcp.StreamableHTTPOptions{
    SessionTimeout: 2 * time.Hour,
    Logger:         logger.NewSlogLoggerWithHandler(mcpLog),
})
protection := http.NewCrossOriginProtection()
handlerWithLogging := loggingHandler(protection.Handler(handler))

2. Audit IdempotentHint: true on tools that mutate state

MCP's IdempotentHint means "calling it again with identical args produces the same result without additional effects". Two current uses don't match that contract:

  • compile (pkg/cli/mcp_tools_readonly.go:104-108) writes .lock.yml files.
  • logs (pkg/cli/mcp_tools_privileged.go:69-73) downloads new artifacts on each call.

Consider dropping IdempotentHint from these tools, or repositioning them as ReadOnlyHint: false.

3. Replace the HTTP loggingHandler with a sending middleware

pkg/cli/mcp_server_http.go:31-61 reimplements request/response logging at the HTTP layer. The SDK already accepts a Logger in StreamableHTTPOptions; an AddSendingMiddleware on the server could log MCP method-level traffic (rather than just HTTP frames).

✨ Feature Opportunities

1. Expose compiled lock files as MCP Resources

Today the server only exposes tools. The Resource primitive is a natural fit for .lock.yml files: clients could resources/list and resources/read instead of re-running compile. The SDK supports server.AddResource for this.

2. Emit ProgressNotification for long-running tools

compile --zizmor --poutine --actionlint and logs can take minutes. The MCP spec includes progress notifications β€” clients like Claude Desktop and Cline render them as progress bars. Wiring this up turns opaque blocking calls into interactive feedback.

3. Optional OAuth for the HTTP MCP server

pkg/cli/mcp_server_http.go currently has no auth on the HTTP transport. With v1.6.0's auth.ClientCredentialsHandler (OAuth 2.0 client-credentials grant), gh-aw could optionally require service-to-service auth when serving HTTP β€” stdio remains naturally guarded by process ownership.

4. DNS rebinding protection if/when SSE is added

PR #891 added DNS rebinding + cross-origin protections to the SSE transport. gh-aw doesn't use SSE today, but it's the right protection for any future long-lived stream.

πŸ“ Best Practice Alignment

  • Migrate off the deprecated StreamableHTTPOptions.CrossOriginProtection field (removed in v1.8.0). The SDK docs explicitly recommend wrapping the handler with http.NewCrossOriginProtection() middleware instead.
  • The mcp-inspect 5-second MCPOperationTimeout (pkg/cli/mcp_inspect_mcp.go:25) is hard-coded β€” large remote tool lists may time out. Consider a --mcp-timeout flag.
  • Verify that the SDK's new "log out-of-band errors" (PR Rename --workflow-dir to --workflows-dir and add support across all workflow commandsΒ #887) messages are reaching gh-aw's mcp:server debug stream via the configured Logger.

πŸ”§ General Improvements

  • Tool icons use single emoji glyphs (mcp.Icon{Source: "πŸ“Š"}). Icon.Source supports URLs / data URLs β€” richer artwork would improve discoverability in MCP tool pickers.
  • Document MCPGODEBUG (the new mechanism for restoring legacy SDK behavior) in docs/ so future debuggers find it.
  • Re-evaluate the manual *mcp.CallToolParamsRaw extraction in pkg/cli/mcp_argument_validation.go:99-103 on the next SDK release β€” there may be a typed helper.

Recommendations

Prioritized actions:

  1. P0 (security): Restore cross-origin protection on the HTTP MCP server. Wrap the handler with http.NewCrossOriginProtection() β€” forward-compatible with v1.8.0.
  2. P1: Correct IdempotentHint annotations on compile and logs.
  3. P2: Adopt ProgressNotification for compile and logs.
  4. P3: Expose .lock.yml files as MCP Resources.
  5. P3: Consider OAuth client-credentials for HTTP-served sessions.

Next Steps

  • Open a focused PR fixing the cross-origin protection regression (small, scoped, security-flagged).
  • Add a separate PR auditing IdempotentHint annotations.
  • Track v1.7.0 / 2026-06-30 spec features: automatic application_type (SEP-837), HTTP header standardization for method/name (SEP-2243).

References:


Generated by Go Fan
Module summary saved to: scratchpad/mods/modelcontextprotocol-go-sdk.md

Generated by 🐹 Go Fan Β· ● 12.3M Β· β—·

  • expires on May 19, 2026, 9:06 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions