Skip to main content
Glama
Rheopyrin

db-access-mcp

by Rheopyrin

db-access-mcp

npm npm downloads license: MIT node

db-access-mcp MCP server

MCP (Model Context Protocol) stdio server that gives AI agents (Claude Code, Claude Desktop, any MCP client) access to configured databases:

  • PostgreSQL (≥ 10)

  • MySQL (≥ 5; read-only sessions require ≥ 5.6)

  • Amazon Redshift

  • Microsoft SQL Server (via tedious)

with SSH / AWS SSM tunnels, pluggable secret providers (env vars, HashiCorp Vault, AWS Secrets Manager) and strict per-instance isolation — many MCP instances can run concurrently on one machine without sharing connections or tunnels, and crashed instances never leave orphaned tunnel processes behind.

Highlights

Tools: connection_list / connection_find / connection_test, query, query_plan (EXPLAIN), query_to_file (streamed CSV/JSONL export that bypasses the model context), up_tunnel / down_tunnel / tunnel_list.

Security-first design:

  • Verified SSH tunnels — the bastion host key is checked against a host_key_sha256 pin or known_hosts, failing closed on mismatch (MITM defence), not blindly trusted.

  • read_only seatbelt + single-statement-by-default, which also closes the SET session read-only off; INSERT … bypass.

  • Confined file exportsquery_to_file writes only under the export dir or an allow_export_paths root; it cannot clobber ~/.ssh, dotfiles or the config dir.

  • No secrets leak out — tools never return stacks or credentials; logs redact by key name and scrub inline user:password@host URIs.

  • Pluggable secret providers — env vars, HashiCorp Vault (dynamic leases with auto-refresh and atomic pool swaps), AWS Secrets Manager, RDS IAM auth tokens and temporary Redshift credentials.

Tunnels & crash-safety: in-process SSH (dies with the process — no orphaned ports) and AWS SSM under a watchdog (killed even on SIGKILL), with AWS SSO bootstrap. Every instance's pools and tunnels are isolated; a crashed instance's tunnels are reaped by the next start.

Related MCP server: postgres-mcp-query-tool

Quick start

// Claude Code / Claude Desktop MCP config
{
  "mcpServers": {
    "db-access-mcp": {
      "command": "npx",
      "args": ["-y", "@rheopyrin/db-access-mcp"]
    }
  }
}

On first start the server creates the working directory ~/.db_acess_mcp (intentional spelling — it is the product contract) with an empty config.json, an empty conf.d/ directory and a full config.example.json covering every dialect, secret provider and tunnel type. The export directory (default /tmp/db-access-mcp/exports) is not created up front — query_to_file makes it on demand on the first export. Edit ~/.db_acess_mcp/config.json, restart the MCP server, done.

Integrating the MCP

The server speaks MCP over stdio: any client that can spawn npx -y @rheopyrin/db-access-mcp (or node <path>/dist/cli.js for a local build) can use it. Every spawned instance is fully isolated — its own pools, tunnels and idle timers — so it is safe to register it in several clients/sessions at once.

Claude Code

# current project only (writes .mcp.json in the project root)
claude mcp add db-access-mcp -- npx -y @rheopyrin/db-access-mcp

# for all your projects
claude mcp add --scope user db-access-mcp -- npx -y @rheopyrin/db-access-mcp

# with options (custom config dir, verbose logs, extra env file)
claude mcp add db-access-mcp -- npx -y @rheopyrin/db-access-mcp --workdir ~/.db_acess_mcp --log-level debug --env-file ~/.db_acess_mcp/secrets.env

Check with /mcp inside a session (server status, reconnect). Server stderr logs land in ~/Library/Caches/claude-cli-nodejs/<project-slug>/mcp-logs-db-access-mcp/ (macOS). After editing config.json, reconnect the server (/mcp) — the config is read at startup only.

Or declare it in the project's .mcp.json directly:

{
  "mcpServers": {
    "db-access-mcp": { "command": "npx", "args": ["-y", "@rheopyrin/db-access-mcp"] }
  }
}

Claude Desktop

Add the same mcpServers block to the config file and restart the app:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Cursor / other MCP clients

Any stdio-capable client works with the same shape — command npx, args ["-y", "@rheopyrin/db-access-mcp"] (Cursor: ~/.cursor/mcp.json, same mcpServers format). Two things to know:

  • stdout is the protocol — if your client shows a JSON-RPC parse error, make sure nothing wraps the command with extra output; all server logs go to stderr.

  • Pass CLI options via args, e.g. ["-y", "@rheopyrin/db-access-mcp", "--workdir", "/opt/mcp-db", "--log-level", "warn"].

Local build (development)

git clone <repo> && cd db_access_mcp && npm ci && npm run build
claude mcp add db-access-mcp-dev -- node /abs/path/db_access_mcp/dist/cli.js --log-level debug

Trying it without a client

npx -y @modelcontextprotocol/inspector npx -y @rheopyrin/db-access-mcp

opens a web UI listing all tools with call forms and live stderr. A sensible first-session sequence: dialect_listconnection_listconnection_test on one connection → query.

Requirements on the host

  • Node.js ≥ 20.19.

  • For ssm tunnels: AWS CLI + session-manager-plugin on PATH; for the SSO bootstrap a browser (login opens interactively).

  • For ssh tunnels: nothing extra (in-process ssh2 client).

CLI options

db-access-mcp [workdir] [exportdir] [--workdir <dir>] [--exportdir <dir>] [--config <file>] [--env-file <file>]... [--log-level <level>]

Option

Env var

Default

--workdir (or first positional)

DB_ACCESS_MCP_WORKDIR

~/.db_acess_mcp

--exportdir (or second positional)

DB_ACCESS_MCP_EXPORTDIR

/tmp/db-access-mcp/exports

--config

DB_ACCESS_MCP_CONFIG

