All plugins

event-read

Read one CalDAV calendar collection you run, with list_events and read_event. Disclosure — Sends CALDAV_USER and CALDAV_PASSWORD as Basic auth to your CALDAV_URL, does not follow redirects, and starts no local program. Over plain http that password travels unencrypted. The tools are on for every surface, including gateways, and the plugin has no user list of its own: anyone you allow on your Hermes gateway reads the calendar with your password. Turn a surface off with hermes tools. On Hermes 0.21.6 and later, while the plugin is enabled, its skill adds one line to the skills index of every session, including surfaces where the tools are turned off. A watch is a cron job you create yourself with hermes cron create. Each run is at least one model turn, there is no daily cap, and the job stays until you run hermes cron remove. Under plugins.isolation: host a no-argument list_events still returns the next 7 days and does not record uids. When the watch can run, it reports a problem on the first run, when the cause changes and again after 23 hours, or on every run when its data directory cannot be written. Each request gives up after 20 seconds without data, and a whole call has no time limit. The watch reads cron.execution_identity.current_cron_execution (Hermes 0.21.6). The printed cron line uses ctx.profile_name. The file write still uses Hermes's write guard. An update can stop the watch if that guard moves. These reads do not ask for approval and do not register a hook. State lives in plugin-data/event-read. Not tested against a vendor-hosted calendar.

TakeshiTGALToolsv0.1.4Updated 9 days ago

About

Read one CalDAV calendar collection you run, with list_events and read_event. Disclosure — Sends CALDAV_USER and CALDAV_PASSWORD as Basic auth to your CALDAV_URL, does not follow redirects, and starts no local program. Over plain http that password travels unencrypted. The tools are on for every surface, including gateways, and the plugin has no user list of its own: anyone you allow on your Hermes gateway reads the calendar with your password. Turn a surface off with hermes tools. On Hermes 0.21.6 and later, while the plugin is enabled, its skill adds one line to the skills index of every session, including surfaces where the tools are turned off. A watch is a cron job you create yourself with hermes cron create. Each run is at least one model turn, there is no daily cap, and the job stays until you run hermes cron remove. Under plugins.isolation: host a no-argument list_events still returns the next 7 days and does not record uids. When the watch can run, it reports a problem on the first run, when the cause changes and again after 23 hours, or on every run when its data directory cannot be written. Each request gives up after 20 seconds without data, and a whole call has no time limit. The watch reads cron.execution_identity.current_cron_execution (Hermes 0.21.6). The printed cron line uses ctx.profile_name. The file write still uses Hermes's write guard. An update can stop the watch if that guard moves. These reads do not ask for approval and do not register a hook. State lives in plugin-data/event-read. Not tested against a vendor-hosted calendar.

event-read

Read one CalDAV collection you run, with list_events and read_event. A cron job you create can tell you when an event uid turns up in the next week. This plugin starts no program of its own.

First request

You do not need a cron job to read the calendar. The watch is optional and comes later.

  1. hermes plugins install TakeshiTGAL/hermes-plugin-event-read --enable
  2. Set CALDAV_URL to the collection, such as http://127.0.0.1:5232/user/calendar/.
  3. Set CALDAV_USER.
  4. Set CALDAV_PASSWORD.
  5. The calendar server is already running.
  6. Ask in chat, for example "what is on my calendar this week". The model calls list_events with no arguments. That call does not ask for approval.

Asking about the calendar is enough, and cron is not required. 日本語: 予定を聞くだけでよい。cron は要らない。モデルは引数なしの list_events を呼ぶ。

Disclosure — sends CALDAV_USER and CALDAV_PASSWORD as Basic to your CALDAV_URL; plain http sends the password unencrypted, and redirects are not followed; it starts no local program; on every surface, including the gateway, with no user list, anyone you allow there uses your password until hermes tools turns it off; you create the watch from the CLI with hermes cron create, with no daily cap, and each run is at least one model turn; under plugins.isolation: host a no-argument list_events still returns the next 7 days and does not record uids; on a judged run, problems are sent first, on a new cause, and after 23 hours, or every run when the data directory cannot be written; a call has no overall deadline, and one request gives up after 20 s with no data; the cron check is cron.execution_identity.current_cron_execution (Hermes 0.21.6) and the write check is Hermes's file guard, so an update can stop the watch; the job remains until hermes cron remove; state lives in <HERMES_HOME>/plugin-data/event-read; on Hermes 0.21.6 and later its skill adds one line to the skills index of every session, even where the tools are turned off. Not tested against a vendor-hosted calendar.

