1Password EnvironmentsのMCP連携がWindowsで解禁 — Claude Code / Codexへの設定手順と実機検証

開発misc-dev

1Password EnvironmentsのMCP連携がWindowsで解禁 — Claude Code / Codexへの設定手順と実機検証

1PasswordがAIエージェント向けの機能を立て続けに出している。5月にはOpenAI Codex向けの「Environments MCPサーバー」、続いてClaude向けのブラウザ統合。ただ、7月2日に検証したときはどちらもWindowsで使えなかった。設定のトグルがオンにできず、調べた結果「macOS / Linux限定」というOS制約に行き着いて撤退した。それから2週間、7月14日の安定版8.12.28でこの制約が解けていた。今日、Claude CodeとCodexの両方から1Password MCPサーバーへの接続を実機で確認したので、その記録と設定手順を残す。

結論

  • 1Password EnvironmentsのMCP連携は、Windows安定版8.12.28(2026-07-14)以降で使える。実機(8.12.28.25)で認証からEnvironments一覧取得まで通った
  • 設定は3手順で済む。アプリの「MCPクライアントとの統合」をオンにし、claude mcp addcodex mcp addで正規バイナリを登録する
  • 値の注入も、ベータCLI(2.38.0-beta.01)を併置すればop run --environmentで通る。Environmentの作成→ダミー値の書き込み→Nodeプロセスへの注入まで一周を実機で確認した。ただし実行のたびに承認ダイアログが出る(実測: 3回中3回、1回あたり約14秒)ので、頻繁に回すワークフローへの組み込みはまだ厳しい。ローカル.envマウントはMac/Linux限定のまま

これができると何が嬉しいのか

一番の嬉しさは、シークレットを扱う作業をAIに任せるときの「見せない・乗せない」が、運用の注意ではなく仕組みで保証されることだ。

  • AIのセッションログにシークレットが乗らない。MCPサーバーには変数の値を返すツールがそもそも無い(一覧・作成・追記のみ)。だからClaude Codeとの会話ログに接続文字列やトークンが混入する事故が構造的に起きない。値に触れるのは、ユーザーが承認した実行プロセスだけになる
  • .env直読みの綱渡りをやめられる。このリポジトリではClaude Codeに.envを読ませない運用にしているため、TursoのトークンをNodeスクリプトへ渡すには、PowerShellでGet-Contentから$env:へ画面に出さずに流すワンライナーが要った。しかもこのワンライナー自体が安全機構に「.env読み取りの回避」と判定されて止まることがあった(6月のティッカー追加作業で実際に発生)。シークレットの置き場が1Passwordになれば、.envを読むこと自体が無くなり、この衝突ごと消える
  • ディスク上の平文シークレットを減らせる。.envはgitignoreしていても平文でディスクに残り続ける。Environmentsに寄せれば保存先は1Passwordの暗号化ストレージになり、アプリのWatchtowerにある「ディスク上の開発者認証情報をチェックする」が、残った平文の検出役を務める

要するに「AIには秘密の名前だけ扱わせて、秘密の中身は金庫から実行プロセスへ直送する」構図が作れる。この直送も、後述の実地テストでWindows上で一周通ることを確認した。残る引っかかりは実行のたびの承認ダイアログだ(後述)。

1PasswordのAIエージェント対応は3系統

系統何ができるかWindows対応
1Password for Claudeブラウザ操作型のClaudeが、資格情報を見ずにログインできる✗(Macのみ)
Environments MCPサーバーClaude Code / CodexからEnvironments(環境変数セット)を管理できる✓(8.12.28で解禁)
CLI op run --environmentEnvironmentsの値を子プロセスの環境変数に注入する△(CLIベータ限定。併置で動作確認済み)

1つ目の1Password for Claudeは、Claudeデスクトップ+Chrome拡張の構成で動くブラウザエージェント向けの統合だ。ユーザーが生体認証で承認すると、1Passwordがページに資格情報を直接注入する。AIはパスワードもワンタイムコードも一切見ない。現時点でMac限定なので、今回は対象外。

