Skip to main content

CIM Notebooks

CIM Notebooks are CIMNotebook's own notebook documents for VS Code: markdown files whose ```sparql and ```shacl code blocks open as notebook cells. Because the on-disk format is plain markdown, notebooks diff and merge cleanly in git, render on any git forge, and need no special tooling to read.

Every code cell is validated by CIMLangServer exactly like a standalone .rq or .shacl file — diagnostics, hover, completion, and go-to-definition all work inside cells, resolved against your configured CGMES schema (see SPARQL Notebooks for how a cell picks its schema with the # [endpoint=...] directive).

VS Code only

CIM Notebooks are a VS Code feature. The IntelliJ plugin validates .rq, .sparql, .ttl, and .shacl files but has no notebook support.

Attribution

The notebook feature is inspired by the SPARQL Notebook extension by Zazuko (MIT-licensed) — CIM Notebooks are an independent implementation that reads and writes Zazuko's .sparqlbook format for interoperability.

File formats

FormatOpens as notebookNotes
*.cimnb.mdby defaultThe native format. A regular markdown file — the suffix just tells VS Code to open it as a notebook.
any *.md / *.markdownvia Open With… → CIM Notebook (Markdown)Turn any markdown document (a README, a runbook) into a notebook without renaming it.
*.sparqlbookvia Open With… → CIM Notebook (SPARQL Book)Zazuko SPARQL Notebook interop (JSON).

To use Open With…: right-click the file in the Explorer → Open With… → pick the CIM Notebook editor. VS Code remembers the choice per file type if you set it as default.

The markdown format

Only top-level, unindented three-backtick fences labelled sparql or shacl become code cells. Everything else — prose, headings, tables, code blocks in other languages (```turtle, ```json, …) — stays markdown:

# Switch survey

Count switches per substation:

```sparql
SELECT ?substation (COUNT(?switch) AS ?n)
WHERE { ?switch cim:Equipment.EquipmentContainer ?substation }
GROUP BY ?substation
```

Shapes for the same data:

```shacl
ex:SwitchShape a sh:NodeShape ;
sh:targetClass cim:Switch .
```

Saving a notebook normalizes the file deterministically: cells are separated by exactly one blank line, trailing blank lines inside cells are dropped, and the file ends with a single newline. Line endings (LF/CRLF) are preserved. Cell outputs are never written to disk — notebook files stay pure source.

Converting between formats

The command CIMNotebook: Convert Notebook (Markdown ⇔ SPARQL Book) writes the active notebook in the other format as a sibling file (report.sparqlbookreport.cimnb.md and back) and opens it. The source file is never modified. Zazuko per-cell metadata survives a .sparqlbook round trip but is dropped when converting to markdown.

Running cells

SPARQL and SHACL cells have a Run button (the CIM Notebook kernel). Execution happens inside CIMLangServer — the same Java process that validates your queries — using Apache Jena's SPARQL 1.1 and SHACL engines, so there is nothing extra to install.

A cell names its target with the same # [endpoint=...] directive used for validation — one directive drives both validate against and run against. The target can be a SPARQL endpoint or local data files:

# [endpoint=http://localhost:3030/cgmes/query]
SELECT ?class (COUNT(?s) AS ?n)
WHERE { ?s a ?class }
GROUP BY ?class ORDER BY DESC(?n)
# [endpoint=./model.xml]
SELECT ?name WHERE { ?s cim:IdentifiedObject.name ?name }

What you get per query kind:

QueryOutput
SELECTResult table (first 50 rows rendered; the full result travels alongside as application/sparql-results+json)
ASKtrue / ❌ false
CONSTRUCT / DESCRIBEThe result graph as Turtle
INSERT / DELETE / other updatesA confirmation with the update endpoint used
SHACL cellA conforms / does-not-conform verdict with severity counts, plus the full validation report as Turtle

Each output ends with a small stats line — row count, duration, and the resolved endpoint. Results are capped server-side (10 000 rows/triples by default) and the output says so when the cap was hit. The raw payload of a result is one click away: the output's ⋯ → Change Presentation menu switches between the rendered view and the plain-text payload (the SPARQL Results JSON, or the Turtle of a graph/report).

Updates run against the endpoint's update service: for Fuseki-style URLs the client derives it from the query URL (…/query or …/sparql…/update); any other URL is assumed to accept updates directly.

Cancel and timeout. The notebook Stop button cancels the running request (the server stops waiting and aborts the query where the endpoint supports it). Requests time out after 30 seconds by default.

Querying local files — including CIMXML models

When a directive is not an http(s):// URL it is taken as a file path, relative to the notebook's own directory. The file is parsed in-process and queried directly — no triple store, no import step:

  • CIMXML models (*.xml): parsed by OpenCGMES's IEC 61970-552 parser. The model body and its md:FullModel header are both queryable.
  • RDF files: anything Jena reads by extension — .ttl, .rdf, .owl, .nt, .nq, .trig, ….

Several directives in one cell query the union of the files: named graphs stay addressable with GRAPH, and a bare ?s ?p ?o sees everything.

# [endpoint=./model.xml]
# [endpoint=./boundary.ttl]
SELECT (COUNT(*) AS ?triples) WHERE { ?s ?p ?o }

A directive can also be a glob pattern* matches within a directory, ** crosses directories, and {a,b} picks alternatives, just like the SPARQL Notebook extension:

# [endpoint=./rdf/*.ttl]
SELECT (COUNT(*) AS ?triples) WHERE { ?s ?p ?o }
# [endpoint=./rdf/{a,b}.ttl]
SELECT (COUNT(*) AS ?triples) WHERE { ?s ?p ?o }

A pattern that matches no files fails the run with an error (it won't silently query nothing); a file matched by several directives is read once.

Parsed files are cached in the language server and re-parsed automatically when the file changes on disk, so edit model → re-run cell just works. Local files are read-only: a SPARQL Update against a file target is rejected — updates need an HTTP endpoint.

Multiple file directives are the only repetition that means something. A cell runs against exactly one endpoint, so mixing kinds (a file and a URL, a file and a connection name) or repeating a URL or a connection name is reported as an error rather than silently resolved to one of them — remove the directives you didn't mean.

Note the dual meaning of the directive: for validation, the .ttl/.rdf/.owl files are loaded as the schema (several files or a pattern's matches form one union schema), while instance-data files (.xml models, .nt/.nq/.trig dumps) keep the workspace schema so diagnostics stay meaningful — and either way the files are what the cell runs against. So # [endpoint=./model.xml] + # [endpoint=./schema.ttl] queries both files while validating the query against schema.ttl.

Running SHACL cells

A SHACL cell's text is the shapes graph; running the cell validates the target data against those shapes. The same # [endpoint=...] directives pick the data:

  • Local files — the shapes are checked in-process (Apache Jena SHACL) against the union of the referenced files, CIMXML models included:

    # [endpoint=./model.xml]
    ex:SwitchNameShape a sh:NodeShape ;
    sh:targetClass cim:Switch ;
    sh:property [ sh:path cim:IdentifiedObject.name ; sh:minCount 1 ] .
  • HTTP endpoints — the shapes are POSTed as Turtle to the endpoint's SHACL service (Fuseki's shacl operation and compatibles), which validates its own data. For Fuseki-style URLs the service is derived from the query URL (…/query…/shacl); because Fuseki requires a graph selector, ?graph=default (the default graph) is added automatically — put an explicit ?graph=… in the directive to validate a named graph.

The output is a ✅ conforms / ❌ does-not-conform banner with counts per severity (violations, warnings, infos), followed by the full sh:ValidationReport as Turtle. A run that finds violations is still a successful run — non-conformance is the result, not an error.

Named connections

Endpoints you use often can be declared once in opencgmes.jsonc (the same file that configures validation) under a cimnotebook section, and referenced by name:

{
"cimnotebook": {
"connections": [
{
"name": "local-fuseki",
"url": "http://localhost:3030/cgmes/query",
"default": true,
},
{
"name": "prod",
"url": "https://sparql.example.org/query",
"authType": "basic",
},
],
"queryTimeoutSeconds": 30, // workspace default for cell executions
"maxRows": 10000, // workspace default result cap
},
}
# [endpoint=local-fuseki]
SELECT * WHERE { ?s ?p ?o } LIMIT 10

A connection name has no slashes and no dots — that's how it is told apart from a file path. updateUrl/shaclUrl are optional and derived from url when omitted. Connections (and the rest of the config) can also be edited in the CIMNotebook sidebar — see Configuration sidebar.

Every cell shows its resolved target in the cell status bar (local-fuseki, a URL, model.xml (+1), or a no endpoint warning); clicking it — or running CIMNotebook: Set Cell Endpoint… — picks a connection, a URL (with a small recent-URLs history above the input box), or one or more data files via fuzzy, search-as-you-type matching over the workspace (with Browse… for files outside it and Enter path manually… for a file that doesn't exist yet). Picking several files writes one # [endpoint=...] directive per file — the same union target described above. One constraint inherited from the directive syntax: a path containing spaces cannot be referenced (the directive value ends at the first whitespace character).

Where a cell without a directive runs

A cell with no # [endpoint=...] line of its own is not stranded — the target is resolved in three steps, and the first one that answers wins:

  1. The cell's own directive.
  2. The notebook default. Set Cell Endpoint… offers to apply a single connection, URL, or file as the Notebook default instead of writing it into the cell. It is remembered in VS Code's workspace state — deliberately not in the notebook file and not in opencgmes.jsonc — so it is a private, per-workspace convenience: a notebook shared with a colleague carries only its directives. The picker shows the current default in its title and offers Clear the notebook default while one is set.
  3. The default connection. The opencgmes.jsonc connection marked "default": true.

Validation follows the same three steps, so a cell is always validated against the endpoint it runs against: schema-based diagnostics, completion, hover, and go-to-definition all use the schema of the resolved target (a .ttl/.rdf/.owl file or the endpoint's schema graphs). A target that carries no schema — a CIMXML model, an .nt dump — keeps the workspace schema from opencgmes.jsonc instead, since instance data would only produce false diagnostics.

Credentials

Connections with "authType": "basic" prompt once for username and password on first run and offer to keep them in VS Code secret storage (the OS keychain). Passwords are never written to opencgmes.jsonc, VS Code settings, or the notebook — they travel only inside the execute request to the local language server, which turns them into the HTTP Authorization header. Manage them with CIMNotebook: Set Connection Credentials… and Clear Connection Credentials….

Verbose tracing logs requests

The CIMNotebook (trace) output channel (LSP trace, off by default) logs full request payloads — including credentials of an execute request. Leave tracing off when working with authenticated endpoints, or clear the channel afterwards.

Current limitations:

  • Only HTTP basic authentication is supported.