What you set

CALDAV_URL is one http or https collection URL. It needs a host and a non-empty path. A path of / alone is accepted. A username in the URL, a query, a fragment, an empty path, or surrounding spaces is rejected before a connection. CALDAV_USER and CALDAV_PASSWORD are sent as Basic on every request. A carriage return or line feed in either is rejected before a request. The password is not written into the result, the error text, or a saved file.

Over plain http the password is sent unencrypted. Responses 301, 302, 303, 307, and 308 are not followed, including when they stay on the same host. Authorization is not sent to a second URL. A proxy set in the environment is not used, so the password is not sent to it. http is not silently rewritten to https.

A request gives up after 20 seconds without data from the server. That is a per-request limit, not a deadline for the whole call. There is no overall deadline and no retry, so one call is one request that either answers or fails.

What a call returns

list_events and read_event are the only tools. The toolset is event_read. There is no slash command and no plugin CLI. An empty collection and a window that matches nothing are empty results, not errors.

read_event looks up one uid with a property filter. It does not GET a path. A CalDAV text match is a substring match, so a server may answer meet-1 with meet-1 and meet-12; the exact uid is picked out of what came back, and the others are not returned. No event with that uid is No event with that uid. and is not an empty success. If the only rows that came back are other uids, that is still not found, and none is chosen.

Each event has a uid, summary, start, end, tzid, and repeats. A missing DTEND leaves end null. A one-day end or a zero-length end is not invented. A DATE value stays a date. A TZID is returned as written. A value that ends in Z has a null tzid. Times are not converted. Attachments are not fetched. No scheduling reply is sent. VTODO and VJOURNAL are not events.

Repeats are not expanded. One recurring event is one row, whose start is the master's DTSTART, and a later occurrence of a saved uid is not announced. Moving or cancelling a single occurrence is normal iCalendar: the calendar object then holds the master plus one component per changed occurrence, all with the same uid and each with its own RECURRENCE-ID (RFC 5545 section 3.8.4.4). Those components are grouped into the one row of their uid, with repeats true. The summary on that row is the master's, not the changed occurrence's. Two copies of the same uid are ambiguous only when the RECURRENCE-ID is also the same, or when both are masters. If a server returns a changed occurrence without its master, that occurrence's own start is used. On 2026-10-10 a local Radicale 3.8.2 server returned a weekly event with a moved occurrence, a weekly event with a cancelled occurrence (EXDATE), and an all-day weekly event, each as one row. This plugin sends Depth: 1. It does not say that every server requires that header.

Success is HTTP 207 whose root is a multistatus. Prefixes are ignored, and text/xml is accepted. An empty multistatus is an empty list. HTTP 200 is a failure even when the body is a multistatus, and so is HTML, plain text, or a 207 that is not a multistatus. A 401 whose body says forbidden stays a 401, not a 403 or an empty calendar. 400 and 404 are not swapped. If any inner status is not 200, the whole call fails. There is no retry. A timeout is a failure. XML nested more than 100 levels deep is a failure, not a calendar. The message names the cause, for example The calendar could not be read (http_401). The seen uids were not changed.

More than 50 events is not the whole list. A ranged list returns the first 50 in document order, with truncated true, and does not record them. A no-argument cron watch that sees more than 50 returns no events, says too many, and does not record them. Its next step is to shrink the collection until that window holds 50 or fewer; the next no-argument list_events then records them. That notice uses the same cadence as a read failure. A response that is too large to parse is an error and does not change the saved uids. An event with no uid fails the whole call and records nothing.

The watch

Only a no-argument list_events on a cron turn that was saved successfully moves the seen uids. The window is this plugin's clock, in UTC, from now truncated to a second through seven days later. The start is included and the end is excluded.

