loci-memory 0.0.2__tar.gz
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.
- loci_memory-0.0.2/LICENSE +21 -0
- loci_memory-0.0.2/PKG-INFO +78 -0
- loci_memory-0.0.2/README.md +59 -0
- loci_memory-0.0.2/pyproject.toml +23 -0
- loci_memory-0.0.2/setup.cfg +4 -0
- loci_memory-0.0.2/src/loci-memory/__init__.py +0 -0
- loci_memory-0.0.2/src/loci-memory/loci-memory.py +599 -0
- loci_memory-0.0.2/src/loci_memory.egg-info/PKG-INFO +78 -0
- loci_memory-0.0.2/src/loci_memory.egg-info/SOURCES.txt +10 -0
- loci_memory-0.0.2/src/loci_memory.egg-info/dependency_links.txt +1 -0
- loci_memory-0.0.2/src/loci_memory.egg-info/requires.txt +3 -0
- loci_memory-0.0.2/src/loci_memory.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [2026] [Daniel Roque Gonçalves de Almeida]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: loci-memory
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Chroma based MCP Server - Provide memory capabilities using Vector Database for LLM Applications
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: chroma,mcp,vector-database,llm,memory
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
12
|
+
Requires-Python: >=3.12
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: chromadb>=1.0.16
|
|
16
|
+
Requires-Dist: fastmcp>=3.2.3
|
|
17
|
+
Requires-Dist: typing-extensions>=4.13.1
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Flexible Client Types**
|
|
23
|
+
- Ephemeral (in-memory) for testing and development
|
|
24
|
+
- Persistent for file-based storage
|
|
25
|
+
|
|
26
|
+
- **Collection Management**
|
|
27
|
+
- Create, modify, and delete collections
|
|
28
|
+
- List all collections with pagination support
|
|
29
|
+
- Get collection information and statistics
|
|
30
|
+
- Configure HNSW parameters for optimized vector search
|
|
31
|
+
- Select embedding functions when creating collections
|
|
32
|
+
|
|
33
|
+
- **Document Operations**
|
|
34
|
+
- Add documents with optional metadata and custom IDs
|
|
35
|
+
- Query documents using semantic search
|
|
36
|
+
- Advanced filtering using metadata and document content
|
|
37
|
+
- Retrieve documents by IDs or filters
|
|
38
|
+
- Full text search capabilities
|
|
39
|
+
|
|
40
|
+
### Supported Tools
|
|
41
|
+
|
|
42
|
+
- `chroma_list_collections` - List all collections with pagination support
|
|
43
|
+
- `chroma_create_collection` - Create a new collection with optional HNSW configuration
|
|
44
|
+
- `chroma_peek_collection` - View a sample of documents in a collection
|
|
45
|
+
- `chroma_get_collection_info` - Get detailed information about a collection
|
|
46
|
+
- `chroma_get_collection_count` - Get the number of documents in a collection
|
|
47
|
+
- `chroma_modify_collection` - Update a collection's name or metadata
|
|
48
|
+
- `chroma_delete_collection` - Delete a collection
|
|
49
|
+
- `chroma_add_documents` - Add documents with optional metadata and custom IDs
|
|
50
|
+
- `chroma_query_documents` - Query documents using semantic search with advanced filtering
|
|
51
|
+
- `chroma_get_documents` - Retrieve documents by IDs or filters with pagination
|
|
52
|
+
- `chroma_update_documents` - Update existing documents' content, metadata, or embeddings
|
|
53
|
+
- `chroma_delete_documents` - Delete specific documents from a collection
|
|
54
|
+
|
|
55
|
+
### Embedding Functions
|
|
56
|
+
Chroma MCP supports several embedding functions: `default`, `cohere`, `openai`, `jina`, `voyageai`, and `roboflow`.
|
|
57
|
+
|
|
58
|
+
The embedding functions utilize Chroma's collection configuration, which persists the selected embedding function of a collection for retrieval. Once a collection is created using the collection configuration, on retrieval for future queries and inserts, the same embedding function will be used, without needing to specify the embedding function again. Embedding function persistance was added in v1.0.0 of Chroma, so if you created a collection using version <=0.6.3, this feature is not supported.
|
|
59
|
+
|
|
60
|
+
When accessing embedding functions that utilize external APIs, please be sure to add the environment variable for the API key with the correct format, found in [Embedding Function Environment Variables](#embedding-function-environment-variables)
|
|
61
|
+
|
|
62
|
+
## Usage with LM Studio
|
|
63
|
+
|
|
64
|
+
Add the following to your `mcp.json` file:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
"chroma": {
|
|
68
|
+
"command": "uvx",
|
|
69
|
+
"args": [
|
|
70
|
+
"loci-memory",
|
|
71
|
+
"--client-type",
|
|
72
|
+
"persistent",
|
|
73
|
+
"--data-dir",
|
|
74
|
+
"/full/path/to/your/data/directory"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
[GitHub-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
## Features
|
|
2
|
+
|
|
3
|
+
- **Flexible Client Types**
|
|
4
|
+
- Ephemeral (in-memory) for testing and development
|
|
5
|
+
- Persistent for file-based storage
|
|
6
|
+
|
|
7
|
+
- **Collection Management**
|
|
8
|
+
- Create, modify, and delete collections
|
|
9
|
+
- List all collections with pagination support
|
|
10
|
+
- Get collection information and statistics
|
|
11
|
+
- Configure HNSW parameters for optimized vector search
|
|
12
|
+
- Select embedding functions when creating collections
|
|
13
|
+
|
|
14
|
+
- **Document Operations**
|
|
15
|
+
- Add documents with optional metadata and custom IDs
|
|
16
|
+
- Query documents using semantic search
|
|
17
|
+
- Advanced filtering using metadata and document content
|
|
18
|
+
- Retrieve documents by IDs or filters
|
|
19
|
+
- Full text search capabilities
|
|
20
|
+
|
|
21
|
+
### Supported Tools
|
|
22
|
+
|
|
23
|
+
- `chroma_list_collections` - List all collections with pagination support
|
|
24
|
+
- `chroma_create_collection` - Create a new collection with optional HNSW configuration
|
|
25
|
+
- `chroma_peek_collection` - View a sample of documents in a collection
|
|
26
|
+
- `chroma_get_collection_info` - Get detailed information about a collection
|
|
27
|
+
- `chroma_get_collection_count` - Get the number of documents in a collection
|
|
28
|
+
- `chroma_modify_collection` - Update a collection's name or metadata
|
|
29
|
+
- `chroma_delete_collection` - Delete a collection
|
|
30
|
+
- `chroma_add_documents` - Add documents with optional metadata and custom IDs
|
|
31
|
+
- `chroma_query_documents` - Query documents using semantic search with advanced filtering
|
|
32
|
+
- `chroma_get_documents` - Retrieve documents by IDs or filters with pagination
|
|
33
|
+
- `chroma_update_documents` - Update existing documents' content, metadata, or embeddings
|
|
34
|
+
- `chroma_delete_documents` - Delete specific documents from a collection
|
|
35
|
+
|
|
36
|
+
### Embedding Functions
|
|
37
|
+
Chroma MCP supports several embedding functions: `default`, `cohere`, `openai`, `jina`, `voyageai`, and `roboflow`.
|
|
38
|
+
|
|
39
|
+
The embedding functions utilize Chroma's collection configuration, which persists the selected embedding function of a collection for retrieval. Once a collection is created using the collection configuration, on retrieval for future queries and inserts, the same embedding function will be used, without needing to specify the embedding function again. Embedding function persistance was added in v1.0.0 of Chroma, so if you created a collection using version <=0.6.3, this feature is not supported.
|
|
40
|
+
|
|
41
|
+
When accessing embedding functions that utilize external APIs, please be sure to add the environment variable for the API key with the correct format, found in [Embedding Function Environment Variables](#embedding-function-environment-variables)
|
|
42
|
+
|
|
43
|
+
## Usage with LM Studio
|
|
44
|
+
|
|
45
|
+
Add the following to your `mcp.json` file:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
"chroma": {
|
|
49
|
+
"command": "uvx",
|
|
50
|
+
"args": [
|
|
51
|
+
"loci-memory",
|
|
52
|
+
"--client-type",
|
|
53
|
+
"persistent",
|
|
54
|
+
"--data-dir",
|
|
55
|
+
"/full/path/to/your/data/directory"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[GitHub-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "loci-memory"
|
|
3
|
+
version = "0.0.2"
|
|
4
|
+
description = "Chroma based MCP Server - Provide memory capabilities using Vector Database for LLM Applications"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
|
|
9
|
+
keywords = ["chroma", "mcp", "vector-database", "llm", "memory"]
|
|
10
|
+
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Topic :: Software Development :: Libraries :: Python Modules"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
dependencies = [
|
|
20
|
+
"chromadb>=1.0.16",
|
|
21
|
+
"fastmcp>=3.2.3",
|
|
22
|
+
"typing-extensions>=4.13.1",
|
|
23
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
# loci-memory.py
|
|
2
|
+
from typing import Dict, List, TypedDict, Union
|
|
3
|
+
from mcp.server.fastmcp import FastMCP # Framework for building MCP Applications
|
|
4
|
+
import chromadb
|
|
5
|
+
import os # Python os Module
|
|
6
|
+
import argparse # Argument parser
|
|
7
|
+
|
|
8
|
+
from chromadb.api.collection_configuration import (
|
|
9
|
+
CreateCollectionConfiguration
|
|
10
|
+
)
|
|
11
|
+
from chromadb.api import EmbeddingFunction
|
|
12
|
+
from chromadb.utils.embedding_functions import (
|
|
13
|
+
DefaultEmbeddingFunction,
|
|
14
|
+
CohereEmbeddingFunction,
|
|
15
|
+
OpenAIEmbeddingFunction,
|
|
16
|
+
JinaEmbeddingFunction,
|
|
17
|
+
VoyageAIEmbeddingFunction,
|
|
18
|
+
RoboflowEmbeddingFunction,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Create an MCP server
|
|
22
|
+
mcp = FastMCP(
|
|
23
|
+
"LOCI",
|
|
24
|
+
instructions="Provides memory capabilities to an LLM. Start with get_summary() for an overview.")
|
|
25
|
+
|
|
26
|
+
# Global variables
|
|
27
|
+
_chroma_client = None
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Create Parser
|
|
31
|
+
#
|
|
32
|
+
def create_parser():
|
|
33
|
+
"""Create and return the argument parser."""
|
|
34
|
+
parser = argparse.ArgumentParser(description='FastMCP server for Chroma DB')
|
|
35
|
+
parser.add_argument('--client-type',
|
|
36
|
+
choices=['persistent', 'ephemeral'],
|
|
37
|
+
default=os.getenv('CHROMA_CLIENT_TYPE', 'ephemeral'),
|
|
38
|
+
help='Type of Chroma client to use')
|
|
39
|
+
parser.add_argument('--data-dir',
|
|
40
|
+
default=os.getenv('CHROMA_DATA_DIR'),
|
|
41
|
+
help='Directory for persistent client data (only used with persistent client)')
|
|
42
|
+
return parser
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# Get Chroma Client
|
|
46
|
+
#
|
|
47
|
+
def get_chroma_client(args=None):
|
|
48
|
+
"""Get or create the global Chroma client instance."""
|
|
49
|
+
global _chroma_client
|
|
50
|
+
if _chroma_client is None:
|
|
51
|
+
if args is None:
|
|
52
|
+
# Create parser and parse args if not provided
|
|
53
|
+
parser = create_parser()
|
|
54
|
+
args = parser.parse_args()
|
|
55
|
+
|
|
56
|
+
if args.client_type == 'persistent':
|
|
57
|
+
if not args.data_dir:
|
|
58
|
+
raise ValueError("Data directory must be provided via --data-dir flag when using persistent client")
|
|
59
|
+
_chroma_client = chromadb.PersistentClient(path=args.data_dir)
|
|
60
|
+
else: # ephemeral
|
|
61
|
+
_chroma_client = chromadb.EphemeralClient()
|
|
62
|
+
|
|
63
|
+
return _chroma_client
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@mcp.tool()
|
|
67
|
+
async def chroma_list_collections(
|
|
68
|
+
limit: int | None = None,
|
|
69
|
+
offset: int | None = None
|
|
70
|
+
) -> List[str]:
|
|
71
|
+
"""List all collection names in the Chroma database with pagination support.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
limit: Optional maximum number of collections to return
|
|
75
|
+
offset: Optional number of collections to skip before returning results
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
List of collection names or ["__NO_COLLECTIONS_FOUND__"] if database is empty
|
|
79
|
+
"""
|
|
80
|
+
client = get_chroma_client()
|
|
81
|
+
try:
|
|
82
|
+
colls = client.list_collections(limit=limit, offset=offset)
|
|
83
|
+
# Safe handling: If colls is None or empty, return a special marker
|
|
84
|
+
if not colls:
|
|
85
|
+
return ["__NO_COLLECTIONS_FOUND__"]
|
|
86
|
+
# Otherwise iterate to get collection names
|
|
87
|
+
return [coll.name for coll in colls]
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
raise Exception(f"Failed to list collections: {str(e)}") from e
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# Declare a variable with common embedding functions
|
|
94
|
+
#
|
|
95
|
+
mcp_embedding_functions: Dict[str, EmbeddingFunction] = {
|
|
96
|
+
"default": DefaultEmbeddingFunction,
|
|
97
|
+
"cohere": CohereEmbeddingFunction,
|
|
98
|
+
"openai": OpenAIEmbeddingFunction,
|
|
99
|
+
"jina": JinaEmbeddingFunction,
|
|
100
|
+
"voyageai": VoyageAIEmbeddingFunction,
|
|
101
|
+
"roboflow": RoboflowEmbeddingFunction,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@mcp.tool()
|
|
105
|
+
async def chroma_create_collection(
|
|
106
|
+
collection_name: str,
|
|
107
|
+
embedding_function_name: str = "default",
|
|
108
|
+
metadata: Dict | None = None,
|
|
109
|
+
) -> str:
|
|
110
|
+
"""Create a new Chroma collection with configurable HNSW parameters.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
collection_name: Name of the collection to create
|
|
114
|
+
embedding_function_name: Name of the embedding function to use. Options: 'default', 'cohere', 'openai', 'jina', 'voyageai', 'ollama', 'roboflow'
|
|
115
|
+
metadata: Optional metadata dict to add to the collection
|
|
116
|
+
"""
|
|
117
|
+
client = get_chroma_client()
|
|
118
|
+
|
|
119
|
+
embedding_function = mcp_embedding_functions[embedding_function_name]
|
|
120
|
+
|
|
121
|
+
configuration=CreateCollectionConfiguration(
|
|
122
|
+
embedding_function=embedding_function()
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
client.create_collection(
|
|
127
|
+
name=collection_name,
|
|
128
|
+
configuration=configuration,
|
|
129
|
+
metadata=metadata
|
|
130
|
+
)
|
|
131
|
+
config_msg = f" with configuration: {configuration}"
|
|
132
|
+
return f"Successfully created collection {collection_name}{config_msg}"
|
|
133
|
+
except Exception as e:
|
|
134
|
+
raise Exception(f"Failed to create collection '{collection_name}': {str(e)}") from e
|
|
135
|
+
|
|
136
|
+
@mcp.tool()
|
|
137
|
+
async def chroma_peek_collection(
|
|
138
|
+
collection_name: str,
|
|
139
|
+
limit: int = 5
|
|
140
|
+
) -> Dict:
|
|
141
|
+
"""Peek at documents in a Chroma collection.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
collection_name: Name of the collection to peek into
|
|
145
|
+
limit: Number of documents to peek at
|
|
146
|
+
"""
|
|
147
|
+
client = get_chroma_client()
|
|
148
|
+
try:
|
|
149
|
+
collection = client.get_collection(collection_name)
|
|
150
|
+
results = collection.peek(limit=limit)
|
|
151
|
+
return results
|
|
152
|
+
except Exception as e:
|
|
153
|
+
raise Exception(f"Failed to peek collection '{collection_name}': {str(e)}") from e
|
|
154
|
+
|
|
155
|
+
@mcp.tool()
|
|
156
|
+
async def chroma_get_collection_info(collection_name: str) -> Dict:
|
|
157
|
+
"""Get information about a Chroma collection.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
collection_name: Name of the collection to get info about
|
|
161
|
+
"""
|
|
162
|
+
client = get_chroma_client()
|
|
163
|
+
try:
|
|
164
|
+
collection = client.get_collection(collection_name)
|
|
165
|
+
|
|
166
|
+
# Get collection count
|
|
167
|
+
count = collection.count()
|
|
168
|
+
|
|
169
|
+
# Peek at a few documents
|
|
170
|
+
peek_results = collection.peek(limit=3)
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
"name": collection_name,
|
|
174
|
+
"count": count,
|
|
175
|
+
"sample_documents": peek_results
|
|
176
|
+
}
|
|
177
|
+
except Exception as e:
|
|
178
|
+
raise Exception(f"Failed to get collection info for '{collection_name}': {str(e)}") from e
|
|
179
|
+
|
|
180
|
+
@mcp.tool()
|
|
181
|
+
async def chroma_get_collection_count(collection_name: str) -> int:
|
|
182
|
+
"""Get the number of documents in a Chroma collection.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
collection_name: Name of the collection to count
|
|
186
|
+
"""
|
|
187
|
+
client = get_chroma_client()
|
|
188
|
+
try:
|
|
189
|
+
collection = client.get_collection(collection_name)
|
|
190
|
+
return collection.count()
|
|
191
|
+
except Exception as e:
|
|
192
|
+
raise Exception(f"Failed to get collection count for '{collection_name}': {str(e)}") from e
|
|
193
|
+
|
|
194
|
+
@mcp.tool()
|
|
195
|
+
async def chroma_modify_collection(
|
|
196
|
+
collection_name: str,
|
|
197
|
+
new_name: str | None = None,
|
|
198
|
+
new_metadata: Dict | None = None,
|
|
199
|
+
) -> str:
|
|
200
|
+
"""Modify a Chroma collection's name or metadata.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
collection_name: Name of the collection to modify
|
|
204
|
+
new_name: Optional new name for the collection
|
|
205
|
+
new_metadata: Optional new metadata for the collection
|
|
206
|
+
"""
|
|
207
|
+
client = get_chroma_client()
|
|
208
|
+
try:
|
|
209
|
+
collection = client.get_collection(collection_name)
|
|
210
|
+
collection.modify(name=new_name, metadata=new_metadata)
|
|
211
|
+
|
|
212
|
+
modified_aspects = []
|
|
213
|
+
if new_name:
|
|
214
|
+
modified_aspects.append("name")
|
|
215
|
+
if new_metadata:
|
|
216
|
+
modified_aspects.append("metadata")
|
|
217
|
+
|
|
218
|
+
return f"Successfully modified collection {collection_name}: updated {' and '.join(modified_aspects)}"
|
|
219
|
+
except Exception as e:
|
|
220
|
+
raise Exception(f"Failed to modify collection '{collection_name}': {str(e)}") from e
|
|
221
|
+
|
|
222
|
+
@mcp.tool()
|
|
223
|
+
async def chroma_fork_collection(
|
|
224
|
+
collection_name: str,
|
|
225
|
+
new_collection_name: str,
|
|
226
|
+
) -> str:
|
|
227
|
+
"""Fork a Chroma collection.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
collection_name: Name of the collection to fork
|
|
231
|
+
new_collection_name: Name of the new collection to create
|
|
232
|
+
metadata: Optional metadata dict to add to the new collection
|
|
233
|
+
"""
|
|
234
|
+
client = get_chroma_client()
|
|
235
|
+
try:
|
|
236
|
+
collection = client.get_collection(collection_name)
|
|
237
|
+
collection.fork(new_collection_name)
|
|
238
|
+
return f"Successfully forked collection {collection_name} to {new_collection_name}"
|
|
239
|
+
except Exception as e:
|
|
240
|
+
raise Exception(f"Failed to fork collection '{collection_name}': {str(e)}") from e
|
|
241
|
+
|
|
242
|
+
@mcp.tool()
|
|
243
|
+
async def chroma_delete_collection(collection_name: str) -> str:
|
|
244
|
+
"""Delete a Chroma collection.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
collection_name: Name of the collection to delete
|
|
248
|
+
"""
|
|
249
|
+
client = get_chroma_client()
|
|
250
|
+
try:
|
|
251
|
+
client.delete_collection(collection_name)
|
|
252
|
+
return f"Successfully deleted collection {collection_name}"
|
|
253
|
+
except Exception as e:
|
|
254
|
+
raise Exception(f"Failed to delete collection '{collection_name}': {str(e)}") from e
|
|
255
|
+
|
|
256
|
+
##### Document Tools #####
|
|
257
|
+
@mcp.tool()
|
|
258
|
+
async def chroma_add_documents(
|
|
259
|
+
collection_name: str,
|
|
260
|
+
documents: List[str],
|
|
261
|
+
ids: List[str],
|
|
262
|
+
metadatas: List[Dict] | None = None
|
|
263
|
+
) -> str:
|
|
264
|
+
"""Add documents to a Chroma collection.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
collection_name: Name of the collection to add documents to
|
|
268
|
+
documents: List of text documents to add
|
|
269
|
+
ids: List of IDs for the documents (required)
|
|
270
|
+
metadatas: Optional list of metadata dictionaries for each document
|
|
271
|
+
"""
|
|
272
|
+
if not documents:
|
|
273
|
+
raise ValueError("The 'documents' list cannot be empty.")
|
|
274
|
+
|
|
275
|
+
if not ids:
|
|
276
|
+
raise ValueError("The 'ids' list is required and cannot be empty.")
|
|
277
|
+
|
|
278
|
+
# Check if there are empty strings in the ids list
|
|
279
|
+
if any(not id.strip() for id in ids):
|
|
280
|
+
raise ValueError("IDs cannot be empty strings.")
|
|
281
|
+
|
|
282
|
+
if len(ids) != len(documents):
|
|
283
|
+
raise ValueError(f"Number of ids ({len(ids)}) must match number of documents ({len(documents)}).")
|
|
284
|
+
|
|
285
|
+
client = get_chroma_client()
|
|
286
|
+
try:
|
|
287
|
+
collection = client.get_or_create_collection(collection_name)
|
|
288
|
+
|
|
289
|
+
# Check for duplicate IDs
|
|
290
|
+
existing_ids = collection.get(include=[])["ids"]
|
|
291
|
+
duplicate_ids = [id for id in ids if id in existing_ids]
|
|
292
|
+
|
|
293
|
+
if duplicate_ids:
|
|
294
|
+
raise ValueError(
|
|
295
|
+
f"The following IDs already exist in collection '{collection_name}': {duplicate_ids}. "
|
|
296
|
+
f"Use 'chroma_update_documents' to update existing documents."
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
result = collection.add(
|
|
300
|
+
documents=documents,
|
|
301
|
+
metadatas=metadatas,
|
|
302
|
+
ids=ids
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Check the return value
|
|
306
|
+
if result and isinstance(result, dict):
|
|
307
|
+
# If the return value is a dictionary, it may contain success information
|
|
308
|
+
if 'success' in result and not result['success']:
|
|
309
|
+
raise Exception(f"Failed to add documents: {result.get('error', 'Unknown error')}")
|
|
310
|
+
|
|
311
|
+
# If the return value contains the actual number added
|
|
312
|
+
if 'count' in result:
|
|
313
|
+
return f"Successfully added {result['count']} documents to collection {collection_name}"
|
|
314
|
+
|
|
315
|
+
# Default return
|
|
316
|
+
return f"Successfully added {len(documents)} documents to collection {collection_name}, result is {result}"
|
|
317
|
+
except Exception as e:
|
|
318
|
+
raise Exception(f"Failed to add documents to collection '{collection_name}': {str(e)}") from e
|
|
319
|
+
|
|
320
|
+
@mcp.tool()
|
|
321
|
+
async def chroma_query_documents(
|
|
322
|
+
collection_name: str,
|
|
323
|
+
query_texts: List[str],
|
|
324
|
+
n_results: int = 5,
|
|
325
|
+
where: Dict | None = None,
|
|
326
|
+
where_document: Dict | None = None,
|
|
327
|
+
include: List[str] = ["documents", "metadatas", "distances"]
|
|
328
|
+
) -> Dict:
|
|
329
|
+
"""Query documents from a Chroma collection with advanced filtering.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
collection_name: Name of the collection to query
|
|
333
|
+
query_texts: List of query texts to search for
|
|
334
|
+
n_results: Number of results to return per query
|
|
335
|
+
where: Optional metadata filters using Chroma's query operators
|
|
336
|
+
Examples:
|
|
337
|
+
- Simple equality: {"metadata_field": "value"}
|
|
338
|
+
- Comparison: {"metadata_field": {"$gt": 5}}
|
|
339
|
+
- Logical AND: {"$and": [{"field1": {"$eq": "value1"}}, {"field2": {"$gt": 5}}]}
|
|
340
|
+
- Logical OR: {"$or": [{"field1": {"$eq": "value1"}}, {"field1": {"$eq": "value2"}}]}
|
|
341
|
+
where_document: Optional document content filters
|
|
342
|
+
Examples:
|
|
343
|
+
- Contains: {"$contains": "value"}
|
|
344
|
+
- Not contains: {"$not_contains": "value"}
|
|
345
|
+
- Regex: {"$regex": "[a-z]+"}
|
|
346
|
+
- Not regex: {"$not_regex": "[a-z]+"}
|
|
347
|
+
- Logical AND: {"$and": [{"$contains": "value1"}, {"$not_regex": "[a-z]+"}]}
|
|
348
|
+
- Logical OR: {"$or": [{"$regex": "[a-z]+"}, {"$not_contains": "value2"}]}
|
|
349
|
+
include: List of what to include in response. By default, this will include documents, metadatas, and distances.
|
|
350
|
+
"""
|
|
351
|
+
if not query_texts:
|
|
352
|
+
raise ValueError("The 'query_texts' list cannot be empty.")
|
|
353
|
+
|
|
354
|
+
client = get_chroma_client()
|
|
355
|
+
try:
|
|
356
|
+
collection = client.get_collection(collection_name)
|
|
357
|
+
return collection.query(
|
|
358
|
+
query_texts=query_texts,
|
|
359
|
+
n_results=n_results,
|
|
360
|
+
where=where,
|
|
361
|
+
where_document=where_document,
|
|
362
|
+
include=include
|
|
363
|
+
)
|
|
364
|
+
except Exception as e:
|
|
365
|
+
raise Exception(f"Failed to query documents from collection '{collection_name}': {str(e)}") from e
|
|
366
|
+
|
|
367
|
+
@mcp.tool()
|
|
368
|
+
async def chroma_get_documents(
|
|
369
|
+
collection_name: str,
|
|
370
|
+
ids: List[str] | None = None,
|
|
371
|
+
where: Dict | None = None,
|
|
372
|
+
where_document: Dict | None = None,
|
|
373
|
+
include: List[str] = ["documents", "metadatas"],
|
|
374
|
+
limit: int | None = None,
|
|
375
|
+
offset: int | None = None
|
|
376
|
+
) -> Dict:
|
|
377
|
+
"""Get documents from a Chroma collection with optional filtering.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
collection_name: Name of the collection to get documents from
|
|
381
|
+
ids: Optional list of document IDs to retrieve
|
|
382
|
+
where: Optional metadata filters using Chroma's query operators
|
|
383
|
+
Examples:
|
|
384
|
+
- Simple equality: {"metadata_field": "value"}
|
|
385
|
+
- Comparison: {"metadata_field": {"$gt": 5}}
|
|
386
|
+
- Logical AND: {"$and": [{"field1": {"$eq": "value1"}}, {"field2": {"$gt": 5}}]}
|
|
387
|
+
- Logical OR: {"$or": [{"field1": {"$eq": "value1"}}, {"field1": {"$eq": "value2"}}]}
|
|
388
|
+
where_document: Optional document content filters
|
|
389
|
+
Examples:
|
|
390
|
+
- Contains: {"$contains": "value"}
|
|
391
|
+
- Not contains: {"$not_contains": "value"}
|
|
392
|
+
- Regex: {"$regex": "[a-z]+"}
|
|
393
|
+
- Not regex: {"$not_regex": "[a-z]+"}
|
|
394
|
+
- Logical AND: {"$and": [{"$contains": "value1"}, {"$not_regex": "[a-z]+"}]}
|
|
395
|
+
- Logical OR: {"$or": [{"$regex": "[a-z]+"}, {"$not_contains": "value2"}]}
|
|
396
|
+
include: List of what to include in response. By default, this will include documents, and metadatas.
|
|
397
|
+
limit: Optional maximum number of documents to return
|
|
398
|
+
offset: Optional number of documents to skip before returning results
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Dictionary containing the matching documents, their IDs, and requested includes
|
|
402
|
+
"""
|
|
403
|
+
client = get_chroma_client()
|
|
404
|
+
try:
|
|
405
|
+
collection = client.get_collection(collection_name)
|
|
406
|
+
return collection.get(
|
|
407
|
+
ids=ids,
|
|
408
|
+
where=where,
|
|
409
|
+
where_document=where_document,
|
|
410
|
+
include=include,
|
|
411
|
+
limit=limit,
|
|
412
|
+
offset=offset
|
|
413
|
+
)
|
|
414
|
+
except Exception as e:
|
|
415
|
+
raise Exception(f"Failed to get documents from collection '{collection_name}': {str(e)}") from e
|
|
416
|
+
|
|
417
|
+
@mcp.tool()
|
|
418
|
+
async def chroma_update_documents(
|
|
419
|
+
collection_name: str,
|
|
420
|
+
ids: List[str],
|
|
421
|
+
embeddings: List[List[float]] | None = None,
|
|
422
|
+
metadatas: List[Dict] | None = None,
|
|
423
|
+
documents: List[str] | None = None
|
|
424
|
+
) -> str:
|
|
425
|
+
"""Update documents in a Chroma collection.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
collection_name: Name of the collection to update documents in
|
|
429
|
+
ids: List of document IDs to update (required)
|
|
430
|
+
embeddings: Optional list of new embeddings for the documents.
|
|
431
|
+
Must match length of ids if provided.
|
|
432
|
+
metadatas: Optional list of new metadata dictionaries for the documents.
|
|
433
|
+
Must match length of ids if provided.
|
|
434
|
+
documents: Optional list of new text documents.
|
|
435
|
+
Must match length of ids if provided.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
A confirmation message indicating the number of documents updated.
|
|
439
|
+
|
|
440
|
+
Raises:
|
|
441
|
+
ValueError: If 'ids' is empty or if none of 'embeddings', 'metadatas',
|
|
442
|
+
or 'documents' are provided, or if the length of provided
|
|
443
|
+
update lists does not match the length of 'ids'.
|
|
444
|
+
Exception: If the collection does not exist or if the update operation fails.
|
|
445
|
+
"""
|
|
446
|
+
if not ids:
|
|
447
|
+
raise ValueError("The 'ids' list cannot be empty.")
|
|
448
|
+
|
|
449
|
+
if embeddings is None and metadatas is None and documents is None:
|
|
450
|
+
raise ValueError(
|
|
451
|
+
"At least one of 'embeddings', 'metadatas', or 'documents' "
|
|
452
|
+
"must be provided for update."
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Ensure provided lists match the length of ids if they are not None
|
|
456
|
+
if embeddings is not None and len(embeddings) != len(ids):
|
|
457
|
+
raise ValueError("Length of 'embeddings' list must match length of 'ids' list.")
|
|
458
|
+
if metadatas is not None and len(metadatas) != len(ids):
|
|
459
|
+
raise ValueError("Length of 'metadatas' list must match length of 'ids' list.")
|
|
460
|
+
if documents is not None and len(documents) != len(ids):
|
|
461
|
+
raise ValueError("Length of 'documents' list must match length of 'ids' list.")
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
client = get_chroma_client()
|
|
465
|
+
try:
|
|
466
|
+
collection = client.get_collection(collection_name)
|
|
467
|
+
except Exception as e:
|
|
468
|
+
raise Exception(
|
|
469
|
+
f"Failed to get collection '{collection_name}': {str(e)}"
|
|
470
|
+
) from e
|
|
471
|
+
|
|
472
|
+
# Prepare arguments for update, excluding None values at the top level
|
|
473
|
+
update_args = {
|
|
474
|
+
"ids": ids,
|
|
475
|
+
"embeddings": embeddings,
|
|
476
|
+
"metadatas": metadatas,
|
|
477
|
+
"documents": documents,
|
|
478
|
+
}
|
|
479
|
+
kwargs = {k: v for k, v in update_args.items() if v is not None}
|
|
480
|
+
|
|
481
|
+
try:
|
|
482
|
+
collection.update(**kwargs)
|
|
483
|
+
return (
|
|
484
|
+
f"Successfully processed update request for {len(ids)} documents in "
|
|
485
|
+
f"collection '{collection_name}'. Note: Non-existent IDs are ignored by ChromaDB."
|
|
486
|
+
)
|
|
487
|
+
except Exception as e:
|
|
488
|
+
raise Exception(
|
|
489
|
+
f"Failed to update documents in collection '{collection_name}': {str(e)}"
|
|
490
|
+
) from e
|
|
491
|
+
|
|
492
|
+
@mcp.tool()
|
|
493
|
+
async def chroma_delete_documents(
|
|
494
|
+
collection_name: str,
|
|
495
|
+
ids: List[str]
|
|
496
|
+
) -> str:
|
|
497
|
+
"""Delete documents from a Chroma collection.
|
|
498
|
+
|
|
499
|
+
Args:
|
|
500
|
+
collection_name: Name of the collection to delete documents from
|
|
501
|
+
ids: List of document IDs to delete
|
|
502
|
+
|
|
503
|
+
Returns:
|
|
504
|
+
A confirmation message indicating the number of documents deleted.
|
|
505
|
+
|
|
506
|
+
Raises:
|
|
507
|
+
ValueError: If 'ids' is empty
|
|
508
|
+
Exception: If the collection does not exist or if the delete operation fails.
|
|
509
|
+
"""
|
|
510
|
+
if not ids:
|
|
511
|
+
raise ValueError("The 'ids' list cannot be empty.")
|
|
512
|
+
|
|
513
|
+
client = get_chroma_client()
|
|
514
|
+
try:
|
|
515
|
+
collection = client.get_collection(collection_name)
|
|
516
|
+
except Exception as e:
|
|
517
|
+
raise Exception(
|
|
518
|
+
f"Failed to get collection '{collection_name}': {str(e)}"
|
|
519
|
+
) from e
|
|
520
|
+
|
|
521
|
+
try:
|
|
522
|
+
collection.delete(ids=ids)
|
|
523
|
+
return (
|
|
524
|
+
f"Successfully deleted {len(ids)} documents from "
|
|
525
|
+
f"collection '{collection_name}'. Note: Non-existent IDs are ignored by ChromaDB."
|
|
526
|
+
)
|
|
527
|
+
except Exception as e:
|
|
528
|
+
raise Exception(
|
|
529
|
+
f"Failed to delete documents from collection '{collection_name}': {str(e)}"
|
|
530
|
+
) from e
|
|
531
|
+
|
|
532
|
+
def validate_thought_data(input_data: Dict) -> Dict:
|
|
533
|
+
"""Validate thought data structure."""
|
|
534
|
+
if not input_data.get("sessionId"):
|
|
535
|
+
raise ValueError("Invalid sessionId: must be provided")
|
|
536
|
+
if not input_data.get("thought") or not isinstance(input_data.get("thought"), str):
|
|
537
|
+
raise ValueError("Invalid thought: must be a string")
|
|
538
|
+
if not input_data.get("thoughtNumber") or not isinstance(input_data.get("thoughtNumber"), int):
|
|
539
|
+
raise ValueError("Invalid thoughtNumber: must be a number")
|
|
540
|
+
if not input_data.get("totalThoughts") or not isinstance(input_data.get("totalThoughts"), int):
|
|
541
|
+
raise ValueError("Invalid totalThoughts: must be a number")
|
|
542
|
+
if not isinstance(input_data.get("nextThoughtNeeded"), bool):
|
|
543
|
+
raise ValueError("Invalid nextThoughtNeeded: must be a boolean")
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
"sessionId": input_data.get("sessionId"),
|
|
547
|
+
"thought": input_data.get("thought"),
|
|
548
|
+
"thoughtNumber": input_data.get("thoughtNumber"),
|
|
549
|
+
"totalThoughts": input_data.get("totalThoughts"),
|
|
550
|
+
"nextThoughtNeeded": input_data.get("nextThoughtNeeded"),
|
|
551
|
+
"isRevision": input_data.get("isRevision"),
|
|
552
|
+
"revisesThought": input_data.get("revisesThought"),
|
|
553
|
+
"branchFromThought": input_data.get("branchFromThought"),
|
|
554
|
+
"branchId": input_data.get("branchId"),
|
|
555
|
+
"needsMoreThoughts": input_data.get("needsMoreThoughts"),
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
def main():
|
|
559
|
+
"""Entry point for the Chroma MCP server."""
|
|
560
|
+
parser = create_parser()
|
|
561
|
+
args = parser.parse_args()
|
|
562
|
+
|
|
563
|
+
# Initialize client with parsed args
|
|
564
|
+
try:
|
|
565
|
+
get_chroma_client(args)
|
|
566
|
+
print("Successfully initialized Chroma client")
|
|
567
|
+
except Exception as e:
|
|
568
|
+
print(f"Failed to initialize Chroma client: {str(e)}")
|
|
569
|
+
raise
|
|
570
|
+
|
|
571
|
+
# Initialize and run the server
|
|
572
|
+
print("Starting MCP server")
|
|
573
|
+
mcp.run(transport='stdio')
|
|
574
|
+
|
|
575
|
+
#
|
|
576
|
+
# Declare the tool set
|
|
577
|
+
#
|
|
578
|
+
"""
|
|
579
|
+
@mcp.tool(
|
|
580
|
+
name="find_products", # Custom tool name for the LLM
|
|
581
|
+
description="Search the product catalog with optional category filtering.", # Custom description
|
|
582
|
+
tags={"catalog", "search"}, # Optional tags for organization/filtering
|
|
583
|
+
meta={"version": "1.2", "author": "product-team"} # Custom metadata
|
|
584
|
+
)
|
|
585
|
+
def test_test(
|
|
586
|
+
query: str,
|
|
587
|
+
max_results: int = 10, # Optional - has default value
|
|
588
|
+
category: str | None = None # Optional - can be None
|
|
589
|
+
) -> list[dict]:
|
|
590
|
+
"" "Internal function description (ignored if description is provided above)." ""
|
|
591
|
+
# Implementation...
|
|
592
|
+
print(f"Searching for '{query}' in category '{category}'")
|
|
593
|
+
return [{"id": 2, "name": "Another Product"}]
|
|
594
|
+
"""
|
|
595
|
+
|
|
596
|
+
if __name__ == "__main__":
|
|
597
|
+
mcp.run()
|
|
598
|
+
#mcp.run(transport="http", port=8000) # Run Web Version
|
|
599
|
+
#main()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: loci-memory
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Chroma based MCP Server - Provide memory capabilities using Vector Database for LLM Applications
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: chroma,mcp,vector-database,llm,memory
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
12
|
+
Requires-Python: >=3.12
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: chromadb>=1.0.16
|
|
16
|
+
Requires-Dist: fastmcp>=3.2.3
|
|
17
|
+
Requires-Dist: typing-extensions>=4.13.1
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Flexible Client Types**
|
|
23
|
+
- Ephemeral (in-memory) for testing and development
|
|
24
|
+
- Persistent for file-based storage
|
|
25
|
+
|
|
26
|
+
- **Collection Management**
|
|
27
|
+
- Create, modify, and delete collections
|
|
28
|
+
- List all collections with pagination support
|
|
29
|
+
- Get collection information and statistics
|
|
30
|
+
- Configure HNSW parameters for optimized vector search
|
|
31
|
+
- Select embedding functions when creating collections
|
|
32
|
+
|
|
33
|
+
- **Document Operations**
|
|
34
|
+
- Add documents with optional metadata and custom IDs
|
|
35
|
+
- Query documents using semantic search
|
|
36
|
+
- Advanced filtering using metadata and document content
|
|
37
|
+
- Retrieve documents by IDs or filters
|
|
38
|
+
- Full text search capabilities
|
|
39
|
+
|
|
40
|
+
### Supported Tools
|
|
41
|
+
|
|
42
|
+
- `chroma_list_collections` - List all collections with pagination support
|
|
43
|
+
- `chroma_create_collection` - Create a new collection with optional HNSW configuration
|
|
44
|
+
- `chroma_peek_collection` - View a sample of documents in a collection
|
|
45
|
+
- `chroma_get_collection_info` - Get detailed information about a collection
|
|
46
|
+
- `chroma_get_collection_count` - Get the number of documents in a collection
|
|
47
|
+
- `chroma_modify_collection` - Update a collection's name or metadata
|
|
48
|
+
- `chroma_delete_collection` - Delete a collection
|
|
49
|
+
- `chroma_add_documents` - Add documents with optional metadata and custom IDs
|
|
50
|
+
- `chroma_query_documents` - Query documents using semantic search with advanced filtering
|
|
51
|
+
- `chroma_get_documents` - Retrieve documents by IDs or filters with pagination
|
|
52
|
+
- `chroma_update_documents` - Update existing documents' content, metadata, or embeddings
|
|
53
|
+
- `chroma_delete_documents` - Delete specific documents from a collection
|
|
54
|
+
|
|
55
|
+
### Embedding Functions
|
|
56
|
+
Chroma MCP supports several embedding functions: `default`, `cohere`, `openai`, `jina`, `voyageai`, and `roboflow`.
|
|
57
|
+
|
|
58
|
+
The embedding functions utilize Chroma's collection configuration, which persists the selected embedding function of a collection for retrieval. Once a collection is created using the collection configuration, on retrieval for future queries and inserts, the same embedding function will be used, without needing to specify the embedding function again. Embedding function persistance was added in v1.0.0 of Chroma, so if you created a collection using version <=0.6.3, this feature is not supported.
|
|
59
|
+
|
|
60
|
+
When accessing embedding functions that utilize external APIs, please be sure to add the environment variable for the API key with the correct format, found in [Embedding Function Environment Variables](#embedding-function-environment-variables)
|
|
61
|
+
|
|
62
|
+
## Usage with LM Studio
|
|
63
|
+
|
|
64
|
+
Add the following to your `mcp.json` file:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
"chroma": {
|
|
68
|
+
"command": "uvx",
|
|
69
|
+
"args": [
|
|
70
|
+
"loci-memory",
|
|
71
|
+
"--client-type",
|
|
72
|
+
"persistent",
|
|
73
|
+
"--data-dir",
|
|
74
|
+
"/full/path/to/your/data/directory"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
[GitHub-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/loci-memory/__init__.py
|
|
5
|
+
src/loci-memory/loci-memory.py
|
|
6
|
+
src/loci_memory.egg-info/PKG-INFO
|
|
7
|
+
src/loci_memory.egg-info/SOURCES.txt
|
|
8
|
+
src/loci_memory.egg-info/dependency_links.txt
|
|
9
|
+
src/loci_memory.egg-info/requires.txt
|
|
10
|
+
src/loci_memory.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
loci-memory
|