cypher-shell
Neo4j Cypher Shell skill. Connect once, query fast, inspect schemas.
Connection Config
writes
(env vars)
and injects a one-line loader into the user's shell profile (
,
, or
). After that, every new Bash invocation has credentials loaded automatically via native cypher-shell env vars:
Execution pattern (after connect):
bash
cypher-shell --format verbose "<QUERY>"
No
, no flags, no credentials. Just
+ query.
Pre-flight for every command (except
and
): verify env var
is set. If not:
bash
if [ -z "$NEO4J_URI" ]; then echo "Not connected. Run /cypher-shell connect <uri>"; exit 1; fi
Route $ARGUMENTS
Parse the
first word of
to determine the action:
| First word | Action |
|---|
| Connect to instance |
| Run Cypher query |
| Retrieve graph schema |
| Test current connection |
| Show install instructions |
| (none) | Show usage help |
Everything after the first word becomes the sub-argument.
connect [URI]
With URI (contains )
- Ask user for username (default: ) and password
- Optionally ask for database name
- Write credentials file:
bash
cat > ~/.neo4j-connection << 'EOF'
export NEO4J_URI="<uri>"
export NEO4J_USERNAME="<user>"
export NEO4J_PASSWORD="<pass>"
export NEO4J_DATABASE="<db>"
EOF
chmod 600 ~/.neo4j-connection
- Inject loader into shell profile so every future shell session auto-loads credentials. Detect the profile file and append the loader line only if not already present:
bash
# Detect shell profile
if [ -f ~/.zshrc ]; then
SHELL_PROFILE=~/.zshrc
elif [ -f ~/.bashrc ]; then
SHELL_PROFILE=~/.bashrc
elif [ -f ~/.bash_profile ]; then
SHELL_PROFILE=~/.bash_profile
else
SHELL_PROFILE=~/.profile
fi
# Append loader only if not already there
LOADER='[ -f ~/.neo4j-connection ] && source ~/.neo4j-connection'
grep -qF "$LOADER" "$SHELL_PROFILE" 2>/dev/null || echo "$LOADER" >> "$SHELL_PROFILE"
- Load credentials into current session (so this session works immediately without restart):
bash
source ~/.neo4j-connection
- Test connection:
cypher-shell "RETURN 'connected' AS status;"
- On success, fetch server info:
bash
cypher-shell --format verbose \
"CALL dbms.components() YIELD name, versions, edition RETURN name, versions[0] AS version, edition;"
bash
cypher-shell --format verbose \
"SHOW DATABASES YIELD name, currentStatus, default RETURN name, currentStatus, default ORDER BY name;"
Report: version, edition, databases, URI. Confirm that credentials are persisted and no flags needed going forward.
Without URI
If
exists, show current config (mask password). Otherwise ask for URI.
Protocol schemes
| Scheme | Encryption | Routing |
|---|
| None | Yes (cluster) |
| TLS (CA-verified) | Yes |
| TLS (self-signed) | Yes |
| None | No (single) |
| TLS (CA-verified) | No |
| TLS (self-signed) | No |
test
- Verify env var is set (if not, tell user to run
/cypher-shell connect <uri>
)
cypher-shell "RETURN 'ok' AS status;"
- Show: URI, user, database, version
Troubleshooting (if test fails)
- — not found? ->
- — need 21+
- — port not open? Neo4j not running
- Auth error — reset at http://localhost:7474 or
cypher-shell --change-password
- TLS error — try or
install
Print install instructions for the user (do NOT run them):
- macOS:
brew install cypher-shell
- Debian/Ubuntu:
curl -fsSL https://debian.neo4j.com/neotechnology.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/neo4j.gpg && echo 'deb [signed-by=/usr/share/keyrings/neo4j.gpg] https://debian.neo4j.com stable latest' | sudo tee /etc/apt/sources.list.d/neo4j.list && sudo apt-get update && sudo apt-get install -y cypher-shell
- Docker:
docker run --rm -it --network host neo4j/neo4j:latest cypher-shell
Requires Java 21+.
query [CYPHER or shortcut]
Full Cypher
If sub-argument contains Cypher keywords (
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
), run directly:
bash
cypher-shell --format verbose "<sub-argument>"
Shortcuts
| Shortcut | Query |
|---|
| MATCH (n) RETURN labels(n) AS label, count(n) AS count ORDER BY count DESC;
then MATCH ()-[r]->() RETURN type(r) AS type, count(r) AS count ORDER BY count DESC;
|
| MATCH (n) WHERE NOT (n)--() RETURN labels(n) AS label, n.name AS name, count(*) AS count ORDER BY count DESC;
|
| SHOW INDEXES YIELD name, type, labelsOrTypes, properties, state RETURN name, type, labelsOrTypes, properties, state;
|
| SHOW CONSTRAINTS YIELD name, type, labelsOrTypes, properties RETURN name, type, labelsOrTypes, properties;
|
| WARN + ASK CONFIRMATION then CALL { MATCH (n) WITH n LIMIT 10000 DETACH DELETE n } IN TRANSACTIONS OF 10000 ROWS;
then verify MATCH (n) RETURN count(n) AS remaining;
|
No sub-argument -> show shortcuts list.
schema [Label]
Full schema (no label argument)
Run ALL and present formatted summary:
cypher
MATCH (n) RETURN labels(n) AS label, count(n) AS count ORDER BY count DESC;
cypher
MATCH ()-[r]->() RETURN type(r) AS type, count(r) AS count ORDER BY count DESC;
cypher
MATCH (a)-[r]->(b) RETURN DISTINCT labels(a) AS from_label, type(r) AS relationship, labels(b) AS to_label ORDER BY from_label, relationship, to_label;
cypher
MATCH (n) WITH labels(n) AS lbls, keys(n) AS props RETURN DISTINCT lbls AS label, props AS properties ORDER BY lbls;
cypher
MATCH ()-[r]->() WITH type(r) AS rel, keys(r) AS props WHERE size(props) > 0 RETURN DISTINCT rel AS relationship, props AS properties ORDER BY rel;
cypher
SHOW INDEXES YIELD name, type, labelsOrTypes, properties, state RETURN name, type, labelsOrTypes, properties, state ORDER BY name;
cypher
SHOW CONSTRAINTS YIELD name, type, labelsOrTypes, properties RETURN name, type, labelsOrTypes, properties ORDER BY name;
Present as ASCII schema map:
Graph Schema
============
Nodes: X total across Y labels
Rels: Z total across W types
[File] (145) props: name, path, language
--[:CONTAINS]--> [Function]
--[:IMPORTS]--> [File]
[Function] (312) props: name, file, line
--[:CALLS]--> [Function]
Indexes: idx_file_path RANGE :File(path) ONLINE
Constraints: uniq_file_id UNIQUENESS :File(id)
Label deep-dive (argument matches a label)
cypher
MATCH (n:<LABEL>) RETURN n LIMIT 5;
cypher
MATCH (n:<LABEL>) WITH n LIMIT 100 UNWIND keys(n) AS key
RETURN DISTINCT key AS property, head(collect(DISTINCT valueType(n[key]))) AS type, count(*) AS present_in ORDER BY present_in DESC;
cypher
MATCH (n:<LABEL>)-[r]->(m) RETURN type(r) AS rel, labels(m) AS target, count(*) AS count ORDER BY count DESC;
cypher
MATCH (n:<LABEL>)<-[r]-(m) RETURN type(r) AS rel, labels(m) AS source, count(*) AS count ORDER BY count DESC;
cypher
MATCH (n:<LABEL>) RETURN count(n) AS total;
cypher
MATCH (n:<LABEL>) WHERE NOT (n)--() RETURN count(n) AS orphans;
cypher-shell CLI Reference
Key flags
| Flag | Description | Default |
|---|
| Connection URI | |
| Username | env |
| Password | env |
| Database | env |
| Execute .cypher file | — |
| Set params: | |
--format {auto,verbose,plain}
| Output format | |
--access-mode {read,write}
| Access mode | |
| Force non-interactive | |
--fail-fast / --fail-at-end
| Error handling for files | |
| Rows for table width | |
| Wrap long columns | |
| e.g. , | |
--error-format {gql,legacy,stacktrace}
| Error display | |
--encryption {true,false,default}
| Encryption | |
| Run as user | — |
| Change password | — |
| Tab-complete (5+) | |
| Query notifications | |
| Auto-exit | |
| Debug log | — |
| Version | — |
Shell commands (interactive)
(list)
:access-mode [read|write]
Cypher quick patterns
cypher
MATCH (n:Label {prop: "val"}) RETURN n;
MATCH (a)-[:REL]->(b) RETURN a.name, b.name;
MATCH path = (a)-[*1..3]->(b) WHERE a.name = "x" RETURN path;
MATCH path = shortestPath((a {name:"x"})-[*]-(b {name:"y"})) RETURN path;
MATCH (n:Label) RETURN n.prop, count(n) ORDER BY count(n) DESC;
MERGE (n:Label {id: "x"}) ON CREATE SET n.created = timestamp() ON MATCH SET n.updated = timestamp();
CALL { MATCH (n:Label) WITH n LIMIT 10000 DETACH DELETE n } IN TRANSACTIONS OF 10000 ROWS;
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row CREATE (:Label {name: row.name});
EXPLAIN MATCH (n)-[r]->(m) RETURN n, r, m;
PROFILE MATCH (n)-[r]->(m) RETURN n, r, m;
Cypher 25 (Neo4j 2025.06+):
,
,
,
,
,
(vector),
coll.distinct/flatten/indexOf/insert/max/min/remove/sort
.