{"_id":"@aaarc/handfree-ssh-mcp","_rev":"20-af2e4c100a50c1aac687144038a173dd","name":"@aaarc/handfree-ssh-mcp","dist-tags":{"latest":"2.0.0"},"versions":{"1.0.0":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.0","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.0","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"4758fb95d7322dc14bc772580ab5260b01719dac","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.0.tgz","fileCount":32,"integrity":"sha512-dDwr12gbXLH7uF+edL9/h6WjILE9W1mvjE6nzlo9p03of+y57ET7n5HsASjGphd9YN87uZMcErZeLLQTldgslg==","signatures":[{"sig":"MEUCIAQ/dE0FPCr0wWvgwHSa95qo6tr7Lytb1E/h6457D4t4AiEAyVZGnLBDNo7hu0YF8m4Z4dAvMKje7G29E3IJLMKpq08=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":310972},"main":"build/index.js","type":"module","gitHead":"7a644f159bd00b2943a164d8b77cfde6fc01e37d","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.0_1783242106298_0.40490181176557183","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.1","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.1","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"1df8b5f32c4aa3b1e3df87199e79c883417d679a","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.1.tgz","fileCount":24,"integrity":"sha512-01yhlSLhfKgKxmCSEN2e9AUpEvDu0BHdv18/HTriNygp3X1ELR7c8tp02ts7nNJ/HAOgU16+iZeMq6vBsaOntQ==","signatures":[{"sig":"MEUCIQDoHZmJDSz4ScnUq/jAwQTPFwt9N/Bk6cYjbWVTmDjvVwIgBuJ+cMbsXZjlVkGHx0rb1LN5pLQ1uOVaxX/z8+tDz5I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":186663},"main":"build/index.js","type":"module","gitHead":"7a644f159bd00b2943a164d8b77cfde6fc01e37d","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.1_1783244356625_0.7346703735510995","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.2","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.2","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"e331cd818818f1570361da6af1459e01d8451216","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.2.tgz","fileCount":24,"integrity":"sha512-JX7wLNi0HqiaO+/+iUbjf88+ekg6ygIDpLHnQsUsGaftaSlOFv68INr3wWd8DVc+a7gyT4iRf75beTX9bvK1tA==","signatures":[{"sig":"MEYCIQDxZnjDTKi5IlI3hznphaZy8h5oogepGvm0LFVQN0hOOgIhALe9R2qyeIVJXJ+oo6Fl1ZiMeL9JxG5hp6M3zhC/ihVR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":202156},"main":"build/index.js","type":"module","gitHead":"4261ed2f994db096f6f13897feece722f51d7c54","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.2_1783246423237_0.7303674714872403","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.3","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.3","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"8dc7d771e88a4579169bf44ca3b58e7d0abc3f93","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.3.tgz","fileCount":24,"integrity":"sha512-z9KM6HRrh4+LtK4e4otfTPcA5jzqFVdJGt+rNiQhpXUKTk9RbVqRLvfY4P2kPuAp9iLsiQn67bRe3YAsrFQuDQ==","signatures":[{"sig":"MEUCIBuG5o4QRnAMFvHcqAqUASdq6gYEFtZtLtemcTf27CHBAiEA/HOtB0cdI0eEawG2HhFYsV2EM8348DUR9h9pGWUYxPE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":203630},"main":"build/index.js","type":"module","gitHead":"a3b0d63494c4cce1bbda8bfd34c691cad3762787","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.3_1783246712929_0.6127420080653465","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.4","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.4","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"788b20e4ae4f2cc97fe579fe7b63f60447ecbfb6","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.4.tgz","fileCount":24,"integrity":"sha512-LcSoStBPA/0SODw6oAYGBZVx0C8+dr7cHYRxTPd5WoHGSLEqYio714+YHNQcmwV0ReXE4W44h2hDXMfqWvekMg==","signatures":[{"sig":"MEYCIQCYMkm1rLpqphMDXvO5iHwbGvIA4Xs4Dw/4UIlxA4JTfQIhANNDrbA0LCErwPG8htykdTTnKZzix4OEiWSFeX9wZZEY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":203266},"main":"build/index.js","type":"module","gitHead":"c5546dd76f799b9a39dcff3cc05600ba73095bd7","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.4_1783247944335_0.17036902663508324","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.5","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.5","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"0fe011a6955d4bb8a73acdda923f390ad8cf4ee4","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.5.tgz","fileCount":24,"integrity":"sha512-PdohXbUfM6W30OEqEKSolrL4s5D0GbnR8eIinp9oxelBrfcL6a/KWsMz/hAPUkk2vxd8uz41LLOX1v7/WjiwjQ==","signatures":[{"sig":"MEYCIQDeAwjTA4LQs9Ie+Qh0BtTEBpUuF2/c1fAQUigAm3ubugIhALBezyYhi0g+9jrOFIcCMsHSjYtSFHVy+xDO8u2riIFd","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":205488},"main":"build/index.js","type":"module","gitHead":"3ecc91ffaba5cf7a66dab6d07c3104ad545f7159","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.5_1783256712276_0.6672521410983037","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.6","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.6","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"0f218d1b70d898c58226096d6a4eb4dab92c498f","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.6.tgz","fileCount":25,"integrity":"sha512-MhHMxHMv/S+39hIIfZoqrJz0wCmrXLUI9IgIo75psJj+mUKPgbzWHSXcLI6o79B4VlCDsbbM5u3gVll7fIcXjA==","signatures":[{"sig":"MEQCID7bZSBsbb5+wafK03o38o50odNXzp8O/2Sqlk4PtlFnAiB4udWhrj0Vf8qHAMeaEZ/oOZHXUjD/gnvLpWh4MNt29w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":234105},"main":"build/index.js","type":"module","gitHead":"3ecc91ffaba5cf7a66dab6d07c3104ad545f7159","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.6_1783276073661_0.7794055049055062","host":"s3://npm-registry-packages-npm-production"}},"1.0.7":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.7","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.7","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"d701ca894b2a83fb1c85ffdd631843b464463760","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.7.tgz","fileCount":25,"integrity":"sha512-wDHWZ7KOotyGkjH1k+igU9v8/AU99cpEayR48tCC2dGOO6AHMTOl94DeC8YhHXcVd9+2XZMLgMpIT0WpDw19Ag==","signatures":[{"sig":"MEQCIFWKHMrfxps1jZIpL51Zk2HGDuy3E0Wi9W41BNr7e5k5AiBaKYIqTIztT7dqn6F1s9mxnhXAcqA3LVSJDMn5YSaXuw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":253363},"main":"build/index.js","type":"module","gitHead":"3ecc91ffaba5cf7a66dab6d07c3104ad545f7159","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.7_1783279442670_0.7604731095127959","host":"s3://npm-registry-packages-npm-production"}},"1.0.9":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.9","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.9","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"1a16743085246878c444dd4d956100c95ca66603","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.9.tgz","fileCount":25,"integrity":"sha512-yrL1lznNSwCErn02kNElL+NKm1eyK6hlE9KjIa/M7k6HO4m43uG8hNgJ2XTBgVZnUYKfq25NcIeFFjlwE2h7AQ==","signatures":[{"sig":"MEUCICiNaec+4bsmogj6Z3Jqx2s0SoHfno/P9Z26+pqlRdOcAiEAtDwfg8gM+ghyS4SzcpUufMmsh1fbtvJVhlIC0MdV1n0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":260109},"main":"build/index.js","type":"module","gitHead":"3ecc91ffaba5cf7a66dab6d07c3104ad545f7159","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.9_1783284276184_0.1840779994125119","host":"s3://npm-registry-packages-npm-production"}},"1.0.10":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.10","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.10","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"6cb660dd3d9556bbc45eb538099a30e56cc93a94","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.10.tgz","fileCount":25,"integrity":"sha512-mAVS2SuP05a/A4LMSyXmF3mTs4Cg+z9N4oAJpVzDOvnB7xRn1S16m3exYF7d4T1jKFXURUZ+n+zQPSN0Ze4Neg==","signatures":[{"sig":"MEUCIQDiMQa3xaI9gbiT2nrlIbO5VjWRiFrZtCc+kV9PVExCaAIgIZ73Rramab7TJiey6jchpdKGhMSVTopjSvH988kxj40=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":263971},"main":"build/index.js","type":"module","gitHead":"3071b84247cbc6e1dddd97d53bbb5e82a7fd6d5e","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.10_1783291002113_0.6779246162854349","host":"s3://npm-registry-packages-npm-production"}},"1.0.11":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.11","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.11","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"ad36e67c35ba00c1c1a7102827c93fcd4065f96a","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.11.tgz","fileCount":25,"integrity":"sha512-Fv7bnCHroyGkaRosRu0/12d7vLsMExJ56JitO4gRio8CoI9rDOYYPyj4S2lDhoKPawe4DdBABGLW1vQoDxAjig==","signatures":[{"sig":"MEYCIQCE/m4km0f0GtIlPjGdPmOgEUcUc0JZM8JxRfyjFBA4IgIhAODFt/loBmHLTKVvUNy5XL+RiN7xn4+gp00Et09Umckt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":267951},"main":"build/index.js","type":"module","gitHead":"3071b84247cbc6e1dddd97d53bbb5e82a7fd6d5e","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.11_1783363650947_0.9838406519617517","host":"s3://npm-registry-packages-npm-production"}},"1.0.12":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.12","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.12","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"53b43bb0dded0e4a3cd251dbba4a8346f00299e0","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.12.tgz","fileCount":25,"integrity":"sha512-e2SqdHixbBkQAMP0TLOpzpmZXbaqb6+qHjzsqSrBukizBYbldyi+U6HP7gHKkGvK/N39kIuk9GojMqoT8KSgoA==","signatures":[{"sig":"MEUCIQDQbfaw/26m21e6ZTQ1OWMLfumRwvy8J0wWiUXJg6dw7AIgYsf7xx2tzMXqAPneFn0291f/1A3FZzWXv2vV2Nq5skc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":268249},"main":"build/index.js","type":"module","gitHead":"fccd8283491d8aa1b7ee3ec3c9260bfe3151dea1","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.12_1783393186659_0.05774789854671569","host":"s3://npm-registry-packages-npm-production"}},"1.0.13":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.13","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.13","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"c7639e03bafa528c225b47bdadbd34d18c453641","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.13.tgz","fileCount":25,"integrity":"sha512-r/hLQ680DOTYIw5+yOk8AZp3qbKG80k5pxA07u4VEyTxWME3AGhIoTdtUZhCNymn0PF0mAcSSyE8fuxTiLRRhw==","signatures":[{"sig":"MEUCIQC6KijWD8F1fRrCE3ssgE5MVOYv7v8Hv3vhcU1TIdPVMAIgUMPMEeCzo4e3+/CbENom4C4kf00yQN5in6rXLemtAQg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":270506},"main":"build/index.js","type":"module","gitHead":"1cf01c99e82245f27e7acaa0732113e316fe39e3","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.13_1783399190819_0.20239454731261008","host":"s3://npm-registry-packages-npm-production"}},"1.0.14":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.14","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.14","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"8b8975f05e3894250751c73b18f86fb8b501bf43","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.14.tgz","fileCount":25,"integrity":"sha512-eGuXOEDtXEqW8548eygEj+XN/5mxCmkBTbI6Qwti17OcD1lR3DM1k1pcESXrSrSZSbgq9qP4le7R9VnBt4lKXg==","signatures":[{"sig":"MEQCIB7BR+AP00MRvPFiTHnQ+OFV6lQ/qFk0R7oCn8b6keqIAiAkXGtDiQM+5prQBJ4FqYhZ0DggtPT3yx7nLdhybd0EZg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":270600},"main":"build/index.js","type":"module","gitHead":"f630950c5a043e4c6815fd758391e278ddce99b5","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"10.9.8","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.14_1783657807062_0.32263664875916365","host":"s3://npm-registry-packages-npm-production"}},"1.0.15":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.15","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.15","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"e9ad97222248c15a125ef2885afb0cf5321d28fd","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.15.tgz","fileCount":27,"integrity":"sha512-tj5tvnb0YvnMfb5JXvZQv1l4cD26AK/VMs7QeSEH3XAYoden+r5FJ0RfZEsHownfSv6U4sj6bUtUiYzpCohckw==","signatures":[{"sig":"MEUCIGzEV+tGBojbLGlAUsb0DgLV+zVgkWfA1FZx+ER7hOptAiEAwHuNGzByaJdqo62Pwt0CCjQKKQBCLDrTf/RnT8+dN+4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":289553},"main":"build/index.js","type":"module","gitHead":"f630950c5a043e4c6815fd758391e278ddce99b5","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.15_1783865747541_0.6109870819663152","host":"s3://npm-registry-packages-npm-production"}},"1.0.16":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.16","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.16","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"4f4df418813e5792690315a283e1f2cd7810b11f","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.16.tgz","fileCount":27,"integrity":"sha512-+4LeNxwJLjVvZ8nbXP/ORnx2HUuvc8dT1ddVvLOfFjNtWU8Vp5bBZd9xzRZMp97xYEOp4KnsuAgVXXPh693sUQ==","signatures":[{"sig":"MEYCIQDavE3iuSk0zUbwO8oRGnn47eTI3+6oyPstRisYVgZH5QIhAPiDM9FnuBE12MpJh7OwsXOjJaDXd6axjWfOfAJwWj8A","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":296062},"main":"build/index.js","type":"module","gitHead":"42c85fbb90af7ef0bfb551eda1f6af8328101b49","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.16_1783929667407_0.19639029367015515","host":"s3://npm-registry-packages-npm-production"}},"1.0.17":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.17","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.17","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"fc71ac876c39b166467079dcef2b7e5b89f17275","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.17.tgz","fileCount":27,"integrity":"sha512-p/4MsR1uCGRCGxXbY0rEr4WpsIdHI6Mqp8Bp9z+8RwSRjHRFHhNRZe6Lk2jvcDmqTPhs6ykowgGlWAvM+QWffw==","signatures":[{"sig":"MEUCIQDPi2kOUjea7HnmFS4QNKvJJ6U756IrH936RIU0Tm71gwIgMP1JGvwsxs+JjFsUEPzk6wejr89sCEcxnl8LWuveUYE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":301756},"main":"build/index.js","type":"module","gitHead":"7d35740404a08e3f6a21fc28b706aa714a3cfaff","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.19.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.17_1784554918257_0.8856156334491521","host":"s3://npm-registry-packages-npm-production"}},"1.0.18":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.18","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.18","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"e228bdecd33123f8ba0922c3772af151fb7a2431","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.18.tgz","fileCount":27,"integrity":"sha512-PPPoOMVv9epew21iUv1EsulRHVOCslFiVKq3YIGX1nCKNKURr6VsbgTMdwLf+QyEmG6HUY3BuweUcLNp+rbkIQ==","signatures":[{"sig":"MEUCIA1v561F2eIhsbDAH/aRKM6SdF4I4+uLbJPCFvkSIceAAiEAjuju7numpjV6u+CL85+7Wpn7mq3e6SNrQPft6Gmb/20=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":305859},"main":"build/index.js","type":"module","gitHead":"66ffc043e9abd4b8ecbb9793b56c789b3dc130fb","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js build/tests/status-collector.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","test:status":"npm run build && node --test build/tests/status-collector.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js","smoke:list-servers":"npm run build && node -- scripts/smoke-list-servers.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"10.9.8","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.18_1784803530474_0.5474719802550709","host":"s3://npm-registry-packages-npm-production"}},"1.0.19":{"name":"@aaarc/handfree-ssh-mcp","version":"1.0.19","keywords":["ssh","mcp","server","cli"],"author":{"name":"Junki"},"license":"ISC","_id":"@aaarc/handfree-ssh-mcp@1.0.19","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"bin":{"handfree-ssh-mcp":"build/index.js"},"dist":{"shasum":"e28f0f8d387bcfb2c107235e2709285ec9794603","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-1.0.19.tgz","fileCount":27,"integrity":"sha512-/Z+6tZXqzxE7IdUyuNJwoch9Jp+0KzqwJXBpONgzwEea6Kj6RZs/7a7Etk5HaQIumJmOlCGyoihh7yRjEcL0BA==","signatures":[{"sig":"MEUCIDztxJiuzvRo5HYUVM2SnaGL+zrpfFKVFJGhbgOGyPoMAiEAhn3o6ivPueoY0wXRhLDNVpuykXIcCmMOwtZ77txA2hE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":317320},"main":"build/index.js","type":"module","gitHead":"66ffc043e9abd4b8ecbb9793b56c789b3dc130fb","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js build/tests/status-collector.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","test:status":"npm run build && node --test build/tests/status-collector.test.js","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js","smoke:list-servers":"npm run build && node -- scripts/smoke-list-servers.js"},"_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"_nodeVersion":"24.18.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/handfree-ssh-mcp_1.0.19_1785435630015_0.8060134461370145","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"_id":"@aaarc/handfree-ssh-mcp@2.0.0","bin":{"handfree-ssh-mcp":"build/index.js"},"bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"dist":{"shasum":"2612a5a401398e15044e309432117225c0295a86","tarball":"https://registry.npmjs.org/@aaarc/handfree-ssh-mcp/-/handfree-ssh-mcp-2.0.0.tgz","fileCount":68,"integrity":"sha512-ACkXzUJ06v+BnEFeJyaSekAHjXKDmLnT5Fjgb9pqav+oG7Z/QfKt4aR5CXPhTCbDsxUupgVAqQoxE0EKObVDpw==","signatures":[{"sig":"MEYCIQCiQidkdOEh65kJhN8MvtAOfbM/QTfayoOR9Qe0Q+/xowIhAJA79XPKJ1L8Z0IXtPbAn/D+FZ0c/i+LaknPZ5xwT1pC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCworI5yDR6t7G5XUl6kZAmjMsuOBoh3T1WJF7eT6iVeQIgIte/CxqejESwZAUGFI2wGRBXQwuIvPDj9hEAQu1bmnc="}],"unpackedSize":629919},"main":"build/index.js","name":"@aaarc/handfree-ssh-mcp","type":"module","author":{"name":"Junki"},"gitHead":"13d6fedb621cef6cbca36c99cb1f1b2dab0c3b82","license":"ISC","scripts":{"test":"npm run build && node --test build/tests/config.test.js build/tests/command-validation.test.js build/tests/tool-error.test.js build/tests/tools.test.js build/tests/output-collector.test.js build/tests/output-log-writer.test.js build/tests/recent-fixes.test.js build/tests/sftp-stall-timeout.test.js build/tests/keepalive-channel-open.test.js build/tests/status-collector.test.js build/tests/transfer-real.test.js build/tests/batch-upload-real.test.js build/tests/walker-real.test.js build/tests/preflight.test.js build/tests/ssh-lab.test.js build/tests/contracts.test.js build/tests/characterization-legacy-tools.test.js build/tests/validate-contracts.test.js build/tests/private-key-error-redaction.test.js build/tests/run-pure-units.test.js build/tests/run-wrapper-script.test.js build/tests/run-service-real.test.js build/tests/run-config-loader-real.test.js build/tests/run-tools-wiring.test.js build/tests/run-push-collect-pure.test.js build/tests/run-push-collect-real.test.js build/tests/run-profile-registry.test.js build/tests/run-retry-real.test.js build/tests/benchmark-stats.test.js build/tests/direct-transfer.test.js build/tests/direct-transfer-linux-pending.test.js build/tests/multi-connection-download-real.test.js build/tests/run-acceptance-linux-pending.test.js","build":"node scripts/build.js","test:all":"npm run test && npm run test:integration","test:cmd":"npm run build && node --test build/tests/command-validation.test.js","preflight":"node scripts/preflight.mjs --require A","test:tools":"npm run build && node --test build/tests/tools.test.js","test:config":"npm run build && node --test build/tests/config.test.js","test:status":"npm run build && node --test build/tests/status-collector.test.js","test:package":"node scripts/test-package.mjs","prepublishOnly":"npm run build","test:integration":"npm run build && node build/tests/integration.test.js","smoke:list-servers":"npm run build && node -- scripts/smoke-list-servers.js","test:transfer:real":"npm run build && node --test build/tests/transfer-real.test.js","validate:contracts":"npm run build && node scripts/validate-contracts.mjs","test:acceptance:ssh":"node scripts/ssh-lab.mjs test ssh","bench:transfer:smoke":"node scripts/benchmark.mjs --profile smoke","bench:transfer:sweep":"node scripts/benchmark.mjs --profile sweep","bench:transfer:release":"node scripts/benchmark.mjs --profile release","test:coverage:transfer":"npm run build && node --experimental-test-coverage --test-coverage-include=build/services/ssh-connection-manager.js --test-coverage-include=build/tools/transfer.js --test-coverage-lines=45 --test-coverage-branches=54 --test-coverage-functions=60 --test build/tests/transfer-real.test.js"},"version":"2.0.0","_npmUser":{"name":"aaarc","email":"ironboxplus@gmail.com"},"homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","keywords":["ssh","mcp","server","cli"],"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"_npmVersion":"11.7.0","description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","directories":{},"maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"_nodeVersion":"24.18.0","dependencies":{"zod":"^3.24.2","ssh2":"^1.16.0","socks":"^2.8.6","js-yaml":"^4.1.0","@modelcontextprotocol/sdk":"1.17.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.2","@types/node":"^22.13.10","@types/ssh2":"^1.15.5","@types/js-yaml":"^4.0.9","zod-to-json-schema":"^3.24.1"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/handfree-ssh-mcp_2.0.0_1789565629122_0.08229957672341559"}}},"time":{"created":"2026-07-05T09:01:46.115Z","modified":"2026-09-16T13:33:49.384Z","1.0.0":"2026-07-05T09:01:46.441Z","1.0.1":"2026-07-05T09:39:16.769Z","1.0.2":"2026-07-05T10:13:43.385Z","1.0.3":"2026-07-05T10:18:33.098Z","1.0.4":"2026-07-05T10:39:04.466Z","1.0.5":"2026-07-05T13:05:12.426Z","1.0.6":"2026-07-05T18:27:53.793Z","1.0.7":"2026-07-05T19:24:02.820Z","1.0.9":"2026-07-05T20:44:36.323Z","1.0.10":"2026-07-05T22:36:42.270Z","1.0.11":"2026-07-06T18:47:31.096Z","1.0.12":"2026-07-07T02:59:46.824Z","1.0.13":"2026-07-07T04:39:50.962Z","1.0.14":"2026-07-10T04:30:07.244Z","1.0.15":"2026-07-12T14:15:47.679Z","1.0.16":"2026-07-13T08:01:07.570Z","1.0.17":"2026-07-20T13:41:58.394Z","1.0.18":"2026-07-23T10:45:30.628Z","1.0.19":"2026-07-30T18:20:30.176Z","2.0.0":"2026-09-16T13:33:49.213Z"},"bugs":{"url":"https://github.com/Ironboxplus/handfree-ssh-mcp/issues"},"author":{"name":"Junki"},"license":"ISC","homepage":"https://github.com/Ironboxplus/handfree-ssh-mcp#readme","keywords":["ssh","mcp","server","cli"],"repository":{"url":"git+https://github.com/Ironboxplus/handfree-ssh-mcp.git","type":"git"},"description":"Handfree SSH MCP Server - A hands-free SSH automation tool via MCP protocol","maintainers":[{"name":"aaarc","email":"ironboxplus@gmail.com"}],"readme":"# 🤖 handfree-ssh-mcp\r\n\r\n**Configure once. Let the LLM handle the rest.**\r\n\r\n> 🧪 99.9% AI-coded [Include this Readme]. No artisanal hand-crafted code here.\r\n\r\nA hands-free SSH automation tool via MCP. Fork of [ssh-mcp-server](https://github.com/classfang/ssh-mcp-server) designed for autonomous AI agent operations.\r\n\r\n## 🎯 Philosophy\r\n\r\nThe original ssh-mcp-server requires passing credentials and options via CLI arguments every time. That's tedious.\r\n\r\n**handfree-ssh-mcp** takes a different approach:\r\n\r\n1. **Reuse your existing `~/.ssh/config`** automatically, or configure servers once in YAML\r\n2. **Set command policies** per server through a YAML overlay\r\n3. **Let the LLM call whatever it needs** - hands-free\r\n\r\nLess manual interventions. Just autonomous SSH execution with safeguards.\r\n\r\n## ✨ What's New\r\n\r\n| Feature | Original | handfree-ssh-mcp |\r\n|---------|----------|------------------|\r\n| Configuration | CLI args | **OpenSSH `~/.ssh/config` + optional YAML overlay** |\r\n| Multi-server | Messy `--ssh` flags | **Clean YAML structure** |\r\n| Command policy | Single comma-separated whitelist | **Blacklist mode by default, optional whitelist mode** |\r\n| Streaming | Not supported | **Real-time output with `stream` param** |\r\n| Discoverability | None | **`show-whitelist` tool for LLM** |\r\n\r\n## 🚀 Quick Start\r\n\r\n### 1. Use your existing `~/.ssh/config`\r\n\r\nIf you already have:\r\n\r\n```sshconfig\r\nHost dev\r\n  HostName 192.168.1.100\r\n  User root\r\n  IdentityFile ~/.ssh/id_ed25519\r\n```\r\n\r\nyou can start the MCP without a YAML file:\r\n\r\n```json\r\n{\r\n  \"mcpServers\": {\r\n    \"ssh\": {\r\n      \"command\": \"npx\",\r\n      \"args\": [\"-y\", \"@aaarc/handfree-ssh-mcp\", \"--enable-servers\", \"dev\"]\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n`--enable-servers` is optional. If you omit it, every concrete `Host` entry loaded from `~/.ssh/config` (plus YAML entries, if any) is enabled.\r\n\r\n### 2. Optional: create `servers.yaml` for policies or overrides\r\n\r\n```yaml\r\nsshConfig: true  # default; loads ~/.ssh/config before applying this YAML\r\n\r\nservers:\r\n  dev:  # Server name - use this in --enable-servers\r\n    # host / port / username / privateKey can be omitted when dev exists in ~/.ssh/config.\r\n    # Values here override the OpenSSH config entry when present.\r\n    # commandMode defaults to blacklist: commands are allowed unless they\r\n    # match the built-in dangerous blacklist or a pattern below.\r\n    blacklist:\r\n      - \"^docker system prune.*$\"\r\n\r\n  prod:\r\n    host: XXXXX\r\n    port: 22\r\n    username: deploy\r\n    privateKey: ~/.ssh/id_rsa\r\n    commandMode: whitelist\r\n    whitelist:\r\n      - \"^ls.*$\"        # Read only\r\n      - \"^cat.*$\"\r\n      - \"^tail.*$\"\r\n    blacklist:\r\n      - \"^rm.*$\"        # Never allow delete\r\n      - \"^shutdown.*$\"\r\n      - \"^reboot.*$\"\r\n```\r\n\r\n### 3. Add to MCP Config\r\n\r\n```json\r\n{\r\n  \"mcpServers\": {\r\n    \"ssh\": {\r\n      \"command\": \"node\",\r\n      \"args\": [\r\n        \"/path/to/handfree-ssh-mcp/build/index.js\",\r\n        \"--config\", \"/path/to/servers.yaml\",\r\n        \"--enable-servers\", \"dev,prod\"\r\n      ]\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n### 4. Done. Let the LLM Work.\r\n\r\nThe AI can now execute commands on your servers. All within your defined security boundaries.\r\n\r\n---\r\n\r\n## 🛠️ Available Tools\r\n\r\n| Tool | Description |\r\n|------|-------------|\r\n| `execute-command` | Run SSH command (with optional `stream` for real-time output) |\r\n| `show-whitelist` | Show active command policy + SFTP policy + output-log path for a server |\r\n| `close-connection` | Close a cached SSH connection for a server |\r\n| `upload` | Upload local file(s) to a remote server (CRLF-fix for shell scripts, skip-if-identical, optional `reuseConnection`, `vvv`, `fast`; `localPath` accepts an array for batch upload — see [Upload behaviors](#upload-behaviors)) |\r\n| `download` | Download remote file to local disk (optional `reuseConnection`, `vvv`, `fast`) |\r\n| `transfer` | Unified upload / download / server-to-server relay. Supports recursive small-file concurrency (`fileConcurrency`), batch upload (array `localPath`), temporary tar packing (`archive`, `archiveCompression`), fast SFTP by default, a bounded relay read-ahead window, and relay `strategy: \"relay\" \\| \"direct\" \\| \"auto\"` for a source-to-destination direct copy that bypasses this host — see [Upload behaviors](#upload-behaviors). |\r\n| `list-servers` | List configured (enabled) servers. Lean by default; `verbose:true` adds cached system status, `refresh:true` re-collects it (implies verbose). |\r\n| `workspace-run` | Launch an entrypoint on a remote server under a configured `runProfiles.<name>` entry: `[push] → preflight → launching → remote-running → [collect]`. Durable: survives SSH disconnect and MCP adapter restart because all state lives on the remote filesystem, not locally — see [workspace-run (remote runner)](#workspace-run-remote-runner). |\r\n| `run-status` | Query a `workspace-run` run's current status by `runId` (reads the remote state directory over SFTP; no local cache). |\r\n| `run-logs` | Read a byte-offset window of a run's stdout/stderr, UTF-8-boundary-safe across sequential calls. |\r\n| `run-list` | List runs on a server, newest first. |\r\n| `run-cancel` | Cancel a run: TERM to its process group, then KILL after `graceMs`; re-verifies process identity before signalling. |\r\n| `run-retry` | Launch a fresh run from an earlier run's own stored (non-secret) config snapshot, not the live config; records `parentRunId`. |\r\n| `help` | Self-describing help text for the MCP client |\r\n\r\n### show-whitelist\r\n\r\n**Use this first!** Let the LLM inspect the active command policy:\r\n\r\n```json\r\n{\r\n  \"tool\": \"show-whitelist\",\r\n  \"params\": {\r\n    \"connectionName\": \"dev\"\r\n  }\r\n}\r\n```\r\n\r\nReturns command mode, built-in command guards, configured whitelist/blacklist patterns, and examples when whitelist mode is active.\r\n\r\n### execute-command\r\n\r\n```json\r\n{\r\n  \"tool\": \"execute-command\",\r\n  \"params\": {\r\n    \"cmdString\": \"docker ps\",\r\n    \"connectionName\": \"dev\",\r\n    \"timeout\": 300000,\r\n    \"stream\": true,\r\n    \"reuseConnection\": true,\r\n    \"vvv\": false\r\n  }\r\n}\r\n```\r\n\r\n| Param | Required | Default | Description |\r\n|-------|----------|---------|-------------|\r\n| `cmdString` | ✅ | - | Command to execute |\r\n| `connectionName` | ❌ | First in `--enable-servers` | Which server to run on |\r\n| `timeout` | ❌ | 300000ms (stream) / 30000ms (no stream) | Per-attempt phase timeout in ms. SSH setup, exec-channel opening, and remote command execution each use this cap. |\r\n| `stream` | ❌ | `true` | Real-time streaming output |\r\n| `reuseConnection` | ❌ | `true` | Reuse the cached SSH connection. Set `false` after a timeout or suspected stale cached connection to force a fresh TCP/SSH connection for this command. |\r\n| `vvv` | ❌ | `false` | Append bounded SSH/channel debug output. Use with `reuseConnection: false` when you need fresh ssh2 handshake logs. |\r\n\r\n**When to use `stream: false`:**\r\n- Simple, fast commands (ls, pwd, cat)\r\n- When you don't need real-time feedback\r\n\r\n### close-connection\r\n\r\n```json\r\n{\r\n  \"tool\": \"close-connection\",\r\n  \"params\": {\r\n    \"connectionName\": \"dev\"\r\n  }\r\n}\r\n```\r\n\r\nCloses the cached SSH client for a configured server. This is useful after a timeout or suspected stale reused connection when you want the next default `reuseConnection: true` command to reconnect cleanly. Closing a jump host also closes cached targets whose jump chain uses that host. `reuseConnection: false` commands do not need this because their one-shot SSH clients close after each command.\r\n\r\n### workspace-run (remote runner)\r\n\r\nLaunches an entrypoint on a remote server under a `runProfiles.<name>` entry declared in your YAML config (see [YAML Config Reference](#-yaml-config-reference)). The launched process is detached from the SSH session (its own process group, `setsid`) and writes its own state atomically to `~/.handfree-runs/<runId>/meta.json` / `stdout.log` / `stderr.log` / `pid` / `heartbeat` / `exit.json` on the remote filesystem. There is **no local job store or daemon** — an MCP adapter restart loses nothing because it held nothing, but this also means status/logs/cancel require the remote server to be reachable; there is no offline/cached view of run state.\r\n\r\nThe full pipeline is `[push] → preflight → launching → remote-running → [collect]`:\r\n\r\n```json\r\n{ \"tool\": \"workspace-run\", \"params\": {\r\n  \"profile\": \"qwen-dev\", \"entrypoint\": \"train.py\", \"args\": [\"--epochs\", \"3\"]\r\n} }\r\n{ \"tool\": \"run-status\", \"params\": { \"runId\": \"run_20260915T120000Z_ab12cd34\" } }\r\n{ \"tool\": \"run-logs\", \"params\": { \"runId\": \"run_20260915T120000Z_ab12cd34\", \"offset\": 0 } }\r\n{ \"tool\": \"run-list\", \"params\": { \"profile\": \"qwen-dev\" } }\r\n{ \"tool\": \"run-cancel\", \"params\": { \"runId\": \"run_20260915T120000Z_ab12cd34\" } }\r\n{ \"tool\": \"run-retry\", \"params\": { \"runId\": \"run_20260915T120000Z_ab12cd34\" } }\r\n```\r\n\r\n- **`push`** (default `true`, or the profile's `defaultPush`) uploads `runProfiles.<name>.push.paths` (files and/or directories) to `remoteRoot` before launch, over the same batch/recursive upload used by `upload`/`transfer` — skip-if-identical, so an unchanged file is not re-transferred. Requires `push.paths` to be configured on the profile, or the call fails with `INVALID_CONFIGURATION` before touching the network. Pass `push: false` to skip it and use code already present under `remoteRoot`. A push failure never reaches the launching phase; the error names the failing file/directory and the phase. After push (or when skipped), the entrypoint is stat'd and content-hashed on the remote and recorded, together with a digest of the pushed file list, as the run's `revision` — so you can tell afterwards which code actually ran.\r\n- **`collect`** (default the profile's `collect.paths`) pulls artifacts back by explicit glob (relative to `remoteRoot`) after the run finishes, into `runProfiles.<name>.collect.localDir` — it **never** defaults to pulling the whole `remoteRoot`. Requires `collect.localDir` to be configured whenever `collect.paths`/the `collect` param is non-empty. Requesting collect (whether via the profile default or explicitly) makes the `workspace-run` call **block** (bounded by `timeout`, default the profile's `timeout` or 10 minutes) until the run reaches a terminal state, then collects — a plain launch with no collect requested still returns immediately once the process is confirmed started, unchanged. Enforces a total byte cap and file-count cap (`collect.maxBytes`/`collect.maxFiles`); exceeding either fails the collect phase and reports exactly what was already pulled — no silent truncation. Collect still runs by default even when the run failed or was cancelled (logs/partial artifacts are diagnostic material); a collect failure never rewrites the run's own exit code, it is reported separately (`details.collect`). Pass `collect: []` to disable collect for a call. Glob matching rejects `..`, absolute paths, and never traverses a symlink.\r\n- `sync: \"flush\"` — the persistent sync barrier (Phase 3, not this round). Only `\"none\"`/omitted works; `\"flush\"` returns `SYNC_NOT_AVAILABLE`.\r\n- `environment.type: conda | module | slurm`, `gpu.required`, and `secretEnv` — parse successfully in config but are rejected at launch time with `ENVIRONMENT_ADAPTER_NOT_AVAILABLE` / `GPU_VALIDATION_NOT_AVAILABLE` / `SECRET_ENV_NOT_AVAILABLE`. Only `environment.type: venv` (invokes `<path>/bin/python` directly, never `source activate`) and `environment.type: executable` are supported.\r\n\r\n`run-cancel` sends `TERM` to the run's whole remote process group, waits `graceMs` (default 5000), then `KILL` if it's still alive. Before signalling anything it re-verifies the recorded process identity (remote boot id, pid, pgid, `/proc` start ticks, and a wrapper token read back from the live process's own environment) against what is actually running right now; a mismatch (pid reuse, host reboot) moves the run to `orphaned` instead of risking a signal to an unrelated process.\r\n\r\n`run-retry` launches a fresh run from an **earlier run's own stored config snapshot** (its `meta.json`, read over SFTP) rather than the live `runProfiles` config — so a config hot-reload or a since-deleted profile cannot change what actually gets retried. It gets its own new `runId` and records `parentRunId`; the original run is untouched. Returns `RETRY_SNAPSHOT_UNAVAILABLE` for a run launched before this snapshot feature existed, and `SECRET_REQUIRED` if the snapshot declares `secretEnv` (not re-resolvable this delivery round).\r\n\r\n## 📄 YAML Config Reference\r\n\r\n```yaml\r\n# OpenSSH config loading is enabled by default.\r\n# true = load ~/.ssh/config, false = use YAML only.\r\n# You can also provide explicit paths:\r\nsshConfig:\r\n  enabled: true\r\n  paths:\r\n    - ~/.ssh/config\r\n    - ~/.ssh/config.d/work\r\n\r\n# Eagerly connect to all enabled servers on startup.\r\n# false (default) = lazy connect on first tool call.\r\npreConnect: false\r\n\r\n# Optional: root dir for execute-command full-output logs.\r\n# Per-call logs land under <outputLogDir>/<server>/<user>/<ts>-<pid>-<rand>.log.\r\n# Defaults to <cwd>/.handfree-output when unset. Supports ~ and relative paths.\r\noutputLogDir: ~/handfree-logs\r\n\r\nservers:\r\n  # NOTE: there is NO per-server `enabled` field. Which servers are active is\r\n  # controlled ONLY by the `--enable-servers` launch flag; omit it to enable\r\n  # every loaded server. Adding `enabled: true` under a server does nothing —\r\n  # it is silently ignored. (The `enabled:` above lives under `sshConfig:` and\r\n  # only toggles loading ~/.ssh/config; it is not a per-server switch.)\r\n  server_name:\r\n    # Required only for YAML-only servers. If a same-named Host exists in\r\n    # ~/.ssh/config, these fields are optional overrides.\r\n    host: 192.168.1.1\r\n    port: 22\r\n    username: root\r\n    \r\n    # Auth: use ONE of these. Omit agent to use SSH_AUTH_SOCK when present;\r\n    # set agent only when you need a specific socket path.\r\n    password: xxx\r\n    privateKey: ~/.ssh/id_rsa\r\n    agent: /path/to/ssh-agent.sock\r\n    passphrase: key_password    # If privateKey is encrypted\r\n    \r\n    # Network — at most ONE of `socksProxy` or `jumpHost`. See \"Jump host\" below.\r\n    # socksProxy: socks5://host:port\r\n    # jumpHost: bastion\r\n\r\n    \r\n    # Command policy (regex patterns)\r\n    # Default is blacklist. Set commandMode: whitelist to require a whitelist.\r\n    commandMode: blacklist\r\n    whitelist:                  # Active only in whitelist mode\r\n      - \"^ls.*$\"\r\n      - \"^cat.*$\"\r\n    blacklist:                  # Block matching commands\r\n      - \"^docker system prune.*$\"\r\n    \r\n    # Safe directory for destructive commands (rm, etc.)\r\n    safeDirectory: /home/user   # rm allowed only within this path\r\n\r\n    # SFTP path policy — applies ONLY to upload / download / transfer.\r\n    # execute-command is NOT affected by these lists.\r\n    # Default is OPEN: with `allowedRemoteDirectories` unset/empty, any absolute\r\n    # POSIX remote path is allowed. Configure it to opt into an allowlist instead.\r\n    allowedRemoteDirectories:\r\n      - /home/user\r\n      - /tmp\r\n    # Extra local dirs allowed as SFTP source/target.\r\n    # The MCP working directory is always permitted implicitly.\r\n    allowedLocalDirectories:\r\n      - /path/to/extra/local/dir\r\n    # Bypasses both the remote allowlist and the local directory check entirely.\r\n    # disableSftpPathPolicy: true\r\n\r\n# Optional: workspace-run profiles (PLAN.MD Phase 2). See \"workspace-run\r\n# (remote runner)\" above for exact push/collect semantics.\r\nrunProfiles:\r\n  qwen-dev:\r\n    server: server_name          # must match a servers: entry above\r\n    remoteRoot: /data/arc/qwen   # working directory the entrypoint runs from\r\n    environment:\r\n      type: venv                 # venv | executable (conda/module/slurm parse but are rejected at launch)\r\n      path: /data/arc/venvs/qwen # invokes /data/arc/venvs/qwen/bin/python directly\r\n    allowedEntrypoints:\r\n      - train.py\r\n      - benchmarks/*.py\r\n    env:\r\n      PYTHONUNBUFFERED: \"1\"      # caller-supplied env overrides may only use keys declared here\r\n    # defaultPush: true          # overrides workspace-run's own push default for this profile\r\n    push:\r\n      paths:                     # local sources (files and/or directories) uploaded to remoteRoot\r\n        - E:/projects/qwen       # a directory's CONTENTS land directly under remoteRoot (flattened,\r\n                                  # not nested under \"qwen/\") -- remoteRoot IS the pushed project root\r\n    collect:\r\n      paths:                     # globs relative to remoteRoot; never defaults to the whole tree\r\n        - outputs/*.json\r\n      localDir: E:/collected/qwen # required whenever paths above is non-empty\r\n      maxBytes: 104857600         # optional, default 209715200 (200 MiB)\r\n      maxFiles: 200                # optional, default 1000\r\n```\r\n\r\n### Security note: command policy\r\n\r\n`execute-command` defaults to `commandMode: blacklist`. In that mode commands are allowed unless they match built-in destructive guards, the built-in dangerous-command blacklist, or a server's configured `blacklist:` patterns. Built-in blocked operations include system power commands (`reboot`, `shutdown`, `halt`, `poweroff`, `init 0/6`, `Restart-Computer`/`Stop-Computer`), recursive force delete (`rm -rf`, recursive-force `Remove-Item`, recursive Windows `del`/`rd`), dangerous `rmdir` targets, recursive world-writable `chmod -R 777`, and recursive `chown -R` on `/`. (Output redirection to absolute/home paths is NOT blocked — it's normal logging/`nohup` usage.) Set `commandMode: whitelist` to require every command to match `whitelist:` after blacklist checks. For compatibility, a YAML server that contains `whitelist:` without `commandMode:` is treated as whitelist mode. Set `disableBuiltinGuards: true` and/or `disableBuiltinBlacklist: true` to turn off the built-in guards/blacklist for a server (your own `blacklist:`/`whitelist:` still apply).\r\n\r\n### SFTP path policy\r\n\r\n| Field | Scope | Default behavior |\r\n|---|---|---|\r\n| `allowedRemoteDirectories` | `upload` / `download` / `transfer` only | **Unset/empty = open.** Any absolute POSIX path is allowed. Configuring this list opts into restricting SFTP to those directories. |\r\n| `allowedLocalDirectories` | `upload` / `download` only | Unset = only the MCP working directory is allowed. |\r\n| `disableSftpPathPolicy` | all SFTP tools | When `true`, bypasses both checks above entirely — any remote path, any local path. |\r\n\r\nPath matching (when an allowlist is configured) is exact-equal or `dir + separator` prefix. `..` segments and null bytes are always rejected regardless of policy. Use `show-whitelist` to inspect a server's current SFTP policy.\r\n\r\n### Jump host (ProxyJump-style)\r\n\r\nTunnel a target's SSH connection through another server defined in the same YAML. Useful when the target isn't directly reachable from your machine but a bastion is.\r\n\r\n```yaml\r\nservers:\r\n  bastion:\r\n    host: 1.2.3.4\r\n    username: gate\r\n    privateKey: ~/.ssh/id_rsa\r\n\r\n  target:                       # NOT directly reachable\r\n    host: 10.0.0.5\r\n    username: app\r\n    password: <target-password>\r\n    jumpHost: bastion           # <- tunnel through `bastion`\r\n    whitelist:                  # target's own policy\r\n      - \"^ls( .*)?$\"\r\n      - \"^pwd$\"\r\n```\r\n\r\nRules (enforced at config load — bad configs fail fast):\r\n\r\n- **Chaining to any depth.** The referenced jump host may itself set `jumpHost`, forming a chain `target -> J1 -> J2 -> ...`. The chain is built innermost-first (the deepest, directly-reachable hop connects first). Only **cycles** are rejected.\r\n- **Mutually exclusive with `socksProxy`** on the same target.\r\n- **Self-reference is rejected.** `target.jumpHost: target` is invalid.\r\n- **Independent policy.** The target authenticates with its OWN `username` / `password` / `privateKey`, and its own `whitelist` / `blacklist` / `safeDirectory` / `allowed*Directories` apply. The jump host is purely transport.\r\n- **Jump host is still a normal server.** You can run tools against `bastion` directly; its connection is separate from the tunneling one.\r\n\r\n**Hot-reload note:** `jumpHost` is a connection-level field, so it hot-reloads. Editing a server's `jumpHost` (or any hop's connection fields) in `servers.yaml` resets the affected connection on the fly — the change takes effect on the next tool call, no restart needed. This is chain-aware: if an intermediate hop's host / port / credentials change, every target that tunnels through it is reset too.\r\n\r\n### Connection lifecycle (connect / reconnect)\r\n\r\n- **Lazy by default.** A server's SSH client is created on its first tool call via `ensureConnected()`. Set `preConnect: true` (or pass `--pre-connect`) to open all enabled servers at startup in parallel; failures are logged but don't block startup.\r\n- **Cached clients stay open while reused.** With the default `reuseConnection: true`, `execute-command` does not close the SSH client after each command. If a cached client must be opened first, SSH setup, jump, SOCKS, and channel-open waits are bounded by the call's `timeout`. The cached client is closed on explicit disconnect/server shutdown, config hot-reload for changed connection fields, underlying SSH close/error cleanup, or reconnect after a connection-shaped command failure.\r\n- **Manual cached-client close.** Use `close-connection { connectionName: \"name\" }` to drop a cached SSH client on demand. If the named server is a jump host, cached targets that jump through it are closed too.\r\n- **Auto-reconnect on `execute-command`.** Every command runs inside a retry loop (default 3 attempts: 1 initial + 2 retries) with exponential backoff. If the underlying error matches a connection-shaped pattern (`econnreset`, `epipe`, `socket`, `closed`, `channel`, `end of stream`, or a `SSH_CONNECTION_FAILED` ToolError), the manager closes the dead client, reconnects, and retries the command. Non-connection errors (permission denied, validation, command-not-found) are returned immediately without retry.\r\n- **Optional fresh command/SFTP connections.** `execute-command`, `upload`, `download`, and `transfer` reuse cached SSH clients by default (`reuseConnection: true`). If a call times out or you suspect the cached `ssh2.Client` is stale while native `ssh` works, retry with `reuseConnection: false`; that one operation opens a fresh SSH connection and closes it afterwards. SFTP still opens a new SFTP channel/session per operation.\r\n- **Optional SSH debug output.** Set `vvv: true` on `execute-command` to append bounded SSH/channel debug output to the result or error. For full ssh2 handshake logs, combine it with `reuseConnection: false`; an already-open cached client cannot retroactively emit handshake debug.\r\n- **Optional SFTP debug output.** Set `vvv: true` on `upload`, `download`, or `transfer` to append bounded SSH/SFTP debug where the result is textual. Recursive `transfer` success keeps its structured `{ summary, files }` JSON response, so recursive debug is surfaced on errors rather than appended to successful summaries.\r\n- **SFTP transfers do NOT auto-retry.** `upload` / `download` / `transfer` lazy-connect via the same acquisition path and close stale cached clients on connection-shaped SFTP/channel errors, but a mid-transfer disconnect surfaces as a single failure — re-issue the call manually, preferably with `reuseConnection: false` if you suspect the cached SSH client.\r\n- **No background keepalive or health probe.** Dead connections are only discovered on the next tool call. If you idle for hours through a NATed network, expect the first call after the gap to fail-then-reconnect on its own (you'll see one retry in the logs).\r\n\r\n### Upload behaviors\r\n\r\n- **CRLF auto-fix for shell scripts.** When uploading a `.sh`, `.bash`, or `.zsh` file, any `\\r\\n` line endings are automatically converted to `\\n` before the bytes are sent. The response notes when this happens and how many line endings were rewritten. The local file on disk is left untouched.\r\n- **Skip-if-identical (default on).** Before transferring, `upload` checks whether the remote file already matches the local payload. Files ≤ 256 MiB are compared byte-for-byte; larger files are compared via MD5 (using `md5sum` on the remote host). **Shell scripts (`.sh` / `.bash` / `.zsh`) are compared in a line-ending-agnostic way — both sides are LF-normalized before the comparison, so a CRLF-only diff is treated as identical and the upload is still skipped.** If they match, the upload is skipped and the response says so. Pass `skipIfIdentical: false` to force a re-upload. Recursive `transfer` (`mode: upload`, `recursive: true`) applies the same check per file.\r\n- **Fast single-file SFTP (default on).** `upload`, `download`, and `transfer` upload/download mode use ssh2 `fastPut` / `fastGet` unless `fast: false` is set. Optional `sftpConcurrency` and `chunkSize` tune ssh2's parallel chunks; omitted values use ssh2 defaults. Fast uploads do not preload the local file: when skip-if-identical is enabled, they first compare remote size and only stream SHA-256 digests when sizes match. If a shell-script upload needs CRLF-to-LF conversion, it falls back to the normal safe upload path.\r\n- **Recursive small-file concurrency.** Non-archive recursive upload/download transfers independent files through a bounded worker pool. `fileConcurrency` defaults to 4 and is capped at 8, because each concurrent file opens its own SFTP channel on the same SSH connection and the cap must stay under the remote's `MaxSessions` (OpenSSH's common default is 10) to avoid channel-open failures. The directory tree is created first, so parallel file writes never race their parent directories.\r\n- **Tar-before-transfer archive mode.** Set `archive: true` on `transfer` to package one source file or directory into a temporary tar, transfer that single archive, extract it into the destination directory, and clean temporary archives on the MCP host and remote endpoints. The source basename is preserved. `archiveCompression` accepts `none` (default), `gzip`, `bzip2`, `xz`, or `zstd`; every endpoint that packs or extracts must provide compatible `tar`/compressor support. Archive mode works for upload, download, and relay and does not require `recursive: true`.\r\n- **Windowed relay SFTP (default on).** `transfer mode=relay` reads and writes explicit file ranges rather than serially piping one stream. It keeps a bounded source read-ahead window in MCP-host memory while destination writes are pending, so one source/destination RTT pair does not serialize the whole transfer. The default is `sftpConcurrency: 64` and `chunkSize: 32768` (at most 2 MiB of application chunk buffers); tune either parameter for the link, up to a 64 MiB window. No relay payload is written to local disk. Destination ranges may finish out of order, then the existing size and best-effort MD5 checks verify the final file.\r\n- **Relay skip-if-identical (default on).** `transfer mode=relay` does the same check between two remote servers: matching size on both sides plus matching `md5sum` skips the transfer. If `md5sum` is missing on either server, the check falls back to a normal transfer.\r\n- **Relay `strategy` (direct transfer).** `transfer mode=relay` accepts `strategy: \"relay\" | \"direct\" | \"auto\"`. `\"relay\"` (default) is the windowed-SFTP behavior above, unchanged. `\"direct\"` runs the copy **on the source server** (`rsync`, else `tar` piped over `ssh`; never `rclone`) so this MCP host never reads or writes the file's bytes — it fails explicitly, with the precise reason, if the source cannot reach the destination directly, the destination host key is not already pinned on the source, non-interactive `existing-remote-key` auth is unavailable, or neither backend is installed on the source. Auth relies entirely on trust already present on the source server (its own default SSH identity/`known_hosts`); this tool never reads, constructs, or transfers a private key, and never passes `StrictHostKeyChecking=no` or an equivalent auto-accept flag. Direct transfer cannot traverse NAT — point it at a reachable pair, use a jump host/overlay network, or fall back to `strategy: \"relay\"`. `\"auto\"` probes the same conditions (route, host key, backend availability, auth, and the destination's path policy) with a real timeout and uses direct when possible; otherwise it falls back to relay and reports the accurate reason it did so.\r\n- **Batch upload (array `localPath`).** Both `upload` and `transfer mode=upload` accept `localPath` as either a single string (unchanged 1.x behavior, plain-text response) or an array of local file paths for a batch upload. In batch mode, `remotePath` must be a directory and every source lands at `remotePath/<basename>`. Basename collisions across sources, or the exact same path repeated in the array, fail the whole call with `BATCH_TARGET_COLLISION` (listing the conflicts) **before any remote write happens** — collisions are never silently deduplicated. An empty array returns `INVALID_CONFIGURATION`; more than 1000 entries returns `BATCH_TOO_LARGE`. Concurrency reuses the same `fileConcurrency` (default 4, capped at 8) as recursive transfers. `onError` controls failure handling: `\"abort\"` (default) stops scheduling new files once one fails and drains in-flight ones; `\"continue\"` attempts every file regardless of earlier failures. Both modes return a per-file `uploaded` / `skipped` / `failed` status with failure reasons. Every file still goes through the normal single-file rules (skip-if-identical, CRLF→LF fix, `fast`, path policy) — nothing is reimplemented for batch mode. The response is structured JSON (`{ results, total, uploadedCount, skippedCount, failedCount, crlfFixedCount }`) instead of the plain-text single-file result. `archive: true` is not supported together with a batch `localPath` in this release; `download`/`relay` do not accept an array `localPath`.\r\n\r\n### `execute-command` output capping & full logs\r\n\r\n`execute-command` always persists the FULL stdout and stderr of every invocation to a local plain-text log file, then returns only a tail-truncated view to the caller. This keeps the LLM-visible payload small without losing any data.\r\n\r\n- **Default cap:** 65536 bytes (64 KiB) per stream, tail-only. Set `maxOutputBytes` on the tool call to raise/lower the cap.\r\n- **Streaming is unaffected.** When `stream: true`, every chunk still reaches the progress channel live; only the final aggregated return value is capped.\r\n- **Log path:** `<outputLogDir>/<server-name>/<username>/<timestamp>-<pid>-<rand>.log`. Default `outputLogDir` is `<cwd>/.handfree-output`. Override with a top-level `outputLogDir:` entry in `servers.yaml` (supports `~` and relative paths).\r\n- **Log format:** plain UTF-8 text with `=== META ===` / `=== STDOUT ===` / `=== STDERR ===` / `=== END ===` separators. Tail with `tail -f` while a command is running? Not yet — the file is finalized on close.\r\n- **Truncation marker:** when output is trimmed, the returned text starts with an `[OUTPUT TRUNCATED]` header that lists total bytes, bytes dropped, and the on-disk log path. When output fits within the cap, no header is added and no log path is reported (the file is still written).\r\n- **Retention:** none. Manage cleanup yourself (e.g. `find .handfree-output -mtime +7 -delete`).\r\n- **Failures are non-fatal.** If the log file cannot be written, the command still completes and the failure is logged as an MCP-side warning.\r\n\r\n```yaml\r\n# servers.yaml\r\noutputLogDir: ~/handfree-logs  # optional; defaults to <cwd>/.handfree-output\r\nservers:\r\n  dev: { ... }\r\n```\r\n\r\n## ⚙️ CLI Options\r\n\r\n```text\r\n--config          Optional path to YAML config/policy overlay\r\n--ssh-config      Optional OpenSSH config path(s), comma-separated or repeated\r\n--no-ssh-config   Disable automatic ~/.ssh/config loading\r\n--enable-servers  Optional comma-separated list of servers to enable\r\n--pre-connect     Eagerly connect to all enabled servers on startup\r\n                  (overrides `preConnect` in YAML). Default: lazy connect.\r\n```\r\n\r\n> **Note**: `--enable-servers` controls which servers are available. The first server listed becomes the default when `connectionName` is not specified.\r\n> If `--enable-servers` is omitted, all loaded servers are enabled; when more than one server is enabled, tools require `connectionName`.\r\n\r\n### OpenSSH config support\r\n\r\nThe loader understands concrete `Host` entries and applies normal OpenSSH-style first-value matching against wildcard defaults. It supports `HostName`, `User`, `Port`, `IdentityFile`, `IdentityAgent`, `Include`, and common tokens such as `%h`, `%n`, `%p`, `%r`, `%u`, `%d`, and `%%`. Wildcard-only `Host *` blocks are used as defaults but are not exposed as runnable server names. `Match` blocks are ignored.\r\n\r\nConnection settings are hot-reloaded when the loaded YAML/OpenSSH config files change. If host, user, port, identity, agent, passphrase, or proxy settings change, the existing SSH client for that server is closed and the next tool call reconnects with the new values. Whitelist, blacklist, SFTP policy, and output log settings also update live.\r\n\r\nExample with selective servers:\r\n\r\n```json\r\n{\r\n  \"args\": [\r\n    \"--config\", \"servers.yaml\",\r\n    \"--enable-servers\", \"dev,staging\"\r\n  ]\r\n}\r\n```\r\n\r\n## 🛡️ Security\r\n\r\n- **Pick the right command policy**: use default blacklist mode for flexible automation, or `commandMode: whitelist` for locked-down hosts\r\n- **Keep secrets safe**: Add `servers.yaml` to `.gitignore`\r\n- **Per-server control**: Prod can be locked down, dev can be permissive\r\n\r\n## 📋 TODO & PLAN\r\n\r\n### High Priority\r\n- [x] **Complete test coverage**: Add tests for `list-servers`, `upload`, `download`, `show-whitelist`, streaming mode, timeout/kill\r\n- [ ] **Session support**: Add tools to list/create/resume/close persistent SSH sessions (for multi-command workflows)\r\n- [ ] **LLM-based whitelist**: Allow LLM to propose commands, with human approval adding to dynamic whitelist\r\n\r\n### Nice to Have\r\n- [x] **Command history / output archive**: full stdout/stderr of every `execute-command` is persisted under `<outputLogDir>/<server>/<user>/*.log`\r\n- [x] **Multi-command execution**: Execute multiple commands in sequence with `&&` or `;` safely (fixed: `2>/dev/null` now allowed)\r\n- [x] **Connection auto-recovery**: `execute-command` retries with exponential backoff and forced reconnect on connection-shaped errors\r\n- [x] **SFTP connection reuse controls**: `upload` / `download` / `transfer` support `reuseConnection`, `vvv`, and explicit fresh one-shot SSH clients\r\n- [x] **Fast, concurrent, and archive transfer**: default-on ssh2 `fastPut` / `fastGet`, bounded recursive `fileConcurrency`, tar-before-transfer compression, and bounded relay read-ahead chunks\r\n- [ ] **SFTP retry parity**: extend the retry-with-reconnect loop to `upload` / `download` / `transfer`\r\n- [x] **SSH keepalive**: cached and jump SSH clients use `keepaliveInterval` / `keepaliveCountMax` (defaults: 5000 ms / 2 unanswered probes) to detect half-open connections\r\n- [ ] **Server health check**: optional periodic ping to detect drops proactively\r\n\r\n## 📄 License\r\n\r\nISC License\r\n\r\n- Original work: © 2025 junki.cn ([ssh-mcp-server](https://github.com/classfang/ssh-mcp-server))\r\n- Modifications: © 2026 woqucc (handfree-ssh-mcp)\r\n","readmeFilename":"README.md"}