mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
Enhance routing and settings functionality (#29)
This commit is contained in:
@@ -2,7 +2,7 @@ import { Request, Response } from 'express';
|
||||
import { ApiResponse } from '../types/index.js';
|
||||
import {
|
||||
getAllGroups,
|
||||
getGroupById,
|
||||
getGroupByIdOrName,
|
||||
createGroup,
|
||||
updateGroup,
|
||||
updateGroupServers,
|
||||
@@ -41,7 +41,7 @@ export const getGroup = (req: Request, res: Response): void => {
|
||||
return;
|
||||
}
|
||||
|
||||
const group = getGroupById(id);
|
||||
const group = getGroupByIdOrName(id);
|
||||
if (!group) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
@@ -318,7 +318,7 @@ export const getGroupServers = (req: Request, res: Response): void => {
|
||||
return;
|
||||
}
|
||||
|
||||
const group = getGroupById(id);
|
||||
const group = getGroupByIdOrName(id);
|
||||
if (!group) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
notifyToolChanged,
|
||||
toggleServerStatus,
|
||||
} from '../services/mcpService.js';
|
||||
import { loadSettings } from '../config/index.js';
|
||||
import { loadSettings, saveSettings } from '../config/index.js';
|
||||
|
||||
export const getAllServers = (_: Request, res: Response): void => {
|
||||
try {
|
||||
@@ -244,3 +244,60 @@ export const toggleServer = async (req: Request, res: Response): Promise<void> =
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const updateSystemConfig = (req: Request, res: Response): void => {
|
||||
try {
|
||||
const { routing } = req.body;
|
||||
|
||||
if (!routing || (typeof routing.enableGlobalRoute !== 'boolean' && typeof routing.enableGroupNameRoute !== 'boolean')) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid system configuration provided',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = loadSettings();
|
||||
if (!settings.systemConfig) {
|
||||
settings.systemConfig = {
|
||||
routing: {
|
||||
enableGlobalRoute: true,
|
||||
enableGroupNameRoute: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!settings.systemConfig.routing) {
|
||||
settings.systemConfig.routing = {
|
||||
enableGlobalRoute: true,
|
||||
enableGroupNameRoute: true
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof routing.enableGlobalRoute === 'boolean') {
|
||||
settings.systemConfig.routing.enableGlobalRoute = routing.enableGlobalRoute;
|
||||
}
|
||||
|
||||
if (typeof routing.enableGroupNameRoute === 'boolean') {
|
||||
settings.systemConfig.routing.enableGroupNameRoute = routing.enableGroupNameRoute;
|
||||
}
|
||||
|
||||
if (saveSettings(settings)) {
|
||||
res.json({
|
||||
success: true,
|
||||
data: settings.systemConfig,
|
||||
message: 'System configuration updated successfully',
|
||||
});
|
||||
} else {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to save system configuration',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Internal server error',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
updateServer,
|
||||
deleteServer,
|
||||
toggleServer,
|
||||
updateSystemConfig
|
||||
} from '../controllers/serverController.js';
|
||||
import {
|
||||
getGroups,
|
||||
@@ -46,6 +47,7 @@ export const initRoutes = (app: express.Application): void => {
|
||||
router.put('/servers/:name', updateServer);
|
||||
router.delete('/servers/:name', deleteServer);
|
||||
router.post('/servers/:name/toggle', toggleServer);
|
||||
router.put('/system-config', updateSystemConfig);
|
||||
|
||||
// Group management routes
|
||||
router.get('/groups', getGroups);
|
||||
|
||||
@@ -32,7 +32,7 @@ export class AppServer {
|
||||
initMcpServer(config.mcpHubName, config.mcpHubVersion)
|
||||
.then(() => {
|
||||
console.log('MCP server initialized successfully');
|
||||
this.app.get('/sse/:groupId?', (req, res) => handleSseConnection(req, res));
|
||||
this.app.get('/sse/:group?', (req, res) => handleSseConnection(req, res));
|
||||
this.app.post('/messages', handleSseMessage);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -9,10 +9,19 @@ export const getAllGroups = (): IGroup[] => {
|
||||
return settings.groups || [];
|
||||
};
|
||||
|
||||
// Get group by ID
|
||||
export const getGroupById = (id: string): IGroup | undefined => {
|
||||
// Get group by ID or name
|
||||
export const getGroupByIdOrName = (key: string): IGroup | undefined => {
|
||||
const settings = loadSettings();
|
||||
const routingConfig = settings.systemConfig?.routing || {
|
||||
enableGlobalRoute: true,
|
||||
enableGroupNameRoute: true,
|
||||
};
|
||||
const groups = getAllGroups();
|
||||
return groups.find((group) => group.id === id);
|
||||
return (
|
||||
groups.find(
|
||||
(group) => group.id === key || (group.name === key && routingConfig.enableGroupNameRoute),
|
||||
) || undefined
|
||||
);
|
||||
};
|
||||
|
||||
// Create a new group
|
||||
@@ -218,6 +227,6 @@ export const removeServerFromGroup = (groupId: string, serverName: string): IGro
|
||||
|
||||
// Get all servers in a group
|
||||
export const getServersInGroup = (groupId: string): string[] => {
|
||||
const group = getGroupById(groupId);
|
||||
const group = getGroupByIdOrName(groupId);
|
||||
return group ? group.servers : [];
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ServerInfo, ServerConfig } from '../types/index.js';
|
||||
import { loadSettings, saveSettings, expandEnvVars } from '../config/index.js';
|
||||
import config from '../config/index.js';
|
||||
import { get } from 'http';
|
||||
import { getGroupId } from './sseService.js';
|
||||
import { getGroup } from './sseService.js';
|
||||
import { getServersInGroup } from './groupService.js';
|
||||
|
||||
let currentServer: Server;
|
||||
@@ -316,12 +316,12 @@ export const createMcpServer = (name: string, version: string): Server => {
|
||||
const server = new Server({ name, version }, { capabilities: { tools: {} } });
|
||||
server.setRequestHandler(ListToolsRequestSchema, async (_, extra) => {
|
||||
const sessionId = extra.sessionId || '';
|
||||
const groupId = getGroupId(sessionId);
|
||||
console.log(`Handling ListToolsRequest for groupId: ${groupId}`);
|
||||
const group = getGroup(sessionId);
|
||||
console.log(`Handling ListToolsRequest for group: ${group}`);
|
||||
const allServerInfos = serverInfos.filter((serverInfo) => {
|
||||
if (serverInfo.enabled === false) return false;
|
||||
if (!groupId) return true;
|
||||
const serversInGroup = getServersInGroup(groupId);
|
||||
if (!group) return true;
|
||||
const serversInGroup = getServersInGroup(group);
|
||||
return serversInGroup.includes(serverInfo.name);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,33 +1,48 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
||||
import { getMcpServer } from './mcpService.js';
|
||||
import { loadSettings } from '../config/index.js';
|
||||
|
||||
const transports: { [sessionId: string]: { transport: SSEServerTransport; groupId: string } } = {};
|
||||
const transports: { [sessionId: string]: { transport: SSEServerTransport; group: string } } = {};
|
||||
|
||||
export const getGroupId = (sessionId: string): string => {
|
||||
return transports[sessionId]?.groupId || '';
|
||||
export const getGroup = (sessionId: string): string => {
|
||||
return transports[sessionId]?.group || '';
|
||||
};
|
||||
|
||||
export const handleSseConnection = async (req: Request, res: Response): Promise<void> => {
|
||||
const settings = loadSettings();
|
||||
const routingConfig = settings.systemConfig?.routing || {
|
||||
enableGlobalRoute: true,
|
||||
enableGroupNameRoute: true,
|
||||
};
|
||||
const group = req.params.group;
|
||||
|
||||
// Check if this is a global route (no group) and if it's allowed
|
||||
if (!group && !routingConfig.enableGlobalRoute) {
|
||||
res.status(403).send('Global routes are disabled. Please specify a group ID.');
|
||||
return;
|
||||
}
|
||||
|
||||
const transport = new SSEServerTransport('/messages', res);
|
||||
const groupId = req.params.groupId;
|
||||
transports[transport.sessionId] = { transport, groupId };
|
||||
transports[transport.sessionId] = { transport, group: group };
|
||||
|
||||
res.on('close', () => {
|
||||
delete transports[transport.sessionId];
|
||||
console.log(`SSE connection closed: ${transport.sessionId}`);
|
||||
});
|
||||
|
||||
console.log(`New SSE connection established: ${transport.sessionId}`);
|
||||
console.log(
|
||||
`New SSE connection established: ${transport.sessionId} with group: ${group || 'global'}`,
|
||||
);
|
||||
await getMcpServer().connect(transport);
|
||||
};
|
||||
|
||||
export const handleSseMessage = async (req: Request, res: Response): Promise<void> => {
|
||||
const sessionId = req.query.sessionId as string;
|
||||
const { transport, groupId } = transports[sessionId];
|
||||
req.params.groupId = groupId;
|
||||
req.query.groupId = groupId;
|
||||
console.log(`Received message for sessionId: ${sessionId} in groupId: ${groupId}`);
|
||||
const { transport, group } = transports[sessionId];
|
||||
req.params.group = group;
|
||||
req.query.group = group;
|
||||
console.log(`Received message for sessionId: ${sessionId} in group: ${group}`);
|
||||
if (transport) {
|
||||
await transport.handlePostMessage(req, res);
|
||||
} else {
|
||||
|
||||
@@ -78,6 +78,13 @@ export interface McpSettings {
|
||||
[key: string]: ServerConfig; // Key-value pairs of server names and their configurations
|
||||
};
|
||||
groups?: IGroup[]; // Array of server groups
|
||||
systemConfig?: {
|
||||
routing?: {
|
||||
enableGlobalRoute?: boolean; // Controls whether the /sse endpoint without group is enabled
|
||||
enableGroupNameRoute?: boolean; // Controls whether group routing by name is allowed
|
||||
};
|
||||
// Add other system configuration sections here in the future
|
||||
};
|
||||
}
|
||||
|
||||
// Configuration details for an individual server
|
||||
|
||||
Reference in New Issue
Block a user