couchbase-mcp-server 0.4.0__py3-none-any.whl → 0.5.0__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.
- {couchbase_mcp_server-0.4.0.dist-info → couchbase_mcp_server-0.5.0.dist-info}/METADATA +47 -25
- couchbase_mcp_server-0.5.0.dist-info/RECORD +16 -0
- mcp_server.py +22 -5
- tools/__init__.py +9 -0
- tools/kv.py +20 -6
- tools/query.py +27 -6
- tools/server.py +64 -16
- utils/__init__.py +2 -6
- utils/config.py +0 -25
- utils/connection.py +29 -4
- utils/context.py +21 -37
- couchbase_mcp_server-0.4.0.dist-info/RECORD +0 -16
- {couchbase_mcp_server-0.4.0.dist-info → couchbase_mcp_server-0.5.0.dist-info}/WHEEL +0 -0
- {couchbase_mcp_server-0.4.0.dist-info → couchbase_mcp_server-0.5.0.dist-info}/entry_points.txt +0 -0
- {couchbase_mcp_server-0.4.0.dist-info → couchbase_mcp_server-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {couchbase_mcp_server-0.4.0.dist-info → couchbase_mcp_server-0.5.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: couchbase-mcp-server
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Couchbase MCP Server - The Developer Data Platform for Critical Applications in Our AI World
|
|
5
5
|
Author-email: Nithish Raghunandanan <devadvocates@couchbase.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -26,7 +26,7 @@ Dynamic: license-file
|
|
|
26
26
|
|
|
27
27
|
An [MCP](https://modelcontextprotocol.io/) server implementation of Couchbase that allows LLMs to directly interact with Couchbase clusters.
|
|
28
28
|
|
|
29
|
-
[](https://opensource.org/licenses/Apache-2.0) [](https://www.python.org/downloads/) [](https://pypi.org/project/couchbase-mcp-server/) [](https://mseep.ai/app/13fce476-0e74-4b1e-ab82-1df2a3204809)
|
|
29
|
+
[](https://opensource.org/licenses/Apache-2.0) [](https://www.python.org/downloads/) [](https://pypi.org/project/couchbase-mcp-server/) [](https://mseep.ai/app/13fce476-0e74-4b1e-ab82-1df2a3204809) [](https://archestra.ai/mcp-catalog/couchbase-ecosystem__mcp-server-couchbase)
|
|
30
30
|
|
|
31
31
|
<a href="https://glama.ai/mcp/servers/@Couchbase-Ecosystem/mcp-server-couchbase">
|
|
32
32
|
<img width="380" height="200" src="https://glama.ai/mcp/servers/@Couchbase-Ecosystem/mcp-server-couchbase/badge" alt="Couchbase Server MCP server" />
|
|
@@ -34,7 +34,10 @@ An [MCP](https://modelcontextprotocol.io/) server implementation of Couchbase th
|
|
|
34
34
|
|
|
35
35
|
## Features
|
|
36
36
|
|
|
37
|
+
- Get a list of all the buckets in the cluster
|
|
37
38
|
- Get a list of all the scopes and collections in the specified bucket
|
|
39
|
+
- Get a list of all the scopes in the specified bucket
|
|
40
|
+
- Get a list of all the collections in a specified scope and bucket. Note that this tool requires the cluster to have Query service.
|
|
38
41
|
- Get the structure for a collection
|
|
39
42
|
- Get a document by ID from a specified scope and collection
|
|
40
43
|
- Upsert a document by ID to a specified scope and collection
|
|
@@ -53,7 +56,7 @@ An [MCP](https://modelcontextprotocol.io/) server implementation of Couchbase th
|
|
|
53
56
|
|
|
54
57
|
## Configuration
|
|
55
58
|
|
|
56
|
-
The MCP server can be run either from the
|
|
59
|
+
The MCP server can be run either from the prebuilt PyPI package or the source using uv.
|
|
57
60
|
|
|
58
61
|
### Running from PyPI
|
|
59
62
|
|
|
@@ -61,6 +64,8 @@ We publish a pre built [PyPI package](https://pypi.org/project/couchbase-mcp-ser
|
|
|
61
64
|
|
|
62
65
|
#### Server Configuration using Pre built Package for MCP Clients
|
|
63
66
|
|
|
67
|
+
#### Basic Authentication
|
|
68
|
+
|
|
64
69
|
```json
|
|
65
70
|
{
|
|
66
71
|
"mcpServers": {
|
|
@@ -70,8 +75,27 @@ We publish a pre built [PyPI package](https://pypi.org/project/couchbase-mcp-ser
|
|
|
70
75
|
"env": {
|
|
71
76
|
"CB_CONNECTION_STRING": "couchbases://connection-string",
|
|
72
77
|
"CB_USERNAME": "username",
|
|
73
|
-
"CB_PASSWORD": "password"
|
|
74
|
-
|
|
78
|
+
"CB_PASSWORD": "password"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
or
|
|
86
|
+
|
|
87
|
+
#### mTLS
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"couchbase": {
|
|
93
|
+
"command": "uvx",
|
|
94
|
+
"args": ["couchbase-mcp-server"],
|
|
95
|
+
"env": {
|
|
96
|
+
"CB_CONNECTION_STRING": "couchbases://connection-string",
|
|
97
|
+
"CB_CLIENT_CERT_PATH": "/path/to/client-certificate.pem",
|
|
98
|
+
"CB_CLIENT_KEY_PATH": "/path/to/client.key"
|
|
75
99
|
}
|
|
76
100
|
}
|
|
77
101
|
}
|
|
@@ -108,8 +132,7 @@ This is the common configuration for the MCP clients such as Claude Desktop, Cur
|
|
|
108
132
|
"env": {
|
|
109
133
|
"CB_CONNECTION_STRING": "couchbases://connection-string",
|
|
110
134
|
"CB_USERNAME": "username",
|
|
111
|
-
"CB_PASSWORD": "password"
|
|
112
|
-
"CB_BUCKET_NAME": "bucket_name"
|
|
135
|
+
"CB_PASSWORD": "password"
|
|
113
136
|
}
|
|
114
137
|
}
|
|
115
138
|
}
|
|
@@ -123,17 +146,21 @@ This is the common configuration for the MCP clients such as Claude Desktop, Cur
|
|
|
123
146
|
### Additional Configuration for MCP Server
|
|
124
147
|
|
|
125
148
|
The server can be configured using environment variables or command line arguments:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
149
|
+
| Environment Variable | CLI Argument | Description | Default |
|
|
150
|
+
|--------------------------------|--------------------------|---------------------------------------------------------------------------------------------|------------------------------------------|
|
|
151
|
+
| `CB_CONNECTION_STRING` | `--connection-string` | Connection string to the Couchbase cluster | **Required** |
|
|
152
|
+
| `CB_USERNAME` | `--username` | Username with access to required buckets for basic authentication | **Required (or Client Certificate and Key needed for mTLS)** |
|
|
153
|
+
| `CB_PASSWORD` | `--password` | Password for basic authentication | **Required (or Client Certificate and Key needed for mTLS)** |
|
|
154
|
+
| `CB_CLIENT_CERT_PATH` | `--client-cert-path` | Path to the client certificate file for mTLS authentication| **Required if using mTLS (or Username and Password required)** |
|
|
155
|
+
| `CB_CLIENT_KEY_PATH` | `--client-key-path` | Path to the client key file for mTLS authentication| **Required if using mTLS (or Username and Password required)** |
|
|
156
|
+
| `CB_CA_CERT_PATH` | `--ca-cert-path` | Path to server root certificate for TLS if server is configured with a self-signed/untrusted certificate. This will not be required if you are connecting to Capella | |
|
|
157
|
+
| `CB_MCP_READ_ONLY_QUERY_MODE` | `--read-only-query-mode` | Prevent data modification queries | `true` |
|
|
158
|
+
| `CB_MCP_TRANSPORT` | `--transport` | Transport mode: `stdio`, `http`, `sse` | `stdio` |
|
|
159
|
+
| `CB_MCP_HOST` | `--host` | Host for HTTP/SSE transport modes | `127.0.0.1` |
|
|
160
|
+
| `CB_MCP_PORT` | `--port` | Port for HTTP/SSE transport modes | `8000` |
|
|
161
|
+
|
|
162
|
+
> Note: For authentication, you need either the Username and Password or the Client Certificate and key paths. Optionally, you can specify the CA root certificate path that will be used to validate the server certificates.
|
|
163
|
+
> If both the Client Certificate & key path and the username and password are specified, the client certificates will be used for authentication.
|
|
137
164
|
|
|
138
165
|
You can also check the version of the server using:
|
|
139
166
|
|
|
@@ -232,7 +259,6 @@ uvx couchbase-mcp-server \
|
|
|
232
259
|
--connection-string='<couchbase_connection_string>' \
|
|
233
260
|
--username='<database_username>' \
|
|
234
261
|
--password='<database_password>' \
|
|
235
|
-
--bucket-name='<couchbase_bucket_to_use>' \
|
|
236
262
|
--read-only-query-mode=true \
|
|
237
263
|
--transport=http
|
|
238
264
|
```
|
|
@@ -266,7 +292,6 @@ uvx couchbase-mcp-server \
|
|
|
266
292
|
--connection-string='<couchbase_connection_string>' \
|
|
267
293
|
--username='<database_username>' \
|
|
268
294
|
--password='<database_password>' \
|
|
269
|
-
--bucket-name='<couchbase_bucket_to_use>' \
|
|
270
295
|
--read-only-query-mode=true \
|
|
271
296
|
--transport=sse
|
|
272
297
|
```
|
|
@@ -343,7 +368,6 @@ docker run --rm -i \
|
|
|
343
368
|
-e CB_CONNECTION_STRING='<couchbase_connection_string>' \
|
|
344
369
|
-e CB_USERNAME='<database_user>' \
|
|
345
370
|
-e CB_PASSWORD='<database_password>' \
|
|
346
|
-
-e CB_BUCKET_NAME='<bucket_name>' \
|
|
347
371
|
-e CB_MCP_TRANSPORT='<http|sse|stdio>' \
|
|
348
372
|
-e CB_MCP_READ_ONLY_QUERY_MODE='<true|false>' \
|
|
349
373
|
-e CB_MCP_PORT=9001 \
|
|
@@ -372,8 +396,6 @@ The Docker image can be used in `stdio` transport mode with the following config
|
|
|
372
396
|
"CB_USERNAME=<database_user>",
|
|
373
397
|
"-e",
|
|
374
398
|
"CB_PASSWORD=<database_password>",
|
|
375
|
-
"-e",
|
|
376
|
-
"CB_BUCKET_NAME=<bucket_name>",
|
|
377
399
|
"mcp/couchbase"
|
|
378
400
|
]
|
|
379
401
|
}
|
|
@@ -399,9 +421,9 @@ The Couchbase MCP server can also be used as a managed server in your agentic ap
|
|
|
399
421
|
## Troubleshooting Tips
|
|
400
422
|
|
|
401
423
|
- Ensure the path to your MCP server repository is correct in the configuration if running from source.
|
|
402
|
-
- Verify that your Couchbase connection string, database username, password
|
|
424
|
+
- Verify that your Couchbase connection string, database username, password or the path to the certificates are correct.
|
|
403
425
|
- If using Couchbase Capella, ensure that the cluster is [accessible](https://docs.couchbase.com/cloud/clusters/allow-ip-address.html) from the machine where the MCP server is running.
|
|
404
|
-
- Check that the database user has proper permissions to access
|
|
426
|
+
- Check that the database user has proper permissions to access at least one bucket.
|
|
405
427
|
- Confirm that the `uv` package manager is properly installed and accessible. You may need to provide absolute path to `uv`/`uvx` in the `command` field in the configuration.
|
|
406
428
|
- Check the logs for any errors or warnings that may indicate issues with the MCP server. The location of the logs depend on your MCP client.
|
|
407
429
|
- If you are observing issues running your MCP server from source after updating your local MCP server repository, try running `uv sync` to update the [dependencies](https://docs.astral.sh/uv/concepts/projects/sync/#syncing-the-environment).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
mcp_server.py,sha256=QGKbHL5fnQQnFkZZvK89eEWR6HcmF_0JqV_E6lke3M8,5031
|
|
2
|
+
couchbase_mcp_server-0.5.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
3
|
+
tools/__init__.py,sha256=1FRntOrRYPiITHteCiEu0DrIBEesNgpeeLkvzFpRNv4,1355
|
|
4
|
+
tools/kv.py,sha256=NGUs43iuXElj9rYe4RCyCStqoh5y1fUgbg1oWuU4WeQ,2493
|
|
5
|
+
tools/query.py,sha256=fSSG7XuxnND1lHwI-r8y_M3h6f82vCXxCwB_A2wHN-M,3598
|
|
6
|
+
tools/server.py,sha256=iv_UH4OiBnlPYSSEt1rs3LFeYmIZb_c5mEEYBToS8gU,5188
|
|
7
|
+
utils/__init__.py,sha256=eVRW08PAfJXrB3cYbmHIj3v6kfa1-a9dlNXsEOB0igU,1223
|
|
8
|
+
utils/config.py,sha256=B6H_JYDn6uxtu9juM924zdvNQgSaHh_u6rYME3_0_xQ,268
|
|
9
|
+
utils/connection.py,sha256=NtAU4pmHMZubSJcs_X_lai9o8dih5mW0RyrRdmyp1Po,2892
|
|
10
|
+
utils/constants.py,sha256=w0zvQ1zMzJBg44Yl3aQW8KfaaRPn0BgPOLEe8xLeLSE,487
|
|
11
|
+
utils/context.py,sha256=XZL4M70BMdFBptJ9sT0zxhEey-EvvoSKZJrP_sb7q-A,2286
|
|
12
|
+
couchbase_mcp_server-0.5.0.dist-info/METADATA,sha256=G_R6LVJ0LqtBhF4v58z1-qRms3YgHjDpLLDfGwlEI3Q,20630
|
|
13
|
+
couchbase_mcp_server-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
+
couchbase_mcp_server-0.5.0.dist-info/entry_points.txt,sha256=iU5pF4kIMTnNhoMPHhdH-k8o1Fmxb_iM9qJHCZcL6Ak,57
|
|
15
|
+
couchbase_mcp_server-0.5.0.dist-info/top_level.txt,sha256=cQeSKgjLtHXStynG6fJkj8o4tBep9OkDai_4h5bf_4I,23
|
|
16
|
+
couchbase_mcp_server-0.5.0.dist-info/RECORD,,
|
mcp_server.py
CHANGED
|
@@ -78,9 +78,22 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
|
|
|
78
78
|
help="Couchbase database password (required for operations)",
|
|
79
79
|
)
|
|
80
80
|
@click.option(
|
|
81
|
-
"--
|
|
82
|
-
envvar="
|
|
83
|
-
|
|
81
|
+
"--ca-cert-path",
|
|
82
|
+
envvar="CB_CA_CERT_PATH",
|
|
83
|
+
default=None,
|
|
84
|
+
help="Path to the server trust store (CA certificate) file. The certificate at this path is used to verify the server certificate during the authentication process.",
|
|
85
|
+
)
|
|
86
|
+
@click.option(
|
|
87
|
+
"--client-cert-path",
|
|
88
|
+
envvar="CB_CLIENT_CERT_PATH",
|
|
89
|
+
default=None,
|
|
90
|
+
help="Path to the client certificate file used for mTLS authentication.",
|
|
91
|
+
)
|
|
92
|
+
@click.option(
|
|
93
|
+
"--client-key-path",
|
|
94
|
+
envvar="CB_CLIENT_KEY_PATH",
|
|
95
|
+
default=None,
|
|
96
|
+
help="Path to the client certificate key file used for mTLS authentication.",
|
|
84
97
|
)
|
|
85
98
|
@click.option(
|
|
86
99
|
"--read-only-query-mode",
|
|
@@ -121,7 +134,9 @@ def main(
|
|
|
121
134
|
connection_string,
|
|
122
135
|
username,
|
|
123
136
|
password,
|
|
124
|
-
|
|
137
|
+
ca_cert_path,
|
|
138
|
+
client_cert_path,
|
|
139
|
+
client_key_path,
|
|
125
140
|
read_only_query_mode,
|
|
126
141
|
transport,
|
|
127
142
|
host,
|
|
@@ -133,7 +148,9 @@ def main(
|
|
|
133
148
|
"connection_string": connection_string,
|
|
134
149
|
"username": username,
|
|
135
150
|
"password": password,
|
|
136
|
-
"
|
|
151
|
+
"ca_cert_path": ca_cert_path,
|
|
152
|
+
"client_cert_path": client_cert_path,
|
|
153
|
+
"client_key_path": client_key_path,
|
|
137
154
|
"read_only_query_mode": read_only_query_mode,
|
|
138
155
|
"transport": transport,
|
|
139
156
|
"host": host,
|
tools/__init__.py
CHANGED
|
@@ -19,16 +19,22 @@ from .query import (
|
|
|
19
19
|
|
|
20
20
|
# Server tools
|
|
21
21
|
from .server import (
|
|
22
|
+
get_buckets_in_cluster,
|
|
23
|
+
get_collections_in_scope,
|
|
22
24
|
get_scopes_and_collections_in_bucket,
|
|
25
|
+
get_scopes_in_bucket,
|
|
23
26
|
get_server_configuration_status,
|
|
24
27
|
test_cluster_connection,
|
|
25
28
|
)
|
|
26
29
|
|
|
27
30
|
# List of all tools for easy registration
|
|
28
31
|
ALL_TOOLS = [
|
|
32
|
+
get_buckets_in_cluster,
|
|
29
33
|
get_server_configuration_status,
|
|
30
34
|
test_cluster_connection,
|
|
31
35
|
get_scopes_and_collections_in_bucket,
|
|
36
|
+
get_collections_in_scope,
|
|
37
|
+
get_scopes_in_bucket,
|
|
32
38
|
get_document_by_id,
|
|
33
39
|
upsert_document_by_id,
|
|
34
40
|
delete_document_by_id,
|
|
@@ -41,6 +47,9 @@ __all__ = [
|
|
|
41
47
|
"get_server_configuration_status",
|
|
42
48
|
"test_cluster_connection",
|
|
43
49
|
"get_scopes_and_collections_in_bucket",
|
|
50
|
+
"get_collections_in_scope",
|
|
51
|
+
"get_scopes_in_bucket",
|
|
52
|
+
"get_buckets_in_cluster",
|
|
44
53
|
"get_document_by_id",
|
|
45
54
|
"upsert_document_by_id",
|
|
46
55
|
"delete_document_by_id",
|
tools/kv.py
CHANGED
|
@@ -9,18 +9,25 @@ from typing import Any
|
|
|
9
9
|
|
|
10
10
|
from mcp.server.fastmcp import Context
|
|
11
11
|
|
|
12
|
+
from utils.connection import connect_to_bucket
|
|
12
13
|
from utils.constants import MCP_SERVER_NAME
|
|
13
|
-
from utils.context import
|
|
14
|
+
from utils.context import get_cluster_connection
|
|
14
15
|
|
|
15
16
|
logger = logging.getLogger(f"{MCP_SERVER_NAME}.tools.kv")
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def get_document_by_id(
|
|
19
|
-
ctx: Context,
|
|
20
|
+
ctx: Context,
|
|
21
|
+
bucket_name: str,
|
|
22
|
+
scope_name: str,
|
|
23
|
+
collection_name: str,
|
|
24
|
+
document_id: str,
|
|
20
25
|
) -> dict[str, Any]:
|
|
21
26
|
"""Get a document by its ID from the specified scope and collection.
|
|
22
27
|
If the document is not found, it will raise an exception."""
|
|
23
|
-
|
|
28
|
+
|
|
29
|
+
cluster = get_cluster_connection(ctx)
|
|
30
|
+
bucket = connect_to_bucket(cluster, bucket_name)
|
|
24
31
|
try:
|
|
25
32
|
collection = bucket.scope(scope_name).collection(collection_name)
|
|
26
33
|
result = collection.get(document_id)
|
|
@@ -32,6 +39,7 @@ def get_document_by_id(
|
|
|
32
39
|
|
|
33
40
|
def upsert_document_by_id(
|
|
34
41
|
ctx: Context,
|
|
42
|
+
bucket_name: str,
|
|
35
43
|
scope_name: str,
|
|
36
44
|
collection_name: str,
|
|
37
45
|
document_id: str,
|
|
@@ -39,7 +47,8 @@ def upsert_document_by_id(
|
|
|
39
47
|
) -> bool:
|
|
40
48
|
"""Insert or update a document by its ID.
|
|
41
49
|
Returns True on success, False on failure."""
|
|
42
|
-
|
|
50
|
+
cluster = get_cluster_connection(ctx)
|
|
51
|
+
bucket = connect_to_bucket(cluster, bucket_name)
|
|
43
52
|
try:
|
|
44
53
|
collection = bucket.scope(scope_name).collection(collection_name)
|
|
45
54
|
collection.upsert(document_id, document_content)
|
|
@@ -51,11 +60,16 @@ def upsert_document_by_id(
|
|
|
51
60
|
|
|
52
61
|
|
|
53
62
|
def delete_document_by_id(
|
|
54
|
-
ctx: Context,
|
|
63
|
+
ctx: Context,
|
|
64
|
+
bucket_name: str,
|
|
65
|
+
scope_name: str,
|
|
66
|
+
collection_name: str,
|
|
67
|
+
document_id: str,
|
|
55
68
|
) -> bool:
|
|
56
69
|
"""Delete a document by its ID.
|
|
57
70
|
Returns True on success, False on failure."""
|
|
58
|
-
|
|
71
|
+
cluster = get_cluster_connection(ctx)
|
|
72
|
+
bucket = connect_to_bucket(cluster, bucket_name)
|
|
59
73
|
try:
|
|
60
74
|
collection = bucket.scope(scope_name).collection(collection_name)
|
|
61
75
|
collection.remove(document_id)
|
tools/query.py
CHANGED
|
@@ -10,22 +10,23 @@ from typing import Any
|
|
|
10
10
|
from lark_sqlpp import modifies_data, modifies_structure, parse_sqlpp
|
|
11
11
|
from mcp.server.fastmcp import Context
|
|
12
12
|
|
|
13
|
+
from utils.connection import connect_to_bucket
|
|
13
14
|
from utils.constants import MCP_SERVER_NAME
|
|
14
|
-
from utils.context import
|
|
15
|
+
from utils.context import get_cluster_connection
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(f"{MCP_SERVER_NAME}.tools.query")
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def get_schema_for_collection(
|
|
20
|
-
ctx: Context, scope_name: str, collection_name: str
|
|
21
|
+
ctx: Context, bucket_name: str, scope_name: str, collection_name: str
|
|
21
22
|
) -> dict[str, Any]:
|
|
22
23
|
"""Get the schema for a collection in the specified scope.
|
|
23
24
|
Returns a dictionary with the collection name and the schema returned by running INFER query on the Couchbase collection.
|
|
24
25
|
"""
|
|
25
26
|
schema = {"collection_name": collection_name, "schema": []}
|
|
26
27
|
try:
|
|
27
|
-
query = f"INFER {collection_name}"
|
|
28
|
-
result = run_sql_plus_plus_query(ctx, scope_name, query)
|
|
28
|
+
query = f"INFER `{collection_name}`"
|
|
29
|
+
result = run_sql_plus_plus_query(ctx, bucket_name, scope_name, query)
|
|
29
30
|
# Result is a list of list of schemas. We convert it to a list of schemas.
|
|
30
31
|
if result:
|
|
31
32
|
schema["schema"] = result[0]
|
|
@@ -36,10 +37,13 @@ def get_schema_for_collection(
|
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
def run_sql_plus_plus_query(
|
|
39
|
-
ctx: Context, scope_name: str, query: str
|
|
40
|
+
ctx: Context, bucket_name: str, scope_name: str, query: str
|
|
40
41
|
) -> list[dict[str, Any]]:
|
|
41
42
|
"""Run a SQL++ query on a scope and return the results as a list of JSON objects."""
|
|
42
|
-
|
|
43
|
+
cluster = get_cluster_connection(ctx)
|
|
44
|
+
|
|
45
|
+
bucket = connect_to_bucket(cluster, bucket_name)
|
|
46
|
+
|
|
43
47
|
app_context = ctx.request_context.lifespan_context
|
|
44
48
|
read_only_query_mode = app_context.read_only_query_mode
|
|
45
49
|
logger.info(f"Running SQL++ queries in read-only mode: {read_only_query_mode}")
|
|
@@ -75,3 +79,20 @@ def run_sql_plus_plus_query(
|
|
|
75
79
|
except Exception as e:
|
|
76
80
|
logger.error(f"Error running query: {e!s}", exc_info=True)
|
|
77
81
|
raise
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Don't expose this function to the MCP server until we have a use case
|
|
85
|
+
def run_cluster_query(ctx: Context, query: str, **kwargs: Any) -> list[dict[str, Any]]:
|
|
86
|
+
"""Run a query on the cluster object and return the results as a list of JSON objects."""
|
|
87
|
+
|
|
88
|
+
cluster = get_cluster_connection(ctx)
|
|
89
|
+
results = []
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
result = cluster.query(query, **kwargs)
|
|
93
|
+
for row in result:
|
|
94
|
+
results.append(row)
|
|
95
|
+
return results
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.error(f"Error running query: {e}")
|
|
98
|
+
raise
|
tools/server.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Tools for server operations.
|
|
3
3
|
|
|
4
|
-
This module contains tools for getting the server status, testing the connection, and getting the scopes and collections in the bucket.
|
|
4
|
+
This module contains tools for getting the server status, testing the connection, and getting the buckets in the cluster, the scopes and collections in the bucket.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import logging
|
|
@@ -9,9 +9,11 @@ from typing import Any
|
|
|
9
9
|
|
|
10
10
|
from mcp.server.fastmcp import Context
|
|
11
11
|
|
|
12
|
+
from tools.query import run_cluster_query
|
|
12
13
|
from utils.config import get_settings
|
|
14
|
+
from utils.connection import connect_to_bucket
|
|
13
15
|
from utils.constants import MCP_SERVER_NAME
|
|
14
|
-
from utils.context import
|
|
16
|
+
from utils.context import get_cluster_connection
|
|
15
17
|
|
|
16
18
|
logger = logging.getLogger(f"{MCP_SERVER_NAME}.tools.server")
|
|
17
19
|
|
|
@@ -26,15 +28,16 @@ def get_server_configuration_status(ctx: Context) -> dict[str, Any]:
|
|
|
26
28
|
configuration = {
|
|
27
29
|
"connection_string": settings.get("connection_string", "Not set"),
|
|
28
30
|
"username": settings.get("username", "Not set"),
|
|
29
|
-
"bucket_name": settings.get("bucket_name", "Not set"),
|
|
30
31
|
"read_only_query_mode": settings.get("read_only_query_mode", True),
|
|
31
32
|
"password_configured": bool(settings.get("password")),
|
|
33
|
+
"ca_cert_path_configured": bool(settings.get("ca_cert_path")),
|
|
34
|
+
"client_cert_path_configured": bool(settings.get("client_cert_path")),
|
|
35
|
+
"client_key_path_configured": bool(settings.get("client_key_path")),
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
app_context = ctx.request_context.lifespan_context
|
|
35
39
|
connection_status = {
|
|
36
40
|
"cluster_connected": app_context.cluster is not None,
|
|
37
|
-
"bucket_connected": app_context.bucket is not None,
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
return {
|
|
@@ -45,39 +48,46 @@ def get_server_configuration_status(ctx: Context) -> dict[str, Any]:
|
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
|
|
48
|
-
def test_cluster_connection(
|
|
49
|
-
|
|
51
|
+
def test_cluster_connection(
|
|
52
|
+
ctx: Context, bucket_name: str | None = None
|
|
53
|
+
) -> dict[str, Any]:
|
|
54
|
+
"""Test the connection to Couchbase cluster and optionally to a bucket.
|
|
50
55
|
This tool verifies the connection to the Couchbase cluster and bucket by establishing the connection if it is not already established.
|
|
56
|
+
If bucket name is not provided, it will not try to connect to the bucket specified in the MCP server settings.
|
|
51
57
|
Returns connection status and basic cluster information.
|
|
52
58
|
"""
|
|
53
59
|
try:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
cluster = get_cluster_connection(ctx)
|
|
61
|
+
bucket = None
|
|
62
|
+
if bucket_name:
|
|
63
|
+
bucket = connect_to_bucket(cluster, bucket_name)
|
|
58
64
|
|
|
59
65
|
return {
|
|
60
66
|
"status": "success",
|
|
61
|
-
"cluster_connected":
|
|
62
|
-
"bucket_connected":
|
|
67
|
+
"cluster_connected": cluster.connected,
|
|
68
|
+
"bucket_connected": bucket is not None,
|
|
63
69
|
"bucket_name": bucket_name,
|
|
64
|
-
"message": "Successfully connected to Couchbase cluster
|
|
70
|
+
"message": "Successfully connected to Couchbase cluster",
|
|
65
71
|
}
|
|
66
72
|
except Exception as e:
|
|
67
73
|
return {
|
|
68
74
|
"status": "error",
|
|
69
75
|
"cluster_connected": False,
|
|
70
76
|
"bucket_connected": False,
|
|
77
|
+
"bucket_name": bucket_name,
|
|
71
78
|
"error": str(e),
|
|
72
|
-
"message": "Failed to connect to Couchbase",
|
|
79
|
+
"message": "Failed to connect to Couchbase cluster",
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
|
|
76
|
-
def get_scopes_and_collections_in_bucket(
|
|
83
|
+
def get_scopes_and_collections_in_bucket(
|
|
84
|
+
ctx: Context, bucket_name: str
|
|
85
|
+
) -> dict[str, list[str]]:
|
|
77
86
|
"""Get the names of all scopes and collections in the bucket.
|
|
78
87
|
Returns a dictionary with scope names as keys and lists of collection names as values.
|
|
79
88
|
"""
|
|
80
|
-
|
|
89
|
+
cluster = get_cluster_connection(ctx)
|
|
90
|
+
bucket = connect_to_bucket(cluster, bucket_name)
|
|
81
91
|
try:
|
|
82
92
|
scopes_collections = {}
|
|
83
93
|
collection_manager = bucket.collections()
|
|
@@ -89,3 +99,41 @@ def get_scopes_and_collections_in_bucket(ctx: Context) -> dict[str, list[str]]:
|
|
|
89
99
|
except Exception as e:
|
|
90
100
|
logger.error(f"Error getting scopes and collections: {e}")
|
|
91
101
|
raise
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_buckets_in_cluster(ctx: Context) -> list[str]:
|
|
105
|
+
"""Get the names of all the accessible buckets in the cluster."""
|
|
106
|
+
cluster = get_cluster_connection(ctx)
|
|
107
|
+
bucket_manager = cluster.buckets()
|
|
108
|
+
buckets_with_settings = bucket_manager.get_all_buckets()
|
|
109
|
+
|
|
110
|
+
buckets = []
|
|
111
|
+
for bucket in buckets_with_settings:
|
|
112
|
+
buckets.append(bucket.name)
|
|
113
|
+
|
|
114
|
+
return buckets
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def get_scopes_in_bucket(ctx: Context, bucket_name: str) -> list[str]:
|
|
118
|
+
"""Get the names of all scopes in the given bucket."""
|
|
119
|
+
cluster = get_cluster_connection(ctx)
|
|
120
|
+
bucket = connect_to_bucket(cluster, bucket_name)
|
|
121
|
+
try:
|
|
122
|
+
scopes = bucket.collections().get_all_scopes()
|
|
123
|
+
return [scope.name for scope in scopes]
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.error(f"Error getting scopes in the bucket {bucket_name}: {e}")
|
|
126
|
+
raise
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_collections_in_scope(
|
|
130
|
+
ctx: Context, bucket_name: str, scope_name: str
|
|
131
|
+
) -> list[str]:
|
|
132
|
+
"""Get the names of all collections in the given scope and bucket."""
|
|
133
|
+
|
|
134
|
+
# Get the collections in the scope using system:all_keyspaces collection
|
|
135
|
+
query = "SELECT DISTINCT(name) as collection_name FROM system:all_keyspaces where `bucket`=$bucket_name and `scope`=$scope_name"
|
|
136
|
+
results = run_cluster_query(
|
|
137
|
+
ctx, query, bucket_name=bucket_name, scope_name=scope_name
|
|
138
|
+
)
|
|
139
|
+
return [result["collection_name"] for result in results]
|
utils/__init__.py
CHANGED
|
@@ -7,8 +7,6 @@ This module contains utility functions for configuration, connection, and contex
|
|
|
7
7
|
# Configuration utilities
|
|
8
8
|
from .config import (
|
|
9
9
|
get_settings,
|
|
10
|
-
validate_connection_config,
|
|
11
|
-
validate_required_param,
|
|
12
10
|
)
|
|
13
11
|
|
|
14
12
|
# Connection utilities
|
|
@@ -33,7 +31,7 @@ from .constants import (
|
|
|
33
31
|
# Context utilities
|
|
34
32
|
from .context import (
|
|
35
33
|
AppContext,
|
|
36
|
-
|
|
34
|
+
get_cluster_connection,
|
|
37
35
|
)
|
|
38
36
|
|
|
39
37
|
# Note: Individual modules create their own hierarchical loggers using:
|
|
@@ -42,14 +40,12 @@ from .context import (
|
|
|
42
40
|
__all__ = [
|
|
43
41
|
# Config
|
|
44
42
|
"get_settings",
|
|
45
|
-
"validate_required_param",
|
|
46
|
-
"validate_connection_config",
|
|
47
43
|
# Connection
|
|
48
44
|
"connect_to_couchbase_cluster",
|
|
49
45
|
"connect_to_bucket",
|
|
50
46
|
# Context
|
|
51
47
|
"AppContext",
|
|
52
|
-
"
|
|
48
|
+
"get_cluster_connection",
|
|
53
49
|
# Constants
|
|
54
50
|
"MCP_SERVER_NAME",
|
|
55
51
|
"DEFAULT_READ_ONLY_MODE",
|
utils/config.py
CHANGED
|
@@ -7,32 +7,7 @@ from .constants import MCP_SERVER_NAME
|
|
|
7
7
|
logger = logging.getLogger(f"{MCP_SERVER_NAME}.utils.config")
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def validate_required_param(
|
|
11
|
-
ctx: click.Context, param: click.Parameter, value: str | None
|
|
12
|
-
) -> str:
|
|
13
|
-
"""Validate that a required parameter is not empty."""
|
|
14
|
-
if not value or value.strip() == "":
|
|
15
|
-
raise click.BadParameter(f"{param.name} cannot be empty")
|
|
16
|
-
return value
|
|
17
|
-
|
|
18
|
-
|
|
19
10
|
def get_settings() -> dict:
|
|
20
11
|
"""Get settings from Click context."""
|
|
21
12
|
ctx = click.get_current_context()
|
|
22
13
|
return ctx.obj or {}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def validate_connection_config() -> None:
|
|
26
|
-
"""Validate that all required parameters for the MCP server are available when needed."""
|
|
27
|
-
settings = get_settings()
|
|
28
|
-
required_params = ["connection_string", "username", "password", "bucket_name"]
|
|
29
|
-
missing_params = []
|
|
30
|
-
|
|
31
|
-
for param in required_params:
|
|
32
|
-
if not settings.get(param):
|
|
33
|
-
missing_params.append(param)
|
|
34
|
-
|
|
35
|
-
if missing_params:
|
|
36
|
-
error_msg = f"Missing required parameters for the MCP server: {', '.join(missing_params)}"
|
|
37
|
-
logger.error(error_msg)
|
|
38
|
-
raise ValueError(error_msg)
|
utils/connection.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
from datetime import timedelta
|
|
3
4
|
|
|
4
|
-
from couchbase.auth import PasswordAuthenticator
|
|
5
|
+
from couchbase.auth import CertificateAuthenticator, PasswordAuthenticator
|
|
5
6
|
from couchbase.cluster import Bucket, Cluster
|
|
6
7
|
from couchbase.options import ClusterOptions
|
|
7
8
|
|
|
@@ -11,15 +12,40 @@ logger = logging.getLogger(f"{MCP_SERVER_NAME}.utils.connection")
|
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
def connect_to_couchbase_cluster(
|
|
14
|
-
connection_string: str,
|
|
15
|
+
connection_string: str,
|
|
16
|
+
username: str,
|
|
17
|
+
password: str,
|
|
18
|
+
ca_cert_path: str | None = None,
|
|
19
|
+
client_cert_path: str | None = None,
|
|
20
|
+
client_key_path: str | None = None,
|
|
15
21
|
) -> Cluster:
|
|
16
22
|
"""Connect to Couchbase cluster and return the cluster object if successful.
|
|
23
|
+
The connection can be established using the client certificate and key or the username and password. Optionally, the CA root certificate path can also be provided.
|
|
24
|
+
Either of the path to the client certificate and key or the username and password should be provided.
|
|
25
|
+
If the client certificate and key are provided, the username and password are not used.
|
|
26
|
+
If both the client certificate and key and the username and password are provided, the client certificate is used for authentication.
|
|
17
27
|
If the connection fails, it will raise an exception.
|
|
18
28
|
"""
|
|
19
29
|
|
|
20
30
|
try:
|
|
21
31
|
logger.info("Connecting to Couchbase cluster...")
|
|
22
|
-
|
|
32
|
+
if client_cert_path and client_key_path:
|
|
33
|
+
logger.info("Connecting to Couchbase cluster with client certificate...")
|
|
34
|
+
if not os.path.exists(client_cert_path) or not os.path.exists(
|
|
35
|
+
client_key_path
|
|
36
|
+
):
|
|
37
|
+
raise FileNotFoundError(
|
|
38
|
+
f"Client certificate files not found at {os.path.basename(client_cert_path)} or {os.path.basename(client_key_path)}."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
auth = CertificateAuthenticator(
|
|
42
|
+
cert_path=client_cert_path,
|
|
43
|
+
key_path=client_key_path,
|
|
44
|
+
trust_store_path=ca_cert_path,
|
|
45
|
+
)
|
|
46
|
+
else:
|
|
47
|
+
logger.info("Connecting to Couchbase cluster with password...")
|
|
48
|
+
auth = PasswordAuthenticator(username, password, cert_path=ca_cert_path)
|
|
23
49
|
options = ClusterOptions(auth)
|
|
24
50
|
options.apply_profile("wan_development")
|
|
25
51
|
|
|
@@ -38,7 +64,6 @@ def connect_to_bucket(cluster: Cluster, bucket_name: str) -> Bucket:
|
|
|
38
64
|
If the operation fails, it will raise an exception.
|
|
39
65
|
"""
|
|
40
66
|
try:
|
|
41
|
-
logger.info(f"Connecting to bucket: {bucket_name}")
|
|
42
67
|
bucket = cluster.bucket(bucket_name)
|
|
43
68
|
return bucket
|
|
44
69
|
except Exception as e:
|
utils/context.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
|
|
4
|
-
from couchbase.cluster import
|
|
4
|
+
from couchbase.cluster import Cluster
|
|
5
5
|
from mcp.server.fastmcp import Context
|
|
6
6
|
|
|
7
|
-
from utils.config import get_settings
|
|
8
|
-
from utils.connection import
|
|
7
|
+
from utils.config import get_settings
|
|
8
|
+
from utils.connection import connect_to_couchbase_cluster
|
|
9
9
|
from utils.constants import MCP_SERVER_NAME
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger(f"{MCP_SERVER_NAME}.utils.context")
|
|
@@ -16,7 +16,6 @@ class AppContext:
|
|
|
16
16
|
"""Context for the MCP server."""
|
|
17
17
|
|
|
18
18
|
cluster: Cluster | None = None
|
|
19
|
-
bucket: Bucket | None = None
|
|
20
19
|
read_only_query_mode: bool = True
|
|
21
20
|
|
|
22
21
|
|
|
@@ -30,51 +29,36 @@ def _set_cluster_in_lifespan_context(ctx: Context) -> None:
|
|
|
30
29
|
connection_string = settings.get("connection_string")
|
|
31
30
|
username = settings.get("username")
|
|
32
31
|
password = settings.get("password")
|
|
32
|
+
ca_cert_path = settings.get("ca_cert_path")
|
|
33
|
+
client_cert_path = settings.get("client_cert_path")
|
|
34
|
+
client_key_path = settings.get("client_key_path")
|
|
35
|
+
|
|
33
36
|
cluster = connect_to_couchbase_cluster(
|
|
34
37
|
connection_string, # type: ignore
|
|
35
38
|
username, # type: ignore
|
|
36
39
|
password, # type: ignore
|
|
40
|
+
ca_cert_path,
|
|
41
|
+
client_cert_path,
|
|
42
|
+
client_key_path,
|
|
37
43
|
)
|
|
38
44
|
ctx.request_context.lifespan_context.cluster = cluster
|
|
39
45
|
except Exception as e:
|
|
40
46
|
logger.error(
|
|
41
|
-
|
|
47
|
+
"Failed to connect to Couchbase: %s\n"
|
|
48
|
+
"Verify connection string, and either:\n"
|
|
49
|
+
"- Username/password are correct, or\n"
|
|
50
|
+
"- Client certificate and key exist and match server mapping.\n"
|
|
51
|
+
"If using self-signed or custom CA, set CB_CA_CERT_PATH to the CA file.",
|
|
52
|
+
e,
|
|
42
53
|
)
|
|
43
54
|
raise
|
|
44
55
|
|
|
45
56
|
|
|
46
|
-
def
|
|
47
|
-
"""
|
|
48
|
-
If the bucket is not set, it will try to connect to the bucket using the cluster object in the lifespan context.
|
|
57
|
+
def get_cluster_connection(ctx: Context) -> Cluster:
|
|
58
|
+
"""Return the cluster connection from the lifespan context.
|
|
49
59
|
If the cluster is not set, it will try to connect to the cluster using the connection string, username, and password.
|
|
50
|
-
If the connection fails, it will raise an exception.
|
|
51
60
|
"""
|
|
52
|
-
settings = get_settings()
|
|
53
|
-
bucket_name = settings.get("bucket_name")
|
|
54
|
-
|
|
55
|
-
# If the bucket is not set, try to connect to the bucket using the cluster object in the lifespan context
|
|
56
|
-
app_context = ctx.request_context.lifespan_context
|
|
57
|
-
|
|
58
|
-
try:
|
|
59
|
-
# If the cluster is not set, try to connect to the cluster
|
|
60
|
-
if not app_context.cluster:
|
|
61
|
-
_set_cluster_in_lifespan_context(ctx)
|
|
62
|
-
cluster = app_context.cluster
|
|
63
|
-
|
|
64
|
-
# Try to connect to the bucket using the cluster object
|
|
65
|
-
bucket = connect_to_bucket(cluster, bucket_name) # type: ignore
|
|
66
|
-
app_context.bucket = bucket
|
|
67
|
-
except Exception as e:
|
|
68
|
-
logger.error(
|
|
69
|
-
f"Failed to connect to bucket: {e} \n Please check your bucket name and credentials."
|
|
70
|
-
)
|
|
71
|
-
raise
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def ensure_bucket_connection(ctx: Context) -> Bucket:
|
|
75
|
-
"""Ensure bucket connection is established and return the bucket object."""
|
|
76
|
-
validate_connection_config()
|
|
77
61
|
app_context = ctx.request_context.lifespan_context
|
|
78
|
-
if not app_context.
|
|
79
|
-
|
|
80
|
-
return app_context.
|
|
62
|
+
if not app_context.cluster:
|
|
63
|
+
_set_cluster_in_lifespan_context(ctx)
|
|
64
|
+
return app_context.cluster
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
mcp_server.py,sha256=t-TFlzAPH0qUILFjX33n6VnEvTgWAjCRy3HrKkKosWE,4416
|
|
2
|
-
couchbase_mcp_server-0.4.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
3
|
-
tools/__init__.py,sha256=jVvVnpoSJrYFmxAnx2vkyAuKZ1-Ti1fzdIgbO0yqAcM,1097
|
|
4
|
-
tools/kv.py,sha256=LDDASE4nD4oiTkDzSSYIrmvleqn7e8W-14UOql_SWK8,2199
|
|
5
|
-
tools/query.py,sha256=BYpJkgiCAINqgreW22uk-6JjrKrZkswGYKRhU1l-jLI,2901
|
|
6
|
-
tools/server.py,sha256=a4rDDDYO7WNTcKN8KjiQlnsIZ1_ydlAVgIy5nxxic5g,3251
|
|
7
|
-
utils/__init__.py,sha256=E7Puxqate6J5xdNWSzLFU33UfsbSGE9B4aiMB696v04,1353
|
|
8
|
-
utils/config.py,sha256=vCZfyhQXnbTPwcJi3YfLTXqqirCeHVTSyHui5_aGchs,1136
|
|
9
|
-
utils/connection.py,sha256=2a_syAAMFE2zHEFMzsOJGz4Vhz6Tq3lgna3qfhKva0w,1520
|
|
10
|
-
utils/constants.py,sha256=w0zvQ1zMzJBg44Yl3aQW8KfaaRPn0BgPOLEe8xLeLSE,487
|
|
11
|
-
utils/context.py,sha256=pepc4sCUsmx0itpoJ28_TS03mSXJbQhgTZcN7NAvvTg,3013
|
|
12
|
-
couchbase_mcp_server-0.4.0.dist-info/METADATA,sha256=xSs8q3ywwkWXlJp8GiVeqAYD_vnrEPKHtlprMWNg-UE,19356
|
|
13
|
-
couchbase_mcp_server-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
-
couchbase_mcp_server-0.4.0.dist-info/entry_points.txt,sha256=iU5pF4kIMTnNhoMPHhdH-k8o1Fmxb_iM9qJHCZcL6Ak,57
|
|
15
|
-
couchbase_mcp_server-0.4.0.dist-info/top_level.txt,sha256=cQeSKgjLtHXStynG6fJkj8o4tBep9OkDai_4h5bf_4I,23
|
|
16
|
-
couchbase_mcp_server-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
{couchbase_mcp_server-0.4.0.dist-info → couchbase_mcp_server-0.5.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{couchbase_mcp_server-0.4.0.dist-info → couchbase_mcp_server-0.5.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|