Skip to main content
Glama

Database MCP

CI Release License: MIT Docs

A single-binary MCP server for SQL databases. Connect your AI assistant to MySQL/MariaDB, PostgreSQL, or SQLite with zero runtime dependencies.

Website · Documentation · Releases

demo

Features ✨

  • Multi-database — MySQL/MariaDB, PostgreSQL, and SQLite from one binary

  • MCP tools — schema discovery (listDatabases, listTables, listViews, listTriggers, listFunctions, listProcedures, listMaterializedViews), data access (readQuery, writeQuery), DDL (createDatabase, dropDatabase, dropTable), and explainQuery. Read-only mode hides the write tools (writeQuery, createDatabase, dropDatabase, dropTable). See MCP Tools for per-backend availability.

  • Single binary — ~7 MB, no Python/Node/Docker needed

  • Multiple transports — stdio (for Claude Desktop, Cursor) and HTTP (for remote/multi-client)

  • Layered config — DBMCP_* environment variables, an env file, and an optional YAML config file with ${} interpolation, with sensible defaults per backend

  • SSH tunnel — reach MySQL/MariaDB/PostgreSQL through an SSH bastion with an embedded client: password or key auth, known_hosts verification, automatic reconnect

Related MCP server: CentralMind/Gateway

Install 📦

macOS, Linux, WSL:

curl -fsSL https://dbmcp.haymon.ai/install.sh | bash

Windows PowerShell:

irm https://dbmcp.haymon.ai/install.ps1 | iex

Windows CMD:

curl -fsSL https://dbmcp.haymon.ai/install.cmd -o install.cmd && install.cmd && del install.cmd

See the installation docs for Docker, Cargo, and other methods.

Quick Start 🚀

Using .mcp.json (recommended)

Add a .mcp.json file to your project root. MCP clients read this file and configure the server automatically.

Stdio transport — the client starts and manages the server process:

{
  "mcpServers": {
    "dbmcp": {
      "command": "dbmcp",
      "env": {
        "DBMCP_DB_BACKEND": "mysql",
        "DBMCP_DB_HOST": "127.0.0.1",
        "DBMCP_DB_PORT": "3306",
        "DBMCP_DB_USER": "root",
        "DBMCP_DB_PASSWORD": "secret",
        "DBMCP_DB_NAME": "mydb"
      }
    }
  }
}

HTTP transport — you start the server yourself, the client connects to it:

# Start the server first
DBMCP_TRANSPORT=http DBMCP_DB_BACKEND=mysql DBMCP_DB_USER=root DBMCP_DB_NAME=mydb dbmcp
{
  "mcpServers": {
    "dbmcp": {
      "type": "http",
      "url": "http://127.0.0.1:9001/mcp"
    }
  }
}

Note: The "type": "http" field is required for HTTP transport. Without it, clients like Claude Code will reject the config.

To require a bearer token, start the server with DBMCP_HTTP_AUTH_TOKEN and send it from the client:

DBMCP_HTTP_AUTH_TOKEN="$(openssl rand -hex 32)" DBMCP_TRANSPORT=http DBMCP_DB_BACKEND=mysql DBMCP_DB_USER=root DBMCP_DB_NAME=mydb dbmcp
{
  "mcpServers": {
    "dbmcp": {
      "type": "http",
      "url": "http://127.0.0.1:9001/mcp",
      "headers": { "Authorization": "Bearer ${DBMCP_TOKEN}" }
    }
  }
}

Using environment variables

# MySQL/MariaDB
DBMCP_DB_BACKEND=mysql DBMCP_DB_HOST=localhost DBMCP_DB_USER=root DBMCP_DB_NAME=mydb dbmcp

# PostgreSQL
DBMCP_DB_BACKEND=postgres DBMCP_DB_HOST=localhost DBMCP_DB_USER=postgres DBMCP_DB_NAME=mydb dbmcp

# SQLite
DBMCP_DB_BACKEND=sqlite DBMCP_DB_NAME=./data.db dbmcp

# PostgreSQL through an SSH bastion (DB host is resolved by the bastion)
DBMCP_DB_BACKEND=postgres DBMCP_DB_HOST=db.internal DBMCP_DB_USER=postgres DBMCP_DB_NAME=mydb \
DBMCP_SSH_HOST=bastion.example.com DBMCP_SSH_USER=deploy DBMCP_SSH_KEY=~/.ssh/id_ed25519 dbmcp

