couchbase-mcp-server 0.5.2rc6__py3-none-any.whl → 0.5.3__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.5.2rc6.dist-info → couchbase_mcp_server-0.5.3.dist-info}/METADATA +27 -1
- {couchbase_mcp_server-0.5.2rc6.dist-info → couchbase_mcp_server-0.5.3.dist-info}/RECORD +7 -7
- {couchbase_mcp_server-0.5.2rc6.dist-info → couchbase_mcp_server-0.5.3.dist-info}/WHEEL +1 -1
- tools/__init__.py +21 -0
- tools/query.py +264 -2
- {couchbase_mcp_server-0.5.2rc6.dist-info → couchbase_mcp_server-0.5.3.dist-info}/entry_points.txt +0 -0
- {couchbase_mcp_server-0.5.2rc6.dist-info → couchbase_mcp_server-0.5.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: couchbase-mcp-server
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: Couchbase MCP Server - The Developer Data Platform for Critical Applications in Our AI World
|
|
5
5
|
Project-URL: Homepage, https://github.com/Couchbase-Ecosystem/mcp-server-couchbase
|
|
6
6
|
Project-URL: Documentation, https://github.com/Couchbase-Ecosystem/mcp-server-couchbase#readme
|
|
@@ -19,6 +19,8 @@ Requires-Dist: mcp[cli]<2.0.0,>=1.20.0
|
|
|
19
19
|
Requires-Dist: urllib3>=2.0.0
|
|
20
20
|
Provides-Extra: dev
|
|
21
21
|
Requires-Dist: pre-commit==4.2.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-asyncio==0.24.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest==8.3.3; extra == 'dev'
|
|
22
24
|
Requires-Dist: ruff==0.12.5; extra == 'dev'
|
|
23
25
|
Description-Content-Type: text/markdown
|
|
24
26
|
|
|
@@ -45,12 +47,21 @@ An [MCP](https://modelcontextprotocol.io/) server implementation of Couchbase th
|
|
|
45
47
|
- Upsert a document by ID to a specified scope and collection
|
|
46
48
|
- Delete a document by ID from a specified scope and collection
|
|
47
49
|
- Run a [SQL++ query](https://www.couchbase.com/sqlplusplus/) on a specified scope
|
|
50
|
+
- Queries are automatically scoped to the specified bucket and scope, so use collection names directly (e.g., use `SELECT * FROM users` instead of `SELECT * FROM bucket.scope.users`)
|
|
48
51
|
- There is an option in the MCP server, `CB_MCP_READ_ONLY_QUERY_MODE` that is set to true by default to disable running SQL++ queries that change the data or the underlying collection structure. Note that the documents can still be updated by ID.
|
|
49
52
|
- Get the status of the MCP server
|
|
50
53
|
- Check the cluster credentials by connecting to the cluster
|
|
51
54
|
- List all indexes in the cluster with their definitions, with optional filtering by bucket, scope, collection and index name.
|
|
52
55
|
- Get index recommendations from Couchbase Index Advisor for a given SQL++ query to optimize query performance
|
|
53
56
|
- Get cluster health status and list of all running services
|
|
57
|
+
- Query performance analysis tools:
|
|
58
|
+
- Get longest running queries by average service time
|
|
59
|
+
- Get most frequently executed queries
|
|
60
|
+
- Get queries with the largest response sizes
|
|
61
|
+
- Get queries with the largest result counts
|
|
62
|
+
- Get queries that use a primary index (potential performance concern)
|
|
63
|
+
- Get queries that don't use a covering index
|
|
64
|
+
- Get queries that are not selective (index scans return many more documents than final result)
|
|
54
65
|
|
|
55
66
|
## Prerequisites
|
|
56
67
|
|
|
@@ -433,6 +444,21 @@ The Couchbase MCP server can also be used as a managed server in your agentic ap
|
|
|
433
444
|
- 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.
|
|
434
445
|
- 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).
|
|
435
446
|
|
|
447
|
+
## Integration testing
|
|
448
|
+
|
|
449
|
+
We provide high-level MCP integration tests to verify that the server exposes the expected tools and that they can be invoked against a demo Couchbase cluster.
|
|
450
|
+
|
|
451
|
+
1. Export demo cluster credentials:
|
|
452
|
+
- `CB_CONNECTION_STRING`
|
|
453
|
+
- `CB_USERNAME`
|
|
454
|
+
- `CB_PASSWORD`
|
|
455
|
+
- Optional: `CB_MCP_TEST_BUCKET` (a bucket to probe during the tests)
|
|
456
|
+
2. Run the tests:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
uv run pytest tests/ -v
|
|
460
|
+
```
|
|
461
|
+
|
|
436
462
|
---
|
|
437
463
|
|
|
438
464
|
## 👩💻 Contributing
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
certs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
certs/capella_root_ca.pem,sha256=SuSjgKclcQQg0kheTRd3dg6B0FUsUy717T5n3xcAU_E,1131
|
|
3
3
|
mcp_server.py,sha256=dvHQR-55JhuXX_bj2hLU0ek411qPp0qHEKqAmPdwjKU,4991
|
|
4
|
-
tools/__init__.py,sha256=
|
|
4
|
+
tools/__init__.py,sha256=72DlDEv1j_IpZTp9rL1fA5QGFXp5eburCDObGqpPuFc,2462
|
|
5
5
|
tools/index.py,sha256=cCBr0ptFBVc-HN5SoCauQAh2DsAP_Is8NPUSa6QcLM0,6682
|
|
6
6
|
tools/kv.py,sha256=NGUs43iuXElj9rYe4RCyCStqoh5y1fUgbg1oWuU4WeQ,2493
|
|
7
|
-
tools/query.py,sha256=
|
|
7
|
+
tools/query.py,sha256=5fYrMm6ReXuDn7uu1QdVxoCtUnaqaIFPcAWBNhiTa4Y,11303
|
|
8
8
|
tools/server.py,sha256=4krPjBoQmUHUmOE03T0CcCGFJtoaeHVS91oOrYj8JsA,6670
|
|
9
9
|
utils/__init__.py,sha256=Fcbp-VIK0Gwwy7hl1AjV9NBKZsscmOV2vYdTBam5M3A,1361
|
|
10
10
|
utils/config.py,sha256=B6H_JYDn6uxtu9juM924zdvNQgSaHh_u6rYME3_0_xQ,268
|
|
@@ -12,8 +12,8 @@ utils/connection.py,sha256=NtAU4pmHMZubSJcs_X_lai9o8dih5mW0RyrRdmyp1Po,2892
|
|
|
12
12
|
utils/constants.py,sha256=w0zvQ1zMzJBg44Yl3aQW8KfaaRPn0BgPOLEe8xLeLSE,487
|
|
13
13
|
utils/context.py,sha256=XZL4M70BMdFBptJ9sT0zxhEey-EvvoSKZJrP_sb7q-A,2286
|
|
14
14
|
utils/index_utils.py,sha256=W0rvoBXU_2aB9m-HDlLChZoMzvlIX6FUWF6RTsYGfYM,10910
|
|
15
|
-
couchbase_mcp_server-0.5.
|
|
16
|
-
couchbase_mcp_server-0.5.
|
|
17
|
-
couchbase_mcp_server-0.5.
|
|
18
|
-
couchbase_mcp_server-0.5.
|
|
19
|
-
couchbase_mcp_server-0.5.
|
|
15
|
+
couchbase_mcp_server-0.5.3.dist-info/METADATA,sha256=-UqmmCTDyeujZ_sDWuPEsngT0ufZ4iAJaQUiPBjD3ls,22182
|
|
16
|
+
couchbase_mcp_server-0.5.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
couchbase_mcp_server-0.5.3.dist-info/entry_points.txt,sha256=iU5pF4kIMTnNhoMPHhdH-k8o1Fmxb_iM9qJHCZcL6Ak,57
|
|
18
|
+
couchbase_mcp_server-0.5.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
19
|
+
couchbase_mcp_server-0.5.3.dist-info/RECORD,,
|
tools/__init__.py
CHANGED
|
@@ -16,6 +16,13 @@ from .kv import (
|
|
|
16
16
|
|
|
17
17
|
# Query tools
|
|
18
18
|
from .query import (
|
|
19
|
+
get_longest_running_queries,
|
|
20
|
+
get_most_frequent_queries,
|
|
21
|
+
get_queries_not_selective,
|
|
22
|
+
get_queries_not_using_covering_index,
|
|
23
|
+
get_queries_using_primary_index,
|
|
24
|
+
get_queries_with_large_result_count,
|
|
25
|
+
get_queries_with_largest_response_sizes,
|
|
19
26
|
get_schema_for_collection,
|
|
20
27
|
run_sql_plus_plus_query,
|
|
21
28
|
)
|
|
@@ -47,6 +54,13 @@ ALL_TOOLS = [
|
|
|
47
54
|
get_index_advisor_recommendations,
|
|
48
55
|
list_indexes,
|
|
49
56
|
get_cluster_health_and_services,
|
|
57
|
+
get_queries_not_selective,
|
|
58
|
+
get_queries_not_using_covering_index,
|
|
59
|
+
get_queries_using_primary_index,
|
|
60
|
+
get_queries_with_large_result_count,
|
|
61
|
+
get_queries_with_largest_response_sizes,
|
|
62
|
+
get_longest_running_queries,
|
|
63
|
+
get_most_frequent_queries,
|
|
50
64
|
]
|
|
51
65
|
|
|
52
66
|
__all__ = [
|
|
@@ -65,6 +79,13 @@ __all__ = [
|
|
|
65
79
|
"get_index_advisor_recommendations",
|
|
66
80
|
"list_indexes",
|
|
67
81
|
"get_cluster_health_and_services",
|
|
82
|
+
"get_queries_not_selective",
|
|
83
|
+
"get_queries_not_using_covering_index",
|
|
84
|
+
"get_queries_using_primary_index",
|
|
85
|
+
"get_queries_with_large_result_count",
|
|
86
|
+
"get_queries_with_largest_response_sizes",
|
|
87
|
+
"get_longest_running_queries",
|
|
88
|
+
"get_most_frequent_queries",
|
|
68
89
|
# Convenience
|
|
69
90
|
"ALL_TOOLS",
|
|
70
91
|
]
|
tools/query.py
CHANGED
|
@@ -39,7 +39,15 @@ def get_schema_for_collection(
|
|
|
39
39
|
def run_sql_plus_plus_query(
|
|
40
40
|
ctx: Context, bucket_name: str, scope_name: str, query: str
|
|
41
41
|
) -> list[dict[str, Any]]:
|
|
42
|
-
"""Run a SQL++ query on a scope and return the results as a list of JSON objects.
|
|
42
|
+
"""Run a SQL++ query on a scope and return the results as a list of JSON objects.
|
|
43
|
+
|
|
44
|
+
The query will be run on the specified scope in the specified bucket.
|
|
45
|
+
The query should use collection names directly without bucket/scope prefixes, as the scope context is automatically set.
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
query = "SELECT * FROM users WHERE age > 18"
|
|
49
|
+
# Incorrect: "SELECT * FROM bucket.scope.users WHERE age > 18"
|
|
50
|
+
"""
|
|
43
51
|
cluster = get_cluster_connection(ctx)
|
|
44
52
|
|
|
45
53
|
bucket = connect_to_bucket(cluster, bucket_name)
|
|
@@ -81,7 +89,6 @@ def run_sql_plus_plus_query(
|
|
|
81
89
|
raise
|
|
82
90
|
|
|
83
91
|
|
|
84
|
-
# Don't expose this function to the MCP server until we have a use case
|
|
85
92
|
def run_cluster_query(ctx: Context, query: str, **kwargs: Any) -> list[dict[str, Any]]:
|
|
86
93
|
"""Run a query on the cluster object and return the results as a list of JSON objects."""
|
|
87
94
|
|
|
@@ -96,3 +103,258 @@ def run_cluster_query(ctx: Context, query: str, **kwargs: Any) -> list[dict[str,
|
|
|
96
103
|
except Exception as e:
|
|
97
104
|
logger.error(f"Error running query: {e}")
|
|
98
105
|
raise
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _run_query_tool_with_empty_message(
|
|
109
|
+
ctx: Context,
|
|
110
|
+
query: str,
|
|
111
|
+
*,
|
|
112
|
+
limit: int,
|
|
113
|
+
empty_message: str,
|
|
114
|
+
extra_payload: dict[str, Any] | None = None,
|
|
115
|
+
**query_kwargs: Any,
|
|
116
|
+
) -> list[dict[str, Any]]:
|
|
117
|
+
"""Execute a cluster query with a consistent empty-result response."""
|
|
118
|
+
results = run_cluster_query(ctx, query, limit=limit, **query_kwargs)
|
|
119
|
+
|
|
120
|
+
if results:
|
|
121
|
+
return results
|
|
122
|
+
|
|
123
|
+
payload: dict[str, Any] = {"message": empty_message, "results": []}
|
|
124
|
+
if extra_payload:
|
|
125
|
+
payload.update(extra_payload)
|
|
126
|
+
return [payload]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_longest_running_queries(ctx: Context, limit: int = 10) -> list[dict[str, Any]]:
|
|
130
|
+
"""Get the N longest running queries from the system:completed_requests catalog.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
limit: Number of queries to return (default: 10)
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List of queries with their average service time and count
|
|
137
|
+
"""
|
|
138
|
+
query = """
|
|
139
|
+
SELECT statement,
|
|
140
|
+
DURATION_TO_STR(avgServiceTime) AS avgServiceTime,
|
|
141
|
+
COUNT(1) AS queries
|
|
142
|
+
FROM system:completed_requests
|
|
143
|
+
WHERE UPPER(statement) NOT LIKE 'INFER %'
|
|
144
|
+
AND UPPER(statement) NOT LIKE 'CREATE INDEX%'
|
|
145
|
+
AND UPPER(statement) NOT LIKE 'CREATE PRIMARY INDEX%'
|
|
146
|
+
AND UPPER(statement) NOT LIKE '% SYSTEM:%'
|
|
147
|
+
GROUP BY statement
|
|
148
|
+
LETTING avgServiceTime = AVG(STR_TO_DURATION(serviceTime))
|
|
149
|
+
ORDER BY avgServiceTime DESC
|
|
150
|
+
LIMIT $limit
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
return _run_query_tool_with_empty_message(
|
|
154
|
+
ctx,
|
|
155
|
+
query,
|
|
156
|
+
limit=limit,
|
|
157
|
+
empty_message=(
|
|
158
|
+
"No completed queries were available to calculate longest running queries."
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def get_most_frequent_queries(ctx: Context, limit: int = 10) -> list[dict[str, Any]]:
|
|
164
|
+
"""Get the N most frequent queries from the system:completed_requests catalog.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
limit: Number of queries to return (default: 10)
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
List of queries with their frequency count
|
|
171
|
+
"""
|
|
172
|
+
query = """
|
|
173
|
+
SELECT statement,
|
|
174
|
+
COUNT(1) AS queries
|
|
175
|
+
FROM system:completed_requests
|
|
176
|
+
WHERE UPPER(statement) NOT LIKE 'INFER %'
|
|
177
|
+
AND UPPER(statement) NOT LIKE 'CREATE INDEX%'
|
|
178
|
+
AND UPPER(statement) NOT LIKE 'CREATE PRIMARY INDEX%'
|
|
179
|
+
AND UPPER(statement) NOT LIKE 'EXPLAIN %'
|
|
180
|
+
AND UPPER(statement) NOT LIKE 'ADVISE %'
|
|
181
|
+
AND UPPER(statement) NOT LIKE '% SYSTEM:%'
|
|
182
|
+
GROUP BY statement
|
|
183
|
+
LETTING queries = COUNT(1)
|
|
184
|
+
ORDER BY queries DESC
|
|
185
|
+
LIMIT $limit
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
return _run_query_tool_with_empty_message(
|
|
189
|
+
ctx,
|
|
190
|
+
query,
|
|
191
|
+
limit=limit,
|
|
192
|
+
empty_message=(
|
|
193
|
+
"No completed queries were available to calculate most frequent queries."
|
|
194
|
+
),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def get_queries_with_largest_response_sizes(
|
|
199
|
+
ctx: Context, limit: int = 10
|
|
200
|
+
) -> list[dict[str, Any]]:
|
|
201
|
+
"""Get queries with the largest response sizes from the system:completed_requests catalog.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
limit: Number of queries to return (default: 10)
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
List of queries with their average result size in bytes, KB, and MB
|
|
208
|
+
"""
|
|
209
|
+
query = """
|
|
210
|
+
SELECT statement,
|
|
211
|
+
avgResultSize AS avgResultSizeBytes,
|
|
212
|
+
(avgResultSize / 1000) AS avgResultSizeKB,
|
|
213
|
+
(avgResultSize / 1000000) AS avgResultSizeMB,
|
|
214
|
+
COUNT(1) AS queries
|
|
215
|
+
FROM system:completed_requests
|
|
216
|
+
WHERE UPPER(statement) NOT LIKE 'INFER %'
|
|
217
|
+
AND UPPER(statement) NOT LIKE 'CREATE INDEX%'
|
|
218
|
+
AND UPPER(statement) NOT LIKE 'CREATE PRIMARY INDEX%'
|
|
219
|
+
AND UPPER(statement) NOT LIKE '% SYSTEM:%'
|
|
220
|
+
GROUP BY statement
|
|
221
|
+
LETTING avgResultSize = AVG(resultSize)
|
|
222
|
+
ORDER BY avgResultSize DESC
|
|
223
|
+
LIMIT $limit
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
return _run_query_tool_with_empty_message(
|
|
227
|
+
ctx,
|
|
228
|
+
query,
|
|
229
|
+
limit=limit,
|
|
230
|
+
empty_message=(
|
|
231
|
+
"No completed queries were available to calculate response sizes."
|
|
232
|
+
),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def get_queries_with_large_result_count(
|
|
237
|
+
ctx: Context, limit: int = 10
|
|
238
|
+
) -> list[dict[str, Any]]:
|
|
239
|
+
"""Get queries with the largest result counts from the system:completed_requests catalog.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
limit: Number of queries to return (default: 10)
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
List of queries with their average result count
|
|
246
|
+
"""
|
|
247
|
+
query = """
|
|
248
|
+
SELECT statement,
|
|
249
|
+
avgResultCount,
|
|
250
|
+
COUNT(1) AS queries
|
|
251
|
+
FROM system:completed_requests
|
|
252
|
+
WHERE UPPER(statement) NOT LIKE 'INFER %' AND
|
|
253
|
+
UPPER(statement) NOT LIKE 'CREATE INDEX%' AND
|
|
254
|
+
UPPER(statement) NOT LIKE 'CREATE PRIMARY INDEX%' AND
|
|
255
|
+
UPPER(statement) NOT LIKE '% SYSTEM:%'
|
|
256
|
+
GROUP BY statement
|
|
257
|
+
LETTING avgResultCount = AVG(resultCount)
|
|
258
|
+
ORDER BY avgResultCount DESC
|
|
259
|
+
LIMIT $limit
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
return _run_query_tool_with_empty_message(
|
|
263
|
+
ctx,
|
|
264
|
+
query,
|
|
265
|
+
limit=limit,
|
|
266
|
+
empty_message=(
|
|
267
|
+
"No completed queries were available to calculate result counts."
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def get_queries_using_primary_index(
|
|
273
|
+
ctx: Context, limit: int = 10
|
|
274
|
+
) -> list[dict[str, Any]]:
|
|
275
|
+
"""Get queries that use a primary index from the system:completed_requests catalog.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
limit: Number of queries to return (default: 10)
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
List of queries that use primary indexes, ordered by result count
|
|
282
|
+
"""
|
|
283
|
+
query = """
|
|
284
|
+
SELECT *
|
|
285
|
+
FROM system:completed_requests
|
|
286
|
+
WHERE phaseCounts.`primaryScan` IS NOT MISSING
|
|
287
|
+
AND UPPER(statement) NOT LIKE '% SYSTEM:%'
|
|
288
|
+
ORDER BY resultCount DESC
|
|
289
|
+
LIMIT $limit
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
return _run_query_tool_with_empty_message(
|
|
293
|
+
ctx,
|
|
294
|
+
query,
|
|
295
|
+
limit=limit,
|
|
296
|
+
empty_message=(
|
|
297
|
+
"No queries using the primary index were found in system:completed_requests."
|
|
298
|
+
),
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def get_queries_not_using_covering_index(
|
|
303
|
+
ctx: Context, limit: int = 10
|
|
304
|
+
) -> list[dict[str, Any]]:
|
|
305
|
+
"""Get queries that don't use a covering index from the system:completed_requests catalog.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
limit: Number of queries to return (default: 10)
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
List of queries that perform index scans but also require fetches (not covering)
|
|
312
|
+
"""
|
|
313
|
+
query = """
|
|
314
|
+
SELECT *
|
|
315
|
+
FROM system:completed_requests
|
|
316
|
+
WHERE phaseCounts.`indexScan` IS NOT MISSING
|
|
317
|
+
AND phaseCounts.`fetch` IS NOT MISSING
|
|
318
|
+
AND UPPER(statement) NOT LIKE '% SYSTEM:%'
|
|
319
|
+
ORDER BY resultCount DESC
|
|
320
|
+
LIMIT $limit
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
return _run_query_tool_with_empty_message(
|
|
324
|
+
ctx,
|
|
325
|
+
query,
|
|
326
|
+
limit=limit,
|
|
327
|
+
empty_message=(
|
|
328
|
+
"No queries that require fetches after index scans were found "
|
|
329
|
+
"in system:completed_requests."
|
|
330
|
+
),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def get_queries_not_selective(ctx: Context, limit: int = 10) -> list[dict[str, Any]]:
|
|
335
|
+
"""Get queries that are not very selective from the system:completed_requests catalog.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
limit: Number of queries to return (default: 10)
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
List of queries where index scans return significantly more documents than the final result
|
|
342
|
+
"""
|
|
343
|
+
query = """
|
|
344
|
+
SELECT statement,
|
|
345
|
+
AVG(phaseCounts.`indexScan` - resultCount) AS diff
|
|
346
|
+
FROM system:completed_requests
|
|
347
|
+
WHERE phaseCounts.`indexScan` > resultCount
|
|
348
|
+
GROUP BY statement
|
|
349
|
+
ORDER BY diff DESC
|
|
350
|
+
LIMIT $limit
|
|
351
|
+
"""
|
|
352
|
+
|
|
353
|
+
return _run_query_tool_with_empty_message(
|
|
354
|
+
ctx,
|
|
355
|
+
query,
|
|
356
|
+
limit=limit,
|
|
357
|
+
empty_message=(
|
|
358
|
+
"No non-selective queries were found in system:completed_requests."
|
|
359
|
+
),
|
|
360
|
+
)
|
{couchbase_mcp_server-0.5.2rc6.dist-info → couchbase_mcp_server-0.5.3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{couchbase_mcp_server-0.5.2rc6.dist-info → couchbase_mcp_server-0.5.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|