Skip to main content
Glama
anaborne
by anaborne

gdrive-write-mcp

AIアシスタントにGoogle Driveへの実際の書き込みアクセスを提供するMCPサーバーです。ファイルのID、共有設定、コメント、リビジョン履歴を保持したまま、その場でのコンテンツ更新、追記、検索・置換編集ができます。

CI License: MIT


問題

AIアシスタント向けのGoogle Drive統合のほとんどは、読み取りと作成のみです。ファイルの検索、読み取り、新規作成、古いファイルのゴミ箱への移動はできますが、既存ファイルのコンテンツを変更する手段がありません。

これは小さなギャップのように思えますが、実際はそうではありません。その場での書き込みができないと、「このドキュメントを編集して」という指示は次のようになります。

  1. ファイルを読み取る。

  2. 修正したコンテンツで新しいファイルを作成する。

  3. 古いファイルをゴミ箱に移動する。

結果としてテキストは正しく入っていますが、それ以外のすべてが間違っています。

実際の編集後

作成とゴミ箱移動の後

ファイルID

変更なし

新しい — 既存のリンク、ブックマーク、API参照はすべてゴミ箱に入ったファイルを指すようになる

リビジョン履歴

1つ増える

消える — 「以前のバージョンに戻す」ができない

コメント

保持される

消える

共有設定

保持される

リセットされる — 共同作業者が静かにアクセスを失う

ゴミ箱

触れない

孤立した類似ファイルで埋まる

gdrive-write-mcp はそのギャップを埋めます。GoogleのDrive APIは常にその場でのコンテンツ更新をサポートしてきました。これは、それらをMCP経由で公開する、小さく焦点を絞ったサーバーです。


Related MCP server: Google Docs MCP Server

機能

編集

  • replace_in_file — 完全一致の検索と置換。デフォルトで使うべきツールです。ドキュメント全体を再送信する必要がなく、言及されなかったコンテンツを誤って削除することもありません。

  • append_to_file / prepend_to_file — 既存の内容を再送信せずに、どちらかの端に追加します。ログ、日誌、変更履歴に最適です。

  • update_file_content — ドキュメント全体を置き換えます。本質的に破壊的であるため、モデルにはデフォルトではなく最後の手段として文書化されています。

読み取り

  • read_file — コンテンツに加えて、次の書き込みを安全にするためのrevisionTokenを返します。

  • get_file_metadata — ダウンロードせずにファイルが移動したかどうかを確認します。

  • search_files — Driveのクエリ構文を使用して、ファイル名を書き込みツールが必要とするIDに変換します。

  • list_revisions — その場での編集が保持する履歴です。

作成

  • create_file — 本当に新しいドキュメント用で、ネイティブのGoogle DocやSheetへの変換オプション付きです。


正しく動作する2つの点

1. 同時編集は黙って上書きされず、拒否される

素朴な書き込みツールの失敗モードは、静かで高くつきます。ドキュメントを読み、30秒考えて書き戻すと、その間に同僚が追加した段落を上書きしてしまいます。誰もエラーを受け取りません。段落が失われていることに気づくのは、数日後です。

ここでのすべての読み取りはrevisionTokenを返し、すべての書き込みはそれを受け取ります。

read_file(fileId)                    → revisionToken: "0B1a2…"
update_file_content(fileId, content, expectedRevisionToken: "0B1a2…")

ファイルが変更されている場合、書き込みはエラーで拒否され、モデルに何をすべきか(再読み取り、再適用、再書き込み)を正確に伝えます。単なる409ではありません。対象を絞ったツール(replace_in_file、append_to_file、prepend_to_file)は単一の呼び出し内で読み取りと書き込みを行うため、ガードを自動的に備えており、トークンを自分で扱う必要はありません。

Driveは実際のバイナリコンテンツを持つファイルに対してのみheadRevisionIdを公開します。GoogleネイティブのDocsやSheetsにはそれがないため、人間による同時編集が最も起こりやすい場所(ブラウザのタブで誰かが開いているファイル)では、トークンはmodifiedTimeにフォールバックし、ネイティブファイルも保護されます。

2. ネイティブのGoogleファイルは正直に扱われる

Driveには2つの非常に異なる種類のものが保存されており、それらを混同することがDrive統合におけるバグの最も一般的な原因です。

  • アップロードされたファイル(text/markdown、application/pdf、…)— バイト入力、バイト出力。

  • ネイティブエディタファイル(application/vnd.google-apps.document、…)— 独自のバイトを持ちません。読み取りは具体的な形式へのエクスポートによって行われ、書き込みはDriveが取り込み時に変換する形式をアップロードすることで行われます。

