gogcli-mcp-calendar 2.1.0 → 2.3.0
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/dist/index.js +163 -16
- package/manifest.json +29 -1
- package/package.json +1 -1
- package/src/tools/calendar-extra.ts +135 -0
- package/tests/tools/calendar-extra.test.ts +177 -0
package/dist/index.js
CHANGED
|
@@ -31074,7 +31074,7 @@ async function run(args, options = {}) {
|
|
|
31074
31074
|
|
|
31075
31075
|
// ../gogcli-mcp/src/tools/utils.ts
|
|
31076
31076
|
var accountParam = external_exports.string().optional().describe(
|
|
31077
|
-
"Google account email to use
|
|
31077
|
+
"Google account email to use, e.g. you@gmail.com \u2014 must be the full address, not a bare username. Overrides the GOG_ACCOUNT env var. Omit to use the single configured account."
|
|
31078
31078
|
);
|
|
31079
31079
|
var ids = {
|
|
31080
31080
|
course: external_exports.string().describe("Course ID"),
|
|
@@ -31133,26 +31133,41 @@ function toError(err) {
|
|
|
31133
31133
|
}
|
|
31134
31134
|
var AUTH_ERROR_PATTERN = /\b(401|unauthorized|token.*(expired|revoked)|invalid_grant)\b/i;
|
|
31135
31135
|
var TRANSIENT_ERROR_PATTERN = /\b429\b|\b5\d\d\b|\bquota\b|rateLimit|\bDEADLINE_EXCEEDED\b/i;
|
|
31136
|
+
var GRID_LIMIT_ERROR_PATTERN = /exceeds grid limits/i;
|
|
31136
31137
|
var AUTH_HINT = "\n\nAuthentication may have expired. Use gog_auth_add to re-authorize the account. Ask the user if they would like to re-authenticate.";
|
|
31137
31138
|
var TRANSIENT_HINT = "\n\nThis error is often transient. Retry the same call before trying a different approach (do not fall back to smaller writes or row-by-row operations).";
|
|
31139
|
+
var GRID_LIMIT_HINT = "\n\nThe target range is outside the sheet's current grid. Add the missing rows or columns first with gog_sheets_insert (dimension: rows or cols), then retry the write.";
|
|
31140
|
+
function formatAccountList(raw) {
|
|
31141
|
+
try {
|
|
31142
|
+
const parsed = JSON.parse(raw);
|
|
31143
|
+
if (Array.isArray(parsed?.accounts)) {
|
|
31144
|
+
return parsed.accounts.map((a) => a?.email).filter(Boolean).join("\n");
|
|
31145
|
+
}
|
|
31146
|
+
} catch {
|
|
31147
|
+
}
|
|
31148
|
+
return raw.trim();
|
|
31149
|
+
}
|
|
31150
|
+
async function diagnose(err) {
|
|
31151
|
+
const errText = toError(err).content[0].text;
|
|
31152
|
+
const isAuthError = AUTH_ERROR_PATTERN.test(errText);
|
|
31153
|
+
const isTransientError = !isAuthError && TRANSIENT_ERROR_PATTERN.test(errText);
|
|
31154
|
+
const isGridLimitError = GRID_LIMIT_ERROR_PATTERN.test(errText);
|
|
31155
|
+
const hint = isAuthError ? AUTH_HINT : isTransientError ? TRANSIENT_HINT : isGridLimitError ? GRID_LIMIT_HINT : "";
|
|
31156
|
+
try {
|
|
31157
|
+
const accounts = formatAccountList(await run(["auth", "list"]));
|
|
31158
|
+
return toText(`${errText}
|
|
31159
|
+
|
|
31160
|
+
Configured accounts:
|
|
31161
|
+
${accounts || "(none)"}${hint}`);
|
|
31162
|
+
} catch {
|
|
31163
|
+
return toText(`${errText}${hint}`);
|
|
31164
|
+
}
|
|
31165
|
+
}
|
|
31138
31166
|
async function runOrDiagnose(args, options) {
|
|
31139
31167
|
try {
|
|
31140
31168
|
return toText(await run(args, options));
|
|
31141
31169
|
} catch (err) {
|
|
31142
|
-
|
|
31143
|
-
const errText = base.content[0].text;
|
|
31144
|
-
const isAuthError = AUTH_ERROR_PATTERN.test(errText);
|
|
31145
|
-
const isTransientError = !isAuthError && TRANSIENT_ERROR_PATTERN.test(errText);
|
|
31146
|
-
const hint = isAuthError ? AUTH_HINT : isTransientError ? TRANSIENT_HINT : "";
|
|
31147
|
-
try {
|
|
31148
|
-
const accounts = await run(["auth", "list"]);
|
|
31149
|
-
return toText(`${errText}
|
|
31150
|
-
|
|
31151
|
-
Configured accounts:
|
|
31152
|
-
${accounts}${hint}`);
|
|
31153
|
-
} catch {
|
|
31154
|
-
return toText(`${errText}${hint}`);
|
|
31155
|
-
}
|
|
31170
|
+
return diagnose(err);
|
|
31156
31171
|
}
|
|
31157
31172
|
}
|
|
31158
31173
|
|
|
@@ -31338,9 +31353,15 @@ function registerCalendarTools(server2) {
|
|
|
31338
31353
|
|
|
31339
31354
|
// ../gogcli-mcp/src/tools/sheets.ts
|
|
31340
31355
|
var cellValueParam = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]);
|
|
31356
|
+
var dryRunParam = external_exports.boolean().optional().describe(
|
|
31357
|
+
"Preview the operation without modifying the sheet (gog --dry-run): reports the intended actions and exits without writing."
|
|
31358
|
+
);
|
|
31359
|
+
var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
31360
|
+
'Safety guard against silent overwrites: before writing, read the target range and refuse the write if any target cell already holds data. Costs one extra read. Anchor ranges (e.g. "Sheet1!A1") are expanded to the full area your values will cover; explicit and named ranges are checked as-is.'
|
|
31361
|
+
);
|
|
31341
31362
|
|
|
31342
31363
|
// ../gogcli-mcp/src/server.ts
|
|
31343
|
-
var VERSION = true ? "2.
|
|
31364
|
+
var VERSION = true ? "2.3.0" : "0.0.0";
|
|
31344
31365
|
function createServer(options) {
|
|
31345
31366
|
return new McpServer({
|
|
31346
31367
|
name: options?.name ?? "gogcli",
|
|
@@ -31444,6 +31465,132 @@ function registerExtraCalendarTools(server2) {
|
|
|
31444
31465
|
if (alias) args.push(`--alias=${alias}`);
|
|
31445
31466
|
return runOrDiagnose(args, {});
|
|
31446
31467
|
});
|
|
31468
|
+
server2.registerTool("gog_calendar_calendars", {
|
|
31469
|
+
description: "List the calendars in your calendar list (id, summary, access role, primary flag).",
|
|
31470
|
+
annotations: { readOnlyHint: true },
|
|
31471
|
+
inputSchema: {
|
|
31472
|
+
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
31473
|
+
page: external_exports.string().optional().describe("Page token"),
|
|
31474
|
+
all: external_exports.boolean().optional().describe("Fetch all pages"),
|
|
31475
|
+
account: accountParam
|
|
31476
|
+
}
|
|
31477
|
+
}, async ({ max, page, all, account }) => {
|
|
31478
|
+
const args = ["calendar", "calendars"];
|
|
31479
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31480
|
+
if (page) args.push(`--page=${page}`);
|
|
31481
|
+
if (all) args.push("--all");
|
|
31482
|
+
return runOrDiagnose(args, { account });
|
|
31483
|
+
});
|
|
31484
|
+
server2.registerTool("gog_calendar_search", {
|
|
31485
|
+
description: "Full-text search for events matching a query string, with optional time filters.",
|
|
31486
|
+
annotations: { readOnlyHint: true },
|
|
31487
|
+
inputSchema: {
|
|
31488
|
+
query: external_exports.string().describe("Search query"),
|
|
31489
|
+
from: external_exports.string().optional().describe("Start time (RFC3339, date, or relative: now, today, tomorrow, monday)"),
|
|
31490
|
+
to: external_exports.string().optional().describe("End time (RFC3339, date, or relative: now, today, tomorrow, monday)"),
|
|
31491
|
+
today: external_exports.boolean().optional().describe("Today only"),
|
|
31492
|
+
tomorrow: external_exports.boolean().optional().describe("Tomorrow only"),
|
|
31493
|
+
week: external_exports.boolean().optional().describe("This week (uses weekStart, default Mon)"),
|
|
31494
|
+
days: external_exports.number().optional().describe("Next N days"),
|
|
31495
|
+
weekStart: external_exports.string().optional().describe("Week start day for week (sun, mon, ...)"),
|
|
31496
|
+
calendar: external_exports.string().optional().describe("Calendar ID (default: primary)"),
|
|
31497
|
+
max: external_exports.number().optional().describe("Max results (default: 25)"),
|
|
31498
|
+
account: accountParam
|
|
31499
|
+
}
|
|
31500
|
+
}, async ({ query, from, to, today, tomorrow, week, days, weekStart, calendar, max, account }) => {
|
|
31501
|
+
const args = ["calendar", "search", query];
|
|
31502
|
+
if (from) args.push(`--from=${from}`);
|
|
31503
|
+
if (to) args.push(`--to=${to}`);
|
|
31504
|
+
if (today) args.push("--today");
|
|
31505
|
+
if (tomorrow) args.push("--tomorrow");
|
|
31506
|
+
if (week) args.push("--week");
|
|
31507
|
+
if (days !== void 0) args.push(`--days=${days}`);
|
|
31508
|
+
if (weekStart) args.push(`--week-start=${weekStart}`);
|
|
31509
|
+
if (calendar) args.push(`--calendar=${calendar}`);
|
|
31510
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31511
|
+
return runOrDiagnose(args, { account });
|
|
31512
|
+
});
|
|
31513
|
+
server2.registerTool("gog_calendar_freebusy", {
|
|
31514
|
+
description: "Query free/busy intervals for one or more calendars over a time window.",
|
|
31515
|
+
annotations: { readOnlyHint: true },
|
|
31516
|
+
inputSchema: {
|
|
31517
|
+
from: external_exports.string().describe("Start time (RFC3339, required)"),
|
|
31518
|
+
to: external_exports.string().describe("End time (RFC3339, required)"),
|
|
31519
|
+
calendarIds: external_exports.string().optional().describe("Comma-separated calendar IDs, names, or indices"),
|
|
31520
|
+
all: external_exports.boolean().optional().describe("Query all calendars"),
|
|
31521
|
+
account: accountParam
|
|
31522
|
+
}
|
|
31523
|
+
}, async ({ from, to, calendarIds, all, account }) => {
|
|
31524
|
+
const args = ["calendar", "freebusy"];
|
|
31525
|
+
if (calendarIds) args.push(calendarIds);
|
|
31526
|
+
args.push(`--from=${from}`);
|
|
31527
|
+
args.push(`--to=${to}`);
|
|
31528
|
+
if (all) args.push("--all");
|
|
31529
|
+
return runOrDiagnose(args, { account });
|
|
31530
|
+
});
|
|
31531
|
+
server2.registerTool("gog_calendar_colors", {
|
|
31532
|
+
description: "Show the available calendar and event color palette (color IDs to hex values).",
|
|
31533
|
+
annotations: { readOnlyHint: true },
|
|
31534
|
+
inputSchema: {
|
|
31535
|
+
account: accountParam
|
|
31536
|
+
}
|
|
31537
|
+
}, async ({ account }) => {
|
|
31538
|
+
return runOrDiagnose(["calendar", "colors"], { account });
|
|
31539
|
+
});
|
|
31540
|
+
server2.registerTool("gog_calendar_acl", {
|
|
31541
|
+
description: "List the access control list (sharing rules) for a calendar.",
|
|
31542
|
+
annotations: { readOnlyHint: true },
|
|
31543
|
+
inputSchema: {
|
|
31544
|
+
calendarId: external_exports.string().describe("Calendar ID"),
|
|
31545
|
+
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
31546
|
+
page: external_exports.string().optional().describe("Page token"),
|
|
31547
|
+
all: external_exports.boolean().optional().describe("Fetch all pages"),
|
|
31548
|
+
account: accountParam
|
|
31549
|
+
}
|
|
31550
|
+
}, async ({ calendarId, max, page, all, account }) => {
|
|
31551
|
+
const args = ["calendar", "acl", calendarId];
|
|
31552
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31553
|
+
if (page) args.push(`--page=${page}`);
|
|
31554
|
+
if (all) args.push("--all");
|
|
31555
|
+
return runOrDiagnose(args, { account });
|
|
31556
|
+
});
|
|
31557
|
+
server2.registerTool("gog_calendar_move", {
|
|
31558
|
+
description: "Move an event from one calendar to another; the destination calendar becomes the organizer.",
|
|
31559
|
+
inputSchema: {
|
|
31560
|
+
calendarId: external_exports.string().describe("Source calendar ID"),
|
|
31561
|
+
eventId: external_exports.string().describe("Event ID"),
|
|
31562
|
+
destinationCalendarId: external_exports.string().describe("Destination calendar ID that becomes the event organizer"),
|
|
31563
|
+
sendUpdates: external_exports.enum(["all", "externalOnly", "none"]).optional().describe("Notification mode (default: none)"),
|
|
31564
|
+
account: accountParam
|
|
31565
|
+
}
|
|
31566
|
+
}, async ({ calendarId, eventId, destinationCalendarId, sendUpdates, account }) => {
|
|
31567
|
+
const args = ["calendar", "move", calendarId, eventId, destinationCalendarId];
|
|
31568
|
+
if (sendUpdates) args.push(`--send-updates=${sendUpdates}`);
|
|
31569
|
+
return runOrDiagnose(args, { account });
|
|
31570
|
+
});
|
|
31571
|
+
server2.registerTool("gog_calendar_out_of_office", {
|
|
31572
|
+
description: "Create an Out of Office event that auto-declines invitations during the block.",
|
|
31573
|
+
inputSchema: {
|
|
31574
|
+
from: external_exports.string().describe("Start date or datetime (RFC3339 or YYYY-MM-DD)"),
|
|
31575
|
+
to: external_exports.string().describe("End date or datetime (RFC3339 or YYYY-MM-DD)"),
|
|
31576
|
+
calendarId: external_exports.string().optional().describe("Calendar ID (default: primary)"),
|
|
31577
|
+
summary: external_exports.string().optional().describe('Out of office title (default: "Out of office")'),
|
|
31578
|
+
autoDecline: external_exports.enum(["none", "all", "new"]).optional().describe("Auto-decline mode (default: all)"),
|
|
31579
|
+
declineMessage: external_exports.string().optional().describe("Message for declined invitations"),
|
|
31580
|
+
allDay: external_exports.boolean().optional().describe("Create as an all-day event"),
|
|
31581
|
+
account: accountParam
|
|
31582
|
+
}
|
|
31583
|
+
}, async ({ from, to, calendarId, summary, autoDecline, declineMessage, allDay, account }) => {
|
|
31584
|
+
const args = ["calendar", "out-of-office"];
|
|
31585
|
+
if (calendarId) args.push(calendarId);
|
|
31586
|
+
args.push(`--from=${from}`);
|
|
31587
|
+
args.push(`--to=${to}`);
|
|
31588
|
+
if (summary) args.push(`--summary=${summary}`);
|
|
31589
|
+
if (autoDecline) args.push(`--auto-decline=${autoDecline}`);
|
|
31590
|
+
if (declineMessage) args.push(`--decline-message=${declineMessage}`);
|
|
31591
|
+
if (allDay) args.push("--all-day");
|
|
31592
|
+
return runOrDiagnose(args, { account });
|
|
31593
|
+
});
|
|
31447
31594
|
server2.registerTool("gog_meet_participants", {
|
|
31448
31595
|
description: "List participants from the latest (or a specific) Meet call.",
|
|
31449
31596
|
annotations: { readOnlyHint: true },
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifest_version": "0.3",
|
|
4
4
|
"name": "gogcli-mcp-calendar",
|
|
5
5
|
"display_name": "gogcli (Calendar)",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.3.0",
|
|
7
7
|
"description": "Extended Google Calendar for Claude via gogcli — auth + Calendar events + Meet space management",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
|
@@ -102,6 +102,34 @@
|
|
|
102
102
|
"name": "gog_calendar_run",
|
|
103
103
|
"description": "Run any gog calendar subcommand (escape hatch)"
|
|
104
104
|
},
|
|
105
|
+
{
|
|
106
|
+
"name": "gog_calendar_calendars",
|
|
107
|
+
"description": "List the calendars in your calendar list"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"name": "gog_calendar_search",
|
|
111
|
+
"description": "Full-text search for events with optional time filters"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "gog_calendar_freebusy",
|
|
115
|
+
"description": "Query free/busy intervals for one or more calendars"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "gog_calendar_colors",
|
|
119
|
+
"description": "Show the available calendar and event color palette"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "gog_calendar_acl",
|
|
123
|
+
"description": "List the access control list (sharing rules) for a calendar"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "gog_calendar_move",
|
|
127
|
+
"description": "Move an event to another calendar"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "gog_calendar_out_of_office",
|
|
131
|
+
"description": "Create an Out of Office event that auto-declines invitations"
|
|
132
|
+
},
|
|
105
133
|
{
|
|
106
134
|
"name": "gog_meet_create",
|
|
107
135
|
"description": "Create a Google Meet space"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-calendar",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"mcpName": "io.github.chrischall/gogcli-mcp-calendar",
|
|
5
5
|
"description": "Extended Google Calendar + Meet MCP server via gogcli — auth + Calendar events + Meet space management",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -110,6 +110,141 @@ export function registerExtraCalendarTools(server: McpServer): void {
|
|
|
110
110
|
return runOrDiagnose(args, {});
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
+
// --- gog 0.19.0 calendar reads & CRUD ---
|
|
114
|
+
|
|
115
|
+
server.registerTool('gog_calendar_calendars', {
|
|
116
|
+
description: 'List the calendars in your calendar list (id, summary, access role, primary flag).',
|
|
117
|
+
annotations: { readOnlyHint: true },
|
|
118
|
+
inputSchema: {
|
|
119
|
+
max: z.number().optional().describe('Max results (default: 100)'),
|
|
120
|
+
page: z.string().optional().describe('Page token'),
|
|
121
|
+
all: z.boolean().optional().describe('Fetch all pages'),
|
|
122
|
+
account: accountParam,
|
|
123
|
+
},
|
|
124
|
+
}, async ({ max, page, all, account }) => {
|
|
125
|
+
const args = ['calendar', 'calendars'];
|
|
126
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
127
|
+
if (page) args.push(`--page=${page}`);
|
|
128
|
+
if (all) args.push('--all');
|
|
129
|
+
return runOrDiagnose(args, { account });
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
server.registerTool('gog_calendar_search', {
|
|
133
|
+
description: 'Full-text search for events matching a query string, with optional time filters.',
|
|
134
|
+
annotations: { readOnlyHint: true },
|
|
135
|
+
inputSchema: {
|
|
136
|
+
query: z.string().describe('Search query'),
|
|
137
|
+
from: z.string().optional().describe('Start time (RFC3339, date, or relative: now, today, tomorrow, monday)'),
|
|
138
|
+
to: z.string().optional().describe('End time (RFC3339, date, or relative: now, today, tomorrow, monday)'),
|
|
139
|
+
today: z.boolean().optional().describe('Today only'),
|
|
140
|
+
tomorrow: z.boolean().optional().describe('Tomorrow only'),
|
|
141
|
+
week: z.boolean().optional().describe('This week (uses weekStart, default Mon)'),
|
|
142
|
+
days: z.number().optional().describe('Next N days'),
|
|
143
|
+
weekStart: z.string().optional().describe('Week start day for week (sun, mon, ...)'),
|
|
144
|
+
calendar: z.string().optional().describe('Calendar ID (default: primary)'),
|
|
145
|
+
max: z.number().optional().describe('Max results (default: 25)'),
|
|
146
|
+
account: accountParam,
|
|
147
|
+
},
|
|
148
|
+
}, async ({ query, from, to, today, tomorrow, week, days, weekStart, calendar, max, account }) => {
|
|
149
|
+
const args = ['calendar', 'search', query];
|
|
150
|
+
if (from) args.push(`--from=${from}`);
|
|
151
|
+
if (to) args.push(`--to=${to}`);
|
|
152
|
+
if (today) args.push('--today');
|
|
153
|
+
if (tomorrow) args.push('--tomorrow');
|
|
154
|
+
if (week) args.push('--week');
|
|
155
|
+
if (days !== undefined) args.push(`--days=${days}`);
|
|
156
|
+
if (weekStart) args.push(`--week-start=${weekStart}`);
|
|
157
|
+
if (calendar) args.push(`--calendar=${calendar}`);
|
|
158
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
159
|
+
return runOrDiagnose(args, { account });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
server.registerTool('gog_calendar_freebusy', {
|
|
163
|
+
description: 'Query free/busy intervals for one or more calendars over a time window.',
|
|
164
|
+
annotations: { readOnlyHint: true },
|
|
165
|
+
inputSchema: {
|
|
166
|
+
from: z.string().describe('Start time (RFC3339, required)'),
|
|
167
|
+
to: z.string().describe('End time (RFC3339, required)'),
|
|
168
|
+
calendarIds: z.string().optional().describe('Comma-separated calendar IDs, names, or indices'),
|
|
169
|
+
all: z.boolean().optional().describe('Query all calendars'),
|
|
170
|
+
account: accountParam,
|
|
171
|
+
},
|
|
172
|
+
}, async ({ from, to, calendarIds, all, account }) => {
|
|
173
|
+
const args = ['calendar', 'freebusy'];
|
|
174
|
+
if (calendarIds) args.push(calendarIds);
|
|
175
|
+
args.push(`--from=${from}`);
|
|
176
|
+
args.push(`--to=${to}`);
|
|
177
|
+
if (all) args.push('--all');
|
|
178
|
+
return runOrDiagnose(args, { account });
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
server.registerTool('gog_calendar_colors', {
|
|
182
|
+
description: 'Show the available calendar and event color palette (color IDs to hex values).',
|
|
183
|
+
annotations: { readOnlyHint: true },
|
|
184
|
+
inputSchema: {
|
|
185
|
+
account: accountParam,
|
|
186
|
+
},
|
|
187
|
+
}, async ({ account }) => {
|
|
188
|
+
return runOrDiagnose(['calendar', 'colors'], { account });
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
server.registerTool('gog_calendar_acl', {
|
|
192
|
+
description: 'List the access control list (sharing rules) for a calendar.',
|
|
193
|
+
annotations: { readOnlyHint: true },
|
|
194
|
+
inputSchema: {
|
|
195
|
+
calendarId: z.string().describe('Calendar ID'),
|
|
196
|
+
max: z.number().optional().describe('Max results (default: 100)'),
|
|
197
|
+
page: z.string().optional().describe('Page token'),
|
|
198
|
+
all: z.boolean().optional().describe('Fetch all pages'),
|
|
199
|
+
account: accountParam,
|
|
200
|
+
},
|
|
201
|
+
}, async ({ calendarId, max, page, all, account }) => {
|
|
202
|
+
const args = ['calendar', 'acl', calendarId];
|
|
203
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
204
|
+
if (page) args.push(`--page=${page}`);
|
|
205
|
+
if (all) args.push('--all');
|
|
206
|
+
return runOrDiagnose(args, { account });
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
server.registerTool('gog_calendar_move', {
|
|
210
|
+
description: 'Move an event from one calendar to another; the destination calendar becomes the organizer.',
|
|
211
|
+
inputSchema: {
|
|
212
|
+
calendarId: z.string().describe('Source calendar ID'),
|
|
213
|
+
eventId: z.string().describe('Event ID'),
|
|
214
|
+
destinationCalendarId: z.string().describe('Destination calendar ID that becomes the event organizer'),
|
|
215
|
+
sendUpdates: z.enum(['all', 'externalOnly', 'none']).optional().describe('Notification mode (default: none)'),
|
|
216
|
+
account: accountParam,
|
|
217
|
+
},
|
|
218
|
+
}, async ({ calendarId, eventId, destinationCalendarId, sendUpdates, account }) => {
|
|
219
|
+
const args = ['calendar', 'move', calendarId, eventId, destinationCalendarId];
|
|
220
|
+
if (sendUpdates) args.push(`--send-updates=${sendUpdates}`);
|
|
221
|
+
return runOrDiagnose(args, { account });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
server.registerTool('gog_calendar_out_of_office', {
|
|
225
|
+
description: 'Create an Out of Office event that auto-declines invitations during the block.',
|
|
226
|
+
inputSchema: {
|
|
227
|
+
from: z.string().describe('Start date or datetime (RFC3339 or YYYY-MM-DD)'),
|
|
228
|
+
to: z.string().describe('End date or datetime (RFC3339 or YYYY-MM-DD)'),
|
|
229
|
+
calendarId: z.string().optional().describe('Calendar ID (default: primary)'),
|
|
230
|
+
summary: z.string().optional().describe('Out of office title (default: "Out of office")'),
|
|
231
|
+
autoDecline: z.enum(['none', 'all', 'new']).optional().describe('Auto-decline mode (default: all)'),
|
|
232
|
+
declineMessage: z.string().optional().describe('Message for declined invitations'),
|
|
233
|
+
allDay: z.boolean().optional().describe('Create as an all-day event'),
|
|
234
|
+
account: accountParam,
|
|
235
|
+
},
|
|
236
|
+
}, async ({ from, to, calendarId, summary, autoDecline, declineMessage, allDay, account }) => {
|
|
237
|
+
const args = ['calendar', 'out-of-office'];
|
|
238
|
+
if (calendarId) args.push(calendarId);
|
|
239
|
+
args.push(`--from=${from}`);
|
|
240
|
+
args.push(`--to=${to}`);
|
|
241
|
+
if (summary) args.push(`--summary=${summary}`);
|
|
242
|
+
if (autoDecline) args.push(`--auto-decline=${autoDecline}`);
|
|
243
|
+
if (declineMessage) args.push(`--decline-message=${declineMessage}`);
|
|
244
|
+
if (allDay) args.push('--all-day');
|
|
245
|
+
return runOrDiagnose(args, { account });
|
|
246
|
+
});
|
|
247
|
+
|
|
113
248
|
server.registerTool('gog_meet_participants', {
|
|
114
249
|
description: 'List participants from the latest (or a specific) Meet call.',
|
|
115
250
|
annotations: { readOnlyHint: true },
|
|
@@ -143,6 +143,183 @@ describe('gog_zoom_auth_doctor', () => {
|
|
|
143
143
|
});
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
+
// --- gog 0.19.0 calendar reads & CRUD ---
|
|
147
|
+
|
|
148
|
+
describe('gog_calendar_calendars', () => {
|
|
149
|
+
it('calls runOrDiagnose with no flags', async () => {
|
|
150
|
+
await handlers.get('gog_calendar_calendars')!({});
|
|
151
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['calendar', 'calendars'], { account: undefined });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('passes pagination flags', async () => {
|
|
155
|
+
await handlers.get('gog_calendar_calendars')!({ max: 50, page: 'tok', all: true });
|
|
156
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
157
|
+
['calendar', 'calendars', '--max=50', '--page=tok', '--all'],
|
|
158
|
+
{ account: undefined },
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('omits --all when false', async () => {
|
|
163
|
+
await handlers.get('gog_calendar_calendars')!({ all: false });
|
|
164
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['calendar', 'calendars'], { account: undefined });
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('gog_calendar_search', () => {
|
|
169
|
+
it('calls runOrDiagnose with just the query', async () => {
|
|
170
|
+
await handlers.get('gog_calendar_search')!({ query: 'standup' });
|
|
171
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['calendar', 'search', 'standup'], { account: undefined });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('passes all filter flags when provided', async () => {
|
|
175
|
+
await handlers.get('gog_calendar_search')!({
|
|
176
|
+
query: 'standup',
|
|
177
|
+
from: 'today',
|
|
178
|
+
to: 'tomorrow',
|
|
179
|
+
today: true,
|
|
180
|
+
tomorrow: true,
|
|
181
|
+
week: true,
|
|
182
|
+
days: 7,
|
|
183
|
+
weekStart: 'sun',
|
|
184
|
+
calendar: 'primary',
|
|
185
|
+
max: 10,
|
|
186
|
+
});
|
|
187
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
188
|
+
[
|
|
189
|
+
'calendar', 'search', 'standup',
|
|
190
|
+
'--from=today', '--to=tomorrow',
|
|
191
|
+
'--today', '--tomorrow', '--week',
|
|
192
|
+
'--days=7', '--week-start=sun',
|
|
193
|
+
'--calendar=primary', '--max=10',
|
|
194
|
+
],
|
|
195
|
+
{ account: undefined },
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('omits boolean flags when false', async () => {
|
|
200
|
+
await handlers.get('gog_calendar_search')!({
|
|
201
|
+
query: 'standup', today: false, tomorrow: false, week: false,
|
|
202
|
+
});
|
|
203
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['calendar', 'search', 'standup'], { account: undefined });
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('gog_calendar_freebusy', () => {
|
|
208
|
+
it('calls runOrDiagnose with required from/to only', async () => {
|
|
209
|
+
await handlers.get('gog_calendar_freebusy')!({ from: 'A', to: 'B' });
|
|
210
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
211
|
+
['calendar', 'freebusy', '--from=A', '--to=B'],
|
|
212
|
+
{ account: undefined },
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('passes calendarIds and --all when provided', async () => {
|
|
217
|
+
await handlers.get('gog_calendar_freebusy')!({
|
|
218
|
+
from: 'A', to: 'B', calendarIds: 'primary,team@x.com', all: true,
|
|
219
|
+
});
|
|
220
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
221
|
+
['calendar', 'freebusy', 'primary,team@x.com', '--from=A', '--to=B', '--all'],
|
|
222
|
+
{ account: undefined },
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('omits --all when false', async () => {
|
|
227
|
+
await handlers.get('gog_calendar_freebusy')!({ from: 'A', to: 'B', all: false });
|
|
228
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
229
|
+
['calendar', 'freebusy', '--from=A', '--to=B'],
|
|
230
|
+
{ account: undefined },
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('gog_calendar_colors', () => {
|
|
236
|
+
it('calls runOrDiagnose with the colors subcommand', async () => {
|
|
237
|
+
await handlers.get('gog_calendar_colors')!({});
|
|
238
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['calendar', 'colors'], { account: undefined });
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('gog_calendar_acl', () => {
|
|
243
|
+
it('calls runOrDiagnose with calendarId only', async () => {
|
|
244
|
+
await handlers.get('gog_calendar_acl')!({ calendarId: 'primary' });
|
|
245
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['calendar', 'acl', 'primary'], { account: undefined });
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('passes pagination flags', async () => {
|
|
249
|
+
await handlers.get('gog_calendar_acl')!({ calendarId: 'primary', max: 25, page: 'tok', all: true });
|
|
250
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
251
|
+
['calendar', 'acl', 'primary', '--max=25', '--page=tok', '--all'],
|
|
252
|
+
{ account: undefined },
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('omits --all when false', async () => {
|
|
257
|
+
await handlers.get('gog_calendar_acl')!({ calendarId: 'primary', all: false });
|
|
258
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['calendar', 'acl', 'primary'], { account: undefined });
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('gog_calendar_move', () => {
|
|
263
|
+
it('calls runOrDiagnose with the three positionals', async () => {
|
|
264
|
+
await handlers.get('gog_calendar_move')!({
|
|
265
|
+
calendarId: 'primary', eventId: 'ev1', destinationCalendarId: 'team@x.com',
|
|
266
|
+
});
|
|
267
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
268
|
+
['calendar', 'move', 'primary', 'ev1', 'team@x.com'],
|
|
269
|
+
{ account: undefined },
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('passes --send-updates when provided', async () => {
|
|
274
|
+
await handlers.get('gog_calendar_move')!({
|
|
275
|
+
calendarId: 'primary', eventId: 'ev1', destinationCalendarId: 'team@x.com',
|
|
276
|
+
sendUpdates: 'all',
|
|
277
|
+
});
|
|
278
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
279
|
+
['calendar', 'move', 'primary', 'ev1', 'team@x.com', '--send-updates=all'],
|
|
280
|
+
{ account: undefined },
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe('gog_calendar_out_of_office', () => {
|
|
286
|
+
it('calls runOrDiagnose with required from/to only', async () => {
|
|
287
|
+
await handlers.get('gog_calendar_out_of_office')!({ from: '2026-06-01', to: '2026-06-05' });
|
|
288
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
289
|
+
['calendar', 'out-of-office', '--from=2026-06-01', '--to=2026-06-05'],
|
|
290
|
+
{ account: undefined },
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('passes calendarId and all optional flags when provided', async () => {
|
|
295
|
+
await handlers.get('gog_calendar_out_of_office')!({
|
|
296
|
+
from: '2026-06-01', to: '2026-06-05',
|
|
297
|
+
calendarId: 'primary',
|
|
298
|
+
summary: 'Vacation',
|
|
299
|
+
autoDecline: 'new',
|
|
300
|
+
declineMessage: 'Away',
|
|
301
|
+
allDay: true,
|
|
302
|
+
});
|
|
303
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
304
|
+
[
|
|
305
|
+
'calendar', 'out-of-office', 'primary',
|
|
306
|
+
'--from=2026-06-01', '--to=2026-06-05',
|
|
307
|
+
'--summary=Vacation', '--auto-decline=new',
|
|
308
|
+
'--decline-message=Away', '--all-day',
|
|
309
|
+
],
|
|
310
|
+
{ account: undefined },
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('omits --all-day when false', async () => {
|
|
315
|
+
await handlers.get('gog_calendar_out_of_office')!({ from: '2026-06-01', to: '2026-06-05', allDay: false });
|
|
316
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
317
|
+
['calendar', 'out-of-office', '--from=2026-06-01', '--to=2026-06-05'],
|
|
318
|
+
{ account: undefined },
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
146
323
|
describe('gog_meet_participants', () => {
|
|
147
324
|
it('calls runOrDiagnose with meetingCode', async () => {
|
|
148
325
|
await handlers.get('gog_meet_participants')!({ meetingCode: 'abc-defg-hij' });
|