2つ目が本題のEnvironments MCPサーバーだ。5月のCodex統合の発表では「資格情報はジャストインタイムで発行され、タスクにスコープされ、モデルのコンテキストウィンドウの外に置かれる」とうたわれている。設計の肝は、MCPサーバーがシークレットの値をMCPクライアントに返さないことだ。AIに任せられるのはEnvironmentsの作成・変数名の一覧・値の更新指示までで、値そのものはAIのセッションに乗らない。値を受け取るのは、ユーザーが承認した実行プロセスだけになる。

3つ目のCLI連携は、そのEnvironmentsの値を実際にプロセスへ流す経路で、op runリファレンスによると--environmentフラグは2.33.0-beta.02以降のベータビルド限定。手元の安定版2.34.0にはまだ入っていない(wingetの最新にもない)。ただし後述の通り、ベータ版を安定版と別名で併置すればWindowsでも動いた。

Windows対応のタイムライン

  • 7月2日: 8.12.26.40で検証。設定のトグルがオンにできず、ログとアプリ本体を調べて「macOS / Linux限定・Windowsはベータ進行中」と確認して撤退
  • 7月14日: 安定版8.12.28。1password-mcpバイナリの直接実行に対応し(PATH上にAppExecutionAliasが生える)、「設定 > 開発者」にCLI・SDK・MCPクライアント統合がまとめて配置された
  • 7月15日: ベータ8.12.30-19。DeveloperタブからローカルMCPサーバーを直接実行できる機能を追加
  • 7月18日(今日): 8.12.28.25で実機検証。Claude Code・Codexの両方から接続確認

Environmentsのドキュメントの対応OSにもWindowsが並ぶようになった。7月2日の時点では「解禁バージョンが出たかを確認してから再挑戦する」とルールファイルにメモして終えていたので、ちょうど2週間での再挑戦になった。

設定手順(Windows)

1. 1Passwordアプリ側

1Passwordを8.12.28以上に更新し、「設定 > 開発者」のDeveloper Integrationsで「MCPクライアントとの統合」をオンにする。同じ画面の「1Password CLIと連携」「Integrate with 1Password SDKs」も普段のCLI利用に使うのでオンにしておく。

これで正規のMCPサーバーバイナリがPATH上で使えるようになる。

C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\1password-mcp.exe

2. Claude Codeへの登録

claude mcp add 1password -s user -- "C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\1password-mcp.exe"

初回はauthenticateツールを呼ぶと1Password側に承認が要求され、通るとaccount_idが返る。以降はスコープ付きでEnvironments操作ができる。実機ではこの認証に続けてlist_environmentsが正常応答した(Environmentsをまだ作っていないので空配列だが、7月2日にはここまで一切届かなかった)。

{"id":2,"result":{"content":[{"text":"{\"account_id\":\"DBB3…TAM\"}"}],"isError":false}}
{"id":3,"result":{"content":[{"text":"{\n  \"environments\": []\n}"}],"isError":false}}

3. Codexへの登録

codex mcp add 1password -- "C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\1password-mcp.exe"

1つ注意がある。非対話のcodex execでは認証の承認が自動キャンセルされ、Missing required scope: environments:list. Call the authenticate tool first. で止まる。このエラー自体は1Password本体まで届いている証拠なので接続は問題ないが、初回の認証は対話モードのcodexで通す必要がある。

実地テスト: 値を1つ作って、プロセスに読み込ませてみる

設定が済んだところで、「1Passwordに値を作り、それをプロセスに読み込ませ、AIには何が見えて何が見えないか」を通しで試した。試行錯誤の過程ごと、手順と実測の出力をそのまま残す。

Step 1: Claude CodeがMCPでEnvironmentを作る

Claude Codeに頼むとcreate_environmentツールが呼ばれ、IDが返る。

create_environment { environmentName: "claude-mcp-test" }
→ { "environmentId": "wlj3c74a26cqp3hrkj6az4243m" }

Step 2: ダミーのID/パスワードを書き込む

append_variablesツールで変数を2本書き込む。concealed(伏字)フラグを片方だけ立てて、挙動の差を見る。

append_variables {
  variables: [
    { name: "TEST_LOGIN_ID",       value: "[email protected]", concealed: false },
    { name: "TEST_LOGIN_PASSWORD", value: "dummy-P@ss-1234",       concealed: true  }
  ]
}
→ Environment variables successfully updated.