このサーバーはどちらかを検出し、それに応じてルーティングします。Docsはプレーンテキストではなくマークダウンにエクスポートされるのは、読み取り・変更・書き込みの往復で見出し、リスト、強調が保持され、ドキュメントが静かに平坦化されないようにするためです。バイナリファイルはUTF-8としてデコードされるのではなくbase64エンコードされるため、PDFがテキストツールを通過することで破損することはありません。


インストール

git clone https://github.com/anaborne/gdrive-write-mcp.git
cd gdrive-write-mcp
npm install
npm run build

Node 18以降が必要です。


セットアップ

ステップ1 — Google OAuthクライアントを作成する

  1. Google Cloud Consoleを開き、プロジェクトを作成します(または既存のものを選択します)。

  2. Google Drive APIを有効にします:APIとサービス → ライブラリ → Google Drive API → 有効にする。

  3. OAuth同意画面を構成します:APIとサービス → OAuth同意画面。外部を選択し、必須フィールドを入力し、テストユーザーに自分のGoogleアカウントを追加します。(アプリが「テスト中」の間は、リストされたテストユーザーのみが承認できます。これは個人用ツールに望ましい動作です。)

  4. 認証情報を作成します:APIとサービス → 認証情報 → 認証情報を作成 → OAuthクライアントID → デスクトップアプリ。

  5. クライアントIDとクライアントシークレットをコピーします。

ステップ2 — リフレッシュトークンを取得する

cp .env.example .env
# put GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in .env
npm run authorize

これにより、http://localhost:4181で1回限りの同意フローが開き、リフレッシュトークンが出力されます。それを.envに追加します:

GOOGLE_CLIENT_ID=1234567890-abcdef.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-…
GOOGLE_REFRESH_TOKEN=1//0g…

ステップ3 — MCPクライアントをサーバーに向ける

Claude Desktop — claude_desktop_config.json:

{
  "mcpServers": {
    "gdrive-write": {
      "command": "node",
      "args": ["/absolute/path/to/gdrive-write-mcp/dist/index.js"],
      "env": {
        "GOOGLE_CLIENT_ID": "…",
        "GOOGLE_CLIENT_SECRET": "…",
        "GOOGLE_REFRESH_TOKEN": "…"
      }
    }
  }
}

Claude Code:

claude mcp add gdrive-write \
  --env GOOGLE_CLIENT_ID=… \
  --env GOOGLE_CLIENT_SECRET=… \
  --env GOOGLE_REFRESH_TOKEN=… \
  -- node /absolute/path/to/gdrive-write-mcp/dist/index.js

その他 — サーバーはstdioでMCPを話します。これらの3つの環境変数を設定して、node dist/index.jsをサブプロセスとして起動します。

ステップ4 — 動作を確認する

npm run verify

これはDriveに対して実際のエンドツーエンドチェックを実行します。MCPクライアントと同じ方法でサーバーを起動し、公式のMCPクライアントでstdio経由で駆動し、このプロジェクトが主張する動作を検証します。古い書き込みが拒否されること、拒否された書き込みがファイルを変更しないこと、すべての編集後にファイルIDが変わらないこと、ネイティブのGoogle Docが読み取り・編集・読み取りの往復をDocとして生き残ることなどが含まれます。

Driveに2つの一時ファイルを作成し、終了時に(途中で失敗した場合も含めて)ゴミ箱に移動します。緑のサマリー行が表示されるはずです:

✓ ALL 41 CHECKS PASSED — the server works against live Drive.

何かが失敗した場合、出力は特定のチェックを指名し、返ってきた内容を示します。競合チェックとネイティブDocチェックには、特定の失敗が何を意味するかを説明する追加の診断情報が含まれています。たとえば、バックスラッシュでエスケープされた#は、コンテンツがマークダウンではなくプレーンテキストとしてインポートされたことを意味します。

これは形式的なものではありません。ユニットスイートは49テストでグリーンで、CIもパスしましたが、実際の欠陥がコードに存在していました。マークダウンからネイティブDocを作成すると、文字通り# Headingという文字を含むDocが静かに生成されていました。ライブ実行だけがそれを捕捉しました。モックが実装と同じ誤った仮定をエンコードしていたからです。drive.tsまたはmime.tsを変更した後は、これを実行してください。


ツールリファレンス

read_file

パラメータ

型

必須

説明

fileId

string

はい

DriveファイルID — URLの/d/の後の長い文字列。ファイル名ではありません

コンテンツに加えてrevisionToken、mimeType、modifiedTimeを返します。ネイティブファイルはエクスポートされます(Docs → マークダウン、Sheets → CSV、Slides → プレーンテキスト)。バイナリファイルはbase64エンコードされて返されます。

replace_in_file

パラメータ

型

必須

説明

fileId

string

はい

DriveファイルID

oldString

string

はい

検索する正確なテキスト。空白と改行を含みます

newString

string

はい

置換テキスト。空文字列は削除を意味します

replaceAll

boolean

いいえ

すべての出現箇所を置換(デフォルト false)

マッチングはリテラルであり、正規表現ではありません。検索テキスト内の.や$1は、それらの文字そのものを意味します。oldStringが複数回出現し、replaceAllがfalseの場合、呼び出しは推測せずに失敗します。なぜなら、間違った出現箇所を静かに編集するのは誰も気づかない種類のバグだからです。

append_to_file / prepend_to_file

パラメータ

型

必須

説明

fileId

string

はい

DriveファイルID

text

string

はい

追加するテキスト

separator

string

いいえ

明示的な区切り文字(デフォルト:改行。必要な場合のみ)

繰り返しの追記は均等に区切られ、連続した行や空白行の広がりが生じません。

update_file_content

パラメータ

型

必須

説明

fileId

string

はい

DriveファイルID

content

string

はい

完全な新しいコンテンツ

expectedRevisionToken

string

いいえ

最後の読み取りからのもの — 強く推奨します

すべてを置き換えます。expectedRevisionTokenがないと、最後にファイルを読んだ後に加えられた変更を上書きします。

create_file

パラメータ

型

必須

説明

name

string

はい

拡張子を含むファイル名

content

string

はい

初期コンテンツ

parentId

string

いいえ

フォルダID(デフォルトはマイドライブのルート)

mimeType

string

いいえ

省略時はファイル名から推測されます

convertTo

string

いいえ

例:application/vnd.google-apps.document — マークダウンを実際のDocとしてアップロード

search_files

パラメータ

型

必須

説明

query

string

はい

Driveクエリ構文

pageSize

number

いいえ

最大結果数、1〜100(デフォルト20)

name contains 'budget'
fullText contains 'quarterly review'
'FOLDER_ID' in parents
mimeType = 'application/vnd.google-apps.document'

get_file_metadata / list_revisions

どちらもfileIdを受け取ります。list_revisionsはオプションのpageSizeも受け取ります。


セキュリティ

フルDriveスコープを使用する理由。 このサーバーはデフォルトで https://www.googleapis.com/auth/drive を要求します。より狭い drive.file スコープはアプリ自身が作成したファイルへのアクセスのみを許可するため、既存のドキュメントを編集することを目的とするツールでは機能しません。これは実際のトレードオフであり、埋もれさせるのではなく明確に述べられています。トークンは、承認されたアカウントのDrive内のすべてを読み書きできます。

ワークフローがアシスタント自身が作成したファイルのみに触れる場合は、代わりに狭いスコープを要求してください。承認ステップとサーバーの両方で:

GOOGLE_OAUTH_SCOPE=drive.file

両者は一致している必要があります。リフレッシュトークンは付与されたスコープを保持するため、一方でトークンを発行して他方でサーバーを実行すると、呼び出し時に混乱を招く 403 が発生します。サーバーは起動時にファイル単位のスコープが有効な場合、stderr に警告を出力するため、他人のドキュメントに対する後々の 404 が謎のままになることはありません。

それを制限する方法:

  • 専用のGoogleアカウントを承認し、アクセス可能にしたい特定のファイルまたはフォルダのみを共有する。

  • OAuthアプリをテストモードのままにし、リストされたテストユーザーのみが承認できるようにする。

  • myaccount.google.com/permissions でいつでもアクセスを取り消す。

リフレッシュトークンの取り扱い。 これはDriveへのパスワードです。単独では期限切れになりません。.env(ここではgit-ignoreされています)またはMCPクライアントの設定に保管し、コミットされたファイルには決して入れないでください。漏洩した場合は、上記のリンクで取り消してください。即座に無効化されます。

テレメトリなし。 このサーバーはGoogleのAPIへのネットワーク呼び出しのみを行い、それ以外には一切行いません。


トラブルシューティング

症状

原因と修正

Missing required environment variable…

