Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Connect AI agents to the Butterbase MCP server

Vendor MCP
Open markdown

The Butterbase MCP connector routes your AI agent's tool calls to Butterbase's own MCP server through Scalekit. Each user connects their own Butterbase access token once, and Scalekit sends it with every call, so your agent never handles credentials. It comes with 40 tools.

Tools
40
What they doRead · write · destructive
5 · 28 · 75 read28 write7 destructive
Users sign in with

Setup

  1. Install the SDK

    Terminal window
    npm install @scalekit-sdk/node dotenv
  2. Set your credentials

    Add your Scalekit credentials to your .env file. Find values in app.scalekit.com > Developers > API Credentials.

    .env
    SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
    SCALEKIT_CLIENT_ID=<your-client-id>
    SCALEKIT_CLIENT_SECRET=<your-client-secret>
  3. Create the Butterbase MCP connection

    In AgentKit > Connections, create a Butterbase MCP connection. The name you give it is the connection_name your code passes. See Configure connections.

    Console steps with screenshots

    Register your Butterbase API key with Scalekit so it can authenticate requests to the Butterbase MCP server on behalf of your users. Butterbase MCP uses API key authentication with an account-scoped key.

    1. Generate a Butterbase API key

      • Sign in to the Butterbase dashboard and open API keys in the left sidebar.

      • Click Generate key.

        Butterbase API keys page with the Generate key button

      • Enter a Name for the key, such as Scalekit.

        New API key dialog with the Name field

      • Expand Advanced and set Key scope to Account. The MCP server needs full access across all apps, so don’t pick This app.

      • Under Additional scopes, keep ai:gateway and substrate checked.

        Advanced section with Account key scope and the ai and substrate scopes checked

      • Click Generate.

      Butterbase shows the new key once, and the API keys table lists it with its prefix and scopes

    2. Create a connection in Scalekit

      • In the Scalekit dashboard, go to AgentKit → Connections → Create Connection.
      • Search for Butterbase MCP and click Create.
      • Note the Connection name — use this as connection_name in your code (e.g., butterbasemcp).
    3. Add a connected account

      Connected accounts link a specific user identifier in your system to a Butterbase API key. Add one in the dashboard to test. In production, each user adds their own through the authorization link: they enter their credentials on the page it opens.

      In the dashboard, to test

      • Open the connection and click the Connected Accounts tab → Add account.
      • Fill in Your User’s ID and API Key, then click Save.

      From your backend, if your app already has the credentials

      For example, when users enter them on a settings page in your app:

      import { ConnectorStatus } from '@scalekit-sdk/node'
      const authorizationDetails = {
      details: {
      case: 'staticAuth',
      value: { details: { token: 'bb_sk_...' } },
      },
      };
      let { connectedAccount } = await scalekit.actions.upsertConnectedAccount({
      connectionName: 'butterbasemcp',
      identifier: 'user_123',
      authorizationDetails,
      });
      // Make sure the account is ACTIVE before the first tool call.
      if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
      ({ connectedAccount } = await scalekit.actions.upsertConnectedAccount({
      connectionName: 'butterbasemcp',
      identifier: 'user_123',
      authorizationDetails,
      }));
      }
  4. Authorize a user and make your first call

    quickstart.mts
    import { ScalekitClient } from '@scalekit-sdk/node'
    import 'dotenv/config'
    import { createInterface } from 'node:readline/promises'
    const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
    )
    const actions = scalekit.actions
    const connector = 'butterbasemcp'
    const identifier = 'user_123'
    // Generate an authorization link for the user
    const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
    console.log('Authorize Butterbase MCP:', link)
    const rl = createInterface({ input: process.stdin, output: process.stdout })
    await rl.question('Press Enter after authorizing...')
    rl.close()
    // Make your first call
    const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'butterbasemcp_butterbase_docs',
    toolInput: {},
    })
    console.log(result)
    Terminal window
    npx tsx quickstart.mts

    Each user opens the link once and enters their Butterbase MCP server credentials there. If your app already has a user's credentials, add the account from your backend instead, as the console steps above show. See Authorize a user for the full flow and statuses.

Tools