1つ注意。このツールは値を「書く」ことはできる。AI経由で書けば、その値はAIのセッションを通過する。だからここで書いたのは使い捨てのダミー値だ。本物のシークレットは1Passwordアプリ側で入力すれば、AIには最後まで見えない。

書き込んだ結果はアプリでも確認できる。1Passwordの検索バーに「claude-mcp-test」と入力するか、サイドバーの「開発者」→「環境」から開く。concealedフラグの違いがそのまま表示に出ていて、IDは平文、パスワードは伏字になっている。値の追加・編集もこの画面からできるので、本物のシークレットはここで入力すればいい。

1Passwordアプリ(Windows)でのEnvironment表示。concealed: falseのTEST_LOGIN_IDは平文、concealed: trueのTEST_LOGIN_PASSWORDは伏字で表示される

Step 3: AIから値が読めないことを確かめる

list_variables → { "variableNames": ["TEST_LOGIN_ID", "TEST_LOGIN_PASSWORD"] }

返るのは名前だけ。MCPサーバーには値を返すツールがそもそも定義されていないので、AIがうっかり値を読む事故は構造的に起きない。なおCLI側には承認付きのop environment readがあり、読む手段自体はゼロではない。「AIにシークレットを読ませない」という運用ルールは、CLI経路がある限り引き続き意味を持つ。

Step 4: ベータCLIを併置する

注入に使うop run --environmentは安定版CLIに無いので、ベータ版をzipで取り、安定版のopを置き換えずにop-beta.exeとして並べた。

curl.exe -sL -o op-beta.zip https://cache.agilebits.com/dist/1P/op2/pkg/v2.38.0-beta.01/op_windows_amd64_v2.38.0-beta.01.zip
Expand-Archive op-beta.zip op-beta-dir
copy op-beta-dir\op.exe op-beta.exe
.\op-beta.exe --version   # → 2.38.0-beta.01

Step 5: プロセスに注入して観察する

Environment IDを指定して、環境変数を表示するだけのNodeワンライナーを走らせる。

./op-beta.exe run --environment wlj3c74a26cqp3hrkj6az4243m -- node -e "console.log('ID =', process.env.TEST_LOGIN_ID); console.log('PW =', process.env.TEST_LOGIN_PASSWORD); console.log('PW.length =', (process.env.TEST_LOGIN_PASSWORD||'').length)"

実測の出力がこれだ。

ID = [email protected]
PW = <concealed by 1Password>
PW.length = 15

3行がそれぞれ別のことを証明している。IDはconcealed: falseなのでそのまま出る。パスワードはconcealed: trueなので、プロセスが標準出力にechoしても1Passwordが伏字に置き換える。そして長さの15はdummy-P@ss-1234の文字数と一致する——伏字はあくまで出力のマスクであって、プロセス自身には実値が届いている。

ここで「結局、AIはパスワードの値をどうやって取っているのか」に答えておく。答えは「取っていない」だ。値がどこを通り、どこを通らないかをシーケンス図にするとこうなる。

1Password Environmentsの値の流れを示すシーケンス図。Claudeが渡すのはEnvironment IDだけで、復号された値は1Passwordからop runとnodeプロセスへメモリ内で直送され、Claudeには伏字化された出力だけが返る
図1: パスワードの値はClaudeを迂回してプロセスへ直送される。Claudeが触るのはEnvironment IDと伏字化された出力だけ

Claudeが出すのは「このIDのEnvironmentでop runを起動する」というコマンド文字列だけで(①)、値の要求はop runが1Passwordに直接行い(②)、ユーザーの承認(③)を経て復号された値はop runのメモリに渡る(④)。op runはそれを子プロセスの環境変数としてnodeに注入し(⑤)、Claudeに返る標準出力ではconcealedの値が伏字化されている(⑥)。値が存在するのは1Password・op run・nodeの3プロセスの中だけで、ディスクにもClaudeのセッションログにも現れない。

Step 6: 罠を1つ踏んだ — concealedの実体は「デフォルトで値を非表示にする」

仕上げに「アプリ側で値を変更し、AIには新しい値を見せないまま再実行して、届く長さだけが変わる」という証明実験をした。ところが再実行すると、伏字だったはずのパスワードが平文で出てきた。

ID = [email protected]
PW = dummy-P@ss-12345678   ← 伏字が消えた
PW.length = 19

