mcpField
W
when-free

when-free

本地服务
github.com生产力03 周前更新

关于 when-free

when-free 是面向会议协调的本地空闲时间工具,可读取日历订阅或 ICS 文件,并根据指定日期和时段生成空闲时间,也能解析粘贴的邀约内容。

主要功能

  1. 01读取 Google、Outlook 和 iCloud 日历
  2. 02合并多个日历
  3. 03从消息中提取日期和时段
  4. 04按时长与时区筛选时段
  5. 05提供 CLI、MCP 和 HTTP 接口

适用场景

  1. 01回复会议邀约并提供可用时间
  2. 02汇总工作与个人日历的空档
  3. 03为脚本输出 JSON 空闲时间
  4. 04查看其他时区的可用时段

MCP 相关

MCP 服务向客户端提供 `free_slots` 和 `check_calendars`,基于已配置日历返回空闲时间和日历读取状态。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"yauhenbichel-when-free": {
"command": "uvx",
"args": [
"when-free"
],
"env": {
"WHENFREE_CALENDARS": "<WHENFREE_CALENDARS>",
"WHENFREE_TZ": "<WHENFREE_TZ>"
}
}
}
}
启动命令uvx when-free
需要配置的环境变量
WHENFREE_CALENDARS密钥Private iCal addresses of your calendars, comma-separated. Optional if you saved them with `whenfree add`.
WHENFREE_TZTime zone for answers, like Europe/London. Default: the computer's.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypiwhen-free0.4.1

工具

3

收录的工具列表 · 更新于 2 周前

参数类型必填说明
tostring否日期范围的最后一天,YYYY-MM-DD。
daysstring否指定日期,使用逗号分隔。ISO 日期最可靠:"2026-10-05, 2026-10-06"。也支持:"Thu 1 Oct, Fri 2 Oct"、"tomorrow"、"next week"。
fromstring否日期范围的第一天,YYYY-MM-DD。默认值:今天。
hoursstring否提供时段的部分,格式为 HH:MM-HH:MM,例如 "10:00-16:00",或 "morning"、"afternoon"、"evening"。默认值:用户配置的时段。
messagestring否用于读取建议日期和时间的文本,例如询问可用时间的邮件。会读取明确日期;如果没有明确日期,则读取相对于今天的表达:"tomorrow"、"Thursday"、"next Tuesday"、"next week"、"any afternoon"。
timezonestring否答案使用的时区,例如 "Europe/London"。默认值:用户的时区。
weekendsboolean否在时间范围中包含周六和周日。默认值:false。
min_minutesinteger否返回时段的最短时长,单位为分钟。将其设为会议时长。默认值:用户的设置,通常为 60。
all_day_busyboolean否将全天事件视为忙碌。默认值:false。
include_busyboolean否同时返回每天占用时间的事件及其标题。标题属于隐私信息:仅在用户想查看时询问。默认值:false。
buffer_minutesinteger否每个事件前后预留的空闲时间。默认值:用户的设置,通常为 15。

详细介绍

when-free

有人问你什么时候有空。它会读取你的日历,并给出答案,让你可以直接粘贴。

tests PyPI License: Apache-2.0

输入招聘人员的消息,即可获取这些日期的空闲时段

  • 粘贴消息,获取空闲时段。 它能读懂“下周二或周三,上午 10 点到下午 4 点”,并针对指定日期和时段给出答案。
  • 支持 Google、Outlook 和 iCloud 日历,也支持任何 .ics 文件。还可以同时使用多个日历。
  • 保护隐私。 它在你的电脑上运行,无需账号或 API 密钥,也不会修改你的日历。
  • 也能通过 AI 工具使用: Claude、Cursor 和其他 MCP 客户端、Open WebUI、n8n、Shortcuts、脚本。
  • 也能在其他设备上使用: 手机、平板和手表、Home Assistant(以及通过它使用 Alexa、Google Home、Siri),还有 macOS、Linux 和 Windows 的菜单栏或状态栏。

目录

立即试用 · 添加日历 · 日常使用 · 解析消息 · 通过其他工具使用 · 其他设备 · 隐私 · 设置 · 忙碌时段的判定方式 · 限制 · 问题排查 · 收录位置

一分钟内试用

使用 uv,无需预先安装:

uvx when-free demo

要保留它(需要 Python 3.11 或更高版本):

uv tool install when-free          # or: pipx install when-free
whenfree demo

在使用 Claude Desktop?可以跳过以上步骤:安装一键扩展。

