@1mcp/agent 0.2.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,9 +4,9 @@ import { withErrorHandling } from '../utils/errorHandling.js';
4
4
  /**
5
5
  * Sets up client-to-server notification handlers
6
6
  * @param clients Record of client instances
7
- * @param server The MCP server instance
7
+ * @param serverInfo The MCP server instance
8
8
  */
9
- export function setupClientToServerNotifications(clients, server) {
9
+ export function setupClientToServerNotifications(clients, serverInfo) {
10
10
  const clientNotificationSchemas = [
11
11
  CancelledNotificationSchema,
12
12
  ProgressNotificationSchema,
@@ -16,11 +16,11 @@ export function setupClientToServerNotifications(clients, server) {
16
16
  ToolListChangedNotificationSchema,
17
17
  PromptListChangedNotificationSchema,
18
18
  ];
19
- for (const [name, client] of Object.entries(clients)) {
19
+ for (const [name, clientInfo] of Object.entries(clients)) {
20
20
  clientNotificationSchemas.forEach((schema) => {
21
- client.setNotificationHandler(schema, withErrorHandling(async (notification) => {
21
+ clientInfo.client.setNotificationHandler(schema, withErrorHandling(async (notification) => {
22
22
  logger.info(`Received notification in client: ${name} ${JSON.stringify(notification)}`);
23
- server.notification({
23
+ serverInfo.server.notification({
24
24
  ...notification,
25
25
  params: {
26
26
  ...notification.params,
@@ -34,20 +34,20 @@ export function setupClientToServerNotifications(clients, server) {
34
34
  /**
35
35
  * Sets up server-to-client notification handlers
36
36
  * @param clients Record of client instances
37
- * @param server The MCP server instance
37
+ * @param serverInfo The MCP server instance
38
38
  */
39
- export function setupServerToClientNotifications(clients, server) {
39
+ export function setupServerToClientNotifications(clients, serverInfo) {
40
40
  const serverNotificationSchemas = [
41
41
  CancelledNotificationSchema,
42
42
  ProgressNotificationSchema,
43
43
  InitializedNotificationSchema,
44
44
  RootsListChangedNotificationSchema,
45
45
  ];
46
- for (const [name, client] of Object.entries(clients)) {
46
+ for (const [name, clientInfo] of Object.entries(clients)) {
47
47
  serverNotificationSchemas.forEach((schema) => {
48
- server.setNotificationHandler(schema, withErrorHandling(async (notification) => {
48
+ serverInfo.server.setNotificationHandler(schema, withErrorHandling(async (notification) => {
49
49
  logger.info(`Received notification in server: ${name} ${JSON.stringify(notification)}`);
50
- client.notification({
50
+ clientInfo.client.notification({
51
51
  ...notification,
52
52
  params: {
53
53
  ...notification.params,
@@ -1,19 +1,20 @@
1
- import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
2
- import logger from '../logger/logger.js';
3
- import { setLogLevel } from '../logger/logger.js';
4
- import { MCP_URI_SEPARATOR, MCP_SERVER_NAME } from '../constants.js';
5
- import { executeClientOperation } from '../clients/clientManager.js';
6
- import { ProxyError, parseUri, withErrorHandling } from '../utils/errorHandling.js';
1
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, SetLevelRequestSchema, CallToolResultSchema, CreateMessageRequestSchema, ListRootsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
2
+ import logger, { setLogLevel } from '../logger/logger.js';
3
+ import { MCP_URI_SEPARATOR, MCP_SERVER_NAME, ERROR_CODES } from '../constants.js';
4
+ import { executeClientOperation, executeServerOperation } from '../clients/clientManager.js';
5
+ import { parseUri, withErrorHandling } from '../utils/errorHandling.js';
6
+ import { MCPError } from '../utils/errorTypes.js';
7
+ import { filterClients, byCapabilities, byTags } from '../utils/clientFiltering.js';
7
8
  /**
8
9
  * Sends a partial failure notification to inform clients about backend failures
9
10
  * @param server The MCP server instance
10
11
  * @param operation The operation that partially failed
11
12
  * @param failedClients Array of failed clients with error details
12
13
  */
13
- function sendPartialFailureNotification(server, operation, failedClients) {
14
+ function sendPartialFailureNotification(serverInfo, operation, failedClients) {
14
15
  if (failedClients.length === 0)
15
16
  return;
16
- server.notification({
17
+ serverInfo.server.notification({
17
18
  method: 'notifications/message',
18
19
  params: {
19
20
  level: 'warning',
@@ -27,45 +28,64 @@ function sendPartialFailureNotification(server, operation, failedClients) {
27
28
  },
28
29
  });
29
30
  }
31
+ /**
32
+ * Registers server-specific request handlers
33
+ * @param clients Record of client instances
34
+ * @param serverInfo The MCP server instance
35
+ */
36
+ function registerServerRequestHandlers(clients, serverInfo) {
37
+ Object.entries(clients).forEach(([_, clientInfo]) => {
38
+ clientInfo.client.setRequestHandler(CreateMessageRequestSchema, withErrorHandling(async (request) => {
39
+ return executeServerOperation(serverInfo, (_server) => _server.server.createMessage(request.params, {
40
+ timeout: clientInfo.transport.timeout,
41
+ }));
42
+ }, 'Error creating message'));
43
+ clientInfo.client.setRequestHandler(ListRootsRequestSchema, withErrorHandling(async (request) => {
44
+ return executeServerOperation(serverInfo, (_server) => _server.server.listRoots(request.params, {
45
+ timeout: clientInfo.transport.timeout,
46
+ }));
47
+ }, 'Error listing roots'));
48
+ });
49
+ }
30
50
  /**
31
51
  * Registers all request handlers based on available capabilities
32
52
  * @param clients Record of client instances
33
53
  * @param server The MCP server instance
34
54
  * @param capabilities The server capabilities
55
+ * @param tags Array of tags to filter clients by
35
56
  */
36
- export function registerRequestHandlers(clients, server, capabilities) {
57
+ export function registerRequestHandlers(clients, serverInfo) {
37
58
  // Register logging level handler
38
- server.setRequestHandler(SetLevelRequestSchema, async (request) => {
59
+ serverInfo.server.setRequestHandler(SetLevelRequestSchema, async (request) => {
39
60
  setLogLevel(request.params.level);
40
61
  return {};
41
62
  });
42
- // Register resource-related handlers if capability is available
43
- if (capabilities.resources) {
44
- registerResourceHandlers(clients, server);
45
- }
46
- // Register tool-related handlers if capability is available
47
- if (capabilities.tools) {
48
- registerToolHandlers(clients, server);
49
- }
50
- // Register prompt-related handlers if capability is available
51
- if (capabilities.prompts) {
52
- registerPromptHandlers(clients, server);
53
- }
63
+ // Register resource-related handlers
64
+ registerResourceHandlers(clients, serverInfo);
65
+ // Register tool-related handlers
66
+ registerToolHandlers(clients, serverInfo);
67
+ // Register prompt-related handlers
68
+ registerPromptHandlers(clients, serverInfo);
69
+ // Register server-specific request handlers
70
+ registerServerRequestHandlers(clients, serverInfo);
54
71
  }
55
72
  /**
56
73
  * Registers resource-related request handlers
57
74
  * @param clients Record of client instances
58
- * @param server The MCP server instance
75
+ * @param serverInfo The MCP server instance
59
76
  */
60
- function registerResourceHandlers(clients, server) {
77
+ function registerResourceHandlers(clients, serverInfo) {
61
78
  // List Resources handler
62
- server.setRequestHandler(ListResourcesRequestSchema, withErrorHandling(async (request) => {
79
+ serverInfo.server.setRequestHandler(ListResourcesRequestSchema, withErrorHandling(async (request) => {
63
80
  const resources = [];
64
81
  const failedClients = [];
65
- for (const [name, client] of Object.entries(clients)) {
82
+ const filteredClients = filterClients(byCapabilities({ resources: {} }), byTags(serverInfo.tags))(clients);
83
+ for (const [name, clientInfo] of Object.entries(filteredClients)) {
66
84
  logger.info(`Listing resources for ${name}`);
67
85
  try {
68
- const result = await client.listResources(request.params);
86
+ const result = await clientInfo.client.listResources(request.params, {
87
+ timeout: clientInfo.transport.timeout,
88
+ });
69
89
  resources.push(...result.resources.map((resource) => ({
70
90
  uri: `${name}${MCP_URI_SEPARATOR}${resource.uri}`,
71
91
  name: resource.name,
@@ -78,24 +98,29 @@ function registerResourceHandlers(clients, server) {
78
98
  failedClients.push({ name, error: error instanceof Error ? error.message : String(error) });
79
99
  }
80
100
  }
81
- // If all clients failed, throw an error
82
- if (failedClients.length === Object.keys(clients).length && Object.keys(clients).length > 0) {
83
- throw new ProxyError('Failed to list resources from all clients', new Error(JSON.stringify(failedClients)));
101
+ // If all capable clients failed, throw an error
102
+ if (failedClients.length === Object.keys(filteredClients).length && Object.keys(filteredClients).length > 0) {
103
+ throw new MCPError('Failed to list resources from all capable clients', ERROR_CODES.INTERNAL_SERVER_ERROR, {
104
+ failedClients,
105
+ });
84
106
  }
85
107
  // Send notification about partial failures
86
108
  if (failedClients.length > 0) {
87
- sendPartialFailureNotification(server, 'listResources', failedClients);
109
+ sendPartialFailureNotification(serverInfo, 'listResources', failedClients);
88
110
  }
89
111
  return { resources };
90
112
  }, 'Error listing resources'));
91
113
  // List Resource Templates handler
92
- server.setRequestHandler(ListResourceTemplatesRequestSchema, withErrorHandling(async (request) => {
114
+ serverInfo.server.setRequestHandler(ListResourceTemplatesRequestSchema, withErrorHandling(async (request) => {
93
115
  const resourceTemplates = [];
94
116
  const failedClients = [];
95
- for (const [name, client] of Object.entries(clients)) {
117
+ const capableClients = filterClients(byCapabilities({ resources: {} }), byTags(serverInfo.tags))(clients);
118
+ for (const [name, clientInfo] of Object.entries(capableClients)) {
96
119
  logger.info(`Listing resource templates for ${name}`);
97
120
  try {
98
- const result = await client.listResourceTemplates(request.params);
121
+ const result = await clientInfo.client.listResourceTemplates(request.params, {
122
+ timeout: clientInfo.transport.timeout,
123
+ });
99
124
  resourceTemplates.push(...result.resourceTemplates.map((template) => ({
100
125
  uriTemplate: `${name}${MCP_URI_SEPARATOR}${template.uriTemplate}`,
101
126
  name: template.name,
@@ -109,45 +134,56 @@ function registerResourceHandlers(clients, server) {
109
134
  }
110
135
  }
111
136
  // If all clients failed, throw an error
112
- if (failedClients.length === Object.keys(clients).length && Object.keys(clients).length > 0) {
113
- throw new ProxyError('Failed to list resource templates from all clients', new Error(JSON.stringify(failedClients)));
137
+ if (failedClients.length === Object.keys(capableClients).length && Object.keys(capableClients).length > 0) {
138
+ throw new MCPError('Failed to list resource templates from all clients', ERROR_CODES.INTERNAL_SERVER_ERROR, {
139
+ failedClients,
140
+ });
114
141
  }
115
142
  // Send notification about partial failures
116
143
  if (failedClients.length > 0) {
117
- sendPartialFailureNotification(server, 'listResourceTemplates', failedClients);
144
+ sendPartialFailureNotification(serverInfo, 'listResourceTemplates', failedClients);
118
145
  }
119
146
  return { resourceTemplates };
120
147
  }, 'Error listing resource templates'));
121
148
  // Subscribe Resource handler
122
- server.setRequestHandler(SubscribeRequestSchema, withErrorHandling(async (request) => {
149
+ serverInfo.server.setRequestHandler(SubscribeRequestSchema, withErrorHandling(async (request) => {
123
150
  const { clientName, resourceName } = parseUri(request.params.uri, MCP_URI_SEPARATOR);
124
- return executeClientOperation(clients, clientName, (client) => client.subscribeResource({ ...request.params, uri: resourceName }));
151
+ return executeClientOperation(clients, clientName, (clientInfo) => clientInfo.client.subscribeResource({ ...request.params, uri: resourceName }, {
152
+ timeout: clientInfo.transport.timeout,
153
+ }));
125
154
  }, 'Error subscribing to resource'));
126
155
  // Unsubscribe Resource handler
127
- server.setRequestHandler(UnsubscribeRequestSchema, withErrorHandling(async (request) => {
156
+ serverInfo.server.setRequestHandler(UnsubscribeRequestSchema, withErrorHandling(async (request) => {
128
157
  const { clientName, resourceName } = parseUri(request.params.uri, MCP_URI_SEPARATOR);
129
- return executeClientOperation(clients, clientName, (client) => client.unsubscribeResource({ ...request.params, uri: resourceName }));
158
+ return executeClientOperation(clients, clientName, (clientInfo) => clientInfo.client.unsubscribeResource({ ...request.params, uri: resourceName }, {
159
+ timeout: clientInfo.transport.timeout,
160
+ }));
130
161
  }, 'Error unsubscribing from resource'));
131
162
  // Read Resource handler
132
- server.setRequestHandler(ReadResourceRequestSchema, withErrorHandling(async (request) => {
163
+ serverInfo.server.setRequestHandler(ReadResourceRequestSchema, withErrorHandling(async (request) => {
133
164
  const { clientName, resourceName } = parseUri(request.params.uri, MCP_URI_SEPARATOR);
134
- return executeClientOperation(clients, clientName, (client) => client.readResource({ ...request.params, uri: resourceName }));
165
+ return executeClientOperation(clients, clientName, (clientInfo) => clientInfo.client.readResource({ ...request.params, uri: resourceName }, {
166
+ timeout: clientInfo.transport.timeout,
167
+ }));
135
168
  }, 'Error reading resource'));
136
169
  }
137
170
  /**
138
171
  * Registers tool-related request handlers
139
172
  * @param clients Record of client instances
140
- * @param server The MCP server instance
173
+ * @param serverInfo The MCP server instance
141
174
  */
142
- function registerToolHandlers(clients, server) {
175
+ function registerToolHandlers(clients, serverInfo) {
143
176
  // List Tools handler
144
- server.setRequestHandler(ListToolsRequestSchema, withErrorHandling(async (request) => {
177
+ serverInfo.server.setRequestHandler(ListToolsRequestSchema, withErrorHandling(async (request) => {
145
178
  const tools = [];
146
179
  const failedClients = [];
147
- for (const [name, client] of Object.entries(clients)) {
180
+ const capableClients = filterClients(byCapabilities({ tools: {} }), byTags(serverInfo.tags))(clients);
181
+ for (const [name, clientInfo] of Object.entries(capableClients)) {
148
182
  logger.info(`Listing tools for ${name}`);
149
183
  try {
150
- const result = await client.listTools(request.params);
184
+ const result = await clientInfo.client.listTools(request.params, {
185
+ timeout: clientInfo.transport.timeout,
186
+ });
151
187
  tools.push(...result.tools.map((tool) => ({
152
188
  name: `${name}${MCP_URI_SEPARATOR}${tool.name}`,
153
189
  description: tool.description,
@@ -160,35 +196,40 @@ function registerToolHandlers(clients, server) {
160
196
  }
161
197
  }
162
198
  // If all clients failed, throw an error
163
- if (failedClients.length === Object.keys(clients).length && Object.keys(clients).length > 0) {
164
- throw new ProxyError('Failed to list tools from all clients', new Error(JSON.stringify(failedClients)));
199
+ if (failedClients.length === Object.keys(capableClients).length && Object.keys(capableClients).length > 0) {
200
+ throw new MCPError('Failed to list tools from all clients', ERROR_CODES.INTERNAL_SERVER_ERROR, {
201
+ failedClients,
202
+ });
165
203
  }
166
204
  // Send notification about partial failures
167
205
  if (failedClients.length > 0) {
168
- sendPartialFailureNotification(server, 'listTools', failedClients);
206
+ sendPartialFailureNotification(serverInfo, 'listTools', failedClients);
169
207
  }
170
208
  return { tools };
171
209
  }, 'Error listing tools'));
172
210
  // Call Tool handler
173
- server.setRequestHandler(CallToolRequestSchema, withErrorHandling(async (request) => {
211
+ serverInfo.server.setRequestHandler(CallToolRequestSchema, withErrorHandling(async (request) => {
174
212
  const { clientName, resourceName: toolName } = parseUri(request.params.name, MCP_URI_SEPARATOR);
175
- return executeClientOperation(clients, clientName, (client) => client.callTool({ ...request.params, name: toolName }));
213
+ return executeClientOperation(clients, clientName, (clientInfo) => clientInfo.client.callTool({ ...request.params, name: toolName }, CallToolResultSchema, {
214
+ timeout: clientInfo.transport.timeout,
215
+ }));
176
216
  }, 'Error calling tool'));
177
217
  }
178
218
  /**
179
219
  * Registers prompt-related request handlers
180
220
  * @param clients Record of client instances
181
- * @param server The MCP server instance
221
+ * @param serverInfo The MCP server instance
182
222
  */
183
- function registerPromptHandlers(clients, server) {
223
+ function registerPromptHandlers(clients, serverInfo) {
184
224
  // List Prompts handler
185
- server.setRequestHandler(ListPromptsRequestSchema, withErrorHandling(async (request) => {
225
+ serverInfo.server.setRequestHandler(ListPromptsRequestSchema, withErrorHandling(async (request) => {
186
226
  const prompts = [];
187
227
  const failedClients = [];
188
- for (const [name, client] of Object.entries(clients)) {
228
+ const capableClients = filterClients(byCapabilities({ prompts: {} }), byTags(serverInfo.tags))(clients);
229
+ for (const [name, clientInfo] of Object.entries(capableClients)) {
189
230
  logger.info(`Listing prompts for ${name}`);
190
231
  try {
191
- const result = await client.listPrompts(request.params);
232
+ const result = await clientInfo.client.listPrompts(request.params);
192
233
  prompts.push(...result.prompts.map((prompt) => ({
193
234
  name: `${name}${MCP_URI_SEPARATOR}${prompt.name}`,
194
235
  description: prompt.description,
@@ -201,18 +242,20 @@ function registerPromptHandlers(clients, server) {
201
242
  }
202
243
  }
203
244
  // If all clients failed, throw an error
204
- if (failedClients.length === Object.keys(clients).length && Object.keys(clients).length > 0) {
205
- throw new ProxyError('Failed to list prompts from all clients', new Error(JSON.stringify(failedClients)));
245
+ if (failedClients.length === Object.keys(capableClients).length && Object.keys(capableClients).length > 0) {
246
+ throw new MCPError('Failed to list prompts from all clients', ERROR_CODES.INTERNAL_SERVER_ERROR, {
247
+ failedClients,
248
+ });
206
249
  }
207
250
  // Send notification about partial failures
208
251
  if (failedClients.length > 0) {
209
- sendPartialFailureNotification(server, 'listPrompts', failedClients);
252
+ sendPartialFailureNotification(serverInfo, 'listPrompts', failedClients);
210
253
  }
211
254
  return { prompts };
212
255
  }, 'Error listing prompts'));
213
256
  // Get Prompt handler
214
- server.setRequestHandler(GetPromptRequestSchema, withErrorHandling(async (request) => {
257
+ serverInfo.server.setRequestHandler(GetPromptRequestSchema, withErrorHandling(async (request) => {
215
258
  const { clientName, resourceName: promptName } = parseUri(request.params.name, MCP_URI_SEPARATOR);
216
- return executeClientOperation(clients, clientName, (client) => client.getPrompt({ ...request.params, name: promptName }));
259
+ return executeClientOperation(clients, clientName, (clientInfo) => clientInfo.client.getPrompt({ ...request.params, name: promptName }));
217
260
  }, 'Error getting prompt'));
218
261
  }
package/build/index.js CHANGED
@@ -7,6 +7,7 @@ import logger from './logger/logger.js';
7
7
  import configReloadService from './services/configReloadService.js';
8
8
  import { ConfigManager } from './config/configManager.js';
9
9
  import { ExpressServer } from './server/expressServer.js';
10
+ import { PORT, HOST } from './constants.js';
10
11
  // Parse command line arguments
11
12
  const argv = yargs(hideBin(process.argv))
12
13
  .usage('Usage: $0 [options]')
@@ -18,12 +19,30 @@ const argv = yargs(hideBin(process.argv))
18
19
  choices: ['stdio', 'sse'],
19
20
  default: 'sse',
20
21
  },
22
+ port: {
23
+ alias: 'P',
24
+ describe: 'SSE port to listen on, applicable when transport is sse',
25
+ type: 'number',
26
+ default: PORT,
27
+ },
28
+ host: {
29
+ alias: 'H',
30
+ describe: 'SSE host to listen on, applicable when transport is sse',
31
+ type: 'string',
32
+ default: HOST,
33
+ },
21
34
  config: {
22
35
  alias: 'c',
23
36
  describe: 'Path to the config file',
24
37
  type: 'string',
25
38
  default: undefined,
26
39
  },
40
+ tags: {
41
+ alias: 'g',
42
+ describe: 'Tags to filter clients (comma-separated)',
43
+ type: 'string',
44
+ default: undefined,
45
+ },
27
46
  })
28
47
  .help()
29
48
  .alias('help', 'h')
@@ -67,13 +86,22 @@ async function main() {
67
86
  if (argv.transport === 'stdio') {
68
87
  // Use stdio transport
69
88
  const transport = new StdioServerTransport();
70
- await serverManager.connectTransport(transport, 'stdio');
89
+ // Parse and validate tags from CLI if provided
90
+ let tags;
91
+ if (argv.tags) {
92
+ tags = argv.tags.split(',').filter((tag) => tag.trim().length > 0);
93
+ if (tags.length === 0) {
94
+ logger.warn('No valid tags provided, ignoring tags parameter');
95
+ tags = undefined;
96
+ }
97
+ }
98
+ await serverManager.connectTransport(transport, 'stdio', tags);
71
99
  logger.info('Server started with stdio transport');
72
100
  }
73
101
  else {
74
102
  // Use HTTP/SSE transport
75
103
  const expressServer = new ExpressServer(serverManager);
76
- expressServer.start();
104
+ expressServer.start(argv.port, argv.host);
77
105
  }
78
106
  }
79
107
  catch (error) {
@@ -1,12 +1,6 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
1
  import winston from 'winston';
4
2
  import { MCPTransport } from './mcpTransport.js';
5
- // Ensure logs directory exists
6
- const logsDir = path.join(process.cwd(), 'logs');
7
- if (!fs.existsSync(logsDir)) {
8
- fs.mkdirSync(logsDir, { recursive: true });
9
- }
3
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
10
4
  // Map MCP log levels to Winston log levels
11
5
  const MCP_TO_WINSTON_LEVEL = {
12
6
  debug: 'debug',
@@ -33,26 +27,11 @@ const logger = winston.createLogger({
33
27
  level: 'info',
34
28
  format: customFormat,
35
29
  transports: [
30
+ // Add a silent transport by default to prevent "no transports" warnings
36
31
  new winston.transports.Console({
32
+ silent: true,
37
33
  format: consoleFormat,
38
34
  }),
39
- //
40
- // - Write all logs with importance level of `error` or higher to `error.log`
41
- // (i.e., error, fatal, but not other levels)
42
- //
43
- new winston.transports.File({
44
- filename: path.join(logsDir, 'error.log'),
45
- level: 'error',
46
- format: customFormat,
47
- }),
48
- //
49
- // - Write all logs with importance level of `info` or higher to `combined.log`
50
- // (i.e., fatal, error, warn, and info, but not trace)
51
- //
52
- new winston.transports.File({
53
- filename: path.join(logsDir, 'combined.log'),
54
- format: customFormat,
55
- }),
56
35
  ],
57
36
  // Prevent logger from exiting on error
58
37
  exitOnError: false,
@@ -71,7 +50,15 @@ export function addMCPTransport(server, loggerName) {
71
50
  loggerName: loggerName || '1mcp',
72
51
  level: 'info',
73
52
  });
74
- logger.add(mcpTransport);
53
+ logger.transports = [mcpTransport];
54
+ // Add appropriate transport based on server type
55
+ if (server instanceof SSEServerTransport) {
56
+ // For SSE server, add console transport for visibility
57
+ logger.add(new winston.transports.Console({
58
+ format: consoleFormat,
59
+ level: 'info',
60
+ }));
61
+ }
75
62
  }
76
63
  /**
77
64
  * Set the connection status of the MCP transport
@@ -133,7 +133,7 @@ export class MCPTransport extends Transport {
133
133
  try {
134
134
  this.sendLogNotification(info);
135
135
  }
136
- catch (error) {
136
+ catch (_) {
137
137
  // Ignore any errors during log sending
138
138
  }
139
139
  }
@@ -1,7 +1,7 @@
1
1
  import express from 'express';
2
2
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
3
3
  import logger, { setMCPTransportConnected } from '../logger/logger.js';
4
- import { PORT, SSE_ENDPOINT, MESSAGES_ENDPOINT, ERROR_CODES } from '../constants.js';
4
+ import { SSE_ENDPOINT, MESSAGES_ENDPOINT, ERROR_CODES } from '../constants.js';
5
5
  export class ExpressServer {
6
6
  app;
7
7
  serverManager;
@@ -13,7 +13,7 @@ export class ExpressServer {
13
13
  }
14
14
  setupMiddleware() {
15
15
  // Add error handling middleware
16
- this.app.use((err, req, res, next) => {
16
+ this.app.use((err, req, res, _next) => {
17
17
  logger.error('Express error:', err);
18
18
  res.status(500).json({
19
19
  error: {
@@ -28,8 +28,23 @@ export class ExpressServer {
28
28
  try {
29
29
  logger.info('sse', { query: req.query, headers: req.headers });
30
30
  const transport = new SSEServerTransport(MESSAGES_ENDPOINT, res);
31
+ // Extract and validate tags from query parameters
32
+ let tags;
33
+ if (req.query.tags) {
34
+ const tagsStr = req.query.tags;
35
+ if (typeof tagsStr !== 'string') {
36
+ res.status(400).json({
37
+ error: {
38
+ code: ERROR_CODES.INVALID_PARAMS,
39
+ message: 'Invalid params: tags must be a string',
40
+ },
41
+ });
42
+ return;
43
+ }
44
+ tags = tagsStr.split(',').filter((tag) => tag.trim().length > 0);
45
+ }
31
46
  // Connect the transport using the server manager
32
- await this.serverManager.connectTransport(transport, transport.sessionId);
47
+ await this.serverManager.connectTransport(transport, transport.sessionId, tags);
33
48
  // Update MCP transport connection status when a client connects
34
49
  if (this.serverManager.getActiveTransportsCount() === 1) {
35
50
  setMCPTransportConnected(true);
@@ -86,9 +101,9 @@ export class ExpressServer {
86
101
  }
87
102
  });
88
103
  }
89
- start() {
90
- this.app.listen(PORT, () => {
91
- logger.info(`Server is running on port ${PORT} with HTTP/SSE transport`);
104
+ start(port, host) {
105
+ this.app.listen(port, host, () => {
106
+ logger.info(`Server is running on port ${port} with HTTP/SSE transport`);
92
107
  });
93
108
  }
94
109
  }
@@ -9,33 +9,37 @@ export class ServerManager {
9
9
  serverConfig;
10
10
  serverCapabilities;
11
11
  clients = {};
12
- clientTransports = {};
13
- constructor(config, capabilities, clients, clientTransports) {
12
+ transports = {};
13
+ constructor(config, capabilities, clients, transports) {
14
14
  this.serverConfig = config;
15
15
  this.serverCapabilities = capabilities;
16
16
  this.clients = clients;
17
- this.clientTransports = clientTransports;
17
+ this.transports = transports;
18
18
  }
19
- static getInstance(config, capabilities, clients, clientTransports) {
19
+ static getInstance(config, capabilities, clients, transports) {
20
20
  if (!ServerManager.instance) {
21
- ServerManager.instance = new ServerManager(config, capabilities, clients, clientTransports);
21
+ ServerManager.instance = new ServerManager(config, capabilities, clients, transports);
22
22
  }
23
23
  return ServerManager.instance;
24
24
  }
25
- async connectTransport(transport, sessionId) {
25
+ async connectTransport(transport, sessionId, tags) {
26
26
  try {
27
27
  // Create a new server instance for this transport
28
28
  const server = new Server(this.serverConfig, this.serverCapabilities);
29
29
  // Enhance server with logging middleware
30
30
  enhanceServerWithLogging(server);
31
- // Collect capabilities and register handlers
32
- await setupCapabilities(this.clients, server);
31
+ const serverInfo = {
32
+ server,
33
+ tags,
34
+ };
35
+ // Set up capabilities for this server instance
36
+ await setupCapabilities(this.clients, serverInfo);
33
37
  // Initialize the configuration reload service
34
- configReloadService.initialize(server, this.clientTransports);
38
+ configReloadService.initialize(serverInfo, this.transports);
39
+ // Store the server instance
40
+ this.servers.set(sessionId, serverInfo);
35
41
  // Connect the transport to the new server instance
36
42
  await server.connect(transport);
37
- // Store the server instance
38
- this.servers.set(sessionId, server);
39
43
  logger.info(`Connected transport for session ${sessionId}`);
40
44
  }
41
45
  catch (error) {
@@ -51,17 +55,20 @@ export class ServerManager {
51
55
  }
52
56
  }
53
57
  getTransport(sessionId) {
54
- return this.servers.get(sessionId)?.transport;
58
+ return this.servers.get(sessionId)?.server.transport;
55
59
  }
56
60
  getTransports() {
57
61
  const transports = new Map();
58
62
  for (const [id, server] of this.servers.entries()) {
59
- if (server.transport) {
60
- transports.set(id, server.transport);
63
+ if (server.server.transport) {
64
+ transports.set(id, server.server.transport);
61
65
  }
62
66
  }
63
67
  return transports;
64
68
  }
69
+ getClientTransports() {
70
+ return this.transports;
71
+ }
65
72
  getActiveTransportsCount() {
66
73
  return this.servers.size;
67
74
  }