Skip to main content
Glama
littlebigbrains

@littlebigbrain/mcp

@littlebigbrain/mcp

Eight task-shaped MCP tools that let Claude, Cursor, Codex, or any MCP client search, query, and write a Little Big Brain graph. Ships two ways: a hosted endpoint with OAuth sign-in, and a local stdio server.

The client opens WorkOS sign-in; your machine never stores a Little Big Brain key. Point it at your stack:

{
  "mcpServers": {
    "lbb": {
      "url": "https://mcp.littlebigbrain.com/mcp/<stack-slug>"
    }
  }
}

Codex sends the URL as an OAuth resource, so use the origin plus a stack header instead:

{
  "mcpServers": {
    "lbb": {
      "type": "http",
      "url": "https://mcp.littlebigbrain.com",
      "headers": { "X-LBB-Stack": "<stack-slug>" }
    }
  }
}

Related MCP server: RDF4J MCP Server

Local (stdio)

Run against any data-plane endpoint with a stack API key:

{
  "mcpServers": {
    "lbb": {
      "command": "npx",
      "args": ["-y", "@littlebigbrain/mcp"],
      "env": {
        "LBB_BASE_URL": "https://0abc1def--production.db.eu.littlebigbrain.com",
        "LBB_API_KEY": "lbb_sk_live_..."
      }
    }
  }
}

Set LBB_GRAPH or LBB_BRANCH to target a scope other than main. LBB_BASE_URL has no hosted default: copy endpoint_url from the stack's Connect page. The MCP process exits with a configuration error when it is missing.

Tools

Tool

Use it for

lbb_inspect

graph discovery, complete paginated ontology/schema, publication status, entity, state, history, and provenance

lbb_rdf

import full RDF/OWL or add axioms using INSERT DATA

lbb_query

SPARQL text, structured SPARQL bodies, and canned analysis

lbb_commit

facts, properties, and embeddings

lbb_observe

conversation episodes plus reviewed extraction

lbb_branch

isolation branches and validated merge

lbb_models

shadow evaluation and training datasets

lbb_configure

native ontology definition/evolution and SHACL preview/publication

Read tools return compact structured envelopes by default — use detail, row_limit, and returned cursors to page without silently truncating. Write tools derive an idempotency key unless you provide one.

Query pages preserve complete RDF values and may contain fewer than row_limit rows to fit the 80 KB UTF-8 text budget. Query text uses compact JSON, with the same complete data in structuredContent. The full transport response includes both representations and can exceed 80 KB. Follow the returned next arguments until absent; the cursor advances by rows actually delivered. A single row that exceeds the budget fails explicitly: project fewer fields or use the direct SPARQL HTTP endpoint for that row.

Both lbb_query SPARQL modes (sparql and structured) support retained commit reads through as_of_commit_seq. When omitted, the connector pins the current head commit and reuses it for cursor pages. Valid-time as_of is unsupported and is rejected before an API call, including when carried in an old cursor. Start a new query without that selector or choose a retained commit sequence.

Create and evolve an ontology through MCP

Start with lbb_inspect action=guide; action=graphs helps select an existing scope, and a missing graph returns bootstrap guidance. Decide what questions the graph must answer before choosing classes and relations. Distinguish source-backed facts from hypotheses, and preserve evidence and dates.

Native metadata and stored RDF axioms are separate:

  • lbb_configure action=define_ontology accepts a friendly spec, including class super_types. Unknown spec fields fail explicitly. lbb_json expects an internal serialized ontology, not a friendly spec. Raw OWL supplied to configure is reduced to native metadata; it is not stored as a full document.

  • lbb_rdf action=import stores the complete Turtle, N-Triples, N-Quads, or TriG document as queryable graph facts, including RDF lists, annotations, and OWL axioms. Pass source; the published RDF tier supports only the default RDF graph. Dataset formats must contain only default-graph quads. The first RDF data write selects RDF-native storage, which refuses later property-graph commits; choose the write workflow before bootstrap.

  • lbb_configure action=evolve_ontology supports explicit native changes, including add_super_types. dry_run: true previews define/evolve/publish; the same flag previews lbb_commit mode=facts without writing.

  • lbb_rdf action=update submits SPARQL Update unchanged; currently only INSERT DATA is supported. DELETE, WHERE, and graph replacement are refused. Re-importing is additive and does not remove obsolete axioms. Content-based retry keys are automatic; use a new explicit key for an intentional repeat after other edits.

For example, add a superclass without a browser or RDF conversion:

{
  "action": "update",
  "update": "INSERT DATA { <urn:Person> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <urn:Contact> }"
}

Removing or replacing RDF axioms requires native bounded update support in the engine. Until then, import a revised document into a new versioned LBB graph, verify it, and explicitly switch consumers. Do not implicitly delete the original.

publish_schema activates SHACL shapes against unchanged native metadata. Its preview checks parsing and compatibility without writing objects or scheduling jobs; it does not audit the entire graph. Preview restrictive native edits with evolve, resolve conflicts, then apply. For restrictive SHACL, use warn → inspect conformance → repair → reject.

After applying, inspect action=publication, then verify both asserted axioms and expected inferred answers. lbb_query mode=sparql accepts explicit entailment: "none" | "subclass" | "rdfs" | "owl" (default none), consistency: "eventual" | "strong", and min_indexed_seq. Cursors retain these controls. OWL is the server's supported inference profile, not arbitrary OWL DL. An upload acknowledgement is not proof of successful reasoning.

To read asserted axioms in the default graph, query with entailment: "none" and follow all returned row cursors:

SELECT ?s ?p ?o WHERE {
  ?s ?p ?o
} ORDER BY ?s ?p ?o

lbb_inspect action=ontology and action=schema page complete native metadata with page_size (default 50, maximum 500), optional section, and cursor. Pass the returned next arguments until absent. These pages preserve nested values even with detail=compact; they never replace the remainder with a suggestion to repeat detail=full. If a single entry is too large, the result contains entry_fragment: concatenate serialized_json by char_offset, then JSON-parse the completed entry. Changed metadata invalidates the cursor rather than mixing versions. Restart inspection after applying edits.

Embed the server

For self-hosting behind your own auth, the package also serves the tools over HTTP:

import { createMcpHttpServer } from "@littlebigbrain/mcp";

createMcpHttpServer({
  baseUrl: "https://0abc1def--production.db.eu.littlebigbrain.com",
  mcpPath: "/mcp",
  allowedHosts: ["127.0.0.1", "localhost", "::1"],
}).listen(8080, "127.0.0.1");

