The MindState REPL is an interactive command-line tool that provides two modes for querying PostgreSQL databases with Apache AGE (Graph) extensions:
- LLM Mode (default): Natural language queries processed by an AI assistant
- Direct Mode: Direct Cypher query execution
- Dual Query Modes: Switch between AI-assisted and direct Cypher execution
- Smart Column Detection: Automatically handles complex return clauses with multiple columns
- Multi-Statement Support: Execute multiple Cypher statements in a single input
- Relationship Display: Proper formatting of nodes, relationships, and paths
- Command History: Persistent command history across sessions
- Logging: Optional logging of LLM interactions and database responses
mstate# Execute Cypher files and start REPL
mstate init_graph.cypher
# Execute files and exit (no REPL)
mstate -e init_graph.cypher queries.cyphermstate -s custom_prompt.txtYou can run MindState as an HTTP service:
mstate --api
# or
mstate-apiCore endpoints:
POST /v1/memory/rememberPOST /v1/memory/recallPOST /v1/context/build
| Command | Description |
|---|---|
\q |
Quit the REPL |
\h |
Show help message |
\llm [on|off] |
Toggle LLM mode (AI assistance) |
\log [on|off] |
Toggle logging of interactions |
\mode [shell|memory] |
Switch between low-level shell and higher-level memory workflows |
\remember KIND | CONTENT |
Store canonical memory from TUI workflow |
\recall QUERY |
Run ranked semantic memory recall |
\context QUERY |
Build a bounded context bundle |
\inspect MEMORY_ID |
Inspect stored memory content, metadata, and provenance |
Use TUI mode and switch to memory behavior:
mstate --tuiInside TUI:
\mode memoryto default plain input into remember-flow capture\remember note | content...for explicit captures\recall <query>for ranked retrieval\context <query>for bounded context bundles\inspect <memory_id>for memory inspection
In LLM mode (default), you can use natural language to query and manipulate your graph database.
mstate> \llm on
LLM enabled.
mstate> show all in the database
Here is everything currently in the database, including all nodes and their relationships:
1. Node: TestNode (id: 844424930131969, properties: {msg: "Hello, AGE!"})
- Relationship: OWNS
- Target Node: TargetNode (id: 1125899906842625, properties: {effect: "Booh!"})
2. Node: Window (id: 4222124650659841, properties: {})
- Relationship: HAS_BUTTON
- Target Node: Button (id: 4785074604081153, properties: {name: "start"})
If you want to see nodes that are not connected to any relationships, or need more details about any node or relationship, let me know!
mstate> create a new person named Alice who is 30 years old
I'll create a new Person node for Alice with age 30.
result
{"id": 1407374883553281, "label": "Person", "properties": {"name": "Alice", "age": 30}}::vertex
The Person node for Alice has been successfully created with id 1407374883553281.
mstate> make Alice friends with the TestNode
I'll create a FRIENDS relationship between Alice and the TestNode.
(no results)
The FRIENDS relationship between Alice and TestNode has been created successfully.
mstate> show me all the people and their connections
Here are all the people in the database and their connections:
1. **Alice** (Person, age: 30)
- FRIENDS with → TestNode (msg: "Hello, AGE!")
2. **TestNode** (msg: "Hello, AGE!")
- OWNS → TargetNode (effect: "Booh!")
- FRIENDS with ← Alice (Person, age: 30)
Alice is connected to TestNode through a FRIENDS relationship, and TestNode owns TargetNode.
mstate> find all nodes with more than 2 relationships
mstate> show me the shortest path between Alice and any Button
mstate> create a company called TechCorp and make Alice work there
mstate> delete all OWNS relationships
mstate> show me all isolated nodes (nodes with no relationships)
In direct mode, you write raw Cypher queries that are executed directly against the database.
mstate> \llm off
LLM disabled.
mstate> MATCH (n) RETURN n
result
{"id": 844424930131969, "label": "TestNode", "properties": {"msg": "Hello, AGE!"}}::vertex
{"id": 1125899906842625, "label": "TargetNode", "properties": {"effect": "Booh!"}}::vertex
{"id": 4222124650659841, "label": "Window", "properties": {}}::vertex
{"id": 4785074604081153, "label": "Button", "properties": {"name": "start"}}::vertex
{"id": 1407374883553281, "label": "Person", "properties": {"name": "Alice", "age": 30}}::vertex
mstate> MATCH (n)-[r]->(m) RETURN n, r, m
n r m
{"id": 844424930131969, "label": "TestNode", "properties": {"msg": "Hello, AGE!"}}::vertex {"id": 2251799813685250, "label": "OWNS", "end_id": 1125899906842625, "start_id": 844424930131969, "properties": {}}::edge {"id": 1125899906842625, "label": "TargetNode", "properties": {"effect": "Booh!"}}::vertex
{"id": 4222124650659841, "label": "Window", "properties": {}}::vertex {"id": 4503599627370497, "label": "HAS_BUTTON", "end_id": 4785074604081153, "start_id": 4222124650659841, "properties": {}}::edge {"id": 4785074604081153, "label": "Button", "properties": {"name": "start"}}::vertex
{"id": 1407374883553281, "label": "Person", "properties": {"name": "Alice", "age": 30}}::vertex {"id": 2533274790395905, "label": "FRIENDS", "end_id": 844424930131969, "start_id": 1407374883553281, "properties": {}}::edge {"id": 844424930131969, "label": "TestNode", "properties": {"msg": "Hello, AGE!"}}::vertex
mstate> MATCH p = (a)-[r]->(b) RETURN p
result
[{"id": 844424930131969, "label": "TestNode", "properties": {"msg": "Hello, AGE!"}}::vertex, {"id": 2251799813685250, "label": "OWNS", "end_id": 1125899906842625, "start_id": 844424930131969, "properties": {}}::edge, {"id": 1125899906842625, "label": "TargetNode", "properties": {"effect": "Booh!"}}::vertex]::path
[{"id": 4222124650659841, "label": "Window", "properties": {}}::vertex, {"id": 4503599627370497, "label": "HAS_BUTTON", "end_id": 4785074604081153, "start_id": 4222124650659841, "properties": {}}::edge, {"id": 4785074604081153, "label": "Button", "properties": {"name": "start"}}::vertex]::path
[{"id": 1407374883553281, "label": "Person", "properties": {"name": "Alice", "age": 30}}::vertex, {"id": 2533274790395905, "label": "FRIENDS", "end_id": 844424930131969, "start_id": 1407374883553281, "properties": {}}::edge, {"id": 844424930131969, "label": "TestNode", "properties": {"msg": "Hello, AGE!"}}::vertex]::path
mstate> CREATE (p:Product {name: 'Laptop', price: 999.99}) RETURN p
result
{"id": 1688849860263937, "label": "Product", "properties": {"name": "Laptop", "price": 999.99}}::vertex
You can execute multiple Cypher statements in a single input by separating them with semicolons:
mstate> CREATE (a:Author {name: 'Jane Doe'}); CREATE (b:Book {title: 'Graph Theory'}); MATCH (a:Author), (b:Book) CREATE (a)-[:WROTE]->(b)
--- Statement 1 ---
result
{"id": 1970324836974593, "label": "Author", "properties": {"name": "Jane Doe"}}::vertex
--- Statement 2 ---
result
{"id": 2251799813685251, "label": "Book", "properties": {"title": "Graph Theory"}}::vertex
--- Statement 3 ---
(no results)
The REPL automatically detects return clauses and adjusts column definitions for optimal display:
# Single column - uses default formatting
MATCH (n) RETURN n
# Multiple columns - creates appropriate column definitions
MATCH (n)-[r]->(m) RETURN n as source, r as relationship, m as target
# Complex expressions work automatically
MATCH (n) RETURN n.name as name, count(*) as degreeEnable logging to see the internal interactions:
mstate> \log on
Logging enabled.
mstate> show all nodes
[TOOL] MATCH (n) RETURN n
[DB] result
{"id": 844424930131969, "label": "TestNode", "properties": {"msg": "Hello, AGE!"}}::vertex
{"id": 1125899906842625, "label": "TargetNode", "properties": {"effect": "Booh!"}}::vertex
[LLM] Here are all the nodes currently in the database:
1. **TestNode** (id: 844424930131969)
- Properties: {msg: "Hello, AGE!"}
2. **TargetNode** (id: 1125899906842625)
- Properties: {effect: "Booh!"}
These are the 2 nodes currently stored in your graph database.
Set these in your .env file or environment:
# Database connection
PGHOST=localhost
PGPORT=5432
PGDATABASE=postgres
PGUSER=postgres
PGPASSWORD=secret
# Graph name
AGE_GRAPH=mindstate
# OpenAI settings (for LLM mode)
OPENAI_API_KEY=your_api_key_here
OPENAI_MODEL_NAME=gpt-4.1
OPENAI_TEMPERATURE=0
# API settings
MS_API_HOST=127.0.0.1
MS_API_PORT=8000
# Embedding settings for remember/recall/context
MS_EMBEDDING_PROVIDER=openai
MS_EMBEDDING_MODEL=text-embedding-3-small
MS_EMBEDDING_DIMENSIONS=1536mstate [options] [files...]
Options:
-h, --help Show help message
-e, --execute Execute files and exit (no REPL)
-t, --tui Launch Textual TUI
--api Run FastAPI service
--api-host HOST API host bind address
--api-port PORT API port
-s FILE, --system-prompt FILE
Path to custom system prompt file- Be specific: "Show me all Person nodes with their relationships" is better than "show me stuff"
- Use domain language: The AI understands graph terminology like "nodes", "relationships", "paths"
- Ask for explanations: "Explain what this query does: MATCH (n)-[r*2..3]->(m) RETURN n, m"
- Semicolons are optional: Both
MATCH (n) RETURN nandMATCH (n) RETURN n;work - Multiple statements: Separate with semicolons for batch execution
- Path queries: Use
MATCH p = (a)-[r]->(b) RETURN pto see full path information - Column aliases: Use
RETURN n as node, r as relfor cleaner output
- Use
LIMITclauses for large datasets - Index frequently queried properties
- In LLM mode, complex queries may take longer due to AI processing time
- Direct mode provides immediate query execution
-
"return row and column definition list do not match"
- This is automatically handled by smart column detection
- If you see this error, report it as a bug
-
Connection errors
- Check your
.envfile database settings - Ensure PostgreSQL with AGE is running
- Verify the graph exists:
SELECT * FROM ag_graph;
- Check your
-
LLM not responding
- Check your
OPENAI_API_KEYenvironment variable - Verify internet connectivity
- Switch to direct mode with
\llm off
- Check your
-
Remember fails with embedding unavailable
- Set
MS_EMBEDDING_PROVIDER=localfor local/dev testing - Or provide valid OpenAI/Azure embedding credentials in
.env
- Set
Use logging to debug issues:
mstate> \log on
mstate> \llm off
mstate> YOUR_PROBLEMATIC_QUERY_HERE
This will show you exactly what's being sent to the database.
The examples/ directory contains sample Cypher files you can load:
mstate examples/schema.dsl examples/data.dslThese demonstrate various graph patterns and queries you can use as starting points for your own graphs.
Copyright (c) 2025, Iwan van der Kleijn
This project is licensed under the MIT License. See the LICENSE file for details.