diff --git a/AI_AGENT_FRIENDLY_CLI_PLAN.md b/AI_AGENT_FRIENDLY_CLI_PLAN.md deleted file mode 100644 index 7f92383..0000000 --- a/AI_AGENT_FRIENDLY_CLI_PLAN.md +++ /dev/null @@ -1,335 +0,0 @@ -# AI Agent Friendly CLI Implementation Plan for MCPM - -## Executive Summary - -This document outlines a comprehensive plan to make every MCPM feature accessible via pure parameterized CLI commands, eliminating the need for interactive TUI interfaces when used by AI agents or automation scripts. - -## Current State Analysis - -### ✅ Already AI-Agent Friendly (70% of commands) -- **Core operations**: `search`, `info`, `ls`, `run`, `doctor`, `usage` -- **Profile operations**: `profile ls`, `profile create`, `profile run`, `profile rm` (with `--force`) -- **Configuration**: `config ls`, `config unset`, `config clear-cache` -- **Client listing**: `client ls` -- **Installation**: `install` (with env vars), `uninstall` (with `--force`) - -### ❌ Needs Parameterized Alternatives (30% of commands) -- **Server creation**: `new`, `edit` -- **Configuration**: `config set` -- **Client management**: `client edit`, `client import` -- **Profile management**: `profile edit`, `profile inspect` -- **Migration**: `migrate` (partial) - -## Implementation Phases - -### Phase 1: Server Management (High Priority) - -#### 1.1 `mcpm new` - Non-interactive server creation -**Current**: Interactive form only -```bash -mcpm new # Prompts for all server details -``` - -**Proposed**: Full CLI parameter support -```bash -mcpm new \ - --type {stdio|remote} \ - --command "python -m server" \ - --args "arg1 arg2" \ - --env "KEY1=value1,KEY2=value2" \ - --url "http://example.com" \ - --headers "Authorization=Bearer token" \ - --force -``` - -**Implementation Requirements**: -- Add CLI parameters to `src/mcpm/commands/new.py` -- Create parameter validation logic -- Implement non-interactive server creation flow -- Maintain backward compatibility with interactive mode - -#### 1.2 `mcpm edit` - Non-interactive server editing -**Current**: Interactive form or external editor -```bash -mcpm edit # Interactive form -mcpm edit -e # External editor -``` - -**Proposed**: Field-specific updates -```bash -mcpm edit --name "new_name" -mcpm edit --command "new command" -mcpm edit --args "new args" -mcpm edit --env "KEY=value" -mcpm edit --url "http://new-url.com" -mcpm edit --headers "Header=Value" -mcpm edit --force -``` - -**Implementation Requirements**: -- Add field-specific CLI parameters to `src/mcpm/commands/edit.py` -- Create parameter-to-config mapping logic -- Implement selective field updates -- Support multiple field updates in single command - -### Phase 2: Profile Management (High Priority) - -#### 2.1 `mcpm profile edit` - Non-interactive profile editing -**Current**: Interactive server selection -```bash -mcpm profile edit # Interactive checkbox selection -``` - -**Proposed**: Server management via CLI -```bash -mcpm profile edit --add-server "server1,server2" -mcpm profile edit --remove-server "server3,server4" -mcpm profile edit --set-servers "server1,server2,server5" -mcpm profile edit --rename "new_profile_name" -mcpm profile edit --force -``` - -**Implementation Requirements**: -- Add server management parameters to `src/mcpm/commands/profile/edit.py` -- Create server list parsing utilities -- Implement server validation logic -- Support multiple operations in single command - -#### 2.2 `mcpm profile inspect` - Non-interactive profile inspection -**Current**: Interactive server selection -```bash -mcpm profile inspect # Interactive server selection -``` - -**Proposed**: Direct server specification -```bash -mcpm profile inspect --server "server_name" -mcpm profile inspect --all-servers -mcpm profile inspect --port 3000 -``` - -**Implementation Requirements**: -- Add server selection parameters to `src/mcpm/commands/profile/inspect.py` -- Implement direct server targeting -- Support batch inspection of all servers - -### Phase 3: Client Management (Medium Priority) - -#### 3.1 `mcpm client edit` - Non-interactive client editing -**Current**: Interactive server selection -```bash -mcpm client edit # Interactive server/profile selection -``` - -**Proposed**: Server management via CLI -```bash -mcpm client edit --add-server "server1,server2" -mcpm client edit --remove-server "server3,server4" -mcpm client edit --set-servers "server1,server2,server5" -mcpm client edit --add-profile "profile1,profile2" -mcpm client edit --remove-profile "profile3" -mcpm client edit --config-path "/custom/path" -mcpm client edit --force -``` - -**Implementation Requirements**: -- Add server/profile management parameters to `src/mcpm/commands/client.py` -- Create client configuration update logic -- Support mixed server and profile operations - -#### 3.2 `mcpm client import` - Non-interactive client import -**Current**: Interactive server selection -```bash -mcpm client import # Interactive server selection -``` - -**Proposed**: Automatic or specified import -```bash -mcpm client import --all -mcpm client import --servers "server1,server2" -mcpm client import --create-profile "imported_profile" -mcpm client import --merge-existing -mcpm client import --force -``` - -**Implementation Requirements**: -- Add import control parameters to `src/mcpm/commands/client.py` -- Implement automatic import logic -- Support profile creation during import - -### Phase 4: Configuration Management (Medium Priority) - -#### 4.1 `mcpm config set` - Non-interactive configuration -**Current**: Interactive prompts -```bash -mcpm config set # Interactive key/value prompts -``` - -**Proposed**: Direct key-value setting -```bash -mcpm config set -mcpm config set node_executable "/path/to/node" -mcpm config set registry_url "https://custom-registry.com" -mcpm config set analytics_enabled true -mcpm config set --list # Show available config keys -``` - -**Implementation Requirements**: -- Add direct key-value parameters to `src/mcpm/commands/config.py` -- Create configuration key validation -- Add configuration key listing functionality - -### Phase 5: Migration Enhancement (Low Priority) - -#### 5.1 `mcpm migrate` - Enhanced non-interactive migration -**Current**: Interactive choice prompt -```bash -mcpm migrate # Interactive choice: migrate/start fresh/ignore -``` - -**Proposed**: Direct migration control -```bash -mcpm migrate --auto-migrate # Migrate automatically -mcpm migrate --start-fresh # Start fresh -mcpm migrate --ignore # Ignore v1 config -mcpm migrate --backup-path "/path/to/backup" -``` - -**Implementation Requirements**: -- Add migration control parameters to `src/mcpm/commands/migrate.py` -- Implement automatic migration logic -- Add backup functionality - -## Technical Implementation Strategy - -### 1. Backwards Compatibility -- All existing interactive commands remain unchanged -- New CLI parameters are additive, not replacing -- Interactive mode remains the default when parameters are missing -- Add `--interactive` flag to force interactive mode when needed - -### 2. Flag Standards -- `--force` - Skip all confirmations -- `--json` - Machine-readable output where applicable -- `--verbose` - Detailed output for debugging -- `--dry-run` - Preview changes without applying -- `--non-interactive` - Disable all prompts globally - -### 3. Parameter Validation -- Comprehensive parameter validation before execution -- Clear error messages for invalid combinations -- Help text updates for all new parameters -- Parameter conflict detection and resolution - -### 4. Environment Variable Support -- Extend existing env var pattern to all commands -- `MCPM_FORCE=true` - Global force flag -- `MCPM_NON_INTERACTIVE=true` - Disable all prompts -- `MCPM_JSON_OUTPUT=true` - JSON output by default -- Server-specific env vars for sensitive data - -### 5. Output Standardization -- Consistent JSON output format for programmatic use -- Exit codes: 0 (success), 1 (error), 2 (validation error) -- Structured error messages with error codes -- Progress indicators for long-running operations - -## Code Structure Changes - -### 1. Utility Functions -Create `src/mcpm/utils/non_interactive.py`: -```python -def is_non_interactive() -> bool: - """Check if running in non-interactive mode.""" - -def parse_key_value_pairs(pairs: str) -> dict: - """Parse comma-separated key=value pairs.""" - -def parse_server_list(servers: str) -> list: - """Parse comma-separated server list.""" - -def validate_server_exists(server: str) -> bool: - """Validate that server exists in global config.""" -``` - -### 2. Command Parameter Enhancement -For each command, add: -- CLI parameter decorators -- Parameter validation functions -- Non-interactive execution paths -- Parameter-to-config mapping logic - -### 3. Interactive Detection -Implement detection logic: -- Check for TTY availability -- Check environment variables -- Check for force flags -- Graceful fallback when required parameters are missing - -## Testing Strategy - -### 1. Unit Tests -- All new CLI parameters -- Parameter validation logic -- Non-interactive execution paths -- Parameter parsing utilities - -### 2. Integration Tests -- Full command workflows -- Parameter combination testing -- Error handling scenarios -- Environment variable integration - -### 3. AI Agent Tests -- Headless execution scenarios -- Batch operation testing -- Error recovery testing -- Performance benchmarking - -### 4. Regression Tests -- Ensure interactive modes still work -- Backward compatibility verification -- Help text accuracy -- Exit code consistency - -## Benefits for AI Agents - -1. **Predictable Execution**: No interactive prompts to block automation -2. **Scriptable**: All operations can be scripted and automated -3. **Composable**: Commands can be chained and combined -4. **Debuggable**: Verbose output and clear error messages -5. **Stateless**: No dependency on terminal state or user presence -6. **Batch Operations**: Support for multiple operations in single commands -7. **Error Handling**: Structured error responses for programmatic handling - -## Success Metrics - -- **Coverage**: 100% of MCPM commands have non-interactive alternatives -- **Compatibility**: 100% backward compatibility with existing workflows -- **Performance**: Non-interactive commands execute ≤ 50ms faster than interactive -- **Reliability**: 99.9% success rate for valid parameter combinations -- **Usability**: Clear documentation and help text for all new parameters - -## Timeline - -- **Phase 1**: Server Management (1-2 weeks) -- **Phase 2**: Profile Management (1-2 weeks) -- **Phase 3**: Client Management (2-3 weeks) -- **Phase 4**: Configuration Management (1 week) -- **Phase 5**: Migration Enhancement (1 week) -- **Testing & Documentation**: 1-2 weeks - -**Total Estimated Timeline**: 7-11 weeks - -## Implementation Order - -1. Create utility functions and infrastructure -2. Implement server management commands (highest impact) -3. Implement profile management commands -4. Implement client management commands -5. Implement configuration management commands -6. Implement migration enhancements -7. Add comprehensive testing -8. Update documentation and help text - -This plan transforms MCPM from a user-centric tool with interactive elements into a fully AI-agent-friendly CLI tool while maintaining all existing functionality for human users. \ No newline at end of file diff --git a/src/mcpm/cli.py b/src/mcpm/cli.py index 1aeef56..7d338cc 100644 --- a/src/mcpm/cli.py +++ b/src/mcpm/cli.py @@ -28,7 +28,6 @@ usage, ) from mcpm.commands.share import share -from mcpm.migration import V1ConfigDetector, V1ToV2Migrator from mcpm.utils.logging_config import setup_logging from mcpm.utils.rich_click_config import click, get_header_text import os @@ -117,19 +116,6 @@ def main(ctx, version, help_flag): click.rich_click.FOOTER_TEXT = original_footer return - # Check for v1 configuration and offer migration (even with subcommands) - detector = V1ConfigDetector() - if detector.has_v1_config(): - migrator = V1ToV2Migrator() - migration_choice = migrator.show_migration_prompt() - if migration_choice == "migrate": - migrator.migrate_config() - return - elif migration_choice == "start_fresh": - migrator.start_fresh() - # Continue to execute the subcommand - # If "ignore", continue to subcommand without migration - # If no command was invoked, show help with header and footer if ctx.invoked_subcommand is None: console.print(get_header_text()) diff --git a/src/mcpm/commands/run.py b/src/mcpm/commands/run.py index 240381d..2da28de 100644 --- a/src/mcpm/commands/run.py +++ b/src/mcpm/commands/run.py @@ -131,28 +131,49 @@ async def find_available_port(preferred_port, max_attempts=10): @click.command() @click.argument("server_name") -@click.option("--http", is_flag=True, help="Run server over HTTP instead of stdio") -@click.option("--sse", is_flag=True, help="Run server over SSE instead of stdio") -@click.option("--port", type=int, default=DEFAULT_PORT, help=f"Port for HTTP / SSE mode (default: {DEFAULT_PORT})") -@click.option("--host", type=str, default="127.0.0.1", help="Host address for HTTP / SSE mode (default: 127.0.0.1)") +@click.option("--http", is_flag=True, help="Run server in HTTP mode (mutually exclusive with --sse)") +@click.option("--sse", is_flag=True, help="Run server in SSE mode (mutually exclusive with --http)") +@click.option( + "--port", + type=int, + default=DEFAULT_PORT, + help=f"Port for HTTP/SSE mode (default: {DEFAULT_PORT}, auto-finds available)", +) +@click.option("--host", type=str, default="127.0.0.1", help="Host for HTTP/SSE mode (use 0.0.0.0 for all interfaces)") @click.help_option("-h", "--help") def run(server_name, http, sse, port, host): - """Execute a server from global configuration over stdio, HTTP, or SSE. + """Execute an installed MCP server in stdio (default), HTTP, or SSE mode. + + By default, servers run over **stdio** for direct client communication. + + **Examples:** + + STDIO Mode (default): - Runs an installed MCP server from the global configuration. By default - runs over stdio for client communication, but can run over HTTP with --http - or over SSE with --sse. + \b + mcpm run server-name - Examples: - mcpm run mcp-server-browse # Run over stdio (default) - mcpm run --http mcp-server-browse # Run over HTTP on 127.0.0.1:6276 - mcpm run --sse mcp-server-browse # Run over SSE on 127.0.0.1:6276 - mcpm run --http --port 9000 filesystem # Run over HTTP on 127.0.0.1:9000 - mcpm run --sse --port 9000 filesystem # Run over SSE on 127.0.0.1:9000 - mcpm run --http --host 0.0.0.0 filesystem # Run over HTTP on 0.0.0.0:6276 + HTTP Mode: - Note: stdio mode is typically used in MCP client configurations: + \b + mcpm run --http server-name + mcpm run --http --port 9000 server-name + mcpm run --http --host 0.0.0.0 server-name + + SSE Mode: + + \b + mcpm run --sse server-name + mcpm run --sse --port 9000 server-name + mcpm run --sse --host 0.0.0.0 server-name + + Client Config Example: {"command": ["mcpm", "run", "mcp-server-browse"]} + + **Tips:** + • Port defaults to **6276**, auto-finds if busy + • Host defaults to **127.0.0.1** + • Use `mcpm ls` to see installed servers """ # Validate server name if not server_name or not server_name.strip(): diff --git a/src/mcpm/migration/v1_migrator.py b/src/mcpm/migration/v1_migrator.py index 9dac14f..13435f0 100644 --- a/src/mcpm/migration/v1_migrator.py +++ b/src/mcpm/migration/v1_migrator.py @@ -35,23 +35,48 @@ def __init__(self, config_dir: Optional[Path] = None): def _wait_for_keypress(self, message: str): """Wait for any key press (cross-platform)""" import sys - import termios - import tty + + try: + import termios + import tty + except ImportError: + termios = None + tty = None console.print(message, end="") + console.file.flush() - try: - # Unix/Linux/macOS - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) + if not sys.stdin.isatty(): + console.print() + return + + def _fallback_wait(): try: - tty.setraw(sys.stdin.fileno()) - sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - except (ImportError, AttributeError): - # Windows fallback - use input() which requires Enter - input() + input() + except (EOFError, OSError): + pass + + if termios and tty: + try: + # Unix/Linux/macOS + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + except (AttributeError, termios.error, ValueError, OSError, EOFError): + _fallback_wait() + else: + try: + import msvcrt + try: + msvcrt.getch() + except (OSError, EOFError): + _fallback_wait() + except ImportError: + _fallback_wait() console.print() # Add newline after keypress diff --git a/src/mcpm/utils/rich_click_config.py b/src/mcpm/utils/rich_click_config.py index 0ca8ff1..0c74f44 100644 --- a/src/mcpm/utils/rich_click_config.py +++ b/src/mcpm/utils/rich_click_config.py @@ -165,7 +165,7 @@ def get_header_text(): "mcpm run": [ { "name": "Execution Mode", - "options": ["--http", "--port"], + "options": ["--http", "--sse", "--port", "--host"], }, { "name": "Help", diff --git a/tests/test_global_config.py b/tests/test_global_config.py index d5360ab..1c67b6f 100644 --- a/tests/test_global_config.py +++ b/tests/test_global_config.py @@ -40,18 +40,14 @@ def test_list_shows_global_config(): def test_v2_help_shows_global_model(): """Test that help shows v2.0 global configuration messaging""" - from unittest.mock import patch - - # Mock v1 config detection to avoid migration prompt - with patch("mcpm.cli.V1ConfigDetector.has_v1_config", return_value=False): - runner = CliRunner() - result = runner.invoke(main, ["--help"]) - - assert result.exit_code == 0 - assert "global configuration" in result.output.lower() - assert "profile" in result.output.lower() - assert "install" in result.output - assert "run" in result.output + runner = CliRunner() + result = runner.invoke(main, ["--help"]) + + assert result.exit_code == 0 + assert "global configuration" in result.output.lower() + assert "profile" in result.output.lower() + assert "install" in result.output + assert "run" in result.output def test_deprecated_commands_removed():