mcp.json に平文で入れていたパスキーを 1Password に移動した話

mcp.json に平文で入れていたパスキーを 1Password に移動した話

2025.12.12

こんにちは、高崎@アノテーション です。

はじめに

現在の業務において、お客様とのやり取りは Backlog で行っているのですが、業務を進めるにあたり、Backlog MCP を使って取得した内容を生成 AI にインプットして概要をまとめてもらって整理し、回答については考えた文章をレビューしてもらってから返信する形で業務を進めることがあります。

Backlog を取得する際の API キーは mcp.json に平文で保存していましたが、セキュリティに不安があったため、弊社がパスワード管理ツールとして使用している 1Password に管理させ、CLI ツールにて取得してみた顛末を記事にいたします。

環境

筆者の環境です。

  • PC:MacBook Pro(macOS Sequoia 15.7.1)
  • Editor:VSCode + GitHub Copilot
  • 1Password と 1Password CLI をインストール

前提 mcp.json の中身

最初、使っていた mcp.json です。

これを各プロジェクトの .vscode 配下に保存していました。

.vscode/mcp.json
{
  "servers": {
    "backlog": {
      "command": "docker",
      "args": [
        "run",
        "--pull",
        "missing",
        "-i",
        "--rm",
        "-e",
        "BACKLOG_DOMAIN",
        "-e",
        "BACKLOG_API_KEY",
        "ghcr.io/nulab/backlog-mcp-server"
      ],
      "env": {
        "BACKLOG_DOMAIN": "cm1.backlog.jp",
        "BACKLOG_API_KEY": "取得したAPIキー"
      }
    }
  }
}

と、BACKLOG_API_KEY の部分が平文だったわけですね。

ちなみに、今回の記事において MCP クライアントは docker を起動させていますが、npx の場合でもやり方は同じだと思います。

1Password 側のセットアップ

平文だった API キーを 1Password に保存します。

  1. 新規アイテムをクリック
    op_create_1

  2. 詳細を表示する → API 認証情報を選択
    op_create_2
    op_create_3

  3. 適切な名前(今回はBacklog MCPとします)と、パスワードを追加する項目を選択
    op_create_4

元からある「認証情報」では、フィールド名を変えられないためパスワードを追加するようにします。

今回はAPI Keyと定義します。

  1. Backlog から取得した API キー、フィールド名を定義し保存
    op_create_5

ひとまず取得出来るか mcp.json に入れてやってみる

mcp.json を下記のように設定。

