@index9/mcp 4.0.2 → 5.0.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/dist/cli.js CHANGED
@@ -109,19 +109,30 @@ Parameters:
109
109
  - limit: Page size
110
110
  - cursor: Opaque pagination cursor
111
111
 
112
- Scores: Results include a 'score' field (0-100). Higher = more relevant. The best match in each page scores 100; others are scaled proportionally. Combines semantic similarity and keyword matching. Null when sorting by price or date.
112
+ Each result has: id, name, description, created (unix seconds), createdAt (ISO 8601), contextLength, maxOutputTokens, pricing.{promptPerMillion, completionPerMillion} (USD per million tokens, numbers), capabilities[], score.
113
113
 
114
- Use model IDs from results with get_models for full specs or test_model for live testing.`,
114
+ Scores: 0-100, higher = more relevant. The best match in each page scores 100; others are scaled proportionally. Combines semantic similarity and keyword matching. Null when sorting by price or date.
115
+
116
+ Pass result.id to get_models for full specs or to test_model for live testing.`,
115
117
  requiresKey: false
116
118
  },
117
119
  get_models: {
118
120
  name: "get_models",
119
121
  summary: "Get full model metadata by IDs or aliases (batch, up to 100)",
120
- description: `Get complete specs for a model by ID. Returns pricing, context window, capabilities, architecture, and per-request limits.
122
+ description: `Get full specs for one or more models by id or alias. Accepts up to ${LIMITS.getModelsMax} ids per call \u2014 use this for batch comparison.
123
+
124
+ Call after find_models to inspect candidates, or directly when the user names a model (format: 'provider/model-name').
121
125
 
122
- Call after find_models to get full details, or when the user provides a model ID (format: 'provider/model-name').
126
+ Response: { results: (Model | null)[], missingIds: string[], resolvedAliases?: Record<alias, canonicalId>, ambiguousAliases?: Record<alias, candidateIds[]> }. Each non-null result has:
127
+ - id, canonicalSlug, name, description
128
+ - created (unix seconds), createdAt (ISO 8601), knowledgeCutoff (ISO date or null)
129
+ - contextLength (tokens), maxOutputTokens, isModerated
130
+ - pricing: { promptPerMillion, completionPerMillion, requestUsd, imageUsd } \u2014 all USD, all numbers. Token prices are per million tokens; request/image are per unit.
131
+ - architecture: { inputModalities[], outputModalities[], tokenizer, instructType }
132
+ - capabilities[]: normalized capability flags (same values as find_models and capabilitiesAll/Any)
133
+ - supportedParameters[]: OpenRouter parameters the model accepts (e.g., "temperature", "tools", "response_format")
123
134
 
124
- Returns 404 if model not found. Use find_models to discover valid IDs.`,
135
+ Entries in results are null when the id is unknown; those ids appear in missingIds. Ambiguous aliases appear in ambiguousAliases with candidate canonical ids \u2014 pass a canonical id to disambiguate.`,
125
136
  requiresKey: false
126
137
  },
127
138
  test_model: {
@@ -140,9 +151,9 @@ Parameters:
140
151
  - prompt: Prompt text (required for dryRun; required for live unless userContent provided)
141
152
  - dryRun: If true, return cost estimates only
142
153
  - expectedCompletionTokens: Optional completion token estimate used by dryRun
143
- - max_tokens, systemPrompt, temperature, topP, seed, responseFormat, enforceJson, retries: Live-testing controls (ignored when dryRun=true)
154
+ - maxTokens, systemPrompt, temperature, topP, seed, responseFormat, enforceJson, retries: Live-testing controls (ignored when dryRun=true)
144
155
 
145
- Use find_models or get_models first to identify model IDs.`,
156
+ Results (live): each result carries modelId (the id you passed), resolvedModelId (canonical id, present when the input was an alias), ok, response, latencyMs, tokens { prompt, completion }, cost (USD; live from OpenRouter when available, else estimated from cached pricing), and truncated=true when finish_reason is "length". Use find_models or get_models first to identify model ids.`,
146
157
  requiresKey: true
147
158
  }
148
159
  };
