@buildbase/sdk 0.0.54 โ 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 +8 -4
- package/CHANGELOG.md +53 -0
- package/LICENSE +21 -0
- package/README.md +203 -45
- package/dist/css/styles.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +196 -88
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.d.ts +226 -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 +460 -142
- 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 +14 -3
- package/dist/react/chunks/SettingsDialog-B28WJsAr.cjs +0 -2
- package/dist/react/chunks/SettingsDialog-blhppbNm.mjs +0 -2
- package/dist/react/chunks/SubscriptionDialog-BTWVX3cH.mjs +0 -2
- package/dist/react/chunks/SubscriptionDialog-QKmwvJU1.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-6QJCOE-D.cjs +0 -2
- package/dist/react/chunks/react-CC6T7Zky.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,11 +3037,11 @@ app.post('/webhooks/buildbase', (req, res) => {
|
|
|
2935
3037
|
|
|
2936
3038
|
## Agent Readiness (Discovery)
|
|
2937
3039
|
|
|
2938
|
-
> **Full guide:** [`docs/MCP-AND-AGENT-READINESS.md`](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.
|
|
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.
|
|
2939
3041
|
|
|
2940
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? }`).
|
|
2941
3043
|
|
|
2942
|
-
The fast path is [`createAgentStack`](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`:
|
|
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`:
|
|
2943
3045
|
|
|
2944
3046
|
```ts
|
|
2945
3047
|
// lib/agent-ready.ts
|
|
@@ -2948,17 +3050,17 @@ import { resolveAgentPath, type AgentReadyConfig } from '@buildbase/sdk';
|
|
|
2948
3050
|
export const agentConfig: AgentReadyConfig = {
|
|
2949
3051
|
serverUrl: process.env.BUILDBASE_URL!,
|
|
2950
3052
|
orgId: process.env.BUILDBASE_ORG_ID!,
|
|
2951
|
-
siteUrl: 'https://
|
|
2952
|
-
site: { name: '
|
|
3053
|
+
siteUrl: 'https://example.com',
|
|
3054
|
+
site: { name: 'Acme', description: 'Generate images from templates via API.' },
|
|
2953
3055
|
// Discovery content is YOURS โ defined here in code, not in the platform:
|
|
2954
3056
|
scopes: [
|
|
2955
3057
|
{ name: 'designs:read', description: 'View designs' },
|
|
2956
3058
|
{ name: 'render:execute', description: 'Render images' },
|
|
2957
3059
|
],
|
|
2958
|
-
llmsTxt: '#
|
|
3060
|
+
llmsTxt: '# Acme\n\n> Generate images from templates via API.',
|
|
2959
3061
|
robots: { contentSignals: { search: true, aiInput: true, aiTrain: false } },
|
|
2960
3062
|
sitemap: { urls: ['/', '/pricing'] }, // omit if you already generate sitemaps
|
|
2961
|
-
protectedResources: [{ resource: 'https://
|
|
3063
|
+
protectedResources: [{ resource: 'https://example.com/mcp' }], // scopes inherited from catalog
|
|
2962
3064
|
// skills, security, apiCatalog, mcpServerCard, a2aCard, extraPaths โ all optional
|
|
2963
3065
|
};
|
|
2964
3066
|
|
|
@@ -3049,9 +3151,9 @@ Clean split, no duplication: **your app owns discovery content, the platform own
|
|
|
3049
3151
|
|
|
3050
3152
|
## MCP Server (`@buildbase/sdk/mcp`)
|
|
3051
3153
|
|
|
3052
|
-
> **Full guide:** [`docs/MCP-AND-AGENT-READINESS.md`](docs/MCP-AND-AGENT-READINESS.md).
|
|
3154
|
+
> **Full guide:** [`docs/MCP-AND-AGENT-READINESS.md`](https://unpkg.com/@buildbase/sdk/docs/MCP-AND-AGENT-READINESS.md).
|
|
3053
3155
|
|
|
3054
|
-
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. Stateless Streamable HTTP (MCP 2025-
|
|
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.
|
|
3055
3157
|
|
|
3056
3158
|
The fast path bundles the server + the whole discovery surface from one config:
|
|
3057
3159
|
|
|
@@ -3063,8 +3165,8 @@ import { z } from 'zod';
|
|
|
3063
3165
|
export const agent = createAgentStack({
|
|
3064
3166
|
serverUrl: process.env.NEXT_PUBLIC_BUILDBASE_SERVER_URL!,
|
|
3065
3167
|
orgId: process.env.NEXT_PUBLIC_BUILDBASE_ORG_ID!,
|
|
3066
|
-
siteUrl: 'https://
|
|
3067
|
-
site: { name: '
|
|
3168
|
+
siteUrl: 'https://example.com',
|
|
3169
|
+
site: { name: 'Acme', description: 'Generate images from templates via API.' },
|
|
3068
3170
|
secret: process.env.SYSTEM_SECRET!, // derives buildbaseAuth (verify + aud + sid decrypt)
|
|
3069
3171
|
scopes: [{ name: 'render:execute', description: 'Render images' }],
|
|
3070
3172
|
mcp: {
|
|
@@ -3102,10 +3204,10 @@ const buildbase = BuildBase({
|
|
|
3102
3204
|
|
|
3103
3205
|
export const mcp = createMcpHandler({
|
|
3104
3206
|
buildbase,
|
|
3105
|
-
serverInfo: { name: '
|
|
3207
|
+
serverInfo: { name: 'acme', version: '1.0.0' },
|
|
3106
3208
|
auth: buildbaseAuth({
|
|
3107
3209
|
secret: process.env.SYSTEM_SECRET!,
|
|
3108
|
-
resource: ['https://
|
|
3210
|
+
resource: ['https://example.com/mcp', 'https://example.com/api/mcp'],
|
|
3109
3211
|
requireAudience: true,
|
|
3110
3212
|
}),
|
|
3111
3213
|
builtinTools: 'readonly',
|
|
@@ -3123,14 +3225,14 @@ app.post('/mcp', async (req, res) => {
|
|
|
3123
3225
|
});
|
|
3124
3226
|
```
|
|
3125
3227
|
|
|
3126
|
-
> **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`](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).
|
|
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).
|
|
3127
3229
|
|
|
3128
3230
|
Advertise it via the server card so agents find the endpoint (the stack sets this automatically):
|
|
3129
3231
|
|
|
3130
3232
|
```ts
|
|
3131
3233
|
export const agentConfig: AgentReadyConfig = {
|
|
3132
3234
|
// ...
|
|
3133
|
-
mcpServerCard: mcp.serverCard({ endpoint: 'https://
|
|
3235
|
+
mcpServerCard: mcp.serverCard({ endpoint: 'https://example.com/api/mcp' }),
|
|
3134
3236
|
};
|
|
3135
3237
|
```
|
|
3136
3238
|
|
|
@@ -3159,6 +3261,62 @@ The agent acts as an authenticated app user โ it knows your app, not BuildBase
|
|
|
3159
3261
|
|
|
3160
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.
|
|
3161
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
|
+
|
|
3162
3320
|
### Production hardening
|
|
3163
3321
|
|
|
3164
3322
|
The handler ships with safe defaults; these are the knobs to review before going live:
|
|
@@ -3175,7 +3333,7 @@ export const mcp = createMcpHandler({
|
|
|
3175
3333
|
buildbase,
|
|
3176
3334
|
auth: { verify, resourceMetadataUrl },
|
|
3177
3335
|
builtinTools: { include: ['get_quota_usage', 'record_usage'] },
|
|
3178
|
-
allowedOrigins: ['https://app.
|
|
3336
|
+
allowedOrigins: ['https://app.example.com'],
|
|
3179
3337
|
maxRequestBytes: 512 * 1024,
|
|
3180
3338
|
rateLimit: async auth => ({ ok: await limiter.allow(auth?.userId), retryAfter: 30 }),
|
|
3181
3339
|
onError: (err, ctx) => logger.error({ err, ...ctx }, 'mcp error'),
|
|
@@ -3223,7 +3381,7 @@ mintToken: (claims) => mintAgentToken({ claims, secret: process.env.SYSTEM_SECRE
|
|
|
3223
3381
|
|
|
3224
3382
|
// verify (MCP server) โ HS256 + RFC 8707 audience + sid decrypt โ McpAuthInfo
|
|
3225
3383
|
import { buildbaseAuth } from '@buildbase/sdk/mcp';
|
|
3226
|
-
auth: buildbaseAuth({ secret: process.env.SYSTEM_SECRET!, resource: 'https://
|
|
3384
|
+
auth: buildbaseAuth({ secret: process.env.SYSTEM_SECRET!, resource: 'https://example.com/mcp' }),
|
|
3227
3385
|
```
|
|
3228
3386
|
|
|
3229
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`.
|
|
@@ -3232,7 +3390,7 @@ Bringing your own token format? `auth.verify(token, req) โ McpAuthInfo` accept
|
|
|
3232
3390
|
|
|
3233
3391
|
### Scopes & resources โ restrict or open up
|
|
3234
3392
|
|
|
3235
|
-
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](docs/MCP-AND-AGENT-READINESS.md#scopes--resources-restrict-or-open-up).
|
|
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).
|
|
3236
3394
|
|
|
3237
3395
|
## OAuth2 App Bridge
|
|
3238
3396
|
|
|
@@ -3243,7 +3401,7 @@ BuildBase runs the full OAuth2 authorization flow (login, consent, code, PKCE, r
|
|
|
3243
3401
|
|
|
3244
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.
|
|
3245
3403
|
|
|
3246
|
-
> 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](docs/MCP-AND-AGENT-READINESS.md#auth-minting--verifying-the-token).
|
|
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).
|
|
3247
3405
|
|
|
3248
3406
|
```ts
|
|
3249
3407
|
// applicationTokenUrl handler (Next.js Pages Router)
|
|
@@ -3284,7 +3442,7 @@ When an agent calls your protected API without a valid token, reply with an RFC
|
|
|
3284
3442
|
import { bearerChallenge } from '@buildbase/sdk';
|
|
3285
3443
|
|
|
3286
3444
|
const c = bearerChallenge({
|
|
3287
|
-
resourceMetadataUrl: 'https://
|
|
3445
|
+
resourceMetadataUrl: 'https://example.com/.well-known/oauth-protected-resource',
|
|
3288
3446
|
error: 'invalid_token',
|
|
3289
3447
|
});
|
|
3290
3448
|
res.writeHead(c.status, c.headers).end(c.body);
|