Skip to main content
Glama
littlebigbrains

@littlebigbrain/mcp

lbb_query

Read-only

Query knowledge graphs by asking plain-language questions, running SPARQL or structured queries, searching by meaning, inspecting schemas, and comparing snapshots.

Instructions

Analytical and expert reads. Modes: question (a question in plain words), structured (SPARQL-subset JSON body), sparql (SPARQL text), search (instances by meaning over every searchable class; filter narrows by class and relationship; every hit checked against the graph), analyze, and four tools for writing your own queries: names, describe, commit_at, compare. names: before a query that names an entity (a person, a company), find its IRI with text=; use the first candidate, not a CONTAINS match on the name. describe: before a query on classes or properties you have not seen, read them with question= (or classes/properties as IRIs): how many sampled instances hold each property (a filter on a rare one returns few rows), the values of small classes such as stages, examples and schema statements. commit_at: for a question about a date, find the commit of date=YYYY-MM-DD (or moment=RFC 3339), then run mode=sparql as_of_commit_seq=. compare: for what changed between two points, run one SELECT at before and after (default the latest) with key=[the entity variable]; added, removed and changed come with totals and pages (next). Use mode=question when you have a question in plain words and no SPARQL query: the server picks the kind of question (route), runs queries in a bounded loop, reads their rows, and answers in plain words (about 8 s). The result holds the answer with citations and a chart hint (kind, x, y: columns of the rows), the steps, the route, and the query and rows the answer stands on. Continue or correct that query with mode=sparql. Each question uses model tokens and counts toward a daily limit of the stack. SPARQL is the query language; search finds what the words describe. SPARQL text can also search by meaning inside the query with ?x https://littlebigbrain.com/search#similarTo "words", so the other patterns filter and join the hits in one query; the result's search field reports the plan. To plan a search: lbb_embeddings action=list, then SPARQL for a class's relationships on a sample, then mode=search with explain=true to check the resolved filter before the real search (lbb_inspect action=guide has the queries). Use the exact IRIs returned by names, describe or RDF. Native records use content-addressed entity IRIs, https://littlebigbrain.com/r/NAME relations and https://littlebigbrain.com/class/NAME types (lowercased); imported RDF may use any vocabulary. Never build an entity IRI from a display name. Structured and text queries pin one published watermark for the request.

Input Schema

