Skip to content

Latest commit

 

History

History
896 lines (690 loc) · 24.8 KB

File metadata and controls

896 lines (690 loc) · 24.8 KB

MCP Server Session Management and Platform Detection Research

Research Date: 2025-10-10 Focus Areas: Model Context Protocol (MCP) session management, Apple platform detection, xcodebuild destination handling


Executive Summary

This research explores best practices for:

  1. MCP server session state management
  2. Platform-specific builds across iOS, visionOS, macOS, watchOS, tvOS
  3. Automatic platform detection from simulator UUIDs
  4. xcodebuild destination specifier patterns

Key Findings:

  • MCP sessions require careful security and state lifecycle management
  • Platform detection can be automated using xcrun simctl list --json with runtime identifier parsing
  • Session defaults provide excellent UX by reducing parameter repetition
  • XcodeBuildMCP's current implementation follows many industry best practices

1. MCP Session Management Best Practices

1.1 Security Standards (Official MCP Specification)

Critical Security Requirements:

  1. Session IDs Must Be Secure

    • Use secure, non-deterministic session IDs
    • Avoid predictable or sequential identifiers
    • Use secure random number generators
    • Rotate or expire session IDs to reduce risk
  2. User-Specific Binding

    • Bind session IDs to user-specific information
    • Recommended key format: <user_id>:<session_id>
    • Combine session ID with internal user ID
  3. Authentication Separation

    • Sessions must NOT be used for authentication
    • Session management is separate from auth mechanisms

Security Anti-Pattern (Specification Warning):

"The specification mandates session IDs in URL parameters for HTTP transport, which violates security best practices by exposing sensitive identifiers in server logs, browser history, and network traffic, enabling session hijacking attacks."

Source: MCP Security Best Practices

1.2 State Management Approaches

Definition:

"In MCP, session management means keeping track of a conversation between your LLM application and your server across multiple requests, without which the app would need to start from scratch with every request."

Implementation Patterns:

  1. In-Memory Storage (Simple)

    • Works well for most use cases
    • Fast and straightforward
    • Lost on server restart
    • XcodeBuildMCP's current approach
  2. Persistent Storage (Advanced)

    • Redis for distributed systems
    • Database storage for multi-instance setups
    • Survives server restarts
    • Required for load-balanced deployments
  3. Singleton Server Pattern

    • Single McpServer instance at startup
    • Session state stored in server memory
    • All requests for a session routed to same process
    • Ideal for single-instance deployments and rapid prototyping

Example Implementation (From Research):

class SessionStore {
  private defaults: SessionDefaults = {};

  setDefaults(partial: Partial<SessionDefaults>): void {
    this.defaults = { ...this.defaults, ...partial };
  }

  clear(keys?: string[]): void {
    if (!keys) {
      this.defaults = {};
    } else {
      keys.forEach(k => delete this.defaults[k]);
    }
  }

  get<K extends keyof SessionDefaults>(key: K): SessionDefaults[K] {
    return this.defaults[key];
  }
}

1.3 Session Lifecycle

Phases:

  1. Initialization: Capability negotiation and state setup
  2. Operation: Main working phase with stateful requests
  3. Cleanup: Proper resource disposal and session termination

Best Practices:

  • Add timeout mechanisms for abandoned sessions
  • Clean up resources on session termination
  • Handle server restarts gracefully
  • Support session resumption where appropriate

Source: MCP Session Management 2025


2. Platform Detection Strategies

2.1 Runtime Identifier Parsing with simctl

Command:

xcrun simctl list devices --json

JSON Structure:

{
  "devices": {
    "com.apple.CoreSimulator.SimRuntime.iOS-18-0": [
      {
        "name": "iPhone 16",
        "udid": "E621E1F8-C36C-495A-93FC-0C247A3E6E5F",
        "state": "Booted",
        "isAvailable": true
      }
    ],
    "com.apple.CoreSimulator.SimRuntime.visionOS-2-0": [
      {
        "name": "Apple Vision Pro",
        "udid": "B1234567-89AB-CDEF-0123-456789ABCDEF",
        "state": "Shutdown",
        "isAvailable": true
      }
    ]
  }
}

