@index9/mcp 1.0.18 β†’ 1.0.20

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 (37) hide show
  1. package/README.md +82 -31
  2. package/dist/client.d.ts +3 -6
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +7 -27
  5. package/dist/mcp.d.ts.map +1 -1
  6. package/dist/mcp.js +42 -85
  7. package/dist/schemas.d.ts +130 -50
  8. package/dist/schemas.d.ts.map +1 -1
  9. package/dist/schemas.js +165 -84
  10. package/dist/tools/find_models.d.ts +3 -0
  11. package/dist/tools/find_models.d.ts.map +1 -0
  12. package/dist/tools/find_models.js +4 -0
  13. package/dist/tools/test_model.d.ts.map +1 -1
  14. package/dist/tools/test_model.js +1 -1
  15. package/dist/types/api.d.ts +33 -49
  16. package/dist/types/api.d.ts.map +1 -1
  17. package/dist/types/models.d.ts +3 -7
  18. package/dist/types/models.d.ts.map +1 -1
  19. package/dist/types/models.js +1 -1
  20. package/dist/utils/rateLimiter.d.ts +1 -1
  21. package/dist/utils/rateLimiter.js +1 -1
  22. package/package.json +3 -3
  23. package/dist/tools/compare_models.d.ts +0 -3
  24. package/dist/tools/compare_models.d.ts.map +0 -1
  25. package/dist/tools/compare_models.js +0 -4
  26. package/dist/tools/get_capabilities.d.ts +0 -9
  27. package/dist/tools/get_capabilities.d.ts.map +0 -1
  28. package/dist/tools/get_capabilities.js +0 -19
  29. package/dist/tools/list_models.d.ts +0 -23
  30. package/dist/tools/list_models.d.ts.map +0 -1
  31. package/dist/tools/list_models.js +0 -25
  32. package/dist/tools/recommend_model.d.ts +0 -3
  33. package/dist/tools/recommend_model.d.ts.map +0 -1
  34. package/dist/tools/recommend_model.js +0 -4
  35. package/dist/tools/search_models.d.ts +0 -14
  36. package/dist/tools/search_models.d.ts.map +0 -1
  37. package/dist/tools/search_models.js +0 -5
