gogcli-mcp 1.0.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/settings.local.json +35 -0
- package/.github/workflows/ci.yml +22 -0
- package/.github/workflows/release.yml +76 -0
- package/.github/workflows/tag-and-bump.yml +67 -0
- package/.mcp.json +12 -0
- package/CLAUDE.md +61 -0
- package/README.md +165 -0
- package/SKILL.md +69 -0
- package/dist/index.js +30298 -0
- package/docs/superpowers/plans/2026-04-12-gogcli-mcp.md +758 -0
- package/docs/superpowers/specs/2026-04-12-gogcli-mcp-design.md +102 -0
- package/manifest.json +95 -0
- package/package.json +28 -0
- package/skills/gogcli/SKILL.md +69 -0
- package/src/index.ts +14 -0
- package/src/runner.ts +61 -0
- package/src/tools/sheets.ts +151 -0
- package/tests/runner.test.ts +220 -0
- package/tests/tools/sheets.test.ts +183 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# gogcli-mcp Design Spec
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-04-12
|
|
4
|
+
**Status:** Approved
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
An MCP server that wraps the `gog` CLI (gogcli), exposing Google service operations as typed MCP tools. Sheets is implemented first; the architecture is scaffolded for easy addition of other services (Gmail, Calendar, Drive, etc.).
|
|
9
|
+
|
|
10
|
+
## Architecture
|
|
11
|
+
|
|
12
|
+
### Project Structure
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
gogcli-mcp/
|
|
16
|
+
├── src/
|
|
17
|
+
│ ├── index.ts — MCP server entry point; registers all service tools
|
|
18
|
+
│ ├── runner.ts — Core executor: spawns gog, handles auth & errors
|
|
19
|
+
│ └── tools/
|
|
20
|
+
│ └── sheets.ts — Sheets curated tools + escape hatch
|
|
21
|
+
├── package.json
|
|
22
|
+
├── tsconfig.json
|
|
23
|
+
└── .mcp.json — MCP server config for Claude Code
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Key Principle
|
|
27
|
+
|
|
28
|
+
No service tool file touches `child_process` directly. All execution goes through `runner.ts`. Adding a new service means creating `src/tools/<service>.ts` and registering it in `index.ts`.
|
|
29
|
+
|
|
30
|
+
## Components
|
|
31
|
+
|
|
32
|
+
### `runner.ts`
|
|
33
|
+
|
|
34
|
+
Exports a single `run(service, subcommand, args, options)` function:
|
|
35
|
+
|
|
36
|
+
- Prepends global flags: `--json`, `--no-input`, `--color=never`
|
|
37
|
+
- Injects `--account <email>` from `options.account` → `GOG_ACCOUNT` env var → omit
|
|
38
|
+
- Spawns `gog` as a child process, capturing stdout and stderr separately
|
|
39
|
+
- On exit code 0: returns parsed JSON (or raw stdout if not valid JSON)
|
|
40
|
+
- On non-zero exit: throws an error with stderr content as the message (gogcli stderr is human-readable)
|
|
41
|
+
|
|
42
|
+
### `src/tools/sheets.ts`
|
|
43
|
+
|
|
44
|
+
Exports `registerSheetsTools(server: McpServer)`. Implements:
|
|
45
|
+
|
|
46
|
+
| Tool | gog command | Notes |
|
|
47
|
+
|------|-------------|-------|
|
|
48
|
+
| `gog_sheets_get` | `gog sheets get <id> <range>` | Read-only hint |
|
|
49
|
+
| `gog_sheets_update` | `gog sheets update <id> <range> <values>` | Values as JSON array of arrays |
|
|
50
|
+
| `gog_sheets_append` | `gog sheets append <id> <range> <values>` | Values as JSON array of arrays |
|
|
51
|
+
| `gog_sheets_clear` | `gog sheets clear <id> <range>` | Destructive hint |
|
|
52
|
+
| `gog_sheets_metadata` | `gog sheets metadata <id>` | Returns tabs, named ranges, etc. |
|
|
53
|
+
| `gog_sheets_create` | `gog sheets create <title>` | Returns new spreadsheet ID |
|
|
54
|
+
| `gog_sheets_find_replace` | `gog sheets find-replace <id> <find> <replace>` | |
|
|
55
|
+
| `gog_sheets_run` | `gog sheets <subcommand> [args...]` | Escape hatch for any other sheets op |
|
|
56
|
+
|
|
57
|
+
All tools accept an optional `account` string parameter to override the default account.
|
|
58
|
+
|
|
59
|
+
### `src/index.ts`
|
|
60
|
+
|
|
61
|
+
Creates the `McpServer`, calls `registerSheetsTools(server)` (and future service registrations), connects via `StdioServerTransport`.
|
|
62
|
+
|
|
63
|
+
### `.mcp.json`
|
|
64
|
+
|
|
65
|
+
Documents `GOG_ACCOUNT` as the environment variable for the default Google account. Used by Claude Code to configure the server.
|
|
66
|
+
|
|
67
|
+
## Auth Flow
|
|
68
|
+
|
|
69
|
+
Account resolution order (most specific wins):
|
|
70
|
+
|
|
71
|
+
1. Tool-level `account` parameter (per-call override)
|
|
72
|
+
2. `GOG_ACCOUNT` environment variable (session default)
|
|
73
|
+
3. No `--account` flag (gogcli uses its own configured default)
|
|
74
|
+
|
|
75
|
+
## Error Handling
|
|
76
|
+
|
|
77
|
+
- Runner captures stdout and stderr as separate streams
|
|
78
|
+
- Non-zero exit: throw `Error(stderr)` — gogcli error messages are already user-readable
|
|
79
|
+
- The MCP tool catches the error and returns it as text content so the model sees it
|
|
80
|
+
|
|
81
|
+
## Testing
|
|
82
|
+
|
|
83
|
+
- **Unit tests** (vitest): mock `child_process.spawn` to assert correct argument construction for each tool without executing gogcli
|
|
84
|
+
- **No integration tests**: real-world validation is done by running the server against live gogcli
|
|
85
|
+
|
|
86
|
+
## Adding Future Services
|
|
87
|
+
|
|
88
|
+
To add a new service (e.g., Gmail):
|
|
89
|
+
|
|
90
|
+
1. Create `src/tools/gmail.ts` with `registerGmailTools(server)`
|
|
91
|
+
2. Implement curated tools for common operations + a `gog_gmail_run` escape hatch
|
|
92
|
+
3. Import and call `registerGmailTools(server)` in `src/index.ts`
|
|
93
|
+
4. No changes to `runner.ts` required
|
|
94
|
+
|
|
95
|
+
## Stack
|
|
96
|
+
|
|
97
|
+
- **Language:** TypeScript (ES2022, NodeNext modules)
|
|
98
|
+
- **MCP SDK:** `@modelcontextprotocol/sdk`
|
|
99
|
+
- **Schema validation:** Zod
|
|
100
|
+
- **Build:** esbuild (single bundled output)
|
|
101
|
+
- **Tests:** vitest
|
|
102
|
+
- **Pattern:** Mirrors ofw-mcp structure
|
package/manifest.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/anthropics/dxt/main/dist/mcpb-manifest.schema.json",
|
|
3
|
+
"manifest_version": "0.3",
|
|
4
|
+
"name": "gogcli-mcp",
|
|
5
|
+
"display_name": "gogcli",
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Chris Hall",
|
|
10
|
+
"email": "chris.c.hall@gmail.com",
|
|
11
|
+
"url": "https://github.com/chrischall"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/chrischall/gogcli-mcp"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/chrischall/gogcli-mcp",
|
|
18
|
+
"support": "https://github.com/chrischall/gogcli-mcp/issues",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"google",
|
|
22
|
+
"sheets",
|
|
23
|
+
"spreadsheet",
|
|
24
|
+
"gmail",
|
|
25
|
+
"calendar",
|
|
26
|
+
"drive",
|
|
27
|
+
"gogcli",
|
|
28
|
+
"gog"
|
|
29
|
+
],
|
|
30
|
+
"server": {
|
|
31
|
+
"type": "node",
|
|
32
|
+
"entry_point": "dist/index.js",
|
|
33
|
+
"mcp_config": {
|
|
34
|
+
"command": "node",
|
|
35
|
+
"args": [
|
|
36
|
+
"${__dirname}/dist/index.js"
|
|
37
|
+
],
|
|
38
|
+
"env": {
|
|
39
|
+
"GOG_ACCOUNT": "${user_config.gog_account}"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"user_config": {
|
|
44
|
+
"gog_account": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"title": "Google Account",
|
|
47
|
+
"description": "Default Google account email (optional — uses gogcli's configured default if omitted)",
|
|
48
|
+
"required": false
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"tools": [
|
|
52
|
+
{
|
|
53
|
+
"name": "gog_sheets_get",
|
|
54
|
+
"description": "Read values from a Google Sheets range"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "gog_sheets_update",
|
|
58
|
+
"description": "Write values to a Google Sheets range"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "gog_sheets_append",
|
|
62
|
+
"description": "Append rows to a Google Sheet"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "gog_sheets_clear",
|
|
66
|
+
"description": "Clear values in a Google Sheets range"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "gog_sheets_metadata",
|
|
70
|
+
"description": "Get spreadsheet metadata (title, tabs, named ranges)"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "gog_sheets_create",
|
|
74
|
+
"description": "Create a new Google Spreadsheet"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "gog_sheets_find_replace",
|
|
78
|
+
"description": "Find and replace text across a spreadsheet"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "gog_sheets_run",
|
|
82
|
+
"description": "Run any gog sheets subcommand (escape hatch)"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"compatibility": {
|
|
86
|
+
"platforms": [
|
|
87
|
+
"darwin",
|
|
88
|
+
"linux",
|
|
89
|
+
"win32"
|
|
90
|
+
],
|
|
91
|
+
"runtimes": {
|
|
92
|
+
"node": ">=18.0.0"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gogcli-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server wrapping gogcli for Google service access",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gogcli-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc --noEmit && npm run bundle",
|
|
11
|
+
"bundle": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js",
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"test:coverage": "vitest run --coverage"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
19
|
+
"zod": "^4.3.6"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^25.5.2",
|
|
23
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
24
|
+
"esbuild": "^0.28.0",
|
|
25
|
+
"typescript": "^6.0.2",
|
|
26
|
+
"vitest": "^4.1.2"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gogcli-mcp
|
|
3
|
+
description: Use when the user asks to read, write, or manage Google Sheets. Also triggers for requests involving Google Sheets data like "read my spreadsheet", "update a cell", "append rows", "create a spreadsheet", or "find and replace in Sheets". Broader Google service support (Gmail, Calendar, Drive) can be added via future service modules.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gogcli-mcp
|
|
7
|
+
|
|
8
|
+
MCP server wrapping [gogcli](https://github.com/steipete/gogcli) — provides Claude with access to Google Sheets (and a scaffold for Gmail, Calendar, Drive, and more).
|
|
9
|
+
|
|
10
|
+
- **Source:** [github.com/chrischall/gogcli-mcp](https://github.com/chrischall/gogcli-mcp)
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- [gogcli](https://github.com/steipete/gogcli) installed and authenticated (`gog --help` works in your shell)
|
|
15
|
+
- Node.js 18 or later
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
### Option A — Claude Code (direct MCP)
|
|
20
|
+
|
|
21
|
+
Add to `.mcp.json` in your project:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"gogcli": {
|
|
27
|
+
"command": "node",
|
|
28
|
+
"args": ["/path/to/gogcli-mcp/dist/index.js"],
|
|
29
|
+
"cwd": "/path/to/gogcli-mcp",
|
|
30
|
+
"env": {
|
|
31
|
+
"GOG_ACCOUNT": "you@gmail.com"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Option B — npx
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"gogcli": {
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": ["-y", "gogcli-mcp"],
|
|
46
|
+
"env": {
|
|
47
|
+
"GOG_ACCOUNT": "you@gmail.com"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`GOG_ACCOUNT` is optional — omit it to use gogcli's configured default account. Pass it per-tool-call to target a specific account dynamically.
|
|
55
|
+
|
|
56
|
+
## Available Tools
|
|
57
|
+
|
|
58
|
+
| Tool | What it does |
|
|
59
|
+
|------|-------------|
|
|
60
|
+
| `gog_sheets_get` | Read values from a range |
|
|
61
|
+
| `gog_sheets_update` | Write values to a range |
|
|
62
|
+
| `gog_sheets_append` | Append rows after existing data |
|
|
63
|
+
| `gog_sheets_clear` | Clear values in a range |
|
|
64
|
+
| `gog_sheets_metadata` | Get title, tabs, named ranges |
|
|
65
|
+
| `gog_sheets_create` | Create a new spreadsheet |
|
|
66
|
+
| `gog_sheets_find_replace` | Find and replace across a spreadsheet |
|
|
67
|
+
| `gog_sheets_run` | Run any `gog sheets` subcommand (escape hatch) |
|
|
68
|
+
|
|
69
|
+
All tools accept an optional `account` parameter to override the default Google account for that call.
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { registerSheetsTools } from './tools/sheets.js';
|
|
5
|
+
|
|
6
|
+
const server = new McpServer({ name: 'gogcli', version: '1.0.0' });
|
|
7
|
+
|
|
8
|
+
registerSheetsTools(server);
|
|
9
|
+
|
|
10
|
+
// To add more services: import registerXxxTools and call them here.
|
|
11
|
+
// Example: registerGmailTools(server);
|
|
12
|
+
|
|
13
|
+
const transport = new StdioServerTransport();
|
|
14
|
+
await server.connect(transport);
|
package/src/runner.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import type { ChildProcess } from 'node:child_process';
|
|
3
|
+
|
|
4
|
+
export type Spawner = (
|
|
5
|
+
command: string,
|
|
6
|
+
args: string[],
|
|
7
|
+
options: { env: NodeJS.ProcessEnv },
|
|
8
|
+
) => ChildProcess;
|
|
9
|
+
|
|
10
|
+
export interface RunOptions {
|
|
11
|
+
account?: string;
|
|
12
|
+
spawner?: Spawner;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const TIMEOUT_MS = 30_000;
|
|
16
|
+
|
|
17
|
+
export async function run(args: string[], options: RunOptions = {}): Promise<string> {
|
|
18
|
+
const { account, spawner = spawn as unknown as Spawner } = options;
|
|
19
|
+
|
|
20
|
+
const effectiveAccount = account ?? process.env.GOG_ACCOUNT;
|
|
21
|
+
|
|
22
|
+
const fullArgs = ['--json', '--no-input', '--color=never'];
|
|
23
|
+
if (effectiveAccount) {
|
|
24
|
+
fullArgs.push('--account', effectiveAccount);
|
|
25
|
+
}
|
|
26
|
+
fullArgs.push(...args);
|
|
27
|
+
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const child = spawner('gog', fullArgs, { env: process.env });
|
|
30
|
+
let stdout = '';
|
|
31
|
+
let stderr = '';
|
|
32
|
+
let settled = false;
|
|
33
|
+
|
|
34
|
+
const timer = setTimeout(() => {
|
|
35
|
+
settled = true;
|
|
36
|
+
child.kill();
|
|
37
|
+
reject(new Error(`gog timed out after ${TIMEOUT_MS}ms`));
|
|
38
|
+
}, TIMEOUT_MS);
|
|
39
|
+
|
|
40
|
+
child.stdout!.on('data', (chunk: Buffer) => { stdout += chunk.toString(); });
|
|
41
|
+
child.stderr!.on('data', (chunk: Buffer) => { stderr += chunk.toString(); });
|
|
42
|
+
|
|
43
|
+
child.on('close', (code: number | null) => {
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
if (settled) return;
|
|
46
|
+
settled = true;
|
|
47
|
+
if (code === 0) {
|
|
48
|
+
resolve(stdout);
|
|
49
|
+
} else {
|
|
50
|
+
reject(new Error(stderr.trim() || `gog exited with code ${code}`));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
child.on('error', (err: Error) => {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
if (settled) return;
|
|
57
|
+
settled = true;
|
|
58
|
+
reject(err);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { run } from '../runner.js';
|
|
4
|
+
|
|
5
|
+
const accountParam = z.string().optional().describe(
|
|
6
|
+
'Google account email to use (overrides GOG_ACCOUNT env var)',
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
function toText(output: string): { content: [{ type: 'text'; text: string }] } {
|
|
10
|
+
return { content: [{ type: 'text' as const, text: output }] };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function toError(err: unknown): { content: [{ type: 'text'; text: string }] } {
|
|
14
|
+
return toText(err instanceof Error ? `Error: ${err.message}` : String(err));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function registerSheetsTools(server: McpServer): void {
|
|
18
|
+
server.registerTool('gog_sheets_get', {
|
|
19
|
+
description: 'Read values from a Google Sheets range. Returns a JSON object with a "values" array of rows.',
|
|
20
|
+
annotations: { readOnlyHint: true },
|
|
21
|
+
inputSchema: {
|
|
22
|
+
spreadsheetId: z.string().describe('Spreadsheet ID (from the URL)'),
|
|
23
|
+
range: z.string().describe('Range in A1 notation, e.g. Sheet1!A1:B10 or a named range'),
|
|
24
|
+
account: accountParam,
|
|
25
|
+
},
|
|
26
|
+
}, async ({ spreadsheetId, range, account }) => {
|
|
27
|
+
try {
|
|
28
|
+
return toText(await run(['sheets', 'get', spreadsheetId, range], { account }));
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return toError(err);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
server.registerTool('gog_sheets_update', {
|
|
35
|
+
description: 'Write values to a Google Sheets range, overwriting existing content.',
|
|
36
|
+
annotations: { destructiveHint: true },
|
|
37
|
+
inputSchema: {
|
|
38
|
+
spreadsheetId: z.string().describe('Spreadsheet ID (from the URL)'),
|
|
39
|
+
range: z.string().describe('Top-left cell or range in A1 notation, e.g. Sheet1!A1'),
|
|
40
|
+
values: z.array(z.array(z.string())).describe('2D array of values: outer array is rows, inner is columns'),
|
|
41
|
+
account: accountParam,
|
|
42
|
+
},
|
|
43
|
+
}, async ({ spreadsheetId, range, values, account }) => {
|
|
44
|
+
try {
|
|
45
|
+
return toText(await run(
|
|
46
|
+
['sheets', 'update', spreadsheetId, range, `--values-json=${JSON.stringify(values)}`],
|
|
47
|
+
{ account },
|
|
48
|
+
));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return toError(err);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
server.registerTool('gog_sheets_append', {
|
|
55
|
+
description: 'Append rows to a Google Sheet after the last row with data in the given range.',
|
|
56
|
+
annotations: { destructiveHint: true },
|
|
57
|
+
inputSchema: {
|
|
58
|
+
spreadsheetId: z.string().describe('Spreadsheet ID (from the URL)'),
|
|
59
|
+
range: z.string().describe('Range indicating which sheet/columns to append to, e.g. Sheet1!A:C'),
|
|
60
|
+
values: z.array(z.array(z.string())).describe('2D array of rows to append'),
|
|
61
|
+
account: accountParam,
|
|
62
|
+
},
|
|
63
|
+
}, async ({ spreadsheetId, range, values, account }) => {
|
|
64
|
+
try {
|
|
65
|
+
return toText(await run(
|
|
66
|
+
['sheets', 'append', spreadsheetId, range, `--values-json=${JSON.stringify(values)}`],
|
|
67
|
+
{ account },
|
|
68
|
+
));
|
|
69
|
+
} catch (err) {
|
|
70
|
+
return toError(err);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
server.registerTool('gog_sheets_clear', {
|
|
75
|
+
description: 'Clear all values in a Google Sheets range (formatting is preserved).',
|
|
76
|
+
annotations: { destructiveHint: true },
|
|
77
|
+
inputSchema: {
|
|
78
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
79
|
+
range: z.string().describe('Range in A1 notation to clear'),
|
|
80
|
+
account: accountParam,
|
|
81
|
+
},
|
|
82
|
+
}, async ({ spreadsheetId, range, account }) => {
|
|
83
|
+
try {
|
|
84
|
+
return toText(await run(['sheets', 'clear', spreadsheetId, range], { account }));
|
|
85
|
+
} catch (err) {
|
|
86
|
+
return toError(err);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
server.registerTool('gog_sheets_metadata', {
|
|
91
|
+
description: 'Get spreadsheet metadata: title, sheet tabs, named ranges, and other properties.',
|
|
92
|
+
annotations: { readOnlyHint: true },
|
|
93
|
+
inputSchema: {
|
|
94
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
95
|
+
account: accountParam,
|
|
96
|
+
},
|
|
97
|
+
}, async ({ spreadsheetId, account }) => {
|
|
98
|
+
try {
|
|
99
|
+
return toText(await run(['sheets', 'metadata', spreadsheetId], { account }));
|
|
100
|
+
} catch (err) {
|
|
101
|
+
return toError(err);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
server.registerTool('gog_sheets_create', {
|
|
106
|
+
description: 'Create a new Google Spreadsheet. Returns JSON with the new spreadsheetId and URL.',
|
|
107
|
+
inputSchema: {
|
|
108
|
+
title: z.string().describe('Title for the new spreadsheet'),
|
|
109
|
+
account: accountParam,
|
|
110
|
+
},
|
|
111
|
+
}, async ({ title, account }) => {
|
|
112
|
+
try {
|
|
113
|
+
return toText(await run(['sheets', 'create', title], { account }));
|
|
114
|
+
} catch (err) {
|
|
115
|
+
return toError(err);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
server.registerTool('gog_sheets_find_replace', {
|
|
120
|
+
description: 'Find and replace text across an entire Google Spreadsheet.',
|
|
121
|
+
annotations: { destructiveHint: true },
|
|
122
|
+
inputSchema: {
|
|
123
|
+
spreadsheetId: z.string().describe('Spreadsheet ID'),
|
|
124
|
+
find: z.string().describe('Text to find'),
|
|
125
|
+
replace: z.string().describe('Replacement text'),
|
|
126
|
+
account: accountParam,
|
|
127
|
+
},
|
|
128
|
+
}, async ({ spreadsheetId, find, replace, account }) => {
|
|
129
|
+
try {
|
|
130
|
+
return toText(await run(['sheets', 'find-replace', spreadsheetId, find, replace], { account }));
|
|
131
|
+
} catch (err) {
|
|
132
|
+
return toError(err);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
server.registerTool('gog_sheets_run', {
|
|
137
|
+
description: 'Run any gog sheets subcommand not covered by the other tools. See `gog sheets --help` for the full list of subcommands and their flags.',
|
|
138
|
+
annotations: { destructiveHint: true },
|
|
139
|
+
inputSchema: {
|
|
140
|
+
subcommand: z.string().describe('The gog sheets subcommand to run, e.g. "freeze", "add-tab", "rename-tab"'),
|
|
141
|
+
args: z.array(z.string()).describe('Additional positional args and flags, e.g. ["<spreadsheetId>", "--rows=1"]'),
|
|
142
|
+
account: accountParam,
|
|
143
|
+
},
|
|
144
|
+
}, async ({ subcommand, args, account }) => {
|
|
145
|
+
try {
|
|
146
|
+
return toText(await run(['sheets', subcommand, ...args], { account }));
|
|
147
|
+
} catch (err) {
|
|
148
|
+
return toError(err);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|