mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
feat: add request options configuration to server form (#171)
This commit is contained in:
@@ -36,7 +36,7 @@ export const loadSettings = (): McpSettings => {
|
||||
// Update cache
|
||||
settingsCache = settings;
|
||||
|
||||
console.log(`Loaded settings from ${settingsPath}:`, settings);
|
||||
console.log(`Loaded settings from ${settingsPath}`);
|
||||
return settings;
|
||||
} catch (error) {
|
||||
console.error(`Failed to load settings from ${settingsPath}:`, error);
|
||||
|
||||
@@ -1,37 +1,8 @@
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import fs from 'fs';
|
||||
import { auth } from './auth.js';
|
||||
import { initializeDefaultUser } from '../models/User.js';
|
||||
import config from '../config/index.js';
|
||||
|
||||
// Create __dirname equivalent for ES modules
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// Try to find the correct frontend file path
|
||||
const findFrontendPath = (): string => {
|
||||
// First try development environment path
|
||||
const devPath = path.join(dirname(__dirname), 'frontend', 'dist', 'index.html');
|
||||
if (fs.existsSync(devPath)) {
|
||||
return path.join(dirname(__dirname), 'frontend', 'dist');
|
||||
}
|
||||
|
||||
// Try npm/npx installed path (remove /dist directory)
|
||||
const npmPath = path.join(dirname(dirname(__dirname)), 'frontend', 'dist', 'index.html');
|
||||
if (fs.existsSync(npmPath)) {
|
||||
return path.join(dirname(dirname(__dirname)), 'frontend', 'dist');
|
||||
}
|
||||
|
||||
// If none of the above paths exist, return the most reasonable default path and log a warning
|
||||
console.warn('Warning: Could not locate frontend files. Using default path.');
|
||||
return path.join(dirname(__dirname), 'frontend', 'dist');
|
||||
};
|
||||
|
||||
const frontendPath = findFrontendPath();
|
||||
|
||||
export const errorHandler = (
|
||||
err: Error,
|
||||
_req: Request,
|
||||
@@ -52,6 +23,7 @@ export const initMiddlewares = (app: express.Application): void => {
|
||||
app.use((req, res, next) => {
|
||||
const basePath = config.basePath;
|
||||
// Only apply JSON parsing for API and auth routes, not for SSE or message endpoints
|
||||
// TODO exclude sse responses by mcp endpoint
|
||||
if (
|
||||
req.path !== `${basePath}/sse` &&
|
||||
!req.path.startsWith(`${basePath}/sse/`) &&
|
||||
|
||||
@@ -218,14 +218,27 @@ export const initializeClientsFromSettings = (isInit: boolean): ServerInfo[] =>
|
||||
},
|
||||
},
|
||||
);
|
||||
const timeout = isInit ? Number(config.initTimeout) : Number(config.timeout);
|
||||
|
||||
const initRequestOptions = isInit
|
||||
? {
|
||||
timeout: Number(config.initTimeout) || 60000,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
// Get request options from server configuration, with fallbacks
|
||||
const serverRequestOptions = conf.options || {};
|
||||
const requestOptions = {
|
||||
timeout: serverRequestOptions.timeout || 60000,
|
||||
resetTimeoutOnProgress: serverRequestOptions.resetTimeoutOnProgress || false,
|
||||
maxTotalTimeout: serverRequestOptions.maxTotalTimeout,
|
||||
};
|
||||
|
||||
client
|
||||
.connect(transport, { timeout: timeout })
|
||||
.connect(transport, initRequestOptions || requestOptions)
|
||||
.then(() => {
|
||||
console.log(`Successfully connected client for server: ${name}`);
|
||||
|
||||
client
|
||||
.listTools({}, { timeout: timeout })
|
||||
.listTools({}, initRequestOptions || requestOptions)
|
||||
.then((tools) => {
|
||||
console.log(`Successfully listed ${tools.tools.length} tools for server: ${name}`);
|
||||
const serverInfo = getServerByName(name);
|
||||
@@ -276,6 +289,7 @@ export const initializeClientsFromSettings = (isInit: boolean): ServerInfo[] =>
|
||||
tools: [],
|
||||
client,
|
||||
transport,
|
||||
options: requestOptions,
|
||||
createTime: Date.now(),
|
||||
});
|
||||
console.log(`Initialized client for server: ${name}`);
|
||||
@@ -696,14 +710,12 @@ export const handleCallToolRequest = async (request: any, extra: any) => {
|
||||
|
||||
// Special handling for call_tool
|
||||
if (request.params.name === 'call_tool') {
|
||||
let { toolName, arguments: toolArgs = {} } = request.params.arguments || {};
|
||||
|
||||
let { toolName } = request.params.arguments || {};
|
||||
if (!toolName) {
|
||||
throw new Error('toolName parameter is required');
|
||||
}
|
||||
|
||||
// arguments parameter is now optional
|
||||
|
||||
const { arguments: toolArgs = {} } = request.params.arguments || {};
|
||||
let targetServerInfo: ServerInfo | undefined;
|
||||
if (extra && extra.server) {
|
||||
targetServerInfo = getServerByName(extra.server);
|
||||
@@ -744,10 +756,14 @@ export const handleCallToolRequest = async (request: any, extra: any) => {
|
||||
toolName = toolName.startsWith(`${targetServerInfo.name}-`)
|
||||
? toolName.replace(`${targetServerInfo.name}-`, '')
|
||||
: toolName;
|
||||
const result = await client.callTool({
|
||||
name: toolName,
|
||||
arguments: finalArgs,
|
||||
});
|
||||
const result = await client.callTool(
|
||||
{
|
||||
name: toolName,
|
||||
arguments: finalArgs,
|
||||
},
|
||||
undefined,
|
||||
targetServerInfo.options || {},
|
||||
);
|
||||
|
||||
console.log(`Tool invocation result: ${JSON.stringify(result)}`);
|
||||
return result;
|
||||
@@ -766,7 +782,7 @@ export const handleCallToolRequest = async (request: any, extra: any) => {
|
||||
request.params.name = request.params.name.startsWith(`${serverInfo.name}-`)
|
||||
? request.params.name.replace(`${serverInfo.name}-`, '')
|
||||
: request.params.name;
|
||||
const result = await client.callTool(request.params);
|
||||
const result = await client.callTool(request.params, undefined, serverInfo.options || {});
|
||||
console.log(`Tool call result: ${JSON.stringify(result)}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
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 { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
||||
import { SmartRoutingConfig } from '../utils/smartRouting.js';
|
||||
|
||||
// User interface
|
||||
@@ -107,6 +108,7 @@ export interface ServerConfig {
|
||||
enabled?: boolean; // Flag to enable/disable the server
|
||||
keepAliveInterval?: number; // Keep-alive ping interval in milliseconds (default: 60000ms for SSE servers)
|
||||
tools?: Record<string, { enabled: boolean; description?: string }>; // Tool-specific configurations with enable/disable state and custom descriptions
|
||||
options?: Partial<Pick<RequestOptions, 'timeout' | 'resetTimeoutOnProgress' | 'maxTotalTimeout'>>; // MCP request options configuration
|
||||
}
|
||||
|
||||
// Information about a server's status and tools
|
||||
@@ -117,6 +119,7 @@ export interface ServerInfo {
|
||||
tools: ToolInfo[]; // List of tools available on the server
|
||||
client?: Client; // Client instance for communication
|
||||
transport?: SSEClientTransport | StdioClientTransport | StreamableHTTPClientTransport; // Transport mechanism used
|
||||
options?: RequestOptions; // Options for requests
|
||||
createTime: number; // Timestamp of when the server was created
|
||||
enabled?: boolean; // Flag to indicate if the server is enabled
|
||||
keepAliveIntervalId?: NodeJS.Timeout; // Timer ID for keep-alive ping interval
|
||||
|
||||
Reference in New Issue
Block a user