@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.
Files changed (3) hide show
  1. package/README.md +11 -6
  2. package/mcp/server.mjs +47 -16
  3. 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
- | `get_model_info` | Pricing, context, architecture, features and LMArena ELO for a model |
185
- | `list_models` | List models with optional filter, sort and limit |
186
- | `get_benchmarks` | LMArena ELO score, global rank, vote count and confidence interval for a model |
187
- | `compare_models` | Side-by-side comparison of two models |
188
- | `best_for_task` | Ranked top models for coding/reasoning/general/vision/cheap |
189
- | `refresh_cache` | Force-refresh OpenRouter catalog + LMArena ELO |
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 TOOLS = [
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: 'get_model_info',
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 info',
54
+ title: 'Get model',
46
55
  readOnlyHint: true,
47
56
  idempotentHint: true,
48
57
  openWorldHint: true,
49
58
  },
50
59
  },
51
60
  {
52
- name: 'list_models',
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: 'get_benchmarks',
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 benchmarks',
106
+ title: 'Get benchmark',
98
107
  readOnlyHint: true,
99
108
  idempotentHint: true,
100
109
  openWorldHint: true,
101
110
  },
102
111
  },
103
112
  {
104
- name: 'compare_models',
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: 'best_for_task',
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: 'Best models for task',
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: 'refresh_cache',
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 === 'get_model_info') {
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 === 'list_models') {
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 === 'get_benchmarks') {
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 === 'compare_models') {
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 === 'best_for_task') {
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 === 'refresh_cache') {
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 }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aggc/or-info",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "CLI + MCP server for OpenRouter models: prices, benchmarks, context and comparisons",
5
5
  "type": "module",
6
6
  "engines": {