Release v0.4.2
Security
- Move the Gemini TLS transport to native asyncio (
asyncio.open_connection
withssl=), so connect, handshake and every read are genuinely cancellable:
a slow-loris or stalled peer is now cut off at the request deadline instead of
parking a worker thread on a blockingrecv. The previous design ran blocking
socket I/O on a thread pool shared with DNS resolution (the SSRF guard for both
protocols), so a handful of slow Gemini reads could stall DNS for every request
and escalate one slow server into a whole-server denial of service. - Stop leaking a Gemini status-11 (
SENSITIVE_INPUT) answer: the percent-encoded
query is no longer written to logs, reflected back to the model via
requestInfo, or retained in a cache key. - Reject not-yet-valid certificates on TOFU first use by default (previously
pinned with only a warning unlessreject_expiredwas set). - Reject empty and self-referential Gemini redirects (
INVALID_REDIRECT) so a
malformed3xresponse cannot drive an unbounded client re-fetch loop.
Added
GOPHER_MAX_MENU_ITEMS(default 1000): caps the number of Gopher menu items
returned to the model, mirroring the existing text/gemtext character cap.- Optional positive port allowlist (
GOPHER_ALLOWED_PORTS/
GEMINI_ALLOWED_PORTS) to close the arbitrary-port port-scanning gap left by
the dangerous-ports denylist.
Changed
- Apply the
max_rendered_charscap totext/geminiresponses (previously only
text/*), so a large gemtext page no longer floods the model context; both
gemtext and menu results now carry atruncatedflag. - Drop the over-strict hardcoded TLS 1.2 cipher allow-list in favour of Python's
secure defaults, improving interop with conforming Gemini servers. - De-duplicate the two batch-fetch tools (
gopher_batch_fetch/
gemini_batch_fetch) onto a single shared implementation so the batch
error/contract behaviour has one source of truth (no behaviour change).
Removed
- The unused
http/aiohttpoptional dependency, the never-read
development_modesetting, and the test-onlycreate_tls_clientfactory. - The
GeminiGemtextResult.summary,.plain_textand.structured_content
helper properties: they were never serialized bymodel_dump()so the MCP
tools never exposed them (dead LLM-facing API). The parseddocumentand
raw_contentcarry the same information.
Fixed
- Correct the LLM-facing server instructions,
gopher_fetchparameter
description, and AI Assistant Guide to reference the real serialized
next_urlmenu field (not thenextUrl/urlnames that never appear in the
output). - Sync
server.jsonto the released version and replace placeholder
Release Information:
- Version: 0.4.2
- Release Date: 2026-06-09
- Commit: 93be258
- Workflow: 27209467836
Installation:
pip install gopher-mcp==0.4.2Verification:
All packages are signed and can be verified using Sigstore.