package/README.md CHANGED
@@ -3,24 +3,21 @@
3
3
  [![npm version](https://badge.fury.io/js/@index9%2Fmcp.svg)](https://badge.fury.io/js/@index9%2Fmcp)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- [πŸ“¦ **Install**](#quick-start) β€’ [πŸ› **Issues**](https://github.com/index9-org/mcp/issues)
6
+ [Install](#quick-start) β€’ [Issues](https://github.com/index9-org/mcp/issues)
7
7
 
8
- Real-time model intelligence for your AI assistant. Search 1200+ models, compare pricing and capabilities, test performanceβ€”all with current data, zero cost.
8
+ MCP server for searching and testing AI models. Data from [OpenRouter](https://openrouter.ai).
9
9
 
10
- ## What You Get
10
+ ## Quick Start
11
11
 
12
- Your AI assistant gets live model intelligence for smarter recommendations:
12
+ **Zero Config:** Search and Lookup features work immediately after installation. No API keys required.
13
13
 
14
- - **Search models** by capability, price, or modality across 1200+ options instantly
15
- - **Compare models** side-by-side with current pricing and context windows
16
- - **Get recommendations** tailored to your use case, budget, and requirements
17
- - **Test models** with real API calls to see latency and behavior before you commit
14
+ ### Cursor
18
15
 
19
- ## Quick Start
16
+ Open **Cursor Settings** β†’ **MCP** β†’ **Add new global MCP server**
20
17
 
21
- ### Cursor
18
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=index9&config=eyJjb21tYW5kIjoibnB4IC15IEBpbmRleDkvbWNwIn0%3D)
22
19
 
23
- Open Cursor Settings β†’ MCP β†’ Add new global MCP server
20
+ Or add manually:
24
21
 
25
22
  ```json
26
23
  {
@@ -33,17 +30,32 @@ Open Cursor Settings β†’ MCP β†’ Add new global MCP server
33
30
  }
34
31
  ```
35
32
 
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
-
38
33
  ### VS Code
39
34
 
40
- Open VS Code Settings β†’ MCP Servers β†’ Add Server
35
+ Add to your **MCP Servers** settings:
36
+
37
+ ```json
38
+ {
39
+ "mcp": {
40
+ "servers": {
41
+ "index9": {
42
+ "type": "stdio",
43
+ "command": "npx",
44
+ "args": ["-y", "@index9/mcp"]
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Claude Desktop
52
+
53
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
41
54
 
42
55
  ```json
43
- "mcp": {
44
- "servers": {
56
+ {
57
+ "mcpServers": {
45
58
  "index9": {
46
- "type": "stdio",
47
59
  "command": "npx",
48
60
  "args": ["-y", "@index9/mcp"]
49
61
  }
@@ -51,28 +63,67 @@ Open VS Code Settings β†’ MCP Servers β†’ Add Server
51
63
  }
52
64
  ```
53
65
 
54
- ### Other Clients
66
+ See [index9.dev](https://index9.dev/#installation) for Windsurf, Cline, and other clients.
55
67
 
56
- See [installation guide](https://www.index9.dev/#installation) for Claude Desktop, Claude Code, Windsurf, Cline, and 30+ other MCP clients.
68
+ ## Configuration (Optional)
57
69
 
58
- ## Configuration
70
+ The **find_models** and **get_model** tools are free and require no configuration.
59
71
 
60
- ### Testing Models (Optional)
72
+ To use the **test_model** tool (for running live API calls), you must provide an OpenRouter API key.
61
73
 
62
- To enable live model testing, add your OpenRouter API key from [openrouter.ai/keys](https://openrouter.ai/keys) to your MCP config. Charges go directly to your OpenRouter account.
74
+ ```json
75
+ {
76
+ "mcpServers": {
77
+ "index9": {
78
+ "command": "npx",
79
+ "args": ["-y", "@index9/mcp"],
80
+ "env": {
81
+ "OPENROUTER_API_KEY": "sk-..."
82
+ }
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ _Your key is never stored. It is only used to make ephemeral requests to OpenRouter for testing purposes._
63
89
 
64
- ## Available Tools
90
+ ### CLI Tools (Claude Code, etc.)
65
91
 
66
- - **`list_models`** - Filter models by provider, context, pricing, capabilities, and modality
67
- - **`search_models`** - Natural language search across all models
68
- - **`get_model`** - Get complete model specs and details
69
- - **`compare_models`** - Side-by-side comparison of up to 10 models
70
- - **`recommend_model`** - AI-powered recommendations for your use case
71
- - **`test_model`** - Live API testing with latency and cost estimates (requires API key)
92
+ For CLI-based MCP clients, set the environment variable before running:
93
+
94
+ ```bash
95
+ export OPENROUTER_API_KEY="sk-..."
96
+ ```
72
97
 
73
- ## Data Sources
98
+ Or add it to your shell profile (`~/.zshrc`, `~/.bashrc`) for persistence.
99
+
100
+ ## Tools
101
+
102
+ | Tool | Description | Config Required |
103
+ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------- |
104
+ | `find_models` | Search and filter 300+ AI models. Use natural language ("fast coding model") or strict filters for price, context, and capabilities. | None |
105
+ | `get_model` | Get complete technical specifications. Returns pricing, context windows, max output tokens, and capabilities for any model. | None |
106
+ | `test_model` | Run live performance tests. Execute real API calls to multiple models simultaneously to compare latency, token usage, and costs. | `OPENROUTER_API_KEY` |
107
+
108
+ ## Usage Examples
109
+
110
+ Ask your AI assistant natural questions to find models:
111
+
112
+ - "Find a cheap vision model with at least 128k context"
113
+ - "What are the best models for coding under $1 per million tokens?"
114
+ - "Compare the specs of gpt-4o and claude-3-5-sonnet"
115
+ - "Test the latency of haiku vs gemini-flash with a simple 'hello world' prompt"
116
+
117
+ ## Tip: Add an Auto-Invoke Rule
118
+
119
+ For best results, add a rule so your AI assistant automatically uses index9 when answering model questions:
120
+
121
+ ```text
122
+ When choosing AI models or comparing pricing/capabilities, use index9 MCP tools
123
+ to get current data instead of relying on training knowledge.
124
+ ```
74
125
 
75
- Data sourced from [OpenRouter](https://openrouter.ai) and [Models.dev](https://models.dev).
126
+ Add this to your client's rule system: Cursor Settings β†’ Rules, `.windsurfrules`, `CLAUDE.md`, etc.
76
127
 
77
128
  ## License
78
129
 
package/dist/client.d.ts CHANGED
@@ -1,8 +1,5 @@
1
- import type { ListModelsResponse, GetModelResponse, SearchModelsResponse, CompareModelsResponse, RecommendModelResponse, TestModelResponse, ListModelsQueryParams } from "./types/index.js";
2
- export declare function listModels(params: ListModelsQueryParams): Promise<ListModelsResponse>;
1
+ import type { GetModelResponse, FindModelsResponse, FindModelsRequest, TestModelResponse } from "./types/index.js";
2
+ export declare function findModels(params: FindModelsRequest): Promise<FindModelsResponse>;
3
3
  export declare function getModel(modelId: string): Promise<GetModelResponse>;
4
- export declare function searchModels(query: string, limit?: number, threshold?: number): Promise<SearchModelsResponse>;
5
- export declare function compareModels(modelIds: string[]): Promise<CompareModelsResponse>;
6
- export declare function recommendModel(useCase: string, maxPrice?: number, minContext?: number, requiredCapabilities?: string[], limit?: number): Promise<RecommendModelResponse>;
7
- export declare function testModel(modelIds: string[], testType?: "quick" | "code" | "reasoning" | "instruction" | "tool_calling", openRouterApiKey?: string | null): Promise<TestModelResponse>;
4
+ export declare function testModel(modelIds: string[], testType?: "quick" | "code" | "reasoning" | "instruction" | "tool_calling", openRouterApiKey?: string | null, prompt?: string, maxTokens?: number, temperature?: number, systemPrompt?: string): Promise<TestModelResponse>;
8
5
  //# sourceMappingURL=client.d.ts.map
@@ -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;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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AA2B1B,wBAAsB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAGvF;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGzE;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,EAChC,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAW5B"}
package/dist/client.js CHANGED
@@ -17,43 +17,23 @@ const createAxiosClient = (timeout) => {
17
17
  };
18
18
  const client = createAxiosClient(API_TIMEOUT);
19
19
  const testClient = createAxiosClient(TEST_MODEL_TIMEOUT); // test operations with longer timeout
20
- export async function listModels(params) {
21
- const { data } = await client.get("/models", { params });
20
+ export async function findModels(params) {
21
+ const { data } = await client.post("/models/find", params);
22
22
  return data;
23
23
  }
24
24
  export async function getModel(modelId) {
25
25
  const { data } = await client.get(`/models/${encodeURIComponent(modelId)}`);
26
26
  return data;
27
27
  }
28
- export async function searchModels(query, limit, threshold) {
29
- const { data } = await client.post("/search", {
30
- query,
31
- limit,
32
- threshold,
33
- });
34
- return data;
35
- }
36
- export async function compareModels(modelIds) {
37
- const { data } = await client.post("/models/compare", {
38
- model_ids: modelIds,
39
- });
40
- return data;
41
- }
42
- export async function recommendModel(useCase, maxPrice, minContext, requiredCapabilities, limit) {
43
- const { data } = await client.post("/models/recommend", {
44
- use_case: useCase,
45
- max_price_per_m: maxPrice,
46
- min_context: minContext,
47
- required_capabilities: requiredCapabilities,
48
- limit,
49
- });
50
- return data;
51
- }
52
- export async function testModel(modelIds, testType, openRouterApiKey) {
28
+ export async function testModel(modelIds, testType, openRouterApiKey, prompt, maxTokens, temperature, systemPrompt) {
53
29
  const { data } = await testClient.post("/test", {
54
30
  model_ids: modelIds,
55
31
  test_type: testType,
32
+ custom_prompt: prompt,
56
33
  open_router_api_key: openRouterApiKey || undefined,
34
+ max_tokens: maxTokens,
35
+ temperature,
36
+ system_prompt: systemPrompt,
57
37
  });
58
38
  return data;
59
39
  }
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;AA6BpE,wBAAgB,eAAe,cAgM9B;AAED,wBAAsB,cAAc,kBAoBnC"}
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0BpE,wBAAgB,eAAe,cAmI9B;AAED,wBAAsB,cAAc,kBAoBnC"}
package/dist/mcp.js CHANGED
@@ -5,14 +5,11 @@ import { fileURLToPath } from "node:url";
5
5
  import { dirname, join } from "node:path";
6
6
  import { logger } from "./logger.js";
7
7
  import { checkRateLimit } from "./utils/rateLimiter.js";
8
- import { listModelsTool } from "./tools/list_models.js";
9
- import { searchModelsTool } from "./tools/search_models.js";
8
+ import { findModelsTool } from "./tools/find_models.js";
10
9
  import { getModelTool } from "./tools/get_model.js";
11
- import { compareModelsTool } from "./tools/compare_models.js";
12
- import { recommendModelTool } from "./tools/recommend_model.js";
13
10
  import { testModelTool } from "./tools/test_model.js";
14
11
  import { OPEN_ROUTER_API_KEY } from "./config.js";
15
- import { listModelsSchema, searchModelsSchema, getModelSchema, compareModelsSchema, recommendModelSchema, testModelSchema, } from "./schemas.js";
12
+ import { findModelsSchema, getModelSchema, testModelSchema, findModelsOutputSchema, getModelOutputSchema, testModelOutputSchema, } from "./schemas.js";
16
13
  const __filename = fileURLToPath(import.meta.url);
17
14
  const __dirname = dirname(__filename);
18
15
  const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
@@ -21,17 +18,31 @@ export function createMCPServer() {
21
18
  name: "index9",
22
19
  version: packageJson.version,
23
20
  });
24
- server.registerTool("list_models", {
25
- title: "List AI Models",
26
- description: "Filter AI models by exact criteria (provider, context window, pricing, capabilities). Returns up to 100 models with full details. Use search_models for natural language queries.",
27
- inputSchema: listModelsSchema,
21
+ server.registerTool("find_models", {
22
+ title: "Find AI Models",
23
+ description: `Search and filter 300+ AI models. Returns ranked results with pricing, context windows, and capabilities.
24
+
25
+ Call this tool first to discover model IDs, unless the user provides one (format: 'provider/model-name').
26
+
27
+ Parameters:
28
+ - query: Natural language search (e.g., 'fast cheap coding model')
29
+ - provider: Filter by provider(s). Comma-separated for multiple (e.g., 'openai,anthropic')
30
+ - min_context, max_context, max_price_per_m, capabilities: Exact filters
31
+ - sort_by: 'relevance' (default), 'price_asc', 'price_desc', 'date_desc', 'context_desc'
32
+ - limit, offset: Pagination
33
+
34
+ Scores: Results include a 'score' field (0-150+). Higher = more relevant. Combines semantic similarity, capability matching, and model quality signals. Use for relative ranking, not absolute measurement.
35
+
36
+ Use model IDs from results with get_model for full specs or test_model for live testing.`,
37
+ inputSchema: findModelsSchema,
38
+ outputSchema: findModelsOutputSchema,
28
39
  }, async (input) => {
29
- if (!checkRateLimit("list_models")) {
30
- logger.warn({ tool: "list_models" }, "Rate limit exceeded");
40
+ if (!checkRateLimit("find_models")) {
41
+ logger.warn({ tool: "find_models" }, "Rate limit exceeded");
31
42
  throw new Error("Rate limit exceeded. Please try again later.");
32
43
  }
33
44
  try {
34
- const result = await listModelsTool(input);
45
+ const result = await findModelsTool(input);
35
46
  return {
36
47
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
37
48
  structuredContent: result,
@@ -39,36 +50,19 @@ export function createMCPServer() {
39
50
  }
40
51
  catch (error) {
41
52
  const errorMessage = error instanceof Error ? error.message : String(error);
42
- logger.error({ tool: "list_models", error: errorMessage }, "Tool execution failed");
43
- throw error;
44
- }
45
- });
46
- server.registerTool("search_models", {
47
- title: "Search AI Models",
48
- description: "Search 1200+ AI models using natural language (e.g., 'fast cheap coding model'). Uses semantic search and fuzzy matching. Returns ranked results with similarity scores.",
49
- inputSchema: searchModelsSchema,
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
- }
55
- try {
56
- const result = await searchModelsTool(input);
57
- return {
58
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
59
- structuredContent: result,
60
- };
61
- }
62
- catch (error) {
63
- const errorMessage = error instanceof Error ? error.message : String(error);
64
- logger.error({ tool: "search_models", error: errorMessage }, "Tool execution failed");
53
+ logger.error({ tool: "find_models", error: errorMessage }, "Tool execution failed");
65
54
  throw error;
66
55
  }
67
56
  });
68
57
  server.registerTool("get_model", {
69
58
  title: "Get Model Details",
70
- description: "Get complete details for a specific model by ID: pricing, capabilities, context window, parameters, and deployment info. Most detailed view available.",
59
+ description: `Get complete specs for a model by ID. Returns pricing, context window, capabilities, architecture, and per-request limits.
60
+
61
+ Call after find_models to get full details, or when the user provides a model ID (format: 'provider/model-name').
62
+
63
+ Returns 404 if model not found. Use find_models to discover valid IDs.`,
71
64
  inputSchema: getModelSchema,
65
+ outputSchema: getModelOutputSchema,
72
66
  }, async (input) => {
73
67
  if (!checkRateLimit("get_model")) {
74
68
  logger.warn({ tool: "get_model" }, "Rate limit exceeded");
@@ -87,58 +81,21 @@ export function createMCPServer() {
87
81
  throw error;
88
82
  }
89
83
  });
90
- server.registerTool("compare_models", {
91
- title: "Compare AI Models",
92
- description: "Compare 2-10 AI models side-by-side with unified pricing, context windows, and capabilities. Perfect for final selection decisions.",
93
- inputSchema: compareModelsSchema,
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 || []));
100
- try {
101
- if (model_ids.length < 2) {
102
- throw new Error("At least 2 unique model IDs are required");
103
- }
104
- const result = await compareModelsTool({ model_ids });
105
- return {
106
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
107
- structuredContent: result,
108
- };
109
- }
110
- catch (error) {
111
- const errorMessage = error instanceof Error ? error.message : String(error);
112
- logger.error({ tool: "compare_models", error: errorMessage }, "Tool execution failed");
113
- throw error;
114
- }
115
- });
116
- server.registerTool("recommend_model", {
117
- title: "Get Model Recommendations",
118
- description: "Get AI-powered model recommendations for your use case (e.g., 'coding assistant'). Optionally specify budget, context needs, and required capabilities. Returns ranked suggestions.",
119
- inputSchema: recommendModelSchema,
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
- }
125
- try {
126
- const result = await recommendModelTool(input);
127
- return {
128
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
129
- structuredContent: result,
130
- };
131
- }
132
- catch (error) {
133
- const errorMessage = error instanceof Error ? error.message : String(error);
134
- logger.error({ tool: "recommend_model", error: errorMessage }, "Tool execution failed");
135
- throw error;
136
- }
137
- });
138
84
  server.registerTool("test_model", {
139
85
  title: "Test AI Models",
140
- description: "Test 1-5 models with real API calls via OpenRouter to check latency, performance, and tool calling. Shows actual costs and behavior. Test types: quick (math), code, reasoning, instruction, tool_calling.",
86
+ description: `Make live API calls to 1-5 models via OpenRouter. Returns output text, latency (ms), token usage, and cost estimates.
87
+
88
+ Requires OPENROUTER_API_KEY in MCP client configuration. Costs are billed to your OpenRouter account.
89
+
90
+ Parameters:
91
+ - model_ids: 1-5 model IDs to test (all receive identical prompts)
92
+ - test_type: 'quick' (math), 'code', 'reasoning', 'instruction', 'tool_calling'
93
+ - prompt: Custom prompt (overrides test_type)
94
+ - max_tokens: Response length limit (default 1000)
95
+
96
+ Use find_models or get_model first to identify model IDs.`,
141
97
  inputSchema: testModelSchema,
98
+ outputSchema: testModelOutputSchema,
142
99
  }, async (input) => {
143
100
  if (!checkRateLimit("test_model")) {
144
101
  logger.warn({ tool: "test_model" }, "Rate limit exceeded");
package/dist/schemas.d.ts CHANGED
@@ -1,38 +1,30 @@
1
1
  import { z } from "zod";
2
2
  /**
3
- * List Models Tool Schema
4
- * Filters AI models using exact technical criteria
3
+ * Find Models Tool Schema
4
+ * Unified search and filter for AI models
5
5
  */
6
- export declare const listModelsSchema: z.ZodObject<{
6
+ export declare const findModelsSchema: z.ZodObject<{
7
+ query: z.ZodOptional<z.ZodString>;
7
8
  provider: z.ZodOptional<z.ZodString>;
8
- owner: z.ZodOptional<z.ZodString>;
9
9
  min_context: z.ZodOptional<z.ZodNumber>;
10
- modality: z.ZodOptional<z.ZodEnum<{
11
- audio: "audio";
10
+ max_context: z.ZodOptional<z.ZodNumber>;
11
+ max_price_per_m: z.ZodOptional<z.ZodNumber>;
12
+ capabilities: z.ZodOptional<z.ZodArray<z.ZodEnum<{
12
13
  vision: "vision";
13
- text: "text";
14
+ audio: "audio";
14
15
  video: "video";
15
- image: "image";
16
- }>>;
17
- supports_tool_calling: z.ZodOptional<z.ZodBoolean>;
18
- supports_json_mode: z.ZodOptional<z.ZodBoolean>;
19
- tokenizer: z.ZodOptional<z.ZodString>;
20
- output_modality: z.ZodOptional<z.ZodEnum<{
21
- text: "text";
22
- image: "image";
23
- embeddings: "embeddings";
24
- }>>;
25
- max_price_per_m: z.ZodOptional<z.ZodNumber>;
26
- limit: z.ZodDefault<z.ZodNumber>;
27
- }, z.core.$strip>;
28
- /**
29
- * Search Models Tool Schema
30
- * Natural language semantic search across AI models
31
- */
32
- export declare const searchModelsSchema: z.ZodObject<{
33
- query: z.ZodString;
16
+ tool_calling: "tool_calling";
17
+ json_mode: "json_mode";
18
+ }>>>;
19
+ sort_by: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
20
+ relevance: "relevance";
21
+ price_asc: "price_asc";
22
+ price_desc: "price_desc";
23
+ date_desc: "date_desc";
24
+ context_desc: "context_desc";
25
+ }>>>;
34
26
  limit: z.ZodDefault<z.ZodNumber>;
35
- threshold: z.ZodDefault<z.ZodNumber>;
27
+ offset: z.ZodDefault<z.ZodNumber>;
36
28
  }, z.core.$strip>;
37
29
  /**
38
30
  * Get Model Tool Schema
@@ -41,24 +33,6 @@ export declare const searchModelsSchema: z.ZodObject<{
41
33
  export declare const getModelSchema: z.ZodObject<{
42
34
  model_id: z.ZodString;
43
35
  }, z.core.$strip>;
44
- /**
45
- * Compare Models Tool Schema
46
- * Side-by-side comparison of multiple models
47
- */
48
- export declare const compareModelsSchema: z.ZodObject<{
49
- model_ids: z.ZodArray<z.ZodString>;
50
- }, z.core.$strip>;
51
- /**
52
- * Recommend Model Tool Schema
53
- * AI-powered model recommendations based on use case
54
- */
55
- export declare const recommendModelSchema: z.ZodObject<{
56
- use_case: z.ZodString;
57
- max_price_per_m: z.ZodOptional<z.ZodNumber>;
58
- min_context: z.ZodOptional<z.ZodNumber>;
59
- required_capabilities: z.ZodOptional<z.ZodArray<z.ZodString>>;
60
- limit: z.ZodDefault<z.ZodNumber>;
61
- }, z.core.$strip>;
62
36
  /**
63
37
  * Test Model Tool Schema
64
38
  * Run live API tests against models via OpenRouter
@@ -66,17 +40,123 @@ export declare const recommendModelSchema: z.ZodObject<{
66
40
  export declare const testModelSchema: z.ZodObject<{
67
41
  model_ids: z.ZodArray<z.ZodString>;
68
42
  test_type: z.ZodDefault<z.ZodEnum<{
69
- tool_calling: "tool_calling";
70
43
  quick: "quick";
71
44
  code: "code";
72
45
  reasoning: "reasoning";
73
46
  instruction: "instruction";
47
+ tool_calling: "tool_calling";
74
48
  }>>;
49
+ prompt: z.ZodOptional<z.ZodString>;
50
+ max_tokens: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
51
+ temperature: z.ZodOptional<z.ZodNumber>;
52
+ system_prompt: z.ZodOptional<z.ZodString>;
53
+ }, z.core.$strip>;
54
+ /**
55
+ * Output Schemas for Tool Results
56
+ * Define expected return structures for better LLM understanding
57
+ */
58
+ export declare const findModelsOutputSchema: z.ZodObject<{
59
+ results: z.ZodArray<z.ZodObject<{
60
+ id: z.ZodString;
61
+ name: z.ZodString;
62
+ description: z.ZodNullable<z.ZodString>;
63
+ score: z.ZodNumber;
64
+ provider: z.ZodString;
65
+ context_window: z.ZodNullable<z.ZodNumber>;
66
+ pricing: z.ZodObject<{
67
+ input: z.ZodNullable<z.ZodNumber>;
68
+ output: z.ZodNullable<z.ZodNumber>;
69
+ }, z.core.$strip>;
70
+ capabilities: z.ZodObject<{
71
+ vision: z.ZodNullable<z.ZodBoolean>;
72
+ audio: z.ZodNullable<z.ZodBoolean>;
73
+ tool_calling: z.ZodNullable<z.ZodBoolean>;
74
+ json_mode: z.ZodNullable<z.ZodBoolean>;
75
+ video: z.ZodNullable<z.ZodBoolean>;
76
+ }, z.core.$strip>;
77
+ matched_features: z.ZodOptional<z.ZodArray<z.ZodString>>;
78
+ hugging_face_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
79
+ release_date: z.ZodOptional<z.ZodNullable<z.ZodString>>;
80
+ }, z.core.$strip>>;
81
+ total: z.ZodOptional<z.ZodNumber>;
82
+ }, z.core.$strip>;
83
+ export declare const getModelOutputSchema: z.ZodObject<{
84
+ id: z.ZodString;
85
+ name: z.ZodString;
86
+ provider: z.ZodString;
87
+ description: z.ZodNullable<z.ZodString>;
88
+ family: z.ZodNullable<z.ZodString>;
89
+ version: z.ZodNullable<z.ZodString>;
90
+ release_date: z.ZodNullable<z.ZodString>;
91
+ limits: z.ZodObject<{
92
+ context_window: z.ZodNumber;
93
+ max_output_tokens: z.ZodNullable<z.ZodNumber>;
94
+ }, z.core.$strip>;
95
+ pricing: z.ZodObject<{
96
+ input: z.ZodNullable<z.ZodNumber>;
97
+ output: z.ZodNullable<z.ZodNumber>;
98
+ }, z.core.$strip>;
99
+ extended_pricing: z.ZodNullable<z.ZodObject<{
100
+ image: z.ZodNullable<z.ZodNumber>;
101
+ audio: z.ZodNullable<z.ZodNumber>;
102
+ web_search: z.ZodNullable<z.ZodNumber>;
103
+ cache_read: z.ZodNullable<z.ZodNumber>;
104
+ cache_write: z.ZodNullable<z.ZodNumber>;
105
+ discount: z.ZodNullable<z.ZodNumber>;
106
+ internal_reasoning: z.ZodNullable<z.ZodNumber>;
107
+ }, z.core.$strip>>;
108
+ capabilities: z.ZodObject<{
109
+ vision: z.ZodBoolean;
110
+ audio: z.ZodBoolean;
111
+ tool_calling: z.ZodBoolean;
112
+ json_mode: z.ZodBoolean;
113
+ video: z.ZodBoolean;
114
+ function_calling: z.ZodBoolean;
115
+ custom: z.ZodArray<z.ZodString>;
116
+ }, z.core.$strip>;
117
+ supported_parameters: z.ZodArray<z.ZodString>;
118
+ is_moderated: z.ZodBoolean;
119
+ input_modalities: z.ZodArray<z.ZodString>;
120
+ output_modalities: z.ZodArray<z.ZodString>;
121
+ architecture: z.ZodObject<{
122
+ tokenizer: z.ZodNullable<z.ZodString>;
123
+ instruct_type: z.ZodNullable<z.ZodString>;
124
+ }, z.core.$strip>;
125
+ per_request_limits: z.ZodNullable<z.ZodObject<{
126
+ prompt_tokens: z.ZodNullable<z.ZodNumber>;
127
+ completion_tokens: z.ZodNullable<z.ZodNumber>;
128
+ }, z.core.$strip>>;
129
+ }, z.core.$strip>;
130
+ export declare const testModelOutputSchema: z.ZodObject<{
131
+ test_type: z.ZodString;
132
+ prompt: z.ZodString;
133
+ results: z.ZodArray<z.ZodObject<{
134
+ model_id: z.ZodString;
135
+ model_name: z.ZodString;
136
+ latency_ms: z.ZodNumber;
137
+ output: z.ZodNullable<z.ZodString>;
138
+ tokens_used: z.ZodNullable<z.ZodObject<{
139
+ prompt_tokens: z.ZodNumber;
140
+ completion_tokens: z.ZodNumber;
141
+ total_tokens: z.ZodNumber;
142
+ }, z.core.$strip>>;
143
+ cost_estimate: z.ZodObject<{
144
+ input_cost: z.ZodNullable<z.ZodNumber>;
145
+ output_cost: z.ZodNullable<z.ZodNumber>;
146
+ total_cost: z.ZodNullable<z.ZodNumber>;
147
+ }, z.core.$strip>;
148
+ tool_calls_detected: z.ZodOptional<z.ZodBoolean>;
149
+ tool_calls: z.ZodOptional<z.ZodArray<z.ZodObject<{
150
+ name: z.ZodString;
151
+ arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
152
+ }, z.core.$strip>>>;
153
+ error: z.ZodNullable<z.ZodString>;
154
+ }, z.core.$strip>>;
75
155
  }, z.core.$strip>;
76
- export type ListModelsInput = z.infer<typeof listModelsSchema>;
77
- export type SearchModelsInput = z.infer<typeof searchModelsSchema>;
156
+ export type FindModelsInput = z.infer<typeof findModelsSchema>;
78
157
  export type GetModelInput = z.infer<typeof getModelSchema>;
79
- export type CompareModelsInput = z.infer<typeof compareModelsSchema>;
80
- export type RecommendModelInput = z.infer<typeof recommendModelSchema>;
81
158
  export type TestModelInput = z.infer<typeof testModelSchema>;
159
+ export type FindModelsOutput = z.infer<typeof findModelsOutputSchema>;
160
+ export type GetModelOutput = z.infer<typeof getModelOutputSchema>;
161
+ export type TestModelOutput = z.infer<typeof testModelOutputSchema>;
82
162
  //# sourceMappingURL=schemas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;iBAiC3B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;iBAmB7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,cAAc;;iBAKzB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;iBAM9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;iBA2B/B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;iBAY1B,CAAC;AAGH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAC3D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACrE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACvE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;iBA+D3B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,cAAc;;iBAOzB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;iBA4C1B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;iBA0BjC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkD/B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;iBAiChC,CAAC;AAGH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAC7D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}