converse-mcp-server 1.14.4 → 1.15.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/.env.example CHANGED
@@ -48,4 +48,11 @@ OPENROUTER_API_KEY=your_openrouter_api_key_here
48
48
  # OpenRouter requires a referer for compliance (your app URL or GitHub repo)
49
49
  OPENROUTER_REFERER=https://github.com/FallDownTheSystem/converse
50
50
 
51
+ # ============================================
52
+ # Server Configuration
53
+ # ============================================
54
+
55
+ # Disable async execution tools (check_status, cancel_job) and async parameter
56
+ # When true, removes async capabilities from chat/consensus tools
57
+ # DISABLE_ASYNC_TOOLS=false
51
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "converse-mcp-server",
3
- "version": "1.14.4",
3
+ "version": "1.15.1",
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",
package/src/config.js CHANGED
@@ -112,6 +112,11 @@ const CONFIG_SCHEMA = {
112
112
  ENABLE_RESPONSE_SUMMARIZATION: { type: 'boolean', default: false, description: 'Enable AI-powered response summarization for async operations' },
113
113
  SUMMARIZATION_MODEL: { type: 'string', default: 'gpt-5-nano', description: 'Model to use for summarization tasks (title generation, streaming summaries, final summaries)' },
114
114
  },
115
+
116
+ // Async tools configuration
117
+ async: {
118
+ DISABLE_ASYNC_TOOLS: { type: 'boolean', default: false, description: 'Disable async execution support (removes async parameter from tools and status check tools)' },
119
+ },
115
120
  };
116
121
 
117
122
  // ConfigurationError now imported from errorHandler
@@ -318,6 +323,19 @@ export async function loadConfig() {
318
323
  }
319
324
  }
320
325
 
326
+ // Load Async tools configuration
327
+ config.async = {};
328
+ for (const [key, schema] of Object.entries(CONFIG_SCHEMA.async)) {
329
+ try {
330
+ const value = validateEnvVar(key, process.env[key], schema);
331
+ if (key === 'DISABLE_ASYNC_TOOLS') {
332
+ config.async.disableAsyncTools = value;
333
+ }
334
+ } catch (error) {
335
+ errors.push(error.message);
336
+ }
337
+ }
338
+
321
339
  // Load name and version from package.json
