diff --git a/CHANGELOG.md b/CHANGELOG.md index cb4e429..e581580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.6.2] - 2026-07-26 + +### Fixed + +**The PyPI project page had no description at all.** `pyproject.toml` declared no `readme`, so the built wheel carried a zero-length long description and https://pypi.org/project/blc-ssh-mcp/ rendered an empty body — just the name and a one-line summary. `readme = "README.md"` fixes it. Package metadata cannot be edited on PyPI and published files are immutable, so correcting this requires a release; that is the only reason 0.6.2 exists. + +**Seven README links would have 404'd once rendered on PyPI.** PyPI resolves relative Markdown links against `pypi.org`, not the repository, so `[LICENSE](LICENSE)`, `[CHANGELOG.md](CHANGELOG.md)`, `[SECURITY.md](SECURITY.md)`, `[CONTRIBUTING.md](CONTRIBUTING.md)`, `[compose.yaml](compose.yaml)` and `config/servers.example.toml` are now absolute. Keep any new README link absolute for the same reason. + +### Changed + +- **Summary rewritten** from "SSH MCP server for managing infrastructure via Claude Code" to "MCP server giving AI assistants SSH access to run commands, transfer files, and manage server fleets" — the old one named a single MCP client and omitted what the server actually does. +- **`license` migrated to the PEP 639 SPDX form** (`license = "MPL-2.0"` plus `license-files`) from the deprecated `{text = "..."}` table, and the correspondingly deprecated `License :: OSI Approved ::` classifier was dropped. The published `License-File: LICENSE` metadata is unchanged. +- **More project links on PyPI**: Changelog, Documentation, Security Policy and Container Image, alongside the existing Homepage/Repository/Issues. +- Added the `System Administrators` audience classifier and the `sftp`, `devops`, `automation` keywords. + ## [0.6.1] - 2026-07-26 ### Changed — BREAKING (install command) diff --git a/README.md b/README.md index 99a1661..ccbc17b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ SSH MCP server that lets AI assistants execute commands on remote servers. -[![License: MPL-2.0](https://img.shields.io/badge/License-MPL--2.0-brightgreen.svg)](LICENSE) +[![License: MPL-2.0](https://img.shields.io/badge/License-MPL--2.0-brightgreen.svg)](https://github.com/blackaxgit/ssh-mcp/blob/main/LICENSE) [![Claude Code Ready](https://img.shields.io/badge/Claude_Code-Auto_Install_Ready-blueviolet?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48dGV4dCB4PSIwIiB5PSIxMyIgZm9udC1zaXplPSIxNCI+8J+UpTwvdGV4dD48L3N2Zz4=)](#add-to-claude-code) ## What is this @@ -65,7 +65,7 @@ services: - ~/.ssh:/home/sshmcp/.ssh:ro ``` -The image uses a non-root `sshmcp` user (uid 1000). Mount your SSH keys and config file read-only. [compose.yaml](compose.yaml) in the repo carries a fuller example — it builds from the local Dockerfile rather than pulling the published image, and includes the HTTP-transport service, `ulimits` and healthcheck guidance omitted above. +The image uses a non-root `sshmcp` user (uid 1000). Mount your SSH keys and config file read-only. [compose.yaml](https://github.com/blackaxgit/ssh-mcp/blob/main/compose.yaml) in the repo carries a fuller example — it builds from the local Dockerfile rather than pulling the published image, and includes the HTTP-transport service, `ulimits` and healthcheck guidance omitted above. ### Create a config file @@ -349,7 +349,7 @@ Note two things about the timeouts, because the names invite confusion: `max_parallel_hosts` bounds the whole process, not a single call: the semaphore is built once at startup, so concurrent `execute_on_group` calls share the same budget rather than each getting their own. -Per-server overrides (`hostname`, `port`, `user`, `identity_file`, `jump_host`, `default_dir`, `timeout`) take precedence over `~/.ssh/config`. See [config/servers.example.toml](config/servers.example.toml) for the annotated reference. +Per-server overrides (`hostname`, `port`, `user`, `identity_file`, `jump_host`, `default_dir`, `timeout`) take precedence over `~/.ssh/config`. See [config/servers.example.toml](https://github.com/blackaxgit/ssh-mcp/blob/main/config/servers.example.toml) for the annotated reference. Restrict config file permissions to your user: @@ -404,7 +404,7 @@ ASCII control characters (null bytes, newlines, `\x01..\x1f`, `\x7f`) are normal > **Known limitation: command OUTPUT is NOT redacted.** If you run `cat /etc/mysql/my.cnf`, `env | grep PASSWORD`, or `kubectl get secret X -o yaml`, the stdout/stderr returned to the MCP client will contain plaintext secrets. The redaction pipeline only filters the COMMAND string (what you asked to run), not the OUTPUT (what it printed). Avoid running commands that print secrets via ssh-mcp — pass credentials through env vars, Docker/K8s secrets, or dedicated config files instead. -**Local path confinement (new in 0.6.0; versions ≤ 0.5.6 are affected by the flaw it fixes — see [CHANGELOG.md](CHANGELOG.md)).** SFTP `upload_file` and `download_file` no longer accept arbitrary absolute local paths. Every local path is **relative to a configured transfer root** and is resolved one component at a time beneath it, refusing a symbolic link at *any* component: +**Local path confinement (new in 0.6.0; versions ≤ 0.5.6 are affected by the flaw it fixes — see [CHANGELOG.md](https://github.com/blackaxgit/ssh-mcp/blob/main/CHANGELOG.md)).** SFTP `upload_file` and `download_file` no longer accept arbitrary absolute local paths. Every local path is **relative to a configured transfer root** and is resolved one component at a time beneath it, refusing a symbolic link at *any* component: ```toml [settings] @@ -436,7 +436,7 @@ For production log aggregation, set `SSH_MCP_LOG_FORMAT=json` to emit single-lin When running in Docker, capture stderr with `docker logs` for the audit trail. -For vulnerability reports, see [SECURITY.md](SECURITY.md). Do not open public GitHub issues for security concerns. +For vulnerability reports, see [SECURITY.md](https://github.com/blackaxgit/ssh-mcp/blob/main/SECURITY.md). Do not open public GitHub issues for security concerns. ## Development @@ -448,12 +448,12 @@ uv run pytest uv run ruff check src/ tests/ ``` -See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on making changes and submitting pull requests. +See [CONTRIBUTING.md](https://github.com/blackaxgit/ssh-mcp/blob/main/CONTRIBUTING.md) for guidelines on making changes and submitting pull requests. ## Changelog -See [CHANGELOG.md](CHANGELOG.md). +See [CHANGELOG.md](https://github.com/blackaxgit/ssh-mcp/blob/main/CHANGELOG.md). ## License -Mozilla Public License 2.0. See [LICENSE](LICENSE). +Mozilla Public License 2.0. See [LICENSE](https://github.com/blackaxgit/ssh-mcp/blob/main/LICENSE). diff --git a/pyproject.toml b/pyproject.toml index a6078b4..f261dd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,17 +9,28 @@ # container image stays ghcr.io/blackaxgit/ssh-mcp. name = "blc-ssh-mcp" dynamic = ["version"] -description = "SSH MCP server for managing infrastructure via Claude Code" +description = "MCP server giving AI assistants SSH access to run commands, transfer files, and manage server fleets" +# Without `readme` there is NO long description in the wheel METADATA, and the +# PyPI project page renders an empty body — which is exactly what 0.6.1 shipped. +# The README is written to survive being rendered off-GitHub: every link in it +# is absolute, because PyPI resolves relative links against pypi.org and they +# would 404. Keep it that way when editing README.md. +readme = "README.md" requires-python = ">=3.11" -license = {text = "MPL-2.0"} +# PEP 639 SPDX expression, not the legacy `{text = "..."}` table (deprecated, +# and setuptools/hatchling both warn on it). `license-files` replaces the +# `License :: OSI Approved ::` classifier, which PEP 639 also deprecates — do +# not add that classifier back alongside this. +license = "MPL-2.0" +license-files = ["LICENSE"] authors = [ {name = "SSH MCP Contributors"}, ] -keywords = ["mcp", "ssh", "claude", "infrastructure"] +keywords = ["mcp", "ssh", "claude", "infrastructure", "sftp", "devops", "automation"] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", - "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", + "Intended Audience :: System Administrators", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -53,6 +64,10 @@ ssh-mcp = "ssh_mcp.server:main" Homepage = "https://github.com/blackaxgit/ssh-mcp" Repository = "https://github.com/blackaxgit/ssh-mcp" Issues = "https://github.com/blackaxgit/ssh-mcp/issues" +Changelog = "https://github.com/blackaxgit/ssh-mcp/blob/main/CHANGELOG.md" +Documentation = "https://github.com/blackaxgit/ssh-mcp#readme" +"Security Policy" = "https://github.com/blackaxgit/ssh-mcp/blob/main/SECURITY.md" +"Container Image" = "https://github.com/blackaxgit/ssh-mcp/pkgs/container/ssh-mcp" [project.optional-dependencies] dev = [ diff --git a/src/ssh_mcp/__init__.py b/src/ssh_mcp/__init__.py index f81e717..cca6692 100644 --- a/src/ssh_mcp/__init__.py +++ b/src/ssh_mcp/__init__.py @@ -1,3 +1,3 @@ """SSH MCP Server - Manage infrastructure via MCP clients like Claude Code.""" -__version__ = "0.6.1" +__version__ = "0.6.2"