Claude Media
OAuth callback port is already in useの原因と対処 — Claude Code

OAuth callback port is already in useの原因と対処 — Claude Code

Claude CodeでリモートMCPサーバーにOAuthでサインインすると出るポート競合エラーの原因と、lsofとnetstatで占有プロセスを特定して解消する手順を扱います。

リモートMCPサーバーへのOAuthサインインで「OAuth callback port <port> is already in use — another process may be holding it」と表示されたら、サインインの結果を受け取るためのローカルポートを別のプロセスが握っています。多くは固定コールバックポートの設定が原因です。メッセージに出ているコマンドで占有プロセスを見つけて止めるか、別のポートに切り替えれば解消します。

このエラーは何を意味しているか

リモートMCPサーバーにOAuthでサインインすると、Claude Codeはローカルで待ち受け用のリスナーを立て、ブラウザーからのコールバックを受け取ります。そのポートが別のプロセスに使われていると、サインインはここで失敗します。表示されるメッセージは次の形です。

OAuth callback port <port> is already in use — another process may be holding it. Run `lsof -ti:<port> -sTCP:LISTEN` to find it.

<port>には実際の番号が入ります。Windowsでは提案されるコマンドがnetstat -ano | findstr :<port>に変わります。

エラーの発生源はMCPサーバーでもブラウザーでもなく、手元のマシン上のポート競合です。サーバー側の設定を疑う前に、まず自分の環境を見ます。

なぜ固定ポートのときに起きやすいのか

Claude Codeは、ポートを指定しない場合は空いているポートを自分で選びます。エラーが起きやすいのは、次のどちらかで固定ポートを指定した場合です。

  • 環境変数のMCP_OAUTH_CALLBACK_PORT
  • claude mcp addなどに渡す--callback-portフラグ

固定するのは、MCPサーバー側に「http://localhost:PORT/callback」の形のリダイレクトURIを事前登録する必要があるからです。登録した番号とClaude Codeの待ち受け番号が一致しなければならないので、空いているポートを自動で選ぶ挙動が使えなくなります。

v2.1.289のclaude mcp add --helpには、このフラグが次のように載っています。

claude mcp add --help
  --callback-port <port>       Fixed port for OAuth callback (for servers
                               requiring pre-registered redirect URIs)
  --client-id <clientId>       OAuth client ID for HTTP/SSE servers
  --client-secret              Prompt for OAuth client secret (or set
                               MCP_CLIENT_SECRET env var)

--callback-portは単独でも、--client-idと組み合わせても使えます。単独なら動的クライアント登録のまま番号だけを固定します。

# 固定コールバックポートでサーバーを追加する例
claude mcp add --transport http \
  --callback-port 8080 \
  my-server https://mcp.example.com/mcp

サーバーが事前設定済みの認証情報を求める場合は、クライアントIDとポートを一緒に渡します。--client-secretを付けると、シークレットは入力を伏せたプロンプトで受け取ります。

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

CIのように対話できない環境では、シークレットをMCP_CLIENT_SECRET環境変数で渡すとプロンプトを省けます。コマンドの先頭にMCP_CLIENT_SECRET=your-secretを付けて同じclaude mcp addを実行する形です。このときシークレットがシェルの履歴やログに残る点に注意が必要です。

設定ファイルに書く場合は、oauthオブジェクトのcallbackPortで同じ指定ができます。

claude mcp add-json my-server \
  '{"type":"http","url":"https://mcp.example.com/mcp","oauth":{"callbackPort":8080}}'

登録時に選ぶ番号は、フォームがリダイレクトURIを求めるなら空いている任意のポートでかまいません。

占有しているプロセスを特定して止める

エラーメッセージのコマンドをそのまま実行します。macOSとLinuxの例です。ポートを仮に18765として、空いている状態と、別のプロセスが待ち受けている状態を続けて実行した結果を示します。

# 空いているとき
lsof -ti:18765 -sTCP:LISTEN; echo "exit=$?"
exit=1
 
# python3 -m http.server 18765 --bind 127.0.0.1 を動かしている間
lsof -ti:18765 -sTCP:LISTEN; echo "exit=$?"
84946
exit=0

