gogcli-mcp-calendar 2.23.1 → 2.24.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 +143 -52
- package/manifest.json +2 -2
- package/package.json +1 -1
- package/src/tools/calendar-extra.ts +32 -19
- package/tests/tools/calendar-extra.test.ts +33 -8
package/dist/index.js
CHANGED
|
@@ -31542,6 +31542,55 @@ function normalizeTimestamps(text, tz = displayTimeZone(), naiveTz = naiveSource
|
|
|
31542
31542
|
return JSON.stringify(parsed, null, detectIndent(text));
|
|
31543
31543
|
}
|
|
31544
31544
|
|
|
31545
|
+
// ../gogcli-mcp/src/pagination.ts
|
|
31546
|
+
function detectIndent2(text) {
|
|
31547
|
+
const match = /\n(\s+)\S/.exec(text);
|
|
31548
|
+
return match ? match[1].replace(/\t/g, " ").length : 0;
|
|
31549
|
+
}
|
|
31550
|
+
function stripConsumedPageToken(text) {
|
|
31551
|
+
const trimmed = text.trim();
|
|
31552
|
+
if (trimmed === "" || !trimmed.startsWith("{")) return text;
|
|
31553
|
+
let parsed;
|
|
31554
|
+
try {
|
|
31555
|
+
parsed = JSON.parse(trimmed);
|
|
31556
|
+
} catch {
|
|
31557
|
+
return text;
|
|
31558
|
+
}
|
|
31559
|
+
const obj = parsed;
|
|
31560
|
+
if (obj.nextPageToken !== "") return text;
|
|
31561
|
+
delete obj.nextPageToken;
|
|
31562
|
+
return JSON.stringify(obj, null, detectIndent2(text));
|
|
31563
|
+
}
|
|
31564
|
+
function truncationWarning(returned, count) {
|
|
31565
|
+
const scope = count.total !== void 0 ? `returned ${returned} of ${count.total} matches` : count.atLeast !== void 0 ? `returned ${returned} of at least ${count.atLeast} matches` : `returned ${returned} matches and MORE EXIST beyond this page`;
|
|
31566
|
+
return `INCOMPLETE RESULT SET: ${scope}. Do not report an absence of results based on this response. Page with nextPageToken or narrow the query.`;
|
|
31567
|
+
}
|
|
31568
|
+
function annotateTruncation(out, returned, count) {
|
|
31569
|
+
out.truncated = true;
|
|
31570
|
+
out.returned = returned;
|
|
31571
|
+
if (count.total !== void 0) out.totalMatches = count.total;
|
|
31572
|
+
if (count.atLeast !== void 0) out.totalMatchesAtLeast = count.atLeast;
|
|
31573
|
+
out.warning = truncationWarning(returned, count);
|
|
31574
|
+
}
|
|
31575
|
+
function hasMorePages(parsed) {
|
|
31576
|
+
return typeof parsed.nextPageToken === "string" && parsed.nextPageToken !== "";
|
|
31577
|
+
}
|
|
31578
|
+
function annotateTruncatedList(result, itemsKey) {
|
|
31579
|
+
const first = result.content[0];
|
|
31580
|
+
if (result.isError || first?.type !== "text") return result;
|
|
31581
|
+
let parsed;
|
|
31582
|
+
try {
|
|
31583
|
+
parsed = JSON.parse(first.text);
|
|
31584
|
+
} catch {
|
|
31585
|
+
return result;
|
|
31586
|
+
}
|
|
31587
|
+
const items = parsed[itemsKey];
|
|
31588
|
+
if (!Array.isArray(items) || !hasMorePages(parsed)) return result;
|
|
31589
|
+
const out = { ...parsed };
|
|
31590
|
+
annotateTruncation(out, items.length, {});
|
|
31591
|
+
return rawTextResult(JSON.stringify(out));
|
|
31592
|
+
}
|
|
31593
|
+
|
|
31545
31594
|
// ../gogcli-mcp/src/tools/utils.ts
|
|
31546
31595
|
var accountParam = external_exports.string().optional().describe(
|
|
31547
31596
|
"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."
|
|
@@ -31570,9 +31619,19 @@ var ids = {
|
|
|
31570
31619
|
// People API uses fully-qualified resource names ("people/c123") not bare IDs.
|
|
31571
31620
|
person: external_exports.string().describe("Person resource name (people/...) or email")
|
|
31572
31621
|
};
|
|
31622
|
+
var pageTokenParam = external_exports.string().optional().describe(
|
|
31623
|
+
"Cursor for the NEXT page. Pass back the nextPageToken from a previous response verbatim, keeping the query and max identical: call once, then call again with pageToken=<that value>. A response with NO nextPageToken is the last page."
|
|
31624
|
+
);
|
|
31625
|
+
var pageAliasParam = external_exports.string().optional().describe(
|
|
31626
|
+
"Deprecated alias for pageToken, accepted so existing callers keep working. Use pageToken \u2014 it matches the nextPageToken field in the response."
|
|
31627
|
+
);
|
|
31628
|
+
function resolvePageToken(p) {
|
|
31629
|
+
return p.pageToken ?? p.page;
|
|
31630
|
+
}
|
|
31573
31631
|
var paginationParams = {
|
|
31574
31632
|
max: external_exports.number().int().optional().describe("Max results"),
|
|
31575
|
-
|
|
31633
|
+
pageToken: pageTokenParam,
|
|
31634
|
+
page: pageAliasParam,
|
|
31576
31635
|
all: external_exports.boolean().optional().describe("Fetch all pages")
|
|
31577
31636
|
};
|
|
31578
31637
|
function registerRunTool(server, options) {
|
|
@@ -31647,7 +31706,7 @@ ${accounts || "(none)"}${hint}`);
|
|
|
31647
31706
|
async function runOrDiagnose(args, options) {
|
|
31648
31707
|
try {
|
|
31649
31708
|
const raw = await run(args, options);
|
|
31650
|
-
return rawTextResult(options.lossless ? raw : normalizeTimestamps(raw));
|
|
31709
|
+
return rawTextResult(options.lossless ? raw : stripConsumedPageToken(normalizeTimestamps(raw)));
|
|
31651
31710
|
} catch (err) {
|
|
31652
31711
|
return diagnose(err);
|
|
31653
31712
|
}
|
|
@@ -31697,6 +31756,7 @@ function formatAuthHealth(raw, now) {
|
|
|
31697
31756
|
// ../gogcli-mcp/src/tools/auth.ts
|
|
31698
31757
|
function registerAuthToolsWith(server, defaultServices) {
|
|
31699
31758
|
const servicesDescribe = `Services to authorize: "all" or comma-separated list (e.g. "sheets,gmail,calendar"). Default: "${defaultServices}". Prefer the narrowest set you need \u2014 requesting a service whose Google API is not enabled on the OAuth client's project makes Google reject the WHOLE request with invalid_scope.`;
|
|
31759
|
+
const extraScopesDescribe = "Additional raw OAuth scope URIs to request, comma-separated, on top of the ones `services` implies. Use for scopes no service covers \u2014 e.g. https://www.googleapis.com/auth/bigquery.readonly, required before gog_sheets_datasource_* can read BigQuery-backed Connected Sheets. Leave unset otherwise: an extra scope whose API is not enabled on the OAuth client project makes Google reject the WHOLE authorization with invalid_scope.";
|
|
31700
31760
|
server.registerTool("gog_auth_list", {
|
|
31701
31761
|
description: "List the Google accounts stored in gogcli, with their scopes. This reads local configuration only \u2014 it does not contact Google and does NOT tell you whether an account still works: a signed-out account whose refresh token expired or was revoked is listed here exactly like a healthy one, scopes and all. Use gog_auth_health to check whether an account can actually authenticate.",
|
|
31702
31762
|
annotations: { readOnlyHint: true },
|
|
@@ -31746,11 +31806,14 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
31746
31806
|
annotations: { destructiveHint: true },
|
|
31747
31807
|
inputSchema: {
|
|
31748
31808
|
email: external_exports.string().describe("Google account email to authorize"),
|
|
31749
|
-
services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe)
|
|
31809
|
+
services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe),
|
|
31810
|
+
extraScopes: external_exports.string().optional().describe(extraScopesDescribe)
|
|
31750
31811
|
}
|
|
31751
|
-
}, async ({ email: email3, services = defaultServices }) => {
|
|
31812
|
+
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
31752
31813
|
try {
|
|
31753
|
-
|
|
31814
|
+
const args = ["auth", "add", email3, "--services", services];
|
|
31815
|
+
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`, "--force-consent");
|
|
31816
|
+
return rawTextResult(await run(args, {
|
|
31754
31817
|
interactive: true,
|
|
31755
31818
|
timeout: 3e5
|
|
31756
31819
|
}));
|
|
@@ -31762,14 +31825,14 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
31762
31825
|
description: "Begin REMOTE/headless Google authorization (step 1 of 2). Returns a sign-in URL to open in any browser \u2014 no local server or terminal on the gogcli host is needed, so this works over the hosted connector where the interactive gog_auth_add cannot. Hand the URL to the user; after they sign in, the browser is redirected to a localhost URL that fails to load \u2014 that is expected. They copy that full redirected URL (from the address bar) and you pass it to gog_auth_add_complete. The link is valid for 10 minutes. If you pass a custom `services` here, pass the SAME value to gog_auth_add_complete or the second step will not match this one.",
|
|
31763
31826
|
inputSchema: {
|
|
31764
31827
|
email: external_exports.string().describe("Google account email to authorize"),
|
|
31765
|
-
services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe)
|
|
31828
|
+
services: external_exports.string().optional().default(defaultServices).describe(servicesDescribe),
|
|
31829
|
+
extraScopes: external_exports.string().optional().describe(`${extraScopesDescribe} Pass the SAME value to gog_auth_add_complete.`)
|
|
31766
31830
|
}
|
|
31767
|
-
}, async ({ email: email3, services = defaultServices }) => {
|
|
31831
|
+
}, async ({ email: email3, services = defaultServices, extraScopes }) => {
|
|
31768
31832
|
try {
|
|
31769
|
-
|
|
31770
|
-
|
|
31771
|
-
|
|
31772
|
-
));
|
|
31833
|
+
const args = ["auth", "add", email3, "--remote", "--step", "1", "--services", services, "--force-consent"];
|
|
31834
|
+
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`);
|
|
31835
|
+
return rawTextResult(await run(args, { redactMode: "tokens" }));
|
|
31773
31836
|
} catch (err) {
|
|
31774
31837
|
return errorResult(errorText(err));
|
|
31775
31838
|
}
|
|
@@ -31784,25 +31847,28 @@ function registerAuthToolsWith(server, defaultServices) {
|
|
|
31784
31847
|
),
|
|
31785
31848
|
services: external_exports.string().optional().default(defaultServices).describe(
|
|
31786
31849
|
`Services authorized \u2014 MUST match the value passed to gog_auth_add_url. Default: "${defaultServices}".`
|
|
31850
|
+
),
|
|
31851
|
+
extraScopes: external_exports.string().optional().describe(
|
|
31852
|
+
"Extra OAuth scope URIs \u2014 MUST match the value passed to gog_auth_add_url, for the same reason `services` must: the two steps have to describe the same grant."
|
|
31787
31853
|
)
|
|
31788
31854
|
}
|
|
31789
|
-
}, async ({ email: email3, redirectUrl, services = defaultServices }) => {
|
|
31855
|
+
}, async ({ email: email3, redirectUrl, services = defaultServices, extraScopes }) => {
|
|
31790
31856
|
try {
|
|
31791
|
-
|
|
31792
|
-
|
|
31793
|
-
|
|
31794
|
-
|
|
31795
|
-
|
|
31796
|
-
|
|
31797
|
-
|
|
31798
|
-
|
|
31799
|
-
|
|
31800
|
-
|
|
31801
|
-
|
|
31802
|
-
|
|
31803
|
-
|
|
31804
|
-
|
|
31805
|
-
));
|
|
31857
|
+
const args = [
|
|
31858
|
+
"auth",
|
|
31859
|
+
"add",
|
|
31860
|
+
email3,
|
|
31861
|
+
"--remote",
|
|
31862
|
+
"--step",
|
|
31863
|
+
"2",
|
|
31864
|
+
"--auth-url",
|
|
31865
|
+
redirectUrl,
|
|
31866
|
+
"--services",
|
|
31867
|
+
services,
|
|
31868
|
+
"--force-consent"
|
|
31869
|
+
];
|
|
31870
|
+
if (extraScopes) args.push(`--extra-scopes=${extraScopes}`);
|
|
31871
|
+
return rawTextResult(await run(args));
|
|
31806
31872
|
} catch (err) {
|
|
31807
31873
|
return errorResult(errorText(err));
|
|
31808
31874
|
}
|
|
@@ -31821,30 +31887,44 @@ function authToolsFor(defaultServices) {
|
|
|
31821
31887
|
// ../gogcli-mcp/src/tools/calendar.ts
|
|
31822
31888
|
function registerCalendarTools(server) {
|
|
31823
31889
|
server.registerTool("gog_calendar_events", {
|
|
31824
|
-
description:
|
|
31890
|
+
description: 'List calendar events. Describe the window ONE way and one way only (gog >= 0.36.0 rejects the rest as ambiguous rather than silently discarding a flag): today on its own; or from + to; or from + days; or days on its own (a window of that many days starting today). today cannot be combined with from, to or days, and days cannot be combined with to. gog returns only 10 events by default, so a wide date range is USUALLY INCOMPLETE: raise max, or page with pageToken until the response carries no nextPageToken. A response carrying "truncated": true is an incomplete view \u2014 never conclude an event does not exist from one.',
|
|
31825
31891
|
annotations: { readOnlyHint: true },
|
|
31826
31892
|
inputSchema: {
|
|
31827
31893
|
calendarId: external_exports.string().optional().describe("Calendar ID (default: primary calendar)"),
|
|
31828
31894
|
from: external_exports.string().optional().describe("Start time filter (RFC3339, date, or natural language)"),
|
|
31829
|
-
to: external_exports.string().optional().describe("End time filter (RFC3339, date, or natural language)"),
|
|
31830
|
-
|
|
31895
|
+
to: external_exports.string().optional().describe("End time filter (RFC3339, date, or natural language). Mutually exclusive with today and with days."),
|
|
31896
|
+
// gog >= 0.36.0 (openclaw/gogcli#981). Before that release --days sat in
|
|
31897
|
+
// a switch arm evaluated ahead of --from, so `--from 2026-09-25 --days 5`
|
|
31898
|
+
// silently threw --from away and answered for today instead — at exit 0,
|
|
31899
|
+
// in a well-formed table. It is only exposed here now that it means what
|
|
31900
|
+
// it says.
|
|
31901
|
+
days: external_exports.number().int().positive().optional().describe('Window LENGTH in days (calendar days, DST-aware), measured from `from` when one is given and from today otherwise. Use from + days for "the week of the 25th"; days alone for "the next N days". Mutually exclusive with to and with today.'),
|
|
31902
|
+
today: external_exports.boolean().optional().describe("Only show today's events. A complete window on its own \u2014 mutually exclusive with from, to and days."),
|
|
31831
31903
|
query: external_exports.string().optional().describe("Free text search within events"),
|
|
31832
|
-
|
|
31904
|
+
max: external_exports.number().int().optional().describe("Max events to return. gog defaults to 10, which silently hides the rest \u2014 raise it, or page with pageToken."),
|
|
31905
|
+
pageToken: pageTokenParam,
|
|
31906
|
+
page: pageAliasParam,
|
|
31907
|
+
all: external_exports.boolean().optional().describe('Fetch events from ALL CALENDARS. NOTE: unlike the gmail search tools, this does NOT mean "all pages" \u2014 it widens the calendar set, not the page window. Use pageToken to reach later pages.'),
|
|
31833
31908
|
eventTypes: external_exports.array(external_exports.enum(["default", "birthday", "focus-time", "from-gmail", "out-of-office", "working-location"])).optional().describe("Filter to specific event types (repeatable)"),
|
|
31834
31909
|
timezone: external_exports.string().optional().describe(`Display timezone for event times (IANA name, e.g. America/New_York, or "local" for the system timezone). Default: each event's timezone, then its calendar's timezone.`),
|
|
31835
31910
|
account: accountParam
|
|
31836
31911
|
}
|
|
31837
|
-
}, async ({ calendarId, from, to, today, query, all, eventTypes, timezone, account }) => {
|
|
31912
|
+
}, async ({ calendarId, from, to, days, today, query, max, pageToken, page, all, eventTypes, timezone, account }) => {
|
|
31838
31913
|
const args = ["calendar", "events"];
|
|
31839
31914
|
if (calendarId) args.push(calendarId);
|
|
31840
31915
|
if (from) args.push(`--from=${from}`);
|
|
31841
31916
|
if (to) args.push(`--to=${to}`);
|
|
31917
|
+
if (days !== void 0) args.push(`--days=${days}`);
|
|
31842
31918
|
if (today) args.push("--today");
|
|
31843
31919
|
if (query) args.push(`--query=${query}`);
|
|
31920
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31921
|
+
const token = resolvePageToken({ pageToken, page });
|
|
31922
|
+
if (token) args.push(`--page=${token}`);
|
|
31844
31923
|
if (all) args.push("--all");
|
|
31845
31924
|
if (eventTypes) for (const t of eventTypes) args.push(`--event-types=${t}`);
|
|
31846
31925
|
if (timezone) args.push(`--timezone=${timezone}`);
|
|
31847
|
-
|
|
31926
|
+
const result = await runOrDiagnose(args, { account });
|
|
31927
|
+
return annotateTruncatedList(result, "events");
|
|
31848
31928
|
});
|
|
31849
31929
|
server.registerTool("gog_calendar_get", {
|
|
31850
31930
|
description: "Get a specific calendar event by ID.",
|
|
@@ -31961,7 +32041,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
|
31961
32041
|
);
|
|
31962
32042
|
|
|
31963
32043
|
// ../gogcli-mcp/src/server.ts
|
|
31964
|
-
var VERSION = true ? "2.
|
|
32044
|
+
var VERSION = true ? "2.24.0" : "0.0.0";
|
|
31965
32045
|
|
|
31966
32046
|
// ../gogcli-mcp/src/auth-log.ts
|
|
31967
32047
|
var FAILURES = /* @__PURE__ */ new Set([
|
|
@@ -32502,14 +32582,16 @@ function registerExtraCalendarTools(server) {
|
|
|
32502
32582
|
inputSchema: {
|
|
32503
32583
|
meetingCode: external_exports.string().describe("Meeting code"),
|
|
32504
32584
|
max: external_exports.number().optional().describe("Max results (default: 20)"),
|
|
32505
|
-
|
|
32585
|
+
pageToken: pageTokenParam,
|
|
32586
|
+
page: pageAliasParam,
|
|
32506
32587
|
all: external_exports.boolean().optional().describe("Fetch all pages"),
|
|
32507
32588
|
account: accountParam
|
|
32508
32589
|
}
|
|
32509
|
-
}, async ({ meetingCode, max, page, all, account }) => {
|
|
32590
|
+
}, async ({ meetingCode, max, pageToken, page, all, account }) => {
|
|
32510
32591
|
const args = ["meet", "history", meetingCode];
|
|
32511
32592
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
32512
|
-
|
|
32593
|
+
const token = resolvePageToken({ pageToken, page });
|
|
32594
|
+
if (token) args.push(`--page=${token}`);
|
|
32513
32595
|
if (all) args.push("--all");
|
|
32514
32596
|
return runOrDiagnose(args, { account });
|
|
32515
32597
|
});
|
|
@@ -32548,28 +32630,33 @@ function registerExtraCalendarTools(server) {
|
|
|
32548
32630
|
annotations: { readOnlyHint: true },
|
|
32549
32631
|
inputSchema: {
|
|
32550
32632
|
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
32551
|
-
|
|
32633
|
+
pageToken: pageTokenParam,
|
|
32634
|
+
page: pageAliasParam,
|
|
32552
32635
|
all: external_exports.boolean().optional().describe("Fetch all pages"),
|
|
32553
32636
|
account: accountParam
|
|
32554
32637
|
}
|
|
32555
|
-
}, async ({ max, page, all, account }) => {
|
|
32638
|
+
}, async ({ max, pageToken, page, all, account }) => {
|
|
32556
32639
|
const args = ["calendar", "calendars"];
|
|
32557
32640
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
32558
|
-
|
|
32641
|
+
const token = resolvePageToken({ pageToken, page });
|
|
32642
|
+
if (token) args.push(`--page=${token}`);
|
|
32559
32643
|
if (all) args.push("--all");
|
|
32560
32644
|
return runOrDiagnose(args, { account });
|
|
32561
32645
|
});
|
|
32562
32646
|
server.registerTool("gog_calendar_search", {
|
|
32563
|
-
description: "Full-text search for events matching a query string, with optional time filters.",
|
|
32647
|
+
description: "Full-text search for events matching a query string, with optional time filters. Describe the window ONE way only (gog >= 0.36.0 rejects the rest as ambiguous instead of discarding a flag): one of today / tomorrow / week on its own, or from + to, or from + days, or days on its own. The fixed presets cannot be combined with from, to or days, and days cannot be combined with to.",
|
|
32564
32648
|
annotations: { readOnlyHint: true },
|
|
32565
32649
|
inputSchema: {
|
|
32566
32650
|
query: external_exports.string().describe("Search query"),
|
|
32567
32651
|
from: external_exports.string().optional().describe("Start time (RFC3339, date, or relative: now, today, tomorrow, monday)"),
|
|
32568
|
-
to: external_exports.string().optional().describe("End time (RFC3339, date, or relative: now, today, tomorrow, monday)"),
|
|
32569
|
-
today: external_exports.boolean().optional().describe("Today only"),
|
|
32570
|
-
tomorrow: external_exports.boolean().optional().describe("Tomorrow only"),
|
|
32571
|
-
week: external_exports.boolean().optional().describe("This week (uses weekStart, default Mon)"),
|
|
32572
|
-
|
|
32652
|
+
to: external_exports.string().optional().describe("End time (RFC3339, date, or relative: now, today, tomorrow, monday). Mutually exclusive with days."),
|
|
32653
|
+
today: external_exports.boolean().optional().describe("Today only. A complete window on its own \u2014 not combinable with from/to/days."),
|
|
32654
|
+
tomorrow: external_exports.boolean().optional().describe("Tomorrow only. A complete window on its own \u2014 not combinable with from/to/days."),
|
|
32655
|
+
week: external_exports.boolean().optional().describe("This week (uses weekStart, default Mon). A complete window on its own \u2014 not combinable with from/to/days."),
|
|
32656
|
+
// gog >= 0.36.0 (openclaw/gogcli#981) anchors --days at --from. It used
|
|
32657
|
+
// to mean "next N days from today" no matter what --from said, which is
|
|
32658
|
+
// why the old description here read that way.
|
|
32659
|
+
days: external_exports.number().optional().describe('Window LENGTH in days, measured from `from` when one is given and from today otherwise \u2014 NOT always "the next N days".'),
|
|
32573
32660
|
weekStart: external_exports.string().optional().describe("Week start day for week (sun, mon, ...)"),
|
|
32574
32661
|
calendar: external_exports.string().optional().describe("Calendar ID (default: primary)"),
|
|
32575
32662
|
max: external_exports.number().optional().describe("Max results (default: 25)"),
|
|
@@ -32641,14 +32728,16 @@ function registerExtraCalendarTools(server) {
|
|
|
32641
32728
|
inputSchema: {
|
|
32642
32729
|
calendarId: external_exports.string().describe("Calendar ID"),
|
|
32643
32730
|
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
32644
|
-
|
|
32731
|
+
pageToken: pageTokenParam,
|
|
32732
|
+
page: pageAliasParam,
|
|
32645
32733
|
all: external_exports.boolean().optional().describe("Fetch all pages"),
|
|
32646
32734
|
account: accountParam
|
|
32647
32735
|
}
|
|
32648
|
-
}, async ({ calendarId, max, page, all, account }) => {
|
|
32736
|
+
}, async ({ calendarId, max, pageToken, page, all, account }) => {
|
|
32649
32737
|
const args = ["calendar", "acl", calendarId];
|
|
32650
32738
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
32651
|
-
|
|
32739
|
+
const token = resolvePageToken({ pageToken, page });
|
|
32740
|
+
if (token) args.push(`--page=${token}`);
|
|
32652
32741
|
if (all) args.push("--all");
|
|
32653
32742
|
return runOrDiagnose(args, { account });
|
|
32654
32743
|
});
|
|
@@ -32716,15 +32805,17 @@ function registerExtraCalendarTools(server) {
|
|
|
32716
32805
|
meetingCode: external_exports.string().describe("Meeting code"),
|
|
32717
32806
|
conference: external_exports.string().optional().describe("Specific conference ID (default: most recent)"),
|
|
32718
32807
|
max: external_exports.number().optional().describe("Max results (default: 50)"),
|
|
32719
|
-
|
|
32808
|
+
pageToken: pageTokenParam,
|
|
32809
|
+
page: pageAliasParam,
|
|
32720
32810
|
all: external_exports.boolean().optional().describe("Fetch all pages"),
|
|
32721
32811
|
account: accountParam
|
|
32722
32812
|
}
|
|
32723
|
-
}, async ({ meetingCode, conference, max, page, all, account }) => {
|
|
32813
|
+
}, async ({ meetingCode, conference, max, pageToken, page, all, account }) => {
|
|
32724
32814
|
const args = ["meet", "participants", meetingCode];
|
|
32725
32815
|
if (conference) args.push(`--conference=${conference}`);
|
|
32726
32816
|
if (max !== void 0) args.push(`--max=${max}`);
|
|
32727
|
-
|
|
32817
|
+
const token = resolvePageToken({ pageToken, page });
|
|
32818
|
+
if (token) args.push(`--page=${token}`);
|
|
32728
32819
|
if (all) args.push("--all");
|
|
32729
32820
|
return runOrDiagnose(args, { account });
|
|
32730
32821
|
});
|
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.24.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",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"name": "gog_calendar_events",
|
|
91
|
-
"description": "List calendar events
|
|
91
|
+
"description": "List calendar events; paginated (gog returns only 10 by default) and flags a truncated range"
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
"name": "gog_calendar_get",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-calendar",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.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>",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { accountParam, runOrDiagnose } from '../../../gogcli-mcp/src/lib.js';
|
|
3
|
+
import { accountParam, runOrDiagnose, pageTokenParam, pageAliasParam, resolvePageToken} from '../../../gogcli-mcp/src/lib.js';
|
|
4
4
|
|
|
5
5
|
const meetAccess = z.enum(['open', 'trusted', 'restricted']);
|
|
6
6
|
|
|
@@ -63,14 +63,16 @@ export function registerExtraCalendarTools(server: McpServer): void {
|
|
|
63
63
|
inputSchema: {
|
|
64
64
|
meetingCode: z.string().describe('Meeting code'),
|
|
65
65
|
max: z.number().optional().describe('Max results (default: 20)'),
|
|
66
|
-
|
|
66
|
+
pageToken: pageTokenParam,
|
|
67
|
+
page: pageAliasParam,
|
|
67
68
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
68
69
|
account: accountParam,
|
|
69
70
|
},
|
|
70
|
-
}, async ({ meetingCode, max, page, all, account }) => {
|
|
71
|
+
}, async ({ meetingCode, max, pageToken, page, all, account }) => {
|
|
71
72
|
const args = ['meet', 'history', meetingCode];
|
|
72
73
|
if (max !== undefined) args.push(`--max=${max}`);
|
|
73
|
-
|
|
74
|
+
const token = resolvePageToken({ pageToken, page });
|
|
75
|
+
if (token) args.push(`--page=${token}`);
|
|
74
76
|
if (all) args.push('--all');
|
|
75
77
|
return runOrDiagnose(args, { account });
|
|
76
78
|
});
|
|
@@ -117,29 +119,36 @@ export function registerExtraCalendarTools(server: McpServer): void {
|
|
|
117
119
|
annotations: { readOnlyHint: true },
|
|
118
120
|
inputSchema: {
|
|
119
121
|
max: z.number().optional().describe('Max results (default: 100)'),
|
|
120
|
-
|
|
122
|
+
pageToken: pageTokenParam,
|
|
123
|
+
page: pageAliasParam,
|
|
121
124
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
122
125
|
account: accountParam,
|
|
123
126
|
},
|
|
124
|
-
}, async ({ max, page, all, account }) => {
|
|
127
|
+
}, async ({ max, pageToken, page, all, account }) => {
|
|
125
128
|
const args = ['calendar', 'calendars'];
|
|
126
129
|
if (max !== undefined) args.push(`--max=${max}`);
|
|
127
|
-
|
|
130
|
+
const token = resolvePageToken({ pageToken, page });
|
|
131
|
+
if (token) args.push(`--page=${token}`);
|
|
128
132
|
if (all) args.push('--all');
|
|
129
133
|
return runOrDiagnose(args, { account });
|
|
130
134
|
});
|
|
131
135
|
|
|
132
136
|
server.registerTool('gog_calendar_search', {
|
|
133
|
-
description: 'Full-text search for events matching a query string, with optional time filters.'
|
|
137
|
+
description: 'Full-text search for events matching a query string, with optional time filters. '
|
|
138
|
+
+ 'Describe the window ONE way only (gog >= 0.36.0 rejects the rest as ambiguous instead of discarding a flag): one of today / tomorrow / week on its own, '
|
|
139
|
+
+ 'or from + to, or from + days, or days on its own. The fixed presets cannot be combined with from, to or days, and days cannot be combined with to.',
|
|
134
140
|
annotations: { readOnlyHint: true },
|
|
135
141
|
inputSchema: {
|
|
136
142
|
query: z.string().describe('Search query'),
|
|
137
143
|
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
|
-
|
|
144
|
+
to: z.string().optional().describe('End time (RFC3339, date, or relative: now, today, tomorrow, monday). Mutually exclusive with days.'),
|
|
145
|
+
today: z.boolean().optional().describe('Today only. A complete window on its own — not combinable with from/to/days.'),
|
|
146
|
+
tomorrow: z.boolean().optional().describe('Tomorrow only. A complete window on its own — not combinable with from/to/days.'),
|
|
147
|
+
week: z.boolean().optional().describe('This week (uses weekStart, default Mon). A complete window on its own — not combinable with from/to/days.'),
|
|
148
|
+
// gog >= 0.36.0 (openclaw/gogcli#981) anchors --days at --from. It used
|
|
149
|
+
// to mean "next N days from today" no matter what --from said, which is
|
|
150
|
+
// why the old description here read that way.
|
|
151
|
+
days: z.number().optional().describe('Window LENGTH in days, measured from `from` when one is given and from today otherwise — NOT always "the next N days".'),
|
|
143
152
|
weekStart: z.string().optional().describe('Week start day for week (sun, mon, ...)'),
|
|
144
153
|
calendar: z.string().optional().describe('Calendar ID (default: primary)'),
|
|
145
154
|
max: z.number().optional().describe('Max results (default: 25)'),
|
|
@@ -215,14 +224,16 @@ export function registerExtraCalendarTools(server: McpServer): void {
|
|
|
215
224
|
inputSchema: {
|
|
216
225
|
calendarId: z.string().describe('Calendar ID'),
|
|
217
226
|
max: z.number().optional().describe('Max results (default: 100)'),
|
|
218
|
-
|
|
227
|
+
pageToken: pageTokenParam,
|
|
228
|
+
page: pageAliasParam,
|
|
219
229
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
220
230
|
account: accountParam,
|
|
221
231
|
},
|
|
222
|
-
}, async ({ calendarId, max, page, all, account }) => {
|
|
232
|
+
}, async ({ calendarId, max, pageToken, page, all, account }) => {
|
|
223
233
|
const args = ['calendar', 'acl', calendarId];
|
|
224
234
|
if (max !== undefined) args.push(`--max=${max}`);
|
|
225
|
-
|
|
235
|
+
const token = resolvePageToken({ pageToken, page });
|
|
236
|
+
if (token) args.push(`--page=${token}`);
|
|
226
237
|
if (all) args.push('--all');
|
|
227
238
|
return runOrDiagnose(args, { account });
|
|
228
239
|
});
|
|
@@ -295,15 +306,17 @@ export function registerExtraCalendarTools(server: McpServer): void {
|
|
|
295
306
|
meetingCode: z.string().describe('Meeting code'),
|
|
296
307
|
conference: z.string().optional().describe('Specific conference ID (default: most recent)'),
|
|
297
308
|
max: z.number().optional().describe('Max results (default: 50)'),
|
|
298
|
-
|
|
309
|
+
pageToken: pageTokenParam,
|
|
310
|
+
page: pageAliasParam,
|
|
299
311
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
300
312
|
account: accountParam,
|
|
301
313
|
},
|
|
302
|
-
}, async ({ meetingCode, conference, max, page, all, account }) => {
|
|
314
|
+
}, async ({ meetingCode, conference, max, pageToken, page, all, account }) => {
|
|
303
315
|
const args = ['meet', 'participants', meetingCode];
|
|
304
316
|
if (conference) args.push(`--conference=${conference}`);
|
|
305
317
|
if (max !== undefined) args.push(`--max=${max}`);
|
|
306
|
-
|
|
318
|
+
const token = resolvePageToken({ pageToken, page });
|
|
319
|
+
if (token) args.push(`--page=${token}`);
|
|
307
320
|
if (all) args.push('--all');
|
|
308
321
|
return runOrDiagnose(args, { account });
|
|
309
322
|
});
|
|
@@ -172,16 +172,16 @@ describe('gog_calendar_search', () => {
|
|
|
172
172
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['calendar', 'search', 'standup'], { account: undefined });
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
// One case per LEGAL window, not one case passing every flag at once. gog
|
|
176
|
+
// >= 0.36.0 (openclaw/gogcli#981) rejects a fixed preset alongside
|
|
177
|
+
// from/to/days, and days alongside to, with exit 2 — so the all-flags array
|
|
178
|
+
// this used to assert is one gog will not run, and a mocked test asserting
|
|
179
|
+
// it would keep passing forever while the tool was broken in the field.
|
|
180
|
+
it('passes an explicit from/to range with the non-window filters', async () => {
|
|
176
181
|
await harness.callTool('gog_calendar_search', {
|
|
177
182
|
query: 'standup',
|
|
178
183
|
from: 'today',
|
|
179
184
|
to: 'tomorrow',
|
|
180
|
-
today: true,
|
|
181
|
-
tomorrow: true,
|
|
182
|
-
week: true,
|
|
183
|
-
days: 7,
|
|
184
|
-
weekStart: 'sun',
|
|
185
185
|
calendar: 'primary',
|
|
186
186
|
max: 10,
|
|
187
187
|
});
|
|
@@ -189,14 +189,39 @@ describe('gog_calendar_search', () => {
|
|
|
189
189
|
[
|
|
190
190
|
'calendar', 'search', 'standup',
|
|
191
191
|
'--from=today', '--to=tomorrow',
|
|
192
|
-
'--today', '--tomorrow', '--week',
|
|
193
|
-
'--days=7', '--week-start=sun',
|
|
194
192
|
'--calendar=primary', '--max=10',
|
|
195
193
|
],
|
|
196
194
|
{ account: undefined },
|
|
197
195
|
);
|
|
198
196
|
});
|
|
199
197
|
|
|
198
|
+
it('anchors --days at --from when both are given', async () => {
|
|
199
|
+
await harness.callTool('gog_calendar_search', { query: 'standup', from: '2026-09-25', days: 7 });
|
|
200
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
201
|
+
['calendar', 'search', 'standup', '--from=2026-09-25', '--days=7'],
|
|
202
|
+
{ account: undefined },
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('passes each fixed preset on its own', async () => {
|
|
207
|
+
for (const [param, flag] of [['today', '--today'], ['tomorrow', '--tomorrow'], ['week', '--week']] as const) {
|
|
208
|
+
vi.mocked(lib.runOrDiagnose).mockClear();
|
|
209
|
+
await harness.callTool('gog_calendar_search', { query: 'standup', [param]: true });
|
|
210
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
211
|
+
['calendar', 'search', 'standup', flag],
|
|
212
|
+
{ account: undefined },
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('passes --week-start alongside --week', async () => {
|
|
218
|
+
await harness.callTool('gog_calendar_search', { query: 'standup', week: true, weekStart: 'sun' });
|
|
219
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
220
|
+
['calendar', 'search', 'standup', '--week', '--week-start=sun'],
|
|
221
|
+
{ account: undefined },
|
|
222
|
+
);
|
|
223
|
+
});
|
|
224
|
+
|
|
200
225
|
it('omits boolean flags when false', async () => {
|
|
201
226
|
await harness.callTool('gog_calendar_search', {
|
|
202
227
|
query: 'standup', today: false, tomorrow: false, week: false,
|