Skip to content

v0.22.1

Choose a tag to compare

@csitte csitte released this 11 Oct 18:00
· 76 commits to main since this release

Changed

  • Comparison table: three columns moved again, and the sweep cell for taylorwilsdon changed. npm run table-sources
    reported taylorwilsdon, aaronsb and klodr ahead of the recorded revisions; each was brought forward by reading the
    diff, with the repository-wide claims re-checked in a fresh checkout. taylorwilsdon (78973045, v2.1.0, 170 commits
    on) gained an apply action on its filter tool that translates a filter's criteria into a Gmail search, pages
    messages.list and relabels the matches through batchModify — the first query-driven bulk path on that column, so the
    cell no longer says explicit ids alone. It is labels only, and the ids are never re-checked against live labels, which
    the cell and the paragraph under the table now say. aaronsb (14c25e66, v4.6.0) added listDrafts, deleteDraft and
    createLabel, with the permanent draft deletion added to its no-delete policy — no cell moves; the same round
    corrected a count of ours in docs/comparison-sources.json (the 8 September note said fifteen Gmail operations and
    listed sixteen; it was sixteen, and is nineteen now). klodr (ddb9da01) moved only its version string. The table
    marker stays at 3 September, the Google column's date.
  • Comparison table: three columns moved at once, and one cell changed. npm run table-sources
    reported klodr, a-bonus and taylorwilsdon all ahead of the recorded revision; each was brought
    forward by reading the diff and the repository-wide claims re-checked in a fresh checkout. klodr
    (2fa333bd) moved for the first time since July and grew a download_all_attachments tool that
    carries an output schema of its own, so the structured-outputs cell now says two tools rather than
    one — the only cell to change. The same round corrected a sentence of ours: the 3 September note
    claimed no occurrence of dmarc or spf in that repository, and src/prompts.ts has carried
    "DMARC/SPF failure hints" in a phishing-triage prompt all along — a guess from a header mismatch,
    not a read of Authentication-Results, so the cell stays a dash, but the note's zero was wrong and
    is now corrected in place. a-bonus (2e40593d) touched no Gmail file: credential-loading error
    text, a deferred Google client so the MCP handshake succeeds without credentials, and a Drive
    save-path guard. taylorwilsdon (ed70fb90, v1.29.0, 84 commits on) adds a headers-only format
    and a label visibility value inside existing Gmail tools, and its two scope files widen the Sheets
    and Apps Script levels while the Gmail level table stays as read on 7 September — the second
    instance of a level named for one service reaching into another API. Its new schema middleware
    rewrites input schemas for Gemini and is not an output schema; output_schema still has no
    occurrence there. The README's table note now describes this bring-forward practice instead of
    the 18 September state it had frozen at.
  • Comparison table: the taylorwilsdon column brought forward once more (8475cecf, v1.28.0,
    33 commits on), and this time the Gmail file itself moved: gmail/gmail_tools.py +116/-21, with
    no tool added or removed. A label update no longer resets the two visibility settings to their
    create defaults, an unresolvable thread_id on a draft is now an error instead of a silently
    orphaned draft, and emails wrapped inside an email (message/rfc822) are rendered by the read
    tools, capped in count and depth. That renderer labels the wrapped headers as claimed by the
    attachment and unverified — the closest the repository comes to the subject of the
    sender-authentication cell, and it still verifies none, so the cell stands. The four files the
    least-privilege and token-encryption cells rest on are absent from the diff; the one
    permissions-named test file that grew carries file-limit validation, as on 18 September. The
    repository-wide claims were checked at the HEAD in a full checkout: no output_schema, no
    snooze, no Authentication-Results, no dmarc, GMAIL_METADATA_HEADERS still ends at
    List-Id, fourteen @server.tool decorators counted again. The other four columns are unmoved
    upstream and keep their verified dates — settled on 22 September as the rule for this file: the
    date is the day the cells were read, and an unchanged HEAD is not a reading.
  • get_thread says that full: false keeps authentication — it always did, and the
    description never said so. It listed what the sparse fetch drops (bodies, attachment metadata)
    and elsewhere that every message carries authentication; that both hold at once was left to be
    discovered. A mailbox session discovered it and now relies on it, because it makes an authenticity
    check cost no body. A test pins it, so what was an observation about the current build is a
    promise the next one has to keep — and the way it could break is specific: the sparse path strips
    fields by name, so the risk is a name added to that list, not a change to authentication itself.
  • Comparison table: the taylorwilsdon column brought forward again (d80ddd92, v1.27.1, 11
    commits on), two days after the last round, and this time the diff is not empty where it counts:
    auth/permissions.py and auth/scopes.py both moved, which are the two files the least-privilege
    cell rests on. The change is confined to the chat service — a new chat.memberships.readonly
    scope and contacts.readonly added to chat's readonly level and to the read-only scope map —
    while the gmail entry in the level table is unchanged and core/tool_registry.py does not
    appear in the diff at all. No cell moves. Worth recording anyway, because it is the same caveat
    our cell already carries, now visible in his code: a level named readonly pulls in a scope of a
    different API, so the level names the intent of the service, not the reach of the grant. gmail/
    is absent from the diff; the repository-wide claims were checked at the HEAD in a full checkout,
    because a diff cannot carry them — no output_schema, no snooze, no Authentication-Results,
    no dmarc, GMAIL_METADATA_HEADERS still ends at List-Id, and the fourteen @server.tool
    decorators counted again at this revision rather than carried forward from the correction two
    days ago. The other four columns are unmoved upstream, and their verified dates deliberately
    stay where they are: a HEAD comparison shows that nothing changed, which is not the same as
    having read anything.
  • Comparison table: the taylorwilsdon column is current again (e844d805, v1.27.0, 16
    commits on), brought forward by diff. No cell moves, and this round the Gmail half of the diff is
    empty in the strongest sense: gmail/ does not appear in the changed-file list at all, and
    neither do auth/permissions.py, auth/scopes.py, core/tool_registry.py or
    auth/credential_store.py. The weight of the round is Google Docs — a plain-text export path,
    document links, markdown — which is outside this table. The one auth file that moved drops the
    mirroring of GOOGLE_* into FastMCP's provider environment: bootstrap, not a scope map, despite
    the scope-sounding name of the test that shrank with it. The two repository-wide claims were
    checked at the HEAD instead of through the diff, because a diff cannot carry them: output_schema
    still has no occurrence anywhere, and neither do snooze, Authentication-Results or dmarc.
  • Said plainly what 0.22.0's call-time scope check does and does not do: it explains a refusal,
    it does not prevent one. The request goes to Google and comes back rejected; what changed is that
    the answer then names the gap instead of listing every scope that could be missing. A gate that
    blocked the call up front would need each method's required scopes, not the tier's — a different
    and heavier feature. Prompted by csitte.at, who also corrected a claim we made about
    aaronsb/google-workspace-mcp when reporting this release: we implied his call-time check reads
    the scopes from Google while ours reads the token. It does not — accountAccess in
    src/factory/safety.ts takes them from readCredential(ctx.account), written once at consent,
    and tokeninfo appears nowhere in his repository. Our own docs/comparison-sources.json has
    said "read from the credential file" since 8 September; the claim contradicted a check this
    project had already done and written down. So the remaining difference is order, not source: his
    check blocks before the request, ours explains after it, and a right revoked at Google surfaces
    as a 403 for both. 📌 The notes exist so a claim can be looked up instead of reconstructed —
    reconstructing one anyway is how a checked fact turns back into a guess.

