Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

add oauth support for http based mcp server (like copilot-cli)

オープン
#1,002 コメント 1 件 リアクション 1 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
25/100
issue の種類
機能追加
明瞭さ
説明が足りない
活発さ
静か
技術スタック
javascript, node.js

調査の方向性

まず nodejs/src/session.ts を読み、特に _onEvent と既存の permission および elicitation イベント処理を確認します。Issue に記載されている Copilot CLI のフローと、欠落している mcp.oauth_required パスを比較します。これには、トークン処理、callback 認証、provider への返却が含まれます。HTTP MCP サーバーが 401 の後に失敗するのではなく、説明されている OAuth フローを完了できれば完了です。

索引モデルが issue の本文から書いたものです。

説明

enhancement

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

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

github/copilot-sdk のほかの issue

github/copilot-sdk の issue をすべて見る

似ている issue

Java の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。