@01.software/cli 0.7.1 → 0.8.0
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/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/mcp/{chunk-3ZSKJM43.js → chunk-45ZCPS57.js} +340 -292
- package/dist/mcp/chunk-45ZCPS57.js.map +1 -0
- package/dist/mcp/http.js +261 -120
- package/dist/mcp/http.js.map +1 -1
- package/dist/mcp/stdio.js +1 -1
- package/dist/mcp/vercel.js +602 -415
- package/package.json +3 -3
- package/dist/mcp/chunk-3ZSKJM43.js.map +0 -1
|
@@ -7,40 +7,207 @@ import { z } from "zod";
|
|
|
7
7
|
// src/lib/request-context.ts
|
|
8
8
|
import { AsyncLocalStorage } from "async_hooks";
|
|
9
9
|
var requestContext = new AsyncLocalStorage();
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
function tenantAuthContext() {
|
|
11
|
+
return requestContext.getStore()?.auth ?? null;
|
|
12
|
+
}
|
|
13
|
+
function hasRequestContext() {
|
|
14
|
+
return requestContext.getStore() !== void 0;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
// src/lib/client.ts
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
import {
|
|
19
|
+
CollectionClient,
|
|
20
|
+
CommunityClient,
|
|
21
|
+
ModerationApi,
|
|
22
|
+
ServerCommerceClient,
|
|
23
|
+
createServerClient
|
|
24
|
+
} from "@01.software/sdk";
|
|
25
|
+
|
|
26
|
+
// src/service-auth.ts
|
|
27
|
+
import { createPrivateKey, randomUUID, sign as signBytes } from "crypto";
|
|
28
|
+
import {
|
|
29
|
+
MCP_CONSOLE_SERVICE_AUDIENCE,
|
|
30
|
+
MCP_CONSOLE_SERVICE_SCOPE,
|
|
31
|
+
MCP_OAUTH_ISSUER,
|
|
32
|
+
MCP_SERVICE_TOKEN_LIFETIME_SECONDS,
|
|
33
|
+
MCP_TENANT_CLAIM,
|
|
34
|
+
MCP_TENANT_ROLE_CLAIM
|
|
35
|
+
} from "@01.software/auth-contracts";
|
|
36
|
+
var KEYSET_ENV = "MCP_SERVICE_KEYSET";
|
|
37
|
+
function assertProductionKeysetUse(source) {
|
|
38
|
+
const vercelEnv = process.env.VERCEL_ENV;
|
|
39
|
+
if (vercelEnv && vercelEnv !== "production") {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`${source} is only allowed in production Vercel deployments; non-production MCP service auth needs environment-specific issuer, audience, JWKS URI, and key material`
|
|
42
|
+
);
|
|
26
43
|
}
|
|
27
|
-
|
|
28
|
-
|
|
44
|
+
}
|
|
45
|
+
function parsePrivateJwk() {
|
|
46
|
+
const keyset = signingKeyset();
|
|
47
|
+
const jwk = keyset.current;
|
|
48
|
+
const source = keyset.source;
|
|
49
|
+
if (typeof jwk.d !== "string" || jwk.d.length === 0) {
|
|
50
|
+
throw new Error(`${source} current key must be a private JWK`);
|
|
29
51
|
}
|
|
30
|
-
if (
|
|
31
|
-
|
|
52
|
+
if (typeof jwk.kid !== "string" || jwk.kid.length === 0) {
|
|
53
|
+
throw new Error(`${source} must include kid`);
|
|
54
|
+
}
|
|
55
|
+
return jwk;
|
|
56
|
+
}
|
|
57
|
+
function signingKeyset() {
|
|
58
|
+
const raw = process.env[KEYSET_ENV];
|
|
59
|
+
const source = KEYSET_ENV;
|
|
60
|
+
if (raw) assertProductionKeysetUse(source);
|
|
61
|
+
const parsed = (() => {
|
|
62
|
+
if (!raw) return null;
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(raw);
|
|
65
|
+
} catch {
|
|
66
|
+
throw new Error(`${KEYSET_ENV} is invalid JSON`);
|
|
67
|
+
}
|
|
68
|
+
})();
|
|
69
|
+
if (!parsed) throw new Error("MCP service JWT signing key is not configured");
|
|
70
|
+
const keys = Array.isArray(parsed.keys) ? parsed.keys : [parsed];
|
|
71
|
+
if (keys.length === 0 || keys.length > 2) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`${source} must contain one current key and at most one previous key`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const currentKid = parsed.current_kid;
|
|
77
|
+
if (typeof currentKid !== "string" && keys.length > 1) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`${source} must include current_kid when multiple keys are present`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const current = typeof currentKid === "string" ? keys.find((key) => key.kid === currentKid) : keys[0];
|
|
83
|
+
if (!current) throw new Error(`${source} current_kid is not in keys`);
|
|
84
|
+
return { current, keys, source };
|
|
85
|
+
}
|
|
86
|
+
function algForJwk(jwk) {
|
|
87
|
+
if (jwk.kty === "RSA") return "RS256";
|
|
88
|
+
if (jwk.kty === "EC" && jwk.crv === "P-256") return "ES256";
|
|
89
|
+
throw new Error("MCP service JWT signing key must be RSA or P-256 EC");
|
|
90
|
+
}
|
|
91
|
+
function toPublicJwk(jwk) {
|
|
92
|
+
const {
|
|
93
|
+
d: _d,
|
|
94
|
+
p: _p,
|
|
95
|
+
q: _q,
|
|
96
|
+
dp: _dp,
|
|
97
|
+
dq: _dq,
|
|
98
|
+
qi: _qi,
|
|
99
|
+
oth: _oth,
|
|
100
|
+
...publicJwk
|
|
101
|
+
} = jwk;
|
|
102
|
+
return {
|
|
103
|
+
...publicJwk,
|
|
104
|
+
alg: typeof publicJwk.alg === "string" ? publicJwk.alg : algForJwk(jwk),
|
|
105
|
+
use: "sig"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function base64urlJson(value) {
|
|
109
|
+
return Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
110
|
+
}
|
|
111
|
+
function apiScopesFor(context) {
|
|
112
|
+
return context.scopes.includes("mcp:write") ? ["read", "write"] : ["read"];
|
|
113
|
+
}
|
|
114
|
+
function mcpServicePublicJwks() {
|
|
115
|
+
const keyset = signingKeyset();
|
|
116
|
+
const keys = /* @__PURE__ */ new Map();
|
|
117
|
+
for (const jwk of keyset.keys.map(toPublicJwk)) {
|
|
118
|
+
if (typeof jwk.kid === "string" && jwk.kid.length > 0) {
|
|
119
|
+
keys.set(jwk.kid, jwk);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { keys: [...keys.values()] };
|
|
123
|
+
}
|
|
124
|
+
function signMcpServiceToken(context) {
|
|
125
|
+
if (!context.principalId) {
|
|
126
|
+
throw new Error("MCP OAuth principal is required for Console service auth");
|
|
127
|
+
}
|
|
128
|
+
const jwk = parsePrivateJwk();
|
|
129
|
+
const alg = algForJwk(jwk);
|
|
130
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
131
|
+
const payload = {
|
|
132
|
+
iss: MCP_OAUTH_ISSUER,
|
|
133
|
+
aud: MCP_CONSOLE_SERVICE_AUDIENCE,
|
|
134
|
+
iat: now,
|
|
135
|
+
nbf: now,
|
|
136
|
+
exp: now + MCP_SERVICE_TOKEN_LIFETIME_SECONDS,
|
|
137
|
+
jti: randomUUID(),
|
|
138
|
+
sub: context.principalId,
|
|
139
|
+
act: {
|
|
140
|
+
sub: context.principalId,
|
|
141
|
+
tenant_id: context.tenantId
|
|
142
|
+
},
|
|
143
|
+
[MCP_TENANT_CLAIM]: context.tenantId,
|
|
144
|
+
[MCP_TENANT_ROLE_CLAIM]: context.tenantRole,
|
|
145
|
+
scope: MCP_CONSOLE_SERVICE_SCOPE,
|
|
146
|
+
api_scopes: apiScopesFor(context),
|
|
147
|
+
mcp_scopes: context.scopes
|
|
148
|
+
};
|
|
149
|
+
const header = { alg, kid: jwk.kid, typ: "JWT" };
|
|
150
|
+
const encodedHeader = base64urlJson(header);
|
|
151
|
+
const encodedPayload = base64urlJson(payload);
|
|
152
|
+
const signingInput = `${encodedHeader}.${encodedPayload}`;
|
|
153
|
+
const key = createPrivateKey({ key: jwk, format: "jwk" });
|
|
154
|
+
const signature = alg === "RS256" ? signBytes("RSA-SHA256", Buffer.from(signingInput), key) : signBytes("SHA256", Buffer.from(signingInput), {
|
|
155
|
+
key,
|
|
156
|
+
dsaEncoding: "ieee-p1363"
|
|
157
|
+
});
|
|
158
|
+
return `${signingInput}.${signature.toString("base64url")}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/lib/client.ts
|
|
162
|
+
var MISSING_HTTP_AUTH_CONTEXT_ERROR = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
|
|
163
|
+
function getClient() {
|
|
164
|
+
const oauthContext = tenantAuthContext();
|
|
165
|
+
if (oauthContext) {
|
|
166
|
+
const serviceToken = signMcpServiceToken(oauthContext);
|
|
167
|
+
const client = {
|
|
168
|
+
lastRequestId: null,
|
|
169
|
+
commerce: void 0,
|
|
170
|
+
collections: void 0,
|
|
171
|
+
community: void 0
|
|
172
|
+
};
|
|
173
|
+
const onRequestId = (id) => {
|
|
174
|
+
client.lastRequestId = id;
|
|
175
|
+
};
|
|
176
|
+
client.commerce = new ServerCommerceClient({
|
|
177
|
+
secretKey: serviceToken,
|
|
178
|
+
onRequestId
|
|
179
|
+
});
|
|
180
|
+
client.collections = new CollectionClient(
|
|
181
|
+
"",
|
|
182
|
+
serviceToken,
|
|
183
|
+
void 0,
|
|
184
|
+
void 0,
|
|
185
|
+
onRequestId
|
|
186
|
+
);
|
|
187
|
+
const community = new CommunityClient({ secretKey: serviceToken });
|
|
188
|
+
const moderation = new ModerationApi({ secretKey: serviceToken, onRequestId });
|
|
189
|
+
client.community = Object.assign(community, {
|
|
190
|
+
moderation: {
|
|
191
|
+
banCustomer: moderation.banCustomer.bind(moderation),
|
|
192
|
+
unbanCustomer: moderation.unbanCustomer.bind(moderation)
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
return client;
|
|
32
196
|
}
|
|
197
|
+
if (hasRequestContext()) throw new Error(MISSING_HTTP_AUTH_CONTEXT_ERROR);
|
|
198
|
+
const secretKey = process.env.SOFTWARE_SECRET_KEY;
|
|
199
|
+
const publishableKey = process.env.SOFTWARE_PUBLISHABLE_KEY || process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY;
|
|
33
200
|
if (!secretKey) {
|
|
34
201
|
throw new Error(
|
|
35
|
-
"Authentication required.
|
|
202
|
+
"Authentication required. Set SOFTWARE_SECRET_KEY for stdio transport."
|
|
36
203
|
);
|
|
37
204
|
}
|
|
38
205
|
if (!secretKey.startsWith("sk01_") && !secretKey.startsWith("pat01_")) {
|
|
39
|
-
throw new Error("Invalid
|
|
206
|
+
throw new Error("Invalid SOFTWARE_SECRET_KEY format. Expected sk01_ or pat01_ token.");
|
|
40
207
|
}
|
|
41
208
|
if (!publishableKey) {
|
|
42
209
|
throw new Error(
|
|
43
|
-
"publishableKey is required.
|
|
210
|
+
"publishableKey is required. Set SOFTWARE_PUBLISHABLE_KEY for stdio transport. It is used for rate limiting and monthly quota enforcement via the edge proxy."
|
|
44
211
|
);
|
|
45
212
|
}
|
|
46
213
|
return createServerClient({
|
|
@@ -1084,49 +1251,40 @@ import { COLLECTIONS as COLLECTIONS8 } from "@01.software/sdk";
|
|
|
1084
1251
|
import { createHash } from "crypto";
|
|
1085
1252
|
var BASE_URL = process.env.SOFTWARE_API_URL || "http://localhost:3000";
|
|
1086
1253
|
var TIMEOUT_MS = 5e3;
|
|
1254
|
+
var MISSING_HTTP_AUTH_CONTEXT_ERROR2 = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
|
|
1087
1255
|
function resolveAuthHeaderContext() {
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
publishableKey: h?.["x-publishable-key"] ?? h?.["x-client-key"]
|
|
1256
|
+
const oauthContext = tenantAuthContext();
|
|
1257
|
+
if (oauthContext) {
|
|
1258
|
+
return {
|
|
1259
|
+
apiKey: signMcpServiceToken(oauthContext),
|
|
1260
|
+
mode: "oauth"
|
|
1094
1261
|
};
|
|
1095
|
-
} catch {
|
|
1096
1262
|
}
|
|
1263
|
+
if (hasRequestContext()) throw new Error(MISSING_HTTP_AUTH_CONTEXT_ERROR2);
|
|
1097
1264
|
return {
|
|
1098
|
-
apiKey:
|
|
1099
|
-
|
|
1265
|
+
apiKey: process.env.SOFTWARE_SECRET_KEY,
|
|
1266
|
+
mode: "stdio",
|
|
1267
|
+
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY ?? process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY
|
|
1100
1268
|
};
|
|
1101
1269
|
}
|
|
1102
1270
|
function resolveApiKey() {
|
|
1103
1271
|
const { apiKey } = resolveAuthHeaderContext();
|
|
1104
1272
|
if (!apiKey || typeof apiKey !== "string") {
|
|
1105
1273
|
throw new Error(
|
|
1106
|
-
"Authentication required.
|
|
1274
|
+
"Authentication required. Set SOFTWARE_SECRET_KEY for stdio transport."
|
|
1107
1275
|
);
|
|
1108
1276
|
}
|
|
1109
1277
|
return apiKey;
|
|
1110
1278
|
}
|
|
1111
|
-
function hashKey(apiKey) {
|
|
1112
|
-
return createHash("sha256").update(apiKey).digest("hex");
|
|
1113
|
-
}
|
|
1114
|
-
function resolveAuthCacheKey(apiKey) {
|
|
1115
|
-
const { publishableKey } = resolveAuthHeaderContext();
|
|
1116
|
-
return hashKey(
|
|
1117
|
-
JSON.stringify({
|
|
1118
|
-
apiKey,
|
|
1119
|
-
publishableKey: publishableKey ?? ""
|
|
1120
|
-
})
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
1279
|
function buildAuthHeaders(apiKey) {
|
|
1124
|
-
const { publishableKey } = resolveAuthHeaderContext();
|
|
1125
|
-
const
|
|
1280
|
+
const { mode, publishableKey } = resolveAuthHeaderContext();
|
|
1281
|
+
const headers = {
|
|
1126
1282
|
Authorization: `Bearer ${apiKey}`
|
|
1127
1283
|
};
|
|
1128
|
-
if (
|
|
1129
|
-
|
|
1284
|
+
if (mode === "stdio" && publishableKey) {
|
|
1285
|
+
headers["X-Publishable-Key"] = publishableKey;
|
|
1286
|
+
}
|
|
1287
|
+
return headers;
|
|
1130
1288
|
}
|
|
1131
1289
|
function extractErrorMessage(body) {
|
|
1132
1290
|
if (!body || typeof body !== "object") return void 0;
|
|
@@ -1220,37 +1378,18 @@ async function getCollectionSchemaTool({
|
|
|
1220
1378
|
import { z as z28 } from "zod";
|
|
1221
1379
|
|
|
1222
1380
|
// src/lib/tenant-context.ts
|
|
1223
|
-
var TENANT_CONTEXT_CACHE_TTL_MS = 6e4;
|
|
1224
|
-
var cache = /* @__PURE__ */ new Map();
|
|
1225
1381
|
function getTenantContextPath(includeCounts) {
|
|
1226
1382
|
return includeCounts ? "/api/tenants/context?counts=true" : "/api/tenants/context";
|
|
1227
1383
|
}
|
|
1228
|
-
function getCachedTenantContext(cacheKey) {
|
|
1229
|
-
const cached = cache.get(cacheKey);
|
|
1230
|
-
if (!cached || cached.expiry <= Date.now()) return void 0;
|
|
1231
|
-
return cached.data;
|
|
1232
|
-
}
|
|
1233
1384
|
async function getTenantContext(includeCounts = false) {
|
|
1234
1385
|
const apiKey = resolveApiKey();
|
|
1235
|
-
const cacheKey = resolveAuthCacheKey(apiKey);
|
|
1236
|
-
if (!includeCounts) {
|
|
1237
|
-
const cached = getCachedTenantContext(cacheKey);
|
|
1238
|
-
if (cached) return cached;
|
|
1239
|
-
}
|
|
1240
1386
|
const data = await consoleGet(
|
|
1241
1387
|
getTenantContextPath(includeCounts),
|
|
1242
1388
|
apiKey
|
|
1243
1389
|
);
|
|
1244
|
-
if (!includeCounts) {
|
|
1245
|
-
cache.set(cacheKey, {
|
|
1246
|
-
data,
|
|
1247
|
-
expiry: Date.now() + TENANT_CONTEXT_CACHE_TTL_MS
|
|
1248
|
-
});
|
|
1249
|
-
}
|
|
1250
1390
|
return data;
|
|
1251
1391
|
}
|
|
1252
1392
|
function invalidateTenantContextCache() {
|
|
1253
|
-
cache.clear();
|
|
1254
1393
|
}
|
|
1255
1394
|
|
|
1256
1395
|
// src/tools/get-tenant-context.ts
|
|
@@ -1336,18 +1475,12 @@ async function handler({ includeCounts }) {
|
|
|
1336
1475
|
import { z as z29 } from "zod";
|
|
1337
1476
|
|
|
1338
1477
|
// src/lib/field-config.ts
|
|
1339
|
-
var cache2 = /* @__PURE__ */ new Map();
|
|
1340
|
-
var CACHE_TTL = 6e4;
|
|
1341
1478
|
async function fetchFieldConfigs() {
|
|
1342
1479
|
const apiKey = resolveApiKey();
|
|
1343
|
-
const cacheKey = resolveAuthCacheKey(apiKey);
|
|
1344
|
-
const cached = cache2.get(cacheKey);
|
|
1345
|
-
if (cached && cached.expiry > Date.now()) return cached.data;
|
|
1346
1480
|
const data = await consoleGet(
|
|
1347
1481
|
"/api/field-configs/list",
|
|
1348
1482
|
apiKey
|
|
1349
1483
|
);
|
|
1350
|
-
cache2.set(cacheKey, { data, expiry: Date.now() + CACHE_TTL });
|
|
1351
1484
|
return data;
|
|
1352
1485
|
}
|
|
1353
1486
|
async function saveFieldConfig(body) {
|
|
@@ -1359,7 +1492,6 @@ async function saveFieldConfig(body) {
|
|
|
1359
1492
|
);
|
|
1360
1493
|
}
|
|
1361
1494
|
function invalidateFieldConfigCache() {
|
|
1362
|
-
cache2.clear();
|
|
1363
1495
|
}
|
|
1364
1496
|
|
|
1365
1497
|
// src/tools/list-configurable-fields.ts
|
|
@@ -1757,18 +1889,13 @@ const client = createClient({
|
|
|
1757
1889
|
})
|
|
1758
1890
|
|
|
1759
1891
|
// --- Register ---
|
|
1760
|
-
const { customer
|
|
1892
|
+
const { customer } = await client.customer.register({
|
|
1761
1893
|
name: 'Jane Doe',
|
|
1762
1894
|
email: 'jane@example.com',
|
|
1763
1895
|
password: 'securePassword123',
|
|
1764
1896
|
phone: '+821012345678', // optional
|
|
1765
1897
|
})
|
|
1766
1898
|
|
|
1767
|
-
if (verificationRequired) {
|
|
1768
|
-
// Tenant has requireEmailVerification enabled.
|
|
1769
|
-
// Token delivered via webhook \u2014 prompt user to check email.
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
1899
|
// --- Login ---
|
|
1773
1900
|
const { token, customer: loggedIn } = await client.customer.login({
|
|
1774
1901
|
email: 'jane@example.com',
|
|
@@ -1789,9 +1916,9 @@ await client.customer.forgotPassword('jane@example.com') // sends token via webh
|
|
|
1789
1916
|
await client.customer.resetPassword(token, 'newPassword123')`,
|
|
1790
1917
|
cautions: [
|
|
1791
1918
|
"customer.register/login/me are only available on Client (not ServerClient)",
|
|
1792
|
-
"
|
|
1919
|
+
"registration creates a local customer account; add app-level verification if your project requires it",
|
|
1793
1920
|
"updateProfile only accepts name, phone, and marketingConsent \u2014 not email or password",
|
|
1794
|
-
"forgotPassword sends the token
|
|
1921
|
+
"forgotPassword sends the token to configured tenant webhooks; your webhook handler owns email/SMS delivery"
|
|
1795
1922
|
],
|
|
1796
1923
|
relatedResources: ["docs://sdk/customer-auth", "docs://sdk/getting-started"],
|
|
1797
1924
|
relatedTools: []
|
|
@@ -2037,8 +2164,8 @@ var docIndex = [
|
|
|
2037
2164
|
// Customer Auth
|
|
2038
2165
|
{
|
|
2039
2166
|
title: "Customer Auth \u2014 Login and Register",
|
|
2040
|
-
keywords: ["customer", "login", "register", "auth", "authentication", "customer auth"
|
|
2041
|
-
summary: "client.customer.login({ email, password }) and register({ name, email, password }).
|
|
2167
|
+
keywords: ["customer", "login", "register", "auth", "authentication", "customer auth"],
|
|
2168
|
+
summary: "client.customer.login({ email, password }) and register({ name, email, password }).",
|
|
2042
2169
|
resourceUri: "docs://sdk/customer-auth"
|
|
2043
2170
|
},
|
|
2044
2171
|
{
|
|
@@ -2064,7 +2191,7 @@ var docIndex = [
|
|
|
2064
2191
|
{
|
|
2065
2192
|
title: "Webhooks",
|
|
2066
2193
|
keywords: ["webhook", "hmac", "signature", "WEBHOOK_SECRET", "server-to-server", "event"],
|
|
2067
|
-
summary: "Tenant webhooks deliver server-to-server events
|
|
2194
|
+
summary: "Tenant webhooks deliver server-to-server events such as password reset tokens. Signed with HMAC-SHA256 using PAYLOAD_SECRET.",
|
|
2068
2195
|
resourceUri: "docs://sdk/webhook"
|
|
2069
2196
|
},
|
|
2070
2197
|
// Order API
|
|
@@ -2156,13 +2283,13 @@ var schema33 = {
|
|
|
2156
2283
|
"server-client",
|
|
2157
2284
|
"customer-auth",
|
|
2158
2285
|
"mcp-connection",
|
|
2159
|
-
"
|
|
2286
|
+
"server-credentials",
|
|
2160
2287
|
"webhook-verification"
|
|
2161
2288
|
]).describe("Authentication scenario")
|
|
2162
2289
|
};
|
|
2163
2290
|
var metadata33 = {
|
|
2164
2291
|
name: "sdk-get-auth-setup",
|
|
2165
|
-
description: "Get the
|
|
2292
|
+
description: "Get the current authentication setup for a specific scenario. Returns env var names, code snippets, and security notes.",
|
|
2166
2293
|
annotations: {
|
|
2167
2294
|
title: "Get Auth Setup",
|
|
2168
2295
|
readOnlyHint: true,
|
|
@@ -2195,15 +2322,14 @@ const { data } = client.query.useQuery({ collection: 'products' })`,
|
|
|
2195
2322
|
|
|
2196
2323
|
const client = createServerClient({
|
|
2197
2324
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
2198
|
-
secretKey: process.env.SOFTWARE_SECRET_KEY!
|
|
2325
|
+
secretKey: process.env.SOFTWARE_SECRET_KEY!
|
|
2199
2326
|
})
|
|
2200
2327
|
|
|
2201
2328
|
// Full CRUD operations
|
|
2202
2329
|
const result = await client.collections.from('products').create({ title: 'New Product' })`,
|
|
2203
2330
|
notes: [
|
|
2204
|
-
"ServerClient has full CRUD access
|
|
2205
|
-
"
|
|
2206
|
-
"Browser-based CLI/init login flows may provision a user-scoped PAT (pat01_...) with a default tenant",
|
|
2331
|
+
"ServerClient has full CRUD access and must run only in trusted server code",
|
|
2332
|
+
"Store server credentials in environment variables and rotate them from the Console",
|
|
2207
2333
|
"Use in API routes, server actions, or backend services only",
|
|
2208
2334
|
"React Query hooks available for reads (useQuery, prefetchQuery, etc.) + mutations (useCreate, useUpdate, useRemove)"
|
|
2209
2335
|
]
|
|
@@ -2235,90 +2361,68 @@ client.customer.isAuthenticated()`,
|
|
|
2235
2361
|
notes: [
|
|
2236
2362
|
"Customer auth uses the browser Client (not ServerClient)",
|
|
2237
2363
|
"JWT tokens are managed automatically by the SDK",
|
|
2238
|
-
"
|
|
2364
|
+
"Registration creates a local customer account; add application-level verification if needed"
|
|
2239
2365
|
]
|
|
2240
2366
|
},
|
|
2241
2367
|
"mcp-connection": {
|
|
2242
2368
|
title: "MCP Server Connection",
|
|
2243
|
-
envVars: [
|
|
2369
|
+
envVars: [],
|
|
2244
2370
|
code: `# Claude Code
|
|
2245
|
-
claude mcp add --transport http
|
|
2246
|
-
--header "x-api-key: $SOFTWARE_SECRET_KEY" \\
|
|
2247
|
-
--header "x-publishable-key: $SOFTWARE_PUBLISHABLE_KEY" \\
|
|
2248
|
-
01software https://mcp.01.software/mcp
|
|
2371
|
+
claude mcp add --transport http 01software https://mcp.01.software/mcp
|
|
2249
2372
|
|
|
2250
|
-
# Codex
|
|
2373
|
+
# Codex .codex/config.toml
|
|
2251
2374
|
[mcp_servers.01software]
|
|
2252
2375
|
url = "https://mcp.01.software/mcp"
|
|
2253
2376
|
|
|
2254
|
-
|
|
2255
|
-
x-api-key = "SOFTWARE_SECRET_KEY"
|
|
2256
|
-
x-publishable-key = "SOFTWARE_PUBLISHABLE_KEY"
|
|
2257
|
-
|
|
2258
|
-
# Or use .mcp.json
|
|
2377
|
+
# Or use JSON clients that support OAuth discovery
|
|
2259
2378
|
{
|
|
2260
2379
|
"mcpServers": {
|
|
2261
2380
|
"01software": {
|
|
2262
2381
|
"type": "http",
|
|
2263
|
-
"url": "https://mcp.01.software/mcp"
|
|
2264
|
-
"headers": {
|
|
2265
|
-
"x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
|
|
2266
|
-
"x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
|
|
2267
|
-
}
|
|
2382
|
+
"url": "https://mcp.01.software/mcp"
|
|
2268
2383
|
}
|
|
2269
2384
|
}
|
|
2270
2385
|
}`,
|
|
2271
2386
|
notes: [
|
|
2272
|
-
"MCP
|
|
2273
|
-
"
|
|
2274
|
-
"
|
|
2275
|
-
"Use tenant API keys for shared service integrations; PATs are useful for user-scoped local workflows",
|
|
2276
|
-
"Never commit raw bearer tokens to repo-local MCP config; prefer environment interpolation, client prompts, OS secret managers, or ignored local files",
|
|
2277
|
-
"Avoid passing real tokens directly on shared-machine command lines because shell history and process listings can expose them",
|
|
2278
|
-
"stdio transport: use `npx @01.software/cli mcp` with SOFTWARE_PUBLISHABLE_KEY and SOFTWARE_SECRET_KEY env vars"
|
|
2387
|
+
"HTTP MCP uses OAuth discovery and Authorization Code + PKCE",
|
|
2388
|
+
"Clients that cannot complete OAuth discovery are unsupported until a smoke test proves compatibility",
|
|
2389
|
+
"stdio transport remains a local CLI path and is separate from HTTP MCP OAuth discovery"
|
|
2279
2390
|
]
|
|
2280
2391
|
},
|
|
2281
|
-
"
|
|
2282
|
-
title: "
|
|
2392
|
+
"server-credentials": {
|
|
2393
|
+
title: "Server Credential Management",
|
|
2283
2394
|
envVars: ["SOFTWARE_PUBLISHABLE_KEY", "SOFTWARE_SECRET_KEY"],
|
|
2284
|
-
code: `#
|
|
2285
|
-
#
|
|
2286
|
-
# The generated key has the format: sk01_{40hex}
|
|
2287
|
-
# Copy the publishable key from the same tenant.
|
|
2395
|
+
code: `# Server credentials are managed from the Console, not in code.
|
|
2396
|
+
# Copy both values from the same tenant.
|
|
2288
2397
|
|
|
2289
|
-
# Use them together for
|
|
2290
|
-
export SOFTWARE_PUBLISHABLE_KEY=
|
|
2398
|
+
# Use them together for CLI and server SDK calls.
|
|
2399
|
+
export SOFTWARE_PUBLISHABLE_KEY=pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
2291
2400
|
export SOFTWARE_SECRET_KEY=sk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`,
|
|
2292
2401
|
notes: [
|
|
2293
|
-
"API keys are sk01_{40hex} opaque bearer tokens",
|
|
2294
2402
|
"The matching SOFTWARE_PUBLISHABLE_KEY is still required for tenant routing, rate limits, and quota enforcement",
|
|
2295
|
-
"
|
|
2296
|
-
"
|
|
2297
|
-
"Generate keys from Console > Settings > API Keys \u2014 never derive them in code"
|
|
2403
|
+
"Used for REST/SDK authentication in trusted server contexts",
|
|
2404
|
+
"Manage credentials from the Console and rotate them on exposure"
|
|
2298
2405
|
]
|
|
2299
2406
|
},
|
|
2300
2407
|
"webhook-verification": {
|
|
2301
2408
|
title: "Webhook Verification",
|
|
2302
2409
|
envVars: ["WEBHOOK_SECRET"],
|
|
2303
|
-
code: `import { handleWebhook } from '@01.software/sdk/webhook'
|
|
2410
|
+
code: `import { handleWebhook, createCustomerAuthWebhookHandler } from '@01.software/sdk/webhook'
|
|
2411
|
+
|
|
2412
|
+
const customerAuthHandler = createCustomerAuthWebhookHandler({
|
|
2413
|
+
passwordReset: sendPasswordResetEmail,
|
|
2414
|
+
})
|
|
2304
2415
|
|
|
2305
2416
|
export async function POST(request: Request) {
|
|
2306
|
-
return handleWebhook(request,
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
case 'verification':
|
|
2310
|
-
await sendVerificationEmail(event.data)
|
|
2311
|
-
break
|
|
2312
|
-
case 'password-reset':
|
|
2313
|
-
await sendPasswordResetEmail(event.data)
|
|
2314
|
-
break
|
|
2315
|
-
}
|
|
2316
|
-
}, { secret: process.env.WEBHOOK_SECRET! })
|
|
2417
|
+
return handleWebhook(request, customerAuthHandler, {
|
|
2418
|
+
secret: process.env.WEBHOOK_SECRET!,
|
|
2419
|
+
})
|
|
2317
2420
|
}`,
|
|
2318
2421
|
notes: [
|
|
2319
2422
|
"handleWebhook() takes (request, handler, options) \u2014 handler receives the parsed event",
|
|
2320
2423
|
"WEBHOOK_SECRET is set per-tenant in Console > Settings",
|
|
2321
|
-
"handleWebhook() verifies HMAC-SHA256 signature automatically before calling handler"
|
|
2424
|
+
"handleWebhook() verifies HMAC-SHA256 signature automatically before calling handler",
|
|
2425
|
+
"createCustomerAuthWebhookHandler() is optional; it just routes auth events to your own email/SMS delivery code"
|
|
2322
2426
|
]
|
|
2323
2427
|
}
|
|
2324
2428
|
};
|
|
@@ -2629,8 +2733,8 @@ await client.collections.from('products').remove('id')
|
|
|
2629
2733
|
const { totalDocs } = await client.collections.from('products').count()
|
|
2630
2734
|
|
|
2631
2735
|
// Metadata - generate Next.js Metadata from collection fields
|
|
2632
|
-
// Auto-maps per-collection fields (e.g.
|
|
2633
|
-
const
|
|
2736
|
+
// Auto-maps per-collection fields (e.g. articles: description\u2192description, thumbnail\u2192image)
|
|
2737
|
+
const articleMeta = await client.collections.from('articles').findMetadataById(id, { siteName: 'My Blog' })
|
|
2634
2738
|
const productMeta = await client.collections.from('products').findMetadata(
|
|
2635
2739
|
{ where: { slug: { equals: 'my-product' } } },
|
|
2636
2740
|
{ siteName: 'My Store' }
|
|
@@ -2956,7 +3060,7 @@ var schema38 = {
|
|
|
2956
3060
|
feature: z38.enum([
|
|
2957
3061
|
"ecommerce",
|
|
2958
3062
|
"customers",
|
|
2959
|
-
"
|
|
3063
|
+
"articles",
|
|
2960
3064
|
"documents",
|
|
2961
3065
|
"playlists",
|
|
2962
3066
|
"galleries",
|
|
@@ -3019,22 +3123,22 @@ customer-groups \u2014 Use \`create-collection\` with \`collection='customer-gro
|
|
|
3019
3123
|
|
|
3020
3124
|
### Config
|
|
3021
3125
|
|
|
3022
|
-
-
|
|
3126
|
+
- Customer registration creates a local account; add app-level verification if needed
|
|
3023
3127
|
- Customer auth uses custom JWT (separate from Payload auth)`,
|
|
3024
|
-
|
|
3128
|
+
articles: `## Articles Setup Guide
|
|
3025
3129
|
|
|
3026
3130
|
### Required Collections (count > 0)
|
|
3027
3131
|
|
|
3028
|
-
1. **
|
|
3132
|
+
1. **articles** \u2014 At least 1 article
|
|
3029
3133
|
- Minimum fields: \`{ title, slug }\`
|
|
3030
3134
|
|
|
3031
|
-
2. **
|
|
3135
|
+
2. **article-authors** \u2014 At least 1 author
|
|
3032
3136
|
- Minimum fields: \`{ title, slug }\`
|
|
3033
|
-
- Link authors to
|
|
3137
|
+
- Link authors to articles via the \`authors\` relationship field
|
|
3034
3138
|
|
|
3035
3139
|
### Optional Collections
|
|
3036
3140
|
|
|
3037
|
-
|
|
3141
|
+
article-categories, article-tags`,
|
|
3038
3142
|
documents: `## Documents Setup Guide
|
|
3039
3143
|
|
|
3040
3144
|
### Required Collections (count > 0)
|
|
@@ -3144,7 +3248,7 @@ form-submissions \u2014 Auto-created when forms are submitted by end users`,
|
|
|
3144
3248
|
|
|
3145
3249
|
### Required Collections (count > 0)
|
|
3146
3250
|
|
|
3147
|
-
1. **
|
|
3251
|
+
1. **posts** \u2014 At least 1 post
|
|
3148
3252
|
- Minimum fields: \`{ title, slug }\`
|
|
3149
3253
|
|
|
3150
3254
|
2. **reaction-types** \u2014 At least 1 reaction type defined
|
|
@@ -3156,7 +3260,7 @@ comments, reactions, bookmarks, reports, community-bans
|
|
|
3156
3260
|
|
|
3157
3261
|
### Optional Collections
|
|
3158
3262
|
|
|
3159
|
-
|
|
3263
|
+
post-categories`
|
|
3160
3264
|
};
|
|
3161
3265
|
function featureSetupGuide({ feature }) {
|
|
3162
3266
|
return `# Feature Setup Guide: ${feature}
|
|
@@ -3185,23 +3289,11 @@ function handler6() {
|
|
|
3185
3289
|
|
|
3186
3290
|
## Authentication
|
|
3187
3291
|
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
\`\`\`
|
|
3191
|
-
x-api-key: <sk01_... or pat01_...>
|
|
3192
|
-
x-publishable-key: <pk01_...>
|
|
3193
|
-
\`\`\`
|
|
3194
|
-
|
|
3195
|
-
\`x-client-key\` is accepted as a legacy alias for \`x-publishable-key\`.
|
|
3292
|
+
HTTP MCP uses OAuth discovery and Authorization Code + PKCE.
|
|
3196
3293
|
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
- \`pat01_{40hex}\` \u2014 personal access token for user-scoped local workflows
|
|
3201
|
-
|
|
3202
|
-
\`\`\`
|
|
3203
|
-
SOFTWARE_SECRET_KEY=sk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
3204
|
-
SOFTWARE_PUBLISHABLE_KEY=pk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
3294
|
+
\`\`\`toml
|
|
3295
|
+
[mcp_servers.01software]
|
|
3296
|
+
url = "https://mcp.01.software/mcp"
|
|
3205
3297
|
\`\`\`
|
|
3206
3298
|
|
|
3207
3299
|
## Available Tools (34)
|
|
@@ -3305,7 +3397,17 @@ function handler7() {
|
|
|
3305
3397
|
Carts: ["carts", "cart-items"],
|
|
3306
3398
|
Discounts: ["discounts"],
|
|
3307
3399
|
Documents: ["documents", "document-categories", "document-types"],
|
|
3308
|
-
|
|
3400
|
+
Articles: ["articles", "article-authors", "article-categories", "article-tags"],
|
|
3401
|
+
Community: [
|
|
3402
|
+
"posts",
|
|
3403
|
+
"comments",
|
|
3404
|
+
"reactions",
|
|
3405
|
+
"reaction-types",
|
|
3406
|
+
"bookmarks",
|
|
3407
|
+
"post-categories",
|
|
3408
|
+
"reports",
|
|
3409
|
+
"community-bans"
|
|
3410
|
+
],
|
|
3309
3411
|
Playlists: [
|
|
3310
3412
|
"playlists",
|
|
3311
3413
|
"tracks",
|
|
@@ -3856,7 +3958,7 @@ Customer authentication and profile management. Available on \`Client\` only (\`
|
|
|
3856
3958
|
### Authentication
|
|
3857
3959
|
\`\`\`typescript
|
|
3858
3960
|
// Register
|
|
3859
|
-
const { customer
|
|
3961
|
+
const { customer } = await client.customer.register({
|
|
3860
3962
|
name: 'John',
|
|
3861
3963
|
email: 'john@example.com',
|
|
3862
3964
|
password: 'password123',
|
|
@@ -3891,7 +3993,7 @@ const updated = await client.customer.updateProfile({
|
|
|
3891
3993
|
|
|
3892
3994
|
### Password
|
|
3893
3995
|
\`\`\`typescript
|
|
3894
|
-
// Forgot password (sends reset token
|
|
3996
|
+
// Forgot password (sends reset token to configured tenant webhooks)
|
|
3895
3997
|
await client.customer.forgotPassword(email)
|
|
3896
3998
|
|
|
3897
3999
|
// Reset password with token
|
|
@@ -3901,11 +4003,6 @@ await client.customer.resetPassword(token, newPassword)
|
|
|
3901
4003
|
await client.customer.changePassword(currentPassword, newPassword)
|
|
3902
4004
|
\`\`\`
|
|
3903
4005
|
|
|
3904
|
-
### Email Verification
|
|
3905
|
-
\`\`\`typescript
|
|
3906
|
-
await client.customer.verifyEmail(token)
|
|
3907
|
-
\`\`\`
|
|
3908
|
-
|
|
3909
4006
|
### Orders
|
|
3910
4007
|
\`\`\`typescript
|
|
3911
4008
|
const orders = await client.commerce.orders.listMine({
|
|
@@ -4084,7 +4181,7 @@ if (page1.hasNextPage) {
|
|
|
4084
4181
|
|
|
4085
4182
|
\`\`\`typescript
|
|
4086
4183
|
// Descending (newest first)
|
|
4087
|
-
const result = await client.collections.from('
|
|
4184
|
+
const result = await client.collections.from('articles').find({ sort: '-createdAt' })
|
|
4088
4185
|
|
|
4089
4186
|
// Ascending
|
|
4090
4187
|
const result2 = await client.collections.from('products').find({ sort: 'price' })
|
|
@@ -4379,19 +4476,19 @@ function handler13() {
|
|
|
4379
4476
|
|
|
4380
4477
|
Server-side operations are available via \`client.commerce\` on \`ServerClient\`. Use \`createServerClient\` with both \`publishableKey\` and \`secretKey\`.
|
|
4381
4478
|
|
|
4382
|
-
For backend services, prefer a tenant API key (\`sk01_...\`) in \`SOFTWARE_SECRET_KEY\`.
|
|
4383
|
-
Browser-based CLI/init login flows may instead provision a user-scoped PAT (\`pat01_...\`) with a default tenant.
|
|
4384
|
-
|
|
4385
4479
|
\`\`\`typescript
|
|
4386
4480
|
import { createServerClient } from '@01.software/sdk'
|
|
4387
4481
|
|
|
4388
4482
|
const client = createServerClient({
|
|
4389
4483
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
4390
|
-
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
4484
|
+
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
4391
4485
|
})
|
|
4392
4486
|
\`\`\`
|
|
4393
4487
|
|
|
4394
|
-
|
|
4488
|
+
Use server components, API routes, or server actions only. Never expose
|
|
4489
|
+
\`SOFTWARE_SECRET_KEY\` to browser code, client bundles, logs, or public
|
|
4490
|
+
repositories. If a secret key leaks, rotate it from the Console before deploying
|
|
4491
|
+
again.
|
|
4395
4492
|
|
|
4396
4493
|
## Order API
|
|
4397
4494
|
|
|
@@ -4660,11 +4757,9 @@ const result = await client.customer.register({
|
|
|
4660
4757
|
phone?: '+821012345678',
|
|
4661
4758
|
})
|
|
4662
4759
|
// result.customer - created customer object
|
|
4663
|
-
// result.token? - JWT token (set if email verification not required)
|
|
4664
|
-
// result.verificationRequired? - true if tenant requires email verification
|
|
4665
4760
|
\`\`\`
|
|
4666
4761
|
|
|
4667
|
-
|
|
4762
|
+
Registration creates a local customer account. Projects that need additional email verification should enforce it in application code.
|
|
4668
4763
|
|
|
4669
4764
|
### login()
|
|
4670
4765
|
Authenticate with email and password.
|
|
@@ -4718,12 +4813,12 @@ const updated = await client.customer.updateProfile({
|
|
|
4718
4813
|
## Password
|
|
4719
4814
|
|
|
4720
4815
|
### forgotPassword()
|
|
4721
|
-
Request a password reset. Sends reset token
|
|
4816
|
+
Request a password reset. Sends the reset token to configured tenant webhooks; your webhook handler owns delivery.
|
|
4722
4817
|
|
|
4723
4818
|
\`\`\`typescript
|
|
4724
4819
|
await client.customer.forgotPassword('john@example.com')
|
|
4725
4820
|
// Rate limited: 5 requests/min per tenant+email
|
|
4726
|
-
// Webhook receives: { resetPasswordToken,
|
|
4821
|
+
// Webhook receives: { resetPasswordToken, resetPasswordExpiresAt }
|
|
4727
4822
|
\`\`\`
|
|
4728
4823
|
|
|
4729
4824
|
### resetPassword()
|
|
@@ -4740,15 +4835,6 @@ Change password while authenticated (requires current password).
|
|
|
4740
4835
|
await client.customer.changePassword('currentPassword', 'newPassword123')
|
|
4741
4836
|
\`\`\`
|
|
4742
4837
|
|
|
4743
|
-
## Email Verification
|
|
4744
|
-
|
|
4745
|
-
### verifyEmail()
|
|
4746
|
-
Verify email address using the token received via webhook.
|
|
4747
|
-
|
|
4748
|
-
\`\`\`typescript
|
|
4749
|
-
await client.customer.verifyEmail('verification-token')
|
|
4750
|
-
\`\`\`
|
|
4751
|
-
|
|
4752
4838
|
## Orders
|
|
4753
4839
|
|
|
4754
4840
|
### listMine()
|
|
@@ -4794,12 +4880,7 @@ const client = createClient({
|
|
|
4794
4880
|
async function handleRegister(email: string, password: string, name: string) {
|
|
4795
4881
|
const result = await client.customer.register({ email, password, name })
|
|
4796
4882
|
|
|
4797
|
-
|
|
4798
|
-
// Redirect to "check your email" page
|
|
4799
|
-
return { status: 'verify-email' }
|
|
4800
|
-
}
|
|
4801
|
-
|
|
4802
|
-
// Token is automatically stored; customer is now logged in
|
|
4883
|
+
// Customer is created as a local account.
|
|
4803
4884
|
return { status: 'success', customer: result.customer }
|
|
4804
4885
|
}
|
|
4805
4886
|
|
|
@@ -4901,7 +4982,11 @@ await client.commerce.orders.checkout({ ... })
|
|
|
4901
4982
|
|
|
4902
4983
|
**Environment variables**:
|
|
4903
4984
|
- \`SOFTWARE_PUBLISHABLE_KEY\` \u2014 publishable key (no NEXT_PUBLIC prefix, server-only)
|
|
4904
|
-
- \`SOFTWARE_SECRET_KEY\` \u2014
|
|
4985
|
+
- \`SOFTWARE_SECRET_KEY\` \u2014 server credential
|
|
4986
|
+
|
|
4987
|
+
Never expose \`SOFTWARE_SECRET_KEY\` in browser code, client bundles, logs, or
|
|
4988
|
+
public repositories. If a secret key leaks, rotate it from the Console before
|
|
4989
|
+
deploying again.
|
|
4905
4990
|
|
|
4906
4991
|
## Decision Matrix
|
|
4907
4992
|
|
|
@@ -4967,9 +5052,10 @@ export function ProductList() {
|
|
|
4967
5052
|
|
|
4968
5053
|
## Security Rules
|
|
4969
5054
|
|
|
4970
|
-
-
|
|
5055
|
+
- Keep server credentials in server-only modules.
|
|
4971
5056
|
- Only \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` is safe to use in client components.
|
|
4972
|
-
-
|
|
5057
|
+
- Never import a module that reads \`SOFTWARE_SECRET_KEY\` from a client component.
|
|
5058
|
+
- Rotate any exposed secret key immediately from the Console.
|
|
4973
5059
|
|
|
4974
5060
|
> Ecommerce note: product card pricing lives on \`products.listing.*\`, but authoritative sellable pricing still lives on \`product-variants.price\`.`;
|
|
4975
5061
|
}
|
|
@@ -5134,27 +5220,23 @@ var metadata50 = {
|
|
|
5134
5220
|
function handler17() {
|
|
5135
5221
|
return `# Webhooks
|
|
5136
5222
|
|
|
5137
|
-
The platform dispatches HMAC-SHA256 signed webhook events to your registered URLs
|
|
5223
|
+
The platform dispatches HMAC-SHA256 signed webhook events to your registered URLs. Tenant developers own routing inside their webhook handler.
|
|
5138
5224
|
|
|
5139
5225
|
## Webhook Handling
|
|
5140
5226
|
|
|
5141
|
-
Use the SDK \`handleWebhook\` helper to verify signatures
|
|
5227
|
+
Use the SDK \`handleWebhook\` helper to verify signatures. For customer auth events, use \`createCustomerAuthWebhookHandler\` to wire delivery behavior in your app.
|
|
5142
5228
|
|
|
5143
5229
|
\`\`\`typescript
|
|
5144
|
-
import { handleWebhook } from '@01.software/sdk/webhook'
|
|
5230
|
+
import { handleWebhook, createCustomerAuthWebhookHandler } from '@01.software/sdk/webhook'
|
|
5231
|
+
|
|
5232
|
+
const handler = createCustomerAuthWebhookHandler({
|
|
5233
|
+
passwordReset: async (data) => {
|
|
5234
|
+
await sendPasswordResetEmail(data)
|
|
5235
|
+
},
|
|
5236
|
+
})
|
|
5145
5237
|
|
|
5146
5238
|
export async function POST(request: Request) {
|
|
5147
|
-
return handleWebhook(request,
|
|
5148
|
-
// event.collection, event.operation, event.data
|
|
5149
|
-
switch (event.operation) {
|
|
5150
|
-
case 'verification':
|
|
5151
|
-
await sendVerificationEmail(event.data)
|
|
5152
|
-
break
|
|
5153
|
-
case 'password-reset':
|
|
5154
|
-
await sendPasswordResetEmail(event.data)
|
|
5155
|
-
break
|
|
5156
|
-
}
|
|
5157
|
-
}, {
|
|
5239
|
+
return handleWebhook(request, handler, {
|
|
5158
5240
|
secret: process.env.WEBHOOK_SECRET!,
|
|
5159
5241
|
})
|
|
5160
5242
|
}
|
|
@@ -5166,19 +5248,17 @@ export async function POST(request: Request) {
|
|
|
5166
5248
|
|
|
5167
5249
|
\`\`\`typescript
|
|
5168
5250
|
// app/api/webhooks/route.ts
|
|
5169
|
-
import { handleWebhook } from '@01.software/sdk/webhook'
|
|
5251
|
+
import { handleWebhook, createCustomerAuthWebhookHandler } from '@01.software/sdk/webhook'
|
|
5252
|
+
|
|
5253
|
+
const customerAuthHandler = createCustomerAuthWebhookHandler({
|
|
5254
|
+
passwordReset: sendPasswordResetEmail,
|
|
5255
|
+
})
|
|
5170
5256
|
|
|
5171
5257
|
export async function POST(request: Request) {
|
|
5172
5258
|
return handleWebhook(request, async (event) => {
|
|
5173
5259
|
console.log('Webhook received:', event.collection, event.operation)
|
|
5174
5260
|
|
|
5175
|
-
|
|
5176
|
-
if (event.operation === 'verification') {
|
|
5177
|
-
await sendVerificationEmail(event.data)
|
|
5178
|
-
} else if (event.operation === 'password-reset') {
|
|
5179
|
-
await sendPasswordResetEmail(event.data)
|
|
5180
|
-
}
|
|
5181
|
-
}
|
|
5261
|
+
await customerAuthHandler(event)
|
|
5182
5262
|
}, {
|
|
5183
5263
|
secret: process.env.WEBHOOK_SECRET!,
|
|
5184
5264
|
})
|
|
@@ -5192,49 +5272,13 @@ All webhook events share this envelope:
|
|
|
5192
5272
|
\`\`\`typescript
|
|
5193
5273
|
{
|
|
5194
5274
|
collection: string, // e.g. 'customers'
|
|
5195
|
-
operation: string, // e.g. '
|
|
5275
|
+
operation: string, // e.g. 'password-reset'
|
|
5196
5276
|
data: object, // event-specific payload
|
|
5197
5277
|
}
|
|
5198
5278
|
\`\`\`
|
|
5199
5279
|
|
|
5200
5280
|
## Event Types
|
|
5201
5281
|
|
|
5202
|
-
### Customer Email Verification
|
|
5203
|
-
|
|
5204
|
-
Dispatched when a customer registers on a tenant with \`requireEmailVerification: true\`.
|
|
5205
|
-
|
|
5206
|
-
\`\`\`typescript
|
|
5207
|
-
{
|
|
5208
|
-
collection: 'customers',
|
|
5209
|
-
operation: 'verification',
|
|
5210
|
-
data: {
|
|
5211
|
-
customerId: string,
|
|
5212
|
-
email: string,
|
|
5213
|
-
name: string,
|
|
5214
|
-
verificationToken: string, // raw token to include in verification link
|
|
5215
|
-
}
|
|
5216
|
-
}
|
|
5217
|
-
\`\`\`
|
|
5218
|
-
|
|
5219
|
-
**Usage**: Send the \`verificationToken\` to the customer's email. The customer calls \`client.customer.verifyEmail(token)\` to complete verification.
|
|
5220
|
-
|
|
5221
|
-
\`\`\`typescript
|
|
5222
|
-
// Example: send verification email
|
|
5223
|
-
async function sendVerificationEmail(data: {
|
|
5224
|
-
customerId: string
|
|
5225
|
-
email: string
|
|
5226
|
-
name: string
|
|
5227
|
-
verificationToken: string
|
|
5228
|
-
}) {
|
|
5229
|
-
const verifyUrl = \`https://yourstore.com/verify-email?token=\${data.verificationToken}\`
|
|
5230
|
-
await emailService.send({
|
|
5231
|
-
to: data.email,
|
|
5232
|
-
subject: 'Verify your email',
|
|
5233
|
-
body: \`Click here to verify: \${verifyUrl}\`,
|
|
5234
|
-
})
|
|
5235
|
-
}
|
|
5236
|
-
\`\`\`
|
|
5237
|
-
|
|
5238
5282
|
### Customer Password Reset
|
|
5239
5283
|
|
|
5240
5284
|
Dispatched when a customer calls \`client.customer.forgotPassword(email)\`.
|
|
@@ -5248,7 +5292,7 @@ Dispatched when a customer calls \`client.customer.forgotPassword(email)\`.
|
|
|
5248
5292
|
email: string,
|
|
5249
5293
|
name: string,
|
|
5250
5294
|
resetPasswordToken: string, // raw token to include in reset link
|
|
5251
|
-
|
|
5295
|
+
resetPasswordExpiresAt: string, // ISO 8601 expiry (1 hour from dispatch)
|
|
5252
5296
|
}
|
|
5253
5297
|
}
|
|
5254
5298
|
\`\`\`
|
|
@@ -5261,13 +5305,13 @@ async function sendPasswordResetEmail(data: {
|
|
|
5261
5305
|
email: string
|
|
5262
5306
|
name: string
|
|
5263
5307
|
resetPasswordToken: string
|
|
5264
|
-
|
|
5308
|
+
resetPasswordExpiresAt: string
|
|
5265
5309
|
}) {
|
|
5266
5310
|
const resetUrl = \`https://yourstore.com/reset-password?token=\${data.resetPasswordToken}\`
|
|
5267
5311
|
await emailService.send({
|
|
5268
5312
|
to: data.email,
|
|
5269
5313
|
subject: 'Reset your password',
|
|
5270
|
-
body: \`Reset link (expires \${data.
|
|
5314
|
+
body: \`Reset link (expires \${data.resetPasswordExpiresAt}): \${resetUrl}\`,
|
|
5271
5315
|
})
|
|
5272
5316
|
}
|
|
5273
5317
|
\`\`\`
|
|
@@ -5330,37 +5374,40 @@ function registerStaticResource(server, uri, meta, handler18) {
|
|
|
5330
5374
|
})
|
|
5331
5375
|
);
|
|
5332
5376
|
}
|
|
5333
|
-
function createServer() {
|
|
5377
|
+
function createServer(options = {}) {
|
|
5378
|
+
const toolSurface = options.toolSurface ?? "full";
|
|
5334
5379
|
const server = new McpServer({
|
|
5335
5380
|
name: "01.software MCP Server",
|
|
5336
5381
|
version: "0.1.0"
|
|
5337
5382
|
});
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5383
|
+
if (toolSurface === "full") {
|
|
5384
|
+
registerTool(server, schema, metadata, queryCollection);
|
|
5385
|
+
registerTool(server, schema2, metadata2, getCollectionById);
|
|
5386
|
+
registerTool(server, schema3, metadata3, createCollection);
|
|
5387
|
+
registerTool(server, schema4, metadata4, updateCollection);
|
|
5388
|
+
registerTool(server, schema5, metadata5, deleteCollection);
|
|
5389
|
+
registerTool(server, schema6, metadata6, deleteManyCollection);
|
|
5390
|
+
registerTool(server, schema7, metadata7, updateManyCollection);
|
|
5391
|
+
registerTool(server, schema8, metadata8, getOrder);
|
|
5392
|
+
registerTool(server, schema9, metadata9, createOrder);
|
|
5393
|
+
registerTool(server, schema10, metadata10, updateOrder);
|
|
5394
|
+
registerTool(server, schema11, metadata11, checkout);
|
|
5395
|
+
registerTool(server, schema12, metadata12, createFulfillment);
|
|
5396
|
+
registerTool(server, schema13, metadata13, updateFulfillment);
|
|
5397
|
+
registerTool(server, schema14, metadata14, updateTransaction);
|
|
5398
|
+
registerTool(server, schema15, metadata15, createReturn);
|
|
5399
|
+
registerTool(server, schema16, metadata16, updateReturn);
|
|
5400
|
+
registerTool(server, schema17, metadata17, returnWithRefund);
|
|
5401
|
+
registerTool(server, schema18, metadata18, addCartItem);
|
|
5402
|
+
registerTool(server, schema19, metadata19, updateCartItem);
|
|
5403
|
+
registerTool(server, schema20, metadata20, removeCartItem);
|
|
5404
|
+
registerTool(server, schema21, metadata21, applyDiscount);
|
|
5405
|
+
registerTool(server, schema22, metadata22, removeDiscount);
|
|
5406
|
+
registerTool(server, schema23, metadata23, clearCart);
|
|
5407
|
+
registerTool(server, schema24, metadata24, validateDiscount);
|
|
5408
|
+
registerTool(server, schema25, metadata25, calculateShipping);
|
|
5409
|
+
registerTool(server, schema26, metadata26, stockCheck);
|
|
5410
|
+
}
|
|
5364
5411
|
registerTool(server, schema27, metadata27, getCollectionSchemaTool);
|
|
5365
5412
|
registerTool(server, schema28, metadata28, handler);
|
|
5366
5413
|
registerTool(server, schema29, metadata29, listConfigurableFields);
|
|
@@ -5390,6 +5437,7 @@ function createServer() {
|
|
|
5390
5437
|
|
|
5391
5438
|
export {
|
|
5392
5439
|
requestContext,
|
|
5440
|
+
mcpServicePublicJwks,
|
|
5393
5441
|
createServer
|
|
5394
5442
|
};
|
|
5395
|
-
//# sourceMappingURL=chunk-
|
|
5443
|
+
//# sourceMappingURL=chunk-45ZCPS57.js.map
|