原因は編集時の操作にあった。変数ごとの「⋮」メニューにある「デフォルトで値を非表示にする / 表示する」がconcealedフラグの実体で、編集中に「表示する」へ切り替えるとフラグごと外れる。op runの出力マスクはこのフラグに連動しているため、外れた変数は平文で流れる。「非表示にする」へ戻して再実行したら、伏字も復活した。

ID = [email protected]
PW = <concealed by 1Password>
PW.length = 19

変数の「⋮」メニュー。「デフォルトで値を非表示にする」がconcealedフラグの実体で、op runの出力マスクはこれに連動する

長さが15から19に変わったことで「AIを経由せずに値がプロセスへ届く」ことの証明自体は成立した。同時に教訓が1つ増えた。アプリで値を編集したら、「デフォルトで値を非表示にする」にチェックが残っているか確認する。 ここが外れていると、シークレットのつもりの値がop runのログマスクを素通りして、CIのログや画面共有にそのまま出る。

運用上の実測値: 毎回の承認ダイアログ

実行は毎回約14秒かかり、3回実行して3回とも1Passwordの承認ダイアログが出た。6月にvault item経由のop run --env-fileへの移行を試して、実行のたびの生体認証で断念したのと同じ壁が、Environments経路にもそのまま残っている。単発の作業(デプロイ、たまのバッチ)には今日から使えるが、頻繁に回す再生成系ワークフローに組み込むには、承認頻度を調整する手段が見つかるまで厳しい。

Step 7: Service Accountなら承認は初回だけ — 自動化のテスト

毎回の承認は「人間を挟む」設計そのもので、デスクトップ認証には緩める設定がない。自動化の正攻法はService Account(トークン認証)だ。いきなり本番の日次バッチに入れる前に、テスト用Environment(claude-mcp-test)だけで「本当にダイアログなしで回るか」を試した。

Service Accountはmy.1passwordのDeveloper → Service Accountsから作る。ウィザードでclaude-mcp-testへの読み取りアクセスを明示的に付与した(ここを付け忘れると「Environment was not found」で落ちる既知の罠がある)。発行されたトークンは1Passwordのアイテムに保存した。

トークンの値をこちらが見ずに使えるかも試した。1Passwordのアイテムからop readで取り出し、画面に出さずそのまま環境変数に渡す。

TOKEN="$(op read 'op://Personal/<item-id>/credential')"
OP_SERVICE_ACCOUNT_TOKEN="$TOKEN" ./op-beta.exe run --environment <ID> -- node -e "..."

ここで小さな罠を1つ踏んだ。フィールドを日本語ラベルの認証情報で参照するとinvalid character in secret referenceで弾かれる。op readのsecret referenceは英語のフィールドID(credential)で書く必要がある。

肝心の承認回数はこうなった。

認証方式1回目2回目3回目
デスクトップ認証(Step 5)承認ダイアログダイアログダイアログ
Service Account(このStep)ダイアログ(初回のみ)なしなし

デスクトップ認証は3回とも出たが、Service Accountは初回に1回出ただけで、2回目以降は無人で通った。各実行は4〜5秒で、承認待ちの間もない。op whoamiUser Type: SERVICE_ACCOUNTを返し、SA認証で動いていることを確認した。初回のダイアログは、このマシンでそのトークンを初めて使うことへの一度きりの同意で、デスクトップアプリのない本番サーバーなら出ないはずだ。

これで「日次バッチはService Accountに寄せれば無人化できる」という見通しが、テスト環境で実証できた。本番のturso-replicasへの適用は、Turso接続情報という重い値を扱うぶん慎重にやりたいので、別途Service Accountを作るところから改めて進める。

実務への適用イメージ: Turso接続情報を移すなら

このリポジトリで一番現実的な適用先は、.envに平文で置いているTursoの接続情報(DATABASE_URLとAUTH_TOKEN)だ。移行後の再生成フローをシーケンス図にするとこうなる。

Turso接続情報を1Password Environmentsへ移行した後の再生成フローのシーケンス図。値はアプリで1回登録し、実行のたびに承認つきでop run経由でスクリプトの環境変数に注入される。スクリプト側は無変更で、.envの平文は不要になる
図2: Turso接続情報を移すと、変わるのは値の出どころだけ。スクリプトは環境変数で受け取るまま無変更

