Operations¶
How to configure, run, and observe the gateway. For a step-by-step first run, see the demo walkthrough.
Runtime configuration¶
All configuration comes from the environment and is validated at startup; the process fails fast with a clear message on a bad value.
| Variable | Required | Default | Rules |
|---|---|---|---|
DATABASE_URL |
yes | — | connection string for the runtime role (gateway_reader) |
MAX_ROWS |
no | 200 |
positive integer, at most 10000 |
STATEMENT_TIMEOUT_MS |
no | 5000 |
positive integer, at most 60000 |
LOG_LEVEL |
no | INFO |
one of DEBUG, INFO, WARNING, ERROR, CRITICAL |
DATABASE_URL is required only at run time — it is never needed to build the
container image. MAX_ROWS is the ceiling on rows any tool returns;
STATEMENT_TIMEOUT_MS bounds each query at the database session.
Local Docker usage¶
A local PostgreSQL often already listens on 5432. The project can run on
5433 instead — set both the mapped port and the connection string in .env:
POSTGRES_PORT=5433
DATABASE_URL=postgresql://gateway_reader:gateway_reader_dev_password@localhost:5433/agentdata
docker-compose.yml maps ${POSTGRES_PORT}:5432, so the container still listens
on 5432 internally while the host uses 5433. The loader and server read the port
from DATABASE_URL/POSTGRES_PORT, so nothing else changes.
Container usage¶
The image runs the stdio MCP server, so it is meant to be launched by an MCP host and given its configuration through environment variables:
docker build -t mcp-data-gateway:local .
docker run -i --rm -e DATABASE_URL=... mcp-data-gateway:local
- The image runs as the non-root
gatewayuser. - No secrets are baked in —
.envand.venvare excluded from the build context, and all configuration is read from the environment at run time. - The build itself needs no
DATABASE_URLand no database.
Audit logs¶
Every tool call emits exactly one structured, greppable log line at INFO,
for example:
tool="get_passenger" outcome="success" result_count=1 args={"passenger_id": 1}
tool="survival_by" outcome="error" error_type="InvalidGroupByError" args={"group_by": "name"}
Each line carries the tool name, the sanitized arguments, the outcome
(success/error), and either a result_count or an error_type. Raw SQL,
result rows, DATABASE_URL, and any credential-shaped argument are never
logged. Verbosity follows LOG_LEVEL.
Validation commands¶
| Command | What it checks |
|---|---|
make lint |
ruff lint + format check |
make test |
fast, database-free unit tests |
make audit |
bandit static analysis + pip-audit dependency scan |
make docs |
MkDocs strict build (fails on broken links or nav gaps) |
make smoke |
end-to-end checks against a running stack |
MCP_GATEWAY_RUN_INTEGRATION=1 make test |
also runs the opt-in live-DB tests |
make smoke prints a short setup hint (not a stack trace) when the database or
configuration is missing. See validation for what each check
proves, and the security model for the controls behind
them.