v1.6.0
Fixed
- The source distribution's tests can be collected. The sdist shipped
tests/withoutscripts/, so two maintainer-only tests that import from
scripts/failed at collection — pytest could not even start against the
published archive. Those two files are now excluded from the sdist (they test
our release tooling, not this package), and every include pattern is anchored,
which also stops a lonenpm/README.mdbeing shipped from an otherwise-absent
subtree. Verified by building the sdist and running its suite inside it. - A clean checkout can run
make all.pre-commitdrives the gate but was
undeclared, so it resolved from whatever happened to be onPATHand a fresh
uv sync --all-groupsfailed withFailed to spawn: pre-commit. It is now
pinned in the dev group. The check target also sets the sameSKIPas CI, so
it no longer fails onmainvia theno-commit-to-branchhook while claiming
to mirror CI; the hook still runs on real commits. - One CLI test no longer leaves the process. It called
async_main()
againsthttps://example.com/mcpwithout replacingMCPClient, so it
performed real transport detection — measured at 8 live connections on port
443 — while passing regardless, since it only asserts the welcome banner. It
now injects the existing mocked client and auditor. - The npm wrapper can no longer publish before PyPI. The npm package is a
launcher that runsmcpscore==<version>throughuvx/pipx, and both
publish workflows fire on the same release event with no ordering, so npm
could expose a version whose Python dependency was still propagating — or had
failed to publish at all. The npm job now waits for the pinned version to appear on the PyPI simple
index — reusingwait_for_pypi_indexfromscripts/release.py, because the
JSON metadata endpoint goes green before the index uv and pip actually
resolve against. It also refuses to publish unless the release tag is plain
semver and matches both the wrapper's own version and its
mcpscore.pythonVersionpin — the latter is what the launcher actually runs,
so a stale pin would publish a new npm version that starts an old CLI, and
would make the PyPI wait pass instantly against the already-published old
version. Pre-releases skip npm version sync, so a pre-release tag still
carries the previous stable manifest version. - An unresponsive server can no longer stall an audit indefinitely. The MCP
session was opened withoutread_timeout_seconds(SDK defaultNone), so a
server that accepted a connection and then never answeredtools/list,
resources/list,resources/templates/listorprompts/listheld the audit
forever.MAX_LISTING_PAGESbounded how many pages could succeed but bounded
no time, and stdio and SSE have no transport-level read timeout to fall back
on. Sessions now carry a 60s per-request deadline, and each paginated listing
has a 180s total budget so page count cannot multiply it. A listing that runs
out of budget degrades to a partial result — the existing
incomplete_listingsbehaviour — rather than failing the audit. Thetimeout
argument toMCPClientdocumented itself as a connection timeout while only
governing the handshake; its docstring now says so.
Added
-
Three modern Streamable HTTP readiness rules validate security and
interoperability without invoking tools: invalid foreignOriginvalues must
receive HTTP 403 (readiness_2026_origin_validation), unknown RPC methods
must receive HTTP 404 with JSON-RPC-32601
(readiness_2026_unknown_method_error), and successful requests must return
application/jsonortext/event-stream
(readiness_2026_response_content_type). All three are HIGH. The Origin
requirement is a spec MUST, and its weight depends on the target: for a
locally-bound or plain-http://server it is the direct DNS-rebinding
mitigation, while for a remote HTTPS server it is defence in depth. HIGH is a
single level covering both. The content-type rule reuses existing successful probe
observations; the other two probes use harmlesstools/listand invented
method requests. Network failures and non-HTTP transports skip rather than
fail.Both security probes judge only what they can actually observe. The
Origin
probe first sends a control request without the foreign header: a lone HTTP
403 does not prove Origin validation, because 403 is also how an
access-controlled server refuses everyone, so the check reports
not-applicableunless the control shows the same request would otherwise be
accepted. The unknown-method probe reportsnot-applicableon 401/403 for the
same reason — a request that never reached method dispatch says nothing about
how the server answers an unknown method, and auth-gated servers are healthy.