Release v0.5.0
Security
- A malicious Gemini server can no longer hang the client with an unbounded
status-44 (SLOW_DOWN) backoff. The server-supplied wait is now sanitized and
clamped (a non-finite value such asinfis rejected/capped), so a single
44 infresponse can no longer make every later fetch to that host sleep
forever while holding a concurrency-semaphore slot. - The Gemini TLS close handshake is now time-bounded, so a peer that withholds
close_notifycan no longer tack the OS TCP timeout onto each request after
the read deadline has already been met.
Fixed
- Gopher menu items using the hURL web-link convention (a
URL:<target>
selector, typically on a type-hitem) now expose the real destination as
next_urlinstead of agopher://URL pointing back at the gopher host, so
web/gemini links on real-world menus are followable. - Gopher text-mode un-dot-stuffing (
..→.) is now applied only when the
RFC 1436.terminator is actually present. An unframed document (no
terminator) is returned verbatim, so a literal leading..is no longer
corrupted to.. - A server-side Gemini protocol fault (missing CRLF, over-long meta, bad status
line, empty response) now returns a distinctPROTOCOL_ERRORinstead of
INVALID_REQUEST, so the model is told the server misbehaved rather than that
its own URL was malformed. - A spec-valid comma-separated
langparameter (e.g.lang=en,fr) is no longer
rejected; rejecting it previously discarded the whole MIME type, including the
declared charset, producing mojibake or gemtext misclassification. - A stray unprefixed
GOPHER/GEMINI/SERVERenvironment variable set by
unrelated tooling no longer crashes startup (it was misread as the nested
config object).
Changed
- Gemini binary success responses now return metadata only (size + detected
MIME type) as a newbinaryresult kind, instead of base64-inlining the full
body into the model's context. This matches the Gopher binary path and the
server's documented "binary bodies are returned as metadata only" contract; a
1 MB body previously shipped ~1.4M base64 characters. Re-fetch the resource
directly if the raw bytes are needed. - Gemtext preformat (code-block) content lines no longer repeat a per-line
metadata dict (plus duplicatedalt_text/language); block-level metadata is
kept on the opening```toggle line only. This roughly thirded the
serialized size of code blocks sent to the model. Attribute access is
unchanged; only the serialized output is leaner. - The Gopher menu parser now stops once the item cap (
max_menu_items) is
reached instead of building the entire directory before slicing, so a 1 MB
directory no longer materialises tens of thousands of model objects to keep a
small slice. Truncation is still flagged on the result. - Client-certificate Gemini requests now reuse a per-certificate TLS client
(and its SSL context) instead of rebuilding it on every request, so the system
CA bundle load and cert/key PEM reads — blocking work that ran on the event
loop — happen once per certificate rather than per fetch. - Release/CI hygiene:
prepare-release.pynow bumps the version with an anchored
match (no longer corruptingtarget-version/python_version/minversion
inpyproject.toml) and also updatesserver.json; GitHub release notes no
longer drop the last line of each changelog section; all CI/release/publish
jobs runuv sync --lockedso a stale lockfile fails loudly; the
manual-publish workflow no longer offers a no-oppypitarget; Dependabot now
covers the Docker base image; and the docs site redeploys onsrc/**changes
so the mkdocstrings API reference can't drift.
Release Information:
- Version: 0.5.0
- Release Date: 2026-06-11
- Commit: cd71f7f
- Workflow: 27374295717
Installation:
pip install gopher-mcp==0.5.0Verification:
All packages are signed and can be verified using Sigstore.