mcp-meilisearch 1.3.9 → 1.3.10

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
@@ -92,7 +92,7 @@ const client = new MCPClient("mcp-meilisearch-client");
92
92
 
93
93
  await client.connectToServer("http://localhost:4995/mcp");
94
94
 
95
- const result = await client.callTool("search-across-all-indexes", {
95
+ const result = await client.callTool("global-search", {
96
96
  q: "search kiosco antonio",
97
97
  });
98
98
 
@@ -306,7 +306,7 @@ The MCP server exposes various tools that allow you to interact with Meilisearch
306
306
  - **Parameters**:
307
307
  - `searches` (string, required): JSON array of search queries, each with indexUid and q fields.
308
308
 
309
- #### search-across-all-indexes
309
+ #### global-search
310
310
 
311
311
  - **Description**: Search for a term across all available Meilisearch indexes and return combined results.
312
312
  - **Parameters**:
@@ -1 +1 @@
1
- {"version":3,"file":"search-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/meilisearch/search-tools.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAkCpE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,QAAQ,SAAS,SAoRpD,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"search-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/meilisearch/search-tools.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAwFpE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,QAAQ,SAAS,SA6NpD,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -1,6 +1,59 @@
1
1
  import { z } from "zod";
2
2
  import apiClient from "../../utils/api-handler.js";
3
3
  import { createErrorResponse } from "../../utils/error-handler.js";
4
+ const SearchParamsSchema = {
5
+ indexUid: z.string().describe("Unique identifier of the index"),
6
+ q: z.string().describe("Search query"),
7
+ limit: z
8
+ .number()
9
+ .min(1)
10
+ .optional()
11
+ .describe("Maximum number of results to return (default: 20)"),
12
+ offset: z
13
+ .number()
14
+ .min(0)
15
+ .optional()
16
+ .describe("Number of results to skip (default: 0)"),
17
+ filter: z.string().optional().describe("Filter query to apply"),
18
+ sort: z
19
+ .array(z.string())
20
+ .optional()
21
+ .describe('Attributes to sort by, e.g. ["price:asc"]'),
22
+ facets: z.array(z.string()).optional().describe("Facets to return"),
23
+ attributesToRetrieve: z
24
+ .array(z.string())
25
+ .optional()
26
+ .default(["*"])
27
+ .describe("Attributes to include in results"),
28
+ attributesToCrop: z
29
+ .array(z.string())
30
+ .optional()
31
+ .describe("Attributes to crop"),
32
+ cropLength: z
33
+ .number()
34
+ .optional()
35
+ .describe("Length at which to crop cropped attributes"),
36
+ attributesToHighlight: z
37
+ .array(z.string())
38
+ .optional()
39
+ .describe("Attributes to highlight"),
40
+ highlightPreTag: z
41
+ .string()
42
+ .optional()
43
+ .describe("Tag to insert before highlighted text"),
44
+ highlightPostTag: z
45
+ .string()
46
+ .optional()
47
+ .describe("Tag to insert after highlighted text"),
48
+ showMatchesPosition: z
49
+ .boolean()
50
+ .optional()
51
+ .describe("Whether to include match positions in results"),
52
+ matchingStrategy: z
53
+ .string()
54
+ .optional()
55
+ .describe("Matching strategy: 'all' or 'last'"),
56
+ };
4
57
  /**
5
58
  * Register search tools with the MCP server
6
59
  *
@@ -8,59 +61,7 @@ import { createErrorResponse } from "../../utils/error-handler.js";
8
61
  */
9
62
  export const registerSearchTools = (server) => {
10
63
  // Search in an index
11
- server.tool("search", "Search for documents in a Meilisearch index", {
12
- indexUid: z.string().describe("Unique identifier of the index"),
13
- q: z.string().describe("Search query"),
14
- limit: z
15
- .number()
16
- .min(1)
17
- .optional()
18
- .describe("Maximum number of results to return (default: 20)"),
19
- offset: z
20
- .number()
21
- .min(0)
22
- .optional()
23
- .describe("Number of results to skip (default: 0)"),
24
- filter: z.string().optional().describe("Filter query to apply"),
25
- sort: z
26
- .array(z.string())
27
- .optional()
28
- .describe('Attributes to sort by, e.g. ["price:asc"]'),
29
- facets: z.array(z.string()).optional().describe("Facets to return"),
30
- attributesToRetrieve: z
31
- .array(z.string())
32
- .optional()
33
- .default(["*"])
34
- .describe("Attributes to include in results"),
35
- attributesToCrop: z
36
- .array(z.string())
37
- .optional()
38
- .describe("Attributes to crop"),
39
- cropLength: z
40
- .number()
41
- .optional()
42
- .describe("Length at which to crop cropped attributes"),
43
- attributesToHighlight: z
44
- .array(z.string())
45
- .optional()
46
- .describe("Attributes to highlight"),
47
- highlightPreTag: z
48
- .string()
49
- .optional()
50
- .describe("Tag to insert before highlighted text"),
51
- highlightPostTag: z
52
- .string()
53
- .optional()
54
- .describe("Tag to insert after highlighted text"),
55
- showMatchesPosition: z
56
- .boolean()
57
- .optional()
58
- .describe("Whether to include match positions in results"),
59
- matchingStrategy: z
60
- .string()
61
- .optional()
62
- .describe("Matching strategy: 'all' or 'last'"),
63
- }, { category: "meilisearch" }, async ({ indexUid, q, limit, offset, filter, sort, facets, attributesToRetrieve, attributesToCrop, cropLength, attributesToHighlight, highlightPreTag, highlightPostTag, showMatchesPosition, matchingStrategy, }) => {
64
+ server.tool("search", "Search for documents in a Meilisearch index", SearchParamsSchema, { category: "meilisearch" }, async ({ indexUid, q, limit, offset, filter, sort, facets, attributesToRetrieve, attributesToCrop, cropLength, attributesToHighlight, highlightPreTag, highlightPostTag, showMatchesPosition, matchingStrategy, }) => {
64
65
  try {
65
66
  const response = await apiClient.post(`/indexes/${indexUid}/search`, {
66
67
  q,
@@ -90,22 +91,20 @@ export const registerSearchTools = (server) => {
90
91
  });
91
92
  // Multi-search across multiple indexes
92
93
  server.tool("multi-search", "Perform multiple searches in one request", {
93
- searches: z
94
- .string()
95
- .describe("JSON array of search queries, each with indexUid and q fields"),
96
- }, { category: "meilisearch" }, async ({ searches }) => {
94
+ queries: z
95
+ .array(z.object(SearchParamsSchema))
96
+ .describe("JSON array of search queries, each with indexUid and q fields as required. Other fields of the SearchParams are optional."),
97
+ }, { category: "meilisearch" }, async ({ queries }) => {
97
98
  try {
98
- // Parse the searches string to ensure it's valid JSON
99
- const parsedSearches = JSON.parse(searches);
100
- // Ensure searches is an array
101
- if (!Array.isArray(parsedSearches)) {
99
+ // Ensure queries is an array
100
+ if (!Array.isArray(queries)) {
102
101
  return {
103
102
  isError: true,
104
- content: [{ type: "text", text: "Searches must be a JSON array" }],
103
+ content: [{ type: "text", text: "Queries must be a JSON array" }],
105
104
  };
106
105
  }
107
106
  // Ensure each search has at least indexUid
108
- for (const search of parsedSearches) {
107
+ for (const search of queries) {
109
108
  if (!search.indexUid) {
110
109
  return {
111
110
  isError: true,
@@ -119,7 +118,7 @@ export const registerSearchTools = (server) => {
119
118
  }
120
119
  }
121
120
  const response = await apiClient.post("/multi-search", {
122
- queries: parsedSearches,
121
+ queries,
123
122
  });
124
123
  return {
125
124
  content: [
@@ -131,7 +130,7 @@ export const registerSearchTools = (server) => {
131
130
  return createErrorResponse(error);
132
131
  }
133
132
  });
134
- server.tool("search-across-all-indexes", "Search for a term across all available Meilisearch indexes and return combined results.", {
133
+ server.tool("global-search", "Search for a term across all available Meilisearch indexes and return combined results.", {
135
134
  q: z.string().describe("Search query"),
136
135
  limit: z
137
136
  .number()
@@ -0,0 +1,5 @@
1
+ export declare enum OPEN_ROUTER_API {
2
+ baseURL = "https://openrouter.ai/api/v1",
3
+ keys = "https://openrouter.ai/api/v1/keys"
4
+ }
5
+ //# sourceMappingURL=enums.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAAA,oBAAY,eAAe;IACzB,OAAO,iCAAiC;IACxC,IAAI,sCAAoB;CACzB"}
@@ -0,0 +1,5 @@
1
+ export var OPEN_ROUTER_API;
2
+ (function (OPEN_ROUTER_API) {
3
+ OPEN_ROUTER_API["baseURL"] = "https://openrouter.ai/api/v1";
4
+ OPEN_ROUTER_API["keys"] = "https://openrouter.ai/api/v1/keys";
5
+ })(OPEN_ROUTER_API || (OPEN_ROUTER_API = {}));
@@ -16,6 +16,7 @@ interface AIToolResponse {
16
16
  * to use based on the user's query
17
17
  */
18
18
  export declare class AIService {
19
+ private apiKey;
19
20
  private model;
20
21
  private static instance;
21
22
  private static serverInitialized;
@@ -40,7 +41,7 @@ export declare class AIService {
40
41
  * @param provider AI provider name (defaults to openai)
41
42
  * @param model Optional model to use (defaults to gpt-3.5-turbo)
42
43
  */
43
- initialize(apiKey: string, provider?: AiProviderNameOptions, model?: string): void;
44
+ initialize(apiKey: string, provider?: AiProviderNameOptions, model?: string, forceNewInit?: boolean): void;
44
45
  /**
45
46
  * Set the available tools that can be used by the AI
46
47
  * @param tools Array of tools with name, description, and parameters
@@ -1 +1 @@
1
- {"version":3,"file":"ai-handler.d.ts","sourceRoot":"","sources":["../../src/utils/ai-handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC;AAiBD,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC;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,CAAyC;IACvD,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;IA4BP;;;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,GAAG,IAAI,CAAC;YAwBnB,kBAAkB;YAiClB,uBAAuB;CAiCtC"}
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;AAE5D,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC;AAiBD,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAc;IAC5B,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,CAAyC;IACvD,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,EACd,YAAY,GAAE,OAAe,GAC5B,IAAI;IA6BP;;;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,GAAG,IAAI,CAAC;YAwBnB,kBAAkB;YAiClB,uBAAuB;CAiCtC"}
@@ -1,5 +1,6 @@
1
1
  import { OpenAI } from "openai";
2
2
  import systemPrompt from "../prompts/system.js";
3
+ import { OPEN_ROUTER_API } from "../types/enums.js";
3
4
  import { markdownToJson } from "./response-handler.js";
4
5
  import { InferenceClient } from "@huggingface/inference";
5
6
  /**
@@ -9,6 +10,7 @@ import { InferenceClient } from "@huggingface/inference";
9
10
  * to use based on the user's query
10
11
  */
11
12
  export class AIService {
13
+ apiKey = "";
12
14
  model = "gpt-3.5-turbo";
13
15
  static instance = null;
14
16
  static serverInitialized = false;
@@ -38,26 +40,27 @@ export class AIService {
38
40
  * @param provider AI provider name (defaults to openai)
39
41
  * @param model Optional model to use (defaults to gpt-3.5-turbo)
40
42
  */
41
- initialize(apiKey, provider = "openai", model) {
42
- if (AIService.serverInitialized) {
43
+ initialize(apiKey, provider = "openai", model, forceNewInit = false) {
44
+ if (AIService.serverInitialized && !forceNewInit) {
43
45
  console.warn("AIService has already been initialized by the server.");
44
46
  return;
45
47
  }
48
+ this.apiKey = apiKey;
46
49
  this.provider = provider;
47
50
  if (model)
48
51
  this.model = model;
49
52
  switch (this.provider) {
50
53
  case "openai":
51
- this.client = new OpenAI({ apiKey });
54
+ this.client = new OpenAI({ apiKey: this.apiKey });
52
55
  break;
53
56
  case "openrouter":
54
57
  this.client = new OpenAI({
55
- apiKey,
56
- baseURL: "https://openrouter.ai/api/v1",
58
+ apiKey: this.apiKey,
59
+ baseURL: OPEN_ROUTER_API.baseURL,
57
60
  });
58
61
  break;
59
62
  case "huggingface":
60
- this.client = new InferenceClient(apiKey);
63
+ this.client = new InferenceClient(this.apiKey);
61
64
  break;
62
65
  default:
63
66
  throw new Error(`Unsupported AI provider: ${this.provider}`);
@@ -126,9 +129,9 @@ export class AIService {
126
129
  { role: "user", content: query },
127
130
  ];
128
131
  if (this.provider === "huggingface") {
129
- return this.processHuggingFaceQuery(tools, messages);
132
+ return await this.processHuggingFaceQuery(tools, messages);
130
133
  }
131
- return this.processOpenAIQuery(tools, messages);
134
+ return await this.processOpenAIQuery(tools, messages);
132
135
  }
133
136
  async processOpenAIQuery(tools, messages) {
134
137
  const client = this.client;
@@ -23,7 +23,7 @@ export function markdownToJson(markdownJsonString) {
23
23
  S = S.replace(/,\s*([}\]])/g, "$1");
24
24
  try {
25
25
  const parsedJson = JSON.parse(S);
26
- return parsedJson;
26
+ return parseNestedJsonStrings(parsedJson);
27
27
  }
28
28
  catch (error) {
29
29
  console.error("Failed to parse JSON after transformations.");
@@ -33,3 +33,32 @@ export function markdownToJson(markdownJsonString) {
33
33
  return null;
34
34
  }
35
35
  }
36
+ function tryParseJsonString(str) {
37
+ try {
38
+ if (typeof str === "string" &&
39
+ ((str.startsWith("[") && str.endsWith("]")) ||
40
+ (str.startsWith("{") && str.endsWith("}")))) {
41
+ return JSON.parse(str);
42
+ }
43
+ return str;
44
+ }
45
+ catch {
46
+ return str;
47
+ }
48
+ }
49
+ function parseNestedJsonStrings(obj) {
50
+ if (Array.isArray(obj)) {
51
+ return obj.map((item) => parseNestedJsonStrings(item));
52
+ }
53
+ else if (obj !== null && typeof obj === "object") {
54
+ const result = {};
55
+ for (const key of Object.keys(obj)) {
56
+ result[key] = parseNestedJsonStrings(obj[key]);
57
+ }
58
+ return result;
59
+ }
60
+ else if (typeof obj === "string") {
61
+ return tryParseJsonString(obj);
62
+ }
63
+ return obj;
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-meilisearch",
3
- "version": "1.3.9",
3
+ "version": "1.3.10",
4
4
  "description": "Model Context Protocol (MCP) implementation for Meilisearch",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",