Enhance routing and settings functionality (#29)

This commit is contained in:
samanhappy
2025-04-21 17:51:21 +08:00
committed by GitHub
parent 6bf22025e1
commit afd1ee7a50
21 changed files with 541 additions and 131 deletions

View File

@@ -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,

View File

@@ -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',
});
}
};

View File

@@ -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);

View File

@@ -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) => {

View File

@@ -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 : [];
};

View File

@@ -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);
});

View File

@@ -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 {

View File

@@ -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