Claude Media
Managed AgentsにVault(認証情報保管庫)で秘密情報を渡す

Managed AgentsにVault(認証情報保管庫)で秘密情報を渡す

Managed AgentsのVaultとCredentialの仕組みを、作成・登録・ローテーション・失効検知まで手順とともに解説します。

Vault(認証情報保管庫)とCredentialは何を解決するか

Managed AgentsのVault(認証情報保管庫)は、第三者サービスの認証情報を一度登録しておき、セッション作成時にIDで参照する仕組みです。独自のシークレットストアを運用したり、呼び出しのたびにトークンを送信したりする必要がなくなり、どのエンドユーザーの代理でエージェントが動いたかも追いやすくなります。Vaultの参照はセッション単位のパラメーターなので、エージェント自体はプロダクト単位で、Vaultは利用者単位で分けて管理できます。

前提条件

Managed Agents APIのリクエストにはmanaged-agents-2026-04-01ベータヘッダーが必要です。VaultとCredentialはワークスペース単位のリソースで、そのワークスペースにアクセスできるAPIキーであれば誰でも参照できます。アクセスを取り消したい場合はVaultまたはCredentialそのものを削除します。本番に出す前の通し点検はManaged Agentsのセキュリティ点検表にあります。

ステップ1 — Vaultを作成する

Vault(認証情報保管庫)はエンドユーザー1人に対応するcredentialsの集合です。display_nameを付け、必要ならmetadataで自社のユーザーIDと紐づけます。

vault_id=$(curl --fail-with-body -sS https://api.anthropic.com/v1/vaults \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  -H "content-type: application/json" \
  --data @- <<'EOF' | jq -r '.id'
{
  "display_name": "Alice",
  "metadata": {"external_user_id": "usr_abc123"}
}
EOF
)
echo "$vault_id"  # "vlt_01ABC..."

レスポンスはVaultのレコード全体で、type・id・display_name・metadata・created_at・updated_at・archived_atが入ります。archived_atはアーカイブ前ならnullです。

antCLIを使う場合は、display_nameだけを書いたYAMLを渡します。IDが表示され、claude-lock.jsonにも記録されます。

ant apply vaults/service_accounts.yaml
# vaults/service_accounts.yaml
display_name: Service accounts

ステップ2 — Credentialを登録する

Credential(個々の認証情報)は、何をキーにするかで2種類に分かれます。

くらべる

Credentialの2系統

mcp_oauth / static_bearer

MCPサーバー宛て

mcp_server_urlをキーに登録します。エージェントがそのURLのサーバーへ接続した時点で、トークンが自動で付きます。

environment_variable

環境変数として渡す

secret_name(環境変数名)をキーに登録します。CLI・SDK・直接のAPI呼び出しなど、環境変数で認証するものに使います。

実際のトークンや秘密の値(token、access_token、refresh_token、client_secret、secret_value)は書き込み専用の項目として扱われ、APIレスポンスに含まれることはありません。

MCPサーバーの認証を登録する

OAuth 2.0を使うMCPサーバーにはmcp_oauthを使います。refreshブロックを渡しておくと、アクセストークンが失効したタイミングでAnthropic側が自動でリフレッシュします。refresh.token_endpoint_auth.typeは、リフレッシュ時の認証方法を表します。

  • none: 公開クライアント
  • client_secret_basic: クライアントシークレットをHTTP Basic認証で送る
  • client_secret_post: クライアントシークレットをPOSTボディに入れる
{
  "display_name": "Alice's Slack",
  "auth": {
    "type": "mcp_oauth",
    "mcp_server_url": "https://mcp.slack.com/mcp",
    "access_token": "xoxp-...",
    "expires_at": "2099-12-31T23:59:59Z",
    "refresh": {
      "token_endpoint": "https://slack.com/api/oauth.v2.user.access",
      "client_id": "1234567890.0987654321",
      "scope": "channels:read chat:write",
      "refresh_token": "xoxe-1-...",
      "token_endpoint_auth": {"type": "client_secret_post", "client_secret": "abc123..."}
    }
  }
}

refresh.token_endpointには、リフレッシュトークンを発行したOAuthフローのトークンエンドポイントを指定します。Anthropicはすべてのリフレッシュ要求をこのURLへ送り、作成後は変更できません。上の例のSlackは、ユーザートークン用のoauth.v2.user.accessです。

固定のベアラートークンで済むMCPサーバー(APIキーや個人アクセストークンなど)にはstatic_bearerを使い、リフレッシュの仕組みは要りません。

{
  "display_name": "Linear API key",
  "auth": {
    "type": "static_bearer",
    "mcp_server_url": "https://mcp.linear.app/mcp",
    "token": "lin_api_your_linear_key"
  }
}

MCPサーバーとの接続まわりの実装変化はリモートMCPのOAuth認証で扱っているので、リフレッシュの仕組み自体を理解したい場合はあわせて参照してください。

環境変数として秘密情報を渡す

