{"_id":"@agent-remote/ssh","_rev":"3-21f6ab7e3ab456eab37c239a5adb2def","name":"@agent-remote/ssh","dist-tags":{"latest":"0.0.4"},"versions":{"0.0.2":{"name":"@agent-remote/ssh","version":"0.0.2","license":"Apache-2.0","_id":"@agent-remote/ssh@0.0.2","maintainers":[{"name":"loderunnr","email":"charles.francoise@gmail.com"}],"bin":{"remote-ssh-mcp":"dist/server.js"},"dist":{"shasum":"6f4fe8dde5c676ef61b05f150b75c2142e0a6714","tarball":"https://registry.npmjs.org/@agent-remote/ssh/-/ssh-0.0.2.tgz","fileCount":58,"integrity":"sha512-dgrbjjUd2bM7424g5BHdOzRatSCihqJe8z75Sl+4pSIKd88T5zthziB41kxE4GfMNx5VXJ1w00pdWy+rWkGUzQ==","signatures":[{"sig":"MEUCIEzEp7bHiD6hdeWOG7VPFpn8yolsOIj/15H2ZZV3wVfUAiEAtJ6QaalmoV2NXHqtCMVl7Ze9LnpC+9G0gqiznu2If2k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":335000},"main":"dist/cjs/index.cjs","type":"module","_from":"file:agent-remote-ssh-0.0.2.tgz","types":"dist/index.d.ts","module":"dist/esm/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.cjs"}},"scripts":{"test":"vitest run","build":"tsdown","clean":"rimraf dist","server":"tsx src/server/server.ts","test:e2e":"vitest run --project=e2e","test:unit":"vitest run --project=unit","test:watch":"vitest","build:watch":"tsdown --watch","server:watch":"tsx --watch src/server/server.ts","test:watch:e2e":"vitest --project=e2e","test:watch:unit":"vitest --project=unit"},"_npmUser":{"name":"loderunnr","email":"charles.francoise@gmail.com"},"_resolved":"/tmp/d96d1a994e83d703fb8e52bd45c11d68/agent-remote-ssh-0.0.2.tgz","_integrity":"sha512-dgrbjjUd2bM7424g5BHdOzRatSCihqJe8z75Sl+4pSIKd88T5zthziB41kxE4GfMNx5VXJ1w00pdWy+rWkGUzQ==","_npmVersion":"11.6.0","description":"SSH remote tools for AI agents","directories":{},"_nodeVersion":"24.9.0","dependencies":{"zod":"^3.25.76","diff":"^8.0.2","pino":"^10.1.0","ssh2":"^1.17.0","yargs":"^18.0.0","nanoid":"^5.1.6","minimatch":"^10.0.3","pino-pretty":"^13.1.2","zod-to-json-schema":"^3.24.6","zod-validation-error":"^4.0.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tslib":"^2.8.1","rimraf":"^6.0.1","tsdown":"^0.15.9","vitest":"^3.2.4","typescript":"^5.9.3","@types/ssh2":"^1.15.5","@types/yargs":"^17.0.33","@agent-remote/core":"0.0.1","@modelcontextprotocol/sdk":"^1.20.0","@anthropic-ai/claude-agent-sdk":"^0.1.21"},"peerDependencies":{"@modelcontextprotocol/sdk":"^1.20.0","@anthropic-ai/claude-agent-sdk":"^0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/ssh_0.0.2_1761579879205_0.6358287515428698","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@agent-remote/ssh","version":"0.0.3","license":"Apache-2.0","_id":"@agent-remote/ssh@0.0.3","maintainers":[{"name":"loderunnr","email":"charles.francoise@gmail.com"}],"bin":{"remote-ssh-mcp":"dist/server.js"},"dist":{"shasum":"1499a38f575b19cd0339e3b6f51c6cec30852d93","tarball":"https://registry.npmjs.org/@agent-remote/ssh/-/ssh-0.0.3.tgz","fileCount":58,"integrity":"sha512-3KP7e+awQL+0ZsoEOseSoG8hhxqmiiL/+nWqK1PYC0Ud1BS5QY21fQXmCm2ZDRXf3/8PuQZL4OQU++asoCCDJw==","signatures":[{"sig":"MEUCIDU8FyzeG7m8zLW56nhemTZ7jlbWEfz7IEMHhsJga9iEAiEAsAsClA5cQh4gPzx5kAQ38ek3cDPz8ZVOadQdqAyGGzs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":335000},"main":"dist/cjs/index.cjs","type":"module","_from":"file:agent-remote-ssh-0.0.3.tgz","types":"dist/index.d.ts","module":"dist/esm/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.cjs"}},"scripts":{"test":"vitest run","build":"tsdown","clean":"rimraf dist","server":"tsx src/server/server.ts","test:e2e":"vitest run --project=e2e","test:unit":"vitest run --project=unit","test:watch":"vitest","build:watch":"tsdown --watch","server:watch":"tsx --watch src/server/server.ts","test:watch:e2e":"vitest --project=e2e","test:watch:unit":"vitest --project=unit"},"_npmUser":{"name":"loderunnr","email":"charles.francoise@gmail.com"},"_resolved":"/tmp/dfeb5ba59647d35fc35759dca8621e60/agent-remote-ssh-0.0.3.tgz","_integrity":"sha512-3KP7e+awQL+0ZsoEOseSoG8hhxqmiiL/+nWqK1PYC0Ud1BS5QY21fQXmCm2ZDRXf3/8PuQZL4OQU++asoCCDJw==","_npmVersion":"11.6.0","description":"SSH remote tools for AI agents","directories":{},"_nodeVersion":"24.9.0","dependencies":{"zod":"^3.25.76","diff":"^8.0.2","pino":"^10.1.0","ssh2":"^1.17.0","yargs":"^18.0.0","nanoid":"^5.1.6","minimatch":"^10.0.3","pino-pretty":"^13.1.2","zod-to-json-schema":"^3.24.6","zod-validation-error":"^4.0.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tslib":"^2.8.1","rimraf":"^6.0.1","tsdown":"^0.15.9","vitest":"^3.2.4","typescript":"^5.9.3","@types/ssh2":"^1.15.5","@types/yargs":"^17.0.33","@agent-remote/core":"0.0.2","@modelcontextprotocol/sdk":"^1.20.0","@anthropic-ai/claude-agent-sdk":"^0.1.21"},"peerDependencies":{"@modelcontextprotocol/sdk":"^1.20.0","@anthropic-ai/claude-agent-sdk":"^0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/ssh_0.0.3_1761580362172_0.9734796471272027","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@agent-remote/ssh","version":"0.0.4","description":"SSH remote tools for AI agents","license":"Apache-2.0","author":{"name":"Charles Francoise"},"homepage":"https://github.com/loderunner/agent-remote#readme","repository":{"type":"git","url":"git+https://github.com/loderunner/agent-remote.git","directory":"packages/ssh"},"bugs":{"url":"https://github.com/loderunner/agent-remote/issues"},"keywords":["ai","agents","remote","ssh","ssh2","mcp","model-context-protocol","tools","automation","anthropic","claude","agent-sdk"],"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.cjs"}},"publishConfig":{"access":"public"},"dependencies":{"diff":"^8.0.2","minimatch":"^10.0.3","nanoid":"^5.1.6","pino":"^10.1.0","pino-pretty":"^13.1.2","ssh2":"^1.17.0","yargs":"^18.0.0","zod":"^3.25.76","zod-to-json-schema":"^3.24.6","zod-validation-error":"^4.0.2"},"peerDependencies":{"@anthropic-ai/claude-agent-sdk":"^0.1.0","@modelcontextprotocol/sdk":"^1.20.0"},"devDependencies":{"@anthropic-ai/claude-agent-sdk":"^0.1.21","@modelcontextprotocol/sdk":"^1.20.0","@types/ssh2":"^1.15.5","@types/yargs":"^17.0.33","rimraf":"^6.0.1","tsdown":"^0.15.9","tslib":"^2.8.1","tsx":"^4.20.6","typescript":"^5.9.3","vitest":"^3.2.4","@agent-remote/core":"0.0.2"},"scripts":{"build":"tsdown","build:watch":"tsdown --watch","test":"vitest run","test:watch":"vitest","test:unit":"vitest run --project=unit","test:e2e":"vitest run --project=e2e","test:watch:unit":"vitest --project=unit","test:watch:e2e":"vitest --project=e2e","server":"tsx src/server/server.ts","server:watch":"tsx --watch src/server/server.ts","clean":"rimraf dist"},"main":"dist/cjs/index.cjs","module":"dist/esm/index.js","types":"dist/index.d.ts","bin":{"remote-ssh-mcp":"dist/server.js"},"_id":"@agent-remote/ssh@0.0.4","_integrity":"sha512-jzTmxctAT4Nb0MLHVarHe1o0Qiu2wc+q+pwpHray2IJtAxbYifAVDjMbTyq1XtL2tCSSXos945MbBzL1ZlQolA==","_resolved":"/tmp/61229ddc18bb3cfaee46b2e1760853ad/agent-remote-ssh-0.0.4.tgz","_from":"file:agent-remote-ssh-0.0.4.tgz","_nodeVersion":"24.9.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-jzTmxctAT4Nb0MLHVarHe1o0Qiu2wc+q+pwpHray2IJtAxbYifAVDjMbTyq1XtL2tCSSXos945MbBzL1ZlQolA==","shasum":"1ca2a5e7e6e603d09bd6133561c514820a8b7a8d","tarball":"https://registry.npmjs.org/@agent-remote/ssh/-/ssh-0.0.4.tgz","fileCount":57,"unpackedSize":338595,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCTCadXnOCMKbXq9U+vufr4+ai1uHS9m6IUa0XHyDPTcwIgTqq3gRFzfZaZkt5ZzxrfEMe0r42VMGjREnz1zoWTwnw="}]},"_npmUser":{"name":"loderunnr","email":"charles.francoise@gmail.com"},"directories":{},"maintainers":[{"name":"loderunnr","email":"charles.francoise@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ssh_0.0.4_1761667048219_0.6299687101689009"},"_hasShrinkwrap":false}},"time":{"created":"2025-10-27T15:44:39.085Z","modified":"2025-10-28T15:57:28.582Z","0.0.2":"2025-10-27T15:44:39.386Z","0.0.3":"2025-10-27T15:52:42.361Z","0.0.4":"2025-10-28T15:57:28.395Z"},"license":"Apache-2.0","description":"SSH remote tools for AI agents","maintainers":[{"name":"loderunnr","email":"charles.francoise@gmail.com"}],"readme":"# @agent-remote/ssh\n\n[![npm version](https://img.shields.io/npm/v/@agent-remote/ssh.svg)](https://www.npmjs.com/package/@agent-remote/ssh)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](../../LICENSE)\n\nA TypeScript library for executing commands and managing files on remote systems\nvia SSH. Designed for integration with the Claude Agent SDK to provide AI agents\nwith remote system access.\n\n## Features\n\n- **Bash execution** - Run commands in persistent shell sessions with background\n  execution support\n- **File operations** - Read, write, and edit files remotely using SFTP when\n  available, automatically falling back to shell commands if SFTP is unavailable\n- **Search tools** - Grep pattern search and glob file matching\n- **Type-safe** - Full TypeScript support with Zod validation\n- **Agent SDK integration** - Easy integration with Claude Agent SDK via MCP\n  server\n- **Standalone MCP server** - Run as a standalone MCP server with stdio\n  transport\n\n## Installation\n\n```bash\npnpm add @agent-remote/ssh\n```\n\n## Quick Start\n\n### MCP Server (Standalone)\n\nThe package includes a standalone MCP server executable that can be run from the\ncommand line and communicates over stdio transport. The server is self-contained\nwith all dependencies bundled (except Node.js builtins), making it easy to\ndistribute and run without installing node_modules.\n\n**Installation:**\n\n```bash\nnpm install -g @agent-remote/ssh\n```\n\n**Usage with command line arguments:**\n\n```bash\n# With password authentication\nremote-ssh-mcp --host example.com --username user --password secret\n\n# With private key authentication\nremote-ssh-mcp --host example.com --username user --private-key ~/.ssh/id_rsa\n\n# With SSH agent\nremote-ssh-mcp --host example.com --username user --agent $SSH_AUTH_SOCK\n```\n\n**Usage with environment variables:**\n\n```bash\nexport SSH_HOST=example.com\nexport SSH_USERNAME=user\nexport SSH_PRIVATE_KEY=~/.ssh/id_rsa\nremote-ssh-mcp\n```\n\n**Available options:**\n\n- `--host, -h` - SSH host (or `SSH_HOST` env var)\n- `--port, -p` - SSH port, default 22 (or `SSH_PORT` env var)\n- `--username, -u` - SSH username (or `SSH_USERNAME` env var)\n- `--password` - SSH password (or `SSH_PASSWORD` env var)\n- `--private-key` - Path to private key file (or `SSH_PRIVATE_KEY` env var)\n- `--passphrase` - Passphrase for encrypted private key (or `SSH_PASSPHRASE` env\n  var)\n- `--agent` - SSH agent socket path (or `SSH_AUTH_SOCK` env var)\n- `--default-keys` - Automatically try default SSH keys from `~/.ssh`, enabled\n  by default (or `SSH_DEFAULT_KEYS` env var). Use `--no-try-default-keys` to\n  disable.\n- `--timeout, -t` - SSH connection timeout in milliseconds (or `SSH_TIMEOUT` env\n  var)\n\nThe server exposes all remote tools (bash, read, write, edit, grep, glob, etc.)\nthrough the MCP protocol.\n\n**Authentication behavior:**\n\nThe MCP server mimics OpenSSH's authentication behavior:\n\n1. If you provide `--password` or `--private-key`, it uses that exclusively\n2. If you don't provide explicit credentials, it will:\n   - Try the SSH agent (from `--agent` or `SSH_AUTH_SOCK`)\n   - Automatically fall back to default SSH keys in `~/.ssh/` (id_ed25519,\n     id_ecdsa, id_rsa, id_dsa)\n\nThis means you can connect just like OpenSSH without specifying any auth\noptions:\n\n```bash\n# Just like: ssh user@example.com\nremote-ssh-mcp --host example.com --username user\n```\n\nTo disable the automatic default key fallback:\n\n```bash\nremote-ssh-mcp --host example.com --username user --no-default-keys\n```\n\n### Basic Usage\n\n```typescript\nimport { Remote } from '@agent-remote/ssh';\n\n// Connect to remote server\nconst remote = await Remote.connect({\n  host: 'example.com',\n  username: 'user',\n  privateKey: fs.readFileSync('/path/to/id_rsa'),\n});\n\n// Execute commands\nconst result = await remote.bash.handler({\n  command: 'ls -la',\n});\nconsole.log(result.content[0].text);\n\n// Read files\nconst fileContent = await remote.read.handler({\n  file_path: '/etc/hosts',\n});\n\n// Write files\nawait remote.write.handler({\n  file_path: '/tmp/test.txt',\n  content: 'Hello, world!',\n});\n\n// Clean up\nawait remote.disconnect();\n```\n\n### Using with Claude Agent SDK\n\n```typescript\nimport { Remote } from '@agent-remote/ssh';\n\nconst remote = await Remote.connect({\n  host: 'example.com',\n  username: 'user',\n  privateKey: fs.readFileSync('/path/to/id_rsa'),\n});\n\n// Create an MCP server with all tools\nconst server = remote.createSdkMcpServer();\n\n// Use with Agent SDK...\n```\n\n### Using Individual Tools\n\n```typescript\nconst remote = await Remote.connect(config);\n\n// Each tool is accessible as a property with a handler\nconst tools = [\n  remote.bash,\n  remote.bashOutput,\n  remote.killBash,\n  remote.grep,\n  remote.read,\n  remote.write,\n  remote.edit,\n  remote.glob,\n];\n\n// You can use individual tools in your own MCP server\nimport { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';\n\nconst customServer = createSdkMcpServer({\n  name: 'custom-remote-ssh',\n  version: '1.0.0',\n  tools: [\n    tool(\n      remote.bash.name,\n      remote.bash.description,\n      remote.bash.inputSchema,\n      remote.bash.handler,\n    ),\n    tool(\n      remote.read.name,\n      remote.read.description,\n      remote.read.inputSchema,\n      remote.read.handler,\n    ),\n    tool(\n      remote.write.name,\n      remote.write.description,\n      remote.write.inputSchema,\n      remote.write.handler,\n    ),\n  ],\n});\n```\n\n## API Reference\n\n### Remote Class\n\nThe main class for managing SSH connections and accessing tools.\n\n#### Static Methods\n\n##### `Remote.connect(config: ConnectConfig): Promise<Remote>`\n\nEstablishes a connection to a remote SSH server.\n\n**Parameters:**\n\n- `config` - SSH connection configuration (see SSH Configuration below)\n\n**Returns:** A connected `Remote` instance\n\n**Throws:** If the SSH connection fails\n\n**Note:** SFTP is attempted but not required. If SFTP is unavailable, the\nconnection will still succeed but file operations (read, write, edit) will\nreturn error messages when called. Bash, grep, and glob tools work without SFTP.\n\n**Example:**\n\n```typescript\nconst remote = await Remote.connect({\n  host: 'example.com',\n  username: 'user',\n  password: 'secret',\n});\n```\n\n#### Instance Properties\n\n#### Instance Methods\n\n##### `disconnect(): Promise<void>`\n\nCloses the SSH connection and SFTP session (if present).\n\n**Example:**\n\n```typescript\nawait remote.disconnect();\n```\n\n##### `createSdkMcpServer()`\n\nCreates an MCP server with all remote tools from this Remote instance.\n\n**Returns:** An MCP server instance from the Claude Agent SDK\n\n**Example:**\n\n```typescript\nconst server = remote.createSdkMcpServer();\n```\n\n#### Tool Properties\n\nEach tool is accessed via a getter property that returns a tool definition with\n`name`, `description`, `inputSchema`, and `handler` properties.\n\n##### `bash: BashToolDefinition`\n\nExecutes commands in a persistent shell session.\n\n**Input:**\n\n```typescript\n{\n  command: string;          // The command to execute\n  timeout?: number;         // Optional timeout in milliseconds (max 600000)\n  description?: string;     // Description of what the command does\n  run_in_background?: boolean; // Run in background\n}\n```\n\n**Output:**\n\n```typescript\n{\n  output: string;    // Combined stdout and stderr\n  exitCode?: number; // Exit code (optional)\n  signal?: string;   // Signal used to terminate the command\n  killed?: boolean;  // Whether the command was killed due to timeout\n  shellId?: string;  // Shell ID if background execution\n}\n```\n\n##### `bashOutput: BashOutputToolDefinition`\n\nRetrieves output from a running or completed background bash shell.\n\n**Input:**\n\n```typescript\n{\n  shell_id: string; // Shell ID to retrieve output from\n}\n```\n\n**Output:**\n\n```typescript\n{\n  output: string;                  // New output since last check\n  status: 'running' | 'completed'; // Shell status\n  exitCode?: number;               // Exit code (when completed)\n  signal?: string;                 // Signal (when completed)\n}\n```\n\n##### `killBash: KillBashToolDefinition`\n\nKills a running background shell by its ID.\n\n**Input:**\n\n```typescript\n{\n  shell_id: string;         // Shell ID to kill\n  signal?: string;          // Signal to send (e.g., 'SIGTERM', 'SIGKILL')\n}\n```\n\n**Output:**\n\n```typescript\n{\n  killed: boolean; // Whether the shell was killed\n}\n```\n\n##### `grep: GrepToolDefinition`\n\nSearches for patterns in files or directories.\n\n**Input:**\n\n```typescript\n{\n  pattern: string;          // Regular expression pattern\n  path: string;             // File or directory to search\n  glob?: string;            // Glob pattern to filter files\n  output_mode?: 'content' | 'files_with_matches' | 'count';\n  '-B'?: number;            // Lines of context before match\n  '-A'?: number;            // Lines of context after match\n  '-C'?: number;            // Lines of context before and after\n  '-n'?: boolean;           // Show line numbers\n  '-i'?: boolean;           // Case insensitive\n  head_limit?: number;      // Limit output lines\n}\n```\n\n**Output:**\n\n```typescript\n{\n  mode: 'content' | 'files_with_matches' | 'count';\n  content?: string;         // Matching lines (if mode is 'content')\n  filenames?: string[];     // Matching files (if mode is 'files_with_matches')\n  numFiles?: number;        // Number of files (if mode is 'files_with_matches')\n  numMatches?: number;      // Number of matches (if mode is 'count')\n}\n```\n\n##### `read: ReadToolDefinition`\n\nReads files from the remote filesystem. Automatically uses SFTP when available,\nor falls back to `cat` command.\n\n**Input:**\n\n```typescript\n{\n  file_path: string;        // Absolute path to file\n  offset?: number;          // Line number to start reading from\n  limit?: number;           // Number of lines to read\n}\n```\n\n**Output:**\n\n```typescript\n{\n  content: string; // File content\n  numLines: number; // Number of lines read\n  startLine: number; // Starting line number\n  totalLines: number; // Total lines in file\n}\n```\n\n**Implementation:**\n\n- **SFTP mode**: Uses `sftp.readFile()` for direct file access\n- **Shell fallback**: Uses `cat` command to read file contents\n\n##### `write: WriteToolDefinition`\n\nWrites content to files on the remote filesystem. Automatically uses SFTP when\navailable, or falls back to `printf` command with proper escaping.\n\n**Input:**\n\n```typescript\n{\n  file_path: string; // Absolute path to file\n  content: string; // Content to write\n}\n```\n\n**Output:**\n\n```typescript\n{\n  content: string; // Content that was written\n}\n```\n\n**Implementation:**\n\n- **SFTP mode**: Uses `sftp.writeFile()` for direct file access\n- **Shell fallback**: Uses `printf '%s' '...'` with single-quote escaping to\n  safely handle special characters, newlines, and unicode\n\n##### `edit: EditToolDefinition`\n\nEdits files by replacing text on the remote filesystem. Automatically uses SFTP\nwhen available, or falls back to shell commands.\n\n**Input:**\n\n```typescript\n{\n  file_path: string;        // Absolute path to file\n  old_string: string;       // Text to find\n  new_string: string;       // Text to replace with\n  replace_all?: boolean;    // Replace all occurrences\n}\n```\n\n**Output:**\n\n```typescript\n{\n  replacements: number; // Number of replacements made\n  diff: StructuredPatch; // Unified diff of changes\n}\n```\n\n**Implementation:**\n\n- **SFTP mode**: Uses `sftp.readFile()` and `sftp.writeFile()` for file access\n- **Shell fallback**: Uses `cat` to read, performs replacement locally, then\n  uses `printf` to write back\n\n##### `glob: GlobToolDefinition`\n\nSearches for files matching glob patterns.\n\n**Input:**\n\n```typescript\n{\n  base_path: string;        // Absolute base path to search from\n  pattern: string;          // Glob pattern (e.g., '**/*.ts')\n  include_hidden?: boolean; // Include hidden files\n}\n```\n\n**Output:**\n\n```typescript\n{\n  matches: string[];        // List of matching file paths\n  count: number;            // Number of matches\n}\n```\n\n## SSH Configuration\n\nThe `ConnectConfig` type from the [`ssh2`](https://github.com/mscdex/ssh2)\nlibrary supports various authentication methods:\n\n### Password Authentication\n\n```typescript\nconst remote = await Remote.connect({\n  host: 'example.com',\n  port: 22,\n  username: 'user',\n  password: 'secret',\n});\n```\n\n### Private Key Authentication\n\n```typescript\nimport fs from 'fs';\n\nconst remote = await Remote.connect({\n  host: 'example.com',\n  port: 22,\n  username: 'user',\n  privateKey: fs.readFileSync('/path/to/id_rsa'),\n  passphrase: 'optional-passphrase', // If key is encrypted\n});\n```\n\n### SSH Agent Authentication\n\n```typescript\nconst remote = await Remote.connect({\n  host: 'example.com',\n  port: 22,\n  username: 'user',\n  agent: process.env.SSH_AUTH_SOCK,\n});\n```\n\n### Default Key Authentication\n\nWhen using the MCP server (not the library directly), if you don't provide\nexplicit credentials, the server automatically tries default SSH keys from\n`~/.ssh/` as a fallback. This mimics OpenSSH behavior:\n\n```bash\n# MCP server will try SSH agent, then fall back to ~/.ssh/id_ed25519, etc.\nremote-ssh-mcp --host example.com --username user\n```\n\nThis automatic fallback can be disabled with `--no-default-keys`.\n\n### Advanced Options\n\n```typescript\nconst remote = await Remote.connect({\n  host: 'example.com',\n  port: 22,\n  username: 'user',\n  privateKey: fs.readFileSync('/path/to/id_rsa'),\n  keepaliveInterval: 10000,\n  readyTimeout: 20000,\n  algorithms: {\n    kex: ['ecdh-sha2-nistp256'],\n    cipher: ['aes128-ctr'],\n  },\n});\n```\n\nFor all available configuration options, see the\n[ssh2 documentation](https://github.com/mscdex/ssh2#client-methods).\n\n## Examples\n\n### Error Handling\n\n```typescript\ntry {\n  const remote = await Remote.connect(config);\n\n  try {\n    const result = await remote.bash.handler({\n      command: 'some-command',\n    });\n\n    if (result.isError) {\n      console.error('Command failed:', result.content[0].text);\n    } else {\n      console.log('Success:', result.content[0].text);\n    }\n  } finally {\n    await remote.disconnect();\n  }\n} catch (error) {\n  console.error('Connection failed:', error);\n}\n```\n\n### Background Command Execution\n\n```typescript\nconst remote = await Remote.connect(config);\n\n// Start a long-running command in the background\nconst startResult = await remote.bash.handler({\n  command: 'npm run build',\n  run_in_background: true,\n  description: 'Building project',\n});\n\nconst { shellId } = startResult.structuredContent;\n\n// Check output periodically\nconst checkOutput = async () => {\n  const output = await remote.bashOutput.handler({ shell_id: shellId });\n  console.log(output.content[0].text);\n\n  if (output.structuredContent.status === 'running') {\n    setTimeout(checkOutput, 1000);\n  }\n};\n\ncheckOutput();\n```\n\n### File Search and Edit\n\n```typescript\nconst remote = await Remote.connect(config);\n\n// Find all TypeScript files\nconst files = await remote.glob.handler({\n  base_path: '/home/user/project',\n  pattern: '**/*.ts',\n});\n\n// Search for a pattern\nconst matches = await remote.grep.handler({\n  pattern: 'TODO',\n  path: '/home/user/project',\n  glob: '*.ts',\n  output_mode: 'content',\n  '-n': true,\n});\n\n// Edit a file\nawait remote.edit.handler({\n  file_path: '/home/user/project/config.ts',\n  old_string: 'localhost',\n  new_string: 'example.com',\n  replace_all: true,\n});\n```\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md","homepage":"https://github.com/loderunner/agent-remote#readme","keywords":["ai","agents","remote","ssh","ssh2","mcp","model-context-protocol","tools","automation","anthropic","claude","agent-sdk"],"repository":{"type":"git","url":"git+https://github.com/loderunner/agent-remote.git","directory":"packages/ssh"},"author":{"name":"Charles Francoise"},"bugs":{"url":"https://github.com/loderunner/agent-remote/issues"}}