@index9/mcp 6.0.0 → 6.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 +117 -28
- package/manifest.json +1 -1
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -43,6 +43,16 @@ var Index9MetaSchema = z.object({
|
|
|
43
43
|
retryAfterSeconds: z.number().optional(),
|
|
44
44
|
rateLimit: RateLimitMetaSchema.optional()
|
|
45
45
|
});
|
|
46
|
+
var MissingModelDiagnosticSchema = z.object({
|
|
47
|
+
reason: z.enum(["unknown_provider", "no_match", "suggestions_available", "ambiguous_alias"]),
|
|
48
|
+
provider: z.string().optional(),
|
|
49
|
+
message: z.string()
|
|
50
|
+
});
|
|
51
|
+
var SuggestionEntrySchema = z.object({
|
|
52
|
+
id: z.string(),
|
|
53
|
+
name: z.string(),
|
|
54
|
+
created: z.number().nullable()
|
|
55
|
+
});
|
|
46
56
|
var UserContentTextPartSchema = z.strictObject({
|
|
47
57
|
type: z.literal("text"),
|
|
48
58
|
text: z.string().trim().min(1)
|
|
@@ -101,9 +111,11 @@ Typical workflow:
|
|
|
101
111
|
Key rules:
|
|
102
112
|
- find_models requires \`q\` when \`sortBy=relevance\` (the default). Omit \`q\` only with \`sortBy=created\` or \`sortBy=price\`.
|
|
103
113
|
- find_models price-asc tends to be dominated by free preview models \u2014 pass \`excludeFree=true\` when you want a paid SLA.
|
|
104
|
-
- find_models
|
|
105
|
-
-
|
|
114
|
+
- find_models always emits \`meta.confidence\` ("high" | "low") on semantic queries. Low means no candidate matched on keyword (BM25); \`meta.lowConfidenceReason\` is "no_keyword_matches" or "no_results" and \`meta.suggestion\` carries an actionable hint. Weak hits are capped at score=30 so they don't masquerade as strong matches. Pass \`requireKeywordMatch: true\` to get an empty page instead of weak vector-only neighbors.
|
|
115
|
+
- find_models with sortBy=price exposes \`pricing.effectivePromptPerMillion\` and \`pageInfo.priceSortBasis\` \u2014 sort order may diverge from displayed promptPerMillion for models with per-request fees.
|
|
116
|
+
- Your training-data model IDs are routinely stale. get_models / compare_models / test_model all accept aliases (display names, short names) and return unknown ids in \`missingIds\` with \`suggestions[id]\` ordered newest-first (each entry: \`{id, name, created}\`, where \`created\` is unix seconds), plus \`missingDiagnostics[id].reason\` \u2208 {"unknown_provider", "no_match", "suggestions_available", "ambiguous_alias"}. **Default recovery: retry with \`suggestions[id][0].id\` \u2014 it's the newest viable replacement.** If suggestions is empty or reason="no_match"/"unknown_provider", fall back to \`find_models sortBy=created\` instead.
|
|
106
117
|
- compare_models accepts the same alias formats as get_models. Use it instead of N parallel get_models calls when the user is comparing finalists.
|
|
118
|
+
- test_model pre-flight resolves and filters unresolvable ids out of the OpenRouter call, so stale ids never cost you credits \u2014 they come back in missingIds with the same suggestions/diagnostics surface as get_models. If every id is unresolvable, the call returns 400 with diagnostics and no inference fires.
|
|
107
119
|
- Use test_model with \`dryRun=true\` to estimate cost before live testing. Pass \`expectedPromptTokens\` for capacity planning at sizes you don't want to paste in full.
|
|
108
120
|
- test_model with \`dryRun=false\` (default) requires OPENROUTER_API_KEY and incurs real usage costs.
|
|
109
121
|
- 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.
|
|
@@ -129,11 +141,11 @@ Examples:
|
|
|
129
141
|
|
|
130
142
|
Valid capabilities: ${CAPABILITIES.join(", ")}.
|
|
131
143
|
|
|
132
|
-
Each result: id, name, description, created (unix seconds), createdAt (ISO 8601), contextLength, maxOutputTokens, pricing.{promptPerMillion, completionPerMillion} (
|
|
144
|
+
Each result: id, name, description, created (unix seconds), createdAt (ISO 8601), contextLength, maxOutputTokens, pricing.{promptPerMillion, completionPerMillion} (rounded display $/M), pricing.{promptPerToken, completionPerToken, requestUsd} (exact, use for cost math), inputModalities[] / outputModalities[], capabilities[], score. With sortBy=price, results also expose pricing.effectivePromptPerMillion and pageInfo.priceSortBasis \u2014 sort order may diverge from displayed promptPerMillion for models with per-request fees.
|
|
133
145
|
|
|
134
146
|
\`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.
|
|
135
147
|
|
|
136
|
-
\`q\` must be at least 2 characters when provided. \`meta.confidence\` is "low"
|
|
148
|
+
\`q\` must be at least 2 characters when provided. For semantic queries, \`meta.confidence\` is always emitted as "high" or "low". Low means no candidate matched on keyword (BM25); \`meta.lowConfidenceReason\` is "no_keyword_matches" or "no_results" and \`meta.suggestion\` carries an actionable hint. Pass \`requireKeywordMatch: true\` to suppress weak hits and get an empty page on low confidence.
|
|
137
149
|
|
|
138
150
|
Pass result.id to get_models for full specs or to test_model for live testing.`,
|
|
139
151
|
requiresKey: false
|
|
@@ -146,16 +158,18 @@ Pass result.id to get_models for full specs or to test_model for live testing.`,
|
|
|
146
158
|
|
|
147
159
|
Call after find_models to inspect candidates, or directly when the user names a model (format: 'provider/model-name').
|
|
148
160
|
|
|
149
|
-
Response: { results: (Model | null)[], missingIds: string[], resolvedAliases?: Record<alias, canonicalId>, ambiguousAliases?: Record<alias, candidateIds[]>, suggestions?: Record<unknownId,
|
|
161
|
+
Response: { results: (Model | null)[], missingIds: string[], resolvedAliases?: Record<alias, canonicalId>, ambiguousAliases?: Record<alias, candidateIds[]>, suggestions?: Record<unknownId, Array<{id, name, created}>> }. Each non-null result has:
|
|
150
162
|
- id, canonicalSlug, name, description
|
|
151
163
|
- created (unix seconds), createdAt (ISO 8601), knowledgeCutoff (ISO date or null)
|
|
152
164
|
- contextLength (tokens), maxOutputTokens, isModerated
|
|
153
|
-
- pricing: { promptPerMillion, completionPerMillion, requestUsd, imageUsd } \u2014
|
|
165
|
+
- pricing: { promptPerMillion, completionPerMillion, promptPerToken, completionPerToken, requestUsd, imageUsd } \u2014 *PerMillion is rounded display, *PerToken is exact (use for cost math). request/image are flat per-unit fees.
|
|
154
166
|
- architecture: { inputModalities[], outputModalities[], tokenizer, instructType }
|
|
155
167
|
- capabilities[]: normalized capability flags (same values as find_models and capabilitiesAll/Any)
|
|
156
168
|
- supportedParameters[]: OpenRouter parameters the model accepts (e.g., "temperature", "tools", "response_format")
|
|
157
169
|
|
|
158
|
-
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. Unknown ids that partially match (e.g. "sonnet" \u2192 all Claude Sonnet variants) appear in suggestions
|
|
170
|
+
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. Unknown ids that partially match (e.g. "sonnet" \u2192 all Claude Sonnet variants) appear in \`suggestions\` as up to 5 \`{id, name, created}\` entries **sorted newest-first** \u2014 pick \`suggestions[id][0].id\` for the most current replacement without a second lookup. When token-overlap finds nothing but the id is shaped like \`provider/<unknown>\` and the provider exists, suggestions falls back to the 5 newest models from that provider (real created timestamps, no hardcoded "popular" list).
|
|
171
|
+
|
|
172
|
+
\`missingDiagnostics\` (when present) gives a machine-readable reason per missing id: \`unknown_provider\` (the prefix before / isn't in the catalog \u2014 fix the provider, not the model name), \`ambiguous_alias\`, \`suggestions_available\` (mirrors suggestions[id]), or \`no_match\`.`,
|
|
159
173
|
requiresKey: false
|
|
160
174
|
},
|
|
161
175
|
compare_models: {
|
|
@@ -166,13 +180,13 @@ Entries in results are null when the id is unknown; those ids appear in missingI
|
|
|
166
180
|
|
|
167
181
|
Use this when the user asks "which is cheaper / has more context / supports X" across multiple specific models. Faster than calling get_models and diffing yourself.
|
|
168
182
|
|
|
169
|
-
Response: { models: ModelResponse[], diff: { contextLength, maxOutputTokens, promptPricePerMillion, completionPricePerMillion, tokenizer, inputModalities, outputModalities, capabilities, supportedParameters }, cheapestForPromptPerMillion, largestContext, missingIds, resolvedAliases?, ambiguousAliases?, suggestions? }.
|
|
183
|
+
Response: { models: ModelResponse[], diff: { contextLength, maxOutputTokens, promptPricePerMillion, completionPricePerMillion, tokenizer, inputModalities, outputModalities, capabilities, supportedParameters }, cheapestForPromptPerMillion, largestContext, missingIds, resolvedAliases?, ambiguousAliases?, suggestions?: Record<unknownId, Array<{id, name, created}>> (newest-first), missingDiagnostics? }.
|
|
170
184
|
|
|
171
185
|
Each numeric/string diff field has { allEqual: boolean, values: Record<id, value|null> }. Capability/parameter diffs have { commonAll: string[], uniquePerModel: Record<id, string[]> }. cheapestForPromptPerMillion / largestContext are convenience picks across the supplied models \u2014 null when the field is missing on every model.
|
|
172
186
|
|
|
173
|
-
Optional: pass \`expectedPromptTokens\` AND \`expectedCompletionTokens\` to also receive \`workloadCosts\`
|
|
187
|
+
Optional: pass \`expectedPromptTokens\` AND \`expectedCompletionTokens\` to also receive \`workloadCosts\` and \`cheapestForRealisticWorkload\` \u2014 the actual cheapest given the user's expected token mix. Each \`workloadCosts[i]\` carries \`tokenCostUsd\` (token-only), \`requestCostUsd\` (per-request fee), \`totalCostUsd\` (sum, includes request fees), and \`pricingBasis\` ("exact_per_token" | "rounded_per_million" | "unavailable"). This matters when prompt:completion price ratios diverge across models, or when a model has a per-request fee.
|
|
174
188
|
|
|
175
|
-
Accepts the same alias formats as get_models. Unknown ids are returned in missingIds (with suggestions when partial matches exist).`,
|
|
189
|
+
Accepts the same alias formats as get_models. Unknown ids are returned in missingIds (with \`suggestions[id]\` as newest-first \`{id, name, created}\` entries when partial matches exist, plus \`missingDiagnostics\` carrying a machine-readable reason per id). When fewer than 2 ids resolve, this returns 400 with the diagnostics so you can retry with \`suggestions[id][0].id\` for each missing id.`,
|
|
176
190
|
requiresKey: false
|
|
177
191
|
},
|
|
178
192
|
list_facets: {
|
|
@@ -206,7 +220,11 @@ Parameters:
|
|
|
206
220
|
- expectedCompletionTokens: Optional completion token estimate used by dryRun
|
|
207
221
|
- maxTokens, systemPrompt, temperature, topP, seed, responseFormat, enforceJson, retries: Live-testing controls (ignored when dryRun=true)
|
|
208
222
|
|
|
209
|
-
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".
|
|
223
|
+
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". On failure, results include \`error\` (free-form) plus \`failureReason\` ("insufficient_credits" | "model_unavailable" | "rate_limited" | "timeout" | "invalid_request" | "unknown") so callers can pick a retry strategy without parsing the error string.
|
|
224
|
+
|
|
225
|
+
Results (dryRun): each entry carries \`tokenCostUsd\`, \`requestCostUsd\`, \`totalCostUsd\` (matches \`estimatedCost\`, includes per-request fees), and \`estimatedCostBasis\` (same enum as compare_models.workloadCosts). Use find_models or get_models first to identify model ids.
|
|
226
|
+
|
|
227
|
+
Stale-id recovery: unresolvable model ids are filtered out **before** any OpenRouter call (so they cost nothing) and returned in \`missingIds\` alongside \`suggestions\` (newest-first \`{id, name, created}\` entries), \`resolvedAliases\`, \`ambiguousAliases\`, and \`missingDiagnostics\` \u2014 same shape as get_models / compare_models. If every id is unresolvable, the call returns 400 with diagnostics and no inference fires. Default recovery: retry with \`suggestions[id][0].id\`.`,
|
|
210
228
|
requiresKey: true
|
|
211
229
|
}
|
|
212
230
|
};
|
|
@@ -219,6 +237,7 @@ var PARAM_DESCRIPTIONS = {
|
|
|
219
237
|
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(", ")}.`,
|
|
220
238
|
provider: `Provider prefix filter. Array of provider slugs \u2014 a model matches if its ID starts with any of them (e.g., ['openai'] matches 'openai/gpt-4o'; ['openai','anthropic'] matches both). Pass a single-element array for one provider. Common providers: ${COMMON_PROVIDERS.join(", ")}.`,
|
|
221
239
|
excludeFree: `When true, exclude models with id ending in ':free'. Useful for sortBy=price (which would otherwise be dominated by free-tier preview models) and when you want a paid SLA. Default false.`,
|
|
240
|
+
requireKeywordMatch: `When true, suppress weak vector-only results from semantic queries. If no candidate has a BM25 keyword hit, returns an empty page with meta.confidence='low' and meta.lowConfidenceReason \u2014 instead of returning misleading nearest-neighbor matches. Filter-only queries (sortBy=created or sortBy=price without q) ignore this flag. Default false.`,
|
|
222
241
|
expectedPromptTokens: `Expected number of prompt tokens for dryRun cost estimation. When set, overrides the heuristic that counts characters from the literal \`prompt\` string \u2014 use this for capacity planning ("what would 6000-token reviews cost?") without pasting filler. If both are omitted, the prompt string is tokenized at ~4 chars/token.`,
|
|
223
242
|
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.`
|
|
224
243
|
};
|
|
@@ -556,7 +575,8 @@ var SearchQuerySchema = z2.object({
|
|
|
556
575
|
capabilitiesAny: z2.array(z2.enum(CAPABILITIES)).optional(),
|
|
557
576
|
modality: z2.enum(OUTPUT_MODALITIES).optional(),
|
|
558
577
|
provider: z2.array(z2.string().min(1)).optional(),
|
|
559
|
-
excludeFree: z2.boolean().optional()
|
|
578
|
+
excludeFree: z2.boolean().optional(),
|
|
579
|
+
requireKeywordMatch: z2.boolean().optional()
|
|
560
580
|
}).strict();
|
|
561
581
|
var SearchResultSchema = z2.object({
|
|
562
582
|
id: z2.string(),
|
|
@@ -568,7 +588,11 @@ var SearchResultSchema = z2.object({
|
|
|
568
588
|
maxOutputTokens: z2.number().nullable(),
|
|
569
589
|
pricing: z2.object({
|
|
570
590
|
promptPerMillion: z2.number().nullable(),
|
|
571
|
-
completionPerMillion: z2.number().nullable()
|
|
591
|
+
completionPerMillion: z2.number().nullable(),
|
|
592
|
+
promptPerToken: z2.number().nullable().optional(),
|
|
593
|
+
completionPerToken: z2.number().nullable().optional(),
|
|
594
|
+
requestUsd: z2.number().nullable().optional(),
|
|
595
|
+
effectivePromptPerMillion: z2.number().nullable().optional()
|
|
572
596
|
}),
|
|
573
597
|
inputModalities: z2.array(z2.string()),
|
|
574
598
|
outputModalities: z2.array(z2.string()),
|
|
@@ -582,13 +606,15 @@ var SearchResponseSchema = z2.object({
|
|
|
582
606
|
limit: z2.number(),
|
|
583
607
|
hasMore: z2.boolean(),
|
|
584
608
|
sortBy: SearchSortBySchema,
|
|
585
|
-
sortOrder: SearchSortOrderSchema
|
|
609
|
+
sortOrder: SearchSortOrderSchema,
|
|
610
|
+
priceSortBasis: z2.literal("effective_prompt_per_million").optional()
|
|
586
611
|
}),
|
|
587
612
|
meta: z2.object({
|
|
588
613
|
queryMode: z2.enum(["semantic", "filter_only"]),
|
|
589
614
|
ranking: z2.literal("hybrid_rrf"),
|
|
590
615
|
confidence: z2.enum(["high", "low"]).optional(),
|
|
591
|
-
suggestion: z2.string().optional()
|
|
616
|
+
suggestion: z2.string().optional(),
|
|
617
|
+
lowConfidenceReason: z2.enum(["no_keyword_matches", "no_results"]).optional()
|
|
592
618
|
})
|
|
593
619
|
});
|
|
594
620
|
var FindModelsToolResultSchema = SearchResponseSchema.extend({
|
|
@@ -604,6 +630,8 @@ var BatchModelLookupRequestSchema = z3.object({
|
|
|
604
630
|
var ModelPricingSchema = z3.object({
|
|
605
631
|
promptPerMillion: z3.number().nullable(),
|
|
606
632
|
completionPerMillion: z3.number().nullable(),
|
|
633
|
+
promptPerToken: z3.number().nullable().optional(),
|
|
634
|
+
completionPerToken: z3.number().nullable().optional(),
|
|
607
635
|
requestUsd: z3.number().nullable(),
|
|
608
636
|
imageUsd: z3.number().nullable()
|
|
609
637
|
});
|
|
@@ -634,19 +662,22 @@ var BatchModelLookupResponseSchema = z3.object({
|
|
|
634
662
|
missingIds: z3.array(z3.string()),
|
|
635
663
|
resolvedAliases: z3.record(z3.string(), z3.string()).optional(),
|
|
636
664
|
ambiguousAliases: z3.record(z3.string(), z3.array(z3.string())).optional(),
|
|
637
|
-
suggestions: z3.record(z3.string(), z3.array(
|
|
665
|
+
suggestions: z3.record(z3.string(), z3.array(SuggestionEntrySchema)).optional(),
|
|
666
|
+
missingDiagnostics: z3.record(z3.string(), MissingModelDiagnosticSchema).optional()
|
|
638
667
|
}).strict();
|
|
639
668
|
var GetModelsToolResultSchema = z3.object({
|
|
640
669
|
results: z3.array(ModelResponseSchema.nullable()),
|
|
641
670
|
missingIds: z3.array(z3.string()),
|
|
642
671
|
resolvedAliases: z3.record(z3.string(), z3.string()).optional(),
|
|
643
672
|
ambiguousAliases: z3.record(z3.string(), z3.array(z3.string())).optional(),
|
|
644
|
-
suggestions: z3.record(z3.string(), z3.array(
|
|
673
|
+
suggestions: z3.record(z3.string(), z3.array(SuggestionEntrySchema)).optional(),
|
|
674
|
+
missingDiagnostics: z3.record(z3.string(), MissingModelDiagnosticSchema).optional(),
|
|
645
675
|
_index9: Index9MetaSchema
|
|
646
676
|
});
|
|
647
677
|
|
|
648
678
|
// ../core/dist/schemas/compare.js
|
|
649
679
|
import { z as z4 } from "zod";
|
|
680
|
+
var PricingBasisSchema = z4.enum(["exact_per_token", "rounded_per_million", "unavailable"]);
|
|
650
681
|
var CompareRequestSchema = z4.object({
|
|
651
682
|
ids: z4.array(z4.string().min(1)).min(2, "compare requires at least 2 ids").max(LIMITS.compareModelsMax, `ids must contain between 2 and ${LIMITS.compareModelsMax} model IDs`),
|
|
652
683
|
expectedPromptTokens: z4.number().int().positive().optional(),
|
|
@@ -683,7 +714,10 @@ var CompareWorkloadCostSchema = z4.object({
|
|
|
683
714
|
modelId: z4.string(),
|
|
684
715
|
promptTokens: z4.number().int().nonnegative(),
|
|
685
716
|
completionTokens: z4.number().int().nonnegative(),
|
|
686
|
-
totalCostUsd: z4.number().nullable()
|
|
717
|
+
totalCostUsd: z4.number().nullable(),
|
|
718
|
+
tokenCostUsd: z4.number().nullable().optional(),
|
|
719
|
+
requestCostUsd: z4.number().nullable().optional(),
|
|
720
|
+
pricingBasis: PricingBasisSchema.optional()
|
|
687
721
|
});
|
|
688
722
|
var CompareResponseSchema = z4.object({
|
|
689
723
|
models: z4.array(ModelResponseSchema),
|
|
@@ -694,8 +728,9 @@ var CompareResponseSchema = z4.object({
|
|
|
694
728
|
workloadCosts: z4.array(CompareWorkloadCostSchema).optional(),
|
|
695
729
|
resolvedAliases: z4.record(z4.string(), z4.string()).optional(),
|
|
696
730
|
missingIds: z4.array(z4.string()),
|
|
697
|
-
suggestions: z4.record(z4.string(), z4.array(
|
|
698
|
-
ambiguousAliases: z4.record(z4.string(), z4.array(z4.string())).optional()
|
|
731
|
+
suggestions: z4.record(z4.string(), z4.array(SuggestionEntrySchema)).optional(),
|
|
732
|
+
ambiguousAliases: z4.record(z4.string(), z4.array(z4.string())).optional(),
|
|
733
|
+
missingDiagnostics: z4.record(z4.string(), MissingModelDiagnosticSchema).optional()
|
|
699
734
|
}).strict();
|
|
700
735
|
var CompareModelsToolResultSchema = CompareResponseSchema.extend({
|
|
701
736
|
_index9: Index9MetaSchema
|
|
@@ -770,8 +805,17 @@ var TestPricingUsedSchema = z6.object({
|
|
|
770
805
|
promptPerToken: z6.number().nullable().optional(),
|
|
771
806
|
completionPerToken: z6.number().nullable().optional(),
|
|
772
807
|
promptPerMillion: z6.number().nullable().optional(),
|
|
773
|
-
completionPerMillion: z6.number().nullable().optional()
|
|
808
|
+
completionPerMillion: z6.number().nullable().optional(),
|
|
809
|
+
requestUsd: z6.number().nullable().optional()
|
|
774
810
|
});
|
|
811
|
+
var TestFailureReasonSchema = z6.enum([
|
|
812
|
+
"insufficient_credits",
|
|
813
|
+
"model_unavailable",
|
|
814
|
+
"rate_limited",
|
|
815
|
+
"timeout",
|
|
816
|
+
"invalid_request",
|
|
817
|
+
"unknown"
|
|
818
|
+
]);
|
|
775
819
|
var TestModelMetadataSchema = z6.object({
|
|
776
820
|
id: z6.string(),
|
|
777
821
|
name: z6.string(),
|
|
@@ -796,6 +840,7 @@ var TestResultFailureSchema = z6.object({
|
|
|
796
840
|
ok: z6.literal(false),
|
|
797
841
|
model: TestModelMetadataSchema,
|
|
798
842
|
error: z6.string(),
|
|
843
|
+
failureReason: TestFailureReasonSchema.optional(),
|
|
799
844
|
latencyMs: z6.number().min(0)
|
|
800
845
|
});
|
|
801
846
|
var TestResultSchema = z6.discriminatedUnion("ok", [
|
|
@@ -807,15 +852,28 @@ var TestEstimateResultSchema = z6.object({
|
|
|
807
852
|
resolvedModelId: z6.string().optional(),
|
|
808
853
|
model: TestModelMetadataSchema,
|
|
809
854
|
tokens: UsageTokensSchema,
|
|
810
|
-
estimatedCost: z6.number().nullable().optional()
|
|
855
|
+
estimatedCost: z6.number().nullable().optional(),
|
|
856
|
+
tokenCostUsd: z6.number().nullable().optional(),
|
|
857
|
+
requestCostUsd: z6.number().nullable().optional(),
|
|
858
|
+
totalCostUsd: z6.number().nullable().optional(),
|
|
859
|
+
estimatedCostBasis: PricingBasisSchema.optional()
|
|
811
860
|
});
|
|
861
|
+
var TestResolutionFieldsSchema = {
|
|
862
|
+
missingIds: z6.array(z6.string()).optional(),
|
|
863
|
+
resolvedAliases: z6.record(z6.string(), z6.string()).optional(),
|
|
864
|
+
ambiguousAliases: z6.record(z6.string(), z6.array(z6.string())).optional(),
|
|
865
|
+
suggestions: z6.record(z6.string(), z6.array(SuggestionEntrySchema)).optional(),
|
|
866
|
+
missingDiagnostics: z6.record(z6.string(), MissingModelDiagnosticSchema).optional()
|
|
867
|
+
};
|
|
812
868
|
var TestDryRunResponseSchema = z6.object({
|
|
813
869
|
dryRun: z6.literal(true),
|
|
814
870
|
results: z6.array(TestEstimateResultSchema),
|
|
815
|
-
disclaimer: z6.string()
|
|
871
|
+
disclaimer: z6.string(),
|
|
872
|
+
...TestResolutionFieldsSchema
|
|
816
873
|
});
|
|
817
874
|
var TestLiveResponseSchema = z6.object({
|
|
818
|
-
results: z6.array(TestResultSchema)
|
|
875
|
+
results: z6.array(TestResultSchema),
|
|
876
|
+
...TestResolutionFieldsSchema
|
|
819
877
|
});
|
|
820
878
|
var TestResponseSchema = z6.union([TestDryRunResponseSchema, TestLiveResponseSchema]);
|
|
821
879
|
|
|
@@ -963,6 +1021,22 @@ function extractError(body) {
|
|
|
963
1021
|
}
|
|
964
1022
|
return "Request failed";
|
|
965
1023
|
}
|
|
1024
|
+
var RECOVERY_FIELDS = [
|
|
1025
|
+
"missingIds",
|
|
1026
|
+
"resolvedAliases",
|
|
1027
|
+
"ambiguousAliases",
|
|
1028
|
+
"suggestions",
|
|
1029
|
+
"missingDiagnostics"
|
|
1030
|
+
];
|
|
1031
|
+
function extractRecoveryFields(body) {
|
|
1032
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) return {};
|
|
1033
|
+
const out = {};
|
|
1034
|
+
const b = body;
|
|
1035
|
+
for (const key of RECOVERY_FIELDS) {
|
|
1036
|
+
if (key in b) out[key] = b[key];
|
|
1037
|
+
}
|
|
1038
|
+
return out;
|
|
1039
|
+
}
|
|
966
1040
|
async function callApi(ctx, url, options, responseSchema) {
|
|
967
1041
|
const res = await fetchWithRetry(url, options);
|
|
968
1042
|
let body;
|
|
@@ -973,7 +1047,12 @@ async function callApi(ctx, url, options, responseSchema) {
|
|
|
973
1047
|
}
|
|
974
1048
|
if (!res.ok) {
|
|
975
1049
|
return toResponse(
|
|
976
|
-
{
|
|
1050
|
+
{
|
|
1051
|
+
error: extractError(body),
|
|
1052
|
+
status: res.status,
|
|
1053
|
+
...extractRecoveryFields(body),
|
|
1054
|
+
_index9: buildMeta(ctx, res.headers)
|
|
1055
|
+
},
|
|
977
1056
|
true
|
|
978
1057
|
);
|
|
979
1058
|
}
|
|
@@ -1005,6 +1084,7 @@ async function handleSearchModels(ctx, args) {
|
|
|
1005
1084
|
if (q.modality) params.modality = q.modality;
|
|
1006
1085
|
if (q.provider?.length) params.provider = q.provider.join(",");
|
|
1007
1086
|
if (q.excludeFree === true) params.excludeFree = "true";
|
|
1087
|
+
if (q.requireKeywordMatch === true) params.requireKeywordMatch = "true";
|
|
1008
1088
|
return callApi(
|
|
1009
1089
|
ctx,
|
|
1010
1090
|
buildUrl(ctx.baseUrl, API_PATHS.search, params),
|
|
@@ -1020,7 +1100,11 @@ async function handleGetModels(ctx, args) {
|
|
|
1020
1100
|
return callApi(
|
|
1021
1101
|
ctx,
|
|
1022
1102
|
`${ctx.baseUrl}${API_PATHS.model}`,
|
|
1023
|
-
{
|
|
1103
|
+
{
|
|
1104
|
+
method: "POST",
|
|
1105
|
+
headers: baseHeaders(ctx),
|
|
1106
|
+
body: JSON.stringify(parsed.data)
|
|
1107
|
+
},
|
|
1024
1108
|
BatchModelLookupResponseSchema
|
|
1025
1109
|
);
|
|
1026
1110
|
}
|
|
@@ -1032,7 +1116,11 @@ async function handleCompareModels(ctx, args) {
|
|
|
1032
1116
|
return callApi(
|
|
1033
1117
|
ctx,
|
|
1034
1118
|
`${ctx.baseUrl}${API_PATHS.compare}`,
|
|
1035
|
-
{
|
|
1119
|
+
{
|
|
1120
|
+
method: "POST",
|
|
1121
|
+
headers: baseHeaders(ctx),
|
|
1122
|
+
body: JSON.stringify(parsed.data)
|
|
1123
|
+
},
|
|
1036
1124
|
CompareResponseSchema
|
|
1037
1125
|
);
|
|
1038
1126
|
}
|
|
@@ -1096,7 +1184,8 @@ async function createServer() {
|
|
|
1096
1184
|
capabilitiesAny: z7.array(z7.enum(CAPABILITIES)).optional().describe(PARAM_DESCRIPTIONS.capabilitiesAny),
|
|
1097
1185
|
modality: z7.enum(OUTPUT_MODALITIES).optional().describe(PARAM_DESCRIPTIONS.modality),
|
|
1098
1186
|
provider: z7.array(z7.string().min(1)).optional().describe(PARAM_DESCRIPTIONS.provider),
|
|
1099
|
-
excludeFree: z7.boolean().optional().describe(PARAM_DESCRIPTIONS.excludeFree)
|
|
1187
|
+
excludeFree: z7.boolean().optional().describe(PARAM_DESCRIPTIONS.excludeFree),
|
|
1188
|
+
requireKeywordMatch: z7.boolean().optional().describe(PARAM_DESCRIPTIONS.requireKeywordMatch)
|
|
1100
1189
|
},
|
|
1101
1190
|
outputSchema: FindModelsToolResultSchema.shape,
|
|
1102
1191
|
annotations: { readOnlyHint: true }
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@index9/mcp",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"zod": "^4.4.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/node": "^25.6.
|
|
27
|
+
"@types/node": "^25.6.2",
|
|
28
28
|
"tsup": "^8.5.1",
|
|
29
29
|
"typescript": "6.0.3",
|
|
30
|
-
"vitest": "^4.1.
|
|
31
|
-
"@index9/core": "2.
|
|
30
|
+
"vitest": "^4.1.6",
|
|
31
|
+
"@index9/core": "2.5.0"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
34
|
"node": ">=20"
|