gogcli-mcp-slides 2.18.3 → 2.19.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +217 -4
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/tools/slides-extra.ts +2 -1
- package/tests/tools/slides-extra.test.ts +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "Extended Google Slides for Claude via gogcli — auth + full Slides support (create, edit, export, templates, markdown)",
|
|
10
|
-
"version": "2.
|
|
10
|
+
"version": "2.19.0"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "gogcli (Slides)",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Extended Google Slides for Claude via gogcli — auth + full Slides support (create, edit, export, templates, markdown)",
|
|
18
|
-
"version": "2.
|
|
18
|
+
"version": "2.19.0",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-slides",
|
|
3
3
|
"displayName": "gogcli (Slides)",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.19.0",
|
|
5
5
|
"description": "Extended Google Slides for Claude via gogcli — auth + full Slides support (create, edit, export, templates, markdown)",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/dist/index.js
CHANGED
|
@@ -3644,7 +3644,12 @@ var require_fast_uri = __commonJS({
|
|
|
3644
3644
|
}
|
|
3645
3645
|
function resolve(baseURI, relativeURI, options) {
|
|
3646
3646
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3647
|
-
const
|
|
3647
|
+
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
3648
|
+
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
3649
|
+
if (baseMalformed || relativeMalformed) {
|
|
3650
|
+
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
3651
|
+
}
|
|
3652
|
+
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
3648
3653
|
schemelessOptions.skipEscape = true;
|
|
3649
3654
|
return serialize(resolved, schemelessOptions);
|
|
3650
3655
|
}
|
|
@@ -3770,6 +3775,7 @@ var require_fast_uri = __commonJS({
|
|
|
3770
3775
|
}
|
|
3771
3776
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
3772
3777
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
3778
|
+
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
3773
3779
|
function getParseError(parsed, matches) {
|
|
3774
3780
|
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
3775
3781
|
return 'URI path must start with "/" when authority is present.';
|
|
@@ -3804,6 +3810,20 @@ var require_fast_uri = __commonJS({
|
|
|
3804
3810
|
parsed.error = "URI authority must not contain a literal backslash.";
|
|
3805
3811
|
malformedAuthorityOrPort = true;
|
|
3806
3812
|
}
|
|
3813
|
+
const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
|
|
3814
|
+
if (introducerMatch !== null) {
|
|
3815
|
+
const region = introducerMatch[1];
|
|
3816
|
+
const normalizedRegion = region.replace(/[\t\n\r]/g, "");
|
|
3817
|
+
if (normalizedRegion.length >= 2) {
|
|
3818
|
+
if (normalizedRegion.slice(0, 2) !== "//") {
|
|
3819
|
+
parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
|
|
3820
|
+
malformedAuthorityOrPort = true;
|
|
3821
|
+
} else if (region.length !== normalizedRegion.length) {
|
|
3822
|
+
parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
|
|
3823
|
+
malformedAuthorityOrPort = true;
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3807
3827
|
const matches = uri.match(URI_PARSE);
|
|
3808
3828
|
if (matches) {
|
|
3809
3829
|
parsed.scheme = matches[1];
|
|
@@ -31275,6 +31295,198 @@ async function run(args, options = {}) {
|
|
|
31275
31295
|
}
|
|
31276
31296
|
}
|
|
31277
31297
|
|
|
31298
|
+
// ../gogcli-mcp/src/timestamps.ts
|
|
31299
|
+
var DEFAULT_DISPLAY_TZ = "America/New_York";
|
|
31300
|
+
function isValidTimeZone(tz) {
|
|
31301
|
+
try {
|
|
31302
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
31303
|
+
return true;
|
|
31304
|
+
} catch {
|
|
31305
|
+
return false;
|
|
31306
|
+
}
|
|
31307
|
+
}
|
|
31308
|
+
function displayTimeZone() {
|
|
31309
|
+
const configured = readEnvVar("DISPLAY_TZ");
|
|
31310
|
+
if (configured && isValidTimeZone(configured)) return configured;
|
|
31311
|
+
return DEFAULT_DISPLAY_TZ;
|
|
31312
|
+
}
|
|
31313
|
+
function naiveSourceTimeZone() {
|
|
31314
|
+
const configured = readEnvVar("GOG_TIMEZONE");
|
|
31315
|
+
if (configured && isValidTimeZone(configured)) return configured;
|
|
31316
|
+
return displayTimeZone();
|
|
31317
|
+
}
|
|
31318
|
+
function offsetAt(instant, tz) {
|
|
31319
|
+
const w = wallPartsIn(instant, tz);
|
|
31320
|
+
const asUTC = Date.UTC(w.year, w.month - 1, w.day, w.hour, w.minute, w.second, instant.getUTCMilliseconds());
|
|
31321
|
+
const minutes = Math.round((asUTC - instant.getTime()) / 6e4);
|
|
31322
|
+
const sign = minutes < 0 ? "-" : "+";
|
|
31323
|
+
const abs = Math.abs(minutes);
|
|
31324
|
+
return `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;
|
|
31325
|
+
}
|
|
31326
|
+
function wallPartsIn(instant, tz) {
|
|
31327
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
31328
|
+
timeZone: tz,
|
|
31329
|
+
year: "numeric",
|
|
31330
|
+
month: "2-digit",
|
|
31331
|
+
day: "2-digit",
|
|
31332
|
+
hour: "2-digit",
|
|
31333
|
+
minute: "2-digit",
|
|
31334
|
+
second: "2-digit",
|
|
31335
|
+
// h23 pins midnight to hour 00; without it some ICU builds render hour 24.
|
|
31336
|
+
hourCycle: "h23"
|
|
31337
|
+
}).formatToParts(instant);
|
|
31338
|
+
const out = {};
|
|
31339
|
+
for (const p of parts) {
|
|
31340
|
+
if (p.type !== "literal") out[p.type] = Number(p.value);
|
|
31341
|
+
}
|
|
31342
|
+
return out;
|
|
31343
|
+
}
|
|
31344
|
+
function wallTimeToInstant(y, mo, d, h, mi, s, ms, tz) {
|
|
31345
|
+
let guess = Date.UTC(y, mo - 1, d, h, mi, s, ms);
|
|
31346
|
+
for (let i = 0; i < 2; i += 1) {
|
|
31347
|
+
const seen = wallPartsIn(new Date(guess), tz);
|
|
31348
|
+
const seenUTC = Date.UTC(seen.year, seen.month - 1, seen.day, seen.hour, seen.minute, seen.second, ms);
|
|
31349
|
+
const drift = Date.UTC(y, mo - 1, d, h, mi, s, ms) - seenUTC;
|
|
31350
|
+
if (drift === 0) break;
|
|
31351
|
+
guess += drift;
|
|
31352
|
+
}
|
|
31353
|
+
return new Date(guess);
|
|
31354
|
+
}
|
|
31355
|
+
function pad(n, width = 2) {
|
|
31356
|
+
return String(n).padStart(width, "0");
|
|
31357
|
+
}
|
|
31358
|
+
function isoWithOffset(instant, tz, offset) {
|
|
31359
|
+
const w = wallPartsIn(instant, tz);
|
|
31360
|
+
const msPart = instant.getUTCMilliseconds();
|
|
31361
|
+
const frac = msPart ? `.${pad(msPart, 3)}` : "";
|
|
31362
|
+
return `${pad(w.year, 4)}-${pad(w.month)}-${pad(w.day)}T${pad(w.hour)}:${pad(w.minute)}:${pad(w.second)}${frac}${offset}`;
|
|
31363
|
+
}
|
|
31364
|
+
function formatInstant(instant, tz = displayTimeZone()) {
|
|
31365
|
+
const offset = offsetAt(instant, tz);
|
|
31366
|
+
const display = new Intl.DateTimeFormat("en-US", {
|
|
31367
|
+
timeZone: tz,
|
|
31368
|
+
weekday: "short",
|
|
31369
|
+
month: "short",
|
|
31370
|
+
day: "numeric",
|
|
31371
|
+
year: "numeric",
|
|
31372
|
+
hour: "numeric",
|
|
31373
|
+
minute: "2-digit",
|
|
31374
|
+
timeZoneName: "short"
|
|
31375
|
+
}).format(instant);
|
|
31376
|
+
return { iso: isoWithOffset(instant, tz, offset), display };
|
|
31377
|
+
}
|
|
31378
|
+
var TIMESTAMP_KEYS = /* @__PURE__ */ new Set([
|
|
31379
|
+
"date",
|
|
31380
|
+
// gog gmail message/thread listings ("2026-07-28 03:36")
|
|
31381
|
+
"dateTime",
|
|
31382
|
+
// Calendar event start/end
|
|
31383
|
+
"internalDate",
|
|
31384
|
+
// Gmail, epoch milliseconds (authoritative)
|
|
31385
|
+
"modifiedTime",
|
|
31386
|
+
// Drive
|
|
31387
|
+
"createdTime",
|
|
31388
|
+
// Drive
|
|
31389
|
+
"createTime",
|
|
31390
|
+
"updateTime",
|
|
31391
|
+
"updated",
|
|
31392
|
+
"originalStartTime",
|
|
31393
|
+
"expirationTime",
|
|
31394
|
+
"lastModified",
|
|
31395
|
+
"sentAt",
|
|
31396
|
+
"viewedAt",
|
|
31397
|
+
"modifiedAt",
|
|
31398
|
+
"fetchedBodyAt",
|
|
31399
|
+
"asOf"
|
|
31400
|
+
]);
|
|
31401
|
+
var ZONE_NAME_KEYS = /* @__PURE__ */ new Set(["timeZone", "timezone"]);
|
|
31402
|
+
var RFC3339_WITH_OFFSET = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d{1,9}))?(Z|[+-]\d{2}:?\d{2})$/;
|
|
31403
|
+
var NAIVE_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d{1,9}))?$/;
|
|
31404
|
+
var EPOCH_MILLIS = /^\d{13}$/;
|
|
31405
|
+
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
31406
|
+
function isRealCalendarDate(p) {
|
|
31407
|
+
const utc = new Date(Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second));
|
|
31408
|
+
return utc.getUTCFullYear() === p.year && utc.getUTCMonth() === p.month - 1 && utc.getUTCDate() === p.day && utc.getUTCHours() === p.hour && utc.getUTCMinutes() === p.minute && utc.getUTCSeconds() === p.second;
|
|
31409
|
+
}
|
|
31410
|
+
function parseTimestampValue(key, value, assumeNaiveIn) {
|
|
31411
|
+
if (typeof value !== "string") return null;
|
|
31412
|
+
const raw = value.trim();
|
|
31413
|
+
if (raw === "" || DATE_ONLY.test(raw)) return null;
|
|
31414
|
+
if (key === "internalDate" && EPOCH_MILLIS.test(raw)) {
|
|
31415
|
+
return new Date(Number(raw));
|
|
31416
|
+
}
|
|
31417
|
+
if (RFC3339_WITH_OFFSET.test(raw)) {
|
|
31418
|
+
const parsed = new Date(raw.replace(" ", "T"));
|
|
31419
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
31420
|
+
}
|
|
31421
|
+
const naive = NAIVE_DATE_TIME.exec(raw);
|
|
31422
|
+
if (naive) {
|
|
31423
|
+
const [, y, mo, d, h, mi, s, frac] = naive;
|
|
31424
|
+
const ms = frac ? Number(frac.padEnd(3, "0").slice(0, 3)) : 0;
|
|
31425
|
+
const parts = {
|
|
31426
|
+
year: Number(y),
|
|
31427
|
+
month: Number(mo),
|
|
31428
|
+
day: Number(d),
|
|
31429
|
+
hour: Number(h),
|
|
31430
|
+
minute: Number(mi),
|
|
31431
|
+
second: Number(s ?? "0")
|
|
31432
|
+
};
|
|
31433
|
+
if (!isRealCalendarDate(parts)) return null;
|
|
31434
|
+
return wallTimeToInstant(
|
|
31435
|
+
parts.year,
|
|
31436
|
+
parts.month,
|
|
31437
|
+
parts.day,
|
|
31438
|
+
parts.hour,
|
|
31439
|
+
parts.minute,
|
|
31440
|
+
parts.second,
|
|
31441
|
+
ms,
|
|
31442
|
+
assumeNaiveIn
|
|
31443
|
+
);
|
|
31444
|
+
}
|
|
31445
|
+
return null;
|
|
31446
|
+
}
|
|
31447
|
+
function walk(node, tz, naiveTz) {
|
|
31448
|
+
let changed = false;
|
|
31449
|
+
if (Array.isArray(node)) {
|
|
31450
|
+
for (const item of node) {
|
|
31451
|
+
if (walk(item, tz, naiveTz)) changed = true;
|
|
31452
|
+
}
|
|
31453
|
+
return changed;
|
|
31454
|
+
}
|
|
31455
|
+
if (node === null || typeof node !== "object") return false;
|
|
31456
|
+
const obj = node;
|
|
31457
|
+
for (const key of Object.keys(obj)) {
|
|
31458
|
+
const value = obj[key];
|
|
31459
|
+
if (value !== null && typeof value === "object") {
|
|
31460
|
+
if (walk(value, tz, naiveTz)) changed = true;
|
|
31461
|
+
continue;
|
|
31462
|
+
}
|
|
31463
|
+
if (ZONE_NAME_KEYS.has(key) || !TIMESTAMP_KEYS.has(key)) continue;
|
|
31464
|
+
const instant = parseTimestampValue(key, value, naiveTz);
|
|
31465
|
+
if (!instant) continue;
|
|
31466
|
+
const { iso, display } = formatInstant(instant, tz);
|
|
31467
|
+
obj[key] = iso;
|
|
31468
|
+
obj[`${key}Display`] = display;
|
|
31469
|
+
changed = true;
|
|
31470
|
+
}
|
|
31471
|
+
return changed;
|
|
31472
|
+
}
|
|
31473
|
+
function detectIndent(text) {
|
|
31474
|
+
const match = /\n(\s+)\S/.exec(text);
|
|
31475
|
+
return match ? match[1].replace(/\t/g, " ").length : 0;
|
|
31476
|
+
}
|
|
31477
|
+
function normalizeTimestamps(text, tz = displayTimeZone(), naiveTz = naiveSourceTimeZone()) {
|
|
31478
|
+
const trimmed = text.trim();
|
|
31479
|
+
if (trimmed === "" || !/^[[{]/.test(trimmed)) return text;
|
|
31480
|
+
let parsed;
|
|
31481
|
+
try {
|
|
31482
|
+
parsed = JSON.parse(trimmed);
|
|
31483
|
+
} catch {
|
|
31484
|
+
return text;
|
|
31485
|
+
}
|
|
31486
|
+
if (!walk(parsed, tz, naiveTz)) return text;
|
|
31487
|
+
return JSON.stringify(parsed, null, detectIndent(text));
|
|
31488
|
+
}
|
|
31489
|
+
|
|
31278
31490
|
// ../gogcli-mcp/src/tools/utils.ts
|
|
31279
31491
|
var PAYLOAD_INLINE_MAX = 4096;
|
|
31280
31492
|
function payloadArg(inlineFlag, fileFlag, value, ext) {
|
|
@@ -31375,7 +31587,8 @@ ${accounts || "(none)"}${hint}`);
|
|
|
31375
31587
|
}
|
|
31376
31588
|
async function runOrDiagnose(args, options) {
|
|
31377
31589
|
try {
|
|
31378
|
-
|
|
31590
|
+
const raw = await run(args, options);
|
|
31591
|
+
return rawTextResult(options.lossless ? raw : normalizeTimestamps(raw));
|
|
31379
31592
|
} catch (err) {
|
|
31380
31593
|
return diagnose(err);
|
|
31381
31594
|
}
|
|
@@ -31639,7 +31852,7 @@ function registerSlidesTools(server) {
|
|
|
31639
31852
|
}
|
|
31640
31853
|
|
|
31641
31854
|
// ../gogcli-mcp/src/server.ts
|
|
31642
|
-
var VERSION = true ? "2.
|
|
31855
|
+
var VERSION = true ? "2.19.0" : "0.0.0";
|
|
31643
31856
|
|
|
31644
31857
|
// src/tools/slides-extra.ts
|
|
31645
31858
|
function registerExtraSlidesTools(server) {
|
|
@@ -31784,7 +31997,7 @@ function registerExtraSlidesTools(server) {
|
|
|
31784
31997
|
}, async ({ presentationId, pretty, account }) => {
|
|
31785
31998
|
const args = ["slides", "raw", presentationId];
|
|
31786
31999
|
if (pretty) args.push("--pretty");
|
|
31787
|
-
return runOrDiagnose(args, { account });
|
|
32000
|
+
return runOrDiagnose(args, { account, lossless: true });
|
|
31788
32001
|
});
|
|
31789
32002
|
server.registerTool("gog_slides_locate", {
|
|
31790
32003
|
description: "Locate literal text in a presentation's shapes and table cells, returning the containing element object IDs and UTF-16 ranges needed for gog_slides_style_text / gog_slides_link / gog_slides_bullets / gog_slides_insert_text. Read-only.",
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifest_version": "0.3",
|
|
4
4
|
"name": "gogcli-mcp-slides",
|
|
5
5
|
"display_name": "gogcli (Slides)",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.19.0",
|
|
7
7
|
"description": "Extended Google Slides for Claude via gogcli — auth + full Slides support (create, edit, export, templates, markdown)",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-slides",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"mcpName": "io.github.chrischall/gogcli-mcp-slides",
|
|
5
5
|
"description": "Extended Google Slides MCP server via gogcli — auth + full Slides support",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/gogcli-mcp-slides"
|
|
9
9
|
},
|
|
10
|
-
"version": "2.
|
|
10
|
+
"version": "2.19.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-slides",
|
|
15
|
-
"version": "2.
|
|
15
|
+
"version": "2.19.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
|
@@ -176,7 +176,8 @@ export function registerExtraSlidesTools(server: McpServer): void {
|
|
|
176
176
|
}, async ({ presentationId, pretty, account }) => {
|
|
177
177
|
const args = ['slides', 'raw', presentationId];
|
|
178
178
|
if (pretty) args.push('--pretty');
|
|
179
|
-
|
|
179
|
+
// Verbatim by contract: see the `lossless` note on runOrDiagnose.
|
|
180
|
+
return runOrDiagnose(args, { account, lossless: true });
|
|
180
181
|
});
|
|
181
182
|
|
|
182
183
|
server.registerTool('gog_slides_locate', {
|
|
@@ -342,11 +342,11 @@ describe('gog_slides_replace_slide url mode', () => {
|
|
|
342
342
|
describe('gog_slides_raw', () => {
|
|
343
343
|
it('passes --pretty', async () => {
|
|
344
344
|
await harness.callTool('gog_slides_raw', { presentationId: 'p1', pretty: true });
|
|
345
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'raw', 'p1', '--pretty'], { account: undefined });
|
|
345
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'raw', 'p1', '--pretty'], { account: undefined, lossless: true });
|
|
346
346
|
});
|
|
347
347
|
it('bare', async () => {
|
|
348
348
|
await harness.callTool('gog_slides_raw', { presentationId: 'p1' });
|
|
349
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'raw', 'p1'], { account: undefined });
|
|
349
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'raw', 'p1'], { account: undefined, lossless: true });
|
|
350
350
|
});
|
|
351
351
|
});
|
|
352
352
|
|