@index9/mcp 4.0.0 → 4.0.2

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/dist/cli.js +58 -33
  2. package/manifest.json +1 -1
  3. package/package.json +12 -11
package/dist/cli.js CHANGED
@@ -159,7 +159,6 @@ var PARAM_DESCRIPTIONS = {
159
159
  var SITE = {
160
160
  nav: {
161
161
  brand: "index9",
162
- howItWorks: "How it works",
163
162
  install: "Install",
164
163
  faq: "FAQ",
165
164
  github: "GitHub",
@@ -206,27 +205,6 @@ var SITE = {
206
205
  }
207
206
  ]
208
207
  },
209
- howItWorks: {
210
- label: "Workflow",
211
- heading: "How it works",
212
- steps: [
213
- {
214
- number: "01",
215
- title: "Analyze",
216
- description: "Describe what you need. Your assistant finds models matching your cost, speed, and capability requirements."
217
- },
218
- {
219
- number: "02",
220
- title: "Test",
221
- description: "Run your prompt across the top candidates. See each output with latency and cost."
222
- },
223
- {
224
- number: "03",
225
- title: "Compare",
226
- description: "Pick the best model for your task \u2014 based on real results, not generic rankings."
227
- }
228
- ]
229
- },
230
208
  faq: {
231
209
  label: "FAQ",
232
210
  heading: "Common questions",
@@ -313,13 +291,13 @@ var SITE = {
313
291
  withoutLabel: "Without index9",
314
292
  withLabel: "With index9",
315
293
  withoutItems: [
316
- "Pricing and specs may be weeks old",
317
- "Quality assessed on generic benchmarks, not your task",
318
- "Testing a model means a throwaway script or manual API switching"
294
+ "Model pricing and specs may be weeks old",
295
+ "Quality based on generic benchmarks, not your task",
296
+ "Testing a model means a throwaway script or manual switching"
319
297
  ],
320
298
  withItems: [
321
- "Pricing and specs synced from OpenRouter every 30 minutes",
322
- "Quality measured on your actual prompts, side by side",
299
+ "Models synced from OpenRouter every 30 minutes",
300
+ "Quality measured on prompts for your specific task",
323
301
  "Test and compare in your editor \u2014 no scripts, no switching"
324
302
  ],
325
303
  sampleTableLabel: 'Sample comparison \u2014 "Extract the action items from this meeting transcript"',
@@ -356,7 +334,8 @@ var SITE = {
356
334
  },
357
335
  footer: {
358
336
  brand: "index9",
359
- tagline: "Model data synced from OpenRouter. Search, testing, and MCP tools by index9.",
337
+ tagline1: "Model data synced from OpenRouter",
338
+ tagline2: "Search, testing, and MCP tools by index9",
360
339
  copyright: "\xA9 2026 index9",
361
340
  github: "GitHub",
362
341
  privacy: "Privacy",
@@ -370,7 +349,7 @@ var SITE = {
370
349
  userPrompt: "Find the cheapest, most capable model for my use case.",
371
350
  assistantReplyPrefix: "I'll check your ",
372
351
  assistantReplyFile: "summarize-ticket.ts",
373
- assistantReplySuffix: " handler, search for suitable models, confirm specs for the shortlist, then test the top 3.",
352
+ assistantReplySuffix: " handler, search for suitable models, then test the top 3.",
374
353
  findModelsCall: {
375
354
  label: "find_models",
376
355
  suffix: " \u2014 search by use case",
@@ -614,6 +593,13 @@ function loadConfig() {
614
593
  const env = process.env.INDEX9_API_BASE_URL?.trim();
615
594
  const baseUrl = env || DEFAULT_BASE_URL;
616
595
  const normalized = baseUrl.replace(/\/$/, "");
596
+ try {
597
+ new URL(normalized);
598
+ } catch {
599
+ throw new Error(
600
+ `Invalid INDEX9_API_BASE_URL: ${normalized}. Expected an absolute URL such as https://index9.dev`
601
+ );
602
+ }
617
603
  return {
618
604
  baseUrl: normalized,
619
605
  apiToken: process.env.INDEX9_API_TOKEN?.trim() || void 0,
@@ -623,23 +609,62 @@ function loadConfig() {
623
609
 
624
610
  // src/client.ts
625
611
  var RETRY_DELAYS_MS = [1e3, 2e3, 4e3];
612
+ var ATTEMPT_TIMEOUT_MS = 3e4;
626
613
  function isRetryable(status) {
627
614
  return status === 429 || status >= 500;
628
615
  }
629
616
  async function sleep(ms) {
630
617
  return new Promise((r) => setTimeout(r, ms));
631
618
  }
619
+ function isRetryableError(error) {
620
+ if (!(error instanceof Error)) return false;
621
+ if (error.name === "AbortError" || error.name === "TimeoutError") return true;
622
+ return error instanceof TypeError;
623
+ }
624
+ function toErrorMessage(error) {
625
+ if (error instanceof Error && error.message.trim()) return error.message;
626
+ return "Unknown error";
627
+ }
632
628
  async function fetchWithRetry(url, options) {
633
629
  let lastResponse = null;
630
+ let lastError;
634
631
  for (let i = 0; i <= RETRY_DELAYS_MS.length; i++) {
635
- const res = await fetch(url, options);
636
- lastResponse = res;
637
- if (res.ok || !isRetryable(res.status)) return res;
632
+ const timeoutController = new AbortController();
633
+ const timeoutId = setTimeout(() => {
634
+ timeoutController.abort(new DOMException("Request timed out", "AbortError"));
635
+ }, ATTEMPT_TIMEOUT_MS);
636
+ const externalSignal = options.signal;
637
+ const onAbort = () => {
638
+ timeoutController.abort(
639
+ externalSignal?.reason ?? new DOMException("Request aborted", "AbortError")
640
+ );
641
+ };
642
+ if (externalSignal?.aborted) {
643
+ onAbort();
644
+ } else {
645
+ externalSignal?.addEventListener("abort", onAbort, { once: true });
646
+ }
647
+ try {
648
+ const res = await fetch(url, { ...options, signal: timeoutController.signal });
649
+ lastResponse = res;
650
+ if (res.ok || !isRetryable(res.status)) return res;
651
+ } catch (error) {
652
+ lastError = error;
653
+ if (!isRetryableError(error)) {
654
+ throw new Error(`Request failed: ${toErrorMessage(error)}`);
655
+ }
656
+ } finally {
657
+ clearTimeout(timeoutId);
658
+ externalSignal?.removeEventListener("abort", onAbort);
659
+ }
638
660
  if (i < RETRY_DELAYS_MS.length) {
639
661
  await sleep(RETRY_DELAYS_MS[i]);
640
662
  }
641
663
  }
642
- return lastResponse;
664
+ if (lastResponse) return lastResponse;
665
+ throw new Error(
666
+ `Request failed after ${RETRY_DELAYS_MS.length + 1} attempts: ${toErrorMessage(lastError)}`
667
+ );
643
668
  }
644
669
  function buildUrl(baseUrl, path, params) {
645
670
  const url = new URL(path, baseUrl);
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": "0.3",
3
3
  "name": "index9",
4
- "version": "4.0.0",
4
+ "version": "4.0.1",
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,11 @@
1
1
  {
2
2
  "name": "@index9/mcp",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/index9-org/mcp.git"
8
+ },
4
9
  "bin": {
5
10
  "index9-mcp": "./dist/cli.js"
6
11
  },
@@ -14,20 +19,16 @@
14
19
  "publishConfig": {
15
20
  "access": "public"
16
21
  },
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/index9-org/mcp.git"
20
- },
21
- "license": "MIT",
22
22
  "dependencies": {
23
- "@modelcontextprotocol/sdk": "^1.26.0",
23
+ "@modelcontextprotocol/sdk": "^1.29.0",
24
24
  "zod": "^4.3.6"
25
25
  },
26
26
  "devDependencies": {
27
- "@types/node": "^25.2.3",
27
+ "@types/node": "^25.6.0",
28
28
  "tsup": "^8.5.1",
29
- "typescript": "5.9.3",
30
- "@index9/core": "2.3.0"
29
+ "typescript": "6.0.3",
30
+ "vitest": "^4.1.5",
31
+ "@index9/core": "2.3.1"
31
32
  },
32
33
  "engines": {
33
34
  "node": ">=20"
@@ -37,6 +38,6 @@
37
38
  "clean": "rm -rf dist",
38
39
  "lint": "tsc --noEmit",
39
40
  "start": "node dist/cli.js",
40
- "test": "echo 'No tests in @index9/mcp'"
41
+ "test": "vitest run"
41
42
  }
42
43
  }