The embedded server passes a key bearer to the data plane; the hosted endpoint's OAuth and ownership layer is served separately by the Little Big Brain API.

When using buildLbbServer(client, options) directly, queryTextFormat: "pretty" restores indented query text; the default is "compact". Both formats use the same formatter for page sizing and rendering. Optional timing.observe receives bounded query-stage durations and counts, with no query text or RDF values. No logger is installed by default, including on stdio. Timing observer failures do not affect tool results.

Full tool schemas and examples: docs.littlebigbrain.com/sdks/mcp.

Local end-to-end ontology check

From the repository root, build the server and SDKs, then opt into the isolated real-server MCP test (it creates and removes its own temporary data root):

cargo build -p lbb-server
npm run build -w @littlebigbrain/client
LBB_TEST_SERVER_BIN="$PWD/target/debug/lbb-server" npm test -w @littlebigbrain/mcp

The test verifies native hierarchy evolution, preservation of RDF annotations, subclass/inverse inference, additive edits, and refusal of unsupported deletion.

Available Tools

8 tools
lbb_branchA

Branch lifecycle. Actions: create (fork a new branch off from_branch — the tool's branch argument names the NEW branch) and merge (validate-then-merge: replay from_branch's post-fork commits onto the scoped target branch — its fork parent — as ONE commit with event ids preserved; SHACL-validates the would-be merged state first and refuses with the report on violations; a fact superseded on the target after the fork wins over the branch's version, reported as a supersedure_race conflict; delete_source consumes the merged branch).

ParametersJSON Schema
NameRequiredDescriptionDefault
graphNoGraph to target; defaults to the connection's graph
actionYescreate = fork a new branch; merge = replay a child branch onto its fork parent
branchNoBranch to target; defaults to the connection's branch
validateNomerge only: refuse on SHACL violations of the would-be merged state (default true)
from_branchYescreate: the branch to fork from; merge: the child branch whose commits are replayed
delete_sourceNomerge only: delete every object under the merged branch after success

TDQS

A4.6/5.0
Behavior5/5

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

The description reveals critical behavioral traits: merge replays as one commit, SHACL validation is performed, conflicts are reported, and delete_source is destructive. This goes beyond the annotations which only indicate readOnlyHint=false, destructiveHint=false. No contradiction with annotations.

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

Conciseness4/5

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

The description is concise, with one dense paragraph covering all aspects. It front-loads with 'Branch lifecycle' and then details actions. While packed with information, it remains clear and avoids redundancy, though slightly more structure could improve readability.

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 (6 parameters, 2 required, no output schema), the description provides comprehensive coverage: actions, parameter roles, validation behavior, conflict handling, and destructiveness. It equips an agent to use the tool correctly without missing critical context.

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?

All 6 parameters have schema descriptions (100% coverage). The tool description adds extra context for parameters like 'from_branch' (explains role in create vs. merge) and 'delete_source' (explains it consumes the branch), enhancing understanding beyond the schema definitions.

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 clearly specifies the tool handles the branch lifecycle with two distinct actions ('create' and 'merge'), each explained with sufficient detail. It differentiates from sibling tools by focusing on branching operations, while siblings like lbb_query or lbb_commit serve different purposes.

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

Usage Guidelines4/5

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

The description explains when to use create vs. merge, including specifics like 'merge: replay a child branch onto its fork parent' and 'delete_source consumes the merged branch.' It does not explicitly state when not to use it or mention alternatives, but the context of branching is well-defined.

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

lbb_commitA
Idempotent

Write graph facts, retract them, or label ranked results. mode=facts writes triplets/embeddings/properties; mode=retract removes a wrongly-added fact (by edge or by entity) without a full reset; mode=search_feedback stores query/result relevance labels (Feedback grades: 3=ideal/good, 1=partial, 0=bad; include query, search_id when available, target, rank, score). Explicit idempotency_key wins; when omitted, MCP derives a stable content hash so content-identical retries dedupe. Facts mode defaults edge_idempotency to append; pass skip_unchanged for re-runnable backfills.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNo
graphNoGraph to target; defaults to the connection's graph
branchNoBranch to target; defaults to the connection's branch
dry_runNoValidate a facts commit and return its structured SHACL report without writing. Only supported for mode=facts.
tripletsNo
observed_atNoBackfill timestamp (RFC3339). Records this commit AS OF that instant: stamps transaction time and defaults each triplet's valid_time.start. Replay history in order with observed_at per commit so as-of reads by date work. Omit for live writes.
retract_edgesNomode=retract: specific edges to remove, matched by (source, relation, target).
idempotency_keyNo
search_feedbackNo
edge_idempotencyNoDefaults to append in MCP. Use skip_unchanged for backfills; it skips exact current-edge duplicates and drops evidence-only repeats.
retract_entitiesNomode=retract: entities whose every current edge is removed (a current-state tombstone; the record and its history are kept for as_of reads).
entity_embeddingsNo
entity_propertiesNoTyped scalar attributes per entity. Each item is { type, name, properties }. `properties` is a flat map of field -> value, e.g. { "type": "PERSON", "name": "Ada Lovelace", "properties": { "h_index": 52, "title": "VP", "last_contact": "2026-06-26" } }. Values are coerced to each field's declared type, so a string like "2026-06-26" lands in a date_time field and "52" in an i64 field. (The verbose form [{ field, value: { i64: 52 } }] is also accepted.) Register a field first with lbb_configure evolve_ontology add_property; the commit response echoes written_properties so you can confirm what landed.

TDQS

A4.7/5.0
Behavior5/5

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

Beyond the annotations, the description discloses meaningful behavior: explicit idempotency_key wins, otherwise MCP derives a stable content hash for dedupe, facts mode defaults edge_idempotency to append, and retract removes by edge or entity while avoiding a full reset. This gives an agent a realistic model of side effects and idempotency guarantees before invoking the tool.

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

Conciseness5/5

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

The description is dense but efficient: four sentences front-load the mode taxonomy and then add idempotency/backfill behavior without fluff. This is appropriately concise for a tool with 13 parameters and three distinct operations.

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?

For a complex multi-mode mutation tool with no output schema, the definition covers the mode contract, idempotency rules, feedback grading semantics, and backfill defaults needed to call it correctly. The schema adds detailed parameter descriptions for observed_at, edge_idempotency, retract_entities, and entity_properties, so no critical invocation detail appears missing.

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?

With schema description coverage at 62%, the description compensates by summarizing mode-specific parameter groups and adding hard-to-infer semantics like idempotency_key behavior, edge_idempotency defaults, and the feedback grade scale. It also tells the agent which fields to include for search_feedback (query, search_id, target, rank, score). It does not re-explain every parameter, but the schema fills most of the remaining 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 opens with a precise verb-plus-resource summary: 'Write graph facts, retract them, or label ranked results,' and immediately enumerates the three modes with concrete objects. It clearly identifies lbb_commit as a write/mutation tool, distinguishing it from the read-oriented siblings like lbb_query and lbb_inspect.

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

Usage Guidelines4/5

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

The description gives explicit mode-selection guidance: mode=facts writes triplets/embeddings/properties, mode=retract removes wrongly-added facts without a full reset, and mode=search_feedback stores graded query/result labels. It also provides operational context such as passing skip_unchanged for re-runnable backfills. It does not name sibling tools explicitly, but the mode-level instructions make the intended usage clear.

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

lbb_configureA

Manage native schema metadata. Actions: define_ontology (friendly spec with super_types), evolve_ontology (ordered edits including add_super_types), publish_schema (SHACL activation). All support dry_run previews. Definition/import here extracts native metadata; it does NOT store the complete RDF/OWL document as queryable graph facts. Use lbb_rdf import for full OWL and lbb_rdf update for additive INSERT DATA revisions; RDF deletions are unsupported. Publish_schema accepts unchanged ontology plus shapes; use define/evolve for native ontology changes. Publication enqueues durable conformance; a preview does not validate the whole graph.

ParametersJSON Schema
NameRequiredDescriptionDefault
opsNoOntology changes to apply in order (additive, in-place edits, or subtractive)
graphNoGraph to create or redefine
actionYesSelects the variant (one of: define_ontology, publish_schema, evolve_ontology).
branchNoBranch to target; defaults to the connection's branch
formatNo
shapesNo
sourceNo
dry_runNoPreview the exact definition without creating a graph or writing metadata.
ontologyNo
relationsNo
desired_modeNo
entity_typesNo
merge_defaultNo
confirm_restrictiveNo
allow_data_conflictsNoDeprecated compatibility flag; does not bypass conflicts. Preview subtractive changes with dry_run=true, repair the reported conflicts, then apply.

TDQS

A4.4/5.0
Behavior4/5

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

Annotations only provide readOnlyHint=false and destructiveHint=false, so the description carries most of the behavioral burden. It discloses important traits: dry_run previews exist, the tool does NOT store the full RDF/OWL document, RDF deletions are unsupported, and a preview does not validate the whole graph. It could add more about side effects of tombstoning operations, but the key behavioral caveats are present.

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

Conciseness5/5

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

The description is dense but well-structured: purpose first, then actions, then critical caveats and sibling routing. Every sentence earns its place, and there is no filler or repetition of schema content.

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

Completeness4/5

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

This is a high-complexity tool with 15 parameters, nested objects, and no output schema, yet the description covers the core invocation decisions and major pitfalls: not storing RDF facts, unsupported RDF deletions, preview limitations, and publication semantics. It does not describe return values, which is a small gap given the absence of an output schema, but the rest is sufficient for safe use.

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

Parameters3/5

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

Schema description coverage is 40%, below the 50% threshold, so the description must compensate. It does add action-level meaning: define_ontology uses a friendly spec with super_types, evolve_ontology applies ordered edits, and publish_schema activates SHACL shapes. However, many parameters such as branch, format, source, entity_types, desired_mode, merge_default, and confirm_restrictive are left mostly to the schema, so compensation is only partial.

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 opens with a specific verb and resource ('Manage native schema metadata') and names the three actions. It distinctly differentiates from the closest sibling by stating that definition/import here extracts native metadata and does NOT store the complete RDF/OWL document as queryable graph facts. This is a clear, non-tautological statement of tool ownership.

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?

The description explicitly routes to alternatives: use lbb_rdf import for full OWL and lbb_rdf update for additive INSERT DATA revisions, with RDF deletions unsupported. It also gives intra-tool guidance, stating publish_schema is for unchanged ontology plus shapes while define/evolve are for native ontology changes. This is concrete when/when-not usage guidance.

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

lbb_inspectA
Read-only

Read graph context and exact graph facts. Actions: guide, graphs, publication, ontology, ontology_conformance, schema, ontology_search, metadata, entity, state, history, transitions, why. graphs works before bootstrap; publication reports whether writes are queryable. ontology and schema return complete entries with page_size, section and cursor; follow next until absent. schema reads active native ontology/SHACL metadata without running validation. Query asserted RDF/OWL axioms separately with lbb_query. ontology_conformance serves the durable report referenced by the pinned published root. entity returns one node's metadata, scalar attributes, bounded Base-backed edge neighborhood, history, and observations. Use lbb_query with SPARQL property paths for precise path selection.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNo
as_ofNoValid-time snapshot pin (RFC3339): reproduce the node as of this instant.
graphNoGraph to target; defaults to the connection's graph
queryNoOntology concept, term, or relation to search
top_kNo
actionYesSelects the variant (one of: guide, ontology, ontology_conformance, schema, graphs, publication, ontology_search, metadata, entity, state, history, why, transitions).
branchNoBranch to target; defaults to the connection's branch
cursorNoOpaque lbb_inspect continuation. Repeat action and pass the returned next arguments; rejects changed metadata.
detailNoResponse detail level. Defaults to compact.
sectionNoOptional top-level array to inspect, e.g. entity_type_defs, relation_defs, property_defs, classes, or relations. Omit to page through all sections.
relationNo
entity_idNoEntity id (hex); alternative to entity_type+name
page_sizeNoMaximum complete metadata entries per page; defaults to 50. Nested fields are never truncated; an oversized single entry returns serialized_json fragments to concatenate and parse.
entity_typeNo
source_nameNo
source_typeNo
target_nameNo
target_typeNo
as_of_commit_seqNoSnapshot pin: reproduce the node (state, edges, history) as of this commit_seq.

TDQS

A3.9/5.0
Behavior4/5

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

With readOnlyHint=true already covering the read-only profile, the description productively adds behavior: pagination mechanics ('follow next until absent'), queryability reporting, and 'schema reads active native ontology/SHACL metadata without running validation'. It also describes entity's bounded edge neighborhood. It stops short of outlining error cases or exact response shapes, but is not contradicted by annotations.

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

Conciseness4/5

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

At roughly 130 words, the description is dense but front-loads purpose, then gives action-specific behavior in compact clauses. Each sentence adds either routing, pagination, or variant semantics; the action enumeration is a necessary structural element rather than filler.

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

Completeness4/5

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

For a 19-parameter, 13-action tool with no output schema, the description covers the major variants, pagination, and the key sibling alternative. However, several actions (guide, state, history, transitions, why, metadata) are only named and not described, and some parameter interactions are left implicit, so it is not fully complete.

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

Parameters3/5

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

Schema description coverage is 58%, so the schema still carries much of the parameter burden. The description adds semantics for action, page_size, section, and cursor, but leaves several parameters (name, relation, entity_type, source_name, target_name, etc.) with no explanation beyond the schema, some of which have no schema description either.

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

Purpose4/5

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

The description opens with 'Read graph context and exact graph facts', a specific verb + resource, then enumerates the 13 action variants. It distinguishes itself from lbb_query by directing asserted RDF/OWL axiom queries to that sibling, though it does not explicitly differentiate against lbb_rdf, so it falls just short of full clarity.

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

Usage Guidelines4/5

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

It provides actionable routing signals: 'Query asserted RDF/OWL axioms separately with lbb_query', 'Use lbb_query with SPARQL property paths for precise path selection', and lifecycle context such as 'graphs works before bootstrap' and 'publication reports whether writes are queryable'. It does not give exclusions for other siblings like lbb_rdf or lbb_models, so usage guidance is clear but incomplete.

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

lbb_modelsB
Read-only

Read model-training inputs or compare retrieval configurations over one pinned published snapshot. shadow_eval takes the API ShadowEvalRequest body; dataset actions return bounded training examples at an optional signal split.

ParametersJSON Schema
NameRequiredDescriptionDefault
bodyNo
graphNoGraph to target; defaults to the connection's graph
limitNo
actionYes
branchNoBranch to target; defaults to the connection's branch
detailNoResponse detail level. Defaults to compact.
split_seqNo

TDQS

B3.3/5.0
Behavior4/5

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

Annotations already mark readOnlyHint=true, so no contradiction. Description adds context about operating 'over one pinned published snapshot' and explains action-specific behavior (shadow_eval body, dataset returns bounded examples). This adds value beyond annotations.

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

Conciseness4/5

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

Two sentences with no wasted words, but the information could be better structured (e.g., separating action types into a list). Still efficient.

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

Completeness2/5

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

No output schema exists, so description should clarify return values, but it doesn't. It also omits explanation of the 'limit', 'graph', 'branch', and 'detail' parameters. While it covers the two action groups, it is incomplete for a 7-parameter tool with nested objects.

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

Parameters3/5

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

Schema description coverage is 43%, so the schema leaves gaps. The description partially compensates by explaining the 'shadow_eval' action requires the body parameter and that dataset actions use an optional signal split, but does not cover graph, branch, limit, or detail parameters.

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

Purpose4/5

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

The description clearly states the tool reads model-training inputs or compares retrieval configurations, using verbs like 'Read' and 'compare'. It distinguishes from sibling tools by focusing on model-training and snapshots, but could be more specific about the scope.

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

Usage Guidelines2/5

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

No guidance on when to use this tool versus siblings like lbb_query or lbb_inspect. The description lacks explicit context for selection criteria.

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

lbb_observeA

Remember a conversation: store the turns verbatim as an EPISODE evidence entity, then anchor + gate the supplied facts on an observe branch (LLM extraction cannot poison the main graph). Facts with both endpoints already in the graph are anchored; unanchored facts need confidence >= 0.8 to mint new entities, else they come back needs_review. auto_merge merges the branch onto the scoped branch when SHACL validation is clean (the validate-then-merge). Server flag-gated (--enable-observe). This build takes caller-extracted facts (each with a structured triplet); bare statements come back needs_review.

ParametersJSON Schema
NameRequiredDescriptionDefault
factsNoCaller-extracted candidate facts; omit with extract:false to store the episode only
graphNoGraph to target; defaults to the connection's graph
turnsYesThe conversation slice to remember (stored verbatim)
branchNoBranch to target; defaults to the connection's branch
sourceNoSource label, e.g. support-bot
extractNofalse = store the episode only (default true)
auto_mergeNoMerge onto the scoped branch when validation is clean
session_idYesCaller's conversation id (drives the default observe branch name)
observe_branchNoBranch for the facts (default observe-<hash12(session_id)>)

TDQS

A4/5.0
Behavior4/5

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

The description adds significant behavioral context beyond the minimal annotations (readOnlyHint=false, etc.). It explains the branching model, the anchoring vs. needs_review outcomes, the auto-merge with SHACL validation, and the server flag dependency. No contradictions with annotations.

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

Conciseness4/5

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

The description is well-structured and front-loaded with the main purpose. All sentences add value, though some technical details (e.g., auto_merge behavior) could be slightly more streamlined. It is appropriately sized for the tool's complexity.

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

Completeness3/5

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

Given the tool has 9 parameters and no output schema, the description covers the core logic (anchoring, confidence, auto-merge) and mentions the server flag. However, it does not describe the return format or error conditions, leaving agents to infer the response structure.

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

Parameters3/5

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

Schema description coverage is 100%, providing baseline parameter documentation. The description adds interpretive context (e.g., 'omit with extract:false to store the episode only', 'confidence >= 0.8 to mint new entities'), but does not fully explain all parameters beyond what the schema already states.

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 clearly states the tool's purpose: storing conversation turns verbatim as an EPISODE evidence entity and anchoring/gating facts on an observe branch. It uses specific verbs ('remember', 'store', 'anchor', 'gate') and distinguishes it from siblings by highlighting the observe branch and the fact anchoring behavior.

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

Usage Guidelines4/5

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

The description provides context on when to use the tool (for adding facts without poisoning the main graph) and explains the anchoring logic and confidence threshold. However, it lacks explicit 'when not to use' or direct comparisons to sibling tools like lbb_commit or lbb_ground.

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

lbb_queryA
Read-only

Analytical and expert reads. Modes: structured (SPARQL-subset JSON body), sparql (SPARQL text), analyze. SPARQL is the only query surface. 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. Structured and text queries pin one published watermark for the request.

ParametersJSON Schema
NameRequiredDescriptionDefault
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.
modeYesSelects the variant (one of: structured, sparql, analyze).
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
queryNoSPARQL 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
top_kNo
branchNoBranch to target; defaults to the connection's branch
cursorNoOpaque cursor from a previous lbb_query row page; reruns the original query at the next offset.
detailNoResponse detail level. Defaults to compact.
metricNo
sparqlNo
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.
consistencyNoRead consistency. strong requires publication through head; a pending response is retryable.
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).

