bezalel / docs

Developer docs

Connect any MCP-speaking agent to the Bezalel capability plane. Two values are all a consumer needs: the MCP URL and a bearer token.

Endpoint and auth

MCP endpoint (Streamable HTTP)
https://bezalel.sh/api/mcp
Auth header
Authorization: Bearer <TOKEN>

Tokens look like bzl_… and are minted per agent by the plane’s owner in the dashboard. /api/* on this site proxies same-origin to the plane server; the setup instructions always carry the plane’s current direct URL as well.

The one-paste setup

Paste one prompt into your agent and it registers the plane in its own harness. The Agents page shows it pre-filled with your token:

prompt
Connect yourself to my Bezalel capability plane: fetch https://bezalel.sh/api/setup and follow the instructions. Your token is <TOKEN>.

The setup instructions are a markdown document, kept current by the plane itself, with exact registration commands for every harness — plus skill installation and proactive-memory hooks.

Register by harness

Claude Code

shell
claude mcp add --scope user --transport http bezalel https://bezalel.sh/api/mcp \
  --header "Authorization: Bearer <TOKEN>"

Codex CLI

Append to the file
~/.codex/config.toml
[mcp_servers.bezalel]
url = "https://bezalel.sh/api/mcp"
http_headers = { "Authorization" = "Bearer <TOKEN>" }

Cursor

Merge into the project file, or ~/.cursor/mcp.json for every project
.cursor/mcp.json
{
  "mcpServers": {
    "bezalel": {
      "url": "https://bezalel.sh/api/mcp",
      "headers": { "Authorization": "Bearer <TOKEN>" }
    }
  }
}

OpenClaw

shell
openclaw mcp add bezalel --transport streamable-http \
  --url https://bezalel.sh/api/mcp --header "Authorization: Bearer <TOKEN>"

eve

Set BEZALEL_TOKEN in the app environment
agent/connections/bezalel.ts
import { defineMcpClientConnection } from "eve/connections";

export default defineMcpClientConnection({
  url: "https://bezalel.sh/api/mcp",
  description: "Bezalel capability plane: the owner's connected apps, memory, email, texting, money, computer, and sandboxes.",
  auth: { getToken: async () => ({ token: process.env.BEZALEL_TOKEN! }) },
});

Anything else that speaks MCP: Streamable HTTP endpoint https://bezalel.sh/api/mcp with the Authorization header above.

Verify a connection

Reconnect first — MCP clients cache tool lists per session. Then call the health__check tool: it reports the agent identity and the scopes the token grants. tools/list shows only the tools those scopes can call, and every call is re-checked server-side.

Scopes

Tokens carry capability-domain scopes. A denied call returns a descriptive error naming the missing scope.

health

Connectivity check; reports the agent identity and granted scopes.

memory

Long-term memory shared across every agent: search, add, profile, forget.

email

Real inboxes the plane owns: send, reply, read messages and threads.

imessage

Texting on the owner's paired line: messages, attachments, polls, reactions.

chat

The owner's own bots on Telegram and other chat platforms: wake on their messages, answer in the thread.

finance

The spend ledger plus linked bank accounts and transaction sync.

cards

Saved payment cards in Vault, with agent shopping, spending limits, and owner approval. Connect your Agentcard account in the dashboard first.

computer

The cloud desktop: shell, screenshots, vision-driven tasks.

sandbox

Disposable code-execution microVMs: exec, files, lifecycle.

connectors

Hundreds of third-party apps behind managed OAuth: discover and execute.

apps

Small apps agents build for the owner, with their own data and a screen on the dashboard: list, publish, preview, run.

Plan limits and billing errors

When a call needs more than the owner's plan allows, or billing can't be checked, it fails with one of these code values:

plan_limit

An allowance is used up, compute isn't in the plan, or account capacity is full.

payment_required

The account has no active plan, or a payment is past due.

billing_unavailable

Bezalel couldn't reach its billing service. This isn't a plan limit.

billing_pending

Billing is busy, or this operation already has a billing reservation.

session_ended

A compute session reached its deadline. Start a new session to continue. When transient is true, the work may have run, so check its result before retrying.

Most of these failures also carry a billing object. Every billing object has kind, reason, retryable, and retryAfterSeconds, and a plan_limit also has the other fields below when the plane knows them:

  • kind, reason: The kind is plan_limit, payment_required, or provider_unavailable. The reason is allowance_used_up, not_in_plan, capacity_reached, held_by_running_requests, no_plan, past_due, plan_inactive, billing_unavailable, or billing_busy.
  • retryable, retryAfterSeconds: Whether trying again can work, and how many seconds to wait first.
  • allowance, feature, name, unit, plan: Which allowance it was, such as app_actions, and the plan: free, personal, pro, grace, or null.
  • requested, used, limit, remaining: The request's size, then the usage, limit, and what is left of the plan's allowance this period. Top-up units count in topUpBalance instead.
  • resetsAt, graceEndsAt: When the allowance resets and when grace ends, as ISO 8601 times in UTC.
  • topUpBalance, topUpAvailable, topUpPack: Prepaid top-up units left, or null when that's unknown or the allowance has no packs. Then whether the owner can buy a top-up, and the pack's units and priceUsd.
  • autoTopUp: enabled is false when automatic top-ups don't cover this allowance. status is purchased when the plane just bought a pack but running requests used it, in_progress while another request buys one, waiting when the next try has to wait, owner_action_needed when the owner has to act on the billing page, or off.
  • billingUrl, upgradeUrl, topUpUrl: Absolute links to the owner's billing page, such as https://bezalel.sh/dashboard/billing. Pro has no upgradeUrl, and topUpUrl appears only when the owner can buy a top-up.

A payment_required adds only plan and billingUrl, plus upgradeUrl when no plan is active. It never has allowance, used, limit, resetsAt, or topUpBalance. When billing is unavailable or busy, the kind is provider_unavailable and the object adds nothing, so it has no links.

Retry only when retryable is true, and wait retryAfterSeconds first. Otherwise, pass the message and links to the owner. The same call fails until the owner acts or the allowance resets. A wait of more than five minutes, such as automatic top-ups paused for an hour, isn't retryable either, and its message says until when.

Over MCP, a failed tool call returns isError: true, and its text content is one JSON object with the message in error, the code, billing when there is one, and transient when the work may have run.

Private connections

Let an agent read selected facts from an internal order service through a private gateway. The guide covers setup requirements, agent access, approvals, order tests, and troubleshooting. Worker and gateway provisioning require your operator and the Bezalel team.

Read the private connections guide

Skill playbooks

Per-domain usage playbooks (Agent Skills, SKILL.md convention) are served by the plane:

One skill name per line
GET https://bezalel.sh/api/skills
That skill's SKILL.md
GET https://bezalel.sh/api/skills/<name>

Service status

Live status JSON
GET https://bezalel.sh/api

Returns the plane version, uptime, and which capability domains are configured on this deployment — see it live.

Self-hosting

The plane is a single long-lived Node process, deployable from the repository Dockerfile to any container host. Your deployment uses your own database and provider credentials, and keeps each account’s data separate by owner id, as the hosted service does.

For agents

The overview and link index live at llms.txt. Every page on this site serves markdown from the same URL via Accept: text/markdown, or by appending .md to the path (this page: /docs.md).