CLIやSDK、直接のAPI呼び出しのように環境変数で認証するサービスにはenvironment_variableを使います。エージェントのサンドボックス内には実際の値ではなく不透明なプレースホルダーが置かれ、エージェントが外部へリクエストを送信するタイミング(egress)で本物の値へ差し替えられます。エージェント自身は秘密の値を見ることがありません。

{
  "display_name": "Notion API key for sandbox",
  "auth": {
    "type": "environment_variable",
    "secret_name": "NOTION_API_KEY",
    "secret_value": "ntn_your-secret-here",
    "networking": {
      "type": "limited",
      "allowed_hosts": ["api.notion.com"]
    },
    "injection_location": {"header": true}
  }
}

networking.allowed_hostsは、どの送信先ホストに対してこの秘密を差し替えるかを制御します。エージェントが実際にそのホストへ到達できるかどうかは別の話で、Environment側でも同じドメインを許可しておく必要があります。

injection_locationは、リクエストのどこに秘密を差し込むかをheaderとbodyの真偽値で指定する任意の項目です。networkingと並ぶ別の項目で、allowed_hostsが「どのホストか」を絞るのに対し、こちらは「リクエストのどの部分か」を絞ります。ペイロードはエージェントが扱う内容から組み立てられることが多く、ボディのほうが露出面は広くなります。多くのサービスはAPIキーをヘッダーから読むので、headerだけを有効にするのが狭い設定です。

作成と更新で挙動が違います。

くらべる

injection_locationの作成と更新

省略した項目は false

作成時

オブジェクトを渡すと、書かなかった項目はfalseになります。{"header": true}ならヘッダーだけのCredentialです。オブジェクトごと省略すると、両方が有効になります。

項目ごとにマージ

更新時

指定した項目だけが変わります。{"body": false}はボディを無効にし、headerはそのままです。

両方の位置を無効にする作成・更新は400エラーです。injection_locationやその項目に明示的なnullを渡した場合も400になるので、変えたくない項目は省略します。レスポンスには、解決後の両方の値が必ず入ります。

1つのVaultに登録できるCredentialは最大20件です。mcp_server_urlやsecret_nameはVault内の有効なCredentialの中で一意である必要があり、重複登録は409エラーになります。アーカイブ済みのCredentialは数えません。また、これらのキー自体は作成後に変更できません。変更したい場合はCredentialをアーカイブして作り直します。

環境変数Credentialのスコープを絞り込む

environment_variableタイプのCredentialは、渡す権限の範囲を小さく保つほど安全になります。エージェントはCredentialが許可する範囲であれば何でも実行できてしまうため、必要以上に広い権限のAPIキーを登録すると、エージェントが想定外の挙動をしたときの被害範囲がそのぶん広がります。そのセッションで任せるタスクに必要な最小限のスコープだけを持つキーを発行して登録すれば、被害範囲を小さく保てます。networkingのunrestrictedは、接続先を事前に列挙できない場合に限り、通常はlimitedで具体的なホスト名を列挙します。ホストを絞っておけば、許可していない相手へキーが渡ることもありません。

秘密のプレースホルダーが相手にそのまま届くとき

相手のサービスが認証エラーを返し、受け取ったリクエストに不透明なプレースホルダーの文字列が残っているときの症状です。無効にした位置のプレースホルダーは、置換も削除もされず、そのまま送られます。原因は次の順に切り分けられます。

手順

プレースホルダーが残る原因の切り分け

  1. 1

    差し込み位置が有効か見る

    秘密の送り先がボディなのにbodyが無効、といった食い違いが典型です。Consoleで作ったCredentialはヘッダーだけが有効なので、フォーム形式のトークン要求では起きます。Consoleのフォームでbodyを有効にするか、{"injection_location": {"body": true}}で更新します。

  2. 2

    送信先が allowed_hosts に入っているか見る

    Credentialのnetworking.allowed_hostsに送信先ホストが無いと、そのホスト宛てのリクエストでは差し替えが行われません。

  3. 3

    Environment側のネットワーク設定を見る

    Credentialのallowed_hostsは「差し替えるか」を決めるだけで、通信を許可するわけではありません。Environment側にも同じドメインが必要で、置換されたリクエストが成功するには両方に含まれている必要があります。

ステップ3 — セッション作成時にVaultを参照する

作成したVaultはvault_ids配列でセッションに渡します。

session_id=$(curl --fail-with-body -sS https://api.anthropic.com/v1/sessions \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  -H "content-type: application/json" \
  --data @- <<EOF | jq -r '.id'
{
  "agent": "$agent_id",
  "environment_id": "$environment_id",
  "vault_ids": ["$vault_id"],
  "title": "Alice's Slack digest"
}
EOF
)

一致するMCP Credentialが無いままエージェントがMCPサーバーへ接続しようとすると、未認証のまま接続が試みられ、そのサーバーが認証を要求していればエラーになります。複数のVaultに一致するCredentialがある場合は、先に指定したVaultが優先されます。

マルチエージェントセッションでは、MCPサーバーの宣言はエージェント定義ごと、Vaultはセッションごとの指定です。vault_idsの認証情報は全スレッドに適用され、自分の定義でそのMCPサーバーを宣言しているエージェントだけが、その認証情報で接続します。複数のエージェントがMCPサーバーを使う構成では、全員が使うサーバー分のCredentialをVaultに入れておき、アクセスを絞りたいエージェントの定義には必要なサーバーだけを宣言します。