TDQS

A3.9/5.0
Behavior4/5

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

The description goes well beyond the readOnlyHint annotation by disclosing auto-lowercasing of IRIs with a notes entry, the watermark pinning behavior, rejection of valid-time selectors, and the case-insensitivity of structured predicates. These are behavioral traits an agent needs to know but are not in the annotations.

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 long and dense, but each sentence carries technical necessity (IRI schemes, filter shapes, examples, rejection notes). It is not front-loaded in a scannable way; the core purpose and modes appear first, but the body parameter explanation is a wall of text. It could be tightened without losing critical detail.

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

Completeness4/5

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

Given 18 parameters, 6 enums, and no output schema, the description covers the essential query functionality thoroughly: modes, IRI conventions, entity anchoring, unsupported features, and references to lbb_inspect for ontology discovery. The schema descriptions handle remaining parameters, and the tool's return format is implied but not detailed.

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 coverage is 72% and the description adds substantial meaning for the two main parameters: `body` (detailed shape, filter grammar, group_by/aggregates examples, count optimization) and `query` (SPARQL text, IRI scheme, entailment, auto-lowercase). This goes beyond schema descriptions, especially with runnable examples.

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

Purpose4/5

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

The description clearly states it performs 'Analytical and expert reads' and is the only query surface, with three modes. It distinguishes itself from sibling tools like lbb_inspect (ontology discovery) by referencing it for name discovery, and implies read-only operation. However, it does not explicitly name alternatives or contrast with lbb_rdf, so it stops short of a perfect score.

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

