CodeCraft Lab

MCPサーバーを構築する

ローカル・リモートMCPサーバーの本格的な構築手順、認証設定、シークレット管理、複数サーバーの.mcp.json構成パターンを学びます

応用15分で読了
MCP設定認証シークレット管理

このレッスンで学ぶこと

  • ローカル(stdio)MCPサーバーを本格的な構成オプション込みで構築する手順
  • リモート(HTTP)MCPサーバーの認証パターン(Bearerトークン、OAuth、事前設定済みクライアント)
  • 環境変数・シークレットを安全に扱うための設計原則
  • .mcp.jsonで複数サーバーを管理し、チームに展開するための構成パターン

前提条件

  • Lesson 1「MCPの基礎」を修了していること(クライアント/サーバーモデル、stdio/HTTPの違いを理解している前提で進めます)
  • GitHubアカウントなど、最低1つの外部サービスのアカウントがあると実践しやすくなります

実践レシピのMCPサーバーをローカル環境で設定するでは、Playwright MCPサーバーを例にした基本手順を扱いました。このレッスンでは、それを土台に実務で使うレベルの構成——複数サーバーの同時管理、認証パターンの使い分け、シークレットの安全な受け渡し——まで踏み込みます。

ローカル(stdio)サーバーの本格構築

スコープを意識して追加する

claude mcp addはデフォルトでlocalスコープ(自分専用・現在のプロジェクトのみ)に登録されます。実務では、用途に応じて3つのスコープを使い分けます。

スコープ保存先共有範囲向いている用途
local(デフォルト)~/.claude.json自分のみ・このプロジェクトのみ個人の実験的な設定、機密性の高い認証情報を含むサーバー
project<project>/.mcp.jsonチーム全員(バージョン管理経由)チームで共有するツール(GitHub、社内API等)
user~/.claude.json自分のみ・全プロジェクト個人的によく使うユーティリティサーバー
# チームで共有するサーバーをproject scopeで追加
claude mcp add --transport http github --scope project \
  https://api.githubcopilot.com/mcp/ \
  --header "Authorization: Bearer ${GITHUB_TOKEN}"
 
# 個人的に全プロジェクトで使うサーバーをuser scopeで追加
claude mcp add --scope user playwright -- npx -y @playwright/mcp@latest
スコープは後から変更できない

一度追加したサーバーのスコープを変えたい場合は、既存のエントリをclaude mcp removeで削除してから、新しいスコープで追加し直す必要があります。最初にどのスコープが適切かを考えてから追加しましょう。

CLAUDE_PROJECT_DIRでプロジェクト相対パスを解決する

stdioサーバーには、起動時にCLAUDE_PROJECT_DIRという環境変数が自動的に設定されます。サーバー側のコードでprocess.env.CLAUDE_PROJECT_DIR(Node.js)やos.environ["CLAUDE_PROJECT_DIR"](Python)を参照すれば、Claude Codeの実行時のカレントディレクトリに依存せず、プロジェクトルートを起点としたパス解決ができます。

.mcp.jsoncommandargsの中でこの変数を使う場合は、Claude Code自体の環境にはこの変数が設定されないため、デフォルト値を併記しておくのが安全です。

{
  "mcpServers": {
    "local-script": {
      "type": "stdio",
      "command": "node",
      "args": ["${CLAUDE_PROJECT_DIR:-.}/scripts/mcp-server.js"]
    }
  }
}

リモート(HTTP)サーバーの認証設定

パターン1: 静的トークンをヘッダーで渡す

個人アクセストークン(PAT)方式のサービスでは、--headerでAuthorizationヘッダーを直接指定します。

claude mcp add --transport http github \
  https://api.githubcopilot.com/mcp/ \
  --header "Authorization: Bearer ${GITHUB_TOKEN}"

トークンをコマンドにベタ書きせず、シェルの環境変数を展開する形にしておくと、コマンド履歴にトークンの値そのものが残りません。

パターン2: OAuth 2.0によるブラウザ認証

GitHub・Slack・Sentryなど多くのSaaS連携は、OAuthフローによる認証をサポートしています。Claude Codeは401または403応答を受け取ると、そのサーバーを「認証が必要」として/mcpパネルにフラグを立てます。

