Couchbase MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| CB_MCP_HOST | No | Host for HTTP/SSE transport modes | 127.0.0.1 |
| CB_MCP_PORT | No | Port for HTTP/SSE transport modes | 8000 |
| CB_PASSWORD | Yes | Password for authentication | |
| CB_USERNAME | Yes | Username with bucket access | |
| CB_BUCKET_NAME | Yes | Name of the bucket to access | |
| CB_MCP_TRANSPORT | No | Transport mode: stdio, http, sse | stdio |
| CB_CONNECTION_STRING | Yes | Connection string to the Couchbase cluster | |
| CB_MCP_READ_ONLY_QUERY_MODE | No | Prevent data modification queries | true |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| logging | {} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| extensions | {
"io.modelcontextprotocol/ui": {}
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| get_buckets_in_clusterA | Get the names of all the accessible buckets in the cluster. |
| get_bucket_configA | Get a bucket's configuration and live state — or every bucket's, if bucket_name is omitted. Answers "what is this bucket configured as, and how full is it right now?" — RAM quota, replica count, eviction policy, durability level, storage backend, conflict- resolution type, auto-compaction settings, and basic stats (item count, memory used, ops/sec, disk used). Use this to explain a bucket-level symptom that get_cluster_metrics or get_index_stats pointed at, or as a pre-flight safety check before advising a compaction/quota/durability change — this tool only ever reads; create/edit/flush/delete stay CAO-owned. get_buckets_in_cluster lists bucket names via the SDK but exposes none of this; use this tool once a specific bucket needs explaining. bucket_name must be omitted (not passed as "") to get every bucket — an empty string is rejected rather than silently treated the same as omitting it, since a caller that meant to name a bucket should not get back every bucket's configuration instead. Calls GET /pools/default/buckets/<bucket_name> (one bucket) or GET /pools/default/buckets (every bucket, when bucket_name is omitted). Self-managed Couchbase Server 7.6+ only (Capella is rejected without a REST call); needs the Read-Only Admin (ro_admin) role. Reading the result — returned exactly as the endpoint sends it, nothing stripped or renamed, so every field keeps its REST API name (camelCase):
Returns the endpoint's response unchanged — a dict for one bucket, a list for all buckets. Raises on failure, including a 404 for an unknown bucket name. |
| get_server_configuration_statusA | Get the server status and configuration without establishing connection. This tool can be used to verify if the server is running and check the configuration. |
| test_cluster_connectionA | Test the connection to Couchbase cluster and optionally to a bucket. This tool verifies the connection to the Couchbase cluster and bucket by establishing the connection if it is not already established. If bucket name is not provided, it will not try to connect to the bucket specified in the MCP server settings. Returns connection status and basic cluster information. |
| get_scopes_and_collections_in_bucketA | Get the names of all scopes and collections in the bucket. Returns a dictionary with scope names as keys and lists of collection names as values. |
| get_collections_in_scopeA | Get the names of all collections in the given scope and bucket. |
| get_scopes_in_bucketA | Get the names of all scopes in the given bucket. |
| get_cluster_health_and_servicesA | Check whether the cluster is reachable right now, and where it's broken. This actively pings (see caveat below) the cluster's services and reports, per service:
Scope: cluster-level vs bucket-level ping
service_types optionally restricts which services get pinged. Valid values: "key_value", "query", "search", "analytics", "view", "management", "eventing". Omit to ping every service. An unrecognized value returns an error response instead of raising. Caution — this is somewhat invasive: unlike a passive connection-state check, ping performs a live network round-trip to every targeted service. Prefer a narrow service_types filter, and avoid calling this in tight loops or high-frequency polling. Returns:
|
| get_client_connectivity_reportA | Check whether the client's connections were already broken, and for how long. Unlike get_cluster_health_and_services (which actively pings each service right now), this reports the SDK's own cached connection state without performing any network I/O. It's cheap enough to call frequently, but it's only as fresh as the last time the SDK actually talked to each node — it won't proactively detect a service that just went down if nothing has touched it since. Use get_cluster_health_and_services instead when you need a live, right-now reachability check; there's also no way to filter this report to specific services the way that tool's ping can, since no I/O means nothing to filter. For each known endpoint, reports which service it belongs to, its remote/local addresses, connection state, and last_activity — how long it's been since that connection last saw traffic. Also reports an overall online/degraded/offline cluster state. This call makes no request to the server at all, so it needs no specific RBAC role beyond whatever the initial cluster connection already required — unlike an active ping, it isn't gated on KV/Query/Search or Cluster Admin privileges. Returns:
|
| get_cluster_metricsA | Get one or more cluster statistics over a historic time window in a single call. Use this to quantify a suspected problem (e.g. after get_cluster_health_and_services or get_client_connectivity_report) — is a metric spiking, climbing, or stable over time? Self-managed Couchbase Server 7.6+ only — rejects Capella connections without a REST call. Calls POST /pools/default/stats/range.
To find metric names, call discover_tool_input_values(tool_name="get_cluster_metrics") — it lists or searches the full Couchbase metrics reference bundled with this server, offline. Don't guess a metric name: an unknown name comes back as a per-spec error with no data. Not bounded: any number of specs, window, step, or node count is passed straight through to the REST call, so a broad request (long window, fine step, many nodes/specs) can return a large response. Returns {"status": "success", "data": [...]} (one entry per spec, each with "data" and any per-spec "errors") or {"status": "error", "error": "..."}. |
| get_cluster_tasksA | Get the cluster tasks running right now — rebalance, compaction, XDCR, index build. Answers "what is this cluster doing?" — where stuck rebalances, hung index builds and lagging XDCR surface. One call is a single sample, so pair it with get_cluster_metrics to tell "slow but progressing" from "flatlined". Calls GET /pools/default/tasks. Self-managed Couchbase Server 7.6+ only (Capella is rejected without a REST call); needs the Read-Only Admin (ro_admin) role. Returns the endpoint's array unchanged, or [] if no tasks are reported. Only "type" and "status" are common to every entry; the rest are per type — rebalance: progress, perNode, detailedProgress, stageInfo, subtype; bucket_compaction: bucket, progress, changesDone, totalChanges; xdcr: changesLeft, docsChecked, docsWritten, source, target; global_indexes/indexer: bucket, index, progress, id; loadingSampleBucket: task_id, bucket, bucket_uuid. Reading the result:
|
| get_cluster_health_snapshotA | Get per-node service topology, membership, orchestrator and health in one call. Use this to turn a symptom into a specific node and service: which node is in "warning", what it runs, whether it is still in the cluster, and whether it is the orchestrator — i.e. whether acting on it is disruptive. Also answers "did a topology change leave us in a good state?" after a node replacement or failover. Self-managed Couchbase Server 7.6+ only (Capella is rejected without a REST call); needs the Read-Only Admin (ro_admin) role. Reading the result:
Returns {"cluster": {...}, "nodes": [...]}; raises on failure. |
| get_cluster_system_eventsA | Get the cluster's system event log — what changed on the cluster, and when. This is the RCA timeline: once a symptom is confirmed, it finds the config change, failover, rebalance or service restart that preceded it. Window it to the symptom's onset with since_time, then cross-check that timestamp against get_cluster_metrics. Self-managed Couchbase Server 7.6+ only (Capella is rejected without a REST call); needs the Full Admin or Cluster Admin role. Calls GET /events. Ordering — easy to misread:
A cluster's log is overwhelmingly "info", so expect routine entries in the window; move
the window rather than raising Reading the result:
Returns {"summary": {...}, "events": [...]}; raises on failure. |
| get_cluster_query_vitalsA | Get query-engine health (vitals) from every query-service node. Distinguishes "the workload is heavy" from "the query engine itself is stressed" — get_cluster_metrics covers node hardware; this covers the N1QL service layer specifically: request rate, active/queued request counts, memory, GC, service uptime. Use it after get_cluster_metrics/ get_cluster_health_snapshot show healthy infra but queries are still slow. Calls GET /admin/vitals. Self-managed Couchbase Server 7.6+ only (Capella is rejected without a REST call); needs at minimum the Read-Only Admin (ro_admin) role — verify against your cluster's RBAC before relying on this in production, since the N1QL Admin API's exact privilege requirements are not fully documented. /admin/vitals answers per node, not cluster-wide — each query node only describes itself — so every query-service node is queried and the results are keyed by node ("host:port"). Returns {"status": "success", "vitals": {"host:port": {...}, ...}, "unreachable_nodes": [{"node", "error"}]} (the last key omitted when every node answered), or {"status": "error", "error": ...} if no query node could be reached at all, or the cluster is Capella. |
| get_active_queriesA | Get all queries executing right now, across every query-service node. Use this to find a runaway or stuck statement: elapsed time, the statement text, the client, and current state. Pair with get_cluster_query_vitals to confirm the engine is actually under load from what this returns, and pass a result's "requestId" to delete_active_query to cancel it. Calls GET /admin/active_requests. Self-managed Couchbase Server 7.6+ only (Capella is rejected without a REST call); needs at minimum the Read-Only Admin (ro_admin) role — verify against your cluster's RBAC before relying on this in production. /admin/active_requests answers per node — a request only shows up when the node running it is asked — so every query-service node is queried and the lists are merged into one. Each entry already carries its own "node" field, so there is no need to tag entries by which endpoint answered. Returns {"status": "success", "active_requests": [...merged...], "unreachable_nodes": [{"node", "error"}]} (the last key omitted when every node answered), or {"status": "error", "error": ...} if no query node could be reached at all, or the cluster is Capella. |
| get_document_by_idA | Get a document by its ID from the specified scope and collection. If the document is not found, it will raise an exception. |
| lookup_subdocumentA | Look up parts of a document without fetching the whole thing, using Couchbase sub-document operations. Use this instead of get_document_by_id when you only need a few fields, a presence check, or the size of an array/object inside a document — AND you already know the exact field path(s) to look up (e.g. from a prior get_document_by_id call on this same document, from the user explicitly naming the field, or from a known/confirmed schema for this collection). IMPORTANT: Do NOT guess field paths. If you don't already know the document's exact field names/structure, call get_document_by_id first (or instead) — a guessed path that doesn't exist returns a per-path error here rather than the real data, and reporting "not found" for a wrong guess is worse than just fetching the whole document and reading the right field. Provide one or more of the following. Each is a list of sub-document paths using Couchbase's dot/bracket path syntax (e.g. "address.city", "tags[0]", "tags[-1]" for the last array element):
At least one of get_paths, exists_paths, or count_paths must be provided. As a rule of thumb, keep the combined number of paths across all three to 16 or fewer — Couchbase limits subdocument operations per call, though the exact limit is server-side and may change. If the server rejects the call (too many paths, or another constraint like path length or nesting depth), the whole call fails with {"error": "..."}. A path that doesn't exist (or otherwise fails, e.g. count on a non-array/object) does NOT fail the whole call — it is reported individually as {"error": ...} in the returned dict so the other requested paths can still be resolved. Returns a dict with a key for each category that was requested (only requested categories are included): { "get": {"": {"value": } | {"error": "..."}}, "exists": {"": {"value": true | false} | {"error": "..."}}, "count": {"": {"value": } | {"error": "..."}}, } On a connection/lookup failure, or an invalid request (no paths / too many paths), returns {"error": ""} instead. |
| get_schema_for_collectionA | Get the schema for a collection in the specified scope. Returns a dictionary with the collection name and the schema returned by running INFER query on the Couchbase collection. num_sample_values sets how many example values INFER reports per field. Leave it unset to use the server's own default (5). Pass 0 to omit sample values entirely, keeping only the structure and type information. |
| run_sql_plus_plus_queryA | Run a SQL++ query on a scope and return the results as a list of JSON values. The query will be run on the specified scope in the specified bucket. The query should use collection names directly without bucket/scope prefixes, as the scope context is automatically set. Use Example: query = "SELECT * FROM users WHERE age > 18" # Incorrect: "SELECT * FROM bucket.scope.users WHERE age > 18" For creating a new index (scalar or vector), prefer the create_query_index tool over a raw CREATE INDEX statement here — it defers the build by default and tells you the recommended next step. Use list_indexes to check whether an index is online before relying on it in a query plan. For relevance-scored, fuzzy, or linguistic full-text search (scoring, highlighting, faceting, fuzzy/phrase/wildcard matching), prefer run_search over SQL++'s SEARCH() function — it keeps FTS-specific result shape (score, fragments, facets) out of general query results and gives clearer tracing of what was searched. |
| explain_sql_plus_plus_queryA | Generate and evaluate an EXPLAIN plan for a SQL++ query. It provides information about the execution plan for the query. The EXPLAIN statement is run in the specified scope in the specified bucket. It returns query metadata along with an extracted plan and plan evaluation. |
| get_index_advisor_recommendationsB | Get index recommendations from Couchbase Index Advisor for a given SQL++ query. The Index Advisor analyzes the query and provides recommendations for optimal indexes. This tool works with SELECT, UPDATE, DELETE, or MERGE queries. The queries will be run on the specified scope in the specified bucket. Returns a dictionary with:
Each index object contains:
Raises an error carrying the advisor's message if the advisor cannot analyse the statement (for example, a SQL++ syntax error). |
| list_indexesA | List indexes in the cluster with optional filtering by bucket, scope, collection, and index name. Filters must be provided hierarchically: scope requires bucket, collection requires both, index requires all three.
Set Each result contains: name, definition (CREATE INDEX statement), status, isPrimary, bucket, scope, collection, lastScanTime, is_vector (True for a Hyperscale or Composite Vector Index), and, only when is_vector is True, vector_type ("hyperscale" or "composite"). If a required field is missing, the entry contains warning and raw_index_stats instead. Source depends on cluster version: v8+ queries |
| get_index_statsA | Get per-index statistics (size, fragmentation, scan traffic) from the Index Service. Names which index is responsible for disk or memory pressure — use it after get_cluster_metrics shows a node's index disk climbing. Filters are hierarchical: scope requires bucket, collection requires both, index requires all three. No filters returns every index in the cluster. Returns Numbers are per node. A partitioned index appears under several nodes, each
reporting only its own share: sum Reading the stats:
|
| list_search_indexesA | List Search (FTS) indexes, optionally filtered by bucket and scope. Filtering behavior:
Each entry contains: name, uuid, source_name, source_type, idx_type, bucket, scope. bucket/scope are None for cluster-level (legacy) entries. This is a summary view — to get one index's full definition (mappings, analyzers, plan params), call get_search_index_definition with that entry's name and its own bucket/scope values (both, for a scoped entry; neither, when they're None). Don't reuse this call's filters: listing by bucket_name alone spans several scopes, and get_search_index_definition rejects a bucket without a scope. |
| get_search_index_definitionA | Get the full definition of a single Search (FTS) index. Pass both bucket_name and scope_name together to look up a scope-level (scoped) index in that scope, or omit both to look up a cluster-level ("legacy") index. Passing only one of the two is invalid — Couchbase allows the same index name to exist in different scopes, so the location must be stated explicitly rather than guessed. Returns {"success": True, "name", "source_type", "idx_type", "source_name", "uuid", "params" (mapping/analyzer configuration), "source_uuid", "source_params", "plan_params" (num replicas/partitions), "bucket", "scope"}. If no index with this name exists at the given location, returns {"success": False, "error": ...} — confirm the exact name and location first with list_search_indexes. |
| run_searchA | Run an FTS (Search) query against a Search index, or fetch its execution plan. Use this instead of run_sql_plus_plus_query's SEARCH() function when you need relevance-scored, fuzzy, or linguistic full-text matching (scoring, highlighting, faceting, fuzzy/phrase/wildcard matching) — SQL++ can invoke FTS predicates too, but this tool gives clearer tracing of what data was searched and keeps FTS-specific result shape (score, fragments, facets) out of general query results. query is the raw FTS query JSON body, passed through unvalidated to the Search service — this supports every non-vector FTS query type, e.g.:
Pass both bucket_name and scope_name together to query a scope-level (scoped) index, or omit both to query a cluster-level ("legacy") index. Passing only one is invalid. Confirm the index's exact name/location first with list_search_indexes. Don't guess which fields a query body can target — if you don't already know what the index maps and how those fields are analyzed, call get_search_index_definition first; a field-scoped query against an unmapped field matches nothing rather than erroring. Note this only works for indexes with explicit static field mappings — a dynamically mapped index (dynamic: true) has no fixed field list to inspect, since it indexes whatever fields appear in each document; for those, look at actual document contents instead to know what's queryable. explain: if True, fetches the execution plan instead of normal results. The Search service exposes the query plan per matched document, not as a separate plan-only/ dry-run call — so explain=True still executes the query (with explain enabled) and returns the explanation for each returned hit. limit defaults to 1 in this mode to keep it cheap (pass a larger limit to see the plan for more matched documents), and skip/fields/sort/facets/highlight_fields/disable_scoring/raw are ignored — they only apply when explain=False. limit/skip/fields/sort/facets/highlight_fields/disable_scoring map to the equivalent Search options. limit defaults to 10 when explain=False (matching the other query tools in this server) and to 1 when explain=True. raw is a passthrough dict for any other SearchOptions field not exposed directly (e.g. highlight_style, consistent_with). Returns {"success": True, "index_name", "explain", "limit" (the limit actually applied, since it defaults differently depending on explain), "total_hits", "hits", "facets", "metadata": {"errors","metrics"}} on success, or {"success": False, "error": ...} if the input is invalid or the query fails (e.g. bad query syntax, index not found). total_hits counts the hits actually returned, so it is capped by limit — for how many documents matched overall, read metadata.metrics.total_rows instead, and don't report total_hits as the size of the match set. hits entries are {"id","score","fields","fragments"} when explain=False, or {"id","score","explanation"} when explain=True (facets is empty in that case). |
| run_vector_queryA | Embed a query and run a vector similarity search against a GSI vector index. Embedding: query_text is embedded using the model configured via CB_MCP_EMBEDDING_MODEL_PROVIDER/CB_MCP_EMBEDDING_MODEL/CB_MCP_EMBEDDING_MODEL_API_KEY/CB_MCP_EMBEDDING_MODEL_ENDPOINT (plus CB_MCP_EMBEDDING_MODEL_AWS_* for provider=bedrock). If no provider is configured, this tool returns {"success": False, "error": ...} explaining what to set -- it is not hidden from tool discovery when unconfigured. Index selection: this tool queries a Couchbase 8.0+ GSI vector index (Composite Vector Index or Hyperscale Vector Index) via SQL++'s APPROX_VECTOR_DISTANCE(). Unlike Search-service vector search, GSI selects the index automatically from the vector field referenced in the query -- there is no index_name parameter, and none is needed. distance_metric is required, deliberately with no default: it must match
the num_probes/rerank/top_n_scan are Hyperscale Vector Index tuning
parameters. num_probes (integer) is the number of centroids to probe --
raise it to improve poor recall. rerank (true/false) re-checks the top
candidates against the full vectors; it needs num_probes. top_n_scan
(integer) is how many candidates to rerank; it needs num_probes and
rerank. num_probes alone, or num_probes + rerank, are both valid. A
Composite Vector Index doesn't use any of them. This tool
itself has no index_name to check, but list_indexes now reports each
index's where is an optional raw SQL++ boolean expression (e.g. "doc.status = 'active'") appended to prefilter candidates before the vector ordering -- this tool only ever emits a SELECT, so, unlike run_sql_plus_plus_query, there is no write-guard for it to bypass. If the index was created with a partial-index condition (create_query_index's condition), where must match or imply it for the planner to actually use that index -- otherwise it falls back to a full scan silently (confirmed via EXPLAIN), the same footgun documented above for a mismatched distance_metric. select_fields limits the projected document fields; omit it to return the full document. limit defaults to 10, matching the other query/search tools in this server. Returns {"success": True, "hits": [{"id", "distance", "document": {... selected fields, or the full document}}], "total_hits", "cluster_major_version", and a "warning" key only when the detected cluster major version is below 8 (informational -- a pre-8.0 cluster fails the query naturally with its own "unknown function" error rather than being blocked here)} or {"success": False, "error": ...}. The document is nested under "document" rather than merged into the hit directly -- a document field literally named "id" or "distance" would otherwise silently collide with (and could overwrite) this tool's own id/distance metadata. |
| run_vector_searchA | Run the Search service's vector search, including hybrid and prefiltered search. Embedding: vector_query_text is embedded using the model configured via CB_MCP_EMBEDDING_MODEL_PROVIDER/CB_MCP_EMBEDDING_MODEL/CB_MCP_EMBEDDING_MODEL_API_KEY/CB_MCP_EMBEDDING_MODEL_ENDPOINT (plus CB_MCP_EMBEDDING_MODEL_AWS_* for provider=bedrock). If no provider is configured, this tool returns {"success": False, "error": ...} explaining what to set Unlike run_vector_query's GSI-backed search, this tool always targets a named Search (FTS) index -- index_name is required. Pass both bucket_name and scope_name together to query a scope-level (scoped) index, or omit both to query a cluster-level ("legacy") index. Confirm the index's exact name/location first with list_search_indexes if unsure -- this tool does not pre-validate index_name; a wrong or non-vector index name fails naturally at the Search service with its own error. scalar_query and prefilter are both optional and both take the same raw FTS query JSON body run_search accepts (e.g. {"match": "jacket", "field": "description"}), but they do different things -- do not confuse them:
Both can be set together (prefilter narrows the candidate pool, then scalar_query's relevance signal still contributes to ranking within it). Omit both for a plain vector-only search through this tool (equivalent in intent to run_vector_query, but against a named Search index instead of a GSI vector index). num_candidates bounds how many nearest-neighbor candidates the vector query considers; limit bounds how many hits the search as a whole returns, and both default to 10. For a vector-only search (no scalar_query), limit above num_candidates is a no-op -- there are never more than num_candidates vector hits to return. With scalar_query set, limit can meaningfully exceed num_candidates, since scalar_query's own matches aren't bounded by it. fields requests specific stored index fields per hit. This tool does not fetch full document bodies -- a Search hit carries whatever fields the index stores (row.fields), the same as run_search, not a KV-fetched document (a hit has no reliable per-row collection name to fetch against). Follow up with get_document_by_id for any hit you need the full document for. Returns {"success": True, "index_name", "vector_field", "num_candidates", "limit", "total_hits", "hits": [{"id", "score", "fields"}], "is_hybrid", "is_prefiltered"} or {"success": False, "error": ...}. |
| get_queries_not_selectiveA | Get queries that are not very selective from the system:completed_requests catalog. Prefer this over writing a raw system:completed_requests query via run_sql_plus_plus_query. |
| get_queries_not_using_covering_indexA | Get queries that don't use a covering index from the system:completed_requests catalog. Prefer this over writing a raw system:completed_requests query via run_sql_plus_plus_query. |
| get_queries_using_primary_indexA | Get queries that use a primary index from the system:completed_requests catalog. Prefer this over writing a raw system:completed_requests query via run_sql_plus_plus_query. |
| get_queries_with_large_result_countA | Get queries with the largest result counts from the system:completed_requests catalog. Prefer this over writing a raw system:completed_requests query via run_sql_plus_plus_query. |
| get_queries_with_largest_response_sizesA | Get queries with the largest response sizes from the system:completed_requests catalog. Prefer this over writing a raw system:completed_requests query via run_sql_plus_plus_query. |
| get_longest_running_queriesA | Get the N longest running queries from the system:completed_requests catalog. Prefer this over writing a raw system:completed_requests query via run_sql_plus_plus_query. |
| get_most_frequent_queriesA | Get the N most frequent queries from the system:completed_requests catalog. Prefer this over writing a raw system:completed_requests query via run_sql_plus_plus_query. |
| discover_tool_input_valuesA | Find the exact input values a specific tool requires, from reference data bundled with this server. Call this BEFORE calling a tool that needs an exact identifier you do not already know. Do not guess the identifier and do not browse the Couchbase documentation website for it -- a guessed value comes back from the target tool as an error with no data. The reference data ships inside this server, so this works offline and while the cluster is unreachable, which is exactly when you are most likely to be looking up a metric name. Registered today:
tool_name is the only required argument; everything else is optional. Two ways to call it:
Every response includes the chapters block, so you never need a separate browse call before narrowing. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 36 tools
Most tools target clearly distinct resources/actions, and the descriptions do heavy lifting to separate read-only KV, SQL++, FTS, vector, index, and cluster-ops surfaces. A few pairs are genuinely easy to confuse: run_vector_query (GSI) vs run_vector_search (FTS), get_scopes_and_collections_in_bucket vs get_scopes_in_bucket/get_collections_in_scope, and the three cluster-health tools (health_and_services, health_snapshot, client_connectivity_report). The six query-analysis tools are individually named but form a dense cluster an agent must read carefully.
Names are uniformly snake_case and almost all follow verb_noun (get_, list_, run_, explain_, discover_, test_). Minor deviations: near-duplicate verbs for similar concepts (get_document_by_id vs lookup_subdocument, run_vector_query vs run_vector_search) which read consistently but blur intent. No camelCase or style mixing.
36 tools exceeds the 25+ threshold and the surface spans many domains (KV, SQL++, FTS, vector, indexes, cluster diagnostics). Several query-analysis tools (not_selective, not_using_covering_index, using_primary_index, large_result_count, largest_response_sizes, longest_running, most_frequent) could be consolidated into a single parameterized tool. The count feels heavy relative to what an agent can reliably pick from.
The read/investigation surface is broad and strong, but there are no document write tools (no insert/upsert/replace/delete) despite the server being KV-capable. Several tools reference operations that aren't in the surface: run_sql_plus_plus_query points to create_query_index, and get_active_queries points to delete_active_query — neither exists, creating dead-end references. Read-only by design, but the stated domain (a Couchbase MCP server) implies write/lifecycle coverage that's missing.