@myatkyawthu/mcp-connect 0.2.0 → 0.2.1
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/LICENSE +20 -20
- package/README.md +169 -169
- package/package.json +62 -62
- package/src/cli.js +0 -19
- package/src/defineMCP.js +0 -20
- package/src/index.js +0 -30
- package/src/server/mcpServer.js +11 -77
- package/src/types/mcp.js +0 -69
- package/src/utils/configValidation.js +81 -120
- package/src/utils/logger.js +2 -137
- package/src/utils/validation.js +1 -31
package/src/index.js
CHANGED
|
@@ -1,33 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP-Connect - Dead simple MCP (Model Context Protocol) server for exposing your app functions to AI agents
|
|
3
|
-
*
|
|
4
|
-
* Main entry point - exports public API
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```javascript
|
|
8
|
-
* import { defineMCP } from 'mcp-connect';
|
|
9
|
-
*
|
|
10
|
-
* export default defineMCP({
|
|
11
|
-
* name: 'My App',
|
|
12
|
-
* version: '1.0.0',
|
|
13
|
-
* tools: [
|
|
14
|
-
* ['hello', async ({ name }) => `Hello ${name}!`]
|
|
15
|
-
* ]
|
|
16
|
-
* });
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
// Main API exports
|
|
21
1
|
export { defineMCP } from './defineMCP.js';
|
|
22
2
|
export { MCPConnectServer } from './server/mcpServer.js';
|
|
23
|
-
|
|
24
|
-
// Type validation utilities (for runtime type checking)
|
|
25
3
|
export { isValidMCPConfig, isValidMCPTool, isValidToolDefinition } from './types/mcp.js';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {import('./types/mcp.js').MCPConfig} MCPConfig
|
|
29
|
-
* @typedef {import('./types/mcp.js').MCPTool} MCPTool
|
|
30
|
-
* @typedef {import('./types/mcp.js').ToolDefinition} ToolDefinition
|
|
31
|
-
* @typedef {import('./types/mcp.js').MCPServerOptions} MCPServerOptions
|
|
32
|
-
* @typedef {import('./types/mcp.js').ToolFunction} ToolFunction
|
|
33
|
-
*/
|
package/src/server/mcpServer.js
CHANGED
|
@@ -4,30 +4,15 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprot
|
|
|
4
4
|
import { logger } from '../utils/logger.js';
|
|
5
5
|
import { sanitizeErrorMessage, validateToolArguments } from '../utils/validation.js';
|
|
6
6
|
|
|
7
|
-
// Default timeout for tool execution (30 seconds)
|
|
8
7
|
const DEFAULT_TOOL_TIMEOUT = 30000;
|
|
9
8
|
|
|
10
|
-
/**
|
|
11
|
-
* MCP Connect Server class
|
|
12
|
-
* Handles MCP protocol communication and tool execution
|
|
13
|
-
*/
|
|
14
9
|
export class MCPConnectServer {
|
|
15
|
-
/**
|
|
16
|
-
* Create MCP Connect Server
|
|
17
|
-
* @param {import('../types/mcp.js').MCPConfig} config - MCP configuration
|
|
18
|
-
*/
|
|
19
10
|
constructor(config) {
|
|
20
|
-
/** @type {import('../types/mcp.js').MCPConfig} */
|
|
21
11
|
this.config = config;
|
|
22
|
-
/** @type {boolean} */
|
|
23
12
|
this.isShuttingDown = false;
|
|
24
|
-
/** @type {Map<string, number>} */
|
|
25
13
|
this.requestCounts = new Map();
|
|
26
|
-
|
|
27
|
-
this.maxRequestsPerMinute = 100; // Simple rate limiting
|
|
14
|
+
this.maxRequestsPerMinute = 100;
|
|
28
15
|
|
|
29
|
-
// Create MCP SDK server for STDIO transport
|
|
30
|
-
/** @type {Server} */
|
|
31
16
|
this.server = new Server(
|
|
32
17
|
{
|
|
33
18
|
name: config.name,
|
|
@@ -45,21 +30,13 @@ export class MCPConnectServer {
|
|
|
45
30
|
this.setupGracefulShutdown();
|
|
46
31
|
}
|
|
47
32
|
|
|
48
|
-
/**
|
|
49
|
-
* Set up graceful shutdown handlers
|
|
50
|
-
*/
|
|
51
33
|
setupGracefulShutdown() {
|
|
52
|
-
/**
|
|
53
|
-
* Shutdown handler
|
|
54
|
-
* @param {string} signal - Shutdown signal
|
|
55
|
-
*/
|
|
56
34
|
const shutdown = async (signal) => {
|
|
57
35
|
if (this.isShuttingDown) return;
|
|
58
36
|
this.isShuttingDown = true;
|
|
59
37
|
|
|
60
38
|
logger.serverShutdown(signal);
|
|
61
39
|
try {
|
|
62
|
-
// Give ongoing operations a chance to complete
|
|
63
40
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
64
41
|
process.exit(0);
|
|
65
42
|
} catch (error) {
|
|
@@ -80,14 +57,6 @@ export class MCPConnectServer {
|
|
|
80
57
|
});
|
|
81
58
|
}
|
|
82
59
|
|
|
83
|
-
/**
|
|
84
|
-
* Execute promise with timeout
|
|
85
|
-
* @template T
|
|
86
|
-
* @param {Promise<T>} promise - Promise to execute
|
|
87
|
-
* @param {number} timeoutMs - Timeout in milliseconds
|
|
88
|
-
* @param {string} operation - Operation name for error messages
|
|
89
|
-
* @returns {Promise<T>} Promise that resolves or rejects with timeout
|
|
90
|
-
*/
|
|
91
60
|
async withTimeout(promise, timeoutMs, operation) {
|
|
92
61
|
const timeoutPromise = new Promise((_, reject) => {
|
|
93
62
|
setTimeout(() => {
|
|
@@ -98,11 +67,7 @@ export class MCPConnectServer {
|
|
|
98
67
|
return Promise.race([promise, timeoutPromise]);
|
|
99
68
|
}
|
|
100
69
|
|
|
101
|
-
/**
|
|
102
|
-
* Set up MCP request handlers
|
|
103
|
-
*/
|
|
104
70
|
setupHandlers() {
|
|
105
|
-
// List available tools
|
|
106
71
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
107
72
|
return {
|
|
108
73
|
tools: this.config.tools.map((tool) => ({
|
|
@@ -113,7 +78,6 @@ export class MCPConnectServer {
|
|
|
113
78
|
};
|
|
114
79
|
});
|
|
115
80
|
|
|
116
|
-
// Execute tool calls with proper error handling and timeout
|
|
117
81
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
118
82
|
const requestId = Math.random().toString(36).substring(2, 8);
|
|
119
83
|
|
|
@@ -121,7 +85,6 @@ export class MCPConnectServer {
|
|
|
121
85
|
throw new Error('Server is shutting down');
|
|
122
86
|
}
|
|
123
87
|
|
|
124
|
-
// Simple rate limiting protection
|
|
125
88
|
const currentMinute = Math.floor(Date.now() / 60000);
|
|
126
89
|
const requestKey = `${currentMinute}`;
|
|
127
90
|
const currentCount = this.requestCounts.get(requestKey) || 0;
|
|
@@ -136,7 +99,6 @@ export class MCPConnectServer {
|
|
|
136
99
|
|
|
137
100
|
this.requestCounts.set(requestKey, currentCount + 1);
|
|
138
101
|
|
|
139
|
-
// Clean up old request counts (keep only current and previous minute)
|
|
140
102
|
for (const [key] of this.requestCounts) {
|
|
141
103
|
if (parseInt(key) < currentMinute - 1) {
|
|
142
104
|
this.requestCounts.delete(key);
|
|
@@ -145,10 +107,8 @@ export class MCPConnectServer {
|
|
|
145
107
|
|
|
146
108
|
const { name, arguments: args } = request.params;
|
|
147
109
|
|
|
148
|
-
// Log incoming request
|
|
149
110
|
logger.traceRequest('tools/call', { name, args }, requestId);
|
|
150
111
|
|
|
151
|
-
// Validate request
|
|
152
112
|
if (!name || typeof name !== 'string') {
|
|
153
113
|
const error = new Error('Tool name is required and must be a string');
|
|
154
114
|
logger.traceError('tools/call', error, requestId);
|
|
@@ -168,10 +128,8 @@ export class MCPConnectServer {
|
|
|
168
128
|
logger.toolExecutionStart(name, args, requestId);
|
|
169
129
|
|
|
170
130
|
try {
|
|
171
|
-
// Validate and sanitize arguments
|
|
172
131
|
const validatedArgs = validateToolArguments(args);
|
|
173
132
|
|
|
174
|
-
// Execute tool with timeout and performance tracking
|
|
175
133
|
const toolPromise = Promise.resolve(tool.handler(validatedArgs));
|
|
176
134
|
const result = await logger.timeAsync(
|
|
177
135
|
`Tool "${name}" execution`,
|
|
@@ -179,9 +137,8 @@ export class MCPConnectServer {
|
|
|
179
137
|
name
|
|
180
138
|
);
|
|
181
139
|
|
|
182
|
-
// Handle different result types with size limits
|
|
183
140
|
let content;
|
|
184
|
-
const MAX_RESPONSE_SIZE = 1024 * 1024;
|
|
141
|
+
const MAX_RESPONSE_SIZE = 1024 * 1024;
|
|
185
142
|
|
|
186
143
|
if (typeof result === 'string') {
|
|
187
144
|
content = result;
|
|
@@ -189,24 +146,17 @@ export class MCPConnectServer {
|
|
|
189
146
|
content = 'null';
|
|
190
147
|
} else {
|
|
191
148
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// Handle circular references
|
|
197
|
-
if (typeof value === 'object' && value !== null) {
|
|
198
|
-
if (this.seenObjects && this.seenObjects.has(value)) {
|
|
199
|
-
return '[Circular Reference]';
|
|
200
|
-
}
|
|
201
|
-
if (!this.seenObjects) this.seenObjects = new WeakSet();
|
|
202
|
-
this.seenObjects.add(value);
|
|
149
|
+
const jsonString = JSON.stringify(result, (key, value) => {
|
|
150
|
+
if (typeof value === 'object' && value !== null) {
|
|
151
|
+
if (this.seenObjects && this.seenObjects.has(value)) {
|
|
152
|
+
return '[Circular Reference]';
|
|
203
153
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
154
|
+
if (!this.seenObjects) this.seenObjects = new WeakSet();
|
|
155
|
+
this.seenObjects.add(value);
|
|
156
|
+
}
|
|
157
|
+
return value;
|
|
158
|
+
}, 2);
|
|
208
159
|
|
|
209
|
-
// Reset circular reference tracker
|
|
210
160
|
this.seenObjects = null;
|
|
211
161
|
|
|
212
162
|
content = jsonString;
|
|
@@ -216,7 +166,6 @@ export class MCPConnectServer {
|
|
|
216
166
|
}
|
|
217
167
|
}
|
|
218
168
|
|
|
219
|
-
// Check response size and truncate if necessary
|
|
220
169
|
if (content.length > MAX_RESPONSE_SIZE) {
|
|
221
170
|
const truncated = content.substring(0, MAX_RESPONSE_SIZE - 100);
|
|
222
171
|
content = truncated + '\n\n[Response truncated - exceeded 1MB limit]';
|
|
@@ -243,23 +192,16 @@ export class MCPConnectServer {
|
|
|
243
192
|
logger.toolExecutionError(name, error, duration, requestId);
|
|
244
193
|
logger.traceError('tools/call', error, requestId);
|
|
245
194
|
|
|
246
|
-
// Return MCP-compliant error with sanitized message
|
|
247
195
|
const sanitizedMessage = sanitizeErrorMessage(error);
|
|
248
196
|
throw new Error(`Tool "${name}" failed: ${sanitizedMessage}`);
|
|
249
197
|
}
|
|
250
198
|
});
|
|
251
199
|
}
|
|
252
200
|
|
|
253
|
-
/**
|
|
254
|
-
* Start the MCP server with STDIO transport
|
|
255
|
-
* @returns {Promise<void>} Promise that resolves when server starts
|
|
256
|
-
* @throws {Error} If server fails to start
|
|
257
|
-
*/
|
|
258
201
|
async start() {
|
|
259
202
|
try {
|
|
260
203
|
const transport = new StdioServerTransport();
|
|
261
204
|
|
|
262
|
-
// Enhanced error handling for transport
|
|
263
205
|
transport.onclose = () => {
|
|
264
206
|
if (!this.isShuttingDown) {
|
|
265
207
|
logger.warn('STDIO transport closed unexpectedly - AI client may have disconnected');
|
|
@@ -270,7 +212,6 @@ export class MCPConnectServer {
|
|
|
270
212
|
transport.onerror = (error) => {
|
|
271
213
|
logger.error('STDIO transport error', error);
|
|
272
214
|
|
|
273
|
-
// Provide helpful guidance for common transport errors
|
|
274
215
|
if (error && typeof error === 'object') {
|
|
275
216
|
const errorStr = error.toString();
|
|
276
217
|
|
|
@@ -287,7 +228,6 @@ export class MCPConnectServer {
|
|
|
287
228
|
}
|
|
288
229
|
};
|
|
289
230
|
|
|
290
|
-
// Handle process stdio errors
|
|
291
231
|
process.stdin.on('error', (error) => {
|
|
292
232
|
if (!this.isShuttingDown) {
|
|
293
233
|
logger.error('STDIN error', error);
|
|
@@ -307,7 +247,6 @@ export class MCPConnectServer {
|
|
|
307
247
|
} catch (error) {
|
|
308
248
|
logger.error('Failed to start MCP server', error);
|
|
309
249
|
|
|
310
|
-
// Enhanced error guidance
|
|
311
250
|
if (error instanceof Error) {
|
|
312
251
|
const errorMsg = error.message;
|
|
313
252
|
|
|
@@ -333,13 +272,8 @@ export class MCPConnectServer {
|
|
|
333
272
|
}
|
|
334
273
|
}
|
|
335
274
|
|
|
336
|
-
/**
|
|
337
|
-
* Stop the MCP server
|
|
338
|
-
* @returns {Promise<void>} Promise that resolves when server stops
|
|
339
|
-
*/
|
|
340
275
|
async stop() {
|
|
341
276
|
this.isShuttingDown = true;
|
|
342
|
-
// STDIO transport doesn't need explicit stopping
|
|
343
277
|
logger.info('MCP server stopped');
|
|
344
278
|
}
|
|
345
279
|
}
|
package/src/types/mcp.js
CHANGED
|
@@ -1,60 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP-compliant types using official SDK
|
|
3
|
-
* This file contains JSDoc type definitions for MCP-Connect
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} MCPTool
|
|
8
|
-
* @property {string} name - Tool name
|
|
9
|
-
* @property {string} description - Tool description
|
|
10
|
-
* @property {Record<string, any>} inputSchema - JSON Schema for input validation
|
|
11
|
-
* @property {function(Record<string, any>): Promise<any>|any} handler - Tool execution function
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @typedef {Object} MCPConfig
|
|
16
|
-
* @property {string} name - Server name
|
|
17
|
-
* @property {string} version - Server version
|
|
18
|
-
* @property {string} [description] - Server description
|
|
19
|
-
* @property {MCPTool[]} tools - Array of MCP tools
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @typedef {Object} MCPServerOptions
|
|
24
|
-
* @property {MCPConfig} config - MCP configuration
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Legacy types for backward compatibility
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @typedef {function(...any[]): Promise<any>|any} ToolFunction
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Tool definition - supports both tuple and object formats
|
|
37
|
-
* @typedef {[string, ToolFunction]|{name: string, handler: ToolFunction, description?: string, schema?: Record<string, any>}} ToolDefinition
|
|
38
|
-
*
|
|
39
|
-
* Tuple format: [name, handler]
|
|
40
|
-
* @example ['hello', async (args) => `Hello ${args.name}!`]
|
|
41
|
-
*
|
|
42
|
-
* Object format: {name, handler, description?, schema?}
|
|
43
|
-
* @example {
|
|
44
|
-
* name: 'hello',
|
|
45
|
-
* handler: async (args) => `Hello ${args.name}!`,
|
|
46
|
-
* description: 'Greet a user',
|
|
47
|
-
* schema: { type: 'object', properties: { name: { type: 'string' } } }
|
|
48
|
-
* }
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
// Runtime validation functions for type checking
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Validate MCPTool object
|
|
55
|
-
* @param {any} tool - Tool to validate
|
|
56
|
-
* @returns {boolean} Whether the tool is valid
|
|
57
|
-
*/
|
|
58
1
|
export function isValidMCPTool(tool) {
|
|
59
2
|
return (
|
|
60
3
|
tool &&
|
|
@@ -66,11 +9,6 @@ export function isValidMCPTool(tool) {
|
|
|
66
9
|
);
|
|
67
10
|
}
|
|
68
11
|
|
|
69
|
-
/**
|
|
70
|
-
* Validate MCPConfig object
|
|
71
|
-
* @param {any} config - Config to validate
|
|
72
|
-
* @returns {boolean} Whether the config is valid
|
|
73
|
-
*/
|
|
74
12
|
export function isValidMCPConfig(config) {
|
|
75
13
|
return (
|
|
76
14
|
config &&
|
|
@@ -82,19 +20,12 @@ export function isValidMCPConfig(config) {
|
|
|
82
20
|
);
|
|
83
21
|
}
|
|
84
22
|
|
|
85
|
-
/**
|
|
86
|
-
* Validate ToolDefinition (tuple or object format)
|
|
87
|
-
* @param {any} toolDef - Tool definition to validate
|
|
88
|
-
* @returns {boolean} Whether the tool definition is valid
|
|
89
|
-
*/
|
|
90
23
|
export function isValidToolDefinition(toolDef) {
|
|
91
24
|
if (Array.isArray(toolDef)) {
|
|
92
|
-
// Tuple format: [name, handler]
|
|
93
25
|
return (
|
|
94
26
|
toolDef.length === 2 && typeof toolDef[0] === 'string' && typeof toolDef[1] === 'function'
|
|
95
27
|
);
|
|
96
28
|
} else if (toolDef && typeof toolDef === 'object') {
|
|
97
|
-
// Object format: {name, handler, description?, schema?}
|
|
98
29
|
return (
|
|
99
30
|
typeof toolDef.name === 'string' &&
|
|
100
31
|
typeof toolDef.handler === 'function' &&
|