whenfree demo 会生成下周的模拟日历,并据此回答招聘人员的消息。它不会读取你的任何数据,也不会保存任何内容:

  > Hi! Thanks for applying. Could you share a few times on Tuesday, Wednesday or Thursday next week,
  > between 10am and 4pm? The interview takes about an hour.

  Free between 10:00 and 16:00 (Europe/London), slots of 60+ minutes, 15-minute buffer around events:
  - Tue 13 Oct: 11:45–12:45, 14:15–16:00
  - Wed 14 Oct: 12:15–14:15
  - Thu 15 Oct: 13:15–14:45

添加日历

  1. 复制日历的私有地址。 这是一个以 .ics 结尾的链接:

    日历查找位置
    Google Calendar设置 → 点击左侧的日历 → 集成日历 → iCal 格式的私有地址
    Outlook设置 → 日历 → 共享日历 → 发布日历 → ICS 链接
    iCloud日历 → 点击日历旁的共享图标 → 公开日历(webcal:// 链接也可以)
    其他任意 .ics 链接,或导出的 .ics 文件
  2. 运行 whenfree add 并粘贴地址。 粘贴时不会显示该地址。系统会先检查,只有可用时才会保存:

    $ whenfree add
    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: whenfree
    
  3. 运行 whenfree。 你会看到接下来几个工作日的空闲时间。

在 Mac 上,pbpaste | whenfree add 可以直接从剪贴板读取地址。

请保密该地址。 它就像密码:任何拥有它的人都能读取该日历。只将它提供给 whenfree add,不要用于其他任何地方,例如聊天、Shell 命令或代码仓库。如果地址泄露,请在日历设置中重置。

Google Calendar 分步说明

请在浏览器中操作,因为手机应用不会显示该地址。

  1. 打开 Google Calendar 设置:点击齿轮图标,然后点击设置。
  2. 在左侧栏的我的日历的设置下,点击所需的日历。使用你自己姓名命名的日历就是接收邀请的日历。
  3. 向下滚动到集成日历,复制iCal 格式的私有地址。地址以 basic.ics 结尾。不要选择“iCal 格式的公开地址”,它只适用于你已设为公开的日历。
  4. 运行 whenfree add,并在出现提示时粘贴地址。

找不到“私有地址”?工作和学校账号可能已将其关闭。请改为导出日历(设置 → 导入和导出 → 导出),解压后运行 whenfree add ~/calendars/work.ics。导出的是快照,因此日历变更后需要重新导出。

添加多个日历

只有当你添加的每个日历都显示为空闲时,该时段才会被视为空闲。

whenfree add --name work                 # a second calendar: asks for its address
whenfree add ~/calendars/family.ics      # an exported file instead of an address
whenfree check                           # can every calendar be read?

这些日历保存在 ~/.config/when-free/config.toml 中,每个日历对应一个 [[calendar]] 块,其中包含 name 和 url 或 path。你可以手动编辑该文件。whenfree init 会创建一个空文件。

日常使用

需求运行
查看接下来几个工作日的空闲时间whenfree
查看消息中提到的日期和时间pbpaste | whenfree --message -(Mac 剪贴板)或 whenfree --message invite.txt
查看下周的下午时段whenfree --days "next week" --hours afternoon
指定日期whenfree --days "Thu 15 Oct, Fri 16 Oct"
指定日期范围whenfree --from 2026-10-12 --to 2026-10-16
仅显示足以安排 90 分钟会议的时段whenfree --min 90
查看每天的占用情况whenfree --busy
以其他时区显示结果whenfree --tz America/New_York
获取脚本所需的 JSONwhenfree --format json
复制结果whenfree ... | pbcopy(只会复制时段行)

只有时段行会输出到标准输出。上下文信息会输出到标准错误,因此使用 | pbcopy 时,复制的内容正好可以粘贴到回复中。

阅读消息

--message 可以从普通文本中找出日期和时间。它能理解:

  • 各种常见写法的日期: Thu 1 Oct · Thursday the 1st of October · Monday, October 5th · 5 October · 2026-10-05 · Wednesday 30th
  • 时间: between 10:00am and 4:00pm · 10:00–16:00 · 9-5pm · 2 to 4 pm
  • 未给出日期时使用的词语(这里假设今天是 10 月 1 日星期四):
写法解读为
today · tomorrow · the day after tomorrow10 月 1 日星期四 · 10 月 2 日星期五 · 10 月 3 日星期六
Thursday or Friday即将到来的周四和周五,包含今天:10 月 1 日星期四、10 月 2 日星期五
this Tuesday · next Tuesday本周的星期二(已经过去,因此会被排除并注明)· 下周的星期二,即 10 月 6 日星期二
next week · this week · the week after next对应周的工作日,从今天起算
Tuesday or Wednesday next week10 月 6 日星期二、10 月 7 日星期三
any afternoon · Friday morning · evening12:00–17:00 · 09:00–12:00 · 17:00–20:00,除非指定了具体时间

它总会告诉你如何解读日期,方便你对照消息进行核查。以下规则可以避免它猜测:

  • 明确写出的日期优先。 如果“Next week:”这样的标题下方写着“Wednesday 30th, Thursday 1st”,不会因此再加上整整一周。
  • **“Good morning”是问候语,**不是时间。
  • **已经过去的日期会被排除并注明,**绝不会悄悄忽略。“Wednesday 30th”指最近的一个日期为 30 日的星期三,因此,来自上周的消息会被解读为上周。
  • 相对日期以今天为准。 对于较早的消息,如果其中写着“tomorrow”,请使用 --days 手动提供日期。

when-free 不会连接你的邮箱。你可以粘贴消息、通过管道传入消息,或将消息保存到文件中。

让你自己的语言模型阅读消息

如果你自己运行模型,也可以让模型来阅读。将以下内容添加到设置文件中:

[extract]
command = ["ollama", "run", "llama3.2"]          # the prompt is sent on standard input
# command = ["my-cli", "ask", "{prompt}"]        # or placed where {prompt} is

该命令会接收消息,并且必须输出 JSON:{"days": ["2026-10-05"], "hours": "10:00-16:00", "minutes": 60}。如果失败,则会改用模式匹配。只有在你进行配置后才会运行;使用 --no-extract 可在单次运行中跳过它。消息会发送给该命令所连接的对象,因此处理私人文本时请使用本地模型。

在其他工具中使用

所有方式都会给出相同的答案,只进行读取,并且除非另有要求,否则不会包含事件标题。

你的工具用法设置
Claude Desktop一键扩展双击
Claude Code,搭配知道何时使用它的技能插件两条命令
Claude Code、Cursor、VS Code,以及任何 MCP 客户端MCP 服务一行
Raycast,右键点击消息(macOS)使用方法几分钟
Open WebUI、n8n、Shortcuts、Raycast,以及任何能调用 URL 的工具本地 HTTP 服务whenfree serve
使用函数调用的自有 AgentSchema 和调用两条命令
Python库import whenfree
Claude Code 插件

该插件将 MCP 服务与一项技能打包在一起,用来告诉 Claude 何时使用它:当你询问自己的空闲时间、粘贴询问何时可以见面的消息,或在它提出时间之前。

/plugin marketplace add YauhenBichel/when-free
/plugin install when-free@when-free

然后添加一次日历(如果你尚未添加):uvx when-free add

MCP:Claude、Cursor 和其他助手
claude mcp add when-free -- uvx when-free mcp          # Claude Code

对于 Cursor 和其他 MCP 客户端,将以下内容添加到其服务列表中。uvx 首次运行时会自动获取 when-free,因此无需安装:

{
  "mcpServers": {
    "when-free": { "command": "uvx", "args": ["when-free", "mcp"] }
  }
}

如果你使用 uv tool install 安装,请改用 "command": "whenfree", "args": ["mcp"]。它也已列入 MCP registry,标识为 io.github.YauhenBichel/when-free,并收录于 mcprush 和 M8ven Trust Index。

然后只需询问:“这是招聘人员发来的消息。我能参加其中哪些时间?”助手会调用 free_slots,并根据你的实际日历作答。

工具功能
free_slots每天的空闲时间。接受 days,或 from 和 to,或 message,以及 hours、min_minutes、buffer_minutes、timezone、weekends、all_day_busy、include_busy
check_calendars检查每个日历是否可读取,并提供事件数量。不包含详情或地址

如果工具无法完成任务,助手会收到可读的错误信息(“无法读取日历 ‘work’”),绝不会猜测。

HTTP:Open WebUI、n8n、Shortcuts 和脚本
whenfree serve                    # http://127.0.0.1:8765, only reachable from this computer
curl -H "Authorization: Bearer $(cat ~/.config/when-free/token)" \
  'http://127.0.0.1:8765/free_slots?days=next%20week&hours=afternoon'
  • 使用 JSON 请求体向 /free_slots 发送 POST 请求,或使用带查询字符串的 GET 请求。/check_calendars 也相同。
  • 返回内容为 {"ok": true, "text": "...", "data": {...}},或状态码为 400 的 {"ok": false, "error": "..."}。
  • /openapi.json 提供 OpenAPI 描述。要设置 Open WebUI、n8n 或 Shortcuts,请参阅配置指南。
  • 令牌会在首次运行时创建,并保存在 ~/.config/when-free/token 中,只有你能读取。也可以通过 WHENFREE_TOKEN 设置自己的令牌。每次工具调用都需要令牌。
  • 发往此计算机以外任何主机的请求都会被拒绝。网页只能从你允许的来源调用它:whenfree serve --allow-origin http://localhost:3000。使用 --host 0.0.0.0 时,网络中的其他设备也可以访问;此时只有令牌提供保护,因此仅在你信任的网络中使用。
函数调用执行环境
whenfree schema                    # the tool definitions: name, description, inputSchema
whenfree schema --format openai    # the same, as {"type": "function", "function": {...}}
whenfree call free_slots --args '{"days": "next week", "hours": "10:00-16:00"}'

whenfree call 会输出 {"ok": true, "text": "...", "data": {...}},或 {"ok": false, "error": "..."},并以退出代码 1 结束。参数也可以通过标准输入传入。

Python
from whenfree import api

result = api.find_free(api.Query(days="next week", hours="10:00-16:00", min_minutes=45))
for day in result["days"]:
    print(day["label"], day["free"])          # for example: Mon 12 Oct [['10:00', '11:45'], ['13:15', '16:00']]

find_free 返回普通的字典和列表,可直接用于 json.dumps;遇到问题时会抛出 api.Problem,其中的消息可安全显示。传入 reader= 可自行提供日历文本,而不从日历中获取。

在你的其他设备上

设备开始使用指南
iPhone、iPad、Android、手表whenfree serve --lan,然后运行 whenfree devices add "My phone" 并扫描二维码手机和平板
Home Assistant、Alexa、Google Home、Siri、忙碌指示灯whenfree mqtt --broker mqtt://homeassistant.local智能家居和语音
macOS 菜单栏、Waybar、i3blocks、Polybar、GNOME、Windows 托盘whenfree now --format xbar(或 waybar、i3blocks 等)桌面状态栏
$ whenfree now
Busy until 15:30 · next free 15:45–17:00

设备可以查看你是否有空以及你的空闲时段,但看不到事件标题。每部手机都有独立的令牌,你可以撤销令牌;无法读取的日历会显示为“未知”,绝不会显示为“空闲”。

隐私

  • 它在你的电脑上运行。 它只会获取你的日历订阅源,不会获取其他内容。它从不写入日历,也从不发送消息。
  • 你的日历地址会保密。 地址不会被打印出来,你粘贴时也不会显示,而且会存储在只有你能读取的文件中。错误信息会指出日历名称(“个人”),而不会显示其地址。
  • 助手只能看到空闲时间,看不到你的事件。 除非助手使用 include_busy 请求,否则不会包含事件标题;该选项的说明也会告知模型标题属于隐私信息。没有任何工具可以添加、更改或删除内容,也没有工具会接收日历地址;日历地址只会从你这里传给 whenfree add。

设置

除日历外,所有设置都是可选的。设置存放在 ~/.config/when-free/config.toml 中,命令行标志的优先级高于文件中的设置。

设置默认值标志含义
timezone你系统的时区--tz答案使用的时区,例如 Europe/London
hours09:00-18:00--hours你提供的时段。也可以是 morning、afternoon、evening
min_minutes60--min值得提供的最短时段
buffer_minutes15--buffer每个事件前后预留的空闲时间
weekendsfalse--weekends在日期范围内包含星期六和星期日
all_day_busyfalse--all-day-busy全天事件会占用整天
days_ahead7未指定日期时显示的工作日数量
me[]你的电子邮件地址,这样你拒绝的邀请就不会占用时间
[[calendar]]--calendarname 和 url 或 path。每个日历使用一个区块;whenfree add 会写入这些内容
[extract] command无--no-extract使用你自己的模型读取消息的命令
环境变量

适用于脚本、容器和 MCP 客户端配置。全部为可选项。

变量含义
WHENFREE_CALENDARS以逗号分隔的日历地址或路径。替代文件中的列表。敏感信息:见下文
WHENFREE_CONFIG设置文件的路径
XDG_CONFIG_HOME未设置 WHENFREE_CONFIG 时查找设置文件的位置:$XDG_CONFIG_HOME/when-free/config.toml,否则为 ~/.config/when-free/config.toml
WHENFREE_TZ答案使用的时区。优先级高于文件中的 timezone
TZ你的系统时区;仅在以上两项都未指定时使用
WHENFREE_TOKENwhenfree serve 使用的令牌,用于替代令牌文件。敏感信息
WHENFREE_NOW用作“现在”的 ISO 日期时间,以便重现一次运行

WHENFREE_CALENDARS 中包含密码。 私密日历地址的持有者都能读取该日历。请像保护 API 密钥一样保护此变量:不要将其放入共享的 shell 配置文件、已提交的 MCP 配置、CI 日志和屏幕截图中。whenfree add 写入的设置文件只有你能读取,因此在可行时请改用该文件。

什么情况算作忙碌

日历中的内容是否占用时间?
普通事件是,且前后都会预留缓冲时间
标记为 空闲 的事件否
已取消的事件否
你拒绝的邀请(你的地址在 me 中)否
全天事件否,除非 all_day_busy = true。此时会占用时间,除非该事件标记为“空闲”
重复事件每次重复都占用时间;跳过的日期除外,移动过的事件则按移动后的时间计算

经过缓冲时间后,短于 min_minutes 的时段不会被提供。对于今天,计算从下一个 15 分钟刻度开始,而不是从过去的时间开始。

限制

  • 它只读取,不会预约。 它绝不会写入日历或发送消息。
  • 数据新鲜度取决于日历源。 Google 刷新私密地址需要一些时间,因此刚刚添加的事件可能不会显示。运行 whenfree --busy 查看它读取到的内容。
  • 要么读取所有日历,要么一个也不显示。 如果有一个日历无法读取,就不会显示任何时段,因为忙碌时间可能会被误认为空闲。
  • 重复事件。 支持展开每日、每周、每月(按日期或“第一个星期一”“最后一个星期五”)和每年的规则,以及间隔、结束日期、次数、跳过的日期和已移动的实例。不支持较少见的规则(BYSETPOS、周数)。对于这些规则,首个日期会被标记为占用,并附有说明。
  • 时区取自事件。如果时区数据库不认识某个时区名称(某些 Outlook 导出会出现这种情况),则会按你所在的时区读取。

如果遇到问题

你看到的内容处理方法
No calendar is configured运行 whenfree add。如果你手动编辑了文件,则 url 行仍为空,或文件尚未保存;消息会指出文件名
could not read the calendar 'personal' (...)地址不完整,或不是私密地址;如果能够判断,消息会说明原因。使用复制按钮重新复制
returned a web page, not a calendar 或 did not return iCalendar data该链接不是日历源。它应以 .ics 结尾
Nothing was savedwhenfree add 无法读取日历,因此没有任何更改。修正地址后重新运行
Google 设置中没有“私密地址”你的组织已将其关闭。请改为导出日历:参见Google Calendar,分步指南
刚添加的事件没有显示日历源刷新需要一些时间。参见限制
会议没有标记为占用运行 whenfree --busy 查看读取到的内容。已拒绝的邀请、标记为“空闲”的事件和全天事件不会标记为占用;参见哪些情况会被视为忙碌
whenfree check随时检查每个日历是否都能读取

收录位置

如果你在某处使用了它,或撰文介绍了它,请提交 issue,相关内容就会收录在这里。

开发

uv run --with pytest pytest -q        # a few seconds; no network

测试使用 tests/data/sample.ics 中的小型日历和固定时钟(WHENFREE_NOW)。代码按组件分组:

组件包含内容可使用
core/ical、slots、status、render、errors:事件、忙碌时间、空闲时段、当前状态和措辞。不负责输入或输出无
messages/dates、extract:从消息中读取的日期和小时core
settings/config:设置文件和环境变量无
calendars/sources:从地址获取或从文件读取的日历core、settings
api.pyfind_free、status、check_calendars:所有前端共用的唯一入口以上四项
agents/tools、mcp:提供给 AI Agent 的工具和 MCP 服务api、core
web/server:供工具和手机使用的 HTTP 前端api、agents、devices、calendars、core、settings
devices/widgets、pairing、feed、qr、mobile/:状态栏、手机配对、日历订阅源、手机页面api、core、settings
smarthome/mqtt、homeassistant:通过 Home Assistant 发现机制发布 MQTT 消息api、core
cli/main、demo:whenfree 命令任意组件

如果某个组件导入了不允许使用的组件,tests/test_structure.py 就会报错,从而确保领域代码不依赖任何前端。

请参阅 CONTRIBUTING.md。

许可证

Apache-2.0。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。