-tを付けているので出力はプロセスIDだけになり、-sTCP:LISTENで待ち受け状態のものに絞られます。空いていれば何も表示せず、終了コード1で終わります。占有されていればPIDを表示して終了コード0を返すので、lsof -ti:<port> -sTCP:LISTEN || echo freeのようにシェルスクリプトの分岐にも使えます。

プロセスIDから中身を知りたいときは、続けてpsで名前を確認します。止めてよいプロセスかどうかは、この名前を見て判断します。

ps -p 84946 -o pid,command
  PID COMMAND
84946 …/Python.app/Contents/MacOS/Python -m http.server 18765 --bind 127.0.0.1

コマンドラインの引数まで出るので、開発サーバーなのか、常駐のツールなのかを見分けられます。どのアドレスで待ち受けているかも見たいときは、lsofに-nP -iTCP:<port>を渡します。

lsof -nP -iTCP:18765 -sTCP:LISTEN
COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
Python  84946 user   3u  IPv4 0x57f15f2d8f9544a1      0t0  TCP 127.0.0.1:18765 (LISTEN)

-nでホスト名の解決を省き、-Pでポート番号をサービス名に変えずに表示します。NAMEの列に127.0.0.1:18765と出ているので、このプロセスはループバックアドレスで待ち受けています。

Windowsではnetstatで待ち受けているプロセスのPIDを探します。

netstat -ano | findstr :8080
tasklist /FI "PID eq <PID>"

netstat -anoの最後の列がPIDです。tasklistで名前を引けます。findstr :8080は文字列で絞り込むだけなので、18080のように別の番号の行も混ざります。状態がLISTENINGの行で、ローカルアドレスの列が目的のポートで終わっているものを選びます。

止める操作はkill <PID>(Windowsはtaskkill /PID <PID>)などOS標準の方法で行います。ほかの作業で使っている開発サーバーやデータベースの可能性があるので、名前を確かめてから止めます。止めたあとに先ほどのlsofをもう一度実行し、出力が空で終了コード1に戻ったことを見てからサインインに進みます。

解消したらサインインをやり直す

ポートが空いたら、サインインを最初からやり直します。例は/mcpでサーバーを選ぶやり方です。

/mcp

シェルから直接実行したい場合はclaude mcp login <name>でも同じOAuthフローを動かせます。SSHセッションなどブラウザーが開けない環境では、認可URLが表示されます。手元のマシンでURLを開き、ブラウザーのアドレスバーに出たリダイレクトURL全体をプロンプトに貼り戻します。この貼り付けには対話的な端末が必要で、ssh -tで接続します。

ブラウザーが使える環境でも、--no-browserを付けると同じURLプロンプトに切り替わります。v2.1.289のclaude mcp login --helpでは、このフラグは次の説明です。

--no-browser  Print the authorization URL instead of opening a browser (for
              SSH/headless sessions — paste the redirect URL back when
              prompted)

認証後にブラウザーが接続エラーになった場合も、アドレスバーのURL全体をClaude Codeのプロンプトに貼れば、サインインを続けられます。

別のプロセスがそのポートを必要とし続ける場合

他のプログラムがそのポートを恒常的に使うなら、止めても再び競合します。その場合は、次の手順でポートそのものを切り替えます。

手順

ポートを切り替える3ステップ

  1. 1

    サーバー側で別の番号を登録する

    MCPサーバーの開発者ポータルなどで、空いている別の番号のhttp://localhost:PORT/callbackを登録します。

  2. 2

    Claude Code側の番号を揃える

    MCP_OAUTH_CALLBACK_PORTか--callback-portのうち、使っている方でその番号を指定します。

  3. 3

    サインインをやり直す

    /mcpからサーバーを選んで、サインインを最初から行います。

環境変数の例は次のとおりです。

export MCP_OAUTH_CALLBACK_PORT=8765
claude

MCP_OAUTH_CALLBACK_PORTは、事前設定済みの認証情報でMCPサーバーを追加するときに--callback-portの代わりに使える環境変数です。8765は例示用の番号です。どちらか一方だけを使い、番号がずれないようにします。

