[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "servonaut" version = "2.26.3" description = "Manage AWS, Hetzner, OVH, and custom servers from one TUI — with a built-in AI assistant and MCP server" readme = "README.md" license = "MIT" requires-python = ">=3.10" classifiers = [ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux", "Operating System :: MacOS", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: System :: Systems Administration", ] dependencies = [ "boto3", "tabulate", "textual>=8.0.0", "cryptography>=42.0", "bcrypt>=3.2", "pynacl>=1.5", "httpx>=0.25.0", "httpx-sse>=0.4", "keyring>=24", ] [project.optional-dependencies] test = [ "pytest>=7.0", "pytest-cov>=4.0", "pytest-asyncio>=0.21", "pytest-timeout>=2.3", ] # Upper-bounded on purpose, and on the SDK maintainers' own advice to # library publishers. The 2.x SDK replaced the low-level Server's decorator # registration (@server.list_tools()) with constructor-based on_list_tools= / # on_call_tool= handlers, and removed automatic wrapping of handler return # values. Installing it against this codebase fails at start-up with # "'Server' object has no attribute 'list_tools'". # # Deferred rather than blocked: 2.x exists to carry the stateless # per-request-envelope protocol, which targets horizontal scaling behind a # load balancer. This is a local stdio server, and that protocol revision # cannot be reached through the initialize handshake anyway, so porting # would buy no capability we can currently use. 1.x keeps receiving # critical bug fixes and security patches. # # Lift the cap and port mcp/server.py when any of these becomes true: # 1. We add an HTTP / remote transport, making stateless scaling relevant. # 2. A client we support requires the 2026-07-28 protocol revision. # 3. 1.x reaches end of life, or a security issue lands unpatched there. # 4. We want a 2.x-only feature (Multi Round-Trip Requests, standard # error codes, deprecation annotations). # # The port itself is contained: handler registration plus wrapping the # dispatch return in CallToolResult / ListToolsResult. Imports, Tool # construction, stdio and run() are unaffected — see # tests/test_mcp_sdk_contract.py, which pins that surface. mcp = ["mcp>=1.0.0,<2"] ovh = ["ovh"] hetzner = ["hcloud>=2.0"] # Local speech-to-text for voice input. sounddevice needs the PortAudio # system library, so this stays optional rather than a hard requirement. # numpy is listed explicitly because the capture buffer and the model # handoff both use it directly: neither sounddevice nor faster-whisper # declares it (it arrives transitively via onnxruntime), so relying on # that would break the import the day the chain changes. voice = ["faster-whisper>=1.0", "sounddevice>=0.4", "numpy>=1.24"] # Streaming voice input (words appear while you speak). A separate extra # because it is a different engine with its own runtime, not an add-on to # the batch one — installing both is supported but rarely wanted. voice-streaming = ["sherpa-onnx>=1.13.3", "sounddevice>=0.4", "numpy>=1.24"] # Spoken replies (local text-to-speech). Same runtime as the streaming # engine — a separate extra so batch-engine users can add speech output # without pulling in the streaming recogniser's name. voice-output = ["sherpa-onnx>=1.13.3", "sounddevice>=0.4", "numpy>=1.24"] # Deprecated aliases kept for compatibility — deps are now required. ai = [] sync = [] keyring = [] all = [ # Upper bound mirrors the `mcp` extra above — see the note there. "mcp>=1.0.0,<2", "ovh", "hcloud>=2.0", "keyring>=24", "faster-whisper>=1.0", "sounddevice>=0.4", "numpy>=1.24", "sherpa-onnx>=1.13.3", ] [project.urls] Homepage = "https://github.com/zb-ss/servonaut" Repository = "https://github.com/zb-ss/servonaut" Issues = "https://github.com/zb-ss/servonaut/issues" [project.scripts] servonaut = "servonaut.main:main" [tool.hatch.build.targets.wheel] packages = ["src/servonaut"] [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["src"] # Cap any single test at 60 seconds. The slowest legitimate tests in the # suite (memory probers exercising 7 × 5 s SDK timeouts) finish in ~35 s, # so a 60 s ceiling catches infinite hangs without flaking valid tests. timeout = 60 timeout_method = "thread"