1- from typing import Dict , List , Optional , TypedDict
1+ from typing import Dict , List , TypedDict , Union
22from enum import Enum
33import chromadb
44from mcp .server .fastmcp import FastMCP
@@ -145,8 +145,8 @@ def get_chroma_client(args=None):
145145
146146@mcp .tool ()
147147async def chroma_list_collections (
148- limit : Optional [ int ] = None ,
149- offset : Optional [ int ] = None
148+ limit : int | None = None ,
149+ offset : int | None = None
150150) -> List [str ]:
151151 """List all collection names in the Chroma database with pagination support.
152152
@@ -155,12 +155,15 @@ async def chroma_list_collections(
155155 offset: Optional number of collections to skip before returning results
156156
157157 Returns:
158- List of collection names
158+ List of collection names or ["__NO_COLLECTIONS_FOUND__"] if database is empty
159159 """
160160 client = get_chroma_client ()
161161 try :
162162 colls = client .list_collections (limit = limit , offset = offset )
163- # iterate over colls and output the names
163+ # Safe handling: If colls is None or empty, return a special marker
164+ if not colls :
165+ return ["__NO_COLLECTIONS_FOUND__" ]
166+ # Otherwise iterate to get collection names
164167 return [coll .name for coll in colls ]
165168
166169 except Exception as e :
@@ -177,16 +180,16 @@ async def chroma_list_collections(
177180@mcp .tool ()
178181async def chroma_create_collection (
179182 collection_name : str ,
180- embedding_function_name : Optional [ str ] = "default" ,
181- metadata : Optional [ Dict ] = None ,
182- space : Optional [ str ] = None ,
183- ef_construction : Optional [ int ] = None ,
184- ef_search : Optional [ int ] = None ,
185- max_neighbors : Optional [ int ] = None ,
186- num_threads : Optional [ int ] = None ,
187- batch_size : Optional [ int ] = None ,
188- sync_threshold : Optional [ int ] = None ,
189- resize_factor : Optional [ float ] = None ,
183+ embedding_function_name : str = "default" ,
184+ metadata : Dict | None = None ,
185+ space : str | None = None ,
186+ ef_construction : int | None = None ,
187+ ef_search : int | None = None ,
188+ max_neighbors : int | None = None ,
189+ num_threads : int | None = None ,
190+ batch_size : int | None = None ,
191+ sync_threshold : int | None = None ,
192+ resize_factor : float | None = None ,
190193) -> str :
191194 """Create a new Chroma collection with configurable HNSW parameters.
192195
@@ -305,13 +308,13 @@ async def chroma_get_collection_count(collection_name: str) -> int:
305308@mcp .tool ()
306309async def chroma_modify_collection (
307310 collection_name : str ,
308- new_name : Optional [ str ] = None ,
309- new_metadata : Optional [ Dict ] = None ,
310- ef_search : Optional [ int ] = None ,
311- num_threads : Optional [ int ] = None ,
312- batch_size : Optional [ int ] = None ,
313- sync_threshold : Optional [ int ] = None ,
314- resize_factor : Optional [ float ] = None ,
311+ new_name : str | None = None ,
312+ new_metadata : Dict | None = None ,
313+ ef_search : int | None = None ,
314+ num_threads : int | None = None ,
315+ batch_size : int | None = None ,
316+ sync_threshold : int | None = None ,
317+ resize_factor : float | None = None ,
315318) -> str :
316319 """Modify a Chroma collection's name or metadata.
317320
@@ -377,35 +380,62 @@ async def chroma_delete_collection(collection_name: str) -> str:
377380async def chroma_add_documents (
378381 collection_name : str ,
379382 documents : List [str ],
380- metadatas : Optional [ List [Dict ]] = None ,
381- ids : Optional [ List [str ]] = None
383+ ids : List [str ] ,
384+ metadatas : List [Dict ] | None = None
382385) -> str :
383386 """Add documents to a Chroma collection.
384387
385388 Args:
386389 collection_name: Name of the collection to add documents to
387390 documents: List of text documents to add
391+ ids: List of IDs for the documents (required)
388392 metadatas: Optional list of metadata dictionaries for each document
389- ids: Optional list of IDs for the documents
390393 """
391394 if not documents :
392395 raise ValueError ("The 'documents' list cannot be empty." )
396+
397+ if not ids :
398+ raise ValueError ("The 'ids' list is required and cannot be empty." )
399+
400+ # Check if there are empty strings in the ids list
401+ if any (not id .strip () for id in ids ):
402+ raise ValueError ("IDs cannot be empty strings." )
403+
404+ if len (ids ) != len (documents ):
405+ raise ValueError (f"Number of ids ({ len (ids )} ) must match number of documents ({ len (documents )} )." )
393406
394407 client = get_chroma_client ()
395408 try :
396409 collection = client .get_or_create_collection (collection_name )
397410
398- # Generate sequential IDs if none provided
399- if ids is None :
400- ids = [str (i ) for i in range (len (documents ))]
411+ # Check for duplicate IDs
412+ existing_ids = collection .get (include = [])["ids" ]
413+ duplicate_ids = [id for id in ids if id in existing_ids ]
414+
415+ if duplicate_ids :
416+ raise ValueError (
417+ f"The following IDs already exist in collection '{ collection_name } ': { duplicate_ids } . "
418+ f"Use 'chroma_update_documents' to update existing documents."
419+ )
401420
402- collection .add (
421+ result = collection .add (
403422 documents = documents ,
404423 metadatas = metadatas ,
405424 ids = ids
406425 )
407426
408- return f"Successfully added { len (documents )} documents to collection { collection_name } "
427+ # Check the return value
428+ if result and isinstance (result , dict ):
429+ # If the return value is a dictionary, it may contain success information
430+ if 'success' in result and not result ['success' ]:
431+ raise Exception (f"Failed to add documents: { result .get ('error' , 'Unknown error' )} " )
432+
433+ # If the return value contains the actual number added
434+ if 'count' in result :
435+ return f"Successfully added { result ['count' ]} documents to collection { collection_name } "
436+
437+ # Default return
438+ return f"Successfully added { len (documents )} documents to collection { collection_name } , result is { result } "
409439 except Exception as e :
410440 raise Exception (f"Failed to add documents to collection '{ collection_name } ': { str (e )} " ) from e
411441
@@ -414,8 +444,8 @@ async def chroma_query_documents(
414444 collection_name : str ,
415445 query_texts : List [str ],
416446 n_results : int = 5 ,
417- where : Optional [ Dict ] = None ,
418- where_document : Optional [ Dict ] = None ,
447+ where : Dict | None = None ,
448+ where_document : Dict | None = None ,
419449 include : List [str ] = ["documents" , "metadatas" , "distances" ]
420450) -> Dict :
421451 """Query documents from a Chroma collection with advanced filtering.
@@ -452,12 +482,12 @@ async def chroma_query_documents(
452482@mcp .tool ()
453483async def chroma_get_documents (
454484 collection_name : str ,
455- ids : Optional [ List [str ]] = None ,
456- where : Optional [ Dict ] = None ,
457- where_document : Optional [ Dict ] = None ,
485+ ids : List [str ] | None = None ,
486+ where : Dict | None = None ,
487+ where_document : Dict | None = None ,
458488 include : List [str ] = ["documents" , "metadatas" ],
459- limit : Optional [ int ] = None ,
460- offset : Optional [ int ] = None
489+ limit : int | None = None ,
490+ offset : int | None = None
461491) -> Dict :
462492 """Get documents from a Chroma collection with optional filtering.
463493
@@ -496,9 +526,9 @@ async def chroma_get_documents(
496526async def chroma_update_documents (
497527 collection_name : str ,
498528 ids : List [str ],
499- embeddings : Optional [ List [List [float ]]] = None ,
500- metadatas : Optional [ List [Dict ]] = None ,
501- documents : Optional [ List [str ]] = None
529+ embeddings : List [List [float ]] | None = None ,
530+ metadatas : List [Dict ] | None = None ,
531+ documents : List [str ] | None = None
502532) -> str :
503533 """Update documents in a Chroma collection.
504534
0 commit comments