aiexecode 1.0.88 → 1.0.90

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.

Potentially problematic release.


This version of aiexecode might be problematic. Click here for more details.

Files changed (36) hide show
  1. package/index.js +128 -20
  2. package/mcp-agent-lib/.claude/settings.local.json +9 -0
  3. package/mcp-agent-lib/example/01-basic-usage.js +82 -0
  4. package/mcp-agent-lib/example/02-quick-start.js +52 -0
  5. package/mcp-agent-lib/example/03-http-server.js +76 -0
  6. package/mcp-agent-lib/example/04-multiple-servers.js +117 -0
  7. package/mcp-agent-lib/example/05-error-handling.js +116 -0
  8. package/mcp-agent-lib/example/06-resources-and-prompts.js +174 -0
  9. package/mcp-agent-lib/example/07-advanced-configuration.js +191 -0
  10. package/mcp-agent-lib/example/08-real-world-chatbot.js +331 -0
  11. package/mcp-agent-lib/example/README.md +346 -0
  12. package/mcp-agent-lib/sampleMCPHost/index.js +267 -0
  13. package/mcp-agent-lib/sampleMCPHost/mcp_config.json +18 -0
  14. package/mcp-agent-lib/src/mcp_client.js +302 -77
  15. package/package.json +1 -1
  16. package/payload_viewer/out/404/index.html +1 -1
  17. package/payload_viewer/out/404.html +1 -1
  18. package/payload_viewer/out/index.html +1 -1
  19. package/payload_viewer/out/index.txt +1 -1
  20. package/src/ai_based/orchestrator.js +4 -1
  21. package/src/cli/mcp_cli.js +14 -7
  22. package/src/cli/mcp_commands.js +31 -15
  23. package/src/commands/mcp.js +36 -18
  24. package/src/frontend/App.js +54 -4
  25. package/src/frontend/components/BlankLine.js +5 -3
  26. package/src/frontend/components/ConversationItem.js +43 -10
  27. package/src/system/code_executer.js +6 -0
  28. package/src/system/mcp_integration.js +94 -40
  29. package/src/tools/file_reader.js +6 -0
  30. package/src/tools/glob.js +3 -0
  31. package/src/tools/ripgrep.js +2 -0
  32. package/src/tools/web_downloader.js +3 -0
  33. package/src/util/mcp_config_manager.js +41 -20
  34. /package/payload_viewer/out/_next/static/{dp582oDmc4bDYYIktREJ4 → w4dMVYalgk7djrLxRxWiE}/_buildManifest.js +0 -0
  35. /package/payload_viewer/out/_next/static/{dp582oDmc4bDYYIktREJ4 → w4dMVYalgk7djrLxRxWiE}/_clientMiddlewareManifest.json +0 -0
  36. /package/payload_viewer/out/_next/static/{dp582oDmc4bDYYIktREJ4 → w4dMVYalgk7djrLxRxWiE}/_ssgManifest.js +0 -0
@@ -9,22 +9,36 @@
9
9
  * @author AI Agent Library Team
10
10
  */
11
11
 
12
+ // Node.js 이벤트 시스템 - 서버 연결/해제 등의 이벤트를 외부로 발행하기 위해 사용
12
13
  import { EventEmitter } from 'events';
14
+ // MCP SDK의 핵심 클라이언트 - 실제 MCP 프로토콜 통신 담당
13
15
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
16
+ // Stdio 전송: 로컬 프로세스를 spawn하여 stdin/stdout으로 통신
14
17
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
18
+ // SSE 전송: Server-Sent Events 기반 HTTP 스트리밍 통신
15
19
  import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
20
+ // HTTP 전송: 일반 HTTP/HTTPS 요청-응답 기반 통신
16
21
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
17
22
 
23
+ // 내부 디버그용 로그 함수 - 실제로는 아무 작업도 하지 않음 (성능 최적화)
18
24
  function consolelog() { }
25
+
19
26
  /**
20
- * AI Agent를 위한 고도화된 MCP 클라이언트
21
- * SDK 기반 통합 구현체
27
+ * MCPAgentClient - MCP 서버들과 통신하는 메인 클라이언트 클래스
28
+ *
29
+ * 주요 기능:
30
+ * - 여러 MCP 서버에 동시 연결 (stdio/HTTP/SSE 방식 지원)
31
+ * - Tool 실행, Resource 읽기, Prompt 가져오기 등 MCP 기능 제공
32
+ * - 자동 재시도, 에러 핸들링, 보안 검증 내장
33
+ * - 이벤트 기반 상태 알림 (연결/해제/에러)
22
34
  */