Usage Guidelines4/5

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

The description gives concrete guidance on when to use lbb_inspect to discover relation/type names before querying, and notes that structured mode rejects combinators (use sparql mode instead). It does not explicitly state when to prefer lbb_query over lbb_rdf or other siblings, but the core usage context is clear.

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

lbb_rdfA
DestructiveIdempotent

Store and extend complete RDF/OWL documents through MCP. import accepts Turtle/N-Triples/N-Quads/TriG without conversion; update executes INSERT DATA for additive edits. Replacing/removing axioms is unsupported; use a new versioned LBB graph for a revised document. Named RDF graphs are unsupported; Turtle/N-Triples use the default graph, and dataset formats must contain only default-graph quads. These write graph facts, distinct from lbb_configure's native schema metadata. A first RDF write selects RDF-native storage, which refuses later property-graph commits; choose the write workflow before bootstrap. Retries deduplicate by content unless idempotency_key is supplied. A completed write schedules publication; inspect action=publication and verify using lbb_query entailment=owl.

ParametersJSON Schema
NameRequiredDescriptionDefault
graphNoGraph to target; defaults to the connection's graph
actionYesSelects the variant (one of: import, update).
branchNoBranch to target; defaults to the connection's branch
detailNoResponse detail level. Defaults to compact.
formatNo
sourceNoComplete RDF document. Preserves OWL axioms, RDF lists, labels, comments, and external IRIs as graph facts.
updateNoSPARQL INSERT DATA text to add axioms. Submitted unchanged. DELETE/WHERE/CLEAR and named graphs are currently unsupported and fail without mutation.
base_iriNo
idempotency_keyNo
blank_node_scopeNoStable document scope for blank labels across import chunks.

