Skip to content
Development
Skill

/cb-analytics-query

Use this skill when the user wants to write or improve SQL++ queries against Couchbase Analytics through cb-analytics-mcp. Trigger when they mention "SQL++", "Analytics query", "execute_query", "scan_consistency", "request_plus", "pagination", "EXPLAIN", "truncated", "row cap",

From plugin
couchbase-skills-for-claudeai
430 skills
Install
$ npx -y skills add celticht32/Couchbase-Skills-for-Claude.ai --skill cb-analytics-query --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/cb-analytics-query

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use this skill when the user wants to write or improve SQL++ queries against Couchbase Analytics through cb-analytics-mcp. Trigger when they mention "SQL++", "Analytics query", "execute_query", "scan_consistency", "request_plus", "pagination", "EXPLAIN", "truncated", "row cap",

SKILL.md

cb-analytics-query.SKILL.md
name: cb-analytics-query
description: |
  Use this skill when the user wants to write or improve SQL++ queries against
  Couchbase Analytics through cb-analytics-mcp. Trigger when they mention
  "SQL++", "Analytics query", "execute_query", "scan_consistency", "request_plus",
  "pagination", "EXPLAIN", "truncated", "row cap", or anything about querying
  datasets, dataverses, joins, aggregations, windowing, query plans, or
  N1QL/SQL++ language features in this server's context.
license: MIT

Querying Couchbase Analytics via cb-analytics-mcp

You have **five SQL++ tools** for talking to the Analytics service. Picking the right one matters — they have different cost profiles, rate-limit budgets, and result shapes.

| Tool | Cost | Result shape | When to reach for it | |---|---|---|---| | `execute_query` | high (full result) | rows, may be truncated | DDL, mutations, small SELECTs | | `execute_query_readonly` | high (full result), **cached** | rows, may be truncated, `cached: bool` | repeated SELECTs in an investigation loop | | `execute_query_paginated` | constant per page | first page + handle | SELECTs that might return many rows | | `fetch_next_page` | constant per page | next page for a handle | follow-up to paginated | | `explain_query` | tiny (no execution) | query plan | "why is this slow" |

The cardinal rule: pick the tool that matches what you'll do with the result

If you only need the **first N rows** to answer the user's question, use `execute_query_paginated` with `page_size=N`. Don't pull a million rows through the MCP boundary just to take the first 20.

If you need to **show the user the data** (and the dataset is small), `execute_query_readonly` is fine — but watch for `truncated: true` in the response.

If you need to **make a decision** based on aggregates (`COUNT`, `SUM`, `AVG`, `GROUP BY`), the result is small by construction. Use `execute_query_readonly` and benefit from the cache.

The soft cap (you will see `truncated: true`)

Both `execute_query` and `execute_query_readonly` enforce a server-side row cap (default 1000, configurable via `MAX_QUERY_ROWS`). Responses include:

  • `truncated: true` if the cap kicked in
  • `row_cap`: the cap that was applied (or `null` if disabled)
  • `full_row_count`: how many rows the cluster actually had

When you see `truncated: true`, **do not silently report incomplete data**. Either:

1. Tell the user the result is truncated and ask if they want all rows (then re-issue as paginated), or 2. If you only needed a sample, acknowledge it and continue ("here are the first 1000 of 47,832 matching rows").

The user is operating an LLM-driven tool. Hidden truncation will eventually produce wrong answers.

The cache (you will see `cached: true`)

`execute_query_readonly` results are cached for ~60 seconds keyed by `(cluster, statement, scan_consistency)`. Responses include `cached: true` on a cache hit, `cached: false` on a miss. Practical implications:

  • Repeating an identical query inside an investigation loop is free; lean

into it.

  • If freshness matters (e.g. you're watching an ingestion catch up), add

`scan_consistency="request_plus"` so the cache key differs from the default-consistency cached entry.

  • If you need a definitively fresh read, use `execute_query` (uncached) or

wait 60s.

Pagination, end-to-end

# 1. First page
first = execute_query_paginated(
    statement="SELECT id, name, status FROM Default.Orders WHERE region = $r",
    named_args={"r": "EMEA"},
    page_size=100,
)
handle = first["data"]["pagination_handle"]
# Use first["data"]["results"] — that's your page 0

# 2. Walk pages until exhausted
while first["data"]["has_more"]:
    nxt = fetch_next_page(pagination_handle=handle)
    # Process nxt["data"]["results"]
    if not nxt["data"]["has_more"]:
        break
    handle = nxt["data"]["pagination_handle"]  # handle stays the same; this is for clarity

Important details:

  • `page_size` must be between 1 and 10000. Default 100.
  • A trailing `LIMIT/OFFSET` clause in your statement gets **stripped** —

the server adds its own.

  • Handles expire after 30 minutes of inactivity. If you get

`"not found or expired"`, just call `execute_query_paginated` again to start fresh.

  • When `has_more` is `false`, the handle is auto-dropped on the server.

Don't call `fetch_next_page` again with it.

  • `total_seen` accumulates across pages; use it for progress reporting.

EXPLAIN — your slow-query diagnostic

plan = explain_query(statement="SELECT * FROM Default.Orders WHERE customer_id = 'C-1234'")
# plan["data"]["plan"] is the service-internal JSON plan

When to reach for `explain_query`:

  • The user reports a slow query.
  • You ran a query and `data.metrics.executionTime` was surprising.
  • The user asks "is this query using an index?"
  • You're about to recommend adding an index — check first that the planner

isn't already using one.

You don't have to add `EXPLAIN` to your statement; the tool prepends it if not present.

Parameterisation rules (still apply)

Never interpolate user-controlled values into the statement string. Use `named_args`:

execute_query(
    statement="SELECT * FROM Default.Orders o WHERE o.customer_id = $cust",
    named_args={"cust": "C-1234"},
)

Identifiers (dataset names, field names) **can't** be parameterised by SQL++. If you must inject one, validate it first (the server already does this for `infer_schema`).

Note: `execute_query_paginated` also accepts `named_args` and `positional_args`. Parameter values are reused across pages — no need to re-pass them to `fetch_next_page`.

Scan consistency

  • `not_bounded` (default) — fastest, may see stale results.
  • `request_plus` — wait for ingest to catch up to this point in time. Use

when correctness matters more than latency.

  • `at_plus` — wait for a specific mutation token; rarely needed outside

SDK code.

`scan_consistency` is part of the cache key for `execute_qu

Read more
Ships withcouchbase-skills-for-claudeai

Claude skill files for working with Couchbase — covering every major service and deployment pattern from application integration through AI applications, Kubernetes operations, mobile sync, security hardening, and analytics.

Get the whole plugin