Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,28 @@ jobs:
name: dist
path: dist/

validate-server-json:
# Closes #44. Gate PyPI publish on registry-schema validation so a
# malformed server.json can't half-publish: PyPI accepts everything,
# but the registry rejects schema drift (new required fields, type
# changes) — without this gate, PyPI ships and the registry leg fails,
# leaving a discoverable PyPI release that isn't in the MCP registry.
# Re-running the failed job can't fix PyPI, so the cleanup is manual
# (yank, fix, re-tag). Validating up-front with the SAME pinned
# mcp-publisher version that actually publishes (`./.github/actions/
# install-mcp-publisher`) means schema drift surfaces before any
# external side-effect lands. ci.yml runs the same check on every PR;
# this job is the release-time backstop in case the registry schema
# or publisher version changes between merge and tag-push.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/install-mcp-publisher
- name: Validate server.json against registry schema
run: ./mcp-publisher validate server.json

publish-pypi:
needs: build
needs: [build, validate-server-json]
runs-on: ubuntu-latest
environment: pypi
permissions:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Added

- **publish.yml: gate PyPI publish on `server.json` registry-schema validation** (#89) — closes #44. Adds a `validate-server-json` job that runs before `publish-pypi` on every release tag, mirroring the same check that runs on every PR via `ci.yml`. Without this gate, a malformed `server.json` (new required field in a future registry schema, type change, etc.) would PyPI-publish cleanly and then fail at the registry leg — leaving a discoverable PyPI release that isn't in the MCP registry, with no way to roll back PyPI short of yanking. v0.5.1 hit the registry-leg-fails-after-PyPI scenario for a *different* reason (mcp-publisher OIDC audience mismatch, fixed in #79), so the failure mode isn't theoretical. The new job uses the same `./.github/actions/install-mcp-publisher` composite that `publish-registry` uses, so the validating publisher version always matches the publishing one. Job runs in parallel with `build` (no dep on it), and `publish-pypi` now lists both as `needs:` so a validation failure stops the entire pipeline before any external side-effect. The optional `publish-manifest` artifact named in #44 is deferred — it's a tooling-version reconciliation aid that's only useful if a CI-PR / release-tag mismatch *does* fire, and is straightforward to add later when there's a concrete failure to debug.

- **`additional` parameter exposed on `list_shares`, `list_files`, and `search_files` MCP tools** (#87) — closes #41. The underlying handlers in `modules/filestation/listing.py` and `modules/filestation/search.py` already accepted an `additional: list[str] | None` parameter, but the FastMCP tool registrations in `modules/filestation/__init__.py` never surfaced it — callers couldn't request DSM metadata fields beyond the per-tool defaults. Same class of spec-vs-tool drift as #33 (which added `mtime_from`/`mtime_to` to `search_files`). Now the three tool registrations accept `additional: list[str] | None = None`, with a docstring per tool listing the supported field set; the underlying defaults (`["real_path", "size", "owner", "perm"]` for shares, `["size", "time"]` for files and search) are unchanged so existing callers see no behavior difference. New `validate_additional()` helper in `modules/filestation/helpers.py` rejects unknown values up-front against a union whitelist (`real_path`, `size`, `owner`, `time`, `perm`, `type`, `mount_point_type`, `volume_status`) — DSM silently ignores unknown values (the field just doesn't appear in the response), which would have made typos like `"sze"` invisible; validating up-front surfaces the typo as a clear ToolError naming the bad value and listing the supported set. The whitelist is a union across List + Search APIs; tighter per-API gating isn't worth the maintenance cost since DSM ignores values that don't apply (e.g. `mount_point_type` on a non-share path) without erroring. New tests: 12 cases in `TestValidateAdditional` (7 valid/empty accepted, 4 unknown rejected with clear errors, 1 verifying the tool name appears in the error envelope) plus six round-trip + reject-unknown tests across `TestListShares`, `TestListFiles`, and `TestSearchFiles`. The Search round-trip captures the `list` (poll) request, not `start` — DSM Search returns full file metadata at poll time, so `additional` is sent there. 599 unit tests pass at 96.25% coverage.

### Changed
Expand Down