# HTTP transport (loopback only, no token required)
DBMCP_TRANSPORT=http DBMCP_DB_BACKEND=mysql DBMCP_DB_USER=root DBMCP_DB_NAME=mydb dbmcp

# HTTP transport on all interfaces — a bearer token is REQUIRED
DBMCP_HTTP_AUTH_TOKEN=change-me DBMCP_TRANSPORT=http DBMCP_HTTP_HOST=0.0.0.0 \
DBMCP_DB_BACKEND=mysql DBMCP_DB_USER=root DBMCP_DB_NAME=mydb dbmcp

Warning: Binding a non-loopback address (e.g. DBMCP_HTTP_HOST=0.0.0.0) exposes the database to anyone who can reach the port. dbmcp refuses to start in that case unless DBMCP_HTTP_AUTH_TOKEN is set. The Host/Origin allowlists protect against browser DNS rebinding only — they are not authentication.

Using a config file

# dbmcp.yaml
database:
  backend: postgres
  host: localhost
  user: postgres
  password: ${DB_PASSWORD:?set DB_PASSWORD}
  name: mydb
DB_PASSWORD=secret dbmcp --config-file /path/to/dbmcp.yaml

From an MCP client, pass "args": ["--config-file", "/absolute/path/to/dbmcp.yaml"]; relative paths resolve against the process working directory, which the client controls. See Config File.

Configuration ⚙️

Every setting has a DBMCP_* environment variable and a YAML key. Each setting is resolved with clear precedence:

process environment > env files (--env-file) > config files (--config-file) > defaults

Environment variables are typically set by your MCP client (via env or envFile in the server config). An empty DBMCP_* value counts as unset. Nothing is discovered automatically: a file is read only when you name it.

Flag

Env Variable

Description

--config-file <PATH>

DBMCP_CONFIG_FILE

YAML config file (${} interpolation, strict keys); repeatable (env var: comma list), later wins

--env-file <PATH>

DBMCP_ENV_FILE

Env file of KEY=VALUE lines; repeatable (env var: comma list), later wins

Layer files by repeating a flag, e.g. a shared base plus local overrides: dbmcp --config-file base.yaml --config-file local.yaml. Each setting comes from the last file that sets it; lists replace.

There are no per-setting flags. Booleans accept true/false, 1/0, t/f (not yes/no); lists are comma-separated in env vars.

