# Default stdio mode requires no additional flags

> Understand stdio vs streamable-http transport modes in MCP-PostgreSQL-Ops. Stdio uses local I/O no flags, while streamable-http offers TCP/IP with auth. Learn the differences.

- Repository: [JungJungIn/mcp-postgresql-ops](https://github.com/call518/mcp-postgresql-ops)
- Tags: internals
- Published: 2026-02-26

---

#_stdio vs streamable-http Transport Modes in MCP-PostgreSQL-Ops: 4 Key Differences Explained_

**The primary difference between `stdio` and `streamable-http` transport modes in MCP-PostgreSQL-Ops is that `stdio` uses local standard input/output streams for communication with no authentication, while `streamable-http` runs an HTTP server accessible over TCP/IP with optional Bearer token authentication.**

MCP-PostgreSQL-Ops is a Model Context Protocol (MCP) server implementation for PostgreSQL operations that supports two distinct transport modes depending on your deployment architecture. Understanding the architectural and security differences between these modes is essential for choosing the right configuration for local development versus production network deployments.

## Communication Architecture and Protocol Differences

The transport mode determines how MCP clients communicate with the PostgreSQL operations server.

### Standard I/O (stdio) Transport

In `stdio` mode, the server communicates exclusively through the process's standard input and output streams. This mode is the default when no transport type is specified, implemented in [[`src/mcp_postgresql_ops/mcp_main.py`](https://github.com/call518/mcp-postgresql-ops/blob/main/src/mcp_postgresql_ops/mcp_main.py)](https://github.com/call518/mcp-postgresql-ops/blob/main/src/mcp_postgresql_ops/mcp_main.py) at **line 3738** where the server executes `mcp.run(transport='stdio')`. This approach creates a bidirectional JSON-RPC communication channel over stdin/stdout, making it ideal for local CLI integrations and subprocess-based workflows where the client and server run on the same machine.

### Streamable HTTP Transport

The `streamable-http` mode starts an HTTP server that accepts JSON-RPC requests over TCP/IP. According to the source code at **line 3734** in [`mcp_main.py`](https://github.com/call518/mcp-postgresql-ops/blob/main/mcp_main.py), this mode executes `mcp.run(transport='streamable-http', host=host, port=port)`, binding to a configurable network interface. Unlike stdio, this transport supports remote clients connecting over the network, with the default binding address being `127.0.0.1:8000` unless overridden by command-line arguments or environment variables.

## Configuration Requirements and Validation

Each transport mode enforces different configuration constraints during startup validation.

### Network Binding and Port Configuration

The `stdio` mode requires no network configuration and ignores host or port settings entirely. In contrast, `streamable-http` mode performs strict validation of network parameters. The `validate_config()` function in [`mcp_main.py`](https://github.com/call518/mcp-postgresql-ops/blob/main/mcp_main.py) (lines **3595-3604**) explicitly checks that the host is non-empty and the port falls within the valid range of 1-65535 before attempting to bind the HTTP server.

### Authentication and Security Model

**Stdio mode operates without any authentication layer**, as it assumes a trusted local execution environment. This makes it unsuitable for exposed network interfaces but eliminates configuration overhead for local development.

**Streamable-http mode offers optional Bearer token authentication** controlled by the `--auth-enable` flag or `REMOTE_AUTH_ENABLE` environment variable. When authentication is enabled, the server validates the presence of a secret key (lines **3699-3705** in [`mcp_main.py`](https://github.com/call518/mcp-postgresql-ops/blob/main/mcp_main.py)), logging a fatal error if authentication is enabled without a corresponding `REMOTE_SECRET_KEY` or `--secret-key` argument. If authentication remains disabled, the server logs a security warning at startup (lines **3707-3710**), alerting operators that the HTTP endpoint is exposed without credential protection.

## Implementation Details in Source Code

The transport selection logic resides in the `mcp_postgresql_ops` package with clear branching based on the `transport_type` variable.

### Transport Selection Logic

After parsing CLI arguments, the application checks the `transport_type` value to determine execution path:

- For `stdio`: Calls `mcp.run(transport='stdio')` with no additional parameters
- For `streamable-http`: Extracts host/port from `FASTMCP_HOST` and `FASTMCP_PORT` environment variables or CLI arguments, then invokes the HTTP transport with authentication provider if enabled

### Authentication Provider Integration

When `streamable-http` runs with `--auth-enable`, the code at lines **3713-3715** installs a static token authentication provider using `mcp.auth = _build_static_token_auth(secret_key)`. This middleware intercepts incoming HTTP requests and validates the `Authorization: Bearer <token>` header before processing PostgreSQL operations.

## Practical Usage Examples

### Running Local Development with stdio

For local debugging or integration with CLI tools that spawn the server as a subprocess:

```bash

# Default stdio mode requires no additional flags

python -m mcp_postgresql_ops.mcp_main

# Or explicitly specify stdio transport

mcp-postgresql-ops --type stdio

```

### Deploying Network-Accessible HTTP Server

For remote access or web UI integration with authentication:

```bash

# Start HTTP server on all interfaces with authentication

mcp-postgresql-ops \
  --type streamable-http \
  --host 0.0.0.0 \
  --port 8080 \
  --auth-enable \
  --secret-key "secure-token-12345"

```

### Python Client Connection Examples

**Connecting to stdio mode (local subprocess):**

```python
from mcp_postgresql_ops.mcp_main import main
import subprocess

# Spawn server process and communicate via stdin/stdout

process = subprocess.Popen(
    ["python", "-m", "mcp_postgresql_ops.mcp_main", "--type", "stdio"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True
)

# Send JSON-RPC request

process.stdin.write('{"jsonrpc": "2.0", "method": "list_databases", "id": 1}\n')
process.stdin.flush()
response = process.stdout.readline()
print(response)

```

**Connecting to streamable-http mode with authentication:**

```python
import requests
import json

url = "http://localhost:8080/"
headers = {
    "Authorization": "Bearer secure-token-12345",
    "Content-Type": "application/json"
}

payload = {
    "jsonrpc": "2.0",
    "method": "execute_query",
    "params": {"query": "SELECT version();"},
    "id": 1
}

response = requests.post(url, headers=headers, json=payload)
result = response.json()
print(result["result"])

```

## Summary

- **stdio transport** uses local standard input/output streams with **no network exposure** and **no authentication requirements**, ideal for local CLI integrations and development workflows.
- **streamable-http transport** creates an **HTTP server accessible over TCP/IP**, requires **explicit host/port configuration**, and supports **optional Bearer token authentication** for secure remote access.
- The `validate_config()` function enforces **port range validation (1-65535)** and **host presence checks** exclusively for HTTP mode.
- Authentication in HTTP mode is implemented through `_build_static_token_auth()` when `--auth-enable` is specified, with mandatory secret key validation.
- **Stdio mode** is the default and requires zero configuration, while **streamable-http** must be explicitly selected with the `--type streamable-http` argument.

## Frequently Asked Questions

### When should I use stdio mode versus streamable-http mode?

Use **stdio mode** for local development, CLI tooling, or when running the MCP server as a subprocess where the parent process manages communication through pipes. Use **streamable-http mode** when you need to expose PostgreSQL operations to remote clients, web dashboards, or microservices architectures that communicate over HTTP.

### How do I secure the streamable-http transport in production?

Enable Bearer token authentication by setting `--auth-enable` and providing a strong secret key via `--secret-key` or the `REMOTE_SECRET_KEY` environment variable. The server validates this token against the `Authorization` header on every request. Never run streamable-http without authentication on public networks, as the server logs an explicit warning when starting without credential protection.

### Can stdio mode handle concurrent connections from multiple clients?

No. The **stdio transport** supports only a single client communicating through the process's standard streams. For concurrent client support or multi-tenant scenarios, you must use **streamable-http mode**, which leverages the underlying HTTP server's connection handling capabilities to manage multiple simultaneous requests.

### What environment variables configure each transport mode?

For **stdio mode**, only general MCP variables like `MCP_LOG_LEVEL` and PostgreSQL connection strings are relevant. For **streamable-http**, you must set `FASTMCP_HOST` (default: 127.0.0.1), `FASTMCP_PORT` (default: 8000), and optionally `REMOTE_AUTH_ENABLE` and `REMOTE_SECRET_KEY` when using authentication.