Skip to main content
Glama

MCP SSH Manager

mcp-ssh-manager-setup.md61.1 kB
# MCP SSH Manager - Complete Project Setup Complete instructions for Claude Code. Create all files in `/Users/jeremy/mcp/mcp-ssh-manager/` ## 📦 STEP 1: Project structure Create this folder structure: ``` /Users/jeremy/mcp/mcp-ssh-manager/ ├── src/ │ └── index.js ├── tools/ │ ├── server-manager.py │ ├── test-connection.py │ └── requirements.txt ├── examples/ │ ├── .env.example │ └── claude-code-config.example.json ├── .github/ │ └── workflows/ │ └── test.yml ├── package.json ├── .gitignore ├── LICENSE ├── README.md └── CONTRIBUTING.md ``` ## 📄 STEP 2: Files to create ### 📌 FILE: package.json ```json { "name": "mcp-ssh-manager", "version": "1.0.0", "description": "MCP server for managing multiple SSH connections in Claude Code", "main": "src/index.js", "type": "module", "scripts": { "start": "node src/index.js", "setup": "npm install && pip install -r tools/requirements.txt", "configure": "python tools/server-manager.py", "test-connection": "python tools/test-connection.py" }, "keywords": [ "mcp", "ssh", "claude-code", "model-context-protocol", "remote-server", "ssh-manager" ], "author": "MCP SSH Manager Contributors", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/yourusername/mcp-ssh-manager.git" }, "bugs": { "url": "https://github.com/yourusername/mcp-ssh-manager/issues" }, "homepage": "https://github.com/yourusername/mcp-ssh-manager#readme", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.2", "node-ssh": "^13.2.0", "dotenv": "^16.4.5" }, "engines": { "node": ">=16.0.0" } } ``` ### 📌 FILE: src/index.js ```javascript #!/usr/bin/env node import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from '@modelcontextprotocol/sdk/types.js'; import { NodeSSH } from 'node-ssh'; import * as dotenv from 'dotenv'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Load environment variables dotenv.config({ path: path.join(__dirname, '..', '.env') }); // Map to store active connections const connections = new Map(); // Load server configuration from .env function loadServerConfig() { const servers = {}; // Parse environment variables to extract servers for (const [key, value] of Object.entries(process.env)) { const match = key.match(/^SSH_SERVER_(\w+)_(\w+)$/); if (match) { const [, serverName, field] = match; const serverNameLower = serverName.toLowerCase(); if (!servers[serverNameLower]) { servers[serverNameLower] = {}; } servers[serverNameLower][field.toLowerCase()] = value; } } return servers; } // Get or create SSH connection async function getConnection(serverName) { const normalizedName = serverName.toLowerCase(); if (!connections.has(normalizedName)) { const servers = loadServerConfig(); const serverConfig = servers[normalizedName]; if (!serverConfig) { const availableServers = Object.keys(servers); throw new Error( `Server "${serverName}" not found. Available servers: ${availableServers.join(', ') || 'none'}` ); } const ssh = new NodeSSH(); try { const connectionConfig = { host: serverConfig.host, username: serverConfig.user, port: parseInt(serverConfig.port || '22'), }; // Use password or SSH key if (serverConfig.password) { connectionConfig.password = serverConfig.password; } else if (serverConfig.keypath) { const keyPath = serverConfig.keypath.replace('~', process.env.HOME); connectionConfig.privateKey = fs.readFileSync(keyPath, 'utf8'); } await ssh.connect(connectionConfig); connections.set(normalizedName, ssh); console.error(`✅ Connected to ${serverName}`); } catch (error) { throw new Error(`Failed to connect to ${serverName}: ${error.message}`); } } return connections.get(normalizedName); } // Create MCP server const server = new McpServer({ name: 'mcp-ssh-manager', version: '1.0.0', }); // Register available tools server.registerTool( 'ssh_execute', { description: 'Execute command on remote SSH server', inputSchema: z.object({ server: z.string().describe('Server name from configuration'), command: z.string().describe('Command to execute'), cwd: z.string().optional().describe('Working directory (optional)') }) }, async ({ server: serverName, command, cwd }) => { try { const ssh = await getConnection(serverName); const fullCommand = cwd ? `cd ${cwd} && ${command}` : command; const result = await ssh.execCommand(fullCommand); return { content: [ { type: 'text', text: JSON.stringify({ server: serverName, command: fullCommand, stdout: result.stdout, stderr: result.stderr, code: result.code, success: result.code === 0, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `❌ Error: ${error.message}`, }, ], }; } } ); server.registerTool( 'ssh_upload', { description: 'Upload file to remote SSH server', inputSchema: z.object({ server: z.string().describe('Server name'), localPath: z.string().describe('Local file path'), remotePath: z.string().describe('Remote destination path') }) }, async ({ server: serverName, localPath, remotePath }) => { try { const ssh = await getConnection(serverName); await ssh.putFile(localPath, remotePath); return { content: [ { type: 'text', text: `✅ File uploaded successfully\nServer: ${serverName}\nLocal: ${localPath}\nRemote: ${remotePath}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `❌ Upload error: ${error.message}`, }, ], }; } } ); server.registerTool( 'ssh_download', { description: 'Download file from remote SSH server', inputSchema: z.object({ server: z.string().describe('Server name'), remotePath: z.string().describe('Remote file path'), localPath: z.string().describe('Local destination path') }) }, async ({ server: serverName, remotePath, localPath }) => { try { const ssh = await getConnection(serverName); await ssh.getFile(localPath, remotePath); return { content: [ { type: 'text', text: `✅ File downloaded successfully\nServer: ${serverName}\nRemote: ${remotePath}\nLocal: ${localPath}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `❌ Download error: ${error.message}`, }, ], }; } } ); server.registerTool( 'ssh_list_servers', { description: 'List all configured SSH servers', inputSchema: z.object({}) }, async () => { const servers = loadServerConfig(); const serverInfo = Object.entries(servers).map(([name, config]) => ({ name, host: config.host, user: config.user, port: config.port || '22', auth: config.password ? 'password' : 'key', description: config.description || '' })); return { content: [ { type: 'text', text: JSON.stringify(serverInfo, null, 2), }, ], }; } ); // Clean up connections on shutdown process.on('SIGINT', async () => { console.error('\n🔌 Closing SSH connections...'); for (const [name, ssh] of connections) { ssh.dispose(); console.error(` Closed connection to ${name}`); } process.exit(0); }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); const servers = loadServerConfig(); const serverList = Object.keys(servers); console.error('🚀 MCP SSH Manager Server started'); console.error(`📦 Available servers: ${serverList.length > 0 ? serverList.join(', ') : 'none configured'}`); console.error('💡 Use server-manager.py to add servers'); } main().catch(console.error); ``` ### 📌 FILE: tools/server-manager.py ```python #!/usr/bin/env python3 """ SSH Server Manager - Interface for managing SSH servers """ import os import sys import json import re from pathlib import Path from typing import Dict, Optional import subprocess from getpass import getpass try: import paramiko from colorama import init, Fore, Style from tabulate import tabulate init() except ImportError: print("Installing required packages...") subprocess.run([sys.executable, '-m', 'pip', 'install', 'paramiko', 'python-dotenv', 'colorama', 'tabulate']) import paramiko from colorama import init, Fore, Style from tabulate import tabulate init() class SSHServerManager: def __init__(self): self.script_dir = Path(__file__).parent.parent self.env_file = self.script_dir / '.env' self.servers = self.load_servers() def load_servers(self) -> Dict: """Load servers from .env file""" servers = {} if not self.env_file.exists(): return servers with open(self.env_file, 'r') as f: lines = f.readlines() for line in lines: line = line.strip() if line and not line.startswith('#') and '=' in line: key, value = line.split('=', 1) if key.startswith('SSH_SERVER_'): parts = key.split('_') if len(parts) >= 4: server_name = parts[2].lower() field = '_'.join(parts[3:]).lower() if server_name not in servers: servers[server_name] = {} servers[server_name][field] = value.strip('"\'') return servers def save_servers(self): """Save servers to .env file""" lines = [] # Add header lines.append("# ============================================\n") lines.append("# MCP SSH Manager - Server Configuration\n") lines.append("# ============================================\n") lines.append("# Generated by server-manager.py\n") lines.append("# NEVER commit this file to version control!\n\n") # Add each server for server_name, config in self.servers.items(): lines.append(f"# Server: {server_name}\n") for field, value in config.items(): key = f"SSH_SERVER_{server_name.upper()}_{field.upper()}" # Escape values containing spaces or special characters if ' ' in value or '"' in value or '=' in value: value = f'"{value}"' lines.append(f"{key}={value}\n") lines.append("\n") with open(self.env_file, 'w') as f: f.writelines(lines) print(f"{Fore.GREEN}✅ Configuration saved to {self.env_file}{Style.RESET_ALL}") def test_connection(self, server_name: str) -> bool: """Test connection to a server""" server_name = server_name.lower() if server_name not in self.servers: print(f"{Fore.RED}❌ Server '{server_name}' not found{Style.RESET_ALL}") return False config = self.servers[server_name] print(f"\n{Fore.CYAN}Testing connection to {server_name}...{Style.RESET_ALL}") print(f" Host: {config.get('host')}") print(f" User: {config.get('user')}") print(f" Port: {config.get('port', '22')}") client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: if 'password' in config: client.connect( hostname=config['host'], username=config['user'], password=config['password'], port=int(config.get('port', 22)), timeout=10 ) elif 'keypath' in config: key_path = os.path.expanduser(config['keypath']) client.connect( hostname=config['host'], username=config['user'], key_filename=key_path, port=int(config.get('port', 22)), timeout=10 ) else: print(f"{Fore.RED}❌ No authentication method configured{Style.RESET_ALL}") return False # Test with a simple command stdin, stdout, stderr = client.exec_command('echo "Connection successful" && hostname') output = stdout.read().decode().strip() print(f"{Fore.GREEN}✅ Connection successful!{Style.RESET_ALL}") print(f" Response: {output}") client.close() return True except Exception as e: print(f"{Fore.RED}❌ Connection failed: {e}{Style.RESET_ALL}") return False def validate_server_name(self, name: str) -> bool: """Validate server name""" if not name: return False # Accept letters, numbers, underscores and hyphens return bool(re.match(r'^[a-zA-Z0-9_-]+ ### 📌 FILE: tools/test-connection.py ```python #!/usr/bin/env python3 """Quick SSH connection testing""" import sys import os sys.path.append(os.path.dirname(os.path.abspath(__file__))) from server_manager import SSHServerManager def main(): manager = SSHServerManager() if len(sys.argv) < 2: print("Usage: python test-connection.py <server_name>") print("\nAvailable servers:") for server in manager.servers.keys(): print(f" - {server}") sys.exit(1) server_name = sys.argv[1] success = manager.test_connection(server_name) sys.exit(0 if success else 1) if __name__ == "__main__": main() ``` ### 📌 FILE: tools/requirements.txt ```txt paramiko>=3.4.0 python-dotenv>=1.0.1 colorama>=0.4.6 tabulate>=0.9.0 ``` ### 📌 FILE: examples/.env.example ```env # ============================================ # MCP SSH Manager - Server Configuration # ============================================ # Copy this file to the root directory as .env # and fill with your actual server details # NEVER commit the .env file to version control! # -------------------------------------------- # Example Server 1: Production (Password Auth) # -------------------------------------------- SSH_SERVER_PRODUCTION_HOST=prod.example.com SSH_SERVER_PRODUCTION_USER=admin SSH_SERVER_PRODUCTION_PASSWORD=your_secure_password_here SSH_SERVER_PRODUCTION_PORT=22 SSH_SERVER_PRODUCTION_DESCRIPTION=Main production server # -------------------------------------------- # Example Server 2: Staging (SSH Key Auth) # -------------------------------------------- SSH_SERVER_STAGING_HOST=staging.example.com SSH_SERVER_STAGING_USER=deploy SSH_SERVER_STAGING_KEYPATH=~/.ssh/staging_key SSH_SERVER_STAGING_PORT=22 SSH_SERVER_STAGING_DESCRIPTION=Staging environment # -------------------------------------------- # Example Server 3: Development (Custom Port) # -------------------------------------------- SSH_SERVER_DEVELOPMENT_HOST=192.168.1.100 SSH_SERVER_DEVELOPMENT_USER=developer SSH_SERVER_DEVELOPMENT_PASSWORD=dev_password_here SSH_SERVER_DEVELOPMENT_PORT=2222 SSH_SERVER_DEVELOPMENT_DESCRIPTION=Local development server # -------------------------------------------- # Server Naming Convention: # SSH_SERVER_[NAME]_[PROPERTY] # # Where NAME is your server identifier (UPPERCASE) # And PROPERTY can be: # - HOST: Server hostname or IP # - USER: SSH username # - PASSWORD: SSH password (for password auth) # - KEYPATH: Path to SSH private key (for key auth) # - PORT: SSH port (default: 22) # - DESCRIPTION: Optional description # -------------------------------------------- ``` ### 📌 FILE: examples/claude-code-config.example.json ```json { "apiKey": "your-anthropic-api-key-here", "mcpServers": { "ssh-manager": { "command": "node", "args": ["/Users/jeremy/mcp/mcp-ssh-manager/src/index.js"] }, "example-other-server": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"] } } } ``` ### 📌 FILE: .gitignore ```gitignore # Environment variables - NEVER commit these! .env .env.local .env.*.local # Dependencies node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* package-lock.json # Python __pycache__/ *.py[cod] *$py.class *.so .Python env/ venv/ ENV/ .venv # IDE .vscode/ .idea/ *.swp *.swo *~ .DS_Store # Logs logs/ *.log # Testing coverage/ .nyc_output/ .pytest_cache/ # Build dist/ build/ *.egg-info/ # Temporary files tmp/ temp/ # SSH Keys - NEVER commit these! *.pem *.key id_rsa* id_dsa* id_ecdsa* id_ed25519* # Backup files *.backup *.bak ``` ### 📌 FILE: README.md ```markdown # MCP SSH Manager 🚀 A Model Context Protocol (MCP) server that enables Claude Code to manage multiple SSH connections seamlessly. Features a Python-based CLI for easy server configuration and connection testing. ## 🌟 Features - **Multiple SSH Server Management**: Configure and manage multiple SSH servers from a single interface - **Secure Credential Storage**: Uses `.env` files to store credentials securely (never committed to git) - **Python Management Interface**: User-friendly CLI for adding, testing, and removing servers - **Claude Code Integration**: Seamless integration with Claude Code through MCP - **Connection Testing**: Built-in connection testing before deployment - **Support for Multiple Authentication Methods**: Password and SSH key authentication ## 📋 Prerequisites - Node.js (v16 or higher) - Python 3.8+ - Claude Code installed and configured - npm (comes with Node.js) - pip (Python package manager) ## 🚀 Quick Start ### 1. Clone the Repository ```bash git clone https://github.com/yourusername/mcp-ssh-manager.git cd mcp-ssh-manager ``` ### 2. Install Dependencies ```bash # Install Node.js dependencies npm install # Install Python dependencies pip install -r tools/requirements.txt ``` ### 3. Configure Your Servers Run the interactive server manager: ```bash python tools/server-manager.py ``` Choose from the menu: - `1` - List configured servers - `2` - Add a new server - `3` - Test server connection - `4` - Remove a server - `5` - Update Claude Code configuration - `6` - Install/update dependencies ### 4. Add Your First Server When adding a server, you'll be prompted for: - Server name (e.g., `production`, `staging`, `development`) - Host/IP address - Username - Port (default: 22) - Authentication method (password or SSH key) - Optional description ### 5. Integrate with Claude Code Update your Claude Code configuration: ```bash python tools/server-manager.py update-claude ``` Or manually add to `~/.config/claude-code/claude_code_config.json`: ```json { "mcpServers": { "ssh-manager": { "command": "node", "args": ["/path/to/mcp-ssh-manager/src/index.js"] } } } ``` ### 6. Restart Claude Code After configuration, restart Claude Code to load the MCP server. ## 🔧 Configuration ### Environment Variables The `.env` file stores your server configurations: ```env # Server: production SSH_SERVER_PRODUCTION_HOST=prod.example.com SSH_SERVER_PRODUCTION_USER=admin SSH_SERVER_PRODUCTION_PASSWORD=your_password_here SSH_SERVER_PRODUCTION_PORT=22 SSH_SERVER_PRODUCTION_DESCRIPTION=Production Server # Server: staging (using SSH key) SSH_SERVER_STAGING_HOST=staging.example.com SSH_SERVER_STAGING_USER=deploy SSH_SERVER_STAGING_KEYPATH=~/.ssh/staging_key SSH_SERVER_STAGING_PORT=22 ``` **⚠️ Important**: Never commit your `.env` file to version control! ## 📝 Usage in Claude Code Once configured, you can use these commands in Claude Code: ### List Available Servers ``` Use the ssh_list_servers tool to show all configured servers ``` ### Execute Commands ``` Use ssh_execute on server "production" to run "ls -la /var/www" Execute "docker ps" on the staging server Run "systemctl status nginx" on production ``` ### File Operations ``` Upload file.txt to production:/home/admin/file.txt Download /var/log/app.log from staging server to ./app.log ``` ### Working Directory ``` On production server, run "npm install" in directory /var/www/app ``` ## 🛠️ Available MCP Tools - **ssh_execute**: Execute commands on remote servers - **ssh_upload**: Upload files to remote servers - **ssh_download**: Download files from remote servers - **ssh_list_servers**: List all configured servers ## 📁 Project Structure ``` mcp-ssh-manager/ ├── src/ # Core MCP server implementation │ └── index.js # Main MCP server with all functionality ├── tools/ # Management utilities │ ├── server-manager.py # Interactive server manager │ ├── test-connection.py # Connection testing script │ └── requirements.txt # Python dependencies ├── examples/ # Example configurations │ ├── .env.example # Template for environment variables │ └── claude-code-config.example.json # Claude Code config example └── package.json # Node.js dependencies ``` ## 🧪 Testing Connections Test a specific server connection: ```bash python tools/test-connection.py production ``` Or use the interactive manager: ```bash python tools/server-manager.py # Then choose option 3 ``` ## 🔒 Security Best Practices 1. **Never commit `.env` files** - Always use `.env.example` as a template 2. **Use SSH keys when possible** - More secure than passwords 3. **Limit server access** - Use minimal required permissions 4. **Rotate credentials regularly** - Update passwords and keys periodically 5. **Use strong passwords** - Generate secure passwords for server access ## 🤝 Contributing We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details. ### Development Setup 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/AmazingFeature`) 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) 4. Push to the branch (`git push origin feature/AmazingFeature`) 5. Open a Pull Request ## 📄 License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## 🐛 Troubleshooting ### MCP Tools Not Available in Claude Code 1. Ensure the server is properly configured in Claude Code config 2. Restart Claude Code after configuration changes 3. Check server logs: `node src/index.js` (run manually to see errors) ### Connection Failed 1. Verify server credentials using `test-connection.py` 2. Check network connectivity to the server 3. Ensure SSH service is running on the remote server 4. Verify firewall rules allow SSH connections ### Permission Denied 1. Check username and password/key 2. Verify SSH key permissions: `chmod 600 ~/.ssh/your_key` 3. Ensure user has necessary permissions on the remote server ## 📊 Roadmap - [ ] GUI interface (Tkinter/PyQt) - [ ] Session management and connection pooling - [ ] SFTP browser interface - [ ] Terminal emulator integration - [ ] Connection profiles export/import - [ ] Encrypted credential storage - [ ] Multi-factor authentication support - [ ] Proxy/jump host support ## 🙏 Acknowledgments - Built for use with [Claude Code](https://www.anthropic.com) - Uses the [Model Context Protocol](https://modelcontextprotocol.io) - SSH handling via [node-ssh](https://www.npmjs.com/package/node-ssh) - Python SSH testing with [Paramiko](https://www.paramiko.org) ## 📧 Support For issues, questions, or suggestions: - Open an issue on [GitHub Issues](https://github.com/yourusername/mcp-ssh-manager/issues) - Check existing issues before creating a new one - Provide detailed information for bug reports --- Made with ❤️ for the Claude Code community ``` ### 📌 FILE: CONTRIBUTING.md ```markdown # Contributing to MCP SSH Manager First off, thank you for considering contributing to MCP SSH Manager! It's people like you that make this tool better for everyone. ## Code of Conduct This project and everyone participating in it is governed by our Code of Conduct. By participating, you are expected to uphold this code. ## How Can I Contribute? ### Reporting Bugs Before creating bug reports, please check existing issues as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible: - **Use a clear and descriptive title** - **Describe the exact steps to reproduce the problem** - **Provide specific examples** - **Describe the behavior you observed and expected** - **Include logs and error messages** - **Include your environment details** (OS, Node.js version, Python version) ### Suggesting Enhancements Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please include: - **Use a clear and descriptive title** - **Provide a detailed description of the suggested enhancement** - **Provide specific examples to demonstrate the enhancement** - **Describe the current behavior and expected behavior** - **Explain why this enhancement would be useful** ### Pull Requests 1. Fork the repo and create your branch from `main` 2. If you've added code that should be tested, add tests 3. Ensure the test suite passes 4. Make sure your code follows the existing code style 5. Write a clear commit message ## Development Process 1. Clone your fork: ```bash git clone https://github.com/your-username/mcp-ssh-manager.git cd mcp-ssh-manager ``` 2. Install dependencies: ```bash npm install pip install -r tools/requirements.txt ``` 3. Create a branch: ```bash git checkout -b feature/your-feature-name ``` 4. Make your changes and test them 5. Commit your changes: ```bash git add . git commit -m "Add your descriptive commit message" ``` 6. Push to your fork: ```bash git push origin feature/your-feature-name ``` 7. Open a Pull Request ## Style Guidelines ### JavaScript Style - Use ES6+ features - Use async/await for asynchronous code - Add JSDoc comments for functions - Use meaningful variable names ### Python Style - Follow PEP 8 - Use type hints where appropriate - Add docstrings to functions and classes - Use meaningful variable names ### Commit Messages - Use the present tense ("Add feature" not "Added feature") - Use the imperative mood ("Move cursor to..." not "Moves cursor to...") - Limit the first line to 72 characters or less - Reference issues and pull requests liberally after the first line ## Testing Before submitting a pull request: 1. Test your changes manually 2. Ensure existing functionality still works 3. Test with different server configurations 4. Verify Claude Code integration works ## Documentation - Update README.md if you change functionality - Add JSDoc/docstrings for new functions - Update examples if needed - Keep documentation clear and concise ## Questions? Feel free to open an issue with your question or reach out to the maintainers. Thank you for contributing! 🎉 ``` ### 📌 FILE: LICENSE ``` MIT License Copyright (c) 2024 MCP SSH Manager Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` ### 📌 FILE: .github/workflows/test.yml ```yaml name: Tests on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: node-version: [16.x, 18.x, 20.x] python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install Node dependencies run: npm ci - name: Install Python dependencies run: | python -m pip install --upgrade pip pip install -r tools/requirements.txt - name: Check Node syntax run: node --check src/index.js - name: Check Python syntax run: | python -m py_compile tools/server-manager.py python -m py_compile tools/test-connection.py - name: Create test .env file run: | echo "SSH_SERVER_TEST_HOST=localhost" > .env echo "SSH_SERVER_TEST_USER=test" >> .env echo "SSH_SERVER_TEST_PASSWORD=test" >> .env echo "SSH_SERVER_TEST_PORT=22" >> .env - name: Test server manager run: python tools/server-manager.py list ``` ## 🚀 STEP 3: Installation and Configuration ### Initial installation commands: ```bash # 1. Go to project directory cd /Users/jeremy/mcp/mcp-ssh-manager # 2. Install dependencies npm install pip install -r tools/requirements.txt # 3. Launch server manager python tools/server-manager.py # 4. Add DMIS server (option 2) # Server name: dmis # Host: dmis.neoffice.me # User: neoffice # Password: EkMEut57=s*ov5n00S73 # Port: 22 # Description: DMIS Production Server # 5. Test connection (option 3) # 6. Update Claude Code (option 5) ``` ## 📊 STEP 4: Usage in Claude Code Once installed and configured, restart Claude Code and use these commands: ```bash # List servers Use the ssh_list_servers tool # Execute a command Use ssh_execute on server dmis to run "ls -la" # Restart bench On dmis, execute "cd ~/frappe-bench && bench restart" # Upload/Download Upload file.txt to dmis:/home/neoffice/file.txt Download /var/log/app.log from dmis to ./app.log ``` ## 🔐 STEP 5: Git Configuration (to publish on GitHub) ```bash # Initialize git cd /Users/jeremy/mcp/mcp-ssh-manager git init # VERIFY that .env is NOT in git git status # .env should NOT appear # Add all files git add . # First commit git commit -m "Initial commit: MCP SSH Manager for Claude Code" # Create repo on GitHub, then: git remote add origin https://github.com/YOURUSERNAME/mcp-ssh-manager.git git branch -M main git push -u origin main # Release tag git tag -a v1.0.0 -m "First stable release" git push origin v1.0.0 ``` ## ⚠️ IMPORTANT - Security 1. **NEVER** commit the `.env` file (it's in .gitignore) 2. **ALWAYS** check with `git status` before pushing 3. **USE** `.env.example` as template 4. **DO NOT** put real data in examples ## 📝 Usage Notes - MCP server starts automatically when Claude Code calls it - SSH connections are cached for performance - Use Ctrl+C to properly close connections - Logs appear in Claude Code console ## 🎯 Next Steps After installation: 1. Test MCP tools in Claude Code 2. Add other servers if needed 3. Customize descriptions for each server 4. Publish on GitHub if desired (without .env!) --- **Ready!** Run these commands in Claude Code to create the entire project. 🚀 , name)) def add_server(self): """Add a new server""" print(f"\n{Fore.CYAN}=== Add New SSH Server ==={Style.RESET_ALL}\n") # Server name while True: server_name = input("Server name (e.g., 'dmis', 'production'): ").strip().lower() if not self.validate_server_name(server_name): print(f"{Fore.RED}Invalid name. Use only letters, numbers, underscores, and hyphens.{Style.RESET_ALL}") continue break if server_name in self.servers: overwrite = input(f"{Fore.YELLOW}Server '{server_name}' exists. Overwrite? (y/n): {Style.RESET_ALL}") if overwrite.lower() != 'y': return # Basic configuration config = {} config['host'] = input("Host/IP address: ").strip() if not config['host']: print(f"{Fore.RED}Host cannot be empty{Style.RESET_ALL}") return config['user'] = input("Username: ").strip() if not config['user']: print(f"{Fore.RED}Username cannot be empty{Style.RESET_ALL}") return config['port'] = input("Port [22]: ").strip() or "22" # Authentication method auth_method = input("Authentication method (password/key) [password]: ").strip().lower() or "password" if auth_method == "password": password = getpass("Password: ") if not password: print(f"{Fore.RED}Password cannot be empty{Style.RESET_ALL}") return config['password'] = password else: key_path = input("Path to private key [~/.ssh/id_rsa]: ").strip() or "~/.ssh/id_rsa" # Check if file exists expanded_path = os.path.expanduser(key_path) if not os.path.exists(expanded_path): print(f"{Fore.YELLOW}Warning: Key file not found at {expanded_path}{Style.RESET_ALL}") cont = input("Continue anyway? (y/n): ") if cont.lower() != 'y': return config['keypath'] = key_path # Optional description description = input("Description (optional): ").strip() if description: config['description'] = description # Save self.servers[server_name] = config self.save_servers() # Test connection test = input(f"\n{Fore.CYAN}Test connection now? (y/n): {Style.RESET_ALL}") if test.lower() == 'y': self.test_connection(server_name) def list_servers(self): """List all configured servers""" if not self.servers: print(f"{Fore.YELLOW}No servers configured yet.{Style.RESET_ALL}") print("Use option 2 to add a server.") return print(f"\n{Fore.CYAN}=== Configured SSH Servers ==={Style.RESET_ALL}\n") table_data = [] for name, config in self.servers.items(): auth_type = '🔑 Key' if 'keypath' in config else '🔐 Password' description = config.get('description', '') if len(description) > 30: description = description[:27] + '...' table_data.append([ name, config.get('host', ''), config.get('user', ''), config.get('port', '22'), auth_type, description ]) headers = ['Name', 'Host', 'User', 'Port', 'Auth', 'Description'] print(tabulate(table_data, headers=headers, tablefmt='grid')) def remove_server(self): """Remove a server""" if not self.servers: print(f"{Fore.YELLOW}No servers to remove.{Style.RESET_ALL}") return self.list_servers() server_name = input("\nEnter server name to remove: ").strip().lower() if server_name in self.servers: confirm = input(f"{Fore.YELLOW}Remove server '{server_name}'? (y/n): {Style.RESET_ALL}") if confirm.lower() == 'y': del self.servers[server_name] self.save_servers() print(f"{Fore.GREEN}✅ Server '{server_name}' removed{Style.RESET_ALL}") else: print(f"{Fore.RED}❌ Server '{server_name}' not found{Style.RESET_ALL}") def update_claude_config(self): """Update Claude Code configuration""" config_path = Path.home() / '.config' / 'claude-code' / 'claude_code_config.json' if not config_path.exists(): print(f"{Fore.RED}❌ Claude Code config not found at {config_path}{Style.RESET_ALL}") print("\nTo manually configure Claude Code, add this to your config:") print(f"{Fore.CYAN}") print(json.dumps({ "mcpServers": { "ssh-manager": { "command": "node", "args": [str(self.script_dir / 'src' / 'index.js')] } } }, indent=2)) print(f"{Style.RESET_ALL}") return try: with open(config_path, 'r') as f: config = json.load(f) except json.JSONDecodeError: print(f"{Fore.RED}❌ Invalid JSON in Claude Code config{Style.RESET_ALL}") return # Add our MCP server if 'mcpServers' not in config: config['mcpServers'] = {} config['mcpServers']['ssh-manager'] = { "command": "node", "args": [str(self.script_dir / 'src' / 'index.js')] } with open(config_path, 'w') as f: json.dump(config, f, indent=2) print(f"{Fore.GREEN}✅ Claude Code configuration updated{Style.RESET_ALL}") print(f"{Fore.YELLOW}⚠️ Please restart Claude Code to apply changes{Style.RESET_ALL}") def install_dependencies(self): """Install required dependencies""" print(f"\n{Fore.CYAN}=== Installing Dependencies ==={Style.RESET_ALL}\n") # Install npm dependencies print("Installing npm packages...") result = subprocess.run(['npm', 'install'], cwd=self.script_dir) if result.returncode != 0: print(f"{Fore.RED}❌ Failed to install npm packages{Style.RESET_ALL}") return # Install Python packages print("\nInstalling Python packages...") subprocess.run([sys.executable, '-m', 'pip', 'install', '-r', str(self.script_dir / 'tools' / 'requirements.txt')]) print(f"\n{Fore.GREEN}✅ Dependencies installed{Style.RESET_ALL}") def run_interactive(self): """Interactive menu""" while True: print(f"\n{Fore.CYAN}=== SSH Server Manager ==={Style.RESET_ALL}") print("1. List servers") print("2. Add server") print("3. Test connection") print("4. Remove server") print("5. Update Claude Code config") print("6. Install dependencies") print("0. Exit") choice = input(f"\n{Fore.YELLOW}Choice: {Style.RESET_ALL}").strip() if choice == '1': self.list_servers() elif choice == '2': self.add_server() elif choice == '3': if not self.servers: print(f"{Fore.YELLOW}No servers configured.{Style.RESET_ALL}") else: self.list_servers() server = input("\nServer name to test: ").strip() if server: self.test_connection(server) elif choice == '4': self.remove_server() elif choice == '5': self.update_claude_config() elif choice == '6': self.install_dependencies() elif choice == '0': print(f"{Fore.GREEN}Goodbye!{Style.RESET_ALL}") break else: print(f"{Fore.RED}Invalid choice{Style.RESET_ALL}") def main(): manager = SSHServerManager() if len(sys.argv) > 1: command = sys.argv[1] if command == 'list': manager.list_servers() elif command == 'add': manager.add_server() elif command == 'test' and len(sys.argv) > 2: manager.test_connection(sys.argv[2]) elif command == 'remove' and len(sys.argv) > 2: server_name = sys.argv[2].lower() if server_name in manager.servers: del manager.servers[server_name] manager.save_servers() print(f"Server '{server_name}' removed") elif command == 'update-claude': manager.update_claude_config() elif command == 'install': manager.install_dependencies() else: print(f"Unknown command: {command}") print("Usage: python server-manager.py [list|add|test|remove|update-claude|install]") else: manager.run_interactive() if __name__ == "__main__": main() ``` ### 📌 FILE: tools/test-connection.py ```python #!/usr/bin/env python3 """Test rapide des connexions SSH""" import sys import os sys.path.append(os.path.dirname(os.path.abspath(__file__))) from server_manager import SSHServerManager def main(): manager = SSHServerManager() if len(sys.argv) < 2: print("Usage: python test-connection.py <server_name>") print("\nAvailable servers:") for server in manager.servers.keys(): print(f" - {server}") sys.exit(1) server_name = sys.argv[1] success = manager.test_connection(server_name) sys.exit(0 if success else 1) if __name__ == "__main__": main() ``` ### 📌 FILE: tools/requirements.txt ```txt paramiko>=3.4.0 python-dotenv>=1.0.1 colorama>=0.4.6 tabulate>=0.9.0 ``` ### 📌 FILE: examples/.env.example ```env # ============================================ # MCP SSH Manager - Server Configuration # ============================================ # Copy this file to the root directory as .env # and fill with your actual server details # NEVER commit the .env file to version control! # -------------------------------------------- # Example Server 1: Production (Password Auth) # -------------------------------------------- SSH_SERVER_PRODUCTION_HOST=prod.example.com SSH_SERVER_PRODUCTION_USER=admin SSH_SERVER_PRODUCTION_PASSWORD=your_secure_password_here SSH_SERVER_PRODUCTION_PORT=22 SSH_SERVER_PRODUCTION_DESCRIPTION=Main production server # -------------------------------------------- # Example Server 2: Staging (SSH Key Auth) # -------------------------------------------- SSH_SERVER_STAGING_HOST=staging.example.com SSH_SERVER_STAGING_USER=deploy SSH_SERVER_STAGING_KEYPATH=~/.ssh/staging_key SSH_SERVER_STAGING_PORT=22 SSH_SERVER_STAGING_DESCRIPTION=Staging environment # -------------------------------------------- # Example Server 3: Development (Custom Port) # -------------------------------------------- SSH_SERVER_DEVELOPMENT_HOST=192.168.1.100 SSH_SERVER_DEVELOPMENT_USER=developer SSH_SERVER_DEVELOPMENT_PASSWORD=dev_password_here SSH_SERVER_DEVELOPMENT_PORT=2222 SSH_SERVER_DEVELOPMENT_DESCRIPTION=Local development server # -------------------------------------------- # Server Naming Convention: # SSH_SERVER_[NAME]_[PROPERTY] # # Where NAME is your server identifier (UPPERCASE) # And PROPERTY can be: # - HOST: Server hostname or IP # - USER: SSH username # - PASSWORD: SSH password (for password auth) # - KEYPATH: Path to SSH private key (for key auth) # - PORT: SSH port (default: 22) # - DESCRIPTION: Optional description # -------------------------------------------- ``` ### 📌 FILE: .gitignore ```gitignore # Environment variables - NEVER commit these! .env .env.local .env.*.local # Dependencies node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* package-lock.json # Python __pycache__/ *.py[cod] *$py.class *.so .Python env/ venv/ ENV/ .venv # IDE .vscode/ .idea/ *.swp *.swo *~ .DS_Store # Logs logs/ *.log # Testing coverage/ .nyc_output/ .pytest_cache/ # Build dist/ build/ *.egg-info/ # Temporary files tmp/ temp/ # SSH Keys - NEVER commit these! *.pem *.key id_rsa* id_dsa* id_ecdsa* id_ed25519* # Backup files *.backup *.bak ``` ### 📌 FILE: README.md ```markdown # MCP SSH Manager 🚀 A Model Context Protocol (MCP) server that enables Claude Code to manage multiple SSH connections seamlessly. Features a Python-based CLI for easy server configuration and connection testing. ## 🌟 Features - **Multiple SSH Server Management**: Configure and manage multiple SSH servers from a single interface - **Secure Credential Storage**: Uses `.env` files to store credentials securely (never committed to git) - **Python Management Interface**: User-friendly CLI for adding, testing, and removing servers - **Claude Code Integration**: Seamless integration with Claude Code through MCP - **Connection Testing**: Built-in connection testing before deployment - **Support for Multiple Authentication Methods**: Password and SSH key authentication ## 📋 Prerequisites - Node.js (v16 or higher) - Python 3.8+ - Claude Code installed and configured - npm (comes with Node.js) - pip (Python package manager) ## 🚀 Quick Start ### 1. Clone the Repository ```bash git clone https://github.com/yourusername/mcp-ssh-manager.git cd mcp-ssh-manager ``` ### 2. Install Dependencies ```bash # Install Node.js dependencies npm install # Install Python dependencies pip install -r tools/requirements.txt ``` ### 3. Configure Your Servers Run the interactive server manager: ```bash python tools/server-manager.py ``` Choose from the menu: - `1` - List configured servers - `2` - Add a new server - `3` - Test server connection - `4` - Remove a server - `5` - Update Claude Code configuration - `6` - Install/update dependencies ### 4. Add Your First Server When adding a server, you'll be prompted for: - Server name (e.g., `production`, `staging`, `development`) - Host/IP address - Username - Port (default: 22) - Authentication method (password or SSH key) - Optional description ### 5. Integrate with Claude Code Update your Claude Code configuration: ```bash python tools/server-manager.py update-claude ``` Or manually add to `~/.config/claude-code/claude_code_config.json`: ```json { "mcpServers": { "ssh-manager": { "command": "node", "args": ["/path/to/mcp-ssh-manager/src/index.js"] } } } ``` ### 6. Restart Claude Code After configuration, restart Claude Code to load the MCP server. ## 🔧 Configuration ### Environment Variables The `.env` file stores your server configurations: ```env # Server: production SSH_SERVER_PRODUCTION_HOST=prod.example.com SSH_SERVER_PRODUCTION_USER=admin SSH_SERVER_PRODUCTION_PASSWORD=your_password_here SSH_SERVER_PRODUCTION_PORT=22 SSH_SERVER_PRODUCTION_DESCRIPTION=Production Server # Server: staging (using SSH key) SSH_SERVER_STAGING_HOST=staging.example.com SSH_SERVER_STAGING_USER=deploy SSH_SERVER_STAGING_KEYPATH=~/.ssh/staging_key SSH_SERVER_STAGING_PORT=22 ``` **⚠️ Important**: Never commit your `.env` file to version control! ## 📝 Usage in Claude Code Once configured, you can use these commands in Claude Code: ### List Available Servers ``` Use the ssh_list_servers tool to show all configured servers ``` ### Execute Commands ``` Use ssh_execute on server "production" to run "ls -la /var/www" Execute "docker ps" on the staging server Run "systemctl status nginx" on production ``` ### File Operations ``` Upload file.txt to production:/home/admin/file.txt Download /var/log/app.log from staging server to ./app.log ``` ### Working Directory ``` On production server, run "npm install" in directory /var/www/app ``` ## 🛠️ Available MCP Tools - **ssh_execute**: Execute commands on remote servers - **ssh_upload**: Upload files to remote servers - **ssh_download**: Download files from remote servers - **ssh_list_servers**: List all configured servers ## 📁 Project Structure ``` mcp-ssh-manager/ ├── src/ # Core MCP server implementation │ ├── index.js # Main MCP server │ ├── ssh-handler.js # SSH connection handling │ └── config-loader.js # Configuration management ├── tools/ # Management utilities │ ├── server-manager.py # Interactive server manager │ ├── test-connection.py # Connection testing script │ └── requirements.txt # Python dependencies ├── examples/ # Example configurations └── package.json # Node.js dependencies ``` ## 🧪 Testing Connections Test a specific server connection: ```bash python tools/test-connection.py production ``` Or use the interactive manager: ```bash python tools/server-manager.py # Then choose option 3 ``` ## 🔒 Security Best Practices 1. **Never commit `.env` files** - Always use `.env.example` as a template 2. **Use SSH keys when possible** - More secure than passwords 3. **Limit server access** - Use minimal required permissions 4. **Rotate credentials regularly** - Update passwords and keys periodically 5. **Use strong passwords** - Generate secure passwords for server access ## 🤝 Contributing We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details. ### Development Setup 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/AmazingFeature`) 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) 4. Push to the branch (`git push origin feature/AmazingFeature`) 5. Open a Pull Request ## 📄 License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## 🐛 Troubleshooting ### MCP Tools Not Available in Claude Code 1. Ensure the server is properly configured in Claude Code config 2. Restart Claude Code after configuration changes 3. Check server logs: `node src/index.js` (run manually to see errors) ### Connection Failed 1. Verify server credentials using `test-connection.py` 2. Check network connectivity to the server 3. Ensure SSH service is running on the remote server 4. Verify firewall rules allow SSH connections ### Permission Denied 1. Check username and password/key 2. Verify SSH key permissions: `chmod 600 ~/.ssh/your_key` 3. Ensure user has necessary permissions on the remote server ## 📊 Roadmap - [ ] GUI interface (Tkinter/PyQt) - [ ] Session management and connection pooling - [ ] SFTP browser interface - [ ] Terminal emulator integration - [ ] Connection profiles export/import - [ ] Encrypted credential storage - [ ] Multi-factor authentication support - [ ] Proxy/jump host support ## 🙏 Acknowledgments - Built for use with [Claude Code](https://www.anthropic.com) - Uses the [Model Context Protocol](https://modelcontextprotocol.io) - SSH handling via [node-ssh](https://www.npmjs.com/package/node-ssh) - Python SSH testing with [Paramiko](https://www.paramiko.org) ## 📧 Support For issues, questions, or suggestions: - Open an issue on [GitHub Issues](https://github.com/yourusername/mcp-ssh-manager/issues) - Check existing issues before creating a new one - Provide detailed information for bug reports --- Made with ❤️ for the Claude Code community ``` ### 📌 FILE: CONTRIBUTING.md ```markdown # Contributing to MCP SSH Manager First off, thank you for considering contributing to MCP SSH Manager! It's people like you that make this tool better for everyone. ## Code of Conduct This project and everyone participating in it is governed by our Code of Conduct. By participating, you are expected to uphold this code. ## How Can I Contribute? ### Reporting Bugs Before creating bug reports, please check existing issues as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible: - **Use a clear and descriptive title** - **Describe the exact steps to reproduce the problem** - **Provide specific examples** - **Describe the behavior you observed and expected** - **Include logs and error messages** - **Include your environment details** (OS, Node.js version, Python version) ### Suggesting Enhancements Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please include: - **Use a clear and descriptive title** - **Provide a detailed description of the suggested enhancement** - **Provide specific examples to demonstrate the enhancement** - **Describe the current behavior and expected behavior** - **Explain why this enhancement would be useful** ### Pull Requests 1. Fork the repo and create your branch from `main` 2. If you've added code that should be tested, add tests 3. Ensure the test suite passes 4. Make sure your code follows the existing code style 5. Write a clear commit message ## Development Process 1. Clone your fork: ```bash git clone https://github.com/your-username/mcp-ssh-manager.git cd mcp-ssh-manager ``` 2. Install dependencies: ```bash npm install pip install -r tools/requirements.txt ``` 3. Create a branch: ```bash git checkout -b feature/your-feature-name ``` 4. Make your changes and test them 5. Commit your changes: ```bash git add . git commit -m "Add your descriptive commit message" ``` 6. Push to your fork: ```bash git push origin feature/your-feature-name ``` 7. Open a Pull Request ## Style Guidelines ### JavaScript Style - Use ES6+ features - Use async/await for asynchronous code - Add JSDoc comments for functions - Use meaningful variable names ### Python Style - Follow PEP 8 - Use type hints where appropriate - Add docstrings to functions and classes - Use meaningful variable names ### Commit Messages - Use the present tense ("Add feature" not "Added feature") - Use the imperative mood ("Move cursor to..." not "Moves cursor to...") - Limit the first line to 72 characters or less - Reference issues and pull requests liberally after the first line ## Testing Before submitting a pull request: 1. Test your changes manually 2. Ensure existing functionality still works 3. Test with different server configurations 4. Verify Claude Code integration works ## Documentation - Update README.md if you change functionality - Add JSDoc/docstrings for new functions - Update examples if needed - Keep documentation clear and concise ## Questions? Feel free to open an issue with your question or reach out to the maintainers. Thank you for contributing! 🎉 ``` ### 📌 FILE: LICENSE ``` MIT License Copyright (c) 2024 MCP SSH Manager Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` ### 📌 FILE: .github/workflows/test.yml ```yaml name: Tests on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: node-version: [16.x, 18.x, 20.x] python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install Node dependencies run: npm ci - name: Install Python dependencies run: | python -m pip install --upgrade pip pip install -r tools/requirements.txt - name: Check Node syntax run: node --check src/index.js - name: Check Python syntax run: | python -m py_compile tools/server-manager.py python -m py_compile tools/test-connection.py - name: Create test .env file run: | echo "SSH_SERVER_TEST_HOST=localhost" > .env echo "SSH_SERVER_TEST_USER=test" >> .env echo "SSH_SERVER_TEST_PASSWORD=test" >> .env echo "SSH_SERVER_TEST_PORT=22" >> .env - name: Test server manager run: python tools/server-manager.py list ``` ## 🚀 ÉTAPE 3: Installation et Configuration ### Commandes d'installation initiale: ```bash # 1. Aller dans le dossier du projet cd /Users/jeremy/mcp/mcp-ssh-manager # 2. Installer les dépendances npm install pip install -r tools/requirements.txt # 3. Lancer le gestionnaire de serveurs python tools/server-manager.py # 4. Ajouter le serveur DMIS (option 2) # Server name: dmis # Host: dmis.neoffice.me # User: neoffice # Password: EkMEut57=s*ov5n00S73 # Port: 22 # Description: DMIS Production Server # 5. Tester la connexion (option 3) # 6. Mettre à jour Claude Code (option 5) ``` ## 📊 ÉTAPE 4: Utilisation dans Claude Code Une fois installé et configuré, redémarre Claude Code et utilise ces commandes: ```bash # Lister les serveurs Utilise l'outil ssh_list_servers # Exécuter une commande Utilise ssh_execute sur le serveur dmis pour exécuter "ls -la" # Redémarrer bench Sur dmis, exécute "cd ~/frappe-bench && bench restart" # Upload/Download Upload file.txt vers dmis:/home/neoffice/file.txt Download /var/log/app.log depuis dmis vers ./app.log ``` ## 🔐 ÉTAPE 5: Configuration Git (pour publier sur GitHub) ```bash # Initialiser git cd /Users/jeremy/mcp/mcp-ssh-manager git init # VÉRIFIER que .env n'est PAS dans git git status # .env ne doit PAS apparaître # Ajouter tous les fichiers git add . # Premier commit git commit -m "Initial commit: MCP SSH Manager for Claude Code" # Créer le repo sur GitHub, puis: git remote add origin https://github.com/TONUSERNAME/mcp-ssh-manager.git git branch -M main git push -u origin main # Tag de release git tag -a v1.0.0 -m "First stable release" git push origin v1.0.0 ``` ## ⚠️ IMPORTANT - Sécurité 1. **JAMAIS** commiter le fichier `.env` (il est dans .gitignore) 2. **TOUJOURS** vérifier avec `git status` avant de push 3. **UTILISER** `.env.example` comme template 4. **NE PAS** mettre de vraies données dans les exemples ## 📝 Notes d'utilisation - Le serveur MCP démarre automatiquement quand Claude Code l'appelle - Les connexions SSH sont maintenues en cache pour la performance - Utilisez Ctrl+C pour fermer proprement les connexions - Les logs apparaissent dans la console de Claude Code ## 🎯 Prochaines étapes Après l'installation: 1. Tester les outils MCP dans Claude Code 2. Ajouter d'autres serveurs si nécessaire 3. Personnaliser les descriptions pour chaque serveur 4. Publier sur GitHub si désiré (sans le .env!) --- **Prêt!** Lance ces commandes dans Claude Code pour créer tout le projet. 🚀

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bvisible/mcp-ssh-manager'

If you have feedback or need assistance with the MCP directory API, please join our Discord server