vertexproject/synapse/storm

Claude Code and agent skill by vertexproject/synapse

Trust tier 2
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Reference for the Storm query language used by the Synapse Cortex: lifting, filtering, pivoting and traversal, editing nodes, tags, variables and types, control flow, functions, commands, and libraries. Use when composing, validating, debugging, or explaining Storm queries.

Install

npx vskill@latest install vertexproject/synapse/storm

SKILL.md

Storm Query Language Skill

TRIGGER: When writing, editing, reviewing, validating, debugging, or running Storm (.storm) files, Storm queries, Storm packages, or Synapse query language code. Also triggered when fixing BadSyntax exceptions, troubleshooting Storm parse errors, or running Storm queries against a Cortex.

Language Overview

Storm is an async pipeline-based graph query language for Synapse. Queries are chains of operations and commands that transform streams of (node, path) tuples lazily. Storm files use /* */ and // comments. Inline commands must end with | to return to storm operator syntax.

Validating and Running Storm

IMPORTANT: Claude MUST validate ALL Storm query logic it generates using the MCP tools below. Every Storm query written to a file, embedded in a test, or included in a Storm package MUST be validated before being considered complete. No exceptions.

Validating Storm Syntax (Required)

The storm_validate MCP tool validates Storm syntax without executing the query. Always use it to check a query before running it.

  • Argument: query (the Storm query text).
  • Returns {"valid": true} when the syntax is valid, or {"valid": false, "err": <ErrName>, "mesg": <details>} when the query fails to parse.

Workflow for validating generated Storm:

  1. Call storm_validate with the query text.
  2. If valid is false, read err / mesg (which include line/column info), fix the query, and re-validate before proceeding.
Linting Storm (Required)

The storm_lint MCP tool lints a query against the Cortex's own data model (including extended forms and properties) without executing it. Lint every query before running it, and fix or consider each finding.

  • Arguments: query, plus optional select / ignore / enable lists of rule codes or code prefixes, and an optional v3_ready boolean.
  • Returns {"findings": [{"rule", "severity", "line", "column", "mesg"}, ...]}; an empty list means the query is clean.
  • A syntax error is reported as a single E001 finding with its line and column, so a clean lint also implies valid syntax.
  • Only storm mode queries are supported; lookup mode text usually returns an E001 syntax error.
  • The default rules report errors (E), pipeline usage which may not do what was intended (P), Storm which could be written more simply (S), and a bare true / false / null which is a string rather than a boolean or null (R004). Suppress a deliberate finding with a trailing // noqa or // noqa: <code> comment on that line; E001 cannot be suppressed.
  • Lint with the default rules. Only set v3_ready when asked to check or prepare Storm for Synapse 3.x. It adds the R rules, for code which does not run as intended on Synapse 3.x, and the M rules, for data model forms and properties which are renamed or removed in Synapse 3.x. These findings are not errors on this Cortex: do not replace a form such as inet:ipv4 with its 3.x name, which does not exist here.
Running Storm Queries

The storm MCP tool runs a Storm query against the Cortex and returns a page of result messages: {"messages": [(<type>, <info>), ...], "cursor": <str-or-null>}. Each message is a (<type>, <info>) tuple as yielded by the Storm runtime (e.g. node, print, warn, err, fini).

Each query must be fully drained or cancelled. If cursor is non-null, the query produced more messages than one page and is still running on the server -- you MUST either call storm_continue(cursor) repeatedly until it returns a null cursor (fully drained), or call storm_cancel(cursor) to discard the rest. Never abandon a query with a non-null cursor: it holds a Storm runtime open on the server until it times out. A null cursor means the query is complete and nothing further is required.

The call_storm MCP tool runs a query and returns only the value from its return() statement (no pagination). Use it when the query is written as a single function-style query that returns one value.

storm and call_storm accept:

  • query (required): the Storm query text.
  • opts (optional): Storm query opts, e.g. {"vars": {...}}, {"view": <iden>}, or {"limit": <n>}. Setting "view" is important -- see Selecting a View.

storm_continue and storm_cancel take a single cursor argument. Queries run as the calling user and respect that user's permissions; the view is whatever opts["view"] specifies, else the user's default view.

Use cases:

  • Verify that a query produces the expected nodes and output (storm).
  • Retrieve a computed value from a function-style query (call_storm).
  • Iteratively build up and query graph data across calls.
  • Validate that Storm logic works end-to-end (not just syntax) before deploying it to a package.
Selecting a View

A query always runs in exactly one view. There is no "active session view" -- the view is chosen per call by setting "view" in the opts of storm / call_storm (e.g. opts={"view": "<iden>"}). If "view" is omitted, the query runs in the calling user's default view.

Running a query in the wrong view can be VERY BAD -- it can create, modify, or destroy data in the wrong place. When you are not certain which view the user intends, do NOT guess: call view_list, show the choices, and ask the user to confirm which view to use. Once chosen, reuse that view iden in the opts of every subsequent storm / call_storm call.

View MCP tools:

  • view_list -- list the views the user can read, as {iden, name, parent}. Take the

iden of the intended view and pass it as opts={"view": "<iden>"} on later calls.

  • view_get -- return the iden of the user's default view (the one used when opts omits a