The first success, when there was no announced problem just before it, records the uids and announces nothing. After that, a uid that is in the window and is not in the file is announced as now being in the window. That covers an event created today and an event that already existed outside the window and has moved into it; the plugin cannot tell those apart, and does not call either one newly created. A saved uid is not announced again when the summary or the time changes, when it is deleted, or when the same uid is created again. A deleted uid stays in the file. The saved set is a union, trimmed to the newest 5,000 uids, so a uid dropped by that trim can be announced again. Membership is tested with a set, so a full file does not slow a run. An empty window does not replace saved uids with an empty list. A first success that sees nothing records an empty list and does not announce. After that, a new uid is announced. Both sides empty is No new event uid. It is not an error.

A no-argument call that cannot be judged as cron still reads the same seven-day window and does not touch the seen uids. recorded is false. It does not write a notice into notices.json. That includes a separate process under plugins.isolation: host, a cron check that cannot be imported, a renamed check, an exception, and a result that is not true or false. A check that returns false is a manual call. A manual no-argument call reads the same window, notify is false, and it does not create or change the file. A later cron run can still announce a uid that read saw. A ranged list and read_event read and do not record, including when the run cannot be judged. On a confirmed cron turn those calls say This call did not update the seen uids.

A watch that cannot work is announced the first time, again when the cause changes, and again once at least 23 hours have passed since the last announcement of that cause. The 23 hours leave room for a daily job that fires a few seconds early. The same cause inside that window returns the same message with notify false, so a job every few minutes does not send the same line all day. This cadence covers a read failure, more than 50 events in the window, a CALDAV_URL that no longer matches the saved one, a state file that cannot be read, and missing settings. Recovery is reported once. A bad argument does not update that memory, and neither does a manual or ranged call.

State is two files in <HERMES_HOME>/plugin-data/event-read, each replaced from a temporary file in the same directory. seen.json holds the collection URL and the seen uids, and nothing else writes to it, so a problem never rewrites your uids. notices.json holds only the kind of the last announced problem and when it was announced. A seen.json that is not JSON is left in place and reported on that cadence; delete it yourself and the next success is a first record. An HTTP failure does not save an empty uid list. If the saved URL and CALDAV_URL differ, nothing is read, no file is overwritten, and no event is announced. If the write check refuses the path, a watch stops before HTTP, does not write somewhere else, and announces every run. If the data directory is there but cannot be written, the calendar is read, the save fails, and that failure is announced every run, because it has nowhere to remember that it already did.

Cron

You create the job with Hermes. Hermes does not fire it until a gateway or the cron ticker is running. This plugin has no unschedule command and no default interval. Removing the plugin leaves the job until you run hermes cron remove <id>. When the profile is not default, put -p before the subcommand, for example hermes -p work cron remove <id>. A default profile has no -p. This plugin does not define -p or --profile.

every 1h below is an example you can type. It is not a default. [SILENT] is the Hermes token that suppresses a cron delivery, so the job only reaches you when the tool asks for it.

hermes cron create "every 1h" "Call the list_events tool with no arguments. If notify is true, reply with its message and nothing else. If notify is false, reply with only [SILENT]." --name event-read --deliver local
hermes cron remove <id>
hermes -p work cron create "every 1h" "Call the list_events tool with no arguments. If notify is true, reply with its message and nothing else. If notify is false, reply with only [SILENT]." --name event-read --deliver local
hermes -p work cron remove <id>

--deliver local sends you no message: Hermes only saves the run's output under cron/output/ in the Hermes home, for you to read later. It is the CLI default. To be told without opening that file, create the job with a target you have already configured, such as --deliver telegram or --deliver telegram:<chat_id>. deliver is not a tool argument and is rejected as one. There is no daily cap. Each run is at least one model turn on your bill. A turn that calls the tool makes two or more model requests. bot-chat costs another model turn, because the receiving agent reads the output as a message.

The plugin asks for no Hermes approval. list_events and read_event only read. It does not register a pre_tool_call hook, so Hermes does not open a question. The seen-uid file is written only after a confirmed cron mark, and that write is not an approve line. Requires Hermes 0.21.6 or later, the first stable release with cron.execution_identity.current_cron_execution. The tools are on for every surface, including the gateway, until you turn a surface off with hermes tools. There is no separate user list. Anyone you allow on the gateway can call the tools with your password. Under plugins.isolation: host a no-argument list_events still returns the next 7 days and does not record uids.

The cron check is cron.execution_identity.current_cron_execution, public since Hermes 0.21.6. The write check is Hermes's file guard. The data directory is the path above. If the cron check cannot be used, a no-argument list_events still returns the next 7 days and does not record uids. If the write check refuses the path, the watch stops before HTTP and announces every run. If the data directory is there but cannot be written, the calendar is read, the save fails, and that failure is announced every run. None of these report an empty calendar.

An empty HERMES_HOME scan with Hermes v0.22.2 and with origin-main is Verdict: SAFE, 11 findings, Decision: ALLOWED — Allowed (community source, safe verdict). The findings are a password constant in the tests, git and recall subprocesses in the tests, and example collection URLs in this file and the tests. They stay, because the tests and the setup example need them.

Skill

The plugin registers one skill, caldav-calendar. It tells the model to look up list_events and read_event with tool_search, in English, before it tries web_search. The skill runs nothing and sends nothing by itself.

On Hermes 0.21.6 and later, while the plugin is enabled, the skill adds one line to the skills index of every session. That includes a surface where the tools were turned off with hermes tools: the line stays there, and the calls are simply unavailable. On Hermes 0.21.4 and 0.21.5 the skill is not listed; it loads only when called by its full name, event-read:caldav-calendar. There is no separate user list for the skill; every session that loads the plugin can read it. hermes plugins disable event-read removes the skill and the tools together.

Not tested against a vendor-hosted calendar.

README from the reviewed commit 00e8aed; it updates when the author re-pins.

More by TakeshiTGAL

30 plugins by TakeshiTGAL in the catalog.

TakeshiTGALjp-subsidiesJapanese subsidy search and weekly watch, built on the jGrants public API. Disclosure — Searches api.jgrants-portal.go.jp with no API key and no daily request cap; each call waits at most 60 seconds and retries at most twice, and a result is a search match, not proof that every subsidy came back. The plugin has no user list of its own: anyone you allow on your Hermes gateway can call these tools and change this profile's watch, and the agent can set up a watch by itself. In a Chatwork room listed in CHATWORK_ROOMS, everyone can call them while CHATWORK_ALLOWED_USERS is unset. The tools are on for every platform, including gateways; turn them off per platform with hermes tools. While the plugin is enabled, its skill adds one line to the skills index of every session, including surfaces where the tools are turned off. The /subsidies command is outside hermes tools; limit it with allow_admin_from or user_allowed_commands, or disable the plugin. The optional weekly watch is an unattended cron job that runs a script with no model call and is not a sandbox; delivering it to bot-chat instead runs one model turn on the fetched text. If delivery fails after the script runs, those subsidies are already marked seen and the text stays under cron/output/. Uninstall leaves the cron job, scripts/jp-subsidies-weekly-*.py and plugin-data/jp-subsidies/ until you run hermes cron remove and delete the files. The plugin does not store the name or ID of who asked. Not tested against a live gateway, with the Chatwork plugin loaded, or for result sets of several hundred rows.Communitytools1TakeshiTGALwyoming-voice-bridgeWyoming speech-to-text and text-to-speech for a Wyoming server you run (stt/tts provider `wyoming`), plus a wyoming_voice_status tool that lists the server's models and voices. Disclosure — Audio and text to speak go to the WYOMING_* host:port you set over cleartext, unauthenticated TCP (public addresses accepted). Once selected, the providers send speech in every session, including cron and webhooks. This build imports the installed package only when its file tree is the official wyoming 1.5.4 wheel, and a different tree sends nothing. This build lists no ffmpeg digest, so audio that is not already 16 kHz, 16-bit, mono WAV is not converted. wyoming_voice_status asks with one public pre_tool_call line. Choosing always stores one command_allowlist line for that same which and the same host:port list. A later call skips the question only when a person is present. cron, hermes -q, webhook, msgraph_webhook, and api_server do not send a describe, even after always. yolo and approvals off skip the prompt, and this plugin does not add a refusal for those modes. The plugin does not import Hermes approval, config, or write-guard modules. Hermes 0.21.4 does not list the skill. The v0.21.5 canary and Hermes 0.22.2 add one skill line to every session that loads the plugin, including a surface where the tools are turned off. The status tool is on for every surface, including the gateway, and a guest there can call it and approve it when a person can answer. Turn the wyoming_voice toolset off per surface with hermes tools.Communityvoice1TakeshiTGALauthor-feedEach watch run costs 0 tokens, and the unmanned run holds no tools. Read one public profile and watch one account, with profile_get, author_feed and author_watch. It reads public posts and profiles from the Bluesky social media network, which is built on the AT Protocol (atproto), without signing in. Disclosure — HTTPS GET only to public.api.bsky.app, no API key. Hermes starts the watch Python when the cron job fires. The tools are on for every surface, including gateways, and ask for no approval: anyone you allow on your gateway can call them. Turn a surface off with hermes tools. While the plugin is enabled, its skill adds one line to the skills index of every session, including surfaces where the tools are turned off. hermes author-feed schedule writes a script and creates one cron job that does not call a model. Each run costs 0 tokens and has no daily cap. Empty script output sends nothing. Delivered to local unless you pass --deliver, so no chat by default. Run hermes author-feed unschedule before removal. That removes the job and the script. A leftover job after the plugin is gone stops with hermes cron remove <id>. The scheduled script does not run under plugins.isolation: host, so that setting does not stop the watch. The author_watch tool still refuses a run that is not the saved cron job, including under plugins.isolation: host, and posts that notice on every run of the tool. The first run only records what is already there. One run reads at most the latest 3 pages and announces a new post by this account on them. When every row on those pages is new, with no saved post or mark, and the feed still offers another cursor, it also posts a possible-miss notice, at most once in 24 hours while that keeps happening. Replies, including the account's own thread continuations, are not announced. It relies on private Hermes parts, so an update can stop it. State stays in <HERMES_HOME>/plugin-data/author-feed after uninstall. Seen post URIs advance only on the saved cron job after a successful fetch. The script allows that when cron/executions.db shows that job running under the parent pid and cron/jobs.json names author-feed-watch.py. A direct run and another cron job exit 3 with no GET and no write. The author_watch tool allows that only for the saved job id. Delivery accepts origin, local, all, bot-chat, the Hermes 0.21.6 cron platform names, and public registry names that declare a cron delivery env var. A refused name can be set with hermes cron edit <id> --deliver. bot-chat places the watched account's post text in another model turn that has the profile's tools. hermes cron create cannot set a per-job tool list. A job made by 0.1.x keeps its model turn until the next schedule. On Hermes 0.21.4 and 0.21.5 this plugin does not load, and a leftover job keeps its model turn until hermes cron remove <id>. Running schedule again updates the interval and deliver target and switches an existing model job to the script.CommunitytoolsTakeshiTGALcsharp-game-editorGive it one feature for the Unity project open in your editor, and it builds the scripts, objects and scene in one plan, stops that plan when the compile reports an error CS line, and plays the scene when the compile is clean. Disclosure — Needs MCP for Unity pinned to v10.3.0, plus git and uv. Hermes starts mcpforunityserver==10.3.0 through uvx with telemetry and the update check off. Disk reads run at once. apply_plan asks once and puts back a file the plan did not name. A plan that deletes, runs editor code, can lose unsaved work or writes outside the project asks again, and that summary is sent only when every line fits the approval card; otherwise nothing is sent and confirm_risky_plan asks. unity_docs sends the query to docs.unity3d.com after Hermes asks. unity_generate can call paid providers you configured in MCP for Unity; that call was not part of the check. Tools are on by default on every surface, including the gateway. Only users the gateway admits can reach the tools, and this plugin keeps no user list of its own. Cron and a session with nobody to answer decline. Logs stay in <HERMES_HOME>/plugin-data/agent-plugin-csharp-game-editor-<hash>/logs. Stop with hermes plugins disable csharp-game-editor, or hermes tools for one surface. macOS only.Communitytools