@buildbase/sdk 0.0.53 โ 0.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +16 -6
- package/CHANGELOG.md +99 -0
- package/LICENSE +21 -0
- package/README.md +278 -85
- package/dist/css/styles.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +582 -114
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.d.ts +780 -16
- package/dist/mcp/index.mjs +1 -1
- package/dist/react/chunks/SettingsDialog-B6DhyNae.cjs +2 -0
- package/dist/react/chunks/SettingsDialog-K38BI74F.mjs +2 -0
- package/dist/react/chunks/SubscriptionDialog-BidcQ_vc.mjs +2 -0
- package/dist/react/chunks/SubscriptionDialog-CfDLoOzW.cjs +2 -0
- package/dist/react/chunks/ar-CCKONfUU.mjs +2 -0
- package/dist/react/chunks/ar-DrHMtT1E.cjs +2 -0
- package/dist/react/chunks/de-Dhtorn10.cjs +2 -0
- package/dist/react/chunks/de-X9mfnMFM.mjs +2 -0
- package/dist/react/chunks/es--8iZ7Trv.mjs +2 -0
- package/dist/react/chunks/es-CUR7LB6a.cjs +2 -0
- package/dist/react/chunks/fr-BqMs_F5g.cjs +2 -0
- package/dist/react/chunks/fr-rUp34NG7.mjs +2 -0
- package/dist/react/chunks/hi-B_IiucJZ.mjs +2 -0
- package/dist/react/chunks/hi-C2i1_hdv.cjs +2 -0
- package/dist/react/chunks/ja-4hLI6wMX.cjs +2 -0
- package/dist/react/chunks/ja-CIGOj3vV.mjs +2 -0
- package/dist/react/chunks/react-BXTDLnE8.cjs +2 -0
- package/dist/react/chunks/react-CXCk05WI.mjs +2 -0
- package/dist/react/chunks/zh-BSgd6ai5.cjs +2 -0
- package/dist/react/chunks/zh-DyVvcYFC.mjs +2 -0
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.d.ts +507 -141
- package/dist/react/index.mjs +1 -1
- package/docs/ARCHITECTURE.md +419 -0
- package/docs/ERROR_CODES.md +96 -0
- package/docs/MCP-AND-AGENT-READINESS.md +893 -0
- package/docs/THEMING.md +67 -0
- package/docs/UI-CONFIG.md +207 -0
- package/package.json +17 -4
- package/dist/react/chunks/SettingsDialog-BxDYsie6.mjs +0 -2
- package/dist/react/chunks/SettingsDialog-Dy8W3NA6.cjs +0 -2
- package/dist/react/chunks/SubscriptionDialog-6c5dHoQd.mjs +0 -2
- package/dist/react/chunks/SubscriptionDialog-CpVnnXJg.cjs +0 -2
- package/dist/react/chunks/ar-1Z1MOGAP.mjs +0 -2
- package/dist/react/chunks/ar-VGzBv3AZ.cjs +0 -2
- package/dist/react/chunks/de-BjdDg60Y.cjs +0 -2
- package/dist/react/chunks/de-CXUItplO.mjs +0 -2
- package/dist/react/chunks/es-BBPsSZUp.cjs +0 -2
- package/dist/react/chunks/es-BTbzVutz.mjs +0 -2
- package/dist/react/chunks/fr-Cax48X_S.cjs +0 -2
- package/dist/react/chunks/fr-DPMeOQu3.mjs +0 -2
- package/dist/react/chunks/hi-BJqvpG6d.cjs +0 -2
- package/dist/react/chunks/hi-JfJx-LV7.mjs +0 -2
- package/dist/react/chunks/ja-CDGtLzTk.mjs +0 -2
- package/dist/react/chunks/ja-DvVyj5xX.cjs +0 -2
- package/dist/react/chunks/react-4SlWxHdh.cjs +0 -2
- package/dist/react/chunks/react-OL1uSkpr.mjs +0 -2
- package/dist/react/chunks/zh-BAcqxkxk.cjs +0 -2
- package/dist/react/chunks/zh-BNZ2Ooe8.mjs +0 -2
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ Also works server-side (Next.js API routes, Express, Hono) โ see [Server-Side
|
|
|
7
7
|
## ๐ Table of Contents
|
|
8
8
|
|
|
9
9
|
- [Features](#-features)
|
|
10
|
+
- [Customization Map](#-customization-map)
|
|
10
11
|
- [Installation](#installation)
|
|
11
12
|
- [Quick Start](#quick-start)
|
|
12
13
|
- [UI Configuration](#๏ธ-ui-configuration)
|
|
@@ -19,6 +20,7 @@ Also works server-side (Next.js API routes, Express, Hono) โ see [Server-Side
|
|
|
19
20
|
- [Trial Gates](#-trial-gates)
|
|
20
21
|
- [Push Notifications](#-push-notifications)
|
|
21
22
|
- [Notifications](#-notifications)
|
|
23
|
+
- [Connected Agents](#-connected-agents)
|
|
22
24
|
- [User Management](#-user-management)
|
|
23
25
|
- [Workspace Management](#-complete-workspace-management)
|
|
24
26
|
- [Public Pricing (No Login)](#-public-pricing-no-login)
|
|
@@ -52,6 +54,7 @@ Also works server-side (Next.js API routes, Express, Hono) โ see [Server-Side
|
|
|
52
54
|
- **โณ Trial Gates** - `WhenTrialing`, `WhenNotTrialing`, `WhenTrialEnding` components + `useTrialStatus` hook
|
|
53
55
|
- **๐ Push Notifications** - Browser push notifications with `usePushNotifications` hook, auto-triggers for billing events, and campaign management
|
|
54
56
|
- **๐ฌ Notifications** - Email + push notification system with per-event channel control, workspace preferences, and server-side `notification.send()` API
|
|
57
|
+
- **๐ Connected Agents** - `<ConnectedAgents />` screen to list/revoke authorized AI agents, plus a built-in "Connect an agent" setup guide (ChatGPT, Claude, Cursor, VS Code, Windsurf, Cline) driven by an `mcp` provider prop
|
|
55
58
|
- **๐บ Seat-Based Pricing** - Per-seat billing with included seats, billable seat tracking, and seat limit enforcement
|
|
56
59
|
- **๐ฑ Multi-Currency** - Per-currency pricing variants with workspace billing currency lock
|
|
57
60
|
- **๐ค Affiliate Tracking** - Pass referral data to Stripe checkout via `getCheckoutStripeParams` prop (Rewardful, Endorsely, FirstPromoter, etc.)
|
|
@@ -66,6 +69,31 @@ Also works server-side (Next.js API routes, Express, Hono) โ see [Server-Side
|
|
|
66
69
|
- **๐ Internationalization (i18n)** - 8 locales (en, es, fr, de, ja, zh, hi, ar), ICU MessageFormat, RTL support, native numerals
|
|
67
70
|
- **๐ Workspace Modes** - Personal (solo B2C) or Platform (multi-user B2B), configured from admin dashboard
|
|
68
71
|
|
|
72
|
+
## ๐จ Customization Map
|
|
73
|
+
|
|
74
|
+
Everything the SDK lets you configure, override, or extend โ one row per knob, so you can see the whole surface before reading any deep-dive section. The philosophy throughout: **safe defaults, everything overridable, the SDK accelerates but never gates.**
|
|
75
|
+
|
|
76
|
+
| What you customize | How | Where to read more |
|
|
77
|
+
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
78
|
+
| Provider setup & workspace behavior | `<SaaSOSProvider>` / `<BuildBaseProvider>` props (`serverUrl`, `orgId`, `locale`, workspace auto-create, redirects) | [Configuration Reference](#๏ธ-configuration-reference) |
|
|
79
|
+
| Which SDK UI renders at all | `ui` prop: hide/show settings sections, workspace switcher, per-screen toggles | [UI Configuration](#๏ธ-ui-configuration) ยท `docs/UI-CONFIG.md` |
|
|
80
|
+
| Every user-facing string | `ui.messages` deep-override (e.g. rename "Subscription" โ "Billing"), per-locale | [UI Configuration](#๏ธ-ui-configuration) |
|
|
81
|
+
| Language & number systems | `locale` prop โ 8 locales, ICU plurals, RTL (`ar`), native numerals | [Internationalization](#-internationalization-i18n) |
|
|
82
|
+
| Look & feel | CSS custom properties (`--primary`, `--destructive`, โฆ) โ semantic tokens, light/dark, no palette lock-in | `docs/THEMING.md` |
|
|
83
|
+
| Date/number formats | `ui.formats` (`Intl` options passed through) | `docs/UI-CONFIG.md` |
|
|
84
|
+
| Conditional UI | Gate components: `WhenAuthenticated`, `WhenPermission`, `WhenWorkspaceFeatureEnabled`, `WhenSubscription`, `WhenTrialing`, `WhenQuotaAvailable`, `WhenCreditsAvailable`, โฆ | [Subscription Gates](#-subscription-gates) & siblings |
|
|
85
|
+
| Checkout behavior | `getCheckoutStripeParams` (affiliate/referral params), plan-picker behavior config | [Affiliate Tracking](#affiliate--referral-tracking) |
|
|
86
|
+
| Connect-an-agent guide | `mcp` prop (`url`, `name`, `docsUrl`, `clients`, `prompt`) โ in-app setup guide + `<ConnectedAgents />` screen | [Connected Agents](#-connected-agents) |
|
|
87
|
+
| Error presentation | `ui.errorBoundary`, centralized `handleError` reporting | [Error Handling](#๏ธ-error-handling) |
|
|
88
|
+
| React-free server access | `BuildBase()` factory โ workspaces, usage, credits, notifications from API routes/jobs | [Server-Side Usage](#server-side-usage) |
|
|
89
|
+
| Agent discovery surface | `createAgentStack` / `resolveAgentPath`: robots.txt, llms.txt, `.well-known/*`, all documents overridable (e.g. `config.llmsTxt`) | [Agent Readiness](#agent-readiness-discovery) ยท `docs/MCP-AND-AGENT-READINESS.md` |
|
|
90
|
+
| MCP tools | `builtinTools` (`'readonly'`/`'all'`/`false`/include/exclude) + your own via `defineMcpTool` (custom tools override built-ins) | [MCP Server](#mcp-server-buildbasesdkmcp) |
|
|
91
|
+
| MCP resources & prompts | Opt-in `builtinResources` catalog + `defineMcpResource`/`defineMcpResourceTemplate`/`defineMcpPrompt` | [MCP Server](#mcp-server-buildbasesdkmcp) |
|
|
92
|
+
| MCP auth & hardening | `auth.verify` (your token format) or `buildbaseAuth` preset; `rateLimit`, `cors`, `allowedOrigins`, `formatToolError` | `docs/MCP-AND-AGENT-READINESS.md` |
|
|
93
|
+
| Per-request context for your MCP tools | `context(auth, req)` factory โ `ctx.custom` (your DB, services) | [MCP Server](#mcp-server-buildbasesdkmcp) |
|
|
94
|
+
| Money display | `formatMinorAmountIntl`/`formatCents`/`getCurrencyDecimals` โ full ISO 4217 (0/2/3-decimal currencies) | [Multi-Currency](#-multi-currency--pricing-utilities) |
|
|
95
|
+
| Webhooks from the platform | `verifyWebhookSignature` (HMAC, timing-safe) | [Webhook Verification](#webhook-verification) |
|
|
96
|
+
|
|
69
97
|
## Installation
|
|
70
98
|
|
|
71
99
|
```bash
|
|
@@ -87,6 +115,7 @@ import '@buildbase/sdk/css';
|
|
|
87
115
|
// components/provider.tsx
|
|
88
116
|
'use client';
|
|
89
117
|
|
|
118
|
+
// BuildBaseProvider is an identical brand-aligned alias โ prefer it in new code
|
|
90
119
|
import { SaaSOSProvider } from '@buildbase/sdk/react';
|
|
91
120
|
import { ApiVersion } from '@buildbase/sdk';
|
|
92
121
|
|
|
@@ -283,7 +312,7 @@ function MembersPanel() {
|
|
|
283
312
|
|
|
284
313
|
`useUIConfig()` gives you the raw `ui` object if you only need the config values; `mergeUIConfig(base, override)` is the deep-merge used for per-dialog overrides.
|
|
285
314
|
|
|
286
|
-
๐ **Full guide with the complete toggle reference, precedence rules, and recipes (single-tenant, external billing, white-label, read-only): [docs/UI-CONFIG.md](
|
|
315
|
+
๐ **Full guide with the complete toggle reference, precedence rules, and recipes (single-tenant, external billing, white-label, read-only): [docs/UI-CONFIG.md](https://unpkg.com/@buildbase/sdk/docs/UI-CONFIG.md)**
|
|
287
316
|
|
|
288
317
|
## ๐ Authentication
|
|
289
318
|
|
|
@@ -346,9 +375,16 @@ openWorkspaceSettings('users'); // Workspace members
|
|
|
346
375
|
openWorkspaceSettings('subscription'); // Plan & Billing
|
|
347
376
|
openWorkspaceSettings('usage'); // Quota usage dashboard
|
|
348
377
|
openWorkspaceSettings('features'); // Feature toggles
|
|
378
|
+
openWorkspaceSettings('connected-agents'); // Connected AI agents + connect guide
|
|
349
379
|
openWorkspaceSettings('danger'); // Delete workspace (owner only)
|
|
350
380
|
```
|
|
351
381
|
|
|
382
|
+
Some sections accept an optional second `action` argument to deep-link past the screen. For connected agents, open the setup guide directly:
|
|
383
|
+
|
|
384
|
+
```tsx
|
|
385
|
+
openWorkspaceSettings('connected-agents', { action: 'openConnectGuide' });
|
|
386
|
+
```
|
|
387
|
+
|
|
352
388
|
### Authentication Components
|
|
353
389
|
|
|
354
390
|
For declarative rendering, use the conditional components:
|
|
@@ -504,7 +540,9 @@ Use the `useUserFeatures` hook to check feature flags programmatically:
|
|
|
504
540
|
import { useUserFeatures } from '@buildbase/sdk/react';
|
|
505
541
|
|
|
506
542
|
function FeatureCheck() {
|
|
507
|
-
|
|
543
|
+
// { features, isFeatureEnabled, loading, error, refetch }
|
|
544
|
+
// (the older `isLoading` / `refreshFeatures` names still work but are deprecated)
|
|
545
|
+
const { features, isFeatureEnabled, refetch } = useUserFeatures();
|
|
508
546
|
|
|
509
547
|
return (
|
|
510
548
|
<div>{isFeatureEnabled('premium-features') ? <PremiumContent /> : <StandardContent />}</div>
|
|
@@ -1074,6 +1112,57 @@ Platform mode supports granular overrides from the admin dashboard:
|
|
|
1074
1112
|
|
|
1075
1113
|
These let you achieve team-like, managed, or enterprise-like behavior without a separate mode.
|
|
1076
1114
|
|
|
1115
|
+
## ๐ Connected Agents
|
|
1116
|
+
|
|
1117
|
+
Let users connect AI agents to their account and manage them. `<ConnectedAgents />` is a ready-made screen (also a workspace settings section) that lists the agents a user has authorized โ each with a **Disconnect** action โ and, when you provide an MCP server address, shows a **Connect an agent** button that opens a plain-language setup guide.
|
|
1118
|
+
|
|
1119
|
+
### Enable the connect guide
|
|
1120
|
+
|
|
1121
|
+
Pass an `mcp` config to the provider. Without it the screen still lists and revokes agents; with it, the "Connect an agent" button and setup dialog appear.
|
|
1122
|
+
|
|
1123
|
+
```tsx
|
|
1124
|
+
<SaaSOSProvider
|
|
1125
|
+
// ...serverUrl, orgId, auth
|
|
1126
|
+
mcp={{
|
|
1127
|
+
url: 'https://app.example.com/api/mcp', // your MCP server endpoint (required to show the guide)
|
|
1128
|
+
name: 'Acme', // friendly name used in prose + config snippets
|
|
1129
|
+
docsUrl: 'https://docs.example.com/agents', // optional "Learn more" link
|
|
1130
|
+
}}
|
|
1131
|
+
>
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
The dialog walks users through the major AI apps โ **ChatGPT, Claude, Cursor, VS Code, Windsurf, Cline** โ each with the exact click-path or config-file snippet (your server URL is filled in for them), plus a copy-and-paste prompt for chat assistants. It's fully translated (all 8 locales), RTL-aware, and responsive (full-screen on mobile, centered card on desktop).
|
|
1135
|
+
|
|
1136
|
+
### The screen
|
|
1137
|
+
|
|
1138
|
+
```tsx
|
|
1139
|
+
import { ConnectedAgents } from '@buildbase/sdk/react';
|
|
1140
|
+
|
|
1141
|
+
function AgentsPage() {
|
|
1142
|
+
return <ConnectedAgents />;
|
|
1143
|
+
}
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
Props: `title`, `description`, `disconnectLabel`, `emptyLabel` (all default to translated strings; pass `null` to hide the heading) and `showConnectGuide` (default `true` โ set `false` to hide the connect button even when `mcp` is configured).
|
|
1147
|
+
|
|
1148
|
+
Because it's a settings section, you can also open it programmatically:
|
|
1149
|
+
|
|
1150
|
+
```tsx
|
|
1151
|
+
const { openWorkspaceSettings } = useSaaSAuth();
|
|
1152
|
+
|
|
1153
|
+
openWorkspaceSettings('connected-agents'); // the screen (list + connect button)
|
|
1154
|
+
openWorkspaceSettings('connected-agents', { action: 'openConnectGuide' }); // straight to the setup dialog
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
The same deep link works via URL: `?bb=action:openConnectGuide`.
|
|
1158
|
+
|
|
1159
|
+
### Custom UI
|
|
1160
|
+
|
|
1161
|
+
- **`useConnectedAgents()`** โ headless `{ agents, loading, error, revoking, refresh, revoke }` for a fully custom list.
|
|
1162
|
+
- **`useMcpConnection()`** โ the `mcp` config you passed (or `null`), for building your own guide.
|
|
1163
|
+
- **`<ConnectMcpGuide />`** โ the guide body on its own (copyable server URL + paste-in prompt + per-client accordion), embeddable anywhere. Override the app list via `mcp.clients` and the prompt via `mcp.prompt` (`false` hides it).
|
|
1164
|
+
- **`fillMcpTemplate` / `mcpServerKey`** โ the placeholder helpers (`{{url}}` / `{{name}}` / `{{key}}`) for building custom client snippets.
|
|
1165
|
+
|
|
1077
1166
|
## ๐ค User Management
|
|
1078
1167
|
|
|
1079
1168
|
### User Attributes
|
|
@@ -1084,8 +1173,9 @@ Manage custom user attributes (key-value pairs):
|
|
|
1084
1173
|
import { useUserAttributes } from '@buildbase/sdk/react';
|
|
1085
1174
|
|
|
1086
1175
|
function UserProfile() {
|
|
1087
|
-
|
|
1088
|
-
|
|
1176
|
+
// { attributes, loading, error, refetch, updateAttribute, updateAttributes }
|
|
1177
|
+
// (the older `isLoading` / `refreshAttributes` names still work but are deprecated)
|
|
1178
|
+
const { attributes, loading, updateAttribute, updateAttributes, refetch } = useUserAttributes();
|
|
1089
1179
|
|
|
1090
1180
|
const handleUpdate = async () => {
|
|
1091
1181
|
// Update single attribute
|
|
@@ -1245,19 +1335,23 @@ Plans support **pricing variants** (multi-currency). Use these utilities for dis
|
|
|
1245
1335
|
|
|
1246
1336
|
### Currency utilities
|
|
1247
1337
|
|
|
1248
|
-
| Export
|
|
1249
|
-
|
|
|
1250
|
-
| `CURRENCY_DISPLAY`
|
|
1251
|
-
| `CURRENCY_FLAG`
|
|
1252
|
-
| `PLAN_CURRENCY_CODES`
|
|
1253
|
-
| `PLAN_CURRENCY_OPTIONS`
|
|
1254
|
-
| `getCurrencySymbol(currency)`
|
|
1255
|
-
| `getCurrencyFlag(currency)`
|
|
1256
|
-
| `formatCents(cents, currency)`
|
|
1257
|
-
| `
|
|
1258
|
-
| `
|
|
1259
|
-
| `
|
|
1260
|
-
| `
|
|
1338
|
+
| Export | Purpose |
|
|
1339
|
+
| -------------------------------------------------- | --------------------------------------------------------------- |
|
|
1340
|
+
| `CURRENCY_DISPLAY` | Map of currency code โ symbol (e.g. `usd` โ `$`) |
|
|
1341
|
+
| `CURRENCY_FLAG` | Map of currency code โ flag emoji |
|
|
1342
|
+
| `PLAN_CURRENCY_CODES` | Allowed billing currency codes (for dropdowns/validation) |
|
|
1343
|
+
| `PLAN_CURRENCY_OPTIONS` | Options array for plan currency selects |
|
|
1344
|
+
| `getCurrencySymbol(currency)` | Symbol for a Stripe currency code |
|
|
1345
|
+
| `getCurrencyFlag(currency)` | Flag emoji for a currency code |
|
|
1346
|
+
| `formatCents(cents, currency)` | Format a minor-unit amount with the symbol table |
|
|
1347
|
+
| `formatMinorAmountIntl(amount, currency, locale?)` | Locale-aware `Intl` money formatting of minor units |
|
|
1348
|
+
| `getCurrencyDecimals(currency)` | ISO 4217 minor-unit digits (0 JPY, 2 USD, 3 KWD; CLDR fallback) |
|
|
1349
|
+
| `isZeroDecimalCurrency(currency)` | True when the currency has no minor unit (JPY, KRW, โฆ) |
|
|
1350
|
+
| `minorAmountToDisplay(amount, currency)` | Minor units โ display number string (no symbol) |
|
|
1351
|
+
| `formatOverageRate(cents, currency)` | Format overage rate for display |
|
|
1352
|
+
| `formatOverageRateWithLabel(...)` | Overage rate with optional unit label |
|
|
1353
|
+
| `formatQuotaIncludedOverage(...)` | "X included, then $Y / unit" style text |
|
|
1354
|
+
| `getQuotaUnitLabelFromName(name)` | Human-readable unit label from quota name |
|
|
1261
1355
|
|
|
1262
1356
|
### Pricing variant utilities
|
|
1263
1357
|
|
|
@@ -1297,7 +1391,9 @@ import {
|
|
|
1297
1391
|
const variant = getPricingVariant(planVersion, 'usd');
|
|
1298
1392
|
const cents = getBasePriceCents(planVersion, 'usd', 'monthly');
|
|
1299
1393
|
if (cents != null) {
|
|
1300
|
-
|
|
1394
|
+
// Never divide by 100 yourself โ formatCents applies the currency's ISO 4217
|
|
1395
|
+
// minor-unit factor (JPY ยฅ1,000 stays whole, KWD divides by 1000, USD by 100).
|
|
1396
|
+
console.log(formatCents(cents, 'usd')); // "$12.99"
|
|
1301
1397
|
}
|
|
1302
1398
|
|
|
1303
1399
|
// Quota display with overage
|
|
@@ -2067,7 +2163,7 @@ import { eventEmitter } from '@buildbase/sdk';
|
|
|
2067
2163
|
|
|
2068
2164
|
## ๐ก๏ธ Error Handling
|
|
2069
2165
|
|
|
2070
|
-
The SDK handles errors internally: API failures, auth errors, and component errors are logged and surfaced through hook states (e.g. `error` from `useSaaSWorkspaces`) and callbacks. **SaaSOSProvider** wraps its children in an internal **SDKErrorBoundary** to catch React render errors inside the SDK tree. For app-level errors, wrap your app (or routes) in your own error boundary (e.g. Reactโs `ErrorBoundary` or your frameworkโs error UI). For failed async operations, check the `error` property on hooks and show user feedback (e.g. toast or inline message). See [Error codes](docs/ERROR_CODES.md) for SDK error codes and HTTP mappings.
|
|
2166
|
+
The SDK handles errors internally: API failures, auth errors, and component errors are logged and surfaced through hook states (e.g. `error` from `useSaaSWorkspaces`) and callbacks. **SaaSOSProvider** wraps its children in an internal **SDKErrorBoundary** to catch React render errors inside the SDK tree. For app-level errors, wrap your app (or routes) in your own error boundary (e.g. Reactโs `ErrorBoundary` or your frameworkโs error UI). For failed async operations, check the `error` property on hooks and show user feedback (e.g. toast or inline message). See [Error codes](https://unpkg.com/@buildbase/sdk/docs/ERROR_CODES.md) for SDK error codes and HTTP mappings.
|
|
2071
2167
|
|
|
2072
2168
|
## โ๏ธ Settings
|
|
2073
2169
|
|
|
@@ -2077,7 +2173,11 @@ Access OS-level settings:
|
|
|
2077
2173
|
import { useSaaSSettings } from '@buildbase/sdk/react';
|
|
2078
2174
|
|
|
2079
2175
|
function SettingsExample() {
|
|
2080
|
-
|
|
2176
|
+
// { settings, loading, error, refetch } โ `loading` is true until the one-time
|
|
2177
|
+
// fetch settles, so you can wait instead of rendering with partial defaults.
|
|
2178
|
+
const { settings, loading, refetch } = useSaaSSettings();
|
|
2179
|
+
|
|
2180
|
+
if (loading) return <Spinner />;
|
|
2081
2181
|
|
|
2082
2182
|
return (
|
|
2083
2183
|
<div>
|
|
@@ -2155,9 +2255,11 @@ Prefer these SDK hooks for state and operations instead of `useAppSelector`:
|
|
|
2155
2255
|
| `useSaaSAuth()` | Auth state (user, session, status), signIn(returnUrl?), signOut, openWorkspaceSettings |
|
|
2156
2256
|
| `useSaaSWorkspaces()` | Workspaces, currentWorkspace, loading, switching/switchingToId, CRUD and switch actions |
|
|
2157
2257
|
| `useSaaSOs()` | OS config (serverUrl, version, orgId, auth, settings) when you need the full config object |
|
|
2158
|
-
| `useSaaSSettings()` | Organization settings
|
|
2159
|
-
| `useUserAttributes()` | User attributes
|
|
2160
|
-
| `useUserFeatures()` | User feature flags
|
|
2258
|
+
| `useSaaSSettings()` | Organization settings โ `{ settings, loading, error, refetch }` (prefer this when you only need settings) |
|
|
2259
|
+
| `useUserAttributes()` | User attributes โ `{ attributes, loading, error, refetch, updateAttribute, updateAttributes }` |
|
|
2260
|
+
| `useUserFeatures()` | User feature flags โ `{ features, isFeatureEnabled, loading, error, refetch }` |
|
|
2261
|
+
| `useConnectedAgents()` | Authorized AI agents โ `{ agents, loading, error, revoking, refresh, revoke }` |
|
|
2262
|
+
| `useMcpConnection()` | The `mcp` connection config passed to the provider (or `null`), for a custom connect guide |
|
|
2161
2263
|
| `useSubscriptionContext()` | Subscription for current workspace (response, loading, refetch); use inside SubscriptionContextProvider |
|
|
2162
2264
|
| `useTrialStatus()` | Trial state: `isTrialing`, `daysRemaining`, `trialEndsAt`, `isTrialEnding` |
|
|
2163
2265
|
| `usePushNotifications()` | Push notification state and actions: `isSubscribed`, `subscribe()`, `unsubscribe()` |
|
|
@@ -2177,12 +2279,12 @@ Using hooks keeps your code stable if internal state shape changes and avoids di
|
|
|
2177
2279
|
|
|
2178
2280
|
### Types
|
|
2179
2281
|
|
|
2180
|
-
All TypeScript types are exported for type safety. See the [TypeScript definitions](
|
|
2282
|
+
All TypeScript types are exported for type safety. See the [TypeScript definitions](https://unpkg.com/@buildbase/sdk/dist/index.d.ts) for complete type information.
|
|
2181
2283
|
|
|
2182
2284
|
### Further documentation
|
|
2183
2285
|
|
|
2184
|
-
- [Architecture](docs/ARCHITECTURE.md) โ Layers, providers, APIs (BaseApi, UserApi, WorkspaceApi, SettingsApi), state, auth flow
|
|
2185
|
-
- [Error codes](docs/ERROR_CODES.md) โ SDK error codes and HTTP status mappings
|
|
2286
|
+
- [Architecture](https://unpkg.com/@buildbase/sdk/docs/ARCHITECTURE.md) โ Layers, providers, APIs (BaseApi, UserApi, WorkspaceApi, SettingsApi), state, auth flow
|
|
2287
|
+
- [Error codes](https://unpkg.com/@buildbase/sdk/docs/ERROR_CODES.md) โ SDK error codes and HTTP status mappings
|
|
2186
2288
|
|
|
2187
2289
|
## โ๏ธ Configuration Reference
|
|
2188
2290
|
|
|
@@ -2935,7 +3037,11 @@ app.post('/webhooks/buildbase', (req, res) => {
|
|
|
2935
3037
|
|
|
2936
3038
|
## Agent Readiness (Discovery)
|
|
2937
3039
|
|
|
2938
|
-
|
|
3040
|
+
> **Full guide:** [`docs/MCP-AND-AGENT-READINESS.md`](https://unpkg.com/@buildbase/sdk/docs/MCP-AND-AGENT-READINESS.md) covers the whole surface โ the fast path, the auth model, and the scope/resource customization spectrum. This section is the overview.
|
|
3041
|
+
|
|
3042
|
+
Make a consuming app [agent ready](https://isitagentready.com) by serving the machine-readable discovery documents an AI client looks for โ Agent Card, A2A card, OAuth metadata (RFC 8414/9728), Agent Skills, API Catalog (RFC 9727), MCP Server Card (SEP-1649 v1.0) + discovery manifest (SEP-1960), `robots.txt` with AI-bot rules + Content Signals, `sitemap.xml`, `security.txt`, `llms.txt`/`llms-full.txt`, and `auth.md`. Framework-agnostic and zero React: every function returns a plain `DiscoveryDocument` (`{ status, contentType, body, cacheControl, vary? }`).
|
|
3043
|
+
|
|
3044
|
+
The fast path is [`createAgentStack`](https://unpkg.com/@buildbase/sdk/docs/MCP-AND-AGENT-READINESS.md#fast-path--createagentstack) โ one config derives the MCP handler **and** the whole discovery surface. To wire the discovery layer on its own, use `resolveAgentPath`:
|
|
2939
3045
|
|
|
2940
3046
|
```ts
|
|
2941
3047
|
// lib/agent-ready.ts
|
|
@@ -2944,14 +3050,18 @@ import { resolveAgentPath, type AgentReadyConfig } from '@buildbase/sdk';
|
|
|
2944
3050
|
export const agentConfig: AgentReadyConfig = {
|
|
2945
3051
|
serverUrl: process.env.BUILDBASE_URL!,
|
|
2946
3052
|
orgId: process.env.BUILDBASE_ORG_ID!,
|
|
2947
|
-
siteUrl: 'https://
|
|
2948
|
-
site: { name: '
|
|
3053
|
+
siteUrl: 'https://example.com',
|
|
3054
|
+
site: { name: 'Acme', description: 'Generate images from templates via API.' },
|
|
2949
3055
|
// Discovery content is YOURS โ defined here in code, not in the platform:
|
|
2950
|
-
|
|
3056
|
+
scopes: [
|
|
3057
|
+
{ name: 'designs:read', description: 'View designs' },
|
|
3058
|
+
{ name: 'render:execute', description: 'Render images' },
|
|
3059
|
+
],
|
|
3060
|
+
llmsTxt: '# Acme\n\n> Generate images from templates via API.',
|
|
2951
3061
|
robots: { contentSignals: { search: true, aiInput: true, aiTrain: false } },
|
|
2952
3062
|
sitemap: { urls: ['/', '/pricing'] }, // omit if you already generate sitemaps
|
|
2953
|
-
protectedResources: [{
|
|
2954
|
-
// skills, security, apiCatalog, mcpServerCard,
|
|
3063
|
+
protectedResources: [{ resource: 'https://example.com/mcp' }], // scopes inherited from catalog
|
|
3064
|
+
// skills, security, apiCatalog, mcpServerCard, a2aCard, extraPaths โ all optional
|
|
2955
3065
|
};
|
|
2956
3066
|
|
|
2957
3067
|
export async function serveAgentPath(req: Request): Promise<Response> {
|
|
@@ -3031,21 +3141,61 @@ Clean split, no duplication: **your app owns discovery content, the platform own
|
|
|
3031
3141
|
| llms.txt | `buildLlmsTxt` |
|
|
3032
3142
|
| MCP Server Card + live MCP server | `buildMcpServerCard` + [`@buildbase/sdk/mcp`](#mcp-server-buildbasesdkmcp) |
|
|
3033
3143
|
| Agent Skills / API Catalog | `buildAgentSkillsIndex` / `buildApiCatalog` |
|
|
3144
|
+
| A2A Agent Card | `buildA2AAgentCard` (served by default from `site` + `skills`) |
|
|
3034
3145
|
| OAuth discovery (RFC 8414) + protected resource (RFC 9728) | `resolveAgentPath` (platform-backed) |
|
|
3035
|
-
|
|
|
3036
|
-
|
|
|
3146
|
+
| Web Bot Auth | `buildWebBotAuthDirectory` (opt-in โ publish your JWKS) |
|
|
3147
|
+
| DNS-AID | `buildDnsAidRecords` (returns records โ publish at your DNS provider) |
|
|
3148
|
+
| Commerce (x402/ACP/UCP/MPP), openapi.json | `config.extraPaths` (serve any literal document by path) |
|
|
3037
3149
|
|
|
3038
|
-
**Functions:** `resolveAgentPath`, `resolveWellKnown`, `fetchAgentReadiness`, `clearAgentReadinessCache`, `buildAgentCard`, `buildProtectedResourceMetadata`, `buildAgentSkillsIndex`, `buildSkillMd`, `buildSecurityTxt`, `buildLlmsTxt`, `buildRobotsTxt`, `buildSitemap`, `buildApiCatalog`, `buildMcpServerCard`, `buildAuthMd`, `buildDiscoveryLinkHeader`, `wantsMarkdown`, `negotiateMarkdown`, `provideWebMcpTools`, `sha256Digest`. **Constants:** `AI_BOT_USER_AGENTS`. **Types:** `AgentReadyConfig`, `AgentReadinessBundle`, `AgentSkill`, `ApiCatalogApi`, `McpServerCard`, `RobotsConfig`, `RobotsPolicy`, `ContentSignals`, `SitemapUrl`, `WebMcpTool`, `DiscoveryDocument`.
|
|
3150
|
+
**Functions:** `resolveAgentPath`, `resolveWellKnown`, `fetchAgentReadiness`, `clearAgentReadinessCache`, `buildAgentCard`, `buildA2AAgentCard`, `buildProtectedResourceMetadata`, `buildAgentSkillsIndex`, `buildSkillMd`, `buildSecurityTxt`, `buildLlmsTxt`, `buildLlmsFullTxt`, `buildRobotsTxt`, `buildSitemap`, `buildApiCatalog`, `buildMcpServerCard`, `buildMcpDiscoveryManifest`, `buildAuthMd`, `buildWebBotAuthDirectory`, `buildDnsAidRecords`, `buildDiscoveryLinkHeader`, `wantsMarkdown`, `negotiateMarkdown`, `provideWebMcpTools`, `sha256Digest`. **Constants:** `AI_BOT_USER_AGENTS`. **Types:** `AgentReadyConfig`, `AgentReadinessBundle`, `AgentSkill`, `AppScope`, `ApiCatalogApi`, `A2ACardConfig`, `A2ACardSkill`, `DnsAidRecord`, `McpServerCard`, `RobotsConfig`, `RobotsPolicy`, `ContentSignals`, `SitemapUrl`, `WebMcpTool`, `DiscoveryDocument`.
|
|
3039
3151
|
|
|
3040
3152
|
## MCP Server (`@buildbase/sdk/mcp`)
|
|
3041
3153
|
|
|
3042
|
-
|
|
3154
|
+
> **Full guide:** [`docs/MCP-AND-AGENT-READINESS.md`](https://unpkg.com/@buildbase/sdk/docs/MCP-AND-AGENT-READINESS.md).
|
|
3155
|
+
|
|
3156
|
+
Turn your app into a **live MCP server** so AI agents can operate it: the built-in tools expose your BuildBase state (workspaces, subscription, quotas, credits, feature flags, permissions) and you add your own product tools with zod schemas โ plus **resources** (context a host loads without tool calls) and **prompts** (user-facing workflow templates). Stateless Streamable HTTP (MCP 2025-11-25, negotiating down to 2024-11-05) โ pure functions plus a Web-standard `Request`/`Response` adapter, so it runs on Node 18+, edge, Deno, and Bun. Server-only, zero React; split from the core entry because it uses `zod` at runtime.
|
|
3157
|
+
|
|
3158
|
+
The fast path bundles the server + the whole discovery surface from one config:
|
|
3159
|
+
|
|
3160
|
+
```ts
|
|
3161
|
+
// lib/agent.ts
|
|
3162
|
+
import { createAgentStack, defineMcpTool } from '@buildbase/sdk/mcp';
|
|
3163
|
+
import { z } from 'zod';
|
|
3164
|
+
|
|
3165
|
+
export const agent = createAgentStack({
|
|
3166
|
+
serverUrl: process.env.NEXT_PUBLIC_BUILDBASE_SERVER_URL!,
|
|
3167
|
+
orgId: process.env.NEXT_PUBLIC_BUILDBASE_ORG_ID!,
|
|
3168
|
+
siteUrl: 'https://example.com',
|
|
3169
|
+
site: { name: 'Acme', description: 'Generate images from templates via API.' },
|
|
3170
|
+
secret: process.env.SYSTEM_SECRET!, // derives buildbaseAuth (verify + aud + sid decrypt)
|
|
3171
|
+
scopes: [{ name: 'render:execute', description: 'Render images' }],
|
|
3172
|
+
mcp: {
|
|
3173
|
+
builtinTools: 'readonly',
|
|
3174
|
+
tools: [
|
|
3175
|
+
defineMcpTool({
|
|
3176
|
+
name: 'generate_image',
|
|
3177
|
+
description: 'Render an image from a template',
|
|
3178
|
+
inputSchema: z.object({ templateId: z.string(), data: z.record(z.string(), z.any()) }),
|
|
3179
|
+
requiredScopes: ['render:execute'],
|
|
3180
|
+
execute: async (input, { bb, workspaceId }) => {
|
|
3181
|
+
await bb.usage.record(workspaceId!, { quotaSlug: 'images', quantity: 1 });
|
|
3182
|
+
return renderTemplate(input.templateId, input.data);
|
|
3183
|
+
},
|
|
3184
|
+
}),
|
|
3185
|
+
],
|
|
3186
|
+
},
|
|
3187
|
+
});
|
|
3188
|
+
|
|
3189
|
+
// app/api/mcp/route.ts
|
|
3190
|
+
export const { GET, POST, DELETE, OPTIONS } = agent.routes;
|
|
3191
|
+
```
|
|
3192
|
+
|
|
3193
|
+
Prefer the primitives (own token format, unified web+agent auth)? Wire `createMcpHandler` + `buildbaseAuth` directly โ `buildbaseAuth` does the full verify (HS256, RFC 8707 audience binding, `sid` decrypt) so you never hand-roll it:
|
|
3043
3194
|
|
|
3044
3195
|
```ts
|
|
3045
3196
|
// lib/mcp.ts
|
|
3046
3197
|
import BuildBase from '@buildbase/sdk';
|
|
3047
|
-
import { createMcpHandler, defineMcpTool,
|
|
3048
|
-
import { z } from 'zod';
|
|
3198
|
+
import { createMcpHandler, defineMcpTool, buildbaseAuth } from '@buildbase/sdk/mcp';
|
|
3049
3199
|
|
|
3050
3200
|
const buildbase = BuildBase({
|
|
3051
3201
|
serverUrl: process.env.BUILDBASE_URL!,
|
|
@@ -3054,56 +3204,35 @@ const buildbase = BuildBase({
|
|
|
3054
3204
|
|
|
3055
3205
|
export const mcp = createMcpHandler({
|
|
3056
3206
|
buildbase,
|
|
3057
|
-
serverInfo: { name: '
|
|
3058
|
-
auth: {
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
},
|
|
3065
|
-
// Unauthenticated requests get a 401 whose WWW-Authenticate header points
|
|
3066
|
-
// here, so agents bootstrap the whole OAuth discovery flow (RFC 9728).
|
|
3067
|
-
resourceMetadataUrl: 'https://imejis.io/.well-known/oauth-protected-resource',
|
|
3068
|
-
},
|
|
3069
|
-
builtinTools: 'readonly', // default; 'all' opts into the mutating tools
|
|
3207
|
+
serverInfo: { name: 'acme', version: '1.0.0' },
|
|
3208
|
+
auth: buildbaseAuth({
|
|
3209
|
+
secret: process.env.SYSTEM_SECRET!,
|
|
3210
|
+
resource: ['https://example.com/mcp', 'https://example.com/api/mcp'],
|
|
3211
|
+
requireAudience: true,
|
|
3212
|
+
}),
|
|
3213
|
+
builtinTools: 'readonly',
|
|
3070
3214
|
tools: [
|
|
3071
|
-
defineMcpTool(
|
|
3072
|
-
name: 'generate_image',
|
|
3073
|
-
description: 'Render an image from a template',
|
|
3074
|
-
inputSchema: z.object({ templateId: z.string(), data: z.record(z.string(), z.any()) }),
|
|
3075
|
-
annotations: { readOnlyHint: false },
|
|
3076
|
-
execute: async (input, { bb, workspaceId }) => {
|
|
3077
|
-
await bb.usage.record(workspaceId!, { quotaSlug: 'images', quantity: 1 });
|
|
3078
|
-
return renderTemplate(input.templateId, input.data);
|
|
3079
|
-
},
|
|
3080
|
-
}),
|
|
3215
|
+
/* defineMcpTool(...) */
|
|
3081
3216
|
],
|
|
3082
3217
|
});
|
|
3083
3218
|
```
|
|
3084
3219
|
|
|
3085
3220
|
```ts
|
|
3086
|
-
//
|
|
3087
|
-
import { mcp } from '@/lib/mcp';
|
|
3088
|
-
export const POST = (req: Request) => mcp.fetch(req);
|
|
3089
|
-
export const GET = (req: Request) => mcp.fetch(req); // 405 per spec (no SSE)
|
|
3090
|
-
export const DELETE = (req: Request) => mcp.fetch(req); // 405 (stateless)
|
|
3091
|
-
```
|
|
3092
|
-
|
|
3093
|
-
```ts
|
|
3094
|
-
// Express โ the pure form
|
|
3221
|
+
// Express / Fastify / Node โ the pure form
|
|
3095
3222
|
app.post('/mcp', async (req, res) => {
|
|
3096
3223
|
const r = await mcp.handle({ method: req.method, headers: req.headers, body: req.body });
|
|
3097
3224
|
res.status(r.status).set(r.headers).send(r.body);
|
|
3098
3225
|
});
|
|
3099
3226
|
```
|
|
3100
3227
|
|
|
3101
|
-
|
|
3228
|
+
> **Copy-paste recipes for every framework** โ Next.js (App + Pages Router), Express, Fastify, Hono, Bun, Deno, Cloudflare Workers, and React/SPA (WebMCP) โ plus the per-framework mint route and a production checklist: [`docs/MCP-AND-AGENT-READINESS.md`](https://unpkg.com/@buildbase/sdk/docs/MCP-AND-AGENT-READINESS.md#framework-recipes). Two adapters cover them all: `mcp.fetch(Request)` (web standard โ App Router, Hono, Bun, Deno, edge) and `mcp.handle({method,headers,body})` (pure โ Express, Fastify, Pages Router).
|
|
3229
|
+
|
|
3230
|
+
Advertise it via the server card so agents find the endpoint (the stack sets this automatically):
|
|
3102
3231
|
|
|
3103
3232
|
```ts
|
|
3104
3233
|
export const agentConfig: AgentReadyConfig = {
|
|
3105
3234
|
// ...
|
|
3106
|
-
mcpServerCard: mcp.serverCard({ endpoint: 'https://
|
|
3235
|
+
mcpServerCard: mcp.serverCard({ endpoint: 'https://example.com/api/mcp' }),
|
|
3107
3236
|
};
|
|
3108
3237
|
```
|
|
3109
3238
|
|
|
@@ -3132,6 +3261,62 @@ The agent acts as an authenticated app user โ it knows your app, not BuildBase
|
|
|
3132
3261
|
|
|
3133
3262
|
Want per-scope gating on top? Set `requiredScopes` on your own custom tools and return the granted `scopes` from `auth.verify` โ the handler hides a tool from `tools/list` (and refuses the call) when its `requiredScopes` aren't all granted. A token that carries **no** scopes sees only tools that require none (the built-ins), so a token minted without a `scope` claim can never unlock a scoped tool. Opt-in, using **your** scope names โ the SDK never imposes its own.
|
|
3134
3263
|
|
|
3264
|
+
### Rich tool results (images, links, mixed content)
|
|
3265
|
+
|
|
3266
|
+
Return any JSON-serializable value and the handler wraps it (string โ text block; object โ JSON text + `structuredContent`). For full control โ images, audio, resource links, mixed blocks, or the `isError` flag โ return a wire-shaped `McpToolResult` built with the exported helpers:
|
|
3267
|
+
|
|
3268
|
+
```ts
|
|
3269
|
+
import { mcpImage, mcpResourceLink, mcpText } from '@buildbase/sdk/mcp';
|
|
3270
|
+
|
|
3271
|
+
execute: async (input, ctx) => ({
|
|
3272
|
+
content: [
|
|
3273
|
+
mcpText('Revenue chart for Q2:'),
|
|
3274
|
+
mcpImage(chartPngBase64, 'image/png'),
|
|
3275
|
+
mcpResourceLink('https://app.example.com/reports/q2.pdf', 'q2-report'),
|
|
3276
|
+
],
|
|
3277
|
+
structuredContent: { totalCents: 1_299_00, currency: 'usd' },
|
|
3278
|
+
});
|
|
3279
|
+
```
|
|
3280
|
+
|
|
3281
|
+
### Resources & prompts
|
|
3282
|
+
|
|
3283
|
+
Both are **opt-in**, and `initialize` capabilities only advertise what you actually configured โ existing servers are byte-identical until you opt in.
|
|
3284
|
+
|
|
3285
|
+
```ts
|
|
3286
|
+
createMcpHandler({
|
|
3287
|
+
buildbase,
|
|
3288
|
+
// Lean built-in context catalog: buildbase://profile, buildbase://workspaces,
|
|
3289
|
+
// buildbase://workspace/{workspaceId}. Richer data stays tools-only.
|
|
3290
|
+
builtinResources: true,
|
|
3291
|
+
resources: [
|
|
3292
|
+
defineMcpResource({
|
|
3293
|
+
uri: 'app://docs/getting-started',
|
|
3294
|
+
name: 'getting-started',
|
|
3295
|
+
mimeType: 'text/markdown',
|
|
3296
|
+
read: () => gettingStartedMarkdown, // string | object (JSON) | { text } | { blob }
|
|
3297
|
+
}),
|
|
3298
|
+
],
|
|
3299
|
+
resourceTemplates: [
|
|
3300
|
+
defineMcpResourceTemplate({
|
|
3301
|
+
uriTemplate: 'app://orders/{orderId}',
|
|
3302
|
+
name: 'order',
|
|
3303
|
+
read: (params, _uri, ctx) => ctx.custom.db.order.find(params.orderId),
|
|
3304
|
+
}),
|
|
3305
|
+
],
|
|
3306
|
+
prompts: [
|
|
3307
|
+
defineMcpPrompt({
|
|
3308
|
+
name: 'analyze_usage',
|
|
3309
|
+
description: 'Review quota usage and suggest actions',
|
|
3310
|
+
arguments: [{ name: 'workspaceId', required: true }],
|
|
3311
|
+
get: async (args, ctx) =>
|
|
3312
|
+
`Review this workspace's quota usage:\n${JSON.stringify(await ctx.bb.usage.getAll(args.workspaceId))}`,
|
|
3313
|
+
}),
|
|
3314
|
+
],
|
|
3315
|
+
});
|
|
3316
|
+
```
|
|
3317
|
+
|
|
3318
|
+
`requiredScopes` gates resources and prompts exactly like tools. Deliberately out of scope (the stateless transport has no push channel): resource subscriptions, `listChanged`, sampling, elicitation.
|
|
3319
|
+
|
|
3135
3320
|
### Production hardening
|
|
3136
3321
|
|
|
3137
3322
|
The handler ships with safe defaults; these are the knobs to review before going live:
|
|
@@ -3148,7 +3333,7 @@ export const mcp = createMcpHandler({
|
|
|
3148
3333
|
buildbase,
|
|
3149
3334
|
auth: { verify, resourceMetadataUrl },
|
|
3150
3335
|
builtinTools: { include: ['get_quota_usage', 'record_usage'] },
|
|
3151
|
-
allowedOrigins: ['https://app.
|
|
3336
|
+
allowedOrigins: ['https://app.example.com'],
|
|
3152
3337
|
maxRequestBytes: 512 * 1024,
|
|
3153
3338
|
rateLimit: async auth => ({ ok: await limiter.allow(auth?.userId), retryAfter: 30 }),
|
|
3154
3339
|
onError: (err, ctx) => logger.error({ err, ...ctx }, 'mcp error'),
|
|
@@ -3187,21 +3372,25 @@ The SDK accelerates; it never gates. Everything BuildBase-specific is optional:
|
|
|
3187
3372
|
|
|
3188
3373
|
### Auth in one line of plumbing
|
|
3189
3374
|
|
|
3190
|
-
The OAuth2 app-bridge flow
|
|
3375
|
+
The OAuth2 app-bridge flow mints your app's Bearer tokens (see [OAuth2 App Bridge](#oauth2-app-bridge)). Use the presets and neither the mint nor the verify is hand-rolled โ the per-user BuildBase session rides as an **encrypted `sid`** claim (JWTs are signed, not encrypted), never stored, expiring with the token:
|
|
3191
3376
|
|
|
3192
3377
|
```ts
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
}),
|
|
3378
|
+
// mint (applicationTokenUrl) โ HS256 with your secret, aud from the grant, encrypted sid
|
|
3379
|
+
import { mintAgentToken } from '@buildbase/sdk';
|
|
3380
|
+
mintToken: (claims) => mintAgentToken({ claims, secret: process.env.SYSTEM_SECRET! }),
|
|
3381
|
+
|
|
3382
|
+
// verify (MCP server) โ HS256 + RFC 8707 audience + sid decrypt โ McpAuthInfo
|
|
3383
|
+
import { buildbaseAuth } from '@buildbase/sdk/mcp';
|
|
3384
|
+
auth: buildbaseAuth({ secret: process.env.SYSTEM_SECRET!, resource: 'https://example.com/mcp' }),
|
|
3200
3385
|
```
|
|
3201
3386
|
|
|
3202
|
-
Set `auth: false` to disable auth
|
|
3387
|
+
Bringing your own token format? `auth.verify(token, req) โ McpAuthInfo` accepts any Bearer token; set `auth.resourceMetadataUrl` so 401s carry the RFC 9728 challenge. Set `auth: false` to disable auth (local dev only). Debug audience mismatches with `MCP_AUTH_DEBUG=1`. Test with the MCP Inspector: `npx @modelcontextprotocol/inspector --cli http://localhost:3000/api/mcp --transport http --header "Authorization: Bearer <token>" --method tools/list`.
|
|
3388
|
+
|
|
3389
|
+
**Exports:** `createAgentStack`, `createMcpHandler`, `defineMcpTool`, `builtinMcpTools`, `selectBuiltinTools`, `buildbaseAuth`, `mintAgentToken`, `createSessionRefCrypto`, plus re-exported auth helpers (`signClientJwt`, `verifyClientJwt`, `extractBearerToken`, `bearerChallenge`, `AppBridgeError`). **Types:** `AgentStack`, `AgentStackConfig`, `CreateMcpHandlerConfig`, `McpHandler`, `McpToolDefinition`, `McpToolContext`, `McpAuthInfo`, `McpHttpRequest`, `McpHttpResponse`, `McpToolAnnotations`, `McpBuildBaseClient`, `BuiltinMcpToolName`, `BuildBaseAuthOptions`, `MintAgentTokenOptions`, `SessionRefCrypto`, `McpServerCard`, `ScopedActions`.
|
|
3390
|
+
|
|
3391
|
+
### Scopes & resources โ restrict or open up
|
|
3203
3392
|
|
|
3204
|
-
**
|
|
3393
|
+
Scopes and resources are **app-owned** โ the shared BuildBase authorization server stays scope/resource-agnostic. Scopes have three levers: **declare** a catalog (`scopes: [{ name, description }]` โ `scopes_supported` in your own RFC 9728 protected-resource metadata), **gate per tool** (`requiredScopes` on a custom tool โ hidden/refused unless the token carries them), and the **floor** (every tool runs under the user's BuildBase session, so the platform enforces the user's real permissions โ an agent never exceeds the user). Resources have two: **declare** `protectedResources` (RFC 9728) and **bind** the token audience via `buildbaseAuth({ resource, requireAudience })` (RFC 8707) โ the audience check at your resource is the whole gate. Built-ins are restricted with `builtinTools`, not scopes. Public clients (Claude Desktop, Cursor, Inspector) self-register with PKCE and no secret. Full model: [the guide](https://unpkg.com/@buildbase/sdk/docs/MCP-AND-AGENT-READINESS.md#scopes--resources-restrict-or-open-up).
|
|
3205
3394
|
|
|
3206
3395
|
## OAuth2 App Bridge
|
|
3207
3396
|
|
|
@@ -3212,6 +3401,8 @@ BuildBase runs the full OAuth2 authorization flow (login, consent, code, PKCE, r
|
|
|
3212
3401
|
|
|
3213
3402
|
Both requests carry `Authorization: Bearer <JWT>`, an HS256 JWT signed with your client secret. This toolkit verifies those requests (timing-safe, no `alg` confusion) and shapes the exact response body the platform expects, so you write only the part that's yours: minting/invalidating a token in your own format.
|
|
3214
3403
|
|
|
3404
|
+
> For the common case, `mintToken: (claims) => mintAgentToken({ claims, secret })` is the preset โ it signs HS256 with your secret, binds `aud` to the granted RFC 8707 resource, and embeds the encrypted `sid`. The examples below show the hand-rolled equivalent, for apps that mint their own token format (e.g. one shared with their web sessions). See [the guide](https://unpkg.com/@buildbase/sdk/docs/MCP-AND-AGENT-READINESS.md#auth-minting--verifying-the-token).
|
|
3405
|
+
|
|
3215
3406
|
```ts
|
|
3216
3407
|
// applicationTokenUrl handler (Next.js Pages Router)
|
|
3217
3408
|
import { handleAppTokenRequest } from '@buildbase/sdk';
|
|
@@ -3251,7 +3442,7 @@ When an agent calls your protected API without a valid token, reply with an RFC
|
|
|
3251
3442
|
import { bearerChallenge } from '@buildbase/sdk';
|
|
3252
3443
|
|
|
3253
3444
|
const c = bearerChallenge({
|
|
3254
|
-
resourceMetadataUrl: 'https://
|
|
3445
|
+
resourceMetadataUrl: 'https://example.com/.well-known/oauth-protected-resource',
|
|
3255
3446
|
error: 'invalid_token',
|
|
3256
3447
|
});
|
|
3257
3448
|
res.writeHead(c.status, c.headers).end(c.body);
|
|
@@ -3295,7 +3486,9 @@ return res.json({ id: claims.id, email: claims.email /* โฆ */ });
|
|
|
3295
3486
|
|
|
3296
3487
|
The one-call handlers cover the common path; the underlying pieces are exported for custom flows: `verifyAppTokenRequest` / `verifyAppRevokeRequest` (verify โ claims), `verifyClientJwt`, `extractBearerToken`, and the response builders `appTokenSuccess` / `appTokenFailure`. Verification throws `AppBridgeError` (with a machine-readable `code` like `invalid_signature`, `token_expired`, `invalid_algorithm`). HMAC verification uses the SDK's dependency-free pure-JS HMAC-SHA256, so it behaves identically under ESM, CJS, a bundler, or bare Node.
|
|
3297
3488
|
|
|
3298
|
-
**
|
|
3489
|
+
**Presets** (also on the core entry): `mintAgentToken` (the whole mint โ HS256 + `aud` + encrypted `sid`), `buildbaseAuth` (the whole verify), `createSessionRefCrypto` (the `sid` AES-256-GCM crypto). The platform relays the minted token but never sees your secret โ it can neither forge nor decode your tokens.
|
|
3490
|
+
|
|
3491
|
+
**Exports:** `handleAppTokenRequest`, `handleAppRevokeRequest`, `verifyAppTokenRequest`, `verifyAppRevokeRequest`, `verifyClientJwt`, `signClientJwt`, `extractBearerToken`, `bearerChallenge`, `appTokenSuccess`, `appTokenFailure`, `mintAgentToken`, `buildbaseAuth`, `createSessionRefCrypto`, `AppBridgeError`. **Types:** `AppTokenRequestClaims`, `AppRevokeRequestClaims`, `AppTokenResult`, `AppTokenResponseBody`, `HandlerResult`, `VerifyClientJwtOptions`, `BuildBaseAuthOptions`, `MintAgentTokenOptions`, `SessionRefCrypto`.
|
|
3299
3492
|
|
|
3300
3493
|
## ๐ค Contributing
|
|
3301
3494
|
|