Skip to content

Commit 594e58d

Browse files
authored
Merge pull request #9 from meilisearch/add-search-tool
Add search functionnality
2 parents 591da66 + 0069c53 commit 594e58d

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ A Model Context Protocol (MCP) server for interacting with Meilisearch through L
99
- Task monitoring and API key management
1010
- Built-in logging and monitoring tools
1111
- Dynamic connection configuration to switch between Meilisearch instances
12+
- Smart search across single or multiple indices
1213

1314
## Installation
1415

@@ -62,6 +63,43 @@ Example usage through MCP:
6263
}
6364
```
6465

66+
### Search Functionality
67+
68+
The server provides a flexible search tool that can search across one or all indices:
69+
70+
- `search`: Search through Meilisearch indices with optional parameters
71+
72+
Example usage through MCP:
73+
```json
74+
// Search in a specific index
75+
{
76+
"name": "search",
77+
"arguments": {
78+
"query": "search term",
79+
"indexUid": "movies",
80+
"limit": 10
81+
}
82+
}
83+
84+
// Search across all indices
85+
{
86+
"name": "search",
87+
"arguments": {
88+
"query": "search term",
89+
"limit": 5,
90+
"sort": ["releaseDate:desc"]
91+
}
92+
}
93+
```
94+
95+
Available search parameters:
96+
- `query`: The search query (required)
97+
- `indexUid`: Specific index to search in (optional)
98+
- `limit`: Maximum number of results per index (optional, default: 20)
99+
- `offset`: Number of results to skip (optional, default: 0)
100+
- `filter`: Filter expression (optional)
101+
- `sort`: Sorting rules (optional)
102+
65103
### Running the Server
66104

67105
```bash
@@ -82,6 +120,7 @@ npx @modelcontextprotocol/inspector python -m src.meilisearch_mcp
82120
- API Key Management: create/update/delete API keys
83121
- Task Monitoring: track and manage asynchronous tasks
84122
- System Monitoring: health checks and metrics
123+
- Search: flexible search across single or multiple indices
85124

86125
## Contributing
87126

src/meilisearch_mcp/client.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,63 @@ async def get_stats(self) -> Dict[str, Any]:
4444
"""Get database stats"""
4545
return self.client.get_all_stats()
4646

47+
async def search(
48+
self,
49+
query: str,
50+
index_uid: Optional[str] = None,
51+
limit: Optional[int] = 20,
52+
offset: Optional[int] = 0,
53+
filter: Optional[str] = None,
54+
sort: Optional[List[str]] = None,
55+
**kwargs
56+
) -> Dict[str, Any]:
57+
"""
58+
Search through Meilisearch indices.
59+
If index_uid is provided, search in that specific index.
60+
If not provided, search across all available indices.
61+
"""
62+
try:
63+
# Prepare search parameters, removing None values
64+
search_params = {
65+
"limit": limit if limit is not None else 20,
66+
"offset": offset if offset is not None else 0,
67+
}
68+
69+
if filter is not None:
70+
search_params["filter"] = filter
71+
if sort is not None:
72+
search_params["sort"] = sort
73+
74+
# Add any additional parameters
75+
search_params.update({k: v for k, v in kwargs.items() if v is not None})
76+
77+
if index_uid:
78+
# Search in specific index
79+
index = self.client.index(index_uid)
80+
return index.search(query, search_params)
81+
else:
82+
# Search across all indices
83+
results = {}
84+
indexes = self.client.get_indexes()
85+
86+
for index in indexes["results"]:
87+
try:
88+
search_result = index.search(query, search_params)
89+
if search_result["hits"]: # Only include indices with matches
90+
results[index.uid] = search_result
91+
except Exception as e:
92+
logger.warning(f"Failed to search index {index.uid}: {str(e)}")
93+
continue
94+
95+
return {
96+
"multi_index": True,
97+
"query": query,
98+
"results": results
99+
}
100+
101+
except Exception as e:
102+
raise Exception(f"Search failed: {str(e)}")
103+
47104
async def get_indexes(self) -> Dict[str, Any]:
48105
"""Get all indexes"""
49106
indexes = self.client.get_indexes()

src/meilisearch_mcp/server.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,22 @@ async def handle_list_tools() -> list[types.Tool]:
157157
"required": ["indexUid", "settings"],
158158
},
159159
),
160+
types.Tool(
161+
name="search",
162+
description="Search through Meilisearch indices. If indexUid is not provided, it will search across all indices.",
163+
inputSchema={
164+
"type": "object",
165+
"properties": {
166+
"query": {"type": "string"},
167+
"indexUid": {"type": "string", "optional": True},
168+
"limit": {"type": "integer", "optional": True},
169+
"offset": {"type": "integer", "optional": True},
170+
"filter": {"type": "string", "optional": True},
171+
"sort": {"type": "array", "items": {"type": "string"}, "optional": True},
172+
},
173+
"required": ["query"],
174+
},
175+
),
160176
types.Tool(
161177
name="get-task",
162178
description="Get information about a specific task",
@@ -354,6 +370,25 @@ async def handle_call_tool(
354370
)
355371
]
356372

373+
elif name == "search":
374+
search_results = await self.meili_client.search(
375+
query=arguments["query"],
376+
index_uid=arguments.get("indexUid"),
377+
limit=arguments.get("limit"),
378+
offset=arguments.get("offset"),
379+
filter=arguments.get("filter"),
380+
sort=arguments.get("sort"),
381+
)
382+
383+
# Format the results for better readability
384+
formatted_results = json.dumps(search_results, indent=2, default=json_serializer)
385+
return [
386+
types.TextContent(
387+
type="text",
388+
text=f"Search results for '{arguments['query']}':\n{formatted_results}"
389+
)
390+
]
391+
357392
elif name == "get-task":
358393
task = await self.meili_client.tasks.get_task(arguments["taskUid"])
359394
return [

0 commit comments

Comments
 (0)