google-tools-mcp 1.2.12 → 2.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.
Files changed (97) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +537 -276
  3. package/dist/cachedToolsList.js +52 -52
  4. package/dist/googleDocsApiHelpers.js +76 -12
  5. package/dist/helpers.js +572 -252
  6. package/dist/httpAuth.js +296 -0
  7. package/dist/index.js +162 -11
  8. package/dist/logger.js +87 -87
  9. package/dist/markdown-transformer/docsToMarkdown.js +128 -0
  10. package/dist/markdown-transformer/index.js +17 -7
  11. package/dist/markdown-transformer/markdownToDocs.js +185 -27
  12. package/dist/readTracker.js +136 -17
  13. package/dist/sessionContext.js +37 -0
  14. package/dist/setup.js +187 -9
  15. package/dist/tools/calendar/getBusy.js +64 -64
  16. package/dist/tools/calendar/getEvents.js +141 -141
  17. package/dist/tools/calendar/getFree.js +225 -225
  18. package/dist/tools/calendar/index.js +19 -19
  19. package/dist/tools/calendar/listCalendars.js +38 -38
  20. package/dist/tools/calendar/listRecurringInstances.js +83 -83
  21. package/dist/tools/calendar/manageCalendar.js +121 -121
  22. package/dist/tools/calendar/manageEvent.js +258 -258
  23. package/dist/tools/calendar/moveEvent.js +66 -66
  24. package/dist/tools/docs/addTab.js +10 -12
  25. package/dist/tools/docs/appendToGoogleDoc.js +9 -4
  26. package/dist/tools/docs/deleteRange.js +9 -4
  27. package/dist/tools/docs/findAndReplace.js +9 -4
  28. package/dist/tools/docs/formatting/applyParagraphStyle.js +4 -1
  29. package/dist/tools/docs/formatting/getFormatting.js +171 -171
  30. package/dist/tools/docs/insertImage.js +15 -2
  31. package/dist/tools/docs/insertPageBreak.js +4 -1
  32. package/dist/tools/docs/insertTable.js +4 -1
  33. package/dist/tools/docs/insertTableWithData.js +4 -1
  34. package/dist/tools/docs/modifyText.js +14 -4
  35. package/dist/tools/docs/modifyText.test.js +84 -84
  36. package/dist/tools/docs/readGoogleDoc.js +71 -11
  37. package/dist/tools/docs/renameTab.js +12 -14
  38. package/dist/tools/drafts.js +165 -165
  39. package/dist/tools/drive/createDocument.js +12 -0
  40. package/dist/tools/drive/downloadFile.js +268 -268
  41. package/dist/tools/drive/getFileInfo.js +48 -48
  42. package/dist/tools/drive/getFilePath.js +50 -50
  43. package/dist/tools/drive/listDriveFiles.js +112 -112
  44. package/dist/tools/drive/listSharedDrives.js +52 -52
  45. package/dist/tools/drive/listSharedWithMe.js +97 -97
  46. package/dist/tools/drive/uploadFile.js +111 -111
  47. package/dist/tools/extras/index.js +9 -9
  48. package/dist/tools/extras/readDriveFile.js +197 -196
  49. package/dist/tools/extras/readFile.js +84 -84
  50. package/dist/tools/extras/searchFileContents.js +81 -81
  51. package/dist/tools/forms/batchUpdateForm.js +81 -81
  52. package/dist/tools/forms/createForm.js +54 -54
  53. package/dist/tools/forms/getForm.js +118 -118
  54. package/dist/tools/forms/getFormResponse.js +45 -45
  55. package/dist/tools/forms/index.js +15 -15
  56. package/dist/tools/forms/listFormResponses.js +56 -56
  57. package/dist/tools/forms/setPublishSettings.js +59 -59
  58. package/dist/tools/gmail/drafts.js +165 -165
  59. package/dist/tools/gmail/labels.js +54 -83
  60. package/dist/tools/gmail/messages.js +444 -441
  61. package/dist/tools/gmail/settings.js +358 -528
  62. package/dist/tools/gmail/threads.js +286 -164
  63. package/dist/tools/index.js +511 -473
  64. package/dist/tools/labels.js +103 -103
  65. package/dist/tools/legacyAliases.js +426 -0
  66. package/dist/tools/maps/directions.js +72 -0
  67. package/dist/tools/maps/geocode.js +16 -0
  68. package/dist/tools/maps/index.js +15 -0
  69. package/dist/tools/maps/mapsClient.js +83 -0
  70. package/dist/tools/maps/placeDetails.js +16 -0
  71. package/dist/tools/maps/reverseGeocode.js +19 -0
  72. package/dist/tools/maps/searchNearby.js +54 -0
  73. package/dist/tools/maps/searchPlaces.js +24 -0
  74. package/dist/tools/messages.js +448 -448
  75. package/dist/tools/settings.js +528 -528
  76. package/dist/tools/slides/createPresentation.js +104 -104
  77. package/dist/tools/slides/createShape.js +92 -92
  78. package/dist/tools/slides/createTextBox.js +82 -82
  79. package/dist/tools/slides/deleteSlide.js +30 -30
  80. package/dist/tools/slides/duplicateSlide.js +37 -37
  81. package/dist/tools/slides/exportThumbnail.js +42 -42
  82. package/dist/tools/slides/formatParagraph.js +72 -72
  83. package/dist/tools/slides/formatText.js +84 -84
  84. package/dist/tools/slides/getPresentation.js +87 -87
  85. package/dist/tools/slides/index.js +33 -33
  86. package/dist/tools/slides/reorderSlides.js +41 -41
  87. package/dist/tools/slides/replaceAllText.js +46 -46
  88. package/dist/tools/slides/setBackground.js +58 -58
  89. package/dist/tools/slides/speakerNotes.js +102 -102
  90. package/dist/tools/slides/styleShape.js +111 -111
  91. package/dist/tools/slides/updatePresentation.js +128 -128
  92. package/dist/tools/threads.js +145 -145
  93. package/dist/tools/utils/appendMarkdownToGoogleDoc.js +30 -6
  94. package/dist/tools/utils/replaceDocumentWithMarkdown.js +71 -8
  95. package/dist/updateCheck.js +202 -0
  96. package/dist/workspace.js +122 -0
  97. package/package.json +82 -81