認証が必要なサーバーを追加する

claude mcp add --transport http sentry https://mcp.sentry.dev/mcp

claude mcp listを実行すると ! Needs authentication と表示されます。これは想定どおりの状態です。

セッション内でブラウザ認証を行う

Claude Codeを起動し、/mcpコマンドでパネルを開きます。

/mcp

対象サーバーを選択しAuthenticateを実行すると、ブラウザが開いてログイン・許可フローに進みます。完了するとステータスがConnectedに変わります。

コマンドラインから直接認証する(CLI完結)

セッションを開かずに認証したい場合は、専用コマンドも使えます。

claude mcp login sentry

SSH接続中などローカルブラウザが使えない環境では、認可URLがターミナルに出力されるので、手元のマシンでURLを開き、リダイレクト後のURLをターミナルに貼り付けて完了させます。

パターン3: 事前設定済みOAuthクライアント

一部のサーバーは動的クライアント登録に対応しておらず、事前にOAuthアプリを登録してクライアントIDを払い出す必要があります。その場合は--client-id--client-secret--callback-portを組み合わせます。

claude mcp add --transport http my-server \
  --client-id YOUR_CLIENT_ID --client-secret --callback-port 8080 \
  https://mcp.example.com/mcp

--client-secretはマスクされた入力を求めるプロンプトになるため、シェル履歴にシークレットが残りません。

どの認証パターンを選ぶべきか迷ったら

まず静的トークン(パターン1)が使えるか確認し、なければOAuth(パターン2)、それでも繋がらない「Incompatible auth server」エラーが出る場合のみパターン3を検討する、という優先順位で進めると無駄がありません。

環境変数・シークレット管理のベストプラクティス

.mcp.jsonでの変数展開

.mcp.json${VAR}構文での環境変数展開をサポートしています。これにより、設定ファイル自体はチームで共有しつつ、各メンバーのマシン固有の値(APIキーなど)は環境変数側に分離できます。

{
  "mcpServers": {
    "database": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"]
    },
    "api-server": {
      "type": "http",
      "url": "${API_BASE_URL:-https://api.example.com}/mcp",
      "headers": {
        "Authorization": "Bearer ${API_KEY}"
      }
    }
  }
}

${VAR:-default}の形式でデフォルト値を指定できるため、変数が未設定でも動作させたいケースとそうでないケースを使い分けられます。必須の値(APIキー等)はデフォルトを与えず、未設定時にエラーで気づけるようにしておくのが安全です。

守るべき3原則

シークレット管理の3原則
  1. .mcp.jsonにトークンを直接書き込まない ── プロジェクトスコープの.mcp.jsonはバージョン管理にコミットされる前提のファイルです。値は必ず${VAR}で参照してください
  2. コミット前に機密情報の混入を確認する ── .mcp.jsonをコミットする際は、誤って実際のトークンが値として書かれていないか必ず確認しましょう
  3. 最小権限のトークンを発行する ── GitHubのFine-grained PATのように範囲を絞れるトークンは、操作対象のリポジトリ・権限だけに限定して発行します

データベース接続のように、誤操作の被害が大きい接続先では、読み取り専用ユーザーを使うことも徹底しましょう。

# 本番DBへの接続は読み取り専用ユーザーで
claude mcp add --transport stdio database -- \
  npx -y @bytebase/dbhub \
  --dsn "${DATABASE_URL}"
# チームメンバーは各自の環境でシークレットを設定
export DATABASE_URL="postgresql://readonly_user:password@db.example.com:5432/app"
export GITHUB_TOKEN="ghp_xxxxx"

複数MCPサーバーの管理: .mcp.jsonの構成パターン

実務では複数のMCPサーバーを同時に使うことが普通になります。.mcp.jsonに複数のエントリをまとめておくことで、プロジェクトを開いた瞬間にチーム全員が同じツールセットを使える状態を作れます。

