@index9/mcp 1.0.6 → 1.0.11

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/README.md CHANGED
@@ -33,6 +33,8 @@ Open Cursor Settings → MCP → Add new global MCP server
33
33
  }
34
34
  ```
35
35
 
36
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=index9&config=eyJjb21tYW5kIjoibnB4IC15IEBpbmRleDkvbWNwIn0%3D)
37
+
36
38
  ### VS Code
37
39
 
38
40
  Open VS Code Settings → MCP Servers → Add Server
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,kBAAkB,CAAC;AAuB1B,wBAAsB,UAAU,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAG3F;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGzE;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAG/B;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAKtF;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,EACnB,oBAAoB,CAAC,EAAE,MAAM,EAAE,EAC/B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,sBAAsB,CAAC,CASjC;AAED,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAAE,EAClB,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,EAC1E,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAO5B"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,kBAAkB,CAAC;AA2B1B,wBAAsB,UAAU,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAG3F;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGzE;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAO/B;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAKtF;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,EACnB,oBAAoB,CAAC,EAAE,MAAM,EAAE,EAC/B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,sBAAsB,CAAC,CASjC;AAED,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAAE,EAClB,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,EAC1E,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAO5B"}
package/dist/client.js CHANGED
@@ -1,20 +1,22 @@
1
1
  import axios from "axios";
2
- import { API_URL } from "./config.js";
3
- const client = axios.create({
4
- baseURL: API_URL,
5
- timeout: 30000,
6
- });
7
- client.interceptors.response.use((res) => res, (error) => {
8
- if (error.response?.status === 404) {
9
- const message = error.response?.data?.message || "Resource not found";
2
+ import { API_URL, API_TIMEOUT, TEST_MODEL_TIMEOUT } from "./config.js";
3
+ const createAxiosClient = (timeout) => {
4
+ const client = axios.create({ baseURL: API_URL, timeout });
5
+ client.interceptors.response.use((res) => res, (error) => {
6
+ if (error.response?.status === 404) {
7
+ const message = error.response?.data?.message || "Resource not found";
8
+ throw new Error(message);
9
+ }
10
+ const message = error.response?.data?.message ||
11
+ (error.code === "ECONNABORTED" ? `Request timeout` : null) ||
12
+ (error.code === "ECONNREFUSED" ? `Unable to connect to API server` : null) ||
13
+ error.message;
10
14
  throw new Error(message);
11
- }
12
- const message = error.response?.data?.message ||
13
- (error.code === "ECONNABORTED" ? `Request timeout` : null) ||
14
- (error.code === "ECONNREFUSED" ? `Unable to connect to API server` : null) ||
15
- error.message;
16
- throw new Error(message);
17
- });
15
+ });
16
+ return client;
17
+ };
18
+ const client = createAxiosClient(API_TIMEOUT);
19
+ const testClient = createAxiosClient(TEST_MODEL_TIMEOUT); // test operations with longer timeout
18
20
  export async function listModels(params) {
19
21
  const { data } = await client.get("/models", { params });
20
22
  return data;
@@ -24,7 +26,11 @@ export async function getModel(modelId) {
24
26
  return data;
25
27
  }
26
28
  export async function searchModels(query, limit, threshold) {
27
- const { data } = await client.post("/search", { query, limit, threshold });
29
+ const { data } = await client.post("/search", {
30
+ query,
31
+ limit,
32
+ threshold,
33
+ });
28
34
  return data;
29
35
  }
30
36
  export async function compareModels(modelIds) {
@@ -44,7 +50,7 @@ export async function recommendModel(useCase, maxPrice, minContext, requiredCapa
44
50
  return data;
45
51
  }
46
52
  export async function testModel(modelIds, testType, openRouterApiKey) {
47
- const { data } = await client.post("/test", {
53
+ const { data } = await testClient.post("/test", {
48
54
  model_ids: modelIds,
49
55
  test_type: testType,
50
56
  open_router_api_key: openRouterApiKey || undefined,
package/dist/config.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  declare const API_URL: string;
2
2
  declare const OPEN_ROUTER_API_KEY: string | null;
3
- export { API_URL, OPEN_ROUTER_API_KEY };
3
+ declare const API_TIMEOUT: number;
4
+ declare const TEST_MODEL_TIMEOUT: number;
5
+ declare const RATE_LIMIT_WINDOW_MS: number;
6
+ declare const RATE_LIMIT_MAX_REQUESTS: number;
7
+ export { API_URL, OPEN_ROUTER_API_KEY, API_TIMEOUT, TEST_MODEL_TIMEOUT, RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX_REQUESTS, };
4
8
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,OAAO,QAAyD,CAAC;AACvE,QAAA,MAAM,mBAAmB,eAA0C,CAAC;AAEpE,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,OAAO,QAAyD,CAAC;AACvE,QAAA,MAAM,mBAAmB,eAA0C,CAAC;AAGpE,QAAA,MAAM,WAAW,QAAsD,CAAC;AACxE,QAAA,MAAM,kBAAkB,QAAuD,CAAC;AAGhF,QAAA,MAAM,oBAAoB,QAAwD,CAAC;AACnF,QAAA,MAAM,uBAAuB,QAAyD,CAAC;AAEvF,OAAO,EACL,OAAO,EACP,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,GACxB,CAAC"}
package/dist/config.js CHANGED
@@ -1,3 +1,9 @@
1
1
  const API_URL = process.env.INDEX9_API_URL || "https://index9.dev/api";
2
2
  const OPEN_ROUTER_API_KEY = process.env.OPEN_ROUTER_API_KEY || null;
3
- export { API_URL, OPEN_ROUTER_API_KEY };
3
+ // Configurable timeouts (in milliseconds)
4
+ const API_TIMEOUT = parseInt(process.env.INDEX9_API_TIMEOUT || "30000"); // 30 seconds default
5
+ const TEST_MODEL_TIMEOUT = parseInt(process.env.TEST_MODEL_TIMEOUT || "120000"); // 2 minutes for testing
6
+ // Rate limiting configuration
7
+ const RATE_LIMIT_WINDOW_MS = parseInt(process.env.RATE_LIMIT_WINDOW_MS || "60000"); // 1 minute
8
+ const RATE_LIMIT_MAX_REQUESTS = parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || "100"); // requests per window
9
+ export { API_URL, OPEN_ROUTER_API_KEY, API_TIMEOUT, TEST_MODEL_TIMEOUT, RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX_REQUESTS, };
package/dist/index.js CHANGED
@@ -4,7 +4,9 @@ import { startMCPServer } from "./mcp.js";
4
4
  // Export types for library usage
5
5
  export * from "./types/index.js";
6
6
  async function main() {
7
- logger.info("Starting index9 MCP server");
7
+ if (process.env.DEBUG_MCP === "true") {
8
+ logger.info("Starting index9 MCP server");
9
+ }
8
10
  await startMCPServer();
9
11
  }
10
12
  process.on("unhandledRejection", (error) => {
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,QAAA,MAAM,MAAM,6BAA0D,CAAC;AAEvE,OAAO,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,QAAA,MAAM,MAAM,6BAOX,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,CAAC"}
package/dist/logger.js CHANGED
@@ -1,3 +1,8 @@
1
1
  import pino from "pino";
2
- const logger = pino({ name: "index9", level: "info" }, process.stderr);
2
+ // Minimize stderr logs to avoid interfering with JSON-RPC; log only warnings/errors.
3
+ const logger = pino({
4
+ name: "index9",
5
+ level: process.env.LOG_LEVEL || "warn",
6
+ enabled: process.env.NODE_ENV !== "production" || process.env.DEBUG_MCP === "true",
7
+ }, process.stderr);
3
8
  export { logger };
package/dist/mcp.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4BpE,wBAAgB,eAAe,cAgK9B;AAED,wBAAsB,cAAc,kBAenC"}
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6BpE,wBAAgB,eAAe,cAgM9B;AAED,wBAAsB,cAAc,kBAoBnC"}
package/dist/mcp.js CHANGED
@@ -4,6 +4,7 @@ import { readFileSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { dirname, join } from "node:path";
6
6
  import { logger } from "./logger.js";
7
+ import { checkRateLimit } from "./utils/rateLimiter.js";
7
8
  import { listModelsTool } from "./tools/list_models.js";
8
9
  import { searchModelsTool } from "./tools/search_models.js";
9
10
  import { getModelTool } from "./tools/get_model.js";
@@ -22,13 +23,17 @@ export function createMCPServer() {
22
23
  });
23
24
  server.registerTool("list_models", {
24
25
  title: "List Models",
25
- description: "List AI models with optional filters. Filter by: provider or owner (openai, anthropic, google, etc.), min_context (e.g. 100000), modality (text, vision, audio, video, image), max_price_per_m (USD per million tokens), supports_tool_calling (boolean), supports_json_mode (boolean), tokenizer (model family: GPT, Claude, Llama3), output_modality (text, image, embeddings). Returns pricing, context window, max output tokens, input_modalities array, output_modalities array, capabilities (vision, audio, tool_calling, json_mode), and architecture (tokenizer family) for each model.",
26
+ description: "Find AI models using exact filters like provider (openai, anthropic), context window (min 100000), pricing (max $1/M tokens), modality (vision, audio), and capabilities (tool_calling, json_mode). Best when you know specific technical requirements. Returns up to 100 models with pricing, context windows, and capabilities. NOT for natural language search (use search_models) or recommendations (use recommend_model).",
26
27
  inputSchema: listModelsSchema,
27
28
  }, async (input) => {
29
+ if (!checkRateLimit("list_models")) {
30
+ logger.warn({ tool: "list_models" }, "Rate limit exceeded");
31
+ throw new Error("Rate limit exceeded. Please try again later.");
32
+ }
28
33
  try {
29
34
  const result = await listModelsTool(input);
30
35
  return {
31
- content: [{ type: "text", text: JSON.stringify(result) }],
36
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
32
37
  structuredContent: result,
33
38
  };
34
39
  }
@@ -40,13 +45,17 @@ export function createMCPServer() {
40
45
  });
41
46
  server.registerTool("search_models", {
42
47
  title: "Search Models",
43
- description: 'Semantic search across 1000+ AI models using natural language queries. Uses vector embeddings (semantic search) combined with fuzzy text matching to find relevant models. Examples: "fast cheap model for code generation", "vision model under $1 per million tokens", "best reasoning model with large context". Returns models ranked by similarity score (0-1, default threshold 0.5) with descriptions, pricing, context window, and capabilities (vision, tool_calling).',
48
+ description: "Search 1000+ AI models using natural language queries like 'fast cheap model for code generation' or 'vision model under $1 per million tokens'. Uses semantic search + fuzzy matching to find relevant models. Returns models ranked by relevance (0-1 similarity score) with descriptions, pricing, and capabilities. Perfect for discovery when you can't specify exact technical filters.",
44
49
  inputSchema: searchModelsSchema,
45
50
  }, async (input) => {
51
+ if (!checkRateLimit("search_models")) {
52
+ logger.warn({ tool: "search_models" }, "Rate limit exceeded");
53
+ throw new Error("Rate limit exceeded. Please try again later.");
54
+ }
46
55
  try {
47
56
  const result = await searchModelsTool(input);
48
57
  return {
49
- content: [{ type: "text", text: JSON.stringify(result) }],
58
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
50
59
  structuredContent: result,
51
60
  };
52
61
  }
@@ -58,13 +67,17 @@ export function createMCPServer() {
58
67
  });
59
68
  server.registerTool("get_model", {
60
69
  title: "Get Model",
61
- description: "Get complete details for a specific model by ID. Returns: description, context window, max output tokens, pricing (input/output per million tokens), extended pricing (image, audio, web_search, cache_read, cache_write), capabilities (vision, audio, function_calling, tool_calling, json_mode, custom), supported_parameters, input/output modalities, architecture (tokenizer, instruct_type), is_moderated, per_request_limits, and deployments. If exact match fails, semantic search may be used to find similar models.",
70
+ description: "Get complete details for a specific AI model by ID, including pricing (input/output/cache), capabilities (vision, tool_calling), context window, supported parameters, and deployment info. The most detailed view - use this after finding candidates with other tools. Returns extended pricing for images, audio, and caching. Falls back to semantic search if exact ID not found.",
62
71
  inputSchema: getModelSchema,
63
72
  }, async (input) => {
73
+ if (!checkRateLimit("get_model")) {
74
+ logger.warn({ tool: "get_model" }, "Rate limit exceeded");
75
+ throw new Error("Rate limit exceeded. Please try again later.");
76
+ }
64
77
  try {
65
78
  const result = await getModelTool(input);
66
79
  return {
67
- content: [{ type: "text", text: JSON.stringify(result) }],
80
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
68
81
  structuredContent: result,
69
82
  };
70
83
  }
@@ -76,17 +89,21 @@ export function createMCPServer() {
76
89
  });
77
90
  server.registerTool("compare_models", {
78
91
  title: "Compare Models",
79
- description: "Compare 2-10 models side-by-side. Returns unified view of: context windows, max output tokens, pricing (input/output), and capabilities (vision, tool_calling, custom). Includes not_found array for any model IDs that couldn't be located. Useful for making final selection between candidate models.",
92
+ description: "Compare 2-10 AI models side-by-side with unified pricing, context windows, and capabilities. Perfect for final decisions between shortlisted candidates. Shows pricing, context, and features in comparable format. Returns 'not_found' for invalid model IDs. Use after narrowing options with search/recommend tools.",
80
93
  inputSchema: compareModelsSchema,
81
94
  }, async (input) => {
95
+ if (!checkRateLimit("compare_models")) {
96
+ logger.warn({ tool: "compare_models" }, "Rate limit exceeded");
97
+ throw new Error("Rate limit exceeded. Please try again later.");
98
+ }
99
+ const model_ids = Array.from(new Set(input.model_ids || []));
82
100
  try {
83
- const model_ids = Array.from(new Set(input.model_ids || []));
84
101
  if (model_ids.length < 2) {
85
102
  throw new Error("At least 2 unique model IDs are required");
86
103
  }
87
104
  const result = await compareModelsTool({ model_ids });
88
105
  return {
89
- content: [{ type: "text", text: JSON.stringify(result) }],
106
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
90
107
  structuredContent: result,
91
108
  };
92
109
  }
@@ -98,13 +115,17 @@ export function createMCPServer() {
98
115
  });
99
116
  server.registerTool("recommend_model", {
100
117
  title: "Recommend Model",
101
- description: "Get ranked model recommendations for a use case. Describe what you're building (e.g. 'coding assistant', 'customer support chatbot', 'RAG pipeline'). Optionally set max_price_per_m, min_context, and required_capabilities (vision, tool_calling, audio, video). Returns scored recommendations (higher score = better match) optimized for your requirements with context window and pricing.",
118
+ description: "Get AI-powered recommendations for your specific use case like 'coding assistant', 'customer support chatbot', or 'RAG pipeline'. Optionally set budget ($/M tokens), context requirements, and needed capabilities (vision, tool_calling). Returns up to 20 ranked models with scores, pricing, and context windows. Best for new projects or when you want AI to optimize tradeoffs.",
102
119
  inputSchema: recommendModelSchema,
103
120
  }, async (input) => {
121
+ if (!checkRateLimit("recommend_model")) {
122
+ logger.warn({ tool: "recommend_model" }, "Rate limit exceeded");
123
+ throw new Error("Rate limit exceeded. Please try again later.");
124
+ }
104
125
  try {
105
126
  const result = await recommendModelTool(input);
106
127
  return {
107
- content: [{ type: "text", text: JSON.stringify(result) }],
128
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
108
129
  structuredContent: result,
109
130
  };
110
131
  }
@@ -116,11 +137,16 @@ export function createMCPServer() {
116
137
  });
117
138
  server.registerTool("test_model", {
118
139
  title: "Test Model",
119
- description: "Run live tests against 1-5 models via OpenRouter API. **Requires OPEN_ROUTER_API_KEY environment variable** - set this in your MCP client configuration to enable. Test types: 'quick' (basic math), 'code' (Python function), 'reasoning' (logic puzzle), 'instruction' (follow formatting), 'tool_calling' (verify tool use works). Returns latency, output, token usage, cost estimates, and for tool_calling tests: tool_calls_detected boolean. Use to validate models before committing. Charges are billed directly to your OpenRouter account. Get your API key from: https://openrouter.ai/keys",
140
+ description: "⚠️ REQUIRES OPEN_ROUTER_API_KEY - Test 1-5 AI models with real API calls to check latency, performance, and tool calling. Shows actual costs and behavior before you commit. Test types: quick math, code generation, reasoning, instruction following, tool calling. Bills charged to your OpenRouter account. Get API key at https://openrouter.ai/keys",
120
141
  inputSchema: testModelSchema,
121
142
  }, async (input) => {
143
+ if (!checkRateLimit("test_model")) {
144
+ logger.warn({ tool: "test_model" }, "Rate limit exceeded");
145
+ throw new Error("Rate limit exceeded. Please try again later.");
146
+ }
122
147
  try {
123
148
  if (!OPEN_ROUTER_API_KEY) {
149
+ logger.warn({ tool: "test_model" }, "Missing OpenRouter API key");
124
150
  return {
125
151
  content: [
126
152
  {
@@ -133,7 +159,7 @@ export function createMCPServer() {
133
159
  }
134
160
  const result = await testModelTool(input);
135
161
  return {
136
- content: [{ type: "text", text: JSON.stringify(result) }],
162
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
137
163
  structuredContent: result,
138
164
  };
139
165
  }
@@ -146,11 +172,16 @@ export function createMCPServer() {
146
172
  return server;
147
173
  }
148
174
  export async function startMCPServer() {
149
- logger.info("Starting MCP server with stdio transport");
175
+ // Minimal startup logging to avoid stdio interference
176
+ if (process.env.DEBUG_MCP === "true") {
177
+ logger.info("Starting MCP server with stdio transport");
178
+ }
150
179
  const server = createMCPServer();
151
180
  const transport = new StdioServerTransport();
152
181
  await server.connect(transport);
153
- logger.info("MCP server ready - awaiting requests");
182
+ if (process.env.DEBUG_MCP === "true") {
183
+ logger.info("MCP server ready - awaiting requests");
184
+ }
154
185
  process.on("SIGINT", async () => {
155
186
  logger.info("Shutting down MCP server");
156
187
  await server.close();
@@ -1 +1 @@
1
- {"version":3,"file":"search_models.d.ts","sourceRoot":"","sources":["../../src/tools/search_models.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB;;;;;;;;;;;GAO9D"}
1
+ {"version":3,"file":"search_models.d.ts","sourceRoot":"","sources":["../../src/tools/search_models.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB;;;;;;;;;;;GAG9D"}
@@ -1,9 +1,5 @@
1
1
  import { searchModels } from "../client.js";
2
2
  export async function searchModelsTool(input) {
3
- const query = input.query.trim();
4
- if (!query) {
5
- throw new Error("Search query is required and must not be empty");
6
- }
7
- const result = await searchModels(query, input.limit, input.threshold);
3
+ const result = await searchModels(input.query, input.limit, input.threshold);
8
4
  return { results: result.results };
9
5
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Check if a tool request should be rate limited
3
+ * @param toolName - The tool name (e.g., "list_models", "search_models")
4
+ * @returns true if allowed, false if rate limited
5
+ */
6
+ export declare function checkRateLimit(toolName: string): boolean;
7
+ //# sourceMappingURL=rateLimiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rateLimiter.d.ts","sourceRoot":"","sources":["../../src/utils/rateLimiter.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAexD"}
@@ -0,0 +1,20 @@
1
+ import { RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX_REQUESTS } from "../config.js";
2
+ const requestCounts = new Map();
3
+ /**
4
+ * Check if a tool request should be rate limited
5
+ * @param toolName - The tool name (e.g., "list_models", "search_models")
6
+ * @returns true if allowed, false if rate limited
7
+ */
8
+ export function checkRateLimit(toolName) {
9
+ const now = Date.now();
10
+ const current = requestCounts.get(toolName);
11
+ if (!current || now > current.resetTime) {
12
+ requestCounts.set(toolName, { count: 1, resetTime: now + RATE_LIMIT_WINDOW_MS });
13
+ return true;
14
+ }
15
+ if (current.count >= RATE_LIMIT_MAX_REQUESTS) {
16
+ return false;
17
+ }
18
+ current.count++;
19
+ return true;
20
+ }
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@index9/mcp",
3
- "version": "1.0.6",
4
- "description": "Give your AI assistant up-to-date model knowledge. 1200+ models with real-time pricing, context windows, and capabilities. Latest data, instant responses, zero cost.",
3
+ "mcpName": "io.github.index9-org/mcp",
4
+ "version": "1.0.11",
5
+ "description": "Real-time model intelligence for your AI assistant.",
5
6
  "keywords": [
6
7
  "mcp",
7
8
  "model-context-protocol",