mcp-meilisearch 1.4.2 → 1.4.5
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 +7 -6
- package/dist/client.js +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -60
- package/dist/server.d.ts +17 -13
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +54 -10
- package/dist/tools/core/ai-tools.d.ts.map +1 -1
- package/dist/tools/core/ai-tools.js +4 -23
- package/dist/types/options.d.ts.map +1 -1
- package/dist/utils/ai-handler.d.ts +6 -6
- package/dist/utils/ai-handler.d.ts.map +1 -1
- package/dist/utils/ai-handler.js +70 -59
- package/dist/utils/error-handler.d.ts +0 -3
- package/dist/utils/error-handler.d.ts.map +1 -1
- package/dist/utils/error-handler.js +0 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,14 +63,15 @@ pnpm add mcp-meilisearch
|
|
|
63
63
|
- `aiProviderApiKey`: AI provider API key for AI inference
|
|
64
64
|
- `llmModel`: AI model to use (Default: "gpt-3.5-turbo")
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
Please be aware that not all models support function calling, which is required for proper AI inference in this package. Make sure to select a model that supports the tools parameter.
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
| Provider | Supported Models |
|
|
69
|
+
| ----------- | -------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| OpenAI | [GPT-4, GPT-3.5-turbo and other compatible models](https://platform.openai.com/docs/models) |
|
|
71
|
+
| OpenRouter | [Models with tools parameter support](https://openrouter.ai/models?fmt=cards&supported_parameters=tools) |
|
|
72
|
+
| HuggingFace | [Models with function calling capability](https://huggingface.co/models?other=function+calling) |
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
https://openrouter.ai/models?fmt=cards&supported_parameters=tools
|
|
72
|
-
|
|
73
|
-
Example configuration with OpenRouter:
|
|
74
|
+
#### Example server setup
|
|
74
75
|
|
|
75
76
|
```typescript
|
|
76
77
|
await mcpMeilisearchServer({
|
package/dist/client.js
CHANGED
|
@@ -160,12 +160,6 @@ export class MCPClient {
|
|
|
160
160
|
if (!result.success)
|
|
161
161
|
return result;
|
|
162
162
|
const { toolName, parameters, reasoning } = result.data;
|
|
163
|
-
if (!toolName) {
|
|
164
|
-
return {
|
|
165
|
-
success: false,
|
|
166
|
-
error: "AI could not determine which tool to use for this query",
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
163
|
if (justReasoning) {
|
|
170
164
|
return {
|
|
171
165
|
reasoning,
|
|
@@ -173,6 +167,12 @@ export class MCPClient {
|
|
|
173
167
|
toolUsed: toolName,
|
|
174
168
|
};
|
|
175
169
|
}
|
|
170
|
+
if (!toolName) {
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
error: "No tool name provided in AI response",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
176
|
const toolResult = await this.callTool(toolName, parameters);
|
|
177
177
|
return {
|
|
178
178
|
...toolResult,
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAK7B,OAAO,EAAE,aAAa,EAAyB,MAAM,oBAAoB,CAAC;AAE1E;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,aAA8B,GACtC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAmEtB;AAwCD,eAAe,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
|
-
import { parse as parseUrl } from "node:url";
|
|
3
2
|
import { AIService } from "./utils/ai-handler.js";
|
|
4
|
-
import { initServer } from "./server.js";
|
|
5
3
|
import { configHandler } from "./utils/config-handler.js";
|
|
6
|
-
import {
|
|
7
|
-
const defaultOptions = {
|
|
8
|
-
aiProviderApiKey: "",
|
|
9
|
-
meilisearchApiKey: "",
|
|
10
|
-
llmModel: "gpt-3.5-turbo",
|
|
11
|
-
aiProviderName: "openai",
|
|
12
|
-
meilisearchHost: "http://localhost:7700",
|
|
13
|
-
};
|
|
4
|
+
import { initServer, defaultOptions } from "./server.js";
|
|
14
5
|
/**
|
|
15
6
|
* Start a MCP server
|
|
16
7
|
* @param options Configuration options for the MCP server
|
|
@@ -32,62 +23,18 @@ export async function mcpMeilisearchServer(options = defaultOptions) {
|
|
|
32
23
|
else {
|
|
33
24
|
console.warn("AI provider API key not found. AI will not be available");
|
|
34
25
|
}
|
|
35
|
-
const httpPort = options.httpPort || 4995;
|
|
36
|
-
const transport = options.transport || "http";
|
|
37
26
|
let mcpServerInstance = null;
|
|
38
|
-
const
|
|
27
|
+
const transport = options.transport;
|
|
28
|
+
const mcpEndpoint = options.mcpEndpoint;
|
|
29
|
+
const httpPort = options.httpPort || defaultOptions.httpPort;
|
|
39
30
|
const server = createServer(async (req, res) => {
|
|
40
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
41
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
42
|
-
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, mcp-session-id");
|
|
43
|
-
if (req.method === "OPTIONS") {
|
|
44
|
-
res.statusCode = 200;
|
|
45
|
-
res.end();
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const parsedUrl = parseUrl(req.url || "/", true);
|
|
49
|
-
const pathname = parsedUrl.pathname || "/";
|
|
50
|
-
if (!pathname.startsWith(mcpEndpoint)) {
|
|
51
|
-
res.statusCode = 404;
|
|
52
|
-
res.end(JSON.stringify({ error: "Not found" }));
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
31
|
if (!mcpServerInstance) {
|
|
56
|
-
console.error("MCP server not initialized yet");
|
|
57
32
|
res.statusCode = 503;
|
|
58
33
|
res.setHeader("Content-Type", "application/json");
|
|
59
|
-
res.end(JSON.stringify(
|
|
34
|
+
res.end(JSON.stringify({ error: "MCP server not initialized yet" }));
|
|
60
35
|
return;
|
|
61
36
|
}
|
|
62
|
-
|
|
63
|
-
await mcpServerInstance.handleGetRequest(req, res);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (req.method === "POST") {
|
|
67
|
-
let body = "";
|
|
68
|
-
req.on("data", (chunk) => {
|
|
69
|
-
body += chunk.toString();
|
|
70
|
-
});
|
|
71
|
-
req.on("end", async () => {
|
|
72
|
-
try {
|
|
73
|
-
const jsonBody = JSON.parse(body);
|
|
74
|
-
if (mcpServerInstance) {
|
|
75
|
-
await mcpServerInstance.handlePostRequest(req, res, jsonBody);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
res.statusCode = 503;
|
|
79
|
-
res.end(JSON.stringify(createErrorResponse("MCP server not initialized yet")));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
res.statusCode = 400;
|
|
84
|
-
res.end(JSON.stringify(createErrorResponse("Invalid JSON body")));
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
res.statusCode = 405;
|
|
90
|
-
res.end(JSON.stringify(createErrorResponse("Method not allowed")));
|
|
37
|
+
await mcpServerInstance.handleHttpRequest(req, res, mcpEndpoint);
|
|
91
38
|
});
|
|
92
39
|
await new Promise((resolve) => {
|
|
93
40
|
server.listen(httpPort, () => {
|
|
@@ -141,6 +88,7 @@ if (import.meta.url === `file://${process.argv?.[1]}`) {
|
|
|
141
88
|
break;
|
|
142
89
|
case "aiApiKey":
|
|
143
90
|
options.aiProviderApiKey = value;
|
|
91
|
+
break;
|
|
144
92
|
case "aiProvider":
|
|
145
93
|
options.aiProviderName = value;
|
|
146
94
|
break;
|
|
@@ -149,7 +97,7 @@ if (import.meta.url === `file://${process.argv?.[1]}`) {
|
|
|
149
97
|
break;
|
|
150
98
|
}
|
|
151
99
|
}
|
|
152
|
-
mcpMeilisearchServer(options)
|
|
100
|
+
await mcpMeilisearchServer(options)
|
|
153
101
|
.then(() => console.info("MCP server running"))
|
|
154
102
|
.catch((err) => {
|
|
155
103
|
console.error("Failed to start server:", err);
|
package/dist/server.d.ts
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { IncomingMessage, ServerResponse } from "http";
|
|
2
|
+
import { ServerOptions } from "./types/options.js";
|
|
2
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
/**
|
|
4
|
-
* Configuration for the MCP server
|
|
5
|
-
*/
|
|
6
|
-
interface ServerConfig {
|
|
7
|
-
host: string;
|
|
8
|
-
apiKey: string;
|
|
9
|
-
httpPort: number;
|
|
10
|
-
mcpEndpoint: string;
|
|
11
|
-
sessionTimeout: number;
|
|
12
|
-
sessionCleanupInterval: number;
|
|
13
|
-
}
|
|
14
4
|
/**
|
|
15
5
|
* Return type for the initServer function
|
|
16
6
|
*/
|
|
17
7
|
interface ServerInstance {
|
|
18
8
|
mcpServer: MCPServer;
|
|
19
9
|
}
|
|
10
|
+
export declare const defaultOptions: {
|
|
11
|
+
httpPort: number;
|
|
12
|
+
mcpEndpoint: string;
|
|
13
|
+
sessionTimeout: number;
|
|
14
|
+
sessionCleanupInterval: number;
|
|
15
|
+
};
|
|
20
16
|
/**
|
|
21
17
|
* Implementation of an MCP server for Meilisearch
|
|
22
18
|
*/
|
|
@@ -32,7 +28,14 @@ export declare class MCPServer {
|
|
|
32
28
|
* @param server The underlying MCP server implementation
|
|
33
29
|
* @param config Configuration options
|
|
34
30
|
*/
|
|
35
|
-
constructor(server: McpServer, config?: Partial<
|
|
31
|
+
constructor(server: McpServer, config?: Partial<ServerOptions>);
|
|
32
|
+
/**
|
|
33
|
+
* Handles an HTTP request and routes it to the appropriate handler
|
|
34
|
+
* @param req The HTTP request
|
|
35
|
+
* @param res The HTTP response
|
|
36
|
+
* @param mcpEndpoint The MCP endpoint path
|
|
37
|
+
*/
|
|
38
|
+
handleHttpRequest(req: IncomingMessage, res: ServerResponse, mcpEndpoint?: string): Promise<void>;
|
|
36
39
|
/**
|
|
37
40
|
* Handles an HTTP GET request
|
|
38
41
|
* @param req The HTTP request
|
|
@@ -93,9 +96,10 @@ export declare class MCPServer {
|
|
|
93
96
|
/**
|
|
94
97
|
* Initialize the MCP server with the specified transport
|
|
95
98
|
* @param transport The transport type to use ("stdio" or "http")
|
|
99
|
+
* @param config Configuration options for the server
|
|
96
100
|
* @returns A promise that resolves to the server instances
|
|
97
101
|
* @throws Error if the transport type is unsupported
|
|
98
102
|
*/
|
|
99
|
-
export declare const initServer: (transport
|
|
103
|
+
export declare const initServer: (transport?: "http" | "stdio", config?: Partial<ServerOptions>) => Promise<ServerInstance>;
|
|
100
104
|
export {};
|
|
101
105
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAOvD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAInD,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUpE;;GAEG;AACH,UAAU,cAAc;IACtB,SAAS,EAAE,SAAS,CAAC;CACtB;AASD,eAAO,MAAM,cAAc;;;;;CAK1B,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoB;IAE3D,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAuC;IAEvD;;;;OAIG;gBACS,MAAM,EAAE,SAAS,EAAE,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM;IAOlE;;;;;OAKG;IACG,iBAAiB,CACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,WAAW,GAAE,MAAmC,GAC/C,OAAO,CAAC,IAAI,CAAC;IA+ChB;;;;OAIG;IACG,gBAAgB,CACpB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC;IA+BhB;;;;;OAKG;IACG,iBAAiB,CACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,GAAG,GACR,OAAO,CAAC,IAAI,CAAC;IA+BhB;;OAEG;IACH,QAAQ,IAAI,IAAI;IAqBhB;;;;;OAKG;YACW,uBAAuB;IAuCrC;;OAEG;IACH,OAAO,CAAC,+BAA+B;IAWvC;;OAEG;YACW,gBAAgB;IAmB9B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAa3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAO7B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;OAEG;IACH,OAAO,CAAC,sBAAsB;CA2B/B;AA+DD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GACrB,YAAW,MAAM,GAAG,OAAgB,EACpC,SAAS,OAAO,CAAC,aAAa,CAAC,KAC9B,OAAO,CAAC,cAAc,CAcxB,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -12,13 +12,11 @@ import registerDocumentTools from "./tools/meilisearch/document-tools.js";
|
|
|
12
12
|
import registerSettingsTools from "./tools/meilisearch/settings-tools.js";
|
|
13
13
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
14
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
15
|
-
const
|
|
15
|
+
export const defaultOptions = {
|
|
16
16
|
httpPort: 4995,
|
|
17
17
|
mcpEndpoint: "/mcp",
|
|
18
18
|
sessionTimeout: 3600000,
|
|
19
19
|
sessionCleanupInterval: 60000,
|
|
20
|
-
apiKey: process.env.MEILISEARCH_API_KEY || "",
|
|
21
|
-
host: process.env.MEILISEARCH_HOST || "http://localhost:7700",
|
|
22
20
|
};
|
|
23
21
|
/**
|
|
24
22
|
* Implementation of an MCP server for Meilisearch
|
|
@@ -37,9 +35,52 @@ export class MCPServer {
|
|
|
37
35
|
*/
|
|
38
36
|
constructor(server, config = {}) {
|
|
39
37
|
this.server = server;
|
|
40
|
-
this.config = { ...
|
|
38
|
+
this.config = { ...defaultOptions, ...config };
|
|
41
39
|
this.startSessionCleanup();
|
|
42
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Handles an HTTP request and routes it to the appropriate handler
|
|
43
|
+
* @param req The HTTP request
|
|
44
|
+
* @param res The HTTP response
|
|
45
|
+
* @param mcpEndpoint The MCP endpoint path
|
|
46
|
+
*/
|
|
47
|
+
async handleHttpRequest(req, res, mcpEndpoint = defaultOptions.mcpEndpoint) {
|
|
48
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
49
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
50
|
+
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, mcp-session-id");
|
|
51
|
+
if (req.method === "OPTIONS") {
|
|
52
|
+
res.statusCode = 200;
|
|
53
|
+
res.end();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const parsedUrl = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
57
|
+
const pathname = parsedUrl.pathname || "/";
|
|
58
|
+
if (!pathname.startsWith(mcpEndpoint)) {
|
|
59
|
+
this.sendErrorResponse(res, 404, "Not found");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (req.method === "GET") {
|
|
63
|
+
await this.handleGetRequest(req, res);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (req.method === "POST") {
|
|
67
|
+
let body = "";
|
|
68
|
+
req.on("data", (chunk) => {
|
|
69
|
+
body += chunk.toString();
|
|
70
|
+
});
|
|
71
|
+
req.on("end", async () => {
|
|
72
|
+
try {
|
|
73
|
+
const jsonBody = JSON.parse(body);
|
|
74
|
+
await this.handlePostRequest(req, res, jsonBody);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
this.sendErrorResponse(res, 400, "Invalid JSON body");
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.sendErrorResponse(res, 405, "Method not allowed");
|
|
83
|
+
}
|
|
43
84
|
/**
|
|
44
85
|
* Handles an HTTP GET request
|
|
45
86
|
* @param req The HTTP request
|
|
@@ -215,9 +256,10 @@ export class MCPServer {
|
|
|
215
256
|
* Starts the session cleanup process
|
|
216
257
|
*/
|
|
217
258
|
startSessionCleanup() {
|
|
259
|
+
const cleanupInterval = this.config.sessionCleanupInterval;
|
|
218
260
|
this.cleanupInterval = setInterval(() => {
|
|
219
261
|
this.cleanupExpiredSessions();
|
|
220
|
-
},
|
|
262
|
+
}, cleanupInterval);
|
|
221
263
|
}
|
|
222
264
|
/**
|
|
223
265
|
* Removes expired sessions
|
|
@@ -225,8 +267,9 @@ export class MCPServer {
|
|
|
225
267
|
cleanupExpiredSessions() {
|
|
226
268
|
const now = Date.now();
|
|
227
269
|
const expiredIds = [];
|
|
270
|
+
const sessionTimeout = this.config.sessionTimeout;
|
|
228
271
|
for (const [sessionId, info] of this.sessions.entries()) {
|
|
229
|
-
if (now - info.lastActivity >
|
|
272
|
+
if (now - info.lastActivity > sessionTimeout) {
|
|
230
273
|
expiredIds.push(sessionId);
|
|
231
274
|
}
|
|
232
275
|
}
|
|
@@ -248,7 +291,7 @@ export class MCPServer {
|
|
|
248
291
|
}
|
|
249
292
|
}
|
|
250
293
|
/**
|
|
251
|
-
* Initialize the MCP server with HTTP transport
|
|
294
|
+
* Initialize the MCP server with HTTP transport
|
|
252
295
|
*/
|
|
253
296
|
const initServerHTTPTransport = async (customConfig) => {
|
|
254
297
|
const serverInstance = new McpServer({
|
|
@@ -296,16 +339,17 @@ const initServerStdioTransport = async (customConfig) => {
|
|
|
296
339
|
/**
|
|
297
340
|
* Initialize the MCP server with the specified transport
|
|
298
341
|
* @param transport The transport type to use ("stdio" or "http")
|
|
342
|
+
* @param config Configuration options for the server
|
|
299
343
|
* @returns A promise that resolves to the server instances
|
|
300
344
|
* @throws Error if the transport type is unsupported
|
|
301
345
|
*/
|
|
302
|
-
export const initServer = async (transport, config) => {
|
|
346
|
+
export const initServer = async (transport = "http", config) => {
|
|
303
347
|
try {
|
|
304
348
|
switch (transport) {
|
|
305
|
-
case "stdio":
|
|
306
|
-
return await initServerStdioTransport(config);
|
|
307
349
|
case "http":
|
|
308
350
|
return await initServerHTTPTransport(config);
|
|
351
|
+
case "stdio":
|
|
352
|
+
return await initServerStdioTransport(config);
|
|
309
353
|
default:
|
|
310
354
|
throw new Error(`Unsupported transport type: ${transport}`);
|
|
311
355
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/core/ai-tools.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAapE;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,SAAS,
|
|
1
|
+
{"version":3,"file":"ai-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/core/ai-tools.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAapE;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,SAAS,SAoDhD,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { AIService } from "../../utils/ai-handler.js";
|
|
3
2
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
3
|
+
import { AIService } from "../../utils/ai-handler.js";
|
|
4
4
|
import { createErrorResponse } from "../../utils/error-handler.js";
|
|
5
5
|
/**
|
|
6
6
|
* Register AI tools with the MCP server
|
|
@@ -28,35 +28,16 @@ export const registerAITools = (server) => {
|
|
|
28
28
|
});
|
|
29
29
|
aiService.setAvailableTools(availableTools);
|
|
30
30
|
const result = await aiService.processQuery(query, specificTools);
|
|
31
|
-
if (
|
|
32
|
-
return
|
|
33
|
-
isError: true,
|
|
34
|
-
content: [
|
|
35
|
-
{
|
|
36
|
-
type: "text",
|
|
37
|
-
text: "AI service not initialized. Please provide an API key.",
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
if (!result) {
|
|
43
|
-
return {
|
|
44
|
-
content: [
|
|
45
|
-
{
|
|
46
|
-
type: "text",
|
|
47
|
-
text: "AI couldn't determine an appropriate tool to use for this query.",
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
};
|
|
51
|
-
}
|
|
31
|
+
if (result.error)
|
|
32
|
+
return createErrorResponse(result.error);
|
|
52
33
|
return {
|
|
53
34
|
content: [
|
|
54
35
|
{
|
|
55
36
|
type: "text",
|
|
56
37
|
text: JSON.stringify({
|
|
57
38
|
toolName: result.toolName,
|
|
39
|
+
reasoning: result.reasoning,
|
|
58
40
|
parameters: result.parameters,
|
|
59
|
-
reasoning: result.reasoning || "No explanation provided",
|
|
60
41
|
}, null, 2),
|
|
61
42
|
},
|
|
62
43
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/types/options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/types/options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,qBAAqB,CAAC;IAEvC;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -5,9 +5,10 @@ interface AITool {
|
|
|
5
5
|
parameters: Record<string, any>;
|
|
6
6
|
}
|
|
7
7
|
interface AIToolResponse {
|
|
8
|
-
toolName
|
|
9
|
-
reasoning?: string
|
|
10
|
-
|
|
8
|
+
toolName?: string;
|
|
9
|
+
reasoning?: string;
|
|
10
|
+
error: string | null;
|
|
11
|
+
parameters?: Record<string, unknown>;
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* AI Inference Service
|
|
@@ -16,7 +17,6 @@ interface AIToolResponse {
|
|
|
16
17
|
* to use based on the user's query
|
|
17
18
|
*/
|
|
18
19
|
export declare class AIService {
|
|
19
|
-
private apiKey;
|
|
20
20
|
private model;
|
|
21
21
|
private static instance;
|
|
22
22
|
private static serverInitialized;
|
|
@@ -41,7 +41,7 @@ export declare class AIService {
|
|
|
41
41
|
* @param provider AI provider name (defaults to openai)
|
|
42
42
|
* @param model Optional model to use (defaults to gpt-3.5-turbo)
|
|
43
43
|
*/
|
|
44
|
-
initialize(apiKey: string, provider?: AiProviderNameOptions, model?: string
|
|
44
|
+
initialize(apiKey: string, provider?: AiProviderNameOptions, model?: string): void;
|
|
45
45
|
/**
|
|
46
46
|
* Set the available tools that can be used by the AI
|
|
47
47
|
* @param tools Array of tools with name, description, and parameters
|
|
@@ -66,7 +66,7 @@ export declare class AIService {
|
|
|
66
66
|
* @param specificTools Optional array of specific tool names to consider
|
|
67
67
|
* @returns Object containing the selected tool name and parameters
|
|
68
68
|
*/
|
|
69
|
-
processQuery(query: string, specificTools?: string[]): Promise<AIToolResponse
|
|
69
|
+
processQuery(query: string, specificTools?: string[]): Promise<AIToolResponse>;
|
|
70
70
|
private processOpenAIQuery;
|
|
71
71
|
private processHuggingFaceQuery;
|
|
72
72
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-handler.d.ts","sourceRoot":"","sources":["../../src/utils/ai-handler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"ai-handler.d.ts","sourceRoot":"","sources":["../../src/utils/ai-handler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC;AAmBD,UAAU,cAAc;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA0B;IACjD,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwB;IACrD,OAAO,CAAC,MAAM,CAAgD;IAC9D,OAAO,CAAC,cAAc,CAIb;IAET;;;OAGG;IACH,OAAO;IAEP;;;OAGG;WACW,WAAW,IAAI,SAAS;IAOtC;;;;;;OAMG;IACH,UAAU,CACR,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,qBAAgC,EAC1C,KAAK,CAAC,EAAE,MAAM,GACb,IAAI;IAyBP;;;OAGG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIxC,iBAAiB,IAAI,OAAO;IAI5B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;;;;OAKG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,cAAc,CAAC;YA4BZ,kBAAkB;YAyClB,uBAAuB;CAqCtC"}
|
package/dist/utils/ai-handler.js
CHANGED
|
@@ -10,7 +10,6 @@ import { InferenceClient } from "@huggingface/inference";
|
|
|
10
10
|
* to use based on the user's query
|
|
11
11
|
*/
|
|
12
12
|
export class AIService {
|
|
13
|
-
apiKey = "";
|
|
14
13
|
model = "gpt-3.5-turbo";
|
|
15
14
|
static instance = null;
|
|
16
15
|
static serverInitialized = false;
|
|
@@ -40,27 +39,23 @@ export class AIService {
|
|
|
40
39
|
* @param provider AI provider name (defaults to openai)
|
|
41
40
|
* @param model Optional model to use (defaults to gpt-3.5-turbo)
|
|
42
41
|
*/
|
|
43
|
-
initialize(apiKey, provider = "openai", model
|
|
44
|
-
if (AIService.serverInitialized
|
|
42
|
+
initialize(apiKey, provider = "openai", model) {
|
|
43
|
+
if (AIService.serverInitialized) {
|
|
45
44
|
console.warn("AIService has already been initialized by the server.");
|
|
46
45
|
return;
|
|
47
46
|
}
|
|
48
|
-
this.apiKey = apiKey;
|
|
49
47
|
this.provider = provider;
|
|
50
48
|
if (model)
|
|
51
49
|
this.model = model;
|
|
52
50
|
switch (this.provider) {
|
|
53
51
|
case "openai":
|
|
54
|
-
this.client = new OpenAI({ apiKey
|
|
55
|
-
break;
|
|
56
|
-
case "openrouter":
|
|
57
|
-
this.client = new OpenAI({
|
|
58
|
-
apiKey: this.apiKey,
|
|
59
|
-
baseURL: OPEN_ROUTER_API.baseURL,
|
|
60
|
-
});
|
|
52
|
+
this.client = new OpenAI({ apiKey });
|
|
61
53
|
break;
|
|
62
54
|
case "huggingface":
|
|
63
|
-
this.client = new InferenceClient(
|
|
55
|
+
this.client = new InferenceClient(apiKey);
|
|
56
|
+
break;
|
|
57
|
+
case "openrouter":
|
|
58
|
+
this.client = new OpenAI({ apiKey, baseURL: OPEN_ROUTER_API.baseURL });
|
|
64
59
|
break;
|
|
65
60
|
default:
|
|
66
61
|
throw new Error(`Unsupported AI provider: ${this.provider}`);
|
|
@@ -118,8 +113,11 @@ export class AIService {
|
|
|
118
113
|
* @returns Object containing the selected tool name and parameters
|
|
119
114
|
*/
|
|
120
115
|
async processQuery(query, specificTools) {
|
|
121
|
-
if (!this.ensureInitialized())
|
|
122
|
-
return
|
|
116
|
+
if (!this.ensureInitialized()) {
|
|
117
|
+
return {
|
|
118
|
+
error: "AI service not initialized. Please provide an API key.",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
123
121
|
const mentionedTools = this.extractToolNames(query);
|
|
124
122
|
const toolsToUse = specificTools || (mentionedTools.length ? mentionedTools : undefined);
|
|
125
123
|
const tools = this.getToolDefinitions(toolsToUse);
|
|
@@ -134,54 +132,67 @@ export class AIService {
|
|
|
134
132
|
return await this.processOpenAIQuery(tools, messages);
|
|
135
133
|
}
|
|
136
134
|
async processOpenAIQuery(tools, messages) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
135
|
+
try {
|
|
136
|
+
const client = this.client;
|
|
137
|
+
const response = await client.chat.completions.create({
|
|
138
|
+
tools,
|
|
139
|
+
messages,
|
|
140
|
+
model: this.model,
|
|
141
|
+
tool_choice: "required",
|
|
142
|
+
});
|
|
143
|
+
if (!response.choices?.length) {
|
|
144
|
+
return { error: "No choices returned from OpenAI" };
|
|
145
|
+
}
|
|
146
|
+
const message = response.choices[0].message;
|
|
147
|
+
if (message.content) {
|
|
148
|
+
const toolCall = markdownToJson(message.content);
|
|
149
|
+
if (!toolCall) {
|
|
150
|
+
return {
|
|
151
|
+
error: `Invalid tool call format in content: ${message.content}`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
error: null,
|
|
156
|
+
toolName: toolCall.name,
|
|
157
|
+
parameters: toolCall.parameters,
|
|
158
|
+
reasoning: JSON.stringify(toolCall, null, 2),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return { error: "No tool call or content in OpenAI response" };
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
return { error: `OpenAI API error: ${error}` };
|
|
146
165
|
}
|
|
147
|
-
if (!response?.choices.length)
|
|
148
|
-
return null;
|
|
149
|
-
const message = response.choices[0].message;
|
|
150
|
-
if (!message.content)
|
|
151
|
-
return null;
|
|
152
|
-
const toolCall = markdownToJson(message.content);
|
|
153
|
-
if (!toolCall)
|
|
154
|
-
return null;
|
|
155
|
-
return {
|
|
156
|
-
toolName: toolCall.name,
|
|
157
|
-
parameters: toolCall.parameters,
|
|
158
|
-
reasoning: JSON.stringify(toolCall, null, 2),
|
|
159
|
-
};
|
|
160
166
|
}
|
|
161
167
|
async processHuggingFaceQuery(tools, messages) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
168
|
+
try {
|
|
169
|
+
const client = this.client;
|
|
170
|
+
const response = await client.chatCompletion({
|
|
171
|
+
tools,
|
|
172
|
+
messages,
|
|
173
|
+
model: this.model,
|
|
174
|
+
tool_choice: "required",
|
|
175
|
+
});
|
|
176
|
+
if (!response.choices?.length) {
|
|
177
|
+
return { error: "No choices in Hugging Face response" };
|
|
178
|
+
}
|
|
179
|
+
const message = response.choices[0].message;
|
|
180
|
+
if (message.content) {
|
|
181
|
+
const toolCall = markdownToJson(message.content);
|
|
182
|
+
if (!toolCall) {
|
|
183
|
+
return { error: `Invalid tool call format in content: ${response}` };
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
error: null,
|
|
187
|
+
toolName: toolCall.name,
|
|
188
|
+
parameters: toolCall.parameters,
|
|
189
|
+
reasoning: JSON.stringify(toolCall, null, 2),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return { error: "No tool call or content in Hugging Face response" };
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
return { error: `Hugging Face API error: ${error}` };
|
|
172
196
|
}
|
|
173
|
-
if (!response?.choices.length)
|
|
174
|
-
return null;
|
|
175
|
-
const message = response.choices[0].message;
|
|
176
|
-
if (!message.content)
|
|
177
|
-
return null;
|
|
178
|
-
const toolCall = markdownToJson(message.content);
|
|
179
|
-
if (!toolCall)
|
|
180
|
-
return null;
|
|
181
|
-
return {
|
|
182
|
-
toolName: toolCall.name,
|
|
183
|
-
parameters: toolCall.parameters,
|
|
184
|
-
reasoning: JSON.stringify(toolCall, null, 2),
|
|
185
|
-
};
|
|
186
197
|
}
|
|
187
198
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/utils/error-handler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/utils/error-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,GAAG,KAAG,MAU3C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,GAAG;;;;;;CAK7C,CAAC;;4BAvBoC,GAAG,KAAG,MAAM;iCAkBP,GAAG;;;;;;;;AAO9C,wBAGE"}
|