converse-mcp-server 2.3.1 → 2.4.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 (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 +337 -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
@@ -1,216 +1,220 @@
1
- /**
2
- * OpenRouter Endpoints API Client
3
- *
4
- * Handles fetching model capabilities from OpenRouter's endpoints API.
5
- * Provides caching and error handling for dynamic model discovery.
6
- */
7
-
8
- import { debugLog, debugError } from '../utils/console.js';
9
-
10
- /**
11
- * Parse an OpenRouter model ID into author and slug components
12
- * @param {string} modelId - Model ID in format "author/slug"
13
- * @returns {{author: string, slug: string} | null} Parsed components or null if invalid
14
- */
15
- function parseModelId(modelId) {
16
- if (!modelId || typeof modelId !== 'string') {
17
- return null;
18
- }
19
-
20
- const parts = modelId.split('/');
21
- if (parts.length !== 2) {
22
- return null;
23
- }
24
-
25
- const [author, slug] = parts;
26
- if (!author || !slug) {
27
- return null;
28
- }
29
-
30
- return { author, slug };
31
- }
32
-
33
- /**
34
- * Convert endpoint data to model configuration format
35
- * @param {Object} endpointData - Raw endpoint data from API
36
- * @returns {Object} Model configuration object
37
- */
38
- function convertEndpointToModelConfig(endpointData) {
39
- const data = endpointData.data;
40
- const modelId = data.id;
41
-
42
- // Find the best endpoint (prefer primary providers)
43
- const preferredProviders = ['Anthropic', 'OpenAI', 'Google', 'XAI'];
44
- let selectedEndpoint = data.endpoints[0]; // Default to first
45
-
46
- for (const endpoint of data.endpoints) {
47
- if (preferredProviders.includes(endpoint.provider_name)) {
48
- selectedEndpoint = endpoint;
49
- break;
50
- }
51
- }
52
-
53
- // Extract supported parameters
54
- const supportedParams = selectedEndpoint.supported_parameters || [];
55
-
56
- return {
57
- modelName: modelId,
58
- friendlyName: data.name || `${modelId} (via OpenRouter)`,
59
- description: data.description || `Dynamic model: ${modelId}`,
60
- contextWindow: selectedEndpoint.context_length || 8192,
61
- maxOutputTokens: selectedEndpoint.max_completion_tokens || 4096,
62
- supportsStreaming: true, // Most models support streaming
63
- supportsImages: data.architecture?.input_modalities?.includes('image') || false,
64
- supportsTemperature: supportedParams.includes('temperature'),
65
- supportsWebSearch: false, // Not in API response, conservative default
66
- supportsThinking: supportedParams.includes('reasoning'),
67
- supportsTools: supportedParams.includes('tools'),
68
- timeout: 300000, // 5 minutes default
69
- isDynamic: true,
70
- // Store additional metadata
71
- metadata: {
72
- architecture: data.architecture,
73
- endpoints: data.endpoints,
74
- pricing: selectedEndpoint.pricing,
75
- selectedProvider: selectedEndpoint.provider_name,
76
- maxPromptTokens: selectedEndpoint.max_prompt_tokens
77
- }
78
- };
79
- }
80
-
81
- /**
82
- * Fetch model endpoints from OpenRouter API
83
- * @param {string} modelId - Model ID in format "author/slug"
84
- * @returns {Promise<Object|null>} Model configuration or null if not found
85
- */
86
- export async function fetchModelEndpoints(modelId) {
87
- const parsed = parseModelId(modelId);
88
- if (!parsed) {
89
- debugLog(`[OpenRouter Endpoints] Invalid model ID format: ${modelId}`);
90
- return null;
91
- }
92
-
93
- const { author, slug } = parsed;
94
- const url = `https://openrouter.ai/api/v1/models/${author}/${slug}/endpoints`;
95
-
96
- try {
97
- debugLog(`[OpenRouter Endpoints] Fetching endpoints for ${modelId}`);
98
-
99
- const response = await globalThis.fetch(url, {
100
- method: 'GET',
101
- headers: {
102
- 'Accept': 'application/json'
103
- }
104
- });
105
-
106
- if (response.status === 404) {
107
- debugLog(`[OpenRouter Endpoints] Model not found: ${modelId}`);
108
- return null;
109
- }
110
-
111
- if (!response.ok) {
112
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
113
- }
114
-
115
- const data = await response.json();
116
-
117
- // Validate response structure
118
- if (!data?.data?.id || !data?.data?.endpoints?.length) {
119
- debugLog(`[OpenRouter Endpoints] Invalid response structure for ${modelId}`);
120
- return null;
121
- }
122
-
123
- const modelConfig = convertEndpointToModelConfig(data);
124
- debugLog(`[OpenRouter Endpoints] Successfully fetched config for ${modelId}`);
125
-
126
- return modelConfig;
127
-
128
- } catch (error) {
129
- debugError(`[OpenRouter Endpoints] Error fetching ${modelId}:`, error);
130
- return null;
131
- }
132
- }
133
-
134
- /**
135
- * Create a simple in-memory cache for model endpoints
136
- */
137
- export function createEndpointsCache() {
138
- const cache = new Map();
139
- const DEFAULT_TTL = 24 * 60 * 60 * 1000; // 24 hours
140
- const FAILED_TTL = 5 * 60 * 1000; // 5 minutes for failed requests
141
-
142
- return {
143
- /**
144
- * Get a cached value
145
- * @param {string} key - Cache key
146
- * @returns {{found: boolean, value: any}} Cache result
147
- */
148
- get(key) {
149
- const entry = cache.get(key);
150
- if (!entry) {
151
- return { found: false, value: null };
152
- }
153
-
154
- if (Date.now() > entry.expiry) {
155
- cache.delete(key);
156
- return { found: false, value: null };
157
- }
158
-
159
- return { found: true, value: entry.value };
160
- },
161
-
162
- /**
163
- * Set a cached value
164
- * @param {string} key - Cache key
165
- * @param {Object|null} value - Value to cache
166
- * @param {boolean} isFailure - Whether this is a failed request
167
- */
168
- set(key, value, isFailure = false) {
169
- const ttl = isFailure ? FAILED_TTL : DEFAULT_TTL;
170
- cache.set(key, {
171
- value,
172
- expiry: Date.now() + ttl
173
- });
174
- },
175
-
176
- /**
177
- * Clear the entire cache
178
- */
179
- clear() {
180
- cache.clear();
181
- },
182
-
183
- /**
184
- * Get cache size
185
- * @returns {number} Number of cached entries
186
- */
187
- size() {
188
- return cache.size;
189
- }
190
- };
191
- }
192
-
193
- // Create a singleton cache instance
194
- export const endpointsCache = createEndpointsCache();
195
-
196
- /**
197
- * Fetch model endpoints with caching
198
- * @param {string} modelId - Model ID in format "author/slug"
199
- * @returns {Promise<Object|null>} Model configuration or null if not found
200
- */
201
- export async function fetchModelEndpointsWithCache(modelId) {
202
- // Check cache first
203
- const cached = endpointsCache.get(modelId);
204
- if (cached.found) {
205
- debugLog(`[OpenRouter Endpoints] Using cached config for ${modelId}`);
206
- return cached.value;
207
- }
208
-
209
- // Fetch from API
210
- const config = await fetchModelEndpoints(modelId);
211
-
212
- // Cache the result (including null for not found)
213
- endpointsCache.set(modelId, config, config === null);
214
-
215
- return config;
216
- }
1
+ /**
2
+ * OpenRouter Endpoints API Client
3
+ *
4
+ * Handles fetching model capabilities from OpenRouter's endpoints API.
5
+ * Provides caching and error handling for dynamic model discovery.
6
+ */
7
+
8
+ import { debugLog, debugError } from '../utils/console.js';
9
+
10
+ /**
11
+ * Parse an OpenRouter model ID into author and slug components
12
+ * @param {string} modelId - Model ID in format "author/slug"
13
+ * @returns {{author: string, slug: string} | null} Parsed components or null if invalid
14
+ */
15
+ function parseModelId(modelId) {
16
+ if (!modelId || typeof modelId !== 'string') {
17
+ return null;
18
+ }
19
+
20
+ const parts = modelId.split('/');
21
+ if (parts.length !== 2) {
22
+ return null;
23
+ }
24
+
25
+ const [author, slug] = parts;
26
+ if (!author || !slug) {
27
+ return null;
28
+ }
29
+
30
+ return { author, slug };
31
+ }
32
+
33
+ /**
34
+ * Convert endpoint data to model configuration format
35
+ * @param {Object} endpointData - Raw endpoint data from API
36
+ * @returns {Object} Model configuration object
37
+ */
38
+ function convertEndpointToModelConfig(endpointData) {
39
+ const data = endpointData.data;
40
+ const modelId = data.id;
41
+
42
+ // Find the best endpoint (prefer primary providers)
43
+ const preferredProviders = ['Anthropic', 'OpenAI', 'Google', 'XAI'];
44
+ let selectedEndpoint = data.endpoints[0]; // Default to first
45
+
46
+ for (const endpoint of data.endpoints) {
47
+ if (preferredProviders.includes(endpoint.provider_name)) {
48
+ selectedEndpoint = endpoint;
49
+ break;
50
+ }
51
+ }
52
+
53
+ // Extract supported parameters
54
+ const supportedParams = selectedEndpoint.supported_parameters || [];
55
+
56
+ return {
57
+ modelName: modelId,
58
+ friendlyName: data.name || `${modelId} (via OpenRouter)`,
59
+ description: data.description || `Dynamic model: ${modelId}`,
60
+ contextWindow: selectedEndpoint.context_length || 8192,
61
+ maxOutputTokens: selectedEndpoint.max_completion_tokens || 4096,
62
+ supportsStreaming: true, // Most models support streaming
63
+ supportsImages:
64
+ data.architecture?.input_modalities?.includes('image') || false,
65
+ supportsTemperature: supportedParams.includes('temperature'),
66
+ supportsWebSearch: false, // Not in API response, conservative default
67
+ supportsThinking: supportedParams.includes('reasoning'),
68
+ supportsTools: supportedParams.includes('tools'),
69
+ timeout: 300000, // 5 minutes default
70
+ isDynamic: true,
71
+ // Store additional metadata
72
+ metadata: {
73
+ architecture: data.architecture,
74
+ endpoints: data.endpoints,
75
+ pricing: selectedEndpoint.pricing,
76
+ selectedProvider: selectedEndpoint.provider_name,
77
+ maxPromptTokens: selectedEndpoint.max_prompt_tokens,
78
+ },
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Fetch model endpoints from OpenRouter API
84
+ * @param {string} modelId - Model ID in format "author/slug"
85
+ * @returns {Promise<Object|null>} Model configuration or null if not found
86
+ */
87
+ export async function fetchModelEndpoints(modelId) {
88
+ const parsed = parseModelId(modelId);
89
+ if (!parsed) {
90
+ debugLog(`[OpenRouter Endpoints] Invalid model ID format: ${modelId}`);
91
+ return null;
92
+ }
93
+
94
+ const { author, slug } = parsed;
95
+ const url = `https://openrouter.ai/api/v1/models/${author}/${slug}/endpoints`;
96
+
97
+ try {
98
+ debugLog(`[OpenRouter Endpoints] Fetching endpoints for ${modelId}`);
99
+
100
+ const response = await globalThis.fetch(url, {
101
+ method: 'GET',
102
+ headers: {
103
+ Accept: 'application/json',
104
+ },
105
+ });
106
+
107
+ if (response.status === 404) {
108
+ debugLog(`[OpenRouter Endpoints] Model not found: ${modelId}`);
109
+ return null;
110
+ }
111
+
112
+ if (!response.ok) {
113
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
114
+ }
115
+
116
+ const data = await response.json();
117
+
118
+ // Validate response structure
119
+ if (!data?.data?.id || !data?.data?.endpoints?.length) {
120
+ debugLog(
121
+ `[OpenRouter Endpoints] Invalid response structure for ${modelId}`,
122
+ );
123
+ return null;
124
+ }
125
+
126
+ const modelConfig = convertEndpointToModelConfig(data);
127
+ debugLog(
128
+ `[OpenRouter Endpoints] Successfully fetched config for ${modelId}`,
129
+ );
130
+
131
+ return modelConfig;
132
+ } catch (error) {
133
+ debugError(`[OpenRouter Endpoints] Error fetching ${modelId}:`, error);
134
+ return null;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Create a simple in-memory cache for model endpoints
140
+ */
141
+ export function createEndpointsCache() {
142
+ const cache = new Map();
143
+ const DEFAULT_TTL = 24 * 60 * 60 * 1000; // 24 hours
144
+ const FAILED_TTL = 5 * 60 * 1000; // 5 minutes for failed requests
145
+
146
+ return {
147
+ /**
148
+ * Get a cached value
149
+ * @param {string} key - Cache key
150
+ * @returns {{found: boolean, value: any}} Cache result
151
+ */
152
+ get(key) {
153
+ const entry = cache.get(key);
154
+ if (!entry) {
155
+ return { found: false, value: null };
156
+ }
157
+
158
+ if (Date.now() > entry.expiry) {
159
+ cache.delete(key);
160
+ return { found: false, value: null };
161
+ }
162
+
163
+ return { found: true, value: entry.value };
164
+ },
165
+
166
+ /**
167
+ * Set a cached value
168
+ * @param {string} key - Cache key
169
+ * @param {Object|null} value - Value to cache
170
+ * @param {boolean} isFailure - Whether this is a failed request
171
+ */
172
+ set(key, value, isFailure = false) {
173
+ const ttl = isFailure ? FAILED_TTL : DEFAULT_TTL;
174
+ cache.set(key, {
175
+ value,
176
+ expiry: Date.now() + ttl,
177
+ });
178
+ },
179
+
180
+ /**
181
+ * Clear the entire cache
182
+ */
183
+ clear() {
184
+ cache.clear();
185
+ },
186
+
187
+ /**
188
+ * Get cache size
189
+ * @returns {number} Number of cached entries
190
+ */
191
+ size() {
192
+ return cache.size;
193
+ },
194
+ };
195
+ }
196
+
197
+ // Create a singleton cache instance
198
+ export const endpointsCache = createEndpointsCache();
199
+
200
+ /**
201
+ * Fetch model endpoints with caching
202
+ * @param {string} modelId - Model ID in format "author/slug"
203
+ * @returns {Promise<Object|null>} Model configuration or null if not found
204
+ */
205
+ export async function fetchModelEndpointsWithCache(modelId) {
206
+ // Check cache first
207
+ const cached = endpointsCache.get(modelId);
208
+ if (cached.found) {
209
+ debugLog(`[OpenRouter Endpoints] Using cached config for ${modelId}`);
210
+ return cached.value;
211
+ }
212
+
213
+ // Fetch from API
214
+ const config = await fetchModelEndpoints(modelId);
215
+
216
+ // Cache the result (including null for not found)
217
+ endpointsCache.set(modelId, config, config === null);
218
+
219
+ return config;
220
+ }