hamburger-tech-nits

主にプログラミングのNITSな話

Claude CodeのMCPトークンを1Password CLIで管理するようにした

Claude Code の MCP サーバー設定に API トークンを平文で書いていたのが気になっていたので、1Password CLI(op read)経由で取得するようにした。

課題:設定ファイルに平文トークンが残る

Claude Code で MCP サーバーを使うとき、~/.claude.json に API トークンを直接書く必要がある。

{
  "github": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-github"],
    "env": {
      "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
    }
  }
}

これだと設定ファイルにトークンが平文で残ってしまう。dotfiles をリポジトリ管理していたり、バックアップに含まれていたりすると漏洩リスクがある。

解決策:1Password CLI でトークンを取得する

1Password CLI の op read を使えば、1Password に保存したトークンを実行時に取得できる。設定ファイルにはトークンの参照先だけを書けばよい。

前提

  • 1Password CLI がインストール済み
  • 1Password アプリと CLI の統合が有効(生体認証でロック解除可能)
brew install 1password-cli
# 1Password アプリ → 設定 → 開発者 → 「CLIとの統合」を有効化

1. トークンを 1Password に保存

Development Vault に API Credential として保存する。

op item create \
  --category="API Credential" \
  --title="GitHub MCP Token" \
  --vault="Development" \
  'credential=ghp_xxxxxxxxxxxx'

同じ要領で、使っている MCP サーバー分のトークンを保存していく。

2. ~/.claude.json の設定を書き換える

ポイントは command/bin/bash にして、-c オプションで op read → 環境変数にセット → exec でサーバー起動、という流れにすること。

{
  "github": {
    "command": "/bin/bash",
    "args": [
      "-c",
      "GITHUB_PERSONAL_ACCESS_TOKEN=$(op read 'op://Development/GitHub MCP Token/credential') exec npx -y @modelcontextprotocol/server-github"
    ],
    "env": {}
  }
}

op read の引数は op://Vault名/アイテム名/フィールド名 という URI 形式。

他のサーバーも同じパターンで書ける。

{
  "circleci-mcp-server": {
    "command": "/bin/bash",
    "args": [
      "-c",
      "CIRCLECI_TOKEN=$(op read 'op://Development/CircleCI MCP Token/credential') exec npx -y @circleci/mcp-server-circleci@latest"
    ],
    "env": {}
  },
  "slack": {
    "command": "/bin/bash",
    "args": [
      "-c",
      "SLACK_MCP_XOXP_TOKEN=$(op read 'op://Development/Slack MCP Token/credential') exec npx -y slack-mcp-server@latest --transport stdio"
    ],
    "env": {}
  }
}

3. 複数トークンが必要な場合

&& でつなげて export する。

{
  "command": "/bin/bash",
  "args": [
    "-c",
    "export API_KEY=$(op read 'op://Development/Service API Key/credential') && export API_SECRET=$(op read 'op://Development/Service API Secret/credential') && exec npx some-mcp-server"
  ]
}

exec を付ける理由

exec を付けると bash プロセスが MCP サーバーのプロセスに置き換わる。これがないと bash が親プロセスとして残り続けるので、プロセスが無駄に増える。

HTTP 型サーバーはそのまま

Sentry や Datadog のような HTTP 型(OAuth 認証)のサーバーは、コマンド起動ではないので op read を挟む必要がない。

{
  "sentry": { "type": "http", "url": "https://mcp.sentry.dev/mcp" },
  "datadog": { "type": "http", "url": "https://mcp.datadoghq.com/api/unstable/mcp-server/mcp" }
}

使ってみて

  • Claude Code 起動時に Touch ID の認証が入るだけで、あとは今までと変わらない
  • 設定ファイルからトークンが消えたので、dotfiles を気軽にリポジトリ管理できるようになった
  • 新しい PC をセットアップするときも、1Password にログインすればトークンが使えるのでポータビリティも上がった
  • 1Password のセッションはデフォルト 30 分でタイムアウトするが、生体認証連携していれば自動で再認証されるので特に困らない

設定ファイルにトークンを書かないだけでだいぶ安心感がある。MCP サーバーを複数使っている人にはおすすめ。

MaestroのiOSテスト実行後に`Exception in thread "Thread-4" java.lang.IllegalStateException`のエラー

