gogcli-mcp 2.27.1 → 2.28.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.
@@ -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.27.1"
10
+ "version": "2.28.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.27.1",
18
+ "version": "2.28.0",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gogcli-mcp",
3
3
  "displayName": "gogcli",
4
- "version": "2.27.1",
4
+ "version": "2.28.0",
5
5
  "description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > [!WARNING]
4
4
  > **AI-developed project.** This codebase was built and is actively maintained by [Claude Code](https://www.anthropic.com/claude). Review all code and tool permissions before use.
5
5
 
6
- Base [Model Context Protocol](https://modelcontextprotocol.io) server that gives Claude access to Google Workspace via [gogcli](https://github.com/openclaw/gogcli). Includes 114 tools across 13 services: Sheets, Docs, Gmail, Calendar, Drive, Slides, Classroom, Chat, Apps Script, Tasks, Contacts, the generic Discovery API escape hatch, and Auth.
6
+ Base [Model Context Protocol](https://modelcontextprotocol.io) server that gives Claude access to Google Workspace via [gogcli](https://github.com/openclaw/gogcli). Includes 115 tools across 13 services: Sheets, Docs, Gmail, Calendar, Drive, Slides, Classroom, Chat, Apps Script, Tasks, Contacts, the generic Discovery API escape hatch, and Auth.
7
7
 
8
8
  For extended Sheets or Docs support, see [gogcli-mcp-sheets](https://www.npmjs.com/package/gogcli-mcp-sheets) and [gogcli-mcp-docs](https://www.npmjs.com/package/gogcli-mcp-docs).
9
9
 
@@ -46,7 +46,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
46
46
  claude mcp add gogcli-mcp -- gogcli-mcp
47
47
  ```
48
48
 
49
- ## Tools (114)
49
+ ## Tools (115)
50
50
 
51
51
  | Service | Tools | Includes |
52
52
  |---------|-------|----------|
@@ -57,7 +57,7 @@ claude mcp add gogcli-mcp -- gogcli-mcp
57
57
  | **Drive** | 11 | ls, search, get, mkdir, rename, move, delete, share, extract-text, read-bytes, run |
58
58
  | **Slides** | 7 | export, info, create, copy, list-slides, read-slide, run |
59
59
  | **Classroom** | 25 | courses, students, teachers, roster, coursework, submissions (grade/return/turn-in/reclaim), announcements, topics, invitations, profile, run |
60
- | **Chat** | 12 | spaces list/find/create, threads list, messages list/send, dm send/space, reactions list/create/delete, run |
60
+ | **Chat** | 13 | spaces list/find/create, threads list, messages list/search/send, dm send/space, reactions list/create/delete, run |
61
61
  | **Apps Script** | 8 | get, content, pull, create, deployments, versions, run-function, run |
62
62
  | **Tasks** | 7 | lists, list, get, add, done, delete, run |
63
63
  | **Contacts** | 5 | search, list, get, create, run |
package/SKILL.md CHANGED
@@ -32,7 +32,7 @@ MCP server wrapping [gogcli](https://github.com/openclaw/gogcli) — provides Cl
32
32
 
33
33
  `GOG_ACCOUNT` is optional — omit it to use gogcli's configured default account.
34
34
 
35
- ## Tools (114)
35
+ ## Tools (115)
36
36
 
37
37
  | Service | Tools |
38
38
  |---------|-------|
@@ -43,7 +43,7 @@ MCP server wrapping [gogcli](https://github.com/openclaw/gogcli) — provides Cl
43
43
  | **Drive** (11) | ls, search, get, mkdir, rename, move, delete, share, extract-text, read-bytes, run |
44
44
  | **Slides** (7) | export, info, create, copy, list-slides, read-slide, run |
45
45
  | **Classroom** (25) | courses, students, teachers, roster, coursework, submissions (grade/return/turn-in/reclaim), announcements, topics, invitations, profile, run |
46
- | **Chat** (12) | spaces list/find/create, threads list, messages list/send, dm send/space, reactions list/create/delete, run |
46
+ | **Chat** (13) | spaces list/find/create, threads list, messages list/search/send, dm send/space, reactions list/create/delete, run |
47
47
  | **Apps Script** (8) | get, content, pull, create, deployments, versions, run-function, run |
48
48
  | **Tasks** (7) | lists, list, get, add, done, delete, run |
49
49
  | **Contacts** (5) | search, list, get, create, run |
package/dist/index.js CHANGED
@@ -32755,6 +32755,30 @@ function registerChatTools(server) {
32755
32755
  pushPaginationFlags(args, { max, pageToken, page, all });
32756
32756
  return runOrDiagnose(args, { account });
32757
32757
  });
32758
+ server.registerTool("gog_chat_messages_search", {
32759
+ description: 'Search Chat messages ACROSS every space and DM the account can see (gog >= 0.39.0) \u2014 the tool to reach for when the user asks "where did we discuss X" without naming a room, since gog_chat_messages_list needs a space up front. The query is Google Chat filter syntax, so it takes plain keywords or filters like sender, space, date, mention, unread, link and attachment. It is a search, NOT an export: Chat excludes some conversations (muted spaces among them), so an empty result does not prove a message never existed. view="full" adds each hit\'s read state and space mute setting; read state works on an ordinary chat grant, but the mute setting needs chat.users.spacesettings, which gog\'s chat scope set does NOT request (re-auth with extraScopes to get it). Missing metadata is OMITTED rather than defaulted, so an absent `read` means unknown while an explicit false means unread.' + workspaceOnlyNote,
32760
+ annotations: { readOnlyHint: true },
32761
+ inputSchema: {
32762
+ query: external_exports.string().describe('Google Chat filter-syntax query \u2014 keywords, or filters such as "from:alice@example.com budget"'),
32763
+ order: external_exports.enum(["create_time desc", "relevance desc"]).optional().describe(
32764
+ `Sort order. NOTE the snake_case, which differs from gog_chat_messages_list's camelCase. "relevance desc" needs Google Developer Preview access and errors without it`
32765
+ ),
32766
+ view: external_exports.enum(["basic", "full"]).optional().describe(
32767
+ 'Result view (default "basic"). "full" also requests read state (covered by an ordinary chat grant) and space mute setting (needs chat.users.spacesettings, which that grant does not include)'
32768
+ ),
32769
+ markup: external_exports.enum(["chat", "markdown"]).optional().describe("Syntax to render each hit's formatted text in"),
32770
+ ...paginationParams,
32771
+ max: external_exports.number().int().min(1).max(100).optional().describe("Max results per page (1-100; Chat search caps a page at 100)"),
32772
+ account: accountParam
32773
+ }
32774
+ }, async ({ query, order, view, markup, max, pageToken, page, all, account }) => {
32775
+ const args = ["chat", "messages", "search", query];
32776
+ if (order) args.push(`--order=${order}`);
32777
+ if (view) args.push(`--view=${view}`);
32778
+ if (markup) args.push(`--markup=${markup}`);
32779
+ pushPaginationFlags(args, { max, pageToken, page, all });
32780
+ return runOrDiagnose(args, { account });
32781
+ });
32758
32782
  server.registerTool("gog_chat_messages_send", {
32759
32783
  description: "Post a message to a Chat space. THIS IS IMMEDIATELY VISIBLE TO EVERYONE IN THE SPACE and cannot be unsent through this tool, so treat it like sending mail, not like saving a draft. Pass `thread` to reply inside an existing conversation (from gog_chat_threads_list or a message's thread field); omit it to start a new one. Text supports Chat's markdown-ish formatting (*bold*, _italic_, `code`)." + workspaceOnlyNote,
32760
32784
  inputSchema: {
@@ -34167,7 +34191,7 @@ function registerTasksTools(server) {
34167
34191
  }
34168
34192
 
34169
34193
  // src/server.ts
34170
- var VERSION = true ? "2.27.1" : "0.0.0";
34194
+ var VERSION = true ? "2.28.0" : "0.0.0";
34171
34195
  var BASE_TOOL_REGISTRARS = [
34172
34196
  registerApiTools,
34173
34197
  registerAppScriptTools,
package/dist/lib.js CHANGED
@@ -23325,7 +23325,7 @@ function activeExecutor() {
23325
23325
  return runExecutor.getStore() ?? defaultExecutor;
23326
23326
  }
23327
23327
  var TIMEOUT_MS = 3e4;
23328
- var MIN_GOG_VERSION = "0.38.3";
23328
+ var MIN_GOG_VERSION = "0.39.0";
23329
23329
  function readonlyEnvEnabled() {
23330
23330
  return readEnvVar("GOG_READONLY") !== void 0 && parseBoolEnv("GOG_READONLY", { default: true });
23331
23331
  }
@@ -24604,6 +24604,30 @@ function registerChatTools(server) {
24604
24604
  pushPaginationFlags(args, { max, pageToken, page, all });
24605
24605
  return runOrDiagnose(args, { account });
24606
24606
  });
24607
+ server.registerTool("gog_chat_messages_search", {
24608
+ description: 'Search Chat messages ACROSS every space and DM the account can see (gog >= 0.39.0) \u2014 the tool to reach for when the user asks "where did we discuss X" without naming a room, since gog_chat_messages_list needs a space up front. The query is Google Chat filter syntax, so it takes plain keywords or filters like sender, space, date, mention, unread, link and attachment. It is a search, NOT an export: Chat excludes some conversations (muted spaces among them), so an empty result does not prove a message never existed. view="full" adds each hit\'s read state and space mute setting; read state works on an ordinary chat grant, but the mute setting needs chat.users.spacesettings, which gog\'s chat scope set does NOT request (re-auth with extraScopes to get it). Missing metadata is OMITTED rather than defaulted, so an absent `read` means unknown while an explicit false means unread.' + workspaceOnlyNote,
24609
+ annotations: { readOnlyHint: true },
24610
+ inputSchema: {
24611
+ query: external_exports.string().describe('Google Chat filter-syntax query \u2014 keywords, or filters such as "from:alice@example.com budget"'),
24612
+ order: external_exports.enum(["create_time desc", "relevance desc"]).optional().describe(
24613
+ `Sort order. NOTE the snake_case, which differs from gog_chat_messages_list's camelCase. "relevance desc" needs Google Developer Preview access and errors without it`
24614
+ ),
24615
+ view: external_exports.enum(["basic", "full"]).optional().describe(
24616
+ 'Result view (default "basic"). "full" also requests read state (covered by an ordinary chat grant) and space mute setting (needs chat.users.spacesettings, which that grant does not include)'
24617
+ ),
24618
+ markup: external_exports.enum(["chat", "markdown"]).optional().describe("Syntax to render each hit's formatted text in"),
24619
+ ...paginationParams,
24620
+ max: external_exports.number().int().min(1).max(100).optional().describe("Max results per page (1-100; Chat search caps a page at 100)"),
24621
+ account: accountParam
24622
+ }
24623
+ }, async ({ query, order, view, markup, max, pageToken, page, all, account }) => {
24624
+ const args = ["chat", "messages", "search", query];
24625
+ if (order) args.push(`--order=${order}`);
24626
+ if (view) args.push(`--view=${view}`);
24627
+ if (markup) args.push(`--markup=${markup}`);
24628
+ pushPaginationFlags(args, { max, pageToken, page, all });
24629
+ return runOrDiagnose(args, { account });
24630
+ });
24607
24631
  server.registerTool("gog_chat_messages_send", {
24608
24632
  description: "Post a message to a Chat space. THIS IS IMMEDIATELY VISIBLE TO EVERYONE IN THE SPACE and cannot be unsent through this tool, so treat it like sending mail, not like saving a draft. Pass `thread` to reply inside an existing conversation (from gog_chat_threads_list or a message's thread field); omit it to start a new one. Text supports Chat's markdown-ish formatting (*bold*, _italic_, `code`)." + workspaceOnlyNote,
24609
24633
  inputSchema: {
@@ -26016,7 +26040,7 @@ function registerTasksTools(server) {
26016
26040
  }
26017
26041
 
26018
26042
  // src/server.ts
26019
- var VERSION = true ? "2.27.1" : "0.0.0";
26043
+ var VERSION = true ? "2.28.0" : "0.0.0";
26020
26044
  var BASE_TOOL_REGISTRARS = [
26021
26045
  registerApiTools,
26022
26046
  registerAppScriptTools,
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.27.1",
6
+ "version": "2.28.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",
@@ -452,6 +452,10 @@
452
452
  "name": "gog_chat_messages_list",
453
453
  "description": "Read messages in a space, with mentions and reaction summaries"
454
454
  },
455
+ {
456
+ "name": "gog_chat_messages_search",
457
+ "description": "Search Chat messages across every space and DM the account can see"
458
+ },
455
459
  {
456
460
  "name": "gog_chat_messages_send",
457
461
  "description": "Post a message to a Chat space (immediately visible; supports attachments)"
package/mint.yaml CHANGED
@@ -86,7 +86,7 @@ dependencies:
86
86
  # nine pins too.
87
87
  - kind: github-release
88
88
  repo: openclaw/gogcli
89
- tag: v0.38.3
89
+ tag: v0.39.0
90
90
  asset: "gogcli_*_linux_amd64.tar.gz"
91
91
  bin: [gog]
92
92
  state:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gogcli-mcp",
3
- "version": "2.27.1",
3
+ "version": "2.28.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>",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "source": "github",
8
8
  "subfolder": "packages/gogcli-mcp"
9
9
  },
10
- "version": "2.27.1",
10
+ "version": "2.28.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "gogcli-mcp",
15
- "version": "2.27.1",
15
+ "version": "2.28.0",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },
package/src/runner.ts CHANGED
@@ -217,7 +217,7 @@ const TIMEOUT_MS = 30_000;
217
217
  // so the requirement change is surfaced in the release notes (see
218
218
  // .github/release.yml). This is the single source of truth for the required
219
219
  // version; keep the README/CLAUDE.md mention in sync.
220
- export const MIN_GOG_VERSION = '0.38.3';
220
+ export const MIN_GOG_VERSION = '0.39.0';
221
221
 
222
222
  // Interpret the GOG_READONLY kill-switch. `readEnvVar` already treats blank
223
223
  // values, 'undefined'/'null' sentinels, and unresolved .mcpb placeholders
package/src/tools/chat.ts CHANGED
@@ -11,7 +11,7 @@ import type { GogArg } from '../runner.js';
11
11
  import { attachInlineParam, inlineAttachmentArgs } from '../attachments.js';
12
12
 
13
13
  // Google Chat (gog >= 0.38.0 for the mention/reaction metadata in
14
- // `messages list`; the rest of the surface is older).
14
+ // `messages list`, >= 0.39.0 for `messages search`; the rest is older).
15
15
  //
16
16
  // TWO NAMING SYSTEMS MEET HERE, and mixing them is the mistake this module's
17
17
  // descriptions exist to prevent. Chat identifies everything by RESOURCE NAME —
@@ -135,6 +135,41 @@ export function registerChatTools(server: McpServer): void {
135
135
  return runOrDiagnose(args, { account });
136
136
  });
137
137
 
138
+ server.registerTool('gog_chat_messages_search', {
139
+ description:
140
+ 'Search Chat messages ACROSS every space and DM the account can see (gog >= 0.39.0) — the tool to reach for when the '
141
+ + 'user asks "where did we discuss X" without naming a room, since gog_chat_messages_list needs a space up front. '
142
+ + 'The query is Google Chat filter syntax, so it takes plain keywords or filters like sender, space, date, mention, '
143
+ + 'unread, link and attachment. It is a search, NOT an export: Chat excludes some conversations (muted spaces among '
144
+ + 'them), so an empty result does not prove a message never existed. view="full" adds each hit\'s read state and space '
145
+ + 'mute setting; read state works on an ordinary chat grant, but the mute setting needs chat.users.spacesettings, '
146
+ + 'which gog\'s chat scope set does NOT request (re-auth with extraScopes to get it). Missing metadata is OMITTED '
147
+ + 'rather than defaulted, so an absent `read` means unknown while an explicit false means unread.' + workspaceOnlyNote,
148
+ annotations: { readOnlyHint: true },
149
+ inputSchema: {
150
+ query: z.string().describe('Google Chat filter-syntax query — keywords, or filters such as "from:alice@example.com budget"'),
151
+ order: z.enum(['create_time desc', 'relevance desc']).optional().describe(
152
+ 'Sort order. NOTE the snake_case, which differs from gog_chat_messages_list\'s camelCase. "relevance desc" needs '
153
+ + 'Google Developer Preview access and errors without it',
154
+ ),
155
+ view: z.enum(['basic', 'full']).optional().describe(
156
+ 'Result view (default "basic"). "full" also requests read state (covered by an ordinary chat grant) and space '
157
+ + 'mute setting (needs chat.users.spacesettings, which that grant does not include)',
158
+ ),
159
+ markup: z.enum(['chat', 'markdown']).optional().describe('Syntax to render each hit\'s formatted text in'),
160
+ ...paginationParams,
161
+ max: z.number().int().min(1).max(100).optional().describe('Max results per page (1-100; Chat search caps a page at 100)'),
162
+ account: accountParam,
163
+ },
164
+ }, async ({ query, order, view, markup, max, pageToken, page, all, account }) => {
165
+ const args = ['chat', 'messages', 'search', query];
166
+ if (order) args.push(`--order=${order}`);
167
+ if (view) args.push(`--view=${view}`);
168
+ if (markup) args.push(`--markup=${markup}`);
169
+ pushPaginationFlags(args, { max, pageToken, page, all });
170
+ return runOrDiagnose(args, { account });
171
+ });
172
+
138
173
  server.registerTool('gog_chat_messages_send', {
139
174
  description:
140
175
  'Post a message to a Chat space. THIS IS IMMEDIATELY VISIBLE TO EVERYONE IN THE SPACE and cannot be unsent through '
package/src/worker.ts CHANGED
@@ -38,7 +38,7 @@ import { gogAuth, CONNECTOR_INSTRUCTIONS, type GogProps } from './connector-auth
38
38
  // connector with all ~360 tools at once. Add whichever paths you want as separate
39
39
  // connectors in claude.ai (each authorizes with the same connector key).
40
40
 
41
- const VERSION = '2.27.1'; // x-release-please-version
41
+ const VERSION = '2.28.0'; // x-release-please-version
42
42
 
43
43
  // Build an McpAgent subclass whose init() registers `registrars` onto its server,
44
44
  // each handler wrapped in the ALS scope carrying the per-session Fly executor.
@@ -46,7 +46,7 @@ describe('gog_chat_spaces_list', () => {
46
46
  return undefined as never;
47
47
  });
48
48
  registerChatTools(server);
49
- expect(configs.size).toBe(12);
49
+ expect(configs.size).toBe(13);
50
50
  for (const [name, config] of configs) {
51
51
  expect(config.description, name).toMatch(/consumer accounts|Workspace/i);
52
52
  }
@@ -132,6 +132,66 @@ describe('gog_chat_messages_list', () => {
132
132
  });
133
133
  });
134
134
 
135
+ describe('gog_chat_messages_search', () => {
136
+ it('searches across every space the account can see', async () => {
137
+ vi.mocked(runner.run).mockResolvedValue('{"results":[]}');
138
+ const harness = await setupHandlers();
139
+ await harness.callTool('gog_chat_messages_search', { query: 'project decision' });
140
+ expect(runner.run).toHaveBeenCalledWith(
141
+ ['chat', 'messages', 'search', 'project decision'],
142
+ { account: undefined },
143
+ );
144
+ });
145
+
146
+ it('passes view, markup, order and pagination flags', async () => {
147
+ vi.mocked(runner.run).mockResolvedValue('{"results":[]}');
148
+ const harness = await setupHandlers();
149
+ await harness.callTool('gog_chat_messages_search', {
150
+ query: 'from:alice@example.com budget',
151
+ view: 'full',
152
+ markup: 'markdown',
153
+ order: 'create_time desc',
154
+ max: 100,
155
+ pageToken: 'tok',
156
+ all: true,
157
+ });
158
+ expect(runner.run).toHaveBeenCalledWith(
159
+ ['chat', 'messages', 'search', 'from:alice@example.com budget', '--order=create_time desc',
160
+ '--view=full', '--markup=markdown', '--max=100', '--page=tok', '--all'],
161
+ { account: undefined },
162
+ );
163
+ });
164
+
165
+ // Chat's own ordering vocabulary is snake_case here and camelCase in
166
+ // `messages list`; a model that copies the list tool's "createTime desc"
167
+ // would get a gog error at runtime, so the enum has to reject it here.
168
+ it('rejects an order value search does not accept', async () => {
169
+ const harness = await setupHandlers();
170
+ const result = await harness.callTool('gog_chat_messages_search', {
171
+ query: 'x', order: 'createTime desc',
172
+ });
173
+ expect(result.isError).toBe(true);
174
+ expect(runner.run).not.toHaveBeenCalled();
175
+ });
176
+
177
+ // Chat caps a search page at 100; `messages list` allows more. Letting the
178
+ // larger number through would surface as a gog error on a call the tool
179
+ // already had enough information to refuse.
180
+ it('rejects a page size above Chat search\'s cap of 100', async () => {
181
+ const harness = await setupHandlers();
182
+ const result = await harness.callTool('gog_chat_messages_search', { query: 'x', max: 250 });
183
+ expect(result.isError).toBe(true);
184
+ expect(runner.run).not.toHaveBeenCalled();
185
+ });
186
+
187
+ it('returns error text on failure', async () => {
188
+ vi.mocked(runner.run).mockRejectedValue(new Error('Search failed'));
189
+ const harness = await setupHandlers();
190
+ const result = await harness.callTool('gog_chat_messages_search', { query: 'x' });
191
+ expect(result.content[0].text).toBe('Error: Search failed');
192
+ });
193
+ });
194
+
135
195
  describe('gog_chat_messages_send', () => {
136
196
  it('sends text to a space', async () => {
137
197
  vi.mocked(runner.run).mockResolvedValue('{}');