# pREST

pREST — open-source instant REST (Representational State Transfer) and Model Context Protocol (MCP) APIs for SQL databases. PostgreSQL-first, now multi-database.

**pREST** is open-source software that gives you instant [REST (Representational State Transfer)](/api-reference) and [Model Context Protocol (MCP)](/get-started/mcp-over-http) APIs for SQL databases. Point it at a database and get production-ready HTTP APIs — CRUD, custom SQL routes, auth, ACL, and (from v2.1.0) a read-only MCP endpoint — without hand-writing a backend.

**PostgreSQL is the first native adapter.** Postgres-compatible engines can be certified on that adapter. MySQL, SQLite, and SQL Server are on the [roadmap](/databases/roadmap).

*Last updated: August 11, 2026*

***

## What you get

| Capability     | Detail                                                                                                                                                                        |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Instant REST   | Auto CRUD from schema: `GET/POST/PUT/PATCH/DELETE /{db}/{schema}/{table}`                                                                                                     |
| MCP over HTTP  | Read-only `/_mcp` for AI agents and IDEs ([guide](/get-started/mcp-over-http))                                                                                                |
| Auth & ACL     | JWT/auth stack and table-level permissions; HMAC key minimums enforced (v2.4.2, [#1017](https://github.com/prest/prest/pull/1017))                                            |
| Multi-database | Alias registry across clusters ([guide](/get-started/multi-database)); Postgres/Timescale multi-adapter auto-detect (v2.3.0, [#999](https://github.com/prest/prest/pull/999)) |
| Vector search  | pgvector KNN ordering / distance filtering via `_korder` and `:vecdist` (v2.4.0, [#1011](https://github.com/prest/prest/pull/1011))                                           |
| Observability  | Opt-in OpenTelemetry traces/metrics/logs (v2.4.0, [#1003](https://github.com/prest/prest/pull/1003))                                                                          |
| Custom SQL     | Templated `/_QUERIES` scripts with bound values via `sqlVal` ([guide](/api-reference/custom-queries#binding-values-sqlval-sqllist-ident-v242))                                |
| Plugins        | Middleware and endpoint extensions                                                                                                                                            |

***

## Which databases work today?

| Status                      | Engines                                                                                     |
| --------------------------- | ------------------------------------------------------------------------------------------- |
| **Native**                  | [PostgreSQL](/databases/postgresql)                                                         |
| **Hosted PostgreSQL**       | [Aurora PostgreSQL](/databases/aurora-postgresql), Neon, Supabase, AlloyDB                  |
| **Certified / certifying**  | [CockroachDB](/databases/cockroachdb), [YugabyteDB](/databases/yugabytedb)                  |
| **Compatible with caveats** | [TimescaleDB](/databases/timescaledb), [Amazon Redshift](/databases/amazon-redshift)        |
| **Roadmap**                 | [MySQL](/databases/mysql), [SQLite](/databases/sqlite), [SQL Server](/databases/sql-server) |

Full matrix and labels: [Databases](/databases). Phases: [Database roadmap](/databases/roadmap).

***

## Latest release

[**v2.4.2**](/releases/v2.4.2) — RFC 7518 minimum key sizes for HMAC `jwt.key`, bound values for custom query scripts with credential headers withheld from templates, and SQL removed from logs. Includes the MCP `[expose]` enforcement from [v2.4.1](/releases/v2.4.1) and the observability / pgvector work from [v2.4.0](/releases/v2.4.0).

* Docker: `prest/prest:v2.4.2`
* Go: `go install github.com/prest/prest/v2/cmd/prestd@v2.4.2`
* Studio: [pREST Studio](/get-started/prest-studio)

{% hint style="warning" %}
Upgrading to v2.4.2? Check that `jwt.key` is at least 32 bytes (HS256) first — a shorter key is discarded at startup and auth disables itself. See [v2.4.2 — Upgrading](/releases/v2.4.2#upgrading-from-v241).
{% endhint %}

See [Releases](/releases) and [Upgrading to v2](/get-started/upgrading-to-v2).

***

## Get started

1. [Get pREST](/get-prest) — Docker, Homebrew, or Go
2. [Configuring pREST](/get-started/configuring-prest)
3. [API Reference](/api-reference)
4. Optional: [MCP over HTTP](/get-started/mcp-over-http) for AI clients
5. Optional: [AI and MCP](/ai) — Cursor, Claude Desktop, stdio adapter

***

## FAQ

### What is pREST?

pREST is open-source software for instant REST and MCP APIs on SQL databases. It turns HTTP requests into safe, parameterized database operations using your existing schema.

### Is pREST only for PostgreSQL?

PostgreSQL is the **native** adapter today. Engines that speak the PostgreSQL wire protocol (for example YugabyteDB, CockroachDB, Aurora PostgreSQL) can use that adapter with documented support levels. Other SQL families are planned — see the [roadmap](/databases/roadmap).

### Does pREST provide a REST API?

**REST (Representational State Transfer)** is an architectural style for building APIs over HTTP. pREST exposes your SQL tables as REST resources at `/{database}/{schema}/{table}`, mapping standard verbs to CRUD on the same server as MCP.

Yes. Full reference: [API Reference](/api-reference).

### Does pREST support MCP for AI agents?

**MCP (Model Context Protocol)** is an open standard for connecting AI apps and agents to tools and data. pREST exposes a read-only MCP endpoint at `/_mcp` so clients can discover schemas and query tables through the same server as the REST API.

Yes, from **v2.1.0**. Stdio clients use the [pREST MCP Adapter](/ai/install-prest-mcp) (`brew install prest/tap/prest-mcp`). Full guide: [MCP over HTTP](/get-started/mcp-over-http) · [AI and MCP](/ai).

### How does pREST work with AI?

**Artificial intelligence (AI)** here means applications and agents that use models to reason and act on tools and data. pREST connects them to your SQL catalog through read-only [MCP](/get-started/mcp-over-http) at `/_mcp` and the same auth/ACL as the REST API.

Client setup (adapters, IDE config): [AI and MCP](/ai) · [docs.prestd.com/ai](https://docs.prestd.com/ai).

### How do I start quickly?

Use Docker or Homebrew from [Get pREST](/get-prest), set `PREST_PG_URL` (or `pg.*` / `DATABASE_URL`), and call `http://localhost:3000/{database}/{schema}/{table}`.

***

## Related documentation

* [Key features](/readme/prestd-key-features)
* [Acronyms](/readme/acronyms) ([REST](/readme/acronyms#rest), [MCP](/readme/acronyms#mcp), [AI](/readme/acronyms#ai))
* [Databases](/databases)
* [Database roadmap](/databases/roadmap)
* [AI and MCP](/ai)
* [Who uses pREST](/readme/who-uses-prest)
* [Contributing](/readme/contributing-to-prestd)

Questions? [GitHub Discussions](https://github.com/prest/prest/discussions) or [Discord](https://discord.gg/aB8mwvVEhC).


# Who uses pREST

Products and companies that use pREST

Company and/or project uses pREST? Describe your use case [here](https://github.com/prest/prest/issues/new?title=Case%20Studies:%20).

These are just a few companies and projects that are using (trusting) pREST.

### Companies

| Company        | Site                                    |
| -------------- | --------------------------------------- |
| Nuveo          | [nuveo.ai](https://nuveo.ai/)           |
| Filmow         | [filmow.com](https://filmow.com/)       |
| Levpay         | [levpay.com](https://levpay.com/)       |
| uMov.me        | [umov.me](https://www.umov.me/)         |
| Guiame         | [guiame.com.br](https://guiame.com.br/) |
| Material Agora | gomago.co                               |


# pREST Key Features

Key features of pREST — instant REST and MCP APIs for SQL databases, PostgreSQL-first native adapter, auth, ACL, multi-database, and plugins.

**pREST** is a open-source software that generates HTTP APIs from your schema so teams ship data products without writing boilerplate CRUD services — instant REST and MCP APIs for SQL databases. **PostgreSQL is the first native adapter**; Postgres-compatible engines are documented under [Databases](/databases).

*Last updated: August 11, 2026*

***

## Feature summary

| Feature                    | Why it matters                                                                                                                           |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Automatic REST from schema | Tables become endpoints without hand-written controllers                                                                                 |
| Full CRUD over HTTP        | Familiar verbs map to SQL operations on the active dialect                                                                               |
| MCP over HTTP              | AI agents discover and read data via `/_mcp` (v2.1.0+)                                                                                   |
| pREST Studio               | Embedded UI at `/_studio/` — catalog, REST, and MCP explorers (v2.2.0+)                                                                  |
| Auth & permissions         | JWT/auth stack and table-level ACL; HMAC key minimums enforced (v2.4.2, [#1017](https://github.com/prest/prest/pull/1017))               |
| Multi-database             | Route by alias across clusters; Timescale adapter auto-detect (v2.3.0, [#999](https://github.com/prest/prest/pull/999))                  |
| Vector search              | pgvector KNN ordering / distance filtering (v2.4.0, [#1011](https://github.com/prest/prest/pull/1011))                                   |
| Observability              | Opt-in OpenTelemetry traces/metrics/logs (v2.4.0, [#1003](https://github.com/prest/prest/pull/1003))                                     |
| Custom SQL routes          | Templated `/_QUERIES` for curated operations, with bound values via `sqlVal` (v2.4.2, [#1023](https://github.com/prest/prest/pull/1023)) |
| Plugins                    | Extend with middleware and custom endpoints                                                                                              |
| Postgres-family reach      | Native PG plus certified/compatible wire engines                                                                                         |

***

## Automatic API generation

**REST (Representational State Transfer)** is an architectural style for building APIs over HTTP. pREST exposes your SQL tables as REST resources at `/{database}/{schema}/{table}`, mapping standard verbs to CRUD on the same server as MCP.

pREST introspects the connected database catalog and exposes those resources automatically. You model data in SQL; pREST serves the API. See [API Reference](/api-reference).

***

## CRUD over HTTP

Standard HTTP methods map to insert/select/update/delete on the **current native dialect (PostgreSQL)**. Filtering, ordering, and pagination use query parameters — [Parameters](/api-reference/parameters).

***

## MCP for AI agents

**Artificial intelligence (AI)** here means applications and agents that use models to reason and act on tools and data. **MCP (Model Context Protocol)** is an open standard for connecting AI apps and agents to tools and data. pREST exposes a read-only MCP endpoint at `/_mcp` so clients can discover schemas and query tables through the same server as the REST API.

From v2.1.0, the same process exposes read-only MCP tools — list schemas/tables, describe columns, select rows (max 100). Guide: [MCP over HTTP](/get-started/mcp-over-http).

***

## pREST Studio

From v2.2.0, open `/_studio/` for an embedded catalog / REST / MCP explorer UI (read-only). Guide: [pREST Studio](/get-started/prest-studio).

***

## Observability and vector search

From v2.4.0, opt-in OpenTelemetry instrumentation (traces, metrics, and a `slog`→OTel log bridge) ships behind `otel.enabled` — see [Configuring pREST — Observability](/get-started/configuring-prest#observability-opentelemetry-v240). The same release adds pgvector-backed nearest-neighbor ordering (`_korder`) and distance filtering (`:vecdist`) — [Parameters](/api-reference/parameters#pgvector-knn-ordering-and-distance-filtering-v240).

***

## Authentication and authorization

Protect routes with JWT/auth configuration and restrict tables via `access.tables` / per-user rules — [Auth](/api-reference/auth), [Permissions](/get-started/permissions).

From v2.4.2, HMAC `jwt.key` must meet the RFC 7518 minimum for its algorithm (32 bytes for HS256, 48 for HS384, 64 for HS512), and `jwt.algo` is enforced as the sole permitted signature algorithm — [Auth — HMAC key requirements](/api-reference/auth#hmac-key-requirements-v242). From v2.4.1, the `[expose]` listing settings also apply to the MCP endpoint, so `/_mcp` can no longer enumerate a catalog the REST routes hide — [Configuring pREST — Expose Data](/get-started/configuring-prest#expose-data).

***

## Multi-database and SQL platform direction

* **Today:** PostgreSQL native adapter; Timescale wire + E2E; hosted PG and PG-wire engines per [Databases](/databases). Multi-adapter routing (Postgres + Timescale) shipped in v2.3.0 ([#999](https://github.com/prest/prest/pull/999)) — see [Multi-database](/get-started/multi-database).
* **Next:** MySQL family, SQLite, SQL Server — [roadmap](/databases/roadmap).

pREST is positioned as a **multi-database** API platform; adapters beyond PostgreSQL are explicit roadmap work, not silent claims.

***

## Custom queries and plugins

* [Custom queries](/api-reference/custom-queries)
* [Middleware plugins](/plugins/middleware-plugin)
* [Endpoint plugins](/plugins/endpoint-plugin)

***

## FAQ

### Is pREST only a PostgreSQL tool?

It started with PostgreSQL and that remains the **native** adapter. The product direction is instant REST and MCP APIs for SQL databases — see the [roadmap](/databases/roadmap).

### Can I use pREST with YugabyteDB or CockroachDB?

Yes, via the PostgreSQL wire path with documented labels and matrices — [YugabyteDB](/databases/yugabytedb), [CockroachDB](/databases/cockroachdb).

***

## Related

* [Homepage](/)
* [Acronyms](/readme/acronyms) ([REST](/readme/acronyms#rest), [MCP](/readme/acronyms#mcp), [AI](/readme/acronyms#ai))
* [Databases](/databases)
* [Get Started](/get-started)
* [Who uses pREST](/readme/who-uses-prest)


# Acronyms

Acronyms used in pREST docs — REST, MCP, AI, SQL, HTTP, JWT, ACL, and related terms with short definitions and links to deeper guides.

Short definitions for acronyms used across the pREST docs. Use bare terms in most pages and [link here from Related](#how-to-use-this-page). Expand inline only when a page is meant for SEO or discovery (homepage, FAQ, “What is …?” hubs).

**pREST** is the product name — not an expandable acronym on this page.

## How to use this page

* Prefer linking this glossary (and specific anchors) from a page’s **Related** section when those terms appear in prose.
* Deep guides for REST, MCP, and AI stay on their canonical pages; entries below stay short.

***

## ACL

**Access Control List.** In pREST, table- and user-level rules that restrict which resources clients can reach — see [Permissions](/get-started/permissions).

## AI

**Artificial intelligence.** In these docs, applications and agents that use models to reason and act on tools and data. pREST is not an LLM; it exposes SQL to AI clients via MCP and REST. Deep guide: [MCP over HTTP](/get-started/mcp-over-http). Client setup: [docs.prestd.com/ai](https://docs.prestd.com/ai).

## API

**Application Programming Interface.** A contract for calling a system over a network or library boundary. pREST serves HTTP APIs (REST and MCP) generated from your database schema.

## CRUD

**Create, Read, Update, Delete.** The four basic data operations. pREST maps them to HTTP verbs on `/{database}/{schema}/{table}` — see [API Reference](/api-reference).

## DDL

**Data Definition Language.** SQL statements that change schema (for example `CREATE TABLE`). pREST’s MCP surface in v2.1.0 does not run DDL; use your database tooling or allowed REST/custom-query paths as configured.

## HTTP

**Hypertext Transfer Protocol.** The application protocol pREST uses for REST and MCP over HTTP endpoints.

## IDE

**Integrated Development Environment.** Editors and tools (for example Cursor) that can connect as MCP or HTTP clients to pREST.

## JSON

**JavaScript Object Notation.** The usual request and response body format for pREST REST routes and MCP JSON-RPC payloads.

## JWT

**JSON Web Token.** A signed token format used when pREST auth is configured — see [Auth](/api-reference/auth).

## MCP

**Model Context Protocol.** An open standard for connecting AI apps and agents to tools and data. pREST exposes read-only MCP at `/_mcp`. Spec: [modelcontextprotocol.io](https://modelcontextprotocol.io/). Deep guide: [MCP over HTTP](/get-started/mcp-over-http).

## REST

**Representational State Transfer.** An architectural style for APIs over HTTP. pREST exposes SQL tables as REST resources at `/{database}/{schema}/{table}`. Glossary: [MDN REST](https://developer.mozilla.org/en-US/docs/Glossary/REST). Deep guide: [API Reference](/api-reference).

## SQL

**Structured Query Language.** The language of relational databases. pREST turns HTTP and MCP calls into parameterized SQL against the connected engine (PostgreSQL native today).

## Related

* [Homepage](/)
* [Key features](/readme/prestd-key-features)
* [API Reference](/api-reference)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Databases](/databases)


# Contributing to pREST

contributions are welcome to help improve and grow the project

Thank you for your interest in contributing to pREST! Before you get started, please take a moment to review the following guidelines.

### Code of Conduct

Please review our [Code of Conduct](/readme/code-of-conduct) to understand the expected behaviour within the pREST community.

### How to Contribute

1. Fork the repository to your GitHub account.
2. Clone the forked repository to your local machine:

   ```bash
   git clone https://github.com/<your-username>/prest.git
   ```
3. Create a new branch for your changes:

   ```bash
   git checkout -b feature-branch
   ```
4. Make your changes and commit them:

   ```bash
   git commit -m "Describe of your changes"
   ```
5. Push the changes to your forked repository:

   ```bash
   git push origin feature-branch
   ```
6. Open a pull request on the official pREST repository. Provide a clear title, a description of your changes, and any relevant information.

### Development Setup

If you're planning to make significant contributions or work on new features, follow these steps to set up your development environment:

1. Install Go (if not already installed): [Download Go](https://golang.org/dl/)
2. Install project dependencies:

   ```bash
   go get -u ./...
   ```
3. Run tests to make sure everything is set up correctly:

   ```bash
   go test ./...
   ```
4. Make your changes, update tests if necessary, and ensure that all tests pass before submitting a pull request.

### Coding Guidelines

* Follow the [Effective Go](https://golang.org/doc/effective_go.html) guidelines.
* Adhere to the existing coding style and conventions used in the project.
* Write clear and concise commit messages.
* Document your code using comments where necessary.

### Reporting Issues

If you encounter any issues or have suggestions for improvement, please open an issue on the [GitHub issue tracker](https://github.com/prest/prest/issues).

### Feature Requests

If you have a feature you'd like to see added to pREST, open an issue on the [GitHub issue tracker](https://github.com/prest/prest/issues) and use the "Feature Request" label.

### Review Process

Maintainers will review pull requests, and feedback will be provided. Once approved, changes will be merged into the main branch.

Thank you for contributing to pREST! Your help is greatly appreciated.


# pREST in the Media

Press, tutorials, and community coverage of open-source pREST — videos, guides, comparisons, and integrations.

Press, tutorials, and community coverage of open-source pREST. Use these links for talks, onboarding, or sharing the project.

## Videos

Overview of pREST and generating an API from a database with no hand-written controllers.

* [Mirror your database on an API with 0 code lines! Use pRESTd!](https://www.youtube.com/watch?v=GHZRPY-4W0k\&t=1s\&ab_channel=vmesel-TecheAssuntoscorrelatos)

## Guides and tutorials

* [Accessing Postgres via REST using pREST](https://eltonminetto.dev/en/post/2021-08-31-prest/)
* [pREST is a Libre Low-Code API Engine](https://medevel.com/prest/)
* [Criando uma API REST em minutos com pREST](https://aprendagolang.com.br/2021/10/04/criando-uma-api-rest-em-minutos-com-prest/)
* [Postgres as Rest](https://dev.to/alisonjr/postgres-as-rest-p6n)
* [Constant work to onboarding new members into engineering team](https://dev.to/prestd/constant-work-to-onboarding-new-members-into-engineering-team-18k0)

## Community and launches

* [Y Combinator: Alternative: pREST: Golang based RESTful API](https://news.ycombinator.com/item?id=13960629)
* [Y Combinator: pREST – A fully RESTful API from any existing PostgreSQL database written in Go](https://news.ycombinator.com/item?id=14620990)
* [Product Hunt: pREST](https://www.producthunt.com/products/prest)
* [Postman and database](https://community.postman.com/t/postman-and-database/706)

## Comparisons

* [PostgREST vs pREST | What is the difference?](https://stackshare.io/stackups/postgrest-vs-prest)
* [pREST vs InfluxDB](https://www.libhunt.com/compare-prest-vs-influxdb)

## Other languages

* [Introduction to database Rest API project pREST (Chinese)](https://www.cnblogs.com/harrychinese/p/prest.html)
* [golang microservice development and governance (Chinese)](https://blog.csdn.net/wuzhc/article/details/113183520)
* [Build RESTful API services using pREST (Chinese)](https://yangcongchufang.com/postgresql/prest-save-my-world.html)

## Databases and tooling

* [pREST on YugabyteDB](https://dev.to/yugabyte/prest-on-yugabytedb-f6a)
* [DEV.co: pREST — Auto-Generate REST APIs from PostgreSQL](https://dev.co/databases/open-source/prest)
* [dltHub: pREST Python API Docs](https://dlthub.com/context/source/prest)

## Contribute

Have a talk, post, or write-up about pREST?

1. Fork this docs repository.
2. Add the link under the matching section above.
3. Open a pull request.

## Related

* [Homepage](/)
* [Who uses pREST](/readme/who-uses-prest)
* [Key features](/readme/prestd-key-features)
* [Acronyms](/readme/acronyms)


# Code of Conduct

We are committed to creating a friendly and respectful environment for all participants

### Introduction

This Code of Conduct outlines our expectations for all those who participate in our community and the consequences of unacceptable behaviour.

### Our Pledge

In the interest of fostering an open and welcoming environment, we, as contributors and maintainers, pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

### Expected Behaviour

We expect all contributors and community members to:

* Be respectful and considerate of others.
* Use inclusive language and avoid offensive or derogatory comments.
* Be collaborative and constructive in discussions.
* Show empathy towards other community members.

### Unacceptable Behavior

Unacceptable behaviours include, but are not limited to:

* Harassment or discrimination based on personal characteristics.
* Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, race, religion, or nationality.
* Trolling, insulting/derogatory comments, and personal or political attacks.
* Public or private harassment.
* Publishing others' private information, such as a physical or electronic address, without explicit permission.
* Other conduct which could reasonably be considered inappropriate in a professional setting.

### Consequences of Unacceptable Behavior

Unacceptable behaviour from any community member, including contributors and maintainers, will not be tolerated. Anyone asked to stop unacceptable behaviour is expected to comply immediately.

If a participant engages in unacceptable behaviour, the project maintainers may take any appropriate action, up to and including a temporary ban or permanent expulsion from the project without warning.

### Reporting Guidelines

If you are subject to or witness unacceptable behaviour or have any other concerns, please contact a project maintainer or email <support@prestdlabs.com>. All complaints will be reviewed and investigated promptly and fairly.

### Scope

This Code of Conduct applies within project spaces, on project-related social media accounts, and in public spaces when an individual represents pREST or its community. Examples of representing a project or community include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

### Acknowledgement

This Code of Conduct is adapted from the Contributor Covenant, version 2.0, available at <https://www.contributor-covenant.org/version/2/0/code\\_of\\_conduct.html>.

### Questions

If you have any questions about this Code of Conduct or need clarification on any points, please get in touch with Arthur Silva at <arthur@prestdlabs.com>.

Thank you for being so committed to fostering a positive and inclusive community!


# Code of Ethics

Collaboration, respect, and diversity are the cornerstones of pREST

Our Code of Ethics sets the standard for inclusive behaviour, creating a positive environment for contributors and users.

### Inclusivity

pREST embraces contributors and users from all backgrounds, identities, and experiences. Discrimination or exclusion based on age, gender, sexual orientation, disability, ethnicity, religion, or nationality is strictly prohibited.

### Respectful Communication

Prioritizing respectful and constructive communication is essential. Disagreements should be approached with empathy and courtesy. Harassment, insults, or any form of disrespectful behaviour will not be tolerated.

### Collaboration

Collaboration is at the heart of pREST. We encourage contributors to work together, share knowledge, and support each other. By fostering collaboration, we drive innovation and ensure the continued success of our open-source project.

### Open-mindedness

Diverse perspectives and ideas are highly valued. Open-mindedness enables us to consider different approaches, learn from each other, and adapt to new challenges. Constructive feedback is appreciated as we strive for continuous improvement.

### Accountability

All contributors, including maintainers, are held accountable for their actions and decisions. Honesty, transparency, and a commitment to resolving issues responsibly are crucial for maintaining a healthy and accountable community.

### Inclusivity in Documentation and Code

Creating accessible and inclusive documentation and code is a priority. We welcome contributions that enhance the clarity, usability, and inclusivity of our project's documentation and codebase.

### Reporting Violations

Observing behaviour that violates this Code of Ethics? Please report it to the project maintainers. All reports will be treated confidentially, and appropriate actions will be taken to address violations.

### Acknowledgement

This Code of Ethics draws inspiration and adapts principles from various open-source projects committed to fostering positive and inclusive communities.

### Conclusion

By contributing to pREST, you commit to upholding this Code of Ethics. Let's collectively work towards making pREST a positive, collaborative space for everyone.

Thank you for being a valued member of the pREST community!


# Databases

Which SQL databases pREST supports today — Native PostgreSQL, certified Postgres-compatible engines, and the multi-database roadmap.

This page lists which SQL databases pREST supports today and what is still on the roadmap — the canonical **support labels**.

PostgreSQL is **native**. Postgres-compatible engines are **certified** or documented with caveats on that adapter. MySQL, SQLite, and SQL Server are **Roadmap** until dedicated adapters ship.

*Last updated: July 18, 2026*

***

## Support labels

| Label                       | Meaning                                                      |
| --------------------------- | ------------------------------------------------------------ |
| **Native**                  | First-class dialect adapter                                  |
| **Certified**               | Runs on the native adapter with a published features matrix  |
| **Compatible with caveats** | Connects via PostgreSQL wire protocol; known gaps documented |
| **Hosted PostgreSQL**       | Managed PostgreSQL — same native adapter                     |
| **Roadmap**                 | Planned — not available yet                                  |
| **Experimental**            | Incomplete; use only with explicit expectations              |

***

## PostgreSQL family (available)

| Engine                   | Label                   | Page                                                              |
| ------------------------ | ----------------------- | ----------------------------------------------------------------- |
| PostgreSQL               | Native                  | [postgresql.md](/databases/postgresql)                            |
| Amazon Aurora PostgreSQL | Hosted PostgreSQL       | [aurora-postgresql.md](/databases/aurora-postgresql)              |
| Neon, Supabase, AlloyDB  | Hosted PostgreSQL       | Same adapter as PostgreSQL — use standard `DATABASE_URL` / `pg.*` |
| CockroachDB              | Certified (PG wire)     | [cockroachdb.md](/databases/cockroachdb)                          |
| YugabyteDB (YSQL)        | Certified (PG wire)     | [yugabytedb.md](/databases/yugabytedb)                            |
| TimescaleDB              | Compatible with caveats | [timescaledb.md](/databases/timescaledb)                          |
| Amazon Redshift          | Compatible with caveats | [amazon-redshift.md](/databases/amazon-redshift)                  |

Deep how-tos also live under [Integrations](/integrations).

***

## Roadmap families

| Engine                                               | Label                    | Page                                   |
| ---------------------------------------------------- | ------------------------ | -------------------------------------- |
| MySQL / MariaDB / TiDB / Aurora MySQL                | Roadmap (Phase 2)        | [mysql.md](/databases/mysql)           |
| SQLite                                               | Roadmap (Phase 3)        | [sqlite.md](/databases/sqlite)         |
| SQL Server / Azure SQL                               | Roadmap (Phase 4)        | [sql-server.md](/databases/sql-server) |
| Oracle                                               | Roadmap (Phase 5)        | See [roadmap.md](/databases/roadmap)   |
| Analytical (DuckDB, ClickHouse, Snowflake, BigQuery) | Separate read-only track | See [roadmap.md](/databases/roadmap)   |

Full phases and architecture notes: [Database roadmap](/databases/roadmap).

***

## FAQ

### What databases does pREST support?

Today: **PostgreSQL** (native) and engines that use the PostgreSQL wire protocol, with per-engine matrices. Other SQL families are planned — they are not installable yet.

### Is Aurora / Neon / Supabase supported?

Yes, as **Hosted PostgreSQL**. Use the same connection settings as PostgreSQL. See [Aurora PostgreSQL](/databases/aurora-postgresql).

### When will MySQL or SQLite work?

They are Phase 2 and Phase 3 on the [roadmap](/databases/roadmap). Do not expect a MySQL or SQLite connection string to work with current releases.

***

## Related

* [Homepage](/)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Configuring pREST](/get-started/configuring-prest)
* [Integrations](/integrations)


# Database roadmap

pREST multi-database roadmap — PostgreSQL family first, then MySQL, SQLite, SQL Server; analytical databases on a separate read-only track.

This page describes **planned** SQL adapters for pREST. Nothing here is installable until a release ships it. PostgreSQL is native today — see [Databases](/databases).

*Last updated: July 22, 2026*

***

## Why this order?

Developer usage (for example Stack Overflow surveys) weights **PostgreSQL, MySQL, SQLite, and SQL Server** heavily for application builders. Broader market-visibility rankings put Oracle and warehouses higher, but they are a poorer early fit for open-source adoption per engineering hour.

pREST therefore prioritizes:

1. Certify the **PostgreSQL-compatible** family on the existing adapter
2. **MySQL family** (high SEO + open-source fit)
3. **SQLite** (local/file DX)
4. **SQL Server / Azure SQL** (enterprise)
5. **Oracle** or **analytical read-only** based on demand

***

## Adapter families (target)

```
PostgreSQL family — native today
├── PostgreSQL
├── CockroachDB (certify)
├── YugabyteDB (certify)
└── Aurora PostgreSQL (certify)

MySQL family — Phase 2
├── MySQL, MariaDB (dialect profiles)
├── TiDB, Aurora MySQL (certify after MySQL adapter)

Embedded family — Phase 3
└── SQLite

SQL Server family — Phase 4
├── SQL Server
└── Azure SQL

Later / separate
├── Oracle (Phase 5)
└── Analytical read-only: DuckDB, ClickHouse, Snowflake, BigQuery
```

***

## Phases

| Phase | Focus                                                                                                                                                                                                                                                                      | Status          |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| **1** | Certify CockroachDB, YugabyteDB, Aurora PostgreSQL; Timescale E2E ([v2.2.0](/releases/v2.2.0) / [#988](https://github.com/prest/prest/pull/988)); Timescale adapter + multi-adapter routing ([v2.3.0](/releases/v2.3.0) / [#999](https://github.com/prest/prest/pull/999)) | **In progress** |
| **2** | MySQL/MariaDB native dialect; certify TiDB + Aurora MySQL                                                                                                                                                                                                                  | Roadmap         |
| **3** | SQLite native — one file → REST/MCP                                                                                                                                                                                                                                        | Roadmap         |
| **4** | SQL Server + Azure SQL                                                                                                                                                                                                                                                     | Roadmap         |
| **5** | Oracle vs analytical read-only track                                                                                                                                                                                                                                       | Undecided       |

Current support labels: [Databases](/databases).

***

## Analytical databases (separate track)

Snowflake, Databricks, BigQuery, ClickHouse, and DuckDB speak SQL but are a poor fit for generic transactional CRUD (`POST`/`PATCH`, row updates, transactions).

Planned direction:

```
adapter mode: transactional
adapter mode: analytical-read-only
```

Analytical mode would emphasize schema exploration, controlled queries, scripts, and MCP — not forced row-level CRUD.

***

## Architecture direction

Future adapters should isolate **SQL generation and schema behavior**, not only connection drivers. Design direction (not yet implemented in this docs pass):

* Explicit `Dialect` / `Capabilities` interfaces
* Shared contract-test suite (discovery, CRUD, pagination, JSON, MCP, ACL)
* Separate modules where practical (`prest-adapter-postgres`, `prest-adapter-mysql`, …)

***

## Related

* [Databases](/databases)
* [Homepage](/)
* [Key features](/readme/prestd-key-features)


# PostgreSQL

Expose PostgreSQL as a REST and MCP API with pREST — native adapter, auto CRUD, auth, ACL, and read-only MCP at /\_mcp.

**pREST** turns a PostgreSQL database into an instant REST API and (from v2.1.0) a read-only MCP endpoint. PostgreSQL is the **Native** adapter — the first and primary dialect implementation.

*Last updated: July 11, 2026* · **Label:** Native

***

## Supported features

| Capability                                                | Status                                                                                                                                             |
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Connection                                                | supported                                                                                                                                          |
| Schema discovery                                          | supported                                                                                                                                          |
| CRUD                                                      | supported                                                                                                                                          |
| Filtering / ordering / pagination                         | supported                                                                                                                                          |
| Transactions                                              | supported                                                                                                                                          |
| Scripts (`/_QUERIES`)                                     | supported                                                                                                                                          |
| MCP (`/_mcp`)                                             | supported (v2.1.0+)                                                                                                                                |
| ACL / permissions                                         | supported                                                                                                                                          |
| pgvector KNN / distance filtering (`_korder`, `:vecdist`) | supported (v2.4.0+, requires `pgvector` extension) — see [Parameters](/api-reference/parameters#pgvector-knn-ordering-and-distance-filtering-v240) |

***

## Connection

```sh
export PREST_VERSION=2
export PREST_PG_URL='postgres://user:pass@localhost:5432/mydb?sslmode=disable'
# or discrete PREST_PG_HOST / PREST_PG_USER / PREST_PG_PASS / PREST_PG_DATABASE
```

TOML `[pg]` and multi-database `[[databases]]` are documented in [Configuring pREST](/get-started/configuring-prest) and [Multi-database](/get-started/multi-database).

***

## Quick CRUD example

**REST (Representational State Transfer)** maps HTTP verbs to CRUD on SQL tables at `/{database}/{schema}/{table}` — [API Reference](/api-reference).

```http
GET /mydb/public/users
POST /mydb/public/users
Content-Type: application/json

{"name": "ada"}
```

See also [Parameters](/api-reference/parameters).

***

## MCP

**MCP (Model Context Protocol)** is an open standard for connecting AI apps and agents to tools and data. On PostgreSQL, pREST exposes read-only MCP at `/_mcp` on the same server as REST — [MCP over HTTP](/get-started/mcp-over-http).

```http
GET /_mcp
POST /_mcp
```

AI client setup: [docs.prestd.com/ai](https://docs.prestd.com/ai).

***

## Known limitations

* Requires PostgreSQL **9.5+** (project baseline).
* SQL dialect features beyond what the adapter generates (exotic types, some extensions) may need [custom queries](/api-reference/custom-queries).
* Other SQL engines are not this adapter — see [roadmap](/databases/roadmap).

***

## Related

* [Databases](/databases)
* [Acronyms](/readme/acronyms) ([REST](/readme/acronyms#rest), [MCP](/readme/acronyms#mcp))
* [Get pREST](/get-prest)
* [Deploying with Docker](/deployment/deploying-with-docker)
* [Permissions](/get-started/permissions)


# Amazon Aurora PostgreSQL

Use Amazon Aurora PostgreSQL with pREST — same native PostgreSQL adapter, REST CRUD and MCP over the PostgreSQL wire protocol.

**pREST** works with **Amazon Aurora PostgreSQL** as **Hosted PostgreSQL**: the same **Native** PostgreSQL adapter, pointed at your Aurora cluster endpoint.

*Last updated: July 11, 2026* · **Label:** Hosted PostgreSQL

***

## Supported features

| Capability                        | Status                                           |
| --------------------------------- | ------------------------------------------------ |
| Connection                        | supported (Aurora PG endpoint + SSL as required) |
| Schema discovery                  | supported                                        |
| CRUD                              | supported                                        |
| Filtering / ordering / pagination | supported                                        |
| Transactions                      | supported                                        |
| Scripts (`/_QUERIES`)             | supported                                        |
| MCP (`/_mcp`)                     | supported (v2.1.0+)                              |
| ACL / permissions                 | supported                                        |

Same matrix as [PostgreSQL](/databases/postgresql). Aurora MySQL is **not** supported until the MySQL adapter ships — see [MySQL roadmap](/databases/mysql).

***

## Connection

Use the Aurora **PostgreSQL** writer/reader endpoint with TLS appropriate for your VPC:

```sh
export PREST_VERSION=2
export PREST_PG_URL='postgres://user:pass@my-cluster.cluster-xxxx.region.rds.amazonaws.com:5432/mydb?sslmode=require'
```

Or set `PREST_PG_HOST`, `PREST_PG_PORT`, `PREST_PG_USER`, `PREST_PG_PASS`, `PREST_PG_DATABASE`, and `PREST_PG_SSL_MODE`.

For multi-tenant / multi-cluster setups, register aliases under `[[databases]]` or `DATABASE_ALIAS_N` / `DATABASE_URL_N` — [Multi-database](/get-started/multi-database).

***

## CRUD and MCP

Identical to native PostgreSQL:

* REST: `/{database}/{schema}/{table}` — [API Reference](/api-reference)
* MCP: `/_mcp` — [MCP over HTTP](/get-started/mcp-over-http)

***

## Known limitations

* Use the **PostgreSQL-compatible** Aurora engine, not Aurora MySQL.
* Network access (security groups, private subnets, SSL) is your responsibility.
* Aurora-specific extensions or version skew may differ from community PostgreSQL; validate critical queries in staging.
* Reader endpoints are fine for read-heavy REST/MCP traffic; send writes to the writer endpoint.

***

## Related

* [PostgreSQL](/databases/postgresql)
* [Databases](/databases)
* [Configuring pREST](/get-started/configuring-prest)
* [Database roadmap](/databases/roadmap)


# CockroachDB

CockroachDB REST and MCP via pREST — PostgreSQL wire-compatible, with a documented support matrix and known dialect differences.

**pREST** can expose **CockroachDB** through the **Native** PostgreSQL adapter because CockroachDB implements the PostgreSQL wire protocol. Treat support as **Certified (PG wire)** with the matrix below — not identical to community PostgreSQL in every dialect detail.

*Last updated: July 11, 2026* · **Label:** Certified (PostgreSQL wire)

***

## Supported features

| Capability                        | Status                                               |
| --------------------------------- | ---------------------------------------------------- |
| Connection                        | supported (PG connection string / port)              |
| Schema discovery                  | supported (validate against your CRDB version)       |
| CRUD                              | supported for standard table operations              |
| Filtering / ordering / pagination | supported                                            |
| Transactions                      | supported with CockroachDB transaction semantics     |
| Scripts (`/_QUERIES`)             | supported for SQL accepted by CockroachDB            |
| MCP (`/_mcp`)                     | supported (v2.1.0+) when catalog/query paths succeed |
| ACL / permissions                 | supported (pREST ACL layer)                          |

***

## Connection

Default CockroachDB SQL port is often **26257**:

```sh
export PREST_VERSION=2
export PREST_PG_URL='postgres://root@localhost:26257/defaultdb?sslmode=disable'
```

For secure clusters, use TLS settings required by your deployment (`sslmode=verify-full`, client certs, etc.).

Docker smoke example (illustrative):

```sh
docker run -d --name crdb -p 26257:26257 cockroachdb/cockroach:latest start-single-node --insecure
docker run -d -p 3000:3000 \
  -e PREST_VERSION=2 \
  -e PREST_PG_URL='postgres://root@host.docker.internal:26257/defaultdb?sslmode=disable' \
  -e PREST_DEBUG=true \
  prest/prest:v2.4.2
```

Adjust host networking for your OS/Docker setup.

***

## CRUD example

**REST (Representational State Transfer)** maps HTTP verbs to CRUD on SQL tables at `/{database}/{schema}/{table}` — [API Reference](/api-reference).

```http
GET /defaultdb/public/users
```

***

## MCP

**MCP (Model Context Protocol)** is an open standard for connecting AI apps and agents to tools and data. Point MCP clients at the same `prestd` `/_mcp` endpoint — [MCP over HTTP](/get-started/mcp-over-http). Prefer a read-only DB role for agent traffic.

***

## Known limitations / incompatibilities

* Not every PostgreSQL extension or catalog quirk exists in CockroachDB.
* Some PostgreSQL-specific SQL (types, functions, `RETURNING` edge cases, advanced DDL) may fail; use [custom queries](/api-reference/custom-queries) only when CockroachDB accepts the SQL.
* Serial/identity and migration patterns differ from PostgreSQL — design schemas with CockroachDB docs in mind.
* Run your own integration checks before production; Phase 1 certification means **documented** use of the PG adapter, not a claim of 100% PostgreSQL parity.

***

## Related

* [Databases](/databases)
* [Acronyms](/readme/acronyms) ([REST](/readme/acronyms#rest), [MCP](/readme/acronyms#mcp))
* [PostgreSQL](/databases/postgresql)
* [YugabyteDB](/databases/yugabytedb)
* [Roadmap](/databases/roadmap)


# YugabyteDB

YugabyteDB REST and MCP with pREST — connect via YSQL PostgreSQL wire protocol, with co-location and support matrix guidance.

**pREST** connects to **YugabyteDB** through the **YSQL** PostgreSQL-compatible API. Label: **Certified (PostgreSQL wire)**. Prefer co-locating `prestd` with the database nodes you query.

*Last updated: July 11, 2026* · **Label:** Certified (PostgreSQL wire)

Deep walkthrough (multi-node examples): [Integrations — YugabyteDB](/integrations/yugabytedb).

***

## Supported features

| Capability                        | Status                                      |
| --------------------------------- | ------------------------------------------- |
| Connection                        | supported (YSQL port, default **5433**)     |
| Schema discovery                  | supported                                   |
| CRUD                              | supported for YSQL tables/views             |
| Filtering / ordering / pagination | supported                                   |
| Transactions                      | supported with YugabyteDB transaction model |
| Scripts (`/_QUERIES`)             | supported for YSQL-accepted SQL             |
| MCP (`/_mcp`)                     | supported (v2.1.0+)                         |
| ACL / permissions                 | supported                                   |

***

## Connection

```sh
export PREST_VERSION=2
export PREST_PG_URL='postgres://yugabyte:yugabyte@yb-tserver-n1:5433/yugabyte'
```

Single-node Docker pattern (see integration guide for multi-node):

```sh
docker network create yb-net
docker run -d --hostname yb-tserver-n1 -p 7000:7000 \
  --network yb-net yugabytedb/yugabyte:latest \
  yugabyted start --daemon false --listen yb-tserver-n1

docker run -d -p 3001:3000 --network yb-net \
  -e PREST_VERSION=2 \
  -e PREST_PG_URL=postgres://yugabyte:yugabyte@yb-tserver-n1:5433/yugabyte \
  -e PREST_DEBUG=true \
  prest/prest:v2.4.2
```

***

## Node locality

* One `prestd` behind a load balancer over YugabyteDB nodes, or
* One `prestd` per node (lower latency in geo-distributed setups)

Details: [Integrations — YugabyteDB](/integrations/yugabytedb).

***

## CRUD

**REST (Representational State Transfer)** maps HTTP verbs to CRUD on YSQL tables at `/{database}/{schema}/{table}` — [API Reference](/api-reference).

***

## MCP

**MCP (Model Context Protocol)** is an open standard for connecting AI apps and agents to tools and data. Use `/_mcp` on the same server — [MCP over HTTP](/get-started/mcp-over-http).

***

## Known limitations

* YSQL is PostgreSQL-compatible, not a drop-in for every PostgreSQL extension.
* Default SQL port is **5433**, not 5432.
* Cluster-aware Go drivers are optional advanced setups documented upstream; stock pREST uses the standard PostgreSQL driver path.
* Validate distributed-SQL behavior (transactions, uniqueness) against YugabyteDB docs for your version.

***

## Related

* [Integrations — YugabyteDB](/integrations/yugabytedb)
* [Acronyms](/readme/acronyms) ([REST](/readme/acronyms#rest), [MCP](/readme/acronyms#mcp))
* [Databases](/databases)
* [CockroachDB](/databases/cockroachdb)
* [PostgreSQL](/databases/postgresql)


# TimescaleDB

TimescaleDB with pREST — hypertables over the Postgres wire, auto-detect adapter in v2.3.0 (#999), \_time\_bucket, REST and MCP.

**TimescaleDB** is a PostgreSQL extension for time-series workloads. **pREST** treats it as **Compatible with caveats**: connect like PostgreSQL, use hypertables as tables, and use Timescale-aware operators when the Timescale adapter is selected.

*Last updated: July 22, 2026* · **Label:** Compatible with caveats

{% hint style="info" %}
**v2.2.0+:** E2E coverage on the native PostgreSQL adapter ([#988](https://github.com/prest/prest/pull/988)). See [v2.2.0](/releases/v2.2.0).

**v2.3.0 (**[**#999**](https://github.com/prest/prest/pull/999)**):** pREST **auto-detects** Timescale (extension check) and selects a Timescale adapter with `_time_bucket` and system-schema filtering. Still not a separate dialect family — wire-compatible with PostgreSQL. MySQL/SQLite multi-adapter is roadmap. See [v2.3.0](/releases/v2.3.0).
{% endhint %}

Full Compose example: [Integrations — TimescaleDB](/integrations/timescaledb). Prefer `timescale/timescaledb:latest-pg18` and `CREATE EXTENSION IF NOT EXISTS timescaledb`.

***

## Supported features

| Capability                        | Status                                                                                    |
| --------------------------------- | ----------------------------------------------------------------------------------------- |
| Connection                        | supported (Postgres protocol)                                                             |
| Schema discovery                  | supported — hypertables; `_timescaledb_*` hidden by default on Timescale adapter (v2.3.0) |
| CRUD                              | supported on underlying relations; hypertable semantics apply                             |
| Filtering / ordering / pagination | supported                                                                                 |
| `_time_bucket`                    | **v2.3.0 / #999** — Timescale adapter only (`5m`…`1y`)                                    |
| Transactions                      | supported (PostgreSQL)                                                                    |
| Scripts (`/_QUERIES`)             | supported — preferred for policies, continuous-aggregate DDL                              |
| MCP (`/_mcp`)                     | supported (v2.1.0+) for readable relations                                                |
| ACL / permissions                 | supported                                                                                 |

***

## Connection

Same as PostgreSQL — point `PREST_PG_*` / `PREST_PG_URL` at a TimescaleDB (or Timescale Cloud) instance, or register an alias in [Multi-database](/get-started/multi-database).

```sh
export PREST_VERSION=2
export PREST_PG_HOST=timescaledb
export PREST_PG_USER=prest
export PREST_PG_PASS=prest
export PREST_PG_DATABASE=prest
export PREST_PG_SSL_MODE=disable
```

Since v2.3.0 (#999), startup tries the Timescale adapter first, then falls back to Postgres.

***

## Time bucketing (v2.3.0)

```bash
curl "http://localhost:3000/prest/public/readings?_time_bucket=1h"
curl "http://localhost:3000/prest/public/readings?_time_bucket=1h,measured_at"
```

Intervals: `5m`, `15m`, `1h`, `6h`, `1d`, `7d`, `30d`, `1y`. Include Timescale system schemas with `_include_system_schemas=true`.

***

## MCP and REST

* REST CRUD: [API Reference](/api-reference)
* Continuous aggregates / policies: [custom queries](/api-reference/custom-queries) or SQL
* Multi-alias setups: [Multi-database](/get-started/multi-database)
* MCP: [MCP over HTTP](/get-started/mcp-over-http)

***

## Known limitations

* Stock Postgres adapter (v2.2.0 and earlier) does not implement `_time_bucket` — use `/_QUERIES` or upgrade to v2.3.0 (#999).
* Compression, retention policies, and continuous-aggregate **DDL** stay outside generic CRUD.
* Catalog listing may include hypertables and chunk children; use ACL / expose settings as needed.
* Use a PostgreSQL version Timescale supports for your image/cloud tier (docs examples use PG 18 images).

***

## Related

* [Integrations — TimescaleDB](/integrations/timescaledb)
* [Multi-database](/get-started/multi-database)
* [PostgreSQL](/databases/postgresql)
* [Databases](/databases)
* [v2.2.0 release notes](/releases/v2.2.0)
* [PR #999](https://github.com/prest/prest/pull/999)
* [Acronyms](/readme/acronyms) · [REST](/readme/acronyms#rest) · [MCP](/readme/acronyms#mcp)


# Amazon Redshift

Amazon Redshift with pREST — PostgreSQL-based warehouse connectivity with important dialect differences and a cautious support matrix.

**Amazon Redshift** is PostgreSQL-based but **not** full PostgreSQL. **pREST** can connect using the PostgreSQL driver path with vendor-required parameters. Label: **Compatible with caveats**.

*Last updated: July 11, 2026* · **Label:** Compatible with caveats

Also see: [Integrations — Amazon Redshift](/integrations/amazon-redshift).

***

## Supported features

| Capability                        | Status                                                                      |
| --------------------------------- | --------------------------------------------------------------------------- |
| Connection                        | partial — requires Redshift-compatible URL options                          |
| Schema discovery                  | partial — validate against your cluster                                     |
| CRUD                              | partial — prefer read-heavy / controlled writes; warehouse semantics differ |
| Filtering / ordering / pagination | partial — SQL differences vs PostgreSQL                                     |
| Transactions                      | partial — Redshift transaction model differs                                |
| Scripts (`/_QUERIES`)             | preferred path for Redshift-safe SQL                                        |
| MCP (`/_mcp`)                     | partial — use read-only roles; verify tools against catalog                 |
| ACL / permissions                 | supported at pREST layer; also enforce IAM/DB grants                        |

***

## Connection

Use a PostgreSQL-style URL with Redshift’s `OpenSourceSubProtocolOverride` (see AWS docs on Redshift vs PostgreSQL):

```sh
export PREST_VERSION=2
export DATABASE_URL='postgresql://user:pass@redshift-endpoint:5439/dev?OpenSourceSubProtocolOverride=true'
```

Use `PREST_PG_SSL_*` for TLS. See [Upgrading to v2](/get-started/upgrading-to-v2) for SSL env naming.

***

## Recommendations

* Treat Redshift primarily as **analytics / read** access through pREST.
* Prefer [custom queries](/api-reference/custom-queries) over assuming PostgreSQL CRUD parity.
* For AI agents, use read-only credentials and [MCP over HTTP](/get-started/mcp-over-http).
* Support labels and roadmap: [Databases](/databases).

Also see the shorter integration note: [Integrations — Amazon Redshift](/integrations/amazon-redshift).

***

## Known limitations

* Important SQL and type differences vs PostgreSQL — read [AWS: Redshift and PostgreSQL](https://docs.aws.amazon.com/redshift/latest/dg/c_redshift-and-postgres-sql.html).
* Not a substitute for a transactional OLTP PostgreSQL deployment.
* Do not assume every pREST filter operator maps cleanly; test before production.

***

## Related

* [Integrations — Amazon Redshift](/integrations/amazon-redshift)
* [Databases](/databases)
* [Roadmap](/databases/roadmap) (analytical read-only track)


# MySQL family (roadmap)

MySQL and MariaDB REST/MCP support for pREST is on the roadmap (Phase 2) — not available in current releases.

**Status: Roadmap (Phase 2).** pREST does **not** ship a MySQL or MariaDB adapter yet. Current releases speak the **PostgreSQL** wire protocol only.

*Last updated: July 11, 2026* · **Label:** Roadmap

***

## Planned coverage

One MySQL-family adapter is intended to unlock:

* MySQL
* MariaDB (separate dialect profile over time)
* Amazon Aurora MySQL
* TiDB (after compatibility testing)
* Some Vitess / PlanetScale-style deployments after testing

Intended SEO/product pages later: MySQL REST API, MySQL MCP, MariaDB REST API, Aurora MySQL REST API, TiDB REST API — each with original matrices, not name-only clones.

***

## What works today

Use [PostgreSQL](/databases/postgresql) or a [certified Postgres-compatible engine](/databases). There is **no** supported `mysql://` connection path in current `prestd` builds.

***

## Related

* [Database roadmap](/databases/roadmap)
* [Databases](/databases)
* [Homepage](/)


# SQLite (roadmap)

SQLite REST and MCP support for pREST is on the roadmap (Phase 3) — turn a SQLite file into an API is planned, not shipped.

**Status: Roadmap (Phase 3).** pREST cannot open a SQLite file as a database backend in current releases.

*Last updated: July 11, 2026* · **Label:** Roadmap

***

## Planned positioning

> Turn a SQLite file into a REST and MCP API with one binary.

Target use cases: local development, desktop apps, internal tools, prototypes, edge deployments, AI agents over local data — without running a database server.

SQLite is not “small PostgreSQL”; a dedicated dialect must handle schemas, concurrency, types, and metadata differently.

***

## What works today

Use [PostgreSQL](/databases/postgresql) (including lightweight local Postgres/Docker) or another [available engine](/databases).

***

## Related

* [Database roadmap](/databases/roadmap)
* [Databases](/databases)


# SQL Server (roadmap)

SQL Server and Azure SQL REST/MCP support for pREST is on the roadmap (Phase 4) — not available in current releases.

**Status: Roadmap (Phase 4).** pREST does **not** include a SQL Server or Azure SQL adapter today.

*Last updated: July 11, 2026* · **Label:** Roadmap

***

## Planned coverage

* Microsoft SQL Server
* Azure SQL Database

Expected work includes T-SQL pagination, `IDENTITY` / `OUTPUT`, catalog introspection, parameter syntax, and type mapping — higher effort than MySQL/SQLite.

***

## What works today

Use the [PostgreSQL family](/databases). Enterprise teams on SQL Server should track [roadmap Phase 4](/databases/roadmap) rather than attempting SQL Server connection strings with current builds.

***

## Related

* [Database roadmap](/databases/roadmap)
* [Databases](/databases)


# Releases

**Latest v2 release:** [v2.4.2](/releases/v2.4.2) — RFC 7518 minimum key sizes for HMAC `jwt.key` ([#1017](https://github.com/prest/prest/pull/1017)), bound values for custom query scripts with credential headers withheld from templates, and SQL removed from logs ([#1023](https://github.com/prest/prest/pull/1023)).

For stable v1 releases, see [GitHub Releases](https://github.com/prest/prest/releases/latest).

## Unreleased (main)

Nothing merged after v2.4.2 yet. See [Changes since v2.4.2](/releases/main-since-v2.4.2).

## v2.4.2 highlights

| Area           | Change                                                                                                                                                |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| JWT            | HMAC `jwt.key` minimums (32/48/64 bytes); short keys are discarded and **auth disables itself** ([#1017](https://github.com/prest/prest/pull/1017))   |
| JWT            | `jwt.algo` enforced as the sole permitted signature algorithm ([#1017](https://github.com/prest/prest/pull/1017))                                     |
| Custom queries | Bind values with `sqlVal` / `sqlList` / `ident`; rejected interpolated values now fail with `400` ([#1023](https://github.com/prest/prest/pull/1023)) |
| Custom queries | Credential headers withheld from templates; script path traversal rejected ([#1023](https://github.com/prest/prest/pull/1023))                        |
| Logging        | Script SQL no longer logged; CRUD parameter values replaced by a count ([#1023](https://github.com/prest/prest/pull/1023))                            |

See [v2.4.2 release notes](/releases/v2.4.2). **Check `jwt.key` length before upgrading** — a short key leaves the API unauthenticated rather than refusing to start.

## v2.4.1 highlights

| Area           | Change                                                                                                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MCP            | `/_mcp` honours `[expose]`, closing a catalog-discovery bypass ([#1016](https://github.com/prest/prest/pull/1016))                                      |
| Custom queries | SQL-keyword screen on interpolated script values ([#1016](https://github.com/prest/prest/pull/1016)) — too broad, relaxed in [v2.4.2](/releases/v2.4.2) |

See [v2.4.1 release notes](/releases/v2.4.1) and [Changes since v2.4.0](/releases/main-since-v2.4.0). Upgrade past v2.4.1 to v2.4.2.

## v2.4.0 highlights

| Area          | Change                                                                                                                    |
| ------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Observability | Opt-in OpenTelemetry traces/metrics/logs, local SigNoz dev stack ([#1003](https://github.com/prest/prest/pull/1003))      |
| Vector search | pgvector KNN ordering (`_korder`) and distance filtering (`:vecdist`) ([#1011](https://github.com/prest/prest/pull/1011)) |
| pREST Studio  | Dependency upgrade — auth-dialog and tool-invocation fixes ([#1004](https://github.com/prest/prest/pull/1004))            |

See [v2.4.0 release notes](/releases/v2.4.0) and [Changes since v2.3.0](/releases/main-since-v2.3.0).

{% hint style="info" %}
`_korder` / `:vecdist` and the `[otel]` section landed in v2.4.0 and are unchanged in v2.4.1 and v2.4.2.
{% endhint %}

## v2.3.0 highlights

| Area           | Change                                                                                                                                                                                                      |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Security       | Unauthenticated `_select` SQL-injection fixed — CVSS 9.8 ([GHSA-qvx3-q8vx-9q3c](https://github.com/prest/prest/security/advisories/GHSA-qvx3-q8vx-9q3c), [#1002](https://github.com/prest/prest/pull/1002)) |
| Multi-adapter  | Adapter registry, automatic Postgres/TimescaleDB detection, path-based routing ([#999](https://github.com/prest/prest/pull/999))                                                                            |
| JWKS hardening | `jwx/v3` — non-2xx rejection, 1 MiB body cap, URL redaction in logs ([#1002](https://github.com/prest/prest/pull/1002))                                                                                     |

See [v2.3.0 release notes](/releases/v2.3.0). Upgrade from v2.2.0 as soon as possible for the security fix.

## v2.2.0 highlights

| Area           | Change                                                                                                                                                         |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| pREST Studio   | Embedded UI at `/_studio/` — Data / REST / MCP explorers ([#990](https://github.com/prest/prest/pull/990))                                                     |
| Custom queries | Optional database storage, registry API, query ACL ([#980](https://github.com/prest/prest/pull/980))                                                           |
| TimescaleDB    | E2E certification on the native PostgreSQL adapter ([#988](https://github.com/prest/prest/pull/988))                                                           |
| Config sample  | Fully documented [`prest.sample.toml`](https://github.com/prest/prest/blob/v2.2.0/samples/prest.sample.toml) ([#978](https://github.com/prest/prest/pull/978)) |

See [v2.2.0 release notes](/releases/v2.2.0), [Changes since v2.1.0](/releases/main-since-v2.1.0), and [pREST Studio](/get-started/prest-studio).

## v2.1.0 highlights

| Area               | Change                                                                                                                                  |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| MCP over HTTP      | Read-only `/_mcp` endpoint with JSON-RPC `initialize`, `tools/list`, and `tools/call` ([#977](https://github.com/prest/prest/pull/977)) |
| Schema-aware tools | Per-table `prest.select.{database}.{schema}.{table}` tools with typed input schemas from catalog metadata                               |
| Safety             | Read-only by design; inherits auth, ACL, and identifier validation from the existing HTTP stack                                         |

See [v2.1.0 release notes](/releases/v2.1.0), the [MCP over HTTP guide](/get-started/mcp-over-http), and [AI and MCP](/ai) (Cursor / Claude Desktop / adapter install) for usage and upgrade notes.

## v2.0.0 highlights

| Area              | Change                                                                                                               |
| ----------------- | -------------------------------------------------------------------------------------------------------------------- |
| Multi-database    | `[[databases]]` registry, alias routing, `/_ready` ([#973](https://github.com/prest/prest/pull/973))                 |
| Config resilience | Graceful fallbacks; startup never blocked by bad config ([#974](https://github.com/prest/prest/pull/974))            |
| JWT               | Auto-disable when misconfigured; `jwt.default` defaults to `false` ([#974](https://github.com/prest/prest/pull/974)) |
| Security          | Template sanitization, credential redaction in logs ([#972](https://github.com/prest/prest/pull/972))                |
| OR filtering      | `_or` query parameter ([#958](https://github.com/prest/prest/pull/958))                                              |
| Permissions       | Per-user table permissions ([#912](https://github.com/prest/prest/pull/912))                                         |

See [v2.0.0 release notes](/releases/v2.0.0) and [Changes since rc6](/releases/main-since-rc6) for full details.

## Release candidate history (rc1 – rc6)

The v2 release candidates shipped the following before v2.0.0 was tagged.

### Features

| Version | Change                                                                                               |
| ------- | ---------------------------------------------------------------------------------------------------- |
| rc1     | Per-user table permissions via `[[access.users]]` ([#912](https://github.com/prest/prest/pull/912))  |
| rc6     | OR clause filtering with the `_or` query parameter ([#958](https://github.com/prest/prest/pull/958)) |
| rc6     | Structured JSON logging via Go `slog` ([#950](https://github.com/prest/prest/pull/950))              |
| rc6     | Docker images built with GoReleaser ([#953](https://github.com/prest/prest/pull/953))                |

### Security

| Version | Change                                                                                                                                                                                                                    |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| rc3     | `_returning` parameter hardened against SQL injection ([#935](https://github.com/prest/prest/pull/935))                                                                                                                   |
| rc4     | Unified identifier validation across templates, groupby, and path params ([#938](https://github.com/prest/prest/pull/938), [GHSA-p46v-f2x8-qp98](https://github.com/prest/prest/security/advisories/GHSA-p46v-f2x8-qp98)) |
| rc5     | `tsquery` operator hardened against SQL injection ([#940](https://github.com/prest/prest/pull/940))                                                                                                                       |
| rc6     | JWT auth bypass fixed when default enforcement runs without a key ([#960](https://github.com/prest/prest/pull/960), [GHSA-fj7v-859r-2fm4](https://github.com/prest/prest/security/advisories/GHSA-fj7v-859r-2fm4))        |

### Config and breaking changes

| Version | Change                                                                                                                                                              |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| rc2     | Deprecated `PREST_SSL_*` environment variables and `[ssl]` TOML block removed — use `PREST_PG_SSL_*` / `[pg.ssl]` ([#919](https://github.com/prest/prest/pull/919)) |
| rc2     | Default `pg.ssl.mode` is `disable` when no config file is found (v1 used `require`)                                                                                 |
| rc6     | Server refuses to start when JWT is enabled without verification material (unless debug mode is on)                                                                 |

### Fixes

| Version | Change                                                                                                               |
| ------- | -------------------------------------------------------------------------------------------------------------------- |
| rc2     | Default cache storage path set when caching is disabled ([#918](https://github.com/prest/prest/pull/918))            |
| rc6     | `_select` field names are whitespace-trimmed after comma splitting ([#941](https://github.com/prest/prest/pull/941)) |
| rc6     | Identifier formatting fix ([#955](https://github.com/prest/prest/pull/955))                                          |

## Upgrading

If you are moving from v1 to v2, see the [Upgrading to v2](/get-started/upgrading-to-v2) guide.


# Changes since v2.4.2 (unreleased)

Changes merged to prest main after v2.4.2 — updated as commits land.

[v2.4.2](/releases/v2.4.2) is released and is the current stable tag. This page tracks work merged to `main` **after** it, ahead of the next release — **nothing has merged yet**. Compare: [v2.4.2...main](https://github.com/prest/prest/compare/v2.4.2...main).

***

## Related

* [v2.4.2 release notes](/releases/v2.4.2)
* [Releases](/releases)


# v2.4.2

pREST v2.4.2 — HMAC jwt.key minimums, bound values for custom query scripts, credential headers withheld from templates, and SQL redacted from logs.

Released: August 11, 2026

[GitHub tag](https://github.com/prest/prest/releases/tag/v2.4.2) · [Compare v2.4.1...v2.4.2](https://github.com/prest/prest/compare/v2.4.1...v2.4.2)

**v2.4.2** is a **hardening** release. It enforces RFC 7518 minimum key sizes for HMAC `jwt.key` ([#1017](https://github.com/prest/prest/pull/1017)), withholds credential headers from custom query templates and makes rejected interpolated values fail the request instead of silently emptying ([#1023](https://github.com/prest/prest/pull/1023)), stops writing caller-influenced SQL to logs, and picks up dependency updates ([#1015](https://github.com/prest/prest/pull/1015), [#1005](https://github.com/prest/prest/pull/1005)).

* **Docker:** `prest/prest:v2.4.2`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.4.2`

{% hint style="danger" %}
**Check your `jwt.key` length before upgrading.** An HMAC key shorter than the minimum for its algorithm (32 bytes for HS256) is discarded at startup. pREST **does not refuse to start** — it disables `/auth` and the JWT middleware and keeps serving, so a short key turns an authenticated deployment into an **unauthenticated** one. See [Upgrading from v2.4.1](#upgrading-from-v241).
{% endhint %}

This release includes the MCP `[expose]` enforcement from [v2.4.1](/releases/v2.4.1), and relaxes the over-broad script-value screen that release introduced.

***

## Highlights

### Minimum HMAC key sizes for `jwt.key` ([#1017](https://github.com/prest/prest/pull/1017))

pREST moved from the unmaintained `square/go-jose.v2` to `go-jose/go-jose/v4`, which enforces RFC 7518 key sizes and returns an error for undersized HMAC keys. Rather than surface that at request time, pREST validates the key at config load:

| `jwt.algo`                                          | Minimum `jwt.key` length                         |
| --------------------------------------------------- | ------------------------------------------------ |
| `HS256` (also the default when `jwt.algo` is unset) | **32 bytes**                                     |
| `HS384`                                             | **48 bytes**                                     |
| `HS512`                                             | **64 bytes**                                     |
| `RS*`, `ES*`, `PS*`, `EdDSA`                        | Not checked — `jwt.key` is not used as a MAC key |

The length is the byte length of the raw string, so a 32-character ASCII secret satisfies HS256.

A key below the minimum is **discarded**, and the features that depend on it disable themselves. Startup logs both events at `ERROR`:

```
level=ERROR msg="jwt.key too short for HMAC algorithm" algo=HS256 got=6 want=32
level=ERROR msg="auth disabled: jwt.key is empty"
```

A configured `jwt.jwks` or `jwt.wellknownurl` is unaffected — JWT verification continues against the JWKS even if the HMAC key is discarded.

See [Auth — HMAC key requirements](/api-reference/auth#hmac-key-requirements-v242).

### `jwt.algo` is now enforced ([#1017](https://github.com/prest/prest/pull/1017))

`jwt.algo` was accepted but discarded in earlier v2 releases: tokens were parsed without restricting the permitted signature algorithm. It is now passed to the parser as the single allowed algorithm, which structurally prevents algorithm-confusion attacks.

Two consequences for existing deployments:

* **A token whose `alg` header does not match `jwt.algo` is now rejected** with `401` and `{"error": "failed JWT token parser"}`.
* **The value is matched case-sensitively against a fixed set** — `EdDSA`, `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`, `PS256`, `PS384`, `PS512`. Anything else, including `hs256` in lowercase or an explicitly empty `algo = ""`, makes every request return **HTTP 500** with `unsupported JWT signature algorithm`. Leave `jwt.algo` unset to get the `HS256` default.

### Bind values in custom query scripts ([#1023](https://github.com/prest/prest/pull/1023))

Values a `/_QUERIES` template interpolates become part of the SQL text, so pREST screens them. This release makes the screen predictable and gives templates a way out of it entirely.

**Bind free-form values** with `sqlVal`, `sqlList`, or `ident` and the screen does not apply — the value travels to PostgreSQL out of band, where it can never be parsed as SQL:

```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 or column name, which cannot be bound | `"public"."users"` |

Three related changes:

* **The keyword screen now runs only on values containing a space.** This fixes [#1030](https://github.com/prest/prest/issues/1030), where [v2.4.1](/releases/v2.4.1) blanked single-word values such as the slug `sao-joao-do-sul` (the token `do` is a SQL keyword). The character allow-list and the `--` / `::` rejection still apply to every value.
* **A rejected value that is interpolated now fails the request** with `400`, instead of substituting an empty string and returning `200` with the wrong rows:

  ```
  invalid value for parameter slug: it contains SQL syntax that cannot be
  interpolated safely; use the sqlVal template helper to bind free-form values
  ```

  The offending value is never echoed back. `sqlVal` and `sqlList` are exempt, since a bound value is never rendered into SQL text.
* **Query parameters named `header`, `_param`, or `_header` are ignored** — they are reserved for template data.

See [Custom Queries — Binding values](/api-reference/custom-queries#binding-values-sqlval-sqllist-ident-v242).

### Credential headers withheld from templates ([#1023](https://github.com/prest/prest/pull/1023))

A bearer token is plain base64url text, so it passed the value screen untouched and a template referencing it would interpolate the caller's credential straight into SQL — which was then logged.

These headers are now blanked before templates see them, in both the interpolated and the bound form:

`Authorization` · `Proxy-Authorization` · `Cookie` · `X-Api-Key` · `X-Auth-Token` · `X-Access-Token`

The request still succeeds; the value is simply empty. A template that scoped rows by the caller's token will now match nothing — move that logic to [permissions](/get-started/permissions) or pass a non-credential header.

Other headers rejected by the screen are blanked and logged at `WARN` with the header name only, rather than failing the request — an ordinary `User-Agent` fails the character allow-list on `(` and `;`, so erroring would reject nearly every browser request.

### SQL no longer written to logs ([#1023](https://github.com/prest/prest/pull/1023))

Statements produced from custom query templates are caller-influenced, so they are no longer logged on either the read or the write path. CRUD statements are still logged at `debug`, but their parameter **values** are replaced by a count:

```
level=DEBUG msg="generated SQL" parameter_count=2
```

Custom query script SQL is not visible in logs at any level. Use the database's own statement logging when you need it.

### Script path traversal rejected ([#1023](https://github.com/prest/prest/pull/1023))

Script resolution now verifies that the resolved `.sql` file is inside the queries directory, both lexically and after resolving symlinks. `..` segments and symlinks pointing outside the tree return **400** `invalid script path: <folder>/<script>`. This backs up the identifier validation already applied to the HTTP path, covering callers that reach the adapter directly.

### Dependency updates

`lestrrat-go/jwx/v3` 3.1.1 → 3.2.0 ([#1015](https://github.com/prest/prest/pull/1015)) and `google.golang.org/grpc` 1.81.1 → 1.82.1 ([#1005](https://github.com/prest/prest/pull/1005)). Both are dependency-only with no source changes; JWKS handling and OpenTelemetry export behave as before.

***

## Changes since v2.4.1

| PR                                                | Summary                                                                                                                                                                                                                                                       |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [#1017](https://github.com/prest/prest/pull/1017) | RFC 7518 minimum HMAC key sizes for `jwt.key`; `go-jose/v4` migration; `jwt.algo` enforced as the sole permitted signature algorithm                                                                                                                          |
| [#1023](https://github.com/prest/prest/pull/1023) | Credential headers withheld from script templates; rejected interpolated values fail with `400`; script SQL removed from logs; script path traversal rejected; [#1030](https://github.com/prest/prest/issues/1030) screen relaxation; CI workflow permissions |
| [#1015](https://github.com/prest/prest/pull/1015) | Bump `github.com/lestrrat-go/jwx/v3` 3.1.1 → 3.2.0                                                                                                                                                                                                            |
| [#1005](https://github.com/prest/prest/pull/1005) | Bump `google.golang.org/grpc` 1.81.1 → 1.82.1                                                                                                                                                                                                                 |

Full detail: [compare v2.4.1...v2.4.2](https://github.com/prest/prest/compare/v2.4.1...v2.4.2). Coming from v2.4.0, see also [Changes since v2.4.0](/releases/main-since-v2.4.0).

***

## Upgrading from v2.4.1

1. **Measure `jwt.key` first.** `printf '%s' "$PREST_JWT_KEY" | wc -c` must be at least 32 for HS256, 48 for HS384, or 64 for HS512. Rotate the secret **before** deploying — a short key does not stop startup, it disables `/auth` (which then returns `404`) and passes requests through unauthenticated. After deploying, grep the startup logs for `jwt.key too short`.
2. **Leave `jwt.algo` unset unless you mean it.** A value outside the supported set — including wrong case — makes every request return `500`. If you set it, existing tokens must be signed with that exact algorithm or they now fail with `401`.
3. **Audit `/_QUERIES` templates for credential headers.** `{{index .header "Authorization"}}` and `{{sqlVal "header.Authorization"}}` now render empty.
4. **Rewrite interpolated free-form values as bound values** — `WHERE slug = '{{.slug}}'` becomes `WHERE slug = {{sqlVal "slug"}}`. Search phrases are the common case: a phrase containing `do`, `as`, or `or` is exactly what the screen refuses, and it now returns `400` rather than the wrong rows.
5. **Expect script SQL to disappear from logs.** If a runbook relied on reading generated statements from `prestd` output, switch to PostgreSQL statement logging.
6. Deploy `prest/prest:v2.4.2`, the matching binary, or `go install …@v2.4.2`.

Coming from v2.4.0, apply the [v2.4.1 upgrade notes](/releases/v2.4.1#upgrading-from-v240) as well — MCP discovery changes when `[expose]` is active.

***

## Related

* [Auth — HMAC key requirements](/api-reference/auth#hmac-key-requirements-v242)
* [Custom Queries — Binding values](/api-reference/custom-queries#binding-values-sqlval-sqllist-ident-v242)
* [Configuring pREST — JWT](/get-started/configuring-prest#jwt)
* [v2.4.1 release notes](/releases/v2.4.1) · [v2.4.0 release notes](/releases/v2.4.0)
* [Acronyms](/readme/acronyms) · [JWT](/readme/acronyms#jwt) · [SQL](/readme/acronyms#sql) · [MCP](/readme/acronyms#mcp)


# v2.4.1

pREST v2.4.1 — the MCP endpoint now honours \[expose], closing a catalog discovery bypass, plus a SQL-keyword screen on custom query script values.

Released: July 28, 2026

[GitHub tag](https://github.com/prest/prest/releases/tag/v2.4.1) · [Compare v2.4.0...v2.4.1](https://github.com/prest/prest/compare/v2.4.0...v2.4.1)

**v2.4.1** is a **security** release with a single commit ([#1016](https://github.com/prest/prest/pull/1016)). It makes the MCP endpoint honour the `[expose]` settings — previously `/_mcp` could enumerate the full catalog even when the REST listing routes were denied — and adds a SQL-keyword screen to values that custom query scripts interpolate into SQL.

* **Docker:** `prest/prest:v2.4.1`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.4.1`

{% hint style="warning" %}
The script-value screen shipped here was too broad and was **relaxed in** [**v2.4.2**](/releases/v2.4.2) ([#1030](https://github.com/prest/prest/issues/1030)). On v2.4.1, a single-word value containing a SQL keyword — `sao-joao-do-sul` splits into a token `do` — is silently blanked, and the request still returns `200` with the wrong rows. **Go straight to** [**v2.4.2**](/releases/v2.4.2) rather than stopping on v2.4.1.
{% endhint %}

No configuration keys were added or renamed, and no defaults changed.

***

## Highlights

### MCP honours `[expose]` ([#1016](https://github.com/prest/prest/pull/1016))

`/_mcp` was never routed through the exposure middleware, so an operator who disabled the `/databases`, `/schemas`, or `/tables` REST routes was still serving the entire catalog to any MCP client. The MCP handler now checks the same settings before answering.

When a listing is denied, the corresponding tool returns **HTTP 400** with a JSON-RPC error:

```json
{"jsonrpc":"2.0","id":1,"error":{"code":400,"message":"unauthorized listing"}}
```

The REST routes keep returning **401** with `{"error": "unauthorized listing"}` — same message, different status, because the two surfaces report errors differently.

Discovery (`GET /_mcp`, `tools/list`) is pruned rather than blocked:

| Tool                                         | Behaviour when its listing is denied                 |
| -------------------------------------------- | ---------------------------------------------------- |
| `prest.list_databases`                       | Not advertised; `tools/call` returns `400`           |
| `prest.list_schemas`                         | Not advertised; `tools/call` returns `400`           |
| `prest.list_tables`                          | Not advertised; `tools/call` returns `400`           |
| `prest.select.{database}.{schema}.{table}`   | **Not enumerated at all** if *any* listing is denied |
| `prest.describe_table`, `prest.select_table` | Always advertised                                    |

The per-table tools are withheld because their names embed database, schema, and table names and their descriptions embed column names — enumerating them made `GET /_mcp` a full catalog dump with no `tools/call` required.

`[expose]` governs **discovery**; `[access]` governs **reads**. A client that already knows a table name can still read it through `prest.select_table` subject to permissions. See [MCP over HTTP — Tools reference](/get-started/mcp-over-http#tools-reference) and [Configuring pREST — Expose Data](/get-started/configuring-prest#expose-data).

### Clarified `[expose]` semantics

The three listing flags only take effect while `enabled = true`. The check is "allowed unless exposure control is on *and* this listing is off", so hiding a listing takes **both** keys:

```toml
[expose]
enabled = true      # required — without this, the flags below do nothing
databases = false   # hide the databases listing
schemas = true
tables = true
```

This also means a deployment on the default `enabled = false` is unaffected by this release.

### SQL-keyword screen on script values ([#1016](https://github.com/prest/prest/pull/1016))

Values that a `/_QUERIES` script template interpolates become part of the SQL text. The pre-existing character allow-list was not enough: `0 UNION SELECT passwd FROM pg_shadow` needs no quote, comma, or parenthesis, so it passed through into an unquoted comparison such as `WHERE 1 = {{.field1}}`.

Values are now also rejected when they contain `--`, `::`, or a SQL keyword token (58 keywords, matched case-insensitively, with leading digits stripped so `0union` is caught as `union`).

{% hint style="danger" %}
On v2.4.1 this screen runs on **every** value and on **every** inbound header, and a blanked value does not fail the request — the script runs with an empty string substituted and returns `200`. Legitimate values are affected: `?status=null`, `?sort=order`, `?type=table`, and any slug containing `do`, `as`, or `or`. [v2.4.2](/releases/v2.4.2) restricts the screen to values containing a space and fails the request instead of silently substituting.
{% endhint %}

***

## Changes since v2.4.0

| PR                                                | Summary                                                                                                                                                               |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [#1016](https://github.com/prest/prest/pull/1016) | MCP endpoint honours `[expose]`; listing tools refused and per-table tools withheld when exposure control is active; SQL-keyword screen on custom query script values |

Full detail: [compare v2.4.0...v2.4.1](https://github.com/prest/prest/compare/v2.4.0...v2.4.1).

***

## Upgrading from v2.4.0

Upgrade to [v2.4.2](/releases/v2.4.2) instead — it contains this release plus the fix for the over-broad script screen. If you do deploy v2.4.1:

1. Deploy `prest/prest:v2.4.1`, the matching binary, or `go install …@v2.4.1`.
2. **On the default `[expose] enabled = false`, nothing changes.** MCP behaviour only shifts for deployments that actively restrict listings.
3. **If you restrict listings**, MCP clients relying on auto-discovered `prest.select.{database}.{schema}.{table}` tools must switch to the generic `prest.select_table` tool.
4. **Audit custom query scripts.** Any script parameter or header whose value contains a SQL keyword, `--`, or `::` is silently blanked. Rewrite templates to bind values instead of interpolating them — see [Custom Queries](/api-reference/custom-queries).

***

## Related

* [MCP over HTTP](/get-started/mcp-over-http)
* [Configuring pREST — Expose Data](/get-started/configuring-prest#expose-data)
* [Custom Queries](/api-reference/custom-queries)
* [v2.4.2 release notes](/releases/v2.4.2) · [v2.4.0 release notes](/releases/v2.4.0)
* [Acronyms](/readme/acronyms) · [MCP](/readme/acronyms#mcp) · [SQL](/readme/acronyms#sql)


# Changes since v2.4.0 (in v2.4.1/v2.4.2)

Changes since v2.4.0 included in v2.4.1 and v2.4.2 — MCP expose enforcement, HMAC key minimums, bound script values, and SQL redacted from logs.

**Released as** [**v2.4.1**](/releases/v2.4.1) **and** [**v2.4.2**](/releases/v2.4.2)**.** The commits below were merged after [v2.4.0](/releases/v2.4.0) and are included in those two releases. Compare: [v2.4.0...v2.4.2](https://github.com/prest/prest/compare/v2.4.0...v2.4.2).

Upgrade straight to **v2.4.2** — v2.4.1 shipped a script-value screen that was too broad and was relaxed one release later ([#1030](https://github.com/prest/prest/issues/1030)).

| Commit    | PR                                                | Tag    | Summary                                                                                           |
| --------- | ------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------- |
| `3cf83bc` | [#1016](https://github.com/prest/prest/pull/1016) | v2.4.1 | MCP endpoint honours `[expose]`; SQL-keyword screen on script values                              |
| `c354461` | [#1005](https://github.com/prest/prest/pull/1005) | v2.4.2 | Bump `google.golang.org/grpc` 1.81.1 → 1.82.1                                                     |
| `d25fdd5` | [#1015](https://github.com/prest/prest/pull/1015) | v2.4.2 | Bump `github.com/lestrrat-go/jwx/v3` 3.1.1 → 3.2.0                                                |
| `786d775` | [#1017](https://github.com/prest/prest/pull/1017) | v2.4.2 | HMAC `jwt.key` minimums, `go-jose/v4` migration, `jwt.algo` enforced                              |
| `9070bda` | [#1023](https://github.com/prest/prest/pull/1023) | v2.4.2 | Credential headers withheld from templates, SQL removed from logs, script path traversal rejected |

***

## #1016 — Expose configuration for the MCP endpoint

`/_mcp` bypassed the exposure middleware entirely, so a deployment that disabled the `/databases`, `/schemas`, or `/tables` REST routes still served its full catalog to any MCP client. The MCP handler now consults the same `[expose]` settings.

* `prest.list_databases`, `prest.list_schemas`, and `prest.list_tables` return **400** with JSON-RPC `{"code":400,"message":"unauthorized listing"}` when their listing is denied, and are dropped from `tools/list`.
* If **any** listing is denied, the per-table `prest.select.{database}.{schema}.{table}` tools are not enumerated at all — their names and descriptions disclose table and column names.
* `prest.describe_table` and `prest.select_table` are always advertised; `[expose]` controls discovery, `[access]` controls reads.
* The listing flags only apply while `expose.enabled = true`. Hiding a listing requires `enabled = true` **and** the specific flag set to `false`.

The same PR added a SQL-keyword screen to values interpolated by `/_QUERIES` scripts, closing an unauthenticated injection that needed no quote or parenthesis (`0 UNION SELECT ... FROM ...`). It proved too broad and was relaxed in [#1023](https://github.com/prest/prest/pull/1023) — see below.

Details: [v2.4.1 release notes](/releases/v2.4.1).

***

## #1017 — JWT key handling and dependencies

Migrated from the unmaintained `square/go-jose.v2` to `go-jose/go-jose/v4`, which enforces RFC 7518 HMAC key sizes. pREST now validates the key at config load rather than failing at request time:

| `jwt.algo`                           | Minimum `jwt.key` |
| ------------------------------------ | ----------------- |
| `HS256` (and the default when unset) | 32 bytes          |
| `HS384`                              | 48 bytes          |
| `HS512`                              | 64 bytes          |

An undersized key is discarded, `/auth` is unregistered, and the JWT middleware is removed — **pREST keeps serving, unauthenticated**. Rotate short secrets before upgrading.

`jwt.algo` is also enforced for the first time: it is passed to the parser as the sole permitted signature algorithm, so tokens signed with a different `alg` now return **401**, and a value outside the supported set (including wrong case) returns **500** on every request.

Details: [v2.4.2 release notes](/releases/v2.4.2).

***

## #1023 — SQL logging, script hardening, and CI permissions

* **Credential headers withheld from templates** — `Authorization`, `Proxy-Authorization`, `Cookie`, `X-Api-Key`, `X-Auth-Token`, `X-Access-Token` render empty in `/_QUERIES` scripts, and cannot be recovered through `sqlVal`.
* **Rejected interpolated values fail with 400** instead of substituting an empty string, with a message pointing at the `sqlVal` helper. The value itself is never echoed back.
* **SQL removed from logs** — script statements are no longer logged on the read or write path; CRUD statements keep `sql` at `debug` but replace parameter values with `parameter_count`.
* **Script path traversal rejected** — `..` segments and symlinks escaping the queries directory return **400** `invalid script path`, checked both lexically and after symlink resolution.
* **Error bodies are JSON-escaped**, so messages containing a quote no longer produce unparseable JSON.
* **CI only:** explicit `permissions:` blocks on the build, lint, and duplicate-issue workflows. No runtime effect.

### Script value screen relaxed ([#1030](https://github.com/prest/prest/issues/1030))

The v2.4.1 screen ran on every value, so a single-word value containing a SQL keyword token — `sao-joao-do-sul` splits on `-` and yields `do` — was silently blanked and the request returned `200` with the wrong rows. The screen now applies only to values containing a space; the character allow-list and the `--` / `::` rejection still apply to everything.

Details: [v2.4.2 release notes](/releases/v2.4.2).

***

## #1005, #1015 — Dependency bumps

`google.golang.org/grpc` 1.81.1 → 1.82.1 (indirect, used by the OTLP exporters) and `github.com/lestrrat-go/jwx/v3` 3.1.1 → 3.2.0 (JWKS parsing). Both touch only `go.mod` and `go.sum`.

***

## Related

* [v2.4.2 release notes](/releases/v2.4.2)
* [v2.4.1 release notes](/releases/v2.4.1)
* [v2.4.0 release notes](/releases/v2.4.0)
* [Releases](/releases)


# v2.4.0

pREST v2.4.0 — opt-in OpenTelemetry instrumentation with a local SigNoz dev stack, pgvector nearest-neighbor ordering and distance filtering, and a pREST Studio dependency upgrade.

Released: July 26, 2026

[GitHub tag](https://github.com/prest/prest/releases/tag/v2.4.0) · [Compare v2.3.0...v2.4.0](https://github.com/prest/prest/compare/v2.3.0...v2.4.0)

**v2.4.0** is an **observability and vector search** release. It adds opt-in OpenTelemetry instrumentation with a local SigNoz dev stack ([#1003](https://github.com/prest/prest/pull/1003)), pgvector nearest-neighbor ordering (`_korder`) and distance-threshold filtering (`:vecdist`) ([#1011](https://github.com/prest/prest/pull/1011)), and a routine pREST Studio dependency upgrade ([#1004](https://github.com/prest/prest/pull/1004)).

* **Docker:** `prest/prest:v2.4.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.4.0`

{% hint style="info" %}
No breaking changes and no config migrations. Everything new in this release is **opt-in**: telemetry defaults to off, and `_korder` / `:vecdist` only apply where a client sends them against a `vector` column.
{% endhint %}

See [Changes since v2.3.0](#changes-since-v230) for the commit list.

***

## Highlights

### OpenTelemetry instrumentation + SigNoz dev stack ([#1003](https://github.com/prest/prest/pull/1003))

Opt-in OTLP/gRPC push telemetry: HTTP, Postgres, and MCP traces; `http.server.*` / `db.client.operation.duration` / `db.sql.connection.*` metrics; and a `slog` → OTel logs bridge that still writes to stdout. No new HTTP route is added — there is no `/metrics` scrape endpoint, so the attack surface is unchanged.

New `[otel]` config section (`PREST_OTEL_*` env overrides):

| Key                | Default   | Purpose                                                            |
| ------------------ | --------- | ------------------------------------------------------------------ |
| `enabled`          | `false`   | Master switch — zero overhead and no outbound connections when off |
| `service_name`     | `prestd`  | Resource `service.name`                                            |
| `endpoint`         | *(unset)* | OTLP gRPC collector `host:port`                                    |
| `protocol`         | `grpc`    | Only `grpc` for now                                                |
| `sample_ratio`     | `1.0`     | Trace head sampling, clamped `0.0–1.0`                             |
| `metrics_interval` | `15s`     | Metric export period                                               |
| `insecure`         | `false`   | Disable TLS to collector (local/dev)                               |
| `db_statement`     | `false`   | Record SQL text on DB spans (trusted envs only)                    |

Setup fails closed: if the exporter can't be built at startup, pREST logs a warning and keeps serving with telemetry disabled. Graceful shutdown (`SIGINT`/`SIGTERM` draining via `signal.NotifyContext`) ships alongside it so telemetry flushes on exit. A self-contained SigNoz stack lives under `dev/signoz/` (`make signoz-up` / `make signoz-down`) for local viewing. See [Configuring pREST — Observability](/get-started/configuring-prest#observability-opentelemetry-v240).

### pgvector KNN ordering and distance filtering ([#1011](https://github.com/prest/prest/pull/1011))

Two new query-parameter forms for `vector`-typed columns (requires the `pgvector` extension), both restricted to a fixed metric whitelist (`l2`/`euclidean`, `cosine`/`cos`, `ip`/`inner`/`dot`, `l1`/`manhattan`) and validated end-to-end:

| Parameter          | Form                                         | Example                               | Effect                                                                                          |
| ------------------ | -------------------------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `_korder`          | `<column>:<metric>:<vector>`                 | `_korder=embedding:l2:[1,0,0]`        | Orders by nearest-neighbor distance (KNN); composes with `_order` as an additional sort term    |
| `<column>:vecdist` | `<metric>:<comparison>:<vector>:<threshold>` | `embedding:vecdist=l2:lt:[1,0,0]:0.5` | Filters rows by distance threshold; comparison is restricted to `=`, `!=`, `<`, `<=`, `>`, `>=` |

Malformed metrics, non-numeric vector elements, oversized vectors (>16000 dims, pgvector's own limit), non-scalar comparisons (e.g. `like`), and dimension mismatches all return `400` rather than reaching the database unsafely. See [Parameters — pgvector KNN ordering and distance filtering](/api-reference/parameters#pgvector-knn-ordering-and-distance-filtering-v240).

### pREST Studio dependency upgrade ([#1004](https://github.com/prest/prest/pull/1004))

Routine dependency bump for the embedded Studio UI. Fixes the auth dialog not reflecting saved credentials / "remember me" state on reopen, and avoids building incomplete MCP tool-call requests when argument construction fails. No config or API changes.

***

## Changes since v2.3.0

| PR                                                | Summary                                                                                                             |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [#1003](https://github.com/prest/prest/pull/1003) | Opt-in OpenTelemetry (push) instrumentation — traces, metrics, slog→OTel log bridge — plus a local SigNoz dev stack |
| [#1004](https://github.com/prest/prest/pull/1004) | pREST Studio dependency upgrade; auth-dialog and tool-invocation fixes                                              |
| [#1011](https://github.com/prest/prest/pull/1011) | pgvector nearest-neighbor ordering (`_korder`) and distance-threshold filtering (`:vecdist`)                        |

Full detail: [compare v2.3.0...v2.4.0](https://github.com/prest/prest/compare/v2.3.0...v2.4.0).

***

## Upgrading from v2.3.0

No configuration changes are required; this is a drop-in update.

1. Deploy `prest/prest:v2.4.0`, the matching binary, or `go install …@v2.4.0`.
2. **Telemetry stays off by default.** Set `otel.enabled = true` (or `PREST_OTEL_ENABLED=true`) and `otel.endpoint` only when you have an OTLP/gRPC collector to send to — see [Configuring pREST — Observability](/get-started/configuring-prest#observability-opentelemetry-v240).
3. **pgvector columns** gain `_korder` and `:vecdist` automatically; both require the `pgvector` extension on the target column and are no-ops otherwise.
4. MCP at `/_mcp`, Studio at `/_studio/`, and the multi-adapter registry are unchanged from v2.3.0.

***

## Related

* [Configuring pREST — Observability](/get-started/configuring-prest#observability-opentelemetry-v240)
* [Parameters — pgvector KNN ordering and distance filtering](/api-reference/parameters#pgvector-knn-ordering-and-distance-filtering-v240)
* [pREST Studio](/get-started/prest-studio)
* [v2.3.0 release notes](/releases/v2.3.0)
* [Acronyms](/readme/acronyms) · [MCP](/readme/acronyms#mcp) · [REST](/readme/acronyms#rest)


# Changes since v2.3.0 (in v2.4.0)

Changes since v2.3.0 included in v2.4.0 — OpenTelemetry instrumentation, pREST Studio dependency bump, and pgvector nearest-neighbor / distance-filter support.

**Released as** [**v2.4.0**](/releases/v2.4.0)**.** The commits below were merged after [v2.3.0](/releases/v2.3.0) and are included in the v2.4.0 release. Compare: [v2.3.0...v2.4.0](https://github.com/prest/prest/compare/v2.3.0...v2.4.0).

| Commit    | PR                                                | Summary                                                                                                             |
| --------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `496f893` | [#1003](https://github.com/prest/prest/pull/1003) | Opt-in OpenTelemetry (push) instrumentation — traces, metrics, slog→OTel log bridge — plus a local SigNoz dev stack |
| `e8dca3b` | [#1004](https://github.com/prest/prest/pull/1004) | pREST Studio dependency upgrade; auth-dialog and tool-invocation fixes                                              |
| `e0cd7bd` | [#1011](https://github.com/prest/prest/pull/1011) | pgvector nearest-neighbor ordering (`_korder`) and distance-threshold filtering (`:vecdist`)                        |

***

## #1003 — OpenTelemetry instrumentation + SigNoz dev stack

Adds **opt-in** OTLP/gRPC push telemetry: HTTP, Postgres, and MCP traces; `http.server.*` / `db.client.operation.duration` / `db.sql.connection.*` metrics; and a `slog` → OTel logs bridge that still writes to stdout. No new HTTP route is added (no `/metrics` scrape endpoint), so the attack surface is unchanged.

New `[otel]` config section (`PREST_OTEL_*` env overrides):

| Key                | Default   | Purpose                                                            |
| ------------------ | --------- | ------------------------------------------------------------------ |
| `enabled`          | `false`   | Master switch — zero overhead and no outbound connections when off |
| `service_name`     | `prestd`  | Resource `service.name`                                            |
| `endpoint`         | *(unset)* | OTLP gRPC collector `host:port`                                    |
| `protocol`         | `grpc`    | Only `grpc` for now                                                |
| `sample_ratio`     | `1.0`     | Trace head sampling, clamped `0.0–1.0`                             |
| `metrics_interval` | `15s`     | Metric export period                                               |
| `insecure`         | `false`   | Disable TLS to collector (local/dev)                               |
| `db_statement`     | `false`   | Record SQL text on DB spans (trusted envs only)                    |

Setup fails closed: if the exporter can't be built at startup, pREST logs a warning and keeps serving with telemetry disabled. The PR also adds graceful shutdown (`SIGINT`/`SIGTERM` draining via `signal.NotifyContext`) as a prerequisite for flushing telemetry on exit. A self-contained SigNoz stack lives under `dev/signoz/` (`make signoz-up` / `make signoz-down`) for local viewing.

## #1004 — pREST Studio dependency upgrade

Routine dependency bump for the embedded Studio UI. Fixes the auth dialog not reflecting saved credentials / "remember me" state on reopen, and avoids building incomplete MCP tool-call requests when argument construction fails. No config or API changes.

## #1011 — pgvector KNN ordering and distance filtering

Adds two query-parameter forms for `vector`-typed columns (requires the `pgvector` extension), both restricted to a fixed metric whitelist (`l2`/`euclidean`, `cosine`/`cos`, `ip`/`inner`/`dot`, `l1`/`manhattan`) and validated end-to-end — column via identifier validation, vector literal by round-tripping through `ParseFloat`/`FormatFloat`, threshold as a bound parameter:

| Parameter          | Form                                         | Example                               | Effect                                                                                          |
| ------------------ | -------------------------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `_korder`          | `<column>:<metric>:<vector>`                 | `_korder=embedding:l2:[1,0,0]`        | Orders by nearest-neighbor distance (KNN); composes with `_order` as an additional sort term    |
| `<column>:vecdist` | `<metric>:<comparison>:<vector>:<threshold>` | `embedding:vecdist=l2:lt:[1,0,0]:0.5` | Filters rows by distance threshold; comparison is restricted to `=`, `!=`, `<`, `<=`, `>`, `>=` |

Malformed metrics, non-numeric vector elements, oversized vectors (>16000 dims, pgvector's own limit), non-scalar comparisons (e.g. `like`), and dimension mismatches all return `400` rather than reaching the database unsafely.

***

## Related

* [v2.4.0 release notes](/releases/v2.4.0)
* [v2.3.0 release notes](/releases/v2.3.0)
* [Releases](/releases)


# v2.3.0

pREST v2.3.0 — critical \_select SQL-injection fix (GHSA-qvx3-q8vx-9q3c), multi-adapter architecture with Postgres/TimescaleDB auto-detect, and JWKS hardening.

Released: July 22, 2026

[GitHub tag](https://github.com/prest/prest/releases/tag/v2.3.0) · [Compare v2.2.0...v2.3.0](https://github.com/prest/prest/compare/v2.2.0...v2.3.0)

**v2.3.0** is a **security and architecture** release. It fixes an unauthenticated SQL-injection in the `_select` projection ([GHSA-qvx3-q8vx-9q3c](https://github.com/prest/prest/security/advisories/GHSA-qvx3-q8vx-9q3c), [#1002](https://github.com/prest/prest/pull/1002)) and lands the multi-adapter architecture — an adapter registry with automatic PostgreSQL / TimescaleDB detection and per-database URL routing ([#999](https://github.com/prest/prest/pull/999)).

* **Docker:** `prest/prest:v2.3.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.3.0`

{% hint style="warning" %}
**Security:** upgrade from v2.2.0 (and earlier v2.x) as soon as possible. The `_select` fix addresses a **CVSS 9.8** unauthenticated injection. See [Security](#security-_select-sql-injection-fix-1002) below.
{% endhint %}

{% hint style="info" %}
The versioned Docker tag (`prest/prest:v2.3.0`) and the GitHub release page are still publishing. In the meantime, `prest/prest:latest` (and `latest-noplugins`) carry the v2.3.0 code.
{% endhint %}

See [Changes since v2.2.0](#changes-since-v220) for the commit list.

***

## Highlights

### Security: `_select` SQL-injection fix ([#1002](https://github.com/prest/prest/pull/1002))

An unauthenticated SQL-injection in the `_select` projection was fixed ([GHSA-qvx3-q8vx-9q3c](https://github.com/prest/prest/security/advisories/GHSA-qvx3-q8vx-9q3c), CVSS 9.8) — a follow-up to CVE-2025-58450. Both the field-projection and `_count` paths now pass through a single validation gate.

**Behavior change.** `_select` (and `_count`) values now accept only:

| Form                           | Example                    |
| ------------------------------ | -------------------------- |
| Wildcard                       | `*`                        |
| Identifier (optionally dotted) | `id`, `public.users.name`  |
| Colon-syntax aggregate         | `sum:salary`, `avg:rating` |
| Pre-quoted aggregate           | `SUM("salary") AS "total"` |

Aggregates are limited to `SUM`, `AVG`, `MAX`, `MIN`, `STDDEV`, `VARIANCE`. Anything else — subselects, `pg_*` probing, extra parentheses — returns **`400`** **`ErrInvalidIdentifier`**. `_count` field names are now quoted in the generated SQL. See [Parameters — `_select` validation](/api-reference/parameters#_select-field-validation-v230).

### Multi-adapter architecture ([#999](https://github.com/prest/prest/pull/999))

pREST can now register and serve **multiple databases through different adapters** in one process:

| Capability          | Detail                                                                                                                                   |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Adapter registry    | Databases map to an adapter by alias                                                                                                     |
| Automatic detection | On connect, pREST tries TimescaleDB first, falls back to PostgreSQL — in single-DB and per-alias multi-DB mode. No new config            |
| URL routing         | The `{database}` path segment selects the adapter for the request                                                                        |
| Timescale isolation | `_time_bucket` / `time_bucket` and `_timescaledb_*` schema filtering live in the Timescale adapter; the base Postgres adapter is a no-op |

This uses the existing multi-database registry (`[[databases]]` / `DATABASE_ALIAS_N` / `DATABASE_URL_N`) that shipped in [v2.2.0](/releases/v2.2.0) — no new env vars. Guide: [Multi-database](/get-started/multi-database) · [TimescaleDB](/databases/timescaledb).

### JWKS / JWT hardening ([#1002](https://github.com/prest/prest/pull/1002))

JWKS handling migrated to `jwx/v3`. JWKS fetch now **rejects non-2xx responses**, **caps the response body at 1 MiB**, and **redacts the URL** (drops userinfo, query, and fragment) in logs. Key-matching semantics are unchanged, and there are **no config or environment changes** — `jwt.jwks` / `jwt.wellknownurl` still work as before. See [Auth — JWKS fetch hardening](/api-reference/auth#jwks-fetch-hardening-v230).

***

## Changes since v2.2.0

| PR                                                | Summary                                                                                                                                                                                                                         |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [#1002](https://github.com/prest/prest/pull/1002) | Fix unauthenticated `_select` SQL-injection ([GHSA-qvx3-q8vx-9q3c](https://github.com/prest/prest/security/advisories/GHSA-qvx3-q8vx-9q3c)); migrate JWKS to `jwx/v3` with non-2xx rejection, 1 MiB body cap, and URL redaction |
| [#999](https://github.com/prest/prest/pull/999)   | Multi-adapter registry, automatic Postgres/TimescaleDB detection, adapter-selector routing, Timescale feature isolation                                                                                                         |

Full detail: [compare v2.2.0...v2.3.0](https://github.com/prest/prest/compare/v2.2.0...v2.3.0).

***

## Upgrading from v2.2.0

No configuration changes are required; this is a drop-in update.

1. Deploy `prest/prest:v2.3.0` (or `latest` while the versioned tag publishes), the matching binary, or `go install …@v2.3.0`.
2. **Review `_select` usage.** If clients send non-standard projections (raw SQL functions, quoted expressions outside the aggregate whitelist), they now return `400`. Move complex projections to [custom queries](/api-reference/custom-queries).
3. TimescaleDB aliases are now **auto-detected** and gain `_time_bucket` without config changes — see [Multi-database](/get-started/multi-database#timescale-operators-v230).
4. MCP at `/_mcp` and Studio at `/_studio/` are unchanged from v2.2.0.

***

## Related

* [Parameters — `_select` validation](/api-reference/parameters#_select-field-validation-v230)
* [Auth — JWKS fetch hardening](/api-reference/auth#jwks-fetch-hardening-v230)
* [Multi-database](/get-started/multi-database)
* [TimescaleDB](/databases/timescaledb)
* [Configuring pREST](/get-started/configuring-prest)
* [v2.2.0 release notes](/releases/v2.2.0)
* [Acronyms](/readme/acronyms) · [MCP](/readme/acronyms#mcp) · [REST](/readme/acronyms#rest)


# v2.2.0

pREST v2.2.0 — embedded Studio UI at /\_studio/, database-backed custom queries, TimescaleDB E2E certification, and fully documented sample TOML.

Released: July 18, 2026

[GitHub tag](https://github.com/prest/prest/releases/tag/v2.2.0) · [Download binaries](https://github.com/prest/prest/releases/tag/v2.2.0)

**v2.2.0** adds **pREST Studio** — an embedded admin and explorer UI at `/_studio/` ([#990](https://github.com/prest/prest/pull/990)) — plus database-backed custom query storage ([#980](https://github.com/prest/prest/pull/980)), TimescaleDB E2E certification on the native PostgreSQL adapter ([#988](https://github.com/prest/prest/pull/988)), and a fully documented sample configuration ([#978](https://github.com/prest/prest/pull/978)).

* **Binary:** [v2.2.0 release assets](https://github.com/prest/prest/releases/tag/v2.2.0)
* **Docker:** `prest/prest:v2.2.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.2.0`

Upgrading from [v2.1.0](/releases/v2.1.0) is a drop-in update. Studio is **enabled by default**; set `PREST_STUDIO_ENABLED=false` to turn it off. Custom query storage remains `filesystem` unless you opt into `queries.storage = "database"`.

See [Changes since v2.1.0](/releases/main-since-v2.1.0) for the full commit list.

***

## Highlights

### pREST Studio ([#990](https://github.com/prest/prest/pull/990))

Self-hosted UI embedded in the `prestd` binary and served at `/_studio/`. Same-origin client of the existing REST and MCP APIs — no separate frontend process at runtime.

| Area          | What you get                                              |
| ------------- | --------------------------------------------------------- |
| Overview      | Health/readiness, build metadata, REST/MCP availability   |
| Data Explorer | Browse catalog, preview rows (read-only), copy URL/`curl` |
| REST Explorer | Build GET requests, inspect responses                     |
| MCP Explorer  | List/invoke `/_mcp` tools with generated forms            |

Disable with `PREST_STUDIO_ENABLED=false` or `[studio] enabled = false`. Auth and ACL match the rest of the HTTP stack. Guide: [pREST Studio](/get-started/prest-studio).

### Database-backed custom queries ([#980](https://github.com/prest/prest/pull/980))

Optional `queries.storage = "database"` with `prest_queries` table, `/_QUERIES/registry` admin API, query ACL, and `prestd migrate up|down queries`. Default remains filesystem. See [Custom Queries](/api-reference/custom-queries#database-backed-storage-v220).

### TimescaleDB E2E ([#988](https://github.com/prest/prest/pull/988))

First-class integration tests for TimescaleDB through the native PostgreSQL adapter (still **Compatible with caveats**). See [TimescaleDB](/databases/timescaledb).

### Sample configuration ([#978](https://github.com/prest/prest/pull/978))

[`samples/prest.sample.toml`](https://github.com/prest/prest/blob/v2.2.0/samples/prest.sample.toml) documents every accepted key, including `[studio]` and `[queries]`.

***

## Changes since v2.1.0

| Commit    | PR                                              | Summary                                             |
| --------- | ----------------------------------------------- | --------------------------------------------------- |
| `441dc3d` | [#990](https://github.com/prest/prest/pull/990) | pREST Studio embedded UI at `/_studio/`             |
| `8313b87` | [#980](https://github.com/prest/prest/pull/980) | Database-backed custom query storage, registry, ACL |
| `35eeedd` | [#988](https://github.com/prest/prest/pull/988) | TimescaleDB E2E on the native PostgreSQL adapter    |
| `5fb0290` | [#978](https://github.com/prest/prest/pull/978) | Fully documented sample configuration               |
| `e8cdcac` | [#979](https://github.com/prest/prest/pull/979) | Postgres / Timescale test images toward Postgres 18 |
| `e158f89` | [#998](https://github.com/prest/prest/pull/998) | Dockerfile / GoReleaser updates for Studio embed    |

Also: coverage reporting for fork PRs ([#984](https://github.com/prest/prest/pull/984)), README updates ([#986](https://github.com/prest/prest/pull/986)).

Full detail: [Changes since v2.1.0](/releases/main-since-v2.1.0).

***

## Upgrading from v2.1.0

No breaking changes for existing deployments.

1. Deploy `prest/prest:v2.2.0` (or the matching binary / `go install …@v2.2.0`).
2. Open `https://your-prest-host/_studio/` if you want the UI — or disable Studio if you prefer not to expose it.
3. Optionally enable database-backed queries — see [Custom Queries](/api-reference/custom-queries#database-backed-storage-v220).
4. MCP at `/_mcp` is unchanged from v2.1.0.

***

## Related

* [pREST Studio](/get-started/prest-studio)
* [Custom Queries](/api-reference/custom-queries)
* [TimescaleDB](/databases/timescaledb)
* [Configuring pREST](/get-started/configuring-prest)
* [Changes since v2.1.0](/releases/main-since-v2.1.0)
* [v2.1.0 release notes](/releases/v2.1.0)
* [Acronyms](/readme/acronyms) · [MCP](/readme/acronyms#mcp) · [REST](/readme/acronyms#rest)


# Changes since v2.1.0 (in v2.2.0)

Changes since v2.1.0 included in v2.2.0 — Studio, database-backed queries, TimescaleDB E2E, sample TOML, and related packaging work.

**Released as** [**v2.2.0**](/releases/v2.2.0)**.** The commits below were merged after [v2.1.0](/releases/v2.1.0) and are included in the v2.2.0 release. Compare: [v2.1.0...v2.2.0](https://github.com/prest/prest/compare/v2.1.0...v2.2.0).

| Commit    | PR                                              | Summary                                                                                                              |
| --------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `441dc3d` | [#990](https://github.com/prest/prest/pull/990) | pREST Studio embedded UI at `/_studio/`                                                                              |
| `8313b87` | [#980](https://github.com/prest/prest/pull/980) | Database-backed custom query storage, `/_QUERIES/registry`, query ACL                                                |
| `35eeedd` | [#988](https://github.com/prest/prest/pull/988) | TimescaleDB E2E certification on the native PostgreSQL adapter                                                       |
| `5fb0290` | [#978](https://github.com/prest/prest/pull/978) | Fully documented [`samples/prest.sample.toml`](https://github.com/prest/prest/blob/v2.2.0/samples/prest.sample.toml) |
| `e8cdcac` | [#979](https://github.com/prest/prest/pull/979) | Postgres / Timescale test images bumped toward Postgres 18                                                           |
| `e158f89` | [#998](https://github.com/prest/prest/pull/998) | Dockerfile / GoReleaser configuration for Studio embed                                                               |

Also on this range: coverage reporting for fork PRs ([#984](https://github.com/prest/prest/pull/984)), README/docs updates ([#986](https://github.com/prest/prest/pull/986)), and a small CI fixup ([#989](https://github.com/prest/prest/pull/989)).

***

## #990 — pREST Studio

Embedded admin and explorer UI at `/_studio/` (enabled by default). Same-origin REST and MCP client; disable with `PREST_STUDIO_ENABLED=false`. See [pREST Studio](/get-started/prest-studio) and [v2.2.0](/releases/v2.2.0).

***

## #980 — Database-backed custom queries

Adds `queries.storage = "database"` as an alternative to filesystem `.sql` scripts (default remains `filesystem`).

User-facing surface:

* Table `prest_queries` (configurable `schema` / `table`)
* Admin API `/_QUERIES/registry` when `register_enabled = true`
* Query ACL: `queries.restrict`, `[[queries.scripts]]`, `[[queries.users]]`
* Startup migrate/import from filesystem; CLI `prestd migrate up|down queries`

See [Custom Queries](/api-reference/custom-queries#database-backed-storage-v220).

***

## #988 — TimescaleDB E2E

Certifies TimescaleDB through the existing PostgreSQL adapter (no separate adapter). Adds `make test-integration-timescaledb`, Compose under `integration/timescaledb/`, and hypertable / catalog caveats.

Still labeled **Compatible with caveats** in docs. See [TimescaleDB](/databases/timescaledb) and upstream [DIFFERENCES.md](https://github.com/prest/prest/blob/v2.2.0/integration/timescaledb/DIFFERENCES.md).

***

## #978 — Sample configuration

[`samples/prest.sample.toml`](https://github.com/prest/prest/blob/v2.2.0/samples/prest.sample.toml) documents every accepted key (including `[https]` vs `[pg.ssl]`, `[studio]`, and `[queries]`). Prefer it as the config reference alongside [Configuring pREST](/get-started/configuring-prest).

## Related

* [v2.2.0 release notes](/releases/v2.2.0)
* [v2.1.0 release notes](/releases/v2.1.0)
* [Releases](/releases)
* [pREST Studio](/get-started/prest-studio)
* [Custom Queries](/api-reference/custom-queries)
* [TimescaleDB](/databases/timescaledb)


# v2.1.0

Released: July 9, 2026

[GitHub tag](https://github.com/prest/prest/releases/tag/v2.1.0) · [Download binaries](https://github.com/prest/prest/releases/tag/v2.1.0)

**v2.1.0** adds native [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) support over HTTP ([#977](https://github.com/prest/prest/pull/977)), closing [#959](https://github.com/prest/prest/issues/959). MCP is an open standard for connecting AI apps and agents to tools and data; pREST exposes a read-only endpoint at `/_mcp` so clients can discover and query your SQL catalog (PostgreSQL family today) through the same server that already serves CRUD, custom scripts, and multi-database routes.

* **Binary:** [v2.1.0 release assets](https://github.com/prest/prest/releases/tag/v2.1.0)
* **Docker:** `prest/prest:v2.1.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.1.0`

Upgrading from [v2.0.0](/releases/v2.0.0) is a drop-in update — no configuration changes are required unless you want to use the new endpoint.

See the [MCP over HTTP guide](/get-started/mcp-over-http) for full usage, tool reference, and examples. For AI clients (Cursor, Claude Desktop), start at [AI and MCP](/ai) and install the [pREST MCP Adapter](/ai/install-prest-mcp) via [`brew install prest/tap/prest-mcp`](/get-prest/start-with-homebrew).

***

## Highlights

### MCP over HTTP ([#977](https://github.com/prest/prest/pull/977))

**MCP (Model Context Protocol)** connects AI clients to tools over a standard protocol. pREST exposes a read-only MCP endpoint at `/_mcp` on the same server process. The MCP surface reuses the existing request pipeline — deployment model, auth, ACL, and database routing behave the same as the rest of the API.

| Method | Endpoint | Purpose                                                    |
| ------ | -------- | ---------------------------------------------------------- |
| `GET`  | `/_mcp`  | Discovery payload with server metadata and available tools |
| `POST` | `/_mcp`  | JSON-RPC requests for MCP operations                       |

Supported JSON-RPC methods: `initialize`, `tools/list`, `tools/call`.

Read-only tools include generic discovery (`prest.list_databases`, `prest.list_schemas`, `prest.list_tables`, `prest.describe_table`, `prest.select_table`) and schema-aware per-table select tools (`prest.select.{database}.{schema}.{table}`) generated from catalog metadata with typed input schemas.

Key properties:

* Read-only by design in v2.1.0
* Auth and ACL inherited from the HTTP stack
* Permission-aware tool discovery and execution
* Multi-database alias support in tool names and arguments
* Maximum 100 rows per select

***

## Changes since v2.0.0

| Commit    | PR                                              | Summary                                                                                   |
| --------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `f5e5e5e` | [#977](https://github.com/prest/prest/pull/977) | Native MCP support over HTTP — `/_mcp` endpoint, schema-aware tools, auth/ACL integration |

***

## Upgrading from v2.0.0

No breaking changes. Deploy the new binary or Docker image and optionally point MCP clients at `https://your-prest-host/_mcp`.

If you use authentication, ensure MCP clients send the same credentials your REST clients use — `/_mcp` is protected when auth is enabled.

***

## Documentation links

* [AI and MCP](/ai)
* [MCP over HTTP guide](/get-started/mcp-over-http)
* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [Use with Cursor](/ai/cursor) · [Claude Desktop](/ai/claude-desktop)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)
* [Start with Homebrew](/get-prest/start-with-homebrew) · [Distribution](/get-prest/distribution)
* [v2.0.0 release notes](/releases/v2.0.0)
* [Multi-database guide](/get-started/multi-database)
* [Permissions](/get-started/permissions)
* [Auth](/api-reference/auth)


# v2.0.0

Released: July 7, 2026

[GitHub tag](https://github.com/prest/prest/releases/tag/v2.0.0) · [Download binaries](https://github.com/prest/prest/releases/tag/v2.0.0)

**v2.0.0** is the first stable v2 release. It includes all v2 release-candidate work (rc1–rc6) plus post-rc6 improvements ([#972](https://github.com/prest/prest/pull/972), [#974](https://github.com/prest/prest/pull/974), [#973](https://github.com/prest/prest/pull/973)).

* **Binary:** [v2.0.0 release assets](https://github.com/prest/prest/releases/tag/v2.0.0)
* **Docker:** `prest/prest:v2.0.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.0.0`

See the [v2 RC changelog](/releases) for the full rc1–rc6 history and [Changes since rc6](/releases/main-since-rc6) for detailed notes on #972–#973.

***

## Highlights

### Multi-database ([#973](https://github.com/prest/prest/pull/973))

Route CRUD and custom queries by database alias:

```http
GET /tenant-a/public/users
GET /_QUERIES/tenant-a/myfolder/my_query?field1=foo
```

Configure via `[[databases]]` in TOML or `DATABASE_ALIAS_N` / `DATABASE_URL_N` environment pairs. See the [Multi-database guide](/get-started/multi-database).

### Readiness endpoint ([#973](https://github.com/prest/prest/pull/973))

| Endpoint       | Purpose   | Behavior                                              |
| -------------- | --------- | ----------------------------------------------------- |
| `GET /_health` | Liveness  | Pings the default database                            |
| `GET /_ready`  | Readiness | Pings the default database and every registered alias |

Use `/_ready` for Kubernetes readiness probes.

### Configuration resilience ([#974](https://github.com/prest/prest/pull/974))

Startup is never blocked by bad configuration. Invalid or missing settings produce warnings and safe fallbacks instead of fatal exits.

### JWT auto-disable ([#974](https://github.com/prest/prest/pull/974))

When `jwt.default = true` but no verification material is configured (`jwt.key`, `jwt.jwks`, or `jwt.wellknownurl`), JWT middleware is **auto-disabled** with an error log — the server continues to start. When `auth.enabled = true` without `jwt.key`, auth is also auto-disabled. This prevents authentication bypass via an empty HMAC key ([GHSA-fj7v-859r-2fm4](https://github.com/prest/prest/security/advisories/GHSA-fj7v-859r-2fm4)).

> **v2.0.0-rc6 binary:** the [rc6 release](/releases/v2.0.0-rc6) **refused to start** in the same situations. See the rc6 page for that historical behavior.

### OR clause filtering ([#958](https://github.com/prest/prest/pull/958))

Combine filter conditions with OR logic using `_or`, without writing custom SQL:

```http
GET /db/public/articles?_or=title=$ilike.%search%||name=$ilike.%search%
```

See [Parameters](/api-reference/parameters#or-clause-filtering).

### Per-user permissions ([#912](https://github.com/prest/prest/pull/912))

Fine-grained access control via `[[access.users]]` with per-user table permissions. See [Permissions](/get-started/permissions).

### Structured logging ([#950](https://github.com/prest/prest/pull/950), [#972](https://github.com/prest/prest/pull/972))

Structured JSON logging via Go `slog` is configured on every startup. Set `PREST_LOG_LEVEL` to `debug`, `info`, `warn`, or `error`.

### Security hardening (rc3–rc6)

* `_returning` parameter hardened ([#935](https://github.com/prest/prest/pull/935))
* Unified identifier validation ([#938](https://github.com/prest/prest/pull/938), [GHSA-p46v-f2x8-qp98](https://github.com/prest/prest/security/advisories/GHSA-p46v-f2x8-qp98))
* `tsquery` operator hardened ([#940](https://github.com/prest/prest/pull/940))
* Custom query template parameters sanitized ([#972](https://github.com/prest/prest/pull/972))
* Database credentials redacted in error logs via `logsafe` ([#972](https://github.com/prest/prest/pull/972))

***

## Breaking changes and migration notes

| Change                 | v2.0.0 behavior                                                                                           |
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
| `jwt.default` default  | **`false`** — JWT is off unless explicitly enabled (rc6 implied `true`)                                   |
| JWT missing key        | Auto-disabled with error log; server starts (rc6 refused to start)                                        |
| `PREST_SSL_*` env vars | Removed in rc2 — use `PREST_PG_SSL_*` ([#919](https://github.com/prest/prest/pull/919))                   |
| Default `pg.ssl.mode`  | `disable` when no config file found (v1 used `require`)                                                   |
| Multi-database         | Full `[[databases]]` registry support ([#973](https://github.com/prest/prest/pull/973))                   |
| Per-alias permissions  | `access.tables` supports `database` and `schema` fields ([#973](https://github.com/prest/prest/pull/973)) |

Follow the [Upgrading to v2](/get-started/upgrading-to-v2) guide when migrating from v1 or rc6.

***

## Documentation links

* [Multi-database guide](/get-started/multi-database)
* [Configuring pREST](/get-started/configuring-prest)
* [Upgrading to v2](/get-started/upgrading-to-v2)
* [Changes since rc6 (detailed)](/releases/main-since-rc6)
* [v2.0.0-rc6 release notes](/releases/v2.0.0-rc6)


# Changes since rc6 (in v2.0.0)

**Released as** [**v2.0.0**](/releases/v2.0.0) (tag `8784a25`). The commits below were merged after [v2.0.0-rc6](/releases/v2.0.0-rc6) and are included in the v2.0.0 release.

| Commit    | PR                                              | Date        | Summary                                                                                                            |
| --------- | ----------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------ |
| `c54b7dd` | [#972](https://github.com/prest/prest/pull/972) | Jul 4, 2026 | PrestConf refactor, Postgres adapter/connection pooling, credential redaction in logs, template param sanitization |
| `31a9854` | [#974](https://github.com/prest/prest/pull/974) | Jul 4, 2026 | Config resilience (graceful fallbacks), JWT auto-disable instead of startup abort                                  |
| `8784a25` | [#973](https://github.com/prest/prest/pull/973) | Jul 7, 2026 | Multi-database registry, alias-aware routing, `/_ready`, per-alias permissions                                     |

***

## #972 — Adapter and connection management

Mostly internal, with these user-facing changes:

* **Postgres adapter refactor** with per-request connection context (foundation for multi-database routing and health checks).
* **Credential redaction in error logs** via the `logsafe` package — database passwords and connection strings are not emitted in log output.
* **Custom query template parameter sanitization** — template query parameters are validated; unsafe inputs are rejected. Pass well-formed values to `/_QUERIES` scripts.
* **Context-aware health checks** — health endpoints use request-scoped connection context.

***

## #974 — Configuration resilience

Startup is never blocked by bad configuration. Invalid or missing settings produce warnings and safe fallbacks instead of fatal exits.

### JWT auto-disable (`ensureJWTConfig`)

In v2.0.0, missing JWT verification material no longer aborts startup:

* When `jwt.default = true` and `debug = false`, but no `jwt.key`, `jwt.jwks`, or `jwt.wellknownurl` is configured → JWT middleware is **auto-disabled** with an error log. The server continues to start.
* When `auth.enabled = true` but `jwt.key` is missing → auth is **auto-disabled** with an error log.

This is the safe alternative to the rc6 fail-closed behavior, which still prevents authentication bypass via an empty HMAC key ([GHSA-fj7v-859r-2fm4](https://github.com/prest/prest/security/advisories/GHSA-fj7v-859r-2fm4)).

> **rc6 tagged binary:** the [v2.0.0-rc6](/releases/v2.0.0-rc6) release still **refuses to start** without verification material. See the rc6 page for that behavior.

### `jwt.default` default changed to `false`

The Viper default for `jwt.default` is now **`false`** in v2.0.0 (rc6 implied `true`). JWT middleware is off unless you explicitly enable it.

### Other resilience fallbacks

| Condition                                                              | Behavior                                              |
| ---------------------------------------------------------------------- | ----------------------------------------------------- |
| Missing, unreadable, or malformed TOML                                 | Warnings; Viper defaults applied                      |
| Invalid `access.tables`, `cache.endpoints`, or `databases` keys        | Zero values + warning                                 |
| Queries path unavailable                                               | Fallback to `~/queries`, then disable queries feature |
| Cache storage unavailable                                              | Fallback to `./`, then disable cache                  |
| Invalid registry entries (duplicate alias, missing URL, invalid alias) | Entry skipped with warning                            |

### Structured logging

`setupLogger` runs on **every** startup — structured JSON logging via Go `slog` is always configured, not only when cache is enabled. Use `PREST_LOG_LEVEL` to control verbosity.

***

## #973 — Multi-database

Full multi-database support via a `[[databases]]` registry and environment variable pairs.

### URL routing

All CRUD uses `/{database}/{schema}/{table}`:

```http
GET /tenant-a/public/users
POST /tenant-a/public/orders
GET /_QUERIES/tenant-a/myfolder/my_query?field1=foo
```

The `{database}` segment is either a Postgres database name (legacy mode) or a registered **alias** (registry mode).

### Configuration

**Environment variables** (Kubernetes / production):

```sh
DATABASE_ALIAS_1=tenant-a
DATABASE_URL_1=postgres://user:pass@cluster-a.example.com:5432/app_a?sslmode=require
DATABASE_ALIAS_2=tenant-b
DATABASE_URL_2=postgres://user:pass@cluster-b.example.com:5432/app_b?sslmode=require
```

`PREST_DATABASE_ALIAS_N` and `PREST_DATABASE_URL_N` are accepted aliases. Env pairs win over TOML on conflict.

**TOML** (local development):

```toml
[pg]
database = "prest-test"
single = false

[[databases]]
alias = "prest-test"
host = "postgres"
port = 5432
database = "prest-test"
user = "postgres"
pass = "postgres"
ssl.mode = "disable"

[[databases]]
alias = "secondary-db"
host = "postgres-b"
port = 5432
database = "secondary-cluster"
user = "postgres"
pass = "postgres"
ssl.mode = "disable"
```

See the [Multi-database guide](/get-started/multi-database) for full details.

### `GET /_ready` readiness endpoint

| Endpoint       | Purpose   | Behavior                                              |
| -------------- | --------- | ----------------------------------------------------- |
| `GET /_health` | Liveness  | Pings the default database                            |
| `GET /_ready`  | Readiness | Pings the default database and every registered alias |

Use `/_ready` for Kubernetes readiness probes. See the [Kubernetes deployment manifest](https://github.com/prest/prest/blob/main/install-manifests/kubernetes/deployment.yaml) in the prest repo for a multi-secret example.

### Per-alias permissions

`access.tables` entries support optional `database` and `schema` fields for per-alias access control:

```toml
[[access.tables]]
database = "tenant-a"
schema = "public"
name = "users"
permissions = ["read"]
```

See [Permissions](/get-started/permissions#table-permissions).

***

## Documentation links

* [Multi-database guide](/get-started/multi-database)
* [Configuring pREST](/get-started/configuring-prest)
* [Upgrading to v2](/get-started/upgrading-to-v2)
* [v2.0.0 release notes](/releases/v2.0.0)
* [v2.0.0-rc6 release notes](/releases/v2.0.0-rc6)


# v2.0.0-rc6

Released: July 1, 2026

[GitHub release](https://github.com/prest/prest/releases/tag/v2.0.0-rc6) · [Download binaries](https://github.com/prest/prest/releases/tag/v2.0.0-rc6)

This was the final v2 release candidate before [v2.0.0](/releases/v2.0.0). See the [v2 RC changelog](/releases) for the full rc1–rc6 history.

## What's new in rc6

### OR clause filtering ([#958](https://github.com/prest/prest/pull/958))

You can now combine filter conditions with OR logic using the `_or` query parameter, without writing a custom SQL query.

```http
GET /db/public/articles?_or=title=$ilike.%search%||name=$ilike.%search%
```

Alternatives are separated by `||` (double pipe). See [Parameters](/api-reference/parameters#or-clause-filtering) for full syntax and examples.

### JWT fail-closed startup ([#960](https://github.com/prest/prest/pull/960))

pREST now validates JWT configuration at startup. When `jwt.default = true` (the default) and `debug = false`, the server **refuses to start** unless you provide `jwt.key`, `jwt.jwks`, or `jwt.wellknownurl`. This closes [GHSA-fj7v-859r-2fm4](https://github.com/prest/prest/security/advisories/GHSA-fj7v-859r-2fm4), where an empty HMAC key allowed forged bearer tokens.

When `auth.enabled = true`, `jwt.key` is also required.

> **Superseded in v2.0.0:** ([#974](https://github.com/prest/prest/pull/974)) auto-disables JWT instead of refusing startup. See [v2.0.0 release notes](/releases/v2.0.0) and [Changes since rc6](/releases/main-since-rc6).

See [Configuring pREST — JWT](/get-started/configuring-prest#jwt) for current behavior in v2.0.0.

### Structured logging ([#950](https://github.com/prest/prest/pull/950))

v2 uses Go's `slog` package for structured logging. Set `PREST_LOG_LEVEL` to `debug`, `info`, `warn`, or `error` to control verbosity. SQL queries are logged at debug level.

### `_select` whitespace trimming ([#941](https://github.com/prest/prest/pull/941))

Field names in `_select` are now trimmed of surrounding whitespace. `_select=id, name` produces the same result as `_select=id,name`.

### Docker and release tooling ([#953](https://github.com/prest/prest/pull/953), [#971](https://github.com/prest/prest/pull/971))

Docker images are now built via GoReleaser. Use `prest/prest:v2.0.0-rc6` in your deployments.

### Other changes

| Change                                                                        | Notes                                                   |
| ----------------------------------------------------------------------------- | ------------------------------------------------------- |
| Identifier formatting fix ([#955](https://github.com/prest/prest/pull/955))   | Internal SQL generation improvement                     |
| DI refactor ([#968](https://github.com/prest/prest/pull/968))                 | Internal architecture change, no user-facing API impact |
| Windows ARM builds excluded ([#970](https://github.com/prest/prest/pull/970)) | No `windows/arm` binary in this release                 |

## Upgrade from v1

Follow the [Upgrading to v2](/get-started/upgrading-to-v2) guide.


# Get pREST

Download and run pREST — Docker, Homebrew, or Go — for instant REST and MCP APIs on SQL databases (PostgreSQL native today).

Download and run pREST to get instant REST and MCP APIs for your SQL database. PostgreSQL is the native adapter today — see [Databases](/databases). If this is your first install, start with [Start with Docker](/get-prest/start-with-docker) or [Deploying with Docker](/deployment/deploying-with-docker). Compare channels in [Distribution](/get-prest/distribution).

* Use the Docker Hub image
* Use the GitHub image
* [Homebrew](/get-prest/start-with-homebrew) — `prestd` (homebrew-core) and `prest-mcp` ([prest/tap](https://github.com/prest/homebrew-tap))
* Download from [source](/get-prest/start-with-golang) and run
* GitHub [releases](https://github.com/prest/prest/releases)
* [Distribution channels](/get-prest/distribution) — which method for server vs MCP adapter

## Latest: v2.4.2

[**v2.4.2**](/releases/v2.4.2) enforces minimum HMAC `jwt.key` sizes, adds bound values for custom query scripts, and removes SQL from logs — plus MCP `[expose]` enforcement from [v2.4.1](/releases/v2.4.1), OpenTelemetry and pgvector from [v2.4.0](/releases/v2.4.0), Studio from [v2.2.0](/releases/v2.2.0), and MCP from [v2.1.0](/releases/v2.1.0). See the [upgrade guide](/get-started/upgrading-to-v2), [pREST Studio](/get-started/prest-studio), [MCP over HTTP](/get-started/mcp-over-http), and [AI and MCP](/ai).

* **Binary:** [v2.4.2 assets](https://github.com/prest/prest/releases/tag/v2.4.2)
* **Docker:** `prest/prest:v2.4.2`
* **Go:** `go install github.com/prest/prest/v2/cmd/prestd@v2.4.2`

{% hint style="warning" %}
`jwt.key` must be at least 32 bytes for HS256 from v2.4.2 — a shorter key is discarded at startup and auth disables itself rather than failing the start. See [Auth — HMAC key requirements](/api-reference/auth#hmac-key-requirements-v242).
{% endhint %}

\* \*\*MCP adapter:\*\* \`brew install prest/tap/prest-mcp\` or see \[Install pREST MCP Adapter]\(../ai/install-prest-mcp.md)

Database support labels: [Databases](/databases).

## Related

* [Homepage](/)
* [Acronyms](/readme/acronyms) ([REST](/readme/acronyms#rest), [MCP](/readme/acronyms#mcp))
* [Databases](/databases)
* [Get Started](/get-started)
* [v2.4.2 release notes](/releases/v2.4.2)


# Development Guide

***prestd*** is written in the [go language](https://golang.org) and we use the best practices recommended by the language itself to simplify its contribution. If you are not familiar with the language, read the [Effective Go](https://golang.org/doc/effective_go).

### Development usage

As mentioned before prest is written in **go**, as it is in the document topic of using prest in development mode it is important to know the go language path structure, if you don't know it read the page [How to Write Go Code (with GOPATH)](https://golang.org/doc/gopath_code).

> Assuming you do not have the [repository cloned](https://github.com/prest/prest) locally, we are assuming you are reading this page for the first time

Download all of pREST's dependencies

```sh
git clone git@github.com:prest/prest.git && cd prest
go mod download
```

We recommend using `go run` for development environment, remember that it is necessary environment variables for *p***REST** to connect to PostgreSQL - we will explain in the next steps how to do it

```sh
go run cmd/prestd/main.go
```

Building a **local version** (we will not use flags for production environment)

```sh
go build -o prestd cmd/prestd/main.go
```

Executing the `prestd` after generating binary or using `go run`

```sh
PREST_PG_USER=postgres PREST_PG_PASS=postgres PREST_PG_DATABASE=prest PREST_PG_PORT=5432 PREST_HTTP_PORT=3010 ./prestd
```

> to use `go run` replace `./prestd` with `go run`

or use `'prest.toml'` file as a preset configuration, insert a user to see the changes

### Dev Container

A [devcontainer](https://code.visualstudio.com/docs/remote/containers) is used by the VS Code Remote Containers extension and works by creating a Docker container to do your development in.

> Usually preparing the development environment is not a simple job, especially when we are talking about software that depends on other software for its operation, **this is where** [**devcontainer**](https://code.visualstudio.com/docs/remote/containers) **come in**.

As the development environment is within Docker, you supply the [`Dockerfile`](https://docs.docker.com/engine/reference/builder/) and VS Code will take care of building the image and starting the container for you. Then since you control the `Dockerfile` you can have it install any software you need for your project, set the right version of Node, install global packages, etc.

This is just a plain old `Dockerfile`, you can run it without VS Code using the standard Docker tools and mount a volume in, but the power comes when you combine it with the [`devcontainers.json`](https://code.visualstudio.com/docs/remote/devcontainerjson-reference) file, which gives VS Code instructions on how to configure itself.

Using golang + prettier? Tell the devcontainer to install those extensions so the user has them already installed. Want some VS Code settings enabled by default, specify them so users don’t have to know about it.

#### GitHub Codespaces

A codespace is a development environment that's hosted in the cloud. You can customize your project for Codespaces by committing configuration files to your repository (often known as Configuration-as-Code), which creates a repeatable codespace configuration for all users of your project.

Codespaces run on a variety of VM-based compute options hosted by GitHub.com, which you can configure from 2 core machines up to 32 core machines. You can connect to your codespaces from the browser or locally using Visual Studio Code.

![GitHub codespaces diagram](https://docs.github.com/assets/cb-49622/images/help/codespaces/codespaces-diagram.png)

**How to use the prestd in Codespaces**

1. Access the address to create prestd codespace [here](https://github.com/prest/prest/codespaces)
2. Select the **branch** (we recommend using `main`) and click **create codespace**
3. wait for the setup... it may take a few minutes

**Done** (*#congrats*), you have a development environment for `prestd` with **PostgreSQL** (configured and integrated with `prestd`), vscode plugins (for golang), database viewer, etc.

**Database viewer:**

### Next step in development

If you have come this far I assume that your development environment is working, right?

> if not, go back to the previous topics

To get the environment working with *"all the right stuff"* we recommend setting up (and activating) the API authentication system. To do this, follow the steps below:

> we are writing the example using go code (not binario or docker, if you want to run the commands via docker see here)

```sh
# Run data migration to create user structure for access (JWT)
go run cmd/prestd/main.go migrate up auth

# Create user and password for API access (via JWT)
## user: prest
## pass: prest
# v2 defaults auth.encrypt to bcrypt — hash with htpasswd (apache2-utils / httpd)
HASH=$(htpasswd -nbBC 10 x prest | cut -d: -f2)
psql -d prest -U prest -h localhost -c "INSERT INTO prest_users (name, username, password) VALUES ('pREST Full Name', 'prest', '$HASH')"
# Check if the user was created successfully (by doing a select on the table)
psql -d prest -U prest -h localhost -c "select * from prest_users"
```

**Now the fun begins:**

```sh
# Run prestd server
go run cmd/prestd/main.go
# Generate JWT Token with user and password created
curl -i -X POST http://127.0.0.1:3000/auth -H "Content-Type: application/json" -d '{"username": "prest", "password": "prest"}'
# Access endpoint using JWT Token
curl -i -X GET http://127.0.0.1:3000/prest/public/prest_users -H "Accept: application/json" -H "Authorization: Bearer {TOKEN}"
```

Or you can run the following steps easy with postman tools:

**Samples to getting started with API calls**

**Description**

First api calls and test automation sample.

**Usage**

Import on Postman and execute the following steps:

* Bearer Authentication
* List Databases

This is the manual process to see how things is going.

So, we have the automated way:

```sh
npm i --location=global newman
```

After the installation run the following command:

```sh
newman run samples/prest_first_look.postman_collection.json
```

That's it, you have a way to validate the project running locally, and to test on the environments you need to edit and go forward with your own version of this sample.

### Integration tests

The full integration suite runs inside Docker — no local Postgres setup required ([#973](https://github.com/prest/prest/pull/973)):

```sh
make test-unit
make test-integration
```

Or directly with Docker Compose:

```sh
docker compose -f docker-compose-test.yml up -d --wait postgres postgres-b db-init prestd prestd-multicluster prestd-auth
docker compose -f docker-compose-test.yml run --rm --no-deps tests
docker compose -f docker-compose-test.yml down -v --remove-orphans
```

Compose starts `postgres`, `postgres-b`, a one-shot `db-init` job (`testdata/db-init.sh`), three **prestd** services (`prestd`, `prestd-multicluster`, `prestd-auth`), then runs `go test ./integration/...` in the `tests` container. Standard HTTP integration tests call those servers via `PREST_TEST_URL`, `PREST_MULTICLUSTER_TEST_URL`, and `PREST_AUTH_TEST_URL`.

Running `go test ./integration/...` outside compose skips network tests when those URLs are unset.

Multi-cluster tests require a second Postgres service (`PREST_PG_HOST_B`) — see [`integration/controllers/multicluster_test.go`](https://github.com/prest/prest/blob/main/integration/controllers/multicluster_test.go).

### Execute unit tests locally (integration/e2e)

pREST's unit tests depend on a working Postgres database for SQL query execution, to simplify the preparation of the local environment we use docker (and docker-compose) to upload the environment with Postgres.

**all tests:**

```sh
docker-compose -f testdata/docker-compose.yml up --abort-on-container-exit
```

**package-specific testing:** *in the example below the `config` package will be tested*

```sh
docker-compose -f testdata/docker-compose.yml run --rm prest-test sh ./testdata/runtest.sh ./config
```

**specific function test:** *in the example below will run the test `TestGetDefaultPrestConf` from the `config` package, don't forget to call the `TestMain` function before your function*

```sh
docker-compose -f testdata/docker-compose.yml run prest-test sh ./testdata/runtest.sh ./config -run TestMain,TestGetDefaultPrestConf
```

### Version - Patterns

***prestd*** has the `main` branch as a tip branch and has version branches such as `v1.1` and `v2`. `v1.1` is a release branch and we will tag `v1.1.0` for binary download. If `v1.1.0` has bugs, we will accept pull requests on the `v1.1` branch and publish a `v1.1.1` tag, after bringing the bug fix also to the main branch.

v2.4.2 is the latest stable v2 release. RC tags (e.g. `v2.0.0-rc6`) were published during the release candidate phase. See the [Releases](/releases) page for the changelog.

Since the `main` branch is a tip version, if you wish to use pREST in production, download the latest stable release tag — [v2.4.2](https://github.com/prest/prest/releases/tag/v2.4.2) for v2 or the latest [v1 release](https://github.com/prest/prest/releases/latest). All the branches will be protected via GitHub, all the PRs to every branch must be reviewed by two maintainers and must pass the automatic tests.


# Start with Docker

Run open-source pREST with Docker Compose — PostgreSQL plus prest image v2.4.2, auth migrate, JWT login, and optional MCP smoke check.

Use Docker Compose to bring up PostgreSQL and pREST for a local test environment.

{% hint style="info" %}
**v2 notes:** pin the image to `prest/prest:v2.4.2`, set `PREST_VERSION=2`, and configure JWT (`PREST_JWT_KEY` or equivalent) when JWT enforcement is enabled — or expect JWT to be auto-disabled with a warning (v2+). Use `PREST_DEBUG=true` for local development without JWT. See [Deploying with Docker](/deployment/deploying-with-docker) and [Upgrading to v2](/get-started/upgrading-to-v2).
{% endhint %}

```sh
# Download docker compose file
wget https://raw.githubusercontent.com/prest/prest/main/docker-compose-prod.yml -O docker-compose.yml

# Up (run) PostgreSQL and prestd
docker-compose up
# Run data migration to create user structure for access (JWT)
docker-compose exec prest prestd migrate up auth

# Create user and password for API access (via JWT)
## user: prest
## pass: prest
# v2 defaults auth.encrypt to bcrypt — hash with htpasswd (apache2-utils / httpd)
HASH=$(htpasswd -nbBC 10 x prest | cut -d: -f2)
docker-compose exec postgres psql -d prest -U prest -c "INSERT INTO prest_users (name, username, password) VALUES ('pREST Full Name', 'prest', '$HASH')"
# Check if the user was created successfully (by doing a select on the table)
docker-compose exec postgres psql -d prest -U prest -c "select * from prest_users"

# Generate JWT Token with user and password created
curl -i -X POST http://127.0.0.1:3000/auth -H "Content-Type: application/json" -d '{"username": "prest", "password": "prest"}'
# Access endpoint using JWT Token
curl -i -X GET http://127.0.0.1:3000/prest/public/prest_users -H "Accept: application/json" -H "Authorization: Bearer {TOKEN}"

# Optional: MCP discovery (v2.1.0+)
curl -s http://127.0.0.1:3000/_mcp | head
# Optional: Studio (v2.2.0+)
# open http://127.0.0.1:3000/_studio/
```

### Supported Operating System

* Linux
* macOS
* Windows
* BSD

## Related

* [Deploying with Docker](/deployment/deploying-with-docker)
* [Upgrading to v2](/get-started/upgrading-to-v2)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Acronyms](/readme/acronyms)


# Start with Golang

### Prerequisites

* Go (1.26+)

***

### Quick Start

The `go install` command builds and installs the packages named by the paths on the command line. Executables (main packages) are installed to the directory named by the GOBIN environment variable, which defaults to `$GOPATH/bin` or `$HOME/go/bin` if the **GOPATH** environment variable is not set. Executables in `$GOROOT` are installed in `$GOROOT/bin` or `$GOTOOLDIR` instead of `$GOBIN`. Non-executable packages are built and cached but not installed.

Since **Go 1.16**, if the arguments have version suffixes (like `@latest` or `@v2.2.0`), go install builds packages in module-aware mode, ignoring the go.mod file in the current directory or any parent directory if there is one. This is useful for installing executables without affecting the dependencies of the main module.

Latest stable v2 (recommended):

```sh
go install github.com/prest/prest/v2/cmd/prestd@v2.4.2
```

Or tip of the module:

```sh
go install github.com/prest/prest/cmd/prestd@latest
```

Set `PREST_VERSION=2` when running v2. See [Upgrading to v2](/get-started/upgrading-to-v2).


# Start with Homebrew

### Prerequisites

* macOS (or Linux with [Homebrew](https://brew.sh/))
* [Homebrew](https://brew.sh/)

***

## Install prestd (homebrew-core)

Install the pREST server from the official Homebrew core formula:

```sh
brew install prestd
```

Formula: [formulae.brew.sh/formula/prestd](https://formulae.brew.sh/formula/prestd) [![Homebrew](https://img.shields.io/badge/dynamic/json.svg?url=https://formulae.brew.sh/api/formula/prestd.json\&query=$.versions.stable\&label=homebrew)](https://formulae.brew.sh/formula/prestd)

For MCP features you need **prestd v2.1.0+**. Check what Homebrew installed:

```sh
brew info prestd
```

If your formula is older than v2.1.0, upgrade with `brew upgrade prestd` or install from [GitHub Releases](https://github.com/prest/prest/releases) / [Docker](/deployment/deploying-with-docker) until the core formula catches up.

***

## Install prest-mcp (pREST Homebrew tap)

Clients such as Cursor and Claude Desktop speak MCP over **stdio**. pREST exposes MCP over **HTTP** at `/_mcp`. The official adapter bridges the two.

Formulae that are not in homebrew-core ship from the [prest/homebrew-tap](https://github.com/prest/homebrew-tap) repository:

| Formula     | Package                                                         | Description                                    |
| ----------- | --------------------------------------------------------------- | ---------------------------------------------- |
| `prest-mcp` | [prest-mcp-adapter](https://github.com/prest/prest-mcp-adapter) | Stdio MCP adapter for pREST’s `/_mcp` endpoint |

### Install

```sh
brew install prest/tap/prest-mcp
```

Or tap once, then install:

```sh
brew tap prest/tap
brew install prest-mcp
```

### Quick check

```sh
PREST_MCP_URL=http://localhost:3000/_mcp prest-mcp
```

Point MCP clients at this binary — see [Install pREST MCP Adapter](/ai/install-prest-mcp), the [Homebrew adapter tutorial](/postgres-to-ai-agent/install-prest-mcp-homebrew), [Cursor](/ai/cursor), and [Claude Desktop](/ai/claude-desktop).

***

## Next steps

1. Configure and run **prestd** against your Postgres database — [Configuring pREST](/get-started/configuring-prest).
2. Confirm MCP discovery: `curl -s http://localhost:3000/_mcp`.
3. Connect your IDE with the adapter — [Cursor](/ai/cursor) or [Claude Desktop](/ai/claude-desktop).
4. Compare install channels — [Distribution](/get-prest/distribution).

## Related documentation

* [Install adapter with Homebrew](/postgres-to-ai-agent/install-prest-mcp-homebrew)
* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [AI and MCP](/ai)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Distribution channels](/get-prest/distribution)
* [Get pREST](/get-prest)
* [homebrew-tap](https://github.com/prest/homebrew-tap) · [prest-mcp-adapter](https://github.com/prest/prest-mcp-adapter)


# Distribution channels

Ways to install **pREST** (`prestd`) and the **pREST MCP Adapter** (`prest-mcp`). Pick the channel that matches your platform and workflow.

Docs site: <https://docs.prestd.com/>

***

## Which method?

| Goal                            | Recommended                                                                                                           |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Try pREST quickly               | [Docker](/deployment/deploying-with-docker)                                                                           |
| macOS daily driver for `prestd` | [Homebrew core](/get-prest/start-with-homebrew) (`brew install prestd`)                                               |
| Connect Cursor / Claude to MCP  | [Homebrew tap](/get-prest/start-with-homebrew) (`brew install prest/tap/prest-mcp`) or [Go](/ai/install-prest-mcp#go) |
| Pin a release binary            | [GitHub Releases](https://github.com/prest/prest/releases)                                                            |
| Build from source               | [Start with Golang](/get-prest/start-with-golang)                                                                     |
| MCP via OCI                     | `ghcr.io/prest/prest-mcp-adapter:0.1.3`                                                                               |

MCP requires **pREST v2.1.0+**. Overview: [AI and MCP](/ai).

***

## pREST server (`prestd`)

| Channel  | How                                                                                                          |
| -------- | ------------------------------------------------------------------------------------------------------------ |
| Docker   | `prest/prest:v2.4.2` — [Deploying with Docker](/deployment/deploying-with-docker)                            |
| Homebrew | `brew install prestd` — [Start with Homebrew](/get-prest/start-with-homebrew)                                |
| Go       | `go install github.com/prest/prest/v2/cmd/prestd@v2.4.2` — [Start with Golang](/get-prest/start-with-golang) |
| Binaries | [GitHub Releases](https://github.com/prest/prest/releases/tag/v2.4.2)                                        |

***

## pREST MCP Adapter (`prest-mcp`)

Stdio ↔ HTTP bridge for `/_mcp`. Transport only — no schema/SQL/tools in the adapter.

| Channel      | How                                                                                                        |
| ------------ | ---------------------------------------------------------------------------------------------------------- |
| Homebrew     | `brew install prest/tap/prest-mcp` — [Homebrew tutorial](/postgres-to-ai-agent/install-prest-mcp-homebrew) |
| Go           | `go install github.com/prest/prest-mcp-adapter/cmd/prest-mcp@latest`                                       |
| Docker / OCI | `docker pull ghcr.io/prest/prest-mcp-adapter:0.1.3`                                                        |
| MCP Registry | Package name `io.github.prest/prest`                                                                       |

Full guide: [Install pREST MCP Adapter](/ai/install-prest-mcp).

***

## Agent plugins (optional)

Skills and examples for AI agents — separate from the MCP binary:

| Plugin         | Platform           | Docs                                            |
| -------------- | ------------------ | ----------------------------------------------- |
| prest-cursor   | Cursor             | [pREST for Cursor plugin](/ai/prest-cursor)     |
| prest-openclaw | OpenClaw / ClawHub | [pREST for OpenClaw plugin](/ai/prest-openclaw) |

***

## Related documentation

* [Get pREST](/get-prest)
* [Start with Homebrew](/get-prest/start-with-homebrew)
* [MCP over HTTP](/get-started/mcp-over-http)
* [pREST Studio](/get-started/prest-studio)
* [v2.4.2 release notes](/releases/v2.4.2)


# Get Started

Configure pREST for production — REST (Representational State Transfer), auth, multi-database, and MCP (Model Context Protocol). PostgreSQL is native today.

Configure pREST for production: REST (Representational State Transfer) APIs, auth, multi-database routing, and MCP (Model Context Protocol). PostgreSQL is the native adapter today; see [Databases](/databases) for certified engines and the [roadmap](/databases/roadmap).

* Server configuration — [Configuring pREST](/get-started/configuring-prest)
* [pREST Studio](/get-started/prest-studio) — embedded UI at `/_studio/` (v2.2.0+)
* [Upgrading to v2](/get-started/upgrading-to-v2)
* [Multi-database](/get-started/multi-database)
* [MCP over HTTP](/get-started/mcp-over-http) — Model Context Protocol at `/_mcp`
* [Cache](/get-started/cache)
* [Permissions](/get-started/permissions)
* [Migrating](/get-started/migrating)
* [CORS Support](/get-started/cors-support)

For AI agents and IDEs (install, Cursor, Claude Desktop, read-only Postgres), see [**AI and MCP**](/ai) and the [PostgreSQL to AI agent](/postgres-to-ai-agent) tutorial.

For local builds and contribution setup, see the [Development Guide](/get-prest/development-guide).

### Troubleshooting

If you encounter an error while installing ***prestd*** on any platform, [open a new discussion](https://github.com/prest/prest/discussions/new) or join our [Discord](https://discord.gg/JnRjvu39w8).

### Related

* [Homepage](/)
* [Acronyms](/readme/acronyms) ([REST](/readme/acronyms#rest), [MCP](/readme/acronyms#mcp))
* [Databases](/databases)
* [API Reference](/api-reference)
* [AI and MCP](/ai)


# Configuring pREST

The ***prestd*** configuration is via an *environment variable* or *toml* file. Starting from version [`v1.2.0`](https://github.com/prest/prest/releases/tag/v1.2.0) it will be possible to use `prestd` without any *environment variable* or the *toml* file, but the configurations used will be the described in the default column bellow.

### Environment variables

| var                                  | default          | description                                                                                                                               |
| ------------------------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `PREST_VERSION`                      | `1`              | version used for environment variables. Set to `2` for v2 deployments (recommended). v2 uses `PREST_PG_SSL_*` for PostgreSQL SSL.         |
| `PREST_CONF`                         | `./prest.conf`   |                                                                                                                                           |
| `PREST_MIGRATIONS`                   | `./migrations`   |                                                                                                                                           |
| `PREST_QUERIES_LOCATION`             | `./queries`      |                                                                                                                                           |
| `PREST_HTTP_HOST`                    | `0.0.0.0`        |                                                                                                                                           |
| `PREST_HTTP_PORT` **or** `PORT`      | `3000`           | `PORT` is for cloud factor, *when declared this variable overwrittes* `PREST_HTTP_PORT`                                                   |
| `PREST_PG_HOST`                      | `127.0.0.1`      | host used to connect                                                                                                                      |
| `PREST_PG_USER`                      | `postgres`       | user used to connect                                                                                                                      |
| `PREST_PG_PASS`                      | `postgres`       | password used to connect                                                                                                                  |
| `PREST_PG_DATABASE`                  | `prest`          | database name used to connect                                                                                                             |
| `PREST_PG_PORT`                      | `5432`           |                                                                                                                                           |
| `PREST_PG_URL` **or** `DATABASE_URL` |                  | cloud factor, *when declaring this variable all the previous connection fields are overwritten*                                           |
| `PREST_PG_SSL_MODE`                  | `disable`        | Postgres connection SSL mode (`disable`, `require`, `verify-ca`, `verify-full`). Sample default matches `[pg.ssl]` when no config is set. |
| `PREST_PG_SSL_CERT`                  |                  | Postgres connection SSL certificate                                                                                                       |
| `PREST_PG_SSL_KEY`                   |                  | Postgres connection SSL key                                                                                                               |
| `PREST_PG_SSL_ROOTCERT`              |                  | Postgres connection SSL root certificate                                                                                                  |
| `PREST_PG_SINGLE`                    | `true`           | When `false`, allows routing to multiple databases or aliases. See [Multi-database](/get-started/multi-database).                         |
| `DATABASE_ALIAS_N`                   |                  | Multi-database registry alias (1-based index). Also `PREST_DATABASE_ALIAS_N`. See [Multi-database](/get-started/multi-database).          |
| `DATABASE_URL_N`                     |                  | Connection URL for `DATABASE_ALIAS_N`. Also `PREST_DATABASE_URL_N`. Env wins over TOML.                                                   |
| `PREST_CACHE_ENABLED`                | false            | embedded cache system                                                                                                                     |
| `PREST_CACHE_TIME`                   | 10               | TTL in minute (time to live)                                                                                                              |
| `PREST_CACHE_STORAGEPATH`            | ./               | path where the cache file will be created                                                                                                 |
| `PREST_CACHE_SUFIXFILE`              | .cache.prestd.db | suffix of the name of the file that is created                                                                                            |
| `PREST_JWT_DEFAULT`                  | `false`          | v2+: enable default JWT middleware on all routes (except whitelist)                                                                       |
| `PREST_STUDIO_ENABLED`               | `true`           | v2.2.0+: serve embedded pREST Studio at `/_studio/` (set `false` to disable; returns 404)                                                 |
| `PREST_JWT_KEY`                      |                  | HMAC secret. v2.4.2+ requires ≥32 bytes for HS256, ≥48 for HS384, ≥64 for HS512                                                           |
| `PREST_JWT_ALGO`                     | HS256            | v2.4.2+: the only signature algorithm accepted for verification (case-sensitive). Ignored before v2.4.2                                   |
| `PREST_JWT_WELLKNOWNURL`             |                  | URL of .wellknown config of IDP used to fetch the JWKS used to verify token signature. Ignored if PREST\_JWT\_JWKS is set                 |
| `PREST_JWT_JWKS`                     |                  | JWKS used to verify token signature. If set, PREST\_JWT\_WELLKNOWNURL is ignored                                                          |
| `PREST_JWT_WHITELIST`                | `^\/auth$`       | Regex patterns for endpoints that skip JWT verification (v2)                                                                              |
| `PREST_AUTH_ENABLED`                 | `false`          |                                                                                                                                           |
| `PREST_AUTH_ENCRYPT`                 | `bcrypt`         | v2 default; v1 used `MD5`                                                                                                                 |
| `PREST_AUTH_TYPE`                    | `body`           |                                                                                                                                           |
| `PREST_AUTH_SCHEMA`                  | `public`         |                                                                                                                                           |
| `PREST_AUTH_TABLE`                   | `prest_users`    |                                                                                                                                           |
| `PREST_AUTH_USERNAME`                | `username`       |                                                                                                                                           |
| `PREST_AUTH_PASSWORD`                | `password`       |                                                                                                                                           |
| `PREST_SSL_MODE`                     | `require`        | **v1 only (removed in v2)** — use `PREST_PG_SSL_MODE` instead                                                                             |
| `PREST_SSL_CERT`                     |                  | **v1 only (removed in v2)** — use `PREST_PG_SSL_CERT` instead                                                                             |
| `PREST_SSL_KEY`                      |                  | **v1 only (removed in v2)** — use `PREST_PG_SSL_KEY` instead                                                                              |
| `PREST_SSL_ROOTCERT`                 |                  | **v1 only (removed in v2)** — use `PREST_PG_SSL_ROOTCERT` instead                                                                         |
| `PREST_LOG_LEVEL`                    |                  | v2 only: log verbosity (`debug`, `info`, `warn`, `error`)                                                                                 |
| `PREST_PLUGINPATH`                   | `./lib`          | path to plugin storage `.so`                                                                                                              |
| `PREST_EXPOSE_ENABLED`               | `false`          | when `true`, disables all listing endpoints (`/databases`, `/schemas`, `/tables`). See [Expose Data](#expose-data)                        |
| `PREST_EXPOSE_TABLES`                | `true`           | when `false`, disables table listing. See [Expose Data](#expose-data)                                                                     |
| `PREST_EXPOSE_SCHEMAS`               | `true`           | when `false`, disables schema listing. See [Expose Data](#expose-data)                                                                    |
| `PREST_EXPOSE_DATABASES`             | `true`           | when `false`, disables database listing. See [Expose Data](#expose-data)                                                                  |
| `PREST_JSON_AGG_TYPE`                | `jsonb_agg`      | changes how pREST encodes data from the database, can be set also to `json_agg`                                                           |

### TOML

Optionally the prestd can be configured by TOML file.

You can follow this sample and create your own `prest.toml` file and put this on the same folder that you run `prestd` command.

```toml
migrations = "./migrations"

# debug = true
# enabling debug mode will disable JWT authorization

[http]
port = 3000

[jwt]
default = false
key = "secret"
algo = "HS256"

[auth]
enabled = true
type = "body"
encrypt = "bcrypt"
table = "prest_users"
username = "username"
password = "password"

[pg]
host = "127.0.0.1"
user = "postgres"
pass = "mypass"
port = 5432
database = "prest"
single = true
## or used cloud factor
# URL = "postgresql://user:pass@localhost/mydatabase/?sslmode=disable"

[pg.ssl]
mode = "disable"
cert = "./PATH"
key = "./PATH"
rootcert = "./PATH"

[expose]
enabled = false
databases = true
schemas = true
tables = true
```

### Authorization

#### JWT

JWT middleware is controlled by `jwt.default`. In **v2+**, the default is **`false`**; you must set `jwt.default = true` (or `PREST_JWT_DEFAULT=true`) to enable JWT enforcement. Enabling debug mode disables JWT at runtime.

`/_mcp` (v2.1.0+) uses the same auth stack as other HTTP routes — when JWT or auth is enabled, MCP clients must send the same credentials. See [MCP over HTTP](/get-started/mcp-over-http) and [Auth](/api-reference/auth).

```toml
[jwt]
default = true
key = "a-secret-of-at-least-32-bytes!!!"
```

**HMAC key length (v2.4.2+):** `jwt.key` must meet the RFC 7518 minimum for its algorithm — **32 bytes** for `HS256` (the default), **48** for `HS384`, **64** for `HS512` ([#1017](https://github.com/prest/prest/pull/1017)). A shorter key is **discarded at startup**; pREST keeps serving with `/auth` unregistered and the auth middleware passing requests through unauthenticated. Full detail and the log lines to grep for: [Auth — HMAC key requirements](/api-reference/auth#hmac-key-requirements-v242).

**JWT configuration (v2+):** when `jwt.default = true` and no verification material is provided (`jwt.key`, `jwt.jwks`, or `jwt.wellknownurl`), pREST **auto-disables** the JWT middleware and logs an error — the server continues to start ([#974](https://github.com/prest/prest/pull/974), shipped in v2.0.0). When `auth.enabled = true` without `jwt.key`, the auth endpoint is also auto-disabled. This prevents authentication bypass via an empty HMAC key ([GHSA-fj7v-859r-2fm4](https://github.com/prest/prest/security/advisories/GHSA-fj7v-859r-2fm4)).

> **v2.0.0-rc6 tagged binary:** the [v2.0.0-rc6](/releases/v2.0.0-rc6) release **refuses to start** in the same situations. See [Changes since rc6](/releases/main-since-rc6) for the difference.

Debug mode bypasses JWT enforcement at runtime.

See [Upgrading to v2](/get-started/upgrading-to-v2) for migration guidance from v1.

Since **v2.4.2** ([#1017](https://github.com/prest/prest/pull/1017)), the token is validated against the algorithm configured in `jwt.algo` — not the one declared in the token's own header. A token signed with a different `alg` is rejected with `401`. Earlier v2 releases accepted the header's algorithm, which is what makes this a behavior change on upgrade.

Supported values, matched **case-sensitively**:

* The [HMAC signing method](https://en.wikipedia.org/wiki/HMAC): `HS256`, `HS384`, `HS512`
* The [RSA signing method](https://en.wikipedia.org/wiki/RSA_\(cryptosystem\)): `RS256`, `RS384`, `RS512`, and `PS256`, `PS384`, `PS512`
* The [ECDSA signing method](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm): `ES256`, `ES384`, `ES512`
* `EdDSA`

{% hint style="warning" %}
A value outside that set — including `hs256` in lowercase or an explicit `algo = ""` — makes **every request return HTTP 500** with `unsupported JWT signature algorithm`. Leave `jwt.algo` unset to get the `HS256` default.
{% endhint %}

The provided `jwt.key` or JWKS is then used to verify the signature.

Instead of the key, you could provide the URL of a .well-known OpenID configuration or the JWKS directly through `PREST_JWT_WELLKNOWNURL` or `PREST_JWT_JWKS` as environment variables or by using the TOML configuration file:

```toml
[jwt]
wellknownurl = https://accounts.google.com/.well-known/openid-configuration
jwks = {"keys": [{"kty": "RSA", "alg": "RS256", "kid": "93b495162af0c87cc7a51686294097040daf3b43", "n": "3NXwASNf_7-9hOWDKyZ39qgz-yl_npuIsBgxnhNoE7WyQQl-muajPsQRdFqM-HWsAAbS_WtLrmf2aRSmjXBm8wXHIeJjcrZiWeUnSyfZLDr13jxXhN0rDvdiZEsAlaKuh-iCgwC_pXd0TtWpaYlv5FFguuSitKTOiDR6z3eSZUd0XNxr8POCDQ7VlG_4HyzhsO7nOwgivO-PzekDEbcoLI93U8uzKZXYHSRxYWhoSp47PbM9D5WbuwXqbmXRp9TjiJUy6GqEOJ4K2FNvqe-g6C3BnpPVuHZNaVf8QGP806rWrWPdJ0irGBhg-EasC-sdFSrH3kxMxBFfVsuj69U-7Q", "use": "sig", "e": "AQAB" }]}
```

***prestd*** will parse the JWKS to find the corresponding key from the token and fail if it does not find it.

### White list

By default the `/auth` endpoint does not require JWT. The **whitelist** option configures which endpoints skip JWT verification. In v2, entries are **regular expressions**.

```toml
[jwt]
default = true
whitelist = ["^\\/auth$", "^\\/ping$", "^\\/ping\\/.*"]
```

### Auth

pREST has support in jwt token generation based on two fields (example user and password), being possible to use an existing table from your database to login configuring some parameters in the configuration file (or environment variable), *by default this feature is* **disabled**.

```toml
[auth]
enabled = true
type = "body"
encrypt = "bcrypt"
table = "prest_users"
username = "username"
password = "password"
```

| Name       | Description                                                                                                                                                            |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`  | **Boolean** field that activates or deactivates token generation endpoint support                                                                                      |
| `type`     | Type that will receive the login, support for **body and http basic authentication**                                                                                   |
| `encrypt`  | Type of encryption used in password field. v2 default is `bcrypt`; also supports `MD5` and `SHA1`                                                                      |
| `table`    | Table name we will consult *(query)*                                                                                                                                   |
| `username` | User **field** that will be consulted - if your software uses email just abstract name username (at prestd code level it was necessary to define an internal standard) |
| `password` | Password **field** that will be consulted                                                                                                                              |

> to validate all endpoints with generated jwt token must be activated jwt option

### Expose Data

The expose data settings control access to **discovery**, as opposed to `[access]`, which controls whether data can be read or written. They cover the REST listing routes:

* `/databases`
* `/schemas`
* `/tables`

and, since **v2.4.1** ([#1016](https://github.com/prest/prest/pull/1016)), the catalog surface of the [MCP endpoint](/get-started/mcp-over-http) — `/_mcp` previously bypassed these settings entirely, so a deployment that hid its catalog over REST still exposed it to any MCP client.

By default, all listing endpoints are **enabled** (`expose.enabled = false` means listings are allowed).

{% hint style="warning" %}
`enabled` is the master switch: the three per-listing flags **only take effect while `enabled = true`**. Setting `databases = false` on its own does nothing, because the default `enabled = false` allows every listing regardless.
{% endhint %}

To disable all listings:

```toml
[expose]
enabled = true
databases = false
schemas = false
tables = false
```

To disable just the database listing:

```toml
[expose]
enabled = true      # required
databases = false
schemas = true
tables = true
```

| Name        | Description                                                                        |
| ----------- | ---------------------------------------------------------------------------------- |
| `enabled`   | Set to `true` to activate exposure control. While `false`, everything is listable. |
| `databases` | With `enabled = true`, set to `false` to **disable** *databases* listing.          |
| `schemas`   | With `enabled = true`, set to `false` to **disable** *schemas* listing.            |
| `tables`    | With `enabled = true`, set to `false` to **disable** *tables* listing.             |

Environment overrides: `PREST_EXPOSE_ENABLED`, `PREST_EXPOSE_DATABASES`, `PREST_EXPOSE_SCHEMAS`, `PREST_EXPOSE_TABLES`.

**On MCP (v2.4.1+):** a denied listing removes the matching `prest.list_*` tool from discovery and makes `tools/call` return `400` with `unauthorized listing` (REST returns `401` for the same message). If **any** listing is denied, the per-table `prest.select.{database}.{schema}.{table}` tools are withheld too — their names and descriptions disclose table and column names. `prest.describe_table` and `prest.select_table` remain available, subject to [permissions](/get-started/permissions). See [MCP over HTTP — Safety and limits](/get-started/mcp-over-http#safety-and-limits).

#### Default values for Exposure Settings

| Name      | Default Value |
| --------- | ------------- |
| enabled   | `false`       |
| databases | `true`        |
| schemas   | `true`        |
| tables    | `true`        |

### SSL

PostgreSQL connection SSL is configured via `[pg.ssl]` in TOML or `PREST_PG_SSL_*` environment variables (v2). The legacy `[ssl]` block and `PREST_SSL_*` variables were removed in v2. See [Upgrading to v2](/get-started/upgrading-to-v2).

There are 4 options to set on ssl mode:

| Name          | Description | Comment                                                                                                                       |
| ------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `require`     | Always SSL  | skips SSL verification step; v1 default                                                                                       |
| `disable`     | SSL off     | v2 default when no config file is found                                                                                       |
| `verify-ca`   | Always SSL  | verifies that the certificate presented is signed by a trusted CA                                                             |
| `verify-full` | Always SSL  | verifies that the certificate presented is signed by a trusted CA and the server host name matches the one in the certificate |

### Debug Mode

Set environment variable `PREST_DEBUG` or `debug=true` in `prest.toml`.

```toml
debug = true
```

Debug mode disables JWT middleware at runtime and bypasses startup JWT validation.

### Logging

v2 uses Go's `slog` package for structured logging. JSON logs are emitted to stdout on **every** startup.

Database credentials are redacted in error logs ([#972](https://github.com/prest/prest/pull/972)).

**Since v2.4.2** ([#1023](https://github.com/prest/prest/pull/1023)), logs no longer carry caller-influenced SQL or parameter values:

| Path                                        | What is logged                                                                                                                                                   |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custom query scripts (`/_QUERIES`)          | **No SQL at any level**, on both the read and write paths — the statement is composed from a template the caller feeds. Use PostgreSQL statement logging instead |
| CRUD routes                                 | The statement at `debug`, with parameter **values** replaced by a count: `msg="generated SQL" parameter_count=2`                                                 |
| Script headers rejected by the value screen | A `warn` line naming the header only — never its value. Expect these on script endpoints hit by browsers                                                         |

Set the `PREST_LOG_LEVEL` environment variable to control verbosity:

| Level   | Description                                                               |
| ------- | ------------------------------------------------------------------------- |
| `debug` | CRUD SQL and detailed diagnostics                                         |
| `info`  | General operational messages                                              |
| `warn`  | Warnings such as public mode, config fallbacks, or auto-disabled features |
| `error` | Errors only                                                               |

### Custom queries storage

Filesystem scripts under `PREST_QUERIES_LOCATION` / `[queries] location` are the default. In **v2.2.0+**, database-backed storage and the `/_QUERIES/registry` admin API are also available — see [Custom Queries](/api-reference/custom-queries) and [v2.2.0](/releases/v2.2.0). The commented sample in [`samples/prest.sample.toml`](https://github.com/prest/prest/blob/v2.2.0/samples/prest.sample.toml) lists every `[queries]` key.

### pREST Studio

Embedded UI at `/_studio/` (v2.2.0+, enabled by default):

```toml
[studio]
enabled = true
```

Or `PREST_STUDIO_ENABLED=false` to disable. See [pREST Studio](/get-started/prest-studio).

### Configuration resilience

Since **v2.0.0** ([#974](https://github.com/prest/prest/pull/974)), pREST does not abort startup because of configuration problems. Instead it logs warnings and applies safe fallbacks:

| Problem                                                                   | Fallback                                                                                                               |
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Missing, unreadable, or malformed TOML                                    | Viper defaults and environment overrides                                                                               |
| Invalid structured keys (`access.tables`, `cache.endpoints`, `databases`) | Zero/empty values + warning                                                                                            |
| Queries path unavailable                                                  | Retry `~/queries`; if that fails, disable custom queries                                                               |
| Cache storage path unavailable                                            | Retry `./`; if that fails, disable cache                                                                               |
| Invalid database registry entry                                           | Entry skipped with warning                                                                                             |
| Unsafe JWT/auth config                                                    | JWT or auth auto-disabled with error log                                                                               |
| HMAC `jwt.key` below the RFC 7518 minimum (v2.4.2+)                       | Key discarded, then auth/JWT auto-disabled with error log — see [Auth](/api-reference/auth#hmac-key-requirements-v242) |

### Multi-database

pREST supports routing to multiple databases or clusters via a database registry. Set `pg.single = false` and configure `[[databases]]` entries or `DATABASE_ALIAS_N` / `DATABASE_URL_N` environment pairs.

See the [Multi-database guide](/get-started/multi-database) for URL routing, TOML examples, Kubernetes setup, and connection pooling. Full sample: [examples/multi-database-config.toml](https://github.com/prest/docs/tree/main/get-started/examples/multi-database-config.toml).

Since **v2.3.0** ([#999](https://github.com/prest/prest/pull/999)), each alias can auto-select a **Postgres or Timescale** adapter (MySQL/SQLite still roadmap).

### Observability (OpenTelemetry) (v2.4.0)

Since **v2.4.0** ([#1003](https://github.com/prest/prest/pull/1003)), pREST supports **opt-in** OTLP/gRPC push telemetry — HTTP, Postgres, and MCP traces; `http.server.*` / `db.client.operation.duration` / `db.sql.connection.*` metrics; and a `slog` → OTel logs bridge that still writes to stdout. No new HTTP route is added — there is no `/metrics` scrape endpoint.

```toml
[otel]
enabled = false
service_name = "prestd"
# endpoint = "otel-collector:4317"
protocol = "grpc"
sample_ratio = 1.0
metrics_interval = "15s"
insecure = false
db_statement = false
```

| Key                | Env override                  | Default   | Purpose                                                            |
| ------------------ | ----------------------------- | --------- | ------------------------------------------------------------------ |
| `enabled`          | `PREST_OTEL_ENABLED`          | `false`   | Master switch — zero overhead and no outbound connections when off |
| `service_name`     | `PREST_OTEL_SERVICE_NAME`     | `prestd`  | Resource `service.name`                                            |
| `endpoint`         | `PREST_OTEL_ENDPOINT`         | *(unset)* | OTLP gRPC collector `host:port`                                    |
| `protocol`         | `PREST_OTEL_PROTOCOL`         | `grpc`    | Only `grpc` for now                                                |
| `sample_ratio`     | `PREST_OTEL_SAMPLE_RATIO`     | `1.0`     | Trace head sampling, clamped `0.0–1.0`                             |
| `metrics_interval` | `PREST_OTEL_METRICS_INTERVAL` | `15s`     | Metric export period                                               |
| `insecure`         | `PREST_OTEL_INSECURE`         | `false`   | Disable TLS to collector (local/dev)                               |
| `db_statement`     | `PREST_OTEL_DB_STATEMENT`     | `false`   | Record SQL text on DB spans (trusted environments only)            |

If the exporter can't be built at startup, pREST logs a warning and keeps serving with telemetry disabled — setup fails closed. A self-contained SigNoz stack lives under `dev/signoz/` in the pREST repo (`make signoz-up` / `make signoz-down`) for local viewing. See [v2.4.0 release notes](/releases/v2.4.0).

### CORS support

**Cross-Origin Resource Sharing**

See [CORS Support](/get-started/cors-support).

### Health check endpoints

| Endpoint       | Purpose   | Behavior                                                                                                                                      |
| -------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /_health` | Liveness  | Pings the default database. Returns 503 when unhealthy.                                                                                       |
| `GET /_ready`  | Readiness | Pings the default database and every registered alias ([#973](https://github.com/prest/prest/pull/973)). Use for Kubernetes readiness probes. |

For multi-database deployments, prefer `/_ready` over `/_health` as the readiness probe. See [Deploying with Docker](/deployment/deploying-with-docker).

## Related

* [Multi-database](/get-started/multi-database)
* [examples/multi-database-config.toml](https://github.com/prest/docs/tree/main/get-started/examples/multi-database-config.toml)
* [MCP over HTTP](/get-started/mcp-over-http)
* [pREST Studio](/get-started/prest-studio)
* [Custom Queries](/api-reference/custom-queries)
* [Auth](/api-reference/auth)
* [v2.4.2 release notes](/releases/v2.4.2)
* [v2.4.1 release notes](/releases/v2.4.1)
* [v2.4.0 release notes](/releases/v2.4.0)
* [Acronyms](/readme/acronyms) · [JWT](/readme/acronyms#jwt) · [MCP](/readme/acronyms#mcp)


# pREST Studio

pREST Studio — embedded admin UI at /\_studio/ for catalog browsing, REST GET explorer, and MCP tools. Enabled by default in v2.2.0+.

Open **pREST Studio** at `/_studio/` on the same host as your API. Studio is an embedded, read-only admin and explorer UI shipped inside the `prestd` binary (v2.2.0+) — no separate frontend process.

```
http://localhost:3000/_studio/
```

{% hint style="info" %}
Introduced in [v2.2.0](/releases/v2.2.0) ([#990](https://github.com/prest/prest/pull/990)). Enabled by default.
{% endhint %}

***

## Enable or disable

| Setting                | Default | Effect      |
| ---------------------- | ------- | ----------- |
| `PREST_STUDIO_ENABLED` | `true`  | Env toggle  |
| `[studio] enabled`     | `true`  | TOML toggle |

```toml
[studio]
enabled = true
```

When disabled, `/_studio/` returns **404**.

Studio is a **same-origin** client of the existing REST and MCP APIs. It does not add a separate auth model — use **Set token** in the header for the same JWT your API expects. Bearer tokens stay in memory by default (optional per-tab `sessionStorage`). Theme toggle is in the header as well.

***

## Overview

Server status (health, ready, MCP), catalog counts, metadata from `/_studio/api/meta`, and quick links into the explorers.

![pREST Studio Overview](/files/noJb2wlovgW8htA6pFMc)

***

## Data Explorer

Browse databases, schemas, and tables; inspect column structure; preview rows with filters and pagination. Marked **read-only** — copy the generated URL or `curl` when you need the same GET outside Studio.

![pREST Studio Data Explorer](/files/OWhiOyp6tIJnrakq7JTs)

***

## REST Explorer

Build and run GET requests against the pREST API: set the path, add query parameters, **Send**, then inspect status, timing, and body. **Copy URL** and **Copy curl** for the constructed request.

![pREST Studio REST Explorer](/files/JADdGPVqtjOk76fOhwLD)

***

## MCP Explorer

Connect to `/_mcp`, search and invoke tools with generated forms (or raw JSON), review results and in-memory history, and copy a ready-made config for AI clients (Claude, Cursor, and others).

![pREST Studio MCP Explorer](/files/XFklCs5PcAIHsbDl67ti)

***

## MVP limits

Studio does **not** (yet): mutate data, run a SQL editor, manage migrations or ACL UI, author MCP tools, or keep server-side request history.

For writes, use REST or [custom queries](/api-reference/custom-queries) when your deployment allows them.

***

## Related

* [v2.2.0 release notes](/releases/v2.2.0)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Configuring pREST](/get-started/configuring-prest)
* [Auth](/api-reference/auth)
* [API Reference](/api-reference)
* [Acronyms](/readme/acronyms) · [REST](/readme/acronyms#rest) · [MCP](/readme/acronyms#mcp)


# Multi-database

Run multiple SQL databases in one pREST instance — alias registry, Postgres and Timescale adapters, routing, pooling, and MCP.

Route CRUD, catalog, scripts, and MCP tools across **one or more** SQL databases from a single pREST process. The first URL path segment selects the database (legacy name or registered **alias**).

{% hint style="info" %}
**Multi-adapter (Postgres + Timescale auto-detect)** shipped in [**v2.3.0**](/releases/v2.3.0) ([#999](https://github.com/prest/prest/pull/999)). Registry multi-cluster on the **PostgreSQL** adapter shipped earlier in [v2.0.0](/releases/v2.0.0). MySQL / SQLite adapters are **roadmap**, not installable.
{% endhint %}

| Mode                              | When                                | `{database}` in URL    | Connection target                       |
| --------------------------------- | ----------------------------------- | ---------------------- | --------------------------------------- |
| **Legacy multi-DB**               | No registry configured              | Postgres database name | Same `pg.host`; `dbname` = path segment |
| **Registry multi-cluster**        | `[[databases]]` or env registry set | Registered **alias**   | Per-profile host, port, and credentials |
| **Multi-adapter** (v2.3.0 / #999) | Registry + Timescale detection      | Registered **alias**   | Postgres or Timescale adapter per alias |

```mermaid
flowchart LR
  req["GET /alias/schema/table"] --> mw[Adapter selector]
  mw --> reg[Adapter registry]
  reg --> pg[Postgres adapter]
  reg --> ts[TimescaleDB adapter]
  pg --> db1[(app_db)]
  ts --> db2[(metrics)]
```

***

## URL routing

All table operations use `/{database}/{schema}/{table}`:

```http
GET /tenant-a/public/users
POST /tenant-a/public/orders
GET /tenant-a/public
GET /_QUERIES/tenant-a/myqueries/get_all
```

With Postgres + Timescale aliases (v2.3.0 / #999):

```http
GET /postgres/public/users
GET /timescaledb/public/metrics
```

Script routes accept an optional database prefix (`/_QUERIES/{database}/{queriesLocation}/{script}`). When omitted, the default database (`pg.database`) is used.

Request flow: validate alias → select adapter (registry) → open or reuse pool → execute query.

***

## Configuration

Registry sources are merged in priority order: **indexed env pairs → TOML** (env wins on conflict).

Full sample (sanitized for public docs): [examples/multi-database-config.toml](https://github.com/prest/docs/tree/main/get-started/examples/multi-database-config.toml).

### Environment variables (production / Kubernetes)

Register databases with contiguous 1-based index pairs:

```sh
DATABASE_ALIAS_1=tenant-a
DATABASE_URL_1=postgres://user:pass@cluster-a.example.com:5432/app_a?sslmode=require
DATABASE_ALIAS_2=tenant-b
DATABASE_URL_2=postgres://user:pass@cluster-b.example.com:5432/app_b?sslmode=require
```

`PREST_DATABASE_ALIAS_N` and `PREST_DATABASE_URL_N` are accepted as aliases of the above keys.

See [`install-manifests/kubernetes/deployment.yaml`](https://github.com/prest/prest/blob/main/install-manifests/kubernetes/deployment.yaml) for a multi-secret example with liveness/readiness probes.

### TOML (local development)

`pg.*` remains the default/fallback profile; registry entries override host, port, and credentials per alias:

```toml
[pg]
database = "prest-test"
single = false

[[databases]]
alias = "postgres"
host = "localhost"
port = 5432
database = "app_db"
user = "prest"
pass = "prest"
maxopenconn = 10
maxidleconn = 2

[[databases]]
alias = "timescaledb"
host = "localhost"
port = 5432
database = "metrics"
user = "prest"
pass = "prest"
```

Since v2.3.0 (#999), startup **auto-detects** Timescale (extension present) vs Postgres for each alias. Unreachable aliases log a warning and are skipped; other aliases keep serving.

When no registry is configured, legacy `DATABASE_URL` / `pg.*` behavior is unchanged (single-adapter auto-detect since v2.3.0).

### Per-database SSL

```toml
[[databases]]
alias = "secure_db"
host = "secure.internal"
user = "prest"
pass = "password"
database = "app"

[databases.ssl]
mode = "require"
cert = "/etc/prest/certs/client.crt"
key = "/etc/prest/certs/client.key"
rootcert = "/etc/prest/certs/ca.crt"
```

### Environment variable reference

| Variable                 | Description                                           |
| ------------------------ | ----------------------------------------------------- |
| `DATABASE_ALIAS_N`       | Alias for the Nth registered database (1-based index) |
| `DATABASE_URL_N`         | Connection URL for the Nth registered database        |
| `PREST_DATABASE_ALIAS_N` | Accepted alias for `DATABASE_ALIAS_N`                 |
| `PREST_DATABASE_URL_N`   | Accepted alias for `DATABASE_URL_N`                   |

***

## Alias vs physical database name

* URLs and access rules use the **alias** (e.g. `tenant-a`).
* Connection pools use the profile's `database`, `host`, and credentials (e.g. `app_a` on `cluster-a.example.com`).
* When alias equals the physical database name (legacy mode), behavior matches pre-registry pREST.

***

## `pg.single`

Set `pg.single = false` to allow routing to multiple databases or aliases. When `true` and a registry is active, only the default database alias is accepted.

```toml
[pg]
single = false
```

Or via environment variable: `PREST_PG_SINGLE=false`.

***

## Timescale operators (v2.3.0)

When an alias is attached to the **TimescaleDB adapter**:

| Query param                    | Purpose                                      |
| ------------------------------ | -------------------------------------------- |
| `_time_bucket=1h`              | `GROUP BY time_bucket('1 hour', time)`       |
| `_time_bucket=1h,created_at`   | Same with a custom time column               |
| `_include_system_schemas=true` | Include `_timescaledb_*` schemas in listings |

Supported intervals: `5m`, `15m`, `1h`, `6h`, `1d`, `7d`, `30d`, `1y`.

```bash
curl "http://localhost:3000/timescaledb/public/metrics?_time_bucket=1h"
```

Continuous aggregates appear as queryable relations; create/manage them with SQL or [custom queries](/api-reference/custom-queries). See [TimescaleDB](/databases/timescaledb).

***

## Connection pooling

Pools are keyed by connection URI; aliases that share the same URI share a pool. Connections are opened lazily on first request per alias (or at startup when adapters register, since v2.3.0 / #999).

Configure `maxopenconn` / `maxidleconn` per `[[databases]]` entry. **Budget:** `replicas × aliases × maxopenconn` against each cluster. Use PgBouncer or RDS Proxy when many aliases are registered.

***

## Health checks

| Endpoint       | Purpose   | Behavior                                              |
| -------------- | --------- | ----------------------------------------------------- |
| `GET /_health` | Liveness  | Pings the default database                            |
| `GET /_ready`  | Readiness | Pings the default database and every registered alias |

Use `/_ready` for Kubernetes readiness probes when multiple databases are registered. See [Configuring pREST — Health check](/get-started/configuring-prest#health-check-endpoints).

Unknown or unregistered aliases return **404**.

***

## Access control

`access.tables` entries support optional `database` and `schema` fields for per-alias permissions:

```toml
[[access.tables]]
database = "tenant-a"
schema = "public"
name = "users"
permissions = ["read"]
```

When a database registry is active, permissions are matched against alias + schema + table name. See [Permissions](/get-started/permissions#table-permissions).

***

## MCP and aliases

With [MCP over HTTP](/get-started/mcp-over-http) (v2.1.0+), registered aliases appear in MCP tools the same way they do in REST paths:

| Tool                                       | Multi-database behavior                                 |
| ------------------------------------------ | ------------------------------------------------------- |
| `prest.list_databases`                     | Lists registered aliases (and the default database)     |
| `prest.select.{database}.{schema}.{table}` | `{database}` is the alias when a registry is configured |
| `prest.select_table` / catalog tools       | Accept a database/alias argument where applicable       |

Auth and ACL still apply — MCP is read-only but otherwise shares the HTTP stack.

***

## Migrating from single to multi-database

1. Keep `[pg]` for local/default compatibility.
2. Add `[[databases]]` (or `DATABASE_ALIAS_N` / `DATABASE_URL_N`) with clear aliases (`primary`, `metrics`).
3. Point clients at `/{alias}/...` instead of the physical database name when using the registry.
4. Since v2.3.0 (#999), confirm Timescale aliases get `_time_bucket` if you need time-series grouping.

Single-database deployments without `[[databases]]` keep working.

***

## Local testing

Multi-cluster integration tests live in [`integration/controllers/multicluster_test.go`](https://github.com/prest/prest/blob/main/integration/controllers/multicluster_test.go). They require a second Postgres service (`PREST_PG_HOST_B`) provided by [`docker-compose-test.yml`](https://github.com/prest/prest/blob/main/docker-compose-test.yml):

```bash
make test-integration
```

Timescale E2E: `make test-integration-timescaledb`. See the [Development Guide](/get-prest/development-guide#integration-tests).

## Related

* [examples/multi-database-config.toml](https://github.com/prest/docs/tree/main/get-started/examples/multi-database-config.toml)
* [Configuring pREST](/get-started/configuring-prest)
* [TimescaleDB](/databases/timescaledb)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Permissions](/get-started/permissions)
* [PR #999](https://github.com/prest/prest/pull/999)
* [Acronyms](/readme/acronyms) · [MCP](/readme/acronyms#mcp) · [SQL](/readme/acronyms#sql)


# MCP over HTTP

pREST MCP over HTTP — Model Context Protocol (MCP) read-only tools at /\_mcp for AI agents to discover schemas and query SQL tables on the same server as REST.

pREST v2.1.0+ exposes a read-only [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) endpoint at `/_mcp` on the same server that already serves CRUD, catalog, and custom script routes. MCP clients can discover schema and read table data through JSON-RPC tools — without a separate process or transport.

Today MCP runs on the **Native PostgreSQL adapter** and [Postgres-compatible engines](/databases) you connect with that adapter. Other SQL families are on the [roadmap](/databases/roadmap).

Introduced in [v2.1.0](/releases/v2.1.0) ([#977](https://github.com/prest/prest/pull/977)).

***

## What is MCP?

**MCP (Model Context Protocol)** is an [open standard](https://modelcontextprotocol.io/) for connecting AI applications and agents to external tools and data sources. Instead of each product inventing a private plugin format, MCP defines how clients discover tools and call them (for example over JSON-RPC).

AI agents, IDEs, and other MCP-capable clients use that protocol to list available tools and run them with structured arguments. pREST implements a **read-only** MCP surface at `/_mcp` so those clients can:

* Discover databases, schemas, and tables
* Describe columns
* Select rows (with limits)

through the **same** `prestd` process, auth, ACL, and database routing as the REST API.

MCP in pREST is **not** a separate database, a second port, or a write/DDL interface. In v2.1.0 it does not insert, update, delete, or run arbitrary admin SQL — use REST or curated `/_QUERIES` scripts for writes when your deployment allows them.

***

## What is AI (in these docs)?

**Artificial intelligence (AI)** here means applications and agents that use models to reason and act on tools and data. pREST connects them to your SQL catalog through read-only MCP at `/_mcp` and the same auth/ACL as the REST API.

Typical clients are AI agents, IDEs, and other tools that speak MCP. For client setup (adapters, IDE config), see [docs.prestd.com/ai](https://docs.prestd.com/ai). pREST is the API layer those clients call — it is not itself an LLM or AI model.

***

## How it fits in pREST

The MCP endpoint reuses the existing pREST request pipeline:

```
MCP client → GET or POST /_mcp → auth / ACL → catalog & query execution → SQL database (PG family today)
```

That means MCP inherits the same deployment model, authentication, access control, and database routing as the REST API. There is no second port to configure: MCP runs in-process on the same `prestd` HTTP server.

Many AI clients (Cursor, Claude Desktop, and others) expect a **stdio** MCP process rather than HTTP. For those, use the official [pREST MCP Adapter](/ai/install-prest-mcp) (`prest-mcp`) — a tiny stdio ↔ HTTP bridge. The adapter does not implement tools; pREST still owns schema discovery and queries. See [AI and MCP](/ai) for install and client tutorials.

| Concern        | Behavior                                                                 |
| -------------- | ------------------------------------------------------------------------ |
| Server process | Same `prestd` instance as REST                                           |
| Authentication | HTTP auth stack — `/_mcp` is protected when auth is enabled              |
| Permissions    | `access.tables` and per-user rules apply to tool discovery and execution |
| Multi-database | Database aliases from the registry are used in tool names and arguments  |
| Writes         | **Not supported** in v2.1.0 — read-only by design                        |

***

## Endpoint shape

| Method | Path    | Purpose                                                    |
| ------ | ------- | ---------------------------------------------------------- |
| `GET`  | `/_mcp` | Discovery payload with server metadata and available tools |
| `POST` | `/_mcp` | JSON-RPC 2.0 requests for MCP operations                   |

Supported JSON-RPC methods:

| Method       | Description                                               |
| ------------ | --------------------------------------------------------- |
| `initialize` | Returns server info, capabilities, and usage instructions |
| `tools/list` | Returns the full tool catalog with `inputSchema` per tool |
| `tools/call` | Executes a named tool with optional arguments             |

Unsupported methods return `400 Bad Request` with `unsupported method`.

***

## Discovery (`GET /_mcp`)

A `GET` request returns a JSON document describing the server and all tools visible to the current caller (after auth and ACL filtering):

```json
{
  "name": "prest",
  "protocol": "0.1",
  "endpoint": "/_mcp",
  "description": "Read-only MCP endpoint backed by pREST catalog and query execution.",
  "tools": [
    {
      "name": "prest.list_databases",
      "description": "List accessible databases.",
      "inputSchema": { "type": "object", "properties": {} }
    }
  ],
  "capabilities": {
    "tools": { "listChanged": false }
  }
}
```

Use discovery to inspect available tools and their typed `inputSchema` before calling them.

***

## Tools reference

{% hint style="info" %}
Since **v2.4.1** ([#1016](https://github.com/prest/prest/pull/1016)), the three `prest.list_*` tools honour the `[expose]` settings. A denied listing is dropped from discovery and its `tools/call` returns `400` with `unauthorized listing`. See [Safety and limits](#safety-and-limits).
{% endhint %}

### `prest.list_databases`

List database aliases accessible to the current caller.

**Requires:** `expose.databases` (v2.4.1+).

**Arguments:** none.

**Returns:** array of database objects. In registry multi-database mode, each entry includes:

| Field           | Description                                           |
| --------------- | ----------------------------------------------------- |
| `name`          | Registered alias (used in URLs and tool names)        |
| `datname`       | Same as `name`                                        |
| `physical_name` | Physical Postgres database name on the target cluster |

In legacy single-host mode, returns databases from the catalog query.

***

### `prest.list_schemas`

List readable schemas for a database alias.

**Requires:** `expose.schemas` (v2.4.1+).

**Arguments:**

| Field      | Required | Description                                            |
| ---------- | -------- | ------------------------------------------------------ |
| `database` | No       | Database alias; defaults to `pg.database` when omitted |

**Returns:** array of schema objects (filtered by `access.tables` permissions).

***

### `prest.list_tables`

List readable tables for a database alias and optional schema.

**Requires:** `expose.tables` (v2.4.1+).

**Arguments:**

| Field      | Required | Description                                                       |
| ---------- | -------- | ----------------------------------------------------------------- |
| `database` | No       | Database alias; defaults to `pg.database` when omitted            |
| `schema`   | No       | Filter to a single schema; omit to list across accessible schemas |

**Returns:** array of table objects with `name`, `schema`, and related metadata.

***

### `prest.describe_table`

Return column metadata for a table.

**Arguments:**

| Field      | Required | Description                                            |
| ---------- | -------- | ------------------------------------------------------ |
| `database` | No       | Database alias; defaults to `pg.database` when omitted |
| `schema`   | Yes      | Schema name                                            |
| `table`    | Yes      | Table name                                             |

**Returns:**

```json
{
  "database": "prest-test",
  "schema": "public",
  "table": "test",
  "columns": [
    {
      "name": "id",
      "data_type": "integer",
      "nullable": false,
      "position": 1
    }
  ],
  "count": 1
}
```

Column objects may also include `max_length`, `generated`, `updatable`, and `default_value` when available from the catalog.

***

### `prest.select_table`

Read rows from a table using a generic tool name.

**Arguments:**

| Field      | Required | Description                                                         |
| ---------- | -------- | ------------------------------------------------------------------- |
| `database` | No       | Database alias; defaults to `pg.database` when omitted              |
| `schema`   | Yes      | Schema name                                                         |
| `table`    | Yes      | Table name                                                          |
| `columns`  | No       | Project specific columns; omit to select all permitted columns      |
| `filters`  | No       | Column-aware equality filters (see below)                           |
| `order_by` | No       | Sort fields — use `field` for ascending, `-field` for descending    |
| `limit`    | No       | Row limit (1–100); defaults to **100** when omitted or out of range |
| `offset`   | No       | Row offset; defaults to **0**                                       |

**Returns:**

```json
{
  "database": "prest-test",
  "schema": "public",
  "table": "Reply",
  "columns": ["id", "name"],
  "rows": [{ "id": 1, "name": "prest tester" }],
  "count": 1
}
```

***

### `prest.select.{database}.{schema}.{table}`

Schema-aware tools generated from the catalog at startup. Each readable table gets its own tool with a typed `inputSchema` derived from column metadata.

**Tool name pattern:**

```
prest.select.{database}.{schema}.{table}
```

Example: `prest.select.prest-test.public.Reply`

**Arguments:** same as `prest.select_table`, except `database`, `schema`, and `table` are encoded in the tool name and cannot be overridden.

The generated `inputSchema` exposes:

* allowed `columns` (enum of permitted column names)
* allowed `order_by` values (`field` and `-field` for each column)
* column-aware `filters` with types matching each column's data type
* `limit` (integer, 1–100) and `offset` (integer, minimum 0)

In multi-database mode, tools are generated for every registered alias. Tables the caller cannot read are omitted from the tool list.

{% hint style="warning" %}
Since **v2.4.1**, if `[expose] enabled = true` and **any** of `databases` / `schemas` / `tables` is `false`, these per-table tools are **not generated at all** — their names embed database, schema, and table names and their descriptions embed column names, so enumerating them would leak the catalog that `[expose]` is hiding. Clients that relied on auto-discovered per-table tools should use the generic `prest.select_table` instead.
{% endhint %}

***

## Examples

### Initialize

```http
POST /_mcp
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize"
}
```

Response includes `serverInfo`, `capabilities`, and `instructions`:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "serverInfo": { "name": "prest", "version": "0.1" },
    "capabilities": { "tools": { "listChanged": false } },
    "instructions": "Read-only tools are exposed through pREST auth and ACL."
  }
}
```

### List tools

```http
POST /_mcp
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list"
}
```

### Describe a table

```http
POST /_mcp
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "prest.describe_table",
    "arguments": {
      "database": "prest-test",
      "schema": "public",
      "table": "test"
    }
  }
}
```

### Schema-aware select

```http
POST /_mcp
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "prest.select.prest-test.public.Reply",
    "arguments": {
      "columns": ["id", "name"],
      "filters": { "name": "prest tester" },
      "order_by": ["id"],
      "limit": 5,
      "offset": 0
    }
  }
}
```

### List databases

```http
POST /_mcp
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "prest.list_databases"
  }
}
```

***

## Authentication and permissions

When [auth](/api-reference/auth) is enabled, `/_mcp` requires the same credentials as other protected routes. Send the appropriate `Authorization` header (or basic auth, depending on your configuration) with every `GET` and `POST` request.

[Permissions](/get-started/permissions) apply to MCP the same way they apply to REST:

* Tool discovery only lists tables and columns the caller can read.
* `tools/call` on a table without read permission returns an error.
* Per-user `[[access.users]]` rules are respected.

If a tool list appears empty or a select returns a permission error, check your `access.tables` configuration.

***

## Multi-database

MCP tools use **database aliases** — the same identifiers used in REST URLs (`/{database}/{schema}/{table}`). When the `database` argument is omitted, pREST uses the default database (`pg.database`).

| Mode                   | `database` in tools    | See also                                      |
| ---------------------- | ---------------------- | --------------------------------------------- |
| Legacy multi-DB        | Postgres database name | [Multi-database](/get-started/multi-database) |
| Registry multi-cluster | Registered alias       | [Multi-database](/get-started/multi-database) |

`prest.list_databases` returns both the alias (`name`) and `physical_name` in registry mode, so MCP clients can distinguish routing aliases from cluster database names.

When `pg.single = true` and a registry is active, only the default database alias is accepted.

***

## Safety and limits

| Rule                        | Detail                                                                                                             |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Read-only                   | No insert, update, delete, or DDL tools in v2.1.0                                                                  |
| Row cap                     | Maximum **100** rows per select (`limit` defaults to 100)                                                          |
| Identifier validation       | Database, schema, table, column, and filter names are validated                                                    |
| Unsupported tools           | Return `400 Bad Request` with `unsupported tool`                                                                   |
| Catalog discovery (v2.4.1+) | `[expose]` applies to `/_mcp`, not just the REST listing routes — see below                                        |
| Custom queries              | `/_QUERIES` scripts are not exposed through MCP                                                                    |
| Separate process            | MCP runs in-process on `prestd`; optional [stdio adapter](/ai/install-prest-mcp) for clients that cannot call HTTP |

Calling a write tool (for example `prest.drop_table`) returns:

```json
{
  "error": {
    "message": "unsupported tool: prest.drop_table"
  }
}
```

### Catalog discovery and `[expose]` (v2.4.1)

Before **v2.4.1**, `/_mcp` never passed through the exposure middleware — a deployment that disabled the `/databases`, `/schemas`, or `/tables` REST routes still served its entire catalog to any MCP client. Since [#1016](https://github.com/prest/prest/pull/1016), the MCP handler consults the same settings.

| Setting                                            | Effect on `/_mcp`                                                         |
| -------------------------------------------------- | ------------------------------------------------------------------------- |
| `expose.enabled = false` (default)                 | Everything is listable — no change from v2.4.0                            |
| `expose.databases = false` (with `enabled = true`) | `prest.list_databases` dropped from discovery; `tools/call` returns `400` |
| `expose.schemas = false` (with `enabled = true`)   | `prest.list_schemas` dropped from discovery; `tools/call` returns `400`   |
| `expose.tables = false` (with `enabled = true`)    | `prest.list_tables` dropped from discovery; `tools/call` returns `400`    |
| **Any** of the three denied                        | All `prest.select.{database}.{schema}.{table}` tools withheld             |

A denied listing returns a JSON-RPC error rather than a bare HTTP body:

```json
{"jsonrpc":"2.0","id":1,"error":{"code":400,"message":"unauthorized listing"}}
```

The REST routes return **401** for the same message — same rule, different surface.

`[expose]` governs **discovery**; `[access]` governs **reads**. `prest.describe_table` and `prest.select_table` are always advertised, so a client that already knows a table name can still read it subject to [permissions](/get-started/permissions). Configuration: [Configuring pREST — Expose Data](/get-started/configuring-prest#expose-data).

***

## Troubleshooting

| Symptom                                  | Likely cause                                                                                                               |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `401` / `403` on `/_mcp`                 | Auth enabled but credentials missing or invalid — see [Auth](/api-reference/auth)                                          |
| `400 unsupported tool`                   | Tool name not in the catalog or write operation attempted                                                                  |
| `400 unsupported method`                 | JSON-RPC method other than `initialize`, `tools/list`, or `tools/call`                                                     |
| `400 unsupported column`                 | `columns`, `filters`, or `order_by` references a column not permitted or not on the table                                  |
| Empty tool list                          | ACL hides all tables, or catalog is unreachable for registered aliases                                                     |
| `400 unauthorized listing`               | The listing is denied by `[expose]` (v2.4.1+) — see [Catalog discovery and `[expose]`](#catalog-discovery-and-expose-v241) |
| Per-table `prest.select.*` tools missing | `[expose]` is active with at least one listing denied (v2.4.1+); use `prest.select_table`                                  |
| Permission error on select               | Caller lacks read access to the table or specific columns — see [Permissions](/get-started/permissions)                    |
| `invalid identifier in path`             | Schema or table name failed identifier validation                                                                          |

For integration test examples, see [`integration/controllers/mcp_test.go`](https://github.com/prest/prest/blob/v2.1.0/integration/controllers/mcp_test.go) in the prest repository.

***

## Client tutorials

* [AI and MCP](/ai) — landing for agents and IDEs
* [Install pREST MCP Adapter](/ai/install-prest-mcp) — Homebrew, Go, Docker, MCP Registry
* [Use with Cursor](/ai/cursor) · [Claude Desktop](/ai/claude-desktop) · [Other AI tools](/ai/other-clients)
* [Read-only PostgreSQL for AI](/ai/read-only-postgres)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)
* [Start with Homebrew](/get-prest/start-with-homebrew) — `prestd` and `prest/tap/prest-mcp`

## Related documentation

* [Acronyms](/readme/acronyms) ([MCP](/readme/acronyms#mcp), [AI](/readme/acronyms#ai), [REST](/readme/acronyms#rest))
* [MCP Overview](/ai/mcp-overview)
* [v2.1.0 release notes](/releases/v2.1.0)
* [Multi-database](/get-started/multi-database)
* [Permissions](/get-started/permissions)
* [Auth](/api-reference/auth)
* [API Reference](/api-reference)


# pREST MCP Adapter

This page moved to the AI and MCP section.

**→** [**Install pREST MCP Adapter**](/ai/install-prest-mcp)

Also see:

* [MCP Overview](/ai/mcp-overview)
* [AI and MCP landing](/ai)
* Protocol reference: [MCP over HTTP](/get-started/mcp-over-http)


# Connect Cursor to pREST MCP

This page moved to the AI and MCP section.

**→** [**Use with Cursor**](/ai/cursor)

Also see:

* [pREST for Cursor plugin](/ai/prest-cursor)
* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [AI and MCP landing](/ai)


# Connect Claude Desktop to pREST MCP

This page moved to the AI and MCP section.

**→** [**Use with Claude Desktop**](/ai/claude-desktop)

Also see:

* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [AI and MCP landing](/ai)


# Upgrading to v2

This guide covers migrating from pREST v1 to v2.

* **Latest stable v2:** [v2.4.2](https://github.com/prest/prest/releases/tag/v2.4.2)
* **Docker:** `prest/prest:v2.4.2`

See [Releases](/releases) for the full changelog and [v2.4.2 release notes](/releases/v2.4.2). If you are still on v2.2.0 or earlier, note that [v2.3.0](/releases/v2.3.0) included a critical `_select` SQL-injection fix — upgrade promptly.

{% hint style="danger" %}
**Before deploying v2.4.2, check your `jwt.key` length.** From v2.4.2 an HMAC key below the RFC 7518 minimum (32 bytes for HS256, 48 for HS384, 64 for HS512) is discarded at startup. pREST does not refuse to start — it unregisters `/auth` and serves requests unauthenticated. See [Auth — HMAC key requirements](/api-reference/auth#hmac-key-requirements-v242) and [v2.4.2 — Upgrading](/releases/v2.4.2#upgrading-from-v241).
{% endhint %}

MCP over HTTP requires v2.1.0 or later. Studio requires v2.2.0 or later — [pREST Studio](/get-started/prest-studio). For Cursor, Claude Desktop, and other stdio clients, install the [pREST MCP Adapter](/get-started/prest-mcp-adapter) (`brew install prest/tap/prest-mcp`).

***

## 1. Choose your build

**v2.4.2 (recommended):**

* **Binary:** [v2.4.2 release assets](https://github.com/prest/prest/releases/tag/v2.4.2)
* **Docker:** `prest/prest:v2.4.2`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.4.2`

**v2.4.0:**

* **Binary:** [v2.4.0 release assets](https://github.com/prest/prest/releases/tag/v2.4.0)
* **Docker:** `prest/prest:v2.4.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.4.0`

**v2.3.0:**

* **Binary:** [v2.3.0 release assets](https://github.com/prest/prest/releases/tag/v2.3.0)
* **Docker:** `prest/prest:v2.3.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.3.0`

**v2.2.0:**

* **Binary:** [v2.2.0 release assets](https://github.com/prest/prest/releases/tag/v2.2.0)
* **Docker:** `prest/prest:v2.2.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.2.0`

**v2.1.0:**

* **Binary:** [v2.1.0 release assets](https://github.com/prest/prest/releases/tag/v2.1.0)
* **Docker:** `prest/prest:v2.1.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.1.0`

**v2.0.0:**

* **Binary:** [v2.0.0 release assets](https://github.com/prest/prest/releases/tag/v2.0.0)
* **Docker:** `prest/prest:v2.0.0`
* **Go install:** `go install github.com/prest/prest/v2/cmd/prestd@v2.0.0`

**Tip of `main` branch** (for development only):

```sh
go install github.com/prest/prest/v2/cmd/prestd@main
```

> **Note:** Windows ARM binaries are not published ([#970](https://github.com/prest/prest/pull/970)).

***

## 2. Set `PREST_VERSION=2`

Set the environment variable `PREST_VERSION=2` (recommended for all new v2 deployments). This enables v2 naming conventions for configuration, especially PostgreSQL SSL settings.

```sh
export PREST_VERSION=2
```

***

## 3. Migrate SSL environment variables

v2 removed the deprecated `PREST_SSL_*` variables (rc2, [#919](https://github.com/prest/prest/pull/919)). Rename them to the `PREST_PG_SSL_*` equivalents:

| v1 (removed)         | v2                      |
| -------------------- | ----------------------- |
| `PREST_SSL_MODE`     | `PREST_PG_SSL_MODE`     |
| `PREST_SSL_CERT`     | `PREST_PG_SSL_CERT`     |
| `PREST_SSL_KEY`      | `PREST_PG_SSL_KEY`      |
| `PREST_SSL_ROOTCERT` | `PREST_PG_SSL_ROOTCERT` |

In TOML, use `[pg.ssl]` instead of the top-level `[ssl]` block:

```toml
[pg.ssl]
mode = "require"
sslcert = "./PATH"
sslkey = "./PATH"
sslrootcert = "./PATH"
```

**Default change:** when no config file is found, v2 defaults `pg.ssl.mode` to `disable` (v1 used `require`). Set `PREST_PG_SSL_MODE=require` explicitly if you relied on the v1 default.

***

## 4. Configure JWT verification

JWT behavior depends on which build you run:

| Build                           | Missing JWT key when `jwt.default = true`                                                         |
| ------------------------------- | ------------------------------------------------------------------------------------------------- |
| **v2.0.0+** (current)           | JWT middleware **auto-disabled** with error log ([#974](https://github.com/prest/prest/pull/974)) |
| **v2.0.0-rc6 tag** (historical) | Server **refuses to start** ([#960](https://github.com/prest/prest/pull/960))                     |

In **v2+**, `jwt.default` defaults to **`false`** — set `jwt.default = true` explicitly to enable JWT enforcement.

**Options for production:**

1. **Provide verification material** (recommended):

```sh
export PREST_JWT_KEY="your-secret-key"
```

Or configure JWKS / well-known URL via `PREST_JWT_JWKS` / `PREST_JWT_WELLKNOWNURL`.

2. **Disable JWT middleware:**

```toml
[jwt]
default = false
```

3. **Use debug mode** (development only):

```sh
export PREST_DEBUG=true
```

Always check startup logs to confirm JWT and auth are in the expected state.

The JWT whitelist default is regex `^\/auth$` (v1 used `[/auth]`). Review your whitelist if you customized it.

See [Configuring pREST — JWT](/get-started/configuring-prest#jwt) and [Auth](/api-reference/auth).

***

## 5. Multi-database

v2.0.0 includes full multi-database support. Configure a database registry for multi-cluster routing. See the [Multi-database guide](/get-started/multi-database).

Set `pg.single = false` and define `[[databases]]` entries or `DATABASE_ALIAS_N` / `DATABASE_URL_N` environment pairs.

Use `GET /_ready` for Kubernetes readiness probes when running multiple databases.

***

## 6. Review identifier validation

rc4 hardened identifier validation ([GHSA-p46v-f2x8-qp98](https://github.com/prest/prest/security/advisories/GHSA-p46v-f2x8-qp98)). Invalid field or table names in query parameters are now rejected. Test your existing API calls.

***

## 7. Review auth encryption default

v2 defaults `auth.encrypt` to `bcrypt` (v1 used `MD5`). If you use the built-in `/auth` endpoint with existing password hashes, ensure compatibility or update the `encrypt` setting.

***

## 8. Test new v2 features

If applicable, verify:

* **OR filtering:** `_or=field=$eq.a||field=$eq.b` (see [Parameters](/api-reference/parameters))
* **Per-user permissions:** `[[access.users]]` (see [Permissions](/get-started/permissions#user-level-permissions))
* **Multi-database:** alias routing (see [Multi-database](/get-started/multi-database))
* **MCP over HTTP:** `GET` / `POST /_mcp` (v2.1.0+; see [MCP over HTTP](/get-started/mcp-over-http))
* **pREST Studio:** open `/_studio/` (v2.2.0+; see [pREST Studio](/get-started/prest-studio))
* **Database-backed queries:** optional `queries.storage = "database"` (v2.2.0+; see [Custom Queries](/api-reference/custom-queries))
* **Logging:** `PREST_LOG_LEVEL=debug` for structured JSON logs

***

## 9. Docker-specific checklist

```sh
docker run -d -p 3000:3000 \
    -e PREST_VERSION=2 \
    -e PREST_PG_URL=postgres://username:password@hostname:port/dbname \
    -e PREST_JWT_KEY=a-secret-key-of-at-least-32-bytes \
    prest/prest:v2.4.2
```

For local development without JWT, add `-e PREST_DEBUG=true`.

In **v2+**, the server starts even without `PREST_JWT_KEY`, but JWT will be auto-disabled — check logs.

See [Deploying with Docker](/deployment/deploying-with-docker).


# CORS Support

[Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources. CORS also relies on a mechanism by which browsers make a "preflight" request to the server hosting the cross-origin resource, in order to check that the server will permit the actual request. In that preflight, the browser sends headers that indicate the HTTP method and headers that will be used in the actual request.

There are two settings to be made for releasing CORS (Cross-Origin Resource Sharing) in pretsd, **source** and **method**.

In the `prest.toml` you can configure the CORS allowed origin.

Example:

```toml
[cors]
alloworigin = ["https://prestd.com", "http://foo.com"]
allowheaders = ["Content-Type"]
allowmethods = ["GET", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"]
```

> if you want to release all origins just use asterisk `*` as the org item, thus: `alloworigin = ["*"]`


# Cache

Simple caching system to cache the API return in the http **GET method**, ⚠ *by default the caching system is disabled*.

We use key and value database embedded in *prestd* ([BuntDB](https://github.com/tidwall/buntdb)).

We have a timeout system (TTL) for the cached data, by default it is kept for `10 minutes` - with the possibility to change it in the settings.

### Data Architecture

For each URI (with its parameters) a *BuntDB* database cache file is created.

> It was implemented this way with performance in mind - there is no point in putting in a caching system that is slower than the SQL query in PostgreSQL.

* **key:** URI with all string query parameters
* **value:** json return (http body)

#### Because BuntDB

Is a low-level, in-memory, key/value store in pure Go. It persists to disk, is ACID compliant, and uses locking for multiple readers and a single writer. It supports custom indexes and geospatial data. It's ideal for projects that need a dependable database and favor speed over data size.

We didn't want to depend on an external database (and we can't create tables in the existing database), with this premise we decided to use an embedded database (write in Go language) and BuntDB proved to be the best option at the moment, [here you can see the discussion existing since **2017**](https://github.com/prest/prest/issues/112).

### Configuration for specific endpoint *("advanced")*

Activating the caching system all endpoints in your api will have the caching system active, following the defined default configuration.

You can customize the configuration made for one (or more) specific endpoints, for example:

* `/prest/public/my-table` I want more caching time
* `/prest/public/my-table-uncached` I don't want caching, *I need the data that is in the database in "real time*

For this configuration, you must use the TOML's `[[cache.endpoints]]` node (because it is an array it is not possible to configure it via an environment variable).

### Environment vars

| var                       | default          | description                                    |
| ------------------------- | ---------------- | ---------------------------------------------- |
| PREST\_CACHE\_ENABLED     | false            | embedded cache system                          |
| PREST\_CACHE\_TIME        | 10               | TTL in minute (time to live)                   |
| PREST\_CACHE\_STORAGEPATH | ./               | path where the cache file will be created      |
| PREST\_CACHE\_SUFIXFILE   | .cache.prestd.db | suffix of the name of the file that is created |

### TOML

Optionally the pREST can be configured by TOML file.

You can follow this sample and create your own `prest.toml` file and put this on the same folder that you run `prestd` command.

```toml
[cache]
enabled = true
time = 10
storagepath = "./"
sufixfile = ".cache.prestd.db"

    [[cache.endpoints]]
    endpoint = "/prest/public/test"
    time = 5

    # this endpoint will have no caching system
    [[cache.endpoints]]
    enabled = false
    endpoint = "/prest/public/test-disable"
```


# Migrating

Migrations are the ***prestd***'s way of propagating database changes (adding a field, deleting a model table, bulk data modification, etc.). They are designed to be mostly automatic, but you will need to know when to do migrations, when to run them, and the common problems you may run into.

We use *SQL files* for evolution and regration, so you can upgrade and downgrade.

`--url` and `--path` flags are optional if pREST configurations already set.

**apply all available migrations:**

```sh
prestd migrate --url driver://url --path ./migrations up
```

**roll back all migrations:**

```sh
prestd migrate --url driver://url --path ./migrations down
```

**roll back the most recently applied migration, then run it again:**

```sh
prestd migrate --url driver://url --path ./migrations redo
```

**run down and then up command:**

```sh
prestd migrate --url driver://url --path ./migrations reset
```

**show the current migration version:**

```sh
prestd migrate --url driver://url --path ./migrations version
```

**apply the next n migrations:**

```sh
prestd migrate --url driver://url --path ./migrations next +1
prestd migrate --url driver://url --path ./migrations next +2
prestd migrate --url driver://url --path ./migrations next +n
```

**roll back the previous n migrations:**

```sh
prestd migrate --url driver://url --path ./migrations next -1
prestd migrate --url driver://url --path ./migrations next -2
prestd migrate --url driver://url --path ./migrations next -n
```

**create or remove default pREST authentication table:**

```sh
prestd migrate up auth
prestd migrate down auth
```

### Filename Format

A single logical migration is represented as two separate migration files, one to migrate "up" to the specified version from the previous version, and a second to migrate back "down" to the previous version. These migrations can be provided by any one of the supported migration sources.

The ordering and direction of the migration files is determined by the filenames used for them. `migrate` expects the filenames of migrations to have the format:

```
{version}_{title}.up.sql
{version}_{title}.down.sql
```

The `title` of each migration is unused, and is only for readability. Similarly, the `extension` of the migration files is not checked by the library, and should be an appropriate format for the database in use (`.sql` for SQL variants, for instance).

Versions of migrations may be represented as any 64 bit unsigned integer. All migrations are applied upward in order of increasing version number, and downward by decreasing version number.

Common versioning schemes include incrementing integers:

```
1_initialize_schema.down.sql
1_initialize_schema.up.sql
2_add_table.down.sql
2_add_table.up.sql
...
```

Or timestamps at an appropriate resolution:

```
1500360784_initialize_schema.down.sql
1500360784_initialize_schema.up.sql
1500445949_add_table.down.sql
1500445949_add_table.up.sql
...
```

But any scheme resulting in distinct, incrementing integers as versions is valid.

It is suggested that the version number of corresponding `up` and `down` migration files be equivalent for clarity, but they are allowed to differ so long as the relative ordering of the migrations is preserved.

The migration files are permitted to be "empty", in the event that a migration is a no-op or is irreversible. It is recommended to still include both migration files by making the whole migration file consist of a comment. If your database does not support comments, then deleting the migration file will also work. Note, an actual empty file (e.g. a 0 byte file) may cause issues with your database since migrate will attempt to run an empty query. In this case, deleting the migration file will also work.

### Content Format

The format of the migration files themselves varies between database systems. Different databases have different semantics around schema changes and when and how they are allowed to occur (for instance, [if schema changes can occur within a transaction](https://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis)).

As such, the `migrate` library has little to no checking around the format of migration sources. The migration files are generally processed directly by the drivers as raw operations.

### Reversibility of Migrations

Best practice for writing schema migration is that all migrations should be reversible. It should in theory be possible for run migrations down and back up through any and all versions with the state being fully cleaned and recreated by doing so.

By adhering to this recommended practice, development and deployment of new code is cleaner and easier (cleaning database state for a new feature should be as easy as migrating down to a prior version, and back up to the latest).

As opposed to some other migration libraries, `migrate` represents up and down migrations as separate files. This prevents any non-standard file syntax from being introduced which may result in unintended behavior or errors, depending on what database is processing the file.

While it is technically possible for an up or down migration to exist on its own without an equivalently versioned counterpart, it is strongly recommended to always include a down migration which cleans up the state of the corresponding up migration.


# Permissions

How to manage tables read/writes

This guide will allow you to understand how `pREST` to understand and manage your table's permission management and how you can tailor it to your needs by using the `prest.toml` file.

## Restrict mode

The `prest.toml` file allows you to configure each table's read/write/delete permissions.

```toml
[access]
restrict = true  # can access only the tables listed below
```

`restrict = false`: (default) The pREST will serve in public mode. You can write/read/delete every data without configuring permissions.

`restrict = true`: you need to configure the permissions of all tables.

## Ignore table

If you need to ignore restricted access mode for some tables, you can use the `ignore_table` option, it receives a string list with the names of the tables to be *"ignored"*, by **default,** is an empty list `[]`.

```toml
[access]
restrict = true
ignore_table = ["news"]
```

## Table permissions

Example:

```toml
[[access.tables]]
name = "test"
permissions = ["read", "write", "delete"]
fields = ["id", "name"]
```

When a [database registry](/get-started/multi-database) is active, use optional `database` and `schema` fields to scope permissions to a specific alias:

```toml
[[access.tables]]
database = "tenant-a"
schema = "public"
name = "users"
permissions = ["read"]
fields = ["id", "name"]
```

Permissions are matched against **alias + schema + table name** when the registry is configured. When `database` is omitted, the rule applies to all aliases (legacy behavior).

Multiple configurations for the same table:

```toml
[access]
restrict = true  # can access only the tables listed below

[[access.tables]]
name = "test"
permissions = ["read"]
fields = ["id", "name"]
[[access.tables]]
name = "test"
permissions = ["write"]
fields = ["name"]
```

| attribute   | description                                                                                      |
| ----------- | ------------------------------------------------------------------------------------------------ |
| name        | Table name                                                                                       |
| database    | Optional. Database alias when using a registry ([#973](https://github.com/prest/prest/pull/973)) |
| schema      | Optional. Schema name (default matching applies when omitted)                                    |
| permissions | Table permissions. Options: `read`, `write` and `delete`                                         |
| fields      | Exposed fields permitted for operations                                                          |

### Per-database permissions

When a [database registry](/get-started/multi-database) is active, scope permissions to a specific alias and schema:

```toml
[[access.tables]]
database = "tenant-a"
schema = "public"
name = "users"
permissions = ["read"]
fields = ["id", "name"]
```

Permissions are matched against alias + schema + table name.

## User-level permissions

v2 supports per-user table permissions via `[[access.users]]`. The authenticated user's identity (from the JWT `sub` claim or username) is matched against `access.users.name`.

User-level table permissions restrict or extend the global `access.tables` rules for that specific user.

Example:

```toml
[access]
restrict = true

[[access.tables]]
name = "read_table"
permissions = ["read"]
fields = ["id", "name", "age", "gender"]

[[access.users]]
name = "foo_read"
[[access.users.tables]]
name = "read_table"
permissions = ["read"]
fields = ["id", "name"]
```

In this example, user `foo_read` can only read the `id` and `name` fields on `read_table`, even if the global table permission allows more fields.

For a comprehensive example with multiple users and permission combinations, see [testdata/prest.toml](https://github.com/prest/prest/blob/main/testdata/prest.toml).

MCP (`/_mcp`, v2.1.0+) inherits the same ACL rules as REST table routes — read tools only see tables and fields your access config allows. See [MCP over HTTP](/get-started/mcp-over-http).

## Example configuration

Configuration example: [prest.toml](https://github.com/prest/prest/blob/main/testdata/prest.toml)

```toml
[auth]
table = "prest_users"
username = "username"
password = "password"
metadata = ["first_name", "last_name", "last_login"]

[http]
port = 3000

[cache]
enabled = true

    [[cache.endpoints]]
    endpoint = "/prest/public/test"
    time = 5

[access]
restrict = true  # can access only the tables listed below

    [[access.tables]]
    name = "Reply"
    permissions = ["read", "write", "delete"]
    fields = ["id", "name"]

    [[access.tables]]
    name = "test"
    permissions = ["read", "write", "delete"]
    fields = ["id", "name"]

    [[access.tables]]
    name = "testarray"
    permissions = ["read", "write", "delete"]
    fields = ["id", "data"]

    [[access.tables]]
    name = "test2"
    permissions = ["read", "write", "delete"]
    fields = ["id", "name"]

    [[access.tables]]
    name = "test3"
    permissions = ["read", "write", "delete"]
    fields = ["id", "name"]

    [[access.tables]]
    name = "test4"
    permissions = ["read", "write", "delete"]
    fields = ["id", "name"]

    [[access.tables]]
    name = "test5"
    permissions = ["read", "write", "delete"]
    fields = ["*"]

    [[access.tables]]
    name = "test_readonly_access"
    permissions = ["read"]
    fields = ["id", "name"]

    [[access.tables]]
    name = "test_write_and_delete_access"
    permissions = ["write", "delete"]

    [[access.tables]]
    name = "test_list_only_id"
    permissions = ["read"]
    fields = ["id"]

    [[access.tables]]
    name = "test6"
    permissions = ["read", "write", "delete"]
    fields = ["nuveo", "name"]

    [[access.tables]]
    name = "view_test"
    permissions = ["read"]
    fields = ["player"]

    [[access.tables]]
    name = "test_group_by_table"
    permissions = ["read"]
    fields = ["id", "name", "age", "salary"]
```

## Related

* [Multi-database](/get-started/multi-database)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Auth](/api-reference/auth)
* [Acronyms](/readme/acronyms) · [ACL](/readme/acronyms#acl) · [MCP](/readme/acronyms#mcp)


# AI and MCP

Connect AI agents and IDEs to PostgreSQL through pREST’s read-only [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) endpoint. Use the same server that already exposes a REST API from PostgreSQL — no separate query service required.

pREST v2.1.0+ ships HTTP MCP at `/_mcp`. Clients that only speak **stdio** (Cursor, VS Code / Copilot, Claude Desktop, and many others) use the official **pREST MCP Adapter** (`prest-mcp`) as a transport bridge.

***

## What you get

| Piece                   | Role                                                                                                                             |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **pREST `/_mcp`**       | Read-only MCP tools: list databases/schemas/tables, describe columns, select rows (max 100)                                      |
| **`prest-mcp` adapter** | Stdio ↔ HTTP bridge only — does **not** introspect schemas, generate SQL, or implement tools                                     |
| **pREST for Cursor**    | Optional [Cursor plugin](https://github.com/prest/prest-cursor) (rules and skills) — separate from the MCP binary                |
| **pREST for OpenClaw**  | Optional [OpenClaw plugin](https://clawhub.ai/prest/plugins/prest-openclaw) (skills and examples) — separate from the MCP binary |

```
Cursor / VS Code / Claude / OpenClaw / Cline / Continue
        ↓ stdio
   prest-mcp
        ↓ HTTP POST
   prestd /_mcp
        ↓
   PostgreSQL
```

***

## Start here

1. [MCP Overview](/ai/mcp-overview) — architecture, adapter vs server, security
2. [Install pREST MCP Adapter](/ai/install-prest-mcp) — Homebrew, Go, Docker, MCP Registry
3. Pick a client:
   * [Use with Cursor](/ai/cursor)
   * [Use with VS Code and Copilot](/ai/vscode-copilot)
   * [Use with Claude Desktop](/ai/claude-desktop)
   * [Other AI tools](/ai/other-clients) (Cline, Continue, Windsurf, OpenClaw, generic stdio)
4. [Read-only PostgreSQL for AI](/ai/read-only-postgres) — least-privilege role for agent access
5. Optional agent plugins: [pREST for Cursor](/ai/prest-cursor) · [pREST for OpenClaw](/ai/prest-openclaw)

End-to-end walkthrough: [PostgreSQL to AI agent](/postgres-to-ai-agent).

***

## Protocol reference

Deep tools, auth, ACL, and JSON-RPC details stay in [MCP over HTTP](/get-started/mcp-over-http). This AI section focuses on install and client setup.

***

## Next steps

* Install the adapter: [Install pREST MCP Adapter](/ai/install-prest-mcp) or [Homebrew tutorial](/postgres-to-ai-agent/install-prest-mcp-homebrew)
* Run pREST locally: [Get pREST](/get-prest) · [Distribution channels](/get-prest/distribution)
* Confirm discovery: `curl -s http://localhost:3000/_mcp`


# MCP Overview

pREST exposes a **PostgreSQL MCP server** over HTTP so AI agents can discover schemas and read data through the same process that serves your REST API.

Requires **pREST v2.1.0+**.

***

## Two layers

| Layer                 | What it is          | What it does                                              |
| --------------------- | ------------------- | --------------------------------------------------------- |
| **HTTP MCP endpoint** | `/_mcp` on `prestd` | Implements MCP tools, catalog discovery, and read queries |
| **Stdio adapter**     | `prest-mcp` binary  | Forwards JSON-RPC between stdin/stdout and `POST /_mcp`   |

The adapter is a **transport bridge only**. It does not introspect schemas, generate SQL, or implement tools — those live in pREST.

```
MCP client / AI IDE
        ↓ stdio (NDJSON JSON-RPC)
prest-mcp
        ↓ HTTP POST
http://localhost:3000/_mcp
        ↓
pREST → PostgreSQL
```

| Concern       | Native `/_mcp`           | `prest-mcp` adapter                    |
| ------------- | ------------------------ | -------------------------------------- |
| Transport     | HTTP on the pREST server | Stdio process that proxies to HTTP     |
| Tools / SQL   | Implemented in pREST     | None — forwards JSON-RPC as-is         |
| When to use   | HTTP-capable MCP clients | Clients that only launch stdio servers |
| Extra process | No                       | Yes — one lightweight binary           |

***

## Security defaults for AI

* Prefer a **read-only** PostgreSQL role for agent workflows — see [Read-only PostgreSQL for AI](/ai/read-only-postgres).
* MCP tools in v2.1.0 are **read-only** (no insert/update/delete/DDL). REST may still allow writes on the same server.
* Do not expose unauthenticated `/_mcp` publicly.
* Prefer local or private-network pREST for development.
* When [auth](/api-reference/auth) is enabled, pass a JWT via `PREST_MCP_TOKEN` (adapter) or `Authorization` (HTTP).

***

## Environment variables (adapter)

| Variable               | Required | Default | Description                                                     |
| ---------------------- | -------- | ------- | --------------------------------------------------------------- |
| `PREST_MCP_URL`        | yes      | —       | URL of the HTTP MCP endpoint, e.g. `http://localhost:3000/_mcp` |
| `PREST_MCP_TOKEN`      | no       | —       | Bearer token when pREST auth is enabled                         |
| `PREST_MCP_TIMEOUT_MS` | no       | `30000` | HTTP timeout in milliseconds                                    |

***

## Next steps

* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [Use with Cursor](/ai/cursor) · [VS Code and Copilot](/ai/vscode-copilot) · [Claude Desktop](/ai/claude-desktop) · [Other AI tools](/ai/other-clients)
* Protocol and tools reference: [MCP over HTTP](/get-started/mcp-over-http)

## Related documentation

* [AI and MCP landing](/ai)
* [pREST for Cursor plugin](/ai/prest-cursor)
* [pREST for OpenClaw plugin](/ai/prest-openclaw)
* [v2.1.0 release notes](/releases/v2.1.0)


# Install pREST MCP Adapter

Install `prest-mcp`, the official stdio adapter for pREST’s HTTP MCP endpoint. Use it when your MCP client expects a **stdio** process instead of calling `/_mcp` over HTTP directly.

Repository: [prest/prest-mcp-adapter](https://github.com/prest/prest-mcp-adapter) · Homebrew: [prest/homebrew-tap](https://github.com/prest/homebrew-tap)

Requires pREST **v2.1.0+** with `/_mcp` available. The adapter is a transport bridge only — tools and SQL live in pREST ([MCP Overview](/ai/mcp-overview)).

***

## Homebrew (recommended on macOS)

```sh
brew install prest/tap/prest-mcp
```

Or tap first:

```sh
brew tap prest/tap
brew install prest-mcp
```

Step-by-step: [Install adapter with Homebrew](/postgres-to-ai-agent/install-prest-mcp-homebrew). For `prestd` itself, see [Start with Homebrew](/get-prest/start-with-homebrew).

***

## Go

```sh
go install github.com/prest/prest-mcp-adapter/cmd/prest-mcp@latest
```

Ensure `$(go env GOPATH)/bin` is on your `PATH`.

***

## Docker / OCI

```sh
docker pull ghcr.io/prest/prest-mcp-adapter:0.1.3
```

Run against pREST on the host (Docker Desktop):

```sh
docker run --rm -i \
  -e PREST_MCP_URL=http://host.docker.internal:3000/_mcp \
  ghcr.io/prest/prest-mcp-adapter:0.1.3
```

***

## MCP Registry

Published name: **`io.github.prest/prest`**

Use this identifier when installing from the [MCP Registry](https://github.com/modelcontextprotocol/registry) or clients that resolve registry packages.

***

## Environment variables

| Variable               | Required | Default | Description                                                           |
| ---------------------- | -------- | ------- | --------------------------------------------------------------------- |
| `PREST_MCP_URL`        | yes      | —       | URL of the pREST HTTP MCP endpoint, e.g. `http://localhost:3000/_mcp` |
| `PREST_MCP_TOKEN`      | no       | —       | Bearer token when pREST auth is enabled                               |
| `PREST_MCP_TIMEOUT_MS` | no       | `30000` | HTTP timeout in milliseconds                                          |

***

## Local usage

```sh
PREST_MCP_URL=http://localhost:3000/_mcp prest-mcp
```

With a JWT (when [auth](/api-reference/auth) is enabled):

```sh
PREST_MCP_URL=https://api.example.com/_mcp \
PREST_MCP_TOKEN=secret \
prest-mcp
```

Protocol messages are newline-delimited JSON-RPC on stdin/stdout. Logs go only to stderr.

***

## MCP client configuration

Minimal stdio server entry (Cursor, Claude Desktop, and similar clients):

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

VS Code / Copilot uses `"servers"` and `"type": "stdio"` instead — see [Use with VS Code and Copilot](/ai/vscode-copilot).

Add `PREST_MCP_TOKEN` when auth is enabled. If `prest-mcp` is not on `PATH`, set `"command"` to the absolute binary path (Homebrew: typically `/opt/homebrew/bin/prest-mcp` on Apple Silicon, `/usr/local/bin/prest-mcp` on Intel Mac).

Client walkthroughs:

* [Use with Cursor](/ai/cursor)
* [Use with VS Code and Copilot](/ai/vscode-copilot)
* [Use with Claude Desktop](/ai/claude-desktop)
* [Other AI tools](/ai/other-clients)

***

## Troubleshooting

| Symptom                                              | Likely cause                                                                                                       |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Adapter exits immediately mentioning `PREST_MCP_URL` | Environment variable not set                                                                                       |
| Client shows server disconnected / failed to start   | `prest-mcp` not on `PATH`, or wrong absolute path in config                                                        |
| `401` / `403` from tools                             | Auth enabled but `PREST_MCP_TOKEN` missing or invalid                                                              |
| Connection refused / timeout                         | pREST not running, wrong host/port, or Docker networking (`host.docker.internal`)                                  |
| Empty tool list                                      | ACL hides all tables — see [Permissions](/get-started/permissions) and [MCP over HTTP](/get-started/mcp-over-http) |

***

## Next steps

* [MCP Overview](/ai/mcp-overview)
* [Read-only PostgreSQL for AI](/ai/read-only-postgres)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)
* Protocol reference: [MCP over HTTP](/get-started/mcp-over-http)

## Related documentation

* [Start with Homebrew](/get-prest/start-with-homebrew)
* [Distribution channels](/get-prest/distribution)
* [v2.1.0 release notes](/releases/v2.1.0)
* [prest-mcp-adapter](https://github.com/prest/prest-mcp-adapter) · [homebrew-tap](https://github.com/prest/homebrew-tap)


# Use with Cursor

Connect [Cursor](https://cursor.com/) to PostgreSQL through pREST’s read-only MCP tools. The agent can discover schemas and query data via your running pREST instance.

pREST speaks MCP over HTTP at `/_mcp`. Cursor launches **stdio** MCP servers, so you need the official [`prest-mcp`](/ai/install-prest-mcp) adapter as a bridge.

Requires pREST **v2.1.0+**.

For Cursor rules and skills (separate from MCP), see [pREST for Cursor plugin](/ai/prest-cursor).

***

## Prerequisites

* pREST running locally or on a reachable host (see [Get pREST](/get-prest))
* `prest-mcp` installed ([Homebrew](/postgres-to-ai-agent/install-prest-mcp-homebrew), [Go](/ai/install-prest-mcp#go), or Docker)
* Cursor with MCP support enabled

Verify the HTTP endpoint before configuring Cursor:

```sh
curl -s http://localhost:3000/_mcp | head
```

You should see JSON with `"name": "prest"` and a `tools` array. If auth is enabled, include your usual `Authorization` header.

Prefer a [read-only PostgreSQL role](/ai/read-only-postgres) for AI access.

***

## Install the adapter

```sh
brew install prest/tap/prest-mcp
```

Other options: [Install pREST MCP Adapter](/ai/install-prest-mcp). Confirm the binary is on your `PATH`:

```sh
which prest-mcp
```

***

## Configure Cursor

### Project config (recommended)

Create or edit `.cursor/mcp.json` in your project root:

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

### With authentication

When [auth](/api-reference/auth) is enabled on pREST:

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "https://api.example.com/_mcp",
        "PREST_MCP_TOKEN": "your-jwt-here"
      }
    }
  }
}
```

Prefer injecting the token from your environment or a secret store rather than committing JWTs to git. If you must keep secrets out of the repo, use Cursor’s user-level MCP settings instead of the project file.

### Absolute path

If Cursor cannot find `prest-mcp` on `PATH`:

```json
{
  "mcpServers": {
    "prest": {
      "command": "/opt/homebrew/bin/prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

On Intel Mac Homebrew installs, the path is often `/usr/local/bin/prest-mcp`.

***

## Verify in Cursor

1. Restart Cursor or reload MCP servers from Settings → MCP.
2. Confirm the `prest` server shows as connected.
3. Ask the agent something that needs catalog access, for example:
   * “List databases available through pREST.”
   * “Describe the `public.users` table.”
   * “Select the first 5 rows from `public.orders` ordered by `id`.”

The agent should call tools such as `prest.list_databases`, `prest.describe_table`, or `prest.select.{database}.{schema}.{table}`. Tool behavior and limits are documented in [MCP over HTTP](/get-started/mcp-over-http).

***

## Optional: Docker-backed adapter

If you prefer not to install a local binary:

```json
{
  "mcpServers": {
    "prest": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "PREST_MCP_URL=http://host.docker.internal:3000/_mcp",
        "ghcr.io/prest/prest-mcp-adapter:0.1.3"
      ]
    }
  }
}
```

`host.docker.internal` reaches pREST on the host from Docker Desktop (macOS/Windows). On Linux you may need `--add-host=host.docker.internal:host-gateway` or the host’s LAN IP.

***

## Troubleshooting

| Symptom                         | Fix                                                               |
| ------------------------------- | ----------------------------------------------------------------- |
| Server fails to start           | Ensure `prest-mcp` is installed and the `command` path is correct |
| Tools never appear              | Confirm `curl` to `/_mcp` works; check ACL / empty catalog        |
| Auth errors                     | Set `PREST_MCP_TOKEN` to a valid JWT                              |
| Connection refused              | Start pREST; match host/port in `PREST_MCP_URL`                   |
| Stale tools after schema change | Restart pREST (schema-aware tools are generated at startup)       |

More detail: [Install pREST MCP Adapter](/ai/install-prest-mcp) and [MCP over HTTP](/get-started/mcp-over-http#troubleshooting).

***

## Next steps

* [pREST for Cursor plugin](/ai/prest-cursor) — rules and skills for pREST workflows
* [Read-only PostgreSQL for AI](/ai/read-only-postgres)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)
* [Use with VS Code and Copilot](/ai/vscode-copilot) · [Use with Claude Desktop](/ai/claude-desktop)

## Related documentation

* [MCP Overview](/ai/mcp-overview)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Permissions](/get-started/permissions)
* [Auth](/api-reference/auth)


# pREST for Cursor plugin

**pREST for Cursor** ([prest/prest-cursor](https://github.com/prest/prest-cursor)) is a Cursor **plugin**: rules, skills, and examples that help the agent work with pREST. It is **not** the MCP binary.

| Component        | What it is                                                                                   |
| ---------------- | -------------------------------------------------------------------------------------------- |
| **`prest-mcp`**  | Stdio adapter that talks to `/_mcp` — see [Install pREST MCP Adapter](/ai/install-prest-mcp) |
| **prest-cursor** | Cursor marketplace plugin (rules/skills) that guides setup and workflows                     |

Use both together: install the adapter for live MCP tools, and the plugin for Cursor-native guidance.

***

## What the plugin provides

* Rules for configuring and using pREST MCP safely
* Skills for common setup steps (including MCP)
* Examples such as a [read-only Postgres role](/ai/read-only-postgres) for AI access

Repository: <https://github.com/prest/prest-cursor>

***

## Install

Follow the install instructions in the [prest-cursor README](https://github.com/prest/prest-cursor). Typical options:

* Install from the Cursor plugin / marketplace listing when available
* Or clone / link the plugin locally for development

After install, still configure MCP separately — the plugin does not replace `prest-mcp`:

1. Run pREST **v2.1.0+** with `/_mcp`
2. Install [`prest-mcp`](/ai/install-prest-mcp) (`brew install prest/tap/prest-mcp`)
3. Add `.cursor/mcp.json` as in [Use with Cursor](/ai/cursor)

***

## MCP vs plugin checklist

| Task                                              | Use                                                   |
| ------------------------------------------------- | ----------------------------------------------------- |
| Agent calls `prest.list_databases` / select tools | MCP via `prest-mcp` → `/_mcp`                         |
| Agent follows pREST-specific rules and skills     | prest-cursor plugin                                   |
| Least-privilege DB access                         | [Read-only PostgreSQL for AI](/ai/read-only-postgres) |

***

## Next steps

* [Use with Cursor](/ai/cursor)
* [MCP Overview](/ai/mcp-overview)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)

## Related documentation

* [AI and MCP landing](/ai)
* [pREST for OpenClaw plugin](/ai/prest-openclaw) — sibling plugin for OpenClaw
* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [MCP over HTTP](/get-started/mcp-over-http)


# pREST for OpenClaw plugin

**pREST for OpenClaw** ([prest/prest-openclaw](https://github.com/prest/prest-openclaw)) is an OpenClaw **plugin** on [ClawHub](https://clawhub.ai/prest/plugins/prest-openclaw) (`@prest/prest-openclaw`). It provides skills, examples, and security guidance for building PostgreSQL-backed APIs with pREST. It is **not** the MCP binary and does **not** run pREST or PostgreSQL.

| Component          | What it is                                                                                         |
| ------------------ | -------------------------------------------------------------------------------------------------- |
| **`prest-mcp`**    | Stdio adapter that talks to `/_mcp` — see [Install pREST MCP Adapter](/ai/install-prest-mcp)       |
| **prest-openclaw** | OpenClaw plugin (skills + examples) that guides config, SQL routes, security review, and MCP setup |

Use both together: install the adapter for live MCP tools, and the plugin for OpenClaw-native guidance.

***

## What the plugin provides

| Skill                     | Purpose                                                        |
| ------------------------- | -------------------------------------------------------------- |
| `generate-config`         | Generate `prest.toml`, `.env.example`, optional Docker Compose |
| `generate-sql-route`      | Parameterized SQL under `/_QUERIES/`                           |
| `review-security`         | Audit configs, SQL, auth, and MCP exposure                     |
| `setup-mcp`               | Wire `prest-mcp` and read-only Postgres for agents             |
| `explain-config`          | Plain-language `prest.toml` walkthrough                        |
| `prest-core-guidance`     | Core pREST development rules                                   |
| `prest-security-guidance` | Security defaults and warnings                                 |
| `prest-mcp-guidance`      | MCP and `prest-mcp` connection patterns                        |
| `agent-prompts`           | Copy-paste prompts for common tasks                            |

Examples include Docker Compose setups, SQL template routes, multi-database configs, and a read-only MCP workflow (`examples/mcp-readonly/`).

Repository: <https://github.com/prest/prest-openclaw>

***

## Install from ClawHub

```bash
openclaw plugins install clawhub:@prest/prest-openclaw
openclaw plugins inspect prest
```

Enable the plugin in your OpenClaw config if needed, then restart the gateway:

```bash
openclaw gateway restart
```

Listing: [clawhub.ai/prest/plugins/prest-openclaw](https://clawhub.ai/prest/plugins/prest-openclaw)

***

## MCP setup

The plugin does not replace `prest-mcp`. After installing the plugin:

1. Run pREST **v2.1.0+** with `/_mcp` available
2. Install the adapter: `brew install prest/tap/prest-mcp` — see [Install pREST MCP Adapter](/ai/install-prest-mcp)
3. Wire MCP in OpenClaw using `prest-mcp` and env vars

```bash
export PREST_MCP_URL='http://localhost:3000/_mcp'
# export PREST_MCP_TOKEN='...'   # when auth is required
prest-mcp
```

Example MCP config shape (from the plugin repo’s `examples/mcp-readonly/openclaw-mcp.example.json`):

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "${PREST_MCP_URL}",
        "PREST_MCP_TOKEN": "${PREST_MCP_TOKEN}"
      }
    }
  }
}
```

| Variable               | Required | Description                                     |
| ---------------------- | -------- | ----------------------------------------------- |
| `PREST_MCP_URL`        | Yes      | Full MCP URL, e.g. `http://localhost:3000/_mcp` |
| `PREST_MCP_TOKEN`      | No       | Bearer token when pREST auth is enabled         |
| `PREST_MCP_TIMEOUT_MS` | No       | HTTP timeout override (default `30000`)         |

MCP Registry name: `io.github.prest/prest`

***

## Plugin vs MCP checklist

| Task                                                      | Use                                                   |
| --------------------------------------------------------- | ----------------------------------------------------- |
| Agent calls `prest.list_databases` / select tools         | MCP via `prest-mcp` → `/_mcp`                         |
| Agent follows pREST skills (config, SQL routes, security) | prest-openclaw plugin                                 |
| Least-privilege DB access                                 | [Read-only PostgreSQL for AI](/ai/read-only-postgres) |

***

## Security

* Never expose `/_mcp`, Postgres, or pREST publicly without authentication
* Prefer a [read-only PostgreSQL role](/ai/read-only-postgres) for agent workflows
* Use environment variables for secrets — never commit real credentials
* Set `access.restrict = true` with explicit `[[access.tables]]` in production
* Do not use write-capable database roles in MCP configs

***

## Troubleshooting

| Symptom               | Fix                                                                                                                 |
| --------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Skills not appearing  | Confirm plugin is enabled; restart gateway; run `openclaw plugins inspect prest`                                    |
| MCP connection fails  | `curl http://localhost:3000/_health`; verify `PREST_MCP_URL` ends with `/_mcp`; set `PREST_MCP_TOKEN` if auth is on |
| `prest-mcp` not found | `brew install prest/tap/prest-mcp` or use absolute path                                                             |

Publishing and validation issues: see the [plugin README](https://github.com/prest/prest-openclaw) and [OpenClaw ClawHub docs](https://docs.openclaw.ai/clawhub).

***

## Next steps

* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [MCP Overview](/ai/mcp-overview)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)

## Related documentation

* [AI and MCP landing](/ai)
* [pREST for Cursor plugin](/ai/prest-cursor) — sibling plugin for Cursor
* [MCP over HTTP](/get-started/mcp-over-http)
* [Distribution channels](/get-prest/distribution)


# Use with VS Code and Copilot

Connect [GitHub Copilot](https://code.visualstudio.com/docs/copilot/overview) in [Visual Studio Code](https://code.visualstudio.com/) to PostgreSQL through pREST’s read-only MCP tools. In **Agent** mode, Copilot can discover schemas and query data via your running pREST instance.

pREST speaks MCP over HTTP at `/_mcp`. VS Code launches **stdio** MCP servers, so you need the official [`prest-mcp`](/ai/install-prest-mcp) adapter as a bridge.

Requires pREST **v2.1.0+**.

Official VS Code MCP docs: [Add and manage MCP servers](https://code.visualstudio.com/docs/agent-customization/mcp-servers).

The [pREST for Cursor plugin](/ai/prest-cursor) does **not** apply to VS Code. You still get full MCP tools through `prest-mcp`; only Cursor-specific rules and skills are unavailable here.

***

## Prerequisites

* pREST running locally or on a reachable host (see [Get pREST](/get-prest))
* `prest-mcp` installed ([Homebrew](/postgres-to-ai-agent/install-prest-mcp-homebrew), [Go](/ai/install-prest-mcp#go), or Docker)
* VS Code with [GitHub Copilot Chat](https://code.visualstudio.com/docs/copilot/copilot-chat) and **Agent** mode available

Verify the HTTP endpoint before configuring VS Code:

```sh
curl -s http://localhost:3000/_mcp | head
```

You should see JSON with `"name": "prest"` and a `tools` array. If auth is enabled, include your usual `Authorization` header.

Prefer a [read-only PostgreSQL role](/ai/read-only-postgres) for AI access.

***

## Install the adapter

```sh
brew install prest/tap/prest-mcp
```

Other options: [Install pREST MCP Adapter](/ai/install-prest-mcp). Confirm the binary is on your `PATH`:

```sh
which prest-mcp
```

***

## Configure VS Code

VS Code MCP config uses a top-level **`servers`** key (not `mcpServers`). Each server entry includes `"type": "stdio"`.

### Workspace config (recommended)

Create or edit `.vscode/mcp.json` in your project root:

```json
{
  "servers": {
    "prest": {
      "type": "stdio",
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

### User config

For a global setup that applies across workspaces, run **MCP: Open User Configuration** from the Command Palette and add the same `servers.prest` block.

### With authentication

When [auth](/api-reference/auth) is enabled on pREST:

```json
{
  "servers": {
    "prest": {
      "type": "stdio",
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "https://api.example.com/_mcp",
        "PREST_MCP_TOKEN": "your-jwt-here"
      }
    }
  }
}
```

Prefer injecting the token from your environment or a secret store rather than committing JWTs to git. Use user-level MCP configuration when you need secrets out of the repo.

### Absolute path

If VS Code cannot find `prest-mcp` on `PATH`:

```json
{
  "servers": {
    "prest": {
      "type": "stdio",
      "command": "/opt/homebrew/bin/prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

On Intel Mac Homebrew installs, the path is often `/usr/local/bin/prest-mcp`.

***

## Optional: MCP gallery / registry

VS Code can install servers from the MCP gallery or registry. The published package name is **`io.github.prest/prest`**.

After installing from the gallery, set `PREST_MCP_URL` (and `PREST_MCP_TOKEN` if needed) in the server’s environment so the adapter can reach your pREST instance. Details: [Install pREST MCP Adapter](/ai/install-prest-mcp#mcp-registry).

***

## Verify in Copilot Agent mode

1. Start or restart the `prest` MCP server from the VS Code MCP UI (or reload the window).
2. Open Copilot Chat and switch to **Agent** mode.
3. Confirm the `prest` tools are available to the agent.
4. Ask something that needs catalog access, for example:
   * “List databases available through pREST.”
   * “Describe the `public.users` table.”
   * “Select the first 5 rows from `public.orders` ordered by `id`.”

The agent should call tools such as `prest.list_databases`, `prest.describe_table`, or `prest.select.{database}.{schema}.{table}`. Tool behavior and limits are documented in [MCP over HTTP](/get-started/mcp-over-http).

***

## Optional: Docker-backed adapter

If you prefer not to install a local binary:

```json
{
  "servers": {
    "prest": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "PREST_MCP_URL=http://host.docker.internal:3000/_mcp",
        "ghcr.io/prest/prest-mcp-adapter:0.1.3"
      ]
    }
  }
}
```

`host.docker.internal` reaches pREST on the host from Docker Desktop (macOS/Windows). On Linux you may need `--add-host=host.docker.internal:host-gateway` or the host’s LAN IP.

***

## Troubleshooting

| Symptom                         | Fix                                                                            |
| ------------------------------- | ------------------------------------------------------------------------------ |
| Server fails to start           | Ensure `prest-mcp` is installed and the `command` path is correct              |
| Config ignored / wrong shape    | Use `"servers"` (not `mcpServers`) and include `"type": "stdio"`               |
| Tools never appear              | Confirm `curl` to `/_mcp` works; check ACL / empty catalog; use **Agent** mode |
| Auth errors                     | Set `PREST_MCP_TOKEN` to a valid JWT                                           |
| Connection refused              | Start pREST; match host/port in `PREST_MCP_URL`                                |
| Stale tools after schema change | Restart pREST (schema-aware tools are generated at startup)                    |

More detail: [Install pREST MCP Adapter](/ai/install-prest-mcp) and [MCP over HTTP](/get-started/mcp-over-http#troubleshooting). VS Code reference: [MCP servers in VS Code](https://code.visualstudio.com/docs/agent-customization/mcp-servers).

***

## Next steps

* [Read-only PostgreSQL for AI](/ai/read-only-postgres)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)
* [Use with Cursor](/ai/cursor) · [Use with Claude Desktop](/ai/claude-desktop)
* [Other AI tools](/ai/other-clients)

## Related documentation

* [MCP Overview](/ai/mcp-overview)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Permissions](/get-started/permissions)
* [Auth](/api-reference/auth)
* [VS Code MCP servers](https://code.visualstudio.com/docs/agent-customization/mcp-servers)


# Use with Claude Desktop

Connect [Claude Desktop](https://claude.ai/download) to PostgreSQL through pREST’s read-only MCP tools. Claude can discover schemas and query data via your running pREST instance.

pREST speaks MCP over HTTP at `/_mcp`. Claude Desktop launches **stdio** MCP servers, so you need the official [`prest-mcp`](/ai/install-prest-mcp) adapter as a bridge.

Requires pREST **v2.1.0+**.

***

## Prerequisites

* pREST running locally or on a reachable host (see [Get pREST](/get-prest))
* `prest-mcp` installed ([Homebrew](/postgres-to-ai-agent/install-prest-mcp-homebrew), [Go](/ai/install-prest-mcp#go), or Docker)
* Claude Desktop with MCP support

Verify the HTTP endpoint before configuring Claude:

```sh
curl -s http://localhost:3000/_mcp | head
```

You should see JSON with `"name": "prest"` and a `tools` array. If auth is enabled, include your usual `Authorization` header.

Prefer a [read-only PostgreSQL role](/ai/read-only-postgres) for AI access.

***

## Install the adapter

```sh
brew install prest/tap/prest-mcp
```

Other options: [Install pREST MCP Adapter](/ai/install-prest-mcp). Confirm the binary is on your `PATH` (Claude Desktop often needs an absolute path — see below):

```sh
which prest-mcp
```

***

## Configure Claude Desktop

Edit Claude’s MCP config file:

| Platform | Path                                                              |
| -------- | ----------------------------------------------------------------- |
| macOS    | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows  | `%APPDATA%\Claude\claude_desktop_config.json`                     |

### Basic config

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

### With authentication

When [auth](/api-reference/auth) is enabled on pREST:

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "https://api.example.com/_mcp",
        "PREST_MCP_TOKEN": "your-jwt-here"
      }
    }
  }
}
```

Do not commit this file to a shared repo if it contains tokens.

### Absolute path

GUI apps on macOS often have a minimal `PATH`. Prefer an absolute command:

```json
{
  "mcpServers": {
    "prest": {
      "command": "/opt/homebrew/bin/prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

On Intel Mac Homebrew installs, use `/usr/local/bin/prest-mcp`.

***

## Verify in Claude Desktop

1. Fully quit and reopen Claude Desktop (MCP config is loaded at startup).
2. Open a new chat and look for the MCP / tools indicator for the `prest` server.
3. Ask Claude to use pREST, for example:
   * “Using the prest MCP server, list available databases.”
   * “Describe table `public.users`.”
   * “Fetch up to 5 rows from `public.orders`.”

Claude should invoke tools such as `prest.list_databases`, `prest.describe_table`, or schema-aware `prest.select.*` tools. See [MCP over HTTP](/get-started/mcp-over-http) for arguments, auth, and the 100-row select limit.

***

## Optional: Docker-backed adapter

```json
{
  "mcpServers": {
    "prest": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "PREST_MCP_URL=http://host.docker.internal:3000/_mcp",
        "ghcr.io/prest/prest-mcp-adapter:0.1.3"
      ]
    }
  }
}
```

Ensure Docker Desktop is running before starting Claude.

***

## Troubleshooting

| Symptom                     | Fix                                                                           |
| --------------------------- | ----------------------------------------------------------------------------- |
| `prest` never appears       | Quit Claude completely and reopen; check JSON syntax in the config file       |
| Server error / spawn failed | Use an absolute path to `prest-mcp`; GUI apps may not see Homebrew’s `PATH`   |
| Auth failures               | Set `PREST_MCP_TOKEN`                                                         |
| Connection refused          | Start pREST; confirm `PREST_MCP_URL`                                          |
| Empty tools                 | Check [permissions](/get-started/permissions) and `curl` discovery on `/_mcp` |

Logs from the adapter go to stderr; Claude Desktop surfaces MCP server errors in its developer / MCP UI depending on version.

More detail: [Install pREST MCP Adapter](/ai/install-prest-mcp) and [MCP over HTTP](/get-started/mcp-over-http#troubleshooting).

***

## Next steps

* [Read-only PostgreSQL for AI](/ai/read-only-postgres)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)
* [Use with Cursor](/ai/cursor) · [Use with VS Code and Copilot](/ai/vscode-copilot)
* [Other AI tools](/ai/other-clients)

## Related documentation

* [MCP Overview](/ai/mcp-overview)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Permissions](/get-started/permissions)
* [Auth](/api-reference/auth)


# Other AI tools

Any MCP client that can launch a **stdio** server can connect Cursor-style to PostgreSQL through pREST. Point the client at `prest-mcp` and set `PREST_MCP_URL` to your `/_mcp` endpoint.

Requires pREST **v2.1.0+** and the [pREST MCP Adapter](/ai/install-prest-mcp).

***

## Generic stdio pattern

```sh
PREST_MCP_URL=http://localhost:3000/_mcp prest-mcp
```

Or in client config JSON:

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

Optional: `PREST_MCP_TOKEN`, `PREST_MCP_TIMEOUT_MS` (default `30000`). See [Install pREST MCP Adapter](/ai/install-prest-mcp).

Prefer a [read-only PostgreSQL role](/ai/read-only-postgres) for agent access.

***

## VS Code / GitHub Copilot

VS Code uses a different MCP JSON shape (`servers` with `"type": "stdio"`, typically in `.vscode/mcp.json`). Full walkthrough: [Use with VS Code and Copilot](/ai/vscode-copilot).

***

## OpenClaw

Install the official plugin from [ClawHub](https://clawhub.ai/prest/plugins/prest-openclaw):

```bash
openclaw plugins install clawhub:@prest/prest-openclaw
```

Wire MCP with `prest-mcp` and `PREST_MCP_URL` as in the generic pattern above. Full guide: [pREST for OpenClaw plugin](/ai/prest-openclaw).

***

## Cline

In Cline’s MCP settings, add a stdio server with:

* **Command:** `prest-mcp` (or absolute path)
* **Env:** `PREST_MCP_URL=http://localhost:3000/_mcp`

Reload MCP servers after saving. Ask Cline to list databases or describe a table to confirm tools appear.

***

## Continue

Add an MCP server entry in Continue’s config (shape varies by Continue version). Typical fields:

* Command / args launching `prest-mcp`
* Environment with `PREST_MCP_URL`

If Continue cannot resolve Homebrew binaries, use `/opt/homebrew/bin/prest-mcp` or `/usr/local/bin/prest-mcp`.

***

## Windsurf

Configure Windsurf’s MCP / Cascade server list the same way as other stdio clients: command `prest-mcp`, env `PREST_MCP_URL`. Restart the IDE after changes.

***

## Docker as the command

When you do not want a host binary:

```json
{
  "mcpServers": {
    "prest": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "PREST_MCP_URL=http://host.docker.internal:3000/_mcp",
        "ghcr.io/prest/prest-mcp-adapter:0.1.3"
      ]
    }
  }
}
```

***

## HTTP-native clients

If your client can call MCP over HTTP directly, you may not need the adapter. Point it at `http://localhost:3000/_mcp` (or your deployed URL) and send the same auth headers as REST. Protocol details: [MCP over HTTP](/get-started/mcp-over-http).

***

## Troubleshooting

| Symptom                   | Fix                                                                     |
| ------------------------- | ----------------------------------------------------------------------- |
| Process exits immediately | Set `PREST_MCP_URL`                                                     |
| Spawn / PATH errors       | Use an absolute path to `prest-mcp`                                     |
| Empty tools               | Confirm `curl` discovery; check [permissions](/get-started/permissions) |
| Auth errors               | Set `PREST_MCP_TOKEN`                                                   |

***

## Next steps

* [Use with Cursor](/ai/cursor) · [VS Code and Copilot](/ai/vscode-copilot) · [Claude Desktop](/ai/claude-desktop) · [OpenClaw](/ai/prest-openclaw)
* [MCP Overview](/ai/mcp-overview)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)


# Read-only PostgreSQL for AI

When connecting AI agents to PostgreSQL through pREST MCP, give the agent a **least-privilege** database role: connect, use schemas, and `SELECT` — nothing that can change data.

MCP tools in pREST v2.1.0 are already read-only, but REST on the same server may still allow writes. A read-only Postgres role is the durable safety net for local and shared environments.

***

## What to grant

| Privilege                                      | Why                                              |
| ---------------------------------------------- | ------------------------------------------------ |
| `CONNECT` on the database                      | Allow the role to open a session                 |
| `USAGE` on schemas the agent may browse        | Allow resolving objects in those schemas         |
| `SELECT` on tables (or views) you want exposed | Allow reads only                                 |
| Optional `ALTER DEFAULT PRIVILEGES`            | Auto-grant `SELECT` on future tables in a schema |

Do **not** grant `INSERT`, `UPDATE`, `DELETE`, `TRUNCATE`, or DDL to the AI role.

***

## Example: create a read-only role

Run as a privileged database admin (superuser or owner). Replace names to match your database, schema, and tables.

```sql
-- Create a login role for AI / MCP exploration (set your own password).
DO $$
BEGIN
  IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'prest_readonly') THEN
    CREATE ROLE prest_readonly LOGIN PASSWORD 'change-me';
  END IF;
END
$$;

-- Allow connection to your database (replace `mydb`).
GRANT CONNECT ON DATABASE mydb TO prest_readonly;

-- Allow schema usage (replace `public` if needed).
GRANT USAGE ON SCHEMA public TO prest_readonly;

-- Grant SELECT on specific tables:
GRANT SELECT ON TABLE public.users TO prest_readonly;
-- GRANT SELECT ON TABLE public.orders TO prest_readonly;

-- Or grant SELECT on all existing tables in the schema:
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO prest_readonly;

-- Optional: future tables created by the current role get SELECT for prest_readonly
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO prest_readonly;
```

Point pREST at this role via your usual Postgres connection settings ([Configuring pREST](/get-started/configuring-prest)). Restart `prestd` after changing credentials.

***

## Verify

As the read-only role (or via pREST MCP):

```sql
SELECT current_user;
-- Should be prest_readonly (or your role name)

SELECT * FROM public.users LIMIT 5;
-- Should succeed if SELECT was granted

-- INSERT / UPDATE / DELETE should fail with permission denied
```

Then confirm MCP discovery:

```sh
curl -s http://localhost:3000/_mcp | head
```

Ask your AI client to list tables and select a few rows. Tools and ACL behavior: [MCP over HTTP](/get-started/mcp-over-http) · [Permissions](/get-started/permissions).

***

## Tips

* Use a strong password or prefer peer/cert auth in production; never commit demo passwords.
* Scope `SELECT` to the tables agents actually need — avoid `ALL TABLES` on sensitive schemas.
* Combine with pREST [permissions](/get-started/permissions) so tool discovery matches what Postgres allows.
* For Cursor-specific examples, see [prest-cursor](https://github.com/prest/prest-cursor) (`examples/mcp-readonly`).

***

## Next steps

* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [Use with Cursor](/ai/cursor) · [Claude Desktop](/ai/claude-desktop)
* [PostgreSQL to AI agent](/postgres-to-ai-agent)

## Related documentation

* [MCP Overview](/ai/mcp-overview)
* [Configuring pREST](/get-started/configuring-prest)
* [Permissions](/get-started/permissions)
* [Auth](/api-reference/auth)


# Tutorials

Connect an AI agent (Cursor, VS Code / Copilot, Claude Desktop, or any stdio MCP client) to PostgreSQL using pREST: a REST API from PostgreSQL plus a read-only **PostgreSQL MCP server** on the same process.

This tutorial is local-first. You will:

1. Run pREST against Postgres
2. Prefer a read-only database role
3. Install the `prest-mcp` adapter
4. Wire an AI client to MCP tools

Requires **pREST v2.1.0+**.

***

## Architecture

```
AI client (Cursor / VS Code / Claude / …)
        ↓ stdio
   prest-mcp
        ↓ HTTP POST /_mcp
   prestd
        ↓
   PostgreSQL (read-only role recommended)
```

The adapter only bridges transports. Schema discovery and queries are implemented in pREST — see [MCP Overview](/ai/mcp-overview).

***

## 1. Run PostgreSQL and pREST

Start Postgres locally (Docker, Homebrew, or your existing instance). Then install and run pREST — pick a channel from [Distribution](/get-prest/distribution) or [Get pREST](/get-prest).

Example with Homebrew for the server:

```sh
brew install prestd
```

Configure connection settings ([Configuring pREST](/get-started/configuring-prest)) and start `prestd`. Confirm the REST API and MCP discovery:

```sh
curl -s http://localhost:3000/_mcp | head
```

You should see `"name": "prest"` and a `tools` array.

***

## 2. Use a read-only role

Create a least-privilege role with `CONNECT`, `USAGE`, and `SELECT` only — full SQL: [Read-only PostgreSQL for AI](/ai/read-only-postgres). Point pREST’s Postgres credentials at that role for AI-facing instances.

***

## 3. Install the MCP adapter

On macOS:

```sh
brew install prest/tap/prest-mcp
```

Or:

```sh
go install github.com/prest/prest-mcp-adapter/cmd/prest-mcp@latest
```

Details: [Install pREST MCP Adapter](/ai/install-prest-mcp) · [Homebrew tutorial](/postgres-to-ai-agent/install-prest-mcp-homebrew).

Quick check:

```sh
PREST_MCP_URL=http://localhost:3000/_mcp prest-mcp
```

(Leave it running only if you are testing manually; MCP clients spawn it themselves.)

***

## 4. Connect your AI client

### Cursor

Create `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

Full guide: [Use with Cursor](/ai/cursor). Optional rules/skills: [pREST for Cursor plugin](/ai/prest-cursor).

### VS Code / GitHub Copilot

Create `.vscode/mcp.json` with a `servers` entry (`"type": "stdio"`, command `prest-mcp`, env `PREST_MCP_URL`). Use Copilot **Agent** mode to verify. Guide: [Use with VS Code and Copilot](/ai/vscode-copilot).

### Claude Desktop

Edit `claude_desktop_config.json` with the same `command` / `env` pattern (prefer an absolute path on macOS). Guide: [Use with Claude Desktop](/ai/claude-desktop).

### OpenClaw

```bash
openclaw plugins install clawhub:@prest/prest-openclaw
```

Configure MCP with `prest-mcp` and `PREST_MCP_URL` (see plugin `examples/mcp-readonly/openclaw-mcp.example.json`). Plugin guide: [pREST for OpenClaw](/ai/prest-openclaw).

### Other tools

Cline, Continue, Windsurf, and generic stdio clients: [Other AI tools](/ai/other-clients).

Registry package name: `io.github.prest/prest`.

***

## 5. Try it

Ask the agent:

* “List databases available through pREST.”
* “Describe `public.users`.”
* “Select 5 rows from `public.users`.”

Expected tools include `prest.list_databases`, `prest.describe_table`, and `prest.select.*`. Limits and auth: [MCP over HTTP](/get-started/mcp-over-http).

***

## Troubleshooting

| Symptom                    | Fix                                                                                  |
| -------------------------- | ------------------------------------------------------------------------------------ |
| No `/_mcp` response        | Upgrade to pREST v2.1.0+; confirm host/port                                          |
| Adapter exits on start     | Set `PREST_MCP_URL`                                                                  |
| Client cannot spawn binary | Absolute path to `prest-mcp`; `brew install prest/tap/prest-mcp`                     |
| Empty tools                | Grants / [permissions](/get-started/permissions); restart pREST after schema changes |
| Auth errors                | Set `PREST_MCP_TOKEN` when JWT auth is enabled                                       |

***

## Next steps

* [AI and MCP landing](/ai)
* [MCP Overview](/ai/mcp-overview)
* [Install adapter with Homebrew](/postgres-to-ai-agent/install-prest-mcp-homebrew)
* [Distribution channels](/get-prest/distribution)


# Install adapter with Homebrew

Install `prest-mcp` from the official pREST Homebrew tap so Cursor, Claude Desktop, and other stdio MCP clients can reach PostgreSQL through pREST’s `/_mcp` endpoint.

This page focuses on the **adapter**. For the pREST server formula (`prestd` on homebrew-core), see [Start with Homebrew](/get-prest/start-with-homebrew).

***

## Prerequisites

* macOS (or Linux with [Homebrew](https://brew.sh/))
* pREST **v2.1.0+** running with `/_mcp` available (local is fine)

Confirm MCP discovery:

```sh
curl -s http://localhost:3000/_mcp | head
```

***

## Install

One-shot:

```sh
brew install prest/tap/prest-mcp
```

Or tap, then install:

```sh
brew tap prest/tap
brew install prest-mcp
```

Tap repository: [prest/homebrew-tap](https://github.com/prest/homebrew-tap).

***

## Verify

```sh
which prest-mcp
PREST_MCP_URL=http://localhost:3000/_mcp prest-mcp
```

The process waits on stdin for MCP JSON-RPC. Stop with Ctrl+C after a smoke test. Logs go to stderr only.

***

## Point a client at it

Minimal env for any stdio MCP client:

| Variable               | Required | Notes                             |
| ---------------------- | -------- | --------------------------------- |
| `PREST_MCP_URL`        | yes      | e.g. `http://localhost:3000/_mcp` |
| `PREST_MCP_TOKEN`      | no       | JWT when auth is enabled          |
| `PREST_MCP_TIMEOUT_MS` | no       | Default `30000`                   |

Example Cursor `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "prest": {
      "command": "prest-mcp",
      "env": {
        "PREST_MCP_URL": "http://localhost:3000/_mcp"
      }
    }
  }
}
```

If the GUI app cannot find Homebrew’s `PATH`, use `/opt/homebrew/bin/prest-mcp` (Apple Silicon) or `/usr/local/bin/prest-mcp` (Intel).

***

## Troubleshooting

| Symptom             | Fix                                                   |
| ------------------- | ----------------------------------------------------- |
| Formula not found   | `brew update` then `brew install prest/tap/prest-mcp` |
| Stale binary        | `brew upgrade prest-mcp` or reinstall                 |
| Client spawn failed | Absolute path; restart the IDE                        |
| Connection refused  | Start pREST; check `PREST_MCP_URL`                    |

***

## Next steps

* Full install options (Go, Docker, registry): [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [Use with Cursor](/ai/cursor) · [Claude Desktop](/ai/claude-desktop)
* End-to-end: [PostgreSQL to AI agent](/postgres-to-ai-agent)
* [Start with Homebrew](/get-prest/start-with-homebrew) · [Distribution](/get-prest/distribution)


# API Reference

pREST API reference — REST (Representational State Transfer) HTTP endpoints that map to SQL on the native PostgreSQL adapter (and certified engines).

**pREST** generates HTTP APIs from your database catalog. Today the **native** dialect is PostgreSQL (and Postgres-compatible engines documented under [Databases](/databases)). This reference covers endpoints, parameters, auth, and advanced query patterns.

## What is REST?

**REST (Representational State Transfer)** is an [architectural style](https://developer.mozilla.org/en-US/docs/Glossary/REST) for building APIs over HTTP. pREST exposes your SQL tables as REST resources at `/{database}/{schema}/{table}`, mapping standard verbs to CRUD on the same server as MCP.

Application backends, mobile and web clients, and any HTTP client that needs CRUD on SQL tables use that surface. This reference documents the endpoints below; see also [Parameters](/api-reference/parameters) and [Auth](/api-reference/auth).

***prestd*** implements HTTP verbs that map to SQL data operations on the connected engine.

### GET

> SQL `SELECT` (PostgreSQL dialect mapping)

| Endpoints                           | Description                                                                                                                                 |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `/_health`                          | Liveness probe — pings default database                                                                                                     |
| `/_ready`                           | Readiness probe — pings default database and all registered aliases                                                                         |
| `/_mcp`                             | MCP discovery — server metadata and available read-only tools ([guide](/get-started/mcp-over-http), [stdio adapter](/ai/install-prest-mcp)) |
| `/databases`                        | List all databases                                                                                                                          |
| `/schemas`                          | List all schemas                                                                                                                            |
| `/tables`                           | List all tables                                                                                                                             |
| `/show/{DATABASE}/{SCHEMA}/{TABLE}` | Lists table structure - all fields contained in the table                                                                                   |
| `/{DATABASE}/{SCHEMA}`              | Lists table tables - find by schema                                                                                                         |
| `/{DATABASE}/{SCHEMA}/{TABLE}`      | List all rows, find by database, schema and table                                                                                           |
| `/{DATABASE}/{SCHEMA}/{VIEW}`       | List all rows, find by database, schema and view                                                                                            |

### POST

| Endpoints | Description                                                                                                                           |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `/_mcp`   | MCP JSON-RPC — `initialize`, `tools/list`, `tools/call` ([guide](/get-started/mcp-over-http), [stdio adapter](/ai/install-prest-mcp)) |

> SQL `INSERT` (PostgreSQL dialect mapping)

```
/{DATABASE}/{SCHEMA}/{TABLE}
```

**JSON DATA:**

```
{
    "FIELD1": "string value",
    "FIELD2": 1234567890
}
```

### PATCH and PUT

> SQL `UPDATE` (PostgreSQL dialect mapping)

Using query string to make filter (WHERE), example:

```
/{DATABASE}/{SCHEMA}/{TABLE}?{FIELD NAME}={VALUE}
```

JSON DATA:

```
{
    "FIELD1": "string value",
    "FIELD2": 1234567890,
    "ARRAYFIELD": ["value 1","value 2"]
}
```

> unconditional `update` can update unwanted record

### DELETE

> SQL `DELETE` (PostgreSQL dialect mapping)

Using query string to make filter (WHERE), example:

```
/{DATABASE}/{SCHEMA}/{TABLE}?{FIELD NAME}={VALUE}
```

> unconditional `delete` can delete unwanted record

### Related

* [Databases](/databases)
* [Acronyms](/readme/acronyms) ([REST](/readme/acronyms#rest), [MCP](/readme/acronyms#mcp))
* [Parameters](/api-reference/parameters)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Auth](/api-reference/auth)


# Custom Queries

If you need advanced SQL, write script templates and call them over REST. Scripts accept values from the URL (and headers) and run with the same auth stack as other routes.

***awesome\_folder/example\_of\_powerful.read.sql*****:**

```sql
SELECT * FROM table WHERE name = "{{.field1}}" OR name = "{{.field2}}"
```

**Get result:**

```
GET /_QUERIES/awesome_folder/example_of_powerful?field1=foo&field2=bar
```

With a [database registry](/get-started/multi-database), prefix the database alias:

```
GET /_QUERIES/tenant-a/awesome_folder/example_of_powerful?field1=foo&field2=bar
```

When the database prefix is omitted, the default database (`pg.database`) is used.

{% hint style="warning" %}
**Interpolated values are screened.** A value written into the SQL text — `'{{.field1}}'` above — becomes part of the statement, so pREST rejects values carrying quotes, `--`, `::`, or (for multi-word values) a SQL keyword. Since **v2.4.2** ([#1023](https://github.com/prest/prest/pull/1023)), a rejected value **fails the request with `400`** rather than being silently replaced by an empty string.

For anything user-supplied, [bind the value](#binding-values-sqlval-sqllist-ident-v242) with `{{sqlVal "field1"}}` instead — bound values skip the screen entirely and can never be parsed as SQL.
{% endhint %}

**To activate filesystem scripts**, set a location in `prest.toml`:

```toml
[queries]
location = "/path/to/queries/"
```

Default storage is the filesystem (`queries.storage = "filesystem"`). Database-backed storage is available in **v2.2.0+** — see [below](#database-backed-storage-v220).

### Scripts templates rules

In your scripts, the fields to replace have to look like: *field1 or field2 are examples.*

```sql
SELECT * FROM table WHERE name = "{{.field1}}" OR name = "{{.field2}}"
```

Script file must have a suffix based on http verb:

| HTTP Verb  | Suffix      |
| ---------- | ----------- |
| GET        | .read.sql   |
| POST       | .write.sql  |
| PUT, PATCH | .update.sql |
| DELETE     | .delete.sql |

In `queries.location` you need to have a folder for your scripts:

```shell
queries/
└── foo
    └── some_get.read.sql
    └── some_create.write.sql
    └── some_update.update.sql
    └── some_delete.delete.sql
└── bar
    └── some_get.read.sql
    └── some_create.write.sql
    └── some_update.update.sql
    └── some_delete.delete.sql

URLs to foo folder:

GET    /_QUERIES/foo/some_get?field1=bar
POST   /_QUERIES/foo/some_create?field1=bar
PUT    /_QUERIES/foo/some_update?field1=bar
PATCH  /_QUERIES/foo/some_update?field1=bar
DELETE /_QUERIES/foo/some_delete?field1=bar


URLs to bar folder:

GET    /_QUERIES/bar/some_get?field1=foo
POST   /_QUERIES/bar/some_create?field1=foo
PUT    /_QUERIES/bar/some_update?field1=foo
PATCH  /_QUERIES/bar/some_update?field1=foo
DELETE /_QUERIES/bar/some_delete?field1=foo
```

### Template data

You can access the query parameters of the incoming HTTP request using the `.` notation.

For instance, the following request:

```
GET    /_QUERIES/bar/some_get?field1=foo&field2=bar
```

Makes available the fields `field1` and `field2` in the script:

```sql
{{.field1}}
{{.field2}}
```

You can also access the query headers of the incoming HTTP requests using the `.header` notation.

For instance, the following request:

```
GET    /_QUERIES/bar/some_get
X-UserId: am9obi5kb2VAYW5vbnltb3VzLmNvbQ
X-Application: prest
```

makes available the headers `X-UserId` and `X-Application` in the script:

```
{{index .header "X-UserId"}}
{{index .header "X-Application"}}
```

Header values go through the same screen as query parameters. A rejected header is blanked and logged at `WARN` rather than failing the request — an ordinary `User-Agent` contains `(` and `;`, which the character allow-list refuses, so erroring would reject nearly every browser request.

The keys `header`, `_param`, and `_header` are reserved for template data. Query parameters using those names are ignored.

#### Credential headers (v2.4.2)

Since **v2.4.2** ([#1023](https://github.com/prest/prest/pull/1023)), credential-bearing headers are withheld from templates entirely and render as an empty string:

`Authorization` · `Proxy-Authorization` · `Cookie` · `X-Api-Key` · `X-Auth-Token` · `X-Access-Token`

They are withheld from the bound form too, so `{{sqlVal "header.Authorization"}}` is also empty — blanking them is about secrecy, and binding must not become a way around it. A bearer token is plain base64url text that passed the value screen untouched, so a template referencing it interpolated the caller's credential into SQL that was then logged.

The request still succeeds; only the value is gone. A script that scoped rows by the caller's token now matches nothing — move that logic to [Permissions](/get-started/permissions), or pass a non-credential header such as `X-UserId`.

### Binding values (`sqlVal`, `sqlList`, `ident`) (v2.4.2)

Bind a value instead of interpolating it and the screen does not apply at all. A bound value travels to PostgreSQL out of band as a query parameter, where it can never be parsed as SQL:

```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 or column name, which cannot be bound | `"public"."users"` |

`sqlVal` and `sqlList` also reach headers, using a `header.` prefix:

```sql
SELECT * FROM tenants WHERE app = {{sqlVal "header.X-Application"}}
```

These helpers have existed since v2.0.0, but before **v2.4.2** they bound the *screened* value — so a rejected value bound as `""`. Since v2.4.2 ([#1023](https://github.com/prest/prest/pull/1023)) they resolve the raw value, which is what makes binding a complete alternative to the screen.

{% hint style="info" %}
Prefer binding for anything user-supplied — search phrases especially. A phrase containing a common word such as `do`, `as`, or `or` is exactly what the interpolation screen refuses.
{% endhint %}

**When a value is rejected**, the request fails with `400` and a message naming the parameter. The value itself is never echoed back:

```json
{
  "error": "invalid value for parameter slug: it contains SQL syntax that cannot be interpolated safely; use the sqlVal template helper to bind free-form values"
}
```

This applies to anything that renders the value into SQL text — `{{.slug}}`, `{{inFormat "slug"}}`, `{{unEscape .slug}}`. `sqlVal` and `sqlList` are exempt.

### Template functions

#### isSet

Return true if the param is set.

```sql
SELECT * FROM table
{{if isSet "field1"}}
WHERE name = "{{.field1}}"
{{end}}
```

#### defaultOrValue

Return param value or default value.

```sql
SELECT * FROM table WHERE name = '{{defaultOrValue "field1" "gopher"}}'
```

#### inFormat

If you need to format data for usage on a `IN ('option1', 'option2')` statement. You can use this with the field inside the format. Whenever passing multiple arguments to `inFormat` function, you must use multiple `field1` instances on the URL.

For example: I want to query the field `name` with options `Mary` and `John`.

```sql
-- URL will be equal to /_QUERIES/custom_query/query?name=Mary&name=John

SELECT * FROM names WHERE name IN {{inFormat "name"}}
```

**Future updates** will include the support of multiple strings split by `,` on the same instance of the field.

#### split

Splits a string into substrings separated by a delimiter

```sql
SELECT * FROM table WHERE
name IN ({{ range $index,$part := split 'test1,test2,test3' `,` }}{{if gt $index 0 }},{{end}}'{{$part}}'{{ end }})
```

#### limitOffset

Assemble `limit offset()` string with validation for non-allowed characters *parameters must be integer values*

```sql
SELECT * FROM table {{limitOffset "1" "10"}}
```

**generating the query:**

```sql
SELECT * FROM table LIMIT 10 OFFSET(1 - 1) * 10
```

*We recommend using the default pREST variables `_page` and `_page_size`:*

```sql
{{limitOffset ._page ._page_size}}
```

#### unEscape

URL-decodes a string (percent-encoding and `+`). The result is written into the SQL text, so it is subject to the [value screen](#binding-values-sqlval-sqllist-ident-v242) — bind with `sqlVal` when the value comes from the caller.

```sql
SELECT * FROM table WHERE path = '{{unEscape .path}}'
```

### Database-backed storage (v2.2.0+)

{% hint style="info" %}
Available in **pREST v2.2.0+** ([#980](https://github.com/prest/prest/pull/980)). See [v2.2.0 release notes](/releases/v2.2.0).
{% endhint %}

Set `queries.storage = "database"` to store scripts in the `prest_queries` table instead of (or in addition to importing from) `.sql` files. Execution URLs stay the same (`/_QUERIES/{location}/{script}`).

```toml
[auth]
enabled = true
migrate_on_startup = true

[jwt]
key = "your-secret"
algo = "HS256"

[queries]
storage = "database"          # default: filesystem
schema = "public"
table = "prest_queries"
register_enabled = true
register_admins = ["admin@example.com"]
restrict = true
migrate_on_startup = true     # default true when storage = "database"
import_on_startup = true      # default true when storage = "database"
import_policy = "update"      # skip | update | error
location = "./queries"        # filesystem import source (also PREST_QUERIES_LOCATION)

[[queries.scripts]]
location = "fulltable"
name = "get_all"
permissions = ["read"]

[[queries.users]]
name = "app_user@example.com"
[[queries.users.scripts]]
location = "fulltable"
name = "get_all"
permissions = ["read"]
```

| Key                  | Meaning                                                   |
| -------------------- | --------------------------------------------------------- |
| `storage`            | `filesystem` (default) or `database`                      |
| `schema` / `table`   | Where rows live (default `public.prest_queries`)          |
| `migrate_on_startup` | Create the table on API boot when using database storage  |
| `import_on_startup`  | Import `.sql` files from `location` into the table        |
| `import_policy`      | `skip`, `update`, or `error` when a script already exists |
| `restrict`           | Require auth + script ACL for `/_QUERIES` execution       |
| `register_enabled`   | Enable admin CRUD on `/_QUERIES/registry`                 |
| `register_admins`    | Usernames allowed to use the registry API                 |

**Fail-closed:** `register_enabled` is auto-disabled without `auth.enabled`, `jwt.key`, and a non-empty `register_admins`. `restrict` is auto-disabled without `auth.enabled`.

#### CLI migrate

```sh
prestd migrate up queries
prestd migrate down queries
```

#### Registry API

When `register_enabled = true`, admins listed in `register_admins` can manage stored scripts:

| Method   | Path                                   | Purpose                                    |
| -------- | -------------------------------------- | ------------------------------------------ |
| `GET`    | `/_QUERIES/registry`                   | List (`?database=` / `?location=` filters) |
| `POST`   | `/_QUERIES/registry`                   | Create / upsert                            |
| `GET`    | `/_QUERIES/registry/{location}/{name}` | Get one                                    |
| `PUT`    | `/_QUERIES/registry/{location}/{name}` | Update                                     |
| `DELETE` | `/_QUERIES/registry/{location}/{name}` | Delete                                     |

Optional `{database}` path segment or query param scopes multi-database aliases.

Create/update JSON body fields include `database`, `location`, `name`, `read_sql`, `write_sql`, `update_sql`, `delete_sql`, and `description` (body size capped at 1 MiB).

Full key list: [`samples/prest.sample.toml`](https://github.com/prest/prest/blob/main/samples/prest.sample.toml). Fixture: [`testdata/prest_queries.toml`](https://github.com/prest/prest/blob/main/testdata/prest_queries.toml).

### Ready-made queries

*consultations ready to use prest*

* [Opps CMS](https://github.com/opps/prest-queries)

## Troubleshooting

| Response                                                                  | Cause                                                                                                                                                                                                                                     |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` — `invalid value for parameter <name>: …`                           | An interpolated value was rejected by the screen. [Bind it](#binding-values-sqlval-sqllist-ident-v242) with `sqlVal`.                                                                                                                     |
| `400` — `invalid identifier in path`                                      | The database, folder, or script name contains characters outside the allow-list, including `.` — so `get_all.read` cannot be requested directly.                                                                                          |
| `400` — `invalid script path: <folder>/<script>`                          | The resolved `.sql` file lies outside the queries directory. Since **v2.4.2** ([#1023](https://github.com/prest/prest/pull/1023)), `..` segments and symlinks escaping the tree are rejected both lexically and after symlink resolution. |
| `400` — `could not parse script <folder>/<script>, check your prest logs` | A template parse or render error. Since **v2.4.2** the detail is logged rather than returned, so read the `prestd` logs.                                                                                                                  |
| Empty value where a header was expected                                   | The header is a [credential header](#credential-headers-v242) and is withheld from templates.                                                                                                                                             |

{% hint style="info" %}
Since **v2.4.2**, SQL generated from custom query scripts is **not written to logs at any level** — the statement is caller-influenced. Use PostgreSQL's own statement logging when you need to see it. See [Configuring pREST — Logging](/get-started/configuring-prest#logging).
{% endhint %}

## Related

* [Configuring pREST](/get-started/configuring-prest)
* [Multi-database](/get-started/multi-database)
* [Permissions](/get-started/permissions)
* [v2.4.2 release notes](/releases/v2.4.2) · [v2.2.0 release notes](/releases/v2.2.0)
* [Acronyms](/readme/acronyms) · [REST](/readme/acronyms#rest) · [SQL](/readme/acronyms#sql)


# Auth

***prestd*** has support in **JWT Token** generation based on two fields (example user and password), being possible to use an existing table from your database to login configuring some parameters in the configuration file (or environment variable), *by default this feature is* **disabled**.

* Bearer - [RFC 6750](https://tools.ietf.org/html/rfc6750), bearer tokens to access OAuth 2.0-protected resources
* Basic - [RFC 7617](https://tools.ietf.org/html/rfc7617), base64-encoded credentials. More information below

> understand more about *http authentication* [see this documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication)

***

## JWT verification (v2 defaults)

JWT middleware is **disabled by default** in v2+ (`jwt.default = false`). Set `jwt.default = true` to require a valid Bearer token on all endpoints except those in the JWT whitelist. See [Configuring pREST — JWT](/get-started/configuring-prest#jwt) for full configuration.

MCP (`/_mcp`, v2.1.0+) inherits the same JWT and auth stack as REST routes. When auth is enabled, send credentials on every `GET` and `POST` to `/_mcp`. Stdio clients that use the [pREST MCP Adapter](/get-started/prest-mcp-adapter) can pass a bearer token via `PREST_MCP_TOKEN`.

### Required verification material

When `jwt.default = true` and debug mode is off, you should configure one of the following:

| Setting            | Environment variable     | Purpose                                                                                              |
| ------------------ | ------------------------ | ---------------------------------------------------------------------------------------------------- |
| `jwt.key`          | `PREST_JWT_KEY`          | Shared secret for HMAC algorithms — minimum length applies, see [below](#hmac-key-requirements-v242) |
| `jwt.jwks`         | `PREST_JWT_JWKS`         | JSON Web Key Set for asymmetric verification                                                         |
| `jwt.wellknownurl` | `PREST_JWT_WELLKNOWNURL` | OpenID Connect well-known URL to fetch JWKS                                                          |

**In v2+ (**[**#974**](https://github.com/prest/prest/pull/974)**):** if JWT is enabled but no verification material is configured, JWT middleware is **auto-disabled** with an error log — the server continues to start. When `auth.enabled = true` without `jwt.key`, auth is also auto-disabled.

> **v2.0.0-rc6 tagged binary:** the rc6 release **refuses to start** in the same situations. See [v2.0.0-rc6](/releases/v2.0.0-rc6#jwt-fail-closed-startup-960).

### HMAC key requirements (v2.4.2)

Since **v2.4.2** ([#1017](https://github.com/prest/prest/pull/1017)), pREST validates `jwt.key` against the RFC 7518 minimum for the configured HMAC algorithm at config load. The underlying library (`go-jose/v4`) enforces these sizes itself; checking at startup surfaces the problem in logs instead of at request time.

| `jwt.algo`                                          | Minimum `jwt.key` length                         |
| --------------------------------------------------- | ------------------------------------------------ |
| `HS256` — also the default when `jwt.algo` is unset | **32 bytes**                                     |
| `HS384`                                             | **48 bytes**                                     |
| `HS512`                                             | **64 bytes**                                     |
| `RS*`, `ES*`, `PS*`, `EdDSA`                        | Not checked — `jwt.key` is not used as a MAC key |

The check is on the byte length of the raw string, so a 32-character ASCII secret satisfies HS256. Measure yours with `printf '%s' "$PREST_JWT_KEY" | wc -c`.

{% hint style="danger" %}
**An undersized key fails open, not closed.** pREST starts normally, discards the key, and disables the features that need it. `POST /auth` is no longer registered (clients get **404**, not 401) and every route wrapped by the auth middleware **passes through unauthenticated**. With `jwt.default = true` and no JWKS, the JWT middleware is removed from the stack entirely.

Rotate short secrets **before** upgrading, and check startup logs for:

```
level=ERROR msg="jwt.key too short for HMAC algorithm" algo=HS256 got=6 want=32
level=ERROR msg="auth disabled: jwt.key is empty"
```

{% endhint %}

A configured `jwt.jwks` or `jwt.wellknownurl` is unaffected — verification continues against the JWKS even when an undersized HMAC key is discarded.

### Signature algorithm (`jwt.algo`, v2.4.2)

`jwt.algo` was accepted but discarded in earlier v2 releases — tokens were parsed without restricting the permitted signature algorithm. Since **v2.4.2** it is passed to the parser as the single allowed algorithm, which structurally prevents algorithm-confusion attacks.

* A token whose `alg` header does not match `jwt.algo` is rejected with **401** and `{"error": "failed JWT token parser"}`.
* The value is matched **case-sensitively** against `EdDSA`, `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`, `PS256`, `PS384`, `PS512`. Anything else — including `hs256` in lowercase or an explicit `algo = ""` — makes every request return **HTTP 500** with `unsupported JWT signature algorithm`.

Leave `jwt.algo` unset to get the `HS256` default.

### JWKS fetch hardening (v2.3.0)

When you configure `jwt.wellknownurl` / `PREST_JWT_WELLKNOWNURL`, pREST fetches the JWKS from the identity provider. Since **v2.3.0** ([#1002](https://github.com/prest/prest/pull/1002)), that fetch (now on `jwx/v3`):

* **Rejects non-2xx responses** instead of attempting to parse an error page.
* **Caps the response body at 1 MiB** to bound memory.
* **Redacts the URL in logs** — userinfo, query string, and fragment are dropped.

Key-matching semantics (kid match, single-key with empty kid, empty-HMAC-key bypass guard) are unchanged, and there are **no config or environment changes**: `jwt.jwks` and `jwt.wellknownurl` work exactly as before.

Debug mode (`PREST_DEBUG=true` or `debug = true` in TOML) bypasses JWT enforcement at runtime.

To disable JWT entirely, leave `jwt.default = false` (the default in v2+).

### Whitelist

Endpoints matching the whitelist regex do not require a JWT. The v2 default whitelist is `^\/auth$` (only the `/auth` endpoint). Configure additional patterns in TOML or via `PREST_JWT_WHITELIST`:

```toml
[jwt]
whitelist = ["\\/auth", "\\/ping", "\\/ping\\/.*"]
```

### Upgrading from v1

v1 used `[/auth]` as the default whitelist and did not enforce JWT key configuration at startup. See [Upgrading to v2](/get-started/upgrading-to-v2) for migration steps.

***

## Token generation (`/auth` endpoint)

When `auth.enabled = true`, pREST exposes a `/auth` endpoint that validates credentials against a database table and returns a signed JWT.

### Bearer

```sh
curl -i -X POST http://127.0.0.1:3000/auth -H "Content-Type: application/json" -d '{"username": "<username>", "password": "<password>"}'
```

### Basic

```sh
curl -i -X POST http://127.0.0.1:3000/auth --user "<username>:<password>"
```

## Related

* [Configuring pREST — JWT](/get-started/configuring-prest#jwt)
* [v2.4.2 release notes](/releases/v2.4.2)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Install pREST MCP Adapter](/ai/install-prest-mcp)
* [Permissions](/get-started/permissions)
* [Acronyms](/readme/acronyms) · [JWT](/readme/acronyms#jwt) · [MCP](/readme/acronyms#mcp)


# Parameters

***prestd*** uses query string to apply filtering, sorting, paginating, and etc to api queries.

### Filters

HTTP method `GET`

| query string                             | Description                                                                                                                                                                                                       |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `_page={set page number}`                | the api return is paged, this parameter sets which page you want                                                                                                                                                  |
| `_page_size={number to return by pages}` | delimits the number of records per page, default `10`. Every time you specify a page size, you must include the page you are accessing.                                                                           |
| `?_select={field name 1},{field name 2}` | Limit fields list on result. Comma-separated field names are whitespace-trimmed (`id, name` is equivalent to `id,name`). Values are validated — see [`_select` field validation](#_select-field-validation-v230). |
| `?_count={field name}`                   | Count per field - `*` representation all fields                                                                                                                                                                   |
| `?_count_first=true`                     | Query string `_count` returns a list, passing this parameter will return the first record as a non-list object, **by default** this parameter is set to `false` (*return list non-object*)                        |
| `?_renderer=xml`                         | Set API render syntax, supported: `json` (by default), `xml`                                                                                                                                                      |
| `?_distinct=true`                        | `DISTINCT` clause with SELECT                                                                                                                                                                                     |
| `?_order={FIELD}`                        | `ORDER BY` in sql query. For `DESC` order, use the prefix `-`. For *multiple* orders, the fields are separated by comma `fieldname01,-fieldname02,fieldname03`                                                    |
| `?_groupby={FIELD}`                      | `GROUP BY` in sql query, The grouper is more complicated, a topic has been created to describe how to use                                                                                                         |
| `?{FIELD NAME}={VALUE}`                  | Filter by field, you can set as many query parameters as needed                                                                                                                                                   |
| `?_or={CONDITION}\|\|{CONDITION}`        | OR clause filtering — combine alternatives with `\|\|`. See [OR clause filtering](#or-clause-filtering) below.                                                                                                    |

#### Functions support

Used to perform data **aggregation**(**grouping** and **selection**)

| name     | Use in request   |
| -------- | ---------------- |
| SUM      | `sum:field`      |
| AVG      | `avg:field`      |
| MAX      | `max:field`      |
| MIN      | `min:field`      |
| STDDEV   | `stddev:field`   |
| VARIANCE | `variance:field` |

**`SELECT` with function:**

```
/{DATABASE}/{SCHEMA}/{TABLE}?_select=fieldname00,sum:fieldname01&_groupby=fieldname01
```

**`GROUP BY` with function:**

```
/{DATABASE}/{SCHEMA}/{TABLE}?_groupby=fieldname->>having:GROUPFUNC:FIELDNAME:CONDITION:VALUE-CONDITION
/{DATABASE}/{SCHEMA}/{TABLE}?_select=fieldname00,sum:fieldname01&_groupby=fieldname01->>having:sum:fieldname01:$gt:500
```

### `_select` field validation (v2.3.0)

Since **v2.3.0** ([#1002](https://github.com/prest/prest/pull/1002), [GHSA-qvx3-q8vx-9q3c](https://github.com/prest/prest/security/advisories/GHSA-qvx3-q8vx-9q3c)), `_select` and `_count` values pass through a single validation gate that closes an unauthenticated SQL-injection. Each comma-separated field must be one of:

| Form                          | Example                    |
| ----------------------------- | -------------------------- |
| Wildcard                      | `*`                        |
| Identifier, optionally dotted | `id`, `public.users.name`  |
| Colon-syntax aggregate        | `sum:salary`, `avg:rating` |
| Pre-quoted aggregate          | `SUM("salary") AS "total"` |

Aggregates are limited to `SUM`, `AVG`, `MAX`, `MIN`, `STDDEV`, `VARIANCE`. Anything else — subselects, `pg_*` probing, or extra parentheses — returns **`400`** **`ErrInvalidIdentifier`**. `_count` field names are quoted in the generated SQL (`, celphone` → `, "celphone"`).

For projections that need arbitrary SQL expressions, use a [custom query](/api-reference/custom-queries) instead.

### pgvector KNN ordering and distance filtering (v2.4.0)

Since **v2.4.0** ([#1011](https://github.com/prest/prest/pull/1011)), two query-parameter forms are available for `vector`-typed columns (requires the `pgvector` extension on the target database):

| Parameter          | Form                                         | Example                               | Effect                                                                                       |
| ------------------ | -------------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- |
| `_korder`          | `<column>:<metric>:<vector>`                 | `_korder=embedding:l2:[1,0,0]`        | Orders by nearest-neighbor distance (KNN); composes with `_order` as an additional sort term |
| `<column>:vecdist` | `<metric>:<comparison>:<vector>:<threshold>` | `embedding:vecdist=l2:lt:[1,0,0]:0.5` | Filters rows by distance threshold                                                           |

Metrics are restricted to a fixed whitelist: `l2`/`euclidean`, `cosine`/`cos`, `ip`/`inner`/`dot`, `l1`/`manhattan`. `:vecdist` comparisons are restricted to `=`, `!=`, `<`, `<=`, `>`, `>=` (non-scalar comparisons like `like` are rejected). The column goes through identifier validation, the vector literal round-trips through `ParseFloat`/`FormatFloat`, and the threshold is passed as a bound parameter — malformed metrics, non-numeric vector elements, oversized vectors (>16000 dims, pgvector's own limit), and dimension mismatches all return `400` rather than reaching the database unsafely.

```http
GET /db/public/docs?_korder=embedding:cosine:[0.1,0.2,0.3]&_page_size=5
GET /db/public/docs?embedding:vecdist=l2:lt:[0.1,0.2,0.3]:0.5
```

### Operators Reference Guide

The following operators are used for filtering data in queries. Each operator defines a specific matching condition that determines which records are included in the result set.

***

| Operator         | Description                                                  | Example Usage                                   |
| ---------------- | ------------------------------------------------------------ | ----------------------------------------------- |
| `$eq`            | Matches values that are equal to a specified value.          | `status=$eq.active`                             |
| `$gt`            | Matches values greater than a specified value.               | `age=$gt.25`                                    |
| `$gte`           | Matches values greater than or equal to a specified value.   | `salary=$gte.50000`                             |
| `$lt`            | Matches values less than a specified value.                  | `experience=$lt.5`                              |
| `$lte`           | Matches values less than or equal to a specified value.      | `rating=$lte.4.5`                               |
| `$ne`            | Matches values that are not equal to a specified value.      | `status=$ne.closed`                             |
| `$in`            | Matches any of the values specified in an array.             | `role=$in.admin,editor,viewer`                  |
| `$nin`           | Matches none of the values specified in an array.            | `department=$nin.hr,finance`                    |
| `$null`          | Matches if the field value is null.                          | `remarks=$null`                                 |
| `$notnull`       | Matches if the field value is not null.                      | `remarks=$notnull`                              |
| `$true`          | Matches if the field value is true.                          | `is_verified=$true`                             |
| `$nottrue`       | Matches if the field value is not true.                      | `is_verified=$nottrue`                          |
| `$false`         | Matches if the field value is false.                         | `is_active=$false`                              |
| `$notfalse`      | Matches if the field value is not false.                     | `is_active=$notfalse`                           |
| `$like`          | Matches the entire string (case-sensitive).                  | `name=$like.John%`                              |
| `$ilike`         | Matches the entire string, case-insensitive.                 | `city=$ilike.mumbai%`                           |
| `$nlike`         | Excludes matches that cover the entire string.               | `email=$nlike.%@test.com`                       |
| `$nilike`        | Excludes matches, case-insensitive.                          | `email=$nilike.%@gmail.com`                     |
| `$ltreelanc`     | Checks if left argument is an ancestor of right (or equal).  | `category_path=$ltreelanc.electronics`          |
| `$ltreerdesc`    | Checks if left argument is a descendant of right (or equal). | `category_path=$ltreerdesc.electronics.mobiles` |
| `$ltreematch`    | Checks if ltree matches lquery.                              | `tags=$ltreematch.tech.*`                       |
| `$ltreematchtxt` | Checks if ltree matches ltxtquery.                           | `tags=$ltreematchtxt.smartphone & android`      |

***

### OR clause filtering

Use `_or` to combine filter conditions with OR logic without writing a custom SQL query. Available since v2.0.0-rc6, included in [v2.0.0](/releases/v2.0.0).

Each alternative is `field=$operator.value` using the same operators as the table above. Separate alternatives with `||` (double pipe). The OR group is parenthesized and AND-combined with other query parameters.

```http
GET /db/public/articles?_or=title=$ilike.%search%||name=$ilike.%search%
GET /db/public/items?_or=status=$eq.active||status=$eq.pending&category=$eq.tech
```

The second example matches rows where `(status = 'active' OR status = 'pending') AND category = 'tech'`.

**Notes:**

* Empty or malformed `_or` values are ignored.
* Use `||` to separate alternatives — not a literal `OR` inside values.
* Comma-separated values within a single alternative (e.g. `$in`) are preserved.

***

### Notes

* Comma (`,`) is used to separate multiple values in `$in` and `$nin` operators.
* Pattern matching operators like `$like` and `$ilike` support SQL wildcards (`%`, `_`).
* LTree operators (`$ltreelanc`, `$ltreerdesc`, etc.) are useful for hierarchical data filtering.


# Advanced Queries

***prestd*** allows you to do some advanced queries, but with some limitations in order to make the URL and parameters as clean as possible.

### Filter range

It is common to query a database in a range of time, e.g:

> I need all records that were created from `1997-11-03-03` to `1997-12-05`.

`created_at` field is a timestamp field, so you can use the `$gte` and `$lte` operators to filter by date range.

```sql
SELECT * FROM {SCHEMA}.{TABLE} WHERE created_at >= '1997-11-03-03' AND created_at <= '1997-12-05'
```

**How to do it in prestd?**

```http
GET /{DATABASE}/{SCHEMA}/{TABLE}?created_at='$gte.1997-11-03'&created_at='$lte.1997-12-0'
```

There are other types of operators, see them all here.

### JOIN

HTTP verb `GET`, allows you to join tables, with 1 level of depth - unfortunately the syntax is not so friendly so we limited it to 1 level only.

```
/{DATABASE}/{SCHEMA}/{TABLE}?_join={TYPE}:{TABLE JOIN}:{TABLE.FIELD}:{OPERATOR}:{TABLE JOIN.FIELD}
```

Parameters:

1. Type:
   * `inner`
   * `left`
   * `right`
   * `outer`
2. **Table** used in the join
3. **Table.field** - table name **dot** field
4. Operator:
   * `$eq`
   * `$lt`
   * `$gt`
   * `$lte`
   * `$gte`
5. **Table2.field** - table name **dot** field

Using query string to JOIN tables, example:

```
/{DATABASE}/{SCHEMA}/friends?_join=inner:users:friends.userid:$eq:users.id
```

If you need multiple joins, we recommend using the queues feature (sql script execution).

### JSONb support

PostgreSQL offers type for storing jsonb data. To implement efficient query mechanisms for these data types.

```
?FIELD->>JSONFIELD:jsonb=VALUE
```

#### Example of how to do insertion via `cURL`

**Fields:**

* id: `INT`
* data: `JSONB`

````sh

```sh
curl -i -X POST 'http://127.0.0.1:3000/prest/public/t_mydata' \
  -H 'Content-Type: application/json' \
  -d '{"id": 80, "data": "{\"a\": 123}"}'
````

> [reference](https://github.com/prest/prest/issues/815)

### Full Text Search (with tsquery)

Full Text Searching (or just text search) provides the capability to identify natural-language documents that satisfy a query, and optionally to sort them by relevance to the query. The most common type of search is to find all documents containing given query terms and return them in order of their similarity to the query. Notions of query and similarity are very flexible and depend on the specific application. The simplest search considers query as a set of words and similarity as the frequency of query words in the document.

Is native feature of PostgreSQL since version 8.3, read more [here](https://www.postgresql.org/docs/9.5/textsearch-intro.html).

> A tsquery value stores lexemes that are to be searched for, and combines them honoring the Boolean operators & (AND), | (OR), and ! (NOT). Parentheses can be used to enforce grouping of the operators. `SELECT 'fat & rat'::tsquery;`

> **Security note (v2 rc5):** tsquery input validation was hardened ([#940](https://github.com/prest/prest/pull/940)). Always pass well-formed tsquery values — do not rely on pREST to sanitize malformed input.

```
?FIELD:tsquery=VALUE
```

**Set language**

You can specify the language you want to tokenize in, for example: **portuguese**

```
FIELD$LANGUAGE:tsquery=VALUE
```

**Language list:**

* simple
* arabic
* danish
* dutch
* english
* finnish
* french
* german
* hungarian
* indonesian
* irish
* italian
* lithuanian
* nepali
* norwegian
* portuguese
* romanian
* russian
* spanish
* swedish
* tamil
* turkish

To see all the languages available in your PostgreSQL run this query:

```sql
SELECT cfgname FROM pg_ts_config;
```

### Batch Insert

HTTP verb `POST`, you can insert many rows at once using batch endpoint `/batch/...`.

```
/batch/DATABASE/SCHEMA/TABLE

```

**JSON DATA:**

```
[
    {"FIELD1": "string value", "FIELD2": 1234567890},
    {"FIELD1": "other string value", "FIELD2":1234567891},
]
```

The default insert method is using multiple tuple values like `insert into table values ("value", 123), ("other", 456)`. Returns inserted rows.

You can change this behavior using the header `Prest-Batch-Method` with value `copy`. It's useful for large insertions, but the return is empty.


# Deployment

You can follow one of our guides to deploy ***prestd*** via the technology you prefer:

* [Docker](/deployment/deploying-with-docker) **(Recommended)**
* [Kubernetes](https://github.com/prest/prest/tree/main/install-manifests/kubernetes)
* [Heroku Guide](/deployment/deploy-in-heroku)
* [Homebrew](/get-prest/start-with-homebrew) — `prestd` and `prest/tap/prest-mcp`
* Go language from the [source](https://github.com/prest/prest)
* Binary

The *self-hosted* version is up to the host to keep prestd up to date, and we invite you to receive all news from prestd via the [**alpha program**, learn more here](https://github.com/prest/prest/discussions/467).

### Troubleshooting

If you encounter an error while installing ***prestd*** on any platform, [open a new discussion thread](https://github.com/prest/prest/discussions/new) or join our [Discord](https://discord.gg/JnRjvu39w8).


# Deploying with Docker

This guide assumes you already have a PostgreSQL (or [Postgres-compatible](/databases)) database running and helps you set up ***prestd*** with Docker.

In case you’d like to run ***prestd*** with a fresh PostgreSQL database, follow this guide to deploy ***prestd*** along with a Postgres instance using *Docker Compose*.

### Prerequisites

* [Docker](https://docs.docker.com/get-docker/) (version 20.10.7 or later)
* [Docker Compose](https://docs.docker.com/compose/install/) (version 1.29.2 or later)

Create an installation folder called `prestd` where you would like your `prestd` installation and data storage.

**`cd`** (open/join) into the installation folder.

***

### Quick Start

We will use docker to run pREST and connect to an existing database. To simplify the example we leave the authentication module off and enable debug mode (which disables JWT enforcement).

```shell
docker run -d -p 3000:3000 \
    -e PREST_VERSION=2 \
    -e PREST_PG_URL=postgres://username:password@hostname:port/dbname \
    -e PREST_DEBUG=true \
    prest/prest:v2.4.2
```

> **v2 JWT requirement**: when `PREST_DEBUG` is not set and `jwt.default` is enabled, configure `PREST_JWT_KEY` (or `PREST_JWT_JWKS` / `PREST_JWT_WELLKNOWNURL`) explicitly. In **v2+**, missing verification material auto-disables JWT with a warning — the server still starts. The **v2.0.0-rc6** tagged binary refuses to start in that case. See [Configuring pREST](/get-started/configuring-prest#jwt).

> **Docker images**: v2 images are built via GoReleaser. Pin to a specific tag like `v2.4.2` rather than using `latest` in production. MCP over HTTP (`/_mcp`) is available on v2.1.0+; Studio (`/_studio/`) on v2.2.0+.

Edit the `PREST_PG_URL` env var value, so that you can connect to your Postgres instance.

Examples of `PREST_PG_URL`:

* postgres\://admin:password\@localhost:5432/my-db
* postgres\://admin:@localhost:5432/my-db *(if there is no password)*

> If your password contains special characters (e.g. #, %, $, @, etc.), you need to URL encode them in the `PREST_PG_URL` env var (e.g. %40 for @). You can check the logs to see if the database credentials are proper and if pREST is able to connect to the database. pREST needs access permissions to your Postgres database as described in permissions page.

#### Network config

If your Postgres instance is running on `localhost`, the following changes will be needed to the `docker run` command to allow the Docker container to access the host’s network.

Add the `--net=host` flag to access the host’s Postgres service.

This is what your command should look like:

```shell
docker run -d --net=host -p 3000:3000 \
    -e PREST_PG_URL=...
```

> if you are using another operating system we recommend reading the [docker network documentation](https://docs.docker.com/network/host/), on **macOS** and **Windows** it is different.

### With Docker Compose

> Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the [list of features](https://docs.docker.com/compose/#features).

Compose file in the prest repo: [docker-compose-prod.yml](https://github.com/prest/prest/blob/main/docker-compose-prod.yml)

**Download docker compose file**

```sh
wget https://raw.githubusercontent.com/prest/prest/main/docker-compose-prod.yml -O docker-compose.yml
```

**Up (run) PostgreSQL and prestd**

```sh
docker-compose up
```

**Run data migration to create user structure for access (JWT)**

```sh
docker-compose exec prest prestd migrate up auth
```

**Create user and password for API access (via JWT)**

* **user:** prest
* **pass:** prest

v2 defaults `auth.encrypt` to `bcrypt`. Hash the password with `htpasswd` (apache2-utils / httpd) on the host, then insert:

```sh
HASH=$(htpasswd -nbBC 10 x prest | cut -d: -f2)
docker-compose exec postgres psql -d prest -U prest -c "INSERT INTO prest_users (name, username, password) VALUES ('pREST Full Name', 'prest', '$HASH')"
```

**Check if the user was created successfully (by doing a select on the table)**

```sh
docker-compose exec postgres psql -d prest -U prest -c "select * from prest_users"
```

### First call on API

Example using `curl`:

```sh
curl -i -X GET http://127.0.0.1:3000/databases -H "Content-Type: application/json"
```

Additionally you can:

```sh
# Generate JWT Token with user and password created
curl -i -X POST http://127.0.0.1:3000/auth -H "Content-Type: application/json" -d '{"username": "prest", "password": "prest"}'
# Access endpoint using JWT Token
curl -i -X GET http://127.0.0.1:3000/prest/public/prest_users -H "Accept: application/json" -H "Authorization: Bearer {TOKEN}"
# Optional: MCP discovery (v2.1.0+)
curl -s http://127.0.0.1:3000/_mcp | head
```

### Kubernetes readiness probe

For multi-database or production deployments, use `GET /_ready` as the readiness probe. It pings the default database and every registered alias ([#973](https://github.com/prest/prest/pull/973)).

```yaml
readinessProbe:
  httpGet:
    path: /_ready
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10
livenessProbe:
  httpGet:
    path: /_health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 30
```

See the [Kubernetes deployment manifest](https://github.com/prest/prest/blob/main/install-manifests/kubernetes/deployment.yaml) in the prest repo for a multi-secret example.

## Related

* [Start with Docker](/get-prest/start-with-docker)
* [Upgrading to v2](/get-started/upgrading-to-v2)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Acronyms](/readme/acronyms)


# Deploy in Heroku

This guide shows how to deploy ***prestd*** on Heroku.

### Prerequisites

* Heroku accoount - [*sign up for a free account*](https://signup.heroku.com/)
* [PostgreSQL *add-on*](https://devcenter.heroku.com/articles/heroku-postgresql) - *works with the free version*

***

### Quick Start

Click the below button to deploy ***prestd*** on Heroku in a few seconds:

[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/prest/prest-heroku)

> If you don’t have an account on Heroku, you need to sign up on Heroku. You won’t need a credit card, and once you sign up you’ll be redirected to your Heroku app creation page automatically.

Heroku’s free Postgres add-on is automatically provisioned.


# Plugins

Extend pREST with OS shared libraries (`.so`) for custom middleware and endpoints. Plugins load beside the built-in REST and MCP surfaces — they do not replace them.

[Here](https://github.com/prest/prest/discussions/466#discussion-30623) is a discussion of how we arrived at this architecture.

Write a shared library and configure pREST to load it at server start. pREST uses the [Go plugin system](https://pkg.go.dev/plugin); on platforms where Go plugins are unavailable (notably Windows), plugin endpoints will not load.

Set the library directory with `PREST_PLUGINPATH` (default `./lib`) or TOML:

```toml
pluginpath = "./lib"
```

### Extension-supported modules

* [Endpoint](/plugins/endpoint-plugin)
* [Middleware](/plugins/middleware-plugin)

### Process of building

The first plugin builder targets **Go** source under `./lib`. The runtime can still load `.so` libraries produced by other toolchains; the automatic Go constructor is the documented path today.

## Related

* [Configuring pREST](/get-started/configuring-prest)
* [API Reference](/api-reference)
* [MCP over HTTP](/get-started/mcp-over-http)
* [Acronyms](/readme/acronyms) · [REST](/readme/acronyms#rest) · [MCP](/readme/acronyms#mcp)


# Middleware Plugin

Bring your own middlewares to pREST

With prestd's middleware plugin system it is possible to create new middlewares to process before reaching the http handler (endpoint).

### Naming patterns

The prestd configuration file receives two parameters:

* **File name:** name of the `.so` file to be loaded into the library path (`file` in prestd config file), the root directory of middleware is the direct libraries (by default is `./lib`) **+** `/middlewares` folder
* **Function name:** function name which will be loaded (`{func}MiddlewareLoad`, is actually the prefix of the function name)

#### Filename

The file name will be used in the middleware to identify which library will be loaded **when the server (prestd) loads**.

> After the **server loads, the library is not loaded again**, it is just executed, i.e. if the file (`.so`) is changed after the server loads, the changes are not applied.

#### Function name

When talking about a compiled *library* we have no way of identifying its functions. Given this characteristic we have defined some name and behavior patterns to develop *libraries* for ***prestd***.

**function name:** `{Function Name}MiddlewareLoad`

* `{Function Name}`: The name of the function that will be called
* `MiddlewareLoad`: The suffix of the function name - the function returns `negroni.Handler` (an interface; use `negroni.HandlerFunc` as adapter)

> `fmt.Sprintf("%sMiddlewareLoad", funcName)`

### Example

* **Source code name:** `./lib/src/middlewares/hello.go`
* **Library file name:** `./lib/middlewares/hello.so`
* **Function name:** `HelloMiddlewareLoad`

```go
// nolint
// all plugins must have their package name as `main`
// each plugin is isolated at compile time
package main

import (
	"net/http"

	"github.com/urfave/negroni/v3"
)

// BUILD:
// go build -mod=vendor -trimpath -ldflags "-s -w" \
//   -o ./lib/middlewares/hello.so \
//   -buildmode=plugin ./lib/src/middlewares/hello.go
func HelloMiddlewareLoad() negroni.Handler {
	return negroni.HandlerFunc(func(rw http.ResponseWriter, rq *http.Request, next http.HandlerFunc) {
		rw.Header().Add("X-Hello-Middleware", "Hello Middleware")
		next(rw, rq)
	})
}
```


# Endpoint Plugin

Bring your own custom endpoint to pREST

With prestd's http plugin system it is possible to create new endpoints in the "private" URI,

The plugin endpoint has the following default: `/_PLUGIN/{file}/{func}`

### Naming patterns

The plugin endpoint (`/_PLUGIN/{file}/{func}`) receives two parameters:

* **File name:** The name of the file without the extension (`{file}`)
* **Function name:** The name of the function (`{func}`)

#### File name

The file name will be used on the endpoint to identify which library will be loaded when it **receives the first access**.

> After the **first access the library will not be loaded again**, it will only be executed, i.e., if the file (`.so`) is changed after the first execution it will have no effect because it has already been loaded.

#### Function name

When talking about a compiled *library* we have no way of identifying its functions. Given this characteristic we have defined some name and behavior patterns to develop *libraries* for ***prestd***.

**function name:** `{HTTP Method}{Function Name}Handler`

* `{HTTP Method}`: The HTTP method that the function will be called for (in upper case letters)
* `{Function Name}`: The name of the function that will be called
* `Handler`: The suffix of the function name - it is always `Handler`

> `fmt.Sprintf("%s%sHandler", r.Method, funcName)`

### Example

* **Source code name:** `./lib/src/hello.go`
* **Library file name:** `./lib/hello.so`
* **Function name:** `GETHelloHandler`
* **Endpoint:** `/_PLUGIN/hello/Hello`
* **Verb HTTP:** `GET`

```go
// all plugins must have their package name as `main`
// each plugin is isolated at compile time
package main

import (
 "encoding/json"
)

var (
 // HTTPVars route variables for the current request
 HTTPVars map[string]string
 // URLQuery parses RawQuery and returns the corresponding values
 URLQuery map[string][]string
)

// Response return structure of the get method
type Response struct {
 HTTPVars map[string]string   `json:"http_vars"`
 URLQuery map[string][]string `json:"url_query"`
 MSG      string              `json:"msg"`
}

// GETHelloHandler plugin
// function is invoked via [go language plugin](https://pkg.go.dev/plugin),
// it is not possible to pass parameters, that's why there are global
// variables to receive data from http protocol
//
// BUILD:
// go build -o lib/hello.so -buildmode=plugin lib/src/hello.go
func GETHelloHandler() (ret string) {
 resp := Response{
  HTTPVars: HTTPVars,
  URLQuery: URLQuery,
  MSG:      "Hello plugin caller!",
 }
 respJSON, err := json.Marshal(resp)
 if err != nil {
  return
 }
 ret = string(respJSON)
 return
}

// GETHelloHandler plugin
// same function as GETHelloHandler, but this time we can return status code.
func GETHelloWithStatusHandler() (ret string, code int) {
	resp := Response{
		HTTPVars: HTTPVars,
		URLQuery: URLQuery,
		MSG:      "Hello plugin caller!",
	}
	respJSON, err := json.Marshal(resp)
	if err != nil {
		return
	}
	ret = string(respJSON)
	code = http.StatusAccepted
	return
}
```

**Request:**

```http
GET /_PLUGIN/hello/Hello?abc=123 HTTP/1.1
```

**Response:**

```json
{
  "http_vars": {
    "file": "hello",
    "func": "Hello"
  },
  "url_query": {
    "abc": [
      "123"
    ]
  },
  "msg": "Hello plugin caller!"
}
```


# Integrations

Deep how-tos for Postgres-compatible engines. For support labels and the multi-database roadmap, start at Databases.

Engine-specific **how-tos** (Compose files, multi-node patterns, connection quirks).

For **support labels**, the canonical matrix, and roadmap status, start here:

**→** [**Databases**](/databases) · [**Database roadmap**](/databases/roadmap)

***

## Guides in this section

| Guide                                            | Databases hub                                              |
| ------------------------------------------------ | ---------------------------------------------------------- |
| [Amazon Redshift](/integrations/amazon-redshift) | [databases/amazon-redshift.md](/databases/amazon-redshift) |
| [TimescaleDB](/integrations/timescaledb)         | [databases/timescaledb.md](/databases/timescaledb)         |
| [YugabyteDB](/integrations/yugabytedb)           | [databases/yugabytedb.md](/databases/yugabytedb)           |

Also documented under Databases (no separate integration guide yet):

* [CockroachDB](/databases/cockroachdb)
* [Aurora PostgreSQL](/databases/aurora-postgresql)
* [PostgreSQL](/databases/postgresql)

***

## Related

* [Configuring pREST](/get-started/configuring-prest)
* [MCP over HTTP](/get-started/mcp-over-http)


# Amazon Redshift

Analyze all of your data with the fastest and most widely used cloud data warehouse.

> Amazon Redshift is based on PostgreSQL. Amazon Redshift and PostgreSQL have a number of very important differences that you must be aware of as you design and develop your data warehouse applications. [read more](https://docs.aws.amazon.com/redshift/latest/dg/c_redshift-and-postgres-sql.html)

Amazon Redshift is compatible with postgresql just use the ODBC connection in the environment variable `DATABASE_URL` with the parameter `OpenSourceSubProtocolOverride` to make pREST connect to the database.

```sh
export PREST_VERSION=2
DATABASE_URL=postgresql://localhost:5432/postgres?OpenSourceSubProtocolOverride=true
```

> **v2 notes:** set `PREST_VERSION=2` for v2 deployments. The deprecated `PREST_SSL_*` variables were removed in v2 — use `PREST_PG_SSL_*` instead. When JWT enforcement is enabled, configure JWT (`PREST_JWT_KEY`, `PREST_JWT_JWKS`, or `PREST_JWT_WELLKNOWNURL`) explicitly — or expect JWT to be auto-disabled with a warning in v2+. The v2.0.0-rc6 tagged binary refuses to start without verification material. See [Upgrading to v2](/get-started/upgrading-to-v2).


# TimescaleDB

[TimescaleDB](https://www.timescale.com/) is a category-defining relational database for **time-series data**. Packaged as a PostgreSQL extension, TimescaleDB is designed to be easy to use, easy to get started, and easy to maintain.

[Download this **sample dataset** by Timescale](https://docs.timescale.com/latest/tutorials/other-sample-datasets#in-depth-devices)

### Docker Compose

```bash
mkdir /tmp/prest+timescaledb
cd /tmp/prest+timescaledb
cat <<YML > docker-compose.yml
---
version: "3"
services:
  timescaledb:
    image: timescale/timescaledb:latest-pg18
    volumes:
      - "./data:/var/lib/postgresql/data"
      - "/tmp:/var/tmp"
    environment:
      - POSTGRES_USER=prest
      - POSTGRES_DB=prest
      - POSTGRES_PASSWORD=prest
    ports:
      - "5432:5432"
  prest:
    image: prest/prest:v2.4.2
    links:
      - "timescaledb:timescaledb"
    environment:
      - PREST_VERSION=2
      - PREST_DEBUG=true  # remove comment for enable DEBUG mode (disable JWT)
      - PREST_PG_HOST=timescaledb
      - PREST_PG_USER=prest
      - PREST_PG_PASS=prest
      - PREST_PG_DATABASE=prest
      - PREST_PG_PORT=5432
      - PREST_JWT_DEFAULT=false  # remove if need jwt
      - PREST_PG_SSL_MODE=disable
    depends_on:
      - timescaledb
    ports:
      - "3000:3000"
YML
```

> **v2 notes:** examples use `prest/prest:v2.4.2` with `PREST_VERSION=2`. When JWT enforcement is enabled (`PREST_JWT_DEFAULT=true` or equivalent), configure `PREST_JWT_KEY` (or `PREST_JWT_JWKS` / `PREST_JWT_WELLKNOWNURL`) explicitly — or expect JWT to be auto-disabled with a warning in v2+. The v2.0.0-rc6 tagged binary refuses to start without verification material. See [Deploying with Docker](/deployment/deploying-with-docker) and [Configuring pREST](/get-started/configuring-prest#jwt).

{% hint style="info" %}
**E2E in v2.2.0+:** prest runs Timescale-specific integration suites (`make test-integration-timescaledb`) against this stack ([#988](https://github.com/prest/prest/pull/988)). Database status: [TimescaleDB](/databases/timescaledb).

**v2.3.0 (**[**#999**](https://github.com/prest/prest/pull/999)**):** auto-detect Timescale adapter, `_time_bucket`, and multi-alias setups — see [Multi-database](/get-started/multi-database) and [examples/multi-database-config.toml](https://github.com/prest/docs/tree/main/get-started/examples/multi-database-config.toml).
{% endhint %}

### Starting up the containers

```bash
docker-compose pull
docker-compose up -d
```

### Creating database structure

```bash
curl https://timescaledata.blob.core.windows.net/datasets/devices_small.tar.gz -o /tmp/devices_small.tar.gz
tar -C /tmp -xzvf /tmp/devices_small.tar.gz
docker-compose exec -T timescaledb psql -U prest -f /var/tmp/devices.sql
```

### Loading data

```bash
docker-compose exec -T timescaledb psql -U prest <<SQL
COPY device_info FROM '/var/tmp/devices_small_device_info.csv' WITH (FORMAT CSV);
COPY readings FROM '/var/tmp/devices_small_readings.csv' WITH (FORMAT CSV);
SQL
```

### Simple Query

**SQL execution:**

```bash
docker-compose exec -T timescaledb psql -U prest <<SQL
SELECT
    time, device_id, battery_temperature
FROM
    readings
WHERE
    battery_status = 'charging'
ORDER BY
    time DESC
LIMIT
    10;
SQL
```

**prestd execution:**

```bash
curl -G http://localhost:3000/prest/public/readings \
  -d battery_status='$eq.charging' \
  -d _select=time,device_id,battery_temperature \
  -d _order=-time \
  -d _page=1 \
  -d _page_size=10
```

### Joining tables

**SQL execution:**

```bash
docker-compose exec -T timescaledb psql -U prest <<SQL
SELECT
    time, readings.device_id, cpu_avg_1min,
    battery_level, battery_status, device_info.model
FROM
    readings
    JOIN device_info ON readings.device_id = device_info.device_id
WHERE
    battery_level < 33
    AND battery_status = 'discharging'
ORDER BY
    cpu_avg_1min DESC, time DESC
LIMIT
    5;
SQL
```

**prestd execution:**

```bash
curl -G http://localhost:3000/prest/public/readings \
  -d battery_level='$lt.33' \
  -d battery_status='$eq.discharging' \
  -d _select='time,readings.device_id,cpu_avg_1min,battery_level,battery_status,device_info.model' \
  -d _join='inner:device_info:readings.device_id:$eq:device_info.device_id' \
  -d _order='-cpu_avg_1min,-time' \
  -d _page=1 \
  -d _page_size=5
```

### Using VIEWs

**Creating VIEW named `battery_level_by_hour`:**

```bash
docker-compose exec -T timescaledb psql -U prest <<SQL
CREATE VIEW battery_level_by_hour AS
SELECT
    time_bucket('1 hour', time) AS "hour",
    model,
    min(battery_level) AS min_battery_level,
    max(battery_level) AS max_battery_level
FROM
    readings
    JOIN device_info ON readings.device_id = device_info.device_id
GROUP BY
    "hour", model;
SQL
```

**Aggregating data over `battery_level_by_hour` view:**

```bash
docker-compose exec -T timescaledb psql -U prest <<SQL
SELECT
    hour, min(min_battery_level), max(max_battery_level)
FROM
    battery_level_by_hour
WHERE
    model IN ('pinto', 'focus')
GROUP BY
    hour
ORDER BY
    hour ASC
LIMIT
    12;
SQL
```

**prestd execution:**

```bash
curl -G http://localhost:3000/prest/public/battery_level_by_hour \
  -d model='$in.pinto,focus' \
  -d _select='hour,min:min_battery_level,max:max_battery_level' \
  -d _groupby='hour' \
  -d _order='hour'
```

**Simple SELECT over `battery_level_by_hour` view:**

```bash
docker-compose exec -T timescaledb psql -U prest <<SQL
SELECT
    hour, min_battery_level, max_battery_level
FROM
    battery_level_by_hour
WHERE
    model = 'mustang'
LIMIT
    5;
SQL
```

\*\*prestd execution over `battery_level_by_hour` view:

```bash
curl -G http://localhost:3000/prest/public/battery_level_by_hour \
  -d model='$eq.mustang' \
  -d _select='hour,min_battery_level,max_battery_level' \
  -d _order='hour' \
  -d _page='1' \
  -d _page_size='5'
```

### Batch Insert data

**Using default INSERT statement WITH returning inserted data:**

```bash
curl http://localhost:3000/batch/prest/public/readings \
  -H "Content-Type: application/json" \
  -d @- << JSON
  [
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000000", "battery_level": 43, "battery_status": "discharging", "battery_temperature": 89.8, "bssid": "01:02:03:04:05:06", "cpu_avg_1min": 28.84, "cpu_avg_5min": 16.9047812612903, "cpu_avg_15min": 10.8993036332756, "mem_free": 420023054, "mem_used": 579976946, "rssi": -40, "ssid": "demo-net"},
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000001", "battery_level": 27, "battery_status": "discharging", "battery_temperature": 89.3, "bssid": "A0:B1:C5:D2:E0:F3", "cpu_avg_1min": 4.89, "cpu_avg_5min": 6.63334573320236, "cpu_avg_15min": 9.25968056754939, "mem_free": 717784757, "mem_used": 282215243, "rssi": -41, "ssid": "stealth-net"},
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000002", "battery_level": 29, "battery_status": "discharging", "battery_temperature": 93.7, "bssid": "A0:B1:C5:D2:E0:F3", "cpu_avg_1min": 8.29, "cpu_avg_5min": 6.78591150918263, "cpu_avg_15min": 7.37546420066158, "mem_free": 634081377, "mem_used": 365918623, "rssi": -54, "ssid": "stealth-net"},
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000003", "battery_level": 14, "battery_status": "discharging", "battery_temperature": 93.1, "bssid": "01:02:03:04:05:06", "cpu_avg_1min": 8.83, "cpu_avg_5min": 8.18492270691781, "cpu_avg_15min": 11.3986054360923, "mem_free": 563352328, "mem_used": 436647672, "rssi": -30, "ssid": "demo-net"},
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000004", "battery_level": 58, "battery_status": "discharging", "battery_temperature": 93.2, "bssid": "22:32:A2:B3:05:98", "cpu_avg_1min": 8.79, "cpu_avg_5min": 10.3900175308572, "cpu_avg_15min": 13.5103326842724, "mem_free": 642162250, "mem_used": 357837750, "rssi": -62, "ssid": "demo-5ghz"}
  ]
JSON
```

**Using COPY statement WITHOUT returning inserted data:**

```bash
curl http://localhost:3000/batch/prest/public/readings \
  -H "Content-Type: application/json" \
  -H "Prest-Batch-Method: copy" \
  -d @- << JSON
  [
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000000", "battery_level": 43, "battery_status": "discharging", "battery_temperature": 89.8, "bssid": "01:02:03:04:05:06", "cpu_avg_1min": 28.84, "cpu_avg_5min": 16.9047812612903, "cpu_avg_15min": 10.8993036332756, "mem_free": 420023054, "mem_used": 579976946, "rssi": -40, "ssid": "demo-net"},
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000001", "battery_level": 27, "battery_status": "discharging", "battery_temperature": 89.3, "bssid": "A0:B1:C5:D2:E0:F3", "cpu_avg_1min": 4.89, "cpu_avg_5min": 6.63334573320236, "cpu_avg_15min": 9.25968056754939, "mem_free": 717784757, "mem_used": 282215243, "rssi": -41, "ssid": "stealth-net"},
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000002", "battery_level": 29, "battery_status": "discharging", "battery_temperature": 93.7, "bssid": "A0:B1:C5:D2:E0:F3", "cpu_avg_1min": 8.29, "cpu_avg_5min": 6.78591150918263, "cpu_avg_15min": 7.37546420066158, "mem_free": 634081377, "mem_used": 365918623, "rssi": -54, "ssid": "stealth-net"},
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000003", "battery_level": 14, "battery_status": "discharging", "battery_temperature": 93.1, "bssid": "01:02:03:04:05:06", "cpu_avg_1min": 8.83, "cpu_avg_5min": 8.18492270691781, "cpu_avg_15min": 11.3986054360923, "mem_free": 563352328, "mem_used": 436647672, "rssi": -30, "ssid": "demo-net"},
    {"time": "2020-10-17T20:19:30+00:00", "device_id": "demo000004", "battery_level": 58, "battery_status": "discharging", "battery_temperature": 93.2, "bssid": "22:32:A2:B3:05:98", "cpu_avg_1min": 8.79, "cpu_avg_5min": 10.3900175308572, "cpu_avg_15min": 13.5103326842724, "mem_free": 642162250, "mem_used": 357837750, "rssi": -62, "ssid": "demo-5ghz"}
  ]
JSON
```


# YugabyteDB

[YugabyteDB](https://www.yugabyte.com/) is a PostgreSQL-compatible Open-Source Distributed SQL database. It adds horizontal scalability to applications built for PostgreSQL. We can use *p***REST** works by connecting to any YugabyteDB node.

Start a YugabyteDB cluster with one of the Quick Start methods: [YugabyteDB Documentation / Quick Start](https://docs.yugabyte.com/preview/quick-start/)

Start prestd with the PostgreSQL connection string to one YugabyteDB node ( default port is 5433 )

#### Node locality

In a public or private cloud, there are multiple ways to scale out the pREST servers with YugabyteDB nodes:

* Start `prestd` with `PREST_PG_URL` set to a cluster service (HA proxy, Kubernetes ClusterIP...) over the YugabyteDB nodes
* Start one `prestd` for each YugabyteDB node, with its local IP
* change the `github.com/jackc/pgx` driver to the cluster-aware one `github.com/yugabyte/pgx/v4`, as in <https://docs.yugabyte.com/preview/drivers-orms/go/yb-pgx/>, so that it discovers all nodes of the cluster from any node.

With geo-distribution, it is recommended to co-locate the `prestd` server in the same zone as the database node(s) it connects to. This will lower the latency and increase availability.

#### Testing

Start a YugabyteDB cluster with one of the Quick Start methods: [YugabyteDB Documentation / Quick Start](https://docs.yugabyte.com/preview/quick-start/)

Start `prestd` with the PostgreSQL connection string to one YugabyteDB node ( default port is 5433 )

#### Example

Starting a single-node YugabyteDB cluster on Docker:

```sh
docker network create yb-net

docker run -d --hostname yb-tserver-n1 -p 7000:7000 \
  --network yb-net yugabytedb/yugabyte:2.14.6.0-b30 \
  yugabyted start --daemon false --listen yb-tserver-n1
```

Starting `prestd` connecting to this node:

```sh
docker run -d -p 3001:3000 --network yb-net \
  -e PREST_VERSION=2 \
  -e PREST_PG_URL=postgres://yugabyte:yugabyte@yb-tserver-n1:5433/yugabyte \
  -e PREST_DEBUG=true \
  prest/prest:v2.4.2
```

> **v2 notes:** set `PREST_VERSION=2` for v2 configuration naming. When JWT enforcement is enabled, configure `PREST_JWT_KEY` (or `PREST_JWT_JWKS` / `PREST_JWT_WELLKNOWNURL`) explicitly — or expect JWT to be auto-disabled with a warning in v2+. The v2.0.0-rc6 tagged binary refuses to start without verification material. See [Deploying with Docker](/deployment/deploying-with-docker).

Creating a view (`yb_servers()` is a table function showing all YugabyteDB nodes in the cluster)

```sh
docker run --rm --network yb-net yugabytedb/yugabyte \
  ysqlsh -h yb-tserver-n1 -c "
   create view yb_servers as select host,zone,region,cloud from yb_servers()
"
```

Querying this view through the REST API:

```json
curl -i -X GET "http://127.0.0.1:3001/yugabyte/public/yb_servers" -H "Content-Type: application/json"

[{"host": "yb-tserver-n1", "zone": "rack1", "cloud": "cloud1", "region": "datacenter1"}]
```

Starting two more YugabyteDB nodes (`yb-tserver-n2` and `yb-tserver-n3`) to join the previous one (`yb-tserver-n1`):

```sh
docker run -d --hostname yb-tserver-n2 \
  --network yb-net yugabytedb/yugabyte:2.14.6.0-b30 \
  yugabyted start --join yb-tserver-n1 --daemon false --listen yb-tserver-n2
  
docker run -d --hostname yb-tserver-n3 \
  --network yb-net yugabytedb/yugabyte:2.14.6.0-b30 \
  yugabyted start --join yb-tserver-n1 --daemon false --listen yb-tserver-n3
```

Starting one `prestd` for each YugabyteDB node:

```sh
docker run -d -p 3002:3000 --network yb-net \
  -e PREST_VERSION=2 \
  -e PREST_PG_URL=postgres://yugabyte:yugabyte@yb-tserver-n2:5433/yugabyte \
  -e PREST_DEBUG=true \
  prest/prest:v2.4.2
  
docker run -d -p 3003:3000 --network yb-net \
  -e PREST_VERSION=2 \
  -e PREST_PG_URL=postgres://yugabyte:yugabyte@yb-tserver-n3:5433/yugabyte \
  -e PREST_DEBUG=true \
  prest/prest:v2.4.2
```

Querying any endpoint to read from the view

```json
curl -i -X GET "http://127.0.0.1:3001/yugabyte/public/yb_servers" -H "Content-Type: application/json"

[{"host": "yb-tserver-n3", "zone": "rack1", "cloud": "cloud1", "region": "datacenter1"}, {"host": "yb-tserver-n2", "zone": "rack1", "cloud": "cloud1", "region": "datacenter1"}, {"host": "yb-tserver-n1", "zone": "rack1", "cloud": "cloud1", "region": "datacenter1"}]

curl -i -X GET "http://127.0.0.1:3003/yugabyte/public/yb_servers" -H "Content-Type: application/json"

[{"host": "yb-tserver-n3", "zone": "rack1", "cloud": "cloud1", "region": "datacenter1"}, {"host": "yb-tserver-n2", "zone": "rack1", "cloud": "cloud1", "region": "datacenter1"}, {"host": "yb-tserver-n1", "zone": "rack1", "cloud": "cloud1", "region": "datacenter1"}]
```

All works as with PostgreSQL, with the additional High Availability and Elasticity provided by YugabyteDB 🚀