MaestroのiOSテストをローカルで実行したところ、テスト成功後に以下のエラーメッセージが表示された。

Exception in thread "Thread-4" java.lang.IllegalStateException: An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=3):
Simulator device failed to terminate dev.mobile.maestro-driver-iosUITests.xctrunner.
FBSSystemService reported failure without an error, possibly because the app is not currently running.
Underlying error (domain=NSPOSIXErrorDomain, code=3):
        Application termination failed.
        FBSSystemService reported failure without an error, possibly because the app is not currently running.

        at util.CommandLineUtils.runCommand(CommandLineUtils.kt:47)
        at util.CommandLineUtils.runCommand$default(CommandLineUtils.kt:17)
        at util.LocalSimulatorUtils.terminate(LocalSimulatorUtils.kt:169)
        at xcuitest.installer.LocalXCTestInstaller.close(LocalXCTestInstaller.kt:226)
        at xcuitest.XCTestDriverClient.close(XCTestDriverClient.kt:167)
        at ios.xctest.XCTestIOSDevice.close(XCTestIOSDevice.kt:185)
        at ios.LocalIOSDevice.close(LocalIOSDevice.kt:140)
        at maestro.drivers.IOSDriver$close$1.invoke(IOSDriver.kt:69)
        at maestro.drivers.IOSDriver$close$1.invoke(IOSDriver.kt:65)
        at maestro.utils.Metrics.measured(Metrics.kt:48)
        at maestro.utils.Metrics.measured$default(Metrics.kt:42)
        at maestro.drivers.IOSDriver.close(IOSDriver.kt:65)
        at maestro.Maestro.close(Maestro.kt:501)
        at maestro.cli.session.MaestroSessionManager$MaestroSession.close(MaestroSessionManager.kt:340)
        at maestro.cli.session.MaestroSessionManager$newSession$1.invoke(MaestroSessionManager.kt:101)
        at maestro.cli.session.MaestroSessionManager$newSession$1.invoke(MaestroSessionManager.kt:96)
        at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)

Slackを検索してみたところiOS15で発生するバグであることがわかった。ただし、テストの結果に影響を及ぼさないので無視して良さそうとのこと。

mobile-dev-inc.slack.com

launchAppclearState: trueオプションを使う提案がされていたので試したが、自分の環境では解決しなかった。

onFlowStart:
  - launchApp:
      clearState: true

このスレッドはこのメッセージでやり取りが止まっている

MaestroのiOSデフォルトSimulatorがiOS15なのであえて別バージョンを指定するのも気持ち悪いし、一旦無視することにする。

appIdが正しいのにMaestroで`Unable to launch app`のエラー

ローカル環境でMaestroを動かすときは複数セッションで動かさないほうが良さそう。

Androidのテスト実行後にAndroid用のSimulatorをそのままにしてiOSのSimulatorを立ち上げ、iOSのテストを実行したらUnable to launch app <appId>: Package <appId> is not installedのエラーが発生した。

少し雰囲気が似ているissueを見つけたので試しにAndroidのSimulatorを閉じ、再度iOSのテストを実行したらテストが成功した。

github.com

Cursorのprofileのimportがうまくいかないときの解消方法

PCをリプレイスしたのでCursorの設定変更中。2025/03/14現在、設定同期がうまく動いていない。

github.com

  1. settings.json をコピペし、"extensions.verifySignature": false, を追記する
  2. 古いPCのcursorで、ProfileをExportする。Export先はgistにした。
  3. Profileの保存を押すと拡張機能のインストールが動き出す。

"extensions.verifySignature": false が無かったときは拡張機能のインストールが空振っていたので、それが少し前進した。

英熟語を語源から覚える

英語学習の一環としてiKnowで英単語を勉強するというのを始めた。DMM英会話にバンドルされていたのがきっかけで知り、その時の使い勝手が良かったからDMM英会話終了後にまた個別で契約した。

iknow.jp

英単語を覚えることは難しいが、似たような英熟語を覚えるのがもっと難しいなと思い始めている。例えば、

  1. pull off -> 成功する
  2. pull over -> 車を停めさせる

など。前置詞の使い方だけでガラッと意味が違うので、見た目の雰囲気で覚えられない。

僕はChatGPTのMacアプリが Option + Space でウインドウを立ち上がるようにしているのだが、ちょっとそれについて聞いてみるだけで記憶の定着率が良いことに最近気づいた。

構成するそれぞれの単語のニュアンスや使われ方・どういうプロセスで熟語として成立するようになったのか?をストーリー付きで解説してくれるので、理屈で覚えたいタイプの自分にかなり合っていた。ちょっとした手間だけど聞いたストーリーをベースに意味も熟語も思い出せる確率が上がっている。ただの相談役くらいの活用しかできてなかったので、ChatGPTへの課金が初めて効果を発揮している。

2024年の振り返りと2025年の目標

2024年の振り返り

  • 目の前の仕事に集中するにはあまりに厳しい状況に直面し、精神的なダメージやモチベーションの回復に時間を使った一年だった。どちらかというと環境が悪かった事案なのだが、自分の実力不足と若干の潔癖症・周囲に対する過度な信用が問題を大きくしたのだと思っている。そのへんの意識は少し変えていく必要がある。

  • 完全リモートから週一出社に自主的に切り替えた。ちょっと見たことがある位の同僚がかなり増えた。顔を知っているだけでも安心感がある。普段話すきっかけがない上司とすれ違ったタイミングで会話できたり、トータル良い変化に繋がった。電車に乗る・都内で外食をする・帰宅時にふらっと寄り道をして見慣れない店の商品を見る、といったコロナ禍完全リモート期に消えた何気ない活動が新鮮で、刺激になっている。

  • 生産性向上というフレーズにあまり共感できず、でも自分がやろうとしていることは生産性向上に近い気がする、という自己矛盾を抱えていてここ数年悩んでいた。Saasプロダクトを改善して社会に必要な仕事の時間を減らしたい、目の前の作業も減らしたい、でも仕事によっては全くやる気がしない。これらがそれぞれ別の理由でモチベーションの有無に繋がっていると思っていた。だが突然12月になって「仕事の総量を減らすことが好き」というシンプルな原動力から来るものだと気づくことができた。結構目が覚めるような感覚があって、自分の今後の目標やキャリアを考えるうえでクリアな基準を定めることができて良かった。

  • 他にも色々迷走した結果、来年は過去のキャリアに縛られずに新しいことに挑戦しようと前向きに考えて取り組めるようになった。12月はその後始末の期間みたいになっていて、来年の前半は移行期間のような感じになりそう。今まで技術やグレード基準で物事を考えていて右往左往していたが、良い意味でキャリア面での悩みが変わりそう。

2025年の目標

  • [ ] TOEICのR/Lで700点以上(2024年は600点くらいだった)
  • [ ] TOEICのW/Sを受験(初受験なので点数目標は2026年以降)
  • [ ] 週2回以上のジム通い
  • [ ] 週1回の18時退社と家族の時間を持つ
  • [ ] 仕事の6割以上をモバイルアプリ開発以外の仕事に切り替える(多分バックエンドやWeb)
  • [ ] 週1回のブログ投稿

その他

  • [ ] 引っ越しするかもしれない。家を探したり買ったりするかもしれない
  • [ ] M.A.K.Iでダイヤランクに到達したい
  • [ ] もうちょっと趣味らしい趣味を見つけたい
  • [ ] 月に一本くらいなんかの映画を見たい
  • [ ] 世界情勢がわかるようにしたい。地政学だったり近代史的なところがわかるとニュースを楽しめそうという感覚がある
  • [ ] ヒゲを消し去りたい

Android Gradle plugin requires Java 17 to run. You are currently using Java 11.

FAILURE: Build failed with an exception.

* Where:

Build file ****************************/app/android/app/build.gradle' line: 2

* What went wrong:

An exception occurred applying plugin request [id: 'com.android.application']

> Failed to apply plugin 'com.android.internal.application'.

> Android Gradle plugin requires Java 17 to run. You are currently using Java 11.

Your current JDK is located in /Applications/Android Studio.app/Contents/jbr/Contents/Home

You can try some of the following options:

- changing the IDE settings.
- changing the JAVA_HOME environment variable.

- changing `org.gradle.java.home` in `gradle.properties`.

* Try:

> Run with --stacktrace option to get the stack trace.

> Run with --info or --debug option to get more log output.

> Run with --scan to get full insights.

> Get more help at https://help.gradle.org.

Android Studiojdkの設定を更新することで解決した

Preferences | Build, Execution, Deployment | Build Tools | Gradle