Skip to content

SSE support #57

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

Merged
merged 2 commits into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ RUN apt-get update && apt-get install -y \
COPY docker-entrypoint.sh /app/
RUN chmod +x /app/docker-entrypoint.sh

# Expose the SSE port
EXPOSE 8000

# Run the postgres-mcp server
# Users can pass a database URI or individual connection arguments:
# docker run -it --rm postgres-mcp postgres://user:pass@host:port/dbname
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,30 @@ Many MCP clients have similar configuration files to Claude Desktop, and you can
- If you are using Windsurf, you can navigate to from the `Command Palette` to `Open Windsurf Settings Page` to access the configuration file.
- If you are using Goose run `goose configure`, then select `Add Extension`.

## SSE Transport

Postgres Pro supports the SSE transport, which allows you to connect to the server using a web browser.
To use the SSE transport, you need to start the server with the `--transport sse` flag.

```bash
docker run -p 8000:8000 -e DATABASE_URI=postgresql://username:password@localhost:5432/dbname crystaldba/postgres-mcp --transport sse
```

Then update your MCP client configuration to call the the MCP server.
For example, in Windsurf's `mcp_config.json` you can put:

```json
{
"mcpServers": {
"postgres": {
"type": "sse",
"serverUrl": "http://localhost:8000/sse"
}
}
}
```


## Postgres Extension Installation (Optional)

To enable index tuning and comprehensive performance analysis you need to load the `pg_statements` and `hypopg` extensions on your database.
Expand Down
41 changes: 38 additions & 3 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
replace_localhost() {
local input_str="$1"
local docker_host=""

# Try to determine Docker host address
if ping -c 1 -w 1 host.docker.internal >/dev/null 2>&1; then
docker_host="host.docker.internal"
Expand All @@ -19,15 +19,15 @@ replace_localhost() {
echo "WARNING: Cannot determine Docker host IP. Using original address." >&2
return 1
fi

# Replace localhost with Docker host
if [[ -n "$docker_host" ]]; then
local new_str="${input_str/localhost/$docker_host}"
echo " Remapping: $input_str --> $new_str" >&2
echo "$new_str"
return 0
fi

# No replacement made
echo "$input_str"
return 1
Expand All @@ -53,6 +53,41 @@ for arg in "$@"; do
fi
done

# Check and replace localhost in DATABASE_URI if it exists
if [[ -n "$DATABASE_URI" && "$DATABASE_URI" == *"postgres"*"://"*"localhost"* ]]; then
echo "Found localhost in DATABASE_URI: $DATABASE_URI" >&2
new_uri=$(replace_localhost "$DATABASE_URI")
if [[ $? -eq 0 ]]; then
export DATABASE_URI="$new_uri"
fi
fi

# Check if SSE transport is specified and --sse-host is not already set
has_sse=false
has_sse_host=false

for arg in "${processed_args[@]}"; do
if [[ "$arg" == "--transport" ]]; then
# Check next argument for "sse"
for next_arg in "${processed_args[@]}"; do
if [[ "$next_arg" == "sse" ]]; then
has_sse=true
break
fi
done
elif [[ "$arg" == "--transport=sse" ]]; then
has_sse=true
elif [[ "$arg" == "--sse-host"* ]]; then
has_sse_host=true
fi
done

# Add --sse-host if needed
if [[ "$has_sse" == true ]] && [[ "$has_sse_host" == false ]]; then
echo "SSE transport detected, adding --sse-host=0.0.0.0" >&2
processed_args+=("--sse-host=0.0.0.0")
fi

echo "----------------" >&2
echo "Executing command:" >&2
echo "${processed_args[@]}" >&2
Expand Down
29 changes: 28 additions & 1 deletion src/postgres_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from .sql import obfuscate_password
from .top_queries import TopQueriesCalc

# Initialize FastMCP with default settings
mcp = FastMCP("postgres-mcp")

# Constants
Expand Down Expand Up @@ -501,6 +502,25 @@ async def main():
default=AccessMode.UNRESTRICTED.value,
help="Set SQL access mode: unrestricted (unrestricted) or restricted (read-only with protections)",
)
parser.add_argument(
"--transport",
type=str,
choices=["stdio", "sse"],
default="stdio",
help="Select MCP transport: stdio (default) or sse",
)
parser.add_argument(
"--sse-host",
type=str,
default="localhost",
help="Host to bind SSE server to (default: localhost)",
)
parser.add_argument(
"--sse-port",
type=int,
default=8000,
help="Port for SSE server (default: 8000)",
)

args = parser.parse_args()

Expand Down Expand Up @@ -547,7 +567,14 @@ async def main():
logger.warning("Signal handling not supported on Windows")
pass

await mcp.run_stdio_async()
# Run the server with the selected transport (always async)
if args.transport == "stdio":
await mcp.run_stdio_async()
else:
# Update FastMCP settings for SSE transport
mcp.settings.host = args.sse_host
mcp.settings.port = args.sse_port
await mcp.run_sse_async()


async def shutdown(sig=None):
Expand Down