@xagent-ai/cli 1.0.1 → 1.1.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.
Files changed (136) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  3. package/README.md +280 -280
  4. package/README_CN.md +3 -3
  5. package/dist/ai-client.d.ts.map +1 -1
  6. package/dist/ai-client.js +84 -82
  7. package/dist/ai-client.js.map +1 -1
  8. package/dist/auth.d.ts +0 -1
  9. package/dist/auth.d.ts.map +1 -1
  10. package/dist/auth.js +75 -105
  11. package/dist/auth.js.map +1 -1
  12. package/dist/cli.js +166 -13
  13. package/dist/cli.js.map +1 -1
  14. package/dist/config.d.ts +3 -1
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/config.js +48 -7
  17. package/dist/config.js.map +1 -1
  18. package/dist/context-compressor.d.ts +5 -5
  19. package/dist/context-compressor.js +8 -8
  20. package/dist/context-compressor.js.map +1 -1
  21. package/dist/gui-subagent/action-parser/actionParser.d.ts +7 -0
  22. package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
  23. package/dist/gui-subagent/action-parser/actionParser.js +6 -3
  24. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  25. package/dist/gui-subagent/action-parser/constants.d.ts +6 -0
  26. package/dist/gui-subagent/action-parser/constants.d.ts.map +1 -1
  27. package/dist/gui-subagent/action-parser/constants.js +5 -3
  28. package/dist/gui-subagent/action-parser/constants.js.map +1 -1
  29. package/dist/gui-subagent/action-parser/index.d.ts +6 -0
  30. package/dist/gui-subagent/action-parser/index.d.ts.map +1 -1
  31. package/dist/gui-subagent/action-parser/index.js +5 -3
  32. package/dist/gui-subagent/action-parser/index.js.map +1 -1
  33. package/dist/gui-subagent/action-parser/types.d.ts +4 -0
  34. package/dist/gui-subagent/action-parser/types.d.ts.map +1 -1
  35. package/dist/gui-subagent/action-parser/types.js +3 -3
  36. package/dist/gui-subagent/agent/gui-agent.d.ts +39 -0
  37. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  38. package/dist/gui-subagent/agent/gui-agent.js +164 -89
  39. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  40. package/dist/gui-subagent/agent/index.d.ts +1 -1
  41. package/dist/gui-subagent/agent/index.d.ts.map +1 -1
  42. package/dist/gui-subagent/agent/index.js.map +1 -1
  43. package/dist/gui-subagent/index.d.ts +27 -1
  44. package/dist/gui-subagent/index.d.ts.map +1 -1
  45. package/dist/gui-subagent/index.js +6 -0
  46. package/dist/gui-subagent/index.js.map +1 -1
  47. package/dist/logger.js +1 -1
  48. package/dist/logger.js.map +1 -1
  49. package/dist/mcp.d.ts +1 -0
  50. package/dist/mcp.d.ts.map +1 -1
  51. package/dist/mcp.js +140 -29
  52. package/dist/mcp.js.map +1 -1
  53. package/dist/remote-ai-client.d.ts +111 -0
  54. package/dist/remote-ai-client.d.ts.map +1 -0
  55. package/dist/remote-ai-client.js +558 -0
  56. package/dist/remote-ai-client.js.map +1 -0
  57. package/dist/sdk-output-adapter.d.ts +232 -0
  58. package/dist/sdk-output-adapter.d.ts.map +1 -0
  59. package/dist/sdk-output-adapter.js +636 -0
  60. package/dist/sdk-output-adapter.js.map +1 -0
  61. package/dist/sdk-session-v2.d.ts +13 -0
  62. package/dist/sdk-session-v2.d.ts.map +1 -0
  63. package/dist/sdk-session-v2.js +46 -0
  64. package/dist/sdk-session-v2.js.map +1 -0
  65. package/dist/sdk-session.d.ts +13 -0
  66. package/dist/sdk-session.d.ts.map +1 -0
  67. package/dist/sdk-session.js +48 -0
  68. package/dist/sdk-session.js.map +1 -0
  69. package/dist/session-manager.js +3 -3
  70. package/dist/session-manager.js.map +1 -1
  71. package/dist/session.d.ts +46 -3
  72. package/dist/session.d.ts.map +1 -1
  73. package/dist/session.js +564 -117
  74. package/dist/session.js.map +1 -1
  75. package/dist/skill-invoker.d.ts +40 -4
  76. package/dist/skill-invoker.d.ts.map +1 -1
  77. package/dist/skill-invoker.js +310 -1184
  78. package/dist/skill-invoker.js.map +1 -1
  79. package/dist/skill-loader.d.ts +15 -1
  80. package/dist/skill-loader.d.ts.map +1 -1
  81. package/dist/skill-loader.js +49 -32
  82. package/dist/skill-loader.js.map +1 -1
  83. package/dist/slash-commands.d.ts +4 -2
  84. package/dist/slash-commands.d.ts.map +1 -1
  85. package/dist/slash-commands.js +149 -15
  86. package/dist/slash-commands.js.map +1 -1
  87. package/dist/smart-approval.d.ts +2 -1
  88. package/dist/smart-approval.d.ts.map +1 -1
  89. package/dist/smart-approval.js +29 -3
  90. package/dist/smart-approval.js.map +1 -1
  91. package/dist/system-prompt-generator.d.ts +4 -5
  92. package/dist/system-prompt-generator.d.ts.map +1 -1
  93. package/dist/system-prompt-generator.js +131 -81
  94. package/dist/system-prompt-generator.js.map +1 -1
  95. package/dist/tools.d.ts +17 -6
  96. package/dist/tools.d.ts.map +1 -1
  97. package/dist/tools.js +264 -211
  98. package/dist/tools.js.map +1 -1
  99. package/dist/types.d.ts +0 -1
  100. package/dist/types.d.ts.map +1 -1
  101. package/dist/types.js +0 -1
  102. package/dist/types.js.map +1 -1
  103. package/docs/architecture/mcp-integration-guide.md +194 -131
  104. package/docs/architecture/overview.md +169 -93
  105. package/docs/architecture/tool-system-design.md +56 -11
  106. package/docs/cli/commands.md +238 -189
  107. package/docs/smart-mode.md +281 -257
  108. package/docs/third-party-models.md +247 -256
  109. package/package.json +6 -2
  110. package/src/ai-client.ts +107 -105
  111. package/src/auth.ts +82 -116
  112. package/src/cancellation.ts +1 -1
  113. package/src/cli.ts +178 -13
  114. package/src/config.ts +57 -8
  115. package/src/context-compressor.ts +8 -8
  116. package/src/gui-subagent/action-parser/actionParser.ts +6 -3
  117. package/src/gui-subagent/action-parser/constants.ts +5 -3
  118. package/src/gui-subagent/action-parser/index.ts +5 -3
  119. package/src/gui-subagent/action-parser/types.ts +3 -3
  120. package/src/gui-subagent/agent/gui-agent.ts +210 -103
  121. package/src/gui-subagent/agent/index.ts +1 -1
  122. package/src/gui-subagent/index.ts +26 -2
  123. package/src/index.ts +18 -18
  124. package/src/logger.ts +1 -1
  125. package/src/mcp.ts +149 -30
  126. package/src/remote-ai-client.ts +671 -0
  127. package/src/session-manager.ts +3 -3
  128. package/src/session.ts +742 -178
  129. package/src/skill-invoker.ts +340 -1293
  130. package/src/skill-loader.ts +55 -34
  131. package/src/slash-commands.ts +165 -15
  132. package/src/smart-approval.ts +34 -3
  133. package/src/system-prompt-generator.ts +145 -88
  134. package/src/tools.ts +309 -224
  135. package/src/types.ts +0 -1
  136. package/scripts/init-skills-path.js +0 -58
