
AI agents are increasingly being asked to inspect schemas, write queries, recover from errors, and work with real databases. One common way to support that is through MCP-style tool calling, where actions such as listing tables, describing schemas, and executing queries are exposed as separate tools. That approach works, but for database tasks it can create extra back-and-forth: when a query fails, the agent often needs additional tool calls just to inspect schema and recover.
On the other hand, with its natural advantages are composability and lower cost of trial-and-rror, CLI provides a more direct way to interact with agents. seekdb-cli is a database command-line client designed for agent use. It goes beyond SQL execution with structured output, schema inspection, data profiling, and repair-friendly error responses, so agents can interact with databases more reliably.
In this post, we’ll look at why that matters and how seekdb-cli works in practice with OpenClaw.
This is not a claim that CLI should replace every other integration pattern. In many systems, APIs and tool protocols are the right abstraction. But for database-oriented agent workflows, a CLI has a few practical advantages.
First, a CLI is naturally composable. Its output can be piped, filtered, transformed, or reduced before being passed back into a model. That makes it easier to return only the information the agent actually needs, instead of repeatedly feeding full outputs back into context.
Second, a CLI supports a clean stateless execution model. Each invocation does one thing and returns one result. That makes it easier for an agent to plan, retry, and compose commands predictably, without depending on a long interactive session.
Third, a CLI can make error recovery much cheaper. In MCP-style database interaction, schema inspection, query execution, and repair are often split across separate tool calls. When a query fails, the agent may need several additional calls just to inspect the schema and gather enough context to fix the mistake. A CLI can collapse that loop by returning both the error and the repair clues in the same response.
For example, suppose an agent is asked to find the top 5 users by order amount in the past week, but it uses the wrong column name in the first query.
The key idea is simple: for agent workflows, a good CLI is not just a human terminal tool. It is a stable machine interface.
Most database CLIs were designed for human operators. They work well for interactive use, but they are not ideal for automation by language models.
A typical database client can execute SQL and print an ASCII table. For a human, that is usually enough. For an agent, it is not. Before writing correct SQL, the agent often needs to answer a few questions:
Traditional CLIs rarely package that information into a machine-friendly flow. That gap is what seekdb-cli is meant to address.
seekdb is an intelligent database that supports vector search, full-text search, hybrid search, and in-database AI inference. Its SQL experience is close to MySQL, while adding search and AI-oriented capabilities for modern application workflows.
seekdb-cli is the command-line client for working with seekdb in both human and agent workflows. It can also be used for SQL access with OceanBase’s MySQL-compatible mode and MySQL databases, although some advanced capabilities are specific to seekdb itself.
The goal of seekdb-cli is not just to “run SQL from the terminal.” It is to provide an interface that agents can call repeatedly and reliably.
Compared to traditional CLIs, seekdb-cli incorporates a series of designs specifically for AI Agents:
| Design Decision | Traditional CLI Approach | seekdb-cli Approach |
| Output Format | Prettified tables | JSON by default, machine-parseable |
| Interaction Mode | Interactive REPL | Stateless, one command per result |
| Error Handling | Print error message and wait | Structured error codes + automatically attach relevant schema info on error |
| Data Security | Relies on user discretion | Automatic row limits, write protection, sensitive field masking |
| Self-Description | --help text | ai-guide outputs structured JSON usage guide |
seekdb-cli is designed around four principles that matter in agent-driven workflows.
Commands return structured JSON by default, so agents can consume results directly instead of trying to parse terminal-oriented formatting.
seekdb schema tables
# Output:
{
"ok": true,
"data": [
{"name": "orders", "columns": 4, "rows": 3}
]
}
seekdb-cli includes a set of default guardrails aimed at common operational risks.
{
"ok": false,
"error": {
"code": "LIMIT_REQUIRED",
"message": "Query returns more than 100 rows. Please add LIMIT to your SQL."
}
}# Read operation, executes directly
seekdb sql "SELECT * FROM orders LIMIT 10"
# Write operation, requires --write
seekdb sql --write "INSERT INTO orders (user_id, amount, status, phone) VALUES (4, 199.00, 'paid', '13700000001')"
# Dangerous operation, blocked even with --write
seekdb sql --write "DELETE FROM orders" # ← No WHERE clause, blocked!Note:
These protections improve the safety of agent workflows, but they do not replace database-side controls such as RBAC, least-privilege credentials, network isolation, or auditing.
One of the most expensive parts of agent-driven database work is not query generation itself. It is the recovery loop after a failed query.
If a tool returns only a raw SQL error, the agent, the agent often has to make one or more follow-up calls just to discover what went wrong. That can mean checking table names, describing the schema, and then retrying the query. In longer workflows, those extra recovery steps add up quickly.
seekdb-cli is designed to make that loop shorter. When SQL execution fails, the response can include enough local schema context for the agent to repair the query immediately.
For example, if the problem is a wrong column name, the response can include the valid columns for the relevant table:
seekdb sql "SELECT price FROM orders LIMIT 5"
#sample output:
{
"ok": false,
"error": {
"code": "SQL_ERROR",
"message": "execute sql failed 1054 Unknown column 'price' in 'field list'"
},
"schema": {
"table": "orders",
"columns": ["id", "user_id", "amount", "status", "phone"],
"indexes": ["PRIMARY(id)"]
}
}If the problem is a wrong table name, the response can include nearby table candidates:
seekdb sql "SELECT * FROM ordes LIMIT 5"
#sample output:
{
"ok": false,
"error": {
"code": "SQL_ERROR",
"message": "execute sql failed 1146 Table 'test.ordes' doesn't exist"
},
"schema": {
"tables": ["orders"]
}
}Wrong table name? Returns all available table names:
seekdb sql "SELECT * FROM ordes LIMIT 5"
#sample output:
{"ok": false, "error": {"code": "SQL_ERROR", "message": "execute sql failed 1146 Table 'test.ordes' doesn't exist"}, "schema": {"tables": ["orders"]}}
When an AI Agent sees errors like these, it can immediately self-correct by rewriting the SQL based on the returned schema information, without needing an additional "view table structure" command. This significantly reduces the number of steps AI needs to complete a task.
Running seekdb ai-guide outputs a complete JSON usage guide, including all commands, parameters, recommended workflows, and security rules. An AI Agent only needs to execute this command once to "learn" the entire tool:
This is exactly the "AI-readable documentation" that Karpathy mentioned—not a README for humans, but a structured guide for AI to parse.
To validate seekdb-cli's value in AI Agents, the following demonstrates two common scenarios using seekdb-cli in OpenClaw. You simply tell OpenClaw what you want to do in natural language, and it automatically calls seekdb-cli to complete the task.
pip install seekdb-cliAfter installation, the seekdb command becomes available.By default, seekdb-cli uses the local embedded database at ~/.seekdb/seekdb.db, so you can get started without additional setup. Embedded mode currently supports Linux and macOS 15+, while Windows support is not yet available.
2. Connect to a remote database. you can configure a DSN through a config file, an environment variable, or a command-line flag:
mkdir -p ~/.seekdb
# Connect to remote seekdb / OceanBase / MySQL
echo 'SEEKDB_DSN="seekdb://user:password@host:port/database"' > ~/.seekdb/config.env
# Or use embedded mode (specify local database path)
echo 'SEEKDB_DSN="embedded:/path/to/mydata"' > ~/.seekdb/config.envYou can also point it to a local embedded database directory:
3. Verify the connection:
seekdb status
#Sample output
{
"ok": true,
"data": {
"cli_version": "0.1.0",
"mode": "embedded",
"server_version": "...",
"database": "test",
"connected": true
}
}4. Try it out. Create a table, insert some data, and query:
seekdb sql --write "CREATE TABLE orders (id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, amount DECIMAL(10,2), status VARCHAR(20), phone VARCHAR(20))"
seekdb sql --write "INSERT INTO orders (user_id, amount, status, phone) VALUES (1, 99.00, 'paid', '13812345678'), (2, 256.50, 'paid', '13987654321'), (1, 38.00, 'pending', '13612341234')"
seekdb sql "SELECT * FROM orders LIMIT 10"By default, the output is JSON, which makes it easy to parse programmatically. For human inspection, add --format table:
seekdb --format table sql "SELECT * FROM orders LIMIT 10"At this point, the value of seekdb-cli is not just that it can run SQL. It is that the same interface can also inspect schema, profile data, and help an agent recover from mistakes.
On Linux or macOS 15 and above, seekdb-cli defaul ts to using ~/.seekdb/seekdb.db as the embedded database, requiring no configuration—just write data directly:
# Verify connection (automatically uses default database ~/.seekdb/seekdb.db)
seekdb statusIf you want to use a different database directory path, you can specify it via global configuration:
mkdir -p ~/.seekdb
echo 'SEEKDB_DSN="embedded:/path/to/your/data/directory"' > ~/.seekdb/config.envThe examples below use the default database directly.
Create tables: Users, Products, and Orders
# Users table
seekdb sql --write "
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
email VARCHAR(100),
phone VARCHAR(20),
city VARCHAR(20),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)"
# Products table
seekdb sql --write "
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
category VARCHAR(50),
price DECIMAL(10,2),
stock INT
)"
# Orders table (linked to users and products)
seekdb sql --write "
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
product_id INT,
quantity INT,
amount DECIMAL(10,2),
status VARCHAR(20),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)"Insert test data
# User data (10 users from different cities)
seekdb sql --write "
INSERT INTO users (name, email, phone, city) VALUES
('Zhang San', 'zhang@example.com', '13812345678', 'Beijing'),
('Li Si', 'li@example.com', '13987654321', 'Shanghai'),
('Wang Wu', 'wang@example.com', '13600000001', 'Guangzhou'),
('Zhao Liu', 'zhao@example.com', '13700000002', 'Shenzhen'),
('Chen Qi', 'chen@example.com', '13800000003', 'Beijing'),
('Zhou Ba', 'zhou@example.com', '13900000004', 'Shanghai'),
('Wu Jiu', 'wu@example.com', '13600000005', 'Chengdu'),
('Zheng Shi', 'zheng@example.com', '13700000006', 'Hangzhou'),
('Feng Shiyi', 'feng@example.com', '13800000007', 'Beijing'),
('Chu Shier', 'chu@example.com', '13900000008', 'Shanghai')"
# Product data (different categories)
seekdb sql --write "
INSERT INTO products (name, category, price, stock) VALUES
('Wireless Bluetooth Earbuds', 'Electronics', 299.00, 500),
('Mechanical Keyboard', 'Electronics', 599.00, 200),
('Ergonomic Chair', 'Furniture', 1299.00, 80),
('Coffee Beans (500g)', 'Food', 89.00, 1000),
('Sports Water Bottle', 'Outdoor', 59.00, 800),
('Laptop Stand', 'Electronics', 199.00, 300),
('Noise-Canceling Headphones', 'Electronics', 999.00, 150),
('Yoga Mat', 'Outdoor', 149.00, 400)"
# Order data (past 30 days, simulating realistic distribution)
seekdb sql --write "
INSERT INTO orders (user_id, product_id, quantity, amount, status, created_at) VALUES
(1, 2, 1, 599.00, 'paid', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(1, 7, 1, 999.00, 'paid', DATE_SUB(NOW(), INTERVAL 3 DAY)),
(2, 1, 2, 598.00, 'paid', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(2, 3, 1, 1299.00, 'paid', DATE_SUB(NOW(), INTERVAL 5 DAY)),
(3, 4, 3, 267.00, 'paid', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(3, 5, 2, 118.00, 'pending', DATE_SUB(NOW(), INTERVAL 6 DAY)),
(4, 6, 1, 199.00, 'paid', DATE_SUB(NOW(), INTERVAL 4 DAY)),
(4, 2, 1, 599.00, 'paid', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(5, 1, 1, 299.00, 'paid', DATE_SUB(NOW(), INTERVAL 3 DAY)),
(5, 8, 2, 298.00, 'refund', DATE_SUB(NOW(), INTERVAL 7 DAY)),
(6, 7, 1, 999.00, 'paid', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(7, 3, 1, 1299.00, 'paid', DATE_SUB(NOW(), INTERVAL 4 DAY)),
(8, 4, 5, 445.00, 'paid', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(9, 2, 2, 1198.00, 'paid', DATE_SUB(NOW(), INTERVAL 6 DAY)),
(10,1, 1, 299.00, 'pending', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(1, 5, 3, 177.00, 'paid', DATE_SUB(NOW(), INTERVAL 15 DAY)),
(2, 8, 1, 149.00, 'paid', DATE_SUB(NOW(), INTERVAL 20 DAY)),
(3, 6, 2, 398.00, 'paid', DATE_SUB(NOW(), INTERVAL 25 DAY))"seekdb-cli is useful directly from the terminal, but its design is especially helpful in agent workflows.
To make integration easier, seekdb-cli comes with a companion Agent Skill. Once installed, OpenClaw can discover the tool and use it for database-related tasks without requiring a hand-written prompt every time.
pip install seekdb-agent-skills2.Run the seekdb-agent-skills interactive installer
seekdb-agent-skills3. In the interactive installer, you through selecting an AI tool, select OpenClaw > seekdb-cli to automatically deploy the skill.

After installation, when you make database-related requests to the AI Agent in natural language (like "help me see what tables are in the database"), the Agent automatically loads the seekdb-cli skill and calls the appropriate commands to complete the task.
Open OpenClaw and start conversing in natural language. Now let's move to the practical scenarios.
You say: "Could you please use seekdb-cli to help me check what tables are in the seekdb database, analyze the data distribution, and identify any associations between the tables?"
OpenClaw automatically executes the following:
# Step 1: See what tables exist
seekdb schema tables{"ok": true, "data": [
{"name": "users", "columns": 6, "rows": 10},
{"name": "products", "columns": 5, "rows": 8},
{"name": "orders", "columns": 7, "rows": 18}
]}# Step 2: View detailed structure of orders table
seekdb schema describe orders{"ok": true, "data": {"table": "orders", "columns": [
{"name": "id", "type": "INT", "nullable": false, "key": "PRI"},
{"name": "user_id", "type": "INT", "nullable": true},
{"name": "product_id", "type": "INT", "nullable": true},
{"name": "quantity", "type": "INT", "nullable": true},
{"name": "amount", "type": "DECIMAL(10,2)","nullable": true},
{"name": "status", "type": "VARCHAR(20)", "nullable": true},
{"name": "created_at", "type": "DATETIME", "nullable": true}
]}}# Step 3: Profile the order data distribution
seekdb table profile orders{"ok": true, "data": {"table": "orders", "row_count": 18,
"columns": [
{"name": "status", "null_rate": 0, "distinct": 3,
"top_values": [{"value": "paid", "count": 14}, {"value": "pending", "count": 2}, {"value": "refund", "count": 1}]},
{"name": "amount", "null_rate": 0, "min": 118.00, "max": 1299.00, "avg": 513.17}
]
}}# Step 4: Infer relationships between tables
seekdb relations infer{"ok": true, "data": {"relations": [
{"from": "orders.user_id", "to": "users.id", "confidence": 0.95},
{"from": "orders.product_id", "to": "products.id", "confidence": 0.92}
]}}OpenClaw parses the results and provides you with a clear database overview, including table structures, data distribution, and inter-table relationships.

You say: "Find the top 5 users with the highest order amounts in the past week, and list their names and cities for me"
OpenClaw executes (in two steps):
# Step 1: First query Top 5 user IDs and amounts
seekdb sql "
SELECT user_id, SUM(amount) AS total
FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
AND status = 'paid'
GROUP BY user_id
ORDER BY total DESC
LIMIT 5"{"ok": true, "columns": ["user_id", "total"], "rows": [
{"user_id": 2, "total": "1897.00"},
{"user_id": 9, "total": "1198.00"},
{"user_id": 7, "total": "1299.00"},
{"user_id": 6, "total": "999.00"},
{"user_id": 1, "total": "598.00"}
], "time_ms": 12}# Step 2: Join with users table to get names and cities
seekdb sql "
SELECT u.name, u.city, SUM(o.amount) AS total
FROM orders o JOIN users u ON o.user_id = u.id
WHERE o.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
AND o.status = 'paid'
GROUP BY u.id, u.name, u.city
ORDER BY total DESC
LIMIT 5"{
"ok": true,"columns": ["name", "city", "total"],"rows": [
{"name": "Li Si", "city": "Shanghai", "total": "1897.00"},
{"name": "Zhang San", "city": "Beijing", "total": "1598.00"},
{"name": "Wu Jiu", "city": "Chengdu", "total": "1299.00"},
{"name": "Feng Shiyi", "city": "Beijing", "total": "1198.00"},
{"name": "Zhou Ba", "city": "Shanghai", "total": "999.00"}
],
"affected": 0,
"time_ms": 0
}If the SQL is wrong (e.g., user_id written as uid), seekdb-cli returns error information with schema details, and OpenClaw automatically corrects and retries—you might not even notice the whole process.

You say: "Search for content about 'deployment' in the product_docs collection"
OpenClaw executes:
# Step 1: Confirm collection status
seekdb collection info product_docs{"ok": true, "data": {"name": "product_docs", "count": 5, "dimension": 384, "distance": "cosine"}}# Step 2: Semantic search
seekdb query product_docs --text "deployment" --limit 5{
"ok": true,
"data": {
"results": [
{
"id": "doc1",
"score": 0.3983,
"document": "seekdb Deployment Guide: Supports one-click Docker deployment or manual installation via binary packages. Docker Compose is recommended for production environments.",
"metadata": {}
},
{
"id": "doc5",
"score": 0.0458,
"document": "seekdb Data Backup: Supports both logical and physical backup methods. For production environments, daily full backups combined with binlog for PITR are recommended.",
"metadata": {}
},
{
"id": "doc2",
"score": -0.0383,
"document": "seekdb Vector Index Principles: Uses the HNSW algorithm to build vector indices, supporting various distance metrics including cosine similarity and Euclidean distance.",
"metadata": {}
},
{
"id": "doc4",
"score": -0.0442,
"document": "seekdb Performance Tuning: Properly setting the HNSW ef_construction parameter helps balance indexing speed and search accuracy.",
"metadata": {}
},
{
"id": "doc3",
"score": -0.0861,
"document": "seekdb Hybrid Search: Combines semantic vector search and BM25 full-text search results through the RRF algorithm, achieving better recall and precision than single search methods.",
"metadata": {}
}
],
"count": 5
},
"time_ms": 5382
}--mode hybrid combines semantic vector search and BM25 full-text search results, achieving higher recall than single methods.

seekdb-cli naturally supports Unix pipes and can be freely combined with other tools:
# Use jq to filter Shanghai users from query results
seekdb sql "SELECT * FROM users LIMIT 100" | jq '.rows[] | select(.city == "Shanghai") | {name, email}'
# Calculate total paid order amounts by city, output as CSV
seekdb sql "SELECT u.city, SUM(o.amount) as gmv FROM orders o JOIN users u ON o.user_id=u.id WHERE o.status='paid' GROUP BY u.city ORDER BY gmv DESC LIMIT 10" \
| jq -r '["city","gmv"], (.rows[] | [.city, .gmv]) | @csv'
# Import data into collection
cat docs.jsonl | seekdb add product_docs --stdinThis is the composability that Peter Steinberger talked about—each CLI does one thing, flexibly combined through pipes, far more flexible than a "do-everything" API.

For agent-driven database work, the bottleneck is often not SQL execution itself. It is the loop around it: discovering schema, interpreting results, recovering from errors, and keeping context under control.
seekdb-cli is designed for that loop.
Its core ideas are straightforward:
That combination makes seekdb-cli more than a convenience wrapper around SQL. It makes it a practical interface for database automation in the agent era.
Give it a try:
pip install seekdb-cliProject repository: seekdb-cli