Credentialをローテーションする

display_nameや実際の秘密の値、環境変数Credentialのinjection_locationは更新できます。mcp_server_urlやsecret_name、token_endpoint、client_idのような構造的なフィールドは作成後にロックされ、変更したい場合はCredentialをアーカイブして新規作成します。ローテーション後は実行中のセッションであっても、再起動を挟まずに新しい認証情報へ切り替わります。

Credentialのライフサイクルと失効検知

CredentialはセッションおよびVaultのライフサイクルの中で定期的に再解決され、ローテーション・アーカイブ・削除は実行中のセッションにも再起動を挟まず反映されます。vault.archived・vault.deleted・vault_credential.archived・vault_credential.deleted・vault_credential.refresh_failedの各イベントをwebhookで購読しておけば、失効やリフレッシュ失敗にすぐ気づけます。webhookのVaultイベント一覧には、vault.createdとvault_credential.createdも載っています。

Vaultをアーカイブまたは削除すると、vault.archivedやvault.deletedに加えて、配下のCredentialごとにvault_credential.archivedやvault_credential.deletedも発行されます。Credential数ぶんイベントが増える点に注意が必要です。削除イベントの時点ではオブジェクトが残っていないため、取得し直さずイベントそのものを最終状態として扱います。

mcp_oauthのリフレッシュが失敗した原因は、mcp_oauth_validateエンドポイント(POST /v1/vaults/{vault_id}/credentials/{credential_id}/mcp_oauth_validate)で調べます。返ってくるstatusで次の動きが決まります。

判定

mcp_oauth_validateのstatusと次の動き

  • valid

    トークンは有効です。対応は要りません。

  • invalid

    グラントが失効しているか、OAuthサーバーが4xxでリフレッシュを拒否しました。エンドユーザーに再認可を促します。

  • unknown

    5xx・429・ネットワーク障害のような一時的なエラーです。待って再試行します。

レスポンスはvault_credential_validationオブジェクトで、mcp_probeには失敗したMCPハンドシェイクの段階、refreshにはリフレッシュを試した結果が入ります。

アーカイブと削除、どちらを使うか

VaultやCredentialを使わなくなったときの操作は、アーカイブと削除の2種類があり、監査の要件によって使い分けます。

操作挙動向いている場面
アーカイブ(POST /archive)挙動秘密の値は破棄されるが、レコード自体は監査目的で残る。Vaultをアーカイブすると配下の全Credentialも連鎖してアーカイブされる向いている場面いつ・誰の認証情報を無効化したかを後から追跡したい場合
削除(DELETE)挙動レコードごと完全に削除され、復元できない向いている場面記録自体を残す必要がなく、完全に消し去りたい場合

Credential単体のアーカイブはPOST /v1/vaults/{vault_id}/credentials/{credential_id}/archiveです。

Vaultをアーカイブすると、それを参照する新規セッションの作成は失敗しますが、すでに実行中のセッションはそのまま継続します。Credentialのキー(mcp_server_urlやsecret_name)は、アーカイブ後も表示上は残り、同じキーで新しいCredentialを登録し直せる状態になります。VaultやCredentialの一覧取得はページングされ、新しい順に返され、既定ではアーカイブ済みのレコードは含まれません。含めたい場合はinclude_archived=trueを指定します。

よくあるつまずき

  • サンドボックス内でシークレットの値を検証しようとして失敗する: 環境変数Credentialの実体はegress時にしか差し替わりません。サンドボックス内で値のフォーマットを検証したり、AWS SigV4のように秘密からリクエスト署名を計算したりするクライアントは、プレースホルダーのまま処理してしまい失敗します。
  • OAuthのトークン交換フローで秘密が漏れると誤解する: クライアントクレデンシャルグラントのように、保存した秘密を使ってセッショントークンを取得するフローでは、取得したトークン自体はサンドボックスにそのまま届きます。交換が必要な認証方式は、事前に交換を済ませて得られたトークンをVaultに保存する設計にします。
  • 不正なCredentialを登録してもエラーにならず安心してしまう: CredentialはAPI呼び出し時点では値の妥当性を検証されず、登録された内容がそのまま保存されます。実際の検証はセッション実行時になって初めて行われ、不正な値は認証エラーやダウンストリームのエラーとして表面化しますが、そのエラーが出てもセッション自体は止まらずに続行します。登録直後に「エラーが出ないから正しい」と判断しないことが重要です。

まとめ

環境変数Credentialは、秘密をそのまま送るクライアントにだけ向きます。署名計算や事前のフォーマット検証が要るクライアントなら、MCP側の認証か、事前に交換を済ませたトークンの保存を選ぶことになります。実行中セッションへのファイルの受け渡しは実行中セッションでのファイル操作、セッション・ハーネス・サンドボックスの分離設計はManaged Agentsの設計思想を参照してください。

この記事を共有:XはてブLinkedIn