MCP OAuth .well-known 发现

这个项目是一个可运行的TypeScript示例,说明如何使用GitHub作为OAuth授权服务器来保护MCP服务器。它教完整 .well-known 发现模式:像VS Code和IntelliJ这样的MCP客户端如何自动找到您的授权服务器,为什么范围和授权类型很重要,以及基于浏览器的登录弹出窗口如何端到端工作。
服务器公开了两个MCP工具: get_status (返回服务器运行状况、副驾驶配额使用情况和当前本地观察到的使用情况)以及 java_expert_answer (将Java问题转发给带有Java特定指令的Copilot会话)。
______________________________________________________________________
核心思想: .well-known 作为身份验证的入口点
本报告的核心教导是 MCP客户端永远不需要被告知在哪里进行身份验证相反,服务器在众所周知的路径上发布发现文档,客户端仅从这些文档中找出所有内容——授权服务器、令牌端点、作用域和授权类型。
这如下 RFC 8414 (OAuth授权服务器元数据)和2026年3月的MCP授权指南。
客户端连接到的那一刻 /mcp 如果没有令牌,服务器会用 WWW-Authenticate 指向的标题 .well-known URL。从该单一URL,客户端拥有驱动OAuth流、打开浏览器和使用新令牌重试请求所需的一切——所有这些都不需要用户手动配置任何内容。
______________________________________________________________________
两者 .well-known 端点
1.MCP能力发现-- GET /.well-known/mcp.json
这是 服务器卡.它告诉客户端服务器说的是哪种MCP版本,MCP端点住在哪里,以及需要什么类型的身份验证。客户端可以在尝试连接之前获取此信息,也可以在收到 401.
{
"mcp_version": "2025-11-25",
"server_info": {
"name": "enterprise-mcp",
"version": "1.0.0"
},
"endpoints": [
{
"url": "https://your-mcp-domain.com/mcp",
"transport": "streamable-http",
"auth_type": "oauth2"
}
]
}这 auth_type: "oauth2" 字段表示此端点未打开。客户端必须获取承载令牌才能使用它。如果没有此字段,天真的客户端可能会尝试未经身份验证的访问,并且不明白为什么它一直在接收 401.
2.OAuth资源元数据-- GET /.well-known/oauth-protected-resource
这就是身份验证线路所在的地方。当客户收到 401 来自的挑战 /mcp,它获取此文档以发现运行OAuth流所需的每个细节。
{
"resource": "https://your-mcp-domain.com/mcp",
"resource_name": "Enterprise Data Server",
"authorization_servers": [
"https://github.com/login/oauth"
],
"authorization_endpoint": "https://github.com/login/oauth/authorize",
"token_endpoint": "https://github.com/login/oauth/access_token",
"client_id": "YOUR_GITHUB_CLIENT_ID",
"scopes_supported": ["read:user", "repo", "offline_access"],
"grant_types_supported": ["authorization_code", "refresh_token"]
}此仓库从Express提供此文档并填充 client_id 从 CLIENT_ID 运行时的环境变量。
______________________________________________________________________
这 401 挑战:如何触发发现
发现流程从 WWW-Authenticate 响应标头。当未经身份验证的请求点击时 POST /mcp,中间件()返回:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="http://localhost:3000/.well-known/oauth-protected-resource"此标头是发送给客户端的信号。它说:“我需要一个承载令牌,解释如何获取承载令牌的元数据文档位于此URL。”然后,客户端获取该URL,从中读取授权和令牌端点,并启动OAuth流。每个现代MCP客户端——VS Code、IntelliJ和CLI工具——都理解这种挑战格式。
// src/middleware/validateGitHub.ts
if (!token) {
res.setHeader(
"WWW-Authenticate",
'Bearer resource_metadata="http://localhost:3000/.well-known/oauth-protected-resource"'
);
res.status(401).json({ error: "Authentication Required" });
return;
}______________________________________________________________________
为什么范围很重要
这 scopes_supported 数组输入 /.well-known/oauth-protected-resource 不是装饰性的。它告诉客户端在构建授权URL时要请求的确切GitHub权限范围。如果请求了错误的范围,则生成的令牌将无法访问您的服务器所依赖的GitHub API。
此服务器通过调用来验证令牌 https://api.github.com/user。为了成功,令牌至少需要 read:user。如果您的服务器也读取存储库数据,则令牌需要 repo.
"scopes_supported": ["read:user", "repo"]实用规则: 只宣传服务器实际使用的范围。过度请求作用域会侵蚀用户信任,并可能触发阻止广泛权限令牌的企业GitHub App策略。当您的服务器尝试调用未授予令牌权限的GitHub API时,在请求范围下会导致运行时失败。
______________________________________________________________________
为什么拨款类型很重要
这 grant_types_supported 数组告诉客户端此服务器的授权服务器(GitHub)支持哪个OAuth流。对于IDE客户端来说,有两件事很重要:
| 资助类型 | 目的 |
|---|---|
authorization_code | 基于浏览器的标准登录流程。客户端打开浏览器,用户登录,GitHub用代码重定向回来,客户端用代码交换令牌。 |
refresh_token | 允许客户端在后台静默续订过期的访问令牌,而无需用户再次登录。 |
"grant_types_supported": ["authorization_code", "refresh_token"]如果你忽略了 refresh_token 从该列表中,客户端将不会尝试后台续订。每次访问令牌过期时(通常在启用令牌过期后8小时后),用户将被迫重新登录。对于一次开放数天的IDE来说,这是一个重大的可用性问题。
要在GitHub端启用刷新令牌,请打开 用户到服务器令牌过期 在GitHub OAuth应用程序设置中(可选功能下)。这会导致GitHub在访问令牌旁边发出成对的刷新令牌。
______________________________________________________________________
IDE回调URL:VS Code和IntelliJ
当GitHub在登录后重定向回时,它需要重定向到IDE已经监听的URL。这些是内置在每个IDE中的固定重定向URI处理程序,必须在您的GitHub OAuth应用程序中注册,才能使流程正常工作。
VS代码
VS Code附带了两个内置的重定向处理程序:
| URL | 用法 |
|---|---|
https://vscode.dev/redirect | 基于Web的VS Code(vscode.dev)和桌面VS Code远程流 |
http://127.0.0.1:33418 | 本地VS代码和VS代码内部人员桌面流 |
桌面处理程序在端口上使用本地HTTP服务器 33418 VS代码在OAuth流程中旋转。GitHub重定向到 http://127.0.0.1:33418 使用授权码,VS code会拦截它,将其交换为令牌,并关闭本地服务器。
IntelliJ(和其他JetBrains IDE)
IntelliJ使用本地内置web服务器,所有JetBrains IDE都会自动启动:
| URL | 用法 |
|---|---|
http://127.0.0.1:63342/api/github/oauth/callback | 所有JetBrains IDE(IntelliJ IDEA、PyCharm、WebStorm等) |
港口 63342 是默认的JetBrains内置服务器端口。回调路径 /api/github/oauth/callback 由JetBrains IDE附带的GitHub插件原生处理。
注册所有三个
在您的GitHub OAuth应用程序中,在以下位置添加所有三个回调URL 授权回调URL (GitHub允许多个)。这确保了这些IDE上的用户无需额外配置即可实现无缝的一键登录。
https://vscode.dev/redirect
http://127.0.0.1:33418
http://127.0.0.1:63342/api/github/oauth/callback______________________________________________________________________
端到端身份验证流程
综上所述,这就是用户第一次添加此MCP服务器时发生的情况。这 客户 是VS Code或IntelliJ中的Copilot插件——IDE本身驱动这里显示的每一步;当IDE提示用户时,用户只需点击“登录”即可。
sequenceDiagram
actor User
participant IDE as VS Code Copilot
or IntelliJ Copilot Plugin
participant Server as MCP Server
(this repo)
participant GitHub as GitHub OAuth
User->>IDE: Add MCP server URL to settings
note over IDE: VS Code: .vscode/mcp.json
IntelliJ: MCP plugin settings
IDE->>Server: POST /mcp (no token)
Server-->>IDE: 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata=".../.well-known/oauth-protected-resource"
note over IDE: Reads resource_metadata URL
from WWW-Authenticate header
IDE->>Server: GET /.well-known/oauth-protected-resource
Server-->>IDE: { authorization_endpoint, token_endpoint,
client_id, scopes_supported, grant_types_supported }
note over IDE: Now knows WHERE to authenticate
and WHAT permissions to request
IDE->>GitHub: Open browser to authorization_endpoint
?client_id=...&scope=read:user+repo&redirect_uri=...
note over IDE: VS Code redirects to:
https://vscode.dev/redirect (web)
http://127.0.0.1:33418 (desktop)
IntelliJ redirects to:
http://127.0.0.1:63342/api/github/oauth/callback
User->>GitHub: Sign in and approve scopes
GitHub-->>IDE: Redirect to callback URL with ?code=...
note over IDE: Intercepts redirect on local port
extracts the authorization code
IDE->>GitHub: POST token_endpoint
{ code, client_id }
GitHub-->>IDE: { access_token, refresh_token }
note over IDE: Stores tokens securely
Uses refresh_token to renew silently later
IDE->>Server: POST /mcp
Authorization: Bearer
Server->>GitHub: GET /user (validate token)
GitHub-->>Server: { login: "username", ... }
Server-->>IDE: MCP response (tools, results)
IDE-->>User: Copilot uses MCP tools transparently______________________________________________________________________
GitHub OAuth应用程序设置
创建应用程序
- 首选 设置→ 开发人员设置→ OAuth应用程序→ 新建OAuth应用程序 在GitHub上。
- 集 主页网址 到您的MCP服务器的公共基础URL。
- 添加上面IDE部分中列出的所有三个回调URL。
启用刷新令牌
在OAuth应用程序设置页面中,滚动到 可选功能 并启用 用户到服务器令牌过期如果没有这个,GitHub会发行不到期的代币和 refresh_token 授权类型无效。
______________________________________________________________________
需求
- Node.js 18或更新版本
- npm
- GitHub OAuth应用程序
CLIENT_ID以及上面配置的回调URL
环境设置
创建一个 .env.local 项目根目录中的文件:
CLIENT_ID=your_github_oauth_app_client_id如果满足以下条件,服务器将在启动时退出 CLIENT_ID 不见了。
安装并运行
npm install
npm run build
npm start服务器正在监听 http://localhost:3000.
VS代码MCP配置
工作空间包括 .vscode/mcp.json 将VS代码指向本地服务器:
{
"servers": {
"my-mcp-server-1": {
"url": "http://localhost:3000/mcp",
"type": "http"
}
},
"inputs": []
}因为 /mcp 如果受保护,客户端必须使用GitHub承载令牌进行身份验证。
用户设置
从MCP用户的角度来看,设置故意简单:
- 在VS Code中,将MCP服务器URL添加到
mcp.json. - 当服务器对请求提出质疑时,请按照GitHub登录流程进行操作。
- 身份验证后,客户端使用获取的令牌重试。
对于IntelliJ风格的MCP客户端,相同的发现端点允许IDE在配置MCP URL后自动打开自己的登录流。
工具行为
get_status
此工具现在报告:
- 服务器运行状况(
System Online) - 副驾驶配额期使用情况,包括
premium_interactions - 从Java工具会话中记录的当天本地观察到的使用情况
java_expert_answer
此工具使用以下命令创建Copilot客户端会话 @github/copilot-sdk,注入以Java为中心的自定义指令,发送提供的问题,并返回结果答案文本。
进度更新通过MCP发出 notifications/progress 当工具运行时;最终的工具响应仅包含答案文本。
推理驱动的进度通知目前以短步骤消息的形式发送,并截断了推理增量的预览。
当前实施使用:
- 型号:
gpt-5.4 - 权限处理:
approveAll - 中定义的自定义系统指令
src/javaExpertInstructions.ts
项目结构
src/
index.ts Express app, MCP server setup, tool registration
javaExpertInstructions.ts Java-specific system instructions for Copilot sessions
middleware/
validateGitHub.ts GitHub bearer token validation middleware
tools/
registerJavaExpertTool.ts Java tool registration and Copilot session handling
registerStatusTool.ts Status tool registration with Copilot quota lookup
usage/
dailyUsageStore.ts Local per-day Copilot usage ledger for this server注意事项和限制
- 服务器URL和OAuth元数据当前使用硬编码
http://localhost:3000价值观。 - 目前还没有开发观察脚本;使用
npm run build之前npm start. POST /mcp是唯一经过身份验证的端点。发现终结点是公开的。- README包括用于公共部署的生产样式示例,但签入代码是一个本地示例服务器。
- 当前实施不做广告
offline_access并且不包括resource_name受保护资源元数据中的字段。 - 每日使用量是此服务器实例的本地使用量,反映了通过以下方式观察到的使用情况
java_expert_answer;此示例服务器不公开帐户范围内的日常使用情况。
