@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 +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +23 -17
- package/dist/config.d.ts +5 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -1
- package/dist/index.js +3 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +6 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +46 -15
- package/dist/tools/search_models.d.ts.map +1 -1
- package/dist/tools/search_models.js +1 -5
- package/dist/utils/rateLimiter.d.ts +7 -0
- package/dist/utils/rateLimiter.d.ts.map +1 -0
- package/dist/utils/rateLimiter.js +20 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -33,6 +33,8 @@ Open Cursor Settings → MCP → Add new global MCP server
|
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
[](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
|
package/dist/client.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
|
4
|
-
baseURL: API_URL,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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", {
|
|
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
|
|
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
|
-
|
|
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
|
package/dist/config.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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
|
-
|
|
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) => {
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,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
|
-
|
|
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;
|
|
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: "
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
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
|
-
|
|
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
|
-
|
|
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;;;;;;;;;;;
|
|
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
|
|
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
|
-
"
|
|
4
|
-
"
|
|
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",
|