google-services-mcp 0.1.0-beta.100

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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -0
  3. package/dist/auth/config.d.ts +43 -0
  4. package/dist/auth/config.d.ts.map +1 -0
  5. package/dist/auth/config.js +112 -0
  6. package/dist/auth/config.js.map +1 -0
  7. package/dist/auth/manager.d.ts +50 -0
  8. package/dist/auth/manager.d.ts.map +1 -0
  9. package/dist/auth/manager.js +141 -0
  10. package/dist/auth/manager.js.map +1 -0
  11. package/dist/auth/oauth.d.ts +57 -0
  12. package/dist/auth/oauth.d.ts.map +1 -0
  13. package/dist/auth/oauth.js +223 -0
  14. package/dist/auth/oauth.js.map +1 -0
  15. package/dist/auth/store.d.ts +18 -0
  16. package/dist/auth/store.d.ts.map +1 -0
  17. package/dist/auth/store.js +50 -0
  18. package/dist/auth/store.js.map +1 -0
  19. package/dist/cli.d.ts +7 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +137 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/main.d.ts +3 -0
  24. package/dist/main.d.ts.map +1 -0
  25. package/dist/main.js +21 -0
  26. package/dist/main.js.map +1 -0
  27. package/dist/server.d.ts +3 -0
  28. package/dist/server.d.ts.map +1 -0
  29. package/dist/server.js +28 -0
  30. package/dist/server.js.map +1 -0
  31. package/dist/services/calendar.d.ts +77 -0
  32. package/dist/services/calendar.d.ts.map +1 -0
  33. package/dist/services/calendar.js +141 -0
  34. package/dist/services/calendar.js.map +1 -0
  35. package/dist/services/contacts-tasks.d.ts +61 -0
  36. package/dist/services/contacts-tasks.d.ts.map +1 -0
  37. package/dist/services/contacts-tasks.js +106 -0
  38. package/dist/services/contacts-tasks.js.map +1 -0
  39. package/dist/services/docs.d.ts +32 -0
  40. package/dist/services/docs.d.ts.map +1 -0
  41. package/dist/services/docs.js +58 -0
  42. package/dist/services/docs.js.map +1 -0
  43. package/dist/services/drive.d.ts +72 -0
  44. package/dist/services/drive.d.ts.map +1 -0
  45. package/dist/services/drive.js +131 -0
  46. package/dist/services/drive.js.map +1 -0
  47. package/dist/services/forms.d.ts +30 -0
  48. package/dist/services/forms.d.ts.map +1 -0
  49. package/dist/services/forms.js +54 -0
  50. package/dist/services/forms.js.map +1 -0
  51. package/dist/services/gmail.d.ts +159 -0
  52. package/dist/services/gmail.d.ts.map +1 -0
  53. package/dist/services/gmail.js +499 -0
  54. package/dist/services/gmail.js.map +1 -0
  55. package/dist/services/sheets.d.ts +44 -0
  56. package/dist/services/sheets.d.ts.map +1 -0
  57. package/dist/services/sheets.js +53 -0
  58. package/dist/services/sheets.js.map +1 -0
  59. package/dist/services/slides.d.ts +30 -0
  60. package/dist/services/slides.d.ts.map +1 -0
  61. package/dist/services/slides.js +52 -0
  62. package/dist/services/slides.js.map +1 -0
  63. package/dist/services/youtube.d.ts +45 -0
  64. package/dist/services/youtube.d.ts.map +1 -0
  65. package/dist/services/youtube.js +97 -0
  66. package/dist/services/youtube.js.map +1 -0
  67. package/dist/tools/index.d.ts +3 -0
  68. package/dist/tools/index.d.ts.map +1 -0
  69. package/dist/tools/index.js +953 -0
  70. package/dist/tools/index.js.map +1 -0
  71. package/dist/util/result.d.ts +29 -0
  72. package/dist/util/result.d.ts.map +1 -0
  73. package/dist/util/result.js +21 -0
  74. package/dist/util/result.js.map +1 -0
  75. package/package.json +60 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nabheet S. Sandhu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,303 @@
