← Back to Blog

How to Use pREST's MCP Server — Query PostgreSQL from Cursor, Claude, or curl

pREST v2.1.0 exposes a read-only MCP server at /_mcp on the same HTTP port as the REST API. Agents and scripts send JSON-RPC requests to discover databases, list tables, and run permission-aware SELECT queries — inheriting pREST’s auth and ACL without a separate MCP process. This tutorial covers Docker setup, curl walkthrough, and client wiring for Cursor, Claude Desktop, and generic HTTP tools. Requires v2.1.0+. See the v2.0.0 / v2.1.0 release overview for context.

pREST v2 series: Architecture (rc6)v2.0.0 / v2.1.0 GAThis post (MCP tutorial)

What is pREST MCP?

Model Context Protocol (MCP) is the open standard AI agents use to discover and call tools. pREST’s /_mcp endpoint implements MCP as read-only HTTP JSON-RPC on the existing REST server — same port, same auth middleware, same database routing. Agents call tools/list to discover tables and tools/call to run schema-aware SELECT queries. This is safer than handing agents a raw Postgres connection string: pREST enforces access.tables ACL, caps row counts, and logs requests through the HTTP layer.

When to use pREST MCP vs direct database access

Scenario Use pREST MCP Use direct DB access
Agent exploration in Cursor or Claude Yes — ACL-scoped, read-only Risky — full SQL, no HTTP audit trail
Production writes or migrations No — MCP is read-only in v2.1.0 Yes
Multi-tenant routing via aliases Yes — inherits registry routing Requires per-tenant connection management
Ad-hoc SQL outside pREST permissions No Yes — if you accept the security tradeoff
Audit trail via HTTP auth (JWT/basic) Yes Depends on DB-level logging

Table of Contents

Prerequisites

  • pREST v2.1.0+docker pull prest/prest:v2.1.0 or build from prest/prest
  • PostgreSQL with at least one table pREST can expose
  • prest.toml with access.tables permissions granting read on tables you want agents to query
  • Optional: JWT or basic auth enabled (this tutorial shows unauthenticated local dev first, then auth)

Quick start with Docker

The fastest path is the repo’s integration stack. For a minimal local setup:

# Postgres
docker run -d --name prest-pg \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=prest-test \
  -p 5432:5432 postgres:16

# Create a test table
docker exec prest-pg psql -U postgres -d prest-test -c \
  "CREATE TABLE IF NOT EXISTS public.test (id serial PRIMARY KEY, name text); \
   INSERT INTO public.test (name) VALUES ('prest tester') ON CONFLICT DO NOTHING;"

# pREST v2.1.0 (adjust config mount as needed)
docker run -d --name prestd \
  --link prest-pg:postgres \
  -p 3000:3000 \
  -e PREST_PG_HOST=postgres \
  -e PREST_PG_USER=postgres \
  -e PREST_PG_PASS=postgres \
  -e PREST_PG_DATABASE=prest-test \
  prest/prest:v2.1.0

Ensure your prest.toml grants read access:

[[access.tables]]
name = "test"
schema = "public"
permissions = ["read"]

For a full multi-service setup, see docker-compose-test.yml in the pREST repo.

Verify the server is up:

curl -s http://localhost:3000/_health
curl -s http://localhost:3000/_mcp | head -c 200

Understand the endpoint

pREST serves MCP on the same server as catalog, CRUD, and script routes:

Method Path Purpose
GET /_mcp Discovery payload — server metadata and available tools
POST /_mcp JSON-RPC 2.0 requests

Supported RPC methods:

  • initialize — handshake
  • tools/list — enumerate tools (including per-table selects)
  • tools/call — execute a tool

Protocol version: 0.1. Row cap: 100 rows per select call.

Available tools

Tool Purpose
prest.list_databases List databases or registered aliases
prest.list_schemas List schemas in a database
prest.list_tables List tables in a schema
prest.describe_table Column metadata and typed input schema
prest.select_table Generic table read
prest.select.{database}.{schema}.{table} Per-table tool with column-aware filters

Schema-aware table tools expose typed arguments: columns, filters, order_by (field or -field for descending), limit, and offset. In multi-database mode, tool generation expands across registered aliases.

Walkthrough with curl

1. Discovery

curl -s http://localhost:3000/_mcp | jq .

Expected output: prest server name, protocol version, and a tools array.

2. Initialize

curl -s http://localhost:3000/_mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}' | jq .

Expected output: JSON-RPC result with server info and capabilities.

3. List tools

curl -s http://localhost:3000/_mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | jq '.result.tools[].name'

Expected output: prest.list_databases, prest.describe_table, and generated prest.select.* tools for permitted tables.

4. Describe a table

curl -s http://localhost:3000/_mcp \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "prest.describe_table",
      "arguments": {
        "database": "prest-test",
        "schema": "public",
        "table": "test"
      }
    }
  }' | jq .

Expected output: column names and types for the test table.

5. Select with projection, filter, and ordering

curl -s http://localhost:3000/_mcp \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "tools/call",
    "params": {
      "name": "prest.select.prest-test.public.test",
      "arguments": {
        "columns": ["id", "name"],
        "filters": {"name": "prest tester"},
        "order_by": ["id"],
        "limit": 5,
        "offset": 0
      }
    }
  }' | jq .

Expected output: matching rows as JSON in the tool result.

Auth-enabled setup

