@protocoltooling/fullcalendar 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +44 -122
- package/dist/index.d.ts +27 -1
- package/dist/index.js +161 -96
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,159 +1,81 @@
|
|
|
1
1
|
# FullCalendar WebMCP
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Add WebMCP calendar tools to an existing FullCalendar React app.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Created by Roberto Nevarez. FullCalendar WebMCP exposes structured browser tools without replacing your calendar UI, backend, database, or application state.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[Live demo](https://protocoltooling.com/demo) · [Documentation](https://protocoltooling.com/integrations/fullcalendar) · [WebMCP Challenge notes](./CHALLENGE.md)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
npm install @protocoltooling/fullcalendar
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
### Local / Tarball Installation (Development)
|
|
9
|
+
## Install
|
|
14
10
|
|
|
15
11
|
```bash
|
|
16
|
-
npm
|
|
17
|
-
npm install /path/to/protocoltooling-fullcalendar-0.2.0.tgz
|
|
12
|
+
npm install @protocoltooling/fullcalendar
|
|
18
13
|
```
|
|
19
14
|
|
|
20
|
-
### Peer Dependencies
|
|
21
|
-
|
|
22
|
-
Ensure host peer dependencies are installed:
|
|
23
|
-
|
|
24
|
-
- `react`: `>=17 <20`
|
|
25
|
-
- `@fullcalendar/react`: `^6.0.0 || ^7.0.0`
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Minimal Integration
|
|
30
|
-
|
|
31
15
|
```tsx
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
type CalendarEventRepository,
|
|
38
|
-
} from "@protocoltooling/fullcalendar";
|
|
39
|
-
|
|
40
|
-
export function MyCalendar() {
|
|
41
|
-
const calendarRef = useRef<FullCalendar>(null);
|
|
42
|
-
const [events, setEvents] = useState<CalendarEvent[]>([]);
|
|
43
|
-
|
|
44
|
-
const reloadEvents = useCallback(async () => {
|
|
45
|
-
const data = await eventRepository.list();
|
|
46
|
-
setEvents(data);
|
|
47
|
-
return data;
|
|
48
|
-
}, []);
|
|
49
|
-
|
|
50
|
-
useFullCalendarWebMCP({
|
|
51
|
-
calendarRef,
|
|
52
|
-
events: eventRepository,
|
|
53
|
-
onEventsChanged: reloadEvents,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<FullCalendar
|
|
58
|
-
ref={calendarRef}
|
|
59
|
-
events={events}
|
|
60
|
-
/* ... other FullCalendar options */
|
|
61
|
-
/>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
16
|
+
useFullCalendarWebMCP({
|
|
17
|
+
calendarRef,
|
|
18
|
+
events: repository,
|
|
19
|
+
onEventsChanged: refreshEvents,
|
|
20
|
+
});
|
|
64
21
|
```
|
|
65
22
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
## Repository Contract
|
|
69
|
-
|
|
70
|
-
The host provides an object implementing `CalendarEventRepository` mapped to its existing database or API:
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
import type {
|
|
74
|
-
CalendarEvent,
|
|
75
|
-
CalendarEventQuery,
|
|
76
|
-
CalendarEventRepository,
|
|
77
|
-
CreateCalendarEventInput,
|
|
78
|
-
UpdateCalendarEventInput,
|
|
79
|
-
} from "@protocoltooling/fullcalendar";
|
|
80
|
-
|
|
81
|
-
export const eventRepository: CalendarEventRepository = {
|
|
82
|
-
async list(query?: CalendarEventQuery, options?: { signal?: AbortSignal }): Promise<CalendarEvent[]> {
|
|
83
|
-
return api.events.list(query, options);
|
|
84
|
-
},
|
|
85
|
-
async get(id: string, options?: { signal?: AbortSignal }): Promise<CalendarEvent | null> {
|
|
86
|
-
return api.events.get(id, options);
|
|
87
|
-
},
|
|
88
|
-
async create(input: CreateCalendarEventInput, options?: { signal?: AbortSignal }): Promise<CalendarEvent> {
|
|
89
|
-
return api.events.create(input, options);
|
|
90
|
-
},
|
|
91
|
-
async update(id: string, input: UpdateCalendarEventInput, options?: { signal?: AbortSignal }): Promise<CalendarEvent> {
|
|
92
|
-
return api.events.update(id, input, options);
|
|
93
|
-
},
|
|
94
|
-
async delete(id: string, options?: { signal?: AbortSignal }): Promise<void> {
|
|
95
|
-
return api.events.delete(id, options);
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
```
|
|
23
|
+
Your repository stays authoritative. FullCalendar WebMCP routes agent reads and writes through the same persistence layer your app already uses.
|
|
99
24
|
|
|
100
|
-
|
|
25
|
+
## Agent tools
|
|
101
26
|
|
|
102
|
-
|
|
27
|
+
| Tool | Purpose |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `calendar_get_context` | Read time, timezone, view, and visible range |
|
|
30
|
+
| `calendar_list_events` | Search persisted events |
|
|
31
|
+
| `calendar_get_event` | Read one event by stable id |
|
|
32
|
+
| `calendar_create_event` | Create an event |
|
|
33
|
+
| `calendar_update_event` | Update an event |
|
|
34
|
+
| `calendar_delete_event` | Delete an event |
|
|
103
35
|
|
|
104
|
-
|
|
36
|
+
Tools return structured domain state. The consuming agent handles the conversation.
|
|
105
37
|
|
|
106
|
-
|
|
38
|
+
## Shared state
|
|
107
39
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
---
|
|
40
|
+
```text
|
|
41
|
+
Person ───────────────┐
|
|
42
|
+
↓
|
|
43
|
+
FullCalendar UI
|
|
44
|
+
↓
|
|
45
|
+
host repository
|
|
46
|
+
↑
|
|
47
|
+
WebMCP agent → FullCalendar WebMCP
|
|
48
|
+
```
|
|
119
49
|
|
|
120
|
-
|
|
50
|
+
A person can drag or resize an event, then an agent can read the updated state. An agent can create or reschedule an event, and the same change appears in the human interface.
|
|
121
51
|
|
|
122
|
-
|
|
123
|
-
- **Client Lifecycle:** Tool registration occurs inside `useEffect` via an `AbortController`. React Strict Mode replay and unmount cleanup properly unregister tools with zero orphaned state.
|
|
124
|
-
- **Delayed Runtime:** Automatically waits for late-injected WebMCP runtime if not immediately present at mount time.
|
|
125
|
-
- **React Server Components (RSC):** The package entry includes the `'use client';` directive for seamless import into Next.js App Router client components.
|
|
52
|
+
The demo uses browser-local persistence so every visitor gets an isolated, resettable workspace. Production apps can connect the same repository contract to their existing API and database.
|
|
126
53
|
|
|
127
|
-
|
|
54
|
+
## Security boundary
|
|
128
55
|
|
|
129
|
-
|
|
56
|
+
FullCalendar WebMCP does not authenticate users, store events, or authorize mutations. The host application remains responsible for authentication, authorization, and persistence.
|
|
130
57
|
|
|
131
|
-
|
|
132
|
-
Current package: `@protocoltooling/fullcalendar`
|
|
133
|
-
The old package is deprecated.
|
|
58
|
+
Optional `capabilities` can limit which WebMCP tools are registered. This is defense in depth, not a replacement for server-side authorization.
|
|
134
59
|
|
|
135
|
-
|
|
60
|
+
Host-selected event `metadata` is JSON-safe and read-only through WebMCP writes. FullCalendar `extendedProps` are never exposed automatically.
|
|
136
61
|
|
|
137
|
-
## Development
|
|
62
|
+
## Development
|
|
138
63
|
|
|
139
64
|
```bash
|
|
140
|
-
# Build library package (dist/index.js, dist/index.d.ts)
|
|
141
65
|
npm run build:lib
|
|
142
|
-
|
|
143
|
-
# Run unit, integration, public type, and SSR tests
|
|
144
66
|
npm test
|
|
145
|
-
|
|
146
|
-
# Run typecheck & linter
|
|
147
67
|
npm run typecheck
|
|
148
68
|
npm run lint
|
|
149
|
-
|
|
150
|
-
# Build package tarball and test in external Vite and Next.js consumers
|
|
151
69
|
npm run test:pack
|
|
70
|
+
npm run docs:build
|
|
71
|
+
```
|
|
152
72
|
|
|
153
|
-
|
|
73
|
+
Local example:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
154
76
|
npm run dev
|
|
155
77
|
```
|
|
156
78
|
|
|
157
79
|
## License
|
|
158
80
|
|
|
159
|
-
MIT
|
|
81
|
+
MIT © 2026 Roberto Nevarez
|
package/dist/index.d.ts
CHANGED
|
@@ -96,6 +96,27 @@ interface FullCalendarHandle {
|
|
|
96
96
|
};
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Controls which WebMCP calendar tools are registered at runtime.
|
|
101
|
+
*
|
|
102
|
+
* Disabled capabilities omit the corresponding tools entirely — they are not
|
|
103
|
+
* registered and cannot be discovered or invoked. This is defense in depth /
|
|
104
|
+
* least-privilege exposure only; the host repository and backend remain
|
|
105
|
+
* authoritative for authorization.
|
|
106
|
+
*
|
|
107
|
+
* `calendar_get_context` is always registered regardless of `read`, because it
|
|
108
|
+
* exposes viewport/timezone context rather than persisted event records.
|
|
109
|
+
*/
|
|
110
|
+
interface FullCalendarWebMCPCapabilities {
|
|
111
|
+
/** Register `calendar_list_events` and `calendar_get_event`. Default: true. */
|
|
112
|
+
read?: boolean;
|
|
113
|
+
/** Register `calendar_create_event`. Default: true. */
|
|
114
|
+
create?: boolean;
|
|
115
|
+
/** Register `calendar_update_event`. Default: true. */
|
|
116
|
+
update?: boolean;
|
|
117
|
+
/** Register `calendar_delete_event`. Default: true. */
|
|
118
|
+
delete?: boolean;
|
|
119
|
+
}
|
|
99
120
|
/**
|
|
100
121
|
* Options for the useFullCalendarWebMCP hook.
|
|
101
122
|
*/
|
|
@@ -103,6 +124,11 @@ interface FullCalendarWebMCPOptions {
|
|
|
103
124
|
calendarRef: RefObject<FullCalendarHandle | null>;
|
|
104
125
|
events: CalendarEventRepository;
|
|
105
126
|
onEventsChanged: () => unknown | Promise<unknown>;
|
|
127
|
+
/**
|
|
128
|
+
* Optional least-privilege tool exposure. Omitted fields default to enabled.
|
|
129
|
+
* Does not replace server-side authorization in the host repository/backend.
|
|
130
|
+
*/
|
|
131
|
+
capabilities?: FullCalendarWebMCPCapabilities;
|
|
106
132
|
onRegistrationError?: (error: unknown) => void;
|
|
107
133
|
}
|
|
108
134
|
|
|
@@ -114,4 +140,4 @@ interface FullCalendarWebMCPOptions {
|
|
|
114
140
|
*/
|
|
115
141
|
declare function useFullCalendarWebMCP(options: FullCalendarWebMCPOptions): void;
|
|
116
142
|
|
|
117
|
-
export { type CalendarEvent, type CalendarEventQuery, type CalendarEventRepository, type CreateCalendarEventInput, type FullCalendarHandle, type FullCalendarWebMCPOptions, type JsonObject, type JsonPrimitive, type JsonValue, type UpdateCalendarEventInput, useFullCalendarWebMCP };
|
|
143
|
+
export { type CalendarEvent, type CalendarEventQuery, type CalendarEventRepository, type CreateCalendarEventInput, type FullCalendarHandle, type FullCalendarWebMCPCapabilities, type FullCalendarWebMCPOptions, type JsonObject, type JsonPrimitive, type JsonValue, type UpdateCalendarEventInput, useFullCalendarWebMCP };
|
package/dist/index.js
CHANGED
|
@@ -9,44 +9,96 @@ var emptySchema = {
|
|
|
9
9
|
properties: {},
|
|
10
10
|
additionalProperties: false
|
|
11
11
|
};
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
description: "The human-readable event title."
|
|
17
|
-
},
|
|
18
|
-
start: {
|
|
19
|
-
type: "string",
|
|
20
|
-
format: "date-time",
|
|
21
|
-
description: "Inclusive event start as ISO 8601 with an explicit offset."
|
|
22
|
-
},
|
|
23
|
-
end: {
|
|
24
|
-
type: ["string", "null"],
|
|
25
|
-
format: "date-time",
|
|
26
|
-
description: "Exclusive event end as ISO 8601 with an explicit offset, or null."
|
|
27
|
-
},
|
|
28
|
-
allDay: {
|
|
29
|
-
type: "boolean",
|
|
30
|
-
description: "Whether the event is an all-day event."
|
|
31
|
-
}
|
|
12
|
+
var stableEventIdProperty = {
|
|
13
|
+
type: "string",
|
|
14
|
+
minLength: 1,
|
|
15
|
+
description: "Stable id of the persisted event. Obtain from calendar_list_events, calendar_get_event, or a prior create/update result. Never substitute an event title."
|
|
32
16
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
17
|
+
var queryStartProperty = {
|
|
18
|
+
type: "string",
|
|
19
|
+
format: "date-time",
|
|
20
|
+
description: "Inclusive lower bound for filtering persisted events (ISO 8601 date-time with explicit offset). Omit to include events from any earlier time."
|
|
21
|
+
};
|
|
22
|
+
var queryEndProperty = {
|
|
23
|
+
type: "string",
|
|
24
|
+
format: "date-time",
|
|
25
|
+
description: "Exclusive upper bound for filtering persisted events (ISO 8601 date-time with explicit offset). For one calendar day, set end to the start of the following day. Omit for no upper limit."
|
|
26
|
+
};
|
|
27
|
+
var queryTextProperty = {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Case-insensitive substring matched against event titles. Omit to skip title filtering."
|
|
30
|
+
};
|
|
31
|
+
var createEventTitleProperty = {
|
|
32
|
+
type: "string",
|
|
33
|
+
minLength: 1,
|
|
34
|
+
description: "Display title for the new event."
|
|
35
|
+
};
|
|
36
|
+
var updateEventTitleProperty = {
|
|
37
|
+
type: "string",
|
|
38
|
+
minLength: 1,
|
|
39
|
+
description: "New display title. Omit to leave the current title unchanged."
|
|
40
|
+
};
|
|
41
|
+
var createEventStartProperty = {
|
|
42
|
+
type: "string",
|
|
43
|
+
format: "date-time",
|
|
44
|
+
description: "Inclusive event start as ISO 8601 date-time with an explicit offset (e.g. 2026-09-02T14:00:00-06:00). Resolve relative times with calendar_get_context first."
|
|
45
|
+
};
|
|
46
|
+
var updateEventStartProperty = {
|
|
47
|
+
type: "string",
|
|
48
|
+
format: "date-time",
|
|
49
|
+
description: "New inclusive event start (ISO 8601 with explicit offset). Omit to leave the current start unchanged."
|
|
50
|
+
};
|
|
51
|
+
var createEventEndProperty = {
|
|
52
|
+
type: ["string", "null"],
|
|
53
|
+
format: "date-time",
|
|
54
|
+
description: "Exclusive event end as ISO 8601 date-time with explicit offset, or null for open-ended. Omit on create to use host defaults."
|
|
55
|
+
};
|
|
56
|
+
var updateEventEndProperty = {
|
|
57
|
+
type: ["string", "null"],
|
|
58
|
+
format: "date-time",
|
|
59
|
+
description: "New exclusive event end (ISO 8601 with explicit offset) or null. Omit to leave the current end unchanged."
|
|
60
|
+
};
|
|
61
|
+
var createAllDayProperty = {
|
|
62
|
+
type: "boolean",
|
|
63
|
+
description: "Whether the event spans full calendar days without clock times. Defaults to false when omitted."
|
|
64
|
+
};
|
|
65
|
+
var updateAllDayProperty = {
|
|
66
|
+
type: "boolean",
|
|
67
|
+
description: "Whether the event is all-day. Omit to leave unchanged."
|
|
68
|
+
};
|
|
69
|
+
var contextAnnotations = { readOnlyHint: true };
|
|
70
|
+
var eventReadAnnotations = {
|
|
71
|
+
readOnlyHint: true,
|
|
72
|
+
untrustedContentHint: true
|
|
73
|
+
};
|
|
74
|
+
var eventWriteAnnotations = {
|
|
75
|
+
readOnlyHint: false,
|
|
76
|
+
untrustedContentHint: true
|
|
77
|
+
};
|
|
78
|
+
var deleteAnnotations = { readOnlyHint: false };
|
|
79
|
+
function resolveCapabilities(capabilities) {
|
|
80
|
+
return {
|
|
81
|
+
read: capabilities?.read ?? true,
|
|
82
|
+
create: capabilities?.create ?? true,
|
|
83
|
+
update: capabilities?.update ?? true,
|
|
84
|
+
delete: capabilities?.delete ?? true
|
|
85
|
+
};
|
|
38
86
|
}
|
|
39
87
|
function executionSignal(executeOptions) {
|
|
40
88
|
return executeOptions?.signal;
|
|
41
89
|
}
|
|
90
|
+
function cloneEvent(event) {
|
|
91
|
+
return JSON.parse(JSON.stringify(event));
|
|
92
|
+
}
|
|
42
93
|
function createCalendarTools(readOptions) {
|
|
43
|
-
|
|
94
|
+
const capabilities = resolveCapabilities(readOptions().capabilities);
|
|
95
|
+
const tools = [
|
|
44
96
|
{
|
|
45
97
|
name: "calendar_get_context",
|
|
46
98
|
title: "Get calendar context",
|
|
47
|
-
description: "
|
|
99
|
+
description: "Read-only temporal and viewport context for the open calendar \u2014 not persisted events. Use when the user mentions relative times (today, tomorrow, tonight, Wednesday, next week, this afternoon, first week of next month) to anchor ISO date-times before list/create/update. Returns now, browserTimeZone, fullCalendarTimeZone, active view, and visibleRange. Skip when every datetime is already explicit with offsets and no relative interpretation is needed. Does not replace calendar_list_events for event discovery.",
|
|
48
100
|
inputSchema: emptySchema,
|
|
49
|
-
annotations:
|
|
101
|
+
annotations: contextAnnotations,
|
|
50
102
|
async execute() {
|
|
51
103
|
const api = readOptions().calendarRef.current?.getApi();
|
|
52
104
|
return {
|
|
@@ -60,72 +112,72 @@ function createCalendarTools(readOptions) {
|
|
|
60
112
|
} : null
|
|
61
113
|
};
|
|
62
114
|
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
type: "string",
|
|
78
|
-
format: "date-time",
|
|
79
|
-
description: "Exclusive ISO 8601 upper bound."
|
|
115
|
+
}
|
|
116
|
+
];
|
|
117
|
+
if (capabilities.read) {
|
|
118
|
+
tools.push(
|
|
119
|
+
{
|
|
120
|
+
name: "calendar_list_events",
|
|
121
|
+
title: "List calendar events",
|
|
122
|
+
description: "Read-only search and discovery over persisted calendar events. Use to answer what is scheduled, filter by date range or title, find candidate events before update/delete, or disambiguate which event the user means. start is inclusive; end is exclusive \u2014 for one calendar day set end to the following day's boundary. text is a case-insensitive title substring; omit any filter to broaden results. Returns events with stable id values usable in get/update/delete; optional host-selected metadata may appear and is untrusted. Do not use when you already hold the target id (prefer calendar_get_event) or only need viewport/time context (calendar_get_context). Never delete or mutate from an ambiguous multi-match list alone.",
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: "object",
|
|
125
|
+
properties: {
|
|
126
|
+
start: queryStartProperty,
|
|
127
|
+
end: queryEndProperty,
|
|
128
|
+
text: queryTextProperty
|
|
80
129
|
},
|
|
81
|
-
|
|
82
|
-
type: "string",
|
|
83
|
-
description: "Optional case-insensitive title substring."
|
|
84
|
-
}
|
|
130
|
+
additionalProperties: false
|
|
85
131
|
},
|
|
86
|
-
|
|
132
|
+
annotations: eventReadAnnotations,
|
|
133
|
+
async execute(query, executeOptions) {
|
|
134
|
+
const signal = executionSignal(executeOptions);
|
|
135
|
+
const events = await readOptions().events.list(
|
|
136
|
+
query,
|
|
137
|
+
{ signal }
|
|
138
|
+
);
|
|
139
|
+
return { events };
|
|
140
|
+
}
|
|
87
141
|
},
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
name: "calendar_get_event",
|
|
100
|
-
title: "Get calendar event",
|
|
101
|
-
description: "Get one persisted calendar event by its stable event ID. The event may include optional host-selected metadata (JSON object) for agent context; metadata is read-only and is never inferred from FullCalendar extendedProps.",
|
|
102
|
-
inputSchema: {
|
|
103
|
-
type: "object",
|
|
104
|
-
properties: {
|
|
105
|
-
id: { type: "string", minLength: 1, description: "Stable event ID." }
|
|
142
|
+
{
|
|
143
|
+
name: "calendar_get_event",
|
|
144
|
+
title: "Get calendar event",
|
|
145
|
+
description: "Read-only fetch of exactly one persisted event by stable id. Use when you already have the id from calendar_list_events, a prior create/update result, or the host app \u2014 or to re-read authoritative fields before a mutation when list results are insufficient. Returns { event: CalendarEvent | null }; null means no matching persisted event. Do not use for title/date search (calendar_list_events), do not invent ids from titles, and do not substitute a title for id.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
id: stableEventIdProperty
|
|
150
|
+
},
|
|
151
|
+
required: ["id"],
|
|
152
|
+
additionalProperties: false
|
|
106
153
|
},
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const event = await readOptions().events.get(id, { signal });
|
|
115
|
-
return { event };
|
|
154
|
+
annotations: eventReadAnnotations,
|
|
155
|
+
async execute(input, executeOptions) {
|
|
156
|
+
const { id } = input;
|
|
157
|
+
const signal = executionSignal(executeOptions);
|
|
158
|
+
const event = await readOptions().events.get(id, { signal });
|
|
159
|
+
return { event };
|
|
160
|
+
}
|
|
116
161
|
}
|
|
117
|
-
|
|
118
|
-
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (capabilities.create) {
|
|
165
|
+
tools.push({
|
|
119
166
|
name: "calendar_create_event",
|
|
120
167
|
title: "Create calendar event",
|
|
121
|
-
description: "Create and persist a
|
|
168
|
+
description: "Create and persist a new calendar event (state mutation). Use only for brand-new events the user wants scheduled. Requires title and start (ISO 8601 with explicit offset); optional end (exclusive, ISO 8601 or null) and allDay. Resolve relative times via calendar_get_context first. Returns the persisted event including host-assigned id. Metadata cannot be supplied. Do not use to modify, reschedule, or rename an existing event \u2014 use calendar_update_event.",
|
|
122
169
|
inputSchema: {
|
|
123
170
|
type: "object",
|
|
124
|
-
properties:
|
|
171
|
+
properties: {
|
|
172
|
+
title: createEventTitleProperty,
|
|
173
|
+
start: createEventStartProperty,
|
|
174
|
+
end: createEventEndProperty,
|
|
175
|
+
allDay: createAllDayProperty
|
|
176
|
+
},
|
|
125
177
|
required: ["title", "start"],
|
|
126
178
|
additionalProperties: false
|
|
127
179
|
},
|
|
128
|
-
annotations:
|
|
180
|
+
annotations: eventWriteAnnotations,
|
|
129
181
|
async execute(rawInput, executeOptions) {
|
|
130
182
|
const input = rawInput;
|
|
131
183
|
const signal = executionSignal(executeOptions);
|
|
@@ -134,22 +186,27 @@ function createCalendarTools(readOptions) {
|
|
|
134
186
|
await options.onEventsChanged();
|
|
135
187
|
return { event };
|
|
136
188
|
}
|
|
137
|
-
}
|
|
138
|
-
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
if (capabilities.update) {
|
|
192
|
+
tools.push({
|
|
139
193
|
name: "calendar_update_event",
|
|
140
194
|
title: "Update calendar event",
|
|
141
|
-
description: "
|
|
195
|
+
description: "Persisted partial update of exactly one existing event (state mutation). Requires stable id plus at least one of title, start, end, allDay; omitted fields stay unchanged. Host-selected metadata is preserved and cannot be written here. When the user names an event without an id, find it with calendar_list_events first; calendar_get_event is optional if the list row already has sufficient detail. Use for reschedule, rename, time/end changes, or all-day toggles \u2014 not for creating a duplicate or deleting (calendar_create_event / calendar_delete_event).",
|
|
142
196
|
inputSchema: {
|
|
143
197
|
type: "object",
|
|
144
198
|
properties: {
|
|
145
|
-
id:
|
|
146
|
-
|
|
199
|
+
id: stableEventIdProperty,
|
|
200
|
+
title: updateEventTitleProperty,
|
|
201
|
+
start: updateEventStartProperty,
|
|
202
|
+
end: updateEventEndProperty,
|
|
203
|
+
allDay: updateAllDayProperty
|
|
147
204
|
},
|
|
148
205
|
required: ["id"],
|
|
149
206
|
minProperties: 2,
|
|
150
207
|
additionalProperties: false
|
|
151
208
|
},
|
|
152
|
-
annotations:
|
|
209
|
+
annotations: eventWriteAnnotations,
|
|
153
210
|
async execute(rawInput, executeOptions) {
|
|
154
211
|
const { id, ...input } = rawInput;
|
|
155
212
|
const signal = executionSignal(executeOptions);
|
|
@@ -158,30 +215,38 @@ function createCalendarTools(readOptions) {
|
|
|
158
215
|
await options.onEventsChanged();
|
|
159
216
|
return { event };
|
|
160
217
|
}
|
|
161
|
-
}
|
|
162
|
-
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (capabilities.delete) {
|
|
221
|
+
tools.push({
|
|
163
222
|
name: "calendar_delete_event",
|
|
164
223
|
title: "Delete calendar event",
|
|
165
|
-
description: "
|
|
224
|
+
description: "Permanently delete exactly one persisted calendar event (destructive state mutation). Requires stable id \u2014 obtain via calendar_list_events after disambiguating natural-language references; never pass a title as id and never delete from an ambiguous multi-match list. Use only when the user intends removal/cancellation, not to move, rename, shorten, or otherwise edit (calendar_update_event). Returns { deleted: true, event } where event is the deleted resource snapshot read immediately before removal.",
|
|
166
225
|
inputSchema: {
|
|
167
226
|
type: "object",
|
|
168
227
|
properties: {
|
|
169
|
-
id:
|
|
228
|
+
id: stableEventIdProperty
|
|
170
229
|
},
|
|
171
230
|
required: ["id"],
|
|
172
231
|
additionalProperties: false
|
|
173
232
|
},
|
|
174
|
-
annotations:
|
|
233
|
+
annotations: deleteAnnotations,
|
|
175
234
|
async execute(input, executeOptions) {
|
|
176
235
|
const { id } = input;
|
|
177
236
|
const signal = executionSignal(executeOptions);
|
|
178
237
|
const options = readOptions();
|
|
238
|
+
const existing = await options.events.get(id, { signal });
|
|
239
|
+
if (existing === null) {
|
|
240
|
+
throw new Error(`Event ${id} was not found.`);
|
|
241
|
+
}
|
|
242
|
+
const snapshot = cloneEvent(existing);
|
|
179
243
|
await options.events.delete(id, { signal });
|
|
180
244
|
await options.onEventsChanged();
|
|
181
|
-
return { deleted: true,
|
|
245
|
+
return { deleted: true, event: snapshot };
|
|
182
246
|
}
|
|
183
|
-
}
|
|
184
|
-
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return tools;
|
|
185
250
|
}
|
|
186
251
|
|
|
187
252
|
// src/register-tools.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tool-definitions.ts","../src/register-tools.ts","../src/use-fullcalendar-webmcp.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/// <reference types=\"webmcp-types\" />\n\nimport type {\n CreateCalendarEventInput,\n FullCalendarWebMCPOptions,\n UpdateCalendarEventInput,\n} from \"./types\";\n\ntype OptionsReader = () => FullCalendarWebMCPOptions;\n\nconst emptySchema = {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n} as const;\n\nconst eventProperties = {\n title: {\n type: \"string\",\n minLength: 1,\n description: \"The human-readable event title.\",\n },\n start: {\n type: \"string\",\n format: \"date-time\",\n description: \"Inclusive event start as ISO 8601 with an explicit offset.\",\n },\n end: {\n type: [\"string\", \"null\"],\n format: \"date-time\",\n description: \"Exclusive event end as ISO 8601 with an explicit offset, or null.\",\n },\n allDay: {\n type: \"boolean\",\n description: \"Whether the event is an all-day event.\",\n },\n} as const;\n\nfunction readAnnotations(): WebMCP.ToolAnnotations {\n return { readOnlyHint: true, untrustedContentHint: true };\n}\n\nfunction writeAnnotations(): WebMCP.ToolAnnotations {\n return { readOnlyHint: false, untrustedContentHint: true };\n}\n\n/**\n * WebMCP imperative execute steps always pass ToolExecuteCallbackOptions with a\n * fresh AbortSignal. Some agent shims still call execute(input) with one argument.\n * Never require-destructure options; forward signal when present.\n */\nfunction executionSignal(\n executeOptions: WebMCP.ToolExecuteCallbackOptions | undefined,\n): AbortSignal | undefined {\n return executeOptions?.signal;\n}\n\nexport function createCalendarTools(\n readOptions: OptionsReader,\n): WebMCP.ModelContextTool[] {\n return [\n {\n name: \"calendar_get_context\",\n title: \"Get calendar context\",\n description:\n \"Get the current time, resolved browser timezone, FullCalendar timezone, visible date range, and active view. Call this before interpreting relative dates such as Wednesday or tomorrow.\",\n inputSchema: emptySchema,\n annotations: readAnnotations(),\n async execute() {\n const api = readOptions().calendarRef.current?.getApi();\n return {\n now: new Date().toISOString(),\n browserTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n fullCalendarTimeZone: api?.getOption(\"timeZone\") ?? \"local\",\n view: api?.view.type ?? null,\n visibleRange: api\n ? {\n start: api.view.activeStart.toISOString(),\n end: api.view.activeEnd.toISOString(),\n }\n : null,\n };\n },\n },\n {\n name: \"calendar_list_events\",\n title: \"List calendar events\",\n description:\n \"List persisted calendar events, optionally filtered by an inclusive start, exclusive end, or case-insensitive title text. Use an end boundary one day after the requested day. Events may include optional host-selected metadata (JSON object) for agent context; Protocol Tooling does not automatically expose FullCalendar extendedProps.\",\n inputSchema: {\n type: \"object\",\n properties: {\n start: {\n type: \"string\",\n format: \"date-time\",\n description: \"Inclusive ISO 8601 lower bound.\",\n },\n end: {\n type: \"string\",\n format: \"date-time\",\n description: \"Exclusive ISO 8601 upper bound.\",\n },\n text: {\n type: \"string\",\n description: \"Optional case-insensitive title substring.\",\n },\n },\n additionalProperties: false,\n },\n annotations: readAnnotations(),\n async execute(query, executeOptions) {\n const signal = executionSignal(executeOptions);\n const events = await readOptions().events.list(\n query as { start?: string; end?: string; text?: string },\n { signal },\n );\n return { events };\n },\n },\n {\n name: \"calendar_get_event\",\n title: \"Get calendar event\",\n description:\n \"Get one persisted calendar event by its stable event ID. The event may include optional host-selected metadata (JSON object) for agent context; metadata is read-only and is never inferred from FullCalendar extendedProps.\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: { type: \"string\", minLength: 1, description: \"Stable event ID.\" },\n },\n required: [\"id\"],\n additionalProperties: false,\n },\n annotations: readAnnotations(),\n async execute(input, executeOptions) {\n const { id } = input as { id: string };\n const signal = executionSignal(executeOptions);\n const event = await readOptions().events.get(id, { signal });\n return { event };\n },\n },\n {\n name: \"calendar_create_event\",\n title: \"Create calendar event\",\n description:\n \"Create and persist a generic calendar event. Times must be ISO 8601 values with explicit offsets. Returns the host-assigned stable event ID. Metadata cannot be supplied on create; only title, start, end, and allDay are accepted.\",\n inputSchema: {\n type: \"object\",\n properties: eventProperties,\n required: [\"title\", \"start\"],\n additionalProperties: false,\n },\n annotations: writeAnnotations(),\n async execute(rawInput, executeOptions) {\n const input = rawInput as unknown as CreateCalendarEventInput;\n const signal = executionSignal(executeOptions);\n const options = readOptions();\n const event = await options.events.create(input, { signal });\n await options.onEventsChanged();\n return { event };\n },\n },\n {\n name: \"calendar_update_event\",\n title: \"Update calendar event\",\n description:\n \"Update selected fields on one persisted calendar event using its stable event ID. Omitted fields remain unchanged. Metadata cannot be supplied on update; only title, start, end, and allDay may be changed. Host-owned metadata is preserved by the repository.\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: { type: \"string\", minLength: 1, description: \"Stable event ID.\" },\n ...eventProperties,\n },\n required: [\"id\"],\n minProperties: 2,\n additionalProperties: false,\n },\n annotations: writeAnnotations(),\n async execute(rawInput, executeOptions) {\n const { id, ...input } = rawInput as unknown as UpdateCalendarEventInput & {\n id: string;\n };\n const signal = executionSignal(executeOptions);\n const options = readOptions();\n const event = await options.events.update(id, input, { signal });\n await options.onEventsChanged();\n return { event };\n },\n },\n {\n name: \"calendar_delete_event\",\n title: \"Delete calendar event\",\n description: \"Delete one persisted calendar event by its stable event ID.\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: { type: \"string\", minLength: 1, description: \"Stable event ID.\" },\n },\n required: [\"id\"],\n additionalProperties: false,\n },\n annotations: writeAnnotations(),\n async execute(input, executeOptions) {\n const { id } = input as { id: string };\n const signal = executionSignal(executeOptions);\n const options = readOptions();\n await options.events.delete(id, { signal });\n await options.onEventsChanged();\n return { deleted: true, id };\n },\n },\n ];\n}\n","import { createCalendarTools } from \"./tool-definitions\";\nimport type { FullCalendarWebMCPOptions } from \"./types\";\n\nconst RUNTIME_POLL_INTERVAL_MS = 250;\n\nexport function registerCalendarToolsWhenAvailable(\n readOptions: () => FullCalendarWebMCPOptions,\n lifecycleSignal: AbortSignal,\n) {\n let retryTimer: number | undefined;\n\n const register = async () => {\n if (lifecycleSignal.aborted) return;\n\n const modelContext = document.modelContext;\n if (!modelContext) {\n retryTimer = window.setTimeout(register, RUNTIME_POLL_INTERVAL_MS);\n return;\n }\n\n const registration = new AbortController();\n const unregister = () => registration.abort(lifecycleSignal.reason);\n lifecycleSignal.addEventListener(\"abort\", unregister, { once: true });\n\n try {\n await Promise.all(\n createCalendarTools(readOptions).map((tool) =>\n modelContext.registerTool(tool, { signal: registration.signal }),\n ),\n );\n } catch (error) {\n registration.abort();\n if (!lifecycleSignal.aborted) {\n readOptions().onRegistrationError?.(error);\n }\n }\n };\n\n void register();\n\n lifecycleSignal.addEventListener(\n \"abort\",\n () => {\n if (retryTimer !== undefined) window.clearTimeout(retryTimer);\n },\n { once: true },\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport { registerCalendarToolsWhenAvailable } from \"./register-tools\";\nimport type { FullCalendarWebMCPOptions } from \"./types\";\n\n/**\n * Primary React hook integrating FullCalendar with the browser WebMCP model context.\n *\n * Provides safe client-side registration, unmount cleanup, and continuous\n * binding to host persistence callbacks without re-registering tools across renders.\n */\nexport function useFullCalendarWebMCP(options: FullCalendarWebMCPOptions): void {\n const latestOptions = useRef(options);\n latestOptions.current = options;\n\n useEffect(() => {\n const lifecycle = new AbortController();\n registerCalendarToolsWhenAvailable(\n () => latestOptions.current,\n lifecycle.signal,\n );\n return () => lifecycle.abort();\n }, []);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/tool-definitions.ts","../src/register-tools.ts","../src/use-fullcalendar-webmcp.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/// <reference types=\"webmcp-types\" />\n\nimport type {\n CalendarEvent,\n CreateCalendarEventInput,\n FullCalendarWebMCPCapabilities,\n FullCalendarWebMCPOptions,\n UpdateCalendarEventInput,\n} from \"./types\";\n\ntype OptionsReader = () => FullCalendarWebMCPOptions;\n\nconst emptySchema = {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n} as const;\n\nconst stableEventIdProperty = {\n type: \"string\",\n minLength: 1,\n description:\n \"Stable id of the persisted event. Obtain from calendar_list_events, calendar_get_event, or a prior create/update result. Never substitute an event title.\",\n} as const;\n\nconst queryStartProperty = {\n type: \"string\",\n format: \"date-time\",\n description:\n \"Inclusive lower bound for filtering persisted events (ISO 8601 date-time with explicit offset). Omit to include events from any earlier time.\",\n} as const;\n\nconst queryEndProperty = {\n type: \"string\",\n format: \"date-time\",\n description:\n \"Exclusive upper bound for filtering persisted events (ISO 8601 date-time with explicit offset). For one calendar day, set end to the start of the following day. Omit for no upper limit.\",\n} as const;\n\nconst queryTextProperty = {\n type: \"string\",\n description:\n \"Case-insensitive substring matched against event titles. Omit to skip title filtering.\",\n} as const;\n\nconst createEventTitleProperty = {\n type: \"string\",\n minLength: 1,\n description: \"Display title for the new event.\",\n} as const;\n\nconst updateEventTitleProperty = {\n type: \"string\",\n minLength: 1,\n description: \"New display title. Omit to leave the current title unchanged.\",\n} as const;\n\nconst createEventStartProperty = {\n type: \"string\",\n format: \"date-time\",\n description:\n \"Inclusive event start as ISO 8601 date-time with an explicit offset (e.g. 2026-09-02T14:00:00-06:00). Resolve relative times with calendar_get_context first.\",\n} as const;\n\nconst updateEventStartProperty = {\n type: \"string\",\n format: \"date-time\",\n description:\n \"New inclusive event start (ISO 8601 with explicit offset). Omit to leave the current start unchanged.\",\n} as const;\n\nconst createEventEndProperty = {\n type: [\"string\", \"null\"],\n format: \"date-time\",\n description:\n \"Exclusive event end as ISO 8601 date-time with explicit offset, or null for open-ended. Omit on create to use host defaults.\",\n} as const;\n\nconst updateEventEndProperty = {\n type: [\"string\", \"null\"],\n format: \"date-time\",\n description:\n \"New exclusive event end (ISO 8601 with explicit offset) or null. Omit to leave the current end unchanged.\",\n} as const;\n\nconst createAllDayProperty = {\n type: \"boolean\",\n description:\n \"Whether the event spans full calendar days without clock times. Defaults to false when omitted.\",\n} as const;\n\nconst updateAllDayProperty = {\n type: \"boolean\",\n description: \"Whether the event is all-day. Omit to leave unchanged.\",\n} as const;\n\n/** Library-generated viewport/time context — not user-authored event content. */\nconst contextAnnotations: WebMCP.ToolAnnotations = { readOnlyHint: true };\n\n/** Persisted event reads may include user-controlled titles and host metadata. */\nconst eventReadAnnotations: WebMCP.ToolAnnotations = {\n readOnlyHint: true,\n untrustedContentHint: true,\n};\n\n/** Mutations that return persisted event records with user-controlled fields. */\nconst eventWriteAnnotations: WebMCP.ToolAnnotations = {\n readOnlyHint: false,\n untrustedContentHint: true,\n};\n\n/** Destructive mutation with a minimal structured confirmation payload. */\nconst deleteAnnotations: WebMCP.ToolAnnotations = { readOnlyHint: false };\n\nexport function resolveCapabilities(\n capabilities?: FullCalendarWebMCPCapabilities,\n): Required<FullCalendarWebMCPCapabilities> {\n return {\n read: capabilities?.read ?? true,\n create: capabilities?.create ?? true,\n update: capabilities?.update ?? true,\n delete: capabilities?.delete ?? true,\n };\n}\n\n/**\n * WebMCP imperative execute steps always pass ToolExecuteCallbackOptions with a\n * fresh AbortSignal. Some agent shims still call execute(input) with one argument.\n * Never require-destructure options; forward signal when present.\n */\nfunction executionSignal(\n executeOptions: WebMCP.ToolExecuteCallbackOptions | undefined,\n): AbortSignal | undefined {\n return executeOptions?.signal;\n}\n\n/** JSON-clone so delete snapshots cannot mutate repository-owned objects. */\nfunction cloneEvent(event: CalendarEvent): CalendarEvent {\n return JSON.parse(JSON.stringify(event)) as CalendarEvent;\n}\n\nexport function createCalendarTools(\n readOptions: OptionsReader,\n): WebMCP.ModelContextTool[] {\n const capabilities = resolveCapabilities(readOptions().capabilities);\n const tools: WebMCP.ModelContextTool[] = [\n {\n name: \"calendar_get_context\",\n title: \"Get calendar context\",\n description:\n \"Read-only temporal and viewport context for the open calendar — not persisted events. \" +\n \"Use when the user mentions relative times (today, tomorrow, tonight, Wednesday, next week, this afternoon, first week of next month) to anchor ISO date-times before list/create/update. \" +\n \"Returns now, browserTimeZone, fullCalendarTimeZone, active view, and visibleRange. \" +\n \"Skip when every datetime is already explicit with offsets and no relative interpretation is needed. \" +\n \"Does not replace calendar_list_events for event discovery.\",\n inputSchema: emptySchema,\n annotations: contextAnnotations,\n async execute() {\n const api = readOptions().calendarRef.current?.getApi();\n return {\n now: new Date().toISOString(),\n browserTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n fullCalendarTimeZone: api?.getOption(\"timeZone\") ?? \"local\",\n view: api?.view.type ?? null,\n visibleRange: api\n ? {\n start: api.view.activeStart.toISOString(),\n end: api.view.activeEnd.toISOString(),\n }\n : null,\n };\n },\n },\n ];\n\n if (capabilities.read) {\n tools.push(\n {\n name: \"calendar_list_events\",\n title: \"List calendar events\",\n description:\n \"Read-only search and discovery over persisted calendar events. \" +\n \"Use to answer what is scheduled, filter by date range or title, find candidate events before update/delete, or disambiguate which event the user means. \" +\n \"start is inclusive; end is exclusive — for one calendar day set end to the following day's boundary. \" +\n \"text is a case-insensitive title substring; omit any filter to broaden results. \" +\n \"Returns events with stable id values usable in get/update/delete; optional host-selected metadata may appear and is untrusted. \" +\n \"Do not use when you already hold the target id (prefer calendar_get_event) or only need viewport/time context (calendar_get_context). \" +\n \"Never delete or mutate from an ambiguous multi-match list alone.\",\n inputSchema: {\n type: \"object\",\n properties: {\n start: queryStartProperty,\n end: queryEndProperty,\n text: queryTextProperty,\n },\n additionalProperties: false,\n },\n annotations: eventReadAnnotations,\n async execute(query, executeOptions) {\n const signal = executionSignal(executeOptions);\n const events = await readOptions().events.list(\n query as { start?: string; end?: string; text?: string },\n { signal },\n );\n return { events };\n },\n },\n {\n name: \"calendar_get_event\",\n title: \"Get calendar event\",\n description:\n \"Read-only fetch of exactly one persisted event by stable id. \" +\n \"Use when you already have the id from calendar_list_events, a prior create/update result, or the host app — or to re-read authoritative fields before a mutation when list results are insufficient. \" +\n \"Returns { event: CalendarEvent | null }; null means no matching persisted event. \" +\n \"Do not use for title/date search (calendar_list_events), do not invent ids from titles, and do not substitute a title for id.\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: stableEventIdProperty,\n },\n required: [\"id\"],\n additionalProperties: false,\n },\n annotations: eventReadAnnotations,\n async execute(input, executeOptions) {\n const { id } = input as { id: string };\n const signal = executionSignal(executeOptions);\n const event = await readOptions().events.get(id, { signal });\n return { event };\n },\n },\n );\n }\n\n if (capabilities.create) {\n tools.push({\n name: \"calendar_create_event\",\n title: \"Create calendar event\",\n description:\n \"Create and persist a new calendar event (state mutation). \" +\n \"Use only for brand-new events the user wants scheduled. \" +\n \"Requires title and start (ISO 8601 with explicit offset); optional end (exclusive, ISO 8601 or null) and allDay. \" +\n \"Resolve relative times via calendar_get_context first. \" +\n \"Returns the persisted event including host-assigned id. Metadata cannot be supplied. \" +\n \"Do not use to modify, reschedule, or rename an existing event — use calendar_update_event.\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: createEventTitleProperty,\n start: createEventStartProperty,\n end: createEventEndProperty,\n allDay: createAllDayProperty,\n },\n required: [\"title\", \"start\"],\n additionalProperties: false,\n },\n annotations: eventWriteAnnotations,\n async execute(rawInput, executeOptions) {\n const input = rawInput as unknown as CreateCalendarEventInput;\n const signal = executionSignal(executeOptions);\n const options = readOptions();\n const event = await options.events.create(input, { signal });\n await options.onEventsChanged();\n return { event };\n },\n });\n }\n\n if (capabilities.update) {\n tools.push({\n name: \"calendar_update_event\",\n title: \"Update calendar event\",\n description:\n \"Persisted partial update of exactly one existing event (state mutation). \" +\n \"Requires stable id plus at least one of title, start, end, allDay; omitted fields stay unchanged. \" +\n \"Host-selected metadata is preserved and cannot be written here. \" +\n \"When the user names an event without an id, find it with calendar_list_events first; calendar_get_event is optional if the list row already has sufficient detail. \" +\n \"Use for reschedule, rename, time/end changes, or all-day toggles — not for creating a duplicate or deleting (calendar_create_event / calendar_delete_event).\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: stableEventIdProperty,\n title: updateEventTitleProperty,\n start: updateEventStartProperty,\n end: updateEventEndProperty,\n allDay: updateAllDayProperty,\n },\n required: [\"id\"],\n minProperties: 2,\n additionalProperties: false,\n },\n annotations: eventWriteAnnotations,\n async execute(rawInput, executeOptions) {\n const { id, ...input } = rawInput as unknown as UpdateCalendarEventInput & {\n id: string;\n };\n const signal = executionSignal(executeOptions);\n const options = readOptions();\n const event = await options.events.update(id, input, { signal });\n await options.onEventsChanged();\n return { event };\n },\n });\n }\n\n if (capabilities.delete) {\n tools.push({\n name: \"calendar_delete_event\",\n title: \"Delete calendar event\",\n description:\n \"Permanently delete exactly one persisted calendar event (destructive state mutation). \" +\n \"Requires stable id — obtain via calendar_list_events after disambiguating natural-language references; never pass a title as id and never delete from an ambiguous multi-match list. \" +\n \"Use only when the user intends removal/cancellation, not to move, rename, shorten, or otherwise edit (calendar_update_event). \" +\n \"Returns { deleted: true, event } where event is the deleted resource snapshot read immediately before removal.\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: stableEventIdProperty,\n },\n required: [\"id\"],\n additionalProperties: false,\n },\n annotations: deleteAnnotations,\n async execute(input, executeOptions) {\n const { id } = input as { id: string };\n const signal = executionSignal(executeOptions);\n const options = readOptions();\n const existing = await options.events.get(id, { signal });\n if (existing === null) {\n throw new Error(`Event ${id} was not found.`);\n }\n const snapshot = cloneEvent(existing);\n await options.events.delete(id, { signal });\n await options.onEventsChanged();\n return { deleted: true, event: snapshot };\n },\n });\n }\n\n return tools;\n}\n","import { createCalendarTools } from \"./tool-definitions\";\nimport type { FullCalendarWebMCPOptions } from \"./types\";\n\nconst RUNTIME_POLL_INTERVAL_MS = 250;\n\nexport function registerCalendarToolsWhenAvailable(\n readOptions: () => FullCalendarWebMCPOptions,\n lifecycleSignal: AbortSignal,\n) {\n let retryTimer: number | undefined;\n\n const register = async () => {\n if (lifecycleSignal.aborted) return;\n\n const modelContext = document.modelContext;\n if (!modelContext) {\n retryTimer = window.setTimeout(register, RUNTIME_POLL_INTERVAL_MS);\n return;\n }\n\n const registration = new AbortController();\n const unregister = () => registration.abort(lifecycleSignal.reason);\n lifecycleSignal.addEventListener(\"abort\", unregister, { once: true });\n\n try {\n await Promise.all(\n createCalendarTools(readOptions).map((tool) =>\n modelContext.registerTool(tool, { signal: registration.signal }),\n ),\n );\n } catch (error) {\n registration.abort();\n if (!lifecycleSignal.aborted) {\n readOptions().onRegistrationError?.(error);\n }\n }\n };\n\n void register();\n\n lifecycleSignal.addEventListener(\n \"abort\",\n () => {\n if (retryTimer !== undefined) window.clearTimeout(retryTimer);\n },\n { once: true },\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport { registerCalendarToolsWhenAvailable } from \"./register-tools\";\nimport type { FullCalendarWebMCPOptions } from \"./types\";\n\n/**\n * Primary React hook integrating FullCalendar with the browser WebMCP model context.\n *\n * Provides safe client-side registration, unmount cleanup, and continuous\n * binding to host persistence callbacks without re-registering tools across renders.\n */\nexport function useFullCalendarWebMCP(options: FullCalendarWebMCPOptions): void {\n const latestOptions = useRef(options);\n latestOptions.current = options;\n\n useEffect(() => {\n const lifecycle = new AbortController();\n registerCalendarToolsWhenAvailable(\n () => latestOptions.current,\n lifecycle.signal,\n );\n return () => lifecycle.abort();\n }, []);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protocoltooling/fullcalendar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Add WebMCP tools to FullCalendar React applications.",
|
|
5
|
+
"author": "Roberto Nevarez",
|
|
5
6
|
"publishConfig": {
|
|
6
7
|
"access": "public"
|
|
7
8
|
},
|
|
@@ -9,20 +10,19 @@
|
|
|
9
10
|
"webmcp",
|
|
10
11
|
"fullcalendar",
|
|
11
12
|
"react",
|
|
12
|
-
"mcp",
|
|
13
|
-
"model-context-protocol",
|
|
14
13
|
"calendar",
|
|
15
|
-
"ai-agents"
|
|
14
|
+
"ai-agents",
|
|
15
|
+
"browser-tools"
|
|
16
16
|
],
|
|
17
17
|
"type": "module",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/robertonevarez/
|
|
21
|
+
"url": "git+https://github.com/robertonevarez/fullcalendar-webmcp.git"
|
|
22
22
|
},
|
|
23
|
-
"homepage": "https://
|
|
23
|
+
"homepage": "https://protocoltooling.com/integrations/fullcalendar",
|
|
24
24
|
"bugs": {
|
|
25
|
-
"url": "https://github.com/robertonevarez/
|
|
25
|
+
"url": "https://github.com/robertonevarez/fullcalendar-webmcp/issues"
|
|
26
26
|
},
|
|
27
27
|
"main": "./dist/index.js",
|
|
28
28
|
"module": "./dist/index.js",
|