Skip to content

Initial PostgreSQL MCP support#54431

Merged
gabrielcorado merged 35 commits intomasterfrom
gabrielcorado/pg-mcp-init
Jun 4, 2025
Merged

Initial PostgreSQL MCP support#54431
gabrielcorado merged 35 commits intomasterfrom
gabrielcorado/pg-mcp-init

Conversation

@gabrielcorado
Copy link
Copy Markdown
Contributor

@gabrielcorado gabrielcorado commented May 1, 2025

The initial version of the database access MCP is for PostgreSQL databases. Docs preview PR

For reviewers, here is a quick overview of the entire flow:

  1. Receive a list of databases available on the MCP server.
  2. Check each database's corresponding MCP server (per protocol). This is done using a simple Registry that maps MCP server constructors using database protocols.
  3. If there is no match, the database is skipped. Otherwise, we start an ALPN local proxy for each database.
  4. After that, we start the MCP servers, passing the databases they will serve.
  5. Start the "Root" MCP server, which, in addition to all tools registered by the protocol-specific MCPs, has the served database as a resource and a tool to list them.

What is not currently covered:

  • Automatically refresh the list of served databases. For example, if the user now has access to a different database, they'll need to restart the MCP command to get access to it.
Claude Desktop sample usage
# claude_desktop_config.json
{
  "mcpServers": {
    "teleport-databases": {
      "command": "tsh",
      "args": [
        "mcp",
        "db",
        "start",
        "teleport://databases/pg-dev?dbUser=postgres&dbName=postgres",
        "teleport://databases/pg-prod?dbUser=readonly&dbName=postgres"
      ]
    }
  }
}

resources

tools

Copy link
Copy Markdown
Contributor

@greedy52 greedy52 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 ship it

Comment thread lib/client/db/mcp/errors.go Outdated
Comment thread lib/client/db/mcp/mcp.go Outdated
Comment thread lib/client/db/mcp/server.go Outdated
Comment thread lib/client/db/mcp/server.go
Comment thread lib/client/db/postgres/mcp/mcp.go Outdated
Comment thread lib/client/db/postgres/mcp/mcp.go Outdated
Comment thread tool/tsh/common/mcp.go Outdated
Comment thread tool/tsh/common/mcp.go Outdated
Comment thread tool/tsh/common/mcp.go Outdated
Comment thread tool/tsh/common/mcp.go Outdated
Comment thread lib/client/db/mcp/errors.go Outdated
Comment thread lib/client/db/postgres/mcp/mcp.go Outdated
@greedy52 greedy52 added this to the MCP access MVP (v18.1.0) milestone May 12, 2025
@gabrielcorado gabrielcorado marked this pull request as ready for review May 16, 2025 02:49
@gabrielcorado gabrielcorado requested review from Tener and greedy52 May 16, 2025 02:49
@github-actions github-actions Bot requested review from Joerger and espadolini May 16, 2025 02:50
@github-actions github-actions Bot added size/xl tsh tsh - Teleport's command line tool for logging into nodes running Teleport. labels May 16, 2025
@public-teleport-github-review-bot
Copy link
Copy Markdown

@gabrielcorado - this PR will require admin approval to merge due to its size. Consider breaking it up into a series smaller changes.

@espadolini espadolini removed their request for review May 16, 2025 07:56
Comment thread lib/utils/listener/memory.go
Comment thread tool/tsh/common/mcp.go Outdated
@gabrielcorado
Copy link
Copy Markdown
Contributor Author

gabrielcorado commented May 28, 2025

I've updated the implementation to better fit the login command (which will be added in a separate PR). The changes were:

  • tsh mcp db start now receives a list of databases in the MCP resources format. The resource URI will also include the connection parameters (database user and database name). This way, users can tweak their configuration and access different databases without requiring a branch. (see PR description for a config example).
  • The filtering options --query and --labels will be available on the tsh mcp db login command.
  • The tsh MCP db start command is now hidden, as manually running it on the terminal is ineffective. Users must rely on the login command to generate the correct command.

The new UX flow would consist of users running the login command, which will generate the final tsh mcp db start command and update their MCP client.

@gabrielcorado gabrielcorado added the no-changelog Indicates that a PR does not require a changelog entry label May 28, 2025
Copy link
Copy Markdown
Collaborator