.vscode/mcp.json
{
  "servers": {
    "backlog": {
      "command": "docker",
      "args": [
        "run",
        "--pull",
        "missing",
        "-i",
        "--rm",
        "-e",
        "BACKLOG_DOMAIN",
        "-e",
        "BACKLOG_API_KEY",
        "ghcr.io/nulab/backlog-mcp-server"
      ],
      "env": {
        "BACKLOG_DOMAIN": "cm1.backlog.jp",
        "BACKLOG_API_KEY": "$(op read 'op://Private/Backlog MCP/API Key')"
      }
    }
  }

いざ docker を起動し、MCP API を取得しようとすると 401 エラーが発生して Backlog からデータが取れず…。

動かない理由とその対策

動かない理由は、API キーとして渡す変数がopコマンドの実行結果ではなくそのまま文字列として処理されるため Backlog での認証エラーが発生したことが原因です。

対策としては、docker を実行させるためには以下のようなラッパーシェルを用意し、実行可能な path(今回は ~/.local/bin)へ格納してこれを mcp.json から起動する形で実現します。

~/.local/bin/mcp-backlog.sh
#!/bin/bash

BACKLOG_API_KEY=$(op read 'op://<Vault>/Backlog MCP/API Key')

# API キーが取得できない場合
if [ -z "$BACKLOG_API_KEY" ]; then
    echo "Error: Failed to retrieve Backlog API key from 1Password" >&2
    exit 1
fi

# Docker コンテナを実行
exec docker run \
    --pull missing \
    -i \
    --rm \
    -e BACKLOG_DOMAIN="cm1.backlog.jp" \
    -e BACKLOG_API_KEY="$BACKLOG_API_KEY" \
    ghcr.io/nulab/backlog-mcp-server "$@"

最終的に作った mcp.json は以下のようになります。

.vscode/mcp.json
{
  "servers": {
    "backlog": {
      "command": "mcp-backlog.sh",
      "args": []
    }
  }
}

これで mcp.json から起動すると取得が可能になり、無事平文の表現を消すことが出来ました。

と、思ってたら…

さて、ブログの記事にするか…と思っていたのですが、1Password からこんなブログが。

https://1password.com/blog/securing-mcp-servers-with-1password-stop-credential-exposure-in-your-agent

ってことは。

シェルにしなくてよいのでは?

と、いうことで。

BACKLOG_API_KEY="op://<Vault>/Backlog MCP/API Key"

という env ファイル(今回は~/.env.backlogと定義)を用意し、

op run --env-file ~/.env.backlog -- docker run --pull missing -i --rm -e BACKLOG_DOMAIN=cm1.backlog.jp -e BACKLOG_API_KEY ghcr.io/nulab/backlog-mcp-server

こんな感じの実行をそのまま mcp.json に定義することで良くこんな形に。

~/.env.backlog
BACKLOG_API_KEY="op://<Vault>/Backlog MCP/API Key"
.vscode/mcp.json
{
  "servers": {
    "backlog": {
      "command": "op",
      "args": [
        "run",
        "--env-file",
        "${userHome}/.env.backlog",
        "--",
        "docker",
        "run",
        "--pull",
        "missing",
        "-i",
        "--rm",
        "-e",
        "BACKLOG_DOMAIN=cm1.backlog.jp",
        "-e",
        "BACKLOG_API_KEY",
        "ghcr.io/nulab/backlog-mcp-server"
      ]
    }
  }
}

と、こんな感じで出来たのですが。

ここで、筆者のズボラな虫が脳内にログインして一言。

ファイル2個の管理、面倒じゃない?

と、いうことで再び。

環境変数を最初に定義して実行

例えばこんな形。

BACKLOG_API_KEY="op://<Vault>/Backlog MCP/API Key" && docker run --pull missing -i --rm -e BACKLOG_DOMAIN=cm1.backlog.jp -e BACKLOG_API_KEY=$BACKLOG_API_KEY ghcr.io/nulab/backlog-mcp-server

実行すると docker は立ち上がります。

と、いうことは MCP API も実行できるのですが…。

ps -ef | grep BACKLOG_API_KEY
% ps -ef | grep BACKLOG_API_KEY | grep -v grep
  502 41370 40468   0  5:57午後 ??         0:00.01 sh -c BACKLOG_API_KEY=$(op read 'op://<Vault>/Backlog MCP/API Key') && /<docker のフルパス>/docker run --pull missing -i --rm -e BACKLOG_DOMAIN=cm1.backlog.jp -e BACKLOG_API_KEY=$BACKLOG_API_KEY ghcr.io/nulab/backlog-mcp-server
  502 41385 41370   0  5:57午後 ??         0:00.09 /<docker のフルパス>/docker run --pull missing -i --rm -e BACKLOG_DOMAIN=cm1.backlog.jp -e BACKLOG_API_KEY=<API キーの平文文字> ghcr.io/nulab/backlog-mcp-server
  :

API キーの平文が見えてしまい、セキュリティ上よろしくないという重大な欠点があります。

別のやり方を模索

env BACKLOG_API_KEY='op://<Vault>/Backlog MCP/API Key' op run -- docker run --pull missing -i --rm -e BACKLOG_DOMAIN=cm1.backlog.jp -e BACKLOG_API_KEY ghcr.io/nulab/backlog-mcp-server

これを実行すると…。

%  ps -ef | grep BACKLOG_API_KEY | grep -v grep
  502 44971  7418   0  6:55午後 ttys000    0:00.14 op run -- /<docker のフルパス>/docker run --pull missing -i --rm -e BACKLOG_DOMAIN=cm1.backlog.jp -e BACKLOG_API_KEY ghcr.io/nulab/backlog-mcp-server
  502 44983 44971   0  6:55午後 ttys000    0:00.02 /<docker のフルパス>/docker run --pull missing -i --rm -e BACKLOG_DOMAIN=cm1.backlog.jp -e BACKLOG_API_KEY ghcr.io/nulab/backlog-mcp-server

お、出てこない。

最終系

mcp.json はこうなり、.env.backlog は不要になりました。

.vscode/mcp.json
{
  "servers": {
    "backlog": {
      "command": "env",
      "args": [
        "BACKLOG_API_KEY=op://<Vault>/Backlog MCP/API Key",
        "op",
        "run",
        "--",
        "docker",
        "run",
        "--pull",
        "missing",
        "-i",
        "--rm",
        "-e",
        "BACKLOG_DOMAIN=cm1.backlog.jp",
        "-e",
        "BACKLOG_API_KEY",
        "ghcr.io/nulab/backlog-mcp-server"
      ]
    }
  }
}

動作確認

動かしてみましょう。

プロンプト
https://<Backlog URL>/view/<Issue ID>
MCP API で取得してみて、こちらを要約してください。
`get_issue` を実行しました
Completed with input: {
  "issueKey": <Issue ID>
}

コメントも取得してみます: 

`get_issue_comments` を実行しました
Completed with input: {
  "issueKey": "<Issue ID>"
}

## <Issue ID>詳細要約

### 基本情報
- **課題キー**: <Issue ID>
- **タイトル**: <当該 Issue のタイトル>
- **ステータス**: 処理中
- **担当者**: <担当者>
- **作成日**: 2025年11月17日
  :以下略

無事出力出来ましたし、mcp.json 単体で平文表現を排除出来ました!🎉

おわりに

今回は mcp.json に平文で定義していた API キーを 1Password への管理に載せ替えてみた顛末を記事にしました。

この例では API キーでしたが、これに限らず GitHub のアクセスキーや AWS のクルデンシャルにおいても同じような定義が出来ると思いますので、色々と応用はきくと思います。

この記事が皆さんのお役に立てれば幸いです。

アノテーション株式会社について

アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。

サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。

当社は様々な職種でメンバーを募集しています。

「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイト をぜひご覧ください。

この記事をシェアする

FacebookHatena blogX

関連記事