More Efficient Than MCP: CLI Is the Right Way for AI Agents to Call External Tools

右侧logo

oceanbase database

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.

Why a CLI works well for agent workflows

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.

  • With MCP, the recovery path may look like this: inspect tables, run the wrong query, get an error, describe the table, inspect another table, then retry with the corrected SQL.
  • With a CLI, the failed query can return the error together with the relevant column names, allowing the agent to fix the SQL in the very next step. The result is fewer recovery turns, less repeated schema context, and a tighter feedback loop for the model.

The key idea is simple: for agent workflows, a good CLI is not just a human terminal tool. It is a stable machine interface.

What traditional database CLIs are missing for agents

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:

  • What tables exist?
  • What columns does each table contain?
  • How is data distributed?
  • Which columns are likely related across tables?
  • If a query fails, what is the fastest path to repair?

Traditional CLIs rarely package that information into a machine-friendly flow. That gap is what seekdb-cli is meant to address.

seekdb-cli: A Database Command-Line Client Designed for AI Agents

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.

How seekdb-cli differs from a traditional database CLI

Compared to traditional CLIs, seekdb-cli incorporates a series of designs specifically for AI Agents:

Design DecisionTraditional CLI Approachseekdb-cli Approach
Output FormatPrettified tablesJSON by default, machine-parseable
Interaction ModeInteractive REPLStateless, one command per result
Error HandlingPrint error message and waitStructured error codes + automatically attach relevant schema info on error
Data SecurityRelies on user discretionAutomatic row limits, write protection, sensitive field masking
Self-Description--help textai-guide outputs structured JSON usage guide

Design principles behind seekdb-cli

seekdb-cli is designed around four principles that matter in agent-driven workflows.

Machine-readable by default

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}
  ]
}

Safety guardrails for agent-driven database access

seekdb-cli includes a set of default guardrails aimed at common operational risks.

  • Row Limit Protection: Queries without LIMIT that return more than 100 rows are automatically blocked:
{
  "ok": false,
  "error": {
    "code": "LIMIT_REQUIRED",
    "message": "Query returns more than 100 rows. Please add LIMIT to your SQL."
  }
}
  • Write Protection: Write operations require explicit opt-in with --write. Dangerous statements such as DELETE or UPDATE without a WHERE clause, along with DROP and TRUNCATE, are blocked even when write mode is enabled.
# 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!
  • Sensitive Field Masking: Columns with names such as phone, email, password, or id_card are masked automatically in result output, reducing the risk of exposing sensitive values in agent transcripts or logs.
  • Operation Log: All commands are logged to ~/.seekdb/sql-history.json, with sensitive literals in SQL automatically masked.
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.

Let AI fix its own errors

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.

ai-guide — a self-description endpoint for agents

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.

Real-World Scenarios Using seekdb-cli in OpenClaw

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.

Install and connect to seekdb

  1. Install seekdb using the one liner:
pip install seekdb-cli

 After 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.env

You 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.

Prepare test data

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 status

If 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.env

The 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))"

Using seekdb-cli with OpenClaw

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.

  1. Install the seekdb-agent-skills Python package
pip install seekdb-agent-skills

2.Run the seekdb-agent-skills interactive installer

seekdb-agent-skills

3. In the interactive installer, you through selecting an AI tool, select OpenClaw > seekdb-cli to automatically deploy the skill.

oceanbase database

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.

Scenario 1: Exploring an unfamiliar database

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.

oceanbase database

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.

oceanbase database

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.

oceanbase database

Scenario 2: Combining through pipes

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 --stdin

This 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.

oceanbase database

Conclusion

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:

  • structured JSON output instead of terminal-oriented formatting
  • stateless commands that are easy to automate
  • repair-friendly error responses that help agents recover quickly
  • safety defaults that reduce common operational mistakes
  • shell composability that makes it easy to build larger workflows from small commands

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-cli

Project repository: seekdb-cli


ICON_SHARE
ICON_SHARE
linkedin
Contact Us