google-tools-mcp 1.0.9 → 1.0.11
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/README.md +21 -8
- package/dist/index.js +1 -1
- package/dist/tools/calendar/manageEvent.js +8 -0
- package/dist/tools/calendar/moveEvent.js +6 -0
- package/dist/tools/docs/readGoogleDoc.js +3 -3
- package/dist/tools/drive/getFilePath.js +50 -0
- package/dist/tools/drive/index.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# google-tools-mcp
|
|
2
2
|
|
|
3
|
-
A unified MCP server for Google Workspace — Drive, Docs, Sheets, Gmail, Calendar, and Forms — with **
|
|
3
|
+
A unified MCP server for Google Workspace — Drive, Docs, Sheets, Gmail, Calendar, and Forms — with **153 tools** across 9 categories.
|
|
4
4
|
|
|
5
5
|
All tools are loaded at startup so they're immediately available to your AI agent. No discovery step needed.
|
|
6
6
|
|
|
@@ -10,10 +10,10 @@ Most Google MCP servers split functionality across separate packages. This serve
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
- **
|
|
13
|
+
- **153 tools** across 9 categories, all available immediately
|
|
14
14
|
- **Single auth token** — one OAuth flow covers Drive, Docs, Sheets, Gmail, Calendar, and Forms
|
|
15
15
|
- **Lazy-loading auth** — no browser popup until your first tool call
|
|
16
|
-
- **No lazy tool loading** — all
|
|
16
|
+
- **No lazy tool loading** — all 153 tools are registered eagerly at startup since most MCP clients (including Claude Code) don't support `notifications/tools/list_changed`
|
|
17
17
|
- **Multi-profile support** — separate tokens per Google account
|
|
18
18
|
- **No telemetry**
|
|
19
19
|
|
|
@@ -85,15 +85,28 @@ Add the credentials directly to your MCP configuration:
|
|
|
85
85
|
|
|
86
86
|
#### Claude Code (recommended)
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
**User-scope** (available in all projects):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
claude mcp add -s user google -- npx -y google-tools-mcp
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Project-scope** (available only in the current project):
|
|
89
95
|
|
|
90
96
|
```bash
|
|
91
97
|
claude mcp add google -- npx -y google-tools-mcp
|
|
92
98
|
```
|
|
93
99
|
|
|
94
|
-
|
|
100
|
+
With env vars (Option C):
|
|
95
101
|
|
|
96
102
|
```bash
|
|
103
|
+
# User-scope
|
|
104
|
+
claude mcp add -s user google \
|
|
105
|
+
-e GOOGLE_CLIENT_ID=your-client-id \
|
|
106
|
+
-e GOOGLE_CLIENT_SECRET=your-client-secret \
|
|
107
|
+
-- npx -y google-tools-mcp
|
|
108
|
+
|
|
109
|
+
# Project-scope
|
|
97
110
|
claude mcp add google \
|
|
98
111
|
-e GOOGLE_CLIENT_ID=your-client-id \
|
|
99
112
|
-e GOOGLE_CLIENT_SECRET=your-client-secret \
|
|
@@ -105,7 +118,7 @@ claude mcp add google \
|
|
|
105
118
|
Via the `claude` CLI:
|
|
106
119
|
|
|
107
120
|
```bash
|
|
108
|
-
claude mcp add google \
|
|
121
|
+
claude mcp add -s user google \
|
|
109
122
|
-e GOOGLE_MCP_PROFILE=myprofile \
|
|
110
123
|
-- npx -y google-tools-mcp
|
|
111
124
|
```
|
|
@@ -169,10 +182,10 @@ This stores tokens in `~/.config/google-tools-mcp/work/` instead of the default
|
|
|
169
182
|
|
|
170
183
|
## Tool Categories
|
|
171
184
|
|
|
172
|
-
### `files` (
|
|
185
|
+
### `files` (18 tools)
|
|
173
186
|
Google Drive file management and content reading.
|
|
174
187
|
|
|
175
|
-
`listDriveFiles`, `searchDocuments`, `getFileInfo`, `createFolder`, `listFolderContents`, `getFolderInfo`, `moveFile`, `copyFile`, `renameFile`, `deleteFile`, `createDocument`, `createDocumentFromTemplate`, `listSharedDrives`, `listSharedWithMe`, `downloadFile`, `readFile`, `searchFileContents`
|
|
188
|
+
`listDriveFiles`, `searchDocuments`, `getFileInfo`, `getFilePath`, `createFolder`, `listFolderContents`, `getFolderInfo`, `moveFile`, `copyFile`, `renameFile`, `deleteFile`, `createDocument`, `createDocumentFromTemplate`, `listSharedDrives`, `listSharedWithMe`, `downloadFile`, `readFile`, `searchFileContents`
|
|
176
189
|
|
|
177
190
|
### `documents` (22 tools)
|
|
178
191
|
Google Docs read/write/format with markdown support.
|
package/dist/index.js
CHANGED
|
@@ -69,6 +69,11 @@ export function register(server) {
|
|
|
69
69
|
.array(z.string())
|
|
70
70
|
.optional()
|
|
71
71
|
.describe('RRULE recurrence rules (e.g. ["RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"]). Only for create.'),
|
|
72
|
+
send_updates: z
|
|
73
|
+
.enum(['all', 'externalOnly', 'none'])
|
|
74
|
+
.optional()
|
|
75
|
+
.default('all')
|
|
76
|
+
.describe('Who to send email notifications to: "all" = all attendees (default), "externalOnly" = non-Google Calendar attendees only, "none" = no emails.'),
|
|
72
77
|
}),
|
|
73
78
|
execute: async (args, { log }) => {
|
|
74
79
|
const calendar = await getCalendarClient();
|
|
@@ -80,6 +85,7 @@ export function register(server) {
|
|
|
80
85
|
await calendar.events.delete({
|
|
81
86
|
calendarId: args.calendar_id,
|
|
82
87
|
eventId: args.event_id,
|
|
88
|
+
sendUpdates: args.send_updates,
|
|
83
89
|
});
|
|
84
90
|
return JSON.stringify({ success: true, message: `Event ${args.event_id} deleted.` });
|
|
85
91
|
}
|
|
@@ -96,6 +102,7 @@ export function register(server) {
|
|
|
96
102
|
calendarId: args.calendar_id,
|
|
97
103
|
requestBody: eventBody,
|
|
98
104
|
conferenceDataVersion: args.add_google_meet ? 1 : 0,
|
|
105
|
+
sendUpdates: args.send_updates,
|
|
99
106
|
};
|
|
100
107
|
|
|
101
108
|
const response = await calendar.events.insert(params);
|
|
@@ -119,6 +126,7 @@ export function register(server) {
|
|
|
119
126
|
eventId: args.event_id,
|
|
120
127
|
requestBody: eventBody,
|
|
121
128
|
conferenceDataVersion: args.add_google_meet !== undefined ? 1 : 0,
|
|
129
|
+
sendUpdates: args.send_updates,
|
|
122
130
|
};
|
|
123
131
|
|
|
124
132
|
const response = await calendar.events.update(params);
|
|
@@ -17,6 +17,11 @@ export function register(server) {
|
|
|
17
17
|
destination_calendar_id: z
|
|
18
18
|
.string()
|
|
19
19
|
.describe('Calendar ID to move the event to.'),
|
|
20
|
+
send_updates: z
|
|
21
|
+
.enum(['all', 'externalOnly', 'none'])
|
|
22
|
+
.optional()
|
|
23
|
+
.default('all')
|
|
24
|
+
.describe('Who to send email notifications to: "all" = all attendees (default), "externalOnly" = non-Google Calendar attendees only, "none" = no emails.'),
|
|
20
25
|
}),
|
|
21
26
|
execute: async (args, { log }) => {
|
|
22
27
|
const calendar = await getCalendarClient();
|
|
@@ -29,6 +34,7 @@ export function register(server) {
|
|
|
29
34
|
calendarId: args.source_calendar_id,
|
|
30
35
|
eventId: args.event_id,
|
|
31
36
|
destination: args.destination_calendar_id,
|
|
37
|
+
sendUpdates: args.send_updates,
|
|
32
38
|
});
|
|
33
39
|
|
|
34
40
|
const event = response.data;
|
|
@@ -7,13 +7,13 @@ import { docsJsonToMarkdown } from '../../markdown-transformer/index.js';
|
|
|
7
7
|
export function register(server) {
|
|
8
8
|
server.addTool({
|
|
9
9
|
name: 'readDocument',
|
|
10
|
-
description: "Reads the content of a Google Document. Returns
|
|
10
|
+
description: "Reads the content of a Google Document. Returns markdown by default (formatted content suitable for editing and re-uploading with replaceDocumentWithMarkdown). Use format='text' for plain text, or format='json' for the raw document structure.",
|
|
11
11
|
parameters: DocumentIdParameter.extend({
|
|
12
12
|
format: z
|
|
13
13
|
.enum(['text', 'json', 'markdown'])
|
|
14
14
|
.optional()
|
|
15
|
-
.default('
|
|
16
|
-
.describe("Output format: 'text' (plain text), 'json' (raw API structure, complex)
|
|
15
|
+
.default('markdown')
|
|
16
|
+
.describe("Output format: 'markdown' (formatted content), 'text' (plain text), 'json' (raw API structure, complex)."),
|
|
17
17
|
maxLength: z
|
|
18
18
|
.number()
|
|
19
19
|
.optional()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getDriveClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
export function register(server) {
|
|
6
|
+
server.addTool({
|
|
7
|
+
name: 'getFilePath',
|
|
8
|
+
description: 'Returns the full folder path of a Drive file from root to the file itself (e.g. "My Drive/Projects/Docs/report.pdf"). Walks up the parent chain to reconstruct the complete path.',
|
|
9
|
+
parameters: z.object({
|
|
10
|
+
fileId: z.string().describe('The ID of the file or folder to get the path for.'),
|
|
11
|
+
}),
|
|
12
|
+
execute: async (args, { log }) => {
|
|
13
|
+
const drive = await getDriveClient();
|
|
14
|
+
log.info(`Getting full path for file: ${args.fileId}`);
|
|
15
|
+
try {
|
|
16
|
+
const pathParts = [];
|
|
17
|
+
let currentId = args.fileId;
|
|
18
|
+
|
|
19
|
+
while (currentId) {
|
|
20
|
+
const response = await drive.files.get({
|
|
21
|
+
fileId: currentId,
|
|
22
|
+
fields: 'id,name,parents',
|
|
23
|
+
supportsAllDrives: true,
|
|
24
|
+
});
|
|
25
|
+
const file = response.data;
|
|
26
|
+
if (!file) break;
|
|
27
|
+
|
|
28
|
+
pathParts.unshift(file.name);
|
|
29
|
+
|
|
30
|
+
if (file.parents && file.parents.length > 0) {
|
|
31
|
+
currentId = file.parents[0];
|
|
32
|
+
} else {
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const fullPath = pathParts.join('/');
|
|
38
|
+
|
|
39
|
+
return JSON.stringify({ fileId: args.fileId, path: fullPath }, null, 2);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
log.error(`Error getting file path: ${error.message || error}`);
|
|
42
|
+
if (error.code === 404)
|
|
43
|
+
throw new UserError(`File not found (ID: ${args.fileId}).`);
|
|
44
|
+
if (error.code === 403)
|
|
45
|
+
throw new UserError('Permission denied. Make sure you have access to this file.');
|
|
46
|
+
throw new UserError(`Failed to get file path: ${error.message || 'Unknown error'}`);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -13,6 +13,7 @@ import { register as createFromTemplate } from './createFromTemplate.js';
|
|
|
13
13
|
import { register as listSharedDrives } from './listSharedDrives.js';
|
|
14
14
|
import { register as listSharedWithMe } from './listSharedWithMe.js';
|
|
15
15
|
import { register as downloadFile } from './downloadFile.js';
|
|
16
|
+
import { register as getFilePath } from './getFilePath.js';
|
|
16
17
|
export function registerDriveTools(server) {
|
|
17
18
|
listDriveFiles(server);
|
|
18
19
|
searchGoogleDocs(server);
|
|
@@ -29,4 +30,5 @@ export function registerDriveTools(server) {
|
|
|
29
30
|
listSharedDrives(server);
|
|
30
31
|
listSharedWithMe(server);
|
|
31
32
|
downloadFile(server);
|
|
33
|
+
getFilePath(server);
|
|
32
34
|
}
|