Runtime Identifier Format:

com.apple.CoreSimulator.SimRuntime.{PLATFORM}-{MAJOR}-{MINOR}

Examples:
- com.apple.CoreSimulator.SimRuntime.iOS-18-0
- com.apple.CoreSimulator.SimRuntime.visionOS-2-0
- com.apple.CoreSimulator.SimRuntime.tvOS-18-0
- com.apple.CoreSimulator.SimRuntime.watchOS-11-0

2.2 Platform Detection Algorithm

From UUID to Platform:

async function detectPlatformFromUUID(
  simulatorUuid: string,
  executor: CommandExecutor
): Promise<{ platform: XcodePlatform; runtime: string } | null> {
  const result = await executor(
    ['xcrun', 'simctl', 'list', 'devices', '--json'],
    'Detect Platform'
  );

  if (!result.success) return null;

  const data = JSON.parse(result.output);

  for (const [runtimeId, devices] of Object.entries(data.devices)) {
    const device = devices.find(d => d.udid === simulatorUuid);

    if (device) {
      // Parse runtime identifier
      // com.apple.CoreSimulator.SimRuntime.iOS-18-0 → iOS Simulator
      const platform = extractPlatformFromRuntime(runtimeId);
      return { platform, runtime: runtimeId };
    }
  }

  return null;
}

function extractPlatformFromRuntime(runtimeId: string): XcodePlatform {
  const match = runtimeId.match(/SimRuntime\.(\w+)-/);
  if (!match) return XcodePlatform.iOSSimulator; // fallback

  const platform = match[1];

  switch (platform.toLowerCase()) {
    case 'ios': return XcodePlatform.iOSSimulator;
    case 'visionos': return XcodePlatform.visionOSSimulator;
    case 'tvos': return XcodePlatform.tvOSSimulator;
    case 'watchos': return XcodePlatform.watchOSSimulator;
    default: return XcodePlatform.iOSSimulator;
  }
}

2.3 Fallback: Text Parsing for Apple simctl Bugs

XcodeBuildMCP Implementation (Robust):

The current implementation in list_sims.ts handles Apple's known simctl JSON bugs (duplicate runtime IDs in iOS 26.0 beta) by using dual parsing:

function parseTextOutput(textOutput: string): SimulatorDevice[] {
  const devices: SimulatorDevice[] = [];
  const lines = textOutput.split('\n');
  let currentRuntime = '';

  for (const line of lines) {
    // Match runtime headers like "-- iOS 26.0 --"
    const runtimeMatch = line.match(/^-- ([\w\s.]+) --$/);
    if (runtimeMatch) {
      currentRuntime = runtimeMatch[1];
      continue;
    }

    // Match device lines
    const deviceMatch = line.match(
      /^\s+(.+?)\s+\(([^)]+)\)\s+\((Booted|Shutdown|Booting)\)/
    );

    if (deviceMatch && currentRuntime) {
      devices.push({
        name: deviceMatch[1].trim(),
        udid: deviceMatch[2],
        state: deviceMatch[3],
        runtime: currentRuntime
      });
    }
  }

  return devices;
}

Benefits:

  • Handles Apple's simctl bugs gracefully
  • Provides platform information via runtime string
  • Merges JSON and text results for maximum reliability

3. xcodebuild Destination Specifier Patterns

3.1 Platform-Specific Syntax

Official Documentation: xcodebuild man page

Supported Platforms:

  • macOS
  • iOS
  • iOS Simulator
  • watchOS
  • watchOS Simulator
  • tvOS
  • tvOS Simulator
  • visionOS
  • visionOS Simulator
  • DriverKit

3.2 Destination Specifier Keys

Common Keys:

  • platform - Target platform (required)
  • name - Device/simulator name
  • OS - Operating system version
  • id - Unique device identifier (UUID/UDID)
  • arch - Architecture (arm64, x86_64)

Syntax:

-destination 'key1=value1,key2=value2,...'

3.3 Platform-Specific Examples

iOS Simulator (by UUID):

-destination 'platform=iOS Simulator,id=E621E1F8-C36C-495A-93FC-0C247A3E6E5F'

iOS Simulator (by name):

-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'