TDQS

A5/5.0
Behavior5/5

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

Annotations already mark the tool as non-read-only, non-open-world, idempotent, and destructive. The description goes well beyond these by explaining the permanent storage-mode selection on first write, refusal of later property-graph commits, retry deduplication behavior, and scheduled publication after a completed write. No contradiction with annotations exists; the destructiveHint is consistent with irreversible storage-mode selection.

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

Conciseness5/5

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

The description is dense but every sentence carries unique information. It is front-loaded with the core purpose and action variants, then systematically covers constraints, storage implications, retries, and publication. No filler or repetition of schema details.

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?

For a complex tool with 10 parameters and no output schema, this description is unusually complete. It explains the two execution modes, supported formats, unsupported operations, named-graph restrictions, storage-mode implications, retry semantics, and the publication workflow. The only minor omission is detailed return-value structure, but the description directs users to inspect action=publication and verify with lbb_query.

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

Parameters5/5

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

Schema coverage is 70%, and the description substantially compensates: it explains that source preserves OWL axioms and external IRIs as graph facts, that update is submitted unchanged and fails without mutation for unsupported SPARQL operations, that dataset formats must contain only default-graph quads, and that blank_node_scope provides stable labels across import chunks. This adds significant meaning beyond the schema's terse field descriptions.

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 clearly states the tool stores and extends complete RDF/OWL documents, and explicitly distinguishes the two actions: import accepts RDF serializations without conversion, while update executes SPARQL INSERT DATA for additive edits. This clearly separates it from sibling tools like lbb_configure, which handles native schema metadata.

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?

