@index9/mcp 5.0.0 → 5.1.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,54 @@ 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. Good first calls:
83
+ - sortBy=created (newest first) to see what has recently landed
84
+ - a semantic query describing the task to see current task-fit candidates
85
+ Skip find_models only when the user has typed a specific provider/model-id.
86
+
71
87
  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.
88
+ 1. find_models \u2014 Discover models by semantic query or filters. Start here unless the user named a specific model.
89
+ 2. get_models \u2014 Full metadata for specific IDs or aliases. Use after search, or directly when the user names a model.
90
+ 3. test_model \u2014 Run live inference, or set dryRun=true to estimate token usage/cost without running inference.
75
91
 
76
92
  Key rules:
77
93
  - 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.
94
+ - 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
95
  - Use test_model with \`dryRun=true\` to estimate cost before live testing.
80
96
  - test_model with \`dryRun=false\` (default) requires OPENROUTER_API_KEY and incurs real usage costs.
97
+ - 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
98
  - Cursors are opaque and tied to query/sort/filters. Reuse the same query/sort/filters when paginating. \`limit\` may change between pages.`;
82
99
  var TOOLS = {
83
100
  find_models: {
84
101
  name: "find_models",
102
+ title: "Search AI models",
85
103
  summary: "Search and paginate AI models by semantic query or filters",
86
104
  description: `Search and filter ${MODEL_COUNT} AI models. Returns ranked results with pricing, context windows, and capabilities.
87
105
 
88
- Call this tool first to discover model IDs, unless the user provides one (format: 'provider/model-name').
106
+ Call this first unless the user named a specific model \u2014 your training-data list of "good" models is likely stale.
89
107
 
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.
108
+ 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
109
 
93
110
  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)
111
+ - "1M context under $1" \u2192 q="model", minContext=1000000, maxPrice=1
112
+ - "cheap vision model from openai" \u2192 q="cheap vision model", capabilitiesAll=["vision"], provider="openai"
113
+ - "function calling under $0.50 with 128K" \u2192 q="function calling", capabilitiesAll=["function_calling"], maxPrice=0.5, minContext=128000
114
+ - "best coding model" \u2192 q="best coding model"
115
+ - "what's new" \u2192 sortBy="created" (no q needed)
98
116
 
99
- Convert shorthand: 1K=1000, 1M=1000000, 128K=128000. Prices are in USD per million input tokens.
117
+ Valid capabilities: ${CAPABILITIES.join(", ")}.
100
118
 
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
119
+ Each result: id, name, description, created (unix seconds), createdAt (ISO 8601), contextLength, maxOutputTokens, pricing.{promptPerMillion, completionPerMillion} (numbers, USD per million tokens), capabilities[], score.
111
120
 
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
-
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.
121
+ \`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
122
 
116
123
  Pass result.id to get_models for full specs or to test_model for live testing.`,
117
124
  requiresKey: false
118
125
  },
119
126
  get_models: {
120
127
  name: "get_models",
128
+ title: "Inspect AI models",
121
129
  summary: "Get full model metadata by IDs or aliases (batch, up to 100)",
122
130
  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
131
 
@@ -137,6 +145,7 @@ Entries in results are null when the id is unknown; those ids appear in missingI
137
145
  },
138
146
  test_model: {
139
147
  name: "test_model",
148
+ title: "Test AI models live",
140
149
  summary: "Run live inference or dry-run cost estimation across up to 10 models",
141
150
  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
151
 
@@ -518,6 +527,9 @@ var SearchResponseSchema = z2.object({
518
527
  ranking: z2.literal("hybrid_rrf")
519
528
  })
520
529
  });
530
+ var FindModelsToolResultSchema = SearchResponseSchema.extend({
531
+ _index9: Index9MetaSchema
532
+ });
521
533
 
522
534
  // ../core/dist/schemas/model.js
523
535
  import { z as z3 } from "zod";
@@ -559,6 +571,13 @@ var BatchModelLookupResponseSchema = z3.object({
559
571
  resolvedAliases: z3.record(z3.string(), z3.string()).optional(),
560
572
  ambiguousAliases: z3.record(z3.string(), z3.array(z3.string())).optional()
561
573
  }).strict();
574
+ var GetModelsToolResultSchema = z3.object({
575
+ results: z3.array(ModelResponseSchema.nullable()),
576
+ missingIds: z3.array(z3.string()),
577
+ resolvedAliases: z3.record(z3.string(), z3.string()).optional(),
578
+ ambiguousAliases: z3.record(z3.string(), z3.array(z3.string())).optional(),
579
+ _index9: Index9MetaSchema
580
+ });
562
581
 