visionOS Simulator (by name):

-destination 'platform=visionOS Simulator,name=Apple Vision Pro,OS=2.0'

macOS (with architecture):

-destination 'platform=macOS,arch=arm64'

Generic Platform Build:

-destination 'generic/platform=iOS'
-destination 'generic/platform=visionOS'

Physical Device (by UDID):

-destination 'platform=iOS,id=00008030-001234567890ABCD'

3.4 Multiple Destinations

Parallel Testing:

xcodebuild test \
  -destination 'platform=iOS Simulator,name=iPhone 16' \
  -destination 'platform=iOS Simulator,name=iPhone SE (3rd generation)' \
  -scheme MyApp

xcodebuild automatically chooses the number of devices to run simultaneously.

3.5 Automatic Destination Detection

Key Finding:

"If -destination is omitted, xcodebuild defaults to a destination compatible with the selected scheme."

Implications:

  • Schemes encode default platform targets
  • xcodebuild can infer appropriate destination
  • Fallback behavior for missing destination parameters

Troubleshooting Pattern:

"When in doubt with destination parameters, xcodebuild will fail with nonsense input and list all available destinations for a given scheme."

# Intentionally fail to see available destinations
xcodebuild -scheme MyApp -destination 'INVALID' build

4. XcodeBuildMCP Current Implementation Analysis

4.1 Session Management (Excellent)

Current Implementation: /Users/dalecarman/Groove Jones Dropbox/Dale Carman/Projects/dev/XcodeBuildMCP/src/utils/session-store.ts

Strengths:

  • ✅ Singleton pattern for stateful server
  • ✅ Type-safe SessionDefaults interface
  • ✅ Granular control (set, clear, get operations)
  • ✅ Logging for debugging
  • ✅ Supports partial updates
  • ✅ Supports clearing specific keys or all

Supported Session Defaults:

type SessionDefaults = {
  projectPath?: string;
  workspacePath?: string;
  scheme?: string;
  configuration?: string;
  simulatorName?: string;
  simulatorId?: string;
  deviceId?: string;
  useLatestOS?: boolean;
  arch?: 'arm64' | 'x86_64';
};

Best Practice Alignment:

  • ✅ In-memory storage appropriate for single-instance deployment
  • ✅ Simple timeout mechanism would be valuable addition
  • ⚠️ No explicit session ID binding (not needed for stdio MCP)
  • ⚠️ State lost on restart (acceptable for development tool)

4.2 Platform Handling (Good Foundation)

Current Platform Enum:

enum XcodePlatform {
  macOS = 'macOS',
  iOS = 'iOS',
  iOSSimulator = 'iOS Simulator',
  watchOS = 'watchOS',
  watchOSSimulator = 'watchOS Simulator',
  tvOS = 'tvOS',
  tvOSSimulator = 'tvOS Simulator',
  visionOS = 'visionOS',
  visionOSSimulator = 'visionOS Simulator',
}

Strengths:

  • ✅ Comprehensive platform support
  • ✅ Clear distinction between device and simulator
  • ✅ String values match xcodebuild expectations

Destination String Construction:

function constructDestinationString(
  platform: XcodePlatform,
  simulatorName?: string,
  simulatorId?: string,
  useLatest: boolean = true,
  arch?: string,
): string {
  // UUID takes precedence for simulators
  if (isSimulatorPlatform && simulatorId) {
    return `platform=${platform},id=${simulatorId}`;
  }

  // Name-based with OS version
  if (isSimulatorPlatform && simulatorName) {
    return `platform=${platform},name=${simulatorName}${useLatest ? ',OS=latest' : ''}`;
  }

  // Platform-specific handling for devices and macOS
  switch (platform) {
    case XcodePlatform.macOS:
      return arch ? `platform=macOS,arch=${arch}` : 'platform=macOS';
    case XcodePlatform.iOS:
      return 'generic/platform=iOS';
    // ...
  }
}

Best Practice Alignment:

  • ✅ Follows xcodebuild documentation patterns
  • ✅ UUID precedence over name (more specific)
  • ✅ OS=latest default for simulators
  • ✅ Architecture support for macOS

4.3 Simulator UUID Resolution (Excellent)