discovery: <workdir>/config.json + <workdir>/conf.d/*.json

--env-file (repeatable)

none

--log-level (debug|info|warn|error|silent)

DB_ACCESS_MCP_LOG_LEVEL

info

The workdir holds config.json, conf.d/, config.example.json and the runtime instances/ and sso/ state (unchanged from earlier releases). The exportdir is where query_to_file writes exports (created on demand, not at startup); allow_export_paths adds extra writable roots.

All logs are JSON lines on stderr (stdout belongs to the MCP protocol). Values of keys matching password, token, secret, privateKey etc. are redacted.

MCP tools

Tool

What it does

dialect_list

Lists the supported database dialects: name (the type value for connections), default port and the plan format query_plan produces.

connection_list

Lists configured connections: key, type, description, read_only, host/port/database, tunnel name, metadata. Credentials are never returned (allowlist-based sanitization; connection strings are parsed only for host/port/database).

connection_find

Finds connections by host, port, database, type, read_only and/or metadata key-value pairs. All filters are combined with AND. user/password filters are ignored (and noted in the response).

connection_test

End-to-end health check: secrets → tunnel → pool → one-row server-info query. Returns ok: true with server version/user/database/latency, or ok: false with the failure code and hint (an unreachable DB is a valid result, not a tool error).

query

Executes SQL on a connection. Accepts connection, query, optional database (see multi-database connections), max_rows and timeout_ms overrides. Results are truncated to the row cap with truncated: true.

query_to_file

Executes a query and writes the result to a file (csv/jsonl, inferred from the extension) instead of the model context. file_path is relative to the export dir (default /tmp/db-access-mcp/exports, created on demand), or an absolute/~ path under the export dir or a configured allow_export_paths root — writes outside are rejected. Existing files require overwrite: true. postgres/mysql stream rows (no cap by default); redshift/mssql buffer and are capped at 100k rows.

query_plan

Returns the execution plan without running the query: EXPLAIN (FORMAT JSON) for postgres, EXPLAIN FORMAT=JSON for mysql, text EXPLAIN for redshift, SHOWPLAN_XML for mssql.

up_tunnel

Opens (or reuses) the tunnel configured for a connection and returns {host, port, tunnel_id, reused}. Optional local_port binds an exact local port; if the tunnel is already open on a different port or the port is taken, the call fails with the current port in the error.

down_tunnel

Closes a tunnel by tunnel_id. By default only the up_tunnel pin is released — if query pools still hold the tunnel it stays open (remaining_holders); force: true drains the holder pools and closes it unconditionally.

tunnel_list

Lists the tunnels currently open in this MCP instance with a live health probe: tunnel_id, tunnel name/type, local and remote endpoints, healthy, holder pools (connections), up_tunnel pins, external PIDs.

Security note for query_to_file: writes are confined to the export dir (default /tmp/db-access-mcp/exports) plus any roots listed in allow_export_paths (e.g. ["/tmp", "~/data_files"]) — every subpath below a listed root is allowed, anything else is rejected, so the tool cannot clobber ~/.ssh, dotfiles or the workdir. It only ever creates files (no reads, no appends) and refuses to overwrite without an explicit overwrite: true. Cells are written verbatim; be mindful of CSV-injection when opening exports in Excel.

Configuration reference

The schema is strict — unknown keys are rejected at startup with a readable error (typo protection). Everything inside a connection's options is passed through to the database driver as-is.

{
  "vault":               { /* named Vault servers */ },
  "aws_secret_profiles": { /* named AWS Secrets Manager profiles */ },
  "env_files":           [ /* extra .env files applied at startup */ ],
  "pool":                { /* global pool defaults */ },
  "limits":              { /* global query limits */ },
  "tunnels":             { /* named tunnel definitions */ },
  "connections":         { /* named connections */ }
}

Config files: single or split (conf.d)

Without --config, the server loads <workdir>/config.json (optional) plus every <workdir>/conf.d/*.json (sorted by name, non-recursive, dotfiles ignored) and merges them:

  • Named-record sections (vault, aws_secret_profiles, tunnels, connections) are unioned across files. The same name in two files is a startup error naming both files — no silent overrides.

  • Scalar sections (pool, limits, env_files) may appear in at most one file.

  • --config <file> loads exactly that file; conf.d is not scanned.

Everything can live in a single config.json (that is what the example shows) — conf.d is for splitting per team/project when the config grows.

Env-ref values

Wherever noted below, a config value can be an inline string or a reference to an environment variable, resolved lazily at the moment it is needed:

"token": "hvs.inline"            // inline
"token": { "env": "VAULT_2_TOKEN" }  // read from the environment at use time

A missing variable fails only the connections that actually need it, with an error naming the variable.

connections.<key>

Field

Required

Description

type

yes

postgres | mysql | redshift | mssql

options

yes

Driver passthrough options (see per-dialect notes below). May contain ${provider.path} secret placeholders in any string value. Must declare database and/or a non-empty databases list (connectionString/uri connections carry the database inside the string).

description

no

Free-text description shown by connection_list.

read_only

no

Session-level read-only enforcement (see semantics below). Default false.

metadata

no

Flat map (string/number/boolean values) used by connection_find.

pool

no

Per-connection pool overrides.

limits

no

Per-connection limit overrides.

tunnel

no

{ "target": "<tunnel name>", "localPort": 25432? }. Without localPort a random free port from 20000–45000 is picked.

secrets

no

Exactly one provider per connection: { "<provider>": <spec> }.

Per-dialect options

  • postgres / redshift — anything node-postgres accepts: host, port, database, user, password, ssl, … or a single connectionString (postgres://user:pass@host:5432/db).

  • mysql — anything mysql2 accepts: host, port, database, user, password, or uri (mysql://…). multipleStatements follows the shared rule below.

  • mssql — anything mssql accepts: server (or host alias), port, database, user, password, options: { encrypt, trustServerCertificate, … }, or connectionString (mssql://… URL or ADO style Server=…;Database=…).

options.multipleStatements (default off)

By default a query call runs a single statement. Set "multipleStatements": true in a connection's options to allow several ;-separated statements in one call. Enforced by the engine, not by parsing SQL:

  • mysql — the driver's native multipleStatements flag.

  • postgres / redshift — with it off, queries run over the extended protocol, so the server itself rejects a second statement (42601); no SQL splitting.

  • mssql — cannot be enforced at the protocol level (T-SQL batches), so multi-statement is always allowed here; rely on a read-only DB user.

Leaving it off also closes the SET session-read-only off; INSERT … bypass of a read_only connection (the write can't ride along in a second statement). Like read_only, this is a seatbelt — the real guarantee is a read-only DB user.

Multi-database connections (options.databases)

One server often hosts many databases. Instead of duplicating the connection, declare them all:

"shared-mysql": {
  "type": "mysql",
  "options": { "host": "...", "port": 3306,
               "databases": ["app", "reporting", "audit"],
               "user": "...", "password": "..." }
}

Rules (query, query_plan, query_to_file, connection_test accept an optional database parameter):

  • no database parameter → options.database is used; when the connection declares only a databases list there is no implicit default — the call fails with DATABASE_NOT_FOUND listing the available names;

  • a passed database must equal options.database or be a member of options.databases, otherwise DATABASE_NOT_FOUND;

  • database and databases may be declared together; every connection must declare at least one of them (config error otherwise);

  • each (connection, database) pair gets its own pool; all pools of a connection share one tunnel, released when the last pool closes;

  • connection_list/connection_find expose databases, and the database find-filter matches either the single property or any list member;

  • databases cannot be combined with connectionString/uri.

pool (global and per-connection)

Field

Default

Meaning

max

5

Max connections in the pool.

min

0

Min idle connections kept.

idle_timeout_ms

30000

Driver-level idle client timeout inside the pool.

connection_timeout_ms

10000

Time to wait for a new connection.

limits (global, per-connection, per-call)

Field

Default

Meaning

max_rows

1000

Row cap per result set; exceeded → rows are cut and truncated: true. Overridable per query call.

query_timeout_ms

30000

Query timeout. postgres/redshift: server-side statement_timeout; mysql: client-side inactivity timeout (connection is destroyed, the server may finish the statement); mssql: client-side request.cancel(). Overridable per query call.

idle_close_ms

600000

Per-instance idle timer: a connection unused this long gets its pool closed and its tunnel released.

Resolution order: tool-call argument → connection limits → global limits → defaults.

read_only semantics by dialect

Dialect

Mechanism

Enforcement

postgres

SET default_transaction_read_only = on per checkout

Hard — the server rejects writes (25006).

mysql ≥ 5.6

SET SESSION TRANSACTION READ ONLY per checkout

Hard. On 5.5 the statement fails → warning logged, no enforcement.

redshift

attempted, but Redshift does not support it

Best-effort: warning logged. Use a read-only DB user.

mssql

readOnlyIntent connection option

Only effective on Availability Group read replicas; warning logged. Use a read-only DB user.

For real guarantees always prefer a read-only database user. read_only is a seatbelt, not a security boundary.

Secrets

One provider per connection; placeholders are namespaced by the provider name and resolved against the parsed secret object: ${env.userName}, ${vault.data.password}, ${aws.password}. A placeholder that is the whole string keeps the raw value type (numbers stay numbers); embedded placeholders are string-substituted. Mismatched namespaces are rejected at config load.

env — environment variables

"options": { "user": "${env.userName}", "password": "${env.password}" },
"secrets": { "env": { "userName": "PG_USER_ENV_VAR", "password": "PG_PASSWORD_ENV_VAR" } }

The spec maps placeholder keys to environment variable names. Missing variables fail with the exact list of what is missing. Static — never reloaded.

vault — HashiCorp Vault (multiple servers)

"vault": {
  "vault-main": { "address": "https://vault.example.com:8200", "token": "hvs...." },
  "vault-dr":   { "address": { "env": "VAULT_DR_ADDR" }, "token": { "env": "VAULT_DR_TOKEN" } }
},
...
"secrets": { "vault": { "target": "vault-dr", "path": "secret/data/databases/db4" } }
  • vault is a map of named servers; address/token/namespace accept env-refs, extra keys are passed through to node-vault.

  • secrets.vault.target picks the server. Without target the implicit default client is used, built purely from VAULT_ADDR/VAULT_TOKEN (/VAULT_NAMESPACE) — node-vault's standard variables. The implicit default is not part of the named map: an entry you name "default" is just a regular named entry.

  • KV v2 responses are unwrapped: placeholders address the secret payload directly.

  • Dynamic secrets (e.g. database/creds/<role>) carry a lease: the lease is renewed at ~80% of its TTL (with jitter). When renewal fails (max TTL reached), fresh credentials are requested; if they differ, the connection pool is swapped atomically (new queries use the new pool immediately, in-flight queries finish on the old one, which is then drained).

  • If Vault is unreachable past the lease deadline, the secret is marked stale and re-resolved lazily on next use; an auth failure with a stale secret triggers one forced re-resolve + reconnect.

aws — AWS Secrets Manager (named profiles)

"aws_secret_profiles": {
  "aws-prod": { "aws_profile": "prod", "aws_region": "us-east-1", "reload_interval_ms": 3600000 },
  "aws-dev":  { "aws_profile": { "env": "AWS_DEV_PROFILE" }, "aws_region": { "env": "AWS_DEV_REGION" } }
},
...
"secrets": {
  "aws": {
    "secret_id": "prod/erp/mssql",       // name or full ARN
    "target": "aws-prod",                 // optional: aws_secret_profiles entry
    "version_stage": "AWSCURRENT"         // optional
  }
}

aws_profile, aws_region and reload_interval_ms live in named aws_secret_profiles entries (values accept env-refs). Without target the default AWS SDK credential chain is used (env, shared config, SSO, IMDS…) and the secret is static. The secret value must be a JSON object (SecretString); its keys become the ${aws.*} namespace. AWS Secrets Manager has no leases, so reloading is opt-in via the profile's reload_interval_ms (min 10s): the secret is re-fetched on that interval and rotation is picked up with the same atomic pool swap as Vault.

env_files — extra .env files

"env_files": ["~/.db_acess_mcp/secrets.env"]

Applied at startup, before any secret resolution; --env-file <file> (repeatable) appends to the config list. Files use standard dotenv syntax. Rules (security):

Rule

Why

The real environment always wins — variables present at process start are never overridden by a file.

An env file must not be able to repoint VAULT_ADDR/AWS_* of a running setup.

PATH, NODE_OPTIONS, NODE_EXTRA_CA_CERTS, LD_*/DYLD_* are skipped with a warning.

Prevents binary/loader/TLS-trust hijack for the processes we spawn (aws CLI).

Later files override earlier ones (config env_files first, then --env-file in order).

Deterministic precedence.

A group/other-readable env file logs a warning on POSIX (chmod 600 recommended).

Secrets hygiene.

Values are never logged; keys only at debug level.

Secrets hygiene.

aws_iam — passwordless RDS/Aurora (IAM auth tokens)

"options": {
  "host": "pg.abc.us-east-1.rds.amazonaws.com", "port": 5432, "database": "appdb",
  "user": "${aws_iam.username}", "password": "${aws_iam.token}",
  "ssl": { "rejectUnauthorized": false }          // SSL is MANDATORY for IAM auth
},
"secrets": { "aws_iam": { "username": "readonly", "target": "aws-prod" } }

No password is stored anywhere: a 15-minute SigV4 auth token is generated locally and used as the password; the standard refresh pipeline re-signs it before expiry and swaps the pools atomically. Tokens are always signed for the real RDS host/port from options (never the tunnel's 127.0.0.1), so tunnels work unchanged; connection strings are not supported here. Optional host/port in the spec override the endpoint (e.g. a reader endpoint).

AWS-side prerequisites:

  • IAM auth enabled on the instance/cluster (IAMDatabaseAuthenticationEnabled);

  • the DB user is IAM-bound — postgres: GRANT rds_iam TO readonly; mysql: CREATE USER readonly IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';

  • the caller has rds-db:connect on arn:aws:rds-db:<region>:<acct>:dbuser:<resource-id>/readonly;

  • not supported by RDS for SQL Server. Every connect is auditable in CloudTrail.

aws_redshift_creds — temporary Redshift credentials

"options": { "host": "cluster....redshift.amazonaws.com", "port": 5439, "database": "dwh",
             "user": "${aws_redshift_creds.username}", "password": "${aws_redshift_creds.password}" },
"secrets": { "aws_redshift_creds": { "cluster_id": "my-cluster", "db_user": "readonly",
                                     "target": "aws-prod", "duration_seconds": 3600 } }

redshift:GetClusterCredentials issues a temporary user+password pair (900–3600s); the returned username carries the IAM: prefix and is sent to the server verbatim. TTL comes from the API's expiration and feeds the same auto-refresh + pool-swap pipeline.

SSO bootstrap for AWS profiles

An aws_secret_profiles entry may carry the same sso block as ssm tunnels:

"aws_secret_profiles": {
  "aws-prod": { "aws_profile": "my-profile", "aws_region": "us-east-1",
                "sso": { "session": "my-sso-session", "timeout_ms": 300000 } }
}

Before any provider referencing the profile (aws, aws_iam, aws_redshift_creds) uses credentials, the session is verified with aws sts get-caller-identity; an expired session triggers aws sso login (browser) and the resolution waits up to timeout_ms. sso.profile defaults to the entry's aws_profile. Login dedup is by session name — a tunnel and a secret resolution on the same session share one browser login.

Adding a provider

Implement SecretProvider (src/interfaces/secret-provider.ts) and add one binding line in src/composition/modules/secrets.module.ts. The provider name is both the config key under secrets and the placeholder namespace.

Tunnels

"tunnels": {
  "bastion-ssm": { "type": "ssm", "options": { "target": "i-0123...", "region": "us-east-1", "profile": "default" } },
  "bastion-ssh": { "type": "ssh", "options": { "host": "bastion", "port": 22, "username": "ec2-user", "privateKey": "~/.ssh/id_ed25519" } }
}
  • ssh — runs inside the MCP process (via the ssh2 library): a local listener forwards TCP through the SSH channel. Because it is in-process it dies with the process even on SIGKILL — orphaned ports are impossible. Options: host, port (22), username, password, privateKey (file path, ~ ok), passphrase, agent (true = platform default agent, or an explicit socket/pipe path), ready_timeout_ms. The bastion host key is verified (MITM defence): by default against ~/.ssh/known_hosts (plaintext and hashed entries, and [host]:port for non-standard ports). Pin it explicitly with host_key_sha256 (the ssh-keygen -lf fingerprint, with or without the SHA256: prefix), point known_hosts at another file, or set strict_host_key: false to accept any key (insecure — opt-out only). An unknown or changed key is rejected.

  • ssm — spawns aws ssm start-session --document-name AWS-StartPortForwardingSessionToRemoteHost under a tiny watchdog process. The watchdog holds a stdin pipe from the MCP process: if the MCP process dies for any reason (including SIGKILL), the OS closes the pipe and the watchdog kills the whole aws/session-manager-plugin tree (taskkill /T /F on Windows, process group kill on POSIX). Requires the AWS CLI and session-manager-plugin on PATH. Options: target (instance id), region, profile, document_name.

AWS SSO bootstrap (ssm tunnels)

"bastion-ssm": {
  "type": "ssm",
  "options": { "target": "i-...", "region": "us-east-1", "profile": "prod" },
  "sso": { "session": "my-sso", "profile": "prod", "timeout_ms": 300000 }   // all fields optional
}

When a tunnel has an sso block, the session is verified with aws sts get-caller-identity --profile <profile> before the tunnel opens. If it is missing or expired, a login is started (browser flow): with sso.session set it runs aws sso login --sso-session <name> (the canonical IAM Identity Center form — one session may back several profiles); otherwise aws sso login --profile <profile>. The tunnel waits, polling every 3s, until the session works or timeout_ms (default 5 minutes) elapses — then TUNNEL_FAILED with a hint containing the exact manual command. sso.profile defaults to the tunnel's options.profile; the dedup/marker key is the session name when present.

  • The SSO session is never closed by this server; the login process is not watchdog-wrapped, is never killed and survives the MCP instance.

  • Concurrent logins are deduplicated: within an instance by profile; across instances via a <workdir>/sso/<profile>.login.json marker — a second instance waits for the first login instead of opening another browser tab (markers of dead processes are ignored via PID + start-time checks).

  • SSO tokens and ~/.aws/sso/cache are never read, parsed or logged — only fixed-argument aws CLI invocations, no shell.

Behavior:

  • The tunnel's remote endpoint is taken from the connection's options (host/port as seen from the bastion).

  • Tunnels are cached per instance and keyed by (tunnel name, remote host:port) — connections through the same bastion to the same database share one tunnel; query/up_tunnel reuse an already-open healthy tunnel.

  • Reference counting: the tunnel closes when the last connection using it is closed (including by the idle timer).

  • Every instance writes <workdir>/instances/<pid>-<startTime>.json with its tunnel PIDs. At startup (and every 10 minutes) each instance sweeps files of dead instances: PID liveness check, PID-reuse protection via OS process start time, and a command-line sanity check before killing anything.

  • On a connection error during query, the tunnel is health-checked, reopened if needed, the pool is rebuilt, and the query is retried — up to 3 attempts with exponential backoff. Auth errors are never retried.

Isolation model

Every npx -y @rheopyrin/db-access-mcp process is fully isolated: its own config snapshot, connection pools, tunnels and idle timers. Nothing is shared between instances; the per-instance registry files exist only so that later instances can clean up after a crashed one. Two instances talking to the same database simply hold independent pools (mind your database max_connections; the default pool max is 5 per connection per instance).

Error codes

Tools return isError: true with a structured payload — never raw stacks or credentials:

Code

Meaning

CONFIG_INVALID

Config schema/semantic violation (bad tunnel ref, placeholder namespace, …).

CONNECTION_NOT_FOUND

Unknown connection key.

DATABASE_NOT_FOUND

The requested database is not declared for the connection, or a multi-database connection was called without the database parameter (available names are in the hint).

SECRET_RESOLUTION_FAILED

Provider could not produce the secret (missing env var, Vault/AWS error, bad path).

TUNNEL_FAILED

Tunnel could not be opened / port busy / CLI missing.

CONNECTION_FAILED

Database unreachable after retries, or auth failed.

QUERY_FAILED

SQL error.

QUERY_TIMEOUT

Query exceeded timeout_ms.

Windows notes

  • Paths use os.homedir(); ~ in CLI args and privateKey is expanded manually.

  • Tunnel trees are killed with taskkill /T /F; process inspection uses PowerShell (wmic is gone from Windows 11).

  • AWS CLI v2 (aws.exe) and v1 (aws.cmd) are both handled.

  • Default SSH agent pipe: \\.\pipe\openssh-ssh-agent.

Development

npm ci
npm run lint        # eslint (typescript-eslint, type-checked)
npm run typecheck   # tsc --noEmit
npm test            # unit tests (fast, no docker)
npm run test:integration  # requires Docker: postgres:16, mysql:8, testcontainers/sshd
npm run test:e2e    # builds, then drives dist/cli.js over real stdio MCP
npm run build       # tsup -> dist/cli.js + dist/watchdog.js

Architecture: inversify DI container; every extension point (dialect drivers, secret providers, tunnel providers, MCP tools) is a multi-bound interface collected into a registry — adding an implementation is one class + one binding line. See src/composition/.

Not covered by automated integration tests (unit-tested with mocks; verify manually): Redshift specifics, SSM tunnels (needs real AWS), MSSQL against a live server, Vault/AWS Secrets Manager against live services.

Manual verification checklist

  1. npx -y @rheopyrin/db-access-mcp first run → workdir, config.json, config.example.json created.

  2. Add a real connection → connection_list, query (SELECT 1), query_plan.

  3. Tunneled connection → up_tunnel returns 127.0.0.1:<port>; psql -h 127.0.0.1 -p <port> works.

  4. kill -9 <mcp pid> → tunnel process disappears within seconds (watchdog); next start removes the stale instance file.

  5. Vault dynamic creds: watch the log for secret refreshed / pool swapped after credential rotation around 80% of the lease TTL.

Available Tools

10 tools
connection_findA

Find configured database connections by parameters: host, port, database, type, read_only and/or metadata key-value pairs. All provided filters are combined with AND. Username/password filters are ignored. Returns the same sanitized shape as connection_list.

ParametersJSON Schema
NameRequiredDescriptionDefault
hostNoExact database host to match
portNoDatabase port to match
typeNoDialect: postgres | mysql | redshift
userNoIgnored: connections are never filtered by credentials
databaseNoDatabase name to match
metadataNoMetadata key-value pairs; every pair must match (AND)
passwordNoIgnored: connections are never filtered by credentials
usernameNoIgnored: connections are never filtered by credentials
read_onlyNoMatch connections with this read_only setting

TDQS

A4.2/5.0
Behavior4/5

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

Discloses that username/password filters are ignored and that metadata filters use AND logic. References return shape to connection_list. No annotations provided, so description compensates well.

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?

Two concise sentences, front-loaded with purpose and parameters. No fluff.

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

Completeness4/5

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

Explains filtering behavior and return shape. Missing explicit behavior when no parameters provided, but inferred. Adequate for a non-complex tool.

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%, but description adds value by clarifying ignored parameters and AND logic for metadata. Provides additional meaning beyond the schema.

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?

Description clearly states the tool finds database connections by filtering on specific parameters. It distinguishes from connection_list by specifying filtering via parameters and referencing the same return shape.

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

Usage Guidelines3/5

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

The description implies use for filtering connections but lacks explicit guidance on when to choose this over connection_list or other tools. Sibling tool list provides context but no direct advice.

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

connection_listA

List configured database connections (postgres, mysql, redshift). Returns key, type, description, read_only flag, host/port/database and metadata. Credentials are never included. Use the returned key with the query, query_plan and up_tunnel tools.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

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

No annotations provided, so description carries full burden. It discloses that credentials are never included, which is critical behavioral context. Lacks details on rate limits or ordering, but adequate for a listing tool.

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?

Two sentences, no waste. Front-loaded with purpose, followed by return details and usage guidance.

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 output schema, description explains return shape sufficiently (fields and omission of credentials). Also advises on using the key with related tools. Complete for a listing tool.

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, schema coverage 100%. Baseline 4. Description adds meaning beyond schema by explaining what the tool returns and its usage context.

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 it lists configured database connections, specifies supported types (postgres, mysql, redshift), and enumerates returned fields (key, type, description, read_only, host/port/database, metadata). It distinguishes itself from sibling tools like connection_find and connection_test.

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

Usage Guidelines4/5

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

It explicitly states when to use the tool ('List configured database connections') and how to use the output (key with query, query_plan, up_tunnel). Does not mention when not to use, but context from siblings implies alternatives.

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

connection_testA

Test a configured connection end-to-end: resolves secrets, opens the tunnel if configured, connects and runs a one-row server-info query. Returns ok=true with server version, user, database and latency — or ok=false with the failure code and hint (an unreachable database is a valid test result, not a tool error).

ParametersJSON Schema
NameRequiredDescriptionDefault
databaseNoDatabase to test; required when the connection declares multiple databases
connectionYesConnection key from connection_list

TDQS

A4.7/5.0
Behavior5/5

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

The description fully discloses all behavioral aspects: it resolves secrets, opens tunnels, connects, and runs a query. It explains the return format (ok=true/false with details) and explicitly states that an unreachable database is a valid result, not a tool error. No annotations are provided, so the description carries the full burden and does so excellently.

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 two sentences: first defines the action, second describes the return. No wasted words, front-loaded with key information, and easy to parse.

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 output schema, the description thoroughly explains what the tool returns (ok=true with version, user, database, latency; or ok=false with code and hint). It covers all aspects of the tool's behavior, parameter nuances, and error handling, leaving no gaps.

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%, so baseline is 3. The description adds value by explaining when the database parameter is required ('when the connection declares multiple databases'), which goes 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 tests a configured connection end-to-end, specifying the steps: resolves secrets, opens tunnel if configured, connects, runs a server-info query. It distinguishes from sibling tools like connection_list and connection_find by focusing on testing a single connection's functionality.

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

Usage Guidelines4/5

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

The description provides clear context for when to use the tool (testing a connection) and explains that an unreachable database is a valid test result. However, it does not explicitly compare to alternatives like query or up_tunnel, though the purpose is distinct.

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

dialect_listA

List the database dialects this server supports. Returns each dialect name (usable as the "type" field of a connection in the config), its default port and the execution-plan format produced by query_plan.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.3/5.0
Behavior4/5

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

The description implies a read-only operation by stating it lists dialects, but since no annotations are provided, the description carries the full burden. It does not explicitly state that the tool has no side effects, but the nature of the tool (listing) makes this clear. A score of 4 is appropriate as it is transparent enough for safe use.

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 a single sentence that efficiently conveys the tool's purpose and return value. It is concise, front-loaded, and contains no extraneous information.

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 that there are no parameters, no output schema, and the tool is simple, the description fully explains what the tool does and what it returns (dialect name, default port, execution-plan format). It is complete for an agent to understand its use.

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?

There are no parameters, so the baseline score is 4. The description does not need to add parameter information, and it correctly reflects the tool's behavior without parameters.

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 action ('list'), the resource ('database dialects'), and the specific return fields (name, default port, execution-plan format). It effectively distinguishes this tool from the sibling tools, which deal with connections, queries, and tunnels.

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

Usage Guidelines3/5

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

The description explicitly states what the tool does but does not provide guidance on when to use it versus alternatives. The usage is implied (e.g., before configuring a connection), but no explicit when-not or exclusion criteria are given.

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

down_tunnelA

Close a tunnel previously opened with up_tunnel, by its tunnel_id. By default only the up_tunnel pin is released: if live connection pools still use the tunnel it stays open and their keys are returned in remaining_holders. With force=true the holder pools are drained and the tunnel is closed unconditionally (the next query recreates them).

ParametersJSON Schema
NameRequiredDescriptionDefault
forceNoDrain holder pools and close the tunnel unconditionally
tunnel_idYesTunnel id returned by up_tunnel

TDQS

A4.6/5.0
Behavior5/5

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

No annotations are provided, so the description fully carries the burden of behavioral disclosure. It details the default behavior (only releases up_tunnel pin, returns remaining_holders), the effect of 'force=true' (drains pools, closes unconditionally), and the consequence for subsequent queries (recreates pools). This is comprehensive for a two-operation tool.

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 two sentences long, front-loaded with the primary purpose, and every clause adds value. It is concise without omitting important behavioral details.

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

Completeness4/5

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

The tool has no output schema, so the description indirectly covers return behavior by mentioning 'remaining_holders'. It covers input, behavior, and side effects adequately for a simple mutation. A minor gap is the lack of explicit mention of the return format (e.g., JSON object with remaining_holders).

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?

Input schema coverage is 100%, so the description adds meaningful context beyond the schema. It explains that 'tunnel_id' is returned by 'up_tunnel' and elaborates on the 'force' parameter's conditional behavior. The description enriches the understanding of parameter usage.

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 identifies the action ('Close a tunnel'), the resource ('previously opened with up_tunnel'), and the key identifier ('tunnel_id'). It distinguishes from the sibling tool 'up_tunnel' by specifying it is the inverse operation.

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

Usage Guidelines4/5

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

The description explains when to use the tool (to close a tunnel) and contrasts with 'up_tunnel'. It also describes the default behavior versus the 'force=true' option. However, it does not explicitly state when NOT to use the tool or provide alternatives among siblings.

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

queryA

Execute a SQL query on a configured connection (use connection_list to discover keys). Results are truncated to max_rows (default from config, typically 1000) with truncated=true set; add LIMIT for large tables. Connections marked read_only reject writes at the session level. Multi-statement scripts are passed to the driver as-is (for mysql they require multipleStatements enabled in the connection options).

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesSQL text to execute
databaseNoDatabase to run against; required when the connection declares multiple databases
max_rowsNoRow cap for this call (overrides config)
connectionYesConnection key from connection_list
timeout_msNoQuery timeout in ms for this call (overrides config)

TDQS

A4.3/5.0
Behavior4/5

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

No annotations are provided, so the description carries the full burden. It discloses truncation with a truncated flag, read-only enforcement, and driver-specific multi-statement behavior. It does not detail error handling or logging, but covers essential behavioral traits for a query tool.

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 concise with three sentences. The first sentence states the purpose and a helpful hint. Subsequent sentences efficiently detail truncation, read-only behavior, and multi-statement handling. No extraneous information.

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

Completeness3/5

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

The description covers key behaviors but lacks details on the return format (e.g., structure of results, handling of errors, or what happens with empty results). Given the absence of an output schema, more information about the response would improve completeness for a query tool.

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%, so baseline is 3. The description adds value by explaining the connection parameter (use connection_list to discover keys), max_rows default from config, and multi-statement driver requirement for query. This goes beyond the schema descriptions, justifying a score of 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 the tool executes a SQL query on a configured connection, with a hint to discover connection keys. It distinguishes from sibling tools like query_plan and query_to_file by describing query execution behavior.

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

Usage Guidelines4/5

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

The description provides guidance on when to use (e.g., for SQL execution) and mentions prerequisites (use connection_list). It advises adding LIMIT for large tables and explains behavior for read-only connections and multi-statement scripts. However, it does not explicitly list alternatives or conditions to avoid using this tool.

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

query_planA

Get the execution plan (EXPLAIN) for a SQL query without running it. postgres/mysql return a JSON plan; redshift returns a text plan (Redshift supports neither FORMAT JSON nor EXPLAIN ANALYZE, and its cost numbers are relative — do not compare them to postgres costs).

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesSQL text to explain (the query itself is not executed)
databaseNoDatabase to explain against; required when the connection declares multiple databases
connectionYesConnection key from connection_list

TDQS

A4.4/5.0
Behavior4/5

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

No annotations provided, so description carries full burden. It discloses non-execution, return format differences per dialect, and the redshift relative cost caveat. Lacks details on authorization or error responses.

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?

Two concise sentences. First sentence states core purpose, second sentence delivers essential usage guidelines. No wasted words.

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

Completeness4/5

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

Covers purpose, non-execution, dialect differences, and redshift caveat. Missing details on error handling or output format details, but no output schema exists. Adequate for a tool with clear input schema.

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%, baseline 3. The description adds value by reinforcing the query parameter is not executed and explaining dialect-specific behavior that is not in the schema.

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?

Clearly states the verb 'Get', resource 'execution plan', and distinguishes from running the query. The description explicitly says 'without running it', differentiating from the sibling tool 'query'.

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

Usage Guidelines4/5

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

Provides specific guidance for different database dialects (postgres/mysql vs redshift) and warns about redshift cost numbers. Does not explicitly list when not to use, but the context is clear enough for most agents.

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

query_to_fileA

Execute a SQL query and write the full result to a file (csv or jsonl) instead of returning rows — use this for large exports that must not go through the model context. Relative file_path resolves under the export dir (default /tmp/db-access-mcp/exports); absolute or ~-prefixed paths must fall under the export dir or a configured allow_export_paths root. Parent directories are created. Existing files are not overwritten unless overwrite=true. postgres/mysql stream rows (no row limit by default); redshift/mssql buffer in memory and are capped at 100000 rows.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesSQL text to execute (single statement for streamed dialects)
formatNoOutput format; default inferred from the file extension
databaseNoDatabase to export from; required when the connection declares multiple databases
max_rowsNoOptional row cap for the export
file_pathYesTarget file path: relative to the export dir, or absolute/~ under an allowed export root
overwriteNoReplace the file if it already exists (default false)
connectionYesConnection key from connection_list
timeout_msNoQuery timeout (default from config)

TDQS

A4.6/5.0
Behavior5/5

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

No annotations provided, but description fully discloses file path resolution (relative, absolute, ~), parent directory creation, overwrite behavior, and dialect-specific streaming vs buffering with row caps (redshift/mssql 100k). 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?

Two sentences, no wasted words. Front-loaded with purpose and use case, followed by key behavioral details in a single compact sentence. Very efficient.

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

Completeness4/5

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

Covers main behavioral aspects for a file-export tool: use case, path handling, dialect differences. However, it does not specify what the tool returns (e.g., success message or file path) despite no output schema. For a write command, the outcome is the file itself, so this is a minor gap.

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 has 100% coverage, so baseline is 3. Description adds value by explaining file_path resolution rules, format default from extension, and overwrite default false. Also mentions dialect-specific row caps which relate to max_rows parameter.

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?

Description clearly states it executes a SQL query and writes result to CSV/JSONL file, distinguishing from the sibling 'query' tool by explicitly mentioning 'instead of returning rows'. The use case for large exports is specified.

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

Usage Guidelines4/5

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

Description explicitly says to use for large exports not going through model context, implying alternatives. It details file path rules, overwrite behavior, and dialect-specific row limits. No explicit when-not-to-use but the context is sufficient.

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

tunnel_listA

List the tunnels currently open in THIS MCP instance, with a live health probe each. tunnel_id is accepted by down_tunnel; "connections" are the pools holding the tunnel, "pins" are up_tunnel holds. Configured-but-not-open tunnels are visible via connection_list (the tunnel field).

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

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

With no annotations, the description must convey behavioral traits. It mentions a 'live health probe' and describes output fields (tunnel_id, connections, pins), indicating it's a read-only operation. No side effects are suggested, but it could be more explicit about idempotency or rate limits.

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?

Two sentences, no waste. The main action is front-loaded, and additional context about related tools and fields is provided efficiently.

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 parameterless list tool with no output schema, the description sufficiently covers what it does and what the output contains. It references sibling tools to fill context, making it complete for the agent to select and invoke correctly.

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 tool has zero parameters, so schema description coverage is 100%. Baseline for 0 params is 4, and the description does not need to add parameter details. It adds value by explaining what the output contains.

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 the tunnels currently open in THIS MCP instance' with a specific verb and resource. It distinguishes from sibling tools by mentioning connection_list for configured-but-not-open tunnels, and clarifies how fields relate to other tools like down_tunnel and up_tunnel.

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?

Provides explicit guidance on when to use this tool (to see open tunnels) and points to connection_list as an alternative for configured-but-not-open tunnels. It also explains the meaning of 'connections' and 'pins' in relation to other tools, helping the agent choose correctly.

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

up_tunnelA

Open (or reuse) the tunnel configured for a connection WITHOUT connecting to the database. Returns the local host/port to connect through and a tunnel_id for down_tunnel. The tunnel is closed by down_tunnel, on idle timeout or when this MCP instance exits. Pass local_port to bind an exact local port; this fails if the tunnel is already open on a different port or the port is taken.

ParametersJSON Schema
NameRequiredDescriptionDefault
connectionYesConnection key from connection_list (must have a tunnel configured)
local_portNoExact local port to open the tunnel on (default: port from the config or a random free one)

TDQS

A4.8/5.0
Behavior5/5

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

No annotations provided, so description carries full burden. It discloses reuse behavior, return values (local host/port and tunnel_id), tunnel closure mechanisms (down_tunnel, idle timeout, exit), and exact port binding behavior including failure conditions.

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?

Two sentences, front-loaded with core purpose, followed by lifecycle and optional parameter details. Every sentence adds value with 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?

With no output schema, description explains return values. Covers lifecycle, parameter behaviors, and constraints. With 9 sibling tools, context is adequate and sufficient.

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

Parameters5/5

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

Schema coverage is 100% with descriptions. The description adds meaningful context: 'connection' requires a tunnel configured, 'local_port' default is config or random, and failure conditions if already open on different port or port taken.

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 specifies a clear verb ('Open or reuse') and resource ('tunnel configured for a connection'), and differentiates from sibling tools like 'down_tunnel' and 'tunnel_list' by stating it does not connect to the database and returns a tunnel_id for later use.

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

Usage Guidelines4/5

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

The description explicitly states when to use (without connecting to the database) and provides conditions for the optional parameter 'local_port' (fails if port taken or already open on different port). It mentions lifecycle: tunnel closed by down_tunnel, idle timeout, or exit, but does not directly compare to siblings like 'tunnel_list' or 'down_tunnel'.

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. Dates show when Glama detected each change.

  1. 10 tool updatesv0.1.0
    • First observedconnection_find
    • First observedconnection_list
    • First observedconnection_test
    • First observeddialect_list
    • First observeddown_tunnel
    • First observedquery
    • First observedquery_plan
    • First observedquery_to_file
    • First observedtunnel_list
    • First observedup_tunnel

TDQS

A4.6/5.0
Disambiguation5/5

Each tool has a clearly distinct purpose: connection management (find, list, test, tunnel operations), query execution (query, query_plan, query_to_file), and dialect support (dialect_list). No two tools have overlapping functionality.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern with underscores (e.g., connection_list, query_plan, up_tunnel). The only exception is 'query' which is a simple verb, but it's a common and clear name for the primary operation.

Tool Count5/5

With 10 tools, the server is well-scoped. Each tool serves a specific need for database access and management, and the count is neither too small to be thin nor too large to be overwhelming.

Completeness5/5

The tool set covers all essential operations: listing/finding connections, testing, querying, explaining plans, exporting results, and managing tunnels. There are no obvious gaps for a database access server.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Postgres Pro is an open source Model Context Protocol (MCP) server built to support you and your AI agents throughout the entire development process—from initial coding, through testing and deployment, and to production tuning and maintenance.
    9
    3,260
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that gives an AI agent scoped, safe access to your Postgres databases with per-connection access control, row caps, timeouts, and defense-in-depth read-only enforcement.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Zero-config MCP server that empowers AI agents to safely query SQL and NoSQL databases like PostgreSQL, MySQL, SQLite, MongoDB, and Redis.
    24
    1
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Rheopyrin/db-access-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server