@aggc/or-info 0.2.7 → 0.2.8
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 +11 -6
- package/mcp/server.mjs +47 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -181,12 +181,17 @@ or-info refresh # Force-refresh OpenRouter catalog + LMArena ELO
|
|
|
181
181
|
|
|
182
182
|
| Tool | Description |
|
|
183
183
|
|------|-------------|
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
184
|
+
| `models.get` | Pricing, context, architecture, features and LMArena ELO for a model |
|
|
185
|
+
| `models.list` | List models with optional filter, sort and limit |
|
|
186
|
+
| `models.compare` | Side-by-side comparison of two models |
|
|
187
|
+
| `models.top` | Ranked top models for coding/reasoning/general/vision/cheap |
|
|
188
|
+
| `benchmarks.get` | LMArena ELO score, global rank, vote count and confidence interval for a model |
|
|
189
|
+
| `cache.refresh` | Force-refresh OpenRouter catalog + LMArena ELO |
|
|
190
|
+
|
|
191
|
+
Legacy flat names (`get_model_info`, `list_models`, `get_benchmarks`,
|
|
192
|
+
`compare_models`, `best_for_task`, `refresh_cache`) are still advertised in
|
|
193
|
+
`tools/list` as deprecated aliases (same schemas, prefixed `[Deprecated]`)
|
|
194
|
+
and remain callable. The dot-notation names are the canonical ones.
|
|
190
195
|
|
|
191
196
|
### Register in Claude Code
|
|
192
197
|
|
package/mcp/server.mjs
CHANGED
|
@@ -26,9 +26,18 @@ const MODEL_SUMMARY_SCHEMA = {
|
|
|
26
26
|
|
|
27
27
|
const ELO_SCHEMA = { type: ['object', 'null'], description: 'LMArena ELO entry or null when not tracked' };
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const TOOL_ALIASES = {
|
|
30
|
+
get_model_info: 'models.get',
|
|
31
|
+
list_models: 'models.list',
|
|
32
|
+
compare_models: 'models.compare',
|
|
33
|
+
best_for_task: 'models.top',
|
|
34
|
+
get_benchmarks: 'benchmarks.get',
|
|
35
|
+
refresh_cache: 'cache.refresh',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const CANONICAL_TOOLS = [
|
|
30
39
|
{
|
|
31
|
-
name: '
|
|
40
|
+
name: 'models.get',
|
|
32
41
|
description: 'Get pricing, context length, architecture and features for a specific OpenRouter model',
|
|
33
42
|
inputSchema: {
|
|
34
43
|
type: 'object',
|
|
@@ -42,14 +51,14 @@ const TOOLS = [
|
|
|
42
51
|
properties: { ...MODEL_SUMMARY_SCHEMA.properties, lmarena_elo: ELO_SCHEMA },
|
|
43
52
|
},
|
|
44
53
|
annotations: {
|
|
45
|
-
title: 'Get model
|
|
54
|
+
title: 'Get model',
|
|
46
55
|
readOnlyHint: true,
|
|
47
56
|
idempotentHint: true,
|
|
48
57
|
openWorldHint: true,
|
|
49
58
|
},
|
|
50
59
|
},
|
|
51
60
|
{
|
|
52
|
-
name: '
|
|
61
|
+
name: 'models.list',
|
|
53
62
|
description: 'List OpenRouter models with pricing. Optionally filter by name/id, sort, and limit results.',
|
|
54
63
|
inputSchema: {
|
|
55
64
|
type: 'object',
|
|
@@ -76,7 +85,7 @@ const TOOLS = [
|
|
|
76
85
|
},
|
|
77
86
|
},
|
|
78
87
|
{
|
|
79
|
-
name: '
|
|
88
|
+
name: 'benchmarks.get',
|
|
80
89
|
description: 'Get LMArena ELO ranking for a model: score, global rank, vote count and confidence interval',
|
|
81
90
|
inputSchema: {
|
|
82
91
|
type: 'object',
|
|
@@ -94,14 +103,14 @@ const TOOLS = [
|
|
|
94
103
|
required: ['model_id'],
|
|
95
104
|
},
|
|
96
105
|
annotations: {
|
|
97
|
-
title: 'Get
|
|
106
|
+
title: 'Get benchmark',
|
|
98
107
|
readOnlyHint: true,
|
|
99
108
|
idempotentHint: true,
|
|
100
109
|
openWorldHint: true,
|
|
101
110
|
},
|
|
102
111
|
},
|
|
103
112
|
{
|
|
104
|
-
name: '
|
|
113
|
+
name: 'models.compare',
|
|
105
114
|
description: 'Side-by-side comparison of two models: pricing, context, benchmarks and features',
|
|
106
115
|
inputSchema: {
|
|
107
116
|
type: 'object',
|
|
@@ -127,7 +136,7 @@ const TOOLS = [
|
|
|
127
136
|
},
|
|
128
137
|
},
|
|
129
138
|
{
|
|
130
|
-
name: '
|
|
139
|
+
name: 'models.top',
|
|
131
140
|
description: 'Rank the best models for a specific task, optionally within a price budget',
|
|
132
141
|
inputSchema: {
|
|
133
142
|
type: 'object',
|
|
@@ -160,14 +169,14 @@ const TOOLS = [
|
|
|
160
169
|
required: ['task', 'results'],
|
|
161
170
|
},
|
|
162
171
|
annotations: {
|
|
163
|
-
title: '
|
|
172
|
+
title: 'Top models for task',
|
|
164
173
|
readOnlyHint: true,
|
|
165
174
|
idempotentHint: true,
|
|
166
175
|
openWorldHint: true,
|
|
167
176
|
},
|
|
168
177
|
},
|
|
169
178
|
{
|
|
170
|
-
name: '
|
|
179
|
+
name: 'cache.refresh',
|
|
171
180
|
description: 'Force-refresh the local cache: OpenRouter model catalog + LMArena ELO data',
|
|
172
181
|
inputSchema: { type: 'object', properties: {} },
|
|
173
182
|
outputSchema: {
|
|
@@ -188,6 +197,25 @@ const TOOLS = [
|
|
|
188
197
|
},
|
|
189
198
|
];
|
|
190
199
|
|
|
200
|
+
// Legacy flat names kept advertised in tools/list for discoverability,
|
|
201
|
+
// derived from the canonical tools so input/output schemas stay in sync.
|
|
202
|
+
const LEGACY_BY_CANONICAL = Object.fromEntries(
|
|
203
|
+
Object.entries(TOOL_ALIASES).map(([legacy, canonical]) => [canonical, legacy])
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const LEGACY_TOOLS = CANONICAL_TOOLS.flatMap((tool) => {
|
|
207
|
+
const legacyName = LEGACY_BY_CANONICAL[tool.name];
|
|
208
|
+
if (!legacyName) return [];
|
|
209
|
+
return [{
|
|
210
|
+
...tool,
|
|
211
|
+
name: legacyName,
|
|
212
|
+
description: `[Deprecated] Alias of \`${tool.name}\`. Use \`${tool.name}\` instead.`,
|
|
213
|
+
annotations: { ...tool.annotations, title: `[Deprecated] ${tool.annotations.title}` },
|
|
214
|
+
}];
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const TOOLS = [...CANONICAL_TOOLS, ...LEGACY_TOOLS];
|
|
218
|
+
|
|
191
219
|
function safeModelSummary(model) {
|
|
192
220
|
const price = pricePerMillion(model);
|
|
193
221
|
return {
|
|
@@ -218,9 +246,12 @@ function errorContent(msg) {
|
|
|
218
246
|
}
|
|
219
247
|
|
|
220
248
|
async function handleTool(name, args) {
|
|
249
|
+
// Accept legacy flat names (get_model_info, list_models, ...) by mapping
|
|
250
|
+
// them to the dot-notation canonical names exposed in tools/list.
|
|
251
|
+
name = TOOL_ALIASES[name] ?? name;
|
|
221
252
|
const key = await getApiKey();
|
|
222
253
|
|
|
223
|
-
if (name === '
|
|
254
|
+
if (name === 'models.get') {
|
|
224
255
|
const { model_id } = args;
|
|
225
256
|
if (!model_id || typeof model_id !== 'string') return errorContent('model_id is required');
|
|
226
257
|
const models = await fetchModels({ apiKey: key });
|
|
@@ -230,7 +261,7 @@ async function handleTool(name, args) {
|
|
|
230
261
|
return result({ ...safeModelSummary(model), lmarena_elo: elo ?? null });
|
|
231
262
|
}
|
|
232
263
|
|
|
233
|
-
if (name === '
|
|
264
|
+
if (name === 'models.list') {
|
|
234
265
|
const filter = String(args.filter ?? '').toLowerCase();
|
|
235
266
|
const sortBy = args.sort_by ?? 'name';
|
|
236
267
|
const limit = Math.min(200, Math.max(1, args.limit ?? 50));
|
|
@@ -248,14 +279,14 @@ async function handleTool(name, args) {
|
|
|
248
279
|
return result({ total: models.length, models: models.map(safeModelSummary) });
|
|
249
280
|
}
|
|
250
281
|
|
|
251
|
-
if (name === '
|
|
282
|
+
if (name === 'benchmarks.get') {
|
|
252
283
|
const { model_id } = args;
|
|
253
284
|
if (!model_id || typeof model_id !== 'string') return errorContent('model_id is required');
|
|
254
285
|
const elo = await getElo(model_id);
|
|
255
286
|
return result({ model_id, lmarena_elo: elo ?? null });
|
|
256
287
|
}
|
|
257
288
|
|
|
258
|
-
if (name === '
|
|
289
|
+
if (name === 'models.compare') {
|
|
259
290
|
const { model_a, model_b } = args;
|
|
260
291
|
if (!model_a || !model_b) return errorContent('model_a and model_b are required');
|
|
261
292
|
const [models, eloA, eloB] = await Promise.all([
|
|
@@ -270,7 +301,7 @@ async function handleTool(name, args) {
|
|
|
270
301
|
return result({ a: { ...safeModelSummary(mA), lmarena_elo: eloA }, b: { ...safeModelSummary(mB), lmarena_elo: eloB } });
|
|
271
302
|
}
|
|
272
303
|
|
|
273
|
-
if (name === '
|
|
304
|
+
if (name === 'models.top') {
|
|
274
305
|
const task = args.task ?? 'general';
|
|
275
306
|
const limit = Math.min(20, Math.max(1, args.limit ?? 5));
|
|
276
307
|
const maxPrice = args.max_price_per_m_output ?? undefined;
|
|
@@ -280,7 +311,7 @@ async function handleTool(name, args) {
|
|
|
280
311
|
return result({ task, results: ranked.map((r) => ({ ...safeModelSummary(r.model), score: r.score, lmarena_elo: r.eloEntry })) });
|
|
281
312
|
}
|
|
282
313
|
|
|
283
|
-
if (name === '
|
|
314
|
+
if (name === 'cache.refresh') {
|
|
284
315
|
const [models, elo] = await Promise.all([
|
|
285
316
|
fetchModels({ force: true, apiKey: key }),
|
|
286
317
|
loadLeaderboard({ force: true }),
|