{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": {
        "Authorization": "Bearer ${GITHUB_TOKEN}"
      }
    },
    "database": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"]
    },
    "playwright": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@playwright/mcp@latest"]
    },
    "internal-docs": {
      "type": "http",
      "url": "https://mcp.internal.example.com",
      "headersHelper": "${CLAUDE_PROJECT_DIR:-.}/scripts/get-auth-headers.sh"
    }
  }
}

最後のinternal-docsの例のように、OAuthでも静的トークンでもない独自の認証方式(社内SSO等)を使うサーバーには、headersHelperでヘッダー生成コマンドを指定できます。Claude Codeは接続のたびにこのコマンドを実行し、その標準出力(JSON)をヘッダーとしてマージします。

{
  "mcpServers": {
    "internal-api": {
      "type": "http",
      "url": "https://mcp.internal.example.com",
      "headersHelper": "echo '{\"Authorization\": \"Bearer '\"$(get-token)\"'\"}'"
    }
  }
}

スコープが重複した場合の優先順位

同じ名前のサーバーが複数のスコープで定義されている場合、Claude Codeは以下の優先順位で1つだけを採用します(フィールドのマージは行われません)。

graph LR
    A["local scope"] -->|最優先| Z["採用される定義"]
    B["project scope"] -.->|localがなければ| Z
    C["user scope"] -.->|project/localがなければ| Z
    D["プラグイン提供"] -.->|それ以外がなければ| Z
 
    style A fill:#e8f4fd,stroke:#1a73e8
    style Z fill:#fff3e0,stroke:#e65100
管理コマンドで状態を可視化する

サーバーが増えてくると、どのスコープに何が定義されているか把握しづらくなります。claude mcp listで全体を俯瞰し、特定のサーバーの詳細やどのスコープが有効かを確認したい場合はclaude mcp get <name>を使いましょう。

動作確認とチームへの展開

ローカルで構成を検証する

.mcp.jsonを編集したら、新しいセッションを開いて承認プロンプトとツールの表示を確認します。

claude mcp list
/mcp

READMEまたはCLAUDE.mdに必要な環境変数を明記する

チームメンバーが.mcp.jsonをクローンしたときに迷わないよう、必要な環境変数の一覧と取得方法をドキュメント化しておきます。

プロジェクトスコープのファイルをコミットする

.mcp.json自体をリポジトリにコミットします。実際のトークン値が含まれていないことを最終確認してからコミットしましょう。

git add .mcp.json
git commit -m "MCPサーバー構成を追加"

チームメンバー側の初回承認を確認する

リポジトリをクローンしたメンバーがClaude Codeを起動すると、プロジェクトスコープのサーバーについて承認を求められます。これは意図しないプロセスの自動起動を防ぐ仕組みであり、正常な動作です。

よくある質問

Q. .mcp.jsonを編集してもセッション内に反映されません

Claude Codeはセッション開始時に.mcp.jsonを読み込みます。ファイルを編集した後は、セッションを再起動してください。それでも反映されない場合は/mcpで解析エラーが出ていないか確認します。

Q. 一度拒否したプロジェクトスコープのサーバーを再承認させたい

claude mcp reset-project-choicesで、プロジェクト単位の承認状態をリセットできます。

まとめ

  • ローカルサーバーはlocal/project/userのスコープを用途に応じて選び、CLAUDE_PROJECT_DIRでプロジェクト相対パスを解決する
  • リモートサーバーの認証は「静的トークン → OAuth → 事前設定済みクライアント」の優先順位で検討するとスムーズ
  • シークレットは.mcp.jsonに直書きせず${VAR}展開で分離し、コミット前に必ず混入チェックを行う
  • 複数サーバーは.mcp.jsonに集約し、スコープの優先順位(local > project > user > プラグイン)を理解しておく
個別サーバーの選定で迷ったら

GitHub・Slack・データベースなど具体的なサーバーごとの導入手順は、実践レシピの開発に役立つMCPサーバーを導入するも参考にしてください。本レッスンで学んだ構成パターンを、実際のサービスに当てはめる際のヒントになります。

次のステップ

既存のMCPサーバーを構築・運用できるようになったら、次は「自分たちのドメインに合わせたMCPサーバーを作る」段階に進みます。Lesson 3では、カスタムMCPツールの設計から実装、テスト・デバッグまでを扱います。