view).

  • view_fork -- fork a view (defaults to the user's default view), creating a child view

with its own writable top layer; returns the new fork's iden.

  • view_del -- delete a view (its layers are not deleted).
  • view_merge -- merge a forked view's changes down into its parent (the fork is not

deleted; the view must be a fork without parent quorum voting).

Developing ingest logic (do this for ANY node-editing work): a view_fork followed by a view_del is the safe way to test logic that edits nodes. view_fork a view to get a fork iden, run the ingest by passing that iden as opts={"view": "<fork-iden>"} to the storm tool, inspect the results, then view_del the fork to discard every change -- the underlying data is never touched. Strongly prefer this workflow whenever developing or iterating on ingest or other node-editing Storm. Use view_merge instead of view_del only once the logic is verified and you want to keep the changes.

Discovering the Data Model

Do not guess form or property names. Use:

  • the model_find MCP tool to search the data model by regex (matched against the names and docs of forms, properties, types, and interfaces), or the syn://model MCP resource for the full Synapse data model (forms, properties, types, univs, tagprops, edges, interfaces);
  • the syn://model/form/{name} MCP resource for a single form definition;
  • the syn://stormdocs MCP resource for Storm library, type, and command documentation.

Reading BadSyntax Errors

A BadSyntax exception (surfaced via storm_validate or an err message) contains:

  • `mesg`: Human-readable error (e.g. "Unexpected token 'and' at line 3, column 5, expecting one of: ...")
  • `line` / `column`: Location in the query text
  • `token`: The unexpected token value
  • `at`: Character offset in query text
  • `highlight`: Dict with hash, lines, columns, offsets for precise source mapping

The parser converts raw Lark token names into friendly English descriptions in its error messages.

Syntax Quick Reference

Lifting (Select Nodes)
storm
inet:fqdn // all nodes of a form
inet:fqdn=example.com // by primary value
inet:fqdn:zone=1 // by property value
inet:fqdn:zone // where property is set
#tag.name // by tag
inet:fqdn#tag.name // by form with tag
entity:contact:names*[=example] // by array contents
reverse(inet:fqdn) // reverse lift order

Comparison operators: =, !=, <, >, <=, >=, ~= (regex), ^= (prefix), @= (time/interval)

Filtering
storm
+#tag.name // keep nodes with tag
-:prop=value // remove matching nodes
+{ -> inet:ipv4 +:asn=1234 } // subquery filter (keep)
-{ -> inet:ipv4 } // subquery filter (remove)
+{ -> inet:dns:a } < 2 // subquery filter with count compare
+(:asn=1234 or :asn=5678) // compound: and, or, not
+$(:client:txbytes >= 100) // expression filter (keep)
-$($fqdns.size() > 1) // expression filter (remove)
+:asn::name=woot // embed filter syntax using ::
Pivoting (Graph Traversal)
storm
-> * // pivot to all referenced nodes
-> inet:ipv4 // pivot to specific form
-> (inet:ipv4, inet:ipv6) // pivot to multiple forms
<- * // reverse pivot (incoming refs)
-+> * // join pivot (keep source + targets)
-> { subquery } // raw pivot via subquery
:ipv4 -> inet:ipv4 // property-based pivot
// Light edge traversal
-(refs)> * // walk N1 edges (outbound)
<(refs)- * // walk N2 edges (inbound)
--> * // N-walk (multi-hop)
<-- * // reverse N-walk
// Edge join pivots (keep source + targets)
-(refs)+> * // N1 edge join pivot
<+(refs)- * // N2 edge join pivot
--+> * // N-walk join
<+-- * // reverse N-walk join
Edit Blocks [ ]
storm
// Node creation
[ inet:fqdn=example.com ] // create node
[ inet:fqdn?=example.com ] // try-create (ignore errors)
// Property modification
[ :asn=1234 ] // set property
[ :tags += newval ] // add to array (+=)
[ :tags -= oldval ] // remove from array (-=)
[ :tags ++= $list ] // add multiple (++=)
[ :tags --= $list ] // remove multiple (--=)
[ -:asn ] // delete property
[ :prop ?= value ] // try-set (ignore type errors)
[ :prop*unset= value ] // set only if unset (conditional)
[ :prop={ foo:bar } ] // assign from a subquery (yields a node; its value is assigned)
[ :prop={[ foo:bar=baz ]} ] // the subquery may create the node to assign
// Tag operations
[ +#tag.name ] // add tag
[ +?#tag.name ] // try-add tag (ignore errors)
[ -#tag.name ] // remove tag
[ +#tag=(2023-01-01, 2024-01-01) ] // tag with time interval
[ +#tag:confidence=80 ] // set tag property
[ -#tag:confidence ] // delete tag property
[ +?#$tags ] // add tags from variable
// Light edge operations
[ +(refs)> { inet:ipv4=1.2.3.4 } ] // add edge via subquery
[ -(refs)> { inet:ipv4=1.2.3.4 } ] // remove edge via subquery
[ <(seen)+ $srcnode ] // add N2 edge to variable
// a subquery that returns no nodes (or a $srcnode of (null)) is valid and makes no changes

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
446
Repository forks
91
7-day trend score
31

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/vertexproject/synapse/storm/badge)](https://verified-skill.com/skills/vertexproject/synapse/storm)

Something wrong with this skill or its page? Tell us.