google-drive-files-mcp 0.3.0__tar.gz
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.
- google_drive_files_mcp-0.3.0/.github/workflows/ci.yml +29 -0
- google_drive_files_mcp-0.3.0/.github/workflows/release.yml +36 -0
- google_drive_files_mcp-0.3.0/.gitignore +33 -0
- google_drive_files_mcp-0.3.0/LICENSE +21 -0
- google_drive_files_mcp-0.3.0/PKG-INFO +190 -0
- google_drive_files_mcp-0.3.0/README.md +159 -0
- google_drive_files_mcp-0.3.0/docs/claude-code.md +23 -0
- google_drive_files_mcp-0.3.0/docs/claude-desktop.md +30 -0
- google_drive_files_mcp-0.3.0/docs/other-clients.md +32 -0
- google_drive_files_mcp-0.3.0/docs/setup-google-oauth.md +56 -0
- google_drive_files_mcp-0.3.0/examples/file-into-dated-folder.sh +23 -0
- google_drive_files_mcp-0.3.0/pyproject.toml +57 -0
- google_drive_files_mcp-0.3.0/server.json +50 -0
- google_drive_files_mcp-0.3.0/src/google_drive_files_mcp/__init__.py +2 -0
- google_drive_files_mcp-0.3.0/src/google_drive_files_mcp/__main__.py +5 -0
- google_drive_files_mcp-0.3.0/src/google_drive_files_mcp/auth.py +88 -0
- google_drive_files_mcp-0.3.0/src/google_drive_files_mcp/cli.py +290 -0
- google_drive_files_mcp-0.3.0/src/google_drive_files_mcp/client.py +217 -0
- google_drive_files_mcp-0.3.0/src/google_drive_files_mcp/config.py +48 -0
- google_drive_files_mcp-0.3.0/src/google_drive_files_mcp/server.py +247 -0
- google_drive_files_mcp-0.3.0/src/google_drive_files_mcp/sheets.py +270 -0
- google_drive_files_mcp-0.3.0/tests/__init__.py +0 -0
- google_drive_files_mcp-0.3.0/tests/test_client.py +101 -0
- google_drive_files_mcp-0.3.0/tests/test_config.py +45 -0
- google_drive_files_mcp-0.3.0/tests/test_sheets.py +93 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest]
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
- name: Install
|
|
23
|
+
run: |
|
|
24
|
+
python -m pip install --upgrade pip
|
|
25
|
+
pip install -e ".[dev]"
|
|
26
|
+
- name: Lint
|
|
27
|
+
run: ruff check src tests
|
|
28
|
+
- name: Test
|
|
29
|
+
run: pytest -v
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- run: python -m pip install --upgrade build
|
|
17
|
+
- run: python -m build
|
|
18
|
+
- uses: actions/upload-artifact@v4
|
|
19
|
+
with:
|
|
20
|
+
name: dist
|
|
21
|
+
path: dist/
|
|
22
|
+
|
|
23
|
+
publish:
|
|
24
|
+
needs: build
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
environment:
|
|
27
|
+
name: pypi
|
|
28
|
+
url: https://pypi.org/project/google-drive-files-mcp/
|
|
29
|
+
permissions:
|
|
30
|
+
id-token: write
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/download-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: dist
|
|
35
|
+
path: dist/
|
|
36
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.eggs/
|
|
9
|
+
|
|
10
|
+
# Virtual envs
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
.env
|
|
15
|
+
|
|
16
|
+
# Tooling
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.mypy_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
|
|
22
|
+
# Editors / OS
|
|
23
|
+
.vscode/
|
|
24
|
+
.idea/
|
|
25
|
+
*.swp
|
|
26
|
+
.DS_Store
|
|
27
|
+
|
|
28
|
+
# Credentials — NEVER commit
|
|
29
|
+
credentials.json
|
|
30
|
+
token.json
|
|
31
|
+
client_secret*.json
|
|
32
|
+
*.pem
|
|
33
|
+
*.key
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zayan Khan
|
|
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.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: google-drive-files-mcp
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A minimal MCP server and CLI for Google Drive file management (move, upload, organize) and Google Sheets editing.
|
|
5
|
+
Project-URL: Homepage, https://github.com/zayansalman/google-drive-files-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/zayansalman/google-drive-files-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/zayansalman/google-drive-files-mcp/issues
|
|
8
|
+
Author: Zayan Khan
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: anthropic,claude,files,google-drive,google-sheets,mcp,model-context-protocol,move,spreadsheet,upload
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Office/Business
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: google-api-python-client>=2.100.0
|
|
24
|
+
Requires-Dist: google-auth-httplib2>=0.2.0
|
|
25
|
+
Requires-Dist: google-auth-oauthlib>=1.0.0
|
|
26
|
+
Requires-Dist: mcp>=1.0.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# google-drive-files-mcp
|
|
33
|
+
|
|
34
|
+
<!-- mcp-name: io.github.zayansalman/google-drive-files-mcp -->
|
|
35
|
+
|
|
36
|
+
A focused [Model Context Protocol](https://modelcontextprotocol.io) server (and standalone CLI) for **Google Drive file management** (move, upload, organize) **and Google Sheets editing** (update cells, append rows, edit tabs, format). Fourteen granular, single-purpose tools — each one an explicit operation, with writes returning before/after so nothing is silently overwritten.
|
|
37
|
+
|
|
38
|
+
Built because the hosted Google Drive connectors can search, read, and **copy** files, but cannot **move** them (`copy` duplicates a file with a new ID rather than relocating it), cannot reliably **upload a real binary** (their inline-content API needs the bytes embedded as base64, which breaks for large/binary files), and cannot **edit spreadsheet contents**. This server adds a true move, a resumable upload from a local path, folder creation, and a full set of Google Sheets value/structure/format edits — for any MCP client (Claude Code, Claude Desktop, Cursor, Cline, etc.).
|
|
39
|
+
|
|
40
|
+
## Tools
|
|
41
|
+
|
|
42
|
+
### Drive — files & folders
|
|
43
|
+
- `drive_search(query, only_folders=False, max_results=20)` — find files/folders (and their IDs).
|
|
44
|
+
- `drive_create_folder(name, parent=None)` — create a folder (`parent` accepts an ID, URL, `root`, or an unambiguous folder name).
|
|
45
|
+
- `drive_move(file, dest_folder, keep_existing_parents=False)` — *true move* (removed from its current folder); `keep_existing_parents=True` adds without removing.
|
|
46
|
+
- `drive_upload_file(local_path, parent=None, name=None, mime_type=None)` — upload a local file (any type, incl. large binaries) via a **resumable media upload**. Stored as-is (no Google-format conversion).
|
|
47
|
+
|
|
48
|
+
### Google Sheets — read, edit, structure, format
|
|
49
|
+
- `sheets_get_info(spreadsheet)` — list tabs + sizes (call first to learn tab names).
|
|
50
|
+
- `sheets_read(spreadsheet, range_a1, render_option="FORMATTED_VALUE")` — read a range (`FORMULA`/`UNFORMATTED_VALUE` available).
|
|
51
|
+
- `sheets_write(spreadsheet, range_a1, values, value_input_option="USER_ENTERED")` — overwrite a range; returns `before`. `USER_ENTERED` parses numbers and makes `=SUM(..)` a formula; `RAW` writes literally.
|
|
52
|
+
- `sheets_append(spreadsheet, range_a1, values, ...)` — append rows after a table.
|
|
53
|
+
- `sheets_clear(spreadsheet, range_a1)` — clear values (keeps formatting); returns `before`.
|
|
54
|
+
- `sheets_batch_write(spreadsheet, updates, ...)` — write many ranges atomically.
|
|
55
|
+
- `sheets_add_tab` / `sheets_rename_tab` / `sheets_delete_tab` — manage tabs (delete is destructive).
|
|
56
|
+
- `sheets_format(spreadsheet, range_a1, number_format=None, bold=None, background=None)` — number pattern / bold / `#RRGGBB` background.
|
|
57
|
+
|
|
58
|
+
No rename/copy/trash/delete of *files* (copy already exists in the hosted Drive connector); destructive Sheets ops (`sheets_clear`, `sheets_delete_tab`) are their own explicit tools so you control exactly when they run.
|
|
59
|
+
|
|
60
|
+
## Scope warning (read this)
|
|
61
|
+
|
|
62
|
+
Moving an existing arbitrary file requires the **full `drive` scope** — read/write/delete on **all** your Drive files. There is no narrower scope that can change a file's parents (`drive.file` only covers files the app itself created). This tool is therefore far more powerful than a read-only connector. Treat its cached token like a password (stored `0600`), and prefer an isolated OAuth client/token rather than sharing one with read-only tools.
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install google-drive-files-mcp
|
|
68
|
+
# or: uv tool install google-drive-files-mcp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## One-time setup (~10 min)
|
|
72
|
+
|
|
73
|
+
1. [Google Cloud Console](https://console.cloud.google.com/) → create/pick a project.
|
|
74
|
+
2. Enable the **Drive API** ([library link](https://console.cloud.google.com/apis/library/drive.googleapis.com)) and, for the Sheets tools, the **Sheets API** ([library link](https://console.cloud.google.com/apis/library/sheets.googleapis.com)). (The full `drive` scope already authorizes the Sheets API — no extra consent, just enable the API.)
|
|
75
|
+
3. OAuth consent screen → **Internal** (Workspace; no verification needed even for the restricted full-drive scope) or **External** + add yourself as a test user (personal Gmail).
|
|
76
|
+
4. Credentials → **OAuth client ID** → **Desktop app** → download JSON.
|
|
77
|
+
5. `google-drive-files-mcp setup --import-credentials ~/Downloads/client_secret_*.json` → consent in the browser.
|
|
78
|
+
|
|
79
|
+
Verify:
|
|
80
|
+
```bash
|
|
81
|
+
google-drive-files-mcp status
|
|
82
|
+
google-drive-files-mcp search "Reports" --folders
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Claude Code
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
claude mcp add --scope user google-drive-files google-drive-files-mcp -- serve
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
> Find my "Q2 Report" doc and move it into the "2026 Reports" folder.
|
|
92
|
+
|
|
93
|
+
## Claude Desktop
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"mcpServers": {
|
|
98
|
+
"google-drive-files": { "command": "google-drive-files-mcp", "args": ["serve"] }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## CLI
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
google-drive-files-mcp search "budget" # find a file + its ID
|
|
107
|
+
google-drive-files-mcp search "Reports" --folders # find destination folders
|
|
108
|
+
google-drive-files-mcp mkdir "2026 Reports" # create a folder
|
|
109
|
+
google-drive-files-mcp move <file-url-or-id> "2026 Reports" # move into it (by folder name)
|
|
110
|
+
google-drive-files-mcp move <file-url-or-id> root # move back to My Drive root
|
|
111
|
+
google-drive-files-mcp upload ~/Downloads/report.zip --parent "2026 Reports" # upload a local file
|
|
112
|
+
|
|
113
|
+
# Google Sheets
|
|
114
|
+
google-drive-files-mcp sheet-info <sheet-url-or-id> # list tabs + sizes
|
|
115
|
+
google-drive-files-mcp sheet-read <sheet-url-or-id> "Sheet1!A1:D10" # read a range
|
|
116
|
+
google-drive-files-mcp sheet-write <sheet-url-or-id> "Sheet1!B2:B4" '[[100],[200],[300]]' # set numbers
|
|
117
|
+
google-drive-files-mcp sheet-format <sheet-url-or-id> "Sheet1!B2:B4" --number-format '#,##0.00' --bold
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`drive_move` returns the before/after parents so the move is auditable:
|
|
121
|
+
```json
|
|
122
|
+
{ "id": "1Abc…", "name": "Q2 Report", "moved_from": ["0OldFolderId"], "moved_to": "1NewFolderId", "parents_now": ["1NewFolderId"], "web_view_link": "https://docs.google.com/…" }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Configuration
|
|
126
|
+
|
|
127
|
+
| Variable | Default | What |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `GDRIVE_FILES_MCP_CREDENTIALS` | `~/.config/google-drive-files-mcp/credentials.json` | OAuth client secret |
|
|
130
|
+
| `GDRIVE_FILES_MCP_TOKEN` | `~/.config/google-drive-files-mcp/token.json` | Cached refresh token |
|
|
131
|
+
| `GDRIVE_FILES_MCP_SCOPES` | `https://www.googleapis.com/auth/drive` | OAuth scopes (comma-separated) |
|
|
132
|
+
|
|
133
|
+
## Safety notes
|
|
134
|
+
|
|
135
|
+
- **True move removes the file from its current folder.** Drive items can have multiple parents; `keep_existing_parents=True` adds without removing.
|
|
136
|
+
- **Folder-name destinations must be unambiguous.** If two folders share a name, `drive_move` refuses and lists the candidates so you can pass an explicit ID.
|
|
137
|
+
- **The before/after parents are returned** on every move so an agent (or you) can verify the result.
|
|
138
|
+
|
|
139
|
+
## Troubleshooting
|
|
140
|
+
|
|
141
|
+
**`HttpError 403: Google Drive API has not been used in project … or it is disabled`**
|
|
142
|
+
Enable the Drive API on the project that owns your OAuth client: [console.cloud.google.com/apis/library/drive.googleapis.com](https://console.cloud.google.com/apis/library/drive.googleapis.com) → **Enable**, then wait ~1 min and retry. (This is the single most common first-run error.)
|
|
143
|
+
|
|
144
|
+
**`multiple folders named '…'; pass the folder ID/URL instead`**
|
|
145
|
+
Two or more of your folders share that name, so a name is ambiguous. Run `google-drive-files-mcp search "<name>" --folders` to get the IDs and pass the exact one.
|
|
146
|
+
|
|
147
|
+
**`no folder named '…' found`**
|
|
148
|
+
Create it first (`google-drive-files-mcp mkdir "<name>"`) or pass a folder ID/URL/`root`.
|
|
149
|
+
|
|
150
|
+
**`HttpError 403: insufficientFilePermissions` when moving**
|
|
151
|
+
Either the token lacks the full `drive` scope — re-authorize with `google-drive-files-mcp setup --reauth` — or you don't have edit rights on the file/destination (you can't move a file someone else owns unless they've granted you edit access).
|
|
152
|
+
|
|
153
|
+
**`No valid Google token` from Claude Desktop / cron**
|
|
154
|
+
The first consent needs a browser. Run `google-drive-files-mcp setup` once in a terminal; later headless runs reuse and auto-refresh the cached token.
|
|
155
|
+
|
|
156
|
+
**A move "removed" a file from a shared folder unexpectedly**
|
|
157
|
+
A true move removes the item from its current parent(s). If you meant to *also* keep it where it was, use `keep_existing_parents=true` (CLI `--keep`).
|
|
158
|
+
|
|
159
|
+
## Use it from other clients (Cursor, Cline, Continue, …)
|
|
160
|
+
|
|
161
|
+
Any stdio MCP client works — see [docs/other-clients.md](docs/other-clients.md).
|
|
162
|
+
|
|
163
|
+
## Authentication — bring your own Google OAuth client
|
|
164
|
+
|
|
165
|
+
There are **no API keys and no shipped secrets**. The server authenticates to *your* Google account with an OAuth client *you* create, and caches a refresh token locally. The author has zero access to your data.
|
|
166
|
+
|
|
167
|
+
- **Why your own client?** Google's restricted scopes (here, the full `drive` scope) can't be redistributed in a shared app, and an unverified shared app is capped at 100 users. "Bring your own OAuth client" is the standard pattern for personal-data MCP servers.
|
|
168
|
+
- **What you need:** a free Google Cloud project, the Drive API enabled, an OAuth consent screen, and a Desktop OAuth client. Full walkthrough → [docs/setup-google-oauth.md](docs/setup-google-oauth.md).
|
|
169
|
+
- **Where your token lives:** `~/.config/google-drive-files-mcp/token.json` (mode `0600`). Delete it to revoke locally; revoke fully at [myaccount.google.com/permissions](https://myaccount.google.com/permissions).
|
|
170
|
+
- **No hosted/SaaS option** — everything runs locally; your Drive data never touches a third-party server.
|
|
171
|
+
|
|
172
|
+
## More guides
|
|
173
|
+
|
|
174
|
+
- [docs/setup-google-oauth.md](docs/setup-google-oauth.md) — full OAuth walkthrough (full-`drive` scope) + common errors
|
|
175
|
+
- [docs/claude-code.md](docs/claude-code.md) · [docs/claude-desktop.md](docs/claude-desktop.md) · [docs/other-clients.md](docs/other-clients.md)
|
|
176
|
+
- [examples/](examples/) — e.g. file a document into a `YYYY-MM` folder
|
|
177
|
+
|
|
178
|
+
## Related tools
|
|
179
|
+
|
|
180
|
+
Part of a small family of focused, local MCP servers for Google Workspace data the hosted connectors don't expose:
|
|
181
|
+
|
|
182
|
+
- **[gmail-attachments-mcp](https://github.com/zayansalman/gmail-attachments-mcp)** — download Gmail attachment bytes to disk
|
|
183
|
+
- **[google-drive-comments-mcp](https://github.com/zayansalman/google-drive-comments-mcp)** — read comment threads on Docs/Sheets/Slides
|
|
184
|
+
- **google-drive-files-mcp** — move/organize Drive files *(this repo)*
|
|
185
|
+
|
|
186
|
+
They can share one OAuth login or stay isolated — see each repo's setup.
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# google-drive-files-mcp
|
|
2
|
+
|
|
3
|
+
<!-- mcp-name: io.github.zayansalman/google-drive-files-mcp -->
|
|
4
|
+
|
|
5
|
+
A focused [Model Context Protocol](https://modelcontextprotocol.io) server (and standalone CLI) for **Google Drive file management** (move, upload, organize) **and Google Sheets editing** (update cells, append rows, edit tabs, format). Fourteen granular, single-purpose tools — each one an explicit operation, with writes returning before/after so nothing is silently overwritten.
|
|
6
|
+
|
|
7
|
+
Built because the hosted Google Drive connectors can search, read, and **copy** files, but cannot **move** them (`copy` duplicates a file with a new ID rather than relocating it), cannot reliably **upload a real binary** (their inline-content API needs the bytes embedded as base64, which breaks for large/binary files), and cannot **edit spreadsheet contents**. This server adds a true move, a resumable upload from a local path, folder creation, and a full set of Google Sheets value/structure/format edits — for any MCP client (Claude Code, Claude Desktop, Cursor, Cline, etc.).
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
### Drive — files & folders
|
|
12
|
+
- `drive_search(query, only_folders=False, max_results=20)` — find files/folders (and their IDs).
|
|
13
|
+
- `drive_create_folder(name, parent=None)` — create a folder (`parent` accepts an ID, URL, `root`, or an unambiguous folder name).
|
|
14
|
+
- `drive_move(file, dest_folder, keep_existing_parents=False)` — *true move* (removed from its current folder); `keep_existing_parents=True` adds without removing.
|
|
15
|
+
- `drive_upload_file(local_path, parent=None, name=None, mime_type=None)` — upload a local file (any type, incl. large binaries) via a **resumable media upload**. Stored as-is (no Google-format conversion).
|
|
16
|
+
|
|
17
|
+
### Google Sheets — read, edit, structure, format
|
|
18
|
+
- `sheets_get_info(spreadsheet)` — list tabs + sizes (call first to learn tab names).
|
|
19
|
+
- `sheets_read(spreadsheet, range_a1, render_option="FORMATTED_VALUE")` — read a range (`FORMULA`/`UNFORMATTED_VALUE` available).
|
|
20
|
+
- `sheets_write(spreadsheet, range_a1, values, value_input_option="USER_ENTERED")` — overwrite a range; returns `before`. `USER_ENTERED` parses numbers and makes `=SUM(..)` a formula; `RAW` writes literally.
|
|
21
|
+
- `sheets_append(spreadsheet, range_a1, values, ...)` — append rows after a table.
|
|
22
|
+
- `sheets_clear(spreadsheet, range_a1)` — clear values (keeps formatting); returns `before`.
|
|
23
|
+
- `sheets_batch_write(spreadsheet, updates, ...)` — write many ranges atomically.
|
|
24
|
+
- `sheets_add_tab` / `sheets_rename_tab` / `sheets_delete_tab` — manage tabs (delete is destructive).
|
|
25
|
+
- `sheets_format(spreadsheet, range_a1, number_format=None, bold=None, background=None)` — number pattern / bold / `#RRGGBB` background.
|
|
26
|
+
|
|
27
|
+
No rename/copy/trash/delete of *files* (copy already exists in the hosted Drive connector); destructive Sheets ops (`sheets_clear`, `sheets_delete_tab`) are their own explicit tools so you control exactly when they run.
|
|
28
|
+
|
|
29
|
+
## Scope warning (read this)
|
|
30
|
+
|
|
31
|
+
Moving an existing arbitrary file requires the **full `drive` scope** — read/write/delete on **all** your Drive files. There is no narrower scope that can change a file's parents (`drive.file` only covers files the app itself created). This tool is therefore far more powerful than a read-only connector. Treat its cached token like a password (stored `0600`), and prefer an isolated OAuth client/token rather than sharing one with read-only tools.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install google-drive-files-mcp
|
|
37
|
+
# or: uv tool install google-drive-files-mcp
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## One-time setup (~10 min)
|
|
41
|
+
|
|
42
|
+
1. [Google Cloud Console](https://console.cloud.google.com/) → create/pick a project.
|
|
43
|
+
2. Enable the **Drive API** ([library link](https://console.cloud.google.com/apis/library/drive.googleapis.com)) and, for the Sheets tools, the **Sheets API** ([library link](https://console.cloud.google.com/apis/library/sheets.googleapis.com)). (The full `drive` scope already authorizes the Sheets API — no extra consent, just enable the API.)
|
|
44
|
+
3. OAuth consent screen → **Internal** (Workspace; no verification needed even for the restricted full-drive scope) or **External** + add yourself as a test user (personal Gmail).
|
|
45
|
+
4. Credentials → **OAuth client ID** → **Desktop app** → download JSON.
|
|
46
|
+
5. `google-drive-files-mcp setup --import-credentials ~/Downloads/client_secret_*.json` → consent in the browser.
|
|
47
|
+
|
|
48
|
+
Verify:
|
|
49
|
+
```bash
|
|
50
|
+
google-drive-files-mcp status
|
|
51
|
+
google-drive-files-mcp search "Reports" --folders
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Claude Code
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
claude mcp add --scope user google-drive-files google-drive-files-mcp -- serve
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
> Find my "Q2 Report" doc and move it into the "2026 Reports" folder.
|
|
61
|
+
|
|
62
|
+
## Claude Desktop
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"google-drive-files": { "command": "google-drive-files-mcp", "args": ["serve"] }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## CLI
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
google-drive-files-mcp search "budget" # find a file + its ID
|
|
76
|
+
google-drive-files-mcp search "Reports" --folders # find destination folders
|
|
77
|
+
google-drive-files-mcp mkdir "2026 Reports" # create a folder
|
|
78
|
+
google-drive-files-mcp move <file-url-or-id> "2026 Reports" # move into it (by folder name)
|
|
79
|
+
google-drive-files-mcp move <file-url-or-id> root # move back to My Drive root
|
|
80
|
+
google-drive-files-mcp upload ~/Downloads/report.zip --parent "2026 Reports" # upload a local file
|
|
81
|
+
|
|
82
|
+
# Google Sheets
|
|
83
|
+
google-drive-files-mcp sheet-info <sheet-url-or-id> # list tabs + sizes
|
|
84
|
+
google-drive-files-mcp sheet-read <sheet-url-or-id> "Sheet1!A1:D10" # read a range
|
|
85
|
+
google-drive-files-mcp sheet-write <sheet-url-or-id> "Sheet1!B2:B4" '[[100],[200],[300]]' # set numbers
|
|
86
|
+
google-drive-files-mcp sheet-format <sheet-url-or-id> "Sheet1!B2:B4" --number-format '#,##0.00' --bold
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`drive_move` returns the before/after parents so the move is auditable:
|
|
90
|
+
```json
|
|
91
|
+
{ "id": "1Abc…", "name": "Q2 Report", "moved_from": ["0OldFolderId"], "moved_to": "1NewFolderId", "parents_now": ["1NewFolderId"], "web_view_link": "https://docs.google.com/…" }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Configuration
|
|
95
|
+
|
|
96
|
+
| Variable | Default | What |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `GDRIVE_FILES_MCP_CREDENTIALS` | `~/.config/google-drive-files-mcp/credentials.json` | OAuth client secret |
|
|
99
|
+
| `GDRIVE_FILES_MCP_TOKEN` | `~/.config/google-drive-files-mcp/token.json` | Cached refresh token |
|
|
100
|
+
| `GDRIVE_FILES_MCP_SCOPES` | `https://www.googleapis.com/auth/drive` | OAuth scopes (comma-separated) |
|
|
101
|
+
|
|
102
|
+
## Safety notes
|
|
103
|
+
|
|
104
|
+
- **True move removes the file from its current folder.** Drive items can have multiple parents; `keep_existing_parents=True` adds without removing.
|
|
105
|
+
- **Folder-name destinations must be unambiguous.** If two folders share a name, `drive_move` refuses and lists the candidates so you can pass an explicit ID.
|
|
106
|
+
- **The before/after parents are returned** on every move so an agent (or you) can verify the result.
|
|
107
|
+
|
|
108
|
+
## Troubleshooting
|
|
109
|
+
|
|
110
|
+
**`HttpError 403: Google Drive API has not been used in project … or it is disabled`**
|
|
111
|
+
Enable the Drive API on the project that owns your OAuth client: [console.cloud.google.com/apis/library/drive.googleapis.com](https://console.cloud.google.com/apis/library/drive.googleapis.com) → **Enable**, then wait ~1 min and retry. (This is the single most common first-run error.)
|
|
112
|
+
|
|
113
|
+
**`multiple folders named '…'; pass the folder ID/URL instead`**
|
|
114
|
+
Two or more of your folders share that name, so a name is ambiguous. Run `google-drive-files-mcp search "<name>" --folders` to get the IDs and pass the exact one.
|
|
115
|
+
|
|
116
|
+
**`no folder named '…' found`**
|
|
117
|
+
Create it first (`google-drive-files-mcp mkdir "<name>"`) or pass a folder ID/URL/`root`.
|
|
118
|
+
|
|
119
|
+
**`HttpError 403: insufficientFilePermissions` when moving**
|
|
120
|
+
Either the token lacks the full `drive` scope — re-authorize with `google-drive-files-mcp setup --reauth` — or you don't have edit rights on the file/destination (you can't move a file someone else owns unless they've granted you edit access).
|
|
121
|
+
|
|
122
|
+
**`No valid Google token` from Claude Desktop / cron**
|
|
123
|
+
The first consent needs a browser. Run `google-drive-files-mcp setup` once in a terminal; later headless runs reuse and auto-refresh the cached token.
|
|
124
|
+
|
|
125
|
+
**A move "removed" a file from a shared folder unexpectedly**
|
|
126
|
+
A true move removes the item from its current parent(s). If you meant to *also* keep it where it was, use `keep_existing_parents=true` (CLI `--keep`).
|
|
127
|
+
|
|
128
|
+
## Use it from other clients (Cursor, Cline, Continue, …)
|
|
129
|
+
|
|
130
|
+
Any stdio MCP client works — see [docs/other-clients.md](docs/other-clients.md).
|
|
131
|
+
|
|
132
|
+
## Authentication — bring your own Google OAuth client
|
|
133
|
+
|
|
134
|
+
There are **no API keys and no shipped secrets**. The server authenticates to *your* Google account with an OAuth client *you* create, and caches a refresh token locally. The author has zero access to your data.
|
|
135
|
+
|
|
136
|
+
- **Why your own client?** Google's restricted scopes (here, the full `drive` scope) can't be redistributed in a shared app, and an unverified shared app is capped at 100 users. "Bring your own OAuth client" is the standard pattern for personal-data MCP servers.
|
|
137
|
+
- **What you need:** a free Google Cloud project, the Drive API enabled, an OAuth consent screen, and a Desktop OAuth client. Full walkthrough → [docs/setup-google-oauth.md](docs/setup-google-oauth.md).
|
|
138
|
+
- **Where your token lives:** `~/.config/google-drive-files-mcp/token.json` (mode `0600`). Delete it to revoke locally; revoke fully at [myaccount.google.com/permissions](https://myaccount.google.com/permissions).
|
|
139
|
+
- **No hosted/SaaS option** — everything runs locally; your Drive data never touches a third-party server.
|
|
140
|
+
|
|
141
|
+
## More guides
|
|
142
|
+
|
|
143
|
+
- [docs/setup-google-oauth.md](docs/setup-google-oauth.md) — full OAuth walkthrough (full-`drive` scope) + common errors
|
|
144
|
+
- [docs/claude-code.md](docs/claude-code.md) · [docs/claude-desktop.md](docs/claude-desktop.md) · [docs/other-clients.md](docs/other-clients.md)
|
|
145
|
+
- [examples/](examples/) — e.g. file a document into a `YYYY-MM` folder
|
|
146
|
+
|
|
147
|
+
## Related tools
|
|
148
|
+
|
|
149
|
+
Part of a small family of focused, local MCP servers for Google Workspace data the hosted connectors don't expose:
|
|
150
|
+
|
|
151
|
+
- **[gmail-attachments-mcp](https://github.com/zayansalman/gmail-attachments-mcp)** — download Gmail attachment bytes to disk
|
|
152
|
+
- **[google-drive-comments-mcp](https://github.com/zayansalman/google-drive-comments-mcp)** — read comment threads on Docs/Sheets/Slides
|
|
153
|
+
- **google-drive-files-mcp** — move/organize Drive files *(this repo)*
|
|
154
|
+
|
|
155
|
+
They can share one OAuth login or stay isolated — see each repo's setup.
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Claude Code integration
|
|
2
|
+
|
|
3
|
+
After `pip install google-drive-files-mcp` and `google-drive-files-mcp setup`:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
claude mcp add --scope user google-drive-files google-drive-files-mcp -- serve
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Verify:
|
|
10
|
+
```bash
|
|
11
|
+
claude mcp list | grep google-drive-files
|
|
12
|
+
# google-drive-files: google-drive-files-mcp serve - ✓ Connected
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
In a session:
|
|
16
|
+
> Find my "Q2 Report" doc and move it into the "2026 Reports" folder.
|
|
17
|
+
|
|
18
|
+
Because this tool can move/relocate files, prefer to let it **search first and show you the matches** before moving — `drive_move` returns the before/after parents so every move is auditable.
|
|
19
|
+
|
|
20
|
+
## Remove
|
|
21
|
+
```bash
|
|
22
|
+
claude mcp remove google-drive-files
|
|
23
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Claude Desktop integration
|
|
2
|
+
|
|
3
|
+
After installing and running `google-drive-files-mcp setup`, edit Claude Desktop's MCP config:
|
|
4
|
+
|
|
5
|
+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
6
|
+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
7
|
+
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"mcpServers": {
|
|
12
|
+
"google-drive-files": {
|
|
13
|
+
"command": "google-drive-files-mcp",
|
|
14
|
+
"args": ["serve"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If the command isn't on Claude Desktop's `$PATH`, use the absolute path from `which google-drive-files-mcp`.
|
|
21
|
+
|
|
22
|
+
## Newer Claude Desktop / Cowork builds
|
|
23
|
+
Some builds read MCP config from the unified `~/.claude.json` (shared with Claude Code) rather than `claude_desktop_config.json`. If editing the JSON has no effect after a restart, register via the CLI and relaunch:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
claude mcp add --scope user google-drive-files google-drive-files-mcp -- serve
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Verify
|
|
30
|
+
> Create a folder called "Archive 2026" and move my oldest "Draft" doc into it.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Other MCP clients
|
|
2
|
+
|
|
3
|
+
Any stdio-transport MCP client can use `google-drive-files-mcp`:
|
|
4
|
+
|
|
5
|
+
- **Transport**: stdio
|
|
6
|
+
- **Command**: `google-drive-files-mcp` (or the absolute path)
|
|
7
|
+
- **Args**: `["serve"]`
|
|
8
|
+
- **Env** (optional): `GDRIVE_FILES_MCP_CREDENTIALS`, `GDRIVE_FILES_MCP_TOKEN`, `GDRIVE_FILES_MCP_SCOPES`
|
|
9
|
+
|
|
10
|
+
## Cursor — `~/.cursor/mcp.json`
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"google-drive-files": { "command": "google-drive-files-mcp", "args": ["serve"] }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Cline (VS Code)
|
|
20
|
+
MCP settings → **Add MCP server** → stdio. Command `google-drive-files-mcp`, args `serve`.
|
|
21
|
+
|
|
22
|
+
## Continue.dev — `~/.continue/config.yaml`
|
|
23
|
+
```yaml
|
|
24
|
+
mcpServers:
|
|
25
|
+
- name: google-drive-files
|
|
26
|
+
command: google-drive-files-mcp
|
|
27
|
+
args:
|
|
28
|
+
- serve
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## One-time auth per machine
|
|
32
|
+
Once `google-drive-files-mcp setup` has cached a token at `~/.config/google-drive-files-mcp/token.json`, every client on that machine/user reuses it — no per-client re-authentication.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Google OAuth setup (~10 minutes)
|
|
2
|
+
|
|
3
|
+
This server **moves and organizes files in your Drive**, so it needs the **full `drive` scope** (read + write + delete on all your Drive files). There is no narrower scope that can change a file's parents. Treat the cached token like a password.
|
|
4
|
+
|
|
5
|
+
You create your own OAuth client; nothing is shipped with the package, and nothing leaves your machine.
|
|
6
|
+
|
|
7
|
+
## Step 1 — Project
|
|
8
|
+
1. [Google Cloud Console](https://console.cloud.google.com/) → sign in → project dropdown → **New Project** → name it → **Create** → switch to it.
|
|
9
|
+
|
|
10
|
+
## Step 2 — Enable the Drive API
|
|
11
|
+
[console.cloud.google.com/apis/library/drive.googleapis.com](https://console.cloud.google.com/apis/library/drive.googleapis.com) → **Enable**.
|
|
12
|
+
|
|
13
|
+
## Step 3 — OAuth consent screen
|
|
14
|
+
Left nav → **APIs & Services** → **OAuth consent screen**.
|
|
15
|
+
|
|
16
|
+
| You have | Pick | Why |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Google Workspace | **Internal** | No verification needed *even for the restricted full-`drive` scope* |
|
|
19
|
+
| Personal Gmail | **External** | Add your address under **Test users** or consent is refused |
|
|
20
|
+
|
|
21
|
+
Fill App name / support email / developer contact. Leave the Scopes page empty. Save.
|
|
22
|
+
|
|
23
|
+
## Step 4 — OAuth client
|
|
24
|
+
**Credentials → + Create Credentials → OAuth client ID → Desktop app → Create → Download JSON.**
|
|
25
|
+
|
|
26
|
+
## Step 5 — Hand it to the CLI
|
|
27
|
+
```bash
|
|
28
|
+
google-drive-files-mcp setup --import-credentials ~/Downloads/client_secret_*.json
|
|
29
|
+
```
|
|
30
|
+
A browser opens; the consent screen will say **"See, edit, create, and delete all of your Google Drive files"** — that breadth is required to move arbitrary files. The refresh token is cached at `~/.config/google-drive-files-mcp/token.json` (mode `0600`).
|
|
31
|
+
|
|
32
|
+
Verify:
|
|
33
|
+
```bash
|
|
34
|
+
google-drive-files-mcp status
|
|
35
|
+
google-drive-files-mcp search "Reports" --folders
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Common errors
|
|
39
|
+
|
|
40
|
+
**`HttpError 403: Google Drive API has not been used in project … or it is disabled`**
|
|
41
|
+
Enable the Drive API (Step 2) on the project that owns your OAuth client, then wait ~1 min and retry.
|
|
42
|
+
|
|
43
|
+
**`Access blocked: This app's request is invalid`**
|
|
44
|
+
Personal Gmail without your address under **Test users**. Add it on the consent screen.
|
|
45
|
+
|
|
46
|
+
**`This app isn't verified`**
|
|
47
|
+
Expected for an unverified personal app. **Advanced → Go to <app> (unsafe)**. Verification only matters for public distribution.
|
|
48
|
+
|
|
49
|
+
**`HttpError 403: insufficientFilePermissions` / `insufficientPermissions` when moving**
|
|
50
|
+
The token was authorized with a narrower scope. Re-authorize with the full scope:
|
|
51
|
+
```bash
|
|
52
|
+
google-drive-files-mcp setup --reauth
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Browser opened but never returned**
|
|
56
|
+
Firewall blocking the localhost redirect. Re-run `setup` (new port each time) or allow the connection.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Move a file into a YYYY-MM folder (created if missing) under a parent folder.
|
|
3
|
+
# Handy for filing exports/reports into month buckets.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./file-into-dated-folder.sh <file-url-or-id> [parent-folder-name-or-id]
|
|
7
|
+
#
|
|
8
|
+
# Assumes google-drive-files-mcp is installed and `setup` has been run once.
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
FILE="${1:?usage: file-into-dated-folder.sh <file-url-or-id> [parent]}"
|
|
13
|
+
PARENT="${2:-root}"
|
|
14
|
+
MONTH="$(date +%Y-%m)"
|
|
15
|
+
|
|
16
|
+
# Create (or reuse) the YYYY-MM folder under PARENT, capture its id.
|
|
17
|
+
FOLDER_ID="$(
|
|
18
|
+
google-drive-files-mcp mkdir "${MONTH}" --parent "${PARENT}" \
|
|
19
|
+
| python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])'
|
|
20
|
+
)"
|
|
21
|
+
|
|
22
|
+
google-drive-files-mcp move "${FILE}" "${FOLDER_ID}"
|
|
23
|
+
echo "filed ${FILE} into ${MONTH} (folder ${FOLDER_ID})"
|