移行そのものは4手順で終わる。

  1. Environment「turso-replicas」を作る(アプリからでも、Claude CodeにMCP経由で作らせてもよい)
  2. 2つの変数をアプリ側でconcealedのまま入力する(AIを経由させない。実地テストStep 2の注意そのまま)
  3. op-beta run --environment <ID> -- node scripts/generate-beat-valuation.mjsで動作確認
  4. 通ったら.envを退避・削除し、作業手順書の該当ステップを書き換える

スクリプトには一切手を入れない。環境変数で受け取る作りはそのままで、値の出どころが「ディスクの平文」から「承認つきの1Password」に変わるだけだ。環境変数注入は言語を問わないので、Node以外のスクリプトも同じ形で動く。

ただし前述の通り、実行のたびに承認ダイアログが出る。毎日自動で回す日次バッチにこれを組み込むと、毎朝ダイアログを承認する運用になる。単発の再生成から移して、承認頻度の調整手段が見つかったら全面移行、が現実的な順番だと考えている。

追記(同日): 記事を書いた勢いでこの手順を実際にやった。Environment作成はMCP経由で数秒、.envの全26変数は「.envファイルをインポート」ボタンで一括で取り込めた。1つ引っかかったのは、インポートを選んだだけでは反映されず、「編集中」バナーの「保存」を押すまで確定しないこと(変数一覧が空のままなのをMCPのlist_variablesで検知して気づいた)。移行後の再生成は、.env経由で朝に回した結果とデータ完全一致——差分は生成時刻の2行だけだった。作業手順書の該当ステップはop run --environmentパターンに書き換え済み。.env自体は、日次バッチ(Nodeの--env-fileとPythonのload_dotenv)がまだ参照しているのと毎回承認の壁が残るため、削除せず残している。単発作業から段階的に寄せていく。

まだできないこと

  • ローカル.envマウント: Environmentsの内容を仮想的な.envファイルとして見せる機能は引き続きMac/Linux限定。MCPサーバー自身のinstructionsにも同じ注記が入っている
  • op run --environmentの安定版入り: 実地テストの通りベータ併置で動くが、ベータCLIは名前の通りベータ。普段のスクリプトに正式採用するのは安定版に降りてきてからにしたい
  • 日次バッチの無人化: デスクトップ認証は承認スキップ設定が無く毎回ダイアログが出るが、Service Account認証なら初回だけの承認で以降は無人で回る(Step 7で実証)。残る作業は、本番のturso-replicas用にService Accountを作り、トークンの保管場所(環境変数/資格情報マネージャー等)を決めて、check-earnings日次バッチの.env参照を置き換えること。トークン自体が長期シークレットになるトレードオフはあるが、26個の平文が1個のスコープ限定トークンに集約される

つまりWindowsの現状は「管理はAIに任せられる。注入も通るが、毎回の承認が要る」だ。承認頻度の問題が解ければ、実務のシークレットを丸ごとEnvironmentsに寄せられる。

おまけ: 自作ラッパーの引退と、1つの誤診の訂正

7月2日の検証では、MSIXパッケージからonepassword-mcp.exeを抽出してコピーし、Claude Codeに登録する自作ラッパーまで作っていた。当時はAppExecutionAliasが宣言されておらず、パッケージ内のEXEを外部プロセスから直接起動できなかったからだ。

今日の検証の途中、このラッパー経由の呼び出しがIPC request failedで落ち、ログのInstaller=Unknownエラーを見て「抽出コピーが起動不能になった」と一度診断した。これは誤診だった。真因はデスクトップアプリ側の「MCPクライアントとの統合」がオフだったことで、トグルをオンにしたら抽出コピー経由でも普通に認証が通った。Installer=Unknownはログに出るだけで致命傷ではない。失敗の原因を近くにあったエラーログに安易に帰属させた、という反省として残しておく。

とはいえ8.12.28で正規の起動経路が用意された以上、抽出ハックの役目は終わりだ。登録は正規バイナリに切り替えて引退させた。

OS制約で撤退した機能が2週間で解禁される速度感なので、「未対応」と記録した機能も日付とバージョンをセットで残しておき、定期的に再挑戦するのが良さそうだ。