Yandex Calendar plugin for Hermes Agent: list, create, update, RSVP, move, and delete events over CalDAV.
Project description
hermes-yandex-calendar
Let your Hermes Agent run your Yandex Calendar. "What's on my calendar next week?" — "Move the standup to Thursday and invite Ann." — "Decline the 4pm." The agent reads and writes real events on your real calendar, over CalDAV, with no third-party service in the middle.
- 📅 Seven tools, one toolset — list, create, update, RSVP, move between calendars, delete, and enumerate the calendars themselves.
- 🔒 You choose what it may touch — restrict it to specific calendars, and to
specific actions (
read,read,write, …). A disallowed action is not in the toolset at all. - 🛟 Careful with your data — recurrence rules, alarms, and properties this plugin does not model survive every edit; moves copy the resource byte for byte; an event is deleted only after its copy is safely in place.
- 🔑 App password, not your account password — scoped to CalDAV, revocable in one click.
Tested against Hermes 0.19.x, Python 3.11–3.13.
Quick start
# 1. Install into Hermes (alternatively: pip install hermes-yandex-calendar)
hermes plugins install akinfold/hermes-yandex-calendar --enable
# 2. Add your credentials — the app password comes from
# https://id.yandex.ru/security/app-passwords (scope: "Calendar (CalDAV)")
printf 'YANDEX_CALENDAR_LOGIN=%s\nYANDEX_CALENDAR_APP_PASSWORD=%s\n' \
'you@yandex.ru' 'your-app-password' >> ~/.hermes/.env
Then enable it in ~/.hermes/config.yaml (third-party plugins are off by default):
plugins:
enabled: [yandex_calendar]
That's it. Ask the agent "what do I have tomorrow?" and it will tell you.
Not ready to hand over write access? Add
YANDEX_CALENDAR_ACTIONS=readand it can only look — see Restricting what the agent can do.
The tools
Up to seven standalone tools, in the yandex_calendar toolset:
| Tool | Purpose |
|---|---|
yandex_calendar_list_calendars |
List the calendars the plugin can use (name + href). |
yandex_calendar_list_events |
List events in a time range (summary, start/end, location, description, attendees, busy status, and an href). |
yandex_calendar_create_event |
Create an event (summary, start, optional end/location/description/all-day, attendees, busy status, target calendar). |
yandex_calendar_update_event |
Edit an event by href: change fields, add/remove attendees, toggle busy/free. Recurrence rules and alarms are preserved. |
yandex_calendar_respond_event |
Respond to a meeting invitation — accept, decline, or tentatively accept. |
yandex_calendar_move_event |
Move an event to another calendar, contents intact. |
yandex_calendar_delete_event |
Delete an event by href. |
Yandex Calendar has no public REST API, so this plugin speaks CalDAV
(https://caldav.yandex.ru) directly — the same protocol Yandex's own docs point
third-party clients at. Nothing is proxied through anyone else's servers.
Multiple calendars
Every tool that reads or writes events takes an optional calendar argument — a
calendar name (as returned by yandex_calendar_list_calendars) or its href.
Omit it to use the default (first) calendar. update, move, and delete identify
the event by its href, which already encodes the calendar it lives in.
Restrict which calendars the plugin may touch with YANDEX_CALENDAR_CALENDARS; the
first one in that list becomes the default.
Configuration
| Env var | Required | Default | Meaning |
|---|---|---|---|
YANDEX_CALENDAR_LOGIN |
yes | — | Yandex login / email. |
YANDEX_CALENDAR_APP_PASSWORD |
yes | — | App password for CalDAV — an account password will not work. |
YANDEX_CALENDAR_BASE_URL |
no | https://caldav.yandex.ru |
Override for self-hosted / testing. |
YANDEX_CALENDAR_CALENDARS |
no | (all) | Comma-separated allow-list of calendar names, e.g. Work,Personal. The first is the default calendar. |
YANDEX_CALENDAR_ACTIONS |
no | (all) | Comma-separated allow-list of actions the agent may perform — see below. |
Credentials are read from the environment first, then from ~/.hermes/.env, so they
work in gateway and subprocess runs. Secret values are never logged.
Dates and times are ISO 8601 (2026-07-25 or 2026-07-25T14:00:00+03:00); a datetime
without an offset is treated as UTC.
Restricting what the agent can do
YANDEX_CALENDAR_ACTIONS decides which of the seven tools are registered at all.
A disallowed action is not merely refused at call time: the tool never appears in
the agent's toolset, so it cannot be invoked, and the model is not tempted to try.
Accepted values, comma-separated and case-insensitive — individual actions
(list_calendars, list_events, create_event, update_event, respond_event,
move_event, delete_event), full tool names (yandex_calendar_delete_event), or
the shorthands:
| Shorthand | Expands to |
|---|---|
read |
list_calendars, list_events |
write |
create_event, update_event, respond_event, move_event |
delete |
delete_event |
all |
everything (the default) |
# Look, but don't touch:
YANDEX_CALENDAR_ACTIONS=read
# Full scheduling, but the agent can never delete anything:
YANDEX_CALENDAR_ACTIONS=read,write
# Just enough to answer invitations:
YANDEX_CALENDAR_ACTIONS=list_events,respond_event
Leave it unset for all seven tools. A name that matches nothing is ignored, so a typo can only ever withhold a tool, never grant one — and a value that names nothing recognisable therefore registers nothing at all. The list is applied when the plugin loads: restart Hermes after changing it.
Getting the app password
CalDAV does not accept your normal account password.
- Open https://id.yandex.ru/security/app-passwords.
- Add a password with the Calendar (CalDAV) scope.
- Copy it into
YANDEX_CALENDAR_APP_PASSWORD— it is shown only once, and you can revoke it at any time without touching your account password.
If a tool answers "Authentication failed", this is almost always the cause.
Installing the plugin into Hermes
Option A — from Git (recommended)
hermes plugins install akinfold/hermes-yandex-calendar --enable
Option B — pip
pip install hermes-yandex-calendar
Hermes discovers it through the hermes_agent.plugins entry point; add
yandex_calendar to plugins.enabled.
Option C — drop-in directory
Unzip the release archive into ~/.hermes/plugins/ so you end up with
~/.hermes/plugins/yandex_calendar/plugin.yaml, then enable it the same way.
Development
python -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'
ruff check . && ruff format --check .
pytest # unit tests, no network
Running the live E2E tests
The e2e-marked tests hit a real Yandex account and are deselected by default.
They create, edit, and then delete a throwaway event 400 days out, so a successful
run leaves nothing behind — but the attendees they invite do receive an invitation,
so use addresses you own.
Locally
YANDEX_CALENDAR_LOGIN=you@yandex.ru \
YANDEX_CALENDAR_APP_PASSWORD=xxxx \
pytest -m e2e
Or keep the credentials in ~/.yandex-calendar-login and
~/.yandex-calendar-app-password (plus optional ~/.yandex-calendar-attendees)
and just run pytest -m e2e — see tests/e2e/conftest.py.
On GitHub Actions
The E2E (live) workflow is manual (workflow_dispatch). It reads
YANDEX_CALENDAR_LOGIN, YANDEX_CALENDAR_APP_PASSWORD, and YC_E2E_ATTENDEES
from a GitHub Environment named yandex-calendar-e2e.
Contributing
Issues and PRs are welcome — see CONTRIBUTING.md for the layout, the plugin contract rules worth knowing, and the release process.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hermes_yandex_calendar-0.2.0.tar.gz.
File metadata
- Download URL: hermes_yandex_calendar-0.2.0.tar.gz
- Upload date:
- Size: 34.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16e9b7cf9c2e95d787aaf62c6497b0609e2e20826c397a88ee8058f0fc8a2277
|
|
| MD5 |
94dca37e3bc1c79b16581479780213a1
|
|
| BLAKE2b-256 |
47e425d641fb1a22da34ea42f9f0645dd5a9ebd803fd7c9b8d9750a9c0a06bfb
|
Provenance
The following attestation bundles were made for hermes_yandex_calendar-0.2.0.tar.gz:
Publisher:
release-publish.yml on akinfold/hermes-yandex-calendar
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hermes_yandex_calendar-0.2.0.tar.gz -
Subject digest:
16e9b7cf9c2e95d787aaf62c6497b0609e2e20826c397a88ee8058f0fc8a2277 - Sigstore transparency entry: 2255164013
- Sigstore integration time:
-
Permalink:
akinfold/hermes-yandex-calendar@1b529021ee3e28270323d395d4c662152321b6ec -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/akinfold
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-publish.yml@1b529021ee3e28270323d395d4c662152321b6ec -
Trigger Event:
push
-
Statement type:
File details
Details for the file hermes_yandex_calendar-0.2.0-py3-none-any.whl.
File metadata
- Download URL: hermes_yandex_calendar-0.2.0-py3-none-any.whl
- Upload date:
- Size: 24.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07a6643d1831416688719b7ddd860cbd574f5fa65abe856dfcb22b5d9053e6f5
|
|
| MD5 |
88151680fb000d3cd8f16c7167c3b0a7
|
|
| BLAKE2b-256 |
8afff42093b579d4cb9a61c36517f7c2a4909cb7edd10bc7a23894a8d4b774c9
|
Provenance
The following attestation bundles were made for hermes_yandex_calendar-0.2.0-py3-none-any.whl:
Publisher:
release-publish.yml on akinfold/hermes-yandex-calendar
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hermes_yandex_calendar-0.2.0-py3-none-any.whl -
Subject digest:
07a6643d1831416688719b7ddd860cbd574f5fa65abe856dfcb22b5d9053e6f5 - Sigstore transparency entry: 2255164017
- Sigstore integration time:
-
Permalink:
akinfold/hermes-yandex-calendar@1b529021ee3e28270323d395d4c662152321b6ec -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/akinfold
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-publish.yml@1b529021ee3e28270323d395d4c662152321b6ec -
Trigger Event:
push
-
Statement type: