trilium-calendar-mcp
Provides tools for managing calendar events stored as Trilium notes via ETAPI, including listing calendars and events, creating, updating, deleting, and bulk-upserting events, and handling dates, times, recurrence, tags, locations, colors, and stable CalDAV UIDs.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@trilium-calendar-mcpcreate a meeting with Bob next Monday at 10am"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
trilium-calendar-mcp
Calendar events as MCP tools for AI agents, stored as notes in Trilium.
It is a drop-in replacement for the Nextcloud Calendar MCP (nc_calendar_*): the tool names and
arguments are deliberately the same shape (calendar_*), so an agent that already knows how to
write calendar events only needs the tool prefix changed — no relearning, no new prompt structure.
AI agent ──MCP──▶ trilium-calendar-mcp ──ETAPI──▶ Trilium notes
│
CalDAV facade ─────┘──▶ Apple Calendar / DAVx⁵ / ThunderbirdNotes carrying #startDate are calendar events in Trilium's own calendar view, and the same notes
can be served over CalDAV by a facade (see Interoperability), so one set of notes feeds the agent,
the Trilium UI and standard calendar clients.
Why not let the agent drive the Trilium MCP directly?
The generic Trilium MCP has no calendar semantics: creating one event means create_note plus one
set_attribute call per label, the agent has to know every label name, the inclusive-#endDate
convention and the timezone rules, and mistakes fail silently — a note labelled #startdate
simply never appears in the calendar, with no error. Those rules live in code here, and every event
is validated on the way in.
Related MCP server: dav-mcp
Quickstart
docker run --rm -i \
-e TRILIUM_URL=https://trilium.example.com \
-e TRILIUM_ETAPI_TOKEN=your-etapi-token \
-e TRILIUM_CALENDARS=0hq1wCTuDBjA:Gaming \
ghcr.io/reinforcezwei/trilium-calendar-mcp:latestThat runs over stdio. For an HTTP (streamable) endpoint — what a remote agent infrastructure usually needs — add:
docker run --rm -p 127.0.0.1:8102:8102 \
-e MCP_TRANSPORT=streamable-http -e MCP_HOST=0.0.0.0 -e MCP_PORT=8102 \
-e TRILIUM_URL=https://trilium.example.com \
-e TRILIUM_ETAPI_TOKEN=your-etapi-token \
-e TRILIUM_CALENDARS=0hq1wCTuDBjA:Gaming \
ghcr.io/reinforcezwei/trilium-calendar-mcp:latest
# MCP endpoint: http://host:8102/mcpdocker-compose.yml wires the same thing up with an .env file.
HTTP and
Hostheaders. The MCP SDK rejects requests whoseHostheader is not allow-listed (DNS-rebinding protection,421 Invalid Host header) — a check whose SDK default depends on the bind address and has historically rejected every hostname in containers (python-sdk#1798). This server decides it explicitly instead, so no configuration is needed:
MCP_HOSTprotection
behaviour
0.0.0.0(container/VM default)off
any
Hostis accepted; the network/proxy is the boundary
127.0.0.1/localhoston
localhostand127.0.0.1names accepted — protects a loopback-only server from a browser on the same machineany bind +
MCP_ALLOWED_HOSTSon
only the listed
host:portvalues are acceptedThe startup log states which mode is active. Set
MCP_ALLOWED_HOSTS(and optionallyMCP_ALLOWED_ORIGINS) only when a loopback-bound server is reached under another name — e.g. a reverse proxy on the same host forwarding to127.0.0.1:8102.
Client configuration (stdio)
{
"mcpServers": {
"trilium-calendar": {
"command": "docker",
"args": ["run", "--rm", "-i", "--env-file", "/path/.env",
"ghcr.io/reinforcezwei/trilium-calendar-mcp:latest"]
}
}
}Configuration
Variable | Required | Default | Meaning |
| yes | — | Trilium base URL (no |
| yes | — | ETAPI token (Trilium → Options → ETAPI) |
| yes | — | Calendars to expose — see below |
| no |
| Timezone used to interpret naive datetimes |
| no |
|
|
| no |
| HTTP binding |
| no | — | Optional |
| no | derived | Optional |
| no |
| Set |
| no | — | Custom CA bundle path |
| no |
| Per-request timeout (seconds) |
| no |
| Max notes fetched per calendar scan |
| no |
| Log verbosity (logs go to stderr) |
Calendars and their names
TRILIUM_CALENDARS is a comma-separated list of Trilium note ids, each optionally named:
TRILIUM_CALENDARS=0hq1wCTuDBjA:Gaming,kf8Xq2vLpZ1a:Personal
# or, without aliases (the note's own title is then used as the name)
TRILIUM_CALENDARS=0hq1wCTuDBjA,kf8Xq2vLpZ1aThe name after the colon (or
=) is thecalendar_namethe agent uses. Without an alias the calendar note's Trilium title is used instead, so "Gaming" works either way once the note is titled that.calendar_list_calendarsreturns every calendar withname,note_id,title,timezone,colorandevent_count— that is how the agent resolves "put it in Gaming".All tools accept
calendar_name; it matches the name/alias, the note title, or the note id, case-insensitively. The first configured calendar is the default whencalendar_nameis omitted.An unknown name is an error that lists the valid ones, so the agent can self-correct.
Tools
Tool | Purpose |
| List calendars with their names/ids/timezones — call this first |
| Events in a date range; filters on title, categories, location |
| One event by uid, including description |
| Create an event (returns its uid) |
| Patch an event; only the arguments you pass change |
| Delete by uid (unknown uid = success, safe retries) |
| Events starting in the next N days |
| Bulk create-or-update, keyed on uid |
| Bulk delete by uid |
Migrating an agent from the Nextcloud MCP
Rename the tool prefix; arguments keep their names:
Nextcloud MCP | this server |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Point the prompt's calendar name at a configured name (e.g. Gaming) and the switch is done.
calendar_create_event also accepts event_uid: pass an existing uid to update instead of
creating a duplicate, which makes unattended re-runs idempotent.
Semantics the agent can rely on
Dates —
start_datetimetakesYYYY-MM-DD(all-day) or an ISO datetime. Naive datetimes are read in thetimezoneargument, else the calendar's timezone;Z/offset values are converted. Either way, what is stored is the wall clock in the calendar's timezone.All-day ranges —
end_datetimeis the last day of the event, inclusive ("until the 28th" means the 28th). This differs from Nextcloud'sDTEND, which is the day after: a Nextcloud all-day event ending2026-07-28covered 7/8–7/27. Re-check any all-day ranges in the old prompt when you migrate.Times — timed events get
#startTime/#endTime; an event with no end time is open-ended.Identity — every event carries a stable
#caldavUID. New events get a UUID; passevent_uid(or usecalendar_upsert_events) to update in place.Descriptions — plain text, line breaks preserved. Markdown syntax is not interpreted.
Response fields — every response reflects what is actually stored. An update that does not include
descriptionreturns the stored body (never an empty string), so it is safe to trust the response instead of re-fetching.End fields —
endis the ISO end (ornullwhen a timed event has no end time), andend_dateis the event's last day whenever it has an end. For a same-day timed event (15:00–15:30)end_dateis therefore that same day, notnull. Note that the#endDatelabel is only written when an event spans days (Trilium's convention, which the CalDAV facade understands), so if you inspect the notes directly, expect fewer labels than response fields.Not stored —
status,priority,privacy,attendees,url, reminders/alarms. These arguments are accepted so an existing prompt does not break, and echoed back inignored_fieldsso the agent can see they had no effect.Recurrence — pass an RRULE in
recurrence_rule(FREQ=WEEKLY;BYDAY=MO), optionally bounded withrecurrence_end_date;recurring=Falseorrecurrence_rule=""removes it.
How events are stored
Trilium label | Meaning |
|
|
| last day, inclusive |
|
|
| stable identity / idempotency key |
| categories, comma-separated |
| passthrough fields |
Notes are created as normal Trilium text notes with the event description as their body, so they read naturally in the note tree and the calendar view.
Interoperability with a CalDAV facade
The labels above match the mapping used by the Trilium CalDAV facade
(#endDate inclusive → DTEND +1 day, #caldavUID → UID, #tags → CATEGORIES), so events
written by an agent are served correctly to Apple Calendar, DAVx⁵ and Thunderbird by the same
facade. Nothing here requires the facade, and the facade does not require this server.
Limitations
No reminders/alarms, attendees, or event status — Trilium notes have no equivalent.
No todos/VTODO — event notes only (Trilium's
#todoDatetask notes are a follow-up).Timezones are per calendar, not per event — a single calendar has one timezone; events from several zones are normalised into it.
Listing scans the calendar's children (one ETAPI search) and filters locally; fine for the thousands-of-events scale, not for millions.
No ETags/conflict detection — last write wins, which is fine for an agent-owned calendar.
Development
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytest tests/test_event.py # unit tests, no server needed
TRILIUM_URL=https://... TRILIUM_ETAPI_TOKEN=... pytest # + integration tests against a live instanceIntegration tests create a temporary calendar note under root, exercise create/list/update/
upsert/delete against it, and delete it afterwards (cascading to the events they made). No existing
notes are touched.
Releasing
./release.sh 0.2.0 # bump version, commit, tag
./release.sh 0.2.0 --push # ... and push the commit + tagPushing a v* tag runs the test suite, then builds and publishes
ghcr.io/<owner>/trilium-calendar-mcp (0.2.0, 0.2, latest) for linux/amd64 and
linux/arm64, and opens a GitHub release.
License
MIT
Available Tools
9 toolscalendar_create_eventA
Create a calendar event.
Args:
title: Event title.
start_datetime: YYYY-MM-DD for an all-day event, or an ISO datetime
such as 2026-07-08T11:00:00 (naive = calendar timezone) /
2026-07-08T03:00:00Z (converted to calendar timezone).
calendar_name: Calendar to create in; omit for the default calendar.
end_datetime: ISO datetime end, or for all-day events the LAST day
(inclusive). Empty = same day / no end.
all_day: Force an all-day event (times are ignored).
description: Plain text; line breaks are preserved.
location: Free text location.
categories: Comma-separated categories/tags.
timezone: IANA timezone used to interpret naive datetimes.
color: Colour name or hex.
recurrence_rule: RRULE body, with or without the RRULE: prefix.
recurrence_end_date: YYYY-MM-DD that bounds the series (writes UNTIL).
recurring: Set False to force a non-recurring event.
event_uid: Reuse an existing uid (updates that event instead of
creating a duplicate) — use it to make retries idempotent.
status, priority, privacy, attendees, url, reminder_minutes,
reminder_email, reminders: accepted for compatibility but NOT stored
by Trilium; they are echoed back in ignored_fields.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | ||
| color | No | ||
| title | Yes | ||
| status | No | ||
| all_day | No | ||
| privacy | No | ||
| location | No | ||
| priority | No | ||
| timezone | No | ||
| attendees | No | ||
| event_uid | No | ||
| recurring | No | ||
| reminders | No | ||
| categories | No | ||
| description | No | ||
| end_datetime | No | ||
| calendar_name | No | ||
| reminder_email | No | ||
| start_datetime | Yes | ||
| recurrence_rule | No | ||
| reminder_minutes | No | ||
| recurrence_end_date | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: naive-vs-Z timezone interpretation, all-day semantics with inclusive end day, the UNTIL write for recurrence_end_date, and the transparent note that status/priority/privacy/attendees/url/reminders are NOT stored but echoed in ignored_fields. It omits return-shape and permission/auth details, but the destructive/side-effect behavior is well explained.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The per-argument list is long but tightly front-loaded with the action line and never wastes words; length is justified by 22 parameters, though grouping the ignored compatibility fields together is handled efficiently.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 22-param tool with 0% schema coverage, no annotations, and no output schema, the description covers required formats, defaults, and the critical ignored-fields behavior. Missing auth/prerequisite and return-value notes, but it is otherwise complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate and does: it gives formats for start_datetime and end_datetime, defines calendar_name omission default, explains empty end_datetime, and clarifies the debasement of ignored fields — far beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Create a calendar event') that is unambiguous among siblings like calendar_update_event, calendar_upsert_events, and calendar_delete_event.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It documents the idempotent retry path via event_uid ('use it to make retries idempotent'), which is genuine usage guidance, but it never says when to prefer calendar_upsert_events or how it differs from calendar_update_event.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_delete_eventB
Delete an event by uid. Deleting an unknown uid succeeds (safe retries).
Args: event_uid: Uid of the event to delete. calendar_name: Calendar holding the event; omit to search all calendars.
| Name | Required | Description | Default |
|---|---|---|---|
| event_uid | Yes | ||
| calendar_name | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden, and it does disclose one valuable trait: deleting an unknown uid succeeds, so retries are safe (idempotency). It still omits permission requirements, whether deletion is soft/reversible, and side effects such as attendee notifications, leaving meaningful gaps for a mutation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action and the idempotency caveat, followed by a compact Args block. Every sentence adds signal and there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with no annotations and no output schema, most of what an agent needs is present (target, calendar scoping, retry safety), but permission requirements, confirmation/reversibility semantics, and error behavior are all absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it largely does: event_uid is identified as the event's uid and calendar_name is explained with the non-obvious behavior 'omit to search all calendars'. That adds real meaning the bare schema lacks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (delete) and resource (event) keyed by uid, so the core action is unambiguous. It does not, however, distinguish itself from the sibling calendar_delete_events (plural), which an agent could easily confuse with this single-event tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no when-to-use guidance, no prerequisites, and never names an alternative such as calendar_delete_events. Usage is only implied by the verb itself, matching the calibration case that scored 2 for 'no when-to-use, no prerequisites'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_delete_eventsA
Delete many events by uid in one call.
Args: event_uids: Uids to delete; unknown uids are reported, not an error. calendar_name: Calendar to delete from; omit to search all calendars.
| Name | Required | Description | Default |
|---|---|---|---|
| event_uids | Yes | ||
| calendar_name | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations at all, the description carries the full burden. It usefully discloses the partial-success behavior (unknown uids are reported, not an error) and the default scope of calendar_name, but says nothing about the destructive/irreversible nature of deletion, permissions required, or the shape of the report.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One summary sentence followed by a tight Args block; no filler, and the core action is front-loaded before the parameter detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 2-parameter destructive tool with no annotations and no output schema, the description covers parameter behavior and partial-failure handling, but omits irreversibility/confirmation expectations and what the 'report' of unknown uids actually looks like.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does: it defines event_uids semantics (unknown ones are reported, not fatal) and calendar_name's default 'all calendars' behavior. Both parameters gain meaning beyond their bare names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Delete'), resource ('events'), and scope ('many ... by uid in one call'), which implicitly distinguishes it from the singular calendar_delete_event sibling. It never names the alternative explicitly, so a 5 is not warranted, but the bulk-by-uid framing is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The Args block gives real operating context — unknown uids are reported rather than erroring, and omitting calendar_name searches all calendars — which implies when the tool is appropriate. It offers no explicit when-not guidance or named alternatives, so usage is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_get_eventA
Get one event by its uid, including its description.
Args:
calendar_name: Calendar to look in; omit to search all calendars.
event_uid: The event's uid (from calendar_list_events).
| Name | Required | Description | Default |
|---|---|---|---|
| event_uid | No | ||
| calendar_name | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses that the return includes the event description and that omitting calendar_name searches all calendars. It does not state that this is strictly a read-only operation, nor does it cover error behavior (e.g., event not found) or permission requirements, leaving gaps for a tool with zero annotation support.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, followed by tightly formatted argument notes. Every sentence earns its place with no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no annotations and no output schema, the description adequately covers both parameters and notes the return includes the event description. It omits error-handling and permission context, but for a simple single-event read tool it is largely sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It adds meaning for both parameters: calendar_name ('Calendar to look in; omit to search all calendars') and event_uid ('The event's `uid` (from calendar_list_events)'). This is helpful, though it could go further by describing the uid format or any constraints.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Get') and resource ('one event by its uid'), clearly distinguishing it from list or multi-event siblings. However, it does not explicitly contrast itself with calendar_get_upcoming_events or calendar_list_events, so sibling differentiation is only implicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives parameter-level guidance ('omit to search all calendars') and references calendar_list_events as the source of the uid, implying usage context. But it never states when to choose this tool over list_events or get_upcoming_events, leaving the agent to infer the single-event retrieval scenario.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_get_upcoming_eventsB
List events starting between today and N days ahead.
Args: calendar_name: Calendar to read; omit for all calendars. days_ahead: How many days ahead to include (today counts as day 0). limit: Maximum number of events to return.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| days_ahead | No | ||
| calendar_name | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. 'List' implies a read, and the window semantics are clarified (today = day 0), but there is no mention of result ordering, timezone handling, recurring-event expansion, or whether the read has any side effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One front-loaded summary sentence followed by a compact Args block; every line conveys needed information with no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-param read tool with no annotations and no output schema, the description covers the parameters well but omits return-shape essentials: ordering, timezone basis for 'today', and recurring-event behavior. Adequate but with clear gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate and largely does: it explains that omitting calendar_name reads all calendars, defines days_ahead's day-0 convention, and states that limit is a maximum count. Defaults and types remain only in the schema, but the semantics are meaningfully enriched.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (List) and resource (events) scoped to a time window starting today through N days ahead. This distinguishes it from calendar_list_events by the windowing semantics, though it never explicitly contrasts with that sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Implied usage (get upcoming events within a horizon) is clear from the description, and it notes that omitting calendar_name reads all calendars. However, it never states when to prefer this over calendar_list_events or calendar_get_event, leaving routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_list_calendarsA
List the calendars this server exposes.
Returns each calendar's name (use this as calendar_name in other
tools), its Trilium note_id, note title, timezone, colour and the
current number of events. Start here: calendar names are how the user
refers to calendars (e.g. "Gaming"), and they are what the other tools
accept.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations and no output schema, the description carries the behavioral burden and largely meets it by disclosing the return fields (name, note_id, title, timezone, colour, event count) and implying a non-mutating read via 'List'. It omits auth requirements, pagination, and cost characteristics, which are minor for a zero-parameter listing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action, then the payload, then the call-to-action. The 'Start here' sentence and the closing clause about names being how the user refers to calendars overlap somewhat, but the text is short and each sentence is nearly load-bearing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description's enumeration of return fields is exactly the right compensation, and the identifier hand-off to other tools is explained. Nothing essential is missing for a zero-parameter, read-only list; only secondary details like pagination are absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes no parameters, so the baseline is 4; the description goes slightly beyond by explaining that the returned `name` is the `calendar_name` identifier consumed by sibling tools, which is useful cross-tool semantics rather than schema restatement.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the calendars this server exposes') and enumerates the returned fields, so an agent can immediately distinguish this discovery tool from the event CRUD siblings. The purpose is unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Start here' gives explicit sequencing guidance, and the note that calendar names are what the other tools accept tells the agent how to chain this call into siblings. It does not name exclusions or alternatives, but for a single list-calendars tool that would be artificial.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_list_eventsA
List events, optionally filtered by date range and text.
Args:
calendar_name: Calendar to read; omit to use the default calendar.
start_date: Earliest day to return, YYYY-MM-DD (events overlapping
the range are included).
end_date: Latest day to return, YYYY-MM-DD.
limit: Maximum number of events to return (0 = no limit).
title_contains: Only events whose title contains this text.
categories: Comma-separated categories; matches events carrying any of them.
location_contains: Only events whose location contains this text.
include_descriptions: Fetch event bodies (one extra request per event).
search_all_calendars: Search every configured calendar instead of one.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| end_date | No | ||
| categories | No | ||
| start_date | No | ||
| calendar_name | No | ||
| title_contains | No | ||
| location_contains | No | ||
| include_descriptions | No | ||
| search_all_calendars | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It does disclose useful behavior: overlapping events are included in the date range, and include_descriptions costs one extra request per event. However, it says nothing about permissions, pagination, or what the returned records look like.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded purpose sentence followed by a tight, scannable Args block. Every line is necessary given 0% schema coverage; no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a nine-parameter, zero-annotation, no-output-schema tool, the description covers parameters and key behaviors well. It omits any indication of the return shape or result ordering, but the core invocation information is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does for all nine parameters: formats (YYYY-MM-DD), defaults-by-omission (default calendar), sentinel values (limit 0 = no limit), and matching semantics (overlap for dates, contains for text, any-of for categories). This adds real meaning beyond the bare schema titles and types.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a clear verb+resource ('List events') and names the two filtering axes (date range, text). It is distinguishable from mutating siblings, but it does not explicitly differentiate itself from close read siblings like calendar_get_upcoming_events or calendar_get_event.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use guidance or routing to alternatives. The reader must infer that this is the broad listing tool versus calendar_get_upcoming_events or calendar_get_event. No prerequisites or exclusions are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_update_eventA
Update an existing event. Only the arguments you pass are changed.
The returned event reflects what is now stored: if you do not pass
description, the response carries the existing body (not an empty
string), so you do not need to re-fetch to confirm it survived.
Args:
event_uid: Uid of the event to update.
calendar_name: Calendar holding the event; omit to search all calendars.
(other arguments as in calendar_create_event; end_datetime="" clears
the end, location="" clears the location, recurrence_rule="" or
recurring=False removes the recurrence.)
| Name | Required | Description | Default |
|---|---|---|---|
| color | No | ||
| title | No | ||
| all_day | No | ||
| location | No | ||
| timezone | No | ||
| event_uid | Yes | ||
| recurring | No | ||
| categories | No | ||
| description | No | ||
| end_datetime | No | ||
| calendar_name | No | ||
| start_datetime | No | ||
| recurrence_rule | No | ||
| recurrence_end_date | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does disclose meaningful behavior: updates are partial, omitted fields retain existing values, and specific empty-string values clear fields. It also explains that the returned event reflects stored data. It still lacks auth, error, and rate-limit behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and uses a clear Args block. The returned-event paragraph is somewhat long but earns its place by explaining omission behavior and avoiding a re-fetch. Overall it is well structured with minor verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 14-parameter mutation tool with no annotations and no output schema, the description is not complete enough. It cannot document all parameters, auth requirements, error cases, or return structure, so an agent still lacks important context despite the useful partial-update explanation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for 14 parameters, so the description must compensate. It documents event_uid and calendar_name and gives clearing semantics for end_datetime, location, recurrence_rule, and recurring, but defers most other fields to calendar_create_event without describing them, leaving substantial gaps.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Update an existing event') and immediately clarifies partial-update semantics ('Only the arguments you pass are changed'). It implicitly distinguishes from calendar_create_event by saying 'existing event', though it does not name siblings or address overlap with calendar_upsert_events.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides useful update behavior and operational details, such as omitting calendar_name to search all calendars and clearing values with empty strings. However, it gives no explicit when-to-use guidance versus alternatives like calendar_create_event or calendar_upsert_events, nor any prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calendar_upsert_eventsA
Create or update many events at once, keyed on uid (or on key).
Use this for bulk work (a release calendar for a whole season). Each
entry is created if its identity is new and updated if it already
exists, so re-running the same list updates in place instead of
duplicating. Identity comes from event_uid/uid, or from key: a
short stable slug ("wuwa-3.7-banner-1") that is hashed into a uid, so
a scheduled agent can re-publish its list without storing uids.
Args:
events: List of event objects. Each accepts the arguments of
calendar_create_event (title, start_datetime, end_datetime,
all_day, description, location, categories, color,
recurrence_rule) plus the identity fields event_uid/uid or
key. calendar_name may be set per entry to spread the list
across calendars. Entries with no identity always create new
events.
calendar_name: Default calendar for entries that do not name one.
| Name | Required | Description | Default |
|---|---|---|---|
| events | Yes | ||
| calendar_name | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does well: it discloses upsert semantics (create if new, update if existing), deduplication on identity, identity derivation via `event_uid`/`uid` or a hashed `key`, and that entries with no identity always create new events. It omits auth/rate-limit and response/error behavior, but the critical upsert mechanics are explicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is front-loaded with the core purpose before mechanics and an Args section, and every sentence contributes. It is somewhat long, but the length is justified by the complex upsert semantics and the 0% schema coverage it must offset.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 2-parameter bulk upsert tool with no annotations, no output schema, and 0% schema description coverage, the description is nearly complete: it covers identity, upsert behavior, per-entry calendar spreading, and parameter fields. Minor gaps remain around partial-failure behavior, conflict resolution within a single list, and permission requirements.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate and does: it documents the `events` list as accepting all `calendar_create_event` fields plus identity fields, explains `calendar_name` per-entry override, and clarifies that `calendar_name` at the top level is the default calendar. This adds substantial meaning beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Create or update many events at once, keyed on `uid` (or on `key`).' It clearly distinguishes this bulk upsert tool from the single-event siblings `calendar_create_event` and `calendar_update_event`, which it explicitly references as the source of per-entry arguments.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides clear context for use: 'Use this for bulk work (a release calendar for a whole season).' and explains the idempotent re-run benefit. It names `calendar_create_event` as the argument reference but does not explicitly state when not to use this tool or contrast it directly with single-event siblings, so it falls short of fully explicit routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
9 tool updates
v0.1.2- First observed
calendar_create_event - First observed
calendar_delete_event - First observed
calendar_delete_events - First observed
calendar_get_event - First observed
calendar_get_upcoming_events - First observed
calendar_list_calendars - First observed
calendar_list_events - First observed
calendar_update_event - First observed
calendar_upsert_events
TDQS
Scored across 9 tools
Most tools have clearly distinct actions (list, get, create, update, delete, upcoming, bulk upsert/delete). Slight overlap exists between calendar_get_upcoming_events and calendar_list_events with a date range, and between calendar_create_event's event_uid idempotency and calendar_upsert_events, but descriptions clarify the intended distinctions.
All nine tools follow a consistent calendar_verb_noun pattern with snake_case throughout. Verb choices (list, get, create, update, delete, upsert) are predictable and parallel, including the plural bulk variants.
Nine tools is well-scoped for a calendar server, covering single-event CRUD plus bulk operations and an upcoming shortcut without redundancy. Each tool earns its place.
Full CRUD lifecycle is covered (list, get, create, update, delete) plus bulk upsert/delete, upcoming events, and recurrence support. No obvious calendar operations are missing.
Maintenance
Related MCP Connectors
Calendar API for AI agents: events, availability, Google/Microsoft setup, scheduling, and iCal.
Hosted Google Calendar MCP server for AI agents. No self-hosting or Google Cloud setup.
Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
Scheduling infrastructure for AI agents across Google and Microsoft calendars.
Related MCP Servers
- FlicenseNot gradedqualityFmaintenanceEnables interaction with any CalDAV-compatible calendar server (Yandex, Google, Nextcloud, iCloud, etc.) to list calendars, create/manage events with reminders and attendees, search events, and handle recurring events through natural language.14-
- AlicenseAqualityAmaintenanceTurn any calendar, contact book, or task list into an AI-orchestrated system. Platform-independent via CalDAV/CardDAV works with Nextcloud, Baikal, Fastmail, and any standards-compliant DAV server. 26 tools with field-agnostic updates.27173 npm35MIT
- AlicenseNot gradedqualityFmaintenanceProvider-agnostic CalDAV calendar MCP server that connects any CalDAV calendar to AI assistants, enabling calendar operations like listing, creating, updating, and deleting events.AGPL 3.0
- FlicenseNot gradedqualityBmaintenanceMCP server for Radicale/CalDAV servers, enabling AI agents to manage calendars and task lists by listing, creating, and deleting events and reminders.-