add oauth support for http based mcp server (like copilot-cli)
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 25/100
- issue の種類
- 機能追加
- 明瞭さ
- 説明が足りない
- 活発さ
- 静か
- 技術スタック
- javascript, node.js
- 領域
- api, authentication
調査の方向性
まず nodejs/src/session.ts を読み、特に _onEvent と既存の permission および elicitation イベント処理を確認します。Issue に記載されている Copilot CLI のフローと、欠落している mcp.oauth_required パスを比較します。これには、トークン処理、callback 認証、provider への返却が含まれます。HTTP MCP サーバーが 401 の後に失敗するのではなく、説明されている OAuth フローを完了できれば完了です。
索引モデルが issue の本文から書いたものです。
説明
There's no oauth support from the copilot-sdk, below is the analysis from AI:
The full OAuth delegation flow (5 steps):
Step 1 — CLI session checks if SDK is listening for OAuth events:
// @github/copilot app.js @ ~11489428
let o = this.hasEventListeners("mcp.oauth_required")
? (a,l,c) => this.pendingRequests.requestMcpOAuth(a,l,c)
: void 0; // ← No listeners? onOAuthRequired = undefined
this.mcpHost = new CT(j, n, ..., o, ...);
Step 2 — HTTP MCP server returns 401, CLI tries OAuth:
// @github/copilot app.js @ ~11252358 (processHttpServer)
try {
await this.registry.startHttpMcpClient(e, o); // ← 401 here
} catch(s) {
if (Zfe(s)) { // Zfe = detects auth-required error
let a = await this.getAuthProvider(e, n); // ← Step 3
if (a) { await this.registry.startHttpMcpClient(e, o, a); return; }
}
throw s;
}
Step 3 — getAuthProvider calls onOAuthRequired:
// @github/copilot app.js @ ~11248342
getAuthProvider(e, n) {
if (!this.onOAuthRequired) return; // ← Returns undefined if no handler!
let r = n.oauthClientId ? {clientId: n.oauthClientId, ...} : void 0;
let o = await this.onOAuthRequired(e, n.url, r); // ← Calls Step 4
return o;
}
Step 4 — requestMcpOAuth emits event and waits for response:
// @github/copilot app.js @ ~11267096
async requestMcpOAuth(e, n, r) {
let o = uuid();
let {promise, resolve} = Promise.withResolvers();
this.mcpOAuthRequests.set(o, {resolve});
this.emitEphemeral("mcp.oauth_required", { // ← Emits to SDK
requestId: o, serverName: e, serverUrl: n, staticClientConfig: r
});
return promise; // ← BLOCKS until respondToMcpOAuth() is called
}
Step 5 — SDK session.ts event handler — mcp.oauth_required is NOT handled:
// C:/src/copilot-sdk/nodejs/src/session.ts lines 380-448
private _onEvent(event: SessionEvent): void {
if (event.type === "tool.call") { ... }
else if (event.type === "permission.requested") { ... } // ✅ handled
else if (event.type === "elicitation.requested") { ... } // ✅ handled
else if (event.type === "capabilities.changed") { ... }
// ❌ NO "mcp.oauth_required" case — event is silently ignored
}
So the promise at Step 4 hangs forever → getAuthProvider never returns → processHttpServer catch block throws → server marked as "failed".
BUT — critically, in Step 1, hasEventListeners("mcp.oauth_required") returns false because the SDK never registers a listener. So onOAuthRequired is undefined, and Step 3 returns undefined immediately (no auth provider). The 401 error is then re-thrown, and the server fails without even attempting OAuth.
Here's the full OAuth flow as implemented in the Copilot CLI. The wT class (app.js @ ~11239897) is the OAuth authenticator:
CLI OAuth Flow (readable form):📋
class McpOAuthAuthenticator {
constructor(store) { this.store = store; }
async authenticate(serverUrl, options) {
const { onStatusChange, signal, staticClientConfig } = options;
// 1. Check for existing valid tokens
onStatusChange("checking_existing_tokens");
if (await this.hasValidTokens(serverUrl)) {
return this.createProvider(serverUrl, redirectUri, staticClientConfig);
}
// 2. Try refresh token if available
const tokens = await this.store.getTokens(serverUrl);
if (tokens?.refreshToken) {
onStatusChange("refreshing_tokens");
if (await this.tryRefreshTokens(serverUrl, staticClientConfig)) {
return this.createProvider(serverUrl, redirectUri, staticClientConfig);
}
}
// 3. Discover OAuth metadata from server URL
onStatusChange("discovering_metadata");
const resourceMetadata = await discoverResourceMetadata(serverUrl);
const { metadata } = await discoverAuthServerMetadata(authServerUrl);
// 4. Start local callback server on 127.0.0.1
onStatusChange("starting_callback_server");
const callbackServer = new CallbackServer();
const { callbackUrl } = await callbackServer.start();
// 5. Register OAuth client (dynamic registration) if needed
onStatusChange("checking_registration");
let clientInfo = await provider.clientInformation();
if (!clientInfo) {
onStatusChange("registering_client");
clientInfo = await registerClient(authServerUrl, { metadata, clientMetadata });
}
// 6. Build authorization URL and open browser
const { authorizationUrl, codeVerifier } = await buildAuthorizationUrl(
authServerUrl, { metadata, clientInformation, redirectUrl, state, scope }
);
onStatusChange("waiting_for_browser");
await openBrowser(authorizationUrl.toString()); // ← Opens browser!
// 7. Wait for callback with authorization code
onStatusChange("waiting_for_authorization");
const authCode = await callbackServer.waitForCallback(state, timeoutMs);
// 8. Exchange code for tokens
onStatusChange("exchanging_code");
const tokens = await exchangeCodeForTokens(
authServerUrl, { metadata, clientInformation, authorizationCode: authCode,
codeVerifier, redirectUri }
);
await provider.saveTokens(tokens);
return provider; // ← Returns auth provider for MCP client
}
}
This is a standard OAuth 2.0 Authorization Code + PKCE flow:
Check cached tokens → try refresh → discover OAuth metadata
Start local HTTP server on 127.0.0.1 for the callback
Register as an OAuth client (dynamic client registration)
Open a browser for user to authorize
Wait for redirect callback with auth code
Exchange code for access + refresh tokens
Return auth provider that attaches the token to MCP requests
- 主要言語
- Java
- スター
- 10.5k
- フォーク
- 1.5k
- 平均マージ
- 1日 9時間
- マージ済み PR(30日)
- 131
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
github/copilot-sdk のほかの issue
-
agentic-workflows
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
github/copilot-sdk#2709 · コメント 1 件 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 78/100
github/copilot-sdk#2673 ·
-
bug testing
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
github/copilot-sdk#2628 ·
-
agentic-workflows
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
github/copilot-sdk#2627 · コメント 1 件 ·
-
agentic-workflows
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
github/copilot-sdk#2493 ·
github/copilot-sdk の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
elastic/gradle-plugins#157 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
cryptomator/hub#497 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
johanhaleby/occurrent#1120 ·