converse-mcp-server 2.3.1 → 2.4.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.
Files changed (42) hide show
  1. package/README.md +771 -738
  2. package/docs/API.md +10 -1
  3. package/docs/PROVIDERS.md +8 -4
  4. package/package.json +12 -12
  5. package/src/async/asyncJobStore.js +82 -52
  6. package/src/async/eventBus.js +25 -20
  7. package/src/async/fileCache.js +121 -40
  8. package/src/async/jobRunner.js +65 -39
  9. package/src/async/providerStreamNormalizer.js +203 -117
  10. package/src/config.js +374 -102
  11. package/src/continuationStore.js +32 -24
  12. package/src/index.js +45 -25
  13. package/src/prompts/helpPrompt.js +328 -305
  14. package/src/providers/anthropic.js +303 -119
  15. package/src/providers/codex.js +103 -45
  16. package/src/providers/deepseek.js +24 -8
  17. package/src/providers/google.js +323 -93
  18. package/src/providers/index.js +1 -1
  19. package/src/providers/interface.js +16 -11
  20. package/src/providers/mistral.js +179 -69
  21. package/src/providers/openai-compatible.js +231 -94
  22. package/src/providers/openai.js +1094 -914
  23. package/src/providers/openrouter-endpoints-client.js +220 -216
  24. package/src/providers/openrouter.js +426 -381
  25. package/src/providers/xai.js +153 -56
  26. package/src/resources/helpResource.js +70 -67
  27. package/src/router.js +95 -67
  28. package/src/services/summarizationService.js +51 -24
  29. package/src/systemPrompts.js +89 -89
  30. package/src/tools/cancelJob.js +31 -19
  31. package/src/tools/chat.js +997 -883
  32. package/src/tools/checkStatus.js +86 -65
  33. package/src/tools/consensus.js +400 -234
  34. package/src/tools/index.js +39 -16
  35. package/src/transport/httpTransport.js +82 -55
  36. package/src/utils/contextProcessor.js +54 -37
  37. package/src/utils/errorHandler.js +95 -45
  38. package/src/utils/fileValidator.js +107 -98
  39. package/src/utils/formatStatus.js +122 -64
  40. package/src/utils/logger.js +459 -449
  41. package/src/utils/pathUtils.js +2 -2
  42. package/src/utils/tokenLimiter.js +216 -216
@@ -19,7 +19,7 @@ import { normalizeExtendedPath } from '../utils/pathUtils.js';
19
19
 
20
20
  // Supported Codex models with their configurations
