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.
- package/README.md +771 -738
- package/docs/API.md +10 -1
- package/docs/PROVIDERS.md +8 -4
- package/package.json +12 -12
- package/src/async/asyncJobStore.js +82 -52
- package/src/async/eventBus.js +25 -20
- package/src/async/fileCache.js +121 -40
- package/src/async/jobRunner.js +65 -39
- package/src/async/providerStreamNormalizer.js +203 -117
- package/src/config.js +374 -102
- package/src/continuationStore.js +32 -24
- package/src/index.js +45 -25
- package/src/prompts/helpPrompt.js +328 -305
- package/src/providers/anthropic.js +303 -119
- package/src/providers/codex.js +103 -45
- package/src/providers/deepseek.js +24 -8
- package/src/providers/google.js +323 -93
- package/src/providers/index.js +1 -1
- package/src/providers/interface.js +16 -11
- package/src/providers/mistral.js +179 -69
- package/src/providers/openai-compatible.js +231 -94
- package/src/providers/openai.js +1094 -914
- package/src/providers/openrouter-endpoints-client.js +220 -216
- package/src/providers/openrouter.js +426 -381
- package/src/providers/xai.js +153 -56
- package/src/resources/helpResource.js +70 -67
- package/src/router.js +95 -67
- package/src/services/summarizationService.js +51 -24
- package/src/systemPrompts.js +89 -89
- package/src/tools/cancelJob.js +31 -19
- package/src/tools/chat.js +997 -883
- package/src/tools/checkStatus.js +86 -65
- package/src/tools/consensus.js +400 -234
- package/src/tools/index.js +39 -16
- package/src/transport/httpTransport.js +82 -55
- package/src/utils/contextProcessor.js +54 -37
- package/src/utils/errorHandler.js +95 -45
- package/src/utils/fileValidator.js +107 -98
- package/src/utils/formatStatus.js +122 -64
- package/src/utils/logger.js +459 -449
- package/src/utils/pathUtils.js +2 -2
- 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:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
metadata
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
*
|
|
84
|
-
* @
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
+
}
|