@1mcp/agent 0.1.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.
@@ -0,0 +1,60 @@
1
+ import { CancelledNotificationSchema, ProgressNotificationSchema, LoggingMessageNotificationSchema, ResourceUpdatedNotificationSchema, ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema, PromptListChangedNotificationSchema, InitializedNotificationSchema, RootsListChangedNotificationSchema, } from '@modelcontextprotocol/sdk/types.js';
2
+ import logger from '../logger.js';
3
+ import { withErrorHandling } from '../utils/errorHandling.js';
4
+ /**
5
+ * Sets up client-to-server notification handlers
6
+ * @param clients Record of client instances
7
+ * @param server The MCP server instance
8
+ */
9
+ export function setupClientToServerNotifications(clients, server) {
10
+ const clientNotificationSchemas = [
11
+ CancelledNotificationSchema,
12
+ ProgressNotificationSchema,
13
+ LoggingMessageNotificationSchema,
14
+ ResourceUpdatedNotificationSchema,
15
+ ResourceListChangedNotificationSchema,
16
+ ToolListChangedNotificationSchema,
17
+ PromptListChangedNotificationSchema,
18
+ ];
19
+ for (const [name, client] of Object.entries(clients)) {
20
+ clientNotificationSchemas.forEach((schema) => {
21
+ client.setNotificationHandler(schema, withErrorHandling(async (notification) => {
22
+ logger.info(`Received notification in client: ${name} ${JSON.stringify(notification)}`);
23
+ server.notification({
24
+ ...notification,
25
+ params: {
26
+ ...notification.params,
27
+ server: name,
28
+ },
29
+ });
30
+ }, `Error handling client notification from ${name}`));
31
+ });
32
+ }
33
+ }
34
+ /**
35
+ * Sets up server-to-client notification handlers
36
+ * @param clients Record of client instances
37
+ * @param server The MCP server instance
38
+ */
39
+ export function setupServerToClientNotifications(clients, server) {
40
+ const serverNotificationSchemas = [
41
+ CancelledNotificationSchema,
42
+ ProgressNotificationSchema,
43
+ InitializedNotificationSchema,
44
+ RootsListChangedNotificationSchema,
45
+ ];
46
+ for (const [name, client] of Object.entries(clients)) {
47
+ serverNotificationSchemas.forEach((schema) => {
48
+ server.setNotificationHandler(schema, withErrorHandling(async (notification) => {
49
+ logger.info(`Received notification in server: ${name} ${JSON.stringify(notification)}`);
50
+ client.notification({
51
+ ...notification,
52
+ params: {
53
+ ...notification.params,
54
+ client: name,
55
+ },
56
+ });
57
+ }, `Error handling server notification to ${name}`));
58
+ });
59
+ }
60
+ }
@@ -0,0 +1,218 @@
1
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
2
+ import logger from '../logger.js';
3
+ import { setLogLevel } from '../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';
7
+ /**
8
+ * Sends a partial failure notification to inform clients about backend failures
9
+ * @param server The MCP server instance
10
+ * @param operation The operation that partially failed
11
+ * @param failedClients Array of failed clients with error details
12
+ */
13
+ function sendPartialFailureNotification(server, operation, failedClients) {
14
+ if (failedClients.length === 0)
15
+ return;
16
+ server.notification({
17
+ method: 'notifications/message',
18
+ params: {
19
+ level: 'warning',
20
+ message: `Partial failure during ${operation}`,
21
+ logger: MCP_SERVER_NAME,
22
+ data: {
23
+ operation,
24
+ failedClients,
25
+ timestamp: new Date().toISOString(),
26
+ },
27
+ },
28
+ });
29
+ }
30
+ /**
31
+ * Registers all request handlers based on available capabilities
32
+ * @param clients Record of client instances
33
+ * @param server The MCP server instance
34
+ * @param capabilities The server capabilities
35
+ */
36
+ export function registerRequestHandlers(clients, server, capabilities) {
37
+ // Register logging level handler
38
+ server.setRequestHandler(SetLevelRequestSchema, async (request) => {
39
+ setLogLevel(request.params.level);
40
+ return {};
41
+ });
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
+ }
54
+ }
55
+ /**
56
+ * Registers resource-related request handlers
57
+ * @param clients Record of client instances
58
+ * @param server The MCP server instance
59
+ */
60
+ function registerResourceHandlers(clients, server) {
61
+ // List Resources handler
62
+ server.setRequestHandler(ListResourcesRequestSchema, withErrorHandling(async (request) => {
63
+ const resources = [];
64
+ const failedClients = [];
65
+ for (const [name, client] of Object.entries(clients)) {
66
+ logger.info(`Listing resources for ${name}`);
67
+ try {
68
+ const result = await client.listResources(request.params);
69
+ resources.push(...result.resources.map((resource) => ({
70
+ uri: `${name}${MCP_URI_SEPARATOR}${resource.uri}`,
71
+ name: resource.name,
72
+ description: resource.description,
73
+ mimeType: resource.mimeType,
74
+ })));
75
+ }
76
+ catch (error) {
77
+ logger.error(`Error listing resources for ${name}: ${error}`);
78
+ failedClients.push({ name, error: error instanceof Error ? error.message : String(error) });
79
+ }
80
+ }
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)));
84
+ }
85
+ // Send notification about partial failures
86
+ if (failedClients.length > 0) {
87
+ sendPartialFailureNotification(server, 'listResources', failedClients);
88
+ }
89
+ return { resources };
90
+ }, 'Error listing resources'));
91
+ // List Resource Templates handler
92
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, withErrorHandling(async (request) => {
93
+ const resourceTemplates = [];
94
+ const failedClients = [];
95
+ for (const [name, client] of Object.entries(clients)) {
96
+ logger.info(`Listing resource templates for ${name}`);
97
+ try {
98
+ const result = await client.listResourceTemplates(request.params);
99
+ resourceTemplates.push(...result.resourceTemplates.map((template) => ({
100
+ uriTemplate: `${name}${MCP_URI_SEPARATOR}${template.uriTemplate}`,
101
+ name: template.name,
102
+ description: template.description,
103
+ mimeType: template.mimeType,
104
+ })));
105
+ }
106
+ catch (error) {
107
+ logger.error(`Error listing resource templates for ${name}: ${error}`);
108
+ failedClients.push({ name, error: error instanceof Error ? error.message : String(error) });
109
+ }
110
+ }
111
+ // 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)));
114
+ }
115
+ // Send notification about partial failures
116
+ if (failedClients.length > 0) {
117
+ sendPartialFailureNotification(server, 'listResourceTemplates', failedClients);
118
+ }
119
+ return { resourceTemplates };
120
+ }, 'Error listing resource templates'));
121
+ // Subscribe Resource handler
122
+ server.setRequestHandler(SubscribeRequestSchema, withErrorHandling(async (request) => {
123
+ const { clientName, resourceName } = parseUri(request.params.uri, MCP_URI_SEPARATOR);
124
+ return executeClientOperation(clients, clientName, (client) => client.subscribeResource({ ...request.params, uri: resourceName }));
125
+ }, 'Error subscribing to resource'));
126
+ // Unsubscribe Resource handler
127
+ server.setRequestHandler(UnsubscribeRequestSchema, withErrorHandling(async (request) => {
128
+ const { clientName, resourceName } = parseUri(request.params.uri, MCP_URI_SEPARATOR);
129
+ return executeClientOperation(clients, clientName, (client) => client.unsubscribeResource({ ...request.params, uri: resourceName }));
130
+ }, 'Error unsubscribing from resource'));
131
+ // Read Resource handler
132
+ server.setRequestHandler(ReadResourceRequestSchema, withErrorHandling(async (request) => {
133
+ const { clientName, resourceName } = parseUri(request.params.uri, MCP_URI_SEPARATOR);
134
+ return executeClientOperation(clients, clientName, (client) => client.readResource({ ...request.params, uri: resourceName }));
135
+ }, 'Error reading resource'));
136
+ }
137
+ /**
138
+ * Registers tool-related request handlers
139
+ * @param clients Record of client instances
140
+ * @param server The MCP server instance
141
+ */
142
+ function registerToolHandlers(clients, server) {
143
+ // List Tools handler
144
+ server.setRequestHandler(ListToolsRequestSchema, withErrorHandling(async (request) => {
145
+ const tools = [];
146
+ const failedClients = [];
147
+ for (const [name, client] of Object.entries(clients)) {
148
+ logger.info(`Listing tools for ${name}`);
149
+ try {
150
+ const result = await client.listTools(request.params);
151
+ tools.push(...result.tools.map((tool) => ({
152
+ name: `${name}${MCP_URI_SEPARATOR}${tool.name}`,
153
+ description: tool.description,
154
+ inputSchema: tool.inputSchema,
155
+ })));
156
+ }
157
+ catch (error) {
158
+ logger.error(`Error listing tools for ${name}: ${error}`);
159
+ failedClients.push({ name, error: error instanceof Error ? error.message : String(error) });
160
+ }
161
+ }
162
+ // 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)));
165
+ }
166
+ // Send notification about partial failures
167
+ if (failedClients.length > 0) {
168
+ sendPartialFailureNotification(server, 'listTools', failedClients);
169
+ }
170
+ return { tools };
171
+ }, 'Error listing tools'));
172
+ // Call Tool handler
173
+ server.setRequestHandler(CallToolRequestSchema, withErrorHandling(async (request) => {
174
+ const { clientName, resourceName: toolName } = parseUri(request.params.name, MCP_URI_SEPARATOR);
175
+ return executeClientOperation(clients, clientName, (client) => client.callTool({ ...request.params, name: toolName }));
176
+ }, 'Error calling tool'));
177
+ }
178
+ /**
179
+ * Registers prompt-related request handlers
180
+ * @param clients Record of client instances
181
+ * @param server The MCP server instance
182
+ */
183
+ function registerPromptHandlers(clients, server) {
184
+ // List Prompts handler
185
+ server.setRequestHandler(ListPromptsRequestSchema, withErrorHandling(async (request) => {
186
+ const prompts = [];
187
+ const failedClients = [];
188
+ for (const [name, client] of Object.entries(clients)) {
189
+ logger.info(`Listing prompts for ${name}`);
190
+ try {
191
+ const result = await client.listPrompts(request.params);
192
+ prompts.push(...result.prompts.map((prompt) => ({
193
+ name: `${name}${MCP_URI_SEPARATOR}${prompt.name}`,
194
+ description: prompt.description,
195
+ arguments: prompt.arguments,
196
+ })));
197
+ }
198
+ catch (error) {
199
+ logger.error(`Error listing prompts for ${name}: ${error}`);
200
+ failedClients.push({ name, error: error instanceof Error ? error.message : String(error) });
201
+ }
202
+ }
203
+ // 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)));
206
+ }
207
+ // Send notification about partial failures
208
+ if (failedClients.length > 0) {
209
+ sendPartialFailureNotification(server, 'listPrompts', failedClients);
210
+ }
211
+ return { prompts };
212
+ }, 'Error listing prompts'));
213
+ // Get Prompt handler
214
+ server.setRequestHandler(GetPromptRequestSchema, withErrorHandling(async (request) => {
215
+ const { clientName, resourceName: promptName } = parseUri(request.params.name, MCP_URI_SEPARATOR);
216
+ return executeClientOperation(clients, clientName, (client) => client.getPrompt({ ...request.params, name: promptName }));
217
+ }, 'Error getting prompt'));
218
+ }
package/build/index.js ADDED
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ import express from 'express';
3
+ import yargs from 'yargs';
4
+ import { hideBin } from 'yargs/helpers';
5
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
6
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
+ import { server } from './server.js';
8
+ import logger, { setMCPTransportConnected } from './logger.js';
9
+ import { PORT, SSE_ENDPOINT, MESSAGES_ENDPOINT, ERROR_CODES } from './constants.js';
10
+ import configReloadService from './services/configReloadService.js';
11
+ // Parse command line arguments
12
+ const argv = yargs(hideBin(process.argv))
13
+ .usage('Usage: $0 [options]')
14
+ .options({
15
+ transport: {
16
+ alias: 't',
17
+ describe: 'Transport type to use (stdio or http)',
18
+ type: 'string',
19
+ choices: ['stdio', 'http'],
20
+ default: 'http',
21
+ },
22
+ })
23
+ .help()
24
+ .alias('help', 'h')
25
+ .parseSync();
26
+ const app = express();
27
+ // app.use(express.json());
28
+ // app.use(express.urlencoded({ extended: true }));
29
+ const transportMap = new Map();
30
+ // Add error handling middleware
31
+ app.use((err, req, res, next) => {
32
+ logger.error('Express error:', err);
33
+ res.status(500).json({
34
+ error: {
35
+ code: ERROR_CODES.INTERNAL_SERVER_ERROR,
36
+ message: 'Internal server error',
37
+ },
38
+ });
39
+ });
40
+ app.get(SSE_ENDPOINT, async (req, res) => {
41
+ try {
42
+ logger.info('sse', { query: req.query, headers: req.headers });
43
+ const transport = new SSEServerTransport(MESSAGES_ENDPOINT, res);
44
+ await server.connect(transport);
45
+ transportMap.set(transport.sessionId, transport);
46
+ // Update MCP transport connection status when a client connects
47
+ if (transportMap.size === 1) {
48
+ setMCPTransportConnected(true);
49
+ logger.info('First client connected, enabling MCP logging transport');
50
+ }
51
+ transport.onclose = () => {
52
+ transportMap.delete(transport.sessionId);
53
+ logger.info('transport closed', transport.sessionId);
54
+ // Update MCP transport connection status when all clients disconnect
55
+ if (transportMap.size === 0) {
56
+ setMCPTransportConnected(false);
57
+ logger.info('All clients disconnected, disabling MCP logging transport');
58
+ }
59
+ };
60
+ }
61
+ catch (error) {
62
+ logger.error('SSE connection error:', error);
63
+ res.status(500).end();
64
+ }
65
+ });
66
+ app.post(MESSAGES_ENDPOINT, async (req, res) => {
67
+ try {
68
+ const sessionId = req.query.sessionId;
69
+ if (!sessionId) {
70
+ res.status(400).json({
71
+ error: {
72
+ code: ERROR_CODES.INVALID_PARAMS,
73
+ message: 'Invalid params: sessionId is required',
74
+ },
75
+ });
76
+ return;
77
+ }
78
+ logger.info('message', { body: req.body, sessionId });
79
+ const transport = transportMap.get(sessionId);
80
+ if (transport) {
81
+ await transport.handlePostMessage(req, res);
82
+ return;
83
+ }
84
+ res.status(404).json({
85
+ error: {
86
+ code: ERROR_CODES.TRANSPORT_NOT_FOUND,
87
+ message: 'Transport not found',
88
+ },
89
+ });
90
+ }
91
+ catch (error) {
92
+ logger.error('Message handling error:', error);
93
+ res.status(500).json({
94
+ error: {
95
+ code: ERROR_CODES.INTERNAL_SERVER_ERROR,
96
+ message: 'Internal server error',
97
+ },
98
+ });
99
+ }
100
+ });
101
+ /**
102
+ * Set up graceful shutdown handling
103
+ */
104
+ function setupGracefulShutdown() {
105
+ const shutdown = async () => {
106
+ logger.info('Shutting down server...');
107
+ // Stop the configuration reload service
108
+ configReloadService.stop();
109
+ // Close all transports
110
+ for (const [sessionId, transport] of transportMap.entries()) {
111
+ try {
112
+ transport.close();
113
+ logger.info(`Closed transport: ${sessionId}`);
114
+ }
115
+ catch (error) {
116
+ logger.error(`Error closing transport ${sessionId}: ${error}`);
117
+ }
118
+ }
119
+ logger.info('Server shutdown complete');
120
+ process.exit(0);
121
+ };
122
+ // Handle various signals for graceful shutdown
123
+ process.on('SIGINT', shutdown);
124
+ process.on('SIGTERM', shutdown);
125
+ process.on('SIGHUP', shutdown);
126
+ }
127
+ /**
128
+ * Start the server using the specified transport.
129
+ */
130
+ async function main() {
131
+ // Set up graceful shutdown handling
132
+ setupGracefulShutdown();
133
+ if (argv.transport === 'stdio') {
134
+ // Use stdio transport
135
+ const transport = new StdioServerTransport();
136
+ await server.connect(transport);
137
+ logger.info('Server started with stdio transport');
138
+ }
139
+ else {
140
+ // Use HTTP/SSE transport
141
+ app.listen(PORT, () => {
142
+ logger.info(`Server is running on port ${PORT} with HTTP/SSE transport`);
143
+ });
144
+ }
145
+ }
146
+ main().catch((error) => {
147
+ logger.error('Server error:', error);
148
+ process.exit(1);
149
+ });
@@ -0,0 +1,98 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import winston from 'winston';
4
+ 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
+ }
10
+ // Map MCP log levels to Winston log levels
11
+ const MCP_TO_WINSTON_LEVEL = {
12
+ debug: 'debug',
13
+ info: 'info',
14
+ notice: 'info',
15
+ warning: 'warn',
16
+ error: 'error',
17
+ critical: 'error',
18
+ alert: 'error',
19
+ emergency: 'error',
20
+ };
21
+ // Custom format for console and file output
22
+ const customFormat = winston.format.combine(winston.format.timestamp(), winston.format.printf(({ timestamp, level, message, ...meta }) => {
23
+ const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : '';
24
+ return `${timestamp} [${level.toUpperCase()}] ${message}${metaStr}`;
25
+ }));
26
+ // Create the logger without the MCP transport initially
27
+ const logger = winston.createLogger({
28
+ level: 'info',
29
+ format: customFormat,
30
+ defaultMeta: { service: '1mcp' },
31
+ transports: [
32
+ new winston.transports.Console({
33
+ format: customFormat,
34
+ }),
35
+ //
36
+ // - Write all logs with importance level of `error` or higher to `error.log`
37
+ // (i.e., error, fatal, but not other levels)
38
+ //
39
+ new winston.transports.File({
40
+ filename: path.join(logsDir, 'error.log'),
41
+ level: 'error',
42
+ format: customFormat,
43
+ }),
44
+ //
45
+ // - Write all logs with importance level of `info` or higher to `combined.log`
46
+ // (i.e., fatal, error, warn, and info, but not trace)
47
+ //
48
+ new winston.transports.File({
49
+ filename: path.join(logsDir, 'combined.log'),
50
+ format: customFormat,
51
+ }),
52
+ ],
53
+ // Prevent logger from exiting on error
54
+ exitOnError: false,
55
+ });
56
+ // Store a reference to the MCP transport
57
+ let mcpTransport = null;
58
+ /**
59
+ * Adds the MCP transport to the logger
60
+ * @param server The MCP server instance
61
+ * @param loggerName Optional name for the logger in MCP notifications
62
+ */
63
+ export function addMCPTransport(server, loggerName) {
64
+ // Add the MCP transport to the logger
65
+ mcpTransport = new MCPTransport({
66
+ server,
67
+ loggerName: loggerName || '1mcp-agent',
68
+ level: 'info',
69
+ });
70
+ logger.add(mcpTransport);
71
+ }
72
+ /**
73
+ * Set the connection status of the MCP transport
74
+ * @param connected Whether the server is connected
75
+ */
76
+ export function setMCPTransportConnected(connected) {
77
+ if (mcpTransport) {
78
+ mcpTransport.setConnected(connected);
79
+ }
80
+ }
81
+ /**
82
+ * Set the log level for the logger
83
+ * @param mcpLevel The MCP log level to set
84
+ */
85
+ export function setLogLevel(mcpLevel) {
86
+ // Convert MCP log level to Winston log level
87
+ const winstonLevel = MCP_TO_WINSTON_LEVEL[mcpLevel] || 'info';
88
+ // Set the log level for all transports
89
+ logger.level = winstonLevel;
90
+ logger.transports.forEach((transport) => {
91
+ transport.level = winstonLevel;
92
+ });
93
+ // Also set the level for the MCP transport if it exists
94
+ if (mcpTransport) {
95
+ mcpTransport.level = winstonLevel;
96
+ }
97
+ }
98
+ export default logger;
@@ -0,0 +1,122 @@
1
+ import Transport from 'winston-transport';
2
+ // Map Winston log levels to MCP log levels
3
+ const LOG_LEVEL_MAP = {
4
+ error: 'error',
5
+ warn: 'warning',
6
+ info: 'info',
7
+ verbose: 'debug',
8
+ debug: 'debug',
9
+ silly: 'debug',
10
+ };
11
+ /**
12
+ * Winston transport for sending logs to MCP clients
13
+ */
14
+ export class MCPTransport extends Transport {
15
+ server;
16
+ loggerName;
17
+ connected = false;
18
+ constructor(options) {
19
+ super(options);
20
+ this.server = options.server;
21
+ this.loggerName = options.loggerName || '1mcp-agent';
22
+ }
23
+ /**
24
+ * Set the connection status of the transport
25
+ * @param connected Whether the server is connected
26
+ */
27
+ setConnected(connected) {
28
+ this.connected = connected;
29
+ }
30
+ /**
31
+ * Send a log notification to the MCP client
32
+ * @param info The log info object
33
+ * @returns true if the notification was sent, false otherwise
34
+ */
35
+ sendLogNotification(info) {
36
+ if (!this.server || !this.connected) {
37
+ return false;
38
+ }
39
+ // Map Winston log level to MCP log level
40
+ const level = LOG_LEVEL_MAP[info.level] || 'info';
41
+ const sanitizedData = this.sanitizeData(info);
42
+ // Extract message and metadata
43
+ const { message, level: _, ...meta } = sanitizedData;
44
+ // Create MCP logging notification
45
+ const notification = {
46
+ method: 'notifications/message',
47
+ params: {
48
+ level,
49
+ message: typeof message === 'string' ? message : JSON.stringify(message),
50
+ logger: this.loggerName,
51
+ data: Object.keys(meta).length > 0 ? meta : undefined,
52
+ },
53
+ };
54
+ // Send notification to MCP clients
55
+ try {
56
+ this.server.notification(notification);
57
+ return true;
58
+ }
59
+ catch (error) {
60
+ console.error('Failed to send log notification to MCP client:', error);
61
+ return false;
62
+ }
63
+ }
64
+ /**
65
+ * Sanitize log data to remove sensitive information
66
+ * @param data The data to sanitize
67
+ * @returns Sanitized data
68
+ */
69
+ sanitizeData(data) {
70
+ if (typeof data !== 'object' || data === null) {
71
+ return data;
72
+ }
73
+ // List of potentially sensitive field names
74
+ const sensitiveFields = [
75
+ 'password',
76
+ 'token',
77
+ 'secret',
78
+ 'key',
79
+ 'credential',
80
+ 'auth',
81
+ 'apiKey',
82
+ 'api_key',
83
+ 'accessToken',
84
+ 'access_token',
85
+ 'jwt',
86
+ 'ssn',
87
+ 'creditCard',
88
+ 'credit_card',
89
+ 'cvv',
90
+ 'pin',
91
+ ];
92
+ if (Array.isArray(data)) {
93
+ return data.map((item) => this.sanitizeData(item));
94
+ }
95
+ const sanitized = {};
96
+ for (const [key, value] of Object.entries(data)) {
97
+ // Check if this is a sensitive field
98
+ const isSensitive = sensitiveFields.some((field) => key.toLowerCase().includes(field.toLowerCase()));
99
+ if (isSensitive) {
100
+ sanitized[key] = '[REDACTED]';
101
+ }
102
+ else if (typeof value === 'object' && value !== null) {
103
+ sanitized[key] = this.sanitizeData(value);
104
+ }
105
+ else {
106
+ sanitized[key] = value;
107
+ }
108
+ }
109
+ return sanitized;
110
+ }
111
+ log(info, callback) {
112
+ setImmediate(() => {
113
+ this.emit('logged', info);
114
+ });
115
+ if (!this.connected) {
116
+ callback();
117
+ return;
118
+ }
119
+ this.sendLogNotification(info);
120
+ callback();
121
+ }
122
+ }