Current Implementation: /Users/dalecarman/Groove Jones Dropbox/Dale Carman/Projects/dev/XcodeBuildMCP/src/utils/simulator-utils.ts

Function: determineSimulatorUuid

Strengths:

  • ✅ UUID validation with regex
  • ✅ Name-to-UUID resolution via simctl
  • ✅ Availability checking (isAvailable flag)
  • ✅ Clear error messages
  • ✅ Helpful warnings (e.g., name looks like UUID)

Process:

  1. If UUID provided → use directly
  2. If name provided but looks like UUID → use as UUID with warning
  3. If name provided → query simctl and resolve to UUID
  4. Return detailed error if not found or unavailable

Best Practice Alignment:

  • ✅ Defensive validation
  • ✅ Clear error messages with actionable guidance
  • ✅ JSON parsing of simctl output
  • ✅ Availability filtering

5. Recommendations for XcodeBuildMCP

5.1 Enhance Platform Auto-Detection

Recommendation: Add automatic platform detection from simulator UUID

Implementation:

// src/utils/simulator-utils.ts

export async function detectPlatformFromSimulatorUuid(
  simulatorUuid: string,
  executor: CommandExecutor,
): Promise<{ platform: XcodePlatform; runtime: string } | { error: string }> {
  log('info', `Detecting platform for simulator UUID: ${simulatorUuid}`);

  const listResult = await executor(
    ['xcrun', 'simctl', 'list', 'devices', '--json'],
    'Detect Platform from UUID'
  );

  if (!listResult.success) {
    return { error: 'Failed to list simulators for platform detection' };
  }

  try {
    const data = JSON.parse(listResult.output);

    for (const [runtimeId, devices] of Object.entries(data.devices)) {
      const device = (devices as any[]).find(d => d.udid === simulatorUuid);

      if (device) {
        const platform = extractPlatformFromRuntimeId(runtimeId);
        log('info', `Detected platform: ${platform} from runtime: ${runtimeId}`);
        return { platform, runtime: runtimeId };
      }
    }

    return { error: `Simulator with UUID ${simulatorUuid} not found` };
  } catch (error) {
    return { error: `Failed to parse simulator data: ${error}` };
  }
}

function extractPlatformFromRuntimeId(runtimeId: string): XcodePlatform {
  // Parse: com.apple.CoreSimulator.SimRuntime.iOS-18-0
  const match = runtimeId.match(/SimRuntime\.(\w+)-/);

  if (!match) {
    log('warn', `Could not parse runtime ID: ${runtimeId}, defaulting to iOS Simulator`);
    return XcodePlatform.iOSSimulator;
  }

  const platform = match[1].toLowerCase();

  switch (platform) {
    case 'ios':
      return XcodePlatform.iOSSimulator;
    case 'visionos':
      return XcodePlatform.visionOSSimulator;
    case 'tvos':
      return XcodePlatform.tvOSSimulator;
    case 'watchos':
      return XcodePlatform.watchOSSimulator;
    default:
      log('warn', `Unknown platform: ${platform}, defaulting to iOS Simulator`);
      return XcodePlatform.iOSSimulator;
  }
}

Usage in build_sim.ts:

async function build_simLogic(
  params: BuildSimulatorParams,
  executor: CommandExecutor,
): Promise<ToolResponse> {
  // Auto-detect platform if simulator UUID is provided
  let platform = XcodePlatform.iOSSimulator; // default

  if (params.simulatorId) {
    const detection = await detectPlatformFromSimulatorUuid(
      params.simulatorId,
      executor
    );

    if ('platform' in detection) {
      platform = detection.platform;
      log('info', `Auto-detected platform: ${platform}`);
    } else {
      log('warn', `Platform detection failed: ${detection.error}`);
      // Continue with default iOS Simulator
    }
  }

  return executeXcodeBuildCommand(
    params,
    {
      platform, // Use detected platform
      simulatorId: params.simulatorId,
      simulatorName: params.simulatorName,
      // ...
    },
    // ...
  );
}

Benefits:

  • ✅ Eliminates need for explicit platform parameter
  • ✅ Works seamlessly with session defaults
  • ✅ Supports all Apple platforms automatically
  • ✅ Graceful fallback to iOS Simulator