1
+ # google-services-mcp
2
+
3
+ MCP (Model Context Protocol) server that gives AI agents direct access to your
4
+ Google services — **Gmail, Calendar, Google Meet, Drive, Contacts, Tasks,
5
+ Sheets, Docs, Slides, YouTube, and Forms** — with multi-account OAuth support.
6
+
7
+ Add one account, wire the server into any MCP-compatible AI client, and your
8
+ agent can read and send email, manage your calendar, find files in Drive,
9
+ create documents and spreadsheets, and more — using your real Google data.
10
+
11
+ ## Features
12
+
13
+ - **Gmail** — send, list, read, modify, and reply to messages
14
+ - **Calendar** — list calendars, create/read/update/delete events, create Google Meet links
15
+ - **Drive** — list, read, upload, update, delete, and share files
16
+ - **Contacts** — list, search, and create contacts
17
+ - **Tasks** — list task lists, create/complete/delete tasks
18
+ - **Sheets** — create spreadsheets, read/write/append cell ranges, batch update
19
+ - **Docs** — create documents, read text, insert/replace text, batch update
20
+ - **Slides** — create presentations, add/delete slides, find-and-replace text
21
+ - **YouTube** — search videos, manage uploads/playlists/subscriptions
22
+ - **Forms** — create forms, add questions, read responses
23
+ - **Multi-account** — connect several Google accounts, set a default, or pass
24
+ an `account` argument to any tool
25
+ - **OAuth 2.0** — one-time browser authorization; tokens are stored locally and refreshed automatically
26
+
27
+ > **60 tools** across 11 services, all prefixed `google_`. Full reference:
28
+ > [docs/TOOLS.md](docs/TOOLS.md).
29
+
30
+ ## Requirements
31
+
32
+ - Node.js **24+** (CI runs Node 24; local dev on 26)
33
+ - A Google Cloud project with the required APIs enabled and an OAuth 2.0
34
+ **Desktop app** client (one-time setup, ~10 minutes — see below)
35
+
36
+ ## Install
37
+
38
+ No install needed — run straight from npm via `npx`:
39
+
40
+ ```bash
41
+ npx -y google-services-mcp --version
42
+ ```
43
+
44
+ `npx` downloads the package on first run and caches it. Every command in
45
+ this README (`add`, `list`, `status`, ...) works the same way:
46
+
47
+ ```bash
48
+ npx -y google-services-mcp add personal
49
+ ```
50
+
51
+ > **Pin a version for reproducibility** — check the current release with
52
+ > `npm view google-services-mcp version`, then run an exact version:
53
+ >
54
+ > ```bash
55
+ > npx -y google-services-mcp@<version> add personal
56
+ > ```
57
+ >
58
+ > MCP clients auto-launch this server, so pinning avoids surprise breakage
59
+ > when a new release ships.
60
+
61
+ ### Alternative: global install
62
+
63
+ Prefer a persistent `google-services-mcp` command (and faster MCP client
64
+ startup)?
65
+
66
+ ```bash
67
+ npm install -g google-services-mcp
68
+ ```
69
+
70
+ Then replace `npx -y google-services-mcp` with `google-services-mcp`
71
+ everywhere below.
72
+
73
+ ### Release channels
74
+
75
+ - `latest` — stable releases, published automatically on every merge to `main`
76
+ - `beta` — staging builds, published manually from `main`
77
+ (e.g. `0.x.x-beta.0`)
78
+
79
+ Try the staging channel without touching your stable install:
80
+
81
+ ```bash
82
+ npx -y google-services-mcp@beta --version
83
+ ```
84
+
85
+ All publishes are signed with npm **provenance** (trusted publishing via
86
+ GitHub Actions OIDC) — no npm token is stored in CI.
87
+
88
+ ## Quick start
89
+
90
+ 1. **Add a Google account** — opens your browser for one-time authorization:
91
+
92
+ ```bash
93
+ npx -y google-services-mcp add personal
94
+ ```
95
+
96
+ 2. **Register the server** with your MCP client (see
97
+ [MCP client configuration](#mcp-client-configuration) for Claude Desktop,
98
+ opencode, and generic configs).
99
+
100
+ 3. **Start using it** — ask your agent to "check my email" or "add an event to
101
+ my calendar". Or add more accounts any time: `npx -y google-services-mcp add work`.
102
+
103
+ ## MCP client configuration
104
+
105
+ The server speaks MCP over stdio. Point your client at the
106
+ `google-services-mcp` package via `npx` and pass your OAuth credentials via
107
+ environment variables. (If you installed globally, use `google-services-mcp`
108
+ as the command instead.)
109
+
110
+ ### opencode
111
+
112
+ In `opencode.json` (or your global config):
113
+
114
+ ```json
115
+ {
116
+ "mcp": {
117
+ "google-services-mcp": {
118
+ "type": "local",
119
+ "command": ["npx", "-y", "google-services-mcp"],
120
+ "enabled": true,
121
+ "environment": {
122
+ "GOOGLE_MCP_CLIENT_ID": "your-client-id",
123
+ "GOOGLE_MCP_CLIENT_SECRET": "your-client-secret"
124
+ }
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Claude Desktop / Cursor / other MCP clients
131
+
132
+ In the client's MCP settings file (e.g. `claude_desktop_config.json`):
133
+
134
+ ```json
135
+ {
136
+ "mcpServers": {
137
+ "google-services-mcp": {
138
+ "command": "npx",
139
+ "args": ["-y", "google-services-mcp"],
140
+ "env": {
141
+ "GOOGLE_MCP_CLIENT_ID": "your-client-id",
142
+ "GOOGLE_MCP_CLIENT_SECRET": "your-client-secret"
143
+ }
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ ## Google Cloud setup (one-time)
150
+
151
+ Each installation uses its own OAuth client — credentials are per-user and
152
+ never shared.
153
+
154
+ ### 1. Create a project
155
+
156
+ Open the [Google Cloud Console](https://console.cloud.google.com/), choose
157
+ **Select a project → New Project**, and name it (e.g. `google-services-mcp`).
158
+
159
+ ### 2. Configure the OAuth consent screen
160
+
161
+ **APIs & Services → OAuth consent screen**:
162
+
163
+ 1. User type: **External** (required for regular Google accounts).
164
+ 2. App name: anything you like (e.g. `Google Service MCP`); support email = yours.
165
+ 3. Under **Audience → Test users**, click **Add users** and enter **every Google
166
+ account that will use this server**. While the app is in Testing mode,
167
+ accounts not on this list get `access blocked` on the consent screen.
168
+
169
+ ### 3. Enable the required APIs
170
+
171
+ **APIs & Services → Library**, search and **Enable** each:
172
+
173
+ `Gmail API` · `Google Calendar API` · `Google Drive API` · `People API` ·
174
+ `Google Tasks API` · `Google Sheets API` · `Google Docs API` ·
175
+ `Google Slides API` · `YouTube Data API v3` · `Google Forms API`
176
+
177
+ > **Automated:** with the `gcloud` CLI installed and authenticated
178
+ > (`gcloud auth login`), enable all 10 APIs at once — idempotent, safe to
179
+ > re-run:
180
+ >
181
+ > ```bash
182
+ > bash scripts/enable-apis.sh --project your-project-id
183
+ > ```
184
+
185
+ ### 4. Create OAuth client credentials
186
+
187
+ **APIs & Services → Credentials → Create Credentials → OAuth client ID**:
188
+
189
+ 1. Application type: **Desktop app**.
190
+ 2. Authorized redirect URIs: `http://localhost:8787`.
191
+ 3. Copy the **Client ID** and **Client Secret**.
192
+
193
+ ### 5. Provide the credentials
194
+
195
+ Set these environment variables (or put them in your MCP client config as
196
+ above):
197
+
198
+ ```text
199
+ GOOGLE_MCP_CLIENT_ID=<your-oauth-client-id>
200
+ GOOGLE_MCP_CLIENT_SECRET=<your-oauth-client-secret>
201
+ ```
202
+
203
+ Optional:
204
+
205
+ ```text
206
+ GOOGLE_MCP_DIR=~/.google-services-mcp # where config and tokens live (default ~/.google-services-mcp)
207
+ GOOGLE_MCP_REDIRECT_PORT=8787 # local loopback port for OAuth (default 8787)
208
+ # Full redirect URI override. Takes precedence over REDIRECT_PORT and must
209
+ # exactly match a URI registered for this OAuth client in the Cloud Console.
210
+ # GOOGLE_MCP_REDIRECT_URI=http://127.0.0.1:8787/oauth2callback
211
+ ```
212
+
213
+ ### 6. Connect your first account
214
+
215
+ ```bash
216
+ npx -y google-services-mcp add personal
217
+ ```
218
+
219
+ A browser opens to the Google consent screen. After you approve, the account is
220
+ stored in `~/.google-services-mcp/accounts/` and ready to use. Repeat with a
221
+ different name (e.g. `work`) to connect more accounts.
222
+
223
+ ## CLI reference
224
+
225
+ ```bash
226
+ npx -y google-services-mcp # run the MCP server over stdio
227
+ npx -y google-services-mcp add <name> # add a Google account (opens browser)
228
+ npx -y google-services-mcp list # list connected accounts
229
+ npx -y google-services-mcp remove <name> # remove an account
230
+ npx -y google-services-mcp set-default <name> # set the default account
231
+ npx -y google-services-mcp status # show config and token health
232
+ npx -y google-services-mcp --help # show help
233
+ ```
234
+
235
+ ## For AI agents
236
+
237
+ - All tools are prefixed `google_`, e.g. `google_gmail_list`,
238
+ `google_calendar_list_events`, `google_drive_list_files`.
239
+ - The `account` argument selects which connected account a tool uses;
240
+ omit it to use the default account.
241
+ - Agents can connect new accounts themselves via the **`account_add`** tool —
242
+ it opens the browser consent flow without needing the CLI.
243
+ - See [docs/TOOLS.md](docs/TOOLS.md) for the full tool reference.
244
+
245
+ ## Security
246
+
247
+ - OAuth tokens are stored in `~/.google-services-mcp/accounts/<account-name>.json`
248
+ with restrictive file permissions. Keep them private — never commit them.
249
+ - The server never logs tokens or credentials.
250
+ - Scopes requested: Gmail modify, Calendar, Drive, Contacts, Tasks, Sheets,
251
+ Docs, Slides, YouTube, Forms, plus `userinfo.email` for account display.
252
+ - If a client secret is ever exposed, rotate it in the Google Cloud Console
253
+ (Credentials → your OAuth client → rotate secret).
254
+
255
+ ## Troubleshooting
256
+
257
+ | Symptom | Cause / fix |
258
+ | --- | --- |
259
+ | `access blocked` on consent | Account not in **Test users** (setup step 2.3) — add it and retry |
260
+ | `redirect_uri_mismatch` | Exactly `http://localhost:8787` (no trailing slash), per OAuth client |
261
+ | Tokens stop after ~7 days | App in **Testing** mode — re-run `add` or **Publish app** |
262
+ | `npx` not found | GUI clients may lack PATH — use `/usr/local/bin/npx` or global install |
263
+ | `Google hasn't verified this app` | Normal — click **Advanced → Continue** for personal use |
264
+
265
+ ## Local development
266
+
267
+ ```bash
268
+ git clone https://github.com/nabheet/google-services-mcp.git
269
+ cd google-services-mcp
270
+ npm install
271
+ npm run build # compile to dist/
272
+ npm test # vitest, TDD suites (incl. hermetic E2E in test/e2e/)
273
+ npm run typecheck # tsc --noEmit
274
+ npm run dev # run from source with tsx
275
+ ```
276
+
277
+ See [AGENTS.md](AGENTS.md) for architecture and conventions.
278
+
279
+ ## Releases (maintainers)
280
+
281
+ All publishing happens from GitHub Actions — no local npm login needed:
282
+
283
+ 1. **Stable** — every merge to `main` auto-publishes the next patch version
284
+ to `latest` with provenance, then creates a `vX.Y.Z` tag and GitHub
285
+ release. No manual steps. The next version is derived from the last
286
+ published `latest` on npm, so repeated merges never collide; an
287
+ intentional minor/major bump in `package.json` is honored.
288
+ 2. **Staging** — manual beta builds from `main`: **Actions → CI
289
+ → Run workflow**. Publishes a `beta` build (`0.1.1-beta.X`) under the
290
+ `beta` dist-tag with provenance.
291
+
292
+ Prerelease tags `v*-beta*` publish to `beta` with a prerelease GitHub release
293
+ (patch/minor builds for milestone testing). Publishing uses npm **trusted
294
+ publishing** (OIDC): configure it once per package at
295
+ `npmjs.com/package/google-services-mcp/access` with the GitHub repository
296
+ `nabheet/google-services-mcp` and workflow name `ci.yml`.
297
+ npm allows **one trusted publisher per package** and validates the calling
298
+ workflow's filename — all channels run from the same `ci.yml` file.
299
+ No `NPM_TOKEN` secret is required.
300
+
301
+ ## License
302
+
303
+ MIT
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Configuration for the OAuth client and server defaults.
3
+ *
4
+ * Credentials come from (in order of precedence):
5
+ * 1. Environment variables GOOGLE_MCP_CLIENT_ID / GOOGLE_MCP_CLIENT_SECRET
6
+ * 2. The config file at <dir>/config.json
7
+ *
8
+ * The data directory defaults to ~/.google-services-mcp and can be overridden with
9
+ * GOOGLE_MCP_DIR.
10
+ */
11
+ export declare const DEFAULT_REDIRECT_PORT = 8787;
12
+ /** Scopes requested when adding an account. Covers all services this server exposes. */
13
+ export declare const DEFAULT_SCOPES: string[];
14
+ export interface Config {
15
+ clientId: string;
16
+ clientSecret: string;
17
+ /** Loopback port used during the OAuth redirect. Default 8787. Ignored when redirectUri is set. */
18
+ redirectPort?: number;
19
+ /**
20
+ * Full redirect URI registered with Google, e.g. "http://127.0.0.1:8787/oauth2callback".
21
+ * Takes precedence over redirectPort and must exactly match what is registered for
22
+ * this OAuth client in the Google Cloud Console.
23
+ */
24
+ redirectUri?: string;
25
+ /** Account used when tools are called without an explicit `account`. */
26
+ defaultAccount?: string;
27
+ /** Override the scope list requested at authorization time. */
28
+ scopes?: string[];
29
+ }
30
+ export declare function getDataDir(): string;
31
+ export declare function getConfigPath(): string;
32
+ export declare function getAccountsDir(): string;
33
+ export declare function getAccountPath(name: string): string;
34
+ export declare function sanitizeName(name: string): string;
35
+ export declare function isValidName(name: string): boolean;
36
+ export declare function ensureDirs(): Promise<void>;
37
+ export declare function loadConfig(): Promise<Config>;
38
+ export declare function saveConfig(config: Config): Promise<void>;
39
+ export declare function saveClientCredentials(clientId: string, clientSecret: string): Promise<void>;
40
+ export declare function hasCredentials(config: Config): boolean;
41
+ /** Location of this package's root (for README links etc.). */
42
+ export declare function getPackageRoot(): string;
43
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/auth/config.ts"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AAEH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,wFAAwF;AACxF,eAAO,MAAM,cAAc,EAAE,MAAM,EAuBlC,CAAC;AAEF,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,mGAAmG;IACnG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAwBlD;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9D;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjG;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
@@ -0,0 +1,112 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ /**
6
+ * Configuration for the OAuth client and server defaults.
7
+ *
8
+ * Credentials come from (in order of precedence):
9
+ * 1. Environment variables GOOGLE_MCP_CLIENT_ID / GOOGLE_MCP_CLIENT_SECRET
10
+ * 2. The config file at <dir>/config.json
11
+ *
12
+ * The data directory defaults to ~/.google-services-mcp and can be overridden with
13
+ * GOOGLE_MCP_DIR.
14
+ */
15
+ export const DEFAULT_REDIRECT_PORT = 8787;
16
+ /** Scopes requested when adding an account. Covers all services this server exposes. */
17
+ export const DEFAULT_SCOPES = [
18
+ // Gmail: read, send, and modify (labels/trash) — not permanent delete
19
+ "https://www.googleapis.com/auth/gmail.modify",
20
+ // Calendar + Google Meet (conferenceData on events)
21
+ "https://www.googleapis.com/auth/calendar",
22
+ // Drive: full access to the user's Drive
23
+ "https://www.googleapis.com/auth/drive",
24
+ // Contacts (read/write)
25
+ "https://www.googleapis.com/auth/contacts",
26
+ // Tasks
27
+ "https://www.googleapis.com/auth/tasks",
28
+ // Sheets: read/write spreadsheets
29
+ "https://www.googleapis.com/auth/spreadsheets",
30
+ // Docs: read/write documents
31
+ "https://www.googleapis.com/auth/documents",
32
+ // Slides: read/write presentations
33
+ "https://www.googleapis.com/auth/presentations",
34
+ // YouTube: read/write the user's channel data and playlists
35
+ "https://www.googleapis.com/auth/youtube",
36
+ // Forms: read/write forms and responses
37
+ "https://www.googleapis.com/auth/forms",
38
+ // Identity: lets fetchUserInfo return the signed-in email for account display
39
+ "https://www.googleapis.com/auth/userinfo.email",
40
+ ];
41
+ export function getDataDir() {
42
+ return process.env.GOOGLE_MCP_DIR || path.join(os.homedir(), ".google-services-mcp");
43
+ }
44
+ export function getConfigPath() {
45
+ return path.join(getDataDir(), "config.json");
46
+ }
47
+ export function getAccountsDir() {
48
+ return path.join(getDataDir(), "accounts");
49
+ }
50
+ export function getAccountPath(name) {
51
+ return path.join(getAccountsDir(), `${sanitizeName(name)}.json`);
52
+ }
53
+ export function sanitizeName(name) {
54
+ const cleaned = name.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
55
+ if (!/[a-zA-Z0-9]/.test(cleaned)) {
56
+ throw new Error("Account name must contain at least one alphanumeric character.");
57
+ }
58
+ return cleaned;
59
+ }
60
+ export function isValidName(name) {
61
+ return /^[a-zA-Z0-9_-]+$/.test(name);
62
+ }
63
+ export async function ensureDirs() {
64
+ await fs.mkdir(getAccountsDir(), { recursive: true, mode: 0o700 });
65
+ // mkdir's mode only applies to newly-created dirs; harden pre-existing ones.
66
+ await fs.chmod(getAccountsDir(), 0o700).catch(() => { });
67
+ await fs.chmod(getDataDir(), 0o700).catch(() => { });
68
+ }
69
+ export async function loadConfig() {
70
+ await ensureDirs();
71
+ let file = {};
72
+ try {
73
+ const raw = await fs.readFile(getConfigPath(), "utf8");
74
+ file = JSON.parse(raw);
75
+ }
76
+ catch {
77
+ // No config file yet — env vars may still provide credentials.
78
+ }
79
+ const envId = process.env.GOOGLE_MCP_CLIENT_ID;
80
+ const envSecret = process.env.GOOGLE_MCP_CLIENT_SECRET;
81
+ const envPort = process.env.GOOGLE_MCP_REDIRECT_PORT;
82
+ const envUri = process.env.GOOGLE_MCP_REDIRECT_URI;
83
+ const config = {
84
+ clientId: envId || file.clientId || "",
85
+ clientSecret: envSecret || file.clientSecret || "",
86
+ redirectPort: envPort ? Number(envPort) : file.redirectPort || DEFAULT_REDIRECT_PORT,
87
+ redirectUri: envUri || file.redirectUri || undefined,
88
+ defaultAccount: file.defaultAccount,
89
+ scopes: file.scopes || DEFAULT_SCOPES,
90
+ };
91
+ return config;
92
+ }
93
+ export async function saveConfig(config) {
94
+ await ensureDirs();
95
+ const { clientId, clientSecret, redirectPort, redirectUri, defaultAccount, scopes } = config;
96
+ await fs.writeFile(getConfigPath(), JSON.stringify({ clientId, clientSecret, redirectPort, redirectUri, defaultAccount, scopes }, null, 2), { encoding: "utf8", mode: 0o600 });
97
+ await fs.chmod(getConfigPath(), 0o600).catch(() => { });
98
+ }
99
+ export async function saveClientCredentials(clientId, clientSecret) {
100
+ const config = await loadConfig();
101
+ config.clientId = clientId || config.clientId;
102
+ config.clientSecret = clientSecret || config.clientSecret;
103
+ await saveConfig(config);
104
+ }
105
+ export function hasCredentials(config) {
106
+ return Boolean(config.clientId && config.clientSecret);
107
+ }
108
+ /** Location of this package's root (for README links etc.). */
109
+ export function getPackageRoot() {
110
+ return path.dirname(path.dirname(fileURLToPath(import.meta.url)));
111
+ }
112
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/auth/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { type Auth } from "googleapis";
2
+ import { type Config } from "./config.js";
3
+ import { type StoredAccount } from "./store.js";
4
+ export interface AddAccountOptions {
5
+ /** Set to false to skip opening the browser (headless). */
6
+ openBrowser?: boolean;
7
+ /** Milliseconds to wait for the OAuth callback before failing. Default 5 minutes. */
8
+ timeoutMs?: number;
9
+ }
10
+ export interface AccountStatus {
11
+ name: string;
12
+ email?: string;
13
+ expiresAt?: number;
14
+ tokenHealthy: boolean;
15
+ scopes: string[];
16
+ }
17
+ export interface ServerStatus {
18
+ credentialsConfigured: boolean;
19
+ dataDir: string;
20
+ defaultAccount?: string;
21
+ accounts: AccountStatus[];
22
+ }
23
+ /**
24
+ * Coordinates credentials, per-account token storage, the interactive OAuth
25
+ * flow, and refresh. All service modules resolve auth through here.
26
+ */
27
+ declare class AuthManager {
28
+ getConfig(): Promise<Config>;
29
+ listAccounts(): Promise<StoredAccount[]>;
30
+ /**
31
+ * Resolve an account to use for a tool call.
32
+ * Priority: explicit name -> configured default -> sole account.
33
+ */
34
+ resolveAccount(name?: string): Promise<StoredAccount>;
35
+ /** Get an authenticated googleapis OAuth2 client, refreshing first if needed. */
36
+ getClient(name?: string): Promise<Auth.OAuth2Client>;
37
+ /**
38
+ * Run the interactive OAuth flow to add a new account: opens the consent
39
+ * screen, waits for the loopback callback, exchanges the code, and persists
40
+ * the account alongside the signed-in email.
41
+ */
42
+ addAccount(name: string, options?: AddAccountOptions): Promise<StoredAccount>;
43
+ removeAccount(name: string): Promise<boolean>;
44
+ setDefaultAccount(name: string): Promise<void>;
45
+ saveCredentials(clientId: string, clientSecret: string): Promise<void>;
46
+ getStatus(): Promise<ServerStatus>;
47
+ }
48
+ export declare const authManager: AuthManager;
49
+ export {};
50
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/auth/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAU,MAAM,YAAY,CAAC;AAC/C,OAAO,EACL,KAAK,MAAM,EAMZ,MAAM,aAAa,CAAC;AAYrB,OAAO,EAIL,KAAK,aAAa,EAEnB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qFAAqF;IACrF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED;;;GAGG;AACH,cAAM,WAAW;IACT,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAEjC;IAEK,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAE7C;IAED;;;OAGG;IACG,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAwB1D;IAED,iFAAiF;IAC3E,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAsBzD;IAED;;;;OAIG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,aAAa,CAAC,CA4CtF;IAEK,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElD;IAEK,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQnD;IAEK,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;IAEK,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,CAevC;CACF;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}