package/src/mcp.ts CHANGED
@@ -12,6 +12,7 @@ export class MCPServer {
12
12
  private process: ChildProcess | null = null;
13
13
  private tools: Map<string, MCPTool> = new Map();
14
14
  private isConnected: boolean = false;
15
+ private sessionId: string | null = null; // Save MCP session-id
15
16
 
16
17
  constructor(config: MCPServerConfig) {
17
18
  this.config = config;
@@ -167,9 +168,15 @@ export class MCPServer {
167
168
  { headers }
168
169
  );
169
170
 
170
- if (response.data.result) {
171
- await this.loadTools(headers);
171
+ // Save session-id for subsequent requests (MCP HTTP protocol requirement)
172
+ const mcpSessionId = response.headers['mcp-session-id'];
173
+ if (mcpSessionId) {
174
+ this.sessionId = mcpSessionId;
172
175
  }
176
+
177
+ // Some MCP servers return SSE-over-HTTP format, so we always call loadTools
178
+ // which handles both regular JSON and SSE format responses
179
+ await this.loadTools(headers);
173
180
  } catch (error: any) {
174
181
  console.error(`HTTP connection failed: ${error.message}`);
175
182
  if (error.response) {
@@ -212,6 +219,12 @@ export class MCPServer {
212
219
  throw new Error(`SSE initialize failed: ${initResponse.status} ${initResponse.statusText}`);
213
220
  }
214
221
 
222
+ // Save session-id (MCP SSE protocol requirement)
223
+ const mcpSessionId = initResponse.headers.get('mcp-session-id');
224
+ if (mcpSessionId) {
225
+ this.sessionId = mcpSessionId;
226
+ }
227
+
215
228
  // For SSE endpoints, try to load tools via a separate POST request
216
229
  await this.loadTools(headers);
217
230
 
@@ -243,10 +256,15 @@ export class MCPServer {
243
256
  }
244
257
  } catch (error: any) {
245
258
  clearTimeout(timeoutId);
259
+ const serverInfo = this.config.url || this.config.command || 'MCP server';
246
260
  if (error.name === 'AbortError') {
247
- console.error('SSE connection timed out');
261
+ console.error(`\n❌ SSE connection timed out`);
262
+ console.error(` Server: ${serverInfo}`);
263
+ console.error(` The server is not responding. Please try again later.`);
248
264
  } else {
249
- console.error(`SSE connection failed: ${error.message}`);
265
+ console.error(`\n❌ SSE connection failed`);
266
+ console.error(` Server: ${serverInfo}`);
267
+ console.error(` ${error.message}`);
250
268
  }
251
269
  throw error;
252
270
  }
@@ -266,8 +284,15 @@ export class MCPServer {
266
284
  }
267
285
 
268
286
  private handleJsonRpcMessage(message: any): void {
269
- if (message.method === 'tools/list') {
287
+ // Handle response format: {id: 1, result: {tools: [...]}}
288
+ if (message.result && message.result.tools) {
270
289
  this.handleToolsList(message.result);
290
+ return;
291
+ }
292
+
293
+ // Handle notification format: {method: 'tools/list', params: {...}}
294
+ if (message.method === 'tools/list') {
295
+ this.handleToolsList(message.params);
271
296
  } else if (message.method === 'notifications/initialized') {
272
297
  console.log('MCP Server initialized');
273
298
  }
@@ -282,6 +307,7 @@ export class MCPServer {
282
307
  private handleToolsList(result: any): void {
283
308
  if (result && result.tools) {
284
309
  for (const tool of result.tools) {
310
+ if (!tool.name || typeof tool.name !== 'string' || tool.name.trim() === '') continue;
285
311
  this.tools.set(tool.name, tool);
286
312
  }
287
313
  console.log(`Loaded ${result.tools.length} tools from MCP Server`);
@@ -297,6 +323,17 @@ export class MCPServer {
297
323
  const axios = (await import('axios')).default;
298
324
 
299
325
  try {
326
+ // Build headers with session-id for MCP protocol
327
+ const requestHeaders: Record<string, string> = {
328
+ 'Content-Type': 'application/json',
329
+ ...this.config.headers,
330
+ ...headers
331
+ };
332
+
333
+ if (this.sessionId) {
334
+ requestHeaders['MCP-session-id'] = this.sessionId;
335
+ }
336
+
300
337
  const response = await axios.post(
301
338
  this.config.url,
302
339
  {
@@ -305,25 +342,27 @@ export class MCPServer {
305
342
  method: 'tools/list'
306
343
  },
307
344
  {
308
- headers: {
309
- 'Content-Type': 'application/json',
310
- ...this.config.headers,
311
- ...headers
312
- },
345
+ headers: requestHeaders,
313
346
  timeout: 10000
314
347
  }
315
348
  );
316
349
 
317
350
  let resultData = response.data;
318
351
 
319
- // Check if response is SSE format (starts with "id:" or "data:")
352
+ // Auto-detect response format (HTTP vs SSE)
353
+ const contentType = response.headers['content-type'] || '';
320
354
  const dataStr = response.data?.toString() || '';
321
- if (dataStr.startsWith('id:') || dataStr.startsWith('data:')) {
355
+
356
+ const isSSE = contentType.includes('text/event-stream') ||
357
+ dataStr.startsWith('id:') ||
358
+ dataStr.startsWith('data:');
359
+
360
+ if (isSSE) {
322
361
  // Parse SSE format: "id:1\nevent:message\ndata:{...}"
323
- const dataMatch = dataStr.match(/data:(\{.*\})/);
362
+ const dataMatch = dataStr.match(/data:(.+)$/m);
324
363
  if (dataMatch) {
325
364
  try {
326
- resultData = JSON.parse(dataMatch[1]);
365
+ resultData = JSON.parse(dataMatch[1].trim());
327
366
  } catch (e: any) {
328
367
  console.error(`Failed to parse SSE data: ${e.message}`);
329
368
  }
@@ -332,11 +371,17 @@ export class MCPServer {
332
371
 
333
372
  if (resultData?.result?.tools) {
334
373
  this.handleToolsList(resultData.result);
374
+ } else if (resultData?.tools) {
375
+ this.handleToolsList(resultData);
335
376
  } else if (resultData?.error) {
336
- console.error(`MCP tools/list error: ${resultData.error.message}`);
377
+ console.error(`\n❌ MCP server returned an error`);
378
+ console.error(` ${resultData.error.message || 'Unknown error'}`);
337
379
  }
338
380
  } catch (error: any) {
339
- console.error(`Failed to load MCP tools: ${error.message}`);
381
+ const serverInfo = this.config.url || this.config.command || 'MCP server';
382
+ console.error(`\n❌ Failed to load MCP tools`);
383
+ console.error(` Server: ${serverInfo}`);
384
+ console.error(` ${error.message}`);
340
385
  }
341
386
  }
342
387
 
@@ -371,15 +416,67 @@ export class MCPServer {
371
416
  const axios = (await import('axios')).default;
372
417
 
373
418
  try {
419
+ // Build headers with auth token
420
+ const headers: Record<string, string> = {
421
+ 'Content-Type': 'application/json',
422
+ 'Accept': 'application/json, text/event-stream',
423
+ ...this.config.headers
424
+ };
425
+
426
+ if (this.config.authToken) {
427
+ if (this.config.authToken.startsWith('Bearer ')) {
428
+ headers['Authorization'] = this.config.authToken;
429
+ } else {
430
+ headers['Authorization'] = `Bearer ${this.config.authToken}`;
431
+ }
432
+ }
433
+
434
+ // Add session-id to request headers (MCP HTTP protocol requirement)
435
+ if (this.sessionId) {
436
+ headers['MCP-session-id'] = this.sessionId;
437
+ }
438
+
374
439
  const response = await axios.post(this.config.url!, message, {
375
- headers: {
376
- 'Content-Type': 'application/json',
377
- ...this.config.headers
378
- },
440
+ headers,
379
441
  timeout: this.config.timeout || 30000
380
442
  });
381
443
 
382
- return response.data.result;
444
+ // Update session-id if new one provided in response
445
+ const responseSessionId = response.headers['mcp-session-id'];
446
+ if (responseSessionId) {
447
+ this.sessionId = responseSessionId;
448
+ }
449
+
450
+ // Auto-detect response format (HTTP vs SSE)
451
+ const contentType = response.headers['content-type'] || '';
452
+ let resultData;
453
+
454
+ if (contentType.includes('text/event-stream') ||
455
+ (typeof response.data === 'string' && response.data.startsWith('id:'))) {
456
+ // Parse SSE format: "id:1\nevent:message\ndata:{...}"
457
+ const sseData = response.data;
458
+ const dataMatch = sseData.match(/data:(.+)$/m);
459
+ if (dataMatch) {
460
+ try {
461
+ resultData = JSON.parse(dataMatch[1].trim());
462
+ } catch (e: any) {
463
+ throw new Error(`Failed to parse SSE data: ${e.message}`);
464
+ }
465
+ } else {
466
+ throw new Error('No data field found in SSE response');
467
+ }
468
+ } else {
469
+ // Direct JSON response
470
+ resultData = response.data;
471
+ }
472
+
473
+ // Check for error response
474
+ if (resultData?.isError) {
475
+ const errorMsg = resultData?.content?.[0]?.text || 'Unknown error';
476
+ throw new Error(`MCP server error: ${errorMsg}`);
477
+ }
478
+
479
+ return resultData?.result;
383
480
  } catch (error: any) {
384
481
  throw new Error(`MCP Tool call failed: ${error.message}`);
385
482
  }
@@ -398,18 +495,31 @@ export class MCPServer {
398
495
 
399
496
  const responseHandler = (data: Buffer) => {
400
497
  try {
401
- const response = JSON.parse(data.toString());
498
+ const rawResponse = data.toString();
499
+ const response = JSON.parse(rawResponse);
500
+
501
+ if (process.env.DEBUG === 'mcp' || process.env.DEBUG === 'all') {
502
+ console.log('\n========== MCP STDIO Raw Response ==========');
503
+ console.log('Raw:', rawResponse);
504
+ console.log('Parsed:', JSON.stringify(response, null, 2));
505
+ console.log('==========================================\n');
506
+ }
507
+
402
508
  if (response.id === message.id) {
403
509
  clearTimeout(timeout);
404
510
  this.process?.stdout?.off('data', responseHandler);
405
511
 
406
512
  if (response.error) {
407
- reject(new Error(response.error.message));
513
+ reject(new Error(`MCP tool error: ${response.error.message || 'Unknown error'}`));
408
514
  } else {
409
515
  resolve(response.result);
410
516
  }
411
517
  }
412
518
  } catch (error) {
519
+ console.error('\n========== MCP STDIO Parse Error ==========');
520
+ console.error('Raw data:', data.toString());
521
+ console.error('Error:', error);
522
+ console.error('==========================================\n');
413
523
  reject(error);
414
524
  }
415
525
  };
@@ -452,7 +562,7 @@ export class MCPManager {
452
562
  async connectServer(name: string): Promise<void> {
453
563
  const server = this.servers.get(name);
454
564
  if (!server) {
455
- throw new Error(`MCP Server not found: ${name}`);
565
+ throw new Error(`MCP server not found: ${name}. Please check the server name and try again.`);
456
566
  }
457
567
  await server.connect();
458
568
  }
@@ -463,7 +573,7 @@ export class MCPManager {
463
573
  try {
464
574
  await server.connect();
465
575
  } catch (error) {
466
- console.error(`Failed to connect MCP Server ${name}:`, error);
576
+ console.error(`Failed to connect MCP server ${name}:`, error);
467
577
  }
468
578
  }
469
579
  );
@@ -506,6 +616,7 @@ export class MCPManager {
506
616
 
507
617
  this.servers.forEach((server, serverName) => {
508
618
  server.getTools().forEach(tool => {
619
+ if (!tool.name) return;
509
620
  allTools.set(`${serverName}__${tool.name}`, tool);
510
621
  });
511
622
  });
@@ -514,11 +625,13 @@ export class MCPManager {
514
625
  }
515
626
 
516
627
  async callTool(toolName: string, params: any): Promise<any> {
517
- const [serverName, actualToolName] = toolName.split('__');
518
-
519
- if (!serverName || !actualToolName) {
628
+ // Split only on the first __ to preserve underscores in tool names
629
+ const firstUnderscoreIndex = toolName.indexOf('__');
630
+ if (firstUnderscoreIndex === -1) {
520
631
  throw new Error(`Invalid tool name format: ${toolName}`);
521
632
  }
633
+ const serverName = toolName.substring(0, firstUnderscoreIndex);
634
+ const actualToolName = toolName.substring(firstUnderscoreIndex + 2);
522
635
 
523
636
  const server = this.servers.get(serverName);
524
637
  if (!server) {
@@ -538,12 +651,18 @@ export class MCPManager {
538
651
 
539
652
  this.servers.forEach((server, serverName) => {
540
653
  server.getTools().forEach(tool => {
654
+ if (!tool.name) return;
655
+
541
656
  tools.push({
542
657
  type: 'function',
543
658
  function: {
544
659
  name: `${serverName}__${tool.name}`,
545
- description: tool.description,
546
- parameters: tool.inputSchema
660
+ description: tool.description || `MCP tool: ${tool.name}`,
661
+ parameters: tool.inputSchema || {
662
+ type: 'object',
663
+ properties: {},
664
+ required: []
665
+ }
547
666
  }
548
667
  });
549
668
  });