563
582
  // ../core/dist/schemas/test.js
564
583
  import { z as z4 } from "zod";
@@ -756,10 +775,14 @@ function baseHeaders(ctx) {
756
775
  return h;
757
776
  }
758
777
  function toResponse(payload, isError = false) {
759
- return {
778
+ const response = {
760
779
  content: [{ type: "text", text: JSON.stringify(payload) }],
761
780
  isError: isError || void 0
762
781
  };
782
+ if (!isError && typeof payload === "object" && payload !== null && !Array.isArray(payload)) {
783
+ response.structuredContent = payload;
784
+ }
785
+ return response;
763
786
  }
764
787
  function parseRetryAfterSeconds(value) {
765
788
  if (!value) return void 0;
@@ -891,6 +914,7 @@ async function createServer() {
891
914
  server.registerTool(
892
915
  "find_models",
893
916
  {
917
+ title: TOOLS.find_models.title,
894
918
  description: TOOLS.find_models.description,
895
919
  inputSchema: {
896
920
  q: z5.string().min(1).optional().describe(PARAM_DESCRIPTIONS.q),
@@ -908,6 +932,7 @@ async function createServer() {
908
932
  modality: z5.enum(OUTPUT_MODALITIES).optional().describe(PARAM_DESCRIPTIONS.modality),
909
933
  provider: z5.string().min(1).optional().describe(PARAM_DESCRIPTIONS.provider)
910
934
  },
935
+ outputSchema: FindModelsToolResultSchema.shape,
911
936
  annotations: { readOnlyHint: true }
912
937
  },
913
938
  async (args) => handleSearchModels(ctx, args)
@@ -915,11 +940,13 @@ async function createServer() {
915
940
  server.registerTool(
916
941
  "get_models",
917
942
  {
943
+ title: TOOLS.get_models.title,
918
944
  description: TOOLS.get_models.description,
919
945
  inputSchema: {
920
946
  ids: z5.array(z5.string().min(1)).min(1).max(100).describe("Model identifiers or aliases. Up to 100."),
921
947
  maxDescriptionChars: z5.number().int().min(0).max(2e3).optional().describe("Truncate descriptions to this many characters.")
922
948
  },
949
+ outputSchema: GetModelsToolResultSchema.shape,
923
950
  annotations: { readOnlyHint: true }
924
951
  },
925
952
  async (args) => handleGetModels(ctx, args)
@@ -927,6 +954,7 @@ async function createServer() {
927
954
  server.registerTool(
928
955
  "test_model",
929
956
  {
957
+ title: TOOLS.test_model.title,
930
958
  description: TOOLS.test_model.description,
931
959
  inputSchema: {
932
960
  prompt: z5.string().min(1).optional().describe("Prompt sent to each model."),
@@ -937,7 +965,9 @@ async function createServer() {
937
965
  expectedCompletionTokens: z5.number().int().min(1).optional().describe(PARAM_DESCRIPTIONS.expectedCompletionTokens),
938
966
  models: z5.array(z5.string().min(1)).min(1).max(LIMITS.testModelsMax).describe(`Model IDs to evaluate (1-${LIMITS.testModelsMax}).`),
939
967
  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."),
968
+ maxTokens: z5.number().int().min(1).optional().describe(
969
+ "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."
970
+ ),
941
971
  systemPrompt: z5.string().min(1).optional().describe("System instruction prepended to prompt."),
942
972
  temperature: z5.number().min(0).max(2).optional().describe("Sampling temperature (0-2)."),
943
973
  topP: z5.number().gt(0).max(1).optional().describe("Nucleus sampling (0-1]."),
@@ -948,6 +978,9 @@ async function createServer() {
948
978
  enforceJson: z5.boolean().optional().describe("When true, output must parse as JSON."),
949
979
  retries: z5.number().int().min(0).max(3).optional().describe("Retries for transient failures.")
950
980
  },
981
+ // No outputSchema: test_model returns a z.union of dry-run and live shapes.
982
+ // The SDK supports only ZodRawShape | AnySchema for outputSchema; a discriminated-union
983
+ // output is represented accurately in the tool description instead.
951
984
  annotations: { readOnlyHint: false }
952
985
  },
953
986
  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.0.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.1.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",