$ psql "postgresql://freebase@HOST:5432/bikeshare"
psql (16.2)
bikeshare=> SELECT version();
PostgreSQL 16.2 on x86_64-pc-linux-gnu
That transcript is the whole point of this repository.
A freebase.cloud PostgreSQL instance answers on TCP 5432 speaking
the PostgreSQL frontend/backend protocol v3. libpq connects. psql connects. Prisma,
SQLAlchemy, pgx, ActiveRecord, Knex and DBeaver connect — unmodified, no shim, no proxy
driver. The same database is simultaneously exposed to an LLM over MCP at a single HTTPS
URL. Neither view is a simulation of the other; they are two front doors onto one Postgres 16.
This repo holds the connection recipes, a reference schema, and runnable examples.
- The two access paths
- Setup
- Tool surface
- Client configuration
- Worked example: a bike-share dataset
- Backups: pg_dump over the wire and over MCP
- Known limits
- FAQ
- See also
| Native wire protocol | MCP | |
|---|---|---|
| Endpoint | postgresql://freebase@HOST:5432/DBNAME |
https://freebase.cloud/api/mcp/YOUR_TOKEN |
| Protocol | PostgreSQL v3 frontend/backend | JSON-RPC 2.0 over Streamable HTTP |
| Auth | connection string credentials | token embedded in the URL path |
| Consumers | psql, pgAdmin, ORMs, migration tools | Claude, ChatGPT, Cursor, VS Code, agents |
| Transactions | full — BEGIN/SAVEPOINT/COMMIT |
per-call statements |
| Typical use | migrations, app runtime, dumps | exploration, reporting, agent writes |
Use the wire protocol for anything your build pipeline does. Use MCP for anything a model does. Migrations belong in the first column; "summarise last week's trips" belongs in the second. Nothing stops you from using both against the same free instance.
1. Create the instance. Sign up for a free Postgres 16 instance, create a session, pick PostgreSQL. No credit card, no cluster sizing dialog.
2. Mint an MCP token. Settings → MCP → New Token, select the connection, copy the URL. It looks like:
https://freebase.cloud/api/mcp/YOUR_TOKEN
The token lives in the path. There is no Authorization header anywhere in this document —
if a client config asks you for one, leave it blank.
3. Name the connection. MCP tool names are prefixed with the connection name you chose,
not with the engine. Everything below assumes a connection named bikes. If you called yours
app, read bikes_query as app_query.
4. Register with a client. See Client configuration.
Four tools ship with every connection:
| Tool | Purpose | Notes |
|---|---|---|
bikes_query |
SELECT / read SQL, returns rows |
full PostgreSQL 16 grammar |
bikes_store |
INSERT / UPDATE / upsert |
use for agent writes |
bikes_list_tables |
enumerate tables in the database | cheap discovery call |
bikes_annotate_table |
attach a prose description to a table | improves model accuracy a lot |
PostgreSQL connections additionally surface three helpers that do not exist on the other engines:
| Helper | Purpose |
|---|---|
pg_dump |
logical export of the database as SQL |
pg_restore |
load a previously produced dump |
pg_tables |
Postgres-native catalogue listing, with schema/owner detail |
pg_tables and bikes_list_tables overlap. Prefer pg_tables when you care about which
schema an object lives in; prefer bikes_list_tables when you just want names.
Models guess column semantics from names, and they guess badly on abbreviations. One annotation per table removes most of that guesswork:
bikes_annotate_table(
table: "trips",
description: "One row per completed rental. duration_s is seconds, not minutes.
start_station_id/end_station_id join to stations.station_id.
Rows with end_time IS NULL are rides still in progress."
)
Annotations are stored server-side and are visible to the model on subsequent calls. Do this once per table after your first migration; it is the highest-leverage minute you will spend.
Claude Code
claude mcp add --transport http bikes https://freebase.cloud/api/mcp/YOUR_TOKENAdd --scope project to write a committable .mcp.json; --scope user registers it for
every project on the machine. The generated file:
{ "mcpServers": { "bikes": { "type": "http", "url": "https://freebase.cloud/api/mcp/YOUR_TOKEN" } } }A url entry with no type is a hard error in Claude Code. streamable-http is accepted as
an alias for http.
Claude Desktop / Claude web / Cowork — UI only. Settings (⌘, / Ctrl+,) →
Connectors (Customize → Connectors in newer builds) → Add custom connector →
paste the URL → Add — the Claude connection walkthrough
has the same steps with screenshots. Enable it per conversation with the + button in the composer.
Available on Free, Pro, Max, Team and Enterprise; the Free tier allows one custom connector.
Note that claude_desktop_config.json has no support for remote HTTP servers — the UI is the
path, or bridge through mcp-remote.
Cursor — ~/.cursor/mcp.json or .cursor/mcp.json:
{ "mcpServers": { "bikes": { "url": "https://freebase.cloud/api/mcp/YOUR_TOKEN" } } }No type key here; the presence of url is what marks the server as remote.
VS Code / GitHub Copilot Chat — .vscode/mcp.json, top-level key is servers, and the
token can be prompted for rather than committed. See examples/vscode-mcp.json.
Gemini CLI
gemini mcp add --transport http bikes https://freebase.cloud/api/mcp/YOUR_TOKENIf you hand-edit settings.json instead, the key for Streamable HTTP is httpUrl. A
plain url key means SSE there, and SSE is the deprecated transport — this trips people up
regularly.
ChatGPT — Settings → Apps → Advanced settings → developer mode → Apps →
Create → paste the endpoint → Auth None → Scan Tools → Create. Developer mode is
documented for Pro, Plus, Business, Enterprise and Edu. Full write access is still rolling
out to Business, Enterprise and Edu workspaces, so bikes_store may be unavailable on your
plan even though bikes_query works.
Stdio-only clients — bridge with mcp-remote. Its own README describes it as
experimental; treat it as a fallback, not the default.
examples/bikeshare_schema.sql creates three tables —
stations, bicycles, trips — with a GiST-free, ordinary B-tree index layout, a JSONB
column for station metadata, and a materialised daily rollup. Apply it over the wire:
psql "$FREEBASE_DSN" -f examples/bikeshare_schema.sqlThen ask a model, through MCP:
Which five stations had the worst net dock imbalance last Saturday?
The model reaches for bikes_query and writes something close to:
SELECT s.name,
count(*) FILTER (WHERE t.end_station_id = s.station_id) AS arrivals,
count(*) FILTER (WHERE t.start_station_id = s.station_id) AS departures,
count(*) FILTER (WHERE t.end_station_id = s.station_id)
- count(*) FILTER (WHERE t.start_station_id = s.station_id) AS net
FROM stations s
JOIN trips t ON s.station_id IN (t.start_station_id, t.end_station_id)
WHERE t.start_time >= date_trunc('week', now()) - INTERVAL '2 days'
GROUP BY s.name
ORDER BY abs(net) DESC
LIMIT 5;FILTER, date_trunc, count(*) OVER () and every other Postgres 16 construct are available
because this is real Postgres 16. Nothing in
the MCP layer rewrites your SQL.
examples/trips_report.mjs does the same thing as a plain
Node.js MCP client — useful when you want to see the raw JSON-RPC frames rather than a chat
UI's rendering of them.
Two routes, same output format:
# Route A — the standard tool, over TCP 5432
pg_dump "$FREEBASE_DSN" --no-owner --no-privileges -f backup.sql
# Route B — the MCP helper, no local Postgres install needed
# tool: pg_dump → returns SQL textexamples/dump_restore.sh wraps route A with a schema-only
option and a restore path, because "can I get my data out" should be answerable in one
command. Route B needs nothing installed locally beyond an MCP token. You
own the data either way.
- The free tier targets development, prototyping and small production workloads. It is not positioned as a substitute for a provisioned production cluster, and no SLA, uptime figure, backup retention policy or storage quota is claimed here. Check the dashboard for current quota detail rather than trusting a number in a README.
- MCP calls are not a transaction boundary. Each
bikes_storecall stands alone. If you needBEGIN … COMMITsemantics across several statements, do it over the wire protocol, or express it as a single statement with CTEs. - Token in the URL means URL = credential. Do not commit it, do not paste it into an issue,
and rotate it in Settings → MCP if it leaks. The examples here all read it from
FREEBASE_MCP_URL. - Free-tier Claude allows one custom connector. If you already have one registered, you will need to swap it.
- Extensions: common ones —
uuid-ossp,pgcrypto,hstore,citext— are enabled withCREATE EXTENSIONin the usual way. Anything requiring a compiled module you supply yourself is a different conversation; check before you design around it.
Is the MCP layer just wrapping a REST API over a hosted Postgres?
It is an MCP server in front of a real Postgres 16.2
backend. The proof is that you can attach
psql to the same database and see the rows the model wrote.
Do I need both paths?
No. Plenty of people use only the connection string and never mint a token, and plenty of
people never open psql. They coexist.
Which Postgres version, exactly?
16.2. Version-gated syntax — MERGE, SQL/JSON constructors, random_normal() — behaves as
the 16 documentation says.
Can Prisma or SQLAlchemy talk to it?
Yes; they see a standard connection string. Migration tooling (prisma migrate, alembic)
works because DDL is unrestricted at the SQL level.
Does EXPLAIN ANALYZE work through MCP?
Yes — it is just a statement, and bikes_query returns the plan rows as text. Handy for
letting a model diagnose its own slow query.
Can the model drop my tables? It can run what its tool grants allow. Approve writes deliberately, and keep a dump if the data matters. Most clients let you require confirmation per tool call.
- PostgreSQL instance page
- Connecting Claude to PostgreSQL
- PostgreSQL 16 manual
- MCP specification, revision 2026-07-28
- Streamable HTTP transport
freebase.cloud is an independent service and is not affiliated with the PostgreSQL Global Development Group, Anthropic, OpenAI, Google, Microsoft or Cursor.