5.2 Enhance Session Timeout Management

Recommendation: Add timeout mechanism for abandoned sessions

Implementation:

// src/utils/session-store.ts

class SessionStore {
  private defaults: SessionDefaults = {};
  private lastAccess: number = Date.now();
  private timeoutMs: number = 30 * 60 * 1000; // 30 minutes

  setDefaults(partial: Partial<SessionDefaults>): void {
    this.defaults = { ...this.defaults, ...partial };
    this.lastAccess = Date.now();
    log('info', `[Session] Defaults updated: ${Object.keys(partial).join(', ')}`);
  }

  get<K extends keyof SessionDefaults>(key: K): SessionDefaults[K] {
    this.checkTimeout();
    this.lastAccess = Date.now();
    return this.defaults[key];
  }

  private checkTimeout(): void {
    const elapsed = Date.now() - this.lastAccess;

    if (elapsed > this.timeoutMs && Object.keys(this.defaults).length > 0) {
      log('info', '[Session] Session timeout reached, clearing defaults');
      this.defaults = {};
    }
  }
}

Benefits:

  • ✅ Prevents stale session state
  • ✅ Automatic cleanup after inactivity
  • ✅ Configurable timeout period

5.3 Add Platform-Aware list_sims Tool

Recommendation: Enhance list_sims to group by platform

Current Output:

Available iOS Simulators:

iOS 18.0:
- iPhone 16 (UUID) [Booted]
- iPhone 16 Pro (UUID)

visionOS 2.0:
- Apple Vision Pro (UUID)

Enhanced Output:

Available Simulators by Platform:

📱 iOS Simulators:
  iOS 18.0:
  - iPhone 16 (UUID) [Booted]
  - iPhone 16 Pro (UUID)

🥽 visionOS Simulators:
  visionOS 2.0:
  - Apple Vision Pro (UUID)

📺 tvOS Simulators:
  tvOS 18.0:
  - Apple TV 4K (UUID)

⌚ watchOS Simulators:
  watchOS 11.0:
  - Apple Watch Series 10 (UUID)

Implementation:

// Categorize by platform first, then by runtime
const platformGroups: Record<string, Record<string, SimulatorDevice[]>> = {
  'iOS': {},
  'visionOS': {},
  'tvOS': {},
  'watchOS': {}
};

for (const [runtime, devices] of Object.entries(allDevices)) {
  const platform = extractPlatformFromRuntimeString(runtime);

  if (!platformGroups[platform]) {
    platformGroups[platform] = {};
  }

  platformGroups[platform][runtime] = devices.filter(d => d.isAvailable);
}

// Format output with platform grouping and emoji indicators
let responseText = 'Available Simulators by Platform:\n\n';

for (const [platform, runtimes] of Object.entries(platformGroups)) {
  if (Object.keys(runtimes).length === 0) continue;

  const emoji = getPlatformEmoji(platform);
  responseText += `${emoji} ${platform} Simulators:\n`;

  // ... format devices per runtime
}

Benefits:

  • ✅ Clear platform separation
  • ✅ Visual indicators for platform types
  • ✅ Easier navigation for multi-platform projects
  • ✅ Supports visionOS workflows explicitly

5.4 Session Defaults for Platform

Recommendation: Add platform to session defaults

Implementation:

// src/utils/session-store.ts

export type SessionDefaults = {
  projectPath?: string;
  workspacePath?: string;
  scheme?: string;
  configuration?: string;
  simulatorName?: string;
  simulatorId?: string;
  deviceId?: string;
  useLatestOS?: boolean;
  arch?: 'arm64' | 'x86_64';
  platform?: XcodePlatform; // NEW
};

Usage:

// User sets platform for visionOS development
session_set({ platform: 'visionOS Simulator' });

// Subsequent build commands use visionOS platform automatically
build_sim({ scheme: 'MyVisionApp' });
// → Internally resolves to platform=visionOS Simulator

Benefits:

  • ✅ Explicit platform selection for visionOS/tvOS projects
  • ✅ Overrides auto-detection when needed
  • ✅ Consistent with other session defaults pattern

