converse-mcp-server 2.8.0 → 2.8.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "converse-mcp-server",
3
- "version": "2.8.0",
3
+ "version": "2.8.2",
4
4
  "description": "Converse MCP Server - Converse with other LLMs with chat and consensus tools",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -94,11 +94,12 @@
94
94
  ".env.example"
95
95
  ],
96
96
  "dependencies": {
97
+ "@anthropic-ai/claude-agent-sdk": "^0.1.51",
97
98
  "@anthropic-ai/sdk": "^0.70.0",
98
99
  "@google/genai": "^1.30.0",
99
100
  "@mistralai/mistralai": "^1.10.0",
100
101
  "@modelcontextprotocol/sdk": "^1.22.0",
101
- "@openai/codex-sdk": "^0.58.0",
102
+ "@openai/codex-sdk": "^0.63.0",
102
103
  "ai": "^5.0.101",
103
104
  "ai-sdk-provider-gemini-cli": "^1.4.0",
104
105
  "cors": "^2.8.5",
@@ -95,7 +95,7 @@ export function generateHelpContent(config = null) {
95
95
  return `\`\`\`json
96
96
  {
97
97
  "prompt": "Should we use microservices architecture for our new project?",
98
- "models": ["gpt-5", "gemini-pro", "grok-4"],
98
+ "models": ["codex", "gemini", "claude"],
99
99
  "files": ["./requirements.md", "C:\\\\Users\\\\username\\\\architecture.md"],
100
100
  "enable_cross_feedback": true,
101
101
  "temperature": 0.3
@@ -24,7 +24,7 @@ const SUPPORTED_MODELS = {
24
24
  contextWindow: 200000,
25
25
  maxOutputTokens: 8192,
26
26
  supportsStreaming: true,
27
- supportsImages: false, // SDK has limited image support
27
+ supportsImages: true, // Supported via streaming input mode
28
28
  supportsTemperature: false, // SDK manages temperature internally
29
29
  supportsWebSearch: false, // SDK accesses files directly, not web
30
30
  timeout: 120000, // 2 minutes
@@ -83,14 +83,19 @@ async function getClaudeSDK() {
83
83
  }
84
84
 
85
85
  /**
86
- * Convert message array to single prompt for Claude SDK
87
- * Claude SDK expects single prompts, not message history
86
+ * Convert message array to SDK input format
87
+ * Claude SDK supports two modes:
88
+ * 1. Single message mode (string) - simpler, but no image support
89
+ * 2. Streaming input mode (AsyncGenerator) - supports images
88
90
  *
89
91
  * Strategy:
90
- * - Extract last user message only
91
- * - Handle both string and multimodal content formats
92
+ * - Extract last user message
93
+ * - If message contains images, use streaming input mode
94
+ * - Otherwise, return string prompt for single message mode
95
+ *
96
+ * @returns {Object} { prompt: string | null, sdkMessage: Object | null, hasImages: boolean }
92
97
  */
93
- function convertMessagesToPrompt(messages) {
98
+ function convertMessagesToSdkInput(messages) {
94
99
  if (!Array.isArray(messages)) {
95
100
  throw new ClaudeProviderError(
96
101
  'Messages must be an array',
@@ -117,26 +122,66 @@ function convertMessagesToPrompt(messages) {
117
122
 
118
123
  // Extract text content from message
119
124
  if (typeof lastUserMessage.content === 'string') {
120
- return lastUserMessage.content;
125
+ return {
126
+ prompt: lastUserMessage.content,
127
+ sdkMessage: null,
128
+ hasImages: false,
129
+ };
121
130
  }
122
131
 
123
132
  // Handle array content (multimodal format)
124
133
  if (Array.isArray(lastUserMessage.content)) {
125
- const textParts = lastUserMessage.content
126
- .filter((item) => item.type === 'text')
127
- .map((item) => item.text);
128
-
129
- // Log warning if images present (Claude SDK has limited image support)
134
+ // Check if message contains images
130
135
  const hasImages = lastUserMessage.content.some(
131
136
  (item) => item.type === 'image',
132
137
  );
138
+
133
139
  if (hasImages) {
140
+ // Use streaming input mode for images
141
+ // Convert to SDK message format
142
+ const sdkContent = lastUserMessage.content.map((item) => {
143
+ if (item.type === 'text') {
144
+ return {
145
+ type: 'text',
146
+ text: item.text,
147
+ };
148
+ } else if (item.type === 'image') {
149
+ // SDK expects Anthropic image format
150
+ return {
151
+ type: 'image',
152
+ source: item.source,
153
+ };
154
+ }
155
+ return item;
156
+ });
157
+
134
158
  debugLog(
135
- '[Claude SDK] Warning: Images in message will be ignored (Claude SDK does not support multimodal input)',
159
+ `[Claude SDK] Using streaming input mode for multimodal content (${lastUserMessage.content.filter((i) => i.type === 'image').length} images)`,
136
160
  );
161
+
162
+ return {
163
+ prompt: null,
164
+ sdkMessage: {
165
+ type: 'user',
166
+ message: {
167
+ role: 'user',
168
+ content: sdkContent,
169
+ },
170
+ },
171
+ hasImages: true,
172
+ };
137
173
  }
138
174
 
139
- return textParts.join('\n');
175
+ // No images - extract text only
176
+ const textParts = lastUserMessage.content
177
+ .filter((item) => item.type === 'text')
178
+ .map((item) => item.text);
179
+
180
+ return {
181
+ prompt: textParts.join('\n'),
182
+ sdkMessage: null,
183
+ hasImages: false,
184
+ };
140
185
  }
141
186
 
142
187
  throw new ClaudeProviderError(
@@ -145,6 +190,14 @@ function convertMessagesToPrompt(messages) {
145
190
  );
146
191
  }
147
192
 
193
+ /**
194
+ * Create an async generator that yields a single SDK user message
195
+ * This is required for streaming input mode (image support)
196
+ */
197
+ async function* createSdkMessageGenerator(sdkMessage) {
198
+ yield sdkMessage;
199
+ }
200
+
148
201
  /**
149
202
  * Create stream generator for Claude SDK streaming responses
150
203
  * Yields normalized events compatible with ProviderStreamNormalizer
@@ -153,12 +206,25 @@ function convertMessagesToPrompt(messages) {
153
206
  * - system (subtype: init): Session initialization
154
207
  * - assistant: Model responses with message.content
155
208
  * - result (subtype: success/error_*): Final results with usage
209
+ *
210
+ * @param {Function} queryFn - The SDK query function
211
+ * @param {string|null} prompt - String prompt for single message mode, or null for streaming input mode
212
+ * @param {Object|null} sdkMessage - SDK user message for streaming input mode (with images)
213
+ * @param {Object} options - SDK options (cwd, etc.)
214
+ * @param {AbortSignal} signal - Abort signal for cancellation
156
215
  */
157
- async function* createStreamingGenerator(queryFn, prompt, options, signal) {
216
+ async function* createStreamingGenerator(
217
+ queryFn,
218
+ prompt,
219
+ sdkMessage,
220
+ options,
221
+ signal,
222
+ ) {
158
223
  try {
159
224
  // Build query options
225
+ // Use higher maxTurns to allow for file reading operations
160
226
  const queryOptions = {
161
- maxTurns: 1, // Single turn for chat
227
+ maxTurns: 10, // Allow multiple turns for file operations
162
228
  permissionMode: 'bypassPermissions', // Don't prompt for permissions
163
229
  };
164
230
 
@@ -179,9 +245,16 @@ async function* createStreamingGenerator(queryFn, prompt, options, signal) {
179
245
  });
180
246
  }
181
247
 
248
+ // Determine input mode based on whether we have an SDK message (with images) or plain prompt
249
+ // - Streaming input mode: prompt is AsyncGenerator<SDKUserMessage> - required for images
250
+ // - Single message mode: prompt is string - simpler but no image support
251
+ const queryInput = sdkMessage
252
+ ? createSdkMessageGenerator(sdkMessage) // Streaming input mode for images
253
+ : prompt; // Single message mode for text-only
254
+
182
255
  // Create query generator
183
256
  const response = queryFn({
184
- prompt,
257
+ prompt: queryInput,
185
258
  options: queryOptions,
186
259
  });
187
260
 
@@ -330,8 +403,15 @@ export const claudeProvider = {
330
403
  // Get Claude SDK
331
404
  const query = await getClaudeSDK();
332
405
 
333
- // Convert messages to prompt
334
- const prompt = convertMessagesToPrompt(messages);
406
+ // Convert messages to SDK input format
407
+ // Returns { prompt, sdkMessage, hasImages }
408
+ // - prompt: string for single message mode (text-only)
409
+ // - sdkMessage: SDK user message for streaming input mode (with images)
410
+ const { prompt, sdkMessage, hasImages } = convertMessagesToSdkInput(messages);
411
+
412
+ if (hasImages) {
413
+ debugLog('[Claude SDK] Using streaming input mode for image support');
414
+ }
335
415
 
336
416
  // Build SDK options
337
417
  const sdkOptions = {
@@ -340,7 +420,13 @@ export const claudeProvider = {
340
420
 
341
421
  // Streaming mode
342
422
  if (stream) {
343
- return createStreamingGenerator(query, prompt, sdkOptions, signal);
423
+ return createStreamingGenerator(
424
+ query,
425
+ prompt,
426
+ sdkMessage,
427
+ sdkOptions,
428
+ signal,
429
+ );
344
430
  }
345
431
 
346
432
  // Synchronous mode: consume streaming internally and return complete response
@@ -348,6 +434,7 @@ export const claudeProvider = {
348
434
  const generator = createStreamingGenerator(
349
435
  query,
350
436
  prompt,
437
+ sdkMessage,
351
438
  sdkOptions,
352
439
  signal,
353
440
  );
package/src/tools/chat.js CHANGED
@@ -270,20 +270,35 @@ export async function chatTool(args, dependencies) {
270
270
  let providerName;
271
271
 
272
272
  if (model === 'auto') {
273
- // Auto-select first available provider
274
- const availableProviders = Object.keys(providers).filter((name) => {
273
+ // Auto-select first available provider in priority order
274
+ // Prioritize subscription-based providers (codex, gemini-cli, claude) over API-key providers
275
+ const providerOrder = [
276
+ 'codex',
277
+ 'gemini-cli',
278
+ 'claude',
279
+ 'openai',
280
+ 'google',
281
+ 'xai',
282
+ 'anthropic',
283
+ 'mistral',
284
+ 'deepseek',
285
+ 'openrouter',
286
+ ];
287
+
288
+ for (const name of providerOrder) {
275
289
  const provider = providers[name];
276
- return provider && provider.isAvailable && provider.isAvailable(config);
277
- });
290
+ if (provider && provider.isAvailable && provider.isAvailable(config)) {
291
+ providerName = name;
292
+ selectedProvider = provider;
293
+ break;
294
+ }
295
+ }
278
296
 
279
- if (availableProviders.length === 0) {
297
+ if (!providerName) {
280
298
  return createToolError(
281
299
  'No providers available. Please configure at least one API key.',
282
300
  );
283
301
  }
284
-
285
- providerName = availableProviders[0];
286
- selectedProvider = providers[providerName];
287
302
  } else {
288
303
  // Use specified provider/model
289
304
  // Try to map model to provider
@@ -998,7 +1013,7 @@ chatTool.inputSchema = {
998
1013
  model: {
999
1014
  type: 'string',
1000
1015
  description:
1001
- 'AI model to use. Examples: "auto" (recommended), "codex", "gemini", "gpt-5", "grok-4-0709". Defaults to auto-selection.',
1016
+ 'AI model to use. Examples: "auto" (recommended), "codex", "gemini", "claude", "gpt-5", "grok-4-0709". Defaults to auto-selection.',
1002
1017
  },
1003
1018
  files: {
1004
1019
  type: 'array',
@@ -275,8 +275,12 @@ export async function consensusTool(args, dependencies) {
275
275
  let modelsToProcess = models;
276
276
  if (models.length === 1 && models[0].toLowerCase() === 'auto') {
277
277
  // Find first 3 available providers
278
+ // Prioritize subscription-based providers (codex, gemini-cli, claude) over API-key providers
278
279
  const availableProviders = [];
279
280
  const providerOrder = [
281
+ 'codex',
282
+ 'gemini-cli',
283
+ 'claude',
280
284
  'openai',
281
285
  'google',
282
286
  'xai',
@@ -980,8 +984,12 @@ async function executeConsensusWithStreaming(args, dependencies, context) {
980
984
  // Special handling for single "auto" model - expand to first 3 available providers
981
985
  let modelsToProcess = models;
982
986
  if (models.length === 1 && models[0].toLowerCase() === 'auto') {
987
+ // Prioritize subscription-based providers (codex, gemini-cli, claude) over API-key providers
983
988
  const availableProviders = [];
984
989
  const providerOrder = [
990
+ 'codex',
991
+ 'gemini-cli',
992
+ 'claude',
985
993
  'openai',
986
994
  'google',
987
995
  'xai',
@@ -1609,7 +1617,7 @@ consensusTool.inputSchema = {
1609
1617
  items: { type: 'string' },
1610
1618
  minItems: 1,
1611
1619
  description:
1612
- 'List of models to consult. Example: ["gpt-5", "gemini-pro", "grok-4-0709"]',
1620
+ 'List of models to consult. Example: ["codex", "gemini", "claude"]',
1613
1621
  },
1614
1622
  files: {
1615
1623
  type: 'array',