gogcli-mcp 2.0.11 → 2.2.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/CHANGELOG.md +42 -0
- package/dist/index.js +132 -34
- package/dist/lib.js +132 -34
- package/manifest.json +1 -1
- package/package.json +3 -3
- package/server.json +2 -2
- package/src/tools/calendar.ts +12 -4
- package/src/tools/gmail.ts +1 -1
- package/src/tools/sheets-a1.ts +64 -0
- package/src/tools/sheets.ts +54 -14
- package/src/tools/utils.ts +59 -12
- package/tests/tools/calendar.test.ts +81 -0
- package/tests/tools/sheets-a1.test.ts +70 -0
- package/tests/tools/sheets.test.ts +162 -0
- package/tests/tools/utils.test.ts +103 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
10
|
-
"version": "2.0
|
|
10
|
+
"version": "2.2.0"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "gogcli",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
18
|
-
"version": "2.0
|
|
18
|
+
"version": "2.2.0",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [2.1.0](https://github.com/chrischall/gogcli-mcp/compare/v2.0.13...v2.1.0) (2026-05-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add gogcli-mcp-slides and gogcli-mcp-classroom sub-packages ([cee8724](https://github.com/chrischall/gogcli-mcp/commit/cee872442240e52ae1cac9b9085eabce0c5ff9c3))
|
|
9
|
+
* add manifest.json and SKILL.md to sub-packages for .mcpb and .skill builds ([d1339c6](https://github.com/chrischall/gogcli-mcp/commit/d1339c690b1eeee8221b7cc15fd9e4fa48c1e20a))
|
|
10
|
+
* address open issues for docs + sheets ([7109253](https://github.com/chrischall/gogcli-mcp/commit/7109253b3eb4b3dcc17cda7a8dfb1b6969023bcf))
|
|
11
|
+
* **deploy:** per-sub-package registry listings (MCP Registry, ClawHub, Claude plugins) ([6cc1798](https://github.com/chrischall/gogcli-mcp/commit/6cc1798320af6dd3af709790e6bec30f46c5f3d2))
|
|
12
|
+
* **drive:** add gogcli-mcp-drive sub-package ([d8eef61](https://github.com/chrischall/gogcli-mcp/commit/d8eef61f16266209da70cd05ca0d2378bf3c4613))
|
|
13
|
+
* fold People into contacts and Meet into calendar; add TODO.md ([f649119](https://github.com/chrischall/gogcli-mcp/commit/f64911902094f87ae31420913e9d1a5ba608441f))
|
|
14
|
+
* wrap new gog cli v0.18.0 tools ([173242a](https://github.com/chrischall/gogcli-mcp/commit/173242aafaa8c2f2d7282a2fbd8faedfc65a63bd))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* emit type declarations from base so sub-packages can resolve gogcli-mcp/lib ([9cee5ed](https://github.com/chrischall/gogcli-mcp/commit/9cee5ed97b0cc72dde81c7f8a01d71c95a0291b7))
|
|
20
|
+
* **mcpb:** add per-sub-package .mcpbignore to trim bundles ([e739d9e](https://github.com/chrischall/gogcli-mcp/commit/e739d9ec9ae8e4cc19b1189bb5bf7ac90481c943))
|
|
21
|
+
* **runner:** augment PATH + helpful ENOENT message for missing gog ([34e696f](https://github.com/chrischall/gogcli-mcp/commit/34e696f8c74c7003e6b1bb31991a05184a3b6094))
|
|
22
|
+
* **runner:** fall back to PATH lookup when GOG_PATH is empty string ([a726592](https://github.com/chrischall/gogcli-mcp/commit/a726592c86e869fe27b176798a9b8909e7821f4c))
|
|
23
|
+
* **runner:** treat unresolved .mcpb placeholders as unset env vars ([17a0363](https://github.com/chrischall/gogcli-mcp/commit/17a0363f7dffd2757667e0d1d9377e2e2cb6fbc7))
|
|
24
|
+
* treat empty GOG_PATH as unset, fall back to 'gog' on PATH ([0204638](https://github.com/chrischall/gogcli-mcp/commit/020463813f941b1fa8ec2360346c55abc244675f))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Refactor
|
|
28
|
+
|
|
29
|
+
* aggressive cleanup pass (audit findings) ([e635b39](https://github.com/chrischall/gogcli-mcp/commit/e635b391cf611e9c102a422ffbce5ebe28687b80))
|
|
30
|
+
* make sub-packages focused (auth + service only) ([4301306](https://github.com/chrischall/gogcli-mcp/commit/4301306cecf12e66d67dbc4ce9debae8f3dcfabc))
|
|
31
|
+
* rebalance base vs sub-package tool split (120 -> 84 base) ([1074953](https://github.com/chrischall/gogcli-mcp/commit/1074953833fc4766e85cc0b28cbcc08834f8dc47))
|
|
32
|
+
* remove comments escape hatch from base docs tool ([a5364c6](https://github.com/chrischall/gogcli-mcp/commit/a5364c6df1948e57b185b9d753aa11b033edd0d0))
|
|
33
|
+
* restructure into npm workspaces monorepo ([9645100](https://github.com/chrischall/gogcli-mcp/commit/9645100e8120c03956ee972023b12d2e99876cc5))
|
|
34
|
+
* review-pass cleanup (enums, shared schemas, test harness) ([ea851de](https://github.com/chrischall/gogcli-mcp/commit/ea851deb30b9cfef3f07ed506a011fc363b34018))
|
|
35
|
+
* use relative imports instead of workspace symlinks ([a278d6f](https://github.com/chrischall/gogcli-mcp/commit/a278d6fb31be480571481e9d87e199d261a3f031))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
### Documentation
|
|
39
|
+
|
|
40
|
+
* add per-package READMEs, update root README and CLAUDE.md for monorepo ([19d547e](https://github.com/chrischall/gogcli-mcp/commit/19d547e5ceb14298a75ab297d0a9a11c6ea59e24))
|
|
41
|
+
* fix tool counts and stale descriptions across all docs ([3558c72](https://github.com/chrischall/gogcli-mcp/commit/3558c72fa5c3ee3dbc8a06e9d7fb51bf73ef77c7))
|
|
42
|
+
* update gogcli repo URLs to openclaw/gogcli (was steipete/gogcli) ([951a181](https://github.com/chrischall/gogcli-mcp/commit/951a1818ebc269f203aee723ccbea32c13817d8b))
|
package/dist/index.js
CHANGED
|
@@ -31074,7 +31074,7 @@ async function run(args, options = {}) {
|
|
|
31074
31074
|
|
|
31075
31075
|
// 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
|
|
|
@@ -31254,7 +31269,7 @@ function registerCalendarTools(server2) {
|
|
|
31254
31269
|
return runOrDiagnose(["calendar", "event", calendarId, eventId], { account });
|
|
31255
31270
|
});
|
|
31256
31271
|
server2.registerTool("gog_calendar_create", {
|
|
31257
|
-
description: "Create a calendar event.",
|
|
31272
|
+
description: "Create a calendar event. Set withZoom=true to attach a Zoom meeting (requires Zoom S2S OAuth setup via gog_zoom_auth_setup; the join URL + meeting ID + passcode are appended to the event description \u2014 Google rejects native conference card writes from non-Workspace-Marketplace OAuth clients).",
|
|
31258
31273
|
annotations: { destructiveHint: false },
|
|
31259
31274
|
inputSchema: {
|
|
31260
31275
|
calendarId: external_exports.string().describe('Calendar ID (use "primary" for the default calendar)'),
|
|
@@ -31265,18 +31280,20 @@ function registerCalendarTools(server2) {
|
|
|
31265
31280
|
location: external_exports.string().optional().describe("Event location"),
|
|
31266
31281
|
attendees: external_exports.string().optional().describe("Attendee emails, comma-separated"),
|
|
31267
31282
|
allDay: external_exports.boolean().optional().describe("All-day event (use date-only in from/to)"),
|
|
31283
|
+
withZoom: external_exports.boolean().optional().describe("Create a Zoom video conference for this event (requires Zoom S2S OAuth setup)"),
|
|
31268
31284
|
account: accountParam
|
|
31269
31285
|
}
|
|
31270
|
-
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, account }) => {
|
|
31286
|
+
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, withZoom, account }) => {
|
|
31271
31287
|
const args = ["calendar", "create", calendarId, `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
31272
31288
|
if (description) args.push(`--description=${description}`);
|
|
31273
31289
|
if (location) args.push(`--location=${location}`);
|
|
31274
31290
|
if (attendees) args.push(`--attendees=${attendees}`);
|
|
31275
31291
|
if (allDay) args.push("--all-day");
|
|
31292
|
+
if (withZoom) args.push("--with-zoom");
|
|
31276
31293
|
return runOrDiagnose(args, { account });
|
|
31277
31294
|
});
|
|
31278
31295
|
server2.registerTool("gog_calendar_update", {
|
|
31279
|
-
description: "Update an existing calendar event.",
|
|
31296
|
+
description: "Update an existing calendar event. Zoom: withZoom adds a Zoom meeting, regenerateZoom replaces the existing one, removeZoom strips it (each are independent \u2014 use one per call).",
|
|
31280
31297
|
annotations: { destructiveHint: false },
|
|
31281
31298
|
inputSchema: {
|
|
31282
31299
|
calendarId: external_exports.string().describe("Calendar ID"),
|
|
@@ -31287,9 +31304,12 @@ function registerCalendarTools(server2) {
|
|
|
31287
31304
|
description: external_exports.string().optional().describe("New description"),
|
|
31288
31305
|
location: external_exports.string().optional().describe("New location"),
|
|
31289
31306
|
attendees: external_exports.string().optional().describe("New attendee emails, comma-separated (replaces existing)"),
|
|
31307
|
+
withZoom: external_exports.boolean().optional().describe("Create a Zoom video conference for this event"),
|
|
31308
|
+
regenerateZoom: external_exports.boolean().optional().describe("Replace the event's existing Zoom video conference"),
|
|
31309
|
+
removeZoom: external_exports.boolean().optional().describe("Remove the event's Zoom video conference"),
|
|
31290
31310
|
account: accountParam
|
|
31291
31311
|
}
|
|
31292
|
-
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, account }) => {
|
|
31312
|
+
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, withZoom, regenerateZoom, removeZoom, account }) => {
|
|
31293
31313
|
const args = ["calendar", "update", calendarId, eventId];
|
|
31294
31314
|
if (summary !== void 0) args.push(`--summary=${summary}`);
|
|
31295
31315
|
if (from !== void 0) args.push(`--from=${from}`);
|
|
@@ -31297,6 +31317,9 @@ function registerCalendarTools(server2) {
|
|
|
31297
31317
|
if (description !== void 0) args.push(`--description=${description}`);
|
|
31298
31318
|
if (location !== void 0) args.push(`--location=${location}`);
|
|
31299
31319
|
if (attendees !== void 0) args.push(`--attendees=${attendees}`);
|
|
31320
|
+
if (withZoom) args.push("--with-zoom");
|
|
31321
|
+
if (regenerateZoom) args.push("--regenerate-zoom");
|
|
31322
|
+
if (removeZoom) args.push("--remove-zoom");
|
|
31300
31323
|
return runOrDiagnose(args, { account });
|
|
31301
31324
|
});
|
|
31302
31325
|
server2.registerTool("gog_calendar_delete", {
|
|
@@ -31933,7 +31956,7 @@ function registerDriveTools(server2) {
|
|
|
31933
31956
|
// src/tools/gmail.ts
|
|
31934
31957
|
function registerGmailTools(server2) {
|
|
31935
31958
|
server2.registerTool("gog_gmail_search", {
|
|
31936
|
-
description:
|
|
31959
|
+
description: `Search Gmail threads using Gmail query syntax (e.g. "from:alice subject:invoice is:unread"). The query is passed verbatim to Gmail; a bare name token (from:alison) matches per Gmail's own heuristics, a full address (from:alison@example.com) is exact. To match a contact across several addresses, OR them: from:(a@x.com OR b@y.com).`,
|
|
31937
31960
|
annotations: { readOnlyHint: true },
|
|
31938
31961
|
inputSchema: {
|
|
31939
31962
|
query: external_exports.string().describe("Gmail search query"),
|
|
@@ -31982,8 +32005,62 @@ function registerGmailTools(server2) {
|
|
|
31982
32005
|
registerRunTool(server2, { service: "gmail", examples: '"archive", "mark-read", "labels"' });
|
|
31983
32006
|
}
|
|
31984
32007
|
|
|
32008
|
+
// src/tools/sheets-a1.ts
|
|
32009
|
+
function colToLetter(n) {
|
|
32010
|
+
let s = "";
|
|
32011
|
+
while (n > 0) {
|
|
32012
|
+
const rem = (n - 1) % 26;
|
|
32013
|
+
s = String.fromCharCode(65 + rem) + s;
|
|
32014
|
+
n = Math.floor((n - 1) / 26);
|
|
32015
|
+
}
|
|
32016
|
+
return s;
|
|
32017
|
+
}
|
|
32018
|
+
function letterToCol(s) {
|
|
32019
|
+
let n = 0;
|
|
32020
|
+
for (const ch of s.toUpperCase()) {
|
|
32021
|
+
n = n * 26 + (ch.charCodeAt(0) - 64);
|
|
32022
|
+
}
|
|
32023
|
+
return n;
|
|
32024
|
+
}
|
|
32025
|
+
function expandAnchorRange(range, rows, cols) {
|
|
32026
|
+
const bang = range.lastIndexOf("!");
|
|
32027
|
+
const sheet = bang >= 0 ? range.slice(0, bang + 1) : "";
|
|
32028
|
+
const cell = bang >= 0 ? range.slice(bang + 1) : range;
|
|
32029
|
+
const m = /^([A-Za-z]+)([0-9]+)$/.exec(cell);
|
|
32030
|
+
if (!m) return range;
|
|
32031
|
+
const startCol = letterToCol(m[1]);
|
|
32032
|
+
const startRow = parseInt(m[2], 10);
|
|
32033
|
+
const endCol = colToLetter(startCol + cols - 1);
|
|
32034
|
+
const endRow = startRow + rows - 1;
|
|
32035
|
+
return `${sheet}${m[1].toUpperCase()}${startRow}:${endCol}${endRow}`;
|
|
32036
|
+
}
|
|
32037
|
+
function countNonEmptyCells(getOutput) {
|
|
32038
|
+
let parsed;
|
|
32039
|
+
try {
|
|
32040
|
+
parsed = JSON.parse(getOutput);
|
|
32041
|
+
} catch {
|
|
32042
|
+
return -1;
|
|
32043
|
+
}
|
|
32044
|
+
const values = parsed?.values;
|
|
32045
|
+
if (!Array.isArray(values)) return 0;
|
|
32046
|
+
let count = 0;
|
|
32047
|
+
for (const row of values) {
|
|
32048
|
+
if (!Array.isArray(row)) continue;
|
|
32049
|
+
for (const cell of row) {
|
|
32050
|
+
if (cell !== null && String(cell).trim() !== "") count++;
|
|
32051
|
+
}
|
|
32052
|
+
}
|
|
32053
|
+
return count;
|
|
32054
|
+
}
|
|
32055
|
+
|
|
31985
32056
|
// src/tools/sheets.ts
|
|
31986
32057
|
var cellValueParam = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]);
|
|
32058
|
+
var dryRunParam = external_exports.boolean().optional().describe(
|
|
32059
|
+
"Preview the operation without modifying the sheet (gog --dry-run): reports the intended actions and exits without writing."
|
|
32060
|
+
);
|
|
32061
|
+
var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
32062
|
+
'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.'
|
|
32063
|
+
);
|
|
31987
32064
|
function registerSheetsTools(server2) {
|
|
31988
32065
|
server2.registerTool("gog_sheets_get", {
|
|
31989
32066
|
description: 'Read values from a Google Sheets range. Returns a JSON object with a "values" array of rows.',
|
|
@@ -32003,13 +32080,31 @@ function registerSheetsTools(server2) {
|
|
|
32003
32080
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID (from the URL)"),
|
|
32004
32081
|
range: external_exports.string().describe("Top-left cell or range in A1 notation, e.g. Sheet1!A1"),
|
|
32005
32082
|
values: external_exports.array(external_exports.array(cellValueParam)).describe('2D array of values (rows of columns). Cells may be string/number/boolean/null; strings starting with "=" are formulas.'),
|
|
32083
|
+
dry_run: dryRunParam,
|
|
32084
|
+
fail_if_not_empty: failIfNotEmptyParam,
|
|
32006
32085
|
account: accountParam
|
|
32007
32086
|
}
|
|
32008
|
-
}, async ({ spreadsheetId, range, values, account }) => {
|
|
32009
|
-
|
|
32010
|
-
|
|
32011
|
-
|
|
32012
|
-
|
|
32087
|
+
}, async ({ spreadsheetId, range, values, account, dry_run, fail_if_not_empty }) => {
|
|
32088
|
+
const cols = values.reduce((max, row) => Math.max(max, row.length), 0);
|
|
32089
|
+
if (fail_if_not_empty && values.length > 0 && cols > 0) {
|
|
32090
|
+
const readRange = expandAnchorRange(range, values.length, cols);
|
|
32091
|
+
let existing;
|
|
32092
|
+
try {
|
|
32093
|
+
existing = await run(["sheets", "get", spreadsheetId, readRange], { account });
|
|
32094
|
+
} catch (err) {
|
|
32095
|
+
return diagnose(err);
|
|
32096
|
+
}
|
|
32097
|
+
const occupied = countNonEmptyCells(existing);
|
|
32098
|
+
if (occupied !== 0) {
|
|
32099
|
+
const detail = occupied < 0 ? "could not be verified as empty" : `already contains data in ${occupied} cell(s)`;
|
|
32100
|
+
return toText(
|
|
32101
|
+
`Write aborted (fail_if_not_empty): target range ${readRange} ${detail}. Re-run without fail_if_not_empty to overwrite, or clear it first with gog_sheets_clear.`
|
|
32102
|
+
);
|
|
32103
|
+
}
|
|
32104
|
+
}
|
|
32105
|
+
const args = ["sheets", "update", spreadsheetId, range, `--values-json=${JSON.stringify(values)}`];
|
|
32106
|
+
if (dry_run) args.push("--dry-run");
|
|
32107
|
+
return runOrDiagnose(args, { account });
|
|
32013
32108
|
});
|
|
32014
32109
|
server2.registerTool("gog_sheets_append", {
|
|
32015
32110
|
description: 'Append rows to a Google Sheet after the last row with data in the given range. Values may be strings, numbers, booleans, or null. Strings starting with "=" are interpreted as formulas.',
|
|
@@ -32018,13 +32113,13 @@ function registerSheetsTools(server2) {
|
|
|
32018
32113
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID (from the URL)"),
|
|
32019
32114
|
range: external_exports.string().describe("Range indicating which sheet/columns to append to, e.g. Sheet1!A:C"),
|
|
32020
32115
|
values: external_exports.array(external_exports.array(cellValueParam)).describe('2D array of rows to append. Cells may be string/number/boolean/null; strings starting with "=" are formulas.'),
|
|
32116
|
+
dry_run: dryRunParam,
|
|
32021
32117
|
account: accountParam
|
|
32022
32118
|
}
|
|
32023
|
-
}, async ({ spreadsheetId, range, values, account }) => {
|
|
32024
|
-
|
|
32025
|
-
|
|
32026
|
-
|
|
32027
|
-
);
|
|
32119
|
+
}, async ({ spreadsheetId, range, values, account, dry_run }) => {
|
|
32120
|
+
const args = ["sheets", "append", spreadsheetId, range, `--values-json=${JSON.stringify(values)}`];
|
|
32121
|
+
if (dry_run) args.push("--dry-run");
|
|
32122
|
+
return runOrDiagnose(args, { account });
|
|
32028
32123
|
});
|
|
32029
32124
|
server2.registerTool("gog_sheets_clear", {
|
|
32030
32125
|
description: "Clear all values in a Google Sheets range (formatting is preserved).",
|
|
@@ -32032,13 +32127,16 @@ function registerSheetsTools(server2) {
|
|
|
32032
32127
|
inputSchema: {
|
|
32033
32128
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
32034
32129
|
range: external_exports.string().describe("Range in A1 notation to clear"),
|
|
32130
|
+
dry_run: dryRunParam,
|
|
32035
32131
|
account: accountParam
|
|
32036
32132
|
}
|
|
32037
|
-
}, async ({ spreadsheetId, range, account }) => {
|
|
32038
|
-
|
|
32133
|
+
}, async ({ spreadsheetId, range, account, dry_run }) => {
|
|
32134
|
+
const args = ["sheets", "clear", spreadsheetId, range];
|
|
32135
|
+
if (dry_run) args.push("--dry-run");
|
|
32136
|
+
return runOrDiagnose(args, { account });
|
|
32039
32137
|
});
|
|
32040
32138
|
server2.registerTool("gog_sheets_metadata", {
|
|
32041
|
-
description: "Get spreadsheet metadata: title,
|
|
32139
|
+
description: "Get spreadsheet metadata: title, named ranges, and per-tab properties including grid dimensions (gridProperties.rowCount / columnCount). Use this to learn a sheet's current size before writing \u2014 a write outside the grid fails.",
|
|
32042
32140
|
annotations: { readOnlyHint: true },
|
|
32043
32141
|
inputSchema: {
|
|
32044
32142
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
@@ -32224,7 +32322,7 @@ function registerTasksTools(server2) {
|
|
|
32224
32322
|
}
|
|
32225
32323
|
|
|
32226
32324
|
// src/server.ts
|
|
32227
|
-
var VERSION = true ? "2.0
|
|
32325
|
+
var VERSION = true ? "2.2.0" : "0.0.0";
|
|
32228
32326
|
function createServer(options) {
|
|
32229
32327
|
return new McpServer({
|
|
32230
32328
|
name: options?.name ?? "gogcli",
|
package/dist/lib.js
CHANGED
|
@@ -30981,7 +30981,7 @@ async function run(args, options = {}) {
|
|
|
30981
30981
|
|
|
30982
30982
|
// src/tools/utils.ts
|
|
30983
30983
|
var accountParam = external_exports.string().optional().describe(
|
|
30984
|
-
"Google account email to use
|
|
30984
|
+
"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."
|
|
30985
30985
|
);
|
|
30986
30986
|
var ids = {
|
|
30987
30987
|
course: external_exports.string().describe("Course ID"),
|
|
@@ -31045,26 +31045,41 @@ function toError(err) {
|
|
|
31045
31045
|
}
|
|
31046
31046
|
var AUTH_ERROR_PATTERN = /\b(401|unauthorized|token.*(expired|revoked)|invalid_grant)\b/i;
|
|
31047
31047
|
var TRANSIENT_ERROR_PATTERN = /\b429\b|\b5\d\d\b|\bquota\b|rateLimit|\bDEADLINE_EXCEEDED\b/i;
|
|
31048
|
+
var GRID_LIMIT_ERROR_PATTERN = /exceeds grid limits/i;
|
|
31048
31049
|
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.";
|
|
31049
31050
|
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).";
|
|
31051
|
+
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.";
|
|
31052
|
+
function formatAccountList(raw) {
|
|
31053
|
+
try {
|
|
31054
|
+
const parsed = JSON.parse(raw);
|
|
31055
|
+
if (Array.isArray(parsed?.accounts)) {
|
|
31056
|
+
return parsed.accounts.map((a) => a?.email).filter(Boolean).join("\n");
|
|
31057
|
+
}
|
|
31058
|
+
} catch {
|
|
31059
|
+
}
|
|
31060
|
+
return raw.trim();
|
|
31061
|
+
}
|
|
31062
|
+
async function diagnose(err) {
|
|
31063
|
+
const errText = toError(err).content[0].text;
|
|
31064
|
+
const isAuthError = AUTH_ERROR_PATTERN.test(errText);
|
|
31065
|
+
const isTransientError = !isAuthError && TRANSIENT_ERROR_PATTERN.test(errText);
|
|
31066
|
+
const isGridLimitError = GRID_LIMIT_ERROR_PATTERN.test(errText);
|
|
31067
|
+
const hint = isAuthError ? AUTH_HINT : isTransientError ? TRANSIENT_HINT : isGridLimitError ? GRID_LIMIT_HINT : "";
|
|
31068
|
+
try {
|
|
31069
|
+
const accounts = formatAccountList(await run(["auth", "list"]));
|
|
31070
|
+
return toText(`${errText}
|
|
31071
|
+
|
|
31072
|
+
Configured accounts:
|
|
31073
|
+
${accounts || "(none)"}${hint}`);
|
|
31074
|
+
} catch {
|
|
31075
|
+
return toText(`${errText}${hint}`);
|
|
31076
|
+
}
|
|
31077
|
+
}
|
|
31050
31078
|
async function runOrDiagnose(args, options) {
|
|
31051
31079
|
try {
|
|
31052
31080
|
return toText(await run(args, options));
|
|
31053
31081
|
} catch (err) {
|
|
31054
|
-
|
|
31055
|
-
const errText = base.content[0].text;
|
|
31056
|
-
const isAuthError = AUTH_ERROR_PATTERN.test(errText);
|
|
31057
|
-
const isTransientError = !isAuthError && TRANSIENT_ERROR_PATTERN.test(errText);
|
|
31058
|
-
const hint = isAuthError ? AUTH_HINT : isTransientError ? TRANSIENT_HINT : "";
|
|
31059
|
-
try {
|
|
31060
|
-
const accounts = await run(["auth", "list"]);
|
|
31061
|
-
return toText(`${errText}
|
|
31062
|
-
|
|
31063
|
-
Configured accounts:
|
|
31064
|
-
${accounts}${hint}`);
|
|
31065
|
-
} catch {
|
|
31066
|
-
return toText(`${errText}${hint}`);
|
|
31067
|
-
}
|
|
31082
|
+
return diagnose(err);
|
|
31068
31083
|
}
|
|
31069
31084
|
}
|
|
31070
31085
|
|
|
@@ -31166,7 +31181,7 @@ function registerCalendarTools(server) {
|
|
|
31166
31181
|
return runOrDiagnose(["calendar", "event", calendarId, eventId], { account });
|
|
31167
31182
|
});
|
|
31168
31183
|
server.registerTool("gog_calendar_create", {
|
|
31169
|
-
description: "Create a calendar event.",
|
|
31184
|
+
description: "Create a calendar event. Set withZoom=true to attach a Zoom meeting (requires Zoom S2S OAuth setup via gog_zoom_auth_setup; the join URL + meeting ID + passcode are appended to the event description \u2014 Google rejects native conference card writes from non-Workspace-Marketplace OAuth clients).",
|
|
31170
31185
|
annotations: { destructiveHint: false },
|
|
31171
31186
|
inputSchema: {
|
|
31172
31187
|
calendarId: external_exports.string().describe('Calendar ID (use "primary" for the default calendar)'),
|
|
@@ -31177,18 +31192,20 @@ function registerCalendarTools(server) {
|
|
|
31177
31192
|
location: external_exports.string().optional().describe("Event location"),
|
|
31178
31193
|
attendees: external_exports.string().optional().describe("Attendee emails, comma-separated"),
|
|
31179
31194
|
allDay: external_exports.boolean().optional().describe("All-day event (use date-only in from/to)"),
|
|
31195
|
+
withZoom: external_exports.boolean().optional().describe("Create a Zoom video conference for this event (requires Zoom S2S OAuth setup)"),
|
|
31180
31196
|
account: accountParam
|
|
31181
31197
|
}
|
|
31182
|
-
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, account }) => {
|
|
31198
|
+
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, withZoom, account }) => {
|
|
31183
31199
|
const args = ["calendar", "create", calendarId, `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
31184
31200
|
if (description) args.push(`--description=${description}`);
|
|
31185
31201
|
if (location) args.push(`--location=${location}`);
|
|
31186
31202
|
if (attendees) args.push(`--attendees=${attendees}`);
|
|
31187
31203
|
if (allDay) args.push("--all-day");
|
|
31204
|
+
if (withZoom) args.push("--with-zoom");
|
|
31188
31205
|
return runOrDiagnose(args, { account });
|
|
31189
31206
|
});
|
|
31190
31207
|
server.registerTool("gog_calendar_update", {
|
|
31191
|
-
description: "Update an existing calendar event.",
|
|
31208
|
+
description: "Update an existing calendar event. Zoom: withZoom adds a Zoom meeting, regenerateZoom replaces the existing one, removeZoom strips it (each are independent \u2014 use one per call).",
|
|
31192
31209
|
annotations: { destructiveHint: false },
|
|
31193
31210
|
inputSchema: {
|
|
31194
31211
|
calendarId: external_exports.string().describe("Calendar ID"),
|
|
@@ -31199,9 +31216,12 @@ function registerCalendarTools(server) {
|
|
|
31199
31216
|
description: external_exports.string().optional().describe("New description"),
|
|
31200
31217
|
location: external_exports.string().optional().describe("New location"),
|
|
31201
31218
|
attendees: external_exports.string().optional().describe("New attendee emails, comma-separated (replaces existing)"),
|
|
31219
|
+
withZoom: external_exports.boolean().optional().describe("Create a Zoom video conference for this event"),
|
|
31220
|
+
regenerateZoom: external_exports.boolean().optional().describe("Replace the event's existing Zoom video conference"),
|
|
31221
|
+
removeZoom: external_exports.boolean().optional().describe("Remove the event's Zoom video conference"),
|
|
31202
31222
|
account: accountParam
|
|
31203
31223
|
}
|
|
31204
|
-
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, account }) => {
|
|
31224
|
+
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, withZoom, regenerateZoom, removeZoom, account }) => {
|
|
31205
31225
|
const args = ["calendar", "update", calendarId, eventId];
|
|
31206
31226
|
if (summary !== void 0) args.push(`--summary=${summary}`);
|
|
31207
31227
|
if (from !== void 0) args.push(`--from=${from}`);
|
|
@@ -31209,6 +31229,9 @@ function registerCalendarTools(server) {
|
|
|
31209
31229
|
if (description !== void 0) args.push(`--description=${description}`);
|
|
31210
31230
|
if (location !== void 0) args.push(`--location=${location}`);
|
|
31211
31231
|
if (attendees !== void 0) args.push(`--attendees=${attendees}`);
|
|
31232
|
+
if (withZoom) args.push("--with-zoom");
|
|
31233
|
+
if (regenerateZoom) args.push("--regenerate-zoom");
|
|
31234
|
+
if (removeZoom) args.push("--remove-zoom");
|
|
31212
31235
|
return runOrDiagnose(args, { account });
|
|
31213
31236
|
});
|
|
31214
31237
|
server.registerTool("gog_calendar_delete", {
|
|
@@ -31845,7 +31868,7 @@ function registerDriveTools(server) {
|
|
|
31845
31868
|
// src/tools/gmail.ts
|
|
31846
31869
|
function registerGmailTools(server) {
|
|
31847
31870
|
server.registerTool("gog_gmail_search", {
|
|
31848
|
-
description:
|
|
31871
|
+
description: `Search Gmail threads using Gmail query syntax (e.g. "from:alice subject:invoice is:unread"). The query is passed verbatim to Gmail; a bare name token (from:alison) matches per Gmail's own heuristics, a full address (from:alison@example.com) is exact. To match a contact across several addresses, OR them: from:(a@x.com OR b@y.com).`,
|
|
31849
31872
|
annotations: { readOnlyHint: true },
|
|
31850
31873
|
inputSchema: {
|
|
31851
31874
|
query: external_exports.string().describe("Gmail search query"),
|
|
@@ -31894,8 +31917,62 @@ function registerGmailTools(server) {
|
|
|
31894
31917
|
registerRunTool(server, { service: "gmail", examples: '"archive", "mark-read", "labels"' });
|
|
31895
31918
|
}
|
|
31896
31919
|
|
|
31920
|
+
// src/tools/sheets-a1.ts
|
|
31921
|
+
function colToLetter(n) {
|
|
31922
|
+
let s = "";
|
|
31923
|
+
while (n > 0) {
|
|
31924
|
+
const rem = (n - 1) % 26;
|
|
31925
|
+
s = String.fromCharCode(65 + rem) + s;
|
|
31926
|
+
n = Math.floor((n - 1) / 26);
|
|
31927
|
+
}
|
|
31928
|
+
return s;
|
|
31929
|
+
}
|
|
31930
|
+
function letterToCol(s) {
|
|
31931
|
+
let n = 0;
|
|
31932
|
+
for (const ch of s.toUpperCase()) {
|
|
31933
|
+
n = n * 26 + (ch.charCodeAt(0) - 64);
|
|
31934
|
+
}
|
|
31935
|
+
return n;
|
|
31936
|
+
}
|
|
31937
|
+
function expandAnchorRange(range, rows, cols) {
|
|
31938
|
+
const bang = range.lastIndexOf("!");
|
|
31939
|
+
const sheet = bang >= 0 ? range.slice(0, bang + 1) : "";
|
|
31940
|
+
const cell = bang >= 0 ? range.slice(bang + 1) : range;
|
|
31941
|
+
const m = /^([A-Za-z]+)([0-9]+)$/.exec(cell);
|
|
31942
|
+
if (!m) return range;
|
|
31943
|
+
const startCol = letterToCol(m[1]);
|
|
31944
|
+
const startRow = parseInt(m[2], 10);
|
|
31945
|
+
const endCol = colToLetter(startCol + cols - 1);
|
|
31946
|
+
const endRow = startRow + rows - 1;
|
|
31947
|
+
return `${sheet}${m[1].toUpperCase()}${startRow}:${endCol}${endRow}`;
|
|
31948
|
+
}
|
|
31949
|
+
function countNonEmptyCells(getOutput) {
|
|
31950
|
+
let parsed;
|
|
31951
|
+
try {
|
|
31952
|
+
parsed = JSON.parse(getOutput);
|
|
31953
|
+
} catch {
|
|
31954
|
+
return -1;
|
|
31955
|
+
}
|
|
31956
|
+
const values = parsed?.values;
|
|
31957
|
+
if (!Array.isArray(values)) return 0;
|
|
31958
|
+
let count = 0;
|
|
31959
|
+
for (const row of values) {
|
|
31960
|
+
if (!Array.isArray(row)) continue;
|
|
31961
|
+
for (const cell of row) {
|
|
31962
|
+
if (cell !== null && String(cell).trim() !== "") count++;
|
|
31963
|
+
}
|
|
31964
|
+
}
|
|
31965
|
+
return count;
|
|
31966
|
+
}
|
|
31967
|
+
|
|
31897
31968
|
// src/tools/sheets.ts
|
|
31898
31969
|
var cellValueParam = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]);
|
|
31970
|
+
var dryRunParam = external_exports.boolean().optional().describe(
|
|
31971
|
+
"Preview the operation without modifying the sheet (gog --dry-run): reports the intended actions and exits without writing."
|
|
31972
|
+
);
|
|
31973
|
+
var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
31974
|
+
'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.'
|
|
31975
|
+
);
|
|
31899
31976
|
function registerSheetsTools(server) {
|
|
31900
31977
|
server.registerTool("gog_sheets_get", {
|
|
31901
31978
|
description: 'Read values from a Google Sheets range. Returns a JSON object with a "values" array of rows.',
|
|
@@ -31915,13 +31992,31 @@ function registerSheetsTools(server) {
|
|
|
31915
31992
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID (from the URL)"),
|
|
31916
31993
|
range: external_exports.string().describe("Top-left cell or range in A1 notation, e.g. Sheet1!A1"),
|
|
31917
31994
|
values: external_exports.array(external_exports.array(cellValueParam)).describe('2D array of values (rows of columns). Cells may be string/number/boolean/null; strings starting with "=" are formulas.'),
|
|
31995
|
+
dry_run: dryRunParam,
|
|
31996
|
+
fail_if_not_empty: failIfNotEmptyParam,
|
|
31918
31997
|
account: accountParam
|
|
31919
31998
|
}
|
|
31920
|
-
}, async ({ spreadsheetId, range, values, account }) => {
|
|
31921
|
-
|
|
31922
|
-
|
|
31923
|
-
|
|
31924
|
-
|
|
31999
|
+
}, async ({ spreadsheetId, range, values, account, dry_run, fail_if_not_empty }) => {
|
|
32000
|
+
const cols = values.reduce((max, row) => Math.max(max, row.length), 0);
|
|
32001
|
+
if (fail_if_not_empty && values.length > 0 && cols > 0) {
|
|
32002
|
+
const readRange = expandAnchorRange(range, values.length, cols);
|
|
32003
|
+
let existing;
|
|
32004
|
+
try {
|
|
32005
|
+
existing = await run(["sheets", "get", spreadsheetId, readRange], { account });
|
|
32006
|
+
} catch (err) {
|
|
32007
|
+
return diagnose(err);
|
|
32008
|
+
}
|
|
32009
|
+
const occupied = countNonEmptyCells(existing);
|
|
32010
|
+
if (occupied !== 0) {
|
|
32011
|
+
const detail = occupied < 0 ? "could not be verified as empty" : `already contains data in ${occupied} cell(s)`;
|
|
32012
|
+
return toText(
|
|
32013
|
+
`Write aborted (fail_if_not_empty): target range ${readRange} ${detail}. Re-run without fail_if_not_empty to overwrite, or clear it first with gog_sheets_clear.`
|
|
32014
|
+
);
|
|
32015
|
+
}
|
|
32016
|
+
}
|
|
32017
|
+
const args = ["sheets", "update", spreadsheetId, range, `--values-json=${JSON.stringify(values)}`];
|
|
32018
|
+
if (dry_run) args.push("--dry-run");
|
|
32019
|
+
return runOrDiagnose(args, { account });
|
|
31925
32020
|
});
|
|
31926
32021
|
server.registerTool("gog_sheets_append", {
|
|
31927
32022
|
description: 'Append rows to a Google Sheet after the last row with data in the given range. Values may be strings, numbers, booleans, or null. Strings starting with "=" are interpreted as formulas.',
|
|
@@ -31930,13 +32025,13 @@ function registerSheetsTools(server) {
|
|
|
31930
32025
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID (from the URL)"),
|
|
31931
32026
|
range: external_exports.string().describe("Range indicating which sheet/columns to append to, e.g. Sheet1!A:C"),
|
|
31932
32027
|
values: external_exports.array(external_exports.array(cellValueParam)).describe('2D array of rows to append. Cells may be string/number/boolean/null; strings starting with "=" are formulas.'),
|
|
32028
|
+
dry_run: dryRunParam,
|
|
31933
32029
|
account: accountParam
|
|
31934
32030
|
}
|
|
31935
|
-
}, async ({ spreadsheetId, range, values, account }) => {
|
|
31936
|
-
|
|
31937
|
-
|
|
31938
|
-
|
|
31939
|
-
);
|
|
32031
|
+
}, async ({ spreadsheetId, range, values, account, dry_run }) => {
|
|
32032
|
+
const args = ["sheets", "append", spreadsheetId, range, `--values-json=${JSON.stringify(values)}`];
|
|
32033
|
+
if (dry_run) args.push("--dry-run");
|
|
32034
|
+
return runOrDiagnose(args, { account });
|
|
31940
32035
|
});
|
|
31941
32036
|
server.registerTool("gog_sheets_clear", {
|
|
31942
32037
|
description: "Clear all values in a Google Sheets range (formatting is preserved).",
|
|
@@ -31944,13 +32039,16 @@ function registerSheetsTools(server) {
|
|
|
31944
32039
|
inputSchema: {
|
|
31945
32040
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
31946
32041
|
range: external_exports.string().describe("Range in A1 notation to clear"),
|
|
32042
|
+
dry_run: dryRunParam,
|
|
31947
32043
|
account: accountParam
|
|
31948
32044
|
}
|
|
31949
|
-
}, async ({ spreadsheetId, range, account }) => {
|
|
31950
|
-
|
|
32045
|
+
}, async ({ spreadsheetId, range, account, dry_run }) => {
|
|
32046
|
+
const args = ["sheets", "clear", spreadsheetId, range];
|
|
32047
|
+
if (dry_run) args.push("--dry-run");
|
|
32048
|
+
return runOrDiagnose(args, { account });
|
|
31951
32049
|
});
|
|
31952
32050
|
server.registerTool("gog_sheets_metadata", {
|
|
31953
|
-
description: "Get spreadsheet metadata: title,
|
|
32051
|
+
description: "Get spreadsheet metadata: title, named ranges, and per-tab properties including grid dimensions (gridProperties.rowCount / columnCount). Use this to learn a sheet's current size before writing \u2014 a write outside the grid fails.",
|
|
31954
32052
|
annotations: { readOnlyHint: true },
|
|
31955
32053
|
inputSchema: {
|
|
31956
32054
|
spreadsheetId: external_exports.string().describe("Spreadsheet ID"),
|
|
@@ -32136,7 +32234,7 @@ function registerTasksTools(server) {
|
|
|
32136
32234
|
}
|
|
32137
32235
|
|
|
32138
32236
|
// src/server.ts
|
|
32139
|
-
var VERSION = true ? "2.0
|
|
32237
|
+
var VERSION = true ? "2.2.0" : "0.0.0";
|
|
32140
32238
|
function createServer(options) {
|
|
32141
32239
|
return new McpServer({
|
|
32142
32240
|
name: options?.name ?? "gogcli",
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifest_version": "0.3",
|
|
4
4
|
"name": "gogcli-mcp",
|
|
5
5
|
"display_name": "gogcli",
|
|
6
|
-
"version": "2.0
|
|
6
|
+
"version": "2.2.0",
|
|
7
7
|
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"mcpName": "io.github.chrischall/gogcli-mcp",
|
|
5
5
|
"description": "MCP server wrapping gogcli for Google service access",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"zod": "^4.4.3"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/node": "^25.
|
|
49
|
-
"@vitest/coverage-v8": "^4.1.
|
|
48
|
+
"@types/node": "^25.9.1",
|
|
49
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
50
50
|
"esbuild": "^0.28.0",
|
|
51
51
|
"typescript": "^6.0.2",
|
|
52
52
|
"vitest": "^4.1.6"
|