DD

datasparc/dbhawk-mcp-plugin

Developer tools
0 stars 0 forks Quality 71 Trend 20

Gives an AI assistant access to DBHawk: list the datasources a user is assigned, browse schema/tables/columns, run queries (with the user's access control and column masking applied), and use...

Overview

Gives an AI assistant access to DBHawk: list the datasources a user is assigned, browse schema/tables/columns, run queries (with the user's access control and column masking applied), and use text-to-SQL / optimize / format. Data or schema changes are possible only when the user's allows them — the default tier is read-only (see Reads, writes and MCP tiers). It's a thin bridge over the existing DBHawk REST surface at /api/v2/mcp/. DBHawk decides what runs: every statement is classified and checked against the tier of the user's groups, whatever role the token owner has. DBHAWK_TOKEN is a personal token (dbh_ . ), a JWT — the MCP endpoints won't accept it directly. On its first call the server exchanges it for a short-lived JWT at POST /api/v2/auth/token/exchange (sending the personal token in the X-API-Token header), caches that JWT, and sends it as the Bearer for every /api/v2/mcp/ call.

README

DBHawk MCP — Claude Code plugin + demo connector

Gives an AI assistant access to DBHawk: list the datasources a user is assigned, browse schema/tables/columns, run queries (with the user’s access control and column masking applied), and use DBHawk AI text-to-SQL / optimize / format. Data or schema changes are possible only when the user’s MCP tier allows them — the default tier is read-only (see Reads, writes and MCP tiers).

It’s a thin bridge over the existing DBHawk REST surface at /api/v2/mcp/**. DBHawk decides what runs: every statement is classified and checked against the tier of the user’s groups, whatever role the token owner has.

Auth is two steps. DBHAWK_TOKEN is a personal token (dbh_.), not a JWT — the MCP endpoints won’t accept it directly. On its first call the server exchanges it for a short-lived JWT at POST /api/v2/auth/token/exchange (sending the personal token in the X-API-Token header), caches that JWT, and sends it as the Bearer for every /api/v2/mcp/** call. Shortly before the JWT’s exp the server exchanges a fresh one up front; if a call still gets a 401 it re-exchanges and retries once — transparent to you.

dbhawk-mcp-plugin/                 <- this folder = a Claude Code "marketplace" (push it to git)
├── .claude-plugin/marketplace.json
└── dbhawk/                        <- the actual plugin (installed by users)
    ├── .claude-plugin/plugin.json
    ├── .mcp.json                  <- declares the "dbhawk" stdio MCP server
    └── mcp-server/
        ├── index.js               <- source
        ├── package.json           <- build tooling (maintainers only)
        └── dist/dbhawk-mcp.mjs    <- COMMITTED self-contained bundle (what users run; no npm install)

The bundle in dist/ inlines every dependency, so a bare git clone runs with zero install — that’s what makes /plugin install work.


For users — install

Claude Code (from the marketplace)

Once this repo is on GitHub (see Publish below), point Claude Code at it and install:

/plugin marketplace add datasparc/dbhawk-mcp-plugin
/plugin install dbhawk@dbhawk-marketplace

dbhawk is the plugin name, dbhawk-marketplace is the marketplace name (from marketplace.json). Then set the connection (see Configure) and check the tools are live with /mcp.

Prefer to try it without a git host? Load the local folder for one session:

claude --plugin-dir ./dbhawk-mcp-plugin/dbhawk

Claude Desktop (extension, .mcpb) — recommended

Claude Desktop installs this as a Desktop Extension with a proper GUI settings form (no JSON editing). It’s the same MCP server, packaged as an .mcpb bundle.

  1. Download dbhawk-.mcpb from the Releases page.
  2. In Claude Desktop: Settings → Extensions → Advanced settings → Install and pick the file (or just drag the .mcpb onto the Extensions window).
  3. Fill in the form — DBHawk Base URL, API Token (masked; stored in your OS keychain), Default Datasource (optional) — and save.

Claude Desktop does not auto-update file-installed extensions: to upgrade, download the newer .mcpb and install it again. This is independent of the Claude Code plugin — you don’t need the marketplace plugin installed.

Claude Desktop (manual config, advanced)

Prefer to wire it by hand instead of the .mcpb? Point Desktop at the bundle with an absolute path. Edit claude_desktop_config.json (Windows: %APPDATA%\Claude\claude_desktop_config.json, macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "dbhawk": {
      "command": "C:\\Program Files\\nodejs\\node.exe",
      "args": ["C:\\...\\dbhawk-mcp-plugin\\dbhawk\\mcp-server\\dist\\dbhawk-mcp.mjs"],
      "env": {
        "DBHAWK_BASE_URL": "https://demo.dbhawk.example.com",
        "DBHAWK_TOKEN": "PASTE_MCP_TOKEN_HERE",
        "DBHAWK_DEFAULT_DATASOURCE": ""
      }
    }
  }
}

Restart Claude Desktop fully (including the tray icon).


Configure

The server needs three values (the token owner needs ACCESS_TO_DATA):

Setting Required Meaning
Base URL (DBHAWK_BASE_URL) yes Base URL of DBHawk, e.g. https://demo.dbhawk.example.com (no trailing /api)
API Token (DBHAWK_TOKEN) yes MCP-scoped personal token (dbh_…): DBHawk → User Profile → API Token. Exchanged for a JWT at runtime — paste the personal token as-is, not a JWT
Default Datasource (DBHAWK_DEFAULT_DATASOURCE) no Datasource used when a tool call omits datasource

The plugin declares these as userConfig fields in dbhawk/.claude-plugin/plugin.json, so Claude Code prompts for them when you enable the plugin — the token field is sensitive, so it’s masked on entry and stored in secure storage (OS keychain / ~/.claude/.credentials.json), never in settings.json or git. dbhawk/.mcp.json wires them in via ${user_config.dbhawk_token} etc.

To re-enter or change them later, re-enable the plugin, or set them non-interactively:

claude plugin install dbhawk@dbhawk-marketplace \
  --config dbhawk_base_url=https://demo.dbhawk.example.com \
  --config dbhawk_token=dbh_... \
  --config dbhawk_default_datasource=

Claude Desktop — environment variables

Desktop doesn’t understand plugins or userConfig — pass the three values as env in the claude_desktop_config.json block shown above.


Tools

Tool DBHawk endpoint
list_datasources GET /datasources
get_datasource GET /datasources/{ds}
list_catalogs GET /datasources/{ds}/catalogs (MSSQL / Snowflake; empty for DBs with no catalog level)
list_schemas GET /datasources/{ds}/schemas
list_objects GET /datasources/{ds}/schemas/{schema}/objects
list_columns GET /datasources/{ds}/schemas/{schema}/objects/{object}/columns
get_permissions GET /permissions (the user’s MCP tier(s), allowed operations, row cap, confirmations)
run_query POST /datasources/{ds}/query with readOnly: true (reads only, even if the tier allows writes; row-capped 200/5000)
explain_plan POST /datasources/{ds}/explain (execution plan built and read on one connection; the statement is never executed; needs explain_plan in the tier)
list_sessions GET /datasources/{ds}/sessions (active database sessions; needs active_sessions)
kill_session POST /datasources/{ds}/sessions/{id}/kill (ends one listed session; destructive, confirm flow; needs kill_session)
execute_statement POST /datasources/{ds}/query (data / schema change, only if the MCP tier allows it; confirm flow)
text_to_sql POST /datasources/{ds}/ai/ask
optimize_sql POST /datasources/{ds}/ai/optimize
format_sql POST /format-query

Reads, writes and MCP tiers

What a user may do is set in DBHawk, per user group, by its MCP tier (Admin → MCP Management → MCP Tiers). The default tier is read-only; higher tiers add EXPLAIN, DML (INSERT / UPDATE / DELETE / MERGE), DDL and admin operations. get_permissions tells the model what its tier allows.

Reads and writes are separate tools on purpose. run_query is annotated read-only (readOnlyHint), and DBHawk refuses a write sent through it even when the tier allows one. execute_statement is annotated destructive (destructiveHint), so MCP clients such as Claude ask the user before each call - keep it that way, do not “always allow” it. When the tier also asks for confirmation, DBHawk answers the first call with { confirmationRequired: true, executed: false } and runs the statement only when it is repeated with confirm: true. A read-only datasource refuses writes whatever the tier says.

Demo prompts: “Which datasources do I have?” → “Show tables in schema public of Postgres-Demo” → “What columns does customers have?” → “How many orders last month by status?” (text-to-SQL → run).


For maintainers — publish & rebuild

Publish the marketplace

cd dbhawk-mcp-plugin
git init && git add . && git commit -m "DBHawk MCP plugin"
git remote add origin [email protected]:datasparc/dbhawk-mcp-plugin.git
git push -u origin main

Users then run the two /plugin commands above. To list it in Anthropic’s curated claude-plugins-official directory, submit it via the plugin directory submission form (separate review); your own marketplace works immediately without that.

Rebuild the bundle (after editing index.js)

cd dbhawk/mcp-server
npm install        # once, pulls the SDK + esbuild (build-time only)
npm run build      # regenerates dist/dbhawk-mcp.mjs
git add dist/dbhawk-mcp.mjs && git commit -m "rebuild bundle"

dist/dbhawk-mcp.mjs is committed on purpose — it is the artifact users run. node_modules/ is not.

Tests

cd dbhawk/mcp-server
npm test           # builds the bundle, then runs test/*.test.js
npm run test:only  # runs the tests against the current bundle, without rebuilding

The tests start the real bundle over stdio, drive it with the official MCP client and point it at a fake DBHawk HTTP server that records every request (test/helpers.js), so no DBHawk instance is needed. They cover the tool catalogue and annotations, the read/write contract (run_query always sends readOnly: true, execute_statement sends confirm only when asked), the tool → endpoint mapping, the token exchange (header, JWT caching, one re-exchange on 401) and error reporting. CI (.github/workflows/mcp-server.yml) runs them on Node 18/20/22 and also fails when the committed dist/ bundle is stale, when the mcpb/server copy differs from it, or when the versions in the manifests, server.json and index.js disagree.

Build & release the desktop extension (.mcpb)

The mcpb/ folder is the extension source: mcpb/manifest.json (declares the user_config fields shown in Desktop’s settings form) plus mcpb/server/dbhawk-mcp.mjs (a copy of the same dist/ bundle). After rebuilding the bundle, refresh the copy, then pack and release:

cp dbhawk/mcp-server/dist/dbhawk-mcp.mjs mcpb/server/dbhawk-mcp.mjs   # keep the copy in sync
npx @anthropic-ai/mcpb validate mcpb/manifest.json                   # optional sanity check
npx @anthropic-ai/mcpb pack mcpb dbhawk-.mcpb                # produces the .mcpb

# SBOM of everything inlined into the bundle (all deps are devDependencies, so don't --omit dev)
(cd dbhawk/mcp-server && npm sbom --sbom-format cyclonedx) > dbhawk-mcp-.cdx.json

gh release create v dbhawk-.mcpb dbhawk-mcp-.cdx.json \
  --title "DBHawk " --notes "DBHawk MCP desktop extension"

The .cdx.json is a CycloneDX SBOM listing the exact package versions esbuild inlined into dist/dbhawk-mcp.mjs, so the bundle can be audited without unpacking it. Like the .mcpb, it is git-ignored and ships only as a Release asset.

Bump version in mcpb/manifest.json for every release (Desktop keys upgrades off it). The .mcpb is git-ignored — it ships as a Release asset, not in the tree. Keep mcpb/manifest.json’s version in step with dbhawk/.claude-plugin/plugin.json so the plugin and the extension stay aligned.


Troubleshooting

  • token exchange failed: 401/403 — the personal DBHAWK_TOKEN is wrong, revoked or expired, the user isn’t in the MCP access group, or (SSO/SAML users) the sign-in re-validation window lapsed — log in to DBHawk once via your identity provider, the same token then works again. Reissue if needed.
  • DBHawk API 401/403 (after exchange) — wrong scope or the user lacks ACCESS_TO_DATA.
  • … needs MCP operation(s) …, which your MCP tier … does not allow — the statement type is not in the user’s MCP tier (the default tier is read-only). An admin assigns a higher tier to one of the user’s groups (DBHawk → Admin → MCP Management → MCP Tiers, then the User Group dialog → MCP Tier).
  • … cannot run as a read-only query. Use the execute_statement tool instead. — a write was sent through run_query; the model has to use execute_statement, where the client asks the user first.
  • Datasource '…' is read-only … (403) — the datasource is marked read-only in DBHawk, so writes are refused whatever the MCP tier allows; reads still work.
  • confirmationRequired: true, executed: false — not an error: the tier asks for confirmation of writes. Show the statement to the user and, after they approve it, call execute_statement again with confirm: true.
  • Missing configuration on startup — DBHAWK_BASE_URL / DBHAWK_TOKEN not set.
  • Invalid configuration on startup — DBHAWK_BASE_URL is not an https:// / http:// URL, contains credentials, a query or a trailing /api, or DBHAWK_TOKEN is a JWT instead of the personal dbh_… token.
  • Server not listed in /mcp — confirm Node ≥ 18 and that dist/dbhawk-mcp.mjs exists in the installed plugin. In Claude Desktop, use the full path to node (Desktop has a minimal PATH).
View this README on GitHub

Install

npx @anthropic-ai/mcpb validate mcpb/manifest.json # optional sanity check

Configuration

{ "mcpServers": { "dbhawk": { "command": "C:\\Program Files\\nodejs\\node.exe", "args": ["C:\\...\\dbhawk-mcp-plugin\\dbhawk\\mcp-server\\dist\\dbhawk-mcp.mjs"], "env": { "DBHAWK_BASE_URL": "https://demo.dbhawk.example.com", "DBHAWK_TOKEN": "PASTE_MCP_TOKEN_HERE", "DBHAWK_DEFAULT_DATASOURCE": "" } } } }