@index9/mcp 5.0.0 → 5.2.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
@@ -31,6 +31,16 @@ var COMMON_PROVIDERS = [
31
31
  // ../core/dist/schemas/common.js
32
32
  import { z } from "zod";
33
33
  var ErrorResponseSchema = z.object({ error: z.string() }).strict();
34
+ var RateLimitMetaSchema = z.object({
35
+ limit: z.string().optional(),
36
+ remaining: z.string().optional(),
37
+ reset: z.string().optional()
38
+ });
39
+ var Index9MetaSchema = z.object({
40
+ apiBaseUrl: z.string(),
41
+ retryAfterSeconds: z.number().optional(),
42
+ rateLimit: RateLimitMetaSchema.optional()
43
+ });
34
44
  var UserContentTextPartSchema = z.strictObject({
35
45
  type: z.literal("text"),
36
46
  text: z.string().trim().min(1)
@@ -68,56 +78,58 @@ var LIMITS = {
68
78
  var MODEL_COUNT = "300+";
69
79
  var WORKFLOW_INSTRUCTIONS = `Index9 provides 3 tools for AI model discovery, inspection, and benchmarking.
70
80
 
81
+ IMPORTANT \u2014 your model knowledge is stale by default.
82
+ New AI models ship weekly; pricing, aliases, and capabilities change. Treat any specific model ID, "flagship" name, or "good default" you recall from training as potentially out of date. Before naming, recommending, or benchmarking models, call find_models to anchor on what is actually live right now.
83
+
84
+ When a user asks you to recommend or pick specific models, you MUST call find_models at least once with \`sortBy=created\` (newest first, no q) before committing to a shortlist. Sorting purely by price or using a semantic q alone returns the cheapest-ever cache entries \u2014 which are often superseded by newer cheap models you should have considered. A good pattern:
85
+ 1. find_models sortBy=created, limit=10 \u2014 see what has landed recently (last weeks/months).
86
+ 2. Optional second call: find_models with the task query or price filter to surface established options.
87
+ 3. Cross-reference and pick 2-4 candidates that span recent + established.
88
+
89
+ Skip the anchor call only when the user has typed a specific provider/model-id.
90
+
71
91
  Typical workflow:
72
- 1. **find_models** \u2014 Discover models by semantic query or filters. Start here when the user needs to find models matching criteria.
73
- 2. **get_models** \u2014 Get full metadata for specific model IDs or aliases. Use after search to inspect details, or directly when the user names a specific model.
74
- 3. **test_model** \u2014 Run live inference, or set dryRun=true to estimate token usage/cost without running inference.
92
+ 1. find_models \u2014 Discover models by semantic query or filters. Start here unless the user named a specific model.
93
+ 2. get_models \u2014 Full metadata for specific IDs or aliases. Use after search, or directly when the user names a model.
94
+ 3. test_model \u2014 Run live inference, or set dryRun=true to estimate token usage/cost without running inference.
75
95
 
76
96
  Key rules:
77
97
  - find_models requires \`q\` when \`sortBy=relevance\` (the default). Omit \`q\` only with \`sortBy=created\` or \`sortBy=price\`.
78
- - get_models accepts aliases (display names, short names) \u2014 not just full IDs.
98
+ - get_models accepts aliases (display names, short names) \u2014 not just full IDs. If a model you "know from memory" returns in missingIds, that is a signal to call find_models.
79
99
  - Use test_model with \`dryRun=true\` to estimate cost before live testing.
80
100
  - test_model with \`dryRun=false\` (default) requires OPENROUTER_API_KEY and incurs real usage costs.
101
+ - Reasoning-capable models (capabilities includes "reasoning") burn hidden reasoning tokens against \`maxTokens\` before emitting visible text. Leave \`maxTokens\` unset, or set it to at least 2000, when testing reasoning models \u2014 otherwise results may fail with finish_reason=length.
81
102
  - Cursors are opaque and tied to query/sort/filters. Reuse the same query/sort/filters when paginating. \`limit\` may change between pages.`;
82
103
  var TOOLS = {
83
104
  find_models: {
84
105
  name: "find_models",
106
+ title: "Search AI models",
85
107
  summary: "Search and paginate AI models by semantic query or filters",
86
108
  description: `Search and filter ${MODEL_COUNT} AI models. Returns ranked results with pricing, context windows, and capabilities.
87
109
 
88
- Call this tool first to discover model IDs, unless the user provides one (format: 'provider/model-name').
110
+ Call this first unless the user named a specific model \u2014 your training-data list of "good" models is likely stale. When recommending models to the user, always include one call with sortBy=created (no q) to see what has shipped recently; sorting purely by price surfaces old tiny models and misses this month's cheap frontier.
89
111
 
90
- IMPORTANT \u2014 Extract filters from user queries:
91
- When the user mentions numeric or categorical constraints, you MUST map them to the structured filter parameters below instead of relying solely on \`q\`. The \`q\` parameter drives semantic ranking but does NOT enforce hard constraints.
112
+ Extract filters from user queries. \`q\` drives semantic ranking; numeric and categorical constraints MUST go in structured filters. Shorthand: 1K=1000, 1M=1000000. Prices are USD per million input tokens.
92
113
 
93
114
  Examples:
94
- - "model with 1M context under $1" \u2192 q="model", minContext=1000000, maxPrice=1
95
- - "cheap vision model from openai" \u2192 q="cheap vision model", capabilitiesAll="vision", provider="openai"
96
- - "function calling under $0.50 with 128K context" \u2192 q="function calling", capabilitiesAll="function_calling", maxPrice=0.5, minContext=128000
97
- - "best coding model" \u2192 q="best coding model" (no filters needed)
98
-
99
- Convert shorthand: 1K=1000, 1M=1000000, 128K=128000. Prices are in USD per million input tokens.
115
+ - "1M context under $1" \u2192 q="model", minContext=1000000, maxPrice=1
116
+ - "cheap vision model from openai" \u2192 q="cheap vision model", capabilitiesAll=["vision"], provider="openai"
117
+ - "function calling under $0.50 with 128K" \u2192 q="function calling", capabilitiesAll=["function_calling"], maxPrice=0.5, minContext=128000
118
+ - "best coding model" \u2192 q="best coding model"
119
+ - "what's new" \u2192 sortBy="created" (no q needed)
100
120
 
101
- Parameters:
102
- - q: Semantic search query \u2014 use for intent/quality ranking, not for numeric constraints
103
- - provider: Filter by provider(s). Comma-separated for multiple (e.g., 'openai,anthropic')
104
- - minPrice, maxPrice: Price bounds in USD per million input tokens
105
- - minContext: Minimum context window in tokens
106
- - capabilitiesAll: Capabilities the model MUST have (AND logic). Valid: ${CAPABILITIES.join(", ")}
107
- - capabilitiesAny: Capabilities where at least one must be present (OR logic)
108
- - sortBy: 'relevance' (default), 'created', 'price'
109
- - limit: Page size
110
- - cursor: Opaque pagination cursor
121
+ Valid capabilities: ${CAPABILITIES.join(", ")}.
111
122
 
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.
123
+ Each result: id, name, description, created (unix seconds), createdAt (ISO 8601), contextLength, maxOutputTokens, pricing.{promptPerMillion, completionPerMillion} (numbers, USD per million tokens), capabilities[], score.
113
124
 
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.
125
+ \`score\` is 0-100: the best match per page scores 100; others scale proportionally. Combines semantic similarity and keyword matching. Null when sorting by price or date.
115
126
 
116
127
  Pass result.id to get_models for full specs or to test_model for live testing.`,
117
128
  requiresKey: false
118
129
  },
119
130
  get_models: {
120
131
  name: "get_models",
132
+ title: "Inspect AI models",
121
133
  summary: "Get full model metadata by IDs or aliases (batch, up to 100)",
122
134
  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
135
 
@@ -137,6 +149,7 @@ Entries in results are null when the id is unknown; those ids appear in missingI
137
149
  },
138
150
  test_model: {
139
151
  name: "test_model",
152
+ title: "Test AI models live",
140
153
  summary: "Run live inference or dry-run cost estimation across up to 10 models",
141
154
  description: `Run model tests on 1-${LIMITS.testModelsMax} models. Use dryRun=true to estimate token usage/cost, or dryRun=false (default) to run live OpenRouter inference.
142
155
 
@@ -518,6 +531,9 @@ var SearchResponseSchema = z2.object({
518
531
  ranking: z2.literal("hybrid_rrf")
519
532
  })
520
533
  });
534
+ var FindModelsToolResultSchema = SearchResponseSchema.extend({
535
+ _index9: Index9MetaSchema
536
+ });
521
537
 
522
538
  // ../core/dist/schemas/model.js
523
539
  import { z as z3 } from "zod";
@@ -559,6 +575,13 @@ var BatchModelLookupResponseSchema = z3.object({
559
575
  resolvedAliases: z3.record(z3.string(), z3.string()).optional(),
560
576
  ambiguousAliases: z3.record(z3.string(), z3.array(z3.string())).optional()
561
577
  }).strict();
578
+ var GetModelsToolResultSchema = z3.object({
579
+ results: z3.array(ModelResponseSchema.nullable()),
580
+ missingIds: z3.array(z3.string()),
581
+ resolvedAliases: z3.record(z3.string(), z3.string()).optional(),
582
+ ambiguousAliases: z3.record(z3.string(), z3.array(z3.string())).optional(),
583
+ _index9: Index9MetaSchema
584
+ });
562
585
 
563
586
  // ../core/dist/schemas/test.js
564
587
  import { z as z4 } from "zod";
@@ -756,10 +779,14 @@ function baseHeaders(ctx) {
756
779
  return h;
757
780
  }
758
781
  function toResponse(payload, isError = false) {
759
- return {
782
+ const response = {
760
783
  content: [{ type: "text", text: JSON.stringify(payload) }],
761
784
  isError: isError || void 0
762
785
  };
786
+ if (!isError && typeof payload === "object" && payload !== null && !Array.isArray(payload)) {
787
+ response.structuredContent = payload;
788
+ }
789
+ return response;
763
790
  }
764
791
  function parseRetryAfterSeconds(value) {
765
792
  if (!value) return void 0;
@@ -891,6 +918,7 @@ async function createServer() {
891
918
  server.registerTool(
892
919
  "find_models",
893
920
  {
921
+ title: TOOLS.find_models.title,
894
922
  description: TOOLS.find_models.description,
895
923
  inputSchema: {
896
924
  q: z5.string().min(1).optional().describe(PARAM_DESCRIPTIONS.q),
@@ -908,6 +936,7 @@ async function createServer() {
908
936
  modality: z5.enum(OUTPUT_MODALITIES).optional().describe(PARAM_DESCRIPTIONS.modality),
909
937
  provider: z5.string().min(1).optional().describe(PARAM_DESCRIPTIONS.provider)
910
938
  },
939
+ outputSchema: FindModelsToolResultSchema.shape,
911
940
  annotations: { readOnlyHint: true }
912
941
  },
913
942
  async (args) => handleSearchModels(ctx, args)
@@ -915,11 +944,13 @@ async function createServer() {
915
944
  server.registerTool(
916
945
  "get_models",
917
946
  {
947
+ title: TOOLS.get_models.title,
918
948
  description: TOOLS.get_models.description,
919
949
  inputSchema: {
920
950
  ids: z5.array(z5.string().min(1)).min(1).max(100).describe("Model identifiers or aliases. Up to 100."),
921
951
  maxDescriptionChars: z5.number().int().min(0).max(2e3).optional().describe("Truncate descriptions to this many characters.")
922
952
  },
953
+ outputSchema: GetModelsToolResultSchema.shape,
923
954
  annotations: { readOnlyHint: true }
924
955
  },
925
956
  async (args) => handleGetModels(ctx, args)
@@ -927,6 +958,7 @@ async function createServer() {
927
958
  server.registerTool(
928
959
  "test_model",
929
960
  {
961
+ title: TOOLS.test_model.title,
930
962
  description: TOOLS.test_model.description,
931
963
  inputSchema: {
932
964
  prompt: z5.string().min(1).optional().describe("Prompt sent to each model."),
@@ -937,7 +969,9 @@ async function createServer() {
937
969
  expectedCompletionTokens: z5.number().int().min(1).optional().describe(PARAM_DESCRIPTIONS.expectedCompletionTokens),
938
970
  models: z5.array(z5.string().min(1)).min(1).max(LIMITS.testModelsMax).describe(`Model IDs to evaluate (1-${LIMITS.testModelsMax}).`),
939
971
  timeoutMs: z5.number().int().min(1).optional().describe("Per-model timeout in ms (default 15000, max 60000)."),
940
- maxTokens: z5.number().int().min(1).optional().describe("Completion token cap."),
972
+ maxTokens: z5.number().int().min(1).optional().describe(
973
+ "Completion token cap. For reasoning-capable models, set \u2265 2000 (or omit) \u2014 reasoning tokens count against this before visible output, and too-low caps cause finish_reason=length."
974
+ ),
941
975
  systemPrompt: z5.string().min(1).optional().describe("System instruction prepended to prompt."),
942
976
  temperature: z5.number().min(0).max(2).optional().describe("Sampling temperature (0-2)."),
943
977
  topP: z5.number().gt(0).max(1).optional().describe("Nucleus sampling (0-1]."),
@@ -948,6 +982,9 @@ async function createServer() {
948
982
  enforceJson: z5.boolean().optional().describe("When true, output must parse as JSON."),
949
983
  retries: z5.number().int().min(0).max(3).optional().describe("Retries for transient failures.")
950
984
  },
985
+ // No outputSchema: test_model returns a z.union of dry-run and live shapes.
986
+ // The SDK supports only ZodRawShape | AnySchema for outputSchema; a discriminated-union
987
+ // output is represented accurately in the tool description instead.
951
988
  annotations: { readOnlyHint: false }
952
989
  },
953
990
  async (args) => handleTestModels(ctx, args)
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": "0.3",
3
3
  "name": "index9",
4
- "version": "4.0.2",
4
+ "version": "5.1.0",
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": "5.0.0",
3
+ "version": "5.2.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",