
gcalc-mcp: A Safe MCP Server That Gives Your AI Assistant Google Calendar
- Published on
- • 5 mins read•--- views

Every time I wanted my AI assistant to actually do something with my calendar — not just talk about it — I hit the same wall. OAuth dances, API client libraries, token refresh, scope juggling. A lot of plumbing before you read a single event. So I built gcalc-mcp: a small Model Context Protocol server, written in Go, that wraps the battle-tested gcalcli CLI and exposes Google Calendar as a tidy set of MCP tools over stdio.
The goal was simple. Letting an AI touch your calendar is usually a mess. gcalc-mcp exists to make it boring and safe.
Why Wrap a CLI Instead of Calling the API
The temptation with an MCP server is to reach straight for the Google Calendar API. I deliberately didn't. gcalcli already solved the hard parts — OAuth2, token storage, timezone handling — years ago, and millions of cron jobs trust it. Reusing it means the server calls no Google APIs itself and stores no credentials. You run gcalcli init once, and the server inherits that session.
The whole design comes down to a few problems and the guardrails I picked to solve each one:
| The problem | How gcalc-mcp solves it |
|---|---|
| Connecting an LLM to Google Calendar is a plumbing nightmare. | Reuses gcalcli's existing OAuth. Run gcalcli init once; the server inherits the session. No credentials stored. |
| You don't want a model running arbitrary shell commands. | The runner accepts a hardcoded allowlist of subcommands. Model input is only ever passed as flag values — never as command tokens. Unknown subcommands are rejected before exec. |
| CLI output is built for humans, not machines. | Forces --json wherever supported, and injects --nocolor --lineart ascii everywhere else for clean, deterministic text. |
| One wrong call could nuke your calendar. | Deletion is gated behind an explicit confirm: true. Without it the tool refuses and explains why — no accidental wipes. |
| Interactive prompts hang a stdio server. | TTY-driven flows (init, edit, remind) are intentionally not exposed. Every tool is non-interactive. |
| Dates are annoying. | Natural language passes straight through: today, tomorrow 3pm, next monday, 2026-06-21. |
The Toolbox
The server ships 12 tools, split between reading and writing. Everything destructive is opt-in.
| Tool | What it does |
|---|---|
gcal_auth_status | Confirm setup before anything else. |
gcal_list_calendars | List the calendars on the account. |
gcal_agenda | Agenda view over a date range. |
gcal_search | Search events by text. |
gcal_calw | Week-based calendar grid. |
gcal_calm | Month calendar grid. |
gcal_updates | Recently changed events. |
gcal_conflicts | Find overlapping events. |
gcal_quick_add | Quick-add an event from a natural-language phrase. |
gcal_add_event | Add a detailed event (title / when / where / duration / attendees). |
gcal_import | Import ICS/vCal from a file path or raw content. |
gcal_delete | Destructive — deletes every matching event. Requires confirm: true. |
Tip: call
gcal_auth_statusfirst to confirm the session is alive before anything else.
Setup
Prerequisites
You need gcalcli authenticated once. The server piggybacks on that session.
pipx install gcalcli # or: brew install gcalcli
gcalcli init # one-time OAuth2 browser flow
Build and install
The server is a single Go binary (Go 1.23+).
make build # -> bin/gcalc-mcp
make install # -> /usr/local/bin/gcalc-mcp (sudo)
Wire it into your MCP client
Add a stdio server entry to your client's MCP config — Claude Desktop, Claude Code, or anything else that speaks MCP:
{
"mcpServers": {
"gcalc": {
"type": "stdio",
"command": "/usr/local/bin/gcalc-mcp"
}
}
}
To target a non-default gcalcli config directory, set GCALCLI_CONFIG in the server's environment. That's it — restart the client and your assistant can read and book events.
Safety by Design
This is the part I cared about most. Handing a language model a shell is a classic injection footgun, so the runner never lets model output become a command. Subcommands come from a fixed allowlist; user and model input is only ever bound to flag values or positional arguments, and unknown subcommands are rejected before anything is executed.
Deletion deserves a special mention. gcal_delete removes every matching event, so it refuses to run without confirm: true and explains why. The model has to deliberately ask for destruction — it can't stumble into it. Combined with not exposing interactive commands at all, the failure modes are narrow and predictable.
Why I Think This Pattern Matters
Most MCP servers I see either reimplement an entire API surface or hand the model a raw shell. The middle path — wrap a mature CLI, force machine-readable output, and put a thin allowlist between the model and the system — gets you a useful integration in a few hundred lines without inheriting a new attack surface. gcalcli does the heavy lifting; gcalc-mcp just makes it speak MCP, safely.
The project is MIT-licensed and open on GitHub: github.com/artschekoff/gcalc-mcp. Issues, ideas, and pull requests are welcome — and if you build your own MCP servers around existing CLIs, I'd love to compare notes.
Open for contract collaboration
I am available for contract-based collaboration. If you have an interesting project idea, schedule a call via Calendly.
Schedule a 30-min call