Skip to main content

Yandex Calendar plugin for Hermes Agent: list, create, update, RSVP, move, and delete events over CalDAV.

Project description

hermes-yandex-calendar

PyPI version CI E2E (live) Coverage CodeFactor Ruff License: MIT

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=read and 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.

  1. Open https://id.yandex.ru/security/app-passwords.
  2. Add a password with the Calendar (CalDAV) scope.
  3. 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 \
YC_E2E_ATTENDEES=you+guest@yandex.ru \
pytest -m e2e

YC_E2E_ATTENDEES is the comma-separated list of addresses the throwaway event invites, and each one really is emailed an invitation — so list mailboxes you own. Omit it and the suite falls back to a +e2e sub-address of the account itself, which lands in your own inbox. Two constraints, both learned the hard way against the live server:

  • The addresses must exist. A made-up one (guest@example.com) bounces back into your mailbox.
  • They must not resolve to the account itself. Yandex canonicalises addresses (@ya.ru@yandex.ru) and drops an attendee that equals the ORGANIZER, so a plain alias of your own login silently disappears and the round-trip check fails. A +tag sub-address is delivered to the same mailbox but stays a distinct attendee.

Or keep all three out of the command line, in ~/.yandex-calendar-login, ~/.yandex-calendar-app-password, and ~/.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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hermes_yandex_calendar-0.2.1.tar.gz (35.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hermes_yandex_calendar-0.2.1-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

File details

Details for the file hermes_yandex_calendar-0.2.1.tar.gz.

File metadata

  • Download URL: hermes_yandex_calendar-0.2.1.tar.gz
  • Upload date:
  • Size: 35.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for hermes_yandex_calendar-0.2.1.tar.gz
Algorithm Hash digest
SHA256 4cfc1dbe6051b06368eccc01198aae88b7ae7c76c4544d59fe945971ca6511d8
MD5 52fb7a40af578c201c39509b6c53ae0a
BLAKE2b-256 dbeba8ab0ca6263bb32fdb9a117af686db61239c90355fc7a55d4d2260054d77

See more details on using hashes here.

Provenance

The following attestation bundles were made for hermes_yandex_calendar-0.2.1.tar.gz:

Publisher: release-publish.yml on akinfold/hermes-yandex-calendar

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hermes_yandex_calendar-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for hermes_yandex_calendar-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 91dc9459e8b92dfb2469736bc4176ba6fa217f091ebfb1f5667e082044c29daf
MD5 9064a71d6eaaa09ab04cc4af18716904
BLAKE2b-256 b1f05c1ccb36ee215ad8b3ad30a6610ee66912aa807e3077e5bd21ddfe7c528e

See more details on using hashes here.

Provenance

The following attestation bundles were made for hermes_yandex_calendar-0.2.1-py3-none-any.whl:

Publisher: release-publish.yml on akinfold/hermes-yandex-calendar

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page