Skip to content

v1.6.0

Choose a tag to compare

@a-akimov a-akimov released this 13 Aug 20:14
f7dd814

Fixed

  • The source distribution's tests can be collected. The sdist shipped
    tests/ without scripts/, 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 lone npm/README.md being 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-commit drives the gate but was
    undeclared, so it resolved from whatever happened to be on PATH and a fresh
    uv sync --all-groups failed with Failed to spawn: pre-commit. It is now
    pinned in the dev group. The check target also sets the same SKIP as CI, so
    it no longer fails on main via the no-commit-to-branch hook while claiming
    to mirror CI; the hook still runs on real commits.
  • One CLI test no longer leaves the process. It called async_main()
    against https://example.com/mcp without replacing MCPClient, 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 runs mcpscore==<version> through uvx/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
    — reusing wait_for_pypi_index from scripts/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.pythonVersion pin — 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 without read_timeout_seconds (SDK default None), so a
    server that accepted a connection and then never answered tools/list,
    resources/list, resources/templates/list or prompts/list held the audit
    forever. MAX_LISTING_PAGES bounded 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_listings behaviour — rather than failing the audit. The timeout
    argument to MCPClient documented 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 foreign Origin values 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/json or text/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 harmless tools/list and 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-applicable unless the control shows the same request would otherwise be
    accepted. The unknown-method probe reports not-applicable on 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.