Skip to content

feat(connections): allow database override via "<id>/<database>" syntax#24

Open
rrsela wants to merge 1 commit intosrthkdev:mainfrom
rrsela:feat/database-override-syntax
Open

feat(connections): allow database override via "<id>/<database>" syntax#24
rrsela wants to merge 1 commit intosrthkdev:mainfrom
rrsela:feat/database-override-syntax

Conversation

@rrsela
Copy link
Copy Markdown

@rrsela rrsela commented May 6, 2026

Summary

Adds support for a <connectionId>/<database> override syntax on every tool that takes a connectionId, so a single workspace connection can target any database on the same server — no need to create N workspace connections for an N-database cluster.

Why

PostgreSQL (and other) connections are bound to a single database (the one in the JDBC URL / workspace config). When you browse a multi-DB server in DBeaver's navigator, DBeaver transparently opens additional sessions per database under the hood. The MCP server doesn't replicate that — it builds one client per logical workspace connection — so to query 8 databases on a cluster, you currently have to define 8 separate workspace connections.

This is impractical when:

  • A cluster has many tenant DBs (one DB per service)
  • You want to script lookups across DBs ad-hoc
  • You don't control the workspace config

What changed

  • src/utils.ts: extend sanitizeConnectionId to preserve /; add a small parseConnectionId helper that splits <id>/<database> into { baseId, databaseOverride }.
  • src/config-parser.ts: getConnection consumes parseConnectionId, looks up by baseId as before, and when an override is present returns a shallow clone with:
    • id = ${match.id}/${databaseOverride} (so per-DB pool caches stay separate)
    • database = override
    • properties.database = override
    • all other fields (host/port/user/password/SSL config) preserved
  • The original cached connection object is not mutated.

Backwards compatibility

When the connection ID does not contain /, behaviour is byte-for-byte identical to before. No changes to tool schemas, no breaking changes to existing callers.

Example

// Workspace connection "my-pg" targets database "postgres" by default.

// Before: list_tables only sees "postgres"
{ "connectionId": "my-pg" }

// After: any DB on the same server, no extra workspace connection
{ "connectionId": "my-pg/analytics" }
{ "connectionId": "my-pg/billing" }
{ "connectionId": "my-pg/inventory" }

Tests

Added 8 new tests (53 total, all passing):

  • parseConnectionId: no slash, single slash, empty halves, multiple slashes
  • sanitizeConnectionId: preserves / for the override suffix
  • getConnection: id lookup, name lookup, override applies database to all required fields, cache immutability, unknown base id returns null

npm run typechecknpm run lintnpm test ✓ (53/53)

Test plan

  • Typecheck passes
  • Lint passes
  • All existing tests still pass
  • New unit tests cover the override happy-path, error paths, and immutability invariant
  • Manually verified end-to-end against a real PostgreSQL RDS cluster: a single workspace connection (bound to postgres) successfully listed tables in 7 sibling databases via <id>/<database> syntax.

DBeaver connections are bound to a single database (the one in the JDBC URL
or workspace config). DBeaver itself opens additional sessions per database
under the hood when browsing the navigator, but the MCP server only builds
one client per logical connection — so a cluster with N databases requires
N separate workspace connections to query them all.

This change lets callers pass `<connectionId>/<database>` as the connectionId
to any tool. The parser splits on the first `/`, looks up the base connection
normally, then returns a clone with `database`, `properties.database`, and
a synthetic `id` (`<original-id>/<database>`) so connection pools stay
keyed per target DB. Without a `/`, behaviour is unchanged.

- src/utils.ts: extend sanitizeConnectionId to allow `/`; add parseConnectionId
  helper for the split logic
- src/config-parser.ts: getConnection consumes parseConnectionId and applies
  the override without mutating the cached connection
- tests: cover sanitize/parse helpers plus override happy-path, name lookup,
  unmatched base, and immutability of the cached connection

Example:
  // base connection points at "postgres"
  list_tables({ connectionId: "my-conn/analytics" })
  // queries "analytics" instead, no extra workspace connection needed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant