はじめに
Google Apps Script(GAS)をブラウザのスクリプトエディタで書いて、動いたら放置、直すときはまたブラウザを開いてコピペ。私はこの運用を定期実行のボットで数か月続けて、いよいよ「どのコードが今動いているのか分からない」状態になりました。git で管理したいし、正直なところコードは Claude Code に書かせたい。そこで Google が公開している CLI の clasp を入れて、何ができるのかをひととおり触ってみた記録です。
clasp は「push と pull ができるやつ」くらいの認識で止まっていませんか? 私はそうでした。調べてみると 3.x でコマンド体系がまるごと変わっていて、しかも Claude Code のプラグインとして公式に配布されている。2.x 時代の記事を読んで入れた人ほど、今の clasp を知らないと思います。
対象読者
- GAS を書いたことはあるが、スクリプトエディタ以外で書いたことがない人
- GAS のコードを git で管理したい、または Claude Code に書かせたい人
- 2.x 時代に clasp を触って、それきりになっている人
先に結論をまとめておきます。
| 知りたいこと | 結論 |
|---|---|
| clasp とは | Apps Script API を叩いて、手元のファイルと script.google.com 上のプロジェクトを同期する CLI |
| 主な用途 | ローカル編集・git 管理・バージョンとデプロイの操作・CI からの自動反映 |
| 3.x で変わったこと | コマンド名のフラット化、TypeScript の自動変換廃止、MCP サーバー機能の追加 |
| Claude Code との相性 | リポジトリに MCP 設定が同梱されている。MCP でできるのは同期・作成・clone・一覧の 5 操作で、デプロイは CLI を直接叩く |
参考文献
一次情報はここです。特に README は 3.x の仕様が最も正確に書かれていて、Google 公式ガイドより新しい内容が載っています。
検証環境
- macOS
- Node.js v26.0.0
- clasp 3.4.1(2026-08-28 リリース。執筆時点の最新)
- Claude Code 2.1.197
そもそも clasp って何?
clasp は Command Line Apps Script Projects の略で、Google が GitHub で公開している Node.js 製のオープンソース CLI です。README の冒頭に「公式サポート対象の Google 製品ではない」と明記されているので、社内に導入するときはその前提で扱う必要があります。やっていることはシンプルで、Apps Script API を経由して、手元のディレクトリと script.google.com 上のプロジェクトの中身を同期します。
図の右側を見てください。実際に実行されるコードは常に Apps Script 側にあって、push は手元の一式でリモートの HEAD を丸ごと置き換え、pull はその逆をやります。どちらを正本にするかは clasp が決めるものではなく運用の話で、git で管理するなら手元のリポジトリを正本にして、リモートは「push 先」と割り切るのが自然です。この前提を押さえておくと、後述するバージョンとデプロイが別物である理由もすっと入ります。
一言で言い直すと、clasp は「スクリプトエディタの保存ボタンをターミナルから押せるようにするツール」です。ただし保存だけでなく、バージョンを切る、デプロイする、ログを見る、関数を実行する、といったエディタのメニューにある操作もひととおりコマンドになっています。
使う前の準備で詰まりやすいところ
インストール自体は npm でグローバルに入れるだけです。
npm install -g @google/clasp
準備で必ず踏むのが次の 2 つで、どちらも「やっていないと後で分かりにくいエラーになる」タイプです。
- Apps Script API を有効にする。
https://script.google.com/home/usersettingsを開いてトグルをオンにします。これをやらずにcreate-scriptやpushを叩くとUser has not enabled the Apps Script API. Enable it by visiting ...と、やるべきことが書かれたエラーで止まります。有効化してから反映まで数分かかることがあります -
clasp loginでブラウザが開くので、GAS を使う Google アカウントで認可します。トークンはホームディレクトリの.clasprc.jsonに保存されます
ログイン状態は clasp show-authorized-user で確認できます。ログイン前に叩くと Not logged in. とだけ返ってきます。
clasp login のブラウザ同意画面は、スコープごとにチェックボックスが並ぶ形式です。チェックを入れずに「続行」を押してもログイン自体は成功してしまい、show-authorized-user にはメールアドレスが表示されます。ところがこの状態では Apps Script と Drive のスコープが 1 つも付いていないので、list-scripts や create-script を叩いた瞬間に Request had insufficient authentication scopes. で落ちます。私はこれを実際に踏みました。同意画面では全部のチェックを入れてから続行してください。
ここで私が地味に混乱したのが Node.js のバージョン要件でした。Google 公式ガイドは「20.0.0 以上」、GitHub README の末尾は「22.0.0 以上」、そして package.json の engines は >=20.0.0 と、3 か所で 2 通りの答えが書いてあります。実際に動く下限は 20 のようですが、README が 22 と言っている以上、これから入れるなら 22 以上にしておくのが無難です。私は 26 で問題なく動いています。
会社のアカウントと個人のアカウントを使い分けたい場合は、clasp login --user work のように名前を付けて認可しておくと、以降 clasp --user work push で切り替えられます。ログインし直す必要はありません。
基本の使い方 ── 作る・持ってくる・送る
日常的に使うのはこの 4 つです。
| やりたいこと | コマンド | 補足 |
|---|---|---|
| 新規プロジェクトを作る | clasp create-script --type sheets --title "集計" |
--type は standalone / docs / sheets / slides / forms。webapp / api も受け付けるが standalone の別名で、Web アプリや API としての設定は別途マニフェストとデプロイで行う |
| 既存プロジェクトを手元に持ってくる | clasp clone-script <scriptId> |
スクリプト ID はエディタの「プロジェクトの設定」にある |
| リモートの内容を手元に反映 | clasp pull |
|
| 手元の内容をリモートに反映 | clasp push |
--watch を付けると保存のたびに自動 push |
create-script か clone-script を実行すると、カレントディレクトリに .clasp.json ができます。中身は主に scriptId と rootDir で、このファイルがあるディレクトリが clasp にとってのプロジェクトのルートです。
{
"scriptId": "1AbC...(省略)",
"rootDir": "src"
}
push の対象から外したいファイルは .claspignore に書きます。書き方は .gitignore に似ていますが、マッチングのライブラリが違うので、ディレクトリを除外するときは **/node_modules/** のように書く必要があります。.claspignore を作らなければ、マニフェストと .js / .gs / .html だけを対象にする既定のパターンが適用されます。空でもファイルを置くと既定は使われなくなるので、作るなら除外規則をちゃんと書きます。
pull すると、スクリプトエディタで .gs に見えていたファイルが手元では .js で落ちてきます。逆に手元の .js を push すると、リモートでは .gs として表示されます。最初に見たとき「拡張子を直さないといけないのか」と思って調べましたが、これは clasp が意図的にやっている変換で、そのままで大丈夫です。手元を .gs で揃えたい場合は .clasp.json の scriptExtensions で先頭の拡張子を変えられます。
公開まわり ── バージョンとデプロイ
ここは GAS 側の概念を先に整理しておかないと、コマンドを覚えても使い分けられません。
- push が書き換えるのは HEAD、つまり「エディタで今見えている最新の保存内容」だけです
- バージョンは HEAD のスナップショットで、一度切ると変更できません
- デプロイは「どのバージョンを、Web アプリや API としてどの URL で公開するか」の設定です
コマンドはこの 3 層にそのまま対応しています。
clasp create-deployment --description "本番"
clasp list-deployments
clasp create-version "集計ロジック修正"
clasp update-deployment <deploymentId> --versionNumber 3 --description "本番"
最初の create-deployment はバージョン番号を渡さなければ新しいバージョンを自動で切ってからデプロイします。逆に create-version で先に番号を作ったのに create-deployment に番号を渡さないと、もう 1 つ新しいバージョンができて最初のものが使われません。更新時は --versionNumber で番号を明示し、--description も毎回付けます。省略すると説明が空で上書きされます。
Web アプリとして公開している場合は clasp open-web-app でデプロイ済み URL をブラウザで開けます。コンテナバインド型なら clasp open-container で紐づいたスプレッドシートが開きますが、これは .clasp.json に parentId が保存されている場合だけです。create-script --type sheets で作ったプロジェクトには入りますが、既存プロジェクトを clone-script しただけでは入らないので、必要なら手で追記します。
ブラウザの「デプロイを管理」を開いてバージョン番号を選んで、という操作が全部コマンドになっているので、push → create-version → update-deployment までをシェルスクリプトにしておくと反映漏れが減ります。手貼り運用で一番怖いのは「コードは直したのに、公開中のデプロイが古いバージョンを指したまま」という状態で、コマンド化しておけばここを機械的に潰せます。
3.x で変わったこと(2.x 時代の記事を読む人へ)
日本語の clasp 記事の多くは 2.x 時代のもので、そのまま真似すると微妙にズレます。CHANGELOG で破壊的変更として挙がっているのは次の 3 つで、それに MCP の追加を足した 4 点を順に見ていきます。
意外と量が多くなったので別記事に切り出しています。
Claude Code との相性 ── プラグインと MCP
clasp の GitHub リポジトリには .claude-plugin/plugin.json と claude-mcp.json が同梱されています。v3.2.0(2026-02)で「Claude Code CLI support」として入ったもので、中身は clasp を MCP サーバーとして起動する設定です。
登録は 1 行で済みます。
claude mcp add clasp -- npx -y @google/clasp mcp
README にはプラグインとしてインストールする方法も書かれていますが、中身は同じ MCP サーバーの登録なので、この記事では上の 1 行で進めます。認証は CLI と同じ .clasprc.json を使うので、先に clasp login を済ませておく必要があります。
登録したら claude mcp list に clasp: npx -y @google/clasp mcp - ✔ Connected と並びます。露出されるツールは執筆時点で 5 つだけです。
| ツール名 | 対応する CLI |
|---|---|
push_files |
clasp push |
pull_files |
clasp pull |
create_project |
clasp create-script |
clone_project |
clasp clone-script |
list_projects |
clasp list-scripts |
つまり MCP でできるのは同期・作成・clone・一覧の 5 つだけで、デプロイ・バージョン・関数実行・ログは MCP には無い。create_project も standalone を作るだけで、CLI の --type や --parentId にあたる指定はありません。とはいえ Claude Code は Bash が使えるので、そこは普通に clasp create-deployment を叩いてもらえば済みます。MCP は「プロジェクトのディレクトリをツール呼び出し時に渡す」設計なので、複数の GAS プロジェクトを行き来してもサーバーの再起動が要らないのが地味に効きます。
相性の話でもう一つ大事なのが、Claude Code に GAS の制約を教えておくことです。何も言わないと Node.js のつもりで require を書いたり、npm パッケージを入れようとしたりします。私はプロジェクトの CLAUDE.md にこう書いています。
# このリポジトリは Google Apps Script のプロジェクトです
- ランタイムは V8。モダンな構文は使えるが Node.js ではない。require / import / npm は使えない
- 実行時の require / import は使えない。外部の機能が要るなら UrlFetchApp で REST API を叩くか、Apps Script のライブラリ機能を使うか、Rollup 等で事前に 1 ファイルへバンドルする
- 通常の実行は 6 分で強制終了される(カスタム関数やアドオンは別の短い上限がある)。長い処理はトリガーで分割する
- タイムゾーンは appsscript.json の timeZone に従う。日付文字列を手で組み立てない
- ローカルは .js で書く。push すると Apps Script 側では .gs として表示される
- 反映は clasp push(MCP の push_files でも同じ)
- デプロイは clasp create-deployment。MCP にデプロイ用ツールは無いので Bash で実行する
- .clasp.json の scriptId は書き換えない
ポイントは「Node.js ではない」と「MCP に無い操作は CLI で」の 2 点を明文化しておくことです。Claude Code は clasp --help も読めるので、コマンド名は新名で揃えておきます。
実際に Claude Code から GAS を作って push してみた
最後に、スプレッドシートの A 列を集計する小さな GAS を Claude Code に作らせて、MCP 経由で push するところまでを通しました。
1. clasp でプロジェクトを作る
空のディレクトリで、スプレッドシートに紐づいたプロジェクトを作ります。
clasp create-script --type sheets --title "clasp-playground"
Created new document: https://drive.google.com/open?id=1e54...(省略)
Created new script: https://script.google.com/d/1_mm...(省略)/edit
└─ appsscript.json
Cloned one file..
Drive に新しいスプレッドシートが 1 枚でき、それに紐づいた Apps Script プロジェクトが作られ、マニフェストが手元に落ちてきます。生成された .clasp.json はこうでした。README の例より項目が多く、parentId に紐づいたスプレッドシートの ID が入っています。
{
"scriptId": "1_mm...(省略)",
"rootDir": "",
"parentId": "1e54...(省略)",
"scriptExtensions": [".js", ".gs"],
"htmlExtensions": [".html"],
"jsonExtensions": [".json"],
"filePushOrder": [],
"skipSubdirectories": false
}
ここで 1 つ注意点があります。落ちてきた appsscript.json の timeZone が America/New_York でした。このまま時間主導トリガーを組むと日本時間から 13 時間(冬時間の時期は 14 時間)ずれます。私は先に Asia/Tokyo に書き換えてから進めました。
{
"timeZone": "Asia/Tokyo",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
2. Claude Code に書かせて、MCP で push させる
前節の CLAUDE.md をプロジェクトに置き、Claude Code にこう頼みました。MCP が本当に使われたかを確かめたかったので、この実験では --disallowedTools で Bash を明示的に禁止し、--allowedTools で clasp の MCP ツールとファイル操作だけを自動承認にしています。--allowedTools は「それ以外を使えなくする」設定ではなく「確認なしで通す」設定なので、完全に閉じたいなら --tools で候補自体を絞ります。
claude -p "GAS プロジェクトが <ディレクトリ> にあります。CLAUDE.md を読んでから、A 列(2 行目以降)の値ごとに件数を数えて Summary シートに書き出す関数 summarizeColumnA を Code.js に実装し、clasp の MCP ツール push_files で push してください。Bash は使わないでください。" \
--allowedTools "mcp__clasp__push_files,mcp__clasp__pull_files,mcp__clasp__list_projects,Read,Write,Edit,Glob" \
--disallowedTools "Bash"
Claude Code の動きを追うと、Read で CLAUDE.md を読み、Glob でディレクトリを確認し、Write で Code.js を書き、そのあと mcp__clasp__push_files を呼びました。全体で 13 ターン、約 75 秒です。Claude Code を起動したディレクトリはプロジェクトの外で、MCP ツールの引数 projectDir でプロジェクトの場所を渡しています。
書かれたコードは次のとおりです。require も npm も出てこず、Map で数えて setValues で一括書き込みする GAS らしい形になりました。
function summarizeColumnA() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sourceSheet = ss.getSheets()[0];
const lastRow = sourceSheet.getLastRow();
const counts = new Map();
const order = [];
if (lastRow >= 2) {
const values = sourceSheet.getRange(2, 1, lastRow - 1, 1).getValues();
for (let i = 0; i < values.length; i++) {
const cell = values[i][0];
if (cell === '' || cell === null) {
continue;
}
const key = String(cell);
if (counts.has(key)) {
counts.set(key, counts.get(key) + 1);
} else {
counts.set(key, 1);
order.push(key);
}
}
}
let summarySheet = ss.getSheetByName('Summary');
if (summarySheet === null) {
summarySheet = ss.insertSheet('Summary');
} else {
summarySheet.clear();
}
const output = [['値', '件数']];
for (let i = 0; i < order.length; i++) {
const key = order[i];
output.push([key, counts.get(key)]);
}
summarySheet.getRange(1, 1, output.length, 2).setValues(output);
}
push の結果は MCP ツールの戻り値として JSON で返ってきます。
{"scriptId":"1_mm...(省略)","projectDir":"/Users/.../clasp-playground","files":[".../appsscript.json",".../Code.js"]}
ただし一発では通りませんでした。最初、プロジェクトを /private/tmp 配下に置いていたところ、push_files がこう返してきました。
Security Error: projectDir must be within the user home directory or current working directory.
MCP モードの clasp は、ホームディレクトリの配下か、MCP サーバー自身のカレントディレクトリ(つまり Claude Code を起動した場所)の配下しか触らせない制限を持っています。今回はプロジェクトの外で Claude Code を起動していたので弾かれました。CLI で直接 clasp push する分には /private/tmp でも通るので、MCP 側だけの制約です。Claude Code はこのエラーを読んで、ホーム配下に同じ一式をコピーしてから push をやり直しました。私はその後プロジェクトごとホーム配下に移しています。プロジェクトのディレクトリで Claude Code を起動すれば、この制限には当たりませんし、CLAUDE.md も自動で読み込まれます。
3. 反映を確かめる
別のディレクトリで clasp clone-script <scriptId> を実行すると appsscript.json と Code.js の 2 ファイルが落ちてきて、手元の Code.js と差分なしでした。スクリプトエディタを開くと、手元で .js だったファイルが Code.gs として並んでいます。
4. バージョンとデプロイは CLI で
MCP にはデプロイが無いので、ここは CLI です。ついでに前述の「create-deployment は番号を渡さないと新しいバージョンを切る」を確認しました。
clasp create-version "A列集計の初版"
clasp create-deployment --description "検証用"
clasp list-versions
Created version 1
Deployed AKfycbx...(省略) @2
Found 2 versions.
1 - A列集計の初版
2 - 検証用
先に切ったバージョン 1 は使われず、デプロイは自動で作られたバージョン 2 を指しています。バージョンを自分で管理したいなら create-deployment --versionNumber 1 のように明示が必要です。list-deployments を見ると、自分で作ったものとは別に @HEAD を指すデプロイが最初から 1 つ存在していて、これはエディタの「テストデプロイ」に相当します。
まとめ
clasp を「push と pull のツール」だと思っていた頃と比べると、3.x はだいぶ別物でした。コマンドがエディタの操作をひととおりカバーしていて、git で履歴を持てて、Claude Code から MCP で直接触れる。GAS をブラウザで書く理由が、正直ほとんど無くなりました。
一方で MCP はまだ experimental で、ツールも 5 つだけです。デプロイまで MCP で完結する日は来そうな気がしていて、CHANGELOG を追いながらこの記事も更新していくつもりです。
次は GitHub Actions から clasp push を回して、main にマージしたら本番の GAS が更新される構成を試します。すでに CI 連携をやっている方がいたら、認証情報の持ち方をどうしているか教えてもらえると嬉しいです。