Pass the exact name to execute_tool
Try in PlaygroundRequest a tool
  • butterbasemcp_butterbase_docsRead comprehensive Butterbase documentation (local, no API calls).Read-only

    Butterbase Docs

    Read comprehensive Butterbase documentation (local, no API calls). Available topics: - all: Complete documentation (default) - overview: Platform introduction and key features - mcp: MCP tool reference and examples - rest: HTTP data API (auto-generated REST endpoints) - auth: End-user authentication (OAuth, JWT) - storage: File upload/download with S3 - functions: Serverless functions (triggers, context) - frontend: Static frontend deployment (upload zip, deploy to live URL) - ai: AI model gateway (chat completions, BYOK, usage) - meetings: Meeting bots that join Zoom/Meet/Teams/Webex calls and return recordings + transcripts - billing: Your Butterbase plan, usage meters, app-level Stripe Connect (subscriptions and one-time payments) - platform: MCP over HTTP, /llms.txt, subdomains, suggestions, rate limits - regions: Choosing a region at app creation, moving apps between regions, discovering the live region list - schema: Schema DSL reference (types, indexes, constraints) - sdk: TypeScript SDK installation, client setup, query builder, auth, storage, functions - cli: CLI installation, commands for apps, schema, functions, storage, config - integrations: Third-party integrations (OAuth connect flow, tool execution, SDK, CLI) - substrate: Per-user memory + action coordination plane for AI agents (entities, decisions, attention rules, action ledger, outbox, ws stream, ctx.substrate inside functions) - office: Organization Ask, conversations, decisions, browser confirmation and native business memory across MCP/CLI/SDK Example: Input: { topic: "auth" } Output: Full authentication documentation with OAuth setup, JWT handling, etc. Don't know the topic slug? Pass a freeform { query: "..." } instead and the tool returns the best-matching section plus an index of related topics: Input: { query: "how do I send email" } Output: Ranked topic index + the full text of the top-matching section. Use this to: - Learn Butterbase features and APIs - Get code examples for common tasks - Reference schema DSL syntax - Understand authentication flow - Learn about app monetization (subscriptions and one-time purchases) Note: This is a local documentation tool. No network requests are made. Idempotency: Safe to call anytime (read-only operation).

    Inputs

    querystring
    Freeform search across all docs. When provided (and no topic is given), returns the best-matching section plus a ranked index of related topics. Use when you do not know the exact topic slug.
    topicstring
    Section to return: all (default), overview, mcp, rest, auth, storage, functions, frontend, ai, meetings, billing, platform, regions, schema, sdk, cli, realtime, rag, integrations, substrate, office.one of alloverviewmcprestauthstoragefunctionsfrontendaimeetingsbillingplatformregionsschemasdkclirealtimeragintegrationssubstrateoffice
  • butterbasemcp_list_regionsList the regions an app can be created or moved to.Read-only

    List Regions

    List the regions an app can be created or moved to. Returns the live set of supported regions. Use this before calling init_app or manage_app (action: "move") to validate a region slug, or to present region choices to a user. Returns: { regions: string[] } e.g. { regions: ["us-east-1", "us-west-2"] } Idempotency: Safe to call anytime (read-only operation).

    Inputs

    This tool takes no inputs.

  • butterbasemcp_query_audit_logsQuery audit events for an app — authentication, admin mutations, and function invocations.Read-only

    Query Audit Logs

    Query audit events for an app — authentication, admin mutations, and function invocations. Returns a unified event stream. Each event has: category 'auth' | 'admin' | 'function' event_type e.g. 'login', 'schema.apply', 'function.deploy', 'function.invoke' action 'create' | 'update' | 'delete' | 'invoke' | 'enable' | 'disable' | null resource_type which resource the event acted on resource_id the resource identifier (function name, policy name, deployment id, etc.) actor_type 'platform_user' | 'app_user' | 'api_key' | 'system' | 'anonymous' actor_id platform user id / app user id / api key id event_data event-specific payload success whether the event succeeded correlation_id request id (ties related events together) Use this to: - Investigate who did what and when - Debug failing auth / admin / function flows - Monitor suspicious activity - Trace a request across subsystems via correlation_id Common filters: - category='admin' to see only administrative mutations - resource_type='function' + resource_id='my-fn' to see one function's history - actor_id=<user-id> to see one actor's activity - from / to to narrow to a time window Use nextCursor to resume from the last row without deduplication; omit offset when cursor is set. Idempotency: Safe to call anytime (read-only). Historical auth events predating migration 034 are included (normalized).

    Inputs

    app_idstringrequired
    The app ID (e.g. app_abc123def456)
    actionstring
    Action filterone of createupdatedeleteinvokeenabledisable
    actor_idstring
    Actor ID filter (platform user, app user, or API key id)
    categorystring
    Event category filterone of authadminfunction
    cursorstring
    Opaque nextCursor from a previous response. Do not combine with offset.
    event_typestring
    Exact event type match (e.g. "login", "schema.apply", "function.invoke")
    fromstring
    ISO-8601 timestamp, inclusive lower bound on created_at
    limitinteger
    Number of logs to return (default 100, max 500)default 100
    offsetinteger
    Pagination offset (default 0). Do not combine with cursor.default 0
    resource_idstring
    Resource identifier filter
    resource_typestring
    Resource type filter (e.g. "function", "rls_policy")
    tostring
    ISO-8601 timestamp, exclusive upper bound on created_at
    user_idstring
    DEPRECATED — alias for actor_id
  • butterbasemcp_rag_queryQuery a RAG collection using natural language to retrieve relevant document chunks.Read-only

    Query RAG Collection

    Query a RAG collection using natural language to retrieve relevant document chunks. Performs semantic search over the collection's indexed documents and returns the most relevant chunks ranked by similarity. Optionally synthesizes an AI-generated answer using the retrieved context. Parameters: - query: Natural language question or search phrase - top_k: Number of chunks to retrieve (default 5, max 20) - threshold: Minimum similarity score 0-1 (only return chunks above this score) - synthesize: If true, uses an LLM to generate a natural language answer from the retrieved chunks (default false — returns raw chunks only) - model: LLM model to use for synthesis (only relevant when synthesize is true, default: anthropic/claude-haiku-4.5) - filter: Metadata filter to narrow results (e.g. { category: "faq" }) Example — raw retrieval: Input: { app_id: "app_abc123", collection: "knowledge-base", query: "How do I reset my password?", top_k: 3 } Output: { chunks: [ { text: "To reset your password, go to Settings > Security > Reset Password...", score: 0.92, document_id: "doc_abc", metadata: { category: "faq", source: "help-center" } }, ... ] } Example — with synthesis: Input: { app_id: "app_abc123", collection: "knowledge-base", query: "How do I reset my password?", top_k: 5, synthesize: true } Output: { answer: "To reset your password, navigate to Settings > Security and click...", chunks: [ ... ], model: "gpt-4o-mini" } Example — with metadata filter: Input: { app_id: "app_abc123", collection: "knowledge-base", query: "pricing plans", filter: { category: "billing", version: "2.0" } } Use this to: - Search documentation or knowledge bases using natural language - Build AI-powered Q&A features for end users - Find relevant context for AI assistants - Power search bars with semantic understanding Common errors: - RESOURCE_NOT_FOUND: App or collection doesn't exist - COLLECTION_EMPTY: No documents have been ingested yet Idempotency: Safe to call anytime (read-only operation).

    Inputs

    app_idstringrequired
    The app ID (e.g. app_abc123def456)
    collectionstringrequired
    The collection name to query
    querystringrequired
    Natural language query or search phrase
    filterobject
    Metadata filter to narrow results (e.g. { category: "faq" })
    modelstring
    LLM model for synthesis (only used when synthesize is true)
    synthesizeboolean
    If true, generate a natural language answer from retrieved chunks using an LLMdefault false
    thresholdnumber
    Minimum similarity score (0-1). Only chunks scoring above this threshold are returned.
    top_knumber
    Number of top matching chunks to return (default 5, max 20)default 5
  • butterbasemcp_select_rowsQuery rows from a table using the auto-generated REST API.Read-only

    Select Rows

    Query rows from a table using the auto-generated REST API. By default, this tool authenticates with the platform API key (butterbase_service role), which bypasses Row-Level Security and returns ALL rows regardless of RLS policies. To test RLS enforcement from this tool, use the as_role and as_user parameters: - as_role: "anon" — simulate an anonymous request (butterbase_anon role) - as_role: "user", as_user: "<user-uuid>" — simulate a specific end-user (butterbase_user role) Without as_role, this tool always runs as butterbase_service (full access). Use this to: - Fetch data from tables (as admin/service — sees all rows) - Filter, sort, and paginate results - Select specific columns Example — Basic query: Input: { app_id: "app_abc123", table: "posts", limit: 10 } Output: [ { id: "uuid-1", title: "Hello World", created_at: "2024-01-15T10:00:00Z" }, ... ] Example — With filters: Input: { app_id: "app_abc123", table: "posts", filters: { "status": "eq.published", "created_at": "gt.2024-01-01" }, order: "created_at.desc", limit: 20 } Filter operators: - eq (equals): status=eq.published - neq (not equals): status=neq.draft - gt (greater than): age=gt.18 - gte (greater than or equal): age=gte.18 - lt (less than): price=lt.100 - lte (less than or equal): price=lte.100 - like (pattern match): title=like.%hello% - ilike (case-insensitive): title=ilike.%hello% - is (null/true/false): deleted_at=is.null - in (list): id=in.(1,2,3) - fts (full-text search): title=fts.hello world Common errors: - VALIDATION_TABLE_NOT_FOUND: Table doesn't exist, use manage_schema (action: "get") to verify - VALIDATION_INVALID_SCHEMA: Invalid filter format Idempotency: Safe to call multiple times (read-only operation).

    Inputs

    app_idstringrequired
    The app ID
    tablestringrequired
    The table name
    as_rolestring
    Simulate a specific role for RLS testing. "anon" for anonymous, "user" for authenticated end-user (requires as_user).one of anonuser
    as_userstring
    User ID (UUID) to simulate when as_role is "user". Required when as_role is "user".
    filtersobject
    Filter conditions (column: "operator.value")
    limitinteger
    Maximum number of rows to return
    offsetinteger
    Number of rows to skip
    orderstring
    Sort order (e.g., "created_at.desc")
    selectstring
    Comma-separated column names to return
  • butterbasemcp_create_frontend_deploymentCreate a frontend deployment and get an upload URL.Write

    Create Frontend Deployment

    Create a frontend deployment and get an upload URL. Upload your built frontend as a zip file to the returned URL, then use manage_frontend (action: "start_deployment") to trigger the deploy. Steps: 1. Call this tool to get an upload URL 2. Upload your zip file to the URL (e.g. curl -X PUT "{uploadUrl}" -H "Content-Type: application/zip" --data-binary @frontend.zip) 3. Call manage_frontend (action: "start_deployment") with the returned deployment_id Example: Input: { app_id: "app_abc123", framework: "react-vite" } Output: { deployment_id: "uuid-1234", uploadUrl: "https://...", expiresIn: 900, maxSizeBytes: 104857600 } Prerequisites: - App must exist (use init_app to create) Free plan: 1 deployment per app. Deploying again automatically replaces the previous deployment (no need to delete first). Starter+: unlimited deployments. Framework options: - react-vite: React app built with Vite (zip the dist/ folder) - nextjs-static: Next.js static export (zip the out/ folder) - static: Plain HTML/CSS/JS - other: Any framework that produces static output SPA routing: For SPA frameworks (react-vite, nextjs-static, other), a _redirects file is auto-injected so all routes serve index.html. If your zip already includes a _redirects file, it is preserved. IMPORTANT — Zip file paths must use forward slashes (/), not backslashes (\). On Windows, zips created with built-in tools use backslashes, which causes all files to be served as text/html (breaking JS/CSS with MIME errors). On Windows use Git Bash or WSL to run: cd dist && zip -r ../frontend.zip . Common errors: - RESOURCE_NOT_FOUND: App doesn't exist Idempotency: Not idempotent — creates a new deployment each time (replaces existing on free plan). Your frontend will be deployed to https://<app-name>.butterbase.dev. Next steps: Upload your zip to the returned URL, then call manage_frontend (action: "start_deployment").

    Inputs

    app_idstringrequired
    The app ID
    frameworkstring
    Frontend framework typeone of react-vitenextjs-staticstaticother
  • butterbasemcp_deploy_functionDeploy or update a serverless function with custom business logic.Write

    Deploy Function

    Deploy or update a serverless function with custom business logic. Example: Input: { app_id: "app_abc123", name: "send-welcome-email", code: "export async function handler(req, ctx) { ... }", trigger: { type: "http", config: { method: "POST", path: "/welcome", auth: "required" } } } Output: { function_id: "fn_xyz789", name: "send-welcome-email", url: "https://api.butterbase.ai/v1/app_abc123/fn/send-welcome-email", status: "deployed" } Function signature: export async function handler(request: Request, context: { db: PostgresClient, // Query your app database env: Record<string, string>, // Access envVars user: { id: string } | null, // Current user (if auth: required) waitUntil: (promise: Promise) => void, // Keep alive for background work after response idempotency: { // Webhook / event dedup primitive claim: (key: string, opts?: { scope?: string; ttlSeconds?: number }) => Promise<boolean> } }): Promise<Response> Console output: console.log(), console.info(), console.warn(), console.error(), and console.debug() calls are captured and stored with invocation logs. View them via manage_function (action: "get_logs"). IMPORTANT: Handlers MUST return a Response object (Web API standard). Do NOT return plain objects like { status: 200, body: "..." }. Idempotent webhook handlers with ctx.idempotency.claim(): Third-party webhook providers (Stripe, Telegram, GitHub, Slack, Twilio, Discord) retry delivery on non-2xx responses with the same event id. Use ctx.idempotency.claim() to atomically dedupe — it returns true if you're the first to see this key, false if another invocation already claimed it. export async function handler(req, ctx) { const event = await req.json(); if (!(await ctx.idempotency.claim(event.id))) { // Already processed — ack the retry without re-doing work. return new Response('duplicate', { status: 200 }); } await processEvent(event); return new Response('ok', { status: 200 }); } Options: - scope: 'stripe' | 'telegram' | ... (default: 'default'). Namespace claims so keys from different providers can never collide. - ttlSeconds: mark the claim with an expiry. Cleanup is your responsibility: DELETE FROM _idempotency_keys WHERE expires_at < now(); Background work with ctx.waitUntil(): Use ctx.waitUntil(promise) to keep the function alive after the response is sent. This is useful for fire-and-forget tasks like sending emails or logging. Background work has a 30-second timeout. ctx.db is available inside waitUntil promises. export async function handler(req, ctx) { ctx.waitUntil(fetch("https://api.email.com/send", { method: "POST", body: "..." })); return new Response(JSON.stringify({ accepted: true }), { headers: { "Content-Type": "application/json" } }); } Example: export async function handler(req, ctx) { const data = { hello: "world" }; return new Response(JSON.stringify(data), { status: 200, headers: { "Content-Type": "application/json" } }); } Server-to-server calls from inside a function: - ctx.invoke('fn-name', body?, opts?) — call a sibling function in the same app. Platform-managed bearer, no env plumbing. ctx.user.id propagates automatically; the callee sees ctx.caller.type === 'loopback'. Loop-depth capped at 4. - ctx.invokeDO('class-name', 'instance-key', body?, opts?) — call a sibling Durable Object in the same app. Routed through a platform shim; no public URL or bearer to plumb. Depth shared with ctx.invoke. See docs → Functions → Server-to-server, and Durable Objects → Server-to-server. Row-Level Security in Functions: Functions respect RLS policies based on how they're invoked: - Invoked with end-user JWT → butterbase_user role (RLS enforced) * ctx.db queries see only the user's data * ctx.user.id contains the authenticated user ID * Use case: User-facing operations - Invoked with platform API key → butterbase_service role (RLS bypassed) * ctx.db queries see all data * ctx.user is null * Use case: Admin operations, background jobs - Invoked by cron trigger → butterbase_service role (RLS bypassed) * ctx.db queries see all data * ctx.user is null * Use case: Scheduled tasks, cleanup jobs Trigger types: - http: Invoke via HTTP request (GET, POST, etc) - cron: Schedule periodic execution (e.g., "0 9 * * *" = daily at 9am) - websocket: Trigger on WebSocket event from client via realtime connection - s3_upload: Trigger on file upload [not yet implemented] - webhook: Receive webhooks from external services [not yet implemented] Common errors: - VALIDATION_INVALID_SCHEMA: Check code exports a handler function - RESOURCE_NOT_FOUND: App doesn't exist - Syntax error: Code must be valid TypeScript/JavaScript envVars on redeploy: MERGES into the function's existing env by default — incoming keys overwrite matching existing keys, everything else is kept. Pass envVarsReplace: true to instead REPLACE the entire env blob with just the keys you send (deletes every key you don't include). Use manage_function (action: "update_env") for a dedicated partial update, including deleting a single key by setting its value to null. Idempotency: Safe to call multiple times (updates existing function with same name). Next steps: Use invoke_function to test, then manage_function (action: "get_logs") to debug.

    • Idempotent

    Inputs

    app_idstringrequired
    The app ID to deploy the function to
    codestringrequired
    TypeScript/JavaScript code that exports a handler function
    namestringrequired
    Function name (alphanumeric, hyphens, underscores)
    agent_toolboolean
    When true, this function is exposed to agents in this app as a tool. Agents must still list its name under their graph_spec.tools.functions[] to call it.
    agent_tool_descriptionstring
    Short imperative description shown to the LLM (max 500 chars). Required for any function the model is expected to choose intelligently.
    agent_tool_exposed_tostring
    developer_only (default) — only dashboard/CLI test runs can call it. end_user — also callable by public agent invocations.one of developer_onlyend_user
    agent_tool_modestring
    read_only (default) | read_write. read_write tools require human approval per call (HITL).one of read_onlyread_write
    allow_service_key_impersonationboolean
    Default true. Lets an app-scoped service-key caller assert "act as user X" via the X-Butterbase-As-User header — the runtime populates ctx.user with the asserted id before invoking. Set to false on admin-only or billing-webhook handlers that must never accept an as-user assertion: control-api will 403 such calls at the edge.
    descriptionstring
    Function description
    envVarsobject
    Environment variables (will be encrypted). On redeploy, MERGES into the existing env by default (incoming keys overwrite matching keys; other existing keys are kept). Pass envVarsReplace: true to replace the entire env blob instead.
    envVarsReplaceboolean
    Default false (merge). Set true to REPLACE the entire env blob with just the envVars you send on this call, deleting any existing key not included.
    memoryLimitMbnumber
    Memory limit in MB (default: 128)
    timeoutMsnumber
    Execution timeout in milliseconds (default: 30000)
    triggerobject
    Single-trigger shorthand. Prefer `triggers` for new code; this is kept for back-compat.
    triggersarray
    Canonical multi-trigger array. At most one trigger per type.
  • butterbasemcp_init_appCreate a new backend app with isolated database and API endpoints.Write

    Initialize App

    Create a new backend app with isolated database and API endpoints. Returns: app_id, api_url, url (frontend URL), and provisioning status. Example: Input: { name: "my-blog" } Output: { app_id: "app_abc123", api_url: "https://api.butterbase.dev/v1/app_abc123", url: "https://my-blog.butterbase.dev", _meta: { next_actions: [...] } } URL guide: - api_url: Your API endpoint for database queries, auth, and functions (e.g. https://api.butterbase.dev/v1/app_abc123) - url: Your frontend URL where your deployed site is served (e.g. https://my-blog.butterbase.dev) - These are different! The api_url is for backend requests, the url is where users visit your app. Next steps: Use manage_schema (action: "apply") to define tables, then manage_oauth (action: "configure") for auth. Common errors: - Name already exists: Choose a different name or use manage_app (action: "list") to find existing app - Invalid characters: Use only lowercase letters, numbers, hyphens, underscores - Name too long: Maximum 63 characters The response includes _meta.next_actions with recommended next steps.

    Inputs

    namestringrequired
    App name (lowercase alphanumeric, hyphens, underscores)
    organization_idstring
    Organization to create the app under. Requires membership. Defaults to the API key's bound org, or the caller's personal org.
    regionstring
    Region to provision in (e.g. "us-east-1", "us-west-2"). Defaults to the control-api's home region.