You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
calendars list takes a query: synced:, primary: and access_role: narrow the list, such as gcal personal calendars list synced:true.
commands info describes what each calendar command answers with, and --fields can pick from it.
Changed — may break callers
gcal search <words> is now gcal search events <query>, and gcal status is now gcal store status, the
shape the other WireCat tools use (max: search messages <query>, store status). The old paths are gone, with no
alias. Over MCP the command is search events, and its input is query (was words); a store search's next-page
step names query too. A partial sync's hint points at store status. The permission keys move with them: permissions.store.status and permissions.search.events; permissions.search still covers the search. A config
that still sets permissions.status is refused with config.invalid, which names gcal <profile> config unset permissions.status.
events list takes only the standard field query, and none of its own words.today, week, from:, to:, changed: and overlapping are refused. Write the window as fields: from:2026-11-02 to:2026-11-06 is ends_at>=2026-11-02 starts_at<=2026-11-06, and changed:2026-10-25 is changed_at>=2026-10-25; calendar: and person: stay. Over MCP the input is query ("ends_at>=2026-11-02"), no longer words. What callers lose:
today and week: write the dates. The query language has today but no word for a day ahead, so the end of a
window is an ISO date.
overlapping and the overlaps field: no standard term asks for events that clash, so the list no longer finds
them. Compare starts_at and ends_at of the listed events instead.
starts_at>=… is refused and names the ends_at>=… term to write instead: the store finds events by the moments
they span, so it can bound only the latest start and the earliest end. An event that ends exactly at the ends_at moment is left out.
Without a time term the list still starts at the beginning of today.
Deleting a whole Google calendar and giving it to another account are deny by default, as emptying one is. api.calendar.calendars.delete and api.calendar.calendars.transferOwnership were ask, which over MCP runs with no
question. Set either key to ask or allow to let it run.
session status and session end answer where no keyring can be reached and no sign-in file is kept. They
failed with oauth.not-saved. Now session status says state: missing with keyring: unreachable, and session end says ended: false with keyring: unreachable: this run can use no sign-in, so there is nothing to
show or end. A sign-in kept with --credential-storage file is read as before. session start and the commands that
call Google still fail with oauth.not-saved, which troubleshooting explains. A script
that took the failure to mean "keyring out of reach" reads keyring in the answer instead.
people list takes one event as event:<id>.gcal personal people list 42 is refused as a word with no
field; write people list event:42. Over MCP the input is query ("event:42"), no longer event.
search --backend server answers as the shared search does. Each event's found_by is ["server"] (was ["google"]). The refusals have new reasons: no synced calendar is search.no-calendars (was calendars.none-synced), --offline is search.offline (was offline.refused), --exact, --cursor or --newest is search.server-options, and --backend both is search.one-backend (was search.backend-both).
The calendar commands' help no longer lists --json and --jsonl under each command, and their MCP tools no
longer take json or jsonl inputs. The global --json and --jsonl work as before; an agent that sent jsonl: true was refused.
config show --bot and config set|unset --personal|--bot are hidden. gcal has no bots, so they had nothing
behind them. They are still accepted, so a script that passes one keeps working.
Fixed
Calls an agent sends to gcal mcp serve at the same time each get their own answer. Two calendar reads sent
together could answer one of them empty ({"item":{"value":null}}) or fail it with command.crashed, because the
calls shared which command was running and the store it had opened. Each call now keeps its own
(WC-ARCH-22).
An all-day event with no time zone of its own or of its calendar starts at midnight in your computer's time zone.
gcal read it as midnight UTC: on a computer in Madrid it started an hour late, and in New York on the evening before.
Such an event's timezone now names the zone it was read in, where it was null. An event or calendar that names
its zone is read in that zone, as before. An event already stored keeps its old start until Google sends it again,
after it changes
(WC-TIME-3).