@@ -0,0 +1,202 @@
1
+ // Best-effort check for a newer published version of google-tools-mcp.
2
+ //
3
+ // Why this exists: setup.js points MCP clients directly at a resolved
4
+ // dist/index.js instead of `npx -y google-tools-mcp` (see the fast-launch
5
+ // comment block in setup.js for why). That fixes the startup-timeout race
6
+ // against Claude Code's 30s connection timeout, but it also means nothing
7
+ // ever runs `npm install -g google-tools-mcp@latest` again on its own,
8
+ // unlike npx, which re-resolves to the latest published version on every
9
+ // launch. Left alone, a global-install user would keep running whatever
10
+ // version was installed at setup time forever, silently missing every
11
+ // release after that.
12
+ //
13
+ // This module is the read-only half of the fix: it looks up the latest
14
+ // version published to npm and reports whether the running copy is behind,
15
+ // so index.js can log a clear one-line nudge. It never modifies anything.
16
+ //
17
+ // This intentionally cannot reintroduce the npx timeout bug:
18
+ // - index.js only calls checkForUpdate() AFTER `server.start()` has
19
+ // already resolved, i.e. after the MCP stdio connection is established.
20
+ // Nothing here runs before or during connection setup.
21
+ // - index.js does not await that call. It is fire-and-forget, so even if
22
+ // the network is completely unreachable, the server process itself is
23
+ // never blocked or slowed by this.
24
+ // - The registry fetch is also strictly time-boxed (default 2s, via
25
+ // AbortSignal.timeout) so a hung connection can't leave anything dangling
26
+ // for long, and any failure (timeout, offline, non-2xx, malformed body)
27
+ // resolves to "couldn't check" rather than throwing.
28
+ // - Results are cached to disk and reused for `intervalMs` (default 24h),
29
+ // so most launches skip the network call entirely.
30
+ import * as path from 'path';
31
+
32
+ const REGISTRY_URL = 'https://registry.npmjs.org/google-tools-mcp/latest';
33
+ const DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1000;
34
+ const DEFAULT_TIMEOUT_MS = 2000;
35
+ const STATE_FILE_NAME = 'update-check.json';
36
+
37
+ // Exported for testing.
38
+ export function stateFilePath(configDir) {
39
+ return path.join(configDir, STATE_FILE_NAME);
40
+ }
41
+
42
+ // Compares two `major.minor.patch`-style version strings. Anything after a
43
+ // `-` or `+` (pre-release/build metadata) is ignored, since the registry's
44
+ // `dist-tags.latest` for this package is always a plain release version.
45
+ // Missing/non-numeric segments are treated as 0. Returns 1 if a > b, -1 if
46
+ // a < b, 0 if equal or unparseable.
47
+ export function compareVersions(a, b) {
48
+ const parse = (v) => String(v ?? '').split(/[-+]/)[0].split('.').map((n) => parseInt(n, 10));
49
+ const pa = parse(a);
50
+ const pb = parse(b);
51
+ const len = Math.max(pa.length, pb.length);
52
+ for (let i = 0; i < len; i++) {
53
+ const na = Number.isFinite(pa[i]) ? pa[i] : 0;
54
+ const nb = Number.isFinite(pb[i]) ? pb[i] : 0;
55
+ if (na !== nb) return na > nb ? 1 : -1;
56
+ }
57
+ return 0;
58
+ }
59
+
60
+ // Is `candidate` a real, strictly-newer version than `current`?
61
+ export function isNewerVersion(current, candidate) {
62
+ if (typeof candidate !== 'string' || candidate.length === 0) return false;
63
+ return compareVersions(candidate, current) > 0;
64
+ }
65
+
66
+ // Pure throttle check: has enough time passed since `lastCheckedAt` (an ISO
67
+ // string or anything `new Date()` accepts) to justify another network call?
68
+ // Missing or unparseable timestamps always say "yes, check" so a corrupt
69
+ // cache file degrades to "check a bit more often" rather than "never check
70
+ // again".
71
+ export function shouldCheckNow(lastCheckedAt, now, intervalMs = DEFAULT_INTERVAL_MS) {
72
+ if (!lastCheckedAt) return true;
73
+ const last = new Date(lastCheckedAt).getTime();
74
+ if (!Number.isFinite(last)) return true;
75
+ return now - last >= intervalMs;
76
+ }
77
+
78
+ // Should the update check be skipped entirely, without touching the network
79
+ // or disk? Three ways to opt out:
80
+ // - NO_UPDATE_NOTIFIER: the npm-ecosystem-standard opt-out env var, used by
81
+ // the widely-adopted `update-notifier` package (the library most CLIs,
82
+ // including npm itself, build this exact "is a newer version out"
83
+ // nudge on top of). Per its README: "Users can also opt-out by setting
84
+ // the environment variable NO_UPDATE_NOTIFIER with any value" (see
85
+ // https://github.com/yeoman/update-notifier#readme, fetched 2026-07-25).
86
+ // That is a presence check, not a truthiness check, so it is honored
87
+ // here the same way: any value, including an empty string, counts.
88
+ // - GOOGLE_MCP_NO_UPDATE_CHECK: this repo's own opt-out, following the
89
+ // existing GOOGLE_MCP_* naming already used for GOOGLE_MCP_PROFILE and
90
+ // GOOGLE_MCP_LOG_FILE. Same presence-check semantics as above.
91
+ // - CI: nearly every CI system sets this, and it is the de facto standard
92
+ // generic "am I running in CI" signal in the npm ecosystem (see the
93
+ // `ci-info` package, which is what `is-ci`/many CLIs use to detect it,
94
+ // and which treats CI=false as an explicit escape hatch back to "not
95
+ // CI"; see https://github.com/watson/ci-info#readme, fetched 2026-07-25).
96
+ // Automated/CI runs should never make an unannounced outbound call.
97
+ //
98
+ // This check is intentionally the very first thing checkForUpdate() does,
99
+ // before reading the cache file or touching the network, and it is a handful
100
+ // of object-property lookups, so it costs nothing even when the check would
101
+ // otherwise run.
102
+ export function isUpdateCheckDisabled(env = process.env) {
103
+ if (!env) return false;
104
+ if ('NO_UPDATE_NOTIFIER' in env) return true;
105
+ if ('GOOGLE_MCP_NO_UPDATE_CHECK' in env) return true;
106
+ if (env.CI && env.CI !== 'false') return true;
107
+ return false;
108
+ }
109
+
110
+ async function readState(configDir, { readFile }) {
111
+ try {
112
+ const raw = await readFile(stateFilePath(configDir), 'utf8');
113
+ return JSON.parse(raw);
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+
119
+ async function writeState(configDir, state, { mkdir, writeFile }) {
120
+ try {
121
+ await mkdir(configDir, { recursive: true });
122
+ await writeFile(stateFilePath(configDir), JSON.stringify(state));
123
+ } catch {
124
+ // Best-effort cache only. If we can't persist it, we just eat the
125
+ // cost of checking again next launch instead of surfacing an error.
126
+ }
127
+ }
128
+
129
+ // Fetches the latest published version from the npm registry. Never throws:
130
+ // any network error, timeout, non-2xx response, or malformed body resolves
131
+ // to null, so callers can treat "couldn't check" the same as "nothing to
132
+ // report" instead of as a startup failure. `fetchImpl` defaults to the
133
+ // global `fetch` (available on Node 18+; this repo's CI runs 20 and 22).
134
+ export async function fetchLatestVersion({
135
+ fetchImpl = fetch,
136
+ timeoutMs = DEFAULT_TIMEOUT_MS,
137
+ url = REGISTRY_URL,
138
+ } = {}) {
139
+ try {
140
+ const res = await fetchImpl(url, { signal: AbortSignal.timeout(timeoutMs) });
141
+ if (!res.ok) return null;
142
+ const body = await res.json();
143
+ return typeof body?.version === 'string' ? body.version : null;
144
+ } catch {
145
+ return null;
146
+ }
147
+ }
148
+
149
+ // Orchestrates the full best-effort check: read the cached state, decide
150
+ // whether enough time has passed to justify a network call, fetch the
151
+ // latest version if so, persist the result, and report whether the running
152
+ // copy is behind. Every dependency is injectable so tests never touch a
153
+ // real filesystem or network.
154
+ //
155
+ // Never throws. Any failure anywhere in this pipeline (corrupt cache,
156
+ // unwritable config dir, unreachable registry) resolves to
157
+ // `{ checked: false, latestVersion: null, updateAvailable: false }`, the
158
+ // safest possible answer, since callers only use this to decide whether to
159
+ // print an informational log line, never to gate startup.
160
+ export async function checkForUpdate({
161
+ currentVersion,
162
+ configDir,
163
+ now = Date.now(),
164
+ intervalMs = DEFAULT_INTERVAL_MS,
165
+ fetchLatest = fetchLatestVersion,
166
+ fetchOptions = {},
167
+ readFile,
168
+ writeFile,
169
+ mkdir,
170
+ env = process.env,
171
+ } = {}) {
172
+ // Opt-out check comes first, before any disk read or network call. See
173
+ // isUpdateCheckDisabled() above for the three ways to trigger this.
174
+ if (isUpdateCheckDisabled(env)) {
175
+ return { checked: false, latestVersion: null, updateAvailable: false, skipped: true };
176
+ }
177
+ try {
178
+ const cached = await readState(configDir, { readFile });
179
+ if (cached && !shouldCheckNow(cached.lastCheckedAt, now, intervalMs)) {
180
+ return {
181
+ checked: false,
182
+ latestVersion: cached.latestVersion ?? null,
183
+ updateAvailable: isNewerVersion(currentVersion, cached.latestVersion),
184
+ };
185
+ }
186
+
187
+ const latestVersion = await fetchLatest(fetchOptions);
188
+ await writeState(
189
+ configDir,
190
+ { lastCheckedAt: new Date(now).toISOString(), latestVersion },
191
+ { mkdir, writeFile },
192
+ );
193
+
194
+ return {
195
+ checked: true,
196
+ latestVersion,
197
+ updateAvailable: isNewerVersion(currentVersion, latestVersion),
198
+ };
199
+ } catch {
200
+ return { checked: false, latestVersion: null, updateAvailable: false };
201
+ }
202
+ }
@@ -0,0 +1,122 @@
1
+ // Secure local workspace for the Google Docs local-file editing workflow.
2
+ //
3
+ // readDocument saves a markdown working copy here and replaceDocumentWithMarkdown
4
+ // mirrors inline pushes into it. Because these files hold document contents, the
5
+ // storage has to be hardened against the classic shared-/tmp attacks:
6
+ //
7
+ // * Per-user base directory (`google-tools-mcp-<user>`), created 0700, so a
8
+ // different local user cannot read another user's working copies and cannot
9
+ // win a collision by pre-creating the directory.
10
+ // * The base dir is lstat-checked every write: if it exists but is a symlink
11
+ // (or anything other than a directory we own) we refuse to write, defeating
12
+ // a planted-symlink redirect on the directory itself.
13
+ // * Files are opened with O_NOFOLLOW (where supported) plus O_CREAT|O_TRUNC and
14
+ // mode 0600, so an attacker who pre-creates the target path as a symlink
15
+ // causes the write to fail rather than clobber the victim's file.
16
+ // * Filenames are scoped by documentId AND tabId, so reading two tabs of one
17
+ // document keeps two separate working copies instead of silently overwriting.
18
+ import * as fs from 'fs/promises';
19
+ import { constants as fsConstants } from 'fs';
20
+ import * as os from 'os';
21
+ import * as path from 'path';
22
+
23
+ // Restrict an id to characters that are always safe in a filename. documentId
24
+ // and tabId are Google-issued opaque ids, but sanitizing is cheap defense in
25
+ // depth against path traversal if a caller ever passes something unexpected.
26
+ function sanitizeComponent(value) {
27
+ return String(value).replace(/[^a-zA-Z0-9_.-]/g, '_');
28
+ }
29
+
30
+ // Per-user private base directory under the OS temp dir. Including the username
31
+ // scopes working copies per user; on POSIX the directory is additionally locked
32
+ // to 0700 and ownership-verified in ensureSafeBaseDir().
33
+ //
34
+ // GOOGLE_MCP_WORKSPACE_DIR overrides the computed path entirely when set.
35
+ // This exists so tests (and any caller that wants an isolated workspace) can
36
+ // point at a throwaway sandbox instead of the real per-user production
37
+ // directory, without needing a second code path in production. Unset (the
38
+ // normal production case), behavior is unchanged.
39
+ export function getWorkspaceDir() {
40
+ if (process.env.GOOGLE_MCP_WORKSPACE_DIR) {
41
+ return process.env.GOOGLE_MCP_WORKSPACE_DIR;
42
+ }
43
+ let userPart;
44
+ try {
45
+ userPart = sanitizeComponent(os.userInfo().username);
46
+ }
47
+ catch {
48
+ // userInfo can throw if there is no passwd entry (some sandboxes); fall
49
+ // back to the numeric uid, or a constant on platforms without getuid.
50
+ const uid = typeof process.getuid === 'function' ? process.getuid() : 'nouid';
51
+ userPart = `uid${uid}`;
52
+ }
53
+ if (!userPart) {
54
+ userPart = 'default';
55
+ }
56
+ return path.join(os.tmpdir(), `google-tools-mcp-${userPart}`);
57
+ }
58
+
59
+ // Absolute path of the working copy for a document (and optional tab). tabId is
60
+ // part of the identity so distinct tabs never share a file.
61
+ export function getWorkspacePath(documentId, tabId = null) {
62
+ const base = sanitizeComponent(documentId);
63
+ const name = tabId ? `${base}.${sanitizeComponent(tabId)}.md` : `${base}.md`;
64
+ return path.join(getWorkspaceDir(), name);
65
+ }
66
+
67
+ // Create the base dir with private perms and verify it is a real directory we
68
+ // own — not a symlink an attacker planted to redirect our writes. Throws if the
69
+ // directory exists but is unsafe.
70
+ async function ensureSafeBaseDir() {
71
+ const dir = getWorkspaceDir();
72
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
73
+ // mkdir(recursive) does not reset mode/ownership on a pre-existing entry, so
74
+ // inspect it directly. lstat (not stat) so a symlinked directory is reported
75
+ // as a symlink rather than followed.
76
+ const st = await fs.lstat(dir);
77
+ if (st.isSymbolicLink() || !st.isDirectory()) {
78
+ throw new Error(`Refusing to use workspace directory "${dir}": not a regular directory (possible symlink attack).`);
79
+ }
80
+ if (process.platform !== 'win32') {
81
+ if (typeof process.getuid === 'function' && st.uid !== process.getuid()) {
82
+ throw new Error(`Refusing to use workspace directory "${dir}": owned by another user.`);
83
+ }
84
+ // Re-assert 0700 in case the directory pre-existed with looser perms.
85
+ try {
86
+ await fs.chmod(dir, 0o700);
87
+ }
88
+ catch {
89
+ // Non-fatal: best effort tightening.
90
+ }
91
+ }
92
+ return dir;
93
+ }
94
+
95
+ // Write content to the workspace file for documentId/tabId without following a
96
+ // symlink at the final path component and with 0600 perms. Returns the absolute
97
+ // path written. Throws on any failure (callers decide whether that is fatal).
98
+ export async function writeWorkspaceFile(documentId, content, tabId = null) {
99
+ await ensureSafeBaseDir();
100
+ const filePath = getWorkspacePath(documentId, tabId);
101
+ // O_NOFOLLOW is a no-op / undefined on some platforms (notably Windows);
102
+ // fall back to 0 there. O_CREAT|O_TRUNC gives create-or-overwrite semantics.
103
+ const noFollow = fsConstants.O_NOFOLLOW ?? 0;
104
+ const flags = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_TRUNC | noFollow;
105
+ const handle = await fs.open(filePath, flags, 0o600);
106
+ try {
107
+ await handle.writeFile(content, 'utf-8');
108
+ }
109
+ finally {
110
+ await handle.close();
111
+ }
112
+ if (process.platform !== 'win32') {
113
+ // Tighten perms even if the file pre-existed with a looser mode.
114
+ try {
115
+ await fs.chmod(filePath, 0o600);
116
+ }
117
+ catch {
118
+ // Non-fatal.
119
+ }
120
+ }
121
+ return filePath;
122
+ }
package/package.json CHANGED
@@ -1,81 +1,82 @@
1
- {
2
- "name": "google-tools-mcp",
3
- "version": "1.2.12",
4
- "description": "The easiest MCP server for Google Workspace — Drive, Docs, Sheets, Gmail, Calendar, and Forms. 153 tools with one-click browser auth. Read Word docs, PDFs, and spreadsheets straight from Drive.",
5
- "type": "module",
6
- "bin": {
7
- "google-tools-mcp": "dist/index.js"
8
- },
9
- "files": [
10
- "dist"
11
- ],
12
- "keywords": [
13
- "mcp",
14
- "mcp-server",
15
- "model-context-protocol",
16
- "google",
17
- "google-workspace",
18
- "google-drive",
19
- "google-docs",
20
- "google-sheets",
21
- "google-calendar",
22
- "google-forms",
23
- "gdrive",
24
- "gmail",
25
- "email",
26
- "send-email",
27
- "read-email",
28
- "email-drafts",
29
- "email-labels",
30
- "email-filters",
31
- "sheets",
32
- "spreadsheet",
33
- "docs",
34
- "calendar",
35
- "forms",
36
- "drive",
37
- "pdf",
38
- "read-pdf",
39
- "word",
40
- "docx",
41
- "read-docx",
42
- "file-reader",
43
- "oauth",
44
- "easy-auth",
45
- "browser-auth",
46
- "claude",
47
- "ai",
48
- "llm",
49
- "ai-tools"
50
- ],
51
- "scripts": {
52
- "local:tool": "node scripts/call-local-tool.js",
53
- "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
54
- "test:ci": "node --experimental-vm-modules node_modules/jest/bin/jest.js --ci --coverage"
55
- },
56
- "repository": {
57
- "type": "git",
58
- "url": "git+https://github.com/karthikcsq/google-tools-mcp.git"
59
- },
60
- "homepage": "https://github.com/karthikcsq/google-tools-mcp#readme",
61
- "bugs": {
62
- "url": "https://github.com/karthikcsq/google-tools-mcp/issues"
63
- },
64
- "license": "MIT",
65
- "dependencies": {
66
- "@clack/prompts": "^1.2.0",
67
- "chalk": "^5.6.2",
68
- "diff": "^7.0.0",
69
- "fastmcp": "^3.24.0",
70
- "google-auth-library": "^10.5.0",
71
- "googleapis": "^171.4.0",
72
- "mammoth": "^1.9.0",
73
- "markdown-it": "^14.1.0",
74
- "pdf-parse": "^1.1.1",
75
- "zod": "^3.24.2"
76
- },
77
- "devDependencies": {
78
- "@jest/globals": "^30.3.0",
79
- "jest": "^30.3.0"
80
- }
81
- }
1
+ {
2
+ "name": "google-tools-mcp",
3
+ "version": "2.0.0",
4
+ "description": "The easiest MCP server for Google Workspace — Drive, Docs, Sheets, Gmail, Calendar, and Forms. 153 tools with one-click browser auth. Read Word docs, PDFs, and spreadsheets straight from Drive.",
5
+ "type": "module",
6
+ "bin": {
7
+ "google-tools-mcp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "keywords": [
13
+ "mcp",
14
+ "mcp-server",
15
+ "model-context-protocol",
16
+ "google",
17
+ "google-workspace",
18
+ "google-drive",
19
+ "google-docs",
20
+ "google-sheets",
21
+ "google-calendar",
22
+ "google-forms",
23
+ "gdrive",
24
+ "gmail",
25
+ "email",
26
+ "send-email",
27
+ "read-email",
28
+ "email-drafts",
29
+ "email-labels",
30
+ "email-filters",
31
+ "sheets",
32
+ "spreadsheet",
33
+ "docs",
34
+ "calendar",
35
+ "forms",
36
+ "drive",
37
+ "pdf",
38
+ "read-pdf",
39
+ "word",
40
+ "docx",
41
+ "read-docx",
42
+ "file-reader",
43
+ "oauth",
44
+ "easy-auth",
45
+ "browser-auth",
46
+ "claude",
47
+ "ai",
48
+ "llm",
49
+ "ai-tools"
50
+ ],
51
+ "scripts": {
52
+ "local:tool": "node scripts/call-local-tool.js",
53
+ "start": "node dist/index.js",
54
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
55
+ "test:ci": "node --experimental-vm-modules node_modules/jest/bin/jest.js --ci --coverage"
56
+ },
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "git+https://github.com/karthikcsq/google-tools-mcp.git"
60
+ },
61
+ "homepage": "https://github.com/karthikcsq/google-tools-mcp#readme",
62
+ "bugs": {
63
+ "url": "https://github.com/karthikcsq/google-tools-mcp/issues"
64
+ },
65
+ "license": "MIT",
66
+ "dependencies": {
67
+ "@clack/prompts": "^1.2.0",
68
+ "chalk": "^5.6.2",
69
+ "diff": "^7.0.0",
70
+ "fastmcp": "^3.24.0",
71
+ "google-auth-library": "^10.5.0",
72
+ "googleapis": "^171.4.0",
73
+ "mammoth": "^1.9.0",
74
+ "markdown-it": "^14.1.0",
75
+ "pdf-parse": "^1.1.1",
76
+ "zod": "^3.24.2"
77
+ },
78
+ "devDependencies": {
79
+ "@jest/globals": "^30.3.0",
80
+ "jest": "^30.3.0"
81
+ }
82
+ }