@samanhappy/mcphub 0.9.16 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/README.zh.md +65 -0
- package/dist/config/index.js +17 -4
- package/dist/config/index.js.map +1 -1
- package/dist/controllers/authController.js +16 -0
- package/dist/controllers/authController.js.map +1 -1
- package/dist/controllers/oauthCallbackController.js +276 -0
- package/dist/controllers/oauthCallbackController.js.map +1 -0
- package/dist/controllers/serverController.js +1 -1
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/controllers/userController.js +23 -1
- package/dist/controllers/userController.js.map +1 -1
- package/dist/routes/index.js +3 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/server.js +10 -0
- package/dist/server.js.map +1 -1
- package/dist/services/mcpOAuthProvider.js +472 -0
- package/dist/services/mcpOAuthProvider.js.map +1 -0
- package/dist/services/mcpService.js +321 -191
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/oauthClientRegistration.js +444 -0
- package/dist/services/oauthClientRegistration.js.map +1 -0
- package/dist/services/oauthService.js +216 -0
- package/dist/services/oauthService.js.map +1 -0
- package/dist/services/oauthSettingsStore.js +106 -0
- package/dist/services/oauthSettingsStore.js.map +1 -0
- package/dist/utils/passwordValidation.js +38 -0
- package/dist/utils/passwordValidation.js.map +1 -0
- package/dist/utils/path.js.map +1 -1
- package/frontend/dist/assets/index-BP5IZhlg.js +251 -0
- package/frontend/dist/assets/index-BP5IZhlg.js.map +1 -0
- package/frontend/dist/assets/index-C_58ZhSt.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +3 -2
- package/frontend/dist/assets/index-DDUK8zl_.js +0 -217
- package/frontend/dist/assets/index-DDUK8zl_.js.map +0 -1
- package/frontend/dist/assets/index-DcVhHcn9.css +0 -1
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ MCPHub makes it easy to manage and scale multiple MCP (Model Context Protocol) s
|
|
|
19
19
|
- **Hot-Swappable Configuration**: Add, remove, or update MCP servers on the fly — no downtime required.
|
|
20
20
|
- **Group-Based Access Control**: Organize servers into customizable groups for streamlined permissions management.
|
|
21
21
|
- **Secure Authentication**: Built-in user management with role-based access powered by JWT and bcrypt.
|
|
22
|
+
- **OAuth 2.0 Support**: Full OAuth support for upstream MCP servers with proxy authorization capabilities.
|
|
23
|
+
- **Environment Variable Expansion**: Use environment variables anywhere in your configuration for secure credential management. See [Environment Variables Guide](docs/environment-variables.md).
|
|
22
24
|
- **Docker-Ready**: Deploy instantly with our containerized setup.
|
|
23
25
|
|
|
24
26
|
## 🔧 Quick Start
|
|
@@ -57,6 +59,45 @@ Create a `mcp_settings.json` file to customize your server settings:
|
|
|
57
59
|
}
|
|
58
60
|
```
|
|
59
61
|
|
|
62
|
+
#### OAuth Configuration (Optional)
|
|
63
|
+
|
|
64
|
+
MCPHub supports OAuth 2.0 for authenticating with upstream MCP servers. See the [OAuth feature guide](docs/features/oauth.mdx) for a full walkthrough. In practice you will run into two configuration patterns:
|
|
65
|
+
|
|
66
|
+
- **Dynamic registration servers** (e.g., Vercel, Linear) publish all metadata and allow MCPHub to self-register. Simply declare the server URL and MCPHub handles the rest.
|
|
67
|
+
- **Manually provisioned servers** (e.g., GitHub Copilot) require you to create an OAuth App and provide the issued client ID/secret to MCPHub.
|
|
68
|
+
|
|
69
|
+
Dynamic registration example:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"vercel": {
|
|
75
|
+
"type": "sse",
|
|
76
|
+
"url": "https://mcp.vercel.com"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Manual registration example:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"mcpServers": {
|
|
87
|
+
"github": {
|
|
88
|
+
"type": "sse",
|
|
89
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
90
|
+
"oauth": {
|
|
91
|
+
"clientId": "${GITHUB_OAUTH_APP_ID}",
|
|
92
|
+
"clientSecret": "${GITHUB_OAUTH_APP_SECRET}"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For manual providers, create the OAuth App in the upstream console, set the redirect URI to `http://localhost:3000/oauth/callback` (or your deployed domain), and then plug the credentials into the dashboard or config file.
|
|
100
|
+
|
|
60
101
|
### Docker Deployment
|
|
61
102
|
|
|
62
103
|
**Recommended**: Mount your custom config:
|
|
@@ -106,7 +147,11 @@ This endpoint provides a unified streamable HTTP interface for all your MCP serv
|
|
|
106
147
|
Smart Routing is MCPHub's intelligent tool discovery system that uses vector semantic search to automatically find the most relevant tools for any given task.
|
|
107
148
|
|
|
108
149
|
```
|
|
150
|
+
# Search across all servers
|
|
109
151
|
http://localhost:3000/mcp/$smart
|
|
152
|
+
|
|
153
|
+
# Search within a specific group
|
|
154
|
+
http://localhost:3000/mcp/$smart/{group}
|
|
110
155
|
```
|
|
111
156
|
|
|
112
157
|
**How it Works:**
|
|
@@ -115,6 +160,7 @@ http://localhost:3000/mcp/$smart
|
|
|
115
160
|
2. **Semantic Search**: User queries are converted to vectors and matched against tool embeddings using cosine similarity
|
|
116
161
|
3. **Intelligent Filtering**: Dynamic thresholds ensure relevant results without noise
|
|
117
162
|
4. **Precise Execution**: Found tools can be directly executed with proper parameter validation
|
|
163
|
+
5. **Group Scoping**: Optionally limit searches to servers within a specific group for focused results
|
|
118
164
|
|
|
119
165
|
**Setup Requirements:**
|
|
120
166
|
|
|
@@ -126,6 +172,23 @@ To enable Smart Routing, you need:
|
|
|
126
172
|
- OpenAI API key (or compatible embedding service)
|
|
127
173
|
- Enable Smart Routing in MCPHub settings
|
|
128
174
|
|
|
175
|
+
**Group-Scoped Smart Routing**:
|
|
176
|
+
|
|
177
|
+
You can combine Smart Routing with group filtering to search only within specific server groups:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
# Search only within production servers
|
|
181
|
+
http://localhost:3000/mcp/$smart/production
|
|
182
|
+
|
|
183
|
+
# Search only within development servers
|
|
184
|
+
http://localhost:3000/mcp/$smart/development
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
This enables:
|
|
188
|
+
- **Focused Discovery**: Find tools only from relevant servers
|
|
189
|
+
- **Environment Isolation**: Separate tool discovery by environment (dev, staging, prod)
|
|
190
|
+
- **Team-Based Access**: Limit tool search to team-specific server groups
|
|
191
|
+
|
|
129
192
|
**Group-Specific Endpoints (Recommended)**:
|
|
130
193
|
|
|
131
194
|

