when-free
Reads a Google Calendar via its secret iCal address to compute free availability slots, respecting busy events, buffers, and recurrence.
Reads an iCloud calendar via its public/ICS link so its events are included when calculating free availability.
Can be configured to use a local Ollama model to extract days and time windows from a message for availability queries, falling back to pattern matching if extraction fails.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@when-freecheck my calendar and tell me when I'm free this Thursday between 10am and 4pm"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
when-free
Which days and times am I free? Someone asks for your availability. This reads your calendars and prints the slots you can offer, ready to paste into the reply.
A recruiter writes: "Please share your availability for Thursday 1st, Friday 2nd, Monday 5th, Tuesday 6th or Wednesday 7th, between 10:00am and 4:00pm." You copy the message and run one command:
$ pbpaste | whenfree --message -
Free between 10:00 and 16:00 (Europe/London), slots of 60+ minutes, 15-minute buffer around events:
- Thu 1 Oct: 12:15–16:00
- Fri 2 Oct: 10:15–13:45
- Mon 5 Oct: 10:00–14:45
- Tue 6 Oct: 10:00–14:45
- Wed 7 Oct: 10:15–12:00, 13:00–16:00
Dates and hours were read from the message by pattern matching. Check them against the message.The five lines go to standard output and everything else to standard error, so whenfree ... | pbcopy copies exactly what you paste into your answer.
It runs on your own machine. It needs no account, no API key and no Google Cloud project, has no dependencies beyond Python, and sends nothing anywhere.
Install
Python 3.11 or newer.
pipx install git+https://github.com/YauhenBichel/when-free # or: uv tool install git+https://github.com/YauhenBichel/when-freeRelated MCP server: Chronary
Set up
Copy the private iCal address of your calendar, then run:
$ whenfree add
Where the private address of a calendar is:
Google Calendar: Settings, your calendar, Integrate calendar, "Secret address in iCal format"
Outlook: Settings, Calendar, Shared calendars, Publish a calendar, the ICS link
iCloud: Calendar, the share icon next to the calendar, Public Calendar
Paste the address and press Enter (it is not shown):
Added 'personal': 412 events, 9 block time in the next 14 days.
Saved in /Users/you/.config/when-free/config.toml, readable only by you. Now run: whenfreeIt reads the calendar first and saves it only if that worked. The address is not shown as you paste it and is never printed afterwards. On macOS, pbpaste | whenfree add takes it straight from the clipboard.
Calendar | Where the address is |
Google Calendar | Settings → your calendar → Integrate calendar → Secret address in iCal format |
Outlook | Settings → Calendar → Shared calendars → Publish a calendar → the ICS link |
iCloud | Calendar → the share icon next to the calendar → Public Calendar. A |
Anything else | Any |
Google Calendar, step by step
Do this in a browser. The phone app does not show the address.
Open Google Calendar settings: the gear icon, then Settings.
In the left column, under Settings for my calendars, click the calendar you want. The one with your own name is where invitations arrive.
Scroll down to Integrate calendar and copy Secret address in iCal format. It ends in
basic.ics. Do not take "Public address in iCal format": that one only works for a calendar you have made public.Run
whenfree addand paste it when asked.
Give the address to whenfree add and to nothing else: not to a chat with an assistant, not to a shell command (--calendar with an address stays in your shell history), not to a repository.
More calendars, and doing it by hand
A slot is free only if it is free in every calendar you add.
whenfree add --name work # a second calendar: asks for its address
whenfree add ~/calendars/family.ics # an exported file instead of an address
whenfree init # only creates the settings file, for you to editThe settings file is ~/.config/when-free/config.toml, with one [[calendar]] block per calendar: a name, and a url or a path. whenfree add writes those blocks and leaves the rest of the file as it is.
At any time, check that every calendar can be read:
$ whenfree check
Settings: /Users/you/.config/when-free/config.toml time zone: Europe/London
ok personal: 412 events, 9 block time in the next 14 daysIf it does not work
What you see | What to do |
| Run |
| The address is incomplete or is not the secret one; the message says which when it can tell. Copy it again with the copy button |
| The address is not a feed. It should end in |
|
|
There is no "Secret address" in Google's settings | Work and school accounts can have it switched off by the administrator. Export the calendar instead (Settings → Import & export → Export), unzip it, and run |
An event you just added is missing | The feed is refreshed with a delay. See Limits |
| Run |
The address is a password. Anyone who has it can read that calendar. whenfree never prints it, whenfree add does not show it as you paste, the settings file is readable only by you, and an error names the calendar, not its address. If the address leaks, reset it in your calendar's settings.
Use
whenfree # the next 7 working days
whenfree --from 2026-10-05 --to 2026-10-09 # a range
whenfree --days "Thu 1 Oct, Fri 2 Oct, Mon 5 Oct" # specific days
whenfree --hours 10:00-16:00 --min 90 # only 90-minute slots, in part of the day
whenfree --message invite.txt # days and hours from a message saved to a file
pbpaste | whenfree --message - # the same, from the clipboard (macOS)
whenfree --busy # also show what blocks each day
whenfree --format json # for scripts
whenfree --calendar ~/Downloads/calendar.ics # one calendar, without a settings fileReading a message
--message reads the days and the daily window out of ordinary text by pattern matching. It understands explicit dates in the ways people write them:
Thu 1 Oct · Thursday the 1st of October · Monday, October 5th · 5 October · 2026-10-05 · Wednesday 30th
and hours such as between 10:00am and 4:00pm, 10:00–16:00, 9-5pm, 2 to 4 pm.
A bare "Wednesday 30th" is read as the date nearest to today that is both a Wednesday and a 30th, so a message from last week resolves to last week. Days already past are left out of the answer and named on standard error, never dropped silently.
It does not read "next week" or "any afternoon". For those, give the days yourself with --days or --from and --to. It always says how the dates were read, because you should check them against the message before you reply.
when-free does not connect to your mailbox. You paste the message, pipe it in, or save it to a file.
Reading a message with your own model
If you run a language model yourself, it can do the reading instead. Add to the settings file:
[extract]
command = ["ollama", "run", "llama3.2"] # the prompt is sent on standard input
# command = ["my-cli", "ask", "{prompt}"] # or placed where {prompt} isThe command receives the message and must print JSON: {"days": ["2026-10-05"], "hours": "10:00-16:00", "minutes": 60}. If it fails or prints something else, pattern matching is used. Nothing runs unless you configure it, and --no-extract skips it for one run. The message is sent to whatever that command talks to, so use a local model for private text.
Use it from an assistant or an agent
The same answer is available to programs in three ways. All of them read only, return the same data, and leave event titles out unless asked.
As an MCP server
whenfree mcp is a Model Context Protocol server on standard input and output, with no extra install.
claude mcp add when-free -- whenfree mcp # Claude CodeFor Claude Desktop, Cursor and other MCP clients, add it to their server list:
{
"mcpServers": {
"when-free": { "command": "whenfree", "args": ["mcp"] }
}
}Then ask in ordinary words: "Here is the recruiter's message. Which of those times can I do?" The assistant calls free_slots and answers from your real calendar.
Tool | What it does |
| Free time ranges per day. Takes |
| Whether each calendar can be read, with event counts. No details, no addresses |
A tool that cannot do its job returns an error the assistant can read ("could not read the calendar 'work'"), never a guess.
From a function-calling harness
If your harness registers tools from JSON schemas and runs commands, it needs two things:
whenfree schema # the tool definitions: name, description, inputSchema
whenfree schema --format openai # the same, as {"type": "function", "function": {...}}
whenfree call free_slots --args '{"days": "2026-10-05, 2026-10-06", "hours": "10:00-16:00"}'whenfree call prints one JSON object: {"ok": true, "text": "...", "data": {...}}, or {"ok": false, "error": "..."} with exit code 1. The arguments can also come on standard input.
From Python
from whenfree import api
result = api.find_free(api.Query(days="2026-10-05, 2026-10-06", hours="10:00-16:00", min_minutes=45))
for day in result["days"]:
print(day["label"], day["free"]) # Mon 5 Oct [['10:00', '14:45']]find_free returns plain dicts and lists, ready for json.dumps, and raises api.Problem with a message that is safe to show.
What an agent can and cannot see
Free time: yes. That is the point.
Event titles: only on request.
include_busyis off by default, and its description tells the model that titles are private.Calendar addresses: never. They are not in any tool result or error.
Changing anything: no. There is no tool that writes.
What counts as busy
In your calendar | Blocks time? |
An ordinary event | Yes, plus the buffer on both sides |
An event marked Free | No |
A cancelled event | No |
An invitation you declined (your address in | No |
A whole-day event | No, unless |
A repeating event | Each occurrence, with skipped dates skipped and moved occurrences where they were moved to |
A slot shorter than min_minutes after the buffers are applied is not offered. For today, the day starts at the next quarter hour, not in the past.
Settings
All optional except a calendar. Command-line flags win over the file.
Setting | Default | Flag | Meaning |
| your system's |
| The zone the answer is given in, like |
|
|
| The part of the day you offer |
|
|
| Shortest slot worth offering |
|
|
| Kept free before and after every event |
|
|
| Include Saturday and Sunday in a date range |
|
|
| Whole-day events block the day |
|
| Working days shown when you give no dates | |
|
| Your addresses, so declined invitations do not block | |
|
|
| |
| none |
| A command that reads a message with your own model |
Environment variables, for scripts and containers: WHENFREE_CONFIG (path to the settings file), WHENFREE_CALENDARS (comma-separated addresses or paths, replacing the file's list), WHENFREE_TZ.
Limits, stated plainly
It reads; it does not book. It never writes to a calendar and never sends a message.
It is as fresh as the feed. Google refreshes the secret iCal address with some delay, so an event added a minute ago may not be there yet. Run
whenfree --busyto see what it saw.Recurrence rules. Daily, weekly, monthly (by date, or "first Monday", "last Friday") and yearly rules are expanded, with intervals, end dates, counts, skipped dates and moved occurrences. Rarer rules (
BYSETPOS, week numbers) are not. For those the first date is blocked and a note says so. Nothing is guessed.Every calendar or none. If one calendar cannot be read, no slots are printed, because busy time would look free.
Time zones come from the events. A zone name the tz database does not know (some Outlook exports) is read in your own zone.
Development
uv run --with pytest pytest -q # under a second; no networkThe tests use a small calendar in tests/data/sample.ics and a fixed clock (WHENFREE_NOW). See CONTRIBUTING.md.
Licence
Apache-2.0.
This server cannot be deployed
Maintenance
Related MCP Connectors
Calendar API for AI agents: events, availability, Google/Microsoft setup, scheduling, and iCal.
AI-native scheduling and booking: check availability, book meetings, share links.
GDPR-compliant calendar access for AI assistants: read, create, edit, RSVP. Google, MS 365, Apple.
Scheduling infrastructure for AI agents across Google and Microsoft calendars.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to search and access public calendar data, supporting iCal, CalDAV, and Google Calendar sources with event search, details, and availability checks.-
- AlicenseAqualityAmaintenanceCalendar API purpose-built for AI agents. Exposes tools to manage agents, calendars, and events, find meeting times, run scheduling proposals, set availability rules, manage webhooks, and subscribe to iCal feeds.5465 npmApache 2.0
- AlicenseNot gradedqualityCmaintenanceProvides read-only access to calendar events from iCal feeds, enabling agents to query schedules, search events, and check availability via natural language.AGPL 3.0
- FlicenseNot gradedqualityCmaintenanceEnables natural language interaction with Google Calendar through AI agent tools for scheduling, event management, and availability checking.-