AI Tools
The optional Shiny.Calendar.Extensions.AI package exposes ICalendarStore as
Microsoft.Extensions.AI tool functions (AIFunctions) for
LLM agents. You opt-in per operation — read, create, update, delete — and optionally per
calendar, an allow-list you control on behalf of the agent (this is not an OS permission prompt;
the platform calendar permission must already be granted). AOT-compatible: hand-built schemas and
JsonNode results, no reflection.
using Shiny.Calendar;using Shiny.Calendar.Extensions.AI;
builder.Services.AddCalendarStore(); // registers ICalendarStorebuilder.Services.AddCalendarAITools(tools => tools .AddCalendar(CalendarAICapabilities.Read | CalendarAICapabilities.Create));Resolve the bundle and hand the tools to any IChatClient:
var tools = sp.GetRequiredService<CalendarAITools>().Tools;var response = await chatClient.GetResponseAsync( messages, new ChatOptions { Tools = [.. tools] });Capabilities
Section titled “Capabilities”CalendarAICapabilities is a [Flags] enum, so each operation toggles independently:
| Flag | Tools exposed |
|---|---|
Read |
list_calendars, search_events, get_event |
Create |
create_event |
Update |
update_event |
Delete |
delete_event |
Write |
Create | Update | Delete |
All |
Read | Write |
// read + create only — the agent can add events but not modify or delete them.AddCalendar(CalendarAICapabilities.Read | CalendarAICapabilities.Create)
// full access.AddCalendar(CalendarAICapabilities.All)Scoping to specific calendars
Section titled “Scoping to specific calendars”AddCalendar(capabilities) applies to every calendar on the device.
AddCalendar(calendarId, capabilities) applies to one calendar and replaces the global set for
it — so a per-calendar entry can widen or narrow access. That covers the three shapes you normally
want:
// 1. global — every calendar, same rulesbuilder.Services.AddCalendarAITools(tools => tools .AddCalendar(CalendarAICapabilities.Read));
// 2. mixed — read everything, but full read/write on the work calendar, and hide the private onebuilder.Services.AddCalendarAITools(tools => tools .AddCalendar(CalendarAICapabilities.Read) .AddCalendar(workCalendarId, CalendarAICapabilities.All) .AddCalendar(privateCalendarId, CalendarAICapabilities.None));
// 3. strict allow-list — no global grant, so ONLY these calendars exist to the agentbuilder.Services.AddCalendarAITools(tools => tools .AddCalendar(workCalendarId, CalendarAICapabilities.All) .AddCalendars([teamCalendarId, holidayCalendarId], CalendarAICapabilities.Read));The tools enforce this on every call — the model cannot talk its way around the filter:
list_calendarsreturns only calendars with at least one capability, each with anallowedOperationsarray (read/create/update/delete) so the model knows where it may write.search_eventssearches only readable calendars; passing acalendarIdoutside the filter is an error.get_event,update_event, anddelete_eventresolve the event’s calendar first and refuse when it isn’t allowed.create_eventrefuses a disallowedcalendarId. WhenCreateisn’t granted globally, omittingcalendarIdis an error too — the device default calendar can’t be vetted up front, so the model is told to pick a calendar fromlist_calendars.
A tool is generated when any calendar grants that capability, so AddCalendar(workId, All) on its
own still produces create_event / update_event / delete_event — scoped to that calendar.
Filters chosen at runtime
Section titled “Filters chosen at runtime”Calendar ids are platform-assigned, so the ids you want are usually picked by the user and persisted.
Use the service-provider overload — the callback runs when CalendarAITools is first resolved, so it
can read your own services:
builder.Services.AddCalendarAITools((sp, tools) =>{ var settings = sp.GetRequiredService<AppSettings>(); // the ids the user picked tools.AddCalendars(settings.AgentCalendarIds, CalendarAICapabilities.All);});Generated Tools
Section titled “Generated Tools”| Tool | Capability | Description |
|---|---|---|
list_calendars |
Read | Lists the allowed calendars (id, name, colour, read-only, allowedOperations). |
search_events |
Read | Events in a date window across the readable calendars, optional calendar id + free-text query. |
get_event |
Read | Full detail for one event (attendees, reminders, recurrence). |
create_event |
Create | Creates an event (title, start, end required; ISO-8601 dates). |
update_event |
Update | Updates supplied fields on an existing event. |
delete_event |
Delete | Deletes an event by id. Optional deleteSeries (default false) chooses one occurrence vs. this and all future ones. |
Key Types
Section titled “Key Types”AddCalendarAITools(Action<ICalendarAIToolBuilder>)— DI extension; throws if nothing is added.AddCalendarAITools(Action<IServiceProvider, ICalendarAIToolBuilder>)— same, but the callback runs on first resolve so the filter can come from your own services.ICalendarAIToolBuilder—AddCalendar(CalendarAICapabilities)(global),AddCalendar(string calendarId, CalendarAICapabilities),AddCalendars(IEnumerable<string> calendarIds, CalendarAICapabilities).CalendarAICapabilities[Flags]—None,Read,Create,Update,Delete,Write,All.CalendarAITools— resolve from DI;.ToolsisIReadOnlyList<AITool>.


