mcp server
prest
Instant REST and MCP APIs for PostgreSQL and MySQL, on existing or new databases. CRUD, custom SQL routes, auth and ACL without writing a backend. Low-code, ⚡ realtime, high-performance. Native MySQL
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 14, 2026pRESTd
pREST (PostgreSQL REST) is a production-ready API that delivers instant REST and Model Context Protocol (MCP) APIs on your existing or new SQL database — CRUD, custom SQL routes, auth, ACL, and a read-only MCP endpoint — without hand-writing a backend.
PostgreSQL 9.5+ is the default engine. MySQL 8.0.19+ is a separate native dialect from v2.5.0. Read-only MCP on MySQL is v2.5.1. Set engine = "mysql" to select it. An unset engine stays PostgreSQL. MariaDB, TiDB, and Aurora MySQL stay on the roadmap.
PostgreSQL 9.5+ (default). MySQL 8.0.19+ with
engine = "mysql".
Contributor License Agreement —
Documentation
Full documentation lives at docs.prestd.com.
| Topic | Link |
|---|---|
| Get pREST (Docker, Homebrew, Go) | Get pREST |
| Configuration | Configuring pREST |
| API reference | API Reference |
| MCP over HTTP | MCP over HTTP |
| Multi-database | Multi-database |
| Databases & roadmap | Databases · Roadmap |
| MySQL 8 | MySQL · v2.5.1 |
| AI clients (Cursor, Claude, …) | AI and MCP |
Quick start
Install and run options (Docker, Homebrew, or Go) are documented in Get pREST. Point pREST at Postgres (PREST_PG_URL or pg.* / DATABASE_URL), then call:
GET /{database}/{schema}/{table}
MySQL 8.0.19+ uses the same path. {schema} is the MySQL database. Set the engine and a mysql:// URL. The port defaults to 3306 when unset. PREST_DB_* is read first, then the legacy PREST_PG_* names:
engine = "mysql"
[pg]
url = "mysql://user:pass@localhost:3306/mydb"
export PREST_ENGINE=mysql
export PREST_DB_URL='mysql://user:pass@localhost:3306/mydb'
A missing user or database fails startup. Postgres defaults (user postgres, database prest, port 5432) are not copied onto a MySQL engine. Guide: MySQL.
See Configuring pREST for auth, ACL, custom queries, and MCP.
Passing values to query scripts
Values a _QUERIES script template interpolates become part of the SQL text, so
pREST screens them: anything carrying quotes, --, ::, or — for multi-word
values — a SQL keyword is refused, and a refused value that is interpolated fails
the request with 400.
Bind free-form values instead, and the screen does not apply at all. On PostgreSQL, a bound value travels to the database out of band, where it can never be parsed as SQL:
-- interpolated: screened, and rejected for values like 'compra do mes'
SELECT * FROM articles WHERE slug = '{{.slug}}'
-- bound: the caller's value arrives verbatim, whatever it contains
SELECT * FROM articles WHERE slug = {{sqlVal "slug"}}
| Helper | Use for | Renders |
|---|---|---|
{{sqlVal "key"}} | a single value | $1 |
{{sqlList "key"}} | a repeated query parameter (?tag=a&tag=b) | ($1,$2) |
{{ident "key"}} | a table/column name, which cannot be bound | "public"."users" |
That table is the PostgreSQL rendering. On MySQL, {{sqlVal "key"}} renders ?, {{sqlList "key"}} renders (?,?), and {{ident "key"}} renders backtick names such as `shop`.`users`. With the default prepare = false, MySQL interpolates those ? placeholders into the statement. prepare = true uses binary prepared statements, and then the values travel out of band.
sqlVal and sqlList also reach headers as {{sqlVal "header.X-Application"}}.
Credential headers (Authorization, Cookie, …) are always withheld.
Prefer binding for anything user-supplied — search phrases especially, since a
phrase containing a common word such as do, as or or is exactly what the
interpolation screen refuses.
1-Click Deploy
Heroku
Deploy to Heroku and get a realtime RESTful API backed by Heroku Postgres:
More: Deploy in Heroku.
Contributing
Contributions are welcome. See the Development Guide and Contributing to pREST. Please sign the CLA.
Questions? GitHub Discussions or Discord.
Testing
Run unit tests locally:
make test-unit
Run integration suites inside Docker (no local database required):
# Postgres (full stack: default, auth, multicluster, queries) — also: make test-integration
make test-integration-postgres
# TimescaleDB (Timescale-specific E2E only)
make test-integration-timescaledb
# MySQL 8 (./integration/mysql/... only). Default image mysql:8.4.
# Override: MYSQL_IMAGE=mysql:8.0 make test-integration-mysql
make test-integration-mysql
Or with Docker Compose:
docker compose -f integration/postgres/docker-compose.yml up -d --wait \
postgres postgres-b db-init prestd prestd-multicluster prestd-auth prestd-queries
docker compose -f integration/postgres/docker-compose.yml run --rm --no-deps tests
docker compose -f integration/postgres/docker-compose.yml down -v --remove-orphans
Postgres compose runs ./integration/suites/... and ./integration/postgres/....
TimescaleDB compose runs ./integration/timescaledb/... only (see .github/workflows/test-integration-timescaledb.yml).
MySQL compose runs ./integration/mysql/... only (see .github/workflows/test-integration-mysql.yml). CI runs mysql:8.0, mysql:8.4, and mysql:latest.
Network tests require PREST_TEST_URL (and flavor-specific URLs for the Postgres job); outside Compose those tests skip when the URLs are unset.
Example: Docker Build
Build the Docker image locally for development (compiles from source):
docker build -t prest/prest:latest .
For release builds, GoReleaser uses the same Dockerfile / Dockerfile.noplugins with a pre-built prestd binary. Local source builds can pass version metadata via build arguments:
docker build \
--build-arg VERSION=v1.0.0 \
--build-arg COMMIT=hash \
--build-arg DATE=2026-02-11 \
-t prest/prest:latest .