feat(dao): Implement comprehensive DAO layer (#308)

Co-authored-by: samanhappy@qq.com <my6051199>
This commit is contained in:
samanhappy
2025-08-27 15:21:30 +08:00
committed by GitHub
parent f9019545c3
commit bbd6c891c9
24 changed files with 2935 additions and 126 deletions

View File

@@ -11,16 +11,19 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { ServerInfo, ServerConfig, Tool } from '../types/index.js';
import { loadSettings, saveSettings, expandEnvVars, replaceEnvVars } from '../config/index.js';
import { loadSettings, expandEnvVars, replaceEnvVars } from '../config/index.js';
import config from '../config/index.js';
import { getGroup } from './sseService.js';
import { getServersInGroup, getServerConfigInGroup } from './groupService.js';
import { saveToolsAsVectorEmbeddings, searchToolsByVector } from './vectorSearchService.js';
import { OpenAPIClient } from '../clients/openapi.js';
import { getDataService } from './services.js';
import { getServerDao, ServerConfigWithName } from '../dao/index.js';
const servers: { [sessionId: string]: Server } = {};
const serverDao = getServerDao();
// Helper function to set up keep-alive ping for SSE connections
const setupKeepAlive = (serverInfo: ServerInfo, serverConfig: ServerConfig): void => {
// Only set up keep-alive for SSE connections
@@ -253,15 +256,13 @@ const callToolWithReconnect = async (
serverInfo.client.close();
serverInfo.transport.close();
// Get server configuration to recreate transport
const settings = loadSettings();
const conf = settings.mcpServers[serverInfo.name];
if (!conf) {
const server = await serverDao.findById(serverInfo.name);
if (!server) {
throw new Error(`Server configuration not found for: ${serverInfo.name}`);
}
// Recreate transport using helper function
const newTransport = createTransportFromConfig(serverInfo.name, conf);
const newTransport = createTransportFromConfig(serverInfo.name, server);
// Create new client
const client = new Client(
@@ -335,11 +336,12 @@ export const initializeClientsFromSettings = async (
isInit: boolean,
serverName?: string,
): Promise<ServerInfo[]> => {
const settings = loadSettings();
const allServers: ServerConfigWithName[] = await serverDao.findAll();
const existingServerInfos = serverInfos;
serverInfos = [];
for (const [name, conf] of Object.entries(settings.mcpServers)) {
for (const conf of allServers) {
const { name } = conf;
// Skip disabled servers
if (conf.enabled === false) {
console.log(`Skipping disabled server: ${name}`);
@@ -567,14 +569,14 @@ export const registerAllTools = async (isInit: boolean, serverName?: string): Pr
};
// Get all server information
export const getServersInfo = (): Omit<ServerInfo, 'client' | 'transport'>[] => {
const settings = loadSettings();
export const getServersInfo = async (): Promise<Omit<ServerInfo, 'client' | 'transport'>[]> => {
const allServers: ServerConfigWithName[] = await serverDao.findAll();
const dataService = getDataService();
const filterServerInfos: ServerInfo[] = dataService.filterData
? dataService.filterData(serverInfos)
: serverInfos;
const infos = filterServerInfos.map(({ name, status, tools, prompts, createTime, error }) => {
const serverConfig = settings.mcpServers[name];
const serverConfig = allServers.find((server) => server.name === name);
const enabled = serverConfig ? serverConfig.enabled !== false : true;
// Add enabled status and custom description to each tool
@@ -619,10 +621,8 @@ export const getServerByName = (name: string): ServerInfo | undefined => {
};
// Filter tools by server configuration
const filterToolsByConfig = (serverName: string, tools: Tool[]): Tool[] => {
const settings = loadSettings();
const serverConfig = settings.mcpServers[serverName];
const filterToolsByConfig = async (serverName: string, tools: Tool[]): Promise<Tool[]> => {
const serverConfig = await serverDao.findById(serverName);
if (!serverConfig || !serverConfig.tools) {
// If no tool configuration exists, all tools are enabled by default
return tools;
@@ -645,44 +645,26 @@ export const addServer = async (
name: string,
config: ServerConfig,
): Promise<{ success: boolean; message?: string }> => {
try {
const settings = loadSettings();
if (settings.mcpServers[name]) {
return { success: false, message: 'Server name already exists' };
}
settings.mcpServers[name] = config;
if (!saveSettings(settings)) {
return { success: false, message: 'Failed to save settings' };
}
const server: ServerConfigWithName = { name, ...config };
const result = await serverDao.create(server);
if (result) {
return { success: true, message: 'Server added successfully' };
} catch (error) {
console.error(`Failed to add server: ${name}`, error);
} else {
return { success: false, message: 'Failed to add server' };
}
};
// Remove server
export const removeServer = (name: string): { success: boolean; message?: string } => {
try {
const settings = loadSettings();
if (!settings.mcpServers[name]) {
return { success: false, message: 'Server not found' };
}
delete settings.mcpServers[name];
if (!saveSettings(settings)) {
return { success: false, message: 'Failed to save settings' };
}
serverInfos = serverInfos.filter((serverInfo) => serverInfo.name !== name);
return { success: true, message: 'Server removed successfully' };
} catch (error) {
console.error(`Failed to remove server: ${name}`, error);
return { success: false, message: `Failed to remove server: ${error}` };
export const removeServer = async (
name: string,
): Promise<{ success: boolean; message?: string }> => {
const result = await serverDao.delete(name);
if (!result) {
return { success: false, message: 'Failed to remove server' };
}
serverInfos = serverInfos.filter((serverInfo) => serverInfo.name !== name);
return { success: true, message: 'Server removed successfully' };
};
// Add or update server (supports overriding existing servers for DXT)
@@ -692,9 +674,7 @@ export const addOrUpdateServer = async (
allowOverride: boolean = false,
): Promise<{ success: boolean; message?: string }> => {
try {
const settings = loadSettings();
const exists = !!settings.mcpServers[name];
const exists = await serverDao.exists(name);
if (exists && !allowOverride) {
return { success: false, message: 'Server name already exists' };
}
@@ -708,9 +688,10 @@ export const addOrUpdateServer = async (
serverInfos = serverInfos.filter((serverInfo) => serverInfo.name !== name);
}
settings.mcpServers[name] = config;
if (!saveSettings(settings)) {
return { success: false, message: 'Failed to save settings' };
if (exists) {
await serverDao.update(name, config);
} else {
await serverDao.create({ name, ...config });
}
const action = exists ? 'updated' : 'added';
@@ -745,18 +726,7 @@ export const toggleServerStatus = async (
enabled: boolean,
): Promise<{ success: boolean; message?: string }> => {
try {
const settings = loadSettings();
if (!settings.mcpServers[name]) {
return { success: false, message: 'Server not found' };
}
// Update the enabled status in settings
settings.mcpServers[name].enabled = enabled;
if (!saveSettings(settings)) {
return { success: false, message: 'Failed to save settings' };
}
await serverDao.setEnabled(name, enabled);
// If disabling, disconnect the server and remove from active servers
if (!enabled) {
closeServer(name);
@@ -865,7 +835,7 @@ Available servers: ${serversList}`;
for (const serverInfo of allServerInfos) {
if (serverInfo.tools && serverInfo.tools.length > 0) {
// Filter tools based on server configuration
let enabledTools = filterToolsByConfig(serverInfo.name, serverInfo.tools);
let enabledTools = await filterToolsByConfig(serverInfo.name, serverInfo.tools);
// If this is a group request, apply group-level tool filtering
if (group) {
@@ -880,8 +850,7 @@ Available servers: ${serversList}`;
}
// Apply custom descriptions from server configuration
const settings = loadSettings();
const serverConfig = settings.mcpServers[serverInfo.name];
const serverConfig = await serverDao.findById(serverInfo.name);
const toolsWithCustomDescriptions = enabledTools.map((tool) => {
const toolConfig = serverConfig?.tools?.[tool.name];
return {
@@ -931,8 +900,9 @@ export const handleCallToolRequest = async (request: any, extra: any) => {
const searchResults = await searchToolsByVector(query, limitNum, thresholdNum, servers);
console.log(`Search results: ${JSON.stringify(searchResults)}`);
// Find actual tool information from serverInfos by serverName and toolName
const tools = searchResults
.map((result) => {
// First resolve all tool promises
const resolvedTools = await Promise.all(
searchResults.map(async (result) => {
// Find the server in serverInfos
const server = serverInfos.find(
(serverInfo) =>
@@ -945,17 +915,17 @@ export const handleCallToolRequest = async (request: any, extra: any) => {
const actualTool = server.tools.find((tool) => tool.name === result.toolName);
if (actualTool) {
// Check if the tool is enabled in configuration
const enabledTools = filterToolsByConfig(server.name, [actualTool]);
const enabledTools = await filterToolsByConfig(server.name, [actualTool]);
if (enabledTools.length > 0) {
// Apply custom description from configuration
const settings = loadSettings();
const serverConfig = settings.mcpServers[server.name];
const serverConfig = await serverDao.findById(server.name);
const toolConfig = serverConfig?.tools?.[actualTool.name];
// Return the actual tool info from serverInfos with custom description
return {
...actualTool,
description: toolConfig?.description || actualTool.description,
serverName: result.serverName, // Add serverName for filtering
};
}
}
@@ -966,19 +936,25 @@ export const handleCallToolRequest = async (request: any, extra: any) => {
name: result.toolName,
description: result.description || '',
inputSchema: cleanInputSchema(result.inputSchema || {}),
serverName: result.serverName, // Add serverName for filtering
};
})
.filter((tool) => {
}),
);
// Now filter the resolved tools
const tools = await Promise.all(
resolvedTools.filter(async (tool) => {
// Additional filter to remove tools that are disabled
if (tool.name) {
const serverName = searchResults.find((r) => r.toolName === tool.name)?.serverName;
const serverName = tool.serverName;
if (serverName) {
const enabledTools = filterToolsByConfig(serverName, [tool as Tool]);
const enabledTools = await filterToolsByConfig(serverName, [tool as Tool]);
return enabledTools.length > 0;
}
}
return true; // Keep fallback results
});
}),
);
// Add usage guidance to the response
const response = {
@@ -1234,8 +1210,7 @@ export const handleListPromptsRequest = async (_: any, extra: any) => {
for (const serverInfo of allServerInfos) {
if (serverInfo.prompts && serverInfo.prompts.length > 0) {
// Filter prompts based on server configuration
const settings = loadSettings();
const serverConfig = settings.mcpServers[serverInfo.name];
const serverConfig = await serverDao.findById(serverInfo.name);
let enabledPrompts = serverInfo.prompts;
if (serverConfig && serverConfig.prompts) {