Tasks & calendar MCP server
When answering 401 is the bug
One endpoint that lets an assistant read and write Google Tasks and Calendar, hardened against a specific client's probing behaviour.
- Year
- 2026
- tools live
- 35
- Stack
- Python, FastMCP, Google APIs
- Status
- Live
The problem
Google Tasks and Google Calendar are separate APIs with separate quirks. An assistant asked to plan a day has to call both, reconcile them, and understand which of the two owns a given thing.
Exposing the raw endpoints and hoping the model composes them correctly does not work reliably. So the server exposes 35 tools shaped like the questions people actually ask: give me my day, find me a free slot, reschedule everything overdue.
The constraint
The machine is an always-free ARM instance with two cores and one publicly reachable port, which is SSH. Nothing else can be opened, so the service binds loopback and is published through an outbound-only tunnel. There is no inbound port to attack and no firewall rule to maintain.
SELinux is enforcing, which quietly rules out a whole class of layouts: a unit rooted in a home directory cannot read its own environment file. Everything installs under /opt and gets relabelled.
The decision that took longest to find
The client probes the endpoint without the headers it has been configured with. Answer 401 to that probe and it concludes the server wants a full interactive sign-in flow, then fails with a sign-in error that has nothing to do with the real problem.
It answers 400 instead. A missing or wrong key gets a bad request, not an unauthorised. Omitting the challenge header was not sufficient; the status code alone was the trigger. The well-known discovery paths answer 404 before the auth check runs, for the same reason.
This is the kind of thing that is invisible in documentation and obvious only from the failure.
Three Google behaviours worth writing down
The Tasks due-date filter is date-exclusive, so the obvious range query silently drops the last day.
An all-day calendar event's end date is also exclusive, which means a one-day event ends tomorrow.
Availability has to be computed in the calendar's own timezone rather than the server's, which is why the timezone database is pinned as a dependency rather than inherited from the host.
The result
Live and in daily use, with 35 tools across tasks and calendar, on a machine that costs nothing to run.
At a glance
- Tools
- 35
- Runs on
- 2 cores, 10 GiB, ARM
- Exposure
- Outbound tunnel only
- Auth
- Key on every request