322
340
  try {
323
341
  const packagePath = join(dirname(fileURLToPath(import.meta.url)), '../package.json');
@@ -9,8 +9,9 @@ import { getTools } from '../tools/index.js';
9
9
 
10
10
  /**
11
11
  * Generate comprehensive help content dynamically based on current providers
12
+ * @param {object} config - Configuration object (optional)
12
13
  */
13
- export function generateHelpContent() {
14
+ export function generateHelpContent(config = null) {
14
15
  const providers = getProviders();
15
16
 
16
17
  // Collect all models from all providers
@@ -58,7 +59,7 @@ export function generateHelpContent() {
58
59
  };
59
60
 
60
61
  // Get tools and format their documentation
61
- const tools = getTools();
62
+ const tools = getTools(config);
62
63
  const formatToolParameters = (inputSchema) => {
63
64
  if (!inputSchema || !inputSchema.properties) return '';
64
65
 
@@ -235,11 +236,13 @@ Note: Server name and version are automatically read from package.json.
235
236
 
236
237
  /**
237
238
  * Help prompt handler function
239
+ * @param {object} args - Prompt arguments
240
+ * @param {object} config - Configuration object (optional)
238
241
  */
239
- export async function helpPromptHandler(args = {}) {
242
+ export async function helpPromptHandler(args = {}, config = null) {
240
243
  const { topic } = args;
241
244
 
242
- const fullHelp = generateHelpContent();
245
+ const fullHelp = generateHelpContent(config);
243
246
 
244
247
  // If no topic specified, return full help
245
248
  if (!topic) {
@@ -41,7 +41,7 @@ const SUPPORTED_MODELS = {
41
41
  aliases: ['flashlite', 'flash-lite', 'flash lite', 'flash-lite-2.0', 'gemini flash lite', 'gemini-2.0-flash-lite-latest']
42
42
  },
43
43
  'gemini-2.5-flash': {
44
- modelName: 'gemini-2.5-flash',
44
+ modelName: 'gemini-flash-latest',
45
45
  friendlyName: 'Gemini (Flash 2.5)',
46
46
  contextWindow: 1048576, // 1M tokens
47
47
  maxOutputTokens: 65536,
@@ -53,7 +53,22 @@ const SUPPORTED_MODELS = {
53
53
  maxThinkingTokens: 24576,
54
54
  timeout: 300000,
55
55
  description: 'Ultra-fast (1M context) - Quick analysis, simple queries, rapid iterations with grounding',
56
- aliases: ['flash', 'flash2.5', 'gemini-flash', 'gemini-flash-2.5', 'flash 2.5', 'gemini flash 2.5', 'gemini-2.5-flash-latest']
56
+ aliases: ['flash', 'flash2.5', 'gemini-flash', 'gemini-flash-2.5', 'flash 2.5', 'gemini flash 2.5', 'gemini-2.5-flash', 'gemini-2.5-flash-preview-09-2025', 'gemini-2.5-flash-latest']
57
+ },
58
+ 'gemini-2.5-flash-lite': {
59
+ modelName: 'gemini-flash-lite-latest',
60
+ friendlyName: 'Gemini (Flash Lite 2.5)',
61
+ contextWindow: 1048576, // 1M tokens
62
+ maxOutputTokens: 65536,
63
+ supportsStreaming: true,
64
+ supportsImages: true,
65
+ supportsTemperature: true,
66
+ supportsThinking: true,
67
+ supportsWebSearch: true,
68
+ maxThinkingTokens: 24576,
69
+ timeout: 300000,
70
+ description: 'Lightweight fast model (1M context) - Efficient quick responses with grounding',
71
+ aliases: ['flashlite2.5', 'flash-lite', 'flash lite', 'gemini-flash-lite', 'gemini flash lite', 'gemini-2.5-flash-lite-preview-09-2025', 'gemini-2.5-flash-lite-latest']
57
72
  },
58
73
  'gemini-2.5-pro': {
59
74
  modelName: 'gemini-2.5-pro',
@@ -389,9 +389,9 @@ export const openaiProvider = {
389
389
 
390
390
  // Add reasoning effort for thinking models (o3 series and GPT-5 family)
391
391
  if ((resolvedModel.startsWith('o3') || resolvedModel.startsWith('gpt-5')) && reasoning_effort) {
392
- requestPayload.reasoning = {
392
+ requestPayload.reasoning = {
393
393
  effort: reasoning_effort,
394
- summary: "auto" // Enable reasoning summaries
394
+ summary: 'auto' // Enable reasoning summaries
395
395
  };
396
396
  }
397
397
 
@@ -483,23 +483,23 @@ export const openaiProvider = {
483
483
  if (shouldUseResponsesAPI) {
484
484
  // Handle Responses API response format
485
485
  let reasoningSummary = null;
486
-
486
+
487
487
  if (response.output) {
488
488
  // New format with output array (includes reasoning summaries)
489
489
  const messageOutput = response.output.find(item => item.type === 'message');
490
490
  const reasoningOutput = response.output.find(item => item.type === 'reasoning');
491
-
491
+
492
492
  if (!messageOutput || !messageOutput.content) {
493
493
  throw new OpenAIProviderError('No message content in Responses API response', 'NO_RESPONSE_CONTENT');
494
494
  }
495
-
495
+
496
496
  // Extract content from message output
497
497
  const textContent = messageOutput.content.find(item => item.type === 'output_text');
498
498
  if (!textContent) {
499
499
  throw new OpenAIProviderError('No text content in message output', 'NO_RESPONSE_CONTENT');
500
500
  }
501
501
  content = textContent.text;
502
-
502
+
503
503
  // Extract reasoning summary if available
504
504
  if (reasoningOutput && reasoningOutput.summary) {
505
505
  const summaryText = reasoningOutput.summary.find(item => item.type === 'summary_text');
@@ -513,17 +513,17 @@ export const openaiProvider = {
513
513
  } else {
514
514
  throw new OpenAIProviderError('No output in Responses API response', 'NO_RESPONSE_CONTENT');
515
515
  }
516
-
516
+
517
517
  stopReason = response.status || 'stop';
518
518
  usage = response.usage || {};
519
-
519
+
520
520
  // Store reasoning summary in metadata
521
521
  if (reasoningSummary) {
522
522
  usage.reasoning_summary = reasoningSummary;
523
523
  debugLog(`[OpenAI] Found reasoning summary: ${reasoningSummary.substring(0, 100)}...`);
524
524
  } else {
525
- debugLog(`[OpenAI] No reasoning summary found in response`);
526
- debugLog(`[OpenAI] Response structure:`, JSON.stringify(response, null, 2).substring(0, 500));
525
+ debugLog('[OpenAI] No reasoning summary found in response');
526
+ debugLog('[OpenAI] Response structure:', JSON.stringify(response, null, 2).substring(0, 500));
527
527
  }
528
528
  } else {
529
529
  // Handle Chat Completions API response format
@@ -652,7 +652,7 @@ export const openaiProvider = {
652
652
  break;
653
653
  }
654
654
  if (shouldUseResponsesAPI) {
655
-
655
+
656
656
  // Handle Responses API streaming format
657
657
  if (chunk.type === 'response.output_text.delta') {
658
658
  const content = chunk.delta || '';
@@ -666,14 +666,14 @@ export const openaiProvider = {
666
666
  }
667
667
  } else if (chunk.type === 'response.reasoning_summary_part.added') {
668
668
  // Event 1: reasoning summary part added (usually empty initially)
669
- debugLog(`[OpenAI] *** REASONING PART ADDED`);
669
+ debugLog('[OpenAI] *** REASONING PART ADDED');
670
670
  } else if (chunk.type === 'response.reasoning_summary_part.done') {
671
671
  // Event 2: reasoning summary part completed with full text
672
672
  const summaryText = chunk.part?.text || '';
673
673
  if (summaryText) {
674
674
  totalReasoningSummary = summaryText;
675
675
  debugLog(`[OpenAI] *** REASONING PART DONE: "${summaryText.substring(0, 100)}..."`);
676
-
676
+
677
677
  yield {
678
678
  type: 'reasoning_summary',
679
679
  content: totalReasoningSummary,
@@ -686,7 +686,7 @@ export const openaiProvider = {
686
686
  if (summaryDelta) {
687
687
  totalReasoningSummary += summaryDelta;
688
688
  debugLog(`[OpenAI] *** REASONING TEXT DELTA: "${summaryDelta}"`);
689
-
689
+
690
690
  yield {
691
691
  type: 'reasoning_summary',
692
692
  content: totalReasoningSummary,
@@ -699,7 +699,7 @@ export const openaiProvider = {
699
699
  if (fullSummary) {
700
700
  totalReasoningSummary = fullSummary;
701
701
  debugLog(`[OpenAI] *** REASONING TEXT DONE: "${fullSummary.substring(0, 100)}..."`);
702
-
702
+
703
703
  yield {
704
704
  type: 'reasoning_summary',
705
705
  content: fullSummary,
@@ -68,6 +68,19 @@ const SUPPORTED_MODELS = {
68
68
  timeout: 300000,
69
69
  description: 'Auto-selects the best model for your prompt using NotDiamond routing',
70
70
  aliases: ['openrouter auto', 'auto router', 'auto-router', 'openrouter-auto']
71
+ },
72
+ 'z-ai/glm-4.6': {
73
+ modelName: 'z-ai/glm-4.6',
74
+ friendlyName: 'Z.AI GLM 4.6 (via OpenRouter)',
75
+ contextWindow: 202752,
76
+ maxOutputTokens: 8192,
77
+ supportsStreaming: true,
78
+ supportsImages: false,
79
+ supportsTemperature: true,
80
+ supportsWebSearch: false,
81
+ timeout: 300000,
82
+ description: 'Z.AI GLM 4.6 with 200K context - improved coding, reasoning, and agent performance',
83
+ aliases: ['glm-4.6', 'glm4.6', 'glm 4.6', 'z-ai glm', 'z-ai-glm', 'zai-glm']
71
84
  }
72
85
  };
73
86
 
@@ -39,10 +39,11 @@ export const helpResourceMetadata = {
39
39
 
40
40
  /**
41
41
  * Handler for reading the help resource
42
+ * @param {object} config - Configuration object (optional)
42
43
  * @returns {object} Resource content
43
44
  */
44
- export async function helpResourceHandler() {
45
- const helpContent = generateHelpContent();
45
+ export async function helpResourceHandler(config = null) {
46
+ const helpContent = generateHelpContent(config);
46
47
  const version = getServerVersion();
47
48
 
48
49
  // Add version information to the help content
@@ -63,4 +64,4 @@ export async function helpResourceHandler() {
63
64
  */
64
65
  export function listResources() {
65
66
  return [helpResourceMetadata];
66
- }
67
+ }
package/src/router.js CHANGED
@@ -97,7 +97,7 @@ function validateTool(toolName, tools) {
97
97
  async function createDependencies(config, context = {}) {
98
98
  try {
99
99
  const continuationStore = getContinuationStore();
100
- const tools = getTools();
100
+ const tools = getTools(config);
101
101
  const providers = getProviders();
102
102
 
103
103
  // Initialize async infrastructure
@@ -181,7 +181,7 @@ export async function createRouter(server, config) {
181
181
 
182
182
  // Initialize dependencies with validation
183
183
  const dependencies = await createDependencies(config);
184
- const tools = getTools();
184
+ const tools = getTools(config);
185
185
 
186
186
  createRouterLogger.info(`Router initialized with ${Object.keys(tools).length} tools`);
187
187
 
@@ -307,7 +307,7 @@ export async function createRouter(server, config) {
307
307
 
308
308
  if (promptName === 'help') {
309
309
  const promptArgs = request.params?.arguments || {};
310
- const result = await helpPromptHandler(promptArgs);
310
+ const result = await helpPromptHandler(promptArgs, config);
311
311
 
312
312
  return {
313
313
  description: helpPromptMetadata.description,
@@ -335,7 +335,7 @@ export async function createRouter(server, config) {
335
335
  const resourceUri = request.params?.uri;
336
336
 
337
337
  if (resourceUri === helpResourceMetadata.uri) {
338
- return await helpResourceHandler();
338
+ return await helpResourceHandler(config);
339
339
  }
340
340
 
341
341
  throw new RouterError(
@@ -360,7 +360,7 @@ export async function createRouter(server, config) {
360
360
  // Return router interface for testing purposes
361
361
  return {
362
362
  listTools: async () => {
363
- const tools = getTools();
363
+ const tools = getTools(config);
364
364
  return {
365
365
  tools: Object.entries(tools).map(([name, tool]) => {
366
366
  const toolSchema = {
@@ -553,7 +553,7 @@ export function validateToolArguments(args, schema) {
553
553
  */
554
554
  export async function getRouterStats(dependencies) {
555
555
  try {
556
- const tools = getTools();
556
+ const tools = getTools(dependencies.config);
557
557
  const storeStats = await dependencies.continuationStore.getStats();
558
558
 
559
559
  return {
@@ -97,7 +97,7 @@ export async function cancelJobTool(args, dependencies) {
97
97
  // Calculate actual elapsed time from job creation
98
98
  const elapsedMs = Date.now() - (updatedJobState?.createdAt || jobState.createdAt || Date.now());
99
99
  const elapsedSeconds = elapsedMs / 1000;
100
-
100
+
101
101
  // Format elapsed time
102
102
  let timeStr;
103
103
  if (elapsedSeconds >= 60) {
@@ -110,24 +110,24 @@ export async function cancelJobTool(args, dependencies) {
110
110
 
111
111
  // Build human-readable response parts
112
112
  const parts = [];
113
-
113
+
114
114
  // Status line with proper timing
115
115
  const statusEmoji = '⛔';
116
116
  const startTime = updatedJobState?.createdAt ? new Date(updatedJobState.createdAt).toLocaleString() : 'unknown';
117
-
117
+
118
118
  let statusLine = `${statusEmoji} CANCELLED | ${updatedJobState?.tool?.toUpperCase() || jobState.tool?.toUpperCase() || 'UNKNOWN'} | ${continuation_id} | Started: ${startTime} | ${timeStr} elapsed`;
119
-
119
+
120
120
  // Add title if available
121
121
  if (updatedJobState?.title) {
122
122
  statusLine += ` | "${updatedJobState.title}"`;
123
123
  }
124
-
124
+
125
125
  parts.push(statusLine);
126
-
126
+
127
127
  // Add cancellation info
128
128
  parts.push(`Cancelled at: ${new Date().toLocaleString()}`);
129
129
  parts.push(`Previous status: ${jobState.status}`);
130
-
130
+
131
131
  // Add partial results info if available
132
132
  if (updatedJobState?.accumulated_content) {
133
133
  const preview = updatedJobState.accumulated_content.length > 200
@@ -137,7 +137,7 @@ export async function cancelJobTool(args, dependencies) {
137
137
  } else if (updatedJobState?.result) {
138
138
  parts.push('Partial results available in job state');
139
139
  }
140
-
140
+
141
141
  // Add continuation_id for easy reference
142
142
  parts.push(`continuation_id: ${continuation_id}`);
143
143
 
package/src/tools/chat.js CHANGED
@@ -741,7 +741,7 @@ async function executeChatWithStreaming(args, dependencies, context) {
741
741
  await context.updateJob({
742
742
  reasoning_summary: response.metadata.usage.reasoning_summary
743
743
  });
744
- debugLog(`Chat: Stored reasoning summary`);
744
+ debugLog('Chat: Stored reasoning summary');
745
745
  } catch (error) {
746
746
  debugError('Chat: Failed to store reasoning summary', error);
747
747
  }
@@ -810,7 +810,7 @@ async function executeChatWithStreaming(args, dependencies, context) {
810
810
  }
811
811
 
812
812
  // Tool metadata
813
- chatTool.description = 'GENERAL CHAT & COLLABORATIVE THINKING - For development assistance, brainstorming, and code analysis. Supports files, images, and conversation continuation. Use model: "auto" for automatic model selection.';
813
+ chatTool.description = 'GENERAL CHAT & COLLABORATIVE THINKING - Development assistance, brainstorming, code analysis. Supports files, images, continuation_id for multi-turn conversations. Use model: "auto" for automatic selection.';
814
814
  chatTool.inputSchema = {
815
815
  type: 'object',
816
816
  properties: {
@@ -508,7 +508,7 @@ Please provide your refined response:`;
508
508
 
509
509
  // Create unified status line (similar to async status display)
510
510
  const finalCount = refinedPhase ? refinedPhase.filter(r => r.status === 'success').length : initialPhase.successful.length;
511
- const totalCount = models.length;
511
+ const totalCount = providerCalls.length;
512
512
  const statusLine = config.environment?.nodeEnv !== 'test'
513
513
  ? `✅ COMPLETED | CONSENSUS | ${continuationId} | ${consensusExecutionTime.toFixed(1)}s elapsed | ${finalCount}/${totalCount} succeeded | ${modelsList}\n`
514
514
  : '';
@@ -1336,7 +1336,7 @@ async function executeConsensusPhaseWithStreaming(providerCalls, messages, phase
1336
1336
  }
1337
1337
 
1338
1338
  // Tool metadata
1339
- consensusTool.description = 'PARALLEL CONSENSUS WITH CROSS-MODEL FEEDBACK - Gathers perspectives from multiple AI models simultaneously. Models provide initial responses, then optionally refine based on others\' insights. Returns both phases in a single call. Handles partial failures gracefully. For: complex decisions, architectural choices, technical evaluations. Use models: ["auto"] for automatic model selection.';
1339
+ consensusTool.description = 'PARALLEL CONSENSUS WITH CROSS-MODEL FEEDBACK - Query multiple models simultaneously, then optionally refine responses based on cross-feedback. For complex decisions, architectural choices, technical evaluations. Use models: ["auto"] for automatic selection.';
1340
1340
  consensusTool.inputSchema = {
1341
1341
  type: 'object',
1342
1342
  properties: {
@@ -24,11 +24,50 @@ const tools = {
24
24
  };
25
25
 
26
26
  /**
27
- * Get all available tools
27
+ * Get all available tools, optionally filtered based on configuration
28
+ * @param {object} config - Configuration object (optional)
28
29
  * @returns {object} Map of tool name to tool implementation
29
30
  */
30
- export function getTools() {
31
- return tools;
31
+ export function getTools(config = null) {
32
+ // If no config provided or async tools not disabled, return all tools
33
+ if (!config || !config.async?.disableAsyncTools) {
34
+ return tools;
35
+ }
36
+
37
+ // Filter out async-only tools when async is disabled
38
+ const filteredTools = {};
39
+ for (const [name, tool] of Object.entries(tools)) {
40
+ // Exclude check_status and cancel_job tools
41
+ if (name === 'check_status' || name === 'cancel_job') {
42
+ continue;
43
+ }
44
+
45
+ // Clone the tool to avoid mutating the original
46
+ const clonedTool = tool;
47
+
48
+ // For chat and consensus tools, remove the 'async' parameter from their schemas
49
+ if (name === 'chat' || name === 'consensus') {
50
+ // Create a modified inputSchema without the async parameter
51
+ const modifiedSchema = {
52
+ ...tool.inputSchema,
53
+ properties: { ...tool.inputSchema.properties }
54
+ };
55
+ delete modifiedSchema.properties.async;
56
+
57
+ // Create a wrapper function with modified metadata
58
+ const wrappedTool = async function(...args) {
59
+ return await tool(...args);
60
+ };
61
+ wrappedTool.description = tool.description;
62
+ wrappedTool.inputSchema = modifiedSchema;
63
+
64
+ filteredTools[name] = wrappedTool;
65
+ } else {
66
+ filteredTools[name] = clonedTool;
67
+ }
68
+ }
69
+
70
+ return filteredTools;
32
71
  }
33
72
 
34
73
  /**