Skip to content

MCP go-sdk implementation Gap-1: provide APIs to bind event storage with user identifiers #571

@younaman

Description

@younaman

Issue Summary

The MCP Go SDK's default session ID generation mechanism does not comply with the MCP specification's security best practices for session management.

Current Implementation Analysis

1. Session ID Generation Function

// go-sdk-main/mcp/util.go:22-30
func randText() string {
	// ⌈log₃₂ 2¹²⁸⌉ = 26 chars
	src := make([]byte, 26)
	rand.Read(src)
	for i := range src {
		src[i] = base32alphabet[src[i]%32]
	}
	return string(src)
}

// go-sdk-main/mcp/util.go:20
const base32alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"

2. Default Assignment Logic

// go-sdk-main/mcp/server.go:129-131
if opts.GetSessionID == nil {
    opts.GetSessionID = randText
}

3. Actual Call Path

// go-sdk-main/mcp/streamable.go:221-225
if sessionID == "" {
    // In stateless mode, sessionID may be nonempty even if there's no
    // existing transport.
    sessionID = server.opts.GetSessionID()
}

Complete Call Chain

HTTP Request → ServeHTTP()
    ↓
Check Mcp-Session-Id header
    ↓
If empty → server.opts.GetSessionID()
    ↓
Call randText() function
    ↓
Generate 26-character Base32 random string

Generated Session ID Characteristics

Format: 26-character Base32 encoded string
Character Set: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
Length: 26 characters
Example: ABCDEFGHIJKLMNOPQRSTUVWXYZ or 2ABCDEFGHIJKLMNOPQRSTUVWX

MCP Specification Requirement

According to the MCP specification's security best practices:

MCP servers SHOULD bind session IDs to user-specific information.

When storing or transmitting session-related data (e.g., in a queue), combine the session ID with information unique to the authorized user, such as their internal user ID. Use a key format like <user_id>:<session_id>. This ensures that even if an attacker guesses a session ID, they cannot impersonate another user as the user ID is derived from the user token and not provided by the client.

Compliance Analysis

Current Implementation:

  • No user ID binding
  • Does not follow <user_id>:<session_id> format

Specification Requirement:

  • Format: <user_id>:<session_id>
  • Example: user123:ABCDEFGHIJKLMNOPQRSTUVWXYZ

Recommendation

** One Possible Solutions**

  1. Modify Default Implementation:
func (s *Server) generateSecureSessionID(userID string) string {
    sessionID := randText()
    return fmt.Sprintf("%s:%s", userID, sessionID)
}
  1. Add Security Configuration Options:
type ServerOptions struct {
    // Existing fields...
    
    // New: Secure session ID generator
    SecureSessionIDGenerator func(userID string) string
    
    // New: User ID extractor from auth context
    UserIDExtractor func(ctx context.Context) (string, error)
}

Metadata

Metadata

Assignees

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