Security note: Read-only mode is on by default (DBMCP_DB_READ_ONLY=true). The default HTTP Origin allowlist accepts browser pages from any port on localhost/127.0.0.1/[::1]; on shared or development machines, set DBMCP_HTTP_AUTH_TOKEN or narrow DBMCP_HTTP_ALLOWED_ORIGINS to the explicit origins you trust (e.g. http://localhost:5173).

The SSH tunnel is MySQL/MariaDB/PostgreSQL only and uses an embedded SSH client (no ssh binary). DBMCP_DB_HOST/DBMCP_DB_PORT are resolved by the SSH server. With both a key and a password set, the key is tried first. See SSH Tunnel for host-key policies, limitations, and troubleshooting.

MCP Tools 🧩

listDatabases

Lists accessible databases, paginated via cursor / nextCursor. See Cursor Pagination for iteration details. Not available for SQLite.

listTables

Lists tables in a database, paginated via cursor / nextCursor. See Cursor Pagination for iteration details.

Parameters: database (defaults to the active database; SQLite has no database parameter), cursor, search, detailed.

search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards — pass users% to match names beginning with users, or %order% for substring matching. A bare word with no wildcards matches only an exact table name.

detailed (default false) switches the response shape:

  • Brief (default) — tables is a sorted JSON array of bare table-name strings.

  • Detailed (detailed: true) — tables is a JSON object keyed by table name; each value carries the table's schema, kind, owner, comment, columns[], constraints[], indexes[], and triggers[]. One call returns both the table list and the per-table metadata.

listViews

Lists views in a database, paginated via cursor / nextCursor. Available on MySQL/MariaDB, PostgreSQL (public schema), and SQLite. Parameters: database (defaults to the active database; SQLite has no database parameter), cursor, search, detailed. SQLite returns the brief shape only — search and detailed are not accepted there.

search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards. The search value must remain identical across paginated calls for cursor continuity.

detailed (default false) switches the response shape:

  • Brief (default) — views is a sorted JSON array of bare view-name strings. View names are unique per schema, so no duplicates appear.

  • Detailed (detailed: true) — views is a JSON object keyed by bare view name; each value carries the per-backend metadata payload. PostgreSQL exposes schema, owner, description, definition. MySQL/MariaDB exposes schema, definer, security, checkOption, updatable, characterSetClient, collationConnection, definition. See the listViews reference for source columns, enumerated value sets, and intentional omissions per backend.

See Cursor Pagination for iteration details.

listTriggers

Lists user-defined triggers on tables, paginated via cursor / nextCursor. Internal constraint and foreign-key triggers are excluded. Available on MySQL/MariaDB, PostgreSQL (public schema), and SQLite. Parameters: database (defaults to the active database; SQLite has no database parameter), cursor, search, detailed.

search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards. The search value must remain identical across paginated calls for cursor continuity.

detailed (default false) switches the response shape:

  • Brief (default) — triggers is a sorted JSON array of bare trigger-name strings.

  • Detailed (detailed: true) — triggers is a JSON object keyed by trigger name; each value carries the per-backend metadata payload (timing, events, definition, and backend-specific extras like PostgreSQL status/functionName or MySQL/MariaDB session-context fields). See the listTriggers reference for the full per-backend field list.

See Cursor Pagination for iteration details.

listFunctions

Lists user-defined SQL functions, paginated via cursor / nextCursor. PostgreSQL excludes aggregates, window functions, and procedures; MySQL/MariaDB excludes loadable UDFs (mysql.func). Available on MySQL/MariaDB and PostgreSQL (public schema). Not available for SQLite. Parameters: database (defaults to the active database), cursor, search, detailed.

search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards. The search value must remain identical across paginated calls for cursor continuity.

detailed (default false) switches the response shape:

  • Brief (default) — functions is a sorted JSON array of bare function-name strings. PostgreSQL overloads appear once per overload (duplicate name strings are expected).

  • Detailed (detailed: true) — functions is a JSON object keyed by function signature; each value carries the per-backend metadata payload (language, arguments, return type, definition, and backend-specific extras such as PostgreSQL volatility/strict/parallelSafety or MySQL/MariaDB session-context fields). PostgreSQL keys are name(arguments) (overloads disambiguate); MySQL/MariaDB keys are bare names (no overloading). See the listFunctions reference for the full per-backend field list.

See Cursor Pagination for iteration details.

listProcedures

Lists user-defined stored procedures, paginated via cursor / nextCursor. Available on MySQL/MariaDB and PostgreSQL (public schema, PostgreSQL 11+). Not available for SQLite. Parameters: database (defaults to the active database), cursor, search, detailed.

search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards. The search value must remain identical across paginated calls for cursor continuity.

detailed (default false) switches the response shape:

  • Brief (default) — procedures is a sorted JSON array of bare procedure-name strings. PostgreSQL overloads appear once per overload (duplicate name strings are expected).

  • Detailed (detailed: true) — procedures is a JSON object keyed by procedure signature; each value carries the per-backend metadata payload (language, arguments, security, definition, and backend-specific extras such as PostgreSQL owner or MySQL/MariaDB deterministic/sqlDataAccess/session-context fields). PostgreSQL keys are name(arguments) (overloads disambiguate; zero-arg procedures key as name()); MySQL/MariaDB keys are bare names (no overloading). See the listProcedures reference for the full per-backend field list.

See Cursor Pagination for iteration details.

listMaterializedViews

Lists materialized views in the public schema, paginated via cursor / nextCursor. PostgreSQL only — not available for MySQL/MariaDB or SQLite. Parameters: database (defaults to the active database), cursor, search, detailed.

search is an optional case-insensitive ILIKE pattern with % (any sequence) and _ (single character) as wildcards. SQL meta-characters (', ;, --) are bound as parameter values and never interpolated. The search value must remain identical across paginated calls for cursor continuity.

detailed (default false) switches the response shape:

  • Brief (default) — materializedViews is a sorted JSON array of bare matview-name strings. Matview names are unique per schema, so no duplicates appear.

  • Detailed (detailed: true) — materializedViews is a JSON object keyed by bare matview name; each value carries schema, owner, description (or null when no COMMENT ON MATERIALIZED VIEW), definition (the SELECT body verbatim from pg_matviews.definition), populated (false for matviews created WITH NO DATA and never refreshed), and indexed (true when at least one index exists; REFRESH MATERIALIZED VIEW CONCURRENTLY additionally requires a unique index). Detailed mode deliberately omits column metadata, tablespace, storage parameters, and unique-index detection — recoverable via definition, listTables(detailed=true), or readQuery against pg_indexes. See the listMaterializedViews reference for source columns and operational semantics.

See Cursor Pagination for iteration details.

readQuery

Executes a read-only SQL query (SELECT, SHOW, DESCRIBE, USE, EXPLAIN). Always enforces SQL validation as defence-in-depth. Parameters: query, database, cursor. SELECT results paginate via cursor / nextCursor; SHOW, DESCRIBE, USE, and EXPLAIN return a single page and ignore cursor. See Cursor Pagination for iteration details.

writeQuery

Executes a write SQL query (INSERT, UPDATE, DELETE, CREATE, ALTER, DROP). Only available when read-only mode is disabled. Parameters: query, database.

createDatabase

Creates a database if it doesn't exist. Only available when read-only mode is disabled. Not available for SQLite. Parameters: database.

dropDatabase

Drops an existing database. Refuses to drop the currently connected database. Only available when read-only mode is disabled. Not available for SQLite. Parameters: database.

dropTable

Drops a table from a database. If the table has foreign key dependents, the database error is surfaced to the user. On PostgreSQL, a cascade parameter is available to force the drop with CASCADE. Only available when read-only mode is disabled. Parameters: database, table, cascade (PostgreSQL only).

explainQuery

Returns the execution plan for a SQL query. Supports an optional analyze parameter for actual execution statistics (PostgreSQL and MySQL/MariaDB). In read-only mode, EXPLAIN ANALYZE is only allowed for read-only statements since it actually executes the query. In read-only mode, every EXPLAIN runs inside a read-only transaction, so on MySQL/MariaDB plain EXPLAIN of write statements is rejected. SQLite uses EXPLAIN QUERY PLAN (no ANALYZE support). Always available regardless of read-only mode. Parameters: query, database, analyze (PostgreSQL/MySQL only).

Security 🔒

  • Read-only mode (default) — write tools hidden from AI assistant; readQuery enforces AST-based SQL validation

  • Single-statement enforcement — multi-statement injection blocked at parse level

  • Dangerous function blocking — LOAD_FILE(), INTO OUTFILE, INTO DUMPFILE detected in the AST

  • Identifier validation — database/table names validated against control characters and empty strings

  • HTTP bearer authentication — DBMCP_HTTP_AUTH_TOKEN enforces Authorization: Bearer <token> (constant-time compare, 401 on failure); non-loopback binds refuse to start without a token

  • Origin + Host allowlists — server-side rejection (403) plus CORS preflight; configurable for HTTP transport. Defends against browser DNS rebinding — not a substitute for authentication

  • SSL/TLS — configured via individual DBMCP_DB_SSL_* variables

  • SSH host-key verification — the SSH tunnel checks the server key against known_hosts (yes by default); SSH passwords and passphrases are never logged

  • PII redaction (opt-in, off by default) — when enabled, query tool output passes through a regex-based redactor that rewrites detected PII spans across 46 built-in entity types spanning seven categories: personal (email), financial (cards, IBAN, UK bank accounts, sort and US ABA routing codes, CVV), government IDs (SSN, ITIN, EIN, UK/US passports, NHS, NINO, SIN, VAT), contact (phone), network (IP, URL, MAC), digital identity (API keys, JWTs, PEM private keys, password hashes), and crypto wallets. Toggle: DBMCP_PII_ENABLE (pii.enabled). Operator: DBMCP_PII_OPERATOR (pii.operator) — one of replace (default, entity-aware placeholders like <EMAIL_ADDRESS>), mask (length-preserving *), redact (drop), hash (SHA-256 hex). Optional subset via DBMCP_PII_CATEGORIES (pii.categories) (comma-separated, e.g. financial,government); unset enables all built-ins. Scope: query tool output payloads only. See PII configuration for the full surface.

  • ML/NER redaction (opt-in at runtime, off by default) — adds PERSON, LOCATION, ORGANIZATION, NATIONALITY_RELIGION_POLITICS, and FACILITY detection that regex cannot catch, enabled via the DBMCP_PII_NER_ENABLE (pii.ner_enabled) toggle plus a user-supplied model directory. Which entities are produced depends on the model's labels (CoNLL models give person/location/organization; OntoNotes-class models add NRP and facility). Inference uses ONNX Runtime (model directory holds config.json, tokenizer.json, model.onnx; recommended: the MIT-licensed dslim/bert-base-NER exported to ONNX, int8-quantized for speed). Fail-closed: a model that cannot load aborts startup and an inference error fails the request — never a silent fallback. Respects pii.categories. English for v1.

  • Credential redaction — database password is never shown in logs or debug output

Testing 🧪

# Unit tests
cargo test --workspace --lib --bins

# Integration tests (requires Docker)
./tests/run.sh

# Filter by engine
./tests/run.sh --filter mariadb
./tests/run.sh --filter mysql
./tests/run.sh --filter postgres
./tests/run.sh --filter sqlite

# With MCP Inspector
npx @modelcontextprotocol/inspector ./target/release/dbmcp

# HTTP mode testing
curl -X POST http://localhost:9001/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}'

Project Structure 🗂️

This is a Cargo workspace with the following crates:

Crate

Path

Description

dbmcp

. (root)

Binary shim

dbmcp-cli

crates/cli/

CLI parsing, transports, server bootstrap

dbmcp-sql

crates/sql/

Shared error types, validation, and identifier utilities

dbmcp-config

crates/config/

Configuration structs and validation

dbmcp-server

crates/server/

Shared MCP tool implementations and server info

dbmcp-mysql

crates/mysql/

MySQL/MariaDB backend handler and operations

dbmcp-postgres

crates/postgres/

PostgreSQL backend handler and operations

dbmcp-sqlite

crates/sqlite/

SQLite backend handler and operations

dbmcp-pii

crates/pii/

PII detection and anonymisation of query output

dbmcp-sql-lineage

crates/sql-lineage/

Column-level SQL lineage extraction

dbmcp-ssh

crates/ssh/

SSH tunnel for database connections

sqlx-json

crates/sqlx-json/

Type-safe row-to-JSON conversion for sqlx (RowExt trait)

Development 🧰

cargo build              # Development build
cargo build --release    # Release build (~7 MB)
cargo test               # Run tests
cargo clippy --workspace --tests -- -D warnings  # Lint
cargo fmt                # Format
cargo doc --no-deps      # Build documentation

License 📄

This project is licensed under the MIT License — see the LICENSE file for details.

Available Tools

6 tools
explainQueryExplain QueryA
Read-onlyIdempotent

Return the execution plan for a SQL query to diagnose performance. Use this tool instead of running EXPLAIN directly through readQuery — it provides structured output via EXPLAIN QUERY PLAN.

<when_not_to_use>

  • Running actual queries → use readQuery or writeQuery

  • Checking table structure → use getTableSchema </when_not_to_use>

<what_it_returns> A JSON array of EXPLAIN QUERY PLAN rows showing how SQLite will scan tables, use indexes, and order operations. </what_it_returns>

NameRequiredDescriptionDefault
queryYesThe SQL query to explain.

Output Schema

NameRequiredDescription
rowsYesResult rows, each a JSON object keyed by a column name.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, destructiveHint, idempotentHint, openWorldHint. The description adds value by specifying the output format (JSON array of EXPLAIN QUERY PLAN rows) and explaining how it works under the hood. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with clear sections (main purpose, usecase, when_not_to_use, examples, what_it_returns), no wasted words, and key information front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple tool (1 param, output schema present, annotations complete), the description fully covers purpose, usage, return format, and context for selecting among siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% for the single 'query' parameter. The description's example section reinforces correct parameter usage (e.g., 'explainQuery(query="SELECT ...")'), adding context beyond the schema description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns execution plans to diagnose performance, with a specific verb ('explain') and resource ('SQL query'). It distinguishes from siblings like readQuery by noting it provides structured output via EXPLAIN QUERY PLAN.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'usecase' and 'when_not_to_use' sections explicitly state when to use (slow queries, understanding scans/indexes, index decisions) and when not (running queries via readQuery/writeQuery, checking schemas via getTableSchema), with examples.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getTableSchemaGet Table SchemaA
Read-onlyIdempotent

Get column definitions and foreign key relationships for a table. Requires table — call listTables first.

<what_it_returns> A JSON object with table and columns keyed by column name, each containing type, nullable, key, default, and foreignKey info. </what_it_returns>

NameRequiredDescriptionDefault
tableYesThe table name to inspect. Use `listTables` first to see available tables.

Output Schema

NameRequiredDescription
tableYesName of the inspected table.
columnsYesColumn definitions keyed by column name.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, so the tool is known to be safe. The description adds value by detailing what the tool returns (column types, nullable, keys, defaults, foreign keys) and the exact structure, which is beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with separate sections (usecase, examples, what_it_returns) and is front-loaded with the main purpose. Every sentence is informative and no redundant content exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists (as indicated by context signals), the description covers all necessary aspects: purpose, usage prerequisites, parameter example, and return structure. It is complete and leaves no ambiguity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The sole parameter 'table' is fully described in the input schema with coverage at 100%. The description reinforces the prerequisite 'use listTables first' and provides concrete examples of usage, adding more context than the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Get column definitions and foreign key relationships for a table', using a specific verb and resource. It distinguishes itself from sibling tools like listTables (which lists tables) and explainQuery, making its purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance is provided: 'ALWAYS call this before writing queries' and 'Requires `table` — call `listTables` first.' The <usecase> section and examples further clarify when and how to use the tool, including what to do before invoking it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

listTablesList TablesA
Read-onlyIdempotent

List all tables in the connected SQLite database. Use this tool to discover what tables are available before using other tools.

<what_it_returns> A sorted JSON array of table name strings. </what_it_returns>

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
tablesYesSorted list of table names for this page.
nextCursorNoOpaque cursor pointing to the next page. Absent when this is the final page.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint, idempotentHint), the description adds that the result is a sorted JSON array of table names and describes pagination behavior with nextCursor. This is valuable context not captured by annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with front-loaded main sentence and separate sections for use cases, examples, return value, and pagination. Every section adds value without unnecessary repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description fully covers the tool's behavior for its simplicity: it lists tables, returns sorted array, and supports pagination. Given the presence of an output schema and no parameters, the description is complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has zero parameters, so schema coverage is 100%. The description does not need to add parameter semantics; it correctly omits parameter details. The baseline score for zero parameters is 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'List all tables in the connected SQLite database.' The verb 'list' and resource 'tables' are specific. The examples and usage guidelines differentiate it from siblings like getTableSchema, making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes a <usecase> section explicitly stating when to call the tool (e.g., ALWAYS call this tool FIRST when exploring tables) and provides examples of correct and incorrect usage, demonstrating when to use alternatives like getTableSchema.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

listTriggersList TriggersA
Read-onlyIdempotent

List all triggers in the connected SQLite database.

<what_it_returns> A sorted JSON array of trigger name strings. </what_it_returns>

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
triggersYesSorted list of trigger names for this page.
nextCursorNoOpaque cursor pointing to the next page. Absent when this is the final page.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already show readOnly, destructive, idempotent hints. Description adds pagination behavior and return format (sorted JSON array of trigger name strings), which adds value beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with sections, but somewhat verbose with usecase and examples. It is front-loaded with main purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no parameters and output schema exists, description explains return format (sorted JSON array) and pagination. It covers purpose, usage, and output completely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters are defined, so baseline is 4. The description does not need to add parameter info, and it doesn't attempt to.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'List all triggers in the connected SQLite database.' It uses a specific verb and resource, and distinguishes from sibling tools like listTables and listViews.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The usecase section explicitly lists when to use: investigating side-effects, auditing coverage, user asking. Examples include correct and incorrect uses, with alternative tool for getting trigger body.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

listViewsList ViewsA
Read-onlyIdempotent

List all views in the connected SQLite database.

<what_it_returns> A sorted JSON array of view name strings. </what_it_returns>

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
viewsYesSorted list of view names for this page.
nextCursorNoOpaque cursor pointing to the next page. Absent when this is the final page.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true. Description adds pagination details and return format, providing complete behavioral picture without contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections (usecase, examples, what_it_returns, pagination). Core statement is front-loaded, each sentence provides value, no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple no-parameter tool with output schema, description covers return format (sorted JSON array of view name strings) and pagination, making it fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has zero parameters and schema coverage is 100%. With no parameters, the description doesn't need to add parameter info; baseline 4 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'List all views' with specific verb and resource. Use cases and examples distinguish it from siblings like getTableSchema and listTables, making purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly provides when to use (exploring views, verifying existence) and when not to (see columns → getTableSchema). Examples with checkmarks and crosses give clear guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

readQueryRead QueryA
Read-onlyIdempotent

Execute a read-only SQL query. Allowed statements: SELECT, EXPLAIN.

<when_not_to_use>

  • Data changes (INSERT, UPDATE, DELETE) → use writeQuery

  • Query performance analysis → use explainQuery

  • Discovering tables or columns → use listTables or getTableSchema </when_not_to_use>

<what_it_returns> A JSON array of row objects, each keyed by column name. </what_it_returns>

NameRequiredDescriptionDefault
queryYesThe SQL query to execute.
cursorNoOpaque pagination cursor. Omit (or pass `null`) for the first page. On subsequent calls, pass the `nextCursor` returned by the previous response verbatim. Cursors are opaque — do not parse, modify, or persist. Ignored for `EXPLAIN` statements.

Output Schema

NameRequiredDescription
rowsYesResult rows, each a JSON object keyed by a column name.
nextCursorNoOpaque cursor pointing to the next page. Absent when this is the final page, when the result fits in one page, or when the statement is a non-`SELECT` kind that does not paginate (e.g. `SHOW`, `EXPLAIN`).

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true. The description adds value by specifying allowed statements, describing the return format (<what_it_returns>), explaining pagination behavior (<pagination>) including that EXPLAIN ignores cursor, and showing examples. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections using XML-like tags (usecase, when_not_to_use, examples, what_it_returns, pagination). It is concise but comprehensive, with no redundant information. Every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of the tool (SQL queries with pagination), the description covers all necessary aspects: allowed statements, usage guidance, examples, return structure, and pagination details. The presence of an output schema further reduces the need to describe return values in depth.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with both cursor and query described in the input schema. The description adds extra context: the pagination section explains cursor usage in detail, and examples illustrate valid queries. While the schema already provides good baseline, the description complements it effectively.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Execute a read-only SQL query. Allowed statements: SELECT, EXPLAIN.' It uses a specific verb and resource, and distinguishes from siblings by referencing writeQuery, explainQuery, listTables, and getTableSchema in the when_not_to_use section.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes explicit <usecase> and <when_not_to_use> sections listing when to use the tool and when not to, with clear references to sibling tools. It also provides examples of correct and incorrect usage, making it easy for an agent to decide when to invoke this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 6 tool updatesv0.13.2
    • AddedexplainQuery
    • AddedgetTableSchema
    • AddedlistTables
    • AddedlistTriggers
    • AddedlistViews
    • AddedreadQuery
  2. 6 tool updatesv0.12.0
    • RemovedexplainQuery
    • RemovedgetTableSchema
    • RemovedlistTables
    • RemovedlistTriggers
    • RemovedlistViews
    • RemovedreadQuery
  3. 6 tool updatesv0.11.0
    • AddedexplainQuery
    • AddedgetTableSchema
    • AddedlistTables
    • AddedlistTriggers
    • AddedlistViews
    • AddedreadQuery
  4. 6 tool updatesv0.10.4
    • RemovedexplainQuery
    • RemovedgetTableSchema
    • RemovedlistTables
    • RemovedlistTriggers
    • RemovedlistViews
    • RemovedreadQuery
  5. 6 tool updatesv0.8.0
    • First observedexplainQuery
    • First observedgetTableSchema
    • First observedlistTables
    • First observedlistTriggers
    • First observedlistViews
    • First observedreadQuery

TDQS

A4.6/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a distinct and clearly defined purpose: listing different database objects (tables, views, triggers), retrieving schema details, executing read queries, and explaining query plans. No overlap in functionality.

Naming Consistency5/5

All tool names follow a consistent camelCase verb_noun pattern (listTables, getTableSchema, readQuery, explainQuery). The naming is predictable and intuitive.

Tool Count5/5

With 6 tools, the server covers core database introspection and query operations without being overly large or too sparse. The count is appropriate for its domain.

Completeness2/5

The tool surface lacks a writeQuery tool for data modifications (INSERT, UPDATE, DELETE), which is explicitly referenced in readQuery's guidance. Additionally, DDL operations are absent, making the set incomplete for full database management.

Maintenance

ActivityMaintained
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers