Repository navigation
Releases
v3.3.1
Fixed
- The container installed its dependencies from a fresh resolve of
pyproject.tomlinstead of the committed, Dependabot-maintained
uv.lock.pip install .reads only the floor pins, so the image
shipped whatever had been released that morning -- a dependency set
no test had ever run against. This is the same bug that shipped
google-genai 3.0.0 to production in mcp-gemini while CI tested 2.34.0,
causing an outage there (Part of RT#1564). The builder now exports the
lock to a requirements file in a dedicated stage and installs from it
with--no-deps, so pip cannot re-resolve anything the lock already
decided.
v3.3.0 — the cap was checked after the body was already in memory
RT #1601, the fleet's outbound HTTP contract. Three read paths here — the API, a content download and the OAuth token exchange — and all three had the same defect.
- The cap was checked after the body was in memory. Each path compared
len(response.content) > MAX_RESPONSE_SIZEonce the whole body had arrived. All three now stream, measured before each chunk is copied in, becauseaiter_bytes()promises nothing about chunk size: one chunk can be the whole body. The token exchange gets its own, smaller cap — a token response is a handful of JSON fields, and anything page-sized answering on that URL is not a token. - Requests were nameless. All three paths now send
mcp-pcloud-crunchtools/<version> (+<repo>), the version read from installed package metadata so it cannot drift frompyproject.toml. Malformed JSON from listfolderblamed the parser for what is usually a challenge page. Errors now name the method, the full requested URL, the status, the content-type and a bounded snippet.
A pCloud content URL is signed, so naming it is only safe because the signature comes out first: userinfo is dropped and credential-shaped query parameters are replaced. The token exchange quotes no body at all — it is the one endpoint whose body is a credential.
Two things review caught that are worth recording:
- Replacing
response.textlost the declared charset, so a Latin-1 document would have come back with replacement characters where its accents were. It decodes withresponse.encodingnow. - The old suite patched
httpx.AsyncClient.getandhttpx.Client.post. A patch on a method the code no longer calls does not fail — it lets the test reach the real network, which is how three tests were briefly talking to api.pcloud.com. The suite rides aMockTransportnow, which has no live socket to fall through to.
Constitution pin v1.31.0 → v1.36.0.
v3.2.2
fastmcp 4.1.4 → 4.1.6 and pytest-asyncio 1.4.1 → 1.4.1.post1, both inside their declared ranges (#67, #68).
This is also the first release of this service deployed by the RT Deploy queue rather than by the nightly fleet auto-update. Its unit carries io.crunchtools.deploy=deployer and no io.containers.autoupdate label, so a change reaches production when a ticket names a commit — with a maintenance window, a health probe that has to hold, and one automatic rollback — and not because a container happened to restart.
v3.2.1
Security
- Upgraded locked dependencies within their declared ranges to clear the
advisories the fleet dependency audit found (authlib, certifi, dnspython, fastmcp, joserfc, opentelemetry-api, pyjwt).
v3.2.0
Fixed
pcloud_searchfailed at/(pCloud error 1101) and on large folders such as
/Documents, where one recursivelistfolderoutgrew the 10 MB response
limit. It now walks the tree breadth-first with one non-recursive
listfolderper folder. The depth (8) and result (100) bounds are unchanged;
a new bound of 200 listings stops the walk and tells the caller to narrow the
path (RT #1542).
v3.1.0
Changed
- Container base images pinned to
quay.io/hummingbird/python:3.14*instead of
latest, which moved to Python 3.15.0 on 2026-10-20 (crunchtools/constitution#66).
v3.0.1
Fixed
pcloud_searchcalledsearch, which is not a published pCloud method and
timed out or matched nothing against the live API. It now listspathwith
listfolder(recursive=1) and matches names case-insensitively by
substring. The walk is bounded to 8 levels and 100 results; the output says
when it truncated. The tool name and signature are unchanged.
v3.0.0
Changed
- BREAKING:
pathis now required onpcloud_list_folderand
pcloud_search; it no longer defaults to/. Both tools now publish
readOnlyHint: true. A required argument is never dropped by a gateway, so
an annotated call cannot widen to the whole account.
Fixed
pcloud_read_text_filecalledgettextfilebut parsed agetfilelink
response, so it failed on every call (pCloud rejected the credential with
"Log in required"). It now callsgetfilelink, as the code already expected.- The content download no longer sends the
Authorization: Bearerheader to
the content host. It uses a separate client with no base URL and no
credential; the content URL is already signed.
v2.6.0
Added
- Six tools that only read (
pcloud_get_file_info,pcloud_read_text_file,
pcloud_get_checksum,pcloud_get_file_link,pcloud_get_user_info,
pcloud_auth_status) publishreadOnlyHint: true. A gateway uses it to
decide whether an invalid optional argument may be dropped or must refuse the
call (crunchtools/constitution#35).pcloud_list_folderandpcloud_search
also only read but stay unannotated: their optionalpathdefaults to/,
so a droppedpathwould widen the call to the whole account. - Tests pin every registered tool into
READ_ONLYorWRITES, and check that
each read-only tool calls only pCloud API methods from a named read set and
leaves the token store untouched.
Changed
- Inherits constitution v1.22.0; the workflow pins move with it.
- Constitution is now a v1.18.0 manifest: only repo-specific facts remain;
fleet and profile rules apply by reference. - Constitution validation is pinned via
.github/workflows/constitution.yml. - Dependabot auto-merges GitHub Actions minor and patch updates.
v2.5.0
Browser authorization, driven through MCP
Re-authorizing a containerized pCloud server is now a tool call instead of a runbook. Nothing is copied by hand.
Added
pcloud_auth_status— whether the server holds a usable credential, and if not, what is missing. Verifies a stored token against pCloud rather than assuming its presence means it works.pcloud_auth_start— returns the URL to open. The user approves in a browser; pCloud redirects back and the token is stored server-side.pcloud_auth_result— what the last redirect did, for when status alone cannot tell whether one ever arrived.- A
/callbackroute on the server's own HTTP port via FastMCP'scustom_route. This is what removes the copy-and-paste step: the exchange happens before the page renders. No second listener, no extra port. PCLOUD_OAUTH_REDIRECT_URIis now read by the server as well as the CLI.
Security
- The CSRF
stateoutlives the tool call in a single-slot holder: expires after 10 minutes, single-use, and superseded rather than duplicated when a second authorization starts. Two outstanding valid states would be two ways in. - A redirect's reported region is honoured only when it is a known pCloud endpoint, so it cannot aim the client at an arbitrary host.
Tool count is now 18.
Full Changelog: v2.4.0...v2.5.0