Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions src/meilisearch_mcp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async def search(
offset: Optional[int] = 0,
filter: Optional[str] = None,
sort: Optional[List[str]] = None,
**kwargs
**kwargs,
) -> Dict[str, Any]:
"""
Search through Meilisearch indices.
Expand All @@ -65,12 +65,12 @@ async def search(
"limit": limit if limit is not None else 20,
"offset": offset if offset is not None else 0,
}

if filter is not None:
search_params["filter"] = filter
if sort is not None:
search_params["sort"] = sort

# Add any additional parameters
search_params.update({k: v for k, v in kwargs.items() if v is not None})

Expand All @@ -82,7 +82,7 @@ async def search(
# Search across all indices
results = {}
indexes = self.client.get_indexes()

for index in indexes["results"]:
try:
search_result = index.search(query, search_params)
Expand All @@ -91,13 +91,9 @@ async def search(
except Exception as e:
logger.warning(f"Failed to search index {index.uid}: {str(e)}")
continue

return {
"multi_index": True,
"query": query,
"results": results
}


return {"multi_index": True, "query": query, "results": results}

except Exception as e:
raise Exception(f"Search failed: {str(e)}")

Expand Down
1 change: 0 additions & 1 deletion src/meilisearch_mcp/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ async def get_documents(
return result_dict
else:
return result

except Exception as e:
raise Exception(f"Failed to get documents: {str(e)}")

Expand Down
104 changes: 79 additions & 25 deletions src/meilisearch_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ def json_serializer(obj: Any) -> str:
return str(obj)


def create_server(url: str = "http://localhost:7700", api_key: Optional[str] = None) -> "MeilisearchMCPServer":
def create_server(
url: str = "http://localhost:7700", api_key: Optional[str] = None
) -> "MeilisearchMCPServer":
"""Create and return a configured MeilisearchMCPServer instance"""
return MeilisearchMCPServer(url, api_key)

Expand All @@ -48,13 +50,15 @@ def __init__(
self.server = Server("meilisearch")
self._setup_handlers()

async def update_connection(self, url: Optional[str] = None, api_key: Optional[str] = None):
async def update_connection(
self, url: Optional[str] = None, api_key: Optional[str] = None
):
"""Update connection settings and reinitialize client if needed"""
if url:
self.url = url
if api_key:
self.api_key = api_key

self.meili_client = MeilisearchClient(self.url, self.api_key)
self.logger.info("Updated Meilisearch connection settings", url=self.url)

Expand Down Expand Up @@ -171,7 +175,11 @@ async def handle_list_tools() -> list[types.Tool]:
"limit": {"type": "integer", "optional": True},
"offset": {"type": "integer", "optional": True},
"filter": {"type": "string", "optional": True},
"sort": {"type": "array", "items": {"type": "string"}, "optional": True},
"sort": {
"type": "array",
"items": {"type": "string"},
"optional": True,
},
},
"required": ["query"],
},
Expand All @@ -194,20 +202,44 @@ async def handle_list_tools() -> list[types.Tool]:
"limit": {"type": "integer", "optional": True},
"from": {"type": "integer", "optional": True},
"reverse": {"type": "boolean", "optional": True},
"batchUids": {"type": "array", "items": {"type": "string"}, "optional": True},
"uids": {"type": "array", "items": {"type": "integer"}, "optional": True},
"canceledBy": {"type": "array", "items": {"type": "string"}, "optional": True},
"types": {"type": "array", "items": {"type": "string"}, "optional": True},
"statuses": {"type": "array", "items": {"type": "string"}, "optional": True},
"indexUids": {"type": "array", "items": {"type": "string"}, "optional": True},
"batchUids": {
"type": "array",
"items": {"type": "string"},
"optional": True,
},
"uids": {
"type": "array",
"items": {"type": "integer"},
"optional": True,
},
"canceledBy": {
"type": "array",
"items": {"type": "string"},
"optional": True,
},
"types": {
"type": "array",
"items": {"type": "string"},
"optional": True,
},
"statuses": {
"type": "array",
"items": {"type": "string"},
"optional": True,
},
"indexUids": {
"type": "array",
"items": {"type": "string"},
"optional": True,
},
"afterEnqueuedAt": {"type": "string", "optional": True},
"beforeEnqueuedAt": {"type": "string", "optional": True},
"afterStartedAt": {"type": "string", "optional": True},
"beforeStartedAt": {"type": "string", "optional": True},
"afterFinishedAt": {"type": "string", "optional": True},
"beforeFinishedAt": {"type": "string", "optional": True}
}
}
"beforeFinishedAt": {"type": "string", "optional": True},
},
},
),
types.Tool(
name="cancel-tasks",
Expand Down Expand Up @@ -293,8 +325,7 @@ async def handle_call_tool(

elif name == "update-connection-settings":
await self.update_connection(
arguments.get("url"),
arguments.get("api_key")
arguments.get("url"), arguments.get("api_key")
)
return [
types.TextContent(
Expand All @@ -313,7 +344,9 @@ async def handle_call_tool(

elif name == "list-indexes":
indexes = await self.meili_client.get_indexes()
formatted_json = json.dumps(indexes, indent=2, default=json_serializer)
formatted_json = json.dumps(
indexes, indent=2, default=json_serializer
)
return [
types.TextContent(
type="text", text=f"Indexes:\n{formatted_json}"
Expand Down Expand Up @@ -397,13 +430,15 @@ async def handle_call_tool(
filter=arguments.get("filter"),
sort=arguments.get("sort"),
)

# Format the results for better readability
formatted_results = json.dumps(search_results, indent=2, default=json_serializer)
formatted_results = json.dumps(
search_results, indent=2, default=json_serializer
)
return [
types.TextContent(
type="text",
text=f"Search results for '{arguments['query']}':\n{formatted_results}"
text=f"Search results for '{arguments['query']}':\n{formatted_results}",
)
]

Expand All @@ -416,11 +451,27 @@ async def handle_call_tool(
elif name == "get-tasks":
# Filter out any invalid parameters
valid_params = {
"limit", "from", "reverse", "batchUids", "uids", "canceledBy",
"types", "statuses", "indexUids", "afterEnqueuedAt", "beforeEnqueuedAt",
"afterStartedAt", "beforeStartedAt", "afterFinishedAt", "beforeFinishedAt"
"limit",
"from",
"reverse",
"batchUids",
"uids",
"canceledBy",
"types",
"statuses",
"indexUids",
"afterEnqueuedAt",
"beforeEnqueuedAt",
"afterStartedAt",
"beforeStartedAt",
"afterFinishedAt",
"beforeFinishedAt",
}
filtered_args = {k: v for k, v in arguments.items() if k in valid_params} if arguments else {}
filtered_args = (
{k: v for k, v in arguments.items() if k in valid_params}
if arguments
else {}
)
tasks = await self.meili_client.tasks.get_tasks(filtered_args)
return [types.TextContent(type="text", text=f"Tasks: {tasks}")]

Expand Down Expand Up @@ -479,7 +530,8 @@ async def handle_call_tool(
)
return [
types.TextContent(
type="text", text=f"Index metrics: {json.dumps(metrics.__dict__, default=json_serializer)}"
type="text",
text=f"Index metrics: {json.dumps(metrics.__dict__, default=json_serializer)}",
)
]

Expand Down Expand Up @@ -526,13 +578,15 @@ async def cleanup(self):
self.logger.info("Shutting down MCP server")
self.logger.shutdown()


def main():
"""Main entry point"""
url = os.getenv("MEILI_HTTP_ADDR", "http://localhost:7700")
api_key = os.getenv("MEILI_MASTER_KEY")

server = create_server(url, api_key)
asyncio.run(server.run())


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion src/meilisearch_mcp/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

def serialize_task_results(obj: Any) -> Any:
"""Serialize task results into JSON-compatible format"""
if hasattr(obj, '__dict__'):
if hasattr(obj, "__dict__"):
return {k: serialize_task_results(v) for k, v in obj.__dict__.items()}
elif isinstance(obj, (list, tuple)):
return [serialize_task_results(item) for item in obj]
Expand Down
Loading