-
Notifications
You must be signed in to change notification settings - Fork 46
mcp/examples: add PostgreSQL MCP server example #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Thanks for this large contribution. It's a lot of work, and we appreciate that. It's cool that you have a complete implementation, with a Makefile, shell scripts, Dockerfile, and so on. This is definitely worth living somewhere. I'm just not sure it should live in this repo. There is a lot of code to maintain. What if we get a bug report against the Dockerfile or docker compose config? Or some bug about a runtime failure in a particular installation? I don't know if the maintainers of this repo are up to that sort of commitment. We're going to think about this and get back to you. |
Thanks a lot for your nice words. |
} | ||
|
||
// Parse path: /tableName/schema | ||
parts := []string{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var parts []string
Here and everywhere else.
|
||
const SCHEMA_PATH = "schema" | ||
|
||
type PostgresServer struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this and all related code to postgres_server.go.
} | ||
|
||
pathComponents := []string{} | ||
for _, component := range []string{resourceURL.Path} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why a loop here?
continue | ||
} | ||
subParts := []string{} | ||
for _, subPart := range []string{part} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, why loops over one element?
return nil, fmt.Errorf("invalid resource URI: %w", err) | ||
} | ||
|
||
pathComponents := []string{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there's a lot of parsing code here. It's not really clear what it's done. Split into a separate function, document that function clearly, and add tests.
current = "" | ||
} | ||
} else { | ||
current += string(r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be quadratic. Consider using a []string and calling strings.Join at the end.
// Start a read-only transaction | ||
tx, err := ps.db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) | ||
if err != nil { | ||
return &mcp.CallToolResultFor[struct{}]{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to return an error from a ToolHandler and we'll turn it into a CallToolResult for that error.
} | ||
|
||
// Prepare result slice | ||
var results []map[string]interface{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use any
// Use of this source code is governed by an MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package main |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests that are only about PostgresServer should move to postgres_server_test.go.
The modelcontextprotocol/servers repo is not going to happen. Here's what we'd like to do:
One concern is that the additional non-Go code is a maintenance burden for us. We are also concerned about security. You can leave all the other code here for now if it helps you test my suggested changes. Thanks again for this great piece of work! |
Add standalone PostgreSQL MCP server example with development environment and tooling. This implementation provides read-only database access, schema introspection, and query capabilities while maintaining clean dependency separation from the main SDK.
Motivation and Context
This is part of #33. This PR adds example of implementing mcp on PostgreSQL from https://modelcontextprotocol.io/examples.
The PR provides a production-ready PostgreSQL MCP server example with proper deployment options, testing, and development tooling.
How Has This Been Tested?
Breaking Changes
Types of changes
Checklist
Additional context
Architecture Decisions
examples/postgres/go.mod
for this exampleFiles Added
main.go
- PostgreSQL MCP server implementation with schema introspection and query toolsmain_test.go
- Comprehensive test suite with mock database testinggo.mod/go.sum
- Independent module definition with local SDK referenceDockerfile
- Multi-stage build for production deploymentdocker-compose.yml
- Complete development environment with PostgreSQLinit.sql
- Sample database schema and data for testingMakefile
- Build automation and development workflowdemo.sh
- Interactive demonstration scriptREADME.md
- Complete documentation following Python server format.env.example
- Configuration template.gitignore
- Proper exclusions for Go and Docker artifactsDevelopment Experience
make dev
sets up complete environmentMCP Features Implemented
postgres://host/table/schema
This implementation serves as a reference for building production MCP servers while maintaining clean project architecture.