The description gives explicit when-to-use and when-not-to-use guidance: replacing/removing axioms is unsupported and users should use a new versioned LBB graph instead. It also warns about named-graph limitations, storage mode selection before bootstrap, and retries deduplication, and distinguishes these graph facts from lbb_configure's metadata.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.5.1
    • Changedlbb_commit1 field changed
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "description": "Validate a facts commit and return its structured SHACL report without writing. Only supported for mode=facts.",
        +  "type": "boolean"
        +}
    • Changedlbb_configure9 fields changed
      • changedInput schema / properties / allow_data_conflicts / description
        Previous value: -"Apply subtractive ops (narrow/remove) even when current data conflicts; affected records are kept and begin to warn. Default false rejects a conflicting subtractive request and reports the conflicts."New value: +"Deprecated compatibility flag; does not bypass conflicts. Preview subtractive changes with dry_run=true, repair the reported conflicts, then apply."
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "description": "Preview the exact definition without creating a graph or writing metadata.",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / entity_types / items / additionalProperties
        Removed value: -{}
      • addedInput schema / properties / entity_types / items / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "additionalProperties": {},
        +    "type": "object"
        +  }
        +]
      • removedInput schema / properties / entity_types / items / type
        Removed value: -"object"
      • changedInput schema / properties / ops / items / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "add_domain": {
        -        "description": "Entity-type names to add to the relation's domain (source types)",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "add_range": {
        -        "description": "Entity-type names to add to the relation's range (target types)",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "op": {
        -        "const": "widen_relation",
        -        "type": "string"
        -      },
        -      "relation": {
        -        "description": "Relation to widen, by name (case-insensitive)",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "relation"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "name": {
        -        "description": "Display name of the new entity type (idempotent if it exists)",
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "add_entity_type",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "name"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "cardinality": {
        -        "description": "Defaults to many_to_many",
        -        "enum": [
        -          "one_to_one",
        -          "one_to_many",
        -          "many_to_one",
        -          "many_to_many"
        -        ],
        -        "type": "string"
        -      },
        -      "domain": {
        -        "description": "Entity-type names allowed as the source (domain); must already exist",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "inverse_name": {
        -        "description": "Optional inverse-relation display name, e.g. PHASE_OF (enables one-hop reverse traversal)",
        -        "type": "string"
        -      },
        -      "name": {
        -        "description": "Display name of the new relation, e.g. HAS_PHASE",
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "add_relation",
        -        "type": "string"
        -      },
        -      "range": {
        -        "description": "Entity-type names allowed as the target (range); must already exist",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "reducer": {
        -        "description": "State-reducer token, e.g. append_only (default), latest_wins",
        -        "type": "string"
        -      },
        -      "symmetric": {
        -        "type": "boolean"
        -      },
        -      "temporal_semantics": {
        -        "description": "Defaults to bitemporal",
        -        "enum": [
        -          "atemporal",
        -          "valid_time",
        -          "commit_time",
        -          "bitemporal"
        -        ],
        -        "type": "string"
        -      },
        -      "transitive": {
        -        "type": "boolean"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "name"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "name": {
        -        "description": "Display name of the new scalar property field, e.g. status (idempotent if it exists)",
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "add_property",
        -        "type": "string"
        -      },
        -      "required": {
        -        "description": "Advisory required flag (not enforced on commit)",
        -        "type": "boolean"
        -      },
        -      "value_type": {
        -        "description": "Scalar type; defaults to text. Lets a later lbb_commit set entity_properties[].field",
        -        "enum": [
        -          "bool",
        -          "i64",
        -          "f64",
        -          "date_time",
        -          "keyword",
        -          "text",
        -          "bytes"
        -        ],
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "name"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "from": {
        -        "description": "Current entity-type name",
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "rename_entity_type",
        -        "type": "string"
        -      },
        -      "to": {
        -        "description": "New display name (stable id stays frozen; records keep resolving)",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "from",
        -      "to"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "from": {
        -        "description": "Current relation name",
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "rename_relation",
        -        "type": "string"
        -      },
        -      "to": {
        -        "description": "New display name (stable id stays frozen; edges keep resolving)",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "from",
        -      "to"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "inverse_name": {
        -        "description": "Inverse-relation display name, e.g. PHASE_OF (enables one-hop reverse traversal)",
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "set_relation_inverse",
        -        "type": "string"
        -      },
        -      "relation": {
        -        "description": "Relation to set the inverse on, by name",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "relation",
        -      "inverse_name"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "cardinality": {
        -        "enum": [
        -          "one_to_one",
        -          "one_to_many",
        -          "many_to_one",
        -          "many_to_many"
        -        ],
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "set_relation_cardinality",
        -        "type": "string"
        -      },
        -      "relation": {
        -        "description": "Relation to change, by name",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "relation",
        -      "cardinality"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "op": {
        -        "const": "narrow_relation",
        -        "type": "string"
        -      },
        -      "relation": {
        -        "description": "Relation to narrow, by name",
        -        "type": "string"
        -      },
        -      "remove_domain": {
        -        "description": "Entity-type names to remove from the relation's domain (subtractive)",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "remove_range": {
        -        "description": "Entity-type names to remove from the relation's range (subtractive)",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "relation"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "name": {
        -        "description": "Entity type to tombstone — kept readable for old records, rejected for new commits",
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "remove_entity_type",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "name"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "name": {
        -        "description": "Relation to tombstone — old edges stay readable, rejected for new commits",
        -        "type": "string"
        -      },
        -      "op": {
        -        "const": "remove_relation",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "op",
        -      "name"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "entity_type": {
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "add_super_types",
        +        "type": "string"
        +      },
        +      "super_types": {
        +        "items": {
        +          "type": "string"
        +        },
        +        "minItems": 1,
        +        "type": "array"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "entity_type",
        +      "super_types"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "add_domain": {
        +        "description": "Entity-type names to add to the relation's domain (source types)",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "add_range": {
        +        "description": "Entity-type names to add to the relation's range (target types)",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "op": {
        +        "const": "widen_relation",
        +        "type": "string"
        +      },
        +      "relation": {
        +        "description": "Relation to widen, by name (case-insensitive)",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "relation"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "name": {
        +        "description": "Display name of the new entity type (idempotent if it exists)",
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "add_entity_type",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "name"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "cardinality": {
        +        "description": "Defaults to many_to_many",
        +        "enum": [
        +          "one_to_one",
        +          "one_to_many",
        +          "many_to_one",
        +          "many_to_many"
        +        ],
        +        "type": "string"
        +      },
        +      "domain": {
        +        "description": "Entity-type names allowed as the source (domain); must already exist",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "inverse_name": {
        +        "description": "Optional inverse-relation display name, e.g. PHASE_OF (enables one-hop reverse traversal)",
        +        "type": "string"
        +      },
        +      "name": {
        +        "description": "Display name of the new relation, e.g. HAS_PHASE",
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "add_relation",
        +        "type": "string"
        +      },
        +      "range": {
        +        "description": "Entity-type names allowed as the target (range); must already exist",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "reducer": {
        +        "description": "State-reducer token, e.g. append_only (default), latest_wins",
        +        "type": "string"
        +      },
        +      "symmetric": {
        +        "type": "boolean"
        +      },
        +      "temporal_semantics": {
        +        "description": "Defaults to bitemporal",
        +        "enum": [
        +          "atemporal",
        +          "valid_time",
        +          "commit_time",
        +          "bitemporal"
        +        ],
        +        "type": "string"
        +      },
        +      "transitive": {
        +        "type": "boolean"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "name"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "name": {
        +        "description": "Display name of the new scalar property field, e.g. status (idempotent if it exists)",
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "add_property",
        +        "type": "string"
        +      },
        +      "required": {
        +        "description": "Advisory required flag (not enforced on commit)",
        +        "type": "boolean"
        +      },
        +      "value_type": {
        +        "description": "Scalar type; defaults to text. Lets a later lbb_commit set entity_properties[].field",
        +        "enum": [
        +          "bool",
        +          "i64",
        +          "f64",
        +          "date_time",
        +          "keyword",
        +          "text",
        +          "bytes"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "name"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "from": {
        +        "description": "Current entity-type name",
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "rename_entity_type",
        +        "type": "string"
        +      },
        +      "to": {
        +        "description": "New display name (stable id stays frozen; records keep resolving)",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "from",
        +      "to"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "from": {
        +        "description": "Current relation name",
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "rename_relation",
        +        "type": "string"
        +      },
        +      "to": {
        +        "description": "New display name (stable id stays frozen; edges keep resolving)",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "from",
        +      "to"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "inverse_name": {
        +        "description": "Inverse-relation display name, e.g. PHASE_OF (enables one-hop reverse traversal)",
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "set_relation_inverse",
        +        "type": "string"
        +      },
        +      "relation": {
        +        "description": "Relation to set the inverse on, by name",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "relation",
        +      "inverse_name"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "cardinality": {
        +        "enum": [
        +          "one_to_one",
        +          "one_to_many",
        +          "many_to_one",
        +          "many_to_many"
        +        ],
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "set_relation_cardinality",
        +        "type": "string"
        +      },
        +      "relation": {
        +        "description": "Relation to change, by name",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "relation",
        +      "cardinality"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "op": {
        +        "const": "narrow_relation",
        +        "type": "string"
        +      },
        +      "relation": {
        +        "description": "Relation to narrow, by name",
        +        "type": "string"
        +      },
        +      "remove_domain": {
        +        "description": "Entity-type names to remove from the relation's domain (subtractive)",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "remove_range": {
        +        "description": "Entity-type names to remove from the relation's range (subtractive)",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "relation"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "name": {
        +        "description": "Entity type to tombstone — kept readable for old records, rejected for new commits",
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "remove_entity_type",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "name"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "name": {
        +        "description": "Relation to tombstone — old edges stay readable, rejected for new commits",
        +        "type": "string"
        +      },
        +      "op": {
        +        "const": "remove_relation",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "op",
        +      "name"
        +    ],
        +    "type": "object"
        +  }
        +]
      • removedInput schema / properties / relations / $ref
        Removed value: -"#/properties/entity_types"
      • addedInput schema / properties / relations / items
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "$ref": "#/properties/entity_types/items/anyOf/1"
        +    }
        +  ]
        +}
      • addedInput schema / properties / relations / type
        Added value: +"array"
    • Changedlbb_inspect5 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Selects the variant (one of: guide, ontology, ontology_conformance, schema, ontology_search, metadata, entity, state, history, why, transitions)."New value: +"Selects the variant (one of: guide, ontology, ontology_conformance, schema, graphs, publication, ontology_search, metadata, entity, state, history, why, transitions)."
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "guide",
        -  "ontology",
        -  "ontology_conformance",
        -  "schema",
        -  "ontology_search",
        -  "metadata",
        -  "entity",
        -  "state",
        -  "history",
        -  "why",
        -  "transitions"
        -]New value: +[
        +  "guide",
        +  "ontology",
        +  "ontology_conformance",
        +  "schema",
        +  "graphs",
        +  "publication",
        +  "ontology_search",
        +  "metadata",
        +  "entity",
        +  "state",
        +  "history",
        +  "why",
        +  "transitions"
        +]
      • addedInput schema / properties / cursor
        Added value: +{
        +  "description": "Opaque lbb_inspect continuation. Repeat action and pass the returned next arguments; rejects changed metadata.",
        +  "type": "string"
        +}
      • addedInput schema / properties / page_size
        Added value: +{
        +  "description": "Maximum complete metadata entries per page; defaults to 50. Nested fields are never truncated; an oversized single entry returns serialized_json fragments to concatenate and parse.",
        +  "maximum": 500,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • addedInput schema / properties / section
        Added value: +{
        +  "description": "Optional top-level array to inspect, e.g. entity_type_defs, relation_defs, property_defs, classes, or relations. Omit to page through all sections.",
        +  "type": "string"
        +}
    • Changedlbb_query6 fields changed
      • 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"
    • Addedlbb_rdf
  2. 5 tool updatesv0.4.2
    • Removedlbb_decode
    • Removedlbb_ground
    • Changedlbb_inspect5 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Selects the variant (one of: guide, ontology, ontology_conformance, schema, ontology_search, metadata, entity, state, history, why, traverse, transitions)."New value: +"Selects the variant (one of: guide, ontology, ontology_conformance, schema, ontology_search, metadata, entity, state, history, why, transitions)."
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "guide",
        -  "ontology",
        -  "ontology_conformance",
        -  "schema",
        -  "ontology_search",
        -  "metadata",
        -  "entity",
        -  "state",
        -  "history",
        -  "why",
        -  "traverse",
        -  "transitions"
        -]New value: +[
        +  "guide",
        +  "ontology",
        +  "ontology_conformance",
        +  "schema",
        +  "ontology_search",
        +  "metadata",
        +  "entity",
        +  "state",
        +  "history",
        +  "why",
        +  "transitions"
        +]
      • removedInput schema / properties / direction
        Removed value: -{
        -  "enum": [
        -    "out",
        -    "in",
        -    "both"
        -  ],
        -  "type": "string"
        -}
      • removedInput schema / properties / max_hops
        Removed value: -{
        -  "exclusiveMinimum": 0,
        -  "maximum": 6,
        -  "type": "integer"
        -}
      • removedInput schema / properties / relations
        Removed value: -{
        -  "items": {
        -    "type": "string"
        -  },
        -  "type": "array"
        -}
    • Changedlbb_query2 fields changed
      • 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"
        +]
    • Removedlbb_search
  3. 8 tool updatesv0.2.7
    • Removedlbb_ask
    • Changedlbb_configure6 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Selects the variant (one of: define_ontology, define_rules, publish_schema, evolve_ontology)."New value: +"Selects the variant (one of: define_ontology, publish_schema, evolve_ontology)."
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "define_ontology",
        -  "define_rules",
        -  "publish_schema",
        -  "evolve_ontology"
        -]New value: +[
        +  "define_ontology",
        +  "publish_schema",
        +  "evolve_ontology"
        +]
      • removedInput schema / properties / confirm_empty
        Removed value: -{
        -  "type": "boolean"
        -}
      • changedInput schema / properties / desired_mode / enum
        Previous value: -[
        -  "warn",
        -  "reject"
        -]New value: +[
        +  "off",
        +  "warn",
        +  "reject"
        +]
      • removedInput schema / properties / preview_digest
        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"
        -}
    • Changedlbb_decode5 fields changed
      • removedInput schema / properties / source_name / description
        Removed value: -"Source entity display name"
      • removedInput schema / properties / source_type / description
        Removed value: -"Source entity type; omit to have the DB recover it from the name"
      • removedInput schema / properties / target_name / description
        Removed value: -"Target entity display name"
      • removedInput schema / properties / target_type / description
        Removed value: -"Target entity type; omit to have the DB recover it from the name"
      • removedInput schema / properties / use_model_when_forced / description
        Removed value: -"Call the model even when the type pair forces one relation (default false — a forced pair is answered by the DB alone)"
    • Changedlbb_ground5 fields changed
      • removedInput schema / properties / action / description
        Removed value: -"complete = narrowed vocabulary autocomplete; resolve = snap free text to the nearest real vocabulary; audit = groundability report"
      • changedInput schema / properties / kinds / description
        Previous value: -"[complete/resolve] Restrict to these vocabulary kinds (default: all)"New value: +"Restrict to these vocabulary kinds (default: all)"
      • changedInput schema / properties / sample / description
        Previous value: -"[audit] Entities to sample for narrowing-recall"New value: +"[audit] Entities sampled for narrowing recall"
      • changedInput schema / properties / text / description
        Previous value: -"[resolve] Free text to snap to the nearest real vocabulary item"New value: +"[resolve] Free text to resolve"
      • changedInput schema / properties / top_k / description
        Previous value: -"[complete/resolve] Max results (default 8)"New value: +"Max results (default 8)"
    • Removedlbb_index
    • Changedlbb_inspect12 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Selects the variant (one of: guide, ontology, ontology_conformance, schema, schema_audit, rules, schema_preview, ontology_search, metadata, entity, edges, state, history, why, traverse, transitions)."New value: +"Selects the variant (one of: guide, ontology, ontology_conformance, schema, ontology_search, metadata, entity, state, history, why, traverse, transitions)."
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "guide",
        -  "ontology",
        -  "ontology_conformance",
        -  "schema",
        -  "schema_audit",
        -  "rules",
        -  "schema_preview",
        -  "ontology_search",
        -  "metadata",
        -  "entity",
        -  "edges",
        -  "state",
        -  "history",
        -  "why",
        -  "traverse",
        -  "transitions"
        -]New value: +[
        +  "guide",
        +  "ontology",
        +  "ontology_conformance",
        +  "schema",
        +  "ontology_search",
        +  "metadata",
        +  "entity",
        +  "state",
        +  "history",
        +  "why",
        +  "traverse",
        +  "transitions"
        +]
      • removedInput schema / properties / base_ontology_version
        Removed value: -{
        -  "minimum": 0,
        -  "type": "integer"
        -}
      • removedInput schema / properties / base_shapes_version
        Removed value: -{
        -  "minimum": 0,
        -  "type": "integer"
        -}
      • removedInput schema / properties / cursor
        Removed value: -{
        -  "description": "Opaque cursor from the previous page's `next_cursor`. The response is the unified list envelope { object:'list', data, has_more, next_cursor, total_count }; page a high-degree node (entity detail hard-caps its edge sample) by feeding next_cursor back here until has_more is false. Each row carries valid_time for a per-edge timeline.",
        -  "type": "string"
        -}
      • removedInput schema / properties / desired_mode
        Removed value: -{
        -  "enum": [
        -    "warn",
        -    "reject"
        -  ],
        -  "type": "string"
        -}
      • removedInput schema / properties / direction / description
        Removed value: -"Edges out of / into / touching the entity (default both)."
      • removedInput schema / properties / offset
        Removed value: -{
        -  "description": "Legacy alias for `cursor` (still accepted); prefer paging with `cursor`.",
        -  "minimum": 0,
        -  "type": "integer"
        -}
      • removedInput schema / properties / ontology
        Removed value: -{
        -  "additionalProperties": false,
        -  "properties": {
        -    "format": {
        -      "enum": [
        -        "auto",
        -        "turtle",
        -        "json_ld",
        -        "rdf_xml",
        -        "csv",
        -        "tsv",
        -        "lbb_json",
        -        "spec"
        -      ],
        -      "type": "string"
        -    },
        -    "source": {
        -      "description": "Ontology source text",
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "source"
        -  ],
        -  "type": "object"
        -}
      • removedInput schema / properties / relation / description
        Removed value: -"Filter to a single relation name."
      • removedInput schema / properties / row_limit
        Removed value: -{
        -  "description": "Max edges returned this page (server caps at 1000; default 150).",
        -  "exclusiveMinimum": 0,
        -  "type": "integer"
        -}
      • removedInput schema / properties / shapes
        Removed value: -{
        -  "additionalProperties": false,
        -  "properties": {
        -    "format": {
        -      "enum": [
        -        "auto",
        -        "turtle",
        -        "n_triples",
        -        "n_quads",
        -        "trig"
        -      ],
        -      "type": "string"
        -    },
        -    "source": {
        -      "description": "SHACL/RDF shape source text",
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "source"
        -  ],
        -  "type": "object"
        -}
    • Addedlbb_models
    • Changedlbb_query16 fields changed
      • 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"
        -}
  4. 11 tool updatesv0.2.1
    • First observedlbb_ask
    • First observedlbb_branch
    • First observedlbb_commit
    • First observedlbb_configure
    • First observedlbb_decode
    • First observedlbb_ground
    • First observedlbb_index
    • First observedlbb_inspect
    • First observedlbb_observe
    • First observedlbb_query
    • First observedlbb_search

TDQS

A4/5.0

Scored across 8 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: lbb_rdf handles RDF/OWL document import/update, lbb_commit writes/retracts graph facts and feedback, lbb_inspect reads graph context/metadata, lbb_query executes SPARQL, lbb_configure manages schema metadata, lbb_branch handles branch lifecycle, lbb_models provides model training data, and lbb_observe captures conversations. Despite some overlap in write operations, the descriptions explicitly delineate boundaries (e.g., lbb_rdf vs. lbb_commit), eliminating ambiguity.

Naming Consistency4/5

All tools share the 'lbb_' prefix and use lowercase single-word names, which is readable and predictable. However, the second part mixes verbs (inspect, query, commit, configure, observe) and nouns (rdf, branch, models), so it does not follow a strict verb_noun pattern. The consistency of the prefix and clarity of each name mitigate this minor inconsistency.

Tool Count5/5

With 8 tools, the server is well-scoped for its domain (a knowledge graph with RDF support, schema management, branching, and observation). This falls comfortably within the ideal 3-15 range, and each tool provides a distinct capability without redundancy.

Completeness4/5

The server covers core lifecycle operations: RDF import/update, graph fact writes/retraction, SPARQL querying, schema definition/evolution, branching/merging, model data access, and observation. However, there is no direct RDF deletion (only additive updates via lbb_rdf and retraction via lbb_commit), and no tool for exporting or bulk deleting graph data, leaving minor gaps that agents can work around.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    An MCP server that enables AI-powered exploration of RDF data and SPARQL querying via RDF4J. It provides tools for executing queries, searching knowledge graph resources, and retrieving schema summaries.
    13
    1
    MIT