Claude Codeのheadlessモード(claude -p)を知って、「これを毎朝勝手に走らせれば、朝イチの定型作業が消えるのでは」と思った人がまず突き当たるのがここです。
- 手動で叩けば動くコマンドが、タスクスケジューラ経由だと何も起きずに終わる(エラーすら出ない)
- MCPツールを使わせたいのに、無人実行では許可プロンプトを出せずそこで詰まる
- そもそも「今朝ちゃんと動いたのか」を確認する手段を作っていなかったことに数日後気づく
この記事では、Windowsのタスクスケジューラから claude -p を毎朝定時実行する運用を組んだときに、私が実際に踏んだ3つの落とし穴と、その回避策をまとめます。headlessモード自体の基本(-p の使い方、出力フォーマット等)はこの記事では深追いしません。**「動くコマンドはあるのに、定時実行の運用に乗らない」**部分に絞ります。
注: Claude Codeのフラグや挙動は執筆時点のものです。仕様は更新されるため、最新は公式ドキュメント(https://code.claude.com/docs/ )で確認してください。
この記事で分かること
- タスクスケジューラ×
claude -pの基本構成(スクリプト+ログ+人間の後追い確認) - 落とし穴①: powershell.exe(5.1)だと日本語パスで無言で失敗する話と、pwsh.exe指定での回避
- 落とし穴②: headless実行でMCPツールを使わせるための
--allowedToolsの書き方 - 落とし穴③: 「動いていない」ことに気づける観測性の作り方
- タスクスケジューラの具体的な設定値と、自動実行に与える権限のセキュリティ注意
全体像:毎朝の運用はこの3点セット
やることはシンプルで、次の3点セットです。
-
claude -p "プロンプト"を書いたps1スクリプトを用意する - タスクスケジューラで毎朝定時にそのスクリプトを起動する
- 結果は日付入りログファイルに書き出しておき、人間(自分)が後で目視確認する
ポイントは3番です。headless実行は「Claudeに完全に任せて終わり」ではなく、結果をファイルに残して人間が後追いレビューする前提で組みます。LLMの出力は実行ごとに揺れるので、無人実行と人手確認をワンセットにするのが安全です。
スクリプトの雛形はこうなります。
# daily-claude.ps1
$ErrorActionPreference = "Stop"
$workDir = "C:\work\myrepo" # Claudeに作業させるディレクトリ
$logDir = "C:\work\logs\claude-daily"
$logFile = Join-Path $logDir ("{0}.log" -f (Get-Date -Format "yyyy-MM-dd"))
New-Item -ItemType Directory -Force $logDir | Out-Null
Set-Location $workDir
claude -p "昨日のログファイルを読んで、今日の作業候補を箇条書きで出して" `
--allowedTools "Read" `
*> $logFile
*> はPowerShell 7系で全ストリーム(標準出力・エラー等)をまとめてリダイレクトする書き方です。成功時の出力もエラーも、全部その日のログに落ちます。
ここまでは素直です。問題は、これをタスクスケジューラに載せた瞬間に始まります。
落とし穴①:powershell.exe(5.1)は日本語パスで「無言で」失敗する
最初にハマったのがこれです。タスクスケジューラの「プログラム/スクリプト」に powershell.exe(Windows標準のPowerShell 5.1)を指定し、引数で日本語を含むパスのスクリプトを -File 指定したところ、タスクは「正常終了」扱いなのに、スクリプトの中身が何も実行されないという状態になりました。
- エラーダイアログなし
- イベントログにも手がかりらしい手がかりなし
- タスクスケジューラ上は成功に見える
私の環境(Windows 11)では、pwsh.exe(PowerShell 7系)を明示的に指定したら同じ引数でそのまま動きました。日本語を含むパス+-File 指定の組み合わせで、5.1と7系で挙動が分かれた形です。
教訓は2つです。
その1:タスクスケジューラからは pwsh.exe をフルパスで指定する。 PowerShell 7系は別途インストールが必要です(winget install Microsoft.PowerShell 等)。既定のインストール先は C:\Program Files\PowerShell\7\pwsh.exe です。
その2:スケジューラに登録する前に、同じコマンドラインを手動で叩いて検証する。 定時実行は「失敗に気づきにくい」のが本質的な弱点です。登録するのとまったく同じ実行ファイル・同じ引数を、まずコマンドラインから手で実行して動作確認します。
# タスクスケジューラに登録する前に、これを手で叩いて確認する
& "C:\Program Files\PowerShell\7\pwsh.exe" -NoProfile -File "C:\work\scripts\daily-claude.ps1"
これで動かなければ、スケジューラに載せても動きません。切り分けが一段簡単になります。
落とし穴②:headless実行はMCPツールの許可プロンプトを出せない
対話モードのClaude Codeでは、MCPツールを初めて使うときに「許可しますか?」というプロンプトが出て、人間が承認します。しかしheadless実行(claude -p)は無人です。許可プロンプトを出す相手がいないので、MCPツールを使う処理はそこで進まなくなります。
回避策は、呼び出し時に --allowedTools フラグで使わせたいツールを明示的に許可することです。MCPツールの名前は次の形式です。
mcp__<サーバー名>__<ツール名>
たとえばMCPサーバー myserver の search ツールを許可するならこうです。
claude -p "MCPの検索ツールで昨日の記録を調べて要約して" `
--allowedTools "mcp__myserver__search" `
*> $logFile
settings.jsonでの恒久許可は弾かれるケースがあった
「毎回フラグで書くのは面倒だから、settings.jsonの許可リストに書いて恒久化しよう」と考えるのが自然ですが、私の環境ではClaude Code自身にsettings.jsonを編集させて許可を追加しようとしたところ、安全機構に弾かれて編集が通らないケースがありました。設定ファイルの書き換えはそれ自体がセンシティブな操作として扱われるためのようです(この判定基準の詳細は私は確認できていません)。
結果として、呼び出しごとに --allowedTools で明示指定するのが確実でした。定時実行の場合、コマンドラインはスクリプトに固定で書いてあるので、毎回指定でも手間は一度きりです。むしろ「このジョブに与えた権限がスクリプトを見れば全部分かる」という監査上のメリットもあります。
落とし穴③:「動いていない」ことに気づける仕組みがないと、静かに止まる
定時実行の一番怖い故障モードは、派手なエラーではなく**「いつの間にか動かなくなっていて、誰も気づかない」**です。落とし穴①のような無言の失敗もあるので、観測性は最初から仕込みます。
やることは2つです。
その1:標準出力を日付入りログファイルにリダイレクトする。 冒頭の雛形でやっているとおり、yyyy-MM-dd.log の形でファイルを分けます。「今朝の分」がファイルとして存在するかどうか自体が生存確認になります。
その2:ログファイルの更新日時を確認する習慣(またはコマンド)を持つ。 私は次のワンライナーで直近のログを確認しています。
Get-ChildItem C:\work\logs\claude-daily |
Sort-Object LastWriteTime -Descending |
Select-Object -First 3 Name, LastWriteTime
今日の日付のファイルが先頭に来ていなければ、今朝のジョブは走っていません。「結果の中身を読む」前に「そもそも走ったか」を数秒で判定できます。
タスクスケジューラ側でも「タスクの状態」「前回の実行結果」列は見られますが、前述のとおり中身が実行されていなくても正常終了に見えるパターンがあるので、成果物(ログファイル)側で確認するのが確実です。
タスクスケジューラ設定の要点
落とし穴を踏まえた設定値をまとめます。タスクスケジューラで「基本タスクの作成」→「プログラムの開始」を選び、次のように入力します。
| 設定項目 | 値 |
|---|---|
| プログラム/スクリプト | C:\Program Files\PowerShell\7\pwsh.exe |
| 引数の追加 | -NoProfile -File "C:\work\scripts\daily-claude.ps1" |
| 開始(オプション)=作業ディレクトリ | C:\work\scripts |
| トリガー | 毎日、任意の時刻(例: 7:00) |
補足です。
-
-NoProfileを付けると、ユーザープロファイル($PROFILE)を読み込まずに起動します。プロファイルに書いた設定やモジュール読み込みが定時実行の挙動を変えるのを防ぎ、環境差を減らせます。 -
「開始(オプション)」(作業ディレクトリ)は忘れずに指定します。 空欄だと想定外のディレクトリで起動し、相対パスを使っている処理が壊れます。スクリプト内で
Set-Locationしている場合でも、起動直後のカレントを固定しておくと事故が減ります。 - スクリプトのパスに空白や日本語が含まれる場合、引数内のダブルクォートは必須です。
セキュリティ注意:自動実行に渡す権限は最小限に
無人実行は「人間のチェックが挟まらない」実行です。だからこそ、与える権限は意識的に絞ります。
-
--allowedToolsは、そのジョブに必要なツールだけを列挙します。 読むだけのジョブなら"Read"だけ、MCPツールが1つ必要なら"mcp__myserver__search"だけ。「面倒だから広めに」は無人実行では逆です。 -
--dangerously-skip-permissionsを安易に使わないでください。 名前のとおり権限確認をスキップするフラグで、これを定時実行に付けると「毎朝、権限チェックなしでLLMが動く」状態になります。プロンプトで止まる問題は--allowedToolsの明示で解決できるので、まずそちらを使うべきです。 - 万一の暴走や想定外の出力に備えて、破壊的な操作(コミット、削除、外部送信など)は自動ジョブに含めず、生成結果をファイルに書き出すところまでにして、適用は人間の確認後に手動で行う構成を推奨します。
まとめ
Windowsで claude -p を毎朝の定時実行に乗せるときのチェックリストです。
-
実行エンジンは pwsh.exe(PowerShell 7系)をフルパス指定。powershell.exe(5.1)は日本語パス+
-Fileで無言で失敗することがある - 登録前に、同じコマンドラインを手動で叩いて検証する。定時実行は失敗に気づきにくい
-
MCPツールは
--allowedTools "mcp__<サーバー名>__<ツール名>"で呼び出しごとに明示許可。settings.json編集での恒久化は弾かれるケースがある - 出力は日付入りログファイルへリダイレクトし、更新日時の確認で「そもそも走ったか」を判定できるようにする
- タスクスケジューラでは
-NoProfile -File "…"と作業ディレクトリの指定を忘れない -
権限は最小限。
--dangerously-skip-permissionsは定時実行に付けない
3つの落とし穴はどれも「手動なら気づけるのに、無人だと気づけない」性質のものでした。逆に言えば、手動検証→最小権限→観測性の3点を最初に押さえておけば、毎朝の定型業務は安心してClaude Codeに渡せます。
繰り返しになりますが、Claude Codeのフラグ・挙動は執筆時点の情報です。導入時は公式ドキュメント(https://code.claude.com/docs/ )で最新仕様を確認してください。