Skip to main content
Glama
README.md
# PocketBase MCP Server

[![npm version](https://img.shields.io/npm/v/pocketbase-mcp-bridge.svg)](https://www.npmjs.com/package/pocketbase-mcp-bridge)
[![CI](https://github.com/nestebe/pocketbase-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/nestebe/pocketbase-mcp/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
[![Node](https://img.shields.io/node/v/pocketbase-mcp-bridge.svg)](https://nodejs.org)

A **complete** [Model Context Protocol](https://modelcontextprotocol.io) server for
[PocketBase](https://pocketbase.io). It exposes the full PocketBase management surface —
collections, records, authentication, files, logs, settings, backups and crons — as MCP
tools that an AI assistant (Claude Desktop, Claude Code, Cursor, etc.) can call directly.

Built and validated against **PocketBase v0.39.6** using the official
[`pocketbase` JS SDK](https://github.com/pocketbase/js-sdk).

---

## Features

**55 tools** across every part of the PocketBase API:

| Area | Tools |
| --- | --- |
| **Health & connection** | `health_check`, `auth_info` |
| **Collections (schema)** | `list_collections`, `get_collection`, `get_collection_scaffolds`, `create_collection`, `update_collection`, `delete_collection`, `import_collections`, `truncate_collection` |
| **Records (data)** | `list_records`, `get_full_record_list`, `get_record`, `get_first_record`, `create_record`, `update_record`, `delete_record`, `batch` |
| **Authentication** | `auth_with_password`, `list_auth_methods`, `impersonate`, `request_verification`, `confirm_verification`, `request_password_reset`, `confirm_password_reset`, `request_otp`, `auth_with_otp`, `confirm_email_change`, `list_external_auths`, `unlink_external_auth` |
| **Superusers (admins)** | `list_superusers`, `create_superuser`, `update_superuser`, `delete_superuser` |
| **Files** | `get_file_url`, `get_file_token`, `download_file` (+ uploads via `create_record`/`update_record`) |
| **Logs** | `list_logs`, `get_log`, `get_logs_stats` |
| **Settings** | `get_settings`, `update_settings`, `test_s3`, `test_email`, `generate_apple_client_secret` |
| **Backups** | `list_backups`, `create_backup`, `upload_backup`, `delete_backup`, `restore_backup`, `get_backup_download_url`, `download_backup` |
| **Crons** | `list_crons`, `run_cron` |
| **Escape hatch** | `send_raw_request` (call any endpoint, incl. custom hook routes) |

Highlights:

- **Auto-authentication** as a superuser from environment variables, with transparent
  re-auth when the token expires.
- **File uploads/downloads** to/from the local filesystem (multipart handled for you).
- **Transactional batch** operations (create/update/delete/upsert) in one request.
- **Non-destructive user auth** — authenticating as an end user never clobbers the MCP's
  own superuser session.
- **`send_raw_request`** guarantees completeness: anything the dedicated tools don't cover
  (custom routes, new API features) is still reachable.

---

## Configuration

The server reads its connection settings from environment variables:

| Variable | Required | Description |
| --- | --- | --- |
| `POCKETBASE_URL` | ✅ | Base URL, e.g. `http://localhost:8090`. Prefer `127.0.0.1` over `localhost` to avoid IPv6 resolution issues. |
| `POCKETBASE_ADMIN_EMAIL` | ✅* | Superuser email. |
| `POCKETBASE_ADMIN_PASSWORD` | ✅* | Superuser password. |
| `POCKETBASE_AUTH_COLLECTION` | – | Auth collection to log in against (default `_superusers`). |
| `POCKETBASE_TOKEN` | – | Use a pre-issued token instead of email/password. |

\* Required unless `POCKETBASE_TOKEN` is provided.

### MCP client config

Add the server to your MCP client (Claude Desktop `claude_desktop_config.json`,
Claude Code, Cursor, …). The recommended, zero-install form uses `npx`:

```json
{
  "mcpServers": {
    "pocketbase": {
      "command": "npx",
      "args": ["-y", "pocketbase-mcp-bridge"],
      "env": {
        "POCKETBASE_URL": "",
        "POCKETBASE_ADMIN_EMAIL": "",
        "POCKETBASE_ADMIN_PASSWORD": ""
      }
    }
  }
}
```

Fill the three `env` values with your instance URL and superuser credentials
(keep secrets in `env`, never in `args`). The `-y` flag lets clients launch the
server non-interactively.

<details>
<summary>Alternative: run from a local build (no npm install)</summary>

```json
{
  "mcpServers": {
    "pocketbase": {
      "command": "node",
      "args": ["/absolute/path/to/pocketbase-mcp/dist/index.js"],
      "env": {
        "POCKETBASE_URL": "",
        "POCKETBASE_ADMIN_EMAIL": "",
        "POCKETBASE_ADMIN_PASSWORD": ""
      }
    }
  }
}
```
</details>

<details>
<summary>Alternative: run via Docker</summary>

```json
{
  "mcpServers": {
    "pocketbase": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "POCKETBASE_URL",
        "-e", "POCKETBASE_ADMIN_EMAIL",
        "-e", "POCKETBASE_ADMIN_PASSWORD",
        "pocketbase-mcp-bridge"
      ],
      "env": {
        "POCKETBASE_URL": "http://host.docker.internal:8090",
        "POCKETBASE_ADMIN_EMAIL": "",
        "POCKETBASE_ADMIN_PASSWORD": ""
      }
    }
  }
}
```
</details>

---

## Installation

The published package works out of the box with `npx -y pocketbase-mcp-bridge` (see above) —
no manual install needed once it is on npm.

To build from source:

```bash
git clone https://github.com/nestebe/pocketbase-mcp.git
cd pocketbase-mcp
npm install
npm run build      # compiles TypeScript to dist/
```

The entry point is `dist/index.js` (a stdio MCP server with a `#!/usr/bin/env node`
shebang, also exposed as the `pocketbase-mcp-bridge` bin).

### Docker image

```bash
docker build -t pocketbase-mcp-bridge .
docker run -i --rm \
  -e POCKETBASE_URL=http://host.docker.internal:8090 \
  -e POCKETBASE_ADMIN_EMAIL=admin@example.com \
  -e POCKETBASE_ADMIN_PASSWORD=secret \
  pocketbase-mcp-bridge
```

The image is a stdio server — run it with `-i` (interactive) so the MCP client can talk
to it over stdin/stdout.

---

## Local test instance (`docker-test/`)

A ready-to-run PocketBase is provided for development and validation:

```bash
docker compose -f docker-test/docker-compose.yml up -d
```

- Dashboard: <http://localhost:8090/_/>
- REST API: <http://localhost:8090/api/>
- Superuser (auto-created on first boot):
  - email: `admin@yourdomain.com`
  - password: `your-secure-password-here`

Stop / reset:

```bash
docker compose -f docker-test/docker-compose.yml down          # stop
docker compose -f docker-test/docker-compose.yml down -v       # stop + wipe data
```

> Settings encryption is intentionally **disabled** in this local setup. For production,
> enable it with a **32-character** key via `--encryptionEnv` (see the comments in
> `docker-test/docker-compose.yml`).

### Run the smoke tests

With the container running:

```bash
node scripts/smoke-test.mjs             # collections, records, batch, settings, logs, crons...
node scripts/smoke-test-files-auth.mjs  # users auth, file upload/download, impersonation, backups
```

Both spawn the compiled server over stdio and exercise the tools against the live instance.

---

## Usage notes & gotchas

- **PocketBase 0.23+ removed implicit `created`/`updated` fields.** New base collections
  have no timestamp columns unless you add them. To sort by creation date, add
  `autodate` fields when creating the collection:
  ```json
  { "name": "created", "type": "autodate", "onCreate": true },
  { "name": "updated", "type": "autodate", "onCreate": true, "onUpdate": true }
  ```
  Tip: call `get_collection_scaffolds` to get a template that already includes them.
- **Batch API is disabled by default.** The `batch` tool returns
  *"Batch requests are not allowed"* until you enable it:
  `update_settings { "data": { "batch": { "enabled": true } } }`.
- **Admins are now "superusers"** — a special `_superusers` auth collection. The
  `*_superuser` tools are convenience wrappers over record operations on it.
- **Email-dependent flows** (verification, password reset, OTP, test email) require SMTP
  to be configured in settings.
- **Filtering & sorting** use PocketBase's expression syntax, e.g.
  `filter: 'status = "active" && created > "2024-01-01"'`, `sort: '-created,title'`.
  Use `expand` to inline relations (e.g. `expand: 'author,comments_via_post'`).

---

## Development

```
src/
  index.ts          # stdio entry point + eager auth
  server.ts         # builds the McpServer and registers every tool group
  config.ts         # env parsing
  pocketbase.ts     # SDK client singleton, auth, withAuth() retry helper
  util.ts           # tool result / error helpers
  formdata.ts       # multipart body builder for file uploads
  tools/
    health.ts collections.ts records.ts auth.ts superusers.ts
    files.ts logs.ts settings.ts backups.ts crons.ts raw.ts
```

```bash
npm run build     # tsc -> dist/
npm run watch     # tsc --watch
npm run dev       # run from TS via tsx (no build step)
```

---

## Publishing (maintainers)

The package is distributed on **npm** and listed in the official **MCP Registry**.
Both are automated by [`.github/workflows/release.yml`](.github/workflows/release.yml),
triggered when you publish a GitHub Release.

**One-time setup**

- Create an npm **Automation** (or granular, read+write) access token and add it as the
  repository secret `NPM_TOKEN`. (Later you can migrate to tokenless
  [Trusted Publishing / OIDC](https://docs.npmjs.com/trusted-publishers/) and drop the secret.)
- The MCP Registry step uses **GitHub OIDC** (`id-token: write`) — no secret needed. It
  publishes under the `io.github.nestebe/*` namespace, verified by the `mcpName` field in
  `package.json` matching the `name` in [`server.json`](server.json).

**Cut a release**

```bash
npm version patch          # or minor / major — bumps package.json + creates a git tag
# keep server.json "version" in sync with package.json, then commit it
git push --follow-tags
gh release create v1.0.0 --generate-notes   # publishing the release fires the workflow
```

The workflow then: builds → `npm publish --provenance --access public` → publishes the
`server.json` metadata to the registry.

**Manual publish (without CI)**

```bash
npm publish --access public            # npm (must include the mcpName field)
# then, from the repo root:
mcp-publisher login github             # interactive GitHub OAuth (owner of "nestebe")
mcp-publisher publish                  # pushes server.json to registry.modelcontextprotocol.io
```

Before releasing, verify the tarball and package health:

```bash
npm publish --dry-run     # inspect exactly what ships
npx publint               # lint package.json/exports/bin for publish issues
```

## License

MIT © Nicolas ESTEBE

TDQS

A3.7/5.0

Scored across 55 tools

Disambiguation4/5

Most tools pair a clear action with a distinct resource, and even similar prefixes like request_/confirm_ target clearly different auth workflows. The main ambiguity is between list_records, get_full_record_list, and get_first_record, which all retrieve records and differ only in pagination or convenience.

Naming Consistency4/5

The API is overwhelmingly consistent verb_noun snake_case, following list_, get_, create_, update_, delete_, request_, and confirm_ patterns. A few outliers like health_check, batch, auth_info, and impersonate break the pattern, but they are minor deviations in an otherwise coherent naming scheme.

Tool Count1/5

At 55 tools, this is an extremely large MCP surface for an agent to reason over, crossing the threshold where tool selection becomes a burden. Even though PocketBase is broad, this would be better split into focused servers for records, admin/settings, backups/logs, and auth.

Completeness5/5

The tool set covers collections, records, auth, superusers, settings, logs, backups, cron, and file handling with full CRUD and lifecycle coverage. The send_raw_request escape hatch also eliminates dead ends for any PocketBase endpoint not otherwise exposed.

Maintenance

ActivityStale
ResponsivenessNo issues