NameRequiredDescriptionDefault
keyNocompare: the variables that identify a row's entity, e.g. ["contact"]. With a key the rows are paired: added and removed hold the entities at one point only, changed the entities whose values differ. Without one whole rows are compared.
bodyNoStructured SPARQL-subset 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 } } } }). A `combinators` key (UNION/OPTIONAL/MINUS/EXISTS) is rejected here; express those with SPARQL text under mode=sparql. 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 use the top-level `as_of_commit_seq` argument or the same body field. Valid-time `as_of` and `as_of_valid_time` selectors are unsupported and rejected before HTTP.
dateNocommit_at: YYYY-MM-DD; the last commit written by the end of that day (UTC).
modeYesSelects the variant (one of: structured, sparql, question, search, analyze, names, describe, commit_at, compare).
textNosearch: the query text (required); embedded with the graph's model. names: the question, or the names, to find (required).
afterNocompare: the later point; defaults to the latest commit.
as_ofNoUnsupported in structured and SPARQL text modes; use as_of_commit_seq for a retained commit snapshot.
chartNo
fieldNo
graphNoGraph to target; defaults to the connection's graph
limitNoquestion: rows each query of the server's loop returns, 1 to 1,000. Defaults by detail: compact=20, standard=100, full=1000. names: candidates per name, 1 to 10 (default 5). compare: entries of each list per page, 1 to 1,000 (defaults by detail: 20, 100, 500).
probeNoClusters to read (default 4·√clusters, at least 8). More reads cost latency and find more.
queryNoSPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. Imported RDF retains its exact, case-sensitive IRIs. Use returned IRIs unchanged; lbb_inspect action=entity iri=<full IRI> reads any subject with records/1. The following convention applies only to native records: 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 explicit entailment=subclass, rdfs, or owl for inference (default none). 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. Search by meaning inside the query (the graph needs an embedding; see lbb_embeddings action=list): PREFIX search: <https://littlebigbrain.com/search#> SELECT ?x ?score WHERE { ?x search:similarTo "card payments" ; search:score ?score . ?x <https://littlebigbrain.com/r/calls> ?y } ORDER BY DESC(?score) LIMIT 5. The other patterns filter and join the hits. One search:similarTo per query, with a variable subject, in the main group (not inside OPTIONAL, UNION, MINUS or a subquery). The object is text, an entity IRI (records like that one) or a vector literal. search:top N sets the hits (1 to 1000; default the query LIMIT, then 10) and search:embedding "name" searches one embedding. FILTER(?score > 0.8) keeps close hits. The result's search field reports the plan (nearest, filter_first or search_first), the hits and complete. For a question in words, add search:rerank true ; search:relevance ?r and ORDER BY DESC(?r): the rerank model (Jev) reads the best hits with the text, keeps the top that answer it and binds its answer (0 to 1); search.rerank reports the status. Only a text query reranks. compare: the SELECT to run at both points; leave out LIMIT.
routeNoquestion: the kind of question, when you know it. Omit it and the router model picks it.
top_kNo
anchorNoquestion: entity IRIs the question is about, at most 10 (for example the record the user has open). The server reads each one, and its queries use these IRIs directly instead of matching their names.
beforeNocompare: the earlier point (required): {"as_of_commit_seq": n}, {"date": "YYYY-MM-DD"} or {"moment": "RFC 3339"}.
cursorNoOpaque cursor from a previous lbb_query row page; reruns the original query at the next offset.
detailNoResponse detail level. Defaults to compact.
filterNoConditions every hit must meet: {"class": …} or {"via": …, "to": …}. The response's filter shows how each condition resolved; an unknown relationship or name answers with the options.
metricNo
momentNocommit_at: RFC 3339, e.g. 2026-06-18T12:00:00Z; the last commit written at or before it.
rerankNotrue: the managed rerank model (Jev) orders the best hits by how well each answers the text, and each hit gets its relevance (0 to 1); adds about 0.3 s. false: the similarity order. Omit it to follow the graph's search setting.
sparqlNo
classesNodescribe: class IRIs to describe, at most 20.
contextNoquestion: notes for the model, at most 8,000 characters: what the data means, units, names to prefer.
explainNoPlan without running: the scope, the resolved filter, and the allowed count. No model call.
includeNotext: return the embedded text of each hit.
requestNoThe user's own words behind this query (managed evals). When present the server records an eval trace and the result carries its trace_id; label the rows valid or not with lbb_evals action=label. Omit on continuation pages.
max_rowsNocompare: rows to read per point, 1 to 50,000 (default 20,000). Past it the result says truncated.
questionNoquestion: the question in plain words (required), 1 to 4,000 characters. The server's model runs queries on this graph, reads their rows, and answers. describe: the question whose classes and properties to describe.
timelineNoquestion: dated points that stand for the graph's commits, at most 200. A question about a date reads the commit of the latest point on or before it. Use it when the commits stand for other dates than the days they were written (a demo's milestones, an import of old records); without it the server reads the last commit written by the end of that day.
embeddingNoOne embedding by name (see lbb_embeddings action=list). Omit it to search every searchable class of the graph; each hit names its class.
row_limitNoMaximum query rows to return in this page. Defaults by detail: compact=20, standard=100, full=1000.
entailmentNoReasoning over the pinned RDF generation. Defaults to none. owl includes RDFS, inverse relationships and the supported OWL profile.
propertiesNodescribe: property IRIs to describe, at most 50.
consistencyNoRead consistency. strong requires publication through head; a pending response is retryable.
compare_cursorNocompare: the cursor of the page before, from next; pass it with the same arguments.
min_indexed_seqNoRead-after-write publication floor. Preserved across cursor pages.
as_of_commit_seqNoSnapshot 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).

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed21 schema fields changedv0.16.0
    • changedInput schema / additionalProperties
      Previous value: -trueNew value: +{}
    • removedInput schema / properties / after / properties / as_of_commit_seq / $ref
      Removed value: -"#/properties/before/properties/as_of_commit_seq"
    • addedInput schema / properties / after / properties / as_of_commit_seq / maximum
      Added value: +9007199254740991
    • addedInput schema / properties / after / properties / as_of_commit_seq / minimum
      Added value: +0
    • addedInput schema / properties / after / properties / as_of_commit_seq / type
      Added value: +"integer"
    • removedInput schema / properties / after / properties / date / $ref
      Removed value: -"#/properties/before/properties/date"
    • addedInput schema / properties / after / properties / date / pattern
      Added value: +"^\\d{4}-\\d{2}-\\d{2}$"
    • addedInput schema / properties / after / properties / date / type
      Added value: +"string"
    • removedInput schema / properties / after / properties / moment / $ref
      Removed value: -"#/properties/before/properties/moment"
    • addedInput schema / properties / after / properties / moment / type
      Added value: +"string"
    • addedInput schema / properties / as_of_commit_seq / maximum
      Added value: +9007199254740991
    • addedInput schema / properties / before / properties / as_of_commit_seq / maximum
      Added value: +9007199254740991
    • addedInput schema / properties / body / propertyNames
      Added value: +{
      +  "type": "string"
      +}
    • changedInput schema / properties / min_indexed_seq / minimum
      Previous value: --9007199254740991New value: +0
    • addedInput schema / properties / probe / maximum
      Added value: +9007199254740991
    • changedInput schema / properties / query / description
      Previous value: -"SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. 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 explicit entailment=subclass, rdfs, or owl for inference (default none). 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. Search by meaning inside the query (the graph needs an embedding; see lbb_embeddings action=list): PREFIX search: <https://littlebigbrain.com/search#> SELECT ?x ?score WHERE { ?x search:similarTo \"card payments\" ; search:score ?score . ?x <https://littlebigbrain.com/r/calls> ?y } ORDER BY DESC(?score) LIMIT 5. The other patterns filter and join the hits. One search:similarTo per query, with a variable subject, in the main group (not inside OPTIONAL, UNION, MINUS or a subquery). The object is text, an entity IRI (records like that one) or a vector literal. search:top N sets the hits (1 to 1000; default the query LIMIT, then 10) and search:embedding \"name\" searches one embedding. FILTER(?score > 0.8) keeps close hits. The result's search field reports the plan (nearest, filter_first or search_first), the hits and complete. For a question in words, add search:rerank true ; search:relevance ?r and ORDER BY DESC(?r): the rerank model (Jev) reads the best hits with the text, keeps the top that answer it and binds its answer (0 to 1); search.rerank reports the status. Only a text query reranks. compare: the SELECT to run at both points; leave out LIMIT."New value: +"SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. Imported RDF retains its exact, case-sensitive IRIs. Use returned IRIs unchanged; lbb_inspect action=entity iri=<full IRI> reads any subject with records/1. The following convention applies only to native records: 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 explicit entailment=subclass, rdfs, or owl for inference (default none). 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. Search by meaning inside the query (the graph needs an embedding; see lbb_embeddings action=list): PREFIX search: <https://littlebigbrain.com/search#> SELECT ?x ?score WHERE { ?x search:similarTo \"card payments\" ; search:score ?score . ?x <https://littlebigbrain.com/r/calls> ?y } ORDER BY DESC(?score) LIMIT 5. The other patterns filter and join the hits. One search:similarTo per query, with a variable subject, in the main group (not inside OPTIONAL, UNION, MINUS or a subquery). The object is text, an entity IRI (records like that one) or a vector literal. search:top N sets the hits (1 to 1000; default the query LIMIT, then 10) and search:embedding \"name\" searches one embedding. FILTER(?score > 0.8) keeps close hits. The result's search field reports the plan (nearest, filter_first or search_first), the hits and complete. For a question in words, add search:rerank true ; search:relevance ?r and ORDER BY DESC(?r): the rerank model (Jev) reads the best hits with the text, keeps the top that answer it and binds its answer (0 to 1); search.rerank reports the status. Only a text query reranks. compare: the SELECT to run at both points; leave out LIMIT."
    • removedInput schema / properties / sparql / $ref
      Removed value: -"#/properties/body"
    • addedInput schema / properties / sparql / additionalProperties
      Added value: +{}
    • addedInput schema / properties / sparql / propertyNames
      Added value: +{
      +  "type": "string"
      +}
    • addedInput schema / properties / sparql / type
      Added value: +"object"
    • addedInput schema / properties / timeline / items / properties / as_of_commit_seq / maximum
      Added value: +9007199254740991
  2. Changed18 schema fields changedv0.14.0
    • addedInput schema / properties / after
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "compare: the later point; defaults to the latest commit.",
      +  "properties": {
      +    "as_of_commit_seq": {
      +      "$ref": "#/properties/before/properties/as_of_commit_seq"
      +    },
      +    "date": {
      +      "$ref": "#/properties/before/properties/date"
      +    },
      +    "moment": {
      +      "$ref": "#/properties/before/properties/moment"
      +    }
      +  },
      +  "type": "object"
      +}
    • changedInput schema / properties / anchor / description
      Previous value: -"question: entity IRIs the question is about, at most 10 (for example the record the user has open). The server reads each one, and the query uses these IRIs directly instead of matching their names."New value: +"question: entity IRIs the question is about, at most 10 (for example the record the user has open). The server reads each one, and its queries use these IRIs directly instead of matching their names."
    • addedInput schema / properties / before
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "compare: the earlier point (required): {\"as_of_commit_seq\": n}, {\"date\": \"YYYY-MM-DD\"} or {\"moment\": \"RFC 3339\"}.",
      +  "properties": {
      +    "as_of_commit_seq": {
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "date": {
      +      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      +      "type": "string"
      +    },
      +    "moment": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / classes
      Added value: +{
      +  "description": "describe: class IRIs to describe, at most 20.",
      +  "items": {
      +    "minLength": 1,
      +    "type": "string"
      +  },
      +  "maxItems": 20,
      +  "type": "array"
      +}
    • addedInput schema / properties / compare_cursor
      Added value: +{
      +  "description": "compare: the cursor of the page before, from next; pass it with the same arguments.",
      +  "type": "string"
      +}
    • addedInput schema / properties / date
      Added value: +{
      +  "description": "commit_at: YYYY-MM-DD; the last commit written by the end of that day (UTC).",
      +  "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      +  "type": "string"
      +}
    • addedInput schema / properties / key
      Added value: +{
      +  "description": "compare: the variables that identify a row's entity, e.g. [\"contact\"]. With a key the rows are paired: added and removed hold the entities at one point only, changed the entities whose values differ. Without one whole rows are compared.",
      +  "items": {
      +    "minLength": 1,
      +    "type": "string"
      +  },
      +  "maxItems": 8,
      +  "type": "array"
      +}
    • changedInput schema / properties / limit / description
      Previous value: -"question: rows the run returns, 1 to 1,000. Defaults by detail: compact=20, standard=100, full=1000."New value: +"question: rows each query of the server's loop returns, 1 to 1,000. Defaults by detail: compact=20, standard=100, full=1000. names: candidates per name, 1 to 10 (default 5). compare: entries of each list per page, 1 to 1,000 (defaults by detail: 20, 100, 500)."
    • addedInput schema / properties / max_rows
      Added value: +{
      +  "description": "compare: rows to read per point, 1 to 50,000 (default 20,000). Past it the result says truncated.",
      +  "maximum": 50000,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • changedInput schema / properties / mode / description
      Previous value: -"Selects the variant (one of: structured, sparql, question, search, analyze)."New value: +"Selects the variant (one of: structured, sparql, question, search, analyze, names, describe, commit_at, compare)."
    • changedInput schema / properties / mode / enum
      Previous value: -[
      -  "structured",
      -  "sparql",
      -  "question",
      -  "search",
      -  "analyze"
      -]New value: +[
      +  "structured",
      +  "sparql",
      +  "question",
      +  "search",
      +  "analyze",
      +  "names",
      +  "describe",
      +  "commit_at",
      +  "compare"
      +]
    • addedInput schema / properties / moment
      Added value: +{
      +  "description": "commit_at: RFC 3339, e.g. 2026-06-18T12:00:00Z; the last commit written at or before it.",
      +  "type": "string"
      +}
    • addedInput schema / properties / properties
      Added value: +{
      +  "description": "describe: property IRIs to describe, at most 50.",
      +  "items": {
      +    "minLength": 1,
      +    "type": "string"
      +  },
      +  "maxItems": 50,
      +  "type": "array"
      +}
    • changedInput schema / properties / query / description
      Previous value: -"SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. 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 explicit entailment=subclass, rdfs, or owl for inference (default none). 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. Search by meaning inside the query (the graph needs an embedding; see lbb_embeddings action=list): PREFIX search: <https://littlebigbrain.com/search#> SELECT ?x ?score WHERE { ?x search:similarTo \"card payments\" ; search:score ?score . ?x <https://littlebigbrain.com/r/calls> ?y } ORDER BY DESC(?score) LIMIT 5. The other patterns filter and join the hits. One search:similarTo per query, with a variable subject, in the main group (not inside OPTIONAL, UNION, MINUS or a subquery). The object is text, an entity IRI (records like that one) or a vector literal. search:top N sets the hits (1 to 1000; default the query LIMIT, then 10) and search:embedding \"name\" searches one embedding. FILTER(?score > 0.8) keeps close hits. The result's search field reports the plan (nearest, filter_first or search_first), the hits and complete. For a question in words, add search:rerank true ; search:relevance ?r and ORDER BY DESC(?r): the rerank model (Jev) reads the best hits with the text, keeps the top that answer it and binds its answer (0 to 1); search.rerank reports the status. Only a text query reranks."New value: +"SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. 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 explicit entailment=subclass, rdfs, or owl for inference (default none). 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. Search by meaning inside the query (the graph needs an embedding; see lbb_embeddings action=list): PREFIX search: <https://littlebigbrain.com/search#> SELECT ?x ?score WHERE { ?x search:similarTo \"card payments\" ; search:score ?score . ?x <https://littlebigbrain.com/r/calls> ?y } ORDER BY DESC(?score) LIMIT 5. The other patterns filter and join the hits. One search:similarTo per query, with a variable subject, in the main group (not inside OPTIONAL, UNION, MINUS or a subquery). The object is text, an entity IRI (records like that one) or a vector literal. search:top N sets the hits (1 to 1000; default the query LIMIT, then 10) and search:embedding \"name\" searches one embedding. FILTER(?score > 0.8) keeps close hits. The result's search field reports the plan (nearest, filter_first or search_first), the hits and complete. For a question in words, add search:rerank true ; search:relevance ?r and ORDER BY DESC(?r): the rerank model (Jev) reads the best hits with the text, keeps the top that answer it and binds its answer (0 to 1); search.rerank reports the status. Only a text query reranks. compare: the SELECT to run at both points; leave out LIMIT."
    • changedInput schema / properties / question / description
      Previous value: -"question: the question in plain words (required), 1 to 4,000 characters. The server writes the SPARQL query from a description of this graph, checks it, and runs it."New value: +"question: the question in plain words (required), 1 to 4,000 characters. The server's model runs queries on this graph, reads their rows, and answers. describe: the question whose classes and properties to describe."
    • changedInput schema / properties / route / description
      Previous value: -"question: the kind of query, when you know it. Omit it and the router model selects it."New value: +"question: the kind of question, when you know it. Omit it and the router model picks it."
    • removedInput schema / properties / run
      Removed value: -{
      -  "description": "question: run the query and return its rows. Defaults to true; false returns the query only.",
      -  "type": "boolean"
      -}
    • changedInput schema / properties / text / description
      Previous value: -"The query text (required); embedded with the graph's model."New value: +"search: the query text (required); embedded with the graph's model. names: the question, or the names, to find (required)."
  3. Changed11 schema fields changedv0.9.0
    • addedInput schema / properties / anchor
      Added value: +{
      +  "description": "question: entity IRIs the question is about, at most 10 (for example the record the user has open). The server reads each one, and the query uses these IRIs directly instead of matching their names.",
      +  "items": {
      +    "maxLength": 2000,
      +    "minLength": 1,
      +    "type": "string"
      +  },
      +  "maxItems": 10,
      +  "type": "array"
      +}
    • addedInput schema / properties / context
      Added value: +{
      +  "description": "question: notes for the model, at most 8,000 characters: what the data means, units, names to prefer.",
      +  "type": "string"
      +}
    • addedInput schema / properties / limit
      Added value: +{
      +  "description": "question: rows the run returns, 1 to 1,000. Defaults by detail: compact=20, standard=100, full=1000.",
      +  "maximum": 1000,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • changedInput schema / properties / mode / description
      Previous value: -"Selects the variant (one of: structured, sparql, search, analyze)."New value: +"Selects the variant (one of: structured, sparql, question, search, analyze)."
    • changedInput schema / properties / mode / enum
      Previous value: -[
      -  "structured",
      -  "sparql",
      -  "search",
      -  "analyze"
      -]New value: +[
      +  "structured",
      +  "sparql",
      +  "question",
      +  "search",
      +  "analyze"
      +]
    • changedInput schema / properties / query / description
      Previous value: -"SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. 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 explicit entailment=subclass, rdfs, or owl for inference (default none). 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"New value: +"SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. 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 explicit entailment=subclass, rdfs, or owl for inference (default none). 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. Search by meaning inside the query (the graph needs an embedding; see lbb_embeddings action=list): PREFIX search: <https://littlebigbrain.com/search#> SELECT ?x ?score WHERE { ?x search:similarTo \"card payments\" ; search:score ?score . ?x <https://littlebigbrain.com/r/calls> ?y } ORDER BY DESC(?score) LIMIT 5. The other patterns filter and join the hits. One search:similarTo per query, with a variable subject, in the main group (not inside OPTIONAL, UNION, MINUS or a subquery). The object is text, an entity IRI (records like that one) or a vector literal. search:top N sets the hits (1 to 1000; default the query LIMIT, then 10) and search:embedding \"name\" searches one embedding. FILTER(?score > 0.8) keeps close hits. The result's search field reports the plan (nearest, filter_first or search_first), the hits and complete. For a question in words, add search:rerank true ; search:relevance ?r and ORDER BY DESC(?r): the rerank model (Jev) reads the best hits with the text, keeps the top that answer it and binds its answer (0 to 1); search.rerank reports the status. Only a text query reranks."
    • addedInput schema / properties / question
      Added value: +{
      +  "description": "question: the question in plain words (required), 1 to 4,000 characters. The server writes the SPARQL query from a description of this graph, checks it, and runs it.",
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / rerank
      Added value: +{
      +  "description": "true: the managed rerank model (Jev) orders the best hits by how well each answers the text, and each hit gets its relevance (0 to 1); adds about 0.3 s. false: the similarity order. Omit it to follow the graph's search setting.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / route
      Added value: +{
      +  "description": "question: the kind of query, when you know it. Omit it and the router model selects it.",
      +  "enum": [
      +    "lookup",
      +    "aggregate",
      +    "search",
      +    "history",
      +    "schema",
      +    "unanswerable"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / run
      Added value: +{
      +  "description": "question: run the query and return its rows. Defaults to true; false returns the query only.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / timeline
      Added value: +{
      +  "description": "question: dated points that stand for the graph's commits, at most 200. A question about a date reads the commit of the latest point on or before it. Use it when the commits stand for other dates than the days they were written (a demo's milestones, an import of old records); without it the server reads the last commit written by the end of that day.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "as_of_commit_seq": {
      +        "description": "The commit that holds the graph as it was on that date.",
      +        "minimum": 0,
      +        "type": "integer"
      +      },
      +      "date": {
      +        "description": "YYYY-MM-DD.",
      +        "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      +        "type": "string"
      +      },
      +      "label": {
      +        "description": "A name for the point, such as a milestone.",
      +        "maxLength": 200,
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "date",
      +      "as_of_commit_seq"
      +    ],
      +    "type": "object"
      +  },
      +  "maxItems": 200,
      +  "type": "array"
      +}
  4. Changed11 schema fields changedv0.5.2
    • removedInput schema / properties / branch
      Removed value: -{
      -  "description": "Branch to target; defaults to the connection's branch",
      -  "type": "string"
      -}
    • addedInput schema / properties / embedding
      Added value: +{
      +  "description": "One embedding by name (see lbb_embeddings action=list). Omit it to search every searchable class of the graph; each hit names its class.",
      +  "type": "string"
      +}
    • addedInput schema / properties / explain
      Added value: +{
      +  "description": "Plan without running: the scope, the resolved filter, and the allowed count. No model call.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / filter
      Added value: +{
      +  "description": "Conditions every hit must meet: {\"class\": …} or {\"via\": …, \"to\": …}. The response's filter shows how each condition resolved; an unknown relationship or name answers with the options.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "class": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "items": {
      +              "type": "string"
      +            },
      +            "type": "array"
      +          }
      +        ],
      +        "description": "A class condition: one class IRI or a list (any of); subclasses too. One class condition per search."
      +      },
      +      "direction": {
      +        "description": "With via: out (default), the hit links to the entity; in, the entity links to the hit.",
      +        "enum": [
      +          "out",
      +          "in"
      +        ],
      +        "type": "string"
      +      },
      +      "to": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "items": {
      +              "type": "string"
      +            },
      +            "type": "array"
      +          }
      +        ],
      +        "description": "With via: the entity, an IRI or a name (its label; a spelling slip resolves when one entity is clearly closest), or a list (any of)."
      +      },
      +      "via": {
      +        "description": "A relationship condition: the relationship, a local name (calls), prefix:name, or <iri>.",
      +        "type": "string"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • addedInput schema / properties / include
      Added value: +{
      +  "description": "text: return the embedded text of each hit.",
      +  "items": {
      +    "enum": [
      +      "text"
      +    ],
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • changedInput schema / properties / mode / description
      Previous value: -"Selects the variant (one of: structured, sparql, analyze)."New value: +"Selects the variant (one of: structured, sparql, search, analyze)."
    • changedInput schema / properties / mode / enum
      Previous value: -[
      -  "structured",
      -  "sparql",
      -  "analyze"
      -]New value: +[
      +  "structured",
      +  "sparql",
      +  "search",
      +  "analyze"
      +]
    • addedInput schema / properties / probe
      Added value: +{
      +  "description": "Clusters to read (default 4·√clusters, at least 8). More reads cost latency and find more.",
      +  "exclusiveMinimum": 0,
      +  "type": "integer"
      +}
    • addedInput schema / properties / request
      Added value: +{
      +  "description": "The user's own words behind this query (managed evals). When present the server records an eval trace and the result carries its trace_id; label the rows valid or not with lbb_evals action=label. Omit on continuation pages.",
      +  "type": "string"
      +}
    • addedInput schema / properties / text
      Added value: +{
      +  "description": "The query text (required); embedded with the graph's model.",
      +  "type": "string"
      +}
    • addedInput schema / properties / top_k / maximum
      Added value: +200
  5. Changed6 schema fields changedv0.5.1
    • changedInput schema / properties / as_of / description
      Previous value: -"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."New value: +"Unsupported in structured and SPARQL text modes; use as_of_commit_seq for a retained commit snapshot."
    • changedInput schema / properties / body / description
      Previous value: -"Structured SPARQL-subset 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 } } } }). A `combinators` key (UNION/OPTIONAL/MINUS/EXISTS) is rejected here; express those with SPARQL text under mode=sparql. 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`)."New value: +"Structured SPARQL-subset 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 } } } }). A `combinators` key (UNION/OPTIONAL/MINUS/EXISTS) is rejected here; express those with SPARQL text under mode=sparql. 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 use the top-level `as_of_commit_seq` argument or the same body field. Valid-time `as_of` and `as_of_valid_time` selectors are unsupported and rejected before HTTP."
    • addedInput schema / properties / consistency
      Added value: +{
      +  "description": "Read consistency. strong requires publication through head; a pending response is retryable.",
      +  "enum": [
      +    "eventual",
      +    "strong"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / entailment
      Added value: +{
      +  "description": "Reasoning over the pinned RDF generation. Defaults to none. owl includes RDFS, inverse relationships and the supported OWL profile.",
      +  "enum": [
      +    "none",
      +    "subclass",
      +    "rdfs",
      +    "owl"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / min_indexed_seq
      Added value: +{
      +  "description": "Read-after-write publication floor. Preserved across cursor pages.",
      +  "maximum": 9007199254740991,
      +  "minimum": -9007199254740991,
      +  "type": "integer"
      +}
    • changedInput schema / properties / query / description
      Previous value: -"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"New value: +"SPARQL 1.1 query text (SELECT or ASK). Valid-time as_of is unsupported; use as_of_commit_seq for a retained commit snapshot. 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 explicit entailment=subclass, rdfs, or owl for inference (default none). 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"
  6. Changed2 schema fields changedv0.4.2
    • changedInput schema / properties / body / description
      Previous value: -"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`)."New value: +"Structured SPARQL-subset 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 } } } }). A `combinators` key (UNION/OPTIONAL/MINUS/EXISTS) is rejected here; express those with SPARQL text under mode=sparql. 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`)."
    • changedInput schema / properties / metric / enum
      Previous value: -[
      -  "entity_types",
      -  "relations",
      -  "overview",
      -  "facets",
      -  "sparql"
      -]New value: +[
      +  "entity_types",
      +  "relations",
      +  "overview",
      +  "sparql"
      +]
  7. Changed16 schema fields changedv0.2.7
    • removedInput schema / properties / anchor_name
      Removed value: -{
      -  "type": "string"
      -}
    • removedInput schema / properties / anchor_type
      Removed value: -{
      -  "type": "string"
      -}
    • removedInput schema / properties / explain
      Removed value: -{
      -  "type": "boolean"
      -}
    • removedInput schema / properties / include_derived
      Removed value: -{
      -  "type": "boolean"
      -}
    • removedInput schema / properties / max_derived
      Removed value: -{
      -  "exclusiveMinimum": 0,
      -  "type": "integer"
      -}
    • removedInput schema / properties / max_premises
      Removed value: -{
      -  "exclusiveMinimum": 0,
      -  "type": "integer"
      -}
    • removedInput schema / properties / max_rounds
      Removed value: -{
      -  "exclusiveMinimum": 0,
      -  "type": "integer"
      -}
    • removedInput schema / properties / max_solutions
      Removed value: -{
      -  "exclusiveMinimum": 0,
      -  "type": "integer"
      -}
    • changedInput schema / properties / mode / description
      Previous value: -"Selects the variant (one of: structured, sparql, shacl, infer, retrieval_premises, analyze)."New value: +"Selects the variant (one of: structured, sparql, analyze)."
    • changedInput schema / properties / mode / enum
      Previous value: -[
      -  "structured",
      -  "sparql",
      -  "shacl",
      -  "infer",
      -  "retrieval_premises",
      -  "analyze"
      -]New value: +[
      +  "structured",
      +  "sparql",
      +  "analyze"
      +]
    • removedInput schema / properties / query_top_k
      Removed value: -{
      -  "exclusiveMinimum": 0,
      -  "type": "integer"
      -}
    • removedInput schema / properties / relation
      Removed value: -{
      -  "type": "string"
      -}
    • removedInput schema / properties / rules
      Removed value: -{
      -  "items": {
      -    "additionalProperties": true,
      -    "properties": {
      -      "body": {
      -        "description": "The condition: a basic graph pattern over current edges (asserted + already-derived), joined on shared variables. Terms may be variables or fixed entities.",
      -        "items": {
      -          "additionalProperties": true,
      -          "properties": {
      -            "object": {
      -              "$ref": "#/properties/rules/items/properties/body/items/properties/subject",
      -              "description": "A rule term: { \"var\": \"x\" } (a variable) or { \"entity\": { \"entity_type\": \"DeliveryStatus\", \"name\": \"Complete\" } } (a fixed entity used as a constant in the body or head)"
      -            },
      -            "predicate": {
      -              "description": "Relation name. In a rule body, the reserved \"rdf:type\" makes a type-membership constraint: the object names a class ({ entity: { entity_type: \"Contact\" } }, no name) and matches every entity of that class and its subtypes (rdfs:subClassOf closure), so one rule keyed on a supertype fires for all subtypes. Not allowed in a head or an exists/not_exists filter.",
      -              "type": "string"
      -            },
      -            "subject": {
      -              "anyOf": [
      -                {
      -                  "additionalProperties": true,
      -                  "properties": {
      -                    "var": {
      -                      "description": "A variable, joined across patterns by name",
      -                      "type": "string"
      -                    }
      -                  },
      -                  "required": [
      -                    "var"
      -                  ],
      -                  "type": "object"
      -                },
      -                {
      -                  "additionalProperties": true,
      -                  "properties": {
      -                    "entity": {
      -                      "additionalProperties": true,
      -                      "properties": {
      -                        "entity_id": {
      -                          "description": "Entity id (hex), as an alternative to type+name",
      -                          "type": "string"
      -                        },
      -                        "entity_type": {
      -                          "description": "Entity type name (with `name`, names a fixed entity)",
      -                          "type": "string"
      -                        },
      -                        "name": {
      -                          "description": "Entity name (paired with `entity_type`)",
      -                          "type": "string"
      -                        }
      -                      },
      -                      "type": "object"
      -                    }
      -                  },
      -                  "required": [
      -                    "entity"
      -                  ],
      -                  "type": "object"
      -                }
      -              ],
      -              "description": "A rule term: { \"var\": \"x\" } (a variable) or { \"entity\": { \"entity_type\": \"DeliveryStatus\", \"name\": \"Complete\" } } (a fixed entity used as a constant in the body or head)"
      -            }
      -          },
      -          "type": "object"
      -        },
      -        "type": "array"
      -      },
      -      "combinators": {
      -        "description": "exists/not_exists filters folded over the body. not_exists is stratified negation — it lets a rule express a universal condition, e.g. derive \"phase complete\" only when not_exists an incomplete deliverable. A negation cycle is rejected.",
      -        "items": {
      -          "anyOf": [
      -            {
      -              "additionalProperties": true,
      -              "properties": {
      -                "exists": {
      -                  "items": {
      -                    "$ref": "#/properties/rules/items/properties/body/items"
      -                  },
      -                  "type": "array"
      -                }
      -              },
      -              "required": [
      -                "exists"
      -              ],
      -              "type": "object"
      -            },
      -            {
      -              "additionalProperties": true,
      -              "properties": {
      -                "not_exists": {
      -                  "items": {
      -                    "$ref": "#/properties/rules/items/properties/body/items"
      -                  },
      -                  "type": "array"
      -                }
      -              },
      -              "required": [
      -                "not_exists"
      -              ],
      -              "type": "object"
      -            }
      -          ],
      -          "description": "An existence filter over the body solutions: { exists: [...] } (semijoin — keep rows with a compatible match) or { not_exists: [...] } (negation/antijoin — keep rows with none)"
      -        },
      -        "type": "array"
      -      },
      -      "head": {
      -        "$ref": "#/properties/rules/items/properties/body/items",
      -        "description": "The triple derived once per body solution. Every head variable must be bound by the body; a fixed-entity object derives a constant (e.g. set the rolled-up status to the Complete entity)."
      -      },
      -      "name": {
      -        "type": "string"
      -      },
      -      "order": {
      -        "description": "Run order, low to high (a determinism hint)",
      -        "type": "integer"
      -      }
      -    },
      -    "required": [
      -      "name"
      -    ],
      -    "type": "object"
      -  },
      -  "type": "array"
      -}
    • removedInput schema / properties / shacl_mode
      Removed value: -{
      -  "description": "select returns focus nodes; validate returns a report",
      -  "enum": [
      -    "select",
      -    "validate"
      -  ],
      -  "type": "string"
      -}
    • removedInput schema / properties / shapes
      Removed value: -{
      -  "items": {
      -    "$ref": "#/properties/body"
      -  },
      -  "type": "array"
      -}
    • removedInput schema / properties / threshold
      Removed value: -{
      -  "maximum": 1,
      -  "minimum": 0,
      -  "type": "number"
      -}
  8. First observedv0.2.1

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond readOnlyHint=true, the description discloses important behavior: question mode runs model-backed queries in a bounded loop, takes about 8 seconds, consumes model tokens against a daily limit, and returns answers with citations, steps, route, and query rows. It also notes rerank latency, snapshot pinning semantics, consistency behavior, and that structured/text queries pin one published watermark.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the mode list and then provides substantial useful detail, but it becomes a dense, run-on block with repeated guidance about IRIs, search, and planning. For a 40-parameter tool some length is justified, yet several sentences restate schema-level conventions and could be moved into the schema or omitted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, nine modes, nested body shape, and lack of an output schema, the description is complete enough to use correctly. It explains result fields for question, search, compare, and pagination, plus snapshot, consistency, model-cost, and IRI-handling behaviors that an agent needs before calling it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is already high at 88%, so the baseline is 3. The description adds meaningful mode-level semantics and cross-parameter workflow guidance for names, describe, commit_at, compare, filter usage in search, as_of_commit_seq, and search planning. Some parameters such as graph, chart, field, metric, and top_k are not explained in the description, leaving a small gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description enumerates every mode with a specific verb and resource: question answering, structured SPARQL-subset queries, SPARQL text, semantic search, analysis, name lookup, class/property description, commit lookup, and comparison. It also explicitly distinguishes itself from siblings such as lbb_inspect, lbb_embeddings, and lbb_evals. An agent can identify both the tool's broad role and each mode's purpose without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit routing guidance: use mode=question when you have plain words and no SPARQL, continue or correct with mode=sparql, call names before a query that names an entity, call describe before querying unseen classes/properties, use commit_at for date questions, and use compare for before/after changes. It also names alternatives and prerequisites, such as lbb_embeddings action=list for search planning and lbb_inspect action=guide for query examples.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.