@samanhappy/mcphub 0.0.5 → 0.0.6

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.
Files changed (97) hide show
  1. package/.env.example +2 -0
  2. package/.eslintrc.json +25 -0
  3. package/.github/workflows/build.yml +51 -0
  4. package/.github/workflows/release.yml +19 -0
  5. package/.prettierrc +7 -0
  6. package/Dockerfile +51 -0
  7. package/assets/amap-edit.png +0 -0
  8. package/assets/amap-result.png +0 -0
  9. package/assets/cherry-mcp.png +0 -0
  10. package/assets/cursor-mcp.png +0 -0
  11. package/assets/cursor-query.png +0 -0
  12. package/assets/cursor-tools.png +0 -0
  13. package/assets/dashboard.png +0 -0
  14. package/assets/dashboard.zh.png +0 -0
  15. package/assets/group.png +0 -0
  16. package/assets/group.zh.png +0 -0
  17. package/assets/market.zh.png +0 -0
  18. package/assets/wegroup.jpg +0 -0
  19. package/assets/wegroup.png +0 -0
  20. package/assets/wexin.png +0 -0
  21. package/doc/intro.md +73 -0
  22. package/doc/intro2.md +232 -0
  23. package/entrypoint.sh +10 -0
  24. package/frontend/favicon.ico +0 -0
  25. package/frontend/index.html +13 -0
  26. package/frontend/postcss.config.js +6 -0
  27. package/frontend/src/App.tsx +44 -0
  28. package/frontend/src/components/AddGroupForm.tsx +132 -0
  29. package/frontend/src/components/AddServerForm.tsx +90 -0
  30. package/frontend/src/components/ChangePasswordForm.tsx +158 -0
  31. package/frontend/src/components/EditGroupForm.tsx +149 -0
  32. package/frontend/src/components/EditServerForm.tsx +76 -0
  33. package/frontend/src/components/GroupCard.tsx +143 -0
  34. package/frontend/src/components/MarketServerCard.tsx +153 -0
  35. package/frontend/src/components/MarketServerDetail.tsx +297 -0
  36. package/frontend/src/components/ProtectedRoute.tsx +27 -0
  37. package/frontend/src/components/ServerCard.tsx +230 -0
  38. package/frontend/src/components/ServerForm.tsx +276 -0
  39. package/frontend/src/components/icons/LucideIcons.tsx +14 -0
  40. package/frontend/src/components/layout/Content.tsx +17 -0
  41. package/frontend/src/components/layout/Header.tsx +61 -0
  42. package/frontend/src/components/layout/Sidebar.tsx +98 -0
  43. package/frontend/src/components/ui/Badge.tsx +33 -0
  44. package/frontend/src/components/ui/Button.tsx +0 -0
  45. package/frontend/src/components/ui/DeleteDialog.tsx +48 -0
  46. package/frontend/src/components/ui/Pagination.tsx +128 -0
  47. package/frontend/src/components/ui/Toast.tsx +96 -0
  48. package/frontend/src/components/ui/ToggleGroup.tsx +134 -0
  49. package/frontend/src/components/ui/ToolCard.tsx +38 -0
  50. package/frontend/src/contexts/AuthContext.tsx +159 -0
  51. package/frontend/src/contexts/ToastContext.tsx +60 -0
  52. package/frontend/src/hooks/useGroupData.ts +232 -0
  53. package/frontend/src/hooks/useMarketData.ts +410 -0
  54. package/frontend/src/hooks/useServerData.ts +306 -0
  55. package/frontend/src/hooks/useSettingsData.ts +131 -0
  56. package/frontend/src/i18n.ts +42 -0
  57. package/frontend/src/index.css +20 -0
  58. package/frontend/src/layouts/MainLayout.tsx +33 -0
  59. package/frontend/src/locales/en.json +214 -0
  60. package/frontend/src/locales/zh.json +214 -0
  61. package/frontend/src/main.tsx +12 -0
  62. package/frontend/src/pages/Dashboard.tsx +206 -0
  63. package/frontend/src/pages/GroupsPage.tsx +116 -0
  64. package/frontend/src/pages/LoginPage.tsx +104 -0
  65. package/frontend/src/pages/MarketPage.tsx +356 -0
  66. package/frontend/src/pages/ServersPage.tsx +144 -0
  67. package/frontend/src/pages/SettingsPage.tsx +149 -0
  68. package/frontend/src/services/authService.ts +141 -0
  69. package/frontend/src/types/index.ts +160 -0
  70. package/frontend/src/utils/cn.ts +10 -0
  71. package/frontend/tsconfig.json +31 -0
  72. package/frontend/tsconfig.node.json +10 -0
  73. package/frontend/vite.config.ts +26 -0
  74. package/googled76ca578b6543fbc.html +1 -0
  75. package/jest.config.js +10 -0
  76. package/mcp_settings.json +45 -0
  77. package/package.json +3 -18
  78. package/servers.json +74722 -0
  79. package/src/config/index.ts +46 -0
  80. package/src/controllers/authController.ts +179 -0
  81. package/src/controllers/groupController.ts +341 -0
  82. package/src/controllers/marketController.ts +154 -0
  83. package/src/controllers/serverController.ts +303 -0
  84. package/src/index.ts +17 -0
  85. package/src/middlewares/auth.ts +28 -0
  86. package/src/middlewares/index.ts +43 -0
  87. package/src/models/User.ts +103 -0
  88. package/src/routes/index.ts +96 -0
  89. package/src/server.ts +72 -0
  90. package/src/services/groupService.ts +232 -0
  91. package/src/services/marketService.ts +116 -0
  92. package/src/services/mcpService.ts +385 -0
  93. package/src/services/sseService.ts +119 -0
  94. package/src/types/index.ts +129 -0
  95. package/src/utils/migration.ts +52 -0
  96. package/tsconfig.json +17 -0
  97. package/bin/cli.js +0 -43
