MCP server
Make Base316 operable by an agent.
A Model Context Protocol server for Base316. It lets an agent read your catalog, ask whether a customer may use a feature, and record that they did.
Everything goes through the public REST API with your own secret key, so the server has no privileged access of its own — it can do exactly what that key can do and nothing more.
Configure it
Claude Code, in a project where npx @base316/cli init has run:
claude mcp add base316 -- npx -y @base316/mcpThe server starts in the project directory and reads BASE316_SECRET_KEY and
BASE316_API_URL from its .env.local or .env, so no key goes into the
client's configuration. See Build with an agent.
Anywhere else, pass the key explicitly:
claude mcp add base316 --env BASE316_SECRET_KEY=b316_sk_sandbox_... -- npx -y @base316/mcpClaude Desktop (claude_desktop_config.json) and Cursor (~/.cursor/mcp.json,
or .cursor/mcp.json in a project) take the same block:
{
"mcpServers": {
"base316": {
"command": "npx",
"args": ["-y", "@base316/mcp"],
"env": { "BASE316_SECRET_KEY": "b316_sk_sandbox_..." }
}
}
}| Variable | Required | Meaning |
|---|---|---|
BASE316_SECRET_KEY | yes | A secret key (b316_sk_…), set here or in the project's .env.local / .env. A publishable key is refused: it cannot read customers or invoices. |
BASE316_API_URL | no | Defaults to https://api.base316.com. Point it at http://localhost:3110 to work against a local stack. |
BASE316_MCP_ALLOW_WRITES | no | Set to exactly true to allow writes on a live key. |
Writes are gated on the key
Reads are always available. Anything that changes something — record_event,
create_customer, create_subscription, and any write reached through
call_operation — can put charges on a real customer's invoice, so on a live
key it is refused unless BASE316_MCP_ALLOW_WRITES=true is set deliberately.
Sandbox keys write freely, which is what a sandbox is for.
A disabled tool says so in its own description, so a model can plan around it rather than discovering the refusal by calling it.
Tools
Reads
| Tool | What it answers |
|---|---|
whoami | Which project and environment this key belongs to, and whether it is sandbox or live |
list_products | The products this environment sells |
list_plans | Plans and versions, with prices and the features each grants |
list_features | The features gated on, and the meter behind each metered one |
list_customers / get_customer | Accounts, with currency, terms and collection method |
list_subscriptions | Plan, status and current billing period |
list_invoices | Invoices, including what is still owed rather than only the total |
preview_upcoming_invoice | What a subscription will be charged next, and when — computed the way the billing run will, with nothing stored |
preview_quantity_change | What changing a subscription's quantity would charge now and each period after, and when it applies — nothing is changed |
preview_plan_change | What moving a subscription to another plan would do now and at the period end: entitlements, proration and the next renewal |
check_entitlement | The core one. May this customer use this feature, and how much allowance is left — counting plan limits and prepaid credits |
get_customer_entitlements | Every feature for one customer, allowed or denied and why, with the plan version, credit grants and paying account behind each |
Writes
| Tool | What it does |
|---|---|
record_event | Records one usage event. Requires an idempotencyKey, so a retry after a timeout records it once |
create_customer | Opens a billable account |
create_subscription | Starts billing |
Everything else
The tools above are the hot path. Behind them, three more reach the rest of the API — every endpoint this deployment serves, not a curated subset.
| Tool | What it does |
|---|---|
list_operations | Search everything the API can do. Filter by search, tag or writesOnly |
describe_operation | One operation's parameters and JSON body schema, with its real constraints |
call_operation | Call it, by operationId, with pathParams, query and body |
They are driven by the OpenAPI document, fetched from whichever deployment your key points at — so the reachable surface is the one actually running, not whatever was true when this package was published.
Why not one tool per endpoint? That would be about two hundred tools. A model choosing between two hundred tools chooses worse than one choosing between fifteen, and the list is context you pay for on every turn. Discovery costs one call and keeps the choice small.
call_operation obeys the same gate as everything else, decided per call from
the operation you name: reads go through on a live key, writes do not. An
operationId it cannot identify is treated as a write, because guessing
"harmless" for something unrecognised is the wrong way round.
Working against a local stack
BASE316_API_URL=http://localhost:3110 \
BASE316_SECRET_KEY=b316_sk_sandbox_... \
pnpm --filter @base316/mcp startDiagnostics go to stderr; stdout carries the protocol.

