mcp-server-milvus 0.1.1.dev0__py3-none-any.whl
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.
Potentially problematic release.
This version of mcp-server-milvus might be problematic. Click here for more details.
- mcp_server_milvus/__init__.py +8 -0
- mcp_server_milvus/blogpost.md +18 -0
- mcp_server_milvus/example.env +3 -0
- mcp_server_milvus/server.py +733 -0
- mcp_server_milvus-0.1.1.dev0.dist-info/METADATA +330 -0
- mcp_server_milvus-0.1.1.dev0.dist-info/RECORD +8 -0
- mcp_server_milvus-0.1.1.dev0.dist-info/WHEEL +4 -0
- mcp_server_milvus-0.1.1.dev0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
We've moved from simple chatbots to sophisticated AI agents that can reason, plan, and execute complex tasks with minimal human intervention.
|
|
2
|
+
Agents now can perceive their environment, make decisions, and take actions to achieve specific goals, having a particularly big impact on how we build applications.
|
|
3
|
+
To help with this, the Model Context Protocol (MCP) standard, proposed by Anthropic to standardize how applications provide context to LLMs. It helps building complex workflows on top of LLMs.
|
|
4
|
+
# What is Model Context Protocol (MCP)?
|
|
5
|
+
MCP is an open protocol that has a goal of standardizing ways to connect AI Models to different data sources and tools.
|
|
6
|
+
The idea is to help you build agents and complex workflows on top of LLMs, making them even smarter. It provides:
|
|
7
|
+
- A list of pre-built integrations that LLMs can directly plug into
|
|
8
|
+
- The flexibility to switch between LLM providers and vendors
|
|
9
|
+
The general idea is for MCP to follow a client-server architecture, where a host application can connect to multiple servers:
|
|
10
|
+
[Image]
|
|
11
|
+
- MCP Hosts: Programs like Claude Desktop, IDEs, or AI tools that want to access data through MCP
|
|
12
|
+
- MCP Clients: Protocol clients that maintain 1:1 connections with servers
|
|
13
|
+
- MCP Servers: Lightweight programs that each expose specific capabilities through the standardized Model Context Protocol
|
|
14
|
+
- Local Data Sources: Your computer’s files, databases, and services that MCP servers can securely access
|
|
15
|
+
- Remote Services: External systems available over the internet (e.g., through APIs) that MCP servers can connect to
|
|
16
|
+
|
|
17
|
+
# Using Milvus with MCP
|
|
18
|
+
Milvus,
|
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from typing import Any, AsyncIterator, Optional
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
|
|
7
|
+
from mcp.server.fastmcp import Context, FastMCP
|
|
8
|
+
from pymilvus import DataType, MilvusClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MilvusConnector:
|
|
12
|
+
def __init__(
|
|
13
|
+
self, uri: str, token: Optional[str] = None, db_name: Optional[str] = "default"
|
|
14
|
+
):
|
|
15
|
+
self.uri = uri
|
|
16
|
+
self.token = token
|
|
17
|
+
self.client = MilvusClient(uri=uri, token=token, db_name=db_name)
|
|
18
|
+
|
|
19
|
+
async def list_collections(self) -> list[str]:
|
|
20
|
+
"""List all collections in the database."""
|
|
21
|
+
try:
|
|
22
|
+
return self.client.list_collections()
|
|
23
|
+
except Exception as e:
|
|
24
|
+
raise ValueError(f"Failed to list collections: {str(e)}")
|
|
25
|
+
|
|
26
|
+
async def get_collection_info(self, collection_name: str) -> dict:
|
|
27
|
+
"""Get detailed information about a collection."""
|
|
28
|
+
try:
|
|
29
|
+
return self.client.describe_collection(collection_name)
|
|
30
|
+
except Exception as e:
|
|
31
|
+
raise ValueError(f"Failed to get collection info: {str(e)}")
|
|
32
|
+
|
|
33
|
+
async def search_collection(
|
|
34
|
+
self,
|
|
35
|
+
collection_name: str,
|
|
36
|
+
query_text: str,
|
|
37
|
+
limit: int = 5,
|
|
38
|
+
output_fields: Optional[list[str]] = None,
|
|
39
|
+
drop_ratio: float = 0.2,
|
|
40
|
+
) -> list[dict]:
|
|
41
|
+
"""
|
|
42
|
+
Perform full text search on a collection.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
collection_name: Name of collection to search
|
|
46
|
+
query_text: Text to search for
|
|
47
|
+
limit: Maximum number of results
|
|
48
|
+
output_fields: Fields to return in results
|
|
49
|
+
drop_ratio: Proportion of low-frequency terms to ignore (0.0-1.0)
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
search_params = {"params": {"drop_ratio_search": drop_ratio}}
|
|
53
|
+
|
|
54
|
+
results = self.client.search(
|
|
55
|
+
collection_name=collection_name,
|
|
56
|
+
data=[query_text],
|
|
57
|
+
anns_field="sparse",
|
|
58
|
+
limit=limit,
|
|
59
|
+
output_fields=output_fields,
|
|
60
|
+
search_params=search_params,
|
|
61
|
+
)
|
|
62
|
+
return results
|
|
63
|
+
except Exception as e:
|
|
64
|
+
raise ValueError(f"Search failed: {str(e)}")
|
|
65
|
+
|
|
66
|
+
async def query_collection(
|
|
67
|
+
self,
|
|
68
|
+
collection_name: str,
|
|
69
|
+
filter_expr: str,
|
|
70
|
+
output_fields: Optional[list[str]] = None,
|
|
71
|
+
limit: int = 10,
|
|
72
|
+
) -> list[dict]:
|
|
73
|
+
"""Query collection using filter expressions."""
|
|
74
|
+
try:
|
|
75
|
+
return self.client.query(
|
|
76
|
+
collection_name=collection_name,
|
|
77
|
+
filter=filter_expr,
|
|
78
|
+
output_fields=output_fields,
|
|
79
|
+
limit=limit,
|
|
80
|
+
)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
raise ValueError(f"Query failed: {str(e)}")
|
|
83
|
+
|
|
84
|
+
async def vector_search(
|
|
85
|
+
self,
|
|
86
|
+
collection_name: str,
|
|
87
|
+
vector: list[float],
|
|
88
|
+
vector_field: str,
|
|
89
|
+
limit: int = 5,
|
|
90
|
+
output_fields: Optional[list[str]] = None,
|
|
91
|
+
metric_type: str = "COSINE",
|
|
92
|
+
) -> list[dict]:
|
|
93
|
+
"""
|
|
94
|
+
Perform vector similarity search on a collection.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
collection_name: Name of collection to search
|
|
98
|
+
vector: Query vector
|
|
99
|
+
vector_field: Field containing vectors to search
|
|
100
|
+
limit: Maximum number of results
|
|
101
|
+
output_fields: Fields to return in results
|
|
102
|
+
metric_type: Distance metric (COSINE, L2, IP)
|
|
103
|
+
filter_expr: Optional filter expression
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
search_params = {"metric_type": metric_type, "params": {"nprobe": 10}}
|
|
107
|
+
|
|
108
|
+
results = self.client.search(
|
|
109
|
+
collection_name=collection_name,
|
|
110
|
+
data=[vector],
|
|
111
|
+
anns_field=vector_field,
|
|
112
|
+
search_params=search_params,
|
|
113
|
+
limit=limit,
|
|
114
|
+
output_fields=output_fields,
|
|
115
|
+
|
|
116
|
+
)
|
|
117
|
+
return results
|
|
118
|
+
except Exception as e:
|
|
119
|
+
raise ValueError(f"Vector search failed: {str(e)}")
|
|
120
|
+
|
|
121
|
+
async def hybrid_search(
|
|
122
|
+
self,
|
|
123
|
+
collection_name: str,
|
|
124
|
+
vector: list[float],
|
|
125
|
+
vector_field: str,
|
|
126
|
+
limit: int = 5,
|
|
127
|
+
output_fields: Optional[list[str]] = None,
|
|
128
|
+
metric_type: str = "COSINE",
|
|
129
|
+
) -> list[dict]:
|
|
130
|
+
"""
|
|
131
|
+
Perform hybrid search combining vector similarity and attribute filtering.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
collection_name: Name of collection to search
|
|
135
|
+
vector: Query vector
|
|
136
|
+
vector_field: Field containing vectors to search
|
|
137
|
+
filter_expr: Filter expression for metadata
|
|
138
|
+
limit: Maximum number of results
|
|
139
|
+
output_fields: Fields to return in results
|
|
140
|
+
metric_type: Distance metric (COSINE, L2, IP)
|
|
141
|
+
"""
|
|
142
|
+
raise NotImplementedError('This method is not yet supported.')
|
|
143
|
+
|
|
144
|
+
async def create_collection(
|
|
145
|
+
self,
|
|
146
|
+
collection_name: str,
|
|
147
|
+
schema: dict[str, Any],
|
|
148
|
+
index_params: Optional[dict[str, Any]] = None,
|
|
149
|
+
) -> bool:
|
|
150
|
+
"""
|
|
151
|
+
Create a new collection with the specified schema.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
collection_name: Name for the new collection
|
|
155
|
+
schema: Collection schema definition
|
|
156
|
+
index_params: Optional index parameters
|
|
157
|
+
"""
|
|
158
|
+
try:
|
|
159
|
+
# Check if collection already exists
|
|
160
|
+
if collection_name in self.client.list_collections():
|
|
161
|
+
raise ValueError(f"Collection '{collection_name}' already exists")
|
|
162
|
+
|
|
163
|
+
# Create collection
|
|
164
|
+
self.client.create_collection(
|
|
165
|
+
collection_name=collection_name,
|
|
166
|
+
dimension=schema.get("dimension", 128),
|
|
167
|
+
primary_field=schema.get("primary_field", "id"),
|
|
168
|
+
id_type=schema.get("id_type", DataType.INT64),
|
|
169
|
+
vector_field=schema.get("vector_field", "vector"),
|
|
170
|
+
metric_type=schema.get("metric_type", "COSINE"),
|
|
171
|
+
auto_id=schema.get("auto_id", False),
|
|
172
|
+
enable_dynamic_field=schema.get("enable_dynamic_field", True),
|
|
173
|
+
other_fields=schema.get("other_fields", []),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Create index if params provided
|
|
177
|
+
if index_params:
|
|
178
|
+
self.client.create_index(
|
|
179
|
+
collection_name=collection_name,
|
|
180
|
+
field_name=schema.get("vector_field", "vector"),
|
|
181
|
+
index_params=index_params,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return True
|
|
185
|
+
except Exception as e:
|
|
186
|
+
raise ValueError(f"Failed to create collection: {str(e)}")
|
|
187
|
+
|
|
188
|
+
async def insert_data(
|
|
189
|
+
self, collection_name: str, data: dict[str, list[Any]]
|
|
190
|
+
) -> dict[str, Any]:
|
|
191
|
+
"""
|
|
192
|
+
Insert data into a collection.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
collection_name: Name of collection
|
|
196
|
+
data: Dictionary mapping field names to lists of values
|
|
197
|
+
"""
|
|
198
|
+
try:
|
|
199
|
+
result = self.client.insert(collection_name=collection_name, data=data)
|
|
200
|
+
return result
|
|
201
|
+
except Exception as e:
|
|
202
|
+
raise ValueError(f"Insert failed: {str(e)}")
|
|
203
|
+
|
|
204
|
+
async def delete_entities(
|
|
205
|
+
self, collection_name: str, filter_expr: str
|
|
206
|
+
) -> dict[str, Any]:
|
|
207
|
+
"""
|
|
208
|
+
Delete entities from a collection based on filter expression.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
collection_name: Name of collection
|
|
212
|
+
filter_expr: Filter expression to select entities to delete
|
|
213
|
+
"""
|
|
214
|
+
try:
|
|
215
|
+
result = self.client.delete(
|
|
216
|
+
collection_name=collection_name, expr=filter_expr
|
|
217
|
+
)
|
|
218
|
+
return result
|
|
219
|
+
except Exception as e:
|
|
220
|
+
raise ValueError(f"Delete failed: {str(e)}")
|
|
221
|
+
|
|
222
|
+
async def get_collection_stats(self, collection_name: str) -> dict[str, Any]:
|
|
223
|
+
"""
|
|
224
|
+
Get statistics about a collection.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
collection_name: Name of collection
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
return self.client.get_collection_stats(collection_name)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
raise ValueError(f"Failed to get collection stats: {str(e)}")
|
|
233
|
+
|
|
234
|
+
async def multi_vector_search(
|
|
235
|
+
self,
|
|
236
|
+
collection_name: str,
|
|
237
|
+
vectors: list[list[float]],
|
|
238
|
+
vector_field: str,
|
|
239
|
+
limit: int = 5,
|
|
240
|
+
output_fields: Optional[list[str]] = None,
|
|
241
|
+
metric_type: str = "COSINE",
|
|
242
|
+
search_params: Optional[dict[str, Any]] = None,
|
|
243
|
+
) -> list[list[dict]]:
|
|
244
|
+
"""
|
|
245
|
+
Perform vector similarity search with multiple query vectors.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
collection_name: Name of collection to search
|
|
249
|
+
vectors: List of query vectors
|
|
250
|
+
vector_field: Field containing vectors to search
|
|
251
|
+
limit: Maximum number of results per query
|
|
252
|
+
output_fields: Fields to return in results
|
|
253
|
+
metric_type: Distance metric (COSINE, L2, IP)
|
|
254
|
+
filter_expr: Optional filter expression
|
|
255
|
+
search_params: Additional search parameters
|
|
256
|
+
"""
|
|
257
|
+
try:
|
|
258
|
+
if search_params is None:
|
|
259
|
+
search_params = {"metric_type": metric_type, "params": {"nprobe": 10}}
|
|
260
|
+
|
|
261
|
+
results = self.client.search(
|
|
262
|
+
collection_name=collection_name,
|
|
263
|
+
data=vectors,
|
|
264
|
+
anns_field=vector_field,
|
|
265
|
+
search_params=search_params,
|
|
266
|
+
limit=limit,
|
|
267
|
+
output_fields=output_fields,
|
|
268
|
+
)
|
|
269
|
+
return results
|
|
270
|
+
except Exception as e:
|
|
271
|
+
raise ValueError(f"Multi-vector search failed: {str(e)}")
|
|
272
|
+
|
|
273
|
+
async def create_index(
|
|
274
|
+
self,
|
|
275
|
+
collection_name: str,
|
|
276
|
+
field_name: str,
|
|
277
|
+
index_type: str = "IVF_FLAT",
|
|
278
|
+
metric_type: str = "COSINE",
|
|
279
|
+
params: Optional[dict[str, Any]] = None,
|
|
280
|
+
) -> bool:
|
|
281
|
+
"""
|
|
282
|
+
Create an index on a vector field.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
collection_name: Name of collection
|
|
286
|
+
field_name: Field to index
|
|
287
|
+
index_type: Type of index (IVF_FLAT, HNSW, etc.)
|
|
288
|
+
metric_type: Distance metric (COSINE, L2, IP)
|
|
289
|
+
params: Additional index parameters
|
|
290
|
+
"""
|
|
291
|
+
try:
|
|
292
|
+
if params is None:
|
|
293
|
+
params = {"nlist": 1024}
|
|
294
|
+
|
|
295
|
+
index_params = {
|
|
296
|
+
"index_type": index_type,
|
|
297
|
+
"metric_type": metric_type,
|
|
298
|
+
"params": params,
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
self.client.create_index(
|
|
302
|
+
collection_name=collection_name,
|
|
303
|
+
field_name=field_name,
|
|
304
|
+
index_params=index_params,
|
|
305
|
+
)
|
|
306
|
+
return True
|
|
307
|
+
except Exception as e:
|
|
308
|
+
raise ValueError(f"Failed to create index: {str(e)}")
|
|
309
|
+
|
|
310
|
+
async def bulk_insert(
|
|
311
|
+
self, collection_name: str, data: dict[str, list[Any]], batch_size: int = 1000
|
|
312
|
+
) -> list[dict[str, Any]]:
|
|
313
|
+
"""
|
|
314
|
+
Insert data in batches for better performance.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
collection_name: Name of collection
|
|
318
|
+
data: Dictionary mapping field names to lists of values
|
|
319
|
+
batch_size: Number of records per batch
|
|
320
|
+
"""
|
|
321
|
+
try:
|
|
322
|
+
results = []
|
|
323
|
+
field_names = list(data.keys())
|
|
324
|
+
total_records = len(data[field_names[0]])
|
|
325
|
+
|
|
326
|
+
for i in range(0, total_records, batch_size):
|
|
327
|
+
batch_data = {
|
|
328
|
+
field: data[field][i : i + batch_size] for field in field_names
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
result = self.client.insert(
|
|
332
|
+
collection_name=collection_name, data=batch_data
|
|
333
|
+
)
|
|
334
|
+
results.append(result)
|
|
335
|
+
|
|
336
|
+
return results
|
|
337
|
+
except Exception as e:
|
|
338
|
+
raise ValueError(f"Bulk insert failed: {str(e)}")
|
|
339
|
+
|
|
340
|
+
async def load_collection(
|
|
341
|
+
self, collection_name: str, replica_number: int = 1
|
|
342
|
+
) -> bool:
|
|
343
|
+
"""
|
|
344
|
+
Load a collection into memory for search and query.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
collection_name: Name of collection to load
|
|
348
|
+
replica_number: Number of replicas
|
|
349
|
+
"""
|
|
350
|
+
try:
|
|
351
|
+
self.client.load_collection(
|
|
352
|
+
collection_name=collection_name, replica_number=replica_number
|
|
353
|
+
)
|
|
354
|
+
return True
|
|
355
|
+
except Exception as e:
|
|
356
|
+
raise ValueError(f"Failed to load collection: {str(e)}")
|
|
357
|
+
|
|
358
|
+
async def release_collection(self, collection_name: str) -> bool:
|
|
359
|
+
"""
|
|
360
|
+
Release a collection from memory.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
collection_name: Name of collection to release
|
|
364
|
+
"""
|
|
365
|
+
try:
|
|
366
|
+
self.client.release_collection(collection_name=collection_name)
|
|
367
|
+
return True
|
|
368
|
+
except Exception as e:
|
|
369
|
+
raise ValueError(f"Failed to release collection: {str(e)}")
|
|
370
|
+
|
|
371
|
+
async def get_query_segment_info(self, collection_name: str) -> dict[str, Any]:
|
|
372
|
+
"""
|
|
373
|
+
Get information about query segments.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
collection_name: Name of collection
|
|
377
|
+
"""
|
|
378
|
+
try:
|
|
379
|
+
return self.client.get_query_segment_info(collection_name)
|
|
380
|
+
except Exception as e:
|
|
381
|
+
raise ValueError(f"Failed to get query segment info: {str(e)}")
|
|
382
|
+
|
|
383
|
+
async def upsert_data(
|
|
384
|
+
self, collection_name: str, data: dict[str, list[Any]]
|
|
385
|
+
) -> dict[str, Any]:
|
|
386
|
+
"""
|
|
387
|
+
Upsert data into a collection (insert or update if exists).
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
collection_name: Name of collection
|
|
391
|
+
data: Dictionary mapping field names to lists of values
|
|
392
|
+
"""
|
|
393
|
+
try:
|
|
394
|
+
result = self.client.upsert(collection_name=collection_name, data=data)
|
|
395
|
+
return result
|
|
396
|
+
except Exception as e:
|
|
397
|
+
raise ValueError(f"Upsert failed: {str(e)}")
|
|
398
|
+
|
|
399
|
+
async def get_index_info(
|
|
400
|
+
self, collection_name: str, field_name: Optional[str] = None
|
|
401
|
+
) -> dict[str, Any]:
|
|
402
|
+
"""
|
|
403
|
+
Get information about indexes in a collection.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
collection_name: Name of collection
|
|
407
|
+
field_name: Optional specific field to get index info for
|
|
408
|
+
"""
|
|
409
|
+
try:
|
|
410
|
+
return self.client.describe_index(
|
|
411
|
+
collection_name=collection_name, index_name=field_name
|
|
412
|
+
)
|
|
413
|
+
except Exception as e:
|
|
414
|
+
raise ValueError(f"Failed to get index info: {str(e)}")
|
|
415
|
+
|
|
416
|
+
async def get_collection_loading_progress(
|
|
417
|
+
self, collection_name: str
|
|
418
|
+
) -> dict[str, Any]:
|
|
419
|
+
"""
|
|
420
|
+
Get the loading progress of a collection.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
collection_name: Name of collection
|
|
424
|
+
"""
|
|
425
|
+
try:
|
|
426
|
+
return self.client.get_load_state(collection_name)
|
|
427
|
+
except Exception as e:
|
|
428
|
+
raise ValueError(f"Failed to get loading progress: {str(e)}")
|
|
429
|
+
|
|
430
|
+
async def list_databases(self) -> list[str]:
|
|
431
|
+
"""List all databases in the Milvus instance."""
|
|
432
|
+
try:
|
|
433
|
+
return self.client.list_databases()
|
|
434
|
+
except Exception as e:
|
|
435
|
+
raise ValueError(f"Failed to list databases: {str(e)}")
|
|
436
|
+
|
|
437
|
+
async def use_database(self, db_name: str) -> bool:
|
|
438
|
+
"""Switch to a different database.
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
db_name: Name of the database to use
|
|
442
|
+
"""
|
|
443
|
+
try:
|
|
444
|
+
# Create a new client with the specified database
|
|
445
|
+
self.client = MilvusClient(
|
|
446
|
+
uri=self.uri,
|
|
447
|
+
token=self.token,
|
|
448
|
+
db_name=db_name
|
|
449
|
+
)
|
|
450
|
+
return True
|
|
451
|
+
except Exception as e:
|
|
452
|
+
raise ValueError(f"Failed to switch database: {str(e)}")
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
class MilvusContext:
|
|
456
|
+
def __init__(self, connector: MilvusConnector):
|
|
457
|
+
self.connector = connector
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
@asynccontextmanager
|
|
461
|
+
async def server_lifespan(server: FastMCP) -> AsyncIterator[MilvusContext]:
|
|
462
|
+
"""Manage application lifecycle for Milvus connector."""
|
|
463
|
+
config = server.config
|
|
464
|
+
|
|
465
|
+
connector = MilvusConnector(
|
|
466
|
+
uri=config.get("milvus_uri", "http://localhost:19530"),
|
|
467
|
+
token=config.get("milvus_token"),
|
|
468
|
+
db_name=config.get("db_name", "default"),
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
try:
|
|
472
|
+
yield MilvusContext(connector)
|
|
473
|
+
finally:
|
|
474
|
+
pass
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
mcp = FastMCP("Milvus", lifespan=server_lifespan)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
@mcp.tool()
|
|
481
|
+
async def milvus_text_search(
|
|
482
|
+
collection_name: str,
|
|
483
|
+
query_text: str,
|
|
484
|
+
limit: int = 5,
|
|
485
|
+
output_fields: Optional[list[str]] = None,
|
|
486
|
+
drop_ratio: float = 0.2,
|
|
487
|
+
ctx: Context = None,
|
|
488
|
+
) -> str:
|
|
489
|
+
"""
|
|
490
|
+
Search for documents using full text search in a Milvus collection.
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
collection_name: Name of the collection to search
|
|
494
|
+
query_text: Text to search for
|
|
495
|
+
limit: Maximum number of results to return
|
|
496
|
+
output_fields: Fields to include in results
|
|
497
|
+
drop_ratio: Proportion of low-frequency terms to ignore (0.0-1.0)
|
|
498
|
+
"""
|
|
499
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
500
|
+
results = await connector.search_collection(
|
|
501
|
+
collection_name=collection_name,
|
|
502
|
+
query_text=query_text,
|
|
503
|
+
limit=limit,
|
|
504
|
+
output_fields=output_fields,
|
|
505
|
+
drop_ratio=drop_ratio,
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
output = f"Search results for '{query_text}' in collection '{collection_name}':\n\n"
|
|
509
|
+
for result in results:
|
|
510
|
+
output += f"{result}\n\n"
|
|
511
|
+
|
|
512
|
+
return output
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
@mcp.tool()
|
|
516
|
+
async def milvus_list_collections(ctx: Context) -> str:
|
|
517
|
+
"""List all collections in the database."""
|
|
518
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
519
|
+
collections = await connector.list_collections()
|
|
520
|
+
return f"Collections in database:\n{', '.join(collections)}"
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
@mcp.tool()
|
|
524
|
+
async def milvus_query(
|
|
525
|
+
collection_name: str,
|
|
526
|
+
filter_expr: str,
|
|
527
|
+
output_fields: Optional[list[str]] = None,
|
|
528
|
+
limit: int = 10,
|
|
529
|
+
ctx: Context = None,
|
|
530
|
+
) -> str:
|
|
531
|
+
"""
|
|
532
|
+
Query collection using filter expressions.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
collection_name: Name of the collection to query
|
|
536
|
+
filter_expr: Filter expression (e.g. 'age > 20')
|
|
537
|
+
output_fields: Fields to include in results
|
|
538
|
+
limit: Maximum number of results
|
|
539
|
+
"""
|
|
540
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
541
|
+
results = await connector.query_collection(
|
|
542
|
+
collection_name=collection_name,
|
|
543
|
+
filter_expr=filter_expr,
|
|
544
|
+
output_fields=output_fields,
|
|
545
|
+
limit=limit,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
output = f"Query results for '{filter_expr}' in collection '{collection_name}':\n\n"
|
|
549
|
+
for result in results:
|
|
550
|
+
output += f"{result}\n\n"
|
|
551
|
+
|
|
552
|
+
return output
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
@mcp.tool()
|
|
556
|
+
async def milvus_vector_search(
|
|
557
|
+
collection_name: str,
|
|
558
|
+
vector: list[float],
|
|
559
|
+
vector_field: str = "vector",
|
|
560
|
+
limit: int = 5,
|
|
561
|
+
output_fields: Optional[list[str]] = None,
|
|
562
|
+
metric_type: str = "COSINE",
|
|
563
|
+
filter_expr: Optional[str] = None,
|
|
564
|
+
ctx: Context = None,
|
|
565
|
+
) -> str:
|
|
566
|
+
"""
|
|
567
|
+
Perform vector similarity search on a collection.
|
|
568
|
+
|
|
569
|
+
Args:
|
|
570
|
+
collection_name: Name of the collection to search
|
|
571
|
+
vector: Query vector
|
|
572
|
+
vector_field: Field containing vectors to search
|
|
573
|
+
limit: Maximum number of results
|
|
574
|
+
output_fields: Fields to include in results
|
|
575
|
+
metric_type: Distance metric (COSINE, L2, IP)
|
|
576
|
+
filter_expr: Optional filter expression
|
|
577
|
+
"""
|
|
578
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
579
|
+
results = await connector.vector_search(
|
|
580
|
+
collection_name=collection_name,
|
|
581
|
+
vector=vector,
|
|
582
|
+
vector_field=vector_field,
|
|
583
|
+
limit=limit,
|
|
584
|
+
output_fields=output_fields,
|
|
585
|
+
metric_type=metric_type,
|
|
586
|
+
filter_expr=filter_expr,
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
output = f"Vector search results for '{collection_name}':\n\n"
|
|
590
|
+
for result in results:
|
|
591
|
+
output += f"{result}\n\n"
|
|
592
|
+
|
|
593
|
+
return output
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
@mcp.tool()
|
|
597
|
+
async def milvus_create_collection(
|
|
598
|
+
collection_name: str,
|
|
599
|
+
collection_schema: dict[str, Any],
|
|
600
|
+
index_params: Optional[dict[str, Any]] = None,
|
|
601
|
+
ctx: Context = None,
|
|
602
|
+
) -> str:
|
|
603
|
+
"""
|
|
604
|
+
Create a new collection with specified schema.
|
|
605
|
+
|
|
606
|
+
Args:
|
|
607
|
+
collection_name: Name for the new collection
|
|
608
|
+
collection_schema: Collection schema definition
|
|
609
|
+
index_params: Optional index parameters
|
|
610
|
+
"""
|
|
611
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
612
|
+
success = await connector.create_collection(
|
|
613
|
+
collection_name=collection_name,
|
|
614
|
+
schema=collection_schema,
|
|
615
|
+
index_params=index_params,
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
return f"Collection '{collection_name}' created successfully"
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
@mcp.tool()
|
|
622
|
+
async def milvus_insert_data(
|
|
623
|
+
collection_name: str, data: dict[str, list[Any]], ctx: Context = None
|
|
624
|
+
) -> str:
|
|
625
|
+
"""
|
|
626
|
+
Insert data into a collection.
|
|
627
|
+
|
|
628
|
+
Args:
|
|
629
|
+
collection_name: Name of collection
|
|
630
|
+
data: Dictionary mapping field names to lists of values
|
|
631
|
+
"""
|
|
632
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
633
|
+
result = await connector.insert_data(collection_name=collection_name, data=data)
|
|
634
|
+
|
|
635
|
+
return (
|
|
636
|
+
f"Data inserted into collection '{collection_name}' with result: {str(result)}"
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
@mcp.tool()
|
|
641
|
+
async def milvus_delete_entities(
|
|
642
|
+
collection_name: str, filter_expr: str, ctx: Context = None
|
|
643
|
+
) -> str:
|
|
644
|
+
"""
|
|
645
|
+
Delete entities from a collection based on filter expression.
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
collection_name: Name of collection
|
|
649
|
+
filter_expr: Filter expression to select entities to delete
|
|
650
|
+
"""
|
|
651
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
652
|
+
result = await connector.delete_entities(
|
|
653
|
+
collection_name=collection_name, filter_expr=filter_expr
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
return f"Entities deleted from collection '{collection_name}' with result: {str(result)}"
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
@mcp.tool()
|
|
660
|
+
async def milvus_load_collection(
|
|
661
|
+
collection_name: str, replica_number: int = 1, ctx: Context = None
|
|
662
|
+
) -> str:
|
|
663
|
+
"""
|
|
664
|
+
Load a collection into memory for search and query.
|
|
665
|
+
|
|
666
|
+
Args:
|
|
667
|
+
collection_name: Name of collection to load
|
|
668
|
+
replica_number: Number of replicas
|
|
669
|
+
"""
|
|
670
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
671
|
+
success = await connector.load_collection(
|
|
672
|
+
collection_name=collection_name, replica_number=replica_number
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
return f"Collection '{collection_name}' loaded successfully with {replica_number} replica(s)"
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
@mcp.tool()
|
|
679
|
+
async def milvus_release_collection(collection_name: str, ctx: Context = None) -> str:
|
|
680
|
+
"""
|
|
681
|
+
Release a collection from memory.
|
|
682
|
+
|
|
683
|
+
Args:
|
|
684
|
+
collection_name: Name of collection to release
|
|
685
|
+
"""
|
|
686
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
687
|
+
success = await connector.release_collection(collection_name=collection_name)
|
|
688
|
+
|
|
689
|
+
return f"Collection '{collection_name}' released successfully"
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
@mcp.tool()
|
|
693
|
+
async def milvus_list_databases(ctx: Context = None) -> str:
|
|
694
|
+
"""List all databases in the Milvus instance."""
|
|
695
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
696
|
+
databases = await connector.list_databases()
|
|
697
|
+
return f"Databases in Milvus instance:\n{', '.join(databases)}"
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
@mcp.tool()
|
|
701
|
+
async def milvus_use_database(db_name: str, ctx: Context = None) -> str:
|
|
702
|
+
"""
|
|
703
|
+
Switch to a different database.
|
|
704
|
+
|
|
705
|
+
Args:
|
|
706
|
+
db_name: Name of the database to use
|
|
707
|
+
"""
|
|
708
|
+
connector = ctx.request_context.lifespan_context.connector
|
|
709
|
+
success = await connector.use_database(db_name)
|
|
710
|
+
|
|
711
|
+
return f"Switched to database '{db_name}' successfully"
|
|
712
|
+
|
|
713
|
+
def parse_arguments():
|
|
714
|
+
parser = argparse.ArgumentParser(description="Milvus MCP Server")
|
|
715
|
+
parser.add_argument("--milvus-uri", type=str,
|
|
716
|
+
default="http://localhost:19530", help="Milvus server URI")
|
|
717
|
+
parser.add_argument("--milvus-token", type=str,
|
|
718
|
+
default=None, help="Milvus authentication token")
|
|
719
|
+
parser.add_argument("--milvus-db", type=str,
|
|
720
|
+
default="default", help="Milvus database name")
|
|
721
|
+
return parser.parse_args()
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
if __name__ == "__main__":
|
|
725
|
+
load_dotenv()
|
|
726
|
+
args = parse_arguments()
|
|
727
|
+
mcp.config = {
|
|
728
|
+
"milvus_uri": os.environ.get("MILVUS_URI", args.milvus_uri),
|
|
729
|
+
"milvus_token": os.environ.get("MILVUS_TOKEN", args.milvus_token),
|
|
730
|
+
"db_name": os.environ.get("MILVUS_DB", args.milvus_db),
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
mcp.run()
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-server-milvus
|
|
3
|
+
Version: 0.1.1.dev0
|
|
4
|
+
Summary: MCP server for Milvus
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: click>=8.0.0
|
|
7
|
+
Requires-Dist: dotenv>=0.9.9
|
|
8
|
+
Requires-Dist: mcp[cli]>=1.1.2
|
|
9
|
+
Requires-Dist: pymilvus>=2.5.1
|
|
10
|
+
Requires-Dist: ruff>=0.11.0
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# MCP Server for Milvus
|
|
14
|
+
|
|
15
|
+
> The Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you're building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need.
|
|
16
|
+
|
|
17
|
+
This repository contains a MCP server that provides access to [Milvus](https://milvus.io/) vector database functionality.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
Before using this MCP server, ensure you have:
|
|
24
|
+
|
|
25
|
+
- Python 3.10 or higher
|
|
26
|
+
- A running [Milvus](https://milvus.io/) instance (local or remote)
|
|
27
|
+
- [uv](https://github.com/astral-sh/uv) installed (recommended for running the server)
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
The recommended way to use this MCP server is to run it directly with `uv` without installation. This is how both Claude Desktop and Cursor are configured to use it in the examples below.
|
|
32
|
+
|
|
33
|
+
If you want to clone the repository:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/zilliztech/mcp-server-milvus.git
|
|
37
|
+
cd mcp-server-milvus
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Then you can run the server directly:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv run src/mcp_server_milvus/server.py --milvus-uri http://localhost:19530
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Alternatively you can change the .env file in the `src/mcp_server_milvus/` directory to set the environment variables and run the server with the following command:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv run src/mcp_server_milvus/server.py
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Important: the .env file will have higher priority than the command line arguments.
|
|
53
|
+
|
|
54
|
+
## Supported Applications
|
|
55
|
+
|
|
56
|
+
This MCP server can be used with various LLM applications that support the Model Context Protocol:
|
|
57
|
+
|
|
58
|
+
- **Claude Desktop**: Anthropic's desktop application for Claude
|
|
59
|
+
- **Cursor**: AI-powered code editor with MCP support
|
|
60
|
+
- **Custom MCP clients**: Any application implementing the MCP client specification
|
|
61
|
+
|
|
62
|
+
## Usage with Claude Desktop
|
|
63
|
+
|
|
64
|
+
1. Install Claude Desktop from https://claude.ai/download
|
|
65
|
+
2. Open your Claude Desktop configuration:
|
|
66
|
+
|
|
67
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
68
|
+
|
|
69
|
+
3. Add the following configuration:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"milvus": {
|
|
75
|
+
"command": "/PATH/TO/uv",
|
|
76
|
+
"args": [
|
|
77
|
+
"--directory",
|
|
78
|
+
"/path/to/mcp-server-milvus/src/mcp_server_milvus",
|
|
79
|
+
"run",
|
|
80
|
+
"server.py",
|
|
81
|
+
"--milvus-uri",
|
|
82
|
+
"http://localhost:19530"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
4. Restart Claude Desktop
|
|
90
|
+
|
|
91
|
+
## Usage with Cursor
|
|
92
|
+
|
|
93
|
+
[Cursor also supports MCP](https://docs.cursor.com/context/model-context-protocol) tools. You can add the Milvus MCP server to Cursor in two ways:
|
|
94
|
+
|
|
95
|
+
### Option 1: Using Cursor Settings UI
|
|
96
|
+
|
|
97
|
+
1. Go to `Cursor Settings` > `Features` > `MCP`
|
|
98
|
+
2. Click on the `+ Add New MCP Server` button
|
|
99
|
+
3. Fill out the form:
|
|
100
|
+
|
|
101
|
+
- **Type**: Select `stdio` (since you're running a command)
|
|
102
|
+
- **Name**: `milvus`
|
|
103
|
+
- **Command**: `/PATH/TO/uv --directory /path/to/mcp-server-milvus/src/mcp_server_milvus run server.py --milvus-uri http://127.0.0.1:19530`
|
|
104
|
+
|
|
105
|
+
> ⚠️ Note: Use `127.0.0.1` instead of `localhost` to avoid potential DNS resolution issues.
|
|
106
|
+
|
|
107
|
+
### Option 2: Using Project-specific Configuration (Recommended)
|
|
108
|
+
|
|
109
|
+
Create a `.cursor/mcp.json` file in your project root:
|
|
110
|
+
|
|
111
|
+
1. Create the `.cursor` directory in your project root:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
mkdir -p /path/to/your/project/.cursor
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
2. Create a `mcp.json` file with the following content:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"mcpServers": {
|
|
122
|
+
"milvus": {
|
|
123
|
+
"command": "/PATH/TO/uv",
|
|
124
|
+
"args": [
|
|
125
|
+
"--directory",
|
|
126
|
+
"/path/to/mcp-server-milvus/src/mcp_server_milvus",
|
|
127
|
+
"run",
|
|
128
|
+
"server.py",
|
|
129
|
+
"--milvus-uri",
|
|
130
|
+
"http://127.0.0.1:19530"
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
3. Restart Cursor or reload the window
|
|
138
|
+
|
|
139
|
+
After adding the server, you may need to press the refresh button in the MCP settings to populate the tool list. The Agent will automatically use the Milvus tools when relevant to your queries.
|
|
140
|
+
|
|
141
|
+
### Verifying the Integration
|
|
142
|
+
|
|
143
|
+
To verify that Cursor has successfully integrated with your Milvus MCP server:
|
|
144
|
+
|
|
145
|
+
1. Open Cursor Settings > Features > MCP
|
|
146
|
+
2. Check that "Milvus" appears in the list of MCP servers
|
|
147
|
+
3. Verify that the tools are listed (e.g., milvus_list_collections, milvus_vector_search, etc.)
|
|
148
|
+
4. If the server is enabled but shows an error, check the Troubleshooting section below
|
|
149
|
+
|
|
150
|
+
## Available Tools
|
|
151
|
+
|
|
152
|
+
The server provides the following tools:
|
|
153
|
+
|
|
154
|
+
### Search and Query Operations
|
|
155
|
+
|
|
156
|
+
- `milvus_text_search`: Search for documents using full text search
|
|
157
|
+
|
|
158
|
+
- Parameters:
|
|
159
|
+
- `collection_name`: Name of collection to search
|
|
160
|
+
- `query_text`: Text to search for
|
|
161
|
+
- `limit`: Maximum results (default: 5)
|
|
162
|
+
- `output_fields`: Fields to include in results
|
|
163
|
+
- `drop_ratio`: Proportion of low-frequency terms to ignore (0.0-1.0)
|
|
164
|
+
|
|
165
|
+
- `milvus_vector_search`: Perform vector similarity search on a collection
|
|
166
|
+
|
|
167
|
+
- Parameters:
|
|
168
|
+
- `collection_name`: Name of collection to search
|
|
169
|
+
- `vector`: Query vector
|
|
170
|
+
- `vector_field`: Field containing vectors to search (default: "vector")
|
|
171
|
+
- `limit`: Maximum results (default: 5)
|
|
172
|
+
- `output_fields`: Fields to include in results
|
|
173
|
+
- `metric_type`: Distance metric (COSINE, L2, IP) (default: "COSINE")
|
|
174
|
+
|
|
175
|
+
- `milvus_query`: Query collection using filter expressions
|
|
176
|
+
- Parameters:
|
|
177
|
+
- `collection_name`: Name of collection to query
|
|
178
|
+
- `filter_expr`: Filter expression (e.g. 'age > 20')
|
|
179
|
+
- `output_fields`: Fields to include in results
|
|
180
|
+
- `limit`: Maximum results (default: 10)
|
|
181
|
+
|
|
182
|
+
### Collection Management
|
|
183
|
+
|
|
184
|
+
- `milvus_list_collections`: List all collections in the database
|
|
185
|
+
|
|
186
|
+
- `milvus_create_collection`: Create a new collection with specified schema
|
|
187
|
+
|
|
188
|
+
- Parameters:
|
|
189
|
+
- `collection_name`: Name for the new collection
|
|
190
|
+
- `collection_schema`: Collection schema definition
|
|
191
|
+
- `index_params`: Optional index parameters
|
|
192
|
+
|
|
193
|
+
- `milvus_load_collection`: Load a collection into memory for search and query
|
|
194
|
+
|
|
195
|
+
- Parameters:
|
|
196
|
+
- `collection_name`: Name of collection to load
|
|
197
|
+
- `replica_number`: Number of replicas (default: 1)
|
|
198
|
+
|
|
199
|
+
- `milvus_release_collection`: Release a collection from memory
|
|
200
|
+
- Parameters:
|
|
201
|
+
- `collection_name`: Name of collection to release
|
|
202
|
+
|
|
203
|
+
### Data Operations
|
|
204
|
+
|
|
205
|
+
- `milvus_insert_data`: Insert data into a collection
|
|
206
|
+
|
|
207
|
+
- Parameters:
|
|
208
|
+
- `collection_name`: Name of collection
|
|
209
|
+
- `data`: Dictionary mapping field names to lists of values
|
|
210
|
+
|
|
211
|
+
- `milvus_delete_entities`: Delete entities from a collection based on filter expression
|
|
212
|
+
- Parameters:
|
|
213
|
+
- `collection_name`: Name of collection
|
|
214
|
+
- `filter_expr`: Filter expression to select entities to delete
|
|
215
|
+
|
|
216
|
+
## Environment Variables
|
|
217
|
+
|
|
218
|
+
- `MILVUS_URI`: Milvus server URI (can be set instead of --milvus-uri)
|
|
219
|
+
- `MILVUS_TOKEN`: Optional authentication token
|
|
220
|
+
- `MILVUS_DB`: Database name (defaults to "default")
|
|
221
|
+
|
|
222
|
+
## Development
|
|
223
|
+
|
|
224
|
+
To run the server directly:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
uv run server.py --milvus-uri http://localhost:19530
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Examples
|
|
231
|
+
|
|
232
|
+
### Using Claude Desktop
|
|
233
|
+
|
|
234
|
+
#### Example 1: Listing Collections
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
What are the collections I have in my Milvus DB?
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Claude will then use MCP to check this information on your Milvus DB.
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
I'll check what collections are available in your Milvus database.
|
|
244
|
+
|
|
245
|
+
Here are the collections in your Milvus database:
|
|
246
|
+
|
|
247
|
+
1. rag_demo
|
|
248
|
+
2. test
|
|
249
|
+
3. chat_messages
|
|
250
|
+
4. text_collection
|
|
251
|
+
5. image_collection
|
|
252
|
+
6. customized_setup
|
|
253
|
+
7. streaming_rag_demo
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### Example 2: Searching for Documents
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
Find documents in my text_collection that mention "machine learning"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Claude will use the full-text search capabilities of Milvus to find relevant documents:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
I'll search for documents about machine learning in your text_collection.
|
|
266
|
+
|
|
267
|
+
> View result from milvus-text-search from milvus (local)
|
|
268
|
+
|
|
269
|
+
Here are the documents I found that mention machine learning:
|
|
270
|
+
[Results will appear here based on your actual data]
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Using Cursor
|
|
274
|
+
|
|
275
|
+
#### Example: Creating a Collection
|
|
276
|
+
|
|
277
|
+
In Cursor, you can ask:
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
Create a new collection called 'articles' in Milvus with fields for title (string), content (string), and a vector field (128 dimensions)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Cursor will use the MCP server to execute this operation:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
I'll create a new collection called 'articles' with the specified fields.
|
|
287
|
+
|
|
288
|
+
Collection 'articles' has been created successfully with the following schema:
|
|
289
|
+
- title: string
|
|
290
|
+
- content: string
|
|
291
|
+
- vector: float vector[128]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Troubleshooting
|
|
295
|
+
|
|
296
|
+
### Common Issues
|
|
297
|
+
|
|
298
|
+
#### Connection Errors
|
|
299
|
+
|
|
300
|
+
If you see errors like "Failed to connect to Milvus server":
|
|
301
|
+
|
|
302
|
+
1. Verify your Milvus instance is running: `docker ps` (if using Docker)
|
|
303
|
+
2. Check the URI is correct in your configuration
|
|
304
|
+
3. Ensure there are no firewall rules blocking the connection
|
|
305
|
+
4. Try using `127.0.0.1` instead of `localhost` in the URI
|
|
306
|
+
|
|
307
|
+
#### Authentication Issues
|
|
308
|
+
|
|
309
|
+
If you see authentication errors:
|
|
310
|
+
|
|
311
|
+
1. Verify your `MILVUS_TOKEN` is correct
|
|
312
|
+
2. Check if your Milvus instance requires authentication
|
|
313
|
+
3. Ensure you have the correct permissions for the operations you're trying to perform
|
|
314
|
+
|
|
315
|
+
#### Tool Not Found
|
|
316
|
+
|
|
317
|
+
If the MCP tools don't appear in Claude Desktop or Cursor:
|
|
318
|
+
|
|
319
|
+
1. Restart the application
|
|
320
|
+
2. Check the server logs for any errors
|
|
321
|
+
3. Verify the MCP server is running correctly
|
|
322
|
+
4. Press the refresh button in the MCP settings (for Cursor)
|
|
323
|
+
|
|
324
|
+
### Getting Help
|
|
325
|
+
|
|
326
|
+
If you continue to experience issues:
|
|
327
|
+
|
|
328
|
+
1. Check the [GitHub Issues](https://github.com/zilliztech/mcp-server-milvus/issues) for similar problems
|
|
329
|
+
2. Join the [Zilliz Community Discord](https://discord.gg/zilliz) for support
|
|
330
|
+
3. File a new issue with detailed information about your problem
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
mcp_server_milvus/__init__.py,sha256=QuXnVCH_WU32S0g29nGKalTqUnlG5CIKmuKP4N_6taE,185
|
|
2
|
+
mcp_server_milvus/blogpost.md,sha256=GwGlzSnWyotasaYjd_fxQ4dP_n9pMSitvVikNTqOmVc,1600
|
|
3
|
+
mcp_server_milvus/example.env,sha256=ILqUAROPfUqmamYuuvlmX17XWoLZmgEx19uL5RQjyv4,42
|
|
4
|
+
mcp_server_milvus/server.py,sha256=_a-BgsNeMETsxN-1ub7lsALt7a2AXyca2EbymWSaDWU,23930
|
|
5
|
+
mcp_server_milvus-0.1.1.dev0.dist-info/METADATA,sha256=6iBwWEIqXCcDOSj2X_-AYpNvxPAHnBARgBGLFaH0pZM,9846
|
|
6
|
+
mcp_server_milvus-0.1.1.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
mcp_server_milvus-0.1.1.dev0.dist-info/entry_points.txt,sha256=hgDPpJb9sC54jlIuSX48i0herxeXYQSsf6nh6CuThj0,68
|
|
8
|
+
mcp_server_milvus-0.1.1.dev0.dist-info/RECORD,,
|