21
21
  const SUPPORTED_MODELS = {
22
- 'codex': {
22
+ codex: {
23
23
  modelName: 'codex',
24
24
  friendlyName: 'OpenAI Codex (GPT-5)',
25
25
  contextWindow: 400000,
@@ -29,9 +29,10 @@ const SUPPORTED_MODELS = {
29
29
  supportsTemperature: false, // Codex manages temperature internally
30
30
  supportsWebSearch: false, // Codex accesses files directly, not web
31
31
  timeout: 60000, // 60 seconds (Codex can take 5-20s for responses)
32
- description: 'OpenAI Codex agentic coding assistant with local file access and tool execution',
33
- aliases: ['gpt-5-codex', 'gpt5-codex']
34
- }
32
+ description:
33
+ 'OpenAI Codex agentic coding assistant with local file access and tool execution',
34
+ aliases: ['gpt-5-codex', 'gpt5-codex'],
35
+ },
35
36
  };
36
37
 
37
38
  /**
@@ -66,7 +67,7 @@ async function getCodexSDK() {
66
67
  if (!isCodexAvailable()) {
67
68
  throw new CodexProviderError(
68
69
  'Codex SDK not installed. Install with: npm install @openai/codex-sdk',
69
- 'CODEX_NOT_INSTALLED'
70
+ 'CODEX_NOT_INSTALLED',
70
71
  );
71
72
  }
72
73
 
@@ -78,7 +79,7 @@ async function getCodexSDK() {
78
79
  throw new CodexProviderError(
79
80
  'Failed to load Codex SDK',
80
81
  'CODEX_LOAD_ERROR',
81
- error
82
+ error,
82
83
  );
83
84
  }
84
85
  }
@@ -93,18 +94,27 @@ async function getCodexSDK() {
93
94
  */
94
95
  function convertMessagesToPrompt(messages) {
95
96
  if (!Array.isArray(messages)) {
96
- throw new CodexProviderError('Messages must be an array', ErrorCodes.INVALID_MESSAGES);
97
+ throw new CodexProviderError(
98
+ 'Messages must be an array',
99
+ ErrorCodes.INVALID_MESSAGES,
100
+ );
97
101
  }
98
102
 
99
103
  if (messages.length === 0) {
100
- throw new CodexProviderError('Messages array cannot be empty', ErrorCodes.INVALID_MESSAGES);
104
+ throw new CodexProviderError(
105
+ 'Messages array cannot be empty',
106
+ ErrorCodes.INVALID_MESSAGES,
107
+ );
101
108
  }
102
109
 
103
110
  // Find last user message
104
- const lastUserMessage = messages.filter(m => m.role === 'user').pop();
111
+ const lastUserMessage = messages.filter((m) => m.role === 'user').pop();
105
112
 
106
113
  if (!lastUserMessage) {
107
- throw new CodexProviderError('No user message found in messages array', ErrorCodes.INVALID_MESSAGES);
114
+ throw new CodexProviderError(
115
+ 'No user message found in messages array',
116
+ ErrorCodes.INVALID_MESSAGES,
117
+ );
108
118
  }
109
119
 
110
120
  // Extract text content from message
@@ -115,19 +125,26 @@ function convertMessagesToPrompt(messages) {
115
125
  // Handle array content (multimodal format)
116
126
  if (Array.isArray(lastUserMessage.content)) {
117
127
  const textParts = lastUserMessage.content
118
- .filter(item => item.type === 'text')
119
- .map(item => item.text);
128
+ .filter((item) => item.type === 'text')
129
+ .map((item) => item.text);
120
130
 
121
131
  // Log warning if images present (Codex doesn't support images)
122
- const hasImages = lastUserMessage.content.some(item => item.type === 'image');
132
+ const hasImages = lastUserMessage.content.some(
133
+ (item) => item.type === 'image',
134
+ );
123
135
  if (hasImages) {
124
- debugLog('[Codex] Warning: Images in message will be ignored (Codex does not support multimodal input)');
136
+ debugLog(
137
+ '[Codex] Warning: Images in message will be ignored (Codex does not support multimodal input)',
138
+ );
125
139
  }
126
140
 
127
141
  return textParts.join('\n');
128
142
  }
129
143
 
130
- throw new CodexProviderError('Invalid message content format', ErrorCodes.INVALID_MESSAGES);
144
+ throw new CodexProviderError(
145
+ 'Invalid message content format',
146
+ ErrorCodes.INVALID_MESSAGES,
147
+ );
131
148
  }
132
149
 
133
150
  /**
@@ -148,15 +165,26 @@ async function getThreadIdFromContinuation(continuationId, continuationStore) {
148
165
  * Create stream generator for Codex streaming responses
149
166
  * Yields raw Codex SDK events that will be normalized by ProviderStreamNormalizer
150
167
  */
151
- async function* createStreamingGenerator(thread, prompt, signal, runOptions = {}) {
168
+ async function* createStreamingGenerator(
169
+ thread,
170
+ prompt,
171
+ signal,
172
+ runOptions = {},
173
+ ) {
152
174
  try {
153
175
  // Try with runOptions, fallback without if SDK doesn't support them
154
176
  let eventsPromise;
155
177
  try {
156
178
  eventsPromise = thread.runStreamed(prompt, runOptions);
157
179
  } catch (error) {
158
- if (runOptions.reasoningEffort && (error.message?.includes('reasoningEffort') || error.message?.includes('unknown option'))) {
159
- debugLog('[Codex] reasoning_effort not supported by this SDK version for streaming, retrying without it');
180
+ if (
181
+ runOptions.reasoningEffort &&
182
+ (error.message?.includes('reasoningEffort') ||
183
+ error.message?.includes('unknown option'))
184
+ ) {
185
+ debugLog(
186
+ '[Codex] reasoning_effort not supported by this SDK version for streaming, retrying without it',
187
+ );
160
188
  eventsPromise = thread.runStreamed(prompt);
161
189
  } else {
162
190
  throw error;
@@ -202,20 +230,27 @@ export const codexProvider = {
202
230
  continuationStore,
203
231
  reasoning_effort,
204
232
  temperature,
205
- use_websearch
233
+ use_websearch,
206
234
  } = options;
207
235
 
208
236
  // Validate configuration
209
237
  if (!config) {
210
- throw new CodexProviderError('Configuration is required', ErrorCodes.MISSING_API_KEY);
238
+ throw new CodexProviderError(
239
+ 'Configuration is required',
240
+ ErrorCodes.MISSING_API_KEY,
241
+ );
211
242
  }
212
243
 
213
244
  // Log unsupported parameters at debug level
214
245
  if (temperature !== undefined) {
215
- debugLog('[Codex] Parameter "temperature" not supported by Codex (ignored)');
246
+ debugLog(
247
+ '[Codex] Parameter "temperature" not supported by Codex (ignored)',
248
+ );
216
249
  }
217
250
  if (use_websearch) {
218
- debugLog('[Codex] Parameter "use_websearch" not supported by Codex (ignored)');
251
+ debugLog(
252
+ '[Codex] Parameter "use_websearch" not supported by Codex (ignored)',
253
+ );
219
254
  }
220
255
 
221
256
  try {
@@ -226,9 +261,13 @@ export const codexProvider = {
226
261
  const prompt = convertMessagesToPrompt(messages);
227
262
 
228
263
  // Get thread ID if resuming conversation
229
- const threadId = continuation_id && continuationStore
230
- ? await getThreadIdFromContinuation(continuation_id, continuationStore)
231
- : null;
264
+ const threadId =
265
+ continuation_id && continuationStore
266
+ ? await getThreadIdFromContinuation(
267
+ continuation_id,
268
+ continuationStore,
269
+ )
270
+ : null;
232
271
 
233
272
  // Initialize Codex with API key if provided
234
273
  const codexApiKey = config.providers?.codexapikey;
@@ -246,7 +285,10 @@ export const codexProvider = {
246
285
  // Normalize Windows extended-length paths (\\?\C:\...) to regular paths
247
286
  const workingDirectory = normalizeExtendedPath(rawWorkingDirectory);
248
287
  const sandbox = config.providers?.codexsandboxmode || 'read-only';
249
- const skipGitRepoCheck = config.providers?.codexskipgitcheck !== undefined ? config.providers.codexskipgitcheck : true;
288
+ const skipGitRepoCheck =
289
+ config.providers?.codexskipgitcheck !== undefined
290
+ ? config.providers.codexskipgitcheck
291
+ : true;
250
292
  const approvalPolicy = config.providers?.codexapprovalpolicy || 'never';
251
293
 
252
294
  // Create or resume thread
@@ -254,7 +296,7 @@ export const codexProvider = {
254
296
  workingDirectory,
255
297
  sandbox,
256
298
  skipGitRepoCheck,
257
- approvalPolicy
299
+ approvalPolicy,
258
300
  };
259
301
  const thread = threadId
260
302
  ? codex.resumeThread(threadId, threadOptions)
@@ -274,7 +316,12 @@ export const codexProvider = {
274
316
 
275
317
  // Synchronous mode: consume streaming internally and return complete response
276
318
  const startTime = Date.now();
277
- const generator = createStreamingGenerator(thread, prompt, signal, runOptions);
319
+ const generator = createStreamingGenerator(
320
+ thread,
321
+ prompt,
322
+ signal,
323
+ runOptions,
324
+ );
278
325
 
279
326
  let content = '';
280
327
  let usage = null;
@@ -283,13 +330,19 @@ export const codexProvider = {
283
330
  for await (const event of generator) {
284
331
  if (event?.type === 'thread.started') {
285
332
  threadIdFromStream = event.thread_id;
286
- } else if (event?.type === 'item.completed' && event.item?.type === 'agent_message') {
333
+ } else if (
334
+ event?.type === 'item.completed' &&
335
+ event.item?.type === 'agent_message'
336
+ ) {
287
337
  content += event.item.text || '';
288
338
  } else if (event?.type === 'turn.completed') {
289
339
  usage = event.usage;
290
340
  break; // Exit after turn.completed
291
341
  } else if (event?.type === 'turn.failed') {
292
- throw new CodexProviderError(event.error?.message || 'Turn failed', 'TURN_FAILED');
342
+ throw new CodexProviderError(
343
+ event.error?.message || 'Turn failed',
344
+ 'TURN_FAILED',
345
+ );
293
346
  }
294
347
  }
295
348
 
@@ -303,17 +356,19 @@ export const codexProvider = {
303
356
  provider: 'codex',
304
357
  model,
305
358
  threadId: threadIdFromStream || thread.id,
306
- usage: usage ? {
307
- input_tokens: usage.input_tokens || 0,
308
- output_tokens: usage.output_tokens || 0,
309
- total_tokens: (usage.input_tokens || 0) + (usage.output_tokens || 0),
310
- cached_input_tokens: usage.cached_input_tokens || 0
311
- } : null,
359
+ usage: usage
360
+ ? {
361
+ input_tokens: usage.input_tokens || 0,
362
+ output_tokens: usage.output_tokens || 0,
363
+ total_tokens:
364
+ (usage.input_tokens || 0) + (usage.output_tokens || 0),
365
+ cached_input_tokens: usage.cached_input_tokens || 0,
366
+ }
367
+ : null,
312
368
  response_time_ms: responseTime,
313
- finish_reason: 'stop'
314
- }
369
+ finish_reason: 'stop',
370
+ },
315
371
  };
316
-
317
372
  } catch (error) {
318
373
  debugError('[Codex] Execution error', error);
319
374
 
@@ -322,7 +377,7 @@ export const codexProvider = {
322
377
  throw new CodexProviderError(
323
378
  'Codex authentication failed. Ensure ChatGPT login or CODEX_API_KEY is set.',
324
379
  ErrorCodes.INVALID_API_KEY,
325
- error
380
+ error,
326
381
  );
327
382
  }
328
383
 
@@ -330,7 +385,7 @@ export const codexProvider = {
330
385
  throw new CodexProviderError(
331
386
  'Not a Git repository. Use CODEX_SKIP_GIT_CHECK=true or run \'git init\'',
332
387
  'CONFIGURATION_ERROR',
333
- error
388
+ error,
334
389
  );
335
390
  }
336
391
 
@@ -338,7 +393,7 @@ export const codexProvider = {
338
393
  throw new CodexProviderError(
339
394
  'Codex execution timeout',
340
395
  ErrorCodes.TIMEOUT_ERROR,
341
- error
396
+ error,
342
397
  );
343
398
  }
344
399
 
@@ -346,7 +401,7 @@ export const codexProvider = {
346
401
  throw new CodexProviderError(
347
402
  error.message || 'Codex execution failed',
348
403
  ErrorCodes.API_ERROR,
349
- error
404
+ error,
350
405
  );
351
406
  }
352
407
  },
@@ -389,11 +444,14 @@ export const codexProvider = {
389
444
 
390
445
  // Check aliases
391
446
  for (const [_name, config] of Object.entries(SUPPORTED_MODELS)) {
392
- if (config.aliases && config.aliases.some(alias => alias.toLowerCase() === modelNameLower)) {
447
+ if (
448
+ config.aliases &&
449
+ config.aliases.some((alias) => alias.toLowerCase() === modelNameLower)
450
+ ) {
393
451
  return config;
394
452
  }
395
453
  }
396
454
 
397
455
  return null;
398
- }
456
+ },
399
457
  };
@@ -25,8 +25,16 @@ const SUPPORTED_MODELS = {
25
25
  supportsChatPrefixCompletion: true, // Beta
26
26
  supportsFIMCompletion: true, // Beta
27
27
  timeout: 300000,
28
- description: 'DeepSeek-V3-0324 - Strong MoE model with 671B total/37B active parameters',
29
- aliases: ['deepseek', 'chat', 'deepseek chat', 'deepseek-v3', 'deepseek-chat-v3', 'deepseek v3']
28
+ description:
29
+ 'DeepSeek-V3-0324 - Strong MoE model with 671B total/37B active parameters',
30
+ aliases: [
31
+ 'deepseek',
32
+ 'chat',
33
+ 'deepseek chat',
34
+ 'deepseek-v3',
35
+ 'deepseek-chat-v3',
36
+ 'deepseek v3',
37
+ ],
30
38
  },
31
39
  'deepseek-reasoner': {
32
40
  modelName: 'deepseek-reasoner',
@@ -44,9 +52,18 @@ const SUPPORTED_MODELS = {
44
52
  supportsFIMCompletion: false, // Not supported
45
53
  supportsReasoning: true,
46
54
  timeout: 600000, // Longer timeout for reasoning
47
- description: 'DeepSeek-R1-0528 - Advanced reasoning model with CoT capabilities',
48
- aliases: ['deepseek reasoner', 'reasoner', 'r1', 'deepseek r1', 'deepseek-r1', 'deepseek-reasoner-r1', 'deepseek-r1-0528']
49
- }
55
+ description:
56
+ 'DeepSeek-R1-0528 - Advanced reasoning model with CoT capabilities',
57
+ aliases: [
58
+ 'deepseek reasoner',
59
+ 'reasoner',
60
+ 'r1',
61
+ 'deepseek r1',
62
+ 'deepseek-r1',
63
+ 'deepseek-reasoner-r1',
64
+ 'deepseek-r1-0528',
65
+ ],
66
+ },
50
67
  };
51
68
 
52
69
  /**
@@ -97,7 +114,6 @@ export const deepseekProvider = createOpenAICompatibleProvider({
97
114
  // DeepSeek default parameters
98
115
  top_p: 0.95,
99
116
  frequency_penalty: 0,
100
- presence_penalty: 0
101
- }
117
+ presence_penalty: 0,
118
+ },
102
119
  });
103
-