@@ -150,15 +161,17 @@ var PARAM_DESCRIPTIONS = {
150
161
  q: "Natural language search query describing desired model characteristics (e.g., 'fast cheap coding model'). Uses semantic search with fuzzy matching. Optional - omit to use filters only.",
151
162
  sortBy: `Sort order for results. Options: 'relevance' (best semantic match, default), 'created' (newest models), 'price' (cheapest/most expensive, with sortOrder). Defaults to 'relevance'.`,
152
163
  cursor: `Opaque pagination cursor from a previous response's \`nextCursor\` field. IMPORTANT: cursors are bound to the exact query text, filters, and sort order that produced them. Reuse the same query+filters+sort when paginating. \`limit\` may change between pages. To start a new search, omit the cursor.`,
153
- capabilitiesAll: `Comma-separated capabilities that must ALL be present on the model (AND logic). Valid values: ${CAPABILITIES.join(", ")}. Example: 'function_calling,vision'. Invalid values silently filter to zero results.`,
154
- capabilitiesAny: `Comma-separated capabilities where at least ONE must be present (OR logic). Valid values: ${CAPABILITIES.join(", ")}. Example: 'vision,audio_input'. Invalid values silently filter to zero results.`,
155
- modality: `Required output modality. Filters on the model's output modalities, not input capabilities. For example, 'image' finds image-generation models, while capabilitiesAll=vision finds models that accept image input. Valid values: ${OUTPUT_MODALITIES.join(", ")}.`,
164
+ capabilitiesAll: `Array of capabilities that must ALL be present on the model (AND logic). Valid values: ${CAPABILITIES.join(", ")}. Example: ["function_calling","vision"].`,
165
+ capabilitiesAny: `Array of capabilities where at least ONE must be present (OR logic). Valid values: ${CAPABILITIES.join(", ")}. Example: ["vision","audio_input"].`,
166
+ modality: `Required output modality. Filters on the model's output modalities, not input capabilities. For example, "image" finds image-generation models, while capabilitiesAll=["vision"] finds models that accept image input. Valid values: ${OUTPUT_MODALITIES.join(", ")}.`,
156
167
  provider: `Provider prefix filter. Matches model IDs starting with this prefix (e.g., 'openai' matches 'openai/gpt-4o'). Common providers: ${COMMON_PROVIDERS.join(", ")}.`,
157
168
  expectedCompletionTokens: `Expected number of completion tokens for cost estimation (default: 256). Typical ranges: 100-500 for quick tests, 1000-2000 for code generation, 4000+ for long-form content. This is a heuristic \u2014 actual billed tokens may differ.`
158
169
  };