@r0mant r0mant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment thread lib/client/mcp/uri.go
Comment thread lib/client/mcp/uri.go Outdated
)

// databaseURITemplate template used to parse database resource URIs.
var databaseURITemplate = urlpath.New("/databases/:name")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we include the cluster name here? To be able to add support for trusted clusters in future for example.

Copy link
Copy Markdown
Contributor

@greedy52 greedy52 May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we gonna support more resources and trusted cluster, might just adopt the same scheme Connect uses:

var pathDbs = urlpath.New("/clusters/:cluster/dbs/:dbName")
var pathLeafDbs = urlpath.New("/clusters/:cluster/leaves/:leaf/dbs/:dbName")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated to include the cluster information.

Comment thread lib/client/mcp/uri.go Outdated
Comment thread lib/client/mcp/uri.go
// path returns the resoruce URI full path. For resources, we must include the
// hostname as it indicates the resource type.
func (u ResourceURI) path() string {
return "/" + u.url.Hostname() + u.url.Path
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Use path.Join here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot use the URL.JoinPath because part of the resource URI is parsed into the url.Host, which we want included here.

Comment thread lib/client/db/postgres/mcp/mcp.go Outdated
Comment thread tool/tsh/common/mcp.go Outdated
Comment thread tool/tsh/common/mcp.go Outdated
// otherwise the MCP clients will be stuck waiting for a response.
tc.NonInteractive = false

uris := make([]*mcp.ResourceURI, len(c.databaseURIs))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we deal with duplicates? or worse, same database service with different query params

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a validation for the duplication. The current resource URI wouldn't support a single database configured multiple times.

@public-teleport-github-review-bot public-teleport-github-review-bot Bot removed the request for review from Joerger May 29, 2025 15:12
@gabrielcorado gabrielcorado enabled auto-merge June 4, 2025 20:22
@gabrielcorado gabrielcorado added this pull request to the merge queue Jun 4, 2025
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 4, 2025
@gabrielcorado gabrielcorado added this pull request to the merge queue Jun 4, 2025
Merged via the queue into master with commit 639992c Jun 4, 2025
39 checks passed
@gabrielcorado gabrielcorado deleted the gabrielcorado/pg-mcp-init branch June 4, 2025 21:38
Comment thread lib/utils/cli.go

// InitLogger configures the global logger for a given purpose / verbosity level
func InitLogger(purpose LoggingPurpose, level slog.Level, opts ...LoggerOption) error {
func InitLogger(purpose LoggingPurpose, level slog.Level, opts ...LoggerOption) (*slog.Logger, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This signature change has broken tsh vnet on darwin:

if err := utils.InitLogger(utils.LoggingForDaemon, level, utils.WithOSLog(subsystem)); err != nil {
- probably got missed due to build tags.

greedy52 pushed a commit that referenced this pull request Jun 26, 2025
* feat(mcp): initial postgres mcp

* test(postgres): fix missing mock function

* fix(gomod): go mod tidy all

* refactor: code review suggestions

* fix(tsh): mcp init missing logger

* chore(tsh): missing other route to database field

* refactor: use in-memory net listener

* test(tsh): add mcp db command test

* chore: fix license

* refactor(tsh): move logger init

* test(mcp): sort slices to avoid flakiness

* chore: fix lint

* test(mcp): sort the resources before assertion

* fix(mcp): update error handler for better message

* refactor: code review suggestions

* feat: add external error retriever for more accurate error messages

* refactor: use the same logger init for mcp purposes

* refactor: code review suggestions

* refactor(tsh): rename command to `tsh mcp db start`

* refactor(mcp): protect database resources with rw mutex

* chore: update server godocs

* chore: go mod tidy

* refactor: update command to take list of databases

* chore(mcp): license

* chore(tsh): remove unused function

* refactor: code review suggestions

* refactor(tsh): validate duplicated databases in MCP configuration

* refactor(tsh): rename files to mcp_db

* feat(mcp): add cluster name to the database resource
@greedy52 greedy52 mentioned this pull request Jun 26, 2025
github-merge-queue Bot pushed a commit that referenced this pull request Jul 21, 2025
* Initial PostgreSQL MCP support (#54431)

* feat(mcp): initial postgres mcp

* test(postgres): fix missing mock function

* fix(gomod): go mod tidy all

* refactor: code review suggestions

* fix(tsh): mcp init missing logger

* chore(tsh): missing other route to database field

* refactor: use in-memory net listener

* test(tsh): add mcp db command test

* chore: fix license

* refactor(tsh): move logger init

* test(mcp): sort slices to avoid flakiness

* chore: fix lint

* test(mcp): sort the resources before assertion

* fix(mcp): update error handler for better message

* refactor: code review suggestions

* feat: add external error retriever for more accurate error messages

* refactor: use the same logger init for mcp purposes

* refactor: code review suggestions

* refactor(tsh): rename command to `tsh mcp db start`

* refactor(mcp): protect database resources with rw mutex

* chore: update server godocs

* chore: go mod tidy

* refactor: update command to take list of databases

* chore(mcp): license

* chore(tsh): remove unused function

* refactor: code review suggestions

* refactor(tsh): validate duplicated databases in MCP configuration

* refactor(tsh): rename files to mcp_db

* feat(mcp): add cluster name to the database resource

* fix(tsh): update InitLogger return type (#55479)

* MCP access part 1: update app definition and config (#54706)

* MCP access part 1: update app definition and config

* address feedback

* make -C integrations/operator crd

* MCP access part 2: new role options, access checker, role editor (#54734)

* MCP access part 2: new role options, access checker, role editor

* catch unsupported mcp fields

* simplify mcpToolsToModel

* MCP access part 3: audit events and reporting (#54779)

* MCP access part 3: audit events and reporting

* add new icon, storybook, format

* MCP access part 4: mcputils (#54880)

* MCP access part 4: mcp helpers

* address feedback

* address comment, minor edits

* update mcp-go

* MCP access part 5: Claude desktop config parser (#55179)

* claude desktop config

* rework

* split Config to Config and FileConfig

* add a comment on unofficial linux

* MCP access part 6: "tsh mcp ls" (#55292)

* MCP access part 6: "tsh mcp ls"

* address feedback

* MCP access part 7: MCP app in Web UI (#55306)

* MCP access part 7: MCP app in Web UI

* Make spacing in modal closer to what's in database modal

* add mcp app to ResourceActionButton.story.tsx

* move AppSubKind to shared/services/types.

* remove --format claude (not needed see part 8)

* add jsdoc

---------

Co-authored-by: Rafał Cieślak <rafal.cieslak@goteleport.com>

* MCP access part 8: tsh mcp config (#55370)

* MCP access part 8: tsh mcp login/logout

* change to --format and --config-file

* switch to config and drop logout

* enable debug by default

* remove unused ut functions

* MCP access part 9: tsh mcp connect, stub server, integration test (#55547)

* MCP access part 9: tsh mcp connect, stub server, integration test

* fix tests and lint

* MCP access part 10: server handler (#55644)

* MCP access part 10: server handler

* address feedback and fix docker tests

* add more comments

* minor lint fix

* move set logger default after other checks

* Implement `tsh mcp db config` (#55781)

* feat(tsh): add `tsh mcp db config` subcommand

* chore(claude): fix lint

* refactor: code review suggestions

* refactor: code review suggestions

* test(tsh): add missing option on test case

* chore(tsh): add message on manually adding database URI

* Refactor MCP database access to dial ALPN proxy directly (#55836)

* refactor: dial database instead of using local proxy for MCP servers

* refactor: review suggestions

* manual fixes

* tctl users add/update to support mcp tools trait (#56771)

* tctl users add/update to support mcp tools trait

* revert empty slice capability

* Enhances MCP servers usage with Cursor (#56474)

* feat(mcp): enhances MCP servers usage with Cursor

* refactor: code review suggestions

* mcputils refactor and new mcptest package (#56010)

* mcp server and mcputils refactor

* mcptest package

* allow testing in mcptest

* Teleport MCP demo server (#56637)

* Teleport MCP demo server

* replace guide tool with session tool, and switch to resource label

* add new flag to teleport configure

* replace teleport_session_id with mcp_transport_type

* feat(gomod): update mcp-go to v0.32.0

* eslint-disable-next-line (same in master)

---------

Co-authored-by: Gabriel Corado <gabriel.oliveira@goteleport.com>
Co-authored-by: Rafał Cieślak <rafal.cieslak@goteleport.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog Indicates that a PR does not require a changelog entry size/xl tsh tsh - Teleport's command line tool for logging into nodes running Teleport.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants