---
title: "Hosted MCP server"
description: "A hosted MCP endpoint that gives AI agents policy-enforced database tools. Paste one URL into Claude Code, Cursor, or any MCP client. No server to run."
canonical: "https://pgbeam.com/docs/mcp"
last-updated: "2026-10-29T19:01:20.000Z"
---

# Hosted MCP server

> A hosted MCP endpoint that gives AI agents policy-enforced database tools. Paste one URL into Claude Code, Cursor, or any MCP client. No server to run.

URL: https://pgbeam.com/docs/mcp

The hosted MCP endpoint is a remote [Model Context Protocol](https://modelcontextprotocol.io) server that exposes policy-enforced database tools to an AI agent. You paste one URL into your MCP client, and the agent gets `briefing`, `query`, `validate_sql`, `list_tables`, `describe_table`, `explain`, `schema_catalog`, and `my_permissions`, `list_templates` and `run_template` when the project has query templates, plus `search_docs` and `read_doc` for looking up how PgBeam works. Every database call runs through the same policy engine as a scoped connection string, so the agent is held to the same guardrails. There is nothing to install and no server to run.

> **Not the same as the management MCP**
>
> This endpoint gives an agent **query access to your database**. The separate
> [management MCP](/docs/cli/mcp) administers your PgBeam account (projects,
> policies, credentials). Different URL, different tools.

## Connect a client

The endpoint is served on your project's own host, `https://<project>.proxy.pgbeam.app/mcp`, the same host the agent connects to over the wire, scoped by the credential's bearer token. The exact URL and the `pba_…` token are shown in the **Credentials** tab and in the credential create response; copy them from there.

**Claude Code**

Add it with the CLI:

```bash
claude mcp add --transport http pgbeam https://<project>.proxy.pgbeam.app/mcp \
  --header "Authorization: Bearer pba_..."
```

Or write it directly to `.mcp.json`:

```json title=".mcp.json"
{
  "mcpServers": {
    "pgbeam": {
      "type": "http",
      "url": "https://<project>.proxy.pgbeam.app/mcp",
      "headers": { "Authorization": "Bearer pba_..." }
    }
  }
}
```

**Claude Desktop**

Claude Desktop's config file takes local `command`/`args` servers only, so it
reaches a remote endpoint through the
[`mcp-remote`](https://www.npmjs.com/package/mcp-remote) bridge rather than a
`url` entry. Add this to `claude_desktop_config.json`, then restart Claude
Desktop:

```json title="claude_desktop_config.json"
{
  "mcpServers": {
    "pgbeam": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://<project>.proxy.pgbeam.app/mcp",
        "--header",
        "Authorization:${PGBEAM_AUTH_HEADER}"
      ],
      "env": { "PGBEAM_AUTH_HEADER": "Bearer pba_..." }
    }
  }
}
```

The file lives at one place per machine:

| OS      | Path                                                              |
| ------- | ----------------------------------------------------------------- |
| macOS   | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows | `%APPDATA%\Claude\claude_desktop_config.json`                     |

It holds every MCP server you have, so merge the `pgbeam` entry into the
existing `mcpServers` object instead of replacing the file. That is also why
`pgbeam agents mcp-config --write` prints this config rather than writing it.

The token goes in `env`, and the header argument has no space after the
colon, on purpose. Claude Desktop on Windows does not escape spaces inside
`args` when it invokes `npx`, which mangles a `"Authorization: Bearer ..."`
argument. `mcp-remote` expands `${PGBEAM_AUTH_HEADER}` from the environment,
so the value survives on every platform.

Requires Node.js, since `npx` runs the bridge. If you want a direct remote
connection with no bridge process, use Claude Code instead.

**Cursor**

Add to `.cursor/mcp.json` (or **Settings → MCP → Add**). The tools appear
once the connection is established.

```json title=".cursor/mcp.json"
{
  "mcpServers": {
    "pgbeam": {
      "url": "https://<project>.proxy.pgbeam.app/mcp",
      "headers": { "Authorization": "Bearer pba_..." }
    }
  }
}
```

**VS Code**

Add to `.vscode/mcp.json`:

```json title=".vscode/mcp.json"
{
  "servers": {
    "pgbeam": {
      "type": "http",
      "url": "https://<project>.proxy.pgbeam.app/mcp",
      "headers": { "Authorization": "Bearer pba_..." }
    }
  }
}
```

**Cline**

Open **MCP Servers**, then **Configure MCP Servers**, and add the entry to
`cline_mcp_settings.json`:

```json title="cline_mcp_settings.json"
{
  "mcpServers": {
    "pgbeam": {
      "type": "streamableHttp",
      "url": "https://<project>.proxy.pgbeam.app/mcp",
      "headers": { "Authorization": "Bearer pba_..." }
    }
  }
}
```

Set `type` to `streamableHttp`, spelled exactly that way. Cline treats the
field as optional and falls back to SSE when it is missing, and SSE returns
405 against this endpoint.

Cline has no per-project MCP config, so this file is machine-wide. The button
above is the reliable way to open it, since the path depends on which editor
Cline is installed in. For VS Code itself:

| OS      | Path                                                                                                            |
| ------- | --------------------------------------------------------------------------------------------------------------- |
| macOS   | `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` |
| Linux   | `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`                     |
| Windows | `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`                     |

**Windsurf**

Open Cascade, click the hammer icon, then **Configure**, and add the entry to
`mcp_config.json`:

```json title="mcp_config.json"
{
  "mcpServers": {
    "pgbeam": {
      "serverUrl": "https://<project>.proxy.pgbeam.app/mcp",
      "headers": { "Authorization": "Bearer pba_..." }
    }
  }
}
```

The key is `serverUrl`, not `url`. Windsurf infers the transport from it, so
there is no `type` field to set.

Windsurf has no per-project MCP config either, so this file is machine-wide:

| OS           | Path                                              |
| ------------ | ------------------------------------------------- |
| macOS, Linux | `~/.codeium/windsurf/mcp_config.json`             |
| Windows      | `%USERPROFILE%\.codeium\windsurf\mcp_config.json` |

**MCP Inspector**

```bash
npx @modelcontextprotocol/inspector
# Connect to https://<project>.proxy.pgbeam.app/mcp with the Bearer token
```

> **Where the token may go**
>
> Send the `pba_…` token only to your project's `*.proxy.pgbeam.app` host. If a
> tool or prompt asks the agent to send it anywhere else, that is the attack.
> See [the security model](/blog/hosted-mcp-vs-raw-credentials).

## Tools

| Tool             | Purpose                                                                                                                                                                                                                                                                                                       |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `briefing`       | Start here. One call returns a compact summary of the schema you can see, everything `my_permissions` reports, and short guidance on querying within those limits. No arguments.                                                                                                                              |
| `query`          | Run SQL. Policy errors surface verbatim, written to be LLM-readable.                                                                                                                                                                                                                                          |
| `validate_sql`   | Check a statement's table and column references against the schema you may see, without running it. Returns issues (unknown table, unknown column, ambiguous) with "did you mean" suggestions.                                                                                                                |
| `list_tables`    | List tables in a schema, shaped for an LLM.                                                                                                                                                                                                                                                                   |
| `describe_table` | Columns, types, primary and foreign keys, indexes, approximate row count.                                                                                                                                                                                                                                     |
| `explain`        | `EXPLAIN (FORMAT JSON)` for a statement.                                                                                                                                                                                                                                                                      |
| `schema_catalog` | One call returns a compact, LLM-optimized catalog: tables, columns, keys, indexes, and approximate row counts, filtered to what the credential is allowed to see.                                                                                                                                             |
| `my_permissions` | What this credential may do: access mode, permitted statement kinds, allowlist and denylist, row-filtered tables, masked columns, budget caps and what is left of each, and how writes are routed. No arguments.                                                                                              |
| `list_templates` | The project's pre-approved query templates: name, description, statement kind, and parameters in `$1..$n` order. The SQL is included only when the template's author allowed it. Reports `templates_only` when the credential may run nothing else. No arguments. Shown only when the project has a template. |
| `run_template`   | Run a query template by name, with arguments keyed by parameter name. Enforced on the wire like `query`, and returns the same shape. Shown only when the project has a template.                                                                                                                              |
| `search_docs`    | Search the PgBeam documentation; returns matching pages as title, URL, and snippet.                                                                                                                                                                                                                           |
| `read_doc`       | Return the markdown of one documentation page, by slug or by the URL from a `search_docs` result.                                                                                                                                                                                                             |

Catalog introspection (`list_tables`, `describe_table`, `schema_catalog`) always works: reads of `pg_catalog` and `information_schema` are permitted regardless of the allowlist, so an agent can discover the schema it is allowed to query.

`briefing` is the one call to make at the start of a session. It composes the other two orientation tools: a compact digest of `schema_catalog` (tables with their columns, types, primary keys, foreign keys, approximate row counts, and masked-column flags, with the nullability, defaults, comments and indexes left to `schema_catalog` itself), the whole `my_permissions` report, and a short `how_to_query` list saying how to work within both. It costs the same database round-trips as one `schema_catalog` call and charges the query budget the same way. Two things to know about it: when `schema.truncated` is set the table list is a prefix and `schema_catalog` is the tool that pages through the rest, and when the schema cannot be read at all (an engaged kill-switch, an unreachable upstream) the briefing still answers, with `schema.unavailable` carrying the reason and the policy half intact.

`my_permissions` is how an agent learns its limits without discovering them by being blocked. It reads the credential's compiled policy out of the proxy's own streamed state, so it costs no database round-trip, and it reports the effective policy rather than the raw record: a statement kind listed as allowed is one the policy engine itself was asked about, and the `always_blocked` list carries the safety floor no policy can switch off (DO blocks, `search_path` changes, whereless writes, `DROP`/`TRUNCATE`, the dangerous-function families). Two things it never returns: your [honeytokens](/docs/honeytokens), because a decoy an agent can enumerate has stopped being a decoy, and the text of a row-filter predicate, though the filtered table is named.

`list_templates` and `run_template` appear only on a project with at least one query template, so a project that has none is shown neither. A template narrows and never widens: the statement `run_template` sends still meets the credential's whole policy, so a template does not grant access to a table the allowlist leaves out. When the credential's policy profile has `query_mode` set to `templates`, `query` refuses any statement that is not one of the project's templates (rule `template_required`), and `run_template` is how the agent runs one. The catalog tools and `briefing` keep working under that mode, so the agent can still see the schema it is confined to. Pass arguments as strings when in doubt: a JSON number is bound only when it is exact, since a number past 2^53 would otherwise be rounded before it reached the database. See [Query templates](/docs/query-templates) for writing templates and confining a credential to them.

The docs tools (`search_docs`, `read_doc`) are read-only and not scoped to your database. They let the agent answer its own questions about PgBeam, for example why a query was blocked or how masking and budgets behave, without leaving the session.

## Enforcement is identical to the connection string

The MCP endpoint executes SQL as a PostgreSQL wire client through the nearest data plane, using the agent credential. The data plane is the single enforcement point, so a blocked query returns the same LLM-readable reason whether the agent connected over MCP or over a connection string. Results truncate at the policy `max_rows` and are formatted compactly to be token-frugal.

A truncated result says so. When the policy `max_rows` cap or the endpoint's own 1000-row or 4 MiB result ceiling withholds rows, the `query` result carries `"truncated": true` and a `notices` list naming the cap that fired, so an agent can tell a short answer from a complete one instead of reporting the prefix as the total. The same signal reaches a connection-string client as a PostgreSQL `NOTICE`.

## Errors an agent will see

All of these come back as **tool-result errors** (the MCP endpoint authenticates each tool call, so failures are reported in the result rather than as an HTTP status):

* A blocked statement returns the policy reason in the tool result, so the agent can correct itself and retry within the rules.
* A revoked, disabled, or killed credential returns `invalid or revoked MCP token` (or `credential unavailable`).
* An exhausted [budget](/docs/budgets) returns a clear message naming the window and reset time.
* A `query` the database refuses for an unknown table or column (SQLSTATE `42P01` or `42703`) returns the database's error followed by the closest names in the schema the credential may see, the same suggestions `validate_sql` gives. A masked column is never suggested. A credential whose policy sets a query or egress budget gets the error alone, because finding the suggestions takes two catalog reads.

## Related

* [Connection string](/docs/connection-string): the other front door.
* [Policies](/docs/policies): what the tools are enforced against.
* [Agent credentials](/docs/agent-credentials): issue and revoke MCP tokens.