dsh-plugin-radicale
A plugin for DeepSeek Harness (dsh) that gives your agent one radicale
tool to manage your local Radicale calendar server. You can ask your
agent things like "what's on my calendar?", "add a meeting on Thursday at
10", "make a new calendar for my work stuff", or "cancel the dentist
appointment" and it will list, read, create, update, and search your events
for you.
The tool has eight actions:
- list - the collections on the server, or the events inside one of them
- read - one event in full (this is also how the agent gets the
freshness stamp it needs before an update)
- create - a new event from simple fields (title, start, end, ...)
- update - change an existing event, keeping its identity
- search - find events whose content matches a text
- createCollection - a new, empty calendar you can ask for by name
- cancel - move one event out of your calendar into a dedicated
archive (see below)
- uncancel - put a cancelled event back where it came from
The plugin never deletes. It has no delete action, and nothing it can
send to the server can remove an entry. Cancelling an event is not a
delete: the event is moved into an archive collection (by default
/caldav/cancelled, configurable) and its own file gets two small
X-DSH- lines recording when it was cancelled and where it came from.
That is the traceability: the event is still there, readable, and
restorable, and uncancel reverses the move and strips those lines. If
the event really should be gone, do that in your calendar client.
Installing the plugin
With the dsh command, from the directory containing this checkout:
dsh plugin --profile web add /path/to/radicale
When running dsh from source:
pnpm dsh plugin --profile web add /path/to/radicale
The install is inert: the plugin row ships disabled. Enable it in one
of two spots (pick one, never both):
Every session. In your profile's patch layer
(~/.dsh/profiles/web/cordis.patch.yml, append to your existing file):
- id: radicale
disabled: false
One preset only. In that preset's own file
(~/.dsh/.agent-presets/<preset>/agent.cordis.yml, append):
- id: radicale
name: dsh-plugin-radicale
No other configuration is needed for a server installed with the
defaults below - the plugin and Radicale both use the same default
address out of the box. If your server listens somewhere else, add a
config block with baseUrl set to your server's address, like:
- id: radicale
disabled: false
config:
baseUrl: http://<your-server-address>
Then restart the server.
Configuration
| key | default | meaning |
|---|
baseUrl | the default local address (matches a Radicale server installed with the defaults) | Your Radicale server's address, if it listens somewhere else. Host and port in one value, for example http://<server>:<port>. |
maxItems | 100 | How many events one list or search call may fetch. Lists that hit the cap are flagged truncated. |
cancelledCollection | /caldav/cancelled | The archive collection cancel moves events into. A bare name (cancelled) resolves under /caldav; an explicit path is kept as-is. It is created automatically the first time something is cancelled. |
The config block replaces the plugin's defaults wholesale - list
every key you want to keep.
The plugin assumes the server is reachable without a password. That is the
right setup for a local server (below); it does not carry any credential
configuration of its own.
Installing Radicale
Radicale is a small, self-contained Python program. You install it into a
dedicated virtual environment with uv:
uv venv ~/venv-caldav
source ~/venv-caldav/bin/activate
uv pip install radicale
That one command also brings in its only runtime dependency (vobject), so
there is nothing else to install.
The config file
Create a config file (for example ~/.radicale-config) with:
[storage]
# Where the calendar data lives: one plain .ics file per event,
# easy to back up by copying the folder.
type = multifilesystem
filesystem_folder = ~/.radicale
[rights]
# Let the owner write. Without this line the server is read-only and
# the agent can list but not create or update events.
type = owner_write
[auth]
# No password: the dsh plugin connects without credentials.
type = none
Running it
radicale -c ~/.radicale-config
That is all: the server starts, prints a line with its address, and keeps
running until you stop it (Ctrl-C). To have it start at boot, run it under
systemd, tmux, or whatever service manager you already use.
Once it is up, open a session and ask your agent to list the collections
- it should report your calendars, or an empty list on first run.
Developing the plugin: see AGENTS.md.