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) 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 you connect with that adapter. Other SQL families are on the roadmap.
What is MCP?
MCP (Model Context Protocol) is an open standard 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. 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:
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 (prest-mcp) β a tiny stdio β HTTP bridge. The adapter does not implement tools; pREST still owns schema discovery and queries. See AI and MCP for install and client tutorials.
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
GET
/_mcp
Discovery payload with server metadata and available tools
POST
/_mcp
JSON-RPC 2.0 requests for MCP operations
Supported JSON-RPC methods:
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):
Use discovery to inspect available tools and their typed inputSchema before calling them.
Tools reference
Since v2.4.1 (#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.
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:
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:
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:
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:
database
No
Database alias; defaults to pg.database when omitted
schema
Yes
Schema name
table
Yes
Table name
Returns:
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:
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:
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:
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_byvalues (fieldand-fieldfor each column)column-aware
filterswith types matching each column's data typelimit(integer, 1β100) andoffset(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.
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.
Examples
Initialize
Response includes serverInfo, capabilities, and instructions:
List tools
Describe a table
Schema-aware select
List databases
Authentication and permissions
When 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 apply to MCP the same way they apply to REST:
Tool discovery only lists tables and columns the caller can read.
tools/callon 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
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
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 for clients that cannot call HTTP
Calling a write tool (for example prest.drop_table) returns:
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, 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:
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. Configuration: Configuring pREST β Expose Data.
Troubleshooting
401 / 403 on /_mcp
Auth enabled but credentials missing or invalid β see 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]
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
invalid identifier in path
Schema or table name failed identifier validation
For integration test examples, see integration/controllers/mcp_test.go in the prest repository.
Client tutorials
AI and MCP β landing for agents and IDEs
Install pREST MCP Adapter β Homebrew, Go, Docker, MCP Registry
Start with Homebrew β
prestdandprest/tap/prest-mcp
Related documentation
Last updated