Fixed

  • A tool count of ours was wrong for eighteen days: docs/comparison-sources.json recorded
    sixteen Gmail tools for taylorwilsdon/google_workspace_mcp from 31 August on, most recently as
    “the count of sixteen stands” on 17 September. It is fourteen, and it was fourteen at every
    revision we had recorded — counted at cce49e11, 54b1c56 and fb89c818, not inferred from
    today's. The miscount therefore sits in the single round that read the file end to end, and the
    three diff rounds after it carried the number forward unexamined, because a diff can only say
    that a number did not change. No cell moves: the table never named a count, which is why nothing
    caught it. The note states the correction where the wrong number stood rather than quietly
    replacing it.
  • The npm wait added for the registry race was itself too short, and asked the wrong question.
    It fired for the first time on 0.22.0 and failed the run: npm publish succeeded at 20:37:35,
    the thirty 10-second attempts ran out at 20:42:44, and npm records the version as published at
    20:43:42 — just over six minutes, against a five-minute budget. For 0.21.0 the same wait
    would have cleared in 30 seconds, so the delay is npm's queue and not a constant; the budget is
    now ten minutes and still no guarantee. The second half matters more: at 20:43:58 the two npm
    views disagreed — /mailwarden/0.22.0 answered 200 while the package document did not list the
    version and dist-tags.latest still said 0.21.0, catching up only by 20:45:39. npm install
    reads the package document
    , so a wait that checks the version endpoint alone can go green while
    the version is still uninstallable. Both are checked now and must agree, which also makes a
    re-run dispatched on a superseded tag wait rather than register it. Parsed with node instead
    of jq so the loop can be run where the repository is developed: this step only fires on a tag,
    so the main-branch CI never exercises it, and a step only CI can run is a step only CI can debug.
    All three cases were run against the live registry before committing.
    📌 A retry budget is a measurement, not a guess — and the first thing to check about a wait is
    whether it is waiting for the right signal.

Security

  • Four advisories in the runtime tree closed by moving @modelcontextprotocol/sdk 1.30.0 to 1.32.1.
    Dependabot flagged two of them on the 10 October push and npm audit --omit=dev showed four, all
    reached through the SDK: proxy-addr 2.0.7 (critical, IP spoofing through an IPv4-mapped IPv6 trust
    subnet, GHSA-jqcg-44mw-7w3h), the SDK itself at 1.12.0 to 1.30.1 (high, its OAuth client could be
    steered to send credentials to an authorization server the MCP server names, GHSA-6qxp-vccf-f47h),
    fast-uri 3.1.7 (moderate, host case normalisation, GHSA-hrr3-gc8f-f4qj) and ip-address 10.4.0
    (moderate, four subnet and link-local findings, GHSA-2vr4-cq9g-pvrc and others). npm audit fix
    resolved all of them inside the declared ^1.30.0 range, so only the lockfile moves: the SDK to
    1.32.1, proxy-addr to 2.0.8, fast-uri to 3.1.8, ip-address to 10.7.3 and source-map-js to
    1.2.2. Two of the findings are worth placing: mailwarden is an MCP server and never instantiates
    the SDK's OAuth client, so the high one had no path here; and proxy-addr is Express's
    trust-proxy resolver, which only acts once Express is told to trust a proxy, and --http never
    sets that. Build, the full test suite and npm run smoke pass on the new tree; at the time of this
    entry the runtime dependency tree carries no known advisory. As before: an audit result is a date,
    not a property.