Boring Docs
Control Your App From Claude (MCP)
One file turns any BoringPush deployment into a Model Context Protocol server. Claude Code, Claude Desktop, or any MCP host can then read boards, posts and billing — and create posts, comment and vote through a scoped token, with no new infrastructure.
In this guide
Control Your App From Claude (MCP)
What this is. mcp/server.mjs is a zero-dependency Model Context Protocol server for your BoringPush deployment. It is a thin adapter: every tool call rides the same API and the same scoped Bearer token the bp CLI already uses. Nothing new gets deployed — the file runs on the machine where your MCP host (Claude Code, Claude Desktop, anything that speaks MCP) lives, and talks to your deployment over HTTP.
If you can run
bp whoamiagainst your deployment, you can run this.
How it works
- Your MCP host spawns
mcp/server.mjsas a child process and speaks newline-delimited JSON-RPC over its stdio. - The server answers with a fixed catalog of 15 tools (see below, defined once in
mcp/tools.mjs) — there is no "call any endpoint" tool, by design. - Auth and target come from the same place the CLI keeps them:
- Token:
BP_TOKENenv, else~/.config/boringpush/credentials.json(written bybp login, mode 0600). - Target:
BP_API_URLenv, else the saved default remote, elsehttp://localhost:3000.
- Token:
- The token is never printed, logged, or echoed back into tool results.
One-time login
bp login --remote https://boringpush.com --save-remote --name Claude --scopes read,writeApprove in your browser, and every following MCP call is authenticated. Prefer a read-only first date? Use --scopes read — the write tools will answer with a clear scope hint instead of doing anything.
Wire it into Claude Code
claude mcp add boringpush -- node /full/path/to/this/repo/mcp/server.mjsWire it into Claude Desktop (or any JSON-config host)
{
"mcpServers": {
"boringpush": {
"command": "node",
"args": ["/full/path/to/this/repo/mcp/server.mjs"]
}
}
}No env block needed when you used --save-remote and bp login above. For CI or multiple targets, set BP_API_URL and BP_TOKEN instead.
Remote setup (no local install)
The same 15 tools are served over HTTP at <SITE>/api/mcp (Streamable HTTP, JSON responses). The connection is made through OAuth, so there is no token to paste.
- claude.ai: Settings, Connectors, add a custom connector, and enter
<SITE>/api/mcp. Sign in to BoringPush when asked and approve. - Claude Code:
claude mcp add --transport http boringpush <SITE>/api/mcp - MCP Inspector:
npx @modelcontextprotocol/inspector, choose Streamable HTTP, enter<SITE>/api/mcp, then use the OAuth flow.
Replace <SITE> with your deployment URL (NEXTAUTH_URL).
Read-only by default. The consent page grants read access (boards, posts, orders, account). A client that asks for write access gets an extra unchecked box, "Also allow changes". Without it the connection only lists and runs the read tools; calling a write tool returns a read-only error.
How it is built. /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server describe the endpoints. Clients register themselves at /api/oauth/register, the user approves at /oauth/authorize, and /api/oauth/token exchanges the code (PKCE S256 required) for a normal bp_live_ API token named MCP: <client>. Client ids and codes are signed with AUTH_SECRET, so no new table exists. Tool calls run through the same API routes as REST, so ownership, scope and plan checks are identical.
Limits. Access tokens last 30 days and there are no refresh tokens in v1: reconnect after 30 days. /api/mcp is limited to 120 requests a minute per IP.
Revoking a connection. It is an ordinary API token: bp tokens list, then bp tokens revoke --id <id> --yes.
Add a tool for a new feature
Add one entry to mcp/tools.mjs (name, title, description, inputSchema, scope, route, toRequest, optional validate). Both transports pick it up. If the route is new, also add it to the route map in libs/mcp-remote.js. Do not put rules in the entry: the API route decides who may do what.
What Claude can do
| Tool | Kind | What it does |
|---|---|---|
whoami |
read | Identity, plan, access |
billing_status |
read | Plan, completed orders, total spent |
revenue_summary / revenue_by_day |
read | Revenue, orders, active users, visitors and bounce rate for the last N days, as totals or per day (app admin only; everyone else gets a clear refusal) |
analytics_breakdown |
read | Top countries, channels, referring sites, pages, events such as sign-ups, devices or browsers (app admin only) |
list_boards / get_board |
read | Your boards |
list_posts / get_post |
read | Board posts and details |
list_comments |
read | Post comments¹ |
create_board |
write | New board (needs write scope + active plan) |
create_post |
write | New post, authorship stamped from the token |
add_comment |
write | Comment on a post |
vote_post |
write | Toggle your vote |
set_post_status |
write | Move a post open → planned → in-progress → done (board owner only) |
¹ On deployments from before 2026-10-17 the underlying GET /api/comment route may not exist yet; the tool explains this instead of failing. It starts working with the next deploy.
Safety rails
- Scopes are enforced server-side. A
--scopes readtoken cannot write, no matter what the model asks. - No admin actions. The three admin tools,
revenue_summary,revenue_by_dayandanalytics_breakdown, only read, and the route refuses anyone who isn't the app's admin. Grant/revoke, user management, and webhooks are not tools — the catalog is exactly the 15 above. - Tokens are revocable:
bp tokens list/bp tokens revoke --id <id> --yes. - Plain http is refused outside loopback, same rule as the CLI —
https://orlocalhostonly.
Troubleshooting
- "Not logged in" → run the one-time login above. The server reads the token fresh on every call, so no restart is needed after logging in.
Error 403 … scope→ your token was minted with--scopes read. Re-login withread,writeor keep it read-only on purpose.- Tool works locally but not against boringpush.com → check
bp whoami --remote https://boringpush.comfirst; the MCP server and the CLI share the exact same transport rules.