|
|
@@ -164,7 +227,11 @@ http://localhost:3000/sse
|
|
|
164
227
|
For smart routing, use:
|
|
165
228
|
|
|
166
229
|
```
|
|
230
|
+
# Search across all servers
|
|
167
231
|
http://localhost:3000/sse/$smart
|
|
232
|
+
|
|
233
|
+
# Search within a specific group
|
|
234
|
+
http://localhost:3000/sse/$smart/{group}
|
|
168
235
|
```
|
|
169
236
|
|
|
170
237
|
For targeted access to specific server groups, use the group-based SSE endpoint:
|
package/README.zh.md
CHANGED
|
@@ -57,6 +57,45 @@ MCPHub 通过将多个 MCP(Model Context Protocol)服务器组织为灵活
|
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
#### OAuth 配置(可选)
|
|
61
|
+
|
|
62
|
+
MCPHub 支持通过 OAuth 2.0 访问上游 MCP 服务器。完整说明请参阅[《OAuth 功能指南》](docs/zh/features/oauth.mdx)。实际使用中通常会遇到两类配置:
|
|
63
|
+
|
|
64
|
+
- **支持动态注册的服务器**(如 Vercel、Linear):会公开全部元数据,MCPHub 可自动注册并完成授权,仅需声明服务器地址。
|
|
65
|
+
- **需要手动配置客户端的服务器**(如 GitHub Copilot):需要在提供商后台创建 OAuth 应用,并将获得的 Client ID/Secret 写入 MCPHub。
|
|
66
|
+
|
|
67
|
+
动态注册示例:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"vercel": {
|
|
73
|
+
"type": "sse",
|
|
74
|
+
"url": "https://mcp.vercel.com"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
手动注册示例:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"github": {
|
|
86
|
+
"type": "sse",
|
|
87
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
88
|
+
"oauth": {
|
|
89
|
+
"clientId": "${GITHUB_OAUTH_APP_ID}",
|
|
90
|
+
"clientSecret": "${GITHUB_OAUTH_APP_SECRET}"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
对于需要手动注册的提供商,请先在上游控制台创建 OAuth 应用,将回调地址设置为 `http://localhost:3000/oauth/callback`(或你的部署域名),然后在控制台或配置文件中填写凭据。
|
|
98
|
+
|
|
60
99
|
### Docker 部署
|
|
61
100
|
|
|
62
101
|
**推荐**:挂载自定义配置:
|
|
@@ -106,7 +145,11 @@ http://localhost:3000/mcp
|
|
|
106
145
|
智能路由是 MCPHub 的智能工具发现系统,使用向量语义搜索自动为任何给定任务找到最相关的工具。
|
|
107
146
|
|
|
108
147
|
```
|
|
148
|
+
# 在所有服务器中搜索
|
|
109
149
|
http://localhost:3000/mcp/$smart
|
|
150
|
+
|
|
151
|
+
# 在特定分组中搜索
|
|
152
|
+
http://localhost:3000/mcp/$smart/{group}
|
|
110
153
|
```
|
|
111
154
|
|
|
112
155
|
**工作原理:**
|
|
@@ -115,6 +158,7 @@ http://localhost:3000/mcp/$smart
|
|
|
115
158
|
2. **语义搜索**:用户查询转换为向量并使用余弦相似度与工具嵌入匹配
|
|
116
159
|
3. **智能筛选**:动态阈值确保相关结果且无噪声
|
|
117
160
|
4. **精确执行**:找到的工具可以直接执行并进行适当的参数验证
|
|
161
|
+
5. **分组限定**:可选择将搜索限制在特定分组的服务器内以获得更精确的结果
|
|
118
162
|
|
|
119
163
|
**设置要求:**
|
|
120
164
|
|
|
@@ -126,6 +170,23 @@ http://localhost:3000/mcp/$smart
|
|
|
126
170
|
- OpenAI API 密钥(或兼容的嵌入服务)
|
|
127
171
|
- 在 MCPHub 设置中启用智能路由
|
|
128
172
|
|
|
173
|
+
**分组限定的智能路由**:
|
|
174
|
+
|
|
175
|
+
您可以将智能路由与分组筛选结合使用,仅在特定服务器分组内搜索:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
# 仅在生产服务器中搜索
|
|
179
|
+
http://localhost:3000/mcp/$smart/production
|
|
180
|
+
|
|
181
|
+
# 仅在开发服务器中搜索
|
|
182
|
+
http://localhost:3000/mcp/$smart/development
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
这样可以实现:
|
|
186
|
+
- **精准发现**:仅从相关服务器查找工具
|
|
187
|
+
- **环境隔离**:按环境(开发、测试、生产)分离工具发现
|
|
188
|
+
- **基于团队的访问**:将工具搜索限制在特定团队的服务器分组
|
|
189
|
+
|
|
129
190
|
**基于分组的 HTTP 端点(推荐)**:
|
|
130
191
|

|
|
131
192
|
要针对特定服务器分组进行访问,请使用基于分组的 HTTP 端点:
|
|
@@ -164,7 +225,11 @@ http://localhost:3000/sse
|
|
|
164
225
|
要启用智能路由,请使用:
|
|
165
226
|
|
|
166
227
|
```
|
|
228
|
+
# 在所有服务器中搜索
|
|
167
229
|
http://localhost:3000/sse/$smart
|
|
230
|
+
|
|
231
|
+
# 在特定分组中搜索
|
|
232
|
+
http://localhost:3000/sse/$smart/{group}
|
|
168
233
|
```
|
|
169
234
|
|
|
170
235
|
要针对特定服务器分组进行访问,请使用基于分组的 SSE 端点:
|
package/dist/config/index.js
CHANGED
|
@@ -77,22 +77,35 @@ export const getSettingsCacheInfo = () => {
|
|
|
77
77
|
};
|
|
78
78
|
};
|
|
79
79
|
export function replaceEnvVars(input) {
|
|
80
|
-
// Handle object input
|
|
80
|
+
// Handle object input - recursively expand all nested values
|
|
81
81
|
if (input && typeof input === 'object' && !Array.isArray(input)) {
|
|
82
82
|
const res = {};
|
|
83
83
|
for (const [key, value] of Object.entries(input)) {
|
|
84
84
|
if (typeof value === 'string') {
|
|
85
85
|
res[key] = expandEnvVars(value);
|
|
86
86
|
}
|
|
87
|
+
else if (typeof value === 'object' && value !== null) {
|
|
88
|
+
// Recursively handle nested objects and arrays
|
|
89
|
+
res[key] = replaceEnvVars(value);
|
|
90
|
+
}
|
|
87
91
|
else {
|
|
88
|
-
|
|
92
|
+
// Preserve non-string, non-object values (numbers, booleans, etc.)
|
|
93
|
+
res[key] = value;
|
|
89
94
|
}
|
|
90
95
|
}
|
|
91
96
|
return res;
|
|
92
97
|
}
|
|
93
|
-
// Handle array input
|
|
98
|
+
// Handle array input - recursively expand all elements
|
|
94
99
|
if (Array.isArray(input)) {
|
|
95
|
-
return input.map((item) =>
|
|
100
|
+
return input.map((item) => {
|
|
101
|
+
if (typeof item === 'string') {
|
|
102
|
+
return expandEnvVars(item);
|
|
103
|
+
}
|
|
104
|
+
else if (typeof item === 'object' && item !== null) {
|
|
105
|
+
return replaceEnvVars(item);
|
|
106
|
+
}
|
|
107
|
+
return item;
|
|
108
|
+
});
|
|
96
109
|
}
|
|
97
110
|
// Handle string input
|
|
98
111
|
if (typeof input === 'string') {
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,8 @@ import { validationResult } from 'express-validator';
|
|
|
3
3
|
import { findUserByUsername, verifyPassword, createUser, updateUserPassword, } from '../models/User.js';
|
|
4
4
|
import { getDataService } from '../services/services.js';
|
|
5
5
|
import { JWT_SECRET } from '../config/jwt.js';
|
|
6
|
+
import { validatePasswordStrength, isDefaultPassword } from '../utils/passwordValidation.js';
|
|
7
|
+
import { getPackageVersion } from '../utils/version.js';
|
|
6
8
|
const dataService = getDataService();
|
|
7
9
|
const TOKEN_EXPIRY = '24h';
|
|
8
10
|
// Login user
|
|
@@ -46,6 +48,9 @@ export const login = async (req, res) => {
|
|
|
46
48
|
isAdmin: user.isAdmin || false,
|
|
47
49
|
},
|
|
48
50
|
};
|
|
51
|
+
// Check if user is admin with default password
|
|
52
|
+
const version = getPackageVersion();
|
|
53
|
+
const isUsingDefaultPassword = user.username === 'admin' && user.isAdmin && isDefaultPassword(password) && version !== 'dev';
|
|
49
54
|
jwt.sign(payload, JWT_SECRET, { expiresIn: TOKEN_EXPIRY }, (err, token) => {
|
|
50
55
|
if (err)
|
|
51
56
|
throw err;
|
|
@@ -58,6 +63,7 @@ export const login = async (req, res) => {
|
|
|
58
63
|
isAdmin: user.isAdmin,
|
|
59
64
|
permissions: dataService.getPermissions(user),
|
|
60
65
|
},
|
|
66
|
+
isUsingDefaultPassword,
|
|
61
67
|
});
|
|
62
68
|
});
|
|
63
69
|
}
|
|
@@ -147,6 +153,16 @@ export const changePassword = async (req, res) => {
|
|
|
147
153
|
const { currentPassword, newPassword } = req.body;
|
|
148
154
|
const username = req.user.username;
|
|
149
155
|
try {
|
|
156
|
+
// Validate new password strength
|
|
157
|
+
const validationResult = validatePasswordStrength(newPassword);
|
|
158
|
+
if (!validationResult.isValid) {
|
|
159
|
+
res.status(400).json({
|
|
160
|
+
success: false,
|
|
161
|
+
message: 'Password does not meet security requirements',
|
|
162
|
+
errors: validationResult.errors,
|
|
163
|
+
});
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
150
166
|
// Find user by username
|
|
151
167
|
const user = findUserByUsername(username);
|
|
152
168
|
if (!user) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authController.js","sourceRoot":"","sources":["../../src/controllers/authController.ts"],"names":[],"mappings":"AACA,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"authController.js","sourceRoot":"","sources":["../../src/controllers/authController.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Callback Controller
|
|
3
|
+
*
|
|
4
|
+
* Handles OAuth 2.0 authorization callbacks for upstream MCP servers.
|
|
5
|
+
*
|
|
6
|
+
* This controller implements a simplified callback flow that relies on the MCP SDK
|
|
7
|
+
* to handle the complete OAuth token exchange:
|
|
8
|
+
*
|
|
9
|
+
* 1. Extract authorization code from callback URL
|
|
10
|
+
* 2. Find the corresponding server using the state parameter
|
|
11
|
+
* 3. Store the authorization code temporarily
|
|
12
|
+
* 4. Reconnect the server - SDK's auth() function will:
|
|
13
|
+
* - Automatically discover OAuth endpoints
|
|
14
|
+
* - Exchange the code for tokens using PKCE
|
|
15
|
+
* - Save tokens via our OAuthClientProvider.saveTokens()
|
|
16
|
+
*/
|
|
17
|
+
import { getServerByName, getServerByOAuthState, createTransportFromConfig, } from '../services/mcpService.js';
|
|
18
|
+
import { getNameSeparator, loadSettings } from '../config/index.js';
|
|
19
|
+
/**
|
|
20
|
+
* Generate HTML response page with i18n support
|
|
21
|
+
*/
|
|
22
|
+
const generateHtmlResponse = (type, title, message, details, autoClose = false) => {
|
|
23
|
+
const backgroundColor = type === 'error' ? '#fee' : '#efe';
|
|
24
|
+
const borderColor = type === 'error' ? '#fcc' : '#cfc';
|
|
25
|
+
const titleColor = type === 'error' ? '#c33' : '#3c3';
|
|
26
|
+
const buttonColor = type === 'error' ? '#c33' : '#3c3';
|
|
27
|
+
return `
|
|
28
|
+
<!DOCTYPE html>
|
|
29
|
+
<html>
|
|
30
|
+
<head>
|
|
31
|
+
<title>${title}</title>
|
|
32
|
+
<style>
|
|
33
|
+
body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
|
|
34
|
+
.container { background-color: ${backgroundColor}; border: 1px solid ${borderColor}; padding: 20px; border-radius: 8px; }
|
|
35
|
+
h1 { color: ${titleColor}; margin-top: 0; }
|
|
36
|
+
.detail { margin-top: 10px; padding: 10px; background: #f9f9f9; border-radius: 4px; ${type === 'error' ? 'font-family: monospace; font-size: 12px; white-space: pre-wrap;' : ''} }
|
|
37
|
+
.close-btn { margin-top: 20px; padding: 10px 20px; background: ${buttonColor}; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
|
38
|
+
</style>
|
|
39
|
+
${autoClose ? '<script>setTimeout(() => { window.close(); }, 3000);</script>' : ''}
|
|
40
|
+
</head>
|
|
41
|
+
<body>
|
|
42
|
+
<div class="container">
|
|
43
|
+
<h1>${type === 'success' ? '✓ ' : ''}${title}</h1>
|
|
44
|
+
${details ? details.map((d) => `<div class="detail"><strong>${d.label}:</strong> ${d.value}</div>`).join('') : ''}
|
|
45
|
+
<p>${message}</p>
|
|
46
|
+
${autoClose ? '<p>This window will close automatically in 3 seconds...</p>' : ''}
|
|
47
|
+
<button class="close-btn" onclick="window.close()">${autoClose ? 'Close Now' : 'Close Window'}</button>
|
|
48
|
+
</div>
|
|
49
|
+
</body>
|
|
50
|
+
</html>
|
|
51
|
+
`;
|
|
52
|
+
};
|
|
53
|
+
const normalizeQueryParam = (value) => {
|
|
54
|
+
if (typeof value === 'string') {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
58
|
+
const [first] = value;
|
|
59
|
+
return typeof first === 'string' ? first : undefined;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
};
|
|
63
|
+
const extractServerNameFromState = (stateValue) => {
|
|
64
|
+
try {
|
|
65
|
+
const normalized = stateValue.replace(/-/g, '+').replace(/_/g, '/');
|
|
66
|
+
const padding = (4 - (normalized.length % 4)) % 4;
|
|
67
|
+
const base64 = normalized + '='.repeat(padding);
|
|
68
|
+
const decoded = Buffer.from(base64, 'base64').toString('utf8');
|
|
69
|
+
const payload = JSON.parse(decoded);
|
|
70
|
+
if (payload && typeof payload.server === 'string') {
|
|
71
|
+
return payload.server;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// Ignore decoding errors and fall back to delimiter-based parsing
|
|
76
|
+
}
|
|
77
|
+
const separatorIndex = stateValue.indexOf(':');
|
|
78
|
+
if (separatorIndex > 0) {
|
|
79
|
+
return stateValue.slice(0, separatorIndex);
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Handle OAuth callback after user authorization
|
|
85
|
+
*
|
|
86
|
+
* This endpoint receives the authorization code from the OAuth provider
|
|
87
|
+
* and initiates the server reconnection process.
|
|
88
|
+
*
|
|
89
|
+
* Expected query parameters:
|
|
90
|
+
* - code: Authorization code from OAuth provider
|
|
91
|
+
* - state: Encoded server identifier used for OAuth session validation
|
|
92
|
+
* - error: Optional error code if authorization failed
|
|
93
|
+
* - error_description: Optional error description
|
|
94
|
+
*/
|
|
95
|
+
export const handleOAuthCallback = async (req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const { code, state, error, error_description } = req.query;
|
|
98
|
+
const codeParam = normalizeQueryParam(code);
|
|
99
|
+
const stateParam = normalizeQueryParam(state);
|
|
100
|
+
// Get translation function from request (set by i18n middleware)
|
|
101
|
+
const t = req.t || ((key) => key);
|
|
102
|
+
// Check for authorization errors
|
|
103
|
+
if (error) {
|
|
104
|
+
console.error(`OAuth authorization failed: ${error} - ${error_description || ''}`);
|
|
105
|
+
return res.status(400).send(generateHtmlResponse('error', t('oauthCallback.authorizationFailed'), '', [
|
|
106
|
+
{ label: t('oauthCallback.authorizationFailedError'), value: String(error) },
|
|
107
|
+
...(error_description
|
|
108
|
+
? [
|
|
109
|
+
{
|
|
110
|
+
label: t('oauthCallback.authorizationFailedDetails'),
|
|
111
|
+
value: String(error_description),
|
|
112
|
+
},
|
|
113
|
+
]
|
|
114
|
+
: []),
|
|
115
|
+
]));
|
|
116
|
+
}
|
|
117
|
+
// Validate required parameters
|
|
118
|
+
if (!stateParam) {
|
|
119
|
+
console.error('OAuth callback missing state parameter');
|
|
120
|
+
return res
|
|
121
|
+
.status(400)
|
|
122
|
+
.send(generateHtmlResponse('error', t('oauthCallback.invalidRequest'), t('oauthCallback.missingStateParameter')));
|
|
123
|
+
}
|
|
124
|
+
if (!codeParam) {
|
|
125
|
+
console.error('OAuth callback missing authorization code');
|
|
126
|
+
return res
|
|
127
|
+
.status(400)
|
|
128
|
+
.send(generateHtmlResponse('error', t('oauthCallback.invalidRequest'), t('oauthCallback.missingCodeParameter')));
|
|
129
|
+
}
|
|
130
|
+
console.log(`OAuth callback received - code: present, state: ${stateParam}`);
|
|
131
|
+
// Find server by state parameter
|
|
132
|
+
let serverInfo;
|
|
133
|
+
serverInfo = getServerByOAuthState(stateParam);
|
|
134
|
+
let decodedServerName;
|
|
135
|
+
if (!serverInfo) {
|
|
136
|
+
decodedServerName = extractServerNameFromState(stateParam);
|
|
137
|
+
if (decodedServerName) {
|
|
138
|
+
console.log(`State lookup failed; decoding server name from state: ${decodedServerName}`);
|
|
139
|
+
serverInfo = getServerByName(decodedServerName);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (!serverInfo) {
|
|
143
|
+
console.error(`No server found for OAuth callback. State: ${stateParam}${decodedServerName ? `, decoded server: ${decodedServerName}` : ''}`);
|
|
144
|
+
return res
|
|
145
|
+
.status(400)
|
|
146
|
+
.send(generateHtmlResponse('error', t('oauthCallback.serverNotFound'), `${t('oauthCallback.serverNotFoundMessage')}\n${t('oauthCallback.sessionExpiredMessage')}`));
|
|
147
|
+
}
|
|
148
|
+
// Optional: Validate state parameter for additional security
|
|
149
|
+
if (serverInfo.oauth?.state && serverInfo.oauth.state !== stateParam) {
|
|
150
|
+
console.warn(`State mismatch for server ${serverInfo.name}. Expected: ${serverInfo.oauth.state}, Got: ${stateParam}`);
|
|
151
|
+
// Note: We log a warning but don't fail the request since we have server name as primary identifier
|
|
152
|
+
}
|
|
153
|
+
console.log(`Processing OAuth callback for server: ${serverInfo.name}`);
|
|
154
|
+
// For StreamableHTTPClientTransport, we need to call finishAuth() on the transport
|
|
155
|
+
// This will exchange the authorization code for tokens automatically
|
|
156
|
+
if (serverInfo.transport && 'finishAuth' in serverInfo.transport) {
|
|
157
|
+
try {
|
|
158
|
+
console.log(`Calling transport.finishAuth() for server: ${serverInfo.name}`);
|
|
159
|
+
const currentTransport = serverInfo.transport;
|
|
160
|
+
await currentTransport.finishAuth(codeParam);
|
|
161
|
+
console.log(`Successfully exchanged authorization code for tokens: ${serverInfo.name}`);
|
|
162
|
+
// Refresh server configuration from disk to ensure we pick up newly saved tokens
|
|
163
|
+
const settings = loadSettings();
|
|
164
|
+
const storedConfig = settings.mcpServers?.[serverInfo.name];
|
|
165
|
+
const effectiveConfig = storedConfig || serverInfo.config;
|
|
166
|
+
if (!effectiveConfig) {
|
|
167
|
+
throw new Error(`Missing server configuration for ${serverInfo.name} after OAuth callback`);
|
|
168
|
+
}
|
|
169
|
+
// Keep latest configuration cached on serverInfo
|
|
170
|
+
serverInfo.config = effectiveConfig;
|
|
171
|
+
// Ensure we have up-to-date request options for the reconnect attempt
|
|
172
|
+
if (!serverInfo.options) {
|
|
173
|
+
const requestConfig = effectiveConfig.options || {};
|
|
174
|
+
serverInfo.options = {
|
|
175
|
+
timeout: requestConfig.timeout || 60000,
|
|
176
|
+
resetTimeoutOnProgress: requestConfig.resetTimeoutOnProgress || false,
|
|
177
|
+
maxTotalTimeout: requestConfig.maxTotalTimeout,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// Replace the existing transport instance to avoid reusing a closed/aborted transport
|
|
181
|
+
try {
|
|
182
|
+
if (serverInfo.transport && 'close' in serverInfo.transport) {
|
|
183
|
+
await serverInfo.transport.close();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (closeError) {
|
|
187
|
+
console.warn(`Failed to close existing transport for ${serverInfo.name}:`, closeError);
|
|
188
|
+
}
|
|
189
|
+
console.log(`Rebuilding transport with refreshed credentials for server: ${serverInfo.name}`);
|
|
190
|
+
const refreshedTransport = await createTransportFromConfig(serverInfo.name, effectiveConfig);
|
|
191
|
+
serverInfo.transport = refreshedTransport;
|
|
192
|
+
// Update server status to indicate OAuth is complete
|
|
193
|
+
serverInfo.status = 'connected';
|
|
194
|
+
if (serverInfo.oauth) {
|
|
195
|
+
serverInfo.oauth.authorizationUrl = undefined;
|
|
196
|
+
serverInfo.oauth.state = undefined;
|
|
197
|
+
serverInfo.oauth.codeVerifier = undefined;
|
|
198
|
+
}
|
|
199
|
+
// Check if client needs to be connected
|
|
200
|
+
const isClientConnected = serverInfo.client && serverInfo.client.getServerCapabilities();
|
|
201
|
+
if (!isClientConnected) {
|
|
202
|
+
// Client is not connected yet, connect it
|
|
203
|
+
if (serverInfo.client && serverInfo.transport) {
|
|
204
|
+
console.log(`Connecting client with refreshed transport for: ${serverInfo.name}`);
|
|
205
|
+
try {
|
|
206
|
+
await serverInfo.client.connect(serverInfo.transport, serverInfo.options);
|
|
207
|
+
console.log(`Client connected successfully for: ${serverInfo.name}`);
|
|
208
|
+
// List tools after successful connection
|
|
209
|
+
const capabilities = serverInfo.client.getServerCapabilities();
|
|
210
|
+
console.log(`Server capabilities for ${serverInfo.name}:`, JSON.stringify(capabilities));
|
|
211
|
+
if (capabilities?.tools) {
|
|
212
|
+
console.log(`Listing tools for server: ${serverInfo.name}`);
|
|
213
|
+
const toolsResult = await serverInfo.client.listTools({}, serverInfo.options);
|
|
214
|
+
const separator = getNameSeparator();
|
|
215
|
+
serverInfo.tools = toolsResult.tools.map((tool) => ({
|
|
216
|
+
name: `${serverInfo.name}${separator}${tool.name}`,
|
|
217
|
+
description: tool.description || '',
|
|
218
|
+
inputSchema: tool.inputSchema || {},
|
|
219
|
+
}));
|
|
220
|
+
console.log(`Listed ${serverInfo.tools.length} tools for server: ${serverInfo.name}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
console.log(`Server ${serverInfo.name} does not support tools capability`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch (connectError) {
|
|
227
|
+
console.error(`Error connecting client for ${serverInfo.name}:`, connectError);
|
|
228
|
+
if (connectError instanceof Error) {
|
|
229
|
+
console.error(`Connect error details for ${serverInfo.name}: ${connectError.message}`, connectError.stack);
|
|
230
|
+
}
|
|
231
|
+
// Even if connection fails, mark OAuth as complete
|
|
232
|
+
// The user can try reconnecting from the dashboard
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
console.log(`Cannot connect client for ${serverInfo.name}: client or transport missing`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
console.log(`Client already connected for server: ${serverInfo.name}`);
|
|
241
|
+
}
|
|
242
|
+
console.log(`Successfully completed OAuth flow for server: ${serverInfo.name}`);
|
|
243
|
+
// Return success page
|
|
244
|
+
return res.status(200).send(generateHtmlResponse('success', t('oauthCallback.authorizationSuccessful'), `${t('oauthCallback.successMessage')}\n${t('oauthCallback.autoCloseMessage')}`, [
|
|
245
|
+
{ label: t('oauthCallback.server'), value: serverInfo.name },
|
|
246
|
+
{ label: t('oauthCallback.status'), value: t('oauthCallback.connected') },
|
|
247
|
+
], true));
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
console.error(`Failed to complete OAuth flow for server ${serverInfo.name}:`, error);
|
|
251
|
+
console.error(`Error type: ${typeof error}, Error name: ${error?.constructor?.name}`);
|
|
252
|
+
console.error(`Error message: ${error instanceof Error ? error.message : String(error)}`);
|
|
253
|
+
console.error(`Error stack:`, error instanceof Error ? error.stack : 'No stack trace');
|
|
254
|
+
return res
|
|
255
|
+
.status(500)
|
|
256
|
+
.send(generateHtmlResponse('error', t('oauthCallback.connectionError'), `${t('oauthCallback.connectionErrorMessage')}\n${t('oauthCallback.reconnectMessage')}`, [{ label: '', value: error instanceof Error ? error.message : String(error) }]));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// No transport available or transport doesn't support finishAuth
|
|
261
|
+
console.error(`Transport for server ${serverInfo.name} does not support finishAuth()`);
|
|
262
|
+
return res
|
|
263
|
+
.status(500)
|
|
264
|
+
.send(generateHtmlResponse('error', t('oauthCallback.configurationError'), t('oauthCallback.configurationErrorMessage')));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
console.error('Unexpected error handling OAuth callback:', error);
|
|
269
|
+
// Get translation function from request (set by i18n middleware)
|
|
270
|
+
const t = req.t || ((key) => key);
|
|
271
|
+
return res
|
|
272
|
+
.status(500)
|
|
273
|
+
.send(generateHtmlResponse('error', t('oauthCallback.internalError'), t('oauthCallback.internalErrorMessage')));
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
//# sourceMappingURL=oauthCallbackController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthCallbackController.js","sourceRoot":"","sources":["../../src/controllers/oauthCallbackController.ts"],"names":[],"mappings":"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"}
|