Skip to content

Releases

v0.6.2

Choose a tag to compare

@github-actions github-actions released this 29 Oct 15:37
8053c03

Fixed

  • ADIF is read by qso_graph_lib.adif, the fleet's one ADI reader, which reads ADI as
    ADIF 3.1.7 defines it and passes ADIF's own test files. This server's scanner read a
    Header's fields as the first QSO's, so ADIF_VER and PROGRAMID (with trailing spaces)
    reached tool results in that QSO's adif fields, and a length that ran past the next tag
    returned two QSOs as one. New dependency: qso-graph-lib>=0.2.0 (standard library only).

v0.6.1

Choose a tag to compare

@github-actions github-actions released this 29 Oct 06:05
c3fd1c4

Changed

  • Requests to eQSL.cc are now spaced. Where a provider states a request
    limit this server honours that limit; eQSL.cc states none, so the gap is a
    recorded judgement rather than a published figure — 200 ms, matching what
    sota-mcp and iota-mcp already use, with the reasoning written beside the
    constant so it can be revisited rather than rediscovered. A volunteer or public
    service with no stated limit still deserves restraint (KI7MT, 2026-10-11).

    This is not throttling the user. It limits what this server sends to somebody
    else's service; it does not limit the person asking. The response cache does the
    larger share of the work by removing requests rather than delaying them, and the
    spacing covers what a cache cannot: a model calling tools in a loop over inputs
    that each miss.

  • This server also sent no User-Agent at all, so eQSL could not tell who
    was calling or ask us to ease off. It now identifies itself by name and version.

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 17:33
e8d8e37

Added

  • A 429 from eQSL is now honoured. eQSL asking this client to stop is the
    most explicit form a request limit takes, and it was previously invisible:
    every failure became "eQSL request failed — check network and credentials", so
    a rate limit read as a wrong password. Now the Retry-After interval is read
    and bounded to between a minute and an hour, no further request goes out until
    it has passed, and the tool says how long to wait rather than sleeping through
    it. The AG-list download shares that block — the limit is eQSL's view of this
    client, not of one URL.

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 26 Oct 17:03
3dab284

Changed

  • Python 3.11 is the minimum. 3.10 reached end of life in October 2026
    (PEP 619), so it gets no further security
    releases on any platform. On Windows it was worse than unsupported: uv's 3.10
    is built against OpenSSL 1.1.1w, end of life since September 2023, so HTTPS
    from this server ran over an unmaintained TLS library. certifi does not help
    with that — it supplies the root store, not the implementation.

    3.10 support was already nominal. This package has linted with
    ruff target-version = "py311" and type-checked with mypy python_version = "3.11" for some time, so nothing ever verified the 3.10 claim.

    uv.lock is regenerated and the diff is large, but nothing is upgraded —
    it is the 3.10 resolution branch disappearing. Packages that carried two pins,
    one per Python branch, now carry the single 3.11+ pin they already resolved to.
    An install on 3.11 or later is unchanged.

v0.4.3

Choose a tag to compare

@github-actions github-actions released this 26 Oct 12:41
a0b46f7

Changed

  • --transport streamable-http now says where it bound, and the help text says
    it too. It binds 127.0.0.1, and FastMCP takes no host option, so it was
    never reachable from another machine or from outside a container — and nothing
    said so. The bind address is unchanged: loopback-only is deliberate for a
    server that holds credentials and has no authentication of its own.

v0.4.2

Choose a tag to compare

@github-actions github-actions released this 26 Oct 09:31
2432220

Fixed

  • --version and --help now print and exit. They were ignored, so the
    command started the MCP server on stdio and waited for JSON-RPC — leaving a
    FastMCP banner on screen and an apparently hung terminal. Seven servers in
    this fleet already behaved correctly; this brings the rest in line, and a
    test asserts it.

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 26 Oct 01:39
06cab13
  • A release should appear on the Releases page
  • Move every Node 20 action to its first Node 24 major
  • The locked-dependency audit blocked every release
  • Four upgrades arrived as 57 pull requests
  • Nothing was auditing the lockfile
  • Close every open security advisory in the lockfile
  • A release is a workflow, not a merge
  • Make the publish gate's http:// check portable
  • Assert that certifi is declared, not merely imported

v0.4.0

Choose a tag to compare

@watson-qg watson-qg released this 26 Oct 01:12
f0a8756

Upgrading: a credential failure now returns isError: true instead of a successful result whose
payload contained an error key. Anything reading result["error"] should read the error flag.
Other failures — a network error, eQSL refusing — already reached a client as errors and are
unchanged.

