From 9dc9591166bc219e5a06de4a9620d6a5896cd64a Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 24 Jul 2025 22:51:45 +0000 Subject: [PATCH 1/4] feat: add Claude Code compatibility workaround for MCP specification violation Add XCODEBUILDMCP_CLAUDE_CODE_WORKAROUND environment variable to consolidate multiple content blocks into single text responses. This works around Claude Code''s MCP spec violation where it only shows the first content block, preventing users from seeing test results when stderr warnings are present. Changes: - Add consolidateContentForClaudeCode() utility function in validation.ts - Update build-utils.ts and test-common.ts to apply consolidation - Document new env var in CLAUDE.md - Preserve correct MCP format by default, consolidate only when enabled Fixes issue where test_sim_id_proj returns stderr warnings as errors, preventing access to test results in Claude Code. Co-authored-by: Cameron Cooke --- CLAUDE.md | 5 +++++ src/utils/build-utils.ts | 14 +++++++------ src/utils/test-common.ts | 13 ++++++++---- src/utils/validation.ts | 44 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b38d7898..488cac23 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,6 +52,11 @@ XcodeBuildMCP has two modes to manage its extensive toolset, controlled by the ` - **Environment**: `XCODEBUILDMCP_DYNAMIC_TOOLS=true`. - **Behavior**: Only the `discover_tools` tool is available initially. You can use this tool by providing a natural language task description. The server then uses an LLM call (via MCP Sampling) to identify the most relevant workflow group and dynamically loads only those tools. This conserves context window space. +#### Claude Code Compatibility Workaround +- **Environment**: `XCODEBUILDMCP_CLAUDE_CODE_WORKAROUND=true`. +- **Purpose**: Workaround for Claude Code's MCP specification violation where it only displays the first content block in tool responses. +- **Behavior**: When enabled, multiple content blocks are consolidated into a single text response, separated by `---` dividers. This ensures all information (including test results and stderr warnings) is visible to Claude Code users. + ### Core Architecture Layers 1. **MCP Transport**: stdio protocol communication 2. **Plugin Discovery**: Automatic tool AND resource registration system diff --git a/src/utils/build-utils.ts b/src/utils/build-utils.ts index b094aca2..2509df19 100644 --- a/src/utils/build-utils.ts +++ b/src/utils/build-utils.ts @@ -21,7 +21,7 @@ import { log } from './logger.js'; import { XcodePlatform, constructDestinationString } from './xcode.js'; import { CommandExecutor } from './command.js'; import { ToolResponse, SharedBuildParams, PlatformBuildOptions } from '../types/common.js'; -import { createTextResponse } from './validation.js'; +import { createTextResponse, consolidateContentForClaudeCode } from './validation.js'; import { isXcodemakeEnabled, isXcodemakeAvailable, @@ -273,7 +273,7 @@ export async function executeXcodeBuildCommand( }); } - return errorResponse; + return consolidateContentForClaudeCode(errorResponse); } log('info', `✅ ${platformOptions.logPrefix} ${buildAction} succeeded.`); @@ -347,13 +347,15 @@ When done capturing logs, use: stop_and_get_simulator_log({ logSessionId: 'SESSI }); } - return successResponse; + return consolidateContentForClaudeCode(successResponse); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); log('error', `Error during ${platformOptions.logPrefix} ${buildAction}: ${errorMessage}`); - return createTextResponse( - `Error during ${platformOptions.logPrefix} ${buildAction}: ${errorMessage}`, - true, + return consolidateContentForClaudeCode( + createTextResponse( + `Error during ${platformOptions.logPrefix} ${buildAction}: ${errorMessage}`, + true, + ), ); } } diff --git a/src/utils/test-common.ts b/src/utils/test-common.ts index 6aa343e3..5c6de66d 100644 --- a/src/utils/test-common.ts +++ b/src/utils/test-common.ts @@ -20,7 +20,7 @@ import { join } from 'path'; import { log } from './logger.js'; import { XcodePlatform } from './xcode.js'; import { executeXcodeBuildCommand } from './build-utils.js'; -import { createTextResponse } from './validation.js'; +import { createTextResponse, consolidateContentForClaudeCode } from './validation.js'; import { ToolResponse } from '../types/common.js'; import { CommandExecutor } from './command.js'; @@ -214,7 +214,7 @@ export async function handleTestLogic( await rm(tempDir, { recursive: true, force: true }); // Return combined result - preserve isError from testResult (test failures should be marked as errors) - return { + const combinedResponse: ToolResponse = { content: [ ...(testResult.content || []), { @@ -224,6 +224,9 @@ export async function handleTestLogic( ], isError: testResult.isError, }; + + // Apply Claude Code workaround if enabled + return consolidateContentForClaudeCode(combinedResponse); } catch (parseError) { // If parsing fails, return original test result log('warn', `Failed to parse xcresult bundle: ${parseError}`); @@ -235,11 +238,13 @@ export async function handleTestLogic( log('warn', `Failed to clean up temporary directory: ${cleanupError}`); } - return testResult; + return consolidateContentForClaudeCode(testResult); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); log('error', `Error during test run: ${errorMessage}`); - return createTextResponse(`Error during test run: ${errorMessage}`, true); + return consolidateContentForClaudeCode( + createTextResponse(`Error during test run: ${errorMessage}`, true), + ); } } diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 1e579648..5cc8837a 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -207,5 +207,49 @@ export function validateEnumParam( return { isValid: true }; } +/** + * Consolidates multiple content blocks into a single text response for Claude Code compatibility + * + * Claude Code violates the MCP specification by only showing the first content block. + * This function provides a workaround by concatenating all text content into a single block. + * + * @param response The original ToolResponse with multiple content blocks + * @returns A new ToolResponse with consolidated content + */ +export function consolidateContentForClaudeCode(response: ToolResponse): ToolResponse { + // Check environment variable to enable/disable this workaround + const shouldConsolidate = process.env.XCODEBUILDMCP_CLAUDE_CODE_WORKAROUND === 'true'; + + if (!shouldConsolidate || !response.content || response.content.length <= 1) { + return response; + } + + // Extract all text content and concatenate with separators + const textParts: string[] = []; + + response.content.forEach((item, index) => { + if (item.type === 'text') { + // Add a separator between content blocks (except for the first one) + if (index > 0 && textParts.length > 0) { + textParts.push('\n---\n'); + } + textParts.push(item.text); + } + // Note: Image content is not handled in this workaround as it requires special formatting + }); + + const consolidatedText = textParts.join(''); + + return { + ...response, + content: [ + { + type: 'text', + text: consolidatedText, + }, + ], + }; +} + // Export the ToolResponse type for use in other files export { ToolResponse, ValidationResult }; From 99b8cd83f0ed41ef360bfe1d92e346d3809007ee Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 21:35:43 +0000 Subject: [PATCH 2/4] fix: preserve non-text content in consolidateContentForClaudeCode When original response contains only non-text content (e.g., images), return the original response unchanged instead of creating an empty text block that overwrites the content. Co-authored-by: Cameron Cooke --- src/utils/validation.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 5cc8837a..f28b48c5 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -238,6 +238,11 @@ export function consolidateContentForClaudeCode(response: ToolResponse): ToolRes // Note: Image content is not handled in this workaround as it requires special formatting }); + // If no text content was found, return the original response to preserve non-text content + if (textParts.length === 0) { + return response; + } + const consolidatedText = textParts.join(''); return { From 2e2096c56fbc5d0b15844564d424f9464ab6b88d Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Sat, 26 Jul 2025 22:54:21 +0100 Subject: [PATCH 3/4] docs: enhance Reloaderoo guide with comprehensive CLI and proxy mode coverage - Add detailed command structure and usage examples for both CLI and proxy modes - Include comprehensive troubleshooting sections for both operational modes - Document advanced configuration options and environment variables - Improve workflow explanations with step-by-step development processes - Add MCP inspection server mode documentation - Enhance formatting with clear mode distinctions and emojis for better navigation --- CLAUDE.md | 88 +++++++- docs/RELOADEROO.md | 442 ++++++++++++++++++++++++++++++++++++++++ src/utils/validation.ts | 42 +++- 3 files changed, 568 insertions(+), 4 deletions(-) create mode 100644 docs/RELOADEROO.md diff --git a/CLAUDE.md b/CLAUDE.md index 488cac23..0f748f1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,6 +22,90 @@ npm run inspect # Run interactive MCP protocol inspector npm run diagnostic # Diagnostic CLI ``` +### Development with Reloaderoo + +**Reloaderoo** provides CLI-based testing and hot-reload capabilities for XcodeBuildMCP without requiring MCP client configuration. + +#### Quick Start + +**CLI Mode (Testing & Development):** +```bash +# List all tools +npx reloaderoo inspect list-tools -- node build/index.js + +# Call any tool +npx reloaderoo inspect call-tool list_devices --params '{}' -- node build/index.js + +# Get server information +npx reloaderoo inspect server-info -- node build/index.js + +# List and read resources +npx reloaderoo inspect list-resources -- node build/index.js +npx reloaderoo inspect read-resource "xcodebuildmcp://devices" -- node build/index.js +``` + +**Proxy Mode (MCP Client Integration):** +```bash +# Start persistent server for MCP clients +npx reloaderoo proxy -- node build/index.js + +# With debug logging +npx reloaderoo proxy --log-level debug -- node build/index.js + +# Then ask AI: "Please restart the MCP server to load my changes" +``` + +#### All CLI Inspect Commands + +Reloaderoo provides 8 inspect subcommands for comprehensive MCP server testing: + +```bash +# Server capabilities and information +npx reloaderoo inspect server-info -- node build/index.js + +# Tool management +npx reloaderoo inspect list-tools -- node build/index.js +npx reloaderoo inspect call-tool --params '' -- node build/index.js + +# Resource access +npx reloaderoo inspect list-resources -- node build/index.js +npx reloaderoo inspect read-resource "" -- node build/index.js + +# Prompt management +npx reloaderoo inspect list-prompts -- node build/index.js +npx reloaderoo inspect get-prompt --args '' -- node build/index.js + +# Connectivity testing +npx reloaderoo inspect ping -- node build/index.js +``` + +#### Advanced Options + +```bash +# Custom working directory +npx reloaderoo inspect list-tools --working-dir /custom/path -- node build/index.js + +# Timeout configuration +npx reloaderoo inspect call-tool slow_tool --timeout 60000 --params '{}' -- node build/index.js + +# Raw JSON output (no formatting) +npx reloaderoo inspect server-info --raw -- node build/index.js + +# Debug logging +npx reloaderoo inspect list-tools --log-level debug -- node build/index.js +``` + +#### Key Benefits + +- ✅ **No MCP Client Setup**: Direct CLI access to all 84+ tools +- ✅ **Raw JSON Output**: Perfect for AI agents and programmatic use +- ✅ **Hot-Reload Support**: `restart_server` tool for MCP client development +- ✅ **Claude Code Compatible**: Automatic content block consolidation +- ✅ **8 Inspect Commands**: Complete MCP protocol testing capabilities +- ✅ **Universal Compatibility**: Works on any system via npx + +For complete documentation, examples, and troubleshooting, see @docs/RELOADEROO.md + ## Architecture Overview ### Plugin-Based MCP architecture @@ -53,9 +137,9 @@ XcodeBuildMCP has two modes to manage its extensive toolset, controlled by the ` - **Behavior**: Only the `discover_tools` tool is available initially. You can use this tool by providing a natural language task description. The server then uses an LLM call (via MCP Sampling) to identify the most relevant workflow group and dynamically loads only those tools. This conserves context window space. #### Claude Code Compatibility Workaround -- **Environment**: `XCODEBUILDMCP_CLAUDE_CODE_WORKAROUND=true`. +- **Detection**: Automatic detection when running under Claude Code. - **Purpose**: Workaround for Claude Code's MCP specification violation where it only displays the first content block in tool responses. -- **Behavior**: When enabled, multiple content blocks are consolidated into a single text response, separated by `---` dividers. This ensures all information (including test results and stderr warnings) is visible to Claude Code users. +- **Behavior**: When Claude Code is detected, multiple content blocks are automatically consolidated into a single text response, separated by `---` dividers. This ensures all information (including test results and stderr warnings) is visible to Claude Code users. ### Core Architecture Layers 1. **MCP Transport**: stdio protocol communication diff --git a/docs/RELOADEROO.md b/docs/RELOADEROO.md new file mode 100644 index 00000000..4508d4ca --- /dev/null +++ b/docs/RELOADEROO.md @@ -0,0 +1,442 @@ +# Reloaderoo Integration Guide + +This guide explains how to use Reloaderoo for testing and developing XcodeBuildMCP with both CLI inspection tools and transparent proxy capabilities. + +## Overview + +**Reloaderoo** is a dual-mode MCP development tool that operates as both a CLI inspection tool and a transparent proxy server for the Model Context Protocol (MCP). It provides two distinct operational modes for different development workflows. + +## Installation + +Reloaderoo is available via npm and can be used with npx for universal compatibility. + +```bash +# Use npx to run reloaderoo (works on any system) +npx reloaderoo --help + +# Or install globally if preferred +npm install -g reloaderoo +reloaderoo --help +``` + +## Two Operational Modes + +### 🔍 **CLI Mode** (Inspection & Testing) + +Direct command-line access to MCP servers without client setup - perfect for testing and debugging: + +**Key Benefits:** +- ✅ **One-shot commands** - Test tools, list resources, get server info +- ✅ **No MCP client required** - Perfect for testing and debugging +- ✅ **Raw JSON output** - Ideal for scripts and automation +- ✅ **8 inspection commands** - Complete MCP protocol coverage +- ✅ **AI agent friendly** - Designed for terminal-based AI development workflows + +**Basic Commands:** + +```bash +# List all available tools +npx reloaderoo inspect list-tools -- node build/index.js + +# Call any tool with parameters +npx reloaderoo inspect call-tool --params '' -- node build/index.js + +# Get server information +npx reloaderoo inspect server-info -- node build/index.js + +# List available resources +npx reloaderoo inspect list-resources -- node build/index.js + +# Read a specific resource +npx reloaderoo inspect read-resource "" -- node build/index.js + +# List available prompts +npx reloaderoo inspect list-prompts -- node build/index.js + +# Get a specific prompt +npx reloaderoo inspect get-prompt --args '' -- node build/index.js + +# Check server connectivity +npx reloaderoo inspect ping -- node build/index.js +``` + +**Example Tool Calls:** + +```bash +# List connected devices +npx reloaderoo inspect call-tool list_devices --params '{}' -- node build/index.js + +# Get diagnostic information +npx reloaderoo inspect call-tool diagnostic --params '{}' -- node build/index.js + +# List iOS simulators +npx reloaderoo inspect call-tool list_sims --params '{}' -- node build/index.js + +# Read devices resource +npx reloaderoo inspect read-resource "xcodebuildmcp://devices" -- node build/index.js +``` + +### 🔄 **Proxy Mode** (Hot-Reload Development) + +Transparent MCP proxy server that enables seamless hot-reloading during development: + +**Key Benefits:** +- ✅ **Hot-reload MCP servers** without disconnecting your AI client +- ✅ **Session persistence** - Keep your development context intact +- ✅ **Automatic `restart_server` tool** - AI agents can restart servers on demand +- ✅ **Transparent forwarding** - Full MCP protocol passthrough +- ✅ **Process management** - Spawns, monitors, and restarts your server process + +**Usage:** + +```bash +# Start proxy mode (your AI client connects to this) +npx reloaderoo proxy -- node build/index.js + +# With debug logging +npx reloaderoo proxy --log-level debug -- node build/index.js + +# Then in your AI session, request: +# "Please restart the MCP server to load my latest changes" +``` + +The AI agent will automatically call the `restart_server` tool, preserving your session while reloading code changes. + +## MCP Inspection Server Mode + +Start CLI mode as a persistent MCP server for interactive debugging through MCP clients: + +```bash +# Start reloaderoo in CLI mode as an MCP server +npx reloaderoo inspect mcp -- node build/index.js +``` + +This runs CLI mode as a persistent MCP server, exposing 8 debug tools through the MCP protocol: +- `list_tools` - List all server tools +- `call_tool` - Call any server tool +- `list_resources` - List all server resources +- `read_resource` - Read any server resource +- `list_prompts` - List all server prompts +- `get_prompt` - Get any server prompt +- `get_server_info` - Get comprehensive server information +- `ping` - Test server connectivity + +## Claude Code Compatibility + +When running under Claude Code, XcodeBuildMCP automatically detects the environment and consolidates multiple content blocks into single responses with `---` separators. + +**Automatic Detection Methods:** +1. **Environment Variables**: `CLAUDECODE=1` or `CLAUDE_CODE_ENTRYPOINT=cli` +2. **Parent Process Analysis**: Checks if parent process contains 'claude' +3. **Graceful Fallback**: Falls back to environment variables if process detection fails + +**No Configuration Required**: The consolidation happens automatically when Claude Code is detected. + +## Command Reference + +### Command Structure + +```bash +npx reloaderoo [options] [command] + +Global Options: + -V, --version Output the version number + -h, --help Display help for command + +Commands: + proxy [options] -- 🔄 Run as MCP proxy server (hot-reload mode) + inspect [subcommand] 🔍 Inspect and debug MCP servers (CLI mode) + info [options] 📊 Display version and configuration information + help [command] ❓ Display help for command +``` + +### 🔄 **Proxy Mode Commands** + +```bash +npx reloaderoo proxy [options] -- [child-args...] + +Options: + -w, --working-dir Working directory for the child process + -l, --log-level Log level (debug, info, notice, warning, error, critical) + -f, --log-file Custom log file path (logs to stderr by default) + -t, --restart-timeout Timeout for restart operations (default: 30000ms) + -m, --max-restarts Maximum restart attempts (0-10, default: 3) + -d, --restart-delay Delay between restart attempts (default: 1000ms) + -q, --quiet Suppress non-essential output + --no-auto-restart Disable automatic restart on crashes + --debug Enable debug mode with verbose logging + --dry-run Validate configuration without starting proxy + +Examples: + npx reloaderoo proxy -- node build/index.js + npx reloaderoo -- node build/index.js # Same as above (proxy is default) + npx reloaderoo proxy --log-level debug -- node build/index.js +``` + +### 🔍 **CLI Mode Commands** + +```bash +npx reloaderoo inspect [subcommand] [options] -- [child-args...] + +Subcommands: + server-info [options] Get server information and capabilities + list-tools [options] List all available tools + call-tool [options] Call a specific tool + list-resources [options] List all available resources + read-resource [options] Read a specific resource + list-prompts [options] List all available prompts + get-prompt [options] Get a specific prompt + ping [options] Check server connectivity + +Examples: + npx reloaderoo inspect list-tools -- node build/index.js + npx reloaderoo inspect call-tool list_devices --params '{}' -- node build/index.js + npx reloaderoo inspect server-info -- node build/index.js +``` + +### **Info Command** + +```bash +npx reloaderoo info [options] + +Options: + --verbose Show detailed system information + +Examples: + npx reloaderoo info # Show basic system information + npx reloaderoo info --verbose # Show detailed diagnostics +``` + +### Response Format + +All CLI commands return structured JSON: + +```json +{ + "success": true, + "data": { + // Command-specific response data + }, + "metadata": { + "command": "call-tool:list_devices", + "timestamp": "2025-07-25T08:32:47.042Z", + "duration": 1782 + } +} +``` + +### Error Handling + +When commands fail, you'll receive: + +```json +{ + "success": false, + "error": { + "message": "Error description", + "code": "ERROR_CODE" + }, + "metadata": { + "command": "failed-command", + "timestamp": "2025-07-25T08:32:47.042Z", + "duration": 100 + } +} +``` + +## Development Workflow + +### 🔍 **CLI Mode Workflow** (Testing & Debugging) + +Perfect for testing individual tools or debugging server issues without MCP client setup: + +```bash +# 1. Build XcodeBuildMCP +npm run build + +# 2. Test your server quickly +npx reloaderoo inspect list-tools -- node build/index.js + +# 3. Call specific tools to verify behavior +npx reloaderoo inspect call-tool list_devices --params '{}' -- node build/index.js + +# 4. Check server health and resources +npx reloaderoo inspect ping -- node build/index.js +npx reloaderoo inspect list-resources -- node build/index.js +``` + +### 🔄 **Proxy Mode Workflow** (Hot-Reload Development) + +For full development sessions with AI clients that need persistent connections: + +#### 1. **Start Development Session** +Configure your AI client to connect to reloaderoo proxy instead of your server directly: +```bash +npx reloaderoo proxy -- node build/index.js +# or with debug logging: +npx reloaderoo proxy --log-level debug -- node build/index.js +``` + +#### 2. **Develop Your MCP Server** +Work on your XcodeBuildMCP code as usual - make changes, add tools, modify functionality. + +#### 3. **Test Changes Instantly** +```bash +# Rebuild your changes +npm run build + +# Then ask your AI agent to restart the server: +# "Please restart the MCP server to load my latest changes" +``` + +The agent will call the `restart_server` tool automatically. Your new capabilities are immediately available! + +#### 4. **Continue Development** +Your AI session continues with the updated server capabilities. No connection loss, no context reset. + +### 🛠️ **MCP Inspection Server** (Interactive CLI Debugging) + +For interactive debugging through MCP clients: + +```bash +# Start reloaderoo CLI mode as an MCP server +npx reloaderoo inspect mcp -- node build/index.js + +# Then connect with an MCP client to access debug tools +# Available tools: list_tools, call_tool, list_resources, etc. +``` + +## Troubleshooting + +### 🔄 **Proxy Mode Issues** + +**Server won't start in proxy mode:** +```bash +# Check if XcodeBuildMCP runs independently first +node build/index.js + +# Then try with reloaderoo proxy to validate configuration +npx reloaderoo proxy -- node build/index.js +``` + +**Connection problems with MCP clients:** +```bash +# Enable debug logging to see what's happening +npx reloaderoo proxy --log-level debug -- node build/index.js + +# Check system info and configuration +npx reloaderoo info --verbose +``` + +**Restart failures in proxy mode:** +```bash +# Increase restart timeout +npx reloaderoo proxy --restart-timeout 60000 -- node build/index.js + +# Check restart limits +npx reloaderoo proxy --max-restarts 5 -- node build/index.js +``` + +### 🔍 **CLI Mode Issues** + +**CLI commands failing:** +```bash +# Test basic connectivity first +npx reloaderoo inspect ping -- node build/index.js + +# Enable debug logging for CLI commands +npx reloaderoo inspect list-tools --log-level debug -- node build/index.js +``` + +**JSON parsing errors:** +```bash +# Use --raw flag to see unformatted output (if available) +npx reloaderoo inspect server-info --raw -- node build/index.js + +# Ensure your server outputs valid JSON +node build/index.js | head -10 +``` + +### **General Issues** + +**Command not found:** +```bash +# Ensure npx can find reloaderoo +npx reloaderoo --help + +# If that fails, try installing globally +npm install -g reloaderoo +``` + +**Parameter validation:** +```bash +# Ensure JSON parameters are properly quoted +npx reloaderoo inspect call-tool list_devices --params '{}' -- node build/index.js +``` + +### **General Debug Mode** + +```bash +# Get detailed information about what's happening +npx reloaderoo proxy --debug -- node build/index.js # For proxy mode +npx reloaderoo inspect list-tools --log-level debug -- node build/index.js # For CLI mode + +# View system diagnostics +npx reloaderoo info --verbose +``` + +### Debug Tips + +1. **Always build first**: Run `npm run build` before testing +2. **Check tool names**: Use `inspect list-tools` to see exact tool names +3. **Validate JSON**: Ensure parameters are valid JSON strings +4. **Enable debug logging**: Use `--log-level debug` for verbose output +5. **Test connectivity**: Use `inspect ping` to verify server communication + +## Advanced Usage + +### Environment Variables + +Configure reloaderoo behavior via environment variables: + +```bash +# Logging Configuration +export MCPDEV_PROXY_LOG_LEVEL=debug # Log level (debug, info, notice, warning, error, critical) +export MCPDEV_PROXY_LOG_FILE=/path/to/log # Custom log file path (default: stderr) +export MCPDEV_PROXY_DEBUG_MODE=true # Enable debug mode (true/false) + +# Process Management +export MCPDEV_PROXY_RESTART_LIMIT=5 # Maximum restart attempts (0-10, default: 3) +export MCPDEV_PROXY_AUTO_RESTART=true # Enable/disable auto-restart (true/false) +export MCPDEV_PROXY_TIMEOUT=30000 # Operation timeout in milliseconds +export MCPDEV_PROXY_RESTART_DELAY=1000 # Delay between restart attempts in milliseconds +export MCPDEV_PROXY_CWD=/path/to/directory # Default working directory +``` + +### Custom Working Directory + +```bash +npx reloaderoo proxy --working-dir /custom/path -- node build/index.js +npx reloaderoo inspect list-tools --working-dir /custom/path -- node build/index.js +``` + +### Timeout Configuration + +```bash +npx reloaderoo proxy --restart-timeout 60000 -- node build/index.js +``` + +## Integration with XcodeBuildMCP + +Reloaderoo is specifically configured to work with XcodeBuildMCP's: + +- **84+ Tools**: All workflow groups accessible via CLI +- **4 Resources**: Direct access to devices, simulators, environment, swift-packages +- **Dynamic Tool Discovery**: Compatible with `discover_tools` functionality +- **Claude Code Detection**: Automatic consolidation of multiple content blocks +- **Hot-Reload Support**: Seamless development workflow with `restart_server` + +For more information about XcodeBuildMCP's architecture and capabilities, see: +- [Architecture Guide](ARCHITECTURE.md) +- [Plugin Development Guide](PLUGIN_DEVELOPMENT.md) +- [Testing Guide](TESTING.md) \ No newline at end of file diff --git a/src/utils/validation.ts b/src/utils/validation.ts index f28b48c5..ca08fa5f 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -21,6 +21,7 @@ */ import * as fs from 'fs'; +import { execSync } from 'child_process'; import { log } from './logger.js'; import { ToolResponse, ValidationResult } from '../types/common.js'; import { FileSystemExecutor } from './command.js'; @@ -207,18 +208,55 @@ export function validateEnumParam( return { isValid: true }; } +/** + * Detects if the MCP server is running under Claude Code + * + * Uses multiple detection methods to identify Claude Code reliably: + * 1. Environment variable CLAUDECODE=1 + * 2. Environment variable CLAUDE_CODE_ENTRYPOINT=cli + * 3. Parent process name contains 'claude' + * + * @returns true if Claude Code is detected, false otherwise + */ +function isRunningUnderClaudeCode(): boolean { + // Method 1: Check for Claude Code environment variables + if (process.env.CLAUDECODE === '1' || process.env.CLAUDE_CODE_ENTRYPOINT === 'cli') { + return true; + } + + // Method 2: Check parent process name + try { + const parentPid = process.ppid; + if (parentPid) { + const parentCommand = execSync(`ps -o command= -p ${parentPid}`, { + encoding: 'utf8', + timeout: 1000, + }).trim(); + if (parentCommand.includes('claude')) { + return true; + } + } + } catch (error) { + // If process detection fails, fall back to environment variables only + log('debug', `Failed to detect parent process: ${error}`); + } + + return false; +} + /** * Consolidates multiple content blocks into a single text response for Claude Code compatibility * * Claude Code violates the MCP specification by only showing the first content block. * This function provides a workaround by concatenating all text content into a single block. + * Detection is automatic - no environment variable configuration required. * * @param response The original ToolResponse with multiple content blocks * @returns A new ToolResponse with consolidated content */ export function consolidateContentForClaudeCode(response: ToolResponse): ToolResponse { - // Check environment variable to enable/disable this workaround - const shouldConsolidate = process.env.XCODEBUILDMCP_CLAUDE_CODE_WORKAROUND === 'true'; + // Automatically detect if running under Claude Code + const shouldConsolidate = isRunningUnderClaudeCode(); if (!shouldConsolidate || !response.content || response.content.length <= 1) { return response; From d506619f10b052eca4bf8e749e5ab9195959376c Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Sat, 26 Jul 2025 23:51:27 +0100 Subject: [PATCH 4/4] fix: refactor environment detection to enable test environment isolation - Extract environment detection logic from validation.ts into dedicated environment.ts - Create EnvironmentDetector interface with ProductionEnvironmentDetector implementation - Add automatic test environment override to disable Claude Code detection during tests - Update validation.ts to use environment abstraction - Add createMockEnvironmentDetector utility for testing - Resolves all 44 test failures caused by Claude Code consolidation in test runs - Maintains production Claude Code detection functionality --- src/utils/command.ts | 15 +++++++++ src/utils/environment.ts | 68 ++++++++++++++++++++++++++++++++++++++++ src/utils/index.ts | 1 + src/utils/validation.ts | 40 ++--------------------- 4 files changed, 86 insertions(+), 38 deletions(-) create mode 100644 src/utils/environment.ts diff --git a/src/utils/command.ts b/src/utils/command.ts index 62edf2b4..b88d4fff 100644 --- a/src/utils/command.ts +++ b/src/utils/command.ts @@ -453,3 +453,18 @@ export function createNoopFileSystemExecutor(): FileSystemExecutor { tmpdir: (): string => '/tmp', }; } + +/** + * Create a mock environment detector for testing + * @param options Mock options for environment detection + * @returns Mock environment detector + */ +export function createMockEnvironmentDetector( + options: { + isRunningUnderClaudeCode?: boolean; + } = {}, +): import('./environment.js').EnvironmentDetector { + return { + isRunningUnderClaudeCode: () => options.isRunningUnderClaudeCode ?? false, + }; +} diff --git a/src/utils/environment.ts b/src/utils/environment.ts new file mode 100644 index 00000000..ade5cb27 --- /dev/null +++ b/src/utils/environment.ts @@ -0,0 +1,68 @@ +/** + * Environment Detection Utilities + * + * Provides abstraction for environment detection to enable testability + * while maintaining production functionality. + */ + +import { execSync } from 'child_process'; +import { log } from './logger.js'; + +/** + * Interface for environment detection abstraction + */ +export interface EnvironmentDetector { + /** + * Detects if the MCP server is running under Claude Code + * @returns true if Claude Code is detected, false otherwise + */ + isRunningUnderClaudeCode(): boolean; +} + +/** + * Production implementation of environment detection + */ +export class ProductionEnvironmentDetector implements EnvironmentDetector { + isRunningUnderClaudeCode(): boolean { + // Disable Claude Code detection during tests for environment-agnostic testing + if (process.env.NODE_ENV === 'test' || process.env.VITEST === 'true') { + return false; + } + + // Method 1: Check for Claude Code environment variables + if (process.env.CLAUDECODE === '1' || process.env.CLAUDE_CODE_ENTRYPOINT === 'cli') { + return true; + } + + // Method 2: Check parent process name + try { + const parentPid = process.ppid; + if (parentPid) { + const parentCommand = execSync(`ps -o command= -p ${parentPid}`, { + encoding: 'utf8', + timeout: 1000, + }).trim(); + if (parentCommand.includes('claude')) { + return true; + } + } + } catch (error) { + // If process detection fails, fall back to environment variables only + log('debug', `Failed to detect parent process: ${error}`); + } + + return false; + } +} + +/** + * Default environment detector instance for production use + */ +export const defaultEnvironmentDetector = new ProductionEnvironmentDetector(); + +/** + * Gets the default environment detector for production use + */ +export function getDefaultEnvironmentDetector(): EnvironmentDetector { + return defaultEnvironmentDetector; +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 0af7dabf..9267c1c0 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -10,6 +10,7 @@ export * from './log_capture.js'; export * from './template-manager.js'; export * from './test-common.js'; export * from './xcodemake.js'; +export * from './environment.js'; export * from '../version.js'; export * from '../core/dynamic-tools.js'; export * from '../core/plugin-registry.js'; diff --git a/src/utils/validation.ts b/src/utils/validation.ts index ca08fa5f..1063341e 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -21,10 +21,10 @@ */ import * as fs from 'fs'; -import { execSync } from 'child_process'; import { log } from './logger.js'; import { ToolResponse, ValidationResult } from '../types/common.js'; import { FileSystemExecutor } from './command.js'; +import { getDefaultEnvironmentDetector } from './environment.js'; /** * Creates a text response with the given message @@ -208,42 +208,6 @@ export function validateEnumParam( return { isValid: true }; } -/** - * Detects if the MCP server is running under Claude Code - * - * Uses multiple detection methods to identify Claude Code reliably: - * 1. Environment variable CLAUDECODE=1 - * 2. Environment variable CLAUDE_CODE_ENTRYPOINT=cli - * 3. Parent process name contains 'claude' - * - * @returns true if Claude Code is detected, false otherwise - */ -function isRunningUnderClaudeCode(): boolean { - // Method 1: Check for Claude Code environment variables - if (process.env.CLAUDECODE === '1' || process.env.CLAUDE_CODE_ENTRYPOINT === 'cli') { - return true; - } - - // Method 2: Check parent process name - try { - const parentPid = process.ppid; - if (parentPid) { - const parentCommand = execSync(`ps -o command= -p ${parentPid}`, { - encoding: 'utf8', - timeout: 1000, - }).trim(); - if (parentCommand.includes('claude')) { - return true; - } - } - } catch (error) { - // If process detection fails, fall back to environment variables only - log('debug', `Failed to detect parent process: ${error}`); - } - - return false; -} - /** * Consolidates multiple content blocks into a single text response for Claude Code compatibility * @@ -256,7 +220,7 @@ function isRunningUnderClaudeCode(): boolean { */ export function consolidateContentForClaudeCode(response: ToolResponse): ToolResponse { // Automatically detect if running under Claude Code - const shouldConsolidate = isRunningUnderClaudeCode(); + const shouldConsolidate = getDefaultEnvironmentDetector().isRunningUnderClaudeCode(); if (!shouldConsolidate || !response.content || response.content.length <= 1) { return response;