159
170
  var SITE = {
160
171
  nav: {
161
172
  brand: "index9",
173
+ tools: "Tools",
174
+ howItWorks: "How it works",
162
175
  install: "Install",
163
176
  faq: "FAQ",
164
177
  github: "GitHub",
@@ -168,19 +181,47 @@ var SITE = {
168
181
  titleLine1: "Test AI models on your actual prompts, ",
169
182
  titleLine2: "not generic benchmarks",
170
183
  subtitle: "Compare quality, speed, and cost across 300+ models \u2014 in Cursor, VS Code, or Claude Code.",
171
- getStarted: "Install index9",
184
+ identity: "index9 is an MCP server that lets your AI coding assistant search, compare, and live-test models from inside your editor.",
185
+ audiencePrefix: "Built for",
186
+ audience: ["AI engineers", "Indie developers", "Teams standardizing on models"],
187
+ pricingNote: "Free. You only pay OpenRouter for live model calls.",
188
+ getStarted: "Add to your editor",
172
189
  seeHowItWorks: "See an example",
173
- updatedBadge: "Pricing & specs refreshed "
190
+ updatedBadge: "Model pricing & specs from OpenRouter \u2014 refreshed "
191
+ },
192
+ howItWorks: {
193
+ label: "How it works",
194
+ heading: "Your assistant picks the right model, automatically",
195
+ subtitle: "index9 runs as an MCP (Model Context Protocol) server. Your editor already speaks MCP \u2014 index9 just plugs in as three extra tools your assistant can call.",
196
+ steps: [
197
+ {
198
+ number: "1",
199
+ title: "You ask your assistant",
200
+ body: '"Pick the cheapest model that can summarize this document well." You chat normally \u2014 no new UI to learn.'
201
+ },
202
+ {
203
+ number: "2",
204
+ title: "Your assistant calls index9",
205
+ body: "It runs find_models, get_models, and test_model against live OpenRouter data. Results come back with latency, tokens, and cost for your prompt."
206
+ },
207
+ {
208
+ number: "3",
209
+ title: "You get a measured recommendation",
210
+ body: "The assistant compares real outputs on your real prompt, then recommends the model that fits your constraints. Evidence, not guesswork."
211
+ }
212
+ ]
174
213
  },
175
214
  toolsSection: {
176
215
  label: "Tools",
177
- heading: "Search, test, and compare",
216
+ heading: "Search, inspect, and run live tests",
217
+ subheading: "Three MCP tools your assistant can call. Each maps to one clear job your assistant can do without leaving the chat.",
178
218
  openRouterKey: "OpenRouter API key (live tests only)",
179
219
  noKeyRequired: "No API key required",
180
220
  requiresLabel: "Requires ",
181
221
  cards: [
182
222
  {
183
223
  name: "find_models",
224
+ action: "Search",
184
225
  displayName: "find_models",
185
226
  fullName: null,
186
227
  description: `Search ${MODEL_COUNT} models by what you need \u2014 price, speed, context window, or capabilities like vision and function calling.`,
@@ -189,14 +230,16 @@ var SITE = {
189
230
  },
190
231
  {
191
232
  name: "get_models",
233
+ action: "Inspect",
192
234
  displayName: "get_models",
193
235
  fullName: null,
194
- description: "Get current pricing, limits, and capabilities for any model. Updated from OpenRouter every 30 minutes.",
236
+ description: "Get current pricing, limits, and capabilities for any model. Synced from OpenRouter every 30 minutes.",
195
237
  badge: null,
196
238
  requiresKey: false
197
239
  },
198
240
  {
199
241
  name: "test_model",
242
+ action: "Run live tests",
200
243
  displayName: "test_model",
201
244
  fullName: null,
202
245
  description: "Send your prompt to multiple models. Compare outputs, latency, and cost \u2014 measured, not estimated.",
@@ -234,7 +277,7 @@ var SITE = {
234
277
  },
235
278
  {
236
279
  question: "What's the project status?",
237
- answer: "index9 is in active development. The core tools are stable and ready for daily use, and improvements ship regularly. Issues and feedback are tracked on GitHub.",
280
+ answer: "index9 is live and used daily. Core tools are stable. Improvements ship through changesets on GitHub; issues and feedback are welcome there.",
238
281
  link: null
239
282
  },
240
283
  {
@@ -246,7 +289,8 @@ var SITE = {
246
289
  },
247
290
  install: {
248
291
  label: "Setup",
249
- heading: "Add to your MCP config",
292
+ heading: "Add index9 to your editor",
293
+ subheading: "One line of config for Cursor, VS Code, or Claude Code. Your assistant can start using it immediately.",
250
294
  configs: [
251
295
  {
252
296
  id: "cursor-vscode",
@@ -288,6 +332,7 @@ var SITE = {
288
332
  comparison: {
289
333
  label: "Comparison",
290
334
  heading: "Evidence over intuition",
335
+ subheading: "Benchmark on your real prompts \u2014 not someone else's.",
291
336
  withoutLabel: "Without index9",
292
337
  withLabel: "With index9",
293
338
  withoutItems: [
@@ -439,16 +484,16 @@ var SearchQuerySchema = z2.object({
439
484
  minPrice: z2.number().min(0).optional(),
440
485
  maxPrice: z2.number().min(0).optional(),
441
486
  minContext: z2.number().int().min(1).optional(),
442
- capabilitiesAll: z2.array(z2.string().min(1)).optional(),
443
- capabilitiesAny: z2.array(z2.string().min(1)).optional(),
444
- modality: z2.string().min(1).optional(),
487
+ capabilitiesAll: z2.array(z2.enum(CAPABILITIES)).optional(),
488
+ capabilitiesAny: z2.array(z2.enum(CAPABILITIES)).optional(),
489
+ modality: z2.enum(OUTPUT_MODALITIES).optional(),
445
490
  provider: z2.string().min(1).optional()
446
491
  }).strict();
447
492
  var SearchResultSchema = z2.object({
448
- modelId: z2.string(),
493
+ id: z2.string(),
449
494
  name: z2.string(),
450
495
  description: z2.string(),
451
- createdUnix: z2.number().nullable(),
496
+ created: z2.number().nullable(),
452
497
  createdAt: z2.string().nullable(),
453
498
  contextLength: z2.number().nullable(),
454
499
  maxOutputTokens: z2.number().nullable(),
@@ -480,7 +525,34 @@ var BatchModelLookupRequestSchema = z3.object({
480
525
  ids: z3.array(z3.string().min(1)).min(1, "ids are required").max(LIMITS.getModelsMax, `ids must contain between 1 and ${LIMITS.getModelsMax} model IDs`),
481
526
  maxDescriptionChars: z3.number().int().min(0).max(2e3).optional()
482
527
  }).strict();
483
- var ModelResponseSchema = z3.record(z3.string(), z3.unknown());
528
+ var ModelPricingSchema = z3.object({
529
+ promptPerMillion: z3.number().nullable(),
530
+ completionPerMillion: z3.number().nullable(),
531
+ requestUsd: z3.number().nullable(),
532
+ imageUsd: z3.number().nullable()
533
+ });
534
+ var ModelArchitectureSchema = z3.object({
535
+ inputModalities: z3.array(z3.string()),
536
+ outputModalities: z3.array(z3.string()),
537
+ tokenizer: z3.string().nullable(),
538
+ instructType: z3.string().nullable()
539
+ });
540
+ var ModelResponseSchema = z3.object({
541
+ id: z3.string(),
542
+ canonicalSlug: z3.string().nullable(),
543
+ name: z3.string(),
544
+ description: z3.string(),
545
+ created: z3.number().nullable(),
546
+ createdAt: z3.string().nullable(),
547
+ knowledgeCutoff: z3.string().nullable(),
548
+ contextLength: z3.number().nullable(),
549
+ maxOutputTokens: z3.number().nullable(),
550
+ isModerated: z3.boolean().nullable(),
551
+ pricing: ModelPricingSchema,
552
+ architecture: ModelArchitectureSchema,
553
+ capabilities: z3.array(z3.string()),
554
+ supportedParameters: z3.array(z3.string())
555
+ });
484
556
  var BatchModelLookupResponseSchema = z3.object({
485
557
  results: z3.array(ModelResponseSchema.nullable()),
486
558
  missingIds: z3.array(z3.string()),
@@ -540,7 +612,7 @@ var TestPricingUsedSchema = z4.object({
540
612
  var TestModelMetadataSchema = z4.object({
541
613
  id: z4.string(),
542
614
  name: z4.string(),
543
- createdUnix: z4.number().nullable().optional(),
615
+ created: z4.number().nullable().optional(),
544
616
  createdAt: z4.string().nullable().optional(),
545
617
  pricingUsed: TestPricingUsedSchema.optional()
546
618
  });
@@ -552,7 +624,8 @@ var TestResultSuccessSchema = z4.object({
552
624
  response: z4.string(),
553
625
  latencyMs: z4.number().min(0),
554
626
  tokens: UsageTokensSchema,
555
- cost: z4.number().nullable().optional()
627
+ cost: z4.number().nullable().optional(),
628
+ truncated: z4.boolean().optional()
556
629
  });
557
630
  var TestResultFailureSchema = z4.object({
558
631
  modelId: z4.string(),
@@ -830,9 +903,9 @@ async function createServer() {
830
903
  minPrice: z5.number().min(0).optional().describe("Minimum prompt price in USD per million tokens."),
831
904
  maxPrice: z5.number().min(0).optional().describe("Maximum prompt price in USD per million tokens."),
832
905
  minContext: z5.number().int().min(1).optional().describe("Minimum context window in tokens."),
833
- capabilitiesAll: z5.array(z5.string()).optional().describe(PARAM_DESCRIPTIONS.capabilitiesAll),
834
- capabilitiesAny: z5.array(z5.string()).optional().describe(PARAM_DESCRIPTIONS.capabilitiesAny),
835
- modality: z5.string().optional().describe(PARAM_DESCRIPTIONS.modality),
906
+ capabilitiesAll: z5.array(z5.enum(CAPABILITIES)).optional().describe(PARAM_DESCRIPTIONS.capabilitiesAll),
907
+ capabilitiesAny: z5.array(z5.enum(CAPABILITIES)).optional().describe(PARAM_DESCRIPTIONS.capabilitiesAny),
908
+ modality: z5.enum(OUTPUT_MODALITIES).optional().describe(PARAM_DESCRIPTIONS.modality),
836
909
  provider: z5.string().min(1).optional().describe(PARAM_DESCRIPTIONS.provider)
837
910
  },
838
911
  annotations: { readOnlyHint: true }
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": "0.3",
3
3
  "name": "index9",
4
- "version": "4.0.1",
4
+ "version": "4.0.2",
5
5
  "description": "Search, inspect, and benchmark 300+ AI models from your editor",
6
6
  "author": {
7
7
  "name": "Index9"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@index9/mcp",
3
- "version": "4.0.2",
3
+ "version": "5.0.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",