When auth.enabled = true in config, /_mcp requires the same credentials as other protected routes.

Basic auth:

curl -s -u myuser:mypass http://localhost:3000/_mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'

JWT:

curl -s http://localhost:3000/_mcp \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'

For local development, auth can remain disabled. Note that misconfigured JWT settings disable auth with warning logs rather than blocking startup (PR #974) — do not rely on this in production.

How to connect Cursor MCP to PostgreSQL via pREST

Cursor supports URL-based MCP servers. Add to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "prest": {
      "url": "http://localhost:3000/_mcp"
    }
  }
}

When auth is enabled, add headers:

{
  "mcpServers": {
    "prest": {
      "url": "http://localhost:3000/_mcp",
      "headers": {
        "Authorization": "Bearer YOUR_JWT_TOKEN"
      }
    }
  }
}

Steps:

  1. Save mcp.json
  2. Restart Cursor
  3. Open the MCP tools panel — prest.list_tables, prest.describe_table, and per-table select tools should appear
  4. Try a prompt from the example agent prompts section below

How to connect Claude Desktop to PostgreSQL via pREST

Claude Desktop natively expects stdio MCP servers. pREST serves MCP over HTTP, so use an HTTP-to-stdio bridge such as mcp-remote:

{
  "mcpServers": {
    "prest": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:3000/_mcp"]
    }
  }
}

Add auth via bridge flags if your version supports headers (check mcp-remote docs for the current CLI). pREST does not ship a stdio wrapper — the bridge pattern is the supported approach for Claude Desktop.

Example agent prompts

After connecting MCP in Cursor or Claude, try these copy-paste prompts:

  1. List tables: “Use the prest MCP tools to list all tables in the public schema of prest-test.”
  2. Describe columns: “Use prest.describe_table to show columns and types for public.test.”
  3. Filter rows: “Use prest.select.prest-test.public.test to find rows where name equals ‘prest tester’. Return id and name only.”
  4. Compare schemas: “Use prest MCP to list tables in public and any other schemas you can see, and compare row counts where possible.”

Generic HTTP clients

Any client that can POST JSON-RPC works. Minimal Python example:

import requests

resp = requests.post(
    "http://localhost:3000/_mcp",
    json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
)
for tool in resp.json()["result"]["tools"]:
    print(tool["name"])

Other options:

  • Postman — POST with JSON body, same payloads as the curl examples above
  • Custom agents — loop tools/listtools/call in your orchestration code

The curl walkthrough in this post is the reference implementation for all HTTP clients.

Safety and limits

pREST MCP is read-only by design in v2.1.0:

  • No insert, update, or delete tools
  • Unsupported tool names return 400 Bad Request
  • Existing identifier validation and ACL apply — agents only see tables your access.tables rules permit
  • Maximum 100 rows per select call
  • Multi-database mode generates tools per registered alias; permissions are evaluated per alias and schema

Auth and ACL stay in the HTTP middleware stack — the MCP layer does not reimplement security.

Troubleshooting

Symptom Fix
401 on /_mcp Add basic auth (-u) or Authorization: Bearer header; or disable auth for local dev
Empty or missing tools Check access.tables grants read on the tables you expect
400 on tool call Verify database, schema, and table match your alias and physical names
Tool not found Run tools/list and copy the exact generated name (e.g. prest.select.prest-test.public.test)
Connection refused Confirm pREST is listening on port 3000 and Postgres is reachable

Check pREST logs for permission denials and config warnings.

FAQ

What is pREST’s MCP server?

pREST’s MCP server is a read-only HTTP endpoint at /_mcp introduced in v2.1.0. It exposes JSON-RPC methods for catalog discovery and schema-aware table reads, reusing pREST’s existing authentication, database routing, and access control.

How do I query PostgreSQL from Cursor using MCP?

Add a URL-based MCP server entry in ~/.cursor/mcp.json pointing to http://your-host:3000/_mcp. Include an Authorization header if auth is enabled. Restart Cursor and verify tools appear in the MCP panel.

Is pREST MCP read-only?

Yes. v2.1.0 MCP tools support discovery and SELECT operations only. There are no insert, update, or delete tools. Unsupported operations return 400 Bad Request.

Does pREST MCP work with Claude Desktop?

Claude Desktop expects stdio MCP servers. Use an HTTP bridge such as mcp-remote with npx mcp-remote http://localhost:3000/_mcp in your Claude Desktop MCP config. Verify bridge flags for auth headers against the package’s current documentation.

What is the difference between pREST MCP and PostgREST?

PostgREST exposes PostgreSQL over REST but does not ship an MCP endpoint. pREST v2.1.0 adds read-only MCP tools on the same server as its REST API, with ACL-scoped table selects for agents. Both are Postgres REST layers; pREST targets Go teams needing registry routing and agent integration.

Why use pREST MCP instead of giving agents a database connection string?

A connection string grants full SQL access with no HTTP-level ACL, no row caps, and no integration with pREST’s auth middleware. MCP through pREST limits agents to read-only, permission-scoped tools with a 100-row cap per query.

Also from Insurgency Labs

Engineering leaders using Cursor or Claude for database exploration via pREST MCP may also want read-only delivery metrics from GitHub — DeliveryCompass surfaces PR cycle time and review patterns for EMs without write access to your repos. See the DeliveryCompass overview. Free during Early Access; not a Jira replacement and does not measure deploy-based DORA.