General July 23, 2026

SSHand

Open MCP server giving AI agents SSH access to remote Linux/Unix machines. Includes shell commands, file read/write, and SFTP transfers. Works with Claude, ChatGPT, Cursor, VS Code, and any MCP-compatible client.

SSHand: My First Shipped Product

An open-source MCP server that gives AI agents SSH/SFTP access to remote Linux/Unix machines. This is my first real software product I've actually shipped, with tests, docs, and all the infrastructure that comes with publishing something people can install and use.


What I Built

An MCP (Model Context Protocol) server with 11 production tools for SSH/SFTP:

  • Host management (add, list, remove, test connections)
  • Remote command execution
  • File operations (read, write, upload, download)
  • System introspection
  • Works with Claude, ChatGPT, Cursor, VS Code, OpenAI SDK, and any MCP client. Published on PyPI (v0.1.0 live, v0.2.0 in development). 28 passing tests.


    Skills Demonstrated

    Backend and DevOps

  • Async Python with Paramiko SSH client wrapper
  • Interactive CLI using Rich and Questionary
  • Windows SSH agent detection and fallback handling
  • YAML configuration management with security defaults (credentials never in git)
  • Connection validation that fails fast before users hit issues
  • API and Protocol Design

  • FastMCP server implementation with proper tool definitions
  • Multiple transports (stdio for desktop clients, HTTP for web)
  • Three authentication methods (key files, passwords, SSH agent)
  • User Experience and Product

  • Three-step guided setup wizard (guided first-run, interactive host manager)
  • Connection testing before saving config (catch auth issues upfront)
  • Client config snippets for 8+ MCP clients (Claude Desktop, Cursor, VS Code, OpenAI SDK, Hermes, OpenClaw, HTTP)
  • Simple functional naming over grand alternatives
  • Testing and Quality

  • 28 passing tests covering commands, file ops, host management, auth
  • Real SSH connections in tests, not mocks (catches actual production issues)

  • Development Process

    Real Testing Beats Mocks

    Started with the question: what actually breaks in production? Decided early to use real SSH connections in tests instead of mocking. This was slower but it caught edge cases that mocks would never catch: timeouts, partial auth failures, agent communication issues.

    The payoff: I can ship with confidence. Tests are integration tests, not unit tests, and they verify the actual thing works.

    Security by Default

    Learned the hard way that storing credentials is risky. Host inventory lives outside the repo at ~/.sshand/hosts.yaml by default, so it can't accidentally get committed. For users who want repo-local hosts, .gitignore blocks them from git.

    Added SSH agent option so users can skip storing credentials entirely. This complexity is worth it when the alternative is credential leaks.

    User-First CLI

    First version had just a README with YAML examples. People didn't read it. They had broken configs and left.

    Built an interactive wizard instead. It walks through three steps: add a host, test the connection, print client config. Now users can't mess up the config because they're guided through it. This one feature cut support friction in half.

    Incremental Feature Set

    Started with 11 tools and resisted adding 20 more. Constraint helped. Each tool does one job well. ssh_run_command solves 80% of use cases, and the others exist when needed.

    Multi-Client Compatibility

    MCP is new and changing fast. Built both stdio and HTTP transports so the server works everywhere today, not just Claude Desktop. More testing work but also more resilience when the ecosystem shifts.


    Key Lessons

    1. Real Verification Over Documentation

    Before this, I would write "tested with paramiko" in a README and call it done. Real SSH tests are slower but caught edge cases mocks would miss. Every tool test actually connects to a test server.

    Changed the code: test suite is slower but failures are real failures.

    2. Users Skip Docs; Make the Happy Path Obvious

    First version had no setup wizard. Just a README with YAML. Users didn't read it, broke their configs, gave up.

    Changed the code: invested in a 400-line interactive TUI. It guides users through setup step-by-step. Reduced support friction immediately.

    3. Naming Matters; Modesty Wins

    Wanted names like SSHMaster or RemoteOps. Realized grand names over-promise and make the project feel heavier.

    Changed the code: kept the whole API simple. ssh_run_command not executeRemoteCommand, ssh_list_hosts not getInventory. Consistency made the tool feel coherent.

    4. Credential Handling Is Harder Than It Looks

    Thought supporting three auth methods was simple. Then came Windows SSH agent detection, passphrase handling for encrypted keys, agent socket routing on Linux/Mac, password prompts that don't hang the MCP stream.

    Changed the code: built a platform_utils module just for Windows helpers, added a PassphraseProvider class. Complexity is worth it when users can pick the auth method that fits their setup.

    5. One Feature Per Release; Ship Often

    Wanted v0.2.0 to have 20 new tools. Realized that was greedy. Instead focused on refining what exists.

    Changed the code: v0.2.0 is focused. One or two new tools, better error messages, docs improvements. Shipping twice a quarter beats promising the moon.


    Why This Matters

    This is the first product I've actually shipped. Not a one-off script, not a portfolio project someone might look at. Something people install, integrate into their workflows, and rely on. That changed how I think about code.

    Tests aren't optional extras, they're foundation. Documentation isn't decoration, it's the difference between users and non-users. Security defaults matter because users won't read the warnings. User experience is everything because a bad experience kills adoption faster than any bug.

    Also learned that shipping fast and iterating is better than waiting for perfection. v0.1.0 shipped with what I had. v0.2.0 will ship with what I learn from real usage.


    Links

    GitHub: https://github.com/muradmalik23/sshand

    PyPI: https://pypi.org/project/sshand/

    Status: Active, maintained