止めたのにまたエラーになるときの確認点

占有プロセスを止めて解決したはずなのに、同じエラーが戻ることがあります。確認する点は次の3つです。

まとめ

再発したときの3つの確認点

  • プロセスが戻っている

    止めた直後に別のプロセスが同じポートを取り直していないかを確かめます。サインインの直前にlsofをもう一度実行し、出力が空であることを見ます。

  • サーバー側の登録が古い

    別のポートに切り替えても、サーバーに登録済みのリダイレクトURIが元の番号のままだと、今度は不一致でサインインが失敗します。http://localhost:PORT/callbackも同じ番号に直します。

  • 指定場所の番号がずれている

    番号は環境変数、--callback-port、設定ファイルのoauth.callbackPortのどこにも書けます。片方だけ書き換えて古い番号が残っていないか、現在のシェルの値も確かめます。

シェルに残っている値は、echo $MCP_OAUTH_CALLBACK_PORT(PowerShellでは$env:MCP_OAUTH_CALLBACK_PORT)で見られます。

エラーの見分け方

似たエラーが複数あるので、メッセージの文言で切り分けます。

表示意味見る場所
OAuth callback port <port> is already in use意味指定ポートを他プロセスが占有見る場所lsofまたはnetstatで特定
No available ports for OAuth redirect意味ローカルポートをそもそも待ち受けできない見る場所セキュリティソフトやサンドボックスの設定
Incompatible auth server: does not support dynamic client registration意味サーバーが自動登録に非対応見る場所事前設定済みの認証情報が必要

2行目の「No available ports」は、127.0.0.1での待ち受け自体が妨げられているときに出ます。原因の例はセキュリティソフトやサンドボックスの制限です。v2.1.268より前はOSが割り当てるポートへのフォールバックがなく、Claude Codeが自分で選ぶ範囲のポートがすべて使えない場合にも出ました。Windowsでは、Hyper-Vが予約したポート範囲が原因になることがあります。

3行目は、ポートではなくサーバー側の対応状況の問題です。詳しくはGitHub MCPサーバーのOAuth接続エラーが具体例になります。Claude Code自体のログインで「Is port 0 in use?」と出る場合は、Failed to start OAuth callback serverの原因と回避が別の症状です。

リダイレクトURIの不一致と混同しない

固定ポートを使う設定では、ポートの競合とは別に「リダイレクトURIの不一致」でサインインが失敗する場合があります。v2.1.229のClaude Codeはhttp://127.0.0.1:PORT/callbackの形で送信しており、登録値を完全一致で照合するサーバーはこれを拒否しました。v2.1.231でlocalhostの形に戻っています。同じv2.1.231では、Slackのように事前登録済みのOAuthクライアントを使うサーバーで起きていた、リダイレクトURIの不一致によるサインイン失敗も修正されました。

v2.1.229を使っているなら、Claude Codeを更新するか、127.0.0.1形式のURIも一時的にサーバー側へ登録して回避できます。ポートが空いているのに失敗するときは、この不一致を確かめます。競合を解消してもエラーが変わるだけ、というケースの切り分けにもなります。Entra IDが認可サーバーなら、AADSTS9010010のように別の原因も考えられます。

ポートを固定するかどうかの分かれ目

固定が必要かどうかは、サーバーのドキュメントでリダイレクトURIの事前登録を求められているかで決まります。

くらべる

ポートの扱いは接続先のサーバーで分かれる

動的クライアント登録に対応

指定しない

空いているポートをClaude Codeが選ぶので、固定ポートの衝突は起きません。

リダイレクトURIの事前登録が必要

固定する

登録した番号を--callback-portなどで指定します。事前設定済みの認証情報を使うときも、サーバーに登録した番号と揃えます。

事前登録が必要な側では、登録する番号をほかのプロセスが使っていない値にすれば、このエラーとは衝突しません。

認証まわりのそのほかの不調は、次の記事が入口になります。

まとめ

固定したコールバックポートを別のプロセスが握っているときは、止めるか、サーバー側の登録ごと別の番号に移すかのどちらかです。

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