add oauth support for http based mcp server (like copilot-cli)
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 25/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Cần làm rõ
- Mức độ hoạt động
- Ít trao đổi
- Công nghệ
- javascript, node.js
- Lĩnh vực
- api, authentication
Hướng nghiên cứu
Bắt đầu bằng cách đọc nodejs/src/session.ts, đặc biệt là _onEvent và phần xử lý các sự kiện permission và elicitation hiện có. So sánh đường dẫn mcp.oauth_required còn thiếu với luồng Copilot CLI được mô tả trong issue, bao gồm việc xử lý token, xác thực callback và việc trả về cho provider. Được xem là hoàn tất khi các máy chủ HTTP MCP có thể hoàn thành luồng OAuth được mô tả thay vì bị lỗi sau 401.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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
- Ngôn ngữ chính
- Java
- Star
- 10.5k
- Fork
- 1.5k
- Merge trung bình
- 1 ngày 9 giờ
- Pull request đã merge (30 ngày)
- 131
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của github/copilot-sdk
-
agentic-workflows
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
github/copilot-sdk#2709 · 1 bình luận ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
github/copilot-sdk#2673 ·
-
bug testing
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
github/copilot-sdk#2628 ·
-
agentic-workflows
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
github/copilot-sdk#2627 · 1 bình luận ·
-
agentic-workflows
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
github/copilot-sdk#2493 ·
Tất cả issue của github/copilot-sdk
Issue tương tự
-
awaiting triage bug Causes friction Hop Gui P1 P2 Transforms
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
apache/flink-agents#1152 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
jenkinsci/blueocean-plugin#5417 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
objectionary/eo-graphs#75 ·