@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.
- package/README.md +68 -11
- package/build/capabilities/capabilityManager.js +17 -9
- package/build/clients/clientManager.js +64 -10
- package/build/clients/clientManager.test.js +172 -0
- package/build/config/configManager.js +1 -1
- package/build/config/configManager.test.js +92 -0
- package/build/config/transportConfig.js +17 -38
- package/build/constants.js +2 -1
- package/build/handlers/notificationHandlers.js +10 -10
- package/build/handlers/requestHandlers.js +106 -63
- package/build/index.js +30 -2
- package/build/logger/logger.js +12 -25
- package/build/logger/mcpTransport.js +1 -1
- package/build/server/expressServer.js +21 -6
- package/build/serverManager.js +21 -14
- package/build/serverManager.test.js +158 -0
- package/build/services/configReloadService.js +10 -8
- package/build/test-utils/fixtures/commonFixtures.js +44 -0
- package/build/test-utils/helpers/testHelpers.js +23 -0
- package/build/test-utils/mocks/commonMocks.js +65 -0
- package/build/types.js +31 -0
- package/build/utils/clientFiltering.js +101 -0
- package/build/utils/clientFiltering.test.js +124 -0
- package/build/utils/errorHandling.js +66 -43
- package/build/utils/errorTypes.js +47 -0
- package/package.json +41 -4
|
@@ -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
|
|
7
|
+
* @param serverInfo The MCP server instance
|
|
8
8
|
*/
|
|
9
|
-
export function setupClientToServerNotifications(clients,
|
|
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,
|
|
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
|
|
37
|
+
* @param serverInfo The MCP server instance
|
|
38
38
|
*/
|
|
39
|
-
export function setupServerToClientNotifications(clients,
|
|
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,
|
|
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 {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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(
|
|
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,
|
|
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
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// Register
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
75
|
+
* @param serverInfo The MCP server instance
|
|
59
76
|
*/
|
|
60
|
-
function registerResourceHandlers(clients,
|
|
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
|
-
|
|
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(
|
|
83
|
-
throw new
|
|
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(
|
|
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
|
-
|
|
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(
|
|
113
|
-
throw new
|
|
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(
|
|
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, (
|
|
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, (
|
|
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, (
|
|
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
|
|
173
|
+
* @param serverInfo The MCP server instance
|
|
141
174
|
*/
|
|
142
|
-
function registerToolHandlers(clients,
|
|
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
|
-
|
|
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(
|
|
164
|
-
throw new
|
|
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(
|
|
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, (
|
|
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
|
|
221
|
+
* @param serverInfo The MCP server instance
|
|
182
222
|
*/
|
|
183
|
-
function registerPromptHandlers(clients,
|
|
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
|
-
|
|
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(
|
|
205
|
-
throw new
|
|
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(
|
|
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, (
|
|
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
|
-
|
|
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) {
|
package/build/logger/logger.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
|
@@ -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 {
|
|
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,
|
|
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(
|
|
91
|
-
logger.info(`Server is running on port ${
|
|
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
|
}
|
package/build/serverManager.js
CHANGED
|
@@ -9,33 +9,37 @@ export class ServerManager {
|
|
|
9
9
|
serverConfig;
|
|
10
10
|
serverCapabilities;
|
|
11
11
|
clients = {};
|
|
12
|
-
|
|
13
|
-
constructor(config, capabilities, clients,
|
|
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.
|
|
17
|
+
this.transports = transports;
|
|
18
18
|
}
|
|
19
|
-
static getInstance(config, capabilities, clients,
|
|
19
|
+
static getInstance(config, capabilities, clients, transports) {
|
|
20
20
|
if (!ServerManager.instance) {
|
|
21
|
-
ServerManager.instance = new ServerManager(config, capabilities, clients,
|
|
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
|
-
|
|
32
|
-
|
|
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(
|
|
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
|
}
|