lbb_query
Query Little Big Brain knowledge graphs using structured SPARQL-subset JSON, SPARQL text, SHACL validation, inference rules, or analytics. Supports pagination, grouping, aggregations, and snapshot pinning.
Instructions
Analytical and expert reads. Modes: structured (SPARQL-subset JSON body), sparql (SPARQL text), shacl, infer, retrieval_premises, analyze. Relations are https://littlebigbrain.com/r/NAME and types https://littlebigbrain.com/class/NAME (both lowercased); entities are content-addressed, so anchor a named one by its rdfs:label rather than building its IRI (see the sparql/structured field hints, and lbb_inspect action=ontology for exact names). For structured/sparql row paging, MCP owns limit/offset via row_limit/cursor; a cursor reuses the original query/body and pins continuation pages to the original head commit. When a single page's rows are complete server-side (row_page.has_more false) but too large for one MCP result, the envelope reports rows_shown (fewer than row_page.returned), keeps truncated: true, and hands back a next cursor that pages the same result set at a smaller row_limit — so a large fully-returned result is never silently cut without a way to read the rest.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Structured SPARQL-subset or analytics request body. Shape: { patterns: [{ subject, predicate, object }], filters?, group_by?, group_keys?, aggregates?, having?, order_by?, select?, limit?, distinct? }. Each pattern term is { var: "x" } or a fixed { entity: { entity_type, name } }; `predicate` is a relation name and is case-insensitive here (FOR_CLIENT and for_client both resolve — unlike SPARQL text, which needs the lowercased IRI local name). FILTER — `filters` is a list of conditions, each of exact shape { "compare": { "op": <op>, "left": <term>, "right": <term> } } (or { "and": [<filter>…] }, { "or": [<filter>…] }, { "not": <filter> }). `op` is one of eq | ne | lt | le | gt | ge (NOT the symbols =,<,>). Each <term> is exactly one of { "var": "x" }, { "property": { "var": "x", "field": "amount" } } (a typed scalar attribute), or { "value": <typed> } — and <typed> is exactly one wrapper: { "str": "…" }, { "i64": 5 }, { "f64": 0.9 }, { "bool": true }, { "date_time": "2026-01-01" } (RFC3339), or { "entity": { "entity_type": "T", "name": "N" } }. Complete runnable example — deals whose amount ≥ 1000000: { "patterns": [{ "subject": { "var": "d" }, "predicate": "for_client", "object": { "var": "c" } }], "filters": [{ "compare": { "op": "ge", "left": { "property": { "var": "d", "field": "amount" } }, "right": { "value": { "f64": 1000000 } } } }] }. Comparisons use the field's real declared type (numbers as numbers, datetimes as instants), so they run server-side. GROUP BY supports both entity-identity keys (group_by: ["s"]) and typed scalar keys via group_keys: a property value ({ property: { var, field, as } }) or a calendar bucket of a datetime property ({ date_bucket: { var, field, granularity: year|month|week|day|hour, as } }). Scalar keys come back per group under value_keys[as] — so a per-area breakdown or a commits-per-month time series is one server-side query, no client-side bucketing. Worked example -- commits per area per month in one query: { "patterns": [{ "subject": { "var": "c" }, "predicate": "committed_to", "object": { "var": "repo" } }], "group_keys": [{ "date_bucket": { "var": "c", "field": "committed_at", "granularity": "month", "as": "m" } }, { "property": { "var": "c", "field": "area", "as": "area" } }], "aggregates": [{ "func": "count", "as": "n" }], "order_by": [{ "var": "m" }] } -- area and committed_at are typed entity attributes (set via entity_properties; readable flat under attributes, never a nested metadata blob), and each group returns value_keys.m + value_keys.area + aggregates.n. `having: [...]` takes the same filter shape over the aggregated groups (e.g. { "compare": { "op": "gt", "left": { "var": "n" }, "right": { "value": { "i64": 10 } } } }); it is evaluated only on this grouped path, NOT alongside `combinators` (UNION/OPTIONAL/MINUS), which route to the analytics engine. Cheap aggregate count: pair an equality having (e.g. { "compare": { "op": "eq", "left": { "var": "n" }, "right": { "value": { "i64": 4 } } } }) with row_limit: 1 -- the response row_page.total reports how many groups match without materializing them all, so you read the count off row_page.total instead of paging every matching row. For snapshot pinning prefer the top-level `as_of` / `as_of_commit_seq` arguments below; a bare `as_of` key inside the body is rejected (the body's valid-time field is `as_of_valid_time`). | |
| mode | Yes | Selects the variant (one of: structured, sparql, shacl, infer, retrieval_premises, analyze). | |
| as_of | No | Snapshot pin (valid-time, RFC3339): evaluate the body as of this instant. Folded into the request's `as_of_valid_time`. Top-level here is the supported spelling — a bare `as_of` inside the body is rejected, since the server silently ignores it. | |
| chart | No | ||
| field | No | ||
| graph | No | Graph to target; defaults to the connection's graph | |
| query | No | SPARQL 1.1 query text (SELECT or ASK). IRI scheme: relations are <https://littlebigbrain.com/r/NAME> (NAME lowercased, e.g. writes_to; reverse a relation with the ^ path operator, no stored inverse triple). Types are <https://littlebigbrain.com/class/NAME> (lowercased), matched as `?x a <…/class/NAME>` with rdfs:subClassOf closure on by default. Property fields are <https://littlebigbrain.com/p/NAME> (lowercased). The local name is ALWAYS lowercase — an uppercase one (e.g. <…/r/FOR_CLIENT>) is a different, non-existent IRI that silently matches nothing; this tool auto-lowercases the local name of /r/, /class/, and /p/ IRIs for you and adds a `notes` entry when it does, so a stray uppercase still resolves. (Structured mode's `predicate` is case-insensitive on its own.) Entities are content-addressed <https://littlebigbrain.com/e/HASH> — never build an entity IRI from a name; anchor a named entity by its label instead: `?e <http://www.w3.org/2000/01/rdf-schema#label> "Acme"`. Discover the exact relation and type names with lbb_inspect action=ontology. SELECT and ASK only (CONSTRUCT/DESCRIBE are rejected). Example: SELECT ?service ?db WHERE { ?service <https://littlebigbrain.com/r/writes_to> ?db } LIMIT 10 | |
| rules | No | ||
| top_k | No | ||
| branch | No | Branch to target; defaults to the connection's branch | |
| cursor | No | Opaque cursor from a previous lbb_query row page; reruns the original query at the next offset. | |
| detail | No | Response detail level. Defaults to compact. | |
| metric | No | ||
| shapes | No | ||
| sparql | No | ||
| explain | No | ||
| relation | No | ||
| row_limit | No | Maximum query rows to return in this page. Defaults by detail: compact=20, standard=100, full=1000. | |
| threshold | No | ||
| max_rounds | No | ||
| shacl_mode | No | select returns focus nodes; validate returns a report | |
| anchor_name | No | ||
| anchor_type | No | ||
| max_derived | No | ||
| query_top_k | No | ||
| max_premises | No | ||
| max_solutions | No | ||
| include_derived | No | ||
| as_of_commit_seq | No | Snapshot pin: evaluate the body as of this commit_seq, hiding later commits. Errors if past head. Top-level alias for the body's `as_of_commit_seq` (either works for this one). |