nuuly-postgres-mcp-server 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nuuly_postgres_mcp_server-1.0.0/PKG-INFO +179 -0
- nuuly_postgres_mcp_server-1.0.0/README.md +165 -0
- nuuly_postgres_mcp_server-1.0.0/pyproject.toml +27 -0
- nuuly_postgres_mcp_server-1.0.0/setup.cfg +4 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server/__init__.py +8 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server/__main__.py +26 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server/server.py +146 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server.egg-info/PKG-INFO +179 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server.egg-info/SOURCES.txt +11 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server.egg-info/dependency_links.txt +1 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server.egg-info/entry_points.txt +2 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server.egg-info/requires.txt +3 -0
- nuuly_postgres_mcp_server-1.0.0/src/nuuly_postgres_mcp_server.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nuuly-postgres-mcp-server
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: MCP server for accessing Nuuly PostgreSQL database resources
|
|
5
|
+
Author-email: Nuuly Engineering <engineering@nuuly.com>
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://github.com/urbn/r15-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/urbn/r15-mcp
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: fastmcp>=2.2.1
|
|
12
|
+
Requires-Dist: httpx>=0.24.0
|
|
13
|
+
Requires-Dist: psycopg2-binary>=2.9.5
|
|
14
|
+
|
|
15
|
+
# Nuuly Postgres MCP Server
|
|
16
|
+
|
|
17
|
+
MCP server for accessing Nuuly PostgreSQL database resources through Claude Desktop and other AI assistants.
|
|
18
|
+
|
|
19
|
+
## Contents
|
|
20
|
+
- `src/nuuly_postgres_mcp_server/`: Package source code
|
|
21
|
+
- `__init__.py`: Package initialization
|
|
22
|
+
- `__main__.py`: Entry point for running as a module
|
|
23
|
+
- `server.py`: Main server implementation
|
|
24
|
+
- `pyproject.toml`: Package configuration
|
|
25
|
+
- `.env.example`: Example environment variables
|
|
26
|
+
|
|
27
|
+
## Purpose
|
|
28
|
+
This MCP server provides PostgreSQL database connectivity functionality for the broader MCP platform. It connects to CloudSQL Postgres non-prod databases and enables AI code editors to:
|
|
29
|
+
|
|
30
|
+
1. Understand all tables and schemas
|
|
31
|
+
2. Run queries against the non-prod database
|
|
32
|
+
3. List available databases and their aliases
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### Option 1: Install from PyPI
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install nuuly-postgres-mcp-server
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Option 2: Install from Source
|
|
45
|
+
|
|
46
|
+
1. Clone the repository:
|
|
47
|
+
```bash
|
|
48
|
+
git clone <REPO_URL>
|
|
49
|
+
cd r15-mcp/mcp_servers/postgres-mcp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. Install the package in development mode:
|
|
53
|
+
```bash
|
|
54
|
+
pip install -e .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Local Development
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
- Python 3.8 or higher
|
|
61
|
+
- pip package manager
|
|
62
|
+
|
|
63
|
+
### Setup
|
|
64
|
+
|
|
65
|
+
1. Clone the repository:
|
|
66
|
+
```bash
|
|
67
|
+
git clone <REPO_URL>
|
|
68
|
+
cd r15-mcp/mcp_servers/postgres-mcp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. Create and activate a virtual environment (optional but recommended):
|
|
72
|
+
```bash
|
|
73
|
+
python -m venv venv
|
|
74
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
3. Install development dependencies:
|
|
78
|
+
```bash
|
|
79
|
+
pip install -e .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
4. Set up environment variables:
|
|
83
|
+
```bash
|
|
84
|
+
cp .env.example .env
|
|
85
|
+
# Edit .env with your actual values
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Environment Variables
|
|
89
|
+
|
|
90
|
+
The following environment variables can be set to customize the behavior of the MCP server:
|
|
91
|
+
|
|
92
|
+
- `CLOUD_RUN_PROXY_URL`: URL of the Cloud Run DB Proxy (default: "https://postgres-mcp-toolbox-186512416539.us-east4.run.app")
|
|
93
|
+
- `DB_PROXY_API_KEY`: API key for authenticating with the Cloud Run DB Proxy (required)
|
|
94
|
+
- `LOG_LEVEL`: Logging level (default: INFO)
|
|
95
|
+
|
|
96
|
+
## Usage
|
|
97
|
+
|
|
98
|
+
### For Claude Desktop
|
|
99
|
+
|
|
100
|
+
Add this configuration to your Claude Desktop settings:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"Nuuly Postgres MCP": {
|
|
105
|
+
"command": "nuuly-postgres-mcp",
|
|
106
|
+
"env": {
|
|
107
|
+
"PYTHONUNBUFFERED": "1",
|
|
108
|
+
"CLOUD_RUN_PROXY_URL": "https://postgres-mcp-toolbox-186512416539.us-east4.run.app",
|
|
109
|
+
"DB_PROXY_API_KEY": "YOUR_API_KEY_HERE"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Command Line Usage
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
nuuly-postgres-mcp
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Deployment
|
|
122
|
+
|
|
123
|
+
### As a GCP Cloud Function
|
|
124
|
+
|
|
125
|
+
This MCP server is designed to be deployed as a GCP Cloud Function with the following parameters:
|
|
126
|
+
|
|
127
|
+
- Project: `rental-dev`
|
|
128
|
+
- Region: `us-east4`
|
|
129
|
+
- Serverless VPC Connector: `rental-vpc-connector`
|
|
130
|
+
- Entry point: `main.py`
|
|
131
|
+
|
|
132
|
+
The function connects to CloudSQL PostgreSQL using private IP (10.171.208.52) and uses environment variables for DB credentials.
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
environment_variables:
|
|
136
|
+
CLOUD_RUN_PROXY_URL: "https://postgres-mcp-toolbox-186512416539.us-east4.run.app"
|
|
137
|
+
DB_PROXY_API_KEY: "${DB_PROXY_API_KEY}"
|
|
138
|
+
LOG_LEVEL: "INFO"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Building and Publishing the Package
|
|
142
|
+
|
|
143
|
+
To build and publish the package to PyPI:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
python -m build
|
|
147
|
+
python -m twine upload dist/*
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Available Tools
|
|
151
|
+
|
|
152
|
+
This MCP server provides the following tools to AI assistants:
|
|
153
|
+
|
|
154
|
+
- `run_query`: Execute SQL queries against the database
|
|
155
|
+
- `get_schema`: Get database schema information
|
|
156
|
+
- `list_databases`: List available databases
|
|
157
|
+
|
|
158
|
+
## Troubleshooting
|
|
159
|
+
|
|
160
|
+
### Connection Issues with Claude Desktop
|
|
161
|
+
|
|
162
|
+
If you experience connection issues with Claude Desktop (client transport closing), ensure that:
|
|
163
|
+
|
|
164
|
+
1. The server is running with the correct URL for the Cloud Run DB Proxy
|
|
165
|
+
2. The API key is correctly set and properly escaped in any curl commands (use single quotes)
|
|
166
|
+
3. The server is properly handling heartbeat and shutdown messages
|
|
167
|
+
4. The JSON-RPC initialization response includes `keepAlive: true` and `supportsHeartbeats: true`
|
|
168
|
+
|
|
169
|
+
Example curl command to test the API:
|
|
170
|
+
```bash
|
|
171
|
+
curl -X GET "https://postgres-mcp-toolbox-186512416539.us-east4.run.app/databases" -H 'X-API-KEY: YOUR_API_KEY_HERE'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Common Issues
|
|
175
|
+
|
|
176
|
+
- **Missing API Key**: Ensure the `DB_PROXY_API_KEY` environment variable is set
|
|
177
|
+
- **Permission Errors**: Verify that your API key has the necessary permissions
|
|
178
|
+
- **Timeout Errors**: Check network latency or increase the timeout settings in the code
|
|
179
|
+
- **Heartbeat Issues**: Ensure the MCP server properly implements heartbeat handlers
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Nuuly Postgres MCP Server
|
|
2
|
+
|
|
3
|
+
MCP server for accessing Nuuly PostgreSQL database resources through Claude Desktop and other AI assistants.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
- `src/nuuly_postgres_mcp_server/`: Package source code
|
|
7
|
+
- `__init__.py`: Package initialization
|
|
8
|
+
- `__main__.py`: Entry point for running as a module
|
|
9
|
+
- `server.py`: Main server implementation
|
|
10
|
+
- `pyproject.toml`: Package configuration
|
|
11
|
+
- `.env.example`: Example environment variables
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
This MCP server provides PostgreSQL database connectivity functionality for the broader MCP platform. It connects to CloudSQL Postgres non-prod databases and enables AI code editors to:
|
|
15
|
+
|
|
16
|
+
1. Understand all tables and schemas
|
|
17
|
+
2. Run queries against the non-prod database
|
|
18
|
+
3. List available databases and their aliases
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
### Option 1: Install from PyPI
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install nuuly-postgres-mcp-server
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Option 2: Install from Source
|
|
31
|
+
|
|
32
|
+
1. Clone the repository:
|
|
33
|
+
```bash
|
|
34
|
+
git clone <REPO_URL>
|
|
35
|
+
cd r15-mcp/mcp_servers/postgres-mcp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. Install the package in development mode:
|
|
39
|
+
```bash
|
|
40
|
+
pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Local Development
|
|
44
|
+
|
|
45
|
+
### Prerequisites
|
|
46
|
+
- Python 3.8 or higher
|
|
47
|
+
- pip package manager
|
|
48
|
+
|
|
49
|
+
### Setup
|
|
50
|
+
|
|
51
|
+
1. Clone the repository:
|
|
52
|
+
```bash
|
|
53
|
+
git clone <REPO_URL>
|
|
54
|
+
cd r15-mcp/mcp_servers/postgres-mcp
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
2. Create and activate a virtual environment (optional but recommended):
|
|
58
|
+
```bash
|
|
59
|
+
python -m venv venv
|
|
60
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
3. Install development dependencies:
|
|
64
|
+
```bash
|
|
65
|
+
pip install -e .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
4. Set up environment variables:
|
|
69
|
+
```bash
|
|
70
|
+
cp .env.example .env
|
|
71
|
+
# Edit .env with your actual values
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Environment Variables
|
|
75
|
+
|
|
76
|
+
The following environment variables can be set to customize the behavior of the MCP server:
|
|
77
|
+
|
|
78
|
+
- `CLOUD_RUN_PROXY_URL`: URL of the Cloud Run DB Proxy (default: "https://postgres-mcp-toolbox-186512416539.us-east4.run.app")
|
|
79
|
+
- `DB_PROXY_API_KEY`: API key for authenticating with the Cloud Run DB Proxy (required)
|
|
80
|
+
- `LOG_LEVEL`: Logging level (default: INFO)
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
### For Claude Desktop
|
|
85
|
+
|
|
86
|
+
Add this configuration to your Claude Desktop settings:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"Nuuly Postgres MCP": {
|
|
91
|
+
"command": "nuuly-postgres-mcp",
|
|
92
|
+
"env": {
|
|
93
|
+
"PYTHONUNBUFFERED": "1",
|
|
94
|
+
"CLOUD_RUN_PROXY_URL": "https://postgres-mcp-toolbox-186512416539.us-east4.run.app",
|
|
95
|
+
"DB_PROXY_API_KEY": "YOUR_API_KEY_HERE"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Command Line Usage
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
nuuly-postgres-mcp
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Deployment
|
|
108
|
+
|
|
109
|
+
### As a GCP Cloud Function
|
|
110
|
+
|
|
111
|
+
This MCP server is designed to be deployed as a GCP Cloud Function with the following parameters:
|
|
112
|
+
|
|
113
|
+
- Project: `rental-dev`
|
|
114
|
+
- Region: `us-east4`
|
|
115
|
+
- Serverless VPC Connector: `rental-vpc-connector`
|
|
116
|
+
- Entry point: `main.py`
|
|
117
|
+
|
|
118
|
+
The function connects to CloudSQL PostgreSQL using private IP (10.171.208.52) and uses environment variables for DB credentials.
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
environment_variables:
|
|
122
|
+
CLOUD_RUN_PROXY_URL: "https://postgres-mcp-toolbox-186512416539.us-east4.run.app"
|
|
123
|
+
DB_PROXY_API_KEY: "${DB_PROXY_API_KEY}"
|
|
124
|
+
LOG_LEVEL: "INFO"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Building and Publishing the Package
|
|
128
|
+
|
|
129
|
+
To build and publish the package to PyPI:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
python -m build
|
|
133
|
+
python -m twine upload dist/*
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Available Tools
|
|
137
|
+
|
|
138
|
+
This MCP server provides the following tools to AI assistants:
|
|
139
|
+
|
|
140
|
+
- `run_query`: Execute SQL queries against the database
|
|
141
|
+
- `get_schema`: Get database schema information
|
|
142
|
+
- `list_databases`: List available databases
|
|
143
|
+
|
|
144
|
+
## Troubleshooting
|
|
145
|
+
|
|
146
|
+
### Connection Issues with Claude Desktop
|
|
147
|
+
|
|
148
|
+
If you experience connection issues with Claude Desktop (client transport closing), ensure that:
|
|
149
|
+
|
|
150
|
+
1. The server is running with the correct URL for the Cloud Run DB Proxy
|
|
151
|
+
2. The API key is correctly set and properly escaped in any curl commands (use single quotes)
|
|
152
|
+
3. The server is properly handling heartbeat and shutdown messages
|
|
153
|
+
4. The JSON-RPC initialization response includes `keepAlive: true` and `supportsHeartbeats: true`
|
|
154
|
+
|
|
155
|
+
Example curl command to test the API:
|
|
156
|
+
```bash
|
|
157
|
+
curl -X GET "https://postgres-mcp-toolbox-186512416539.us-east4.run.app/databases" -H 'X-API-KEY: YOUR_API_KEY_HERE'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Common Issues
|
|
161
|
+
|
|
162
|
+
- **Missing API Key**: Ensure the `DB_PROXY_API_KEY` environment variable is set
|
|
163
|
+
- **Permission Errors**: Verify that your API key has the necessary permissions
|
|
164
|
+
- **Timeout Errors**: Check network latency or increase the timeout settings in the code
|
|
165
|
+
- **Heartbeat Issues**: Ensure the MCP server properly implements heartbeat handlers
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nuuly-postgres-mcp-server"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "MCP server for accessing Nuuly PostgreSQL database resources"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{name = "Nuuly Engineering", email = "engineering@nuuly.com"}]
|
|
11
|
+
license = {text = "Proprietary"}
|
|
12
|
+
requires-python = ">=3.8"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"fastmcp>=2.2.1",
|
|
15
|
+
"httpx>=0.24.0",
|
|
16
|
+
"psycopg2-binary>=2.9.5",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/urbn/r15-mcp"
|
|
21
|
+
Repository = "https://github.com/urbn/r15-mcp"
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
nuuly-postgres-mcp = "nuuly_postgres_mcp_server.__main__:main"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
where = ["src"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entry point for running the Nuuly Postgres MCP Server as a module.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from . import run
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
"""Entry point for running the package as a module."""
|
|
11
|
+
# Setup logging configuration
|
|
12
|
+
logging.basicConfig(
|
|
13
|
+
level=logging.INFO,
|
|
14
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
15
|
+
stream=sys.stderr
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Run the MCP server
|
|
19
|
+
try:
|
|
20
|
+
run()
|
|
21
|
+
except Exception as e:
|
|
22
|
+
logging.error(f"Error running MCP server: {e}")
|
|
23
|
+
sys.exit(1)
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
main()
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Nuuly Postgres MCP Server
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import httpx
|
|
7
|
+
import logging
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
# Configure logging
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
# Configuration for Cloud Run DB Proxy
|
|
16
|
+
CLOUD_RUN_PROXY_URL = os.getenv("CLOUD_RUN_PROXY_URL", "https://www.example.com")
|
|
17
|
+
db_proxy_api_key = os.getenv("DB_PROXY_API_KEY")
|
|
18
|
+
|
|
19
|
+
# Import FastMCP
|
|
20
|
+
from fastmcp import FastMCP, Context
|
|
21
|
+
|
|
22
|
+
# Create MCP server with proper initialization
|
|
23
|
+
mcp = FastMCP("Nuuly Postgres MCP")
|
|
24
|
+
|
|
25
|
+
@mcp.tool()
|
|
26
|
+
async def run_query(ctx: Context, database: str, sql: str) -> str:
|
|
27
|
+
"""Run a SQL query against the database and return results.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
ctx: The MCP server provided context.
|
|
31
|
+
database: The name of a database (like orders, waving, box, product, etc)
|
|
32
|
+
sql: The SQL query string to execute. Accepts SELECT, SHOW, EXPLAIN queries only
|
|
33
|
+
Returns:
|
|
34
|
+
A JSON string representing the query result (columns and rows).
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
# Validate API key is available
|
|
38
|
+
if not db_proxy_api_key:
|
|
39
|
+
logger.error("DB_PROXY_API_KEY environment variable not set")
|
|
40
|
+
return json.dumps({"error": "DB_PROXY_API_KEY environment variable not set"})
|
|
41
|
+
|
|
42
|
+
# Prepare request payload
|
|
43
|
+
payload = {
|
|
44
|
+
"database": database,
|
|
45
|
+
"sql": sql
|
|
46
|
+
}
|
|
47
|
+
headers = {"X-API-KEY": db_proxy_api_key}
|
|
48
|
+
url = f"{CLOUD_RUN_PROXY_URL}/query"
|
|
49
|
+
|
|
50
|
+
logger.info(f"Executing query on database: {database}")
|
|
51
|
+
async with httpx.AsyncClient(timeout=15) as client:
|
|
52
|
+
response = await client.post(url, json=payload, headers=headers)
|
|
53
|
+
response.raise_for_status()
|
|
54
|
+
return response.json()
|
|
55
|
+
except httpx.HTTPStatusError as e:
|
|
56
|
+
logger.error(f"HTTPStatusError: status={e.response.status_code}, body={e.response.text}")
|
|
57
|
+
return json.dumps({"error": f"Cloud Run proxy error: {e.response.status_code}", "details": e.response.text})
|
|
58
|
+
except Exception as e:
|
|
59
|
+
logger.error(f"Exception reaching Cloud Run DB proxy: {e}")
|
|
60
|
+
return json.dumps({"error": "Failed to reach Cloud Run DB proxy", "details": str(e)})
|
|
61
|
+
|
|
62
|
+
@mcp.tool()
|
|
63
|
+
async def get_schema(ctx: Context, database: str) -> str:
|
|
64
|
+
"""Get the schema of all tables in the database.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
ctx: The MCP server provided context.
|
|
68
|
+
database: The name of a database (like orders, waving, box, product, etc) - this can
|
|
69
|
+
be an instance name or an alias. A full list of databases is available via the
|
|
70
|
+
`list_databases` tool.
|
|
71
|
+
Returns:
|
|
72
|
+
A JSON string describing the tables and their columns, foreign keys, primary keys,
|
|
73
|
+
and constraints.
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
# Validate API key is available
|
|
77
|
+
if not db_proxy_api_key:
|
|
78
|
+
logger.error("DB_PROXY_API_KEY environment variable not set")
|
|
79
|
+
return json.dumps({"error": "DB_PROXY_API_KEY environment variable not set"})
|
|
80
|
+
|
|
81
|
+
payload = {"database": database}
|
|
82
|
+
headers = {"X-API-KEY": db_proxy_api_key}
|
|
83
|
+
url = f"{CLOUD_RUN_PROXY_URL}/schema"
|
|
84
|
+
|
|
85
|
+
logger.info(f"Fetching schema for database: {database}")
|
|
86
|
+
async with httpx.AsyncClient(timeout=15) as client:
|
|
87
|
+
response = await client.post(url, json=payload, headers=headers)
|
|
88
|
+
response.raise_for_status()
|
|
89
|
+
return response.json()
|
|
90
|
+
except httpx.HTTPStatusError as e:
|
|
91
|
+
logger.error(f"HTTPStatusError: status={e.response.status_code}, body={e.response.text}")
|
|
92
|
+
return json.dumps({"error": f"Cloud Run proxy error: {e.response.status_code}", "details": e.response.text})
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logger.error(f"Exception reaching Cloud Run DB proxy: {e}")
|
|
95
|
+
return json.dumps({"error": "Failed to reach Cloud Run DB proxy", "details": str(e)})
|
|
96
|
+
|
|
97
|
+
@mcp.tool()
|
|
98
|
+
async def list_databases(ctx: Context) -> str:
|
|
99
|
+
"""List all databases and their aliases accessible via the Cloud Run DB Proxy.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
ctx: The MCP server provided context.
|
|
103
|
+
Returns:
|
|
104
|
+
A JSON string representing the list of databases, their aliases, and instance info.
|
|
105
|
+
"""
|
|
106
|
+
try:
|
|
107
|
+
# Validate API key is available
|
|
108
|
+
if not db_proxy_api_key:
|
|
109
|
+
logger.error("DB_PROXY_API_KEY environment variable not set")
|
|
110
|
+
return json.dumps({"error": "DB_PROXY_API_KEY environment variable not set"})
|
|
111
|
+
|
|
112
|
+
headers = {"X-API-KEY": db_proxy_api_key}
|
|
113
|
+
url = f"{CLOUD_RUN_PROXY_URL}/databases"
|
|
114
|
+
|
|
115
|
+
logger.info("Listing available databases")
|
|
116
|
+
async with httpx.AsyncClient(timeout=15) as client:
|
|
117
|
+
response = await client.get(url, headers=headers)
|
|
118
|
+
response.raise_for_status()
|
|
119
|
+
return response.json()
|
|
120
|
+
except httpx.HTTPStatusError as e:
|
|
121
|
+
logger.error(f"HTTPStatusError: status={e.response.status_code}, body={e.response.text}")
|
|
122
|
+
return json.dumps({"error": f"Cloud Run proxy error: {e.response.status_code}", "details": e.response.text})
|
|
123
|
+
except Exception as e:
|
|
124
|
+
logger.error(f"Exception reaching Cloud Run DB proxy: {e}")
|
|
125
|
+
return json.dumps({"error": "Failed to reach Cloud Run DB proxy", "details": str(e)})
|
|
126
|
+
|
|
127
|
+
def run():
|
|
128
|
+
"""Entry point for the MCP server."""
|
|
129
|
+
# Setup logging configuration if not already configured
|
|
130
|
+
if not logging.getLogger().handlers:
|
|
131
|
+
logging.basicConfig(
|
|
132
|
+
level=logging.INFO,
|
|
133
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
134
|
+
stream=sys.stderr
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Add global exception handler
|
|
138
|
+
try:
|
|
139
|
+
logger.info("Starting Nuuly Postgres MCP Server")
|
|
140
|
+
mcp.run()
|
|
141
|
+
except Exception as e:
|
|
142
|
+
logger.error(f"Unhandled exception in MCP server: {e}", exc_info=True)
|
|
143
|
+
sys.exit(1)
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
run()
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nuuly-postgres-mcp-server
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: MCP server for accessing Nuuly PostgreSQL database resources
|
|
5
|
+
Author-email: Nuuly Engineering <engineering@nuuly.com>
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://github.com/urbn/r15-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/urbn/r15-mcp
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: fastmcp>=2.2.1
|
|
12
|
+
Requires-Dist: httpx>=0.24.0
|
|
13
|
+
Requires-Dist: psycopg2-binary>=2.9.5
|
|
14
|
+
|
|
15
|
+
# Nuuly Postgres MCP Server
|
|
16
|
+
|
|
17
|
+
MCP server for accessing Nuuly PostgreSQL database resources through Claude Desktop and other AI assistants.
|
|
18
|
+
|
|
19
|
+
## Contents
|
|
20
|
+
- `src/nuuly_postgres_mcp_server/`: Package source code
|
|
21
|
+
- `__init__.py`: Package initialization
|
|
22
|
+
- `__main__.py`: Entry point for running as a module
|
|
23
|
+
- `server.py`: Main server implementation
|
|
24
|
+
- `pyproject.toml`: Package configuration
|
|
25
|
+
- `.env.example`: Example environment variables
|
|
26
|
+
|
|
27
|
+
## Purpose
|
|
28
|
+
This MCP server provides PostgreSQL database connectivity functionality for the broader MCP platform. It connects to CloudSQL Postgres non-prod databases and enables AI code editors to:
|
|
29
|
+
|
|
30
|
+
1. Understand all tables and schemas
|
|
31
|
+
2. Run queries against the non-prod database
|
|
32
|
+
3. List available databases and their aliases
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### Option 1: Install from PyPI
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install nuuly-postgres-mcp-server
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Option 2: Install from Source
|
|
45
|
+
|
|
46
|
+
1. Clone the repository:
|
|
47
|
+
```bash
|
|
48
|
+
git clone <REPO_URL>
|
|
49
|
+
cd r15-mcp/mcp_servers/postgres-mcp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. Install the package in development mode:
|
|
53
|
+
```bash
|
|
54
|
+
pip install -e .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Local Development
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
- Python 3.8 or higher
|
|
61
|
+
- pip package manager
|
|
62
|
+
|
|
63
|
+
### Setup
|
|
64
|
+
|
|
65
|
+
1. Clone the repository:
|
|
66
|
+
```bash
|
|
67
|
+
git clone <REPO_URL>
|
|
68
|
+
cd r15-mcp/mcp_servers/postgres-mcp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. Create and activate a virtual environment (optional but recommended):
|
|
72
|
+
```bash
|
|
73
|
+
python -m venv venv
|
|
74
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
3. Install development dependencies:
|
|
78
|
+
```bash
|
|
79
|
+
pip install -e .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
4. Set up environment variables:
|
|
83
|
+
```bash
|
|
84
|
+
cp .env.example .env
|
|
85
|
+
# Edit .env with your actual values
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Environment Variables
|
|
89
|
+
|
|
90
|
+
The following environment variables can be set to customize the behavior of the MCP server:
|
|
91
|
+
|
|
92
|
+
- `CLOUD_RUN_PROXY_URL`: URL of the Cloud Run DB Proxy (default: "https://postgres-mcp-toolbox-186512416539.us-east4.run.app")
|
|
93
|
+
- `DB_PROXY_API_KEY`: API key for authenticating with the Cloud Run DB Proxy (required)
|
|
94
|
+
- `LOG_LEVEL`: Logging level (default: INFO)
|
|
95
|
+
|
|
96
|
+
## Usage
|
|
97
|
+
|
|
98
|
+
### For Claude Desktop
|
|
99
|
+
|
|
100
|
+
Add this configuration to your Claude Desktop settings:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"Nuuly Postgres MCP": {
|
|
105
|
+
"command": "nuuly-postgres-mcp",
|
|
106
|
+
"env": {
|
|
107
|
+
"PYTHONUNBUFFERED": "1",
|
|
108
|
+
"CLOUD_RUN_PROXY_URL": "https://postgres-mcp-toolbox-186512416539.us-east4.run.app",
|
|
109
|
+
"DB_PROXY_API_KEY": "YOUR_API_KEY_HERE"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Command Line Usage
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
nuuly-postgres-mcp
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Deployment
|
|
122
|
+
|
|
123
|
+
### As a GCP Cloud Function
|
|
124
|
+
|
|
125
|
+
This MCP server is designed to be deployed as a GCP Cloud Function with the following parameters:
|
|
126
|
+
|
|
127
|
+
- Project: `rental-dev`
|
|
128
|
+
- Region: `us-east4`
|
|
129
|
+
- Serverless VPC Connector: `rental-vpc-connector`
|
|
130
|
+
- Entry point: `main.py`
|
|
131
|
+
|
|
132
|
+
The function connects to CloudSQL PostgreSQL using private IP (10.171.208.52) and uses environment variables for DB credentials.
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
environment_variables:
|
|
136
|
+
CLOUD_RUN_PROXY_URL: "https://postgres-mcp-toolbox-186512416539.us-east4.run.app"
|
|
137
|
+
DB_PROXY_API_KEY: "${DB_PROXY_API_KEY}"
|
|
138
|
+
LOG_LEVEL: "INFO"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Building and Publishing the Package
|
|
142
|
+
|
|
143
|
+
To build and publish the package to PyPI:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
python -m build
|
|
147
|
+
python -m twine upload dist/*
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Available Tools
|
|
151
|
+
|
|
152
|
+
This MCP server provides the following tools to AI assistants:
|
|
153
|
+
|
|
154
|
+
- `run_query`: Execute SQL queries against the database
|
|
155
|
+
- `get_schema`: Get database schema information
|
|
156
|
+
- `list_databases`: List available databases
|
|
157
|
+
|
|
158
|
+
## Troubleshooting
|
|
159
|
+
|
|
160
|
+
### Connection Issues with Claude Desktop
|
|
161
|
+
|
|
162
|
+
If you experience connection issues with Claude Desktop (client transport closing), ensure that:
|
|
163
|
+
|
|
164
|
+
1. The server is running with the correct URL for the Cloud Run DB Proxy
|
|
165
|
+
2. The API key is correctly set and properly escaped in any curl commands (use single quotes)
|
|
166
|
+
3. The server is properly handling heartbeat and shutdown messages
|
|
167
|
+
4. The JSON-RPC initialization response includes `keepAlive: true` and `supportsHeartbeats: true`
|
|
168
|
+
|
|
169
|
+
Example curl command to test the API:
|
|
170
|
+
```bash
|
|
171
|
+
curl -X GET "https://postgres-mcp-toolbox-186512416539.us-east4.run.app/databases" -H 'X-API-KEY: YOUR_API_KEY_HERE'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Common Issues
|
|
175
|
+
|
|
176
|
+
- **Missing API Key**: Ensure the `DB_PROXY_API_KEY` environment variable is set
|
|
177
|
+
- **Permission Errors**: Verify that your API key has the necessary permissions
|
|
178
|
+
- **Timeout Errors**: Check network latency or increase the timeout settings in the code
|
|
179
|
+
- **Heartbeat Issues**: Ensure the MCP server properly implements heartbeat handlers
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/nuuly_postgres_mcp_server/__init__.py
|
|
4
|
+
src/nuuly_postgres_mcp_server/__main__.py
|
|
5
|
+
src/nuuly_postgres_mcp_server/server.py
|
|
6
|
+
src/nuuly_postgres_mcp_server.egg-info/PKG-INFO
|
|
7
|
+
src/nuuly_postgres_mcp_server.egg-info/SOURCES.txt
|
|
8
|
+
src/nuuly_postgres_mcp_server.egg-info/dependency_links.txt
|
|
9
|
+
src/nuuly_postgres_mcp_server.egg-info/entry_points.txt
|
|
10
|
+
src/nuuly_postgres_mcp_server.egg-info/requires.txt
|
|
11
|
+
src/nuuly_postgres_mcp_server.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nuuly_postgres_mcp_server
|