npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tusk-mcp

v0.3.0

Published

PostgreSQL MCP server for AI agents. Schema introspection, SELECT-only query execution, and opt-in write access per target.

Readme

tusk-mcp

PostgreSQL MCP server for AI agents. Exposes schema introspection and SELECT-only query execution over the Model Context Protocol, with write access opt-in per target.

Install

# npx (no install needed)
npx tusk-mcp --host db.example.com --database mydb

# or clone + run
bun install
bun run src/index.ts --host localhost --database mydb

Setup UI

Interactive browser-based setup that generates config for Claude Desktop, Claude Code, Cursor, Windsurf, and OpenAI Codex.

npx tusk-mcp setup

Build standalone binary

bun run build           # Windows
bun run build:linux     # Linux
bun run build:macos     # macOS ARM

Connection

Individual flags (recommended)

tusk-mcp --host db.example.com --port 5432 --user admin --password 'p@ss' --database mydb

Connection string

tusk-mcp --connection-string "postgres://admin:p%[email protected]:5432/mydb"

Unencoded special characters in passwords (@, #) are handled automatically.

Environment variables

PGHOST=db.example.com PGDATABASE=mydb tusk-mcp

Priority: flags > --connection-string > DATABASE_URL > PG* env vars

Multiple environments / databases

A config file defines named targets: environments (local/stage/prod) or entirely different databases within one app. tusk.config.jsonc or tusk.config.json in the working directory is picked up automatically when no connection flags are given; --config <path> (relative or absolute) loads an explicit file. Connection flags cannot be combined with a config file.

{
  "defaultTarget": "local",          // used when a tool call omits target
  "defaults": {                      // merged under every target
    "user": "app",
    "database": "myapp"
  },
  "targets": {
    "local": { "host": "localhost", "access": "write", "description": "dev" },
    "stage": {
      "host": "stage.db.internal",
      "password": "${STAGE_DB_PASSWORD}",   // env var interpolation
      "ssl": true
    },
    "prod": {
      "connectionString": "postgres://ro:${PROD_DB_PASSWORD}@10.0.0.5:5432/myapp",
      "ssh": { "host": "bastion.example.com", "user": "deploy", "key": "~/.ssh/id_rsa" },
      "access": "structure",                // schema visible, queries disabled
      "description": "production"
    },
    "analytics": { "host": "warehouse.db", "database": "events" }
  }
}

Target fields mirror the CLI flags: host, port, user, password, passwordFile, passwordCmd, database, connectionString, ssl (true or {ca, cert, key} paths), ssh ({host, port, user, key, password}), access, description. structureOnly: true is still read as access: "structure"; setting both to different levels is a config error.

Priority: explicit fields > connectionString > defaults. ssh/ssl objects deep-merge, so defaults.ssh can hold the shared bastion and a target override just the port. ${VAR} interpolates from the environment in any string, so the file is safe to commit. Config-file targets ignore PG* env vars; use ${PGPASSWORD} explicitly if wanted.

With multiple targets every tool takes a target enum parameter (optional when defaultTarget is set, absent entirely with a single target), the target list is announced via MCP instructions, and compare-schemas becomes available. Connections are lazy per target: an unreachable bastion never blocks startup or the other targets.

Password security

# From file (Docker/K8s secrets)
tusk-mcp --host db --database mydb --password-file /run/secrets/db_pass

# From command (any secrets manager)
tusk-mcp --host db --database mydb --password-cmd 'vault kv get -field=password secret/db'
tusk-mcp --host db --database mydb --password-cmd 'op read op://vault/db/password'

SSL

Providing any certificate file automatically enables SSL.

tusk-mcp --host db --database mydb --ssl-ca /path/to/ca.crt       # CA verification
tusk-mcp --host db --database mydb \                               # mutual TLS
  --ssl-ca ca.crt --ssl-cert client.crt --ssl-key client.key

SSH tunnel

tusk-mcp --host db-internal --database mydb \
  --ssh-host bastion.example.com --ssh-user deploy --ssh-key ~/.ssh/id_rsa

Access levels

Each target has one access level, read by default.

| access | Tools | Connection | |---|---|---| | structure | overview, describe-tables | read-only transactions | | read | plus execute-query | read-only transactions | | write | plus execute-write | writes allowed, through execute-write only |

tusk-mcp --host db --database mydb --structure-only   # every target: structure
tusk-mcp --host db --database mydb --allow-writes     # every target: write

The two flags are mutually exclusive. --structure-only also narrows a config file, but --allow-writes is rejected alongside one: escalating a target to writes has to be written in the file, where it is reviewable, rather than in an MCP client's command line.

execute-query cannot mutate on any target, write-enabled ones included: every query runs inside a READ ONLY transaction, so a SELECT that hides an INSERT behind a volatile function is refused by the server, not just by the SQL parser. read and structure connections additionally start with default_transaction_read_only. Writes therefore only ever happen through execute-write, which is registered only when some target allows them and whose target parameter lists write-enabled targets only.

execute-write takes one statement per call, reports the affected row count, and previews RETURNING rows. Beyond the target gate it does not filter SQL: on a write target the database role is the boundary, so give that role only the privileges the agent should have.

Tools

| Tool | Description | |---|---| | overview | All schemas with tables, views, and estimated row counts in one call (partitions filtered out) | | describe-tables | Columns, types, PKs, FKs, and enum values for one or more tables per call | | execute-query | Read-only SQL with limit (rejected on structure-only targets) | | execute-write | One mutating statement with affected rows and RETURNING preview (write-enabled targets only) | | compare-schemas | Structural diff of a schema between two targets (multi-target only) |

Output format

Tool results use compact text in content for the AI model and JSON-safe preview data in structuredContent for clients that support structured MCP output. Query text uses a tab-delimited preview with null=\N; strings are JSON-quoted, so empty strings render as "" and the literal string "\\N" is distinct from SQL null. With multiple targets, results start with a target=<name> line.

MCP config

{
  "mcpServers": {
    "tusk": {
      "command": "npx",
      "args": ["-y", "tusk-mcp", "--host", "localhost", "--database", "mydb"]
    }
  }
}

Claude Code

claude mcp add --transport stdio tusk -- npx -y tusk-mcp --host localhost --database mydb

OpenAI Codex (~/.codex/config.toml)

[mcp_servers.tusk]
command = "npx"
args = ["-y", "tusk-mcp", "--host", "localhost", "--database", "mydb"]

All flags

| Flag | Type | Default | Description | |---|---|---|---| | --config | string | - | Multi-target config file (see above) | | --host | string | localhost | PostgreSQL host | | --port | number | 5432 | PostgreSQL port | | --user | string | - | Database user | | --password | string | - | Database password | | --password-file | string | - | Read password from file | | --password-cmd | string | - | Run command for password | | --database | string | - | Database name | | --connection-string | string | - | Full connection URL | | --ssl | boolean | false | Enable SSL without certificate verification | | --ssl-ca | string | - | CA certificate path (enables SSL) | | --ssl-cert | string | - | Client certificate path (enables SSL) | | --ssl-key | string | - | Client key path (enables SSL) | | --ssh-host | string | - | SSH tunnel host | | --ssh-port | number | 22 | SSH tunnel port | | --ssh-user | string | - | SSH username | | --ssh-key | string | - | SSH private key path | | --ssh-password | string | - | SSH password | | --structure-only | boolean | false | Schema only, no execute-query | | --allow-writes | boolean | false | Add execute-write (no config file) |