@@ -0,0 +1,303 @@
1
+ import { Request, Response } from 'express';
2
+ import { ApiResponse, AddServerRequest } from '../types/index.js';
3
+ import {
4
+ getServersInfo,
5
+ addServer,
6
+ removeServer,
7
+ updateMcpServer,
8
+ notifyToolChanged,
9
+ toggleServerStatus,
10
+ } from '../services/mcpService.js';
11
+ import { loadSettings, saveSettings } from '../config/index.js';
12
+
13
+ export const getAllServers = (_: Request, res: Response): void => {
14
+ try {
15
+ const serversInfo = getServersInfo();
16
+ const response: ApiResponse = {
17
+ success: true,
18
+ data: serversInfo,
19
+ };
20
+ res.json(response);
21
+ } catch (error) {
22
+ res.status(500).json({
23
+ success: false,
24
+ message: 'Failed to get servers information',
25
+ });
26
+ }
27
+ };
28
+
29
+ export const getAllSettings = (_: Request, res: Response): void => {
30
+ try {
31
+ const settings = loadSettings();
32
+ const response: ApiResponse = {
33
+ success: true,
34
+ data: settings,
35
+ };
36
+ res.json(response);
37
+ } catch (error) {
38
+ res.status(500).json({
39
+ success: false,
40
+ message: 'Failed to get server settings',
41
+ });
42
+ }
43
+ };
44
+
45
+ export const createServer = async (req: Request, res: Response): Promise<void> => {
46
+ try {
47
+ const { name, config } = req.body as AddServerRequest;
48
+ if (!name || typeof name !== 'string') {
49
+ res.status(400).json({
50
+ success: false,
51
+ message: 'Server name is required',
52
+ });
53
+ return;
54
+ }
55
+
56
+ if (!config || typeof config !== 'object') {
57
+ res.status(400).json({
58
+ success: false,
59
+ message: 'Server configuration is required',
60
+ });
61
+ return;
62
+ }
63
+
64
+ if (!config.url && (!config.command || !config.args)) {
65
+ res.status(400).json({
66
+ success: false,
67
+ message: 'Server configuration must include either a URL or command with arguments',
68
+ });
69
+ return;
70
+ }
71
+
72
+ const result = await addServer(name, config);
73
+ if (result.success) {
74
+ notifyToolChanged();
75
+ res.json({
76
+ success: true,
77
+ message: 'Server added successfully',
78
+ });
79
+ } else {
80
+ res.status(400).json({
81
+ success: false,
82
+ message: result.message || 'Failed to add server',
83
+ });
84
+ }
85
+ } catch (error) {
86
+ res.status(500).json({
87
+ success: false,
88
+ message: 'Internal server error',
89
+ });
90
+ }
91
+ };
92
+
93
+ export const deleteServer = async (req: Request, res: Response): Promise<void> => {
94
+ try {
95
+ const { name } = req.params;
96
+ if (!name) {
97
+ res.status(400).json({
98
+ success: false,
99
+ message: 'Server name is required',
100
+ });
101
+ return;
102
+ }
103
+
104
+ const result = removeServer(name);
105
+ if (result.success) {
106
+ notifyToolChanged();
107
+ res.json({
108
+ success: true,
109
+ message: 'Server removed successfully',
110
+ });
111
+ } else {
112
+ res.status(404).json({
113
+ success: false,
114
+ message: result.message || 'Server not found or failed to remove',
115
+ });
116
+ }
117
+ } catch (error) {
118
+ res.status(500).json({
119
+ success: false,
120
+ message: 'Internal server error',
121
+ });
122
+ }
123
+ };
124
+
125
+ export const updateServer = async (req: Request, res: Response): Promise<void> => {
126
+ try {
127
+ const { name } = req.params;
128
+ const { config } = req.body;
129
+ if (!name) {
130
+ res.status(400).json({
131
+ success: false,
132
+ message: 'Server name is required',
133
+ });
134
+ return;
135
+ }
136
+
137
+ if (!config || typeof config !== 'object') {
138
+ res.status(400).json({
139
+ success: false,
140
+ message: 'Server configuration is required',
141
+ });
142
+ return;
143
+ }
144
+
145
+ if (!config.url && (!config.command || !config.args)) {
146
+ res.status(400).json({
147
+ success: false,
148
+ message: 'Server configuration must include either a URL or command with arguments',
149
+ });
150
+ return;
151
+ }
152
+
153
+ const result = await updateMcpServer(name, config);
154
+ if (result.success) {
155
+ notifyToolChanged();
156
+ res.json({
157
+ success: true,
158
+ message: 'Server updated successfully',
159
+ });
160
+ } else {
161
+ res.status(404).json({
162
+ success: false,
163
+ message: result.message || 'Server not found or failed to update',
164
+ });
165
+ }
166
+ } catch (error) {
167
+ res.status(500).json({
168
+ success: false,
169
+ message: 'Internal server error',
170
+ });
171
+ }
172
+ };
173
+
174
+ export const getServerConfig = (req: Request, res: Response): void => {
175
+ try {
176
+ const { name } = req.params;
177
+ const settings = loadSettings();
178
+ if (!settings.mcpServers || !settings.mcpServers[name]) {
179
+ res.status(404).json({
180
+ success: false,
181
+ message: 'Server not found',
182
+ });
183
+ return;
184
+ }
185
+
186
+ const serverInfo = getServersInfo().find((s) => s.name === name);
187
+ const serverConfig = settings.mcpServers[name];
188
+ const response: ApiResponse = {
189
+ success: true,
190
+ data: {
191
+ name,
192
+ status: serverInfo ? serverInfo.status : 'disconnected',
193
+ tools: serverInfo ? serverInfo.tools : [],
194
+ config: serverConfig,
195
+ },
196
+ };
197
+
198
+ res.json(response);
199
+ } catch (error) {
200
+ res.status(500).json({
201
+ success: false,
202
+ message: 'Failed to get server configuration',
203
+ });
204
+ }
205
+ };
206
+
207
+ export const toggleServer = async (req: Request, res: Response): Promise<void> => {
208
+ try {
209
+ const { name } = req.params;
210
+ const { enabled } = req.body;
211
+ if (!name) {
212
+ res.status(400).json({
213
+ success: false,
214
+ message: 'Server name is required',
215
+ });
216
+ return;
217
+ }
218
+
219
+ if (typeof enabled !== 'boolean') {
220
+ res.status(400).json({
221
+ success: false,
222
+ message: 'Enabled status must be a boolean',
223
+ });
224
+ return;
225
+ }
226
+
227
+ const result = await toggleServerStatus(name, enabled);
228
+ if (result.success) {
229
+ notifyToolChanged();
230
+ res.json({
231
+ success: true,
232
+ message: result.message || `Server ${enabled ? 'enabled' : 'disabled'} successfully`,
233
+ });
234
+ } else {
235
+ res.status(404).json({
236
+ success: false,
237
+ message: result.message || 'Server not found or failed to toggle status',
238
+ });
239
+ }
240
+ } catch (error) {
241
+ res.status(500).json({
242
+ success: false,
243
+ message: 'Internal server error',
244
+ });
245
+ }
246
+ };
247
+
248
+ export const updateSystemConfig = (req: Request, res: Response): void => {
249
+ try {
250
+ const { routing } = req.body;
251
+
252
+ if (!routing || (typeof routing.enableGlobalRoute !== 'boolean' && typeof routing.enableGroupNameRoute !== 'boolean')) {
253
+ res.status(400).json({
254
+ success: false,
255
+ message: 'Invalid system configuration provided',
256
+ });
257
+ return;
258
+ }
259
+
260
+ const settings = loadSettings();
261
+ if (!settings.systemConfig) {
262
+ settings.systemConfig = {
263
+ routing: {
264
+ enableGlobalRoute: true,
265
+ enableGroupNameRoute: true
266
+ }
267
+ };
268
+ }
269
+
270
+ if (!settings.systemConfig.routing) {
271
+ settings.systemConfig.routing = {
272
+ enableGlobalRoute: true,
273
+ enableGroupNameRoute: true
274
+ };
275
+ }
276
+
277
+ if (typeof routing.enableGlobalRoute === 'boolean') {
278
+ settings.systemConfig.routing.enableGlobalRoute = routing.enableGlobalRoute;
279
+ }
280
+
281
+ if (typeof routing.enableGroupNameRoute === 'boolean') {
282
+ settings.systemConfig.routing.enableGroupNameRoute = routing.enableGroupNameRoute;
283
+ }
284
+
285
+ if (saveSettings(settings)) {
286
+ res.json({
287
+ success: true,
288
+ data: settings.systemConfig,
289
+ message: 'System configuration updated successfully',
290
+ });
291
+ } else {
292
+ res.status(500).json({
293
+ success: false,
294
+ message: 'Failed to save system configuration',
295
+ });
296
+ }
297
+ } catch (error) {
298
+ res.status(500).json({
299
+ success: false,
300
+ message: 'Internal server error',
301
+ });
302
+ }
303
+ };
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ import AppServer from './server.js';
2
+
3
+ const appServer = new AppServer();
4
+
5
+ async function boot() {
6
+ try {
7
+ await appServer.initialize();
8
+ appServer.start();
9
+ } catch (error) {
10
+ console.error('Failed to start application:', error);
11
+ process.exit(1);
12
+ }
13
+ }
14
+
15
+ boot();
16
+
17
+ export default appServer.getApp();
@@ -0,0 +1,28 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import jwt from 'jsonwebtoken';
3
+
4
+ // Default secret key - in production, use an environment variable
5
+ const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-this';
6
+
7
+ // Middleware to authenticate JWT token
8
+ export const auth = (req: Request, res: Response, next: NextFunction): void => {
9
+ // Get token from header
10
+ const token = req.header('x-auth-token');
11
+
12
+ // Check if no token
13
+ if (!token) {
14
+ res.status(401).json({ success: false, message: 'No token, authorization denied' });
15
+ return;
16
+ }
17
+
18
+ // Verify token
19
+ try {
20
+ const decoded = jwt.verify(token, JWT_SECRET);
21
+
22
+ // Add user from payload to request
23
+ (req as any).user = (decoded as any).user;
24
+ next();
25
+ } catch (error) {
26
+ res.status(401).json({ success: false, message: 'Token is not valid' });
27
+ }
28
+ };
@@ -0,0 +1,43 @@
1
+ import express, { Request, Response, NextFunction } from 'express';
2
+ import path from 'path';
3
+ import { auth } from './auth.js';
4
+ import { initializeDefaultUser } from '../models/User.js';
5
+
6
+ export const errorHandler = (
7
+ err: Error,
8
+ _req: Request,
9
+ res: Response,
10
+ _next: NextFunction
11
+ ): void => {
12
+ console.error('Unhandled error:', err);
13
+ res.status(500).json({
14
+ success: false,
15
+ message: 'Internal server error',
16
+ });
17
+ };
18
+
19
+ export const initMiddlewares = (app: express.Application): void => {
20
+ app.use(express.static('frontend/dist'));
21
+
22
+ app.use((req, res, next) => {
23
+ if (req.path !== '/sse' && req.path !== '/messages') {
24
+ express.json()(req, res, next);
25
+ } else {
26
+ next();
27
+ }
28
+ });
29
+
30
+ // Initialize default admin user if no users exist
31
+ initializeDefaultUser().catch(err => {
32
+ console.error('Error initializing default user:', err);
33
+ });
34
+
35
+ // Protect all API routes with authentication middleware
36
+ app.use('/api', auth);
37
+
38
+ app.get('/', (_req: Request, res: Response) => {
39
+ res.sendFile(path.join(process.cwd(), 'frontend', 'dist', 'index.html'));
40
+ });
41
+
42
+ app.use(errorHandler);
43
+ };
@@ -0,0 +1,103 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import bcrypt from 'bcryptjs';
4
+ import { IUser, McpSettings } from '../types/index.js';
5
+ import { loadSettings, saveSettings } from '../config/index.js';
6
+
7
+ // Get all users
8
+ export const getUsers = (): IUser[] => {
9
+ try {
10
+ const settings = loadSettings();
11
+ return settings.users || [];
12
+ } catch (error) {
13
+ console.error('Error reading users from settings:', error);
14
+ return [];
15
+ }
16
+ };
17
+
18
+ // Save users to settings
19
+ const saveUsers = (users: IUser[]): void => {
20
+ try {
21
+ const settings = loadSettings();
22
+ settings.users = users;
23
+ saveSettings(settings);
24
+ } catch (error) {
25
+ console.error('Error saving users to settings:', error);
26
+ }
27
+ };
28
+
29
+ // Create a new user
30
+ export const createUser = async (userData: IUser): Promise<IUser | null> => {
31
+ const users = getUsers();
32
+
33
+ // Check if username already exists
34
+ if (users.some(user => user.username === userData.username)) {
35
+ return null;
36
+ }
37
+
38
+ // Hash the password
39
+ const salt = await bcrypt.genSalt(10);
40
+ const hashedPassword = await bcrypt.hash(userData.password, salt);
41
+
42
+ const newUser = {
43
+ username: userData.username,
44
+ password: hashedPassword,
45
+ isAdmin: userData.isAdmin || false
46
+ };
47
+
48
+ users.push(newUser);
49
+ saveUsers(users);
50
+
51
+ return newUser;
52
+ };
53
+
54
+ // Find user by username
55
+ export const findUserByUsername = (username: string): IUser | undefined => {
56
+ const users = getUsers();
57
+ return users.find(user => user.username === username);
58
+ };
59
+
60
+ // Verify user password
61
+ export const verifyPassword = async (
62
+ plainPassword: string,
63
+ hashedPassword: string
64
+ ): Promise<boolean> => {
65
+ return await bcrypt.compare(plainPassword, hashedPassword);
66
+ };
67
+
68
+ // Update user password
69
+ export const updateUserPassword = async (
70
+ username: string,
71
+ newPassword: string
72
+ ): Promise<boolean> => {
73
+ const users = getUsers();
74
+ const userIndex = users.findIndex(user => user.username === username);
75
+
76
+ if (userIndex === -1) {
77
+ return false;
78
+ }
79
+
80
+ // Hash the new password
81
+ const salt = await bcrypt.genSalt(10);
82
+ const hashedPassword = await bcrypt.hash(newPassword, salt);
83
+
84
+ // Update the user's password
85
+ users[userIndex].password = hashedPassword;
86
+ saveUsers(users);
87
+
88
+ return true;
89
+ };
90
+
91
+ // Initialize with default admin user if no users exist
92
+ export const initializeDefaultUser = async (): Promise<void> => {
93
+ const users = getUsers();
94
+
95
+ if (users.length === 0) {
96
+ await createUser({
97
+ username: 'admin',
98
+ password: 'admin123',
99
+ isAdmin: true
100
+ });
101
+ console.log('Default admin user created');
102
+ }
103
+ };
@@ -0,0 +1,96 @@
1
+ import express from 'express';
2
+ import { check } from 'express-validator';
3
+ import {
4
+ getAllServers,
5
+ getAllSettings,
6
+ createServer,
7
+ updateServer,
8
+ deleteServer,
9
+ toggleServer,
10
+ updateSystemConfig
11
+ } from '../controllers/serverController.js';
12
+ import {
13
+ getGroups,
14
+ getGroup,
15
+ createNewGroup,
16
+ updateExistingGroup,
17
+ deleteExistingGroup,
18
+ addServerToExistingGroup,
19
+ removeServerFromExistingGroup,
20
+ getGroupServers,
21
+ updateGroupServersBatch
22
+ } from '../controllers/groupController.js';
23
+ import {
24
+ getAllMarketServers,
25
+ getMarketServer,
26
+ getAllMarketCategories,
27
+ getAllMarketTags,
28
+ searchMarketServersByQuery,
29
+ getMarketServersByCategory,
30
+ getMarketServersByTag
31
+ } from '../controllers/marketController.js';
32
+ import {
33
+ login,
34
+ register,
35
+ getCurrentUser,
36
+ changePassword
37
+ } from '../controllers/authController.js';
38
+ import { auth } from '../middlewares/auth.js';
39
+
40
+ const router = express.Router();
41
+
42
+ export const initRoutes = (app: express.Application): void => {
43
+ // API routes protected by auth middleware in middlewares/index.ts
44
+ router.get('/servers', getAllServers);
45
+ router.get('/settings', getAllSettings);
46
+ router.post('/servers', createServer);
47
+ router.put('/servers/:name', updateServer);
48
+ router.delete('/servers/:name', deleteServer);
49
+ router.post('/servers/:name/toggle', toggleServer);
50
+ router.put('/system-config', updateSystemConfig);
51
+
52
+ // Group management routes
53
+ router.get('/groups', getGroups);
54
+ router.get('/groups/:id', getGroup);
55
+ router.post('/groups', createNewGroup);
56
+ router.put('/groups/:id', updateExistingGroup);
57
+ router.delete('/groups/:id', deleteExistingGroup);
58
+ router.post('/groups/:id/servers', addServerToExistingGroup);
59
+ router.delete('/groups/:id/servers/:serverName', removeServerFromExistingGroup);
60
+ router.get('/groups/:id/servers', getGroupServers);
61
+ // New route for batch updating servers in a group
62
+ router.put('/groups/:id/servers/batch', updateGroupServersBatch);
63
+
64
+ // Market routes
65
+ router.get('/market/servers', getAllMarketServers);
66
+ router.get('/market/servers/search', searchMarketServersByQuery);
67
+ router.get('/market/servers/:name', getMarketServer);
68
+ router.get('/market/categories', getAllMarketCategories);
69
+ router.get('/market/categories/:category', getMarketServersByCategory);
70
+ router.get('/market/tags', getAllMarketTags);
71
+ router.get('/market/tags/:tag', getMarketServersByTag);
72
+
73
+ // Auth routes (these will NOT be protected by auth middleware)
74
+ app.post('/auth/login', [
75
+ check('username', 'Username is required').not().isEmpty(),
76
+ check('password', 'Password is required').not().isEmpty(),
77
+ ], login);
78
+
79
+ app.post('/auth/register', [
80
+ check('username', 'Username is required').not().isEmpty(),
81
+ check('password', 'Password must be at least 6 characters').isLength({ min: 6 }),
82
+ ], register);
83
+
84
+ app.get('/auth/user', auth, getCurrentUser);
85
+
86
+ // Add change password route
87
+ app.post('/auth/change-password', [
88
+ auth,
89
+ check('currentPassword', 'Current password is required').not().isEmpty(),
90
+ check('newPassword', 'New password must be at least 6 characters').isLength({ min: 6 }),
91
+ ], changePassword);
92
+
93
+ app.use('/api', router);
94
+ };
95
+
96
+ export default router;
package/src/server.ts ADDED
@@ -0,0 +1,72 @@
1
+ import express from 'express';
2
+ import config from './config/index.js';
3
+ import path from 'path';
4
+ import { initMcpServer } from './services/mcpService.js';
5
+ import { initMiddlewares } from './middlewares/index.js';
6
+ import { initRoutes } from './routes/index.js';
7
+ import {
8
+ handleSseConnection,
9
+ handleSseMessage,
10
+ handleMcpPostRequest,
11
+ handleMcpOtherRequest,
12
+ } from './services/sseService.js';
13
+ import { migrateUserData } from './utils/migration.js';
14
+ import { initializeDefaultUser } from './models/User.js';
15
+
16
+ export class AppServer {
17
+ private app: express.Application;
18
+ private port: number | string;
19
+
20
+ constructor() {
21
+ this.app = express();
22
+ this.port = config.port;
23
+ }
24
+
25
+ async initialize(): Promise<void> {
26
+ try {
27
+ // Migrate user data from users.json to mcp_settings.json if needed
28
+ migrateUserData();
29
+
30
+ // Initialize default admin user if no users exist
31
+ await initializeDefaultUser();
32
+
33
+ initMiddlewares(this.app);
34
+ initRoutes(this.app);
35
+ console.log('Server initialized successfully');
36
+
37
+ initMcpServer(config.mcpHubName, config.mcpHubVersion)
38
+ .then(() => {
39
+ console.log('MCP server initialized successfully');
40
+ this.app.get('/sse/:group?', (req, res) => handleSseConnection(req, res));
41
+ this.app.post('/messages', handleSseMessage);
42
+ this.app.post('/mcp/:group?', handleMcpPostRequest);
43
+ this.app.get('/mcp/:group?', handleMcpOtherRequest);
44
+ this.app.delete('/mcp/:group?', handleMcpOtherRequest);
45
+ })
46
+ .catch((error) => {
47
+ console.error('Error initializing MCP server:', error);
48
+ throw error;
49
+ })
50
+ .finally(() => {
51
+ this.app.get('*', (_req, res) => {
52
+ res.sendFile(path.join(process.cwd(), 'frontend', 'dist', 'index.html'));
53
+ });
54
+ });
55
+ } catch (error) {
56
+ console.error('Error initializing server:', error);
57
+ throw error;
58
+ }
59
+ }
60
+
61
+ start(): void {
62
+ this.app.listen(this.port, () => {
63
+ console.log(`Server is running on port ${this.port}`);
64
+ });
65
+ }
66
+
67
+ getApp(): express.Application {
68
+ return this.app;
69
+ }
70
+ }
71
+
72
+ export default AppServer;