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

David Zhang
David Zhang
Published on March 17, 2026
9 minute read
Key Takeaways
  • For agent-driven database work, CLI beats MCP-style tool calling: stateless execution, composable output via pipes, and cheaper error recovery because the failed response can include schema context for immediate self-correction.
  • seekdb-cli is a database CLI designed for agents — JSON output by default, safety guardrails (row limits, write protection, sensitive field masking), and a self-description endpoint (ai-guide) that lets any LLM learn the entire tool in one call.
  • Two real-world scenarios with OpenClaw demonstrate the workflow: exploring an unfamiliar database (schema → profile → relations → query) and composing CLI commands through Unix pipes.
  • 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 directlyseekdb sql "SELECT * FROM orders LIMIT 10"# Write operation, requires --writeseekdb sql --write "INSERT INTO orders (user_id, amount, status, phone) VALUES (4, 199.00, 'paid', '13700000001')"# Dangerous operation, blocked even with --writeseekdb 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

  • 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 / MySQLecho '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 ~/.seekdbecho '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 tableseekdb 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 tableseekdb 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.

  • 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 existseekdb 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 tableseekdb 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 distributionseekdb 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 tablesseekdb 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 amountsseekdb sql "SELECT user_id, SUM(amount) AS totalFROM ordersWHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)  AND status = 'paid'GROUP BY user_idORDER BY total DESCLIMIT 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 citiesseekdb sql "SELECT u.name, u.city, SUM(o.amount) AS totalFROM orders o JOIN users u ON o.user_id = u.idWHERE o.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)  AND o.status = 'paid'GROUP BY u.id, u.name, u.cityORDER BY total DESCLIMIT 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 statusseekdb collection info product_docs
    {"ok": true, "data": {"name": "product_docs", "count": 5, "dimension": 384, "distance": "cosine"}}
    # Step 2: Semantic searchseekdb 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 resultsseekdb sql "SELECT * FROM users LIMIT 100" | jq '.rows[] | select(.city == "Shanghai") | {name, email}'# Calculate total paid order amounts by city, output as CSVseekdb 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 collectioncat 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


    Share
    X
    linkedin
    mail