23
35
  export class MCPAgentClient extends EventEmitter {
24
36
  constructor(options = {}) {
25
37
  super();
26
38
 
27
- // 통합된 설정 옵션 - Environment-driven configuration with safe parsing
39
+ // 클라이언트 설정 초기화
40
+ // 환경변수(process.env.MCP_*)와 options 매개변수를 병합하여 우선순위 적용
41
+ // 타임아웃, 재시도, 로깅, 보안 정책 등 모든 동작 방식을 제어
28
42
  this.options = {
29
43
  autoConnect: options.autoConnect ?? true,
30
44
  logLevel: options.logLevel || process.env.MCP_LOG_LEVEL || 'info',
@@ -41,24 +55,33 @@ export class MCPAgentClient extends EventEmitter {
41
55
  maxJsonSize: MCPAgentClient._safeParseInt(process.env.MCP_MAX_JSON_SIZE || options.maxJsonSize, 10 * 1024 * 1024),
42
56
  serverReadyTimeout: MCPAgentClient._safeParseInt(process.env.MCP_SERVER_READY_TIMEOUT || options.serverReadyTimeout, 10000),
43
57
  serverReadyRetries: MCPAgentClient._safeParseInt(process.env.MCP_SERVER_READY_RETRIES || options.serverReadyRetries, 5),
44
- // Security configuration (no hardcoding)
58
+
59
+ // 보안 설정: stdio 서버에서 실행 가능한 명령어 화이트리스트
45
60
  allowedCommands: ['node', 'python', 'python3', 'npx', 'deno'],
61
+
62
+ // 보안 설정: 명령 인자에서 금지되는 셸 특수문자 (command injection 방지)
46
63
  dangerousChars: ['&', ';', '|', '`', '$', '>', '<', '*', '?'],
64
+
65
+ // 응답 파싱: JSON으로 파싱하지 않을 응답의 시작 패턴들
47
66
  responseIndicators: ['✅', '❌', 'Error:', 'Warning:', 'Info:'],
48
- // Security patterns (configurable)
67
+
68
+ // 보안 설정: 프로토타입 오염 공격 탐지 패턴 (prototype pollution 방지)
49
69
  prototypePollutionPatterns: ['__proto__', 'constructor', 'prototype', '["__proto__"]', "['__proto__']", '["constructor"]', "['constructor']", 'Object.prototype', 'Function.prototype', 'Array.prototype'],
50
- // Standard client capabilities for all transports
70
+
71
+ // MCP 프로토콜: 이 클라이언트가 지원하는 기능들을 서버에 알림
51
72
  clientCapabilities: {
52
73
  roots: { listChanged: true },
53
74
  sampling: {},
54
75
  experimental: {}
55
76
  },
56
- // Standard client info
77
+
78
+ // MCP 프로토콜: 클라이언트 식별 정보
57
79
  clientInfo: {
58
80
  name: process.env.MCP_CLIENT_NAME || 'mcp-agent-lib',
59
81
  version: process.env.MCP_CLIENT_VERSION || '1.0.0'
60
82
  },
61
- // Internationalization
83
+
84
+ // 사용자 메시지 템플릿 (다국어 지원용)
62
85
  messages: {
63
86
  initStart: '🚀 MCP Agent Client 초기화 중...',
64
87
  emptyConfig: '⚠️ 빈 설정으로 초기화합니다. 서버를 연결하려면 mcpServers 설정을 제공하세요.',
@@ -83,18 +106,24 @@ export class MCPAgentClient extends EventEmitter {
83
106
  ...options
84
107
  };
85
108
 
86
- // 서버 클라이언트 관리
109
+ // 연결된 서버들의 정보 저장소
110
+ // servers: 서버 메타데이터(이름, 타입, tools, resources, prompts 등)
111
+ // clients: 실제 MCP SDK Client 인스턴스들
87
112
  this.servers = new Map();
88
113
  this.clients = new Map();
89
114
  this.isInitialized = false;
90
115
 
91
- // 메모리 관리 - On-demand cleanup only (no dangerous intervals)
116
+ // 메모리 관리 설정
117
+ // 연결 해제된 서버 정보를 주기적으로 정리 (interval 대신 on-demand 방식)
92
118
  this.memoryCleanupEnabled = options.memoryCleanupEnabled ?? true;
93
119
  this.lastCleanupTime = Date.now();
94
120
  }
95
121
 
96
122
  /**
97
- * Safe integer parsing with fallback
123
+ * 정수 파싱 유틸리티
124
+ *
125
+ * 환경변수나 설정값을 안전하게 숫자로 변환
126
+ * 파싱 실패 시 기본값(fallback) 반환
98
127
  */
99
128
  static _safeParseInt(value, fallback) {
100
129
  const parsed = parseInt(value);
@@ -102,7 +131,9 @@ export class MCPAgentClient extends EventEmitter {
102
131
  }
103
132
 
104
133
  /**
105
- * Safe float parsing with fallback
134
+ * 실수 파싱 유틸리티
135
+ *
136
+ * _safeParseInt와 동일하지만 소수점 숫자 처리
106
137
  */
107
138
  static _safeParseFloat(value, fallback) {
108
139
  const parsed = parseFloat(value);
@@ -110,7 +141,15 @@ export class MCPAgentClient extends EventEmitter {
110
141
  }
111
142
 
112
143
  /**
113
- * Safe JSON stringification with circular reference handling
144
+ * 안전한 JSON 직렬화
145
+ *
146
+ * 문제점:
147
+ * - 순환 참조가 있는 객체는 JSON.stringify 시 에러 발생
148
+ * - 함수나 undefined는 JSON으로 변환 불가
149
+ *
150
+ * 해결책:
151
+ * - WeakSet으로 이미 방문한 객체 추적하여 순환 참조 탐지
152
+ * - 함수와 undefined를 문자열로 표현
114
153
  */
115
154
  safeJsonStringify(obj, space = null) {
116
155
  const seen = new WeakSet();
@@ -121,7 +160,6 @@ export class MCPAgentClient extends EventEmitter {
121
160
  }
122
161
  seen.add(value);
123
162
  }
124
- // Handle functions and undefined
125
163
  if (typeof value === 'function') {
126
164
  return '[Function]';
127
165
  }
@@ -133,13 +171,23 @@ export class MCPAgentClient extends EventEmitter {
133
171
  }
134
172
 
135
173
  /**
136
- * Safe JSON parsing to prevent injection attacks and prototype pollution
174
+ * 안전한 JSON 파싱
175
+ *
176
+ * 보안 위협:
177
+ * 1. Prototype Pollution: __proto__, constructor 등을 이용한 공격
178
+ * 2. JSON Injection: 악의적인 JSON 데이터 삽입
179
+ *
180
+ * 보안 대책:
181
+ * 1. 위험한 패턴 사전 검사 (prototypePollutionPatterns)
182
+ * 2. 응답 크기 제한 (maxResponseSize)
183
+ * 3. 파싱 중 위험한 키 제거
184
+ * 4. 파싱 후 객체를 freeze하여 변경 불가능하게 만듦
137
185
  */
138
186
  safeJsonParse(text) {
139
187
  try {
140
188
  if (typeof text !== 'string') return text;
141
189
 
142
- // Enhanced prototype pollution detection
190
+ // 1단계: 프로토타입 오염 패턴 사전 검사
143
191
  const dangerousPatterns = this.options.prototypePollutionPatterns;
144
192
 
145
193
  const lowerText = text.toLowerCase();
@@ -153,22 +201,24 @@ export class MCPAgentClient extends EventEmitter {
153
201
  }
154
202
  }
155
203
 
204
+ // 2단계: 응답 크기 검사 (DoS 공격 방지)
156
205
  if (text.length > this.options.maxResponseSize) {
157
206
  this.secureLog('warn', this.options.messages.responseSizeExceeds, { size: text.length, limit: this.options.maxResponseSize });
158
207
  return text;
159
208
  }
160
209
 
210
+ // 3단계: JSON 파싱 with reviver 함수
211
+ // reviver: 각 key-value 파싱 시 호출되어 위험한 키 필터링
161
212
  const parsed = JSON.parse(text, (key, value) => {
162
- // Additional key validation during parsing
163
213
  if (typeof key === 'string' && this.options.prototypePollutionPatterns.includes(key)) {
164
214
  this.secureLog('warn', this.options.messages.dangerousKey, { key });
165
- return undefined; // Remove dangerous keys
215
+ return undefined;
166
216
  }
167
217
  return value;
168
218
  });
169
219
 
220
+ // 4단계: 파싱된 객체를 재귀적으로 freeze (불변성 보장)
170
221
  if (parsed && typeof parsed === 'object') {
171
- // Recursively freeze objects to prevent mutation
172
222
  this.deepFreeze(parsed);
173
223
  }
174
224
  return parsed;
@@ -179,7 +229,11 @@ export class MCPAgentClient extends EventEmitter {
179
229
  }
180
230
 
181
231
  /**
182
- * Deep freeze objects recursively to prevent mutation
232
+ * 객체 재귀적 동결
233
+ *
234
+ * Object.freeze()는 얕은 동결만 수행
235
+ * 이 메서드는 중첩된 모든 객체를 재귀적으로 동결하여
236
+ * 파싱된 JSON 데이터의 완전한 불변성 보장
183
237
  */
184
238
  deepFreeze(obj) {
185
239
  if (!obj || typeof obj !== 'object') return obj;
@@ -195,7 +249,10 @@ export class MCPAgentClient extends EventEmitter {
195
249
  }
196
250
 
197
251
  /**
198
- * Create standardized MCP error
252
+ * MCP 표준 에러 생성
253
+ *
254
+ * MCP 프로토콜은 JSON-RPC 기반이므로 에러에 code와 data 필드 포함
255
+ * code: JSON-RPC 에러 코드 (예: -32602 = Invalid params)
199
256
  */
200
257
  createMCPError(code, message, data = null) {
201
258
  const error = new Error(message);
@@ -205,7 +262,12 @@ export class MCPAgentClient extends EventEmitter {
205
262
  }
206
263
 
207
264
  /**
208
- * Secure logging
265
+ * 보안 로깅 시스템
266
+ *
267
+ * 기능:
268
+ * 1. 로그 레벨 필터링 (debug < info < warn < error < silent)
269
+ * 2. 민감 정보 자동 제거 (sanitizeLogData 호출)
270
+ * 3. 타임스탬프 자동 추가
209
271
  */
210
272
  secureLog(level, message, data = {}) {
211
273
  if (!this.options.enableLogging) return;
@@ -228,7 +290,11 @@ export class MCPAgentClient extends EventEmitter {
228
290
  }
229
291
 
230
292
  /**
231
- * Sanitize log data
293
+ * 로그 데이터 민감정보 제거
294
+ *
295
+ * 보안을 위해 로그에 출력되는 데이터에서 민감 정보 제거:
296
+ * - auth, token, key, secret, password 등이 포함된 필드는 [REDACTED]로 대체
297
+ * - 너무 긴 문자열은 잘라내고 [TRUNCATED] 표시
232
298
  */
233
299
  sanitizeLogData(data) {
234
300
  if (!data || typeof data !== 'object') return data;
@@ -251,7 +317,10 @@ export class MCPAgentClient extends EventEmitter {
251
317
  }
252
318
 
253
319
  /**
254
- * Debug logging helper
320
+ * 간단한 로깅 헬퍼
321
+ *
322
+ * secureLog와 유사하지만 더 간단한 버전
323
+ * 이모지 아이콘과 함께 로그 출력
255
324
  */
256
325
  _log(level, message) {
257
326
  const levels = { debug: 0, info: 1, warn: 2, error: 3, silent: 4 };
@@ -274,7 +343,22 @@ export class MCPAgentClient extends EventEmitter {
274
343
  }
275
344
 
276
345
  /**
277
- * 간단한 초기화 - JSON 설정 객체로 연결
346
+ * 클라이언트 초기화
347
+ *
348
+ * 설정 객체(config)를 받아서 여러 MCP 서버에 연결
349
+ *
350
+ * config 형식:
351
+ * {
352
+ * mcpServers: {
353
+ * "서버이름": {
354
+ * type: "stdio" | "http" | "sse",
355
+ * command: "node", // stdio만
356
+ * args: [...], // stdio만
357
+ * url: "https://...", // http/sse만
358
+ * headers: {...} // http/sse만
359
+ * }
360
+ * }
361
+ * }
278
362
  */
279
363
  async initialize(config = {}) {
280
364
  try {
@@ -290,10 +374,11 @@ export class MCPAgentClient extends EventEmitter {
290
374
  configuration = config;
291
375
  }
292
376
 
377
+ // 설정에 정의된 모든 서버에 연결 시도
293
378
  await this.connectFromConfig(configuration);
294
379
  this.isInitialized = true;
295
380
 
296
- // 서버 정보 수집
381
+ // 연결 성공한 서버들의 정보 정리
297
382
  for (const [serverName, serverInfo] of this.servers) {
298
383
  if (serverInfo.connected) {
299
384
  this.servers.set(serverName, {
@@ -315,7 +400,13 @@ export class MCPAgentClient extends EventEmitter {
315
400
  }
316
401
 
317
402
  /**
318
- * Validate server configuration for security
403
+ * 서버 설정 보안 검증
404
+ *
405
+ * 연결 전에 서버 설정이 안전한지 검사:
406
+ * 1. stdio 서버: 허용된 명령어만 실행 가능한지 확인
407
+ * 2. stdio 서버: 인자에 위험한 셸 문자가 없는지 확인
408
+ * 3. http 서버: URL이 http/https 프로토콜인지 확인
409
+ * 4. 환경변수가 문자열 key-value인지 확인
319
410
  */
320
411
  validateServerConfig(serverName, serverConfig) {
321
412
  if (!serverName || typeof serverName !== 'string') {
@@ -326,15 +417,16 @@ export class MCPAgentClient extends EventEmitter {
326
417
  throw new Error('Server config must be an object');
327
418
  }
328
419
 
329
- // Validate command execution for any server that has a command
420
+ // stdio 서버 보안 검증
330
421
  if (serverConfig.command) {
331
422
  const allowedCommands = this.options.allowedCommands || ['node', 'python', 'python3', 'npx', 'deno'];
332
423
 
424
+ // 화이트리스트에 없는 명령어는 실행 불가
333
425
  if (!allowedCommands.includes(serverConfig.command)) {
334
426
  throw new Error(`Command '${serverConfig.command}' is not allowed. Allowed commands: ${allowedCommands.join(', ')}`);
335
427
  }
336
428
 
337
- // Validate arguments to prevent injection
429
+ // 명령 인자에 특수문자 포함 여부 검사 (command injection 방지)
338
430
  if (serverConfig.args && Array.isArray(serverConfig.args)) {
339
431
  const dangerousChars = this.options.dangerousChars || ['&', ';', '|', '`', '$', '>', '<', '*', '?'];
340
432
  for (const arg of serverConfig.args) {
@@ -344,7 +436,7 @@ export class MCPAgentClient extends EventEmitter {
344
436
  }
345
437
  }
346
438
 
347
- // Validate environment variables
439
+ // 환경변수 형식 검증 (문자열 key-value만 허용)
348
440
  if (serverConfig.env && typeof serverConfig.env === 'object') {
349
441
  for (const [key, value] of Object.entries(serverConfig.env)) {
350
442
  if (typeof key !== 'string' || typeof value !== 'string') {
@@ -354,7 +446,7 @@ export class MCPAgentClient extends EventEmitter {
354
446
  }
355
447
  }
356
448
 
357
- // Validate HTTP URLs
449
+ // HTTP/SSE 서버 URL 검증
358
450
  if (serverConfig.type === 'http' && serverConfig.url) {
359
451
  try {
360
452
  const url = new URL(serverConfig.url);
@@ -371,7 +463,12 @@ export class MCPAgentClient extends EventEmitter {
371
463
  }
372
464
 
373
465
  /**
374
- * Connect to all servers defined in configuration
466
+ * 설정 파일의 모든 서버에 연결
467
+ *
468
+ * 각 서버 설정을 순회하며:
469
+ * 1. 보안 검증 수행
470
+ * 2. 서버 타입에 맞는 연결 메서드 호출
471
+ * 3. 연결 실패 시 다른 서버 연결 계속 진행
375
472
  */
376
473
  async connectFromConfig(config) {
377
474
  if (!config || !config.mcpServers) {
@@ -383,13 +480,14 @@ export class MCPAgentClient extends EventEmitter {
383
480
 
384
481
  for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
385
482
  try {
386
- // Validate configuration before connecting - security validation should fail fast
483
+ // 보안 검증 먼저 수행 (fail-fast)
387
484
  this.validateServerConfig(serverName, serverConfig);
388
485
 
389
486
  this._log('info', `🔗 Connecting to server: ${serverName}`);
390
487
 
391
488
  let server;
392
489
 
490
+ // 서버 타입에 따라 적절한 연결 메서드 선택
393
491
  if (serverConfig.type === 'http') {
394
492
  server = await this.connectToHttpServer(serverName, serverConfig);
395
493
  } else if (serverConfig.type === 'sse') {
@@ -404,11 +502,11 @@ export class MCPAgentClient extends EventEmitter {
404
502
  results.push({ name: serverName, success: true, server });
405
503
 
406
504
  } catch (error) {
407
- // If this is a security validation error, re-throw immediately
505
+ // 보안 에러는 즉시 전파 (다른 서버 연결 중단)
408
506
  if (error.message.includes('not allowed') || error.message.includes('dangerous characters')) {
409
507
  throw error;
410
508
  }
411
- // For other connection errors, log and continue
509
+ // 일반 연결 에러는 로그만 남기고 다음 서버 계속 진행
412
510
  this._log('error', `❌ Failed to connect to ${serverName}: ${error.message}`);
413
511
  results.push({ name: serverName, success: false, error: error.message });
414
512
  }
@@ -418,21 +516,33 @@ export class MCPAgentClient extends EventEmitter {
418
516
  }
419
517
 
420
518
  /**
421
- * Connect to an MCP server using stdio transport
519
+ * Stdio 전송 방식으로 MCP 서버 연결
520
+ *
521
+ * 동작 방식:
522
+ * 1. 로컬 프로세스 spawn (예: node server.js)
523
+ * 2. stdin/stdout을 통해 JSON-RPC 통신
524
+ * 3. stderr은 에러 로깅용으로 사용
525
+ *
526
+ * 주요 단계:
527
+ * 1. SDK Transport 객체 생성
528
+ * 2. SDK Client 생성 및 연결
529
+ * 3. 서버 준비 대기 (_waitForServerReady)
530
+ * 4. 사용 가능한 tools/resources/prompts 목록 조회
422
531
  */
423
532
  async connectToStdioServer(serverName, serverConfig) {
424
533
  try {
425
534
  this._log('info', `🔌 Connecting to STDIO server: ${serverName}`);
426
535
 
427
- // Create SDK transport with server parameters - SDK will handle spawning
536
+ // SDK Transport 생성 - SDK 자동으로 프로세스 spawn
428
537
  const transport = new StdioClientTransport({
429
538
  command: serverConfig.command,
430
539
  args: serverConfig.args || [],
431
540
  env: serverConfig.env,
432
541
  cwd: serverConfig.cwd,
433
- stderr: 'pipe' // Capture stderr for logging
542
+ stderr: 'pipe'
434
543
  });
435
544
 
545
+ // MCP SDK Client 생성
436
546
  const client = new Client(
437
547
  this.options.clientInfo,
438
548
  {
@@ -440,6 +550,7 @@ export class MCPAgentClient extends EventEmitter {
440
550
  }
441
551
  );
442
552
 
553
+ // 서버 메타데이터 객체 생성
443
554
  const server = {
444
555
  name: serverName,
445
556
  type: 'stdio',
@@ -452,7 +563,7 @@ export class MCPAgentClient extends EventEmitter {
452
563
  config: serverConfig
453
564
  };
454
565
 
455
- // Setup stderr logging if available
566
+ // stderr 스트림 모니터링 설정 (서버 에러 로깅)
456
567
  const stderrStream = transport.stderr;
457
568
  if (stderrStream) {
458
569
  stderrStream.on('data', (data) => {
@@ -465,7 +576,7 @@ export class MCPAgentClient extends EventEmitter {
465
576
  });
466
577
  }
467
578
 
468
- // Setup transport event handlers
579
+ // Transport 이벤트 핸들러 설정 (연결 상태 추적)
469
580
  transport.onerror = (error) => {
470
581
  this._log('error', `💥 Transport error for ${serverName}: ${error}`);
471
582
  this.emit('serverError', serverName, error);
@@ -477,21 +588,21 @@ export class MCPAgentClient extends EventEmitter {
477
588
  this.emit('serverDisconnected', serverName);
478
589
  };
479
590
 
480
- // Connect using SDK
591
+ // MCP 프로토콜 핸드셰이크 수행
481
592
  await client.connect(transport);
482
593
 
483
- // Get server capabilities and info
594
+ // 서버가 제공하는 기능 정보 가져오기
484
595
  server.capabilities = client.getServerCapabilities();
485
596
  this._setServerStatus(serverName, 'connected');
486
597
 
487
- // 서버 정보를 먼저 저장
598
+ // Map에 저장 (이후 조회 가능하도록)
488
599
  this.servers.set(serverName, server);
489
600
  this.clients.set(serverName, client);
490
601
 
491
- // Wait for server to be ready before querying capabilities
602
+ // 서버가 완전히 준비될 때까지 대기 (재시도 로직 포함)
492
603
  await this._waitForServerReady(client, serverName);
493
604
 
494
- // List available capabilities - 이것이 server 객체를 업데이트함
605
+ // 사용 가능한 tools/resources/prompts 목록 조회 저장
495
606
  await this.listServerCapabilities(serverName);
496
607
 
497
608
  // Log final server statistics without duplicate status setting
@@ -799,14 +910,25 @@ export class MCPAgentClient extends EventEmitter {
799
910
  }
800
911
 
801
912
  /**
802
- * 도구 실행 - 자동으로 적절한 서버 찾아서 실행
913
+ * Tool 실행 (고수준 API)
914
+ *
915
+ * Tool 이름만으로 자동 실행:
916
+ * 1. Tool을 제공하는 서버 자동 검색
917
+ * 2. 해당 서버에서 Tool 실행
918
+ * 3. 실패 시 자동 재시도 (exponential backoff)
919
+ * 4. 결과 정제 및 반환
920
+ *
921
+ * @param toolName - 실행할 Tool 이름
922
+ * @param args - Tool에 전달할 인자 (JSON 객체)
923
+ * @param options - timeout, retries 등 실행 옵션
924
+ * @returns 정제된 실행 결과
803
925
  */
804
926
  async executeTool(toolName, args = {}, options = {}) {
805
927
  this._ensureInitialized();
806
928
 
807
929
  const { timeout = this.options.timeout, retries = this.options.retries } = options;
808
930
 
809
- // 도구를 제공하는 서버 찾기
931
+ // Tool을 제공하는 서버 찾기
810
932
  const serverName = this._findServerForTool(toolName);
811
933
  if (!serverName) {
812
934
  throw new Error(this.options.messages.toolNotFound(toolName));
@@ -815,16 +937,19 @@ export class MCPAgentClient extends EventEmitter {
815
937
  this._log('info', this.options.messages.toolExecuting(toolName, serverName));
816
938
  this._log('debug', `📝 인수: ${JSON.stringify(args)}`);
817
939
 
940
+ // 재시도 로직
818
941
  let lastError;
819
942
  const maxAttempts = Math.max(1, retries);
820
943
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
821
944
  try {
822
945
  const result = await this.callTool(serverName, toolName, args);
823
946
 
947
+ // MCP 프로토콜에서 에러 응답 처리
824
948
  if (result.isError) {
825
949
  throw new Error(result.content[0]?.text || this.options.messages.unknownError);
826
950
  }
827
951
 
952
+ // 성공 시 정제된 결과 반환
828
953
  const cleanResult = {
829
954
  success: true,
830
955
  data: this._cleanToolResult(result),
@@ -840,28 +965,32 @@ export class MCPAgentClient extends EventEmitter {
840
965
  lastError = error;
841
966
  this._log('warn', this.options.messages.toolRetry(attempt, maxAttempts, error?.message || error));
842
967
 
843
- // Check if server became disconnected
968
+ // 서버 연결 끊김 감지 시 즉시 중단
844
969
  const serverInfo = this.servers.get(serverName);
845
970
  if (serverInfo && !this._isServerConnected(serverInfo)) {
846
971
  this._log('error', `Server ${serverName} disconnected during tool execution`);
847
972
  throw new Error(`Server ${serverName} is no longer connected`);
848
973
  }
849
974
 
975
+ // Exponential backoff로 재시도 지연
850
976
  if (attempt < maxAttempts) {
851
- // Robust exponential backoff with proper bounds
852
977
  const delay = this._calculateRetryDelay(attempt);
853
978
  await this._delay(delay);
854
979
  }
855
980
  }
856
981
  }
857
982
 
983
+ // 모든 재시도 실패 시 최종 에러
858
984
  this._log('error', this.options.messages.toolFinalFail(toolName));
859
985
  const errorMessage = lastError?.message || lastError?.toString() || this.options.messages.unknownError;
860
986
  throw new Error(this.options.messages.toolFailedWithRetries(toolName, maxAttempts, errorMessage));
861
987
  }
862
988
 
863
989
  /**
864
- * Call a tool on a specific server
990
+ * Tool 실행 (저수준 API)
991
+ *
992
+ * 특정 서버의 Tool을 직접 호출
993
+ * executeTool과 달리 서버 이름을 직접 지정해야 함
865
994
  */
866
995
  async callTool(serverName, toolName, arguments_ = {}) {
867
996
  this._log('info', `🔧 Calling tool '${toolName}' on ${serverName}`);
@@ -1220,12 +1349,26 @@ export class MCPAgentClient extends EventEmitter {
1220
1349
  }
1221
1350
  }
1222
1351
 
1223
- // Private helper methods
1352
+ // ========================================
1353
+ // Private 헬퍼 메서드들
1354
+ // ========================================
1224
1355
 
1356
+ /**
1357
+ * 초기화 여부 확인
1358
+ *
1359
+ * 대부분의 메서드 시작 시 호출하여
1360
+ * initialize()가 먼저 호출되었는지 확인
1361
+ */
1225
1362
  _ensureInitialized() {
1226
1363
  if (!this.isInitialized) throw new Error('초기화가 필요합니다. initialize()를 먼저 호출하세요.');
1227
1364
  }
1228
1365
 
1366
+ /**
1367
+ * Tool 이름으로 서버 찾기
1368
+ *
1369
+ * 연결된 모든 서버를 순회하며 해당 Tool을 제공하는 서버 검색
1370
+ * 여러 서버가 같은 이름의 Tool을 제공할 경우 먼저 발견된 서버 반환
1371
+ */
1229
1372
  _findServerForTool(toolName) {
1230
1373
  for (const [name, info] of this.servers) {
1231
1374
  if (this._isServerConnected(info) &&
@@ -1234,12 +1377,29 @@ export class MCPAgentClient extends EventEmitter {
1234
1377
  return null;
1235
1378
  }
1236
1379
 
1380
+ /**
1381
+ * Tool 실행 결과 정제
1382
+ *
1383
+ * MCP 프로토콜 응답 형식:
1384
+ * {
1385
+ * content: [
1386
+ * { type: 'text', text: '...' },
1387
+ * { type: 'image', data: '...' },
1388
+ * ...
1389
+ * ]
1390
+ * }
1391
+ *
1392
+ * 정제 작업:
1393
+ * 1. text 타입만 있으면 문자열로 추출
1394
+ * 2. JSON 형태면 파싱 시도
1395
+ * 3. 배열이면 각 항목 개별 처리
1396
+ */
1237
1397
  _cleanToolResult(result) {
1238
1398
  if (!result.content || !Array.isArray(result.content)) {
1239
1399
  return result;
1240
1400
  }
1241
1401
 
1242
- // If result is marked as raw text, return text without JSON parsing
1402
+ // Raw text 모드: JSON 파싱 없이 그대로 반환
1243
1403
  if (result._isRawText) {
1244
1404
  if (result.content.length === 1 && result.content[0].type === 'text') {
1245
1405
  return result.content[0].text;
@@ -1247,9 +1407,9 @@ export class MCPAgentClient extends EventEmitter {
1247
1407
  return result.content.map(item => item.type === 'text' ? item.text : item);
1248
1408
  }
1249
1409
 
1410
+ // 단일 text 항목: JSON 파싱 시도
1250
1411
  if (result.content.length === 1 && result.content[0].type === 'text') {
1251
1412
  const text = result.content[0].text;
1252
- // Only try to parse as JSON if it looks like structured data
1253
1413
  if (this._looksLikeJson(text)) {
1254
1414
  try {
1255
1415
  return this.safeJsonParse(text);
@@ -1264,6 +1424,7 @@ export class MCPAgentClient extends EventEmitter {
1264
1424
  return text;
1265
1425
  }
1266
1426
 
1427
+ // 다중 항목: 각각 처리
1267
1428
  return result.content.map(item => {
1268
1429
  if (item.type === 'text') {
1269
1430
  if (this._looksLikeJson(item.text)) {
@@ -1284,7 +1445,14 @@ export class MCPAgentClient extends EventEmitter {
1284
1445
  }
1285
1446
 
1286
1447
  /**
1287
- * Centralized server state management
1448
+ * 서버 상태 중앙 관리
1449
+ *
1450
+ * 서버 상태를 일관되게 업데이트하고 이벤트 발행:
1451
+ * - status와 connected 필드 동시 업데이트
1452
+ * - 상태 변경 시 'serverStatusChange' 이벤트 발행
1453
+ * - disconnected 시 메모리 정리 트리거
1454
+ *
1455
+ * 가능한 상태: connecting, connected, disconnected, partially_connected, error
1288
1456
  */
1289
1457
  _setServerStatus(serverName, status, additionalInfo = {}) {
1290
1458
  const server = this.servers.get(serverName);
@@ -1325,7 +1493,9 @@ export class MCPAgentClient extends EventEmitter {
1325
1493
  }
1326
1494
 
1327
1495
  /**
1328
- * Check if server is connected (unified status check)
1496
+ * 서버 연결 상태 확인
1497
+ *
1498
+ * connected 또는 partially_connected 상태만 "연결됨"으로 간주
1329
1499
  */
1330
1500
  _isServerConnected(server) {
1331
1501
  if (!server) return false;
@@ -1333,7 +1503,9 @@ export class MCPAgentClient extends EventEmitter {
1333
1503
  }
1334
1504
 
1335
1505
  /**
1336
- * Get server status with fallback
1506
+ * 서버 상태 조회 (폴백 포함)
1507
+ *
1508
+ * status 필드 우선, 없으면 connected 필드로 판단
1337
1509
  */
1338
1510
  _getServerStatus(serverName) {
1339
1511
  const server = this.servers.get(serverName);
@@ -1342,10 +1514,16 @@ export class MCPAgentClient extends EventEmitter {
1342
1514
  }
1343
1515
 
1344
1516
  /**
1345
- * Wait for server to be fully ready before querying capabilities
1346
- */
1347
- /**
1348
- * Robust server readiness checking with exponential backoff
1517
+ * 서버 준비 상태 대기
1518
+ *
1519
+ * 문제: 서버가 connect 직후 바로 listTools() 등을 호출하면 실패할 수 있음
1520
+ * 해결: 서버가 완전히 준비될 때까지 재시도하며 대기
1521
+ *
1522
+ * 동작:
1523
+ * 1. listTools()와 getServerCapabilities() 동시 호출
1524
+ * 2. 둘 중 하나라도 성공하면 준비된 것으로 판단
1525
+ * 3. 실패 시 exponential backoff로 재시도
1526
+ * 4. 타임아웃 또는 최대 재시도 횟수 도달 시 포기
1349
1527
  */
1350
1528
  async _waitForServerReady(client, serverName) {
1351
1529
  const maxRetries = this.options.serverReadyRetries;
@@ -1440,11 +1618,13 @@ export class MCPAgentClient extends EventEmitter {
1440
1618
  }
1441
1619
 
1442
1620
  /**
1443
- * Get basic server info for readiness checking
1621
+ * 서버 정보 조회 (에러 무시)
1622
+ *
1623
+ * 준비 상태 확인용으로 서버 capabilities 조회
1624
+ * 실패해도 에러를 던지지 않고 null 반환
1444
1625
  */
1445
1626
  async _getServerInfo(client) {
1446
1627
  try {
1447
- // Try to get server capabilities/info without causing errors
1448
1628
  return await client.getServerCapabilities?.() || null;
1449
1629
  } catch (error) {
1450
1630
  return null;
@@ -1452,47 +1632,55 @@ export class MCPAgentClient extends EventEmitter {
1452
1632
  }
1453
1633
 
1454
1634
  /**
1455
- * Calculate retry delay with proper exponential backoff and bounds
1635
+ * 재시도 지연시간 계산
1636
+ *
1637
+ * Exponential backoff + jitter:
1638
+ * - 기본 지연 * 2^(attempt-1) 로 지수적 증가
1639
+ * - 랜덤 jitter 추가로 thundering herd 방지
1640
+ * - 최대 지연시간 제한
1641
+ *
1642
+ * 예: baseDelay=1000, attempt=3
1643
+ * → 1000 * 2^2 = 4000ms + jitter
1456
1644
  */
1457
1645
  _calculateRetryDelay(attempt) {
1458
1646
  const baseDelay = this.options.retryDelay;
1459
1647
  const maxDelay = this.options.maxRetryDelay;
1460
1648
 
1461
- // Exponential backoff: baseDelay * 2^(attempt-1)
1462
1649
  const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
1463
1650
 
1464
- // Add jitter (configurable % of the delay) to prevent thundering herd
1465
- const jitterPercent = MCPAgentClient._safeParseFloat(process.env.MCP_JITTER_PERCENT, 0.15); // Default 15%
1651
+ const jitterPercent = MCPAgentClient._safeParseFloat(process.env.MCP_JITTER_PERCENT, 0.15);
1466
1652
  const jitter = exponentialDelay * jitterPercent * Math.random();
1467
1653
 
1468
- // Cap at maximum delay
1469
1654
  return Math.min(exponentialDelay + jitter, maxDelay);
1470
1655
  }
1471
1656
 
1472
1657
  /**
1473
- * Check if text looks like JSON that should be parsed
1658
+ * 텍스트가 JSON인지 판단
1659
+ *
1660
+ * 파싱 시도 여부를 결정하기 위한 휴리스틱:
1661
+ * - { } 또는 [ ]로 감싸져 있어야 함
1662
+ * - base64 문자열 제외
1663
+ * - 이모지나 'Error:' 등으로 시작하는 일반 메시지 제외
1474
1664
  */
1475
1665
  _looksLikeJson(text) {
1476
1666
  if (typeof text !== 'string' || text.length === 0) return false;
1477
1667
 
1478
- // Skip if it's likely a base64 encoded string
1479
1668
  if (/^[A-Za-z0-9+/]*={0,2}$/.test(text.trim())) return false;
1480
1669
 
1481
- // Skip if it starts with response indicators (configurable)
1482
1670
  const responseIndicators = this.options.responseIndicators || ['✅', '❌', 'Error:', 'Warning:', 'Info:'];
1483
1671
  if (responseIndicators.some(indicator => text.startsWith(indicator))) return false;
1484
1672
 
1485
- // Only parse if it starts with { or [ (likely JSON)
1486
1673
  const trimmed = text.trim();
1487
1674
  return (trimmed.startsWith('{') && trimmed.endsWith('}')) ||
1488
1675
  (trimmed.startsWith('[') && trimmed.endsWith(']'));
1489
1676
  }
1490
1677
 
1491
1678
  /**
1492
- * 메모리 정리 작업 수행
1493
- */
1494
- /**
1495
- * Safe on-demand memory cleanup - only when needed
1679
+ * 메모리 정리
1680
+ *
1681
+ * 연결 해제된 서버 정보를 정리하여 메모리 누수 방지
1682
+ * interval 대신 on-demand 방식으로 필요시에만 실행
1683
+ * (disconnected 이벤트 발생 시 또는 명시적 호출 시)
1496
1684
  */
1497
1685
  performMemoryCleanup() {
1498
1686
  if (!this.memoryCleanupEnabled) return;
@@ -1544,24 +1732,61 @@ export class MCPAgentClient extends EventEmitter {
1544
1732
  }
1545
1733
  }
1546
1734
 
1735
+ /**
1736
+ * 지연 유틸리티
1737
+ *
1738
+ * Promise 기반 setTimeout으로 async/await에서 사용 가능
1739
+ */
1547
1740
  _delay(ms) {
1548
1741
  return new Promise(resolve => setTimeout(resolve, ms));
1549
1742
  }
1550
1743
 
1744
+ /**
1745
+ * Tool 카테고리 분류
1746
+ *
1747
+ * 향후 Tool을 카테고리별로 분류할 수 있도록 확장 가능
1748
+ * 현재는 모두 'tool'로 반환
1749
+ */
1551
1750
  _categorizeTool(toolName) {
1552
1751
  return 'tool';
1553
1752
  }
1554
1753
  }
1555
1754
 
1755
+ // ========================================
1756
+ // 팩토리 함수들
1757
+ // ========================================
1758
+
1556
1759
  /**
1557
- * 팩토리 함수 - 간편한 인스턴스 생성
1760
+ * MCPAgentClient 인스턴스 생성 헬퍼
1761
+ *
1762
+ * new MCPAgentClient(options)의 간편 버전
1763
+ *
1764
+ * @param options - 클라이언트 설정 옵션
1765
+ * @returns MCPAgentClient 인스턴스 (초기화 전 상태)
1558
1766
  */
1559
1767
  export function createMCPAgent(options = {}) {
1560
1768
  return new MCPAgentClient(options);
1561
1769
  }
1562
1770
 
1563
1771
  /**
1564
- * 빠른 시작 함수 - JSON 설정 객체로 즉시 초기화
1772
+ * 빠른 시작 함수
1773
+ *
1774
+ * 클라이언트 생성 + 초기화를 한 번에 수행
1775
+ * 가장 간단하게 사용할 수 있는 방법
1776
+ *
1777
+ * 사용 예:
1778
+ * const client = await quickStart({
1779
+ * mcpServers: {
1780
+ * "myserver": {
1781
+ * command: "node",
1782
+ * args: ["server.js"]
1783
+ * }
1784
+ * }
1785
+ * });
1786
+ *
1787
+ * @param config - 서버 설정 (mcpServers 객체 포함)
1788
+ * @param options - 클라이언트 옵션
1789
+ * @returns 초기화 완료된 MCPAgentClient 인스턴스
1565
1790
  */
1566
1791
  export async function quickStart(config = {}, options = {}) {
1567
1792
  const agent = new MCPAgentClient(options);