feat: Enhance group management with server tool configuration (#250)

This commit is contained in:
samanhappy
2025-07-29 17:31:05 +08:00
committed by GitHub
parent 5bb2715094
commit a6cea2ad3f
14 changed files with 827 additions and 152 deletions

View File

@@ -1,9 +1,21 @@
import { v4 as uuidv4 } from 'uuid';
import { IGroup } from '../types/index.js';
import { IGroup, IGroupServerConfig } from '../types/index.js';
import { loadSettings, saveSettings } from '../config/index.js';
import { notifyToolChanged } from './mcpService.js';
import { getDataService } from './services.js';
// Helper function to normalize group servers configuration
const normalizeGroupServers = (servers: string[] | IGroupServerConfig[]): IGroupServerConfig[] => {
return servers.map((server) => {
if (typeof server === 'string') {
// Backward compatibility: string format means all tools
return { name: server, tools: 'all' };
}
// New format: ensure tools defaults to 'all' if not specified
return { name: server.name, tools: server.tools || 'all' };
});
};
// Get all groups
export const getAllGroups = (): IGroup[] => {
const settings = loadSettings();
@@ -32,7 +44,7 @@ export const getGroupByIdOrName = (key: string): IGroup | undefined => {
export const createGroup = (
name: string,
description?: string,
servers: string[] = [],
servers: string[] | IGroupServerConfig[] = [],
owner?: string,
): IGroup | null => {
try {
@@ -44,8 +56,11 @@ export const createGroup = (
return null;
}
// Filter out non-existent servers
const validServers = servers.filter((serverName) => settings.mcpServers[serverName]);
// Normalize servers configuration and filter out non-existent servers
const normalizedServers = normalizeGroupServers(servers);
const validServers: IGroupServerConfig[] = normalizedServers.filter(
(serverConfig) => settings.mcpServers[serverConfig.name],
);
const newGroup: IGroup = {
id: uuidv4(),
@@ -91,9 +106,12 @@ export const updateGroup = (id: string, data: Partial<IGroup>): IGroup | null =>
return null;
}
// If servers array is provided, validate server existence
// If servers array is provided, validate server existence and normalize format
if (data.servers) {
data.servers = data.servers.filter((serverName) => settings.mcpServers[serverName]);
const normalizedServers = normalizeGroupServers(data.servers);
data.servers = normalizedServers.filter(
(serverConfig) => settings.mcpServers[serverConfig.name],
);
}
const updatedGroup = {
@@ -116,7 +134,11 @@ export const updateGroup = (id: string, data: Partial<IGroup>): IGroup | null =>
};
// Update servers in a group (batch update)
export const updateGroupServers = (groupId: string, servers: string[]): IGroup | null => {
// Update group servers (maintaining backward compatibility)
export const updateGroupServers = (
groupId: string,
servers: string[] | IGroupServerConfig[],
): IGroup | null => {
try {
const settings = loadSettings();
if (!settings.groups) {
@@ -128,8 +150,11 @@ export const updateGroupServers = (groupId: string, servers: string[]): IGroup |
return null;
}
// Filter out non-existent servers
const validServers = servers.filter((serverName) => settings.mcpServers[serverName]);
// Normalize and filter out non-existent servers
const normalizedServers = normalizeGroupServers(servers);
const validServers = normalizedServers.filter(
(serverConfig) => settings.mcpServers[serverConfig.name],
);
settings.groups[groupIndex].servers = validServers;
@@ -186,10 +211,12 @@ export const addServerToGroup = (groupId: string, serverName: string): IGroup |
}
const group = settings.groups[groupIndex];
const normalizedServers = normalizeGroupServers(group.servers);
// Add server to group if not already in it
if (!group.servers.includes(serverName)) {
group.servers.push(serverName);
if (!normalizedServers.some((server) => server.name === serverName)) {
normalizedServers.push({ name: serverName, tools: 'all' });
group.servers = normalizedServers;
if (!saveSettings(settings)) {
return null;
@@ -218,7 +245,8 @@ export const removeServerFromGroup = (groupId: string, serverName: string): IGro
}
const group = settings.groups[groupIndex];
group.servers = group.servers.filter((name) => name !== serverName);
const normalizedServers = normalizeGroupServers(group.servers);
group.servers = normalizedServers.filter((server) => server.name !== serverName);
if (!saveSettings(settings)) {
return null;
@@ -234,5 +262,71 @@ export const removeServerFromGroup = (groupId: string, serverName: string): IGro
// Get all servers in a group
export const getServersInGroup = (groupId: string): string[] => {
const group = getGroupByIdOrName(groupId);
return group ? group.servers : [];
if (!group) return [];
const normalizedServers = normalizeGroupServers(group.servers);
return normalizedServers.map((server) => server.name);
};
// Get server configuration from group (including tool selection)
export const getServerConfigInGroup = (
groupId: string,
serverName: string,
): IGroupServerConfig | undefined => {
const group = getGroupByIdOrName(groupId);
if (!group) return undefined;
const normalizedServers = normalizeGroupServers(group.servers);
return normalizedServers.find((server) => server.name === serverName);
};
// Get all server configurations in a group
export const getServerConfigsInGroup = (groupId: string): IGroupServerConfig[] => {
const group = getGroupByIdOrName(groupId);
if (!group) return [];
return normalizeGroupServers(group.servers);
};
// Update tools selection for a specific server in a group
export const updateServerToolsInGroup = (
groupId: string,
serverName: string,
tools: string[] | 'all',
): IGroup | null => {
try {
const settings = loadSettings();
if (!settings.groups) {
return null;
}
const groupIndex = settings.groups.findIndex((group) => group.id === groupId);
if (groupIndex === -1) {
return null;
}
// Verify server exists
if (!settings.mcpServers[serverName]) {
return null;
}
const group = settings.groups[groupIndex];
const normalizedServers = normalizeGroupServers(group.servers);
const serverIndex = normalizedServers.findIndex((server) => server.name === serverName);
if (serverIndex === -1) {
return null; // Server not in group
}
// Update the tools configuration for the server
normalizedServers[serverIndex].tools = tools;
group.servers = normalizedServers;
if (!saveSettings(settings)) {
return null;
}
notifyToolChanged();
return group;
} catch (error) {
console.error(`Failed to update tools for server ${serverName} in group ${groupId}:`, error);
return null;
}
};

View File

@@ -8,7 +8,7 @@ import { ServerInfo, ServerConfig, ToolInfo } from '../types/index.js';
import { loadSettings, saveSettings, expandEnvVars, replaceEnvVars } from '../config/index.js';
import config from '../config/index.js';
import { getGroup } from './sseService.js';
import { getServersInGroup } from './groupService.js';
import { getServersInGroup, getServerConfigInGroup } from './groupService.js';
import { saveToolsAsVectorEmbeddings, searchToolsByVector } from './vectorSearchService.js';
import { OpenAPIClient } from '../clients/openapi.js';
import { getDataService } from './services.js';
@@ -823,10 +823,22 @@ Available servers: ${serversList}`;
const allTools = [];
for (const serverInfo of allServerInfos) {
if (serverInfo.tools && serverInfo.tools.length > 0) {
// Filter tools based on server configuration and apply custom descriptions
const enabledTools = filterToolsByConfig(serverInfo.name, serverInfo.tools);
// Filter tools based on server configuration
let enabledTools = filterToolsByConfig(serverInfo.name, serverInfo.tools);
// Apply custom descriptions from configuration
// If this is a group request, apply group-level tool filtering
if (group) {
const serverConfig = getServerConfigInGroup(group, serverInfo.name);
if (serverConfig && serverConfig.tools !== 'all' && Array.isArray(serverConfig.tools)) {
// Filter tools based on group configuration
const allowedToolNames = serverConfig.tools.map(
(toolName) => `${serverInfo.name}-${toolName}`,
);
enabledTools = enabledTools.filter((tool) => allowedToolNames.includes(tool.name));
}
}
// Apply custom descriptions from server configuration
const settings = loadSettings();
const serverConfig = settings.mcpServers[serverInfo.name];
const toolsWithCustomDescriptions = enabledTools.map((tool) => {