サーバーが認証情報なしで起動しました。MCPクライアントが3つの環境変数をすべて渡しているか確認してください。

Google rejected the credentials (401)

リフレッシュトークンが無効、取り消されている、または別のOAuthクライアントのものです。npm run authorize を再実行してください。

Permission denied (403)

アカウントはファイルを表示できるが書き込みができない、またはトークンが読み取り専用スコープを持っています。編集者アクセスと完全な drive スコープを確認してください。

File not found (404)

IDが間違っている、ファイルがゴミ箱にある、または承認されたアカウントにアクセス権がありません。IDはファイル名ではなく、URLの /d/ の後にあります。

Conflict: file … has changed

設計どおりの動作です。読み取った後に誰かがファイルを編集しました。再読み取り、再適用、再書き込みを行ってください。

No refresh token during authorize

アプリはこのアカウントですでに承認されています。myaccount.google.com/permissions で取り消して再試行してください。

Error 403: access_denied at the consent screen

コードではなく同意設定が原因です。以下を参照してください。

Client shows a parse error on startup

何かがstdoutに書き込んでいます。ここでの診断はすべてstderrに出力されます。フォーク内の余分な console.log はプロトコルストリームを破損させます。

Error 403: access_denied

Googleは、このコードが実行される前に同意画面を拒否しています。auth/drive は制限付きスコープです。Googleの最も厳格な階層であり、制限付きスコープはアプリが許可するように設定されていない限りブロックされます。Google Auth Platform で、次の順序で確認してください:

  1. **対象者 → 公開ステータスが「テスト中」**であり、「本番環境」ではないこと。本番環境の未検証アプリは、作成者自身を含むすべての人に対して制限付きスコープをまったく使用できません。テストモードでは、検証なしで最大100人のリストされたテストユーザーに対して使用できます。

  2. 対象者 → テストユーザーに、サインインに使用する正確なアカウントが含まれていること。

  3. ブランディング → アプリ名、ユーザーサポートメール、開発者連絡先メールがすべて保存されていること。不完全な同意画面は無効な同意画面です。

変更が反映されるまで数分かかります。編集後すぐに失敗する場合は、5分待って再試行してください。

完全に回避するには、ブロックされない非制限のファイル単位スコープを要求してください:

GOOGLE_OAUTH_SCOPE=drive.file npm run authorize

npm run verify が触れるすべてのファイルは、それ自身が作成したものです。したがって、完全な検証スイートは drive.file の下で合格します。同意設定がまだ整理されている間にサーバーが機能することを確認するのに役立ちます。他の場所で作成されたドキュメントには到達しないため、恒久的なものではなく診断パスです。


開発

npm install
npm run build       # compile TypeScript to dist/
npm test            # build, then run the unit suite (no network, no credentials)
npm run verify      # end-to-end check against a real Drive account
npm run typecheck   # type-check without emitting
npm run watch       # rebuild on change

npm test と npm run verify は異なる質問に答えます。ユニットスイートはDrive APIをモックします。ロジックが正しいことを証明し、CIで実行され、認証情報を必要としません。npm run verify は統合が正しいことを証明します。つまり、Googleがこのサーバーが想定するように実際に動作すること、特にネイティブファイル変換とリビジョントークンに関してです。drive.ts または mime.ts への変更は、両方で確認する必要があります。

コードは、ドキュメントを静かに破損させる可能性のある部分がネットワークに触れずにテストできるように構成されています:

src/
  index.ts    entry point; stdio transport
  auth.ts     OAuth client from environment
  drive.ts    Drive operations, incl. the concurrency guard
  edits.ts    pure text transforms — no I/O, fully unit-tested
  mime.ts     native vs. binary vs. textual classification
  tools.ts    MCP tool definitions and handlers
  errors.ts   error types written to be actionable by a model

スイートは、検索/置換のエッジケース(正規表現のように見えるリテラル、置換内の $&、複数行のターゲット、あいまいな一致)、追加/先頭挿入のシームロジック、MIME分類、および並行性ガードをカバーしています。競合する書き込みが決してAPIに到達しないことを含みます。


貢献

Issueとプルリクエストを歓迎します。どの規模の変更でも、作業を開始する前にアプローチを合意できるよう、最初にIssueを開いてください。

ツールを追加する場合は、その純粋なロジックのテストを追加し、それを読むモデル向けに説明を書いてください。何をするかだけでなく、隣接するツールよりもいつそれを使うべきかを示してください。


ライセンス

MIT — LICENSE を参照してください。

Related MCP Connectors

Related MCP Servers