Security-relevant: eQSL's own response body was returned as the error text, and this server's
requests put the password in the query string. A body echoing the request used to carry the
password into the message. That text is now withheld, with a reason, when it contains the secret or
any URL.

  • E501 is adopted (#22, qso-graph-devel#70). It reported nothing — this source was
    already within 95 columns, so adoption was the config change alone with no reflow, and the repo
    carries no noqa. That the rule is enforced rather than inert was checked rather than assumed: a
    planted 117-character line is reported, and removed again the tree is clean.

  • certifi is carried, and both fetch paths verify against it (#21, qso-graph-devel#73). They
    used urllib with no context, so they trusted whichever root store the running Python was built
    against. On macOS that is /etc/ssl/cert.pem, a deliberately reduced OpenSSL file — 128 roots
    against the system keychain's 158, and current rather than stale. eqsl.cc verifies fine against
    it today
    , so this was latent rather than an outage.

  • It matters most on the credentialed path: eQSL's query interface puts the operator's password
    in the query string, so ssl.CERT_NONE there would mean handing the password to whoever
    presented a certificate. That is why verification is never disabled, rather than merely why it
    should not be.

  • The context lives in its own module, _tls.py, because the calls are in two files — client.py
    and ag_cache.py — and the "every urlopen verifies" check scans the whole package. A check
    reading one file would have left the other unverified, and which one is worse depends only on
    which file it happened to read.

  • tests/test_release_gate.py asserts the gate suite imports nothing but the standard library
    and pytest. solar-mcp's 0.3.0 publish failed on exactly that, from the same change.

  • Credential failures are flagged isError (#23). Three tools caught CredentialError and
    returned {"error": "..."}, which reaches a client as a successful call whose payload happens
    to contain the word "error". They now raise ToolError. No decision changed — only how it is
    reported.

  • Measured while doing it: this server was already half-right, by accident. Its tools caught
    only CredentialError, so every other failure — a network error, eQSL refusing — propagated
    uncaught, and fastmcp already reported those as isError with the message intact. That
    behaviour was being relied on with nothing asserting it, so a later tidy-up adding a blanket
    except Exception: return {...} would have silently undone it. It is now pinned by a test, which
    also refuses any {"error": ...} return creeping back into server.py.

  • Fixed: eQSL's own response body could carry the password into the message. last_upload_date
    stripped the tags off whatever eQSL returned and handed that back, and this server's requests put
    the password in the query string. Measured, not assumed: given a body echoing the request the
    secret came straight through. That text is now withheld, with a reason, when it carries the
    secret or anything URL-shaped, and capped — while an ordinary Invalid user name still reaches
    the caller. Same shape as the fix in lotw-mcp.

  • Every tool declares all four annotation hints (#23). All six are read-only, non-destructive
    and idempotent; openWorldHint is true for the five that reach eqsl.cc — three with the
    operator's credentials, two on public endpoints — and false for get_version_info.

  • tests/test_mcp_conformance.py asserts all of the above.

v0.3.8

Choose a tag to compare

@watson-qg watson-qg released this 26 Oct 01:12
70854f9
  • ruff and mypy run in CI (qso-graph-devel#66), as a job the ci-all-green gate requires.
    Settings follow adif-mcp, the reference for every qso-graph Python repo, rather than a style of
    this repo's own. They run once rather than per Python version: both read the source, and neither
    answer changes with the interpreter.
  • Five tool functions were annotated -> dict with no type arguments, so nothing checked what
    they return. They say dict[str, Any] now.
  • E501 is deferred rather than adopted (qso-graph-devel#70): what it reports in these repos are
    widths, not defects, and some lines are long because they name a publisher's field exactly.
  • mcp.run is given the literal fastmcp asks for rather than a str that happens to hold the
    right word.
  • fastmcp is bounded: >=4.0,<5 (qso-graph-devel#60). It was >=3.0 with no upper bound, and
    these servers are run with uvx, which resolves fresh — so a fastmcp 5.0 would have reached
    every user automatically, before anything here had been run against it. The floor rises to 4.0
    because that is what is actually tested: every lock in the fleet held a 4.x, and nothing in CI
    has ever exercised 3.x. A claim of 3.x support that no test backs is not support.
  • fastmcp is locked at 4.1.0, the current release, so CI runs against what a new install gets.
  • The published contact is maintainers@qso-graph.io (qso-graph-devel#69). The authors field
    carried a personal address, and that field is what PyPI shows on the package page. Everything in
    qso-graph is open source and open to contribution, so the contact is the project's.

v0.3.7

Choose a tag to compare

@watson-qg watson-qg released this 26 Oct 01:12
63d73ed
  • LICENSE: the full GPL-3.0 text. The file held only its opening and a link, so GitHub detected no licence.