{ lib, python313Packages, fetchFromGitHub, fetchPypi, }: # Upstream pins `requires-python = ">=3.11,<3.14"`, so this must NOT be built # with the default `python3Packages` (which is 3.14.x on nixos-unstable and # would be rejected by the build backend's requires-python check). Pin the # 3.13 package set explicitly. # # `fastmcp[tasks]` is a HARD runtime dependency: `src/ssh_mcp/tools/exec_tools.py` # does `from fastmcp.server.tasks import TaskConfig` at import time, and the # decorator validates at import. nixpkgs' `fastmcp` installs only the # client+server extras (optional-dependencies are passthru-only in nix and # install nothing), so the `tasks` extra's backend -- pydocket -- has to be # listed explicitly or the server crashes at startup. let # Internal dependency: upstream pins `unidiff>=0.7.5,<0.8.0`, but nixpkgs only # carries 1.0.0. The 0.7.x API surface used by the ssh_edit tool # (`PatchSet.from_string` and hunk iteration) is unchanged in 1.0.0, but # buildPythonApplication's runtime-dep check enforces the specifier, so the # pinned version still has to be provided. 0.7.5 is sdist-only on PyPI # (no wheel), so this is a source build. unidiff = python313Packages.buildPythonPackage rec { pname = "unidiff"; version = "0.7.5"; pyproject = true; build-system = [ python313Packages.setuptools ]; src = fetchPypi { inherit pname version; hash = "sha256-Ll8BYgUiSJRrnwlwpA6eEkI2v4bIK3CCEUOm/B3qJXQ="; }; doCheck = false; pythonImportsCheck = [ "unidiff" ]; meta = with lib; { description = "Unified diff parsing and application"; homepage = "https://github.com/matiasb/python-unidiff"; license = licenses.mit; }; }; in python313Packages.buildPythonApplication rec { pname = "python-ssh-mcp"; version = "1.5.2"; pyproject = true; src = fetchFromGitHub { owner = "Nightreaver"; repo = "python-ssh-mcp"; rev = "73c00c9b0d26add59798bc7b39dff88ac547b15a"; # v1.5.2 hash = "sha256-OfwOiBan5sqGEs/zzHHC4vJdcALOLWaIr8uENCbQ4FU="; }; build-system = [ python313Packages.hatchling ]; dependencies = with python313Packages; [ fastmcp pydocket # the fastmcp[tasks] extra asyncssh pydantic pydantic-settings unidiff ]; # Upstream ships no importable test suite in the sdist workflow used here # (its 1649 tests need the dockerized sshd fixtures); skip and rely on the # import check + the smoke test below. doCheck = false; pythonImportsCheck = [ "ssh_mcp" ]; meta = with lib; { description = "SSH MCP server for multi-host systemd/docker/apt administration (read-only by default, tiered)"; homepage = "https://github.com/Nightreaver/python-ssh-mcp"; license = licenses.gpl3Only; mainProgram = "ssh-mcp"; platforms = platforms.all; maintainers = [ ]; }; }