6. Industry Best Practices Summary

6.1 MCP Server Design Patterns

From Research:

  1. Stateless vs Stateful Servers

    • Stateless: No session persistence, every request independent
    • Stateful: Session state maintained across requests
    • XcodeBuildMCP: Stateful approach appropriate for development workflow
  2. Singleton Server Pattern

    • One McpServer instance at startup
    • Authoritative source for application state
    • Ideal for single-instance deployments
    • XcodeBuildMCP: Follows this pattern effectively
  3. Session Persistence Strategies

    • Memory: Fast, simple, lost on restart (XcodeBuildMCP's approach)
    • Knowledge Graph: Structured memory across sessions (claude-flow example)
    • Database: Multi-instance, survives restarts

Source: MCP Tools Cookbook

6.2 Tool Design Patterns

Progressive Information Gathering:

"Essential MCP tool patterns include: code reviewer prompts, dynamic ReAct pattern prompt generation, progressive information gathering with intelligent questions, sequential tool dependencies for complex workflows requiring state management and ordered execution"

Applied to XcodeBuildMCP:

  • ✅ Session defaults enable progressive parameter gathering
  • ✅ Sequential workflow: set defaults → build → test → deploy
  • ✅ State management for complex multi-step operations

6.3 Platform Detection Best Practices

From Apple Developer Forums and Stack Overflow:

  1. Always Use JSON Output

    xcrun simctl list devices --json
    • Structured data easier to parse
    • Consistent format across Xcode versions
    • Better error handling
  2. Filter by Availability

    • Only show simulators with isAvailable: true
    • Avoid errors from unavailable runtimes
    • Clear user experience
  3. Handle Apple Bugs Gracefully

    • Dual JSON + text parsing (XcodeBuildMCP does this!)
    • Fallback strategies for malformed output
    • Logging for debugging
  4. UUID Validation

    • Regex check before API calls
    • Early validation prevents wasted operations
    • Clear error messages

7. Example MCP Servers Analysis

7.1 Official MCP Servers Repository

Repository: modelcontextprotocol/servers

Session Management Patterns Observed:

  1. Memory Server

    • Knowledge graph-based persistent memory
    • Structured information across sessions
    • Query capabilities for stored data
  2. GitHub Server

    • Stateless design for API operations
    • No session persistence
    • Token-based authentication separate from sessions
  3. SQLite Server

    • Database as session state store
    • Persistent across server restarts
    • Transaction-based state management

Relevance to XcodeBuildMCP:

  • ✅ Memory-based state appropriate for development tool
  • ✅ Session defaults similar to knowledge graph pattern
  • ⚠️ Consider database persistence for production use cases

7.2 Claude Thread Continuity Server

Pattern: Persistent conversation memory

Features:

  • Conversation history across sessions
  • Project state persistence
  • User preference storage

Applied to XcodeBuildMCP:

  • Session defaults = project state
  • Configuration = user preferences
  • Potential for conversation history (build logs)

8. Recommended Reading

Official Documentation

  1. MCP Security Best Practices
  2. xcodebuild Man Page
  3. Apple Developer: Running Apps in Simulator

Community Resources

  1. Xcodebuild Destination Cheatsheet
  2. iOS Simulator Command Line Control
  3. MCP Tools Cookbook

Best Practice Guides

  1. How to MCP - Complete Guide
  2. Building Your First MCP Server - GitHub Blog

9. Conclusion

XcodeBuildMCP demonstrates strong adherence to MCP session management best practices with its SessionStore implementation. The platform handling foundation is solid, but could be enhanced with:

  1. Automatic platform detection from simulator UUIDs
  2. Session timeout management for abandoned sessions
  3. Platform-aware list_sims output for multi-platform projects
  4. Platform in session defaults for explicit visionOS/tvOS workflows

These enhancements would:

  • Reduce user friction in multi-platform projects
  • Improve visionOS and tvOS development experience
  • Maintain compatibility with existing iOS workflows
  • Follow industry best practices for MCP servers

The current implementation provides an excellent foundation for these improvements while maintaining stability and predictability for users.


Research compiled by: Claude (Anthropic) Model: Claude Sonnet 4.5 Date: 2025-10-10