@01.software/cli 0.7.1 → 0.9.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.
@@ -1,71 +1,47 @@
1
1
  // src/handler.ts
2
2
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
3
3
 
4
+ // ../../packages/auth-contracts/dist/index.js
5
+ var MCP_RESOURCE_AUDIENCE = "https://mcp.01.software/mcp";
6
+ var MCP_OAUTH_ISSUER = "https://01.software";
7
+ var MCP_PROTECTED_RESOURCE_METADATA_PATH = "/.well-known/oauth-protected-resource/mcp";
8
+ var MCP_TENANT_CLAIM = "tenant_id";
9
+ var MCP_TENANT_ROLE_CLAIM = "tenant_role";
10
+ var MCP_SCOPES = {
11
+ read: "mcp:read",
12
+ write: "mcp:write"
13
+ };
14
+ var MCP_CONSOLE_SERVICE_AUDIENCE = "https://api.01.software/internal/mcp";
15
+ var MCP_CONSOLE_SERVICE_SCOPE = "console:mcp_proxy";
16
+ var MCP_SERVICE_TOKEN_LIFETIME_SECONDS = 60;
17
+
4
18
  // src/server.ts
5
19
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
20
 
7
- // src/tools/query-collection.ts
8
- import { z } from "zod";
9
-
10
21
  // src/lib/request-context.ts
11
22
  import { AsyncLocalStorage } from "async_hooks";
12
23
  var requestContext = new AsyncLocalStorage();
13
- function headers() {
14
- const ctx = requestContext.getStore();
15
- if (!ctx) return null;
16
- return Object.fromEntries(ctx.headers.entries());
24
+ function tenantAuthContext() {
25
+ return requestContext.getStore()?.auth ?? null;
17
26
  }
18
-
19
- // src/lib/client.ts
20
- import { createServerClient } from "@01.software/sdk";
21
- function getClient() {
22
- let secretKey;
23
- let publishableKey;
24
- try {
25
- const h = headers();
26
- secretKey = h?.["x-api-key"];
27
- publishableKey = h?.["x-publishable-key"] ?? h?.["x-client-key"];
28
- } catch {
29
- }
30
- if (!secretKey) {
31
- secretKey = process.env.SOFTWARE_SECRET_KEY;
32
- }
33
- if (!publishableKey) {
34
- publishableKey = process.env.SOFTWARE_PUBLISHABLE_KEY || process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY;
35
- }
36
- if (!secretKey) {
37
- throw new Error(
38
- "Authentication required. Provide x-api-key header (HTTP) or SOFTWARE_SECRET_KEY env var (stdio)."
39
- );
40
- }
41
- if (!secretKey.startsWith("sk01_") && !secretKey.startsWith("pat01_")) {
42
- throw new Error("Invalid API key format. Expected sk01_ or pat01_ token.");
43
- }
44
- if (!publishableKey) {
45
- throw new Error(
46
- "publishableKey is required. Provide X-Publishable-Key header (HTTP) or SOFTWARE_PUBLISHABLE_KEY env var (stdio). It is used for rate limiting and monthly quota enforcement via the edge proxy."
47
- );
48
- }
49
- return createServerClient({
50
- publishableKey,
51
- secretKey
52
- });
27
+ function hasRequestContext() {
28
+ return requestContext.getStore() !== void 0;
53
29
  }
54
30
 
55
- // src/tools/query-collection.ts
56
- import { COLLECTIONS } from "@01.software/sdk";
57
-
58
31
  // src/lib/tool-utils.ts
59
32
  function toolSuccess(data) {
60
33
  return JSON.stringify({ success: true, ...data }, null, 2);
61
34
  }
62
35
  function toolError(error) {
63
36
  const base = { success: false };
64
- if (error && typeof error === "object" && "code" in error) {
37
+ const isStructured = !!error && typeof error === "object" && ("code" in error || "reason" in error);
38
+ if (isStructured) {
65
39
  const sdkErr = error;
66
40
  base.error = sdkErr.message || "Unknown error";
67
41
  if (sdkErr.status) base.status = sdkErr.status;
68
42
  if (sdkErr.code) base.code = sdkErr.code;
43
+ if (sdkErr.reason) base.reason = sdkErr.reason;
44
+ if (sdkErr.requestId) base.requestId = sdkErr.requestId;
69
45
  if (sdkErr.suggestion) base.suggestion = sdkErr.suggestion;
70
46
  if (sdkErr.details?.errors) base.errors = sdkErr.details.errors;
71
47
  } else {
@@ -115,7 +91,479 @@ function parseJsonWhere(where) {
115
91
  }
116
92
  }
117
93
 
94
+ // src/tool-policy.ts
95
+ var READ_ONLY_ANNOTATION = {
96
+ readOnly: true,
97
+ destructive: false,
98
+ idempotent: true,
99
+ openWorld: false
100
+ };
101
+ var NON_DESTRUCTIVE_MUTATION_ANNOTATION = {
102
+ readOnly: false,
103
+ destructive: false,
104
+ idempotent: false,
105
+ openWorld: false
106
+ };
107
+ var NON_DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION = {
108
+ readOnly: false,
109
+ destructive: false,
110
+ idempotent: true,
111
+ openWorld: false
112
+ };
113
+ var DESTRUCTIVE_NON_IDEMPOTENT_MUTATION_ANNOTATION = {
114
+ readOnly: false,
115
+ destructive: true,
116
+ idempotent: false,
117
+ openWorld: false
118
+ };
119
+ var DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION = {
120
+ readOnly: false,
121
+ destructive: true,
122
+ idempotent: true,
123
+ openWorld: false
124
+ };
125
+ var REASON_IDEMPOTENT_DESTRUCTIVE_UPDATE = "Update operations mutate persisted state but converge to the same end state under repeated identical input.";
126
+ var REASON_CART_EPHEMERAL = "Cart is pre-checkout ephemeral state; reversal is possible by reissuing the prior input. Console enforces tenant scope.";
127
+ var TOOL_POLICY_MANIFEST = {
128
+ // ── Read-only collection / validation (mcp:read, tenant-viewer) ──
129
+ "query-collection": {
130
+ category: "read-only-collection",
131
+ oauthScope: MCP_SCOPES.read,
132
+ consoleRole: "tenant-viewer",
133
+ consoleSurface: "GET /api/{collection}",
134
+ annotationPolicy: READ_ONLY_ANNOTATION
135
+ },
136
+ "get-collection-by-id": {
137
+ category: "read-only-collection",
138
+ oauthScope: MCP_SCOPES.read,
139
+ consoleRole: "tenant-viewer",
140
+ consoleSurface: "GET /api/{collection}/{id}",
141
+ annotationPolicy: READ_ONLY_ANNOTATION
142
+ },
143
+ "get-order": {
144
+ category: "read-only-collection",
145
+ oauthScope: MCP_SCOPES.read,
146
+ consoleRole: "tenant-viewer",
147
+ consoleSurface: "GET /api/orders/{id}",
148
+ annotationPolicy: READ_ONLY_ANNOTATION
149
+ },
150
+ "stock-check": {
151
+ category: "read-only-collection",
152
+ oauthScope: MCP_SCOPES.read,
153
+ consoleRole: "tenant-viewer",
154
+ consoleSurface: "GET /api/products/{id}/stock",
155
+ annotationPolicy: READ_ONLY_ANNOTATION
156
+ },
157
+ "validate-discount": {
158
+ category: "read-only-collection",
159
+ oauthScope: MCP_SCOPES.read,
160
+ consoleRole: "tenant-viewer",
161
+ consoleSurface: "POST /api/discounts/validate",
162
+ annotationPolicy: READ_ONLY_ANNOTATION
163
+ },
164
+ "calculate-shipping": {
165
+ category: "read-only-collection",
166
+ oauthScope: MCP_SCOPES.read,
167
+ consoleRole: "tenant-viewer",
168
+ consoleSurface: "POST /api/shipping/calculate",
169
+ annotationPolicy: READ_ONLY_ANNOTATION
170
+ },
171
+ "get-collection-schema": {
172
+ category: "read-only-collection",
173
+ oauthScope: MCP_SCOPES.read,
174
+ consoleRole: "tenant-viewer",
175
+ consoleSurface: "GET /api/tenants/schema/{collectionSlug}",
176
+ annotationPolicy: READ_ONLY_ANNOTATION
177
+ },
178
+ "list-configurable-fields": {
179
+ category: "read-only-collection",
180
+ oauthScope: MCP_SCOPES.read,
181
+ consoleRole: "tenant-viewer",
182
+ consoleSurface: "GET /api/tenants/field-config",
183
+ annotationPolicy: READ_ONLY_ANNOTATION
184
+ },
185
+ // ── Tenant context (mcp:read, tenant-viewer) ──
186
+ "get-tenant-context": {
187
+ category: "read-only-tenant",
188
+ oauthScope: MCP_SCOPES.read,
189
+ consoleRole: "tenant-viewer",
190
+ consoleSurface: "GET /api/tenants/context",
191
+ annotationPolicy: READ_ONLY_ANNOTATION
192
+ },
193
+ // ── Cart mutations (mcp:write, tenant-editor) ──
194
+ "add-cart-item": {
195
+ category: "mutation-cart",
196
+ oauthScope: MCP_SCOPES.write,
197
+ consoleRole: "tenant-editor",
198
+ consoleSurface: "POST /api/carts/{id}/items",
199
+ annotationPolicy: NON_DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION
200
+ },
201
+ "update-cart-item": {
202
+ category: "mutation-cart",
203
+ oauthScope: MCP_SCOPES.write,
204
+ consoleRole: "tenant-editor",
205
+ consoleSurface: "PATCH /api/carts/{id}/items/{itemId}",
206
+ annotationPolicy: DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION,
207
+ exemptionReason: REASON_CART_EPHEMERAL
208
+ },
209
+ "remove-cart-item": {
210
+ category: "mutation-cart",
211
+ oauthScope: MCP_SCOPES.write,
212
+ consoleRole: "tenant-editor",
213
+ consoleSurface: "DELETE /api/carts/{id}/items/{itemId}",
214
+ annotationPolicy: DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION,
215
+ exemptionReason: REASON_CART_EPHEMERAL
216
+ },
217
+ "clear-cart": {
218
+ category: "mutation-cart",
219
+ oauthScope: MCP_SCOPES.write,
220
+ consoleRole: "tenant-editor",
221
+ consoleSurface: "POST /api/carts/{id}/clear",
222
+ annotationPolicy: DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION,
223
+ exemptionReason: REASON_CART_EPHEMERAL
224
+ },
225
+ "apply-discount": {
226
+ category: "mutation-cart",
227
+ oauthScope: MCP_SCOPES.write,
228
+ consoleRole: "tenant-editor",
229
+ consoleSurface: "POST /api/carts/{id}/discount",
230
+ annotationPolicy: DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION,
231
+ exemptionReason: REASON_CART_EPHEMERAL
232
+ },
233
+ "remove-discount": {
234
+ category: "mutation-cart",
235
+ oauthScope: MCP_SCOPES.write,
236
+ consoleRole: "tenant-editor",
237
+ consoleSurface: "DELETE /api/carts/{id}/discount",
238
+ annotationPolicy: DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION,
239
+ exemptionReason: REASON_CART_EPHEMERAL
240
+ },
241
+ // ── Order mutations (mcp:write, tenant-admin) ──
242
+ "checkout": {
243
+ category: "mutation-order",
244
+ oauthScope: MCP_SCOPES.write,
245
+ consoleRole: "tenant-admin",
246
+ consoleSurface: "POST /api/checkout",
247
+ annotationPolicy: DESTRUCTIVE_NON_IDEMPOTENT_MUTATION_ANNOTATION
248
+ },
249
+ "create-order": {
250
+ category: "mutation-order",
251
+ oauthScope: MCP_SCOPES.write,
252
+ consoleRole: "tenant-admin",
253
+ consoleSurface: "POST /api/orders",
254
+ annotationPolicy: DESTRUCTIVE_NON_IDEMPOTENT_MUTATION_ANNOTATION
255
+ },
256
+ "update-order": {
257
+ category: "mutation-order",
258
+ oauthScope: MCP_SCOPES.write,
259
+ consoleRole: "tenant-admin",
260
+ consoleSurface: "PATCH /api/orders/{id}",
261
+ annotationPolicy: DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION,
262
+ exemptionReason: REASON_IDEMPOTENT_DESTRUCTIVE_UPDATE
263
+ },
264
+ // ── Fulfillment mutations (mcp:write, tenant-admin) ──
265
+ "create-fulfillment": {
266
+ category: "mutation-fulfillment",
267
+ oauthScope: MCP_SCOPES.write,
268
+ consoleRole: "tenant-admin",
269
+ consoleSurface: "POST /api/orders/{id}/fulfillments",
270
+ annotationPolicy: NON_DESTRUCTIVE_MUTATION_ANNOTATION
271
+ },
272
+ "update-fulfillment": {
273
+ category: "mutation-fulfillment",
274
+ oauthScope: MCP_SCOPES.write,
275
+ consoleRole: "tenant-admin",
276
+ consoleSurface: "PATCH /api/fulfillments/{id}",
277
+ annotationPolicy: DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION,
278
+ exemptionReason: REASON_IDEMPOTENT_DESTRUCTIVE_UPDATE
279
+ },
280
+ // ── Return mutations (mcp:write, tenant-admin) ──
281
+ "create-return": {
282
+ category: "mutation-return",
283
+ oauthScope: MCP_SCOPES.write,
284
+ consoleRole: "tenant-admin",
285
+ consoleSurface: "POST /api/returns",
286
+ annotationPolicy: DESTRUCTIVE_NON_IDEMPOTENT_MUTATION_ANNOTATION
287
+ },
288
+ "update-return": {
289
+ category: "mutation-return",
290
+ oauthScope: MCP_SCOPES.write,
291
+ consoleRole: "tenant-admin",
292
+ consoleSurface: "PATCH /api/returns/{id}",
293
+ annotationPolicy: DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION,
294
+ exemptionReason: REASON_IDEMPOTENT_DESTRUCTIVE_UPDATE
295
+ },
296
+ "return-with-refund": {
297
+ category: "mutation-return",
298
+ oauthScope: MCP_SCOPES.write,
299
+ consoleRole: "tenant-admin",
300
+ consoleSurface: "POST /api/returns/with-refund",
301
+ annotationPolicy: DESTRUCTIVE_NON_IDEMPOTENT_MUTATION_ANNOTATION
302
+ },
303
+ // ── Transaction mutations (mcp:write, tenant-admin) ──
304
+ "update-transaction": {
305
+ category: "mutation-transaction",
306
+ oauthScope: MCP_SCOPES.write,
307
+ consoleRole: "tenant-admin",
308
+ consoleSurface: "PATCH /api/transactions/{id}",
309
+ annotationPolicy: DESTRUCTIVE_NON_IDEMPOTENT_MUTATION_ANNOTATION
310
+ },
311
+ // ── Field-config mutations (mcp:write, tenant-admin) ──
312
+ "update-field-config": {
313
+ category: "mutation-field-config",
314
+ oauthScope: MCP_SCOPES.write,
315
+ consoleRole: "tenant-admin",
316
+ consoleSurface: "PATCH /api/tenants/field-config",
317
+ annotationPolicy: NON_DESTRUCTIVE_IDEMPOTENT_MUTATION_ANNOTATION
318
+ },
319
+ // ── SDK doc tools (mcp:read, tenant-viewer, sdk-static surface) ──
320
+ "sdk-get-recipe": {
321
+ category: "sdk-doc",
322
+ oauthScope: MCP_SCOPES.read,
323
+ consoleRole: "tenant-viewer",
324
+ consoleSurface: "sdk-static",
325
+ annotationPolicy: READ_ONLY_ANNOTATION
326
+ },
327
+ "sdk-search-docs": {
328
+ category: "sdk-doc",
329
+ oauthScope: MCP_SCOPES.read,
330
+ consoleRole: "tenant-viewer",
331
+ consoleSurface: "sdk-static",
332
+ annotationPolicy: READ_ONLY_ANNOTATION
333
+ },
334
+ "sdk-get-auth-setup": {
335
+ category: "sdk-doc",
336
+ oauthScope: MCP_SCOPES.read,
337
+ consoleRole: "tenant-viewer",
338
+ consoleSurface: "sdk-static",
339
+ annotationPolicy: READ_ONLY_ANNOTATION
340
+ },
341
+ "sdk-get-collection-pattern": {
342
+ category: "sdk-doc",
343
+ oauthScope: MCP_SCOPES.read,
344
+ consoleRole: "tenant-viewer",
345
+ consoleSurface: "sdk-static",
346
+ annotationPolicy: READ_ONLY_ANNOTATION
347
+ }
348
+ };
349
+ function evaluateToolPolicy(toolName, scopes) {
350
+ const entry = TOOL_POLICY_MANIFEST[toolName];
351
+ if (!entry) {
352
+ return {
353
+ allowed: false,
354
+ reason: "tool_policy_missing",
355
+ message: `No tool-policy entry for ${toolName}`
356
+ };
357
+ }
358
+ if (!scopes.includes(entry.oauthScope)) {
359
+ return {
360
+ allowed: false,
361
+ reason: "insufficient_scope",
362
+ message: `Tool ${toolName} requires ${entry.oauthScope}`
363
+ };
364
+ }
365
+ return { allowed: true, entry };
366
+ }
367
+
368
+ // src/tools/query-collection.ts
369
+ import { z } from "zod";
370
+
371
+ // src/lib/client.ts
372
+ import {
373
+ CollectionClient,
374
+ CommunityClient,
375
+ ModerationApi,
376
+ ServerCommerceClient,
377
+ createServerClient
378
+ } from "@01.software/sdk";
379
+
380
+ // src/service-auth.ts
381
+ import { createPrivateKey, randomUUID, sign as signBytes } from "crypto";
382
+ var KEYSET_ENV = "MCP_SERVICE_KEYSET";
383
+ function assertProductionKeysetUse(source) {
384
+ const vercelEnv = process.env.VERCEL_ENV;
385
+ if (vercelEnv && vercelEnv !== "production") {
386
+ throw new Error(
387
+ `${source} is only allowed in production Vercel deployments; non-production MCP service auth needs environment-specific issuer, audience, JWKS URI, and key material`
388
+ );
389
+ }
390
+ }
391
+ function parsePrivateJwk() {
392
+ const keyset = signingKeyset();
393
+ const jwk = keyset.current;
394
+ const source = keyset.source;
395
+ if (typeof jwk.d !== "string" || jwk.d.length === 0) {
396
+ throw new Error(`${source} current key must be a private JWK`);
397
+ }
398
+ if (typeof jwk.kid !== "string" || jwk.kid.length === 0) {
399
+ throw new Error(`${source} must include kid`);
400
+ }
401
+ return jwk;
402
+ }
403
+ function signingKeyset() {
404
+ const raw = process.env[KEYSET_ENV];
405
+ const source = KEYSET_ENV;
406
+ if (raw) assertProductionKeysetUse(source);
407
+ const parsed = (() => {
408
+ if (!raw) return null;
409
+ try {
410
+ return JSON.parse(raw);
411
+ } catch {
412
+ throw new Error(`${KEYSET_ENV} is invalid JSON`);
413
+ }
414
+ })();
415
+ if (!parsed) throw new Error("MCP service JWT signing key is not configured");
416
+ const keys = Array.isArray(parsed.keys) ? parsed.keys : [parsed];
417
+ if (keys.length === 0 || keys.length > 2) {
418
+ throw new Error(
419
+ `${source} must contain one current key and at most one previous key`
420
+ );
421
+ }
422
+ const currentKid = parsed.current_kid;
423
+ if (typeof currentKid !== "string" && keys.length > 1) {
424
+ throw new Error(
425
+ `${source} must include current_kid when multiple keys are present`
426
+ );
427
+ }
428
+ const current = typeof currentKid === "string" ? keys.find((key) => key.kid === currentKid) : keys[0];
429
+ if (!current) throw new Error(`${source} current_kid is not in keys`);
430
+ return { current, keys, source };
431
+ }
432
+ function algForJwk(jwk) {
433
+ if (jwk.kty === "RSA") return "RS256";
434
+ if (jwk.kty === "EC" && jwk.crv === "P-256") return "ES256";
435
+ throw new Error("MCP service JWT signing key must be RSA or P-256 EC");
436
+ }
437
+ function toPublicJwk(jwk) {
438
+ const {
439
+ d: _d,
440
+ p: _p,
441
+ q: _q,
442
+ dp: _dp,
443
+ dq: _dq,
444
+ qi: _qi,
445
+ oth: _oth,
446
+ ...publicJwk
447
+ } = jwk;
448
+ return {
449
+ ...publicJwk,
450
+ alg: typeof publicJwk.alg === "string" ? publicJwk.alg : algForJwk(jwk),
451
+ use: "sig"
452
+ };
453
+ }
454
+ function base64urlJson(value) {
455
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
456
+ }
457
+ function apiScopesFor(context) {
458
+ return context.scopes.includes("mcp:write") ? ["read", "write"] : ["read"];
459
+ }
460
+ function mcpServicePublicJwks() {
461
+ const keyset = signingKeyset();
462
+ const keys = /* @__PURE__ */ new Map();
463
+ for (const jwk of keyset.keys.map(toPublicJwk)) {
464
+ if (typeof jwk.kid === "string" && jwk.kid.length > 0) {
465
+ keys.set(jwk.kid, jwk);
466
+ }
467
+ }
468
+ return { keys: [...keys.values()] };
469
+ }
470
+ function signMcpServiceToken(context) {
471
+ if (!context.principalId) {
472
+ throw new Error("MCP OAuth principal is required for Console service auth");
473
+ }
474
+ const jwk = parsePrivateJwk();
475
+ const alg = algForJwk(jwk);
476
+ const now = Math.floor(Date.now() / 1e3);
477
+ const payload = {
478
+ iss: MCP_OAUTH_ISSUER,
479
+ aud: MCP_CONSOLE_SERVICE_AUDIENCE,
480
+ iat: now,
481
+ nbf: now,
482
+ exp: now + MCP_SERVICE_TOKEN_LIFETIME_SECONDS,
483
+ jti: randomUUID(),
484
+ sub: context.principalId,
485
+ act: {
486
+ sub: context.principalId,
487
+ tenant_id: context.tenantId
488
+ },
489
+ [MCP_TENANT_CLAIM]: context.tenantId,
490
+ [MCP_TENANT_ROLE_CLAIM]: context.tenantRole,
491
+ scope: MCP_CONSOLE_SERVICE_SCOPE,
492
+ api_scopes: apiScopesFor(context),
493
+ mcp_scopes: context.scopes
494
+ };
495
+ const header = { alg, kid: jwk.kid, typ: "JWT" };
496
+ const encodedHeader = base64urlJson(header);
497
+ const encodedPayload = base64urlJson(payload);
498
+ const signingInput = `${encodedHeader}.${encodedPayload}`;
499
+ const key = createPrivateKey({ key: jwk, format: "jwk" });
500
+ const signature = alg === "RS256" ? signBytes("RSA-SHA256", Buffer.from(signingInput), key) : signBytes("SHA256", Buffer.from(signingInput), {
501
+ key,
502
+ dsaEncoding: "ieee-p1363"
503
+ });
504
+ return `${signingInput}.${signature.toString("base64url")}`;
505
+ }
506
+
507
+ // src/lib/client.ts
508
+ var MISSING_HTTP_AUTH_CONTEXT_ERROR = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
509
+ function getClient() {
510
+ const oauthContext = tenantAuthContext();
511
+ if (oauthContext) {
512
+ const serviceToken = signMcpServiceToken(oauthContext);
513
+ const client = {
514
+ lastRequestId: null,
515
+ commerce: void 0,
516
+ collections: void 0,
517
+ community: void 0
518
+ };
519
+ const onRequestId = (id) => {
520
+ client.lastRequestId = id;
521
+ };
522
+ client.commerce = new ServerCommerceClient({
523
+ secretKey: serviceToken,
524
+ onRequestId
525
+ });
526
+ client.collections = new CollectionClient(
527
+ "",
528
+ serviceToken,
529
+ void 0,
530
+ void 0,
531
+ onRequestId
532
+ );
533
+ const community = new CommunityClient({ secretKey: serviceToken });
534
+ const moderation = new ModerationApi({ secretKey: serviceToken, onRequestId });
535
+ client.community = Object.assign(community, {
536
+ moderation: {
537
+ banCustomer: moderation.banCustomer.bind(moderation),
538
+ unbanCustomer: moderation.unbanCustomer.bind(moderation)
539
+ }
540
+ });
541
+ return client;
542
+ }
543
+ if (hasRequestContext()) throw new Error(MISSING_HTTP_AUTH_CONTEXT_ERROR);
544
+ const secretKey = process.env.SOFTWARE_SECRET_KEY;
545
+ const publishableKey = process.env.SOFTWARE_PUBLISHABLE_KEY || process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY;
546
+ if (!secretKey) {
547
+ throw new Error(
548
+ "Authentication required. Set SOFTWARE_SECRET_KEY for stdio transport."
549
+ );
550
+ }
551
+ if (!secretKey.startsWith("sk01_") && !secretKey.startsWith("pat01_")) {
552
+ throw new Error("Invalid SOFTWARE_SECRET_KEY format. Expected sk01_ or pat01_ token.");
553
+ }
554
+ if (!publishableKey) {
555
+ throw new Error(
556
+ "publishableKey is required. Set SOFTWARE_PUBLISHABLE_KEY for stdio transport. It is used for rate limiting and monthly quota enforcement via the edge proxy."
557
+ );
558
+ }
559
+ return createServerClient({
560
+ publishableKey,
561
+ secretKey
562
+ });
563
+ }
564
+
118
565
  // src/tools/query-collection.ts
566
+ import { COLLECTIONS } from "@01.software/sdk";
119
567
  var schema = {
120
568
  collection: z.enum(COLLECTIONS).describe("Collection name (required)"),
121
569
  where: z.string().optional().describe(
@@ -206,201 +654,12 @@ async function getCollectionById({
206
654
  }
207
655
  }
208
656
 
209
- // src/tools/create-collection.ts
657
+ // src/tools/get-order.ts
210
658
  import { z as z3 } from "zod";
211
- import { COLLECTIONS as COLLECTIONS3 } from "@01.software/sdk";
212
659
  var schema3 = {
213
- collection: z3.enum(COLLECTIONS3).describe("Collection name (required)"),
214
- data: z3.record(z3.string(), z3.unknown()).describe(
215
- "Data to create (required). Use get-collection-schema first to understand writable fields, hidden fields, and required metadata. Server will validate and reject invalid fields."
216
- )
660
+ orderNumber: z3.string().min(1).describe("Order number to look up (required)")
217
661
  };
218
662
  var metadata3 = {
219
- name: "create-collection",
220
- description: "Create a new collection item",
221
- annotations: {
222
- title: "Create collection item",
223
- readOnlyHint: false,
224
- destructiveHint: false,
225
- idempotentHint: false
226
- }
227
- };
228
- async function createCollection({
229
- collection,
230
- data
231
- }) {
232
- try {
233
- const client = getClient().collections;
234
- const result = await client.from(collection).create(data);
235
- return toolSuccess({ data: result.doc, message: result.message });
236
- } catch (error) {
237
- return toolError(error);
238
- }
239
- }
240
-
241
- // src/tools/update-collection.ts
242
- import { z as z4 } from "zod";
243
- import { COLLECTIONS as COLLECTIONS4 } from "@01.software/sdk";
244
- var schema4 = {
245
- collection: z4.enum(COLLECTIONS4).describe("Collection name (required)"),
246
- id: z4.string().min(1).describe("Item ID (required)"),
247
- data: z4.record(z4.string(), z4.unknown()).describe(
248
- "Data to update (required). Use get-collection-by-id first to check current structure, then get-collection-schema to confirm writable fields and required metadata. Server will validate and reject invalid fields."
249
- )
250
- };
251
- var metadata4 = {
252
- name: "update-collection",
253
- description: "Update an existing collection item",
254
- annotations: {
255
- title: "Update collection item",
256
- readOnlyHint: false,
257
- destructiveHint: true,
258
- idempotentHint: true
259
- }
260
- };
261
- async function updateCollection({
262
- collection,
263
- id,
264
- data
265
- }) {
266
- try {
267
- const client = getClient().collections;
268
- const result = await client.from(collection).update(id, data);
269
- return toolSuccess({ data: result.doc, message: result.message });
270
- } catch (error) {
271
- return toolError(error);
272
- }
273
- }
274
-
275
- // src/tools/delete-collection.ts
276
- import { z as z5 } from "zod";
277
- import { COLLECTIONS as COLLECTIONS5 } from "@01.software/sdk";
278
- var schema5 = {
279
- collection: z5.enum(COLLECTIONS5).describe("Collection name (required)"),
280
- id: z5.string().min(1).describe("Item ID (required)")
281
- };
282
- var metadata5 = {
283
- name: "delete-collection",
284
- description: "Delete a collection item",
285
- annotations: {
286
- title: "Delete collection item",
287
- readOnlyHint: false,
288
- destructiveHint: true,
289
- idempotentHint: true
290
- }
291
- };
292
- async function deleteCollection({
293
- collection,
294
- id
295
- }) {
296
- try {
297
- const client = getClient();
298
- await client.collections.from(collection).remove(id);
299
- return toolSuccess({ message: "Deleted successfully." });
300
- } catch (error) {
301
- return toolError(error);
302
- }
303
- }
304
-
305
- // src/tools/delete-many-collection.ts
306
- import { z as z6 } from "zod";
307
- import { COLLECTIONS as COLLECTIONS6 } from "@01.software/sdk";
308
- var schema6 = {
309
- collection: z6.enum(COLLECTIONS6).describe("Collection name (required)"),
310
- where: z6.string().describe(
311
- `Filter conditions (JSON string, required). Determines which items to delete. Example: '{"status":{"equals":"archived"}}'`
312
- )
313
- };
314
- var metadata6 = {
315
- name: "delete-many-collection",
316
- description: "Bulk delete collection items matching a filter. All matching items will be permanently deleted.",
317
- annotations: {
318
- title: "Bulk delete collection items",
319
- readOnlyHint: false,
320
- destructiveHint: true,
321
- idempotentHint: true
322
- }
323
- };
324
- async function deleteManyCollection({
325
- collection,
326
- where
327
- }) {
328
- try {
329
- const client = getClient().collections;
330
- const parsed = parseJsonWhere(where);
331
- if (!parsed.success) return parsed.error;
332
- if (!parsed.data || typeof parsed.data !== "object" || Object.keys(parsed.data).length === 0) {
333
- return toolError(
334
- new Error(
335
- 'Empty "where" filter is not allowed for bulk deletes. Provide at least one filter condition.'
336
- )
337
- );
338
- }
339
- const result = await client.from(collection).removeMany(parsed.data);
340
- return toolSuccess({
341
- totalDocs: result.totalDocs,
342
- message: `Deleted ${result.totalDocs} item(s).`
343
- });
344
- } catch (error) {
345
- return toolError(error);
346
- }
347
- }
348
-
349
- // src/tools/update-many-collection.ts
350
- import { z as z7 } from "zod";
351
- import { COLLECTIONS as COLLECTIONS7 } from "@01.software/sdk";
352
- var schema7 = {
353
- collection: z7.enum(COLLECTIONS7).describe("Collection name (required)"),
354
- where: z7.string().describe(
355
- `Filter conditions (JSON string, required). Determines which items to update. Example: '{"status":{"equals":"draft"}}'`
356
- ),
357
- data: z7.record(z7.string(), z7.unknown()).describe(
358
- "Data to update (required). Partial updates supported. Server will validate and reject invalid fields."
359
- )
360
- };
361
- var metadata7 = {
362
- name: "update-many-collection",
363
- description: "Bulk update collection items matching a filter. All matching items will be updated with the provided data.",
364
- annotations: {
365
- title: "Bulk update collection items",
366
- readOnlyHint: false,
367
- destructiveHint: true,
368
- idempotentHint: true
369
- }
370
- };
371
- async function updateManyCollection({
372
- collection,
373
- where,
374
- data
375
- }) {
376
- try {
377
- const client = getClient().collections;
378
- const parsed = parseJsonWhere(where);
379
- if (!parsed.success) return parsed.error;
380
- if (!parsed.data || typeof parsed.data !== "object" || Object.keys(parsed.data).length === 0) {
381
- return toolError(
382
- new Error(
383
- 'Empty "where" filter is not allowed for bulk updates. Provide at least one filter condition.'
384
- )
385
- );
386
- }
387
- const result = await client.from(collection).updateMany(parsed.data, data);
388
- return toolSuccess({
389
- data: result.docs,
390
- totalDocs: result.totalDocs,
391
- message: `Updated ${result.totalDocs} item(s).`
392
- });
393
- } catch (error) {
394
- return toolError(error);
395
- }
396
- }
397
-
398
- // src/tools/get-order.ts
399
- import { z as z8 } from "zod";
400
- var schema8 = {
401
- orderNumber: z8.string().min(1).describe("Order number to look up (required)")
402
- };
403
- var metadata8 = {
404
663
  name: "get-order",
405
664
  description: "Get order details by order number. Returns order with related data (depth:1).",
406
665
  annotations: {
@@ -428,26 +687,26 @@ async function getOrder({
428
687
  }
429
688
 
430
689
  // src/tools/create-order.ts
431
- import { z as z9 } from "zod";
432
- var schema9 = {
433
- pgPaymentId: z9.string().optional().describe("PG payment ID (optional \u2014 omit for free orders)"),
434
- orderNumber: z9.string().min(1).describe("Unique order number (required)"),
435
- customerSnapshot: z9.object({
436
- name: z9.string().optional().describe("Customer name"),
437
- email: z9.string().describe("Customer email (required)"),
438
- phone: z9.string().optional().describe("Customer phone")
690
+ import { z as z4 } from "zod";
691
+ var schema4 = {
692
+ pgPaymentId: z4.string().optional().describe("PG payment ID (optional \u2014 omit for free orders)"),
693
+ orderNumber: z4.string().min(1).describe("Unique order number (required)"),
694
+ customerSnapshot: z4.object({
695
+ name: z4.string().optional().describe("Customer name"),
696
+ email: z4.string().describe("Customer email (required)"),
697
+ phone: z4.string().optional().describe("Customer phone")
439
698
  }).describe("Customer snapshot at time of order (required)"),
440
- shippingAddress: z9.record(z9.string(), z9.unknown()).describe(
699
+ shippingAddress: z4.record(z4.string(), z4.unknown()).describe(
441
700
  "Shipping address object (required). Fields: postalCode, address1, address2, deliveryMessage, recipientName, phone"
442
701
  ),
443
- orderItems: z9.array(z9.record(z9.string(), z9.unknown())).describe(
702
+ orderItems: z4.array(z4.record(z4.string(), z4.unknown())).describe(
444
703
  "Array of order item objects (required). Each: { product, variant, option, quantity, unitPrice?, totalPrice? }"
445
704
  ),
446
- totalAmount: z9.number().nonnegative().describe("Total order amount (required, min 0)"),
447
- shippingAmount: z9.number().nonnegative().optional().describe("Shipping amount (optional, default 0)"),
448
- discountCode: z9.string().optional().describe("Discount code to apply (optional)")
705
+ totalAmount: z4.number().nonnegative().describe("Total order amount (required, min 0)"),
706
+ shippingAmount: z4.number().nonnegative().optional().describe("Shipping amount (optional, default 0)"),
707
+ discountCode: z4.string().optional().describe("Discount code to apply (optional)")
449
708
  };
450
- var metadata9 = {
709
+ var metadata4 = {
451
710
  name: "create-order",
452
711
  description: "Create a new order with products and shipping information. Supports idempotency.",
453
712
  annotations: {
@@ -470,10 +729,10 @@ async function createOrder(params) {
470
729
  }
471
730
 
472
731
  // src/tools/update-order.ts
473
- import { z as z10 } from "zod";
474
- var schema10 = {
475
- orderNumber: z10.string().min(1).describe("Order number (required)"),
476
- status: z10.enum([
732
+ import { z as z5 } from "zod";
733
+ var schema5 = {
734
+ orderNumber: z5.string().min(1).describe("Order number (required)"),
735
+ status: z5.enum([
477
736
  "pending",
478
737
  "paid",
479
738
  "failed",
@@ -486,7 +745,7 @@ var schema10 = {
486
745
  "New order status. Return-related statuses (return_requested, return_processing, returned) must be set via Return endpoints."
487
746
  )
488
747
  };
489
- var metadata10 = {
748
+ var metadata5 = {
490
749
  name: "update-order",
491
750
  description: "Update order status. Automatically adjusts stock on status changes (e.g., canceled restores stock).",
492
751
  annotations: {
@@ -510,17 +769,17 @@ async function updateOrder({
510
769
  }
511
770
 
512
771
  // src/tools/checkout.ts
513
- import { z as z11 } from "zod";
514
- var schema11 = {
515
- cartId: z11.string().min(1).describe("Cart ID to convert to order (required)"),
516
- pgPaymentId: z11.string().optional().describe("PG payment ID (optional \u2014 omit for free orders)"),
517
- orderNumber: z11.string().min(1).describe("Unique order number (required)"),
518
- customerSnapshot: z11.record(z11.string(), z11.unknown()).describe(
772
+ import { z as z6 } from "zod";
773
+ var schema6 = {
774
+ cartId: z6.string().min(1).describe("Cart ID to convert to order (required)"),
775
+ pgPaymentId: z6.string().optional().describe("PG payment ID (optional \u2014 omit for free orders)"),
776
+ orderNumber: z6.string().min(1).describe("Unique order number (required)"),
777
+ customerSnapshot: z6.record(z6.string(), z6.unknown()).describe(
519
778
  "Customer snapshot object (required). Fields: { name?, email, phone? }"
520
779
  ),
521
- discountCode: z11.string().optional().describe("Discount code to apply (optional)")
780
+ discountCode: z6.string().optional().describe("Discount code to apply (optional)")
522
781
  };
523
- var metadata11 = {
782
+ var metadata6 = {
524
783
  name: "checkout",
525
784
  description: "Convert a cart to an order. Validates stock, creates order and transaction, marks cart as completed. Supports idempotency.",
526
785
  annotations: {
@@ -543,21 +802,21 @@ async function checkout(params) {
543
802
  }
544
803
 
545
804
  // src/tools/create-fulfillment.ts
546
- import { z as z12 } from "zod";
547
- var schema12 = {
548
- orderNumber: z12.string().min(1).describe("Order number (required)"),
549
- carrier: z12.string().optional().describe("Shipping carrier name (optional)"),
550
- trackingNumber: z12.string().optional().describe(
805
+ import { z as z7 } from "zod";
806
+ var schema7 = {
807
+ orderNumber: z7.string().min(1).describe("Order number (required)"),
808
+ carrier: z7.string().optional().describe("Shipping carrier name (optional)"),
809
+ trackingNumber: z7.string().optional().describe(
551
810
  'Tracking number (optional). Setting carrier + tracking triggers "shipped" status'
552
811
  ),
553
- items: z12.array(
554
- z12.object({
555
- orderItem: z12.string().min(1).describe("Order item ID"),
556
- quantity: z12.number().int().positive().describe("Quantity to fulfill")
812
+ items: z7.array(
813
+ z7.object({
814
+ orderItem: z7.string().min(1).describe("Order item ID"),
815
+ quantity: z7.number().int().positive().describe("Quantity to fulfill")
557
816
  })
558
817
  ).describe("Array of items to fulfill (required)")
559
818
  };
560
- var metadata12 = {
819
+ var metadata7 = {
561
820
  name: "create-fulfillment",
562
821
  description: "Create a shipment/fulfillment for order items. Auto-updates order status (paid \u2192 preparing \u2192 shipped).",
563
822
  annotations: {
@@ -588,20 +847,20 @@ async function createFulfillment({
588
847
  }
589
848
 
590
849
  // src/tools/update-fulfillment.ts
591
- import { z as z13 } from "zod";
592
- var schema13 = {
593
- fulfillmentId: z13.string().min(1).describe("Fulfillment ID (required)"),
594
- status: z13.enum(["packed", "shipped", "delivered", "failed"]).describe(
850
+ import { z as z8 } from "zod";
851
+ var schema8 = {
852
+ fulfillmentId: z8.string().min(1).describe("Fulfillment ID (required)"),
853
+ status: z8.enum(["packed", "shipped", "delivered", "failed"]).describe(
595
854
  "New fulfillment status (required). FSM: pending\u2192packed/shipped/failed, packed\u2192shipped/failed, shipped\u2192delivered/failed"
596
855
  ),
597
- carrier: z13.string().optional().describe(
856
+ carrier: z8.string().optional().describe(
598
857
  "Shipping carrier (optional, changeable only in pending/packed status)"
599
858
  ),
600
- trackingNumber: z13.string().optional().describe(
859
+ trackingNumber: z8.string().optional().describe(
601
860
  "Tracking number (optional, changeable only in pending/packed status)"
602
861
  )
603
862
  };
604
- var metadata13 = {
863
+ var metadata8 = {
605
864
  name: "update-fulfillment",
606
865
  description: "Update fulfillment status, carrier, and tracking number. Auto-updates order status when all fulfillments are delivered.",
607
866
  annotations: {
@@ -631,15 +890,134 @@ async function updateFulfillment({
631
890
  }
632
891
  }
633
892
 
893
+ // ../../packages/contracts/src/tenant/index.ts
894
+ import { z as z9 } from "zod";
895
+ var tenantFieldConfigStateSchema = z9.object({
896
+ hiddenFields: z9.array(z9.string()),
897
+ isHidden: z9.boolean()
898
+ }).strict();
899
+ var tenantContextQuerySchema = z9.object({
900
+ counts: z9.literal("true").optional()
901
+ }).strict();
902
+ var tenantContextToolInputSchema = z9.object({
903
+ includeCounts: z9.boolean().optional().default(false).describe(
904
+ "Include per-collection document counts and config status (bypasses cache, slower)"
905
+ )
906
+ }).strict();
907
+ var tenantContextResponseSchema = z9.object({
908
+ tenant: z9.object({
909
+ id: z9.string(),
910
+ name: z9.string(),
911
+ plan: z9.string(),
912
+ planSource: z9.string().optional(),
913
+ authoritative: z9.boolean().optional(),
914
+ capabilityVersion: z9.string().optional(),
915
+ isDevMode: z9.boolean()
916
+ }).strict(),
917
+ features: z9.array(z9.string()),
918
+ collections: z9.object({
919
+ active: z9.array(z9.string()),
920
+ inactive: z9.array(z9.string())
921
+ }).strict(),
922
+ fieldConfigs: z9.record(z9.string(), tenantFieldConfigStateSchema),
923
+ counts: z9.record(z9.string(), z9.number()).optional(),
924
+ config: z9.object({
925
+ webhookConfigured: z9.boolean()
926
+ }).strict().optional()
927
+ }).strict();
928
+ var COLLECTION_SCHEMA_CONTRACT_VERSION = 1;
929
+ var collectionSchemaEndpointParamsSchema = z9.object({
930
+ collectionSlug: z9.string().min(1, "collectionSlug is required")
931
+ }).strict();
932
+ function createCollectionSchemaToolInputSchema(collections) {
933
+ return z9.object({
934
+ collection: z9.enum(collections).describe("Collection name (required)")
935
+ }).strict();
936
+ }
937
+ var collectionFieldOptionSchema = z9.object({
938
+ label: z9.string(),
939
+ value: z9.string()
940
+ }).strict();
941
+ var collectionFieldSchema = z9.lazy(
942
+ () => z9.object({
943
+ name: z9.string(),
944
+ path: z9.string(),
945
+ type: z9.string(),
946
+ required: z9.literal(true).optional(),
947
+ unique: z9.literal(true).optional(),
948
+ hasMany: z9.literal(true).optional(),
949
+ relationTo: z9.union([z9.string(), z9.array(z9.string())]).optional(),
950
+ options: z9.array(collectionFieldOptionSchema).optional(),
951
+ hidden: z9.literal(true).optional(),
952
+ systemManaged: z9.literal(true).optional(),
953
+ writable: z9.boolean().optional(),
954
+ fields: z9.array(collectionFieldSchema).optional()
955
+ }).strict()
956
+ );
957
+ var collectionSchemaResponseSchema = z9.object({
958
+ contractVersion: z9.literal(COLLECTION_SCHEMA_CONTRACT_VERSION),
959
+ mode: z9.literal("effective"),
960
+ collection: z9.object({
961
+ slug: z9.string(),
962
+ timestamps: z9.boolean(),
963
+ alwaysActive: z9.boolean(),
964
+ feature: z9.string().nullable(),
965
+ systemFields: z9.array(z9.string()),
966
+ visibility: z9.object({
967
+ collectionHidden: z9.boolean(),
968
+ hiddenFields: z9.array(z9.string())
969
+ }).strict(),
970
+ fields: z9.array(collectionFieldSchema)
971
+ }).strict()
972
+ }).strict();
973
+
974
+ // ../../packages/contracts/src/ecommerce/index.ts
975
+ import { z as z10 } from "zod";
976
+ var transactionStatusSchema = z10.enum([
977
+ "pending",
978
+ "paid",
979
+ "failed",
980
+ "canceled"
981
+ ]);
982
+ var updateTransactionSchema = z10.object({
983
+ pgPaymentId: z10.string().min(1, "pgPaymentId is required").describe("PG payment ID (required)"),
984
+ status: transactionStatusSchema.describe(
985
+ "New transaction status (required)"
986
+ ),
987
+ paymentMethod: z10.string().optional().describe("Payment method (optional)"),
988
+ receiptUrl: z10.string().optional().describe("Receipt URL (optional)"),
989
+ paymentKey: z10.string().min(1).optional().describe("Provider payment key for verified paid confirmation"),
990
+ amount: z10.number().int().positive().optional().describe("Provider-confirmed amount for verified paid confirmation")
991
+ }).strict();
992
+ var UpdateTransactionSchema = updateTransactionSchema;
993
+ var returnReasonSchema = z10.enum([
994
+ "change_of_mind",
995
+ "defective",
996
+ "wrong_delivery",
997
+ "damaged",
998
+ "other"
999
+ ]);
1000
+ var restockActionSchema = z10.enum(["return_to_stock", "discard"]);
1001
+ var returnWithRefundItemSchema = z10.object({
1002
+ orderItem: z10.union([z10.string(), z10.number()]).transform(String),
1003
+ quantity: z10.number().int().positive("quantity must be a positive integer"),
1004
+ restockAction: restockActionSchema.default("return_to_stock")
1005
+ }).strict();
1006
+ var returnWithRefundSchema = z10.object({
1007
+ orderNumber: z10.string().min(1, "orderNumber is required").describe("Order number (required)"),
1008
+ reason: returnReasonSchema.optional().describe("Return reason (optional)"),
1009
+ reasonDetail: z10.string().optional().describe("Detailed reason text (optional)"),
1010
+ returnItems: z10.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
1011
+ refundAmount: z10.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
1012
+ pgPaymentId: z10.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
1013
+ paymentKey: z10.string().min(1).optional().describe("Provider payment key for verified refund"),
1014
+ refundReceiptUrl: z10.string().optional().describe("Refund receipt URL (optional)")
1015
+ }).strict();
1016
+ var ReturnWithRefundSchema = returnWithRefundSchema;
1017
+
634
1018
  // src/tools/update-transaction.ts
635
- import { z as z14 } from "zod";
636
- var schema14 = {
637
- pgPaymentId: z14.string().min(1).describe("PG payment ID (required)"),
638
- status: z14.enum(["pending", "paid", "failed", "canceled"]).describe("New transaction status (required)"),
639
- paymentMethod: z14.string().optional().describe("Payment method (optional)"),
640
- receiptUrl: z14.string().optional().describe("Receipt URL (optional)")
641
- };
642
- var metadata14 = {
1019
+ var schema9 = UpdateTransactionSchema.shape;
1020
+ var metadata9 = {
643
1021
  name: "update-transaction",
644
1022
  description: "Update transaction status, payment method, and receipt URL.",
645
1023
  annotations: {
@@ -653,16 +1031,21 @@ async function updateTransaction({
653
1031
  pgPaymentId,
654
1032
  status,
655
1033
  paymentMethod,
656
- receiptUrl
1034
+ receiptUrl,
1035
+ paymentKey,
1036
+ amount
657
1037
  }) {
658
1038
  try {
659
1039
  const client = getClient();
660
- const result = await client.commerce.orders.updateTransaction({
1040
+ const params = {
661
1041
  pgPaymentId,
662
1042
  status,
663
1043
  paymentMethod,
664
- receiptUrl
665
- });
1044
+ receiptUrl,
1045
+ paymentKey,
1046
+ amount
1047
+ };
1048
+ const result = await client.commerce.orders.updateTransaction(params);
666
1049
  return toolSuccess({ data: result });
667
1050
  } catch (error) {
668
1051
  return toolError(error);
@@ -670,20 +1053,20 @@ async function updateTransaction({
670
1053
  }
671
1054
 
672
1055
  // src/tools/create-return.ts
673
- import { z as z15 } from "zod";
674
- var schema15 = {
675
- orderNumber: z15.string().min(1).describe("Order number (required)"),
676
- reason: z15.enum(["change_of_mind", "defective", "wrong_delivery", "damaged", "other"]).optional().describe("Return reason (optional)"),
677
- reasonDetail: z15.string().optional().describe("Detailed reason text (optional)"),
678
- returnItems: z15.array(
679
- z15.object({
680
- orderItem: z15.string().min(1).describe("Order item ID"),
681
- quantity: z15.number().int().positive().describe("Quantity to return")
1056
+ import { z as z11 } from "zod";
1057
+ var schema10 = {
1058
+ orderNumber: z11.string().min(1).describe("Order number (required)"),
1059
+ reason: z11.enum(["change_of_mind", "defective", "wrong_delivery", "damaged", "other"]).optional().describe("Return reason (optional)"),
1060
+ reasonDetail: z11.string().optional().describe("Detailed reason text (optional)"),
1061
+ returnItems: z11.array(
1062
+ z11.object({
1063
+ orderItem: z11.string().min(1).describe("Order item ID"),
1064
+ quantity: z11.number().int().positive().describe("Quantity to return")
682
1065
  })
683
1066
  ).describe("Array of products to return (required)"),
684
- refundAmount: z15.number().nonnegative().describe("Refund amount (required, min 0)")
1067
+ refundAmount: z11.number().nonnegative().describe("Refund amount (required, min 0)")
685
1068
  };
686
- var metadata15 = {
1069
+ var metadata10 = {
687
1070
  name: "create-return",
688
1071
  description: "Create a return request for an order. Only works for delivered/confirmed orders. Updates order status to return_requested.",
689
1072
  annotations: {
@@ -716,14 +1099,14 @@ async function createReturn({
716
1099
  }
717
1100
 
718
1101
  // src/tools/update-return.ts
719
- import { z as z16 } from "zod";
720
- var schema16 = {
721
- returnId: z16.string().min(1).describe("Return ID (required)"),
722
- status: z16.enum(["processing", "approved", "rejected", "completed"]).describe(
1102
+ import { z as z12 } from "zod";
1103
+ var schema11 = {
1104
+ returnId: z12.string().min(1).describe("Return ID (required)"),
1105
+ status: z12.enum(["processing", "approved", "rejected", "completed"]).describe(
723
1106
  "New return status (required). Valid transitions: requested\u2192processing/rejected, processing\u2192approved/rejected, approved\u2192completed"
724
1107
  )
725
1108
  };
726
- var metadata16 = {
1109
+ var metadata11 = {
727
1110
  name: "update-return",
728
1111
  description: "Update return status with FSM validation. Restores inventory on completion, reverts order status on rejection.",
729
1112
  annotations: {
@@ -747,22 +1130,8 @@ async function updateReturn({
747
1130
  }
748
1131
 
749
1132
  // src/tools/return-with-refund.ts
750
- import { z as z17 } from "zod";
751
- var schema17 = {
752
- orderNumber: z17.string().min(1).describe("Order number (required)"),
753
- reason: z17.enum(["change_of_mind", "defective", "wrong_delivery", "damaged", "other"]).optional().describe("Return reason (optional)"),
754
- reasonDetail: z17.string().optional().describe("Detailed reason text (optional)"),
755
- returnItems: z17.array(
756
- z17.object({
757
- orderItem: z17.string().min(1).describe("Order item ID"),
758
- quantity: z17.number().int().positive().describe("Quantity to return")
759
- })
760
- ).describe("Array of products to return (required)"),
761
- refundAmount: z17.number().nonnegative().describe("Refund amount (required, min 0)"),
762
- pgPaymentId: z17.string().min(1).describe("PG payment ID for refund (required)"),
763
- refundReceiptUrl: z17.string().optional().describe("Refund receipt URL (optional)")
764
- };
765
- var metadata17 = {
1133
+ var schema12 = ReturnWithRefundSchema.shape;
1134
+ var metadata12 = {
766
1135
  name: "return-with-refund",
767
1136
  description: "Combined return + refund operation. Creates return, restores stock, cancels transaction, updates order status.",
768
1137
  annotations: {
@@ -779,19 +1148,22 @@ async function returnWithRefund({
779
1148
  returnItems,
780
1149
  refundAmount,
781
1150
  pgPaymentId,
1151
+ paymentKey,
782
1152
  refundReceiptUrl
783
1153
  }) {
784
1154
  try {
785
1155
  const client = getClient();
786
- const result = await client.commerce.orders.returnWithRefund({
1156
+ const params = {
787
1157
  orderNumber,
788
1158
  reason,
789
1159
  reasonDetail,
790
1160
  returnItems,
791
1161
  refundAmount,
792
1162
  pgPaymentId,
1163
+ paymentKey,
793
1164
  refundReceiptUrl
794
- });
1165
+ };
1166
+ const result = await client.commerce.orders.returnWithRefund(params);
795
1167
  return toolSuccess({ data: result });
796
1168
  } catch (error) {
797
1169
  return toolError(error);
@@ -799,15 +1171,15 @@ async function returnWithRefund({
799
1171
  }
800
1172
 
801
1173
  // src/tools/add-cart-item.ts
802
- import { z as z18 } from "zod";
803
- var schema18 = {
804
- cartId: z18.string().min(1).describe("Cart ID (required)"),
805
- product: z18.string().min(1).describe("Product ID (required)"),
806
- variant: z18.string().min(1).describe("Product variant ID (required)"),
807
- option: z18.string().min(1).describe("Product option ID (required)"),
808
- quantity: z18.number().int().positive().describe("Quantity to add (required, positive integer)")
1174
+ import { z as z13 } from "zod";
1175
+ var schema13 = {
1176
+ cartId: z13.string().min(1).describe("Cart ID (required)"),
1177
+ product: z13.string().min(1).describe("Product ID (required)"),
1178
+ variant: z13.string().min(1).describe("Product variant ID (required)"),
1179
+ option: z13.string().min(1).describe("Product option ID (required)"),
1180
+ quantity: z13.number().int().positive().describe("Quantity to add (required, positive integer)")
809
1181
  };
810
- var metadata18 = {
1182
+ var metadata13 = {
811
1183
  name: "add-cart-item",
812
1184
  description: "Add a product to cart. Validates stock, merges quantity if item already exists, recalculates totals.",
813
1185
  annotations: {
@@ -840,12 +1212,12 @@ async function addCartItem({
840
1212
  }
841
1213
 
842
1214
  // src/tools/update-cart-item.ts
843
- import { z as z19 } from "zod";
844
- var schema19 = {
845
- cartItemId: z19.string().min(1).describe("Cart item ID (required)"),
846
- quantity: z19.number().int().positive().describe("New quantity (required, positive integer)")
1215
+ import { z as z14 } from "zod";
1216
+ var schema14 = {
1217
+ cartItemId: z14.string().min(1).describe("Cart item ID (required)"),
1218
+ quantity: z14.number().int().positive().describe("New quantity (required, positive integer)")
847
1219
  };
848
- var metadata19 = {
1220
+ var metadata14 = {
849
1221
  name: "update-cart-item",
850
1222
  description: "Update cart item quantity. Validates stock availability, recalculates cart totals.",
851
1223
  annotations: {
@@ -869,11 +1241,11 @@ async function updateCartItem({
869
1241
  }
870
1242
 
871
1243
  // src/tools/remove-cart-item.ts
872
- import { z as z20 } from "zod";
873
- var schema20 = {
874
- cartItemId: z20.string().min(1).describe("Cart item ID to remove (required)")
1244
+ import { z as z15 } from "zod";
1245
+ var schema15 = {
1246
+ cartItemId: z15.string().min(1).describe("Cart item ID to remove (required)")
875
1247
  };
876
- var metadata20 = {
1248
+ var metadata15 = {
877
1249
  name: "remove-cart-item",
878
1250
  description: "Remove an item from cart. Recalculates cart totals after removal.",
879
1251
  annotations: {
@@ -896,12 +1268,12 @@ async function removeCartItem({
896
1268
  }
897
1269
 
898
1270
  // src/tools/apply-discount.ts
899
- import { z as z21 } from "zod";
900
- var schema21 = {
901
- cartId: z21.string().min(1).describe("Cart ID (required)"),
902
- discountCode: z21.string().describe("Discount code to apply (required)")
1271
+ import { z as z16 } from "zod";
1272
+ var schema16 = {
1273
+ cartId: z16.string().min(1).describe("Cart ID (required)"),
1274
+ discountCode: z16.string().describe("Discount code to apply (required)")
903
1275
  };
904
- var metadata21 = {
1276
+ var metadata16 = {
905
1277
  name: "apply-discount",
906
1278
  description: "Apply a discount code to a cart. Validates the code, updates cart totals, and sets free shipping if applicable.",
907
1279
  annotations: {
@@ -925,11 +1297,11 @@ async function applyDiscount({
925
1297
  }
926
1298
 
927
1299
  // src/tools/remove-discount.ts
928
- import { z as z22 } from "zod";
929
- var schema22 = {
930
- cartId: z22.string().min(1).describe("Cart ID (required)")
1300
+ import { z as z17 } from "zod";
1301
+ var schema17 = {
1302
+ cartId: z17.string().min(1).describe("Cart ID (required)")
931
1303
  };
932
- var metadata22 = {
1304
+ var metadata17 = {
933
1305
  name: "remove-discount",
934
1306
  description: "Remove the applied discount code from a cart and recalculate totals.",
935
1307
  annotations: {
@@ -952,11 +1324,11 @@ async function removeDiscount({
952
1324
  }
953
1325
 
954
1326
  // src/tools/clear-cart.ts
955
- import { z as z23 } from "zod";
956
- var schema23 = {
957
- cartId: z23.string().min(1).describe("Cart ID (required)")
1327
+ import { z as z18 } from "zod";
1328
+ var schema18 = {
1329
+ cartId: z18.string().min(1).describe("Cart ID (required)")
958
1330
  };
959
- var metadata23 = {
1331
+ var metadata18 = {
960
1332
  name: "clear-cart",
961
1333
  description: "Remove all items from a cart, reset discount and amounts. Shipping fee is preserved.",
962
1334
  annotations: {
@@ -979,12 +1351,12 @@ async function clearCart({
979
1351
  }
980
1352
 
981
1353
  // src/tools/validate-discount.ts
982
- import { z as z24 } from "zod";
983
- var schema24 = {
984
- code: z24.string().describe("Discount code to validate (required)"),
985
- orderAmount: z24.number().describe("Order amount for validation (required)")
1354
+ import { z as z19 } from "zod";
1355
+ var schema19 = {
1356
+ code: z19.string().describe("Discount code to validate (required)"),
1357
+ orderAmount: z19.number().describe("Order amount for validation (required)")
986
1358
  };
987
- var metadata24 = {
1359
+ var metadata19 = {
988
1360
  name: "validate-discount",
989
1361
  description: "Validate a discount code. Checks active status, date range, usage limits, minimum order amount, and calculates discount.",
990
1362
  annotations: {
@@ -1011,13 +1383,13 @@ async function validateDiscount({
1011
1383
  }
1012
1384
 
1013
1385
  // src/tools/calculate-shipping.ts
1014
- import { z as z25 } from "zod";
1015
- var schema25 = {
1016
- shippingPolicyId: z25.string().optional().describe("Shipping policy ID (uses default policy if omitted)"),
1017
- orderAmount: z25.number().describe("Order amount for fee calculation (required)"),
1018
- postalCode: z25.string().optional().describe("Postal code for Jeju surcharge detection (63000-63644)")
1386
+ import { z as z20 } from "zod";
1387
+ var schema20 = {
1388
+ shippingPolicyId: z20.string().optional().describe("Shipping policy ID (uses default policy if omitted)"),
1389
+ orderAmount: z20.number().describe("Order amount for fee calculation (required)"),
1390
+ postalCode: z20.string().optional().describe("Postal code for Jeju surcharge detection (63000-63644)")
1019
1391
  };
1020
- var metadata25 = {
1392
+ var metadata20 = {
1021
1393
  name: "calculate-shipping",
1022
1394
  description: "Calculate shipping fee based on order amount and postal code. Supports free shipping threshold and Jeju surcharge.",
1023
1395
  annotations: {
@@ -1046,18 +1418,18 @@ async function calculateShipping({
1046
1418
  }
1047
1419
 
1048
1420
  // src/tools/stock-check.ts
1049
- import { z as z26 } from "zod";
1050
- var schema26 = {
1051
- items: z26.array(
1052
- z26.object({
1053
- variantId: z26.string().describe("Product variant ID"),
1054
- quantity: z26.number().int().positive().describe("Requested quantity")
1421
+ import { z as z21 } from "zod";
1422
+ var schema21 = {
1423
+ items: z21.array(
1424
+ z21.object({
1425
+ variantId: z21.string().describe("Product variant ID"),
1426
+ quantity: z21.number().int().positive().describe("Requested quantity")
1055
1427
  })
1056
1428
  ).describe(
1057
1429
  "Array of items to check stock for (required, max 100). Each: { variantId, quantity }"
1058
1430
  )
1059
1431
  };
1060
- var metadata26 = {
1432
+ var metadata21 = {
1061
1433
  name: "stock-check",
1062
1434
  description: "Batch check product option stock availability. Returns per-item availability and an allAvailable flag.",
1063
1435
  annotations: {
@@ -1080,56 +1452,46 @@ async function stockCheck({
1080
1452
  }
1081
1453
 
1082
1454
  // src/tools/get-collection-schema.ts
1083
- import { z as z27 } from "zod";
1084
- import { COLLECTIONS as COLLECTIONS8 } from "@01.software/sdk";
1455
+ import { COLLECTIONS as COLLECTIONS3 } from "@01.software/sdk";
1085
1456
 
1086
1457
  // src/lib/console-api.ts
1087
1458
  import { createHash } from "crypto";
1088
1459
  var BASE_URL = process.env.SOFTWARE_API_URL || "http://localhost:3000";
1089
1460
  var TIMEOUT_MS = 5e3;
1461
+ var MISSING_HTTP_AUTH_CONTEXT_ERROR2 = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
1090
1462
  function resolveAuthHeaderContext() {
1091
- let context = {};
1092
- try {
1093
- const h = headers();
1094
- context = {
1095
- apiKey: h?.["x-api-key"],
1096
- publishableKey: h?.["x-publishable-key"] ?? h?.["x-client-key"]
1463
+ const oauthContext = tenantAuthContext();
1464
+ if (oauthContext) {
1465
+ return {
1466
+ apiKey: signMcpServiceToken(oauthContext),
1467
+ mode: "oauth"
1097
1468
  };
1098
- } catch {
1099
1469
  }
1470
+ if (hasRequestContext()) throw new Error(MISSING_HTTP_AUTH_CONTEXT_ERROR2);
1100
1471
  return {
1101
- apiKey: context.apiKey ?? process.env.SOFTWARE_SECRET_KEY,
1102
- publishableKey: context.publishableKey ?? process.env.SOFTWARE_PUBLISHABLE_KEY ?? process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY
1472
+ apiKey: process.env.SOFTWARE_SECRET_KEY,
1473
+ mode: "stdio",
1474
+ publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY ?? process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY
1103
1475
  };
1104
1476
  }
1105
1477
  function resolveApiKey() {
1106
1478
  const { apiKey } = resolveAuthHeaderContext();
1107
1479
  if (!apiKey || typeof apiKey !== "string") {
1108
1480
  throw new Error(
1109
- "Authentication required. Provide x-api-key header (HTTP) or SOFTWARE_SECRET_KEY env var (stdio)."
1481
+ "Authentication required. Set SOFTWARE_SECRET_KEY for stdio transport."
1110
1482
  );
1111
1483
  }
1112
1484
  return apiKey;
1113
1485
  }
1114
- function hashKey(apiKey) {
1115
- return createHash("sha256").update(apiKey).digest("hex");
1116
- }
1117
- function resolveAuthCacheKey(apiKey) {
1118
- const { publishableKey } = resolveAuthHeaderContext();
1119
- return hashKey(
1120
- JSON.stringify({
1121
- apiKey,
1122
- publishableKey: publishableKey ?? ""
1123
- })
1124
- );
1125
- }
1126
1486
  function buildAuthHeaders(apiKey) {
1127
- const { publishableKey } = resolveAuthHeaderContext();
1128
- const headers2 = {
1487
+ const { mode, publishableKey } = resolveAuthHeaderContext();
1488
+ const headers = {
1129
1489
  Authorization: `Bearer ${apiKey}`
1130
1490
  };
1131
- if (publishableKey) headers2["X-Publishable-Key"] = publishableKey;
1132
- return headers2;
1491
+ if (mode === "stdio" && publishableKey) {
1492
+ headers["X-Publishable-Key"] = publishableKey;
1493
+ }
1494
+ return headers;
1133
1495
  }
1134
1496
  function extractErrorMessage(body) {
1135
1497
  if (!body || typeof body !== "object") return void 0;
@@ -1185,17 +1547,16 @@ async function consolePost(path, body, apiKey) {
1185
1547
  // src/lib/collection-schema.ts
1186
1548
  async function getCollectionSchema(collection) {
1187
1549
  const apiKey = resolveApiKey();
1188
- return consoleGet(
1550
+ const data = await consoleGet(
1189
1551
  `/api/tenants/schema/${encodeURIComponent(collection)}`,
1190
1552
  apiKey
1191
1553
  );
1554
+ return collectionSchemaResponseSchema.parse(data);
1192
1555
  }
1193
1556
 
1194
1557
  // src/tools/get-collection-schema.ts
1195
- var schema27 = {
1196
- collection: z27.enum(COLLECTIONS8).describe("Collection name (required)")
1197
- };
1198
- var metadata27 = {
1558
+ var schema22 = createCollectionSchemaToolInputSchema(COLLECTIONS3).shape;
1559
+ var metadata22 = {
1199
1560
  name: "get-collection-schema",
1200
1561
  description: "Get the authoritative tenant-aware collection schema from console. Use this before create/update to understand writable fields, hidden fields, required metadata, and collection-level visibility.",
1201
1562
  annotations: {
@@ -1219,48 +1580,22 @@ async function getCollectionSchemaTool({
1219
1580
  }
1220
1581
  }
1221
1582
 
1222
- // src/tools/get-tenant-context.ts
1223
- import { z as z28 } from "zod";
1224
-
1225
1583
  // src/lib/tenant-context.ts
1226
- var TENANT_CONTEXT_CACHE_TTL_MS = 6e4;
1227
- var cache = /* @__PURE__ */ new Map();
1228
1584
  function getTenantContextPath(includeCounts) {
1229
1585
  return includeCounts ? "/api/tenants/context?counts=true" : "/api/tenants/context";
1230
1586
  }
1231
- function getCachedTenantContext(cacheKey) {
1232
- const cached = cache.get(cacheKey);
1233
- if (!cached || cached.expiry <= Date.now()) return void 0;
1234
- return cached.data;
1235
- }
1236
1587
  async function getTenantContext(includeCounts = false) {
1237
1588
  const apiKey = resolveApiKey();
1238
- const cacheKey = resolveAuthCacheKey(apiKey);
1239
- if (!includeCounts) {
1240
- const cached = getCachedTenantContext(cacheKey);
1241
- if (cached) return cached;
1242
- }
1243
1589
  const data = await consoleGet(
1244
1590
  getTenantContextPath(includeCounts),
1245
1591
  apiKey
1246
1592
  );
1247
- if (!includeCounts) {
1248
- cache.set(cacheKey, {
1249
- data,
1250
- expiry: Date.now() + TENANT_CONTEXT_CACHE_TTL_MS
1251
- });
1252
- }
1253
- return data;
1254
- }
1255
- function invalidateTenantContextCache() {
1256
- cache.clear();
1593
+ return tenantContextResponseSchema.parse(data);
1257
1594
  }
1258
1595
 
1259
1596
  // src/tools/get-tenant-context.ts
1260
- var schema28 = {
1261
- includeCounts: z28.boolean().optional().default(false).describe("Include per-collection document counts and config status (bypasses cache, slower)")
1262
- };
1263
- var metadata28 = {
1597
+ var schema23 = tenantContextToolInputSchema.shape;
1598
+ var metadata23 = {
1264
1599
  name: "get-tenant-context",
1265
1600
  description: "Get current tenant features, active collections, and field visibility. Call this at the start of every session. Use includeCounts=true to also get per-collection document counts for setup diagnostics.",
1266
1601
  annotations: {
@@ -1270,7 +1605,9 @@ var metadata28 = {
1270
1605
  idempotentHint: true
1271
1606
  }
1272
1607
  };
1273
- async function handler({ includeCounts }) {
1608
+ async function handler({
1609
+ includeCounts
1610
+ }) {
1274
1611
  try {
1275
1612
  const ctx = await getTenantContext(includeCounts);
1276
1613
  const lines = [
@@ -1323,11 +1660,10 @@ async function handler({ includeCounts }) {
1323
1660
  }
1324
1661
  }
1325
1662
  if (ctx.config) {
1663
+ lines.push("", "## Config Status");
1326
1664
  lines.push(
1327
- "",
1328
- "## Config Status"
1665
+ `- Webhook configured: ${ctx.config.webhookConfigured ? "Yes" : "No"}`
1329
1666
  );
1330
- lines.push(`- Webhook configured: ${ctx.config.webhookConfigured ? "Yes" : "No"}`);
1331
1667
  }
1332
1668
  return toolSuccess({ context: lines.join("\n") });
1333
1669
  } catch (error) {
@@ -1336,21 +1672,15 @@ async function handler({ includeCounts }) {
1336
1672
  }
1337
1673
 
1338
1674
  // src/tools/list-configurable-fields.ts
1339
- import { z as z29 } from "zod";
1675
+ import { z as z22 } from "zod";
1340
1676
 
1341
1677
  // src/lib/field-config.ts
1342
- var cache2 = /* @__PURE__ */ new Map();
1343
- var CACHE_TTL = 6e4;
1344
1678
  async function fetchFieldConfigs() {
1345
1679
  const apiKey = resolveApiKey();
1346
- const cacheKey = resolveAuthCacheKey(apiKey);
1347
- const cached = cache2.get(cacheKey);
1348
- if (cached && cached.expiry > Date.now()) return cached.data;
1349
1680
  const data = await consoleGet(
1350
1681
  "/api/field-configs/list",
1351
1682
  apiKey
1352
1683
  );
1353
- cache2.set(cacheKey, { data, expiry: Date.now() + CACHE_TTL });
1354
1684
  return data;
1355
1685
  }
1356
1686
  async function saveFieldConfig(body) {
@@ -1362,16 +1692,15 @@ async function saveFieldConfig(body) {
1362
1692
  );
1363
1693
  }
1364
1694
  function invalidateFieldConfigCache() {
1365
- cache2.clear();
1366
1695
  }
1367
1696
 
1368
1697
  // src/tools/list-configurable-fields.ts
1369
- var schema29 = {
1370
- collection: z29.string().optional().describe(
1698
+ var schema24 = {
1699
+ collection: z22.string().optional().describe(
1371
1700
  "Filter by collection slug (optional \u2014 returns all if omitted). Use this filter to reduce response size when you know which collection to check."
1372
1701
  )
1373
1702
  };
1374
- var metadata29 = {
1703
+ var metadata24 = {
1375
1704
  name: "list-configurable-fields",
1376
1705
  description: "List all configurable fields for tenant collections with current visibility state. Shows which fields can be shown/hidden and their current status. Returns all collections including inactive features \u2014 cross-reference with get-tenant-context for active features. Response includes ~300 fields across 47 collections \u2014 use collection filter when possible.",
1377
1706
  annotations: {
@@ -1402,17 +1731,17 @@ async function listConfigurableFields(params) {
1402
1731
  }
1403
1732
 
1404
1733
  // src/tools/update-field-config.ts
1405
- import { z as z30 } from "zod";
1406
- var schema30 = {
1407
- collection: z30.string().min(1).describe("Collection slug (required)"),
1408
- hiddenFields: z30.array(z30.string().min(1).max(200)).max(300).describe(
1734
+ import { z as z23 } from "zod";
1735
+ var schema25 = {
1736
+ collection: z23.string().min(1).describe("Collection slug (required)"),
1737
+ hiddenFields: z23.array(z23.string().min(1).max(200)).max(300).describe(
1409
1738
  "Fields to hide (required). This is a FULL REPLACE \u2014 fields NOT in this list will be shown. Pass [] to show all fields. Use list-configurable-fields first to see available field paths."
1410
1739
  ),
1411
- isHidden: z30.boolean().optional().describe(
1740
+ isHidden: z23.boolean().optional().describe(
1412
1741
  "Hide the entire collection from Admin Panel (optional). When true, individual hiddenFields are irrelevant."
1413
1742
  )
1414
1743
  };
1415
- var metadata30 = {
1744
+ var metadata25 = {
1416
1745
  name: "update-field-config",
1417
1746
  description: "Update field visibility configuration for a tenant collection. Hidden fields are removed from the Admin Panel UI. IMPORTANT: hiddenFields is a full replace, not a merge. Always call list-configurable-fields first to see current state.",
1418
1747
  annotations: {
@@ -1430,7 +1759,6 @@ async function updateFieldConfig(params) {
1430
1759
  isHidden: params.isHidden
1431
1760
  });
1432
1761
  invalidateFieldConfigCache();
1433
- invalidateTenantContextCache();
1434
1762
  return toolSuccess({
1435
1763
  message: `Field config updated for '${params.collection}'`,
1436
1764
  data: result
@@ -1441,7 +1769,7 @@ async function updateFieldConfig(params) {
1441
1769
  }
1442
1770
 
1443
1771
  // src/tools/sdk-get-recipe.ts
1444
- import { z as z31 } from "zod";
1772
+ import { z as z24 } from "zod";
1445
1773
 
1446
1774
  // src/lib/sdk-recipes.ts
1447
1775
  var recipes = {
@@ -1593,7 +1921,7 @@ const result = await client.collections.from('products').create({
1593
1921
  "Returns result.doc (not the document directly)"
1594
1922
  ],
1595
1923
  relatedResources: ["docs://sdk/query-builder"],
1596
- relatedTools: ["create-collection"]
1924
+ relatedTools: ["query-collection", "get-collection-schema"]
1597
1925
  }
1598
1926
  },
1599
1927
  "update-item": {
@@ -1622,7 +1950,7 @@ const result = await client.collections.from('products').update('product-id', {
1622
1950
  "Partial updates are supported \u2014 omitted fields retain their current value"
1623
1951
  ],
1624
1952
  relatedResources: ["docs://sdk/query-builder"],
1625
- relatedTools: ["update-collection"]
1953
+ relatedTools: ["get-collection-by-id", "get-collection-schema"]
1626
1954
  }
1627
1955
  },
1628
1956
  "delete-item": {
@@ -1646,7 +1974,7 @@ console.log('Deleted:', deleted.title)`,
1646
1974
  "Throws if the item does not exist"
1647
1975
  ],
1648
1976
  relatedResources: ["docs://sdk/query-builder"],
1649
- relatedTools: ["delete-collection"]
1977
+ relatedTools: ["get-collection-by-id", "query-collection"]
1650
1978
  }
1651
1979
  },
1652
1980
  "infinite-scroll": {
@@ -1760,18 +2088,13 @@ const client = createClient({
1760
2088
  })
1761
2089
 
1762
2090
  // --- Register ---
1763
- const { customer, verificationRequired } = await client.customer.register({
2091
+ const { customer } = await client.customer.register({
1764
2092
  name: 'Jane Doe',
1765
2093
  email: 'jane@example.com',
1766
2094
  password: 'securePassword123',
1767
2095
  phone: '+821012345678', // optional
1768
2096
  })
1769
2097
 
1770
- if (verificationRequired) {
1771
- // Tenant has requireEmailVerification enabled.
1772
- // Token delivered via webhook \u2014 prompt user to check email.
1773
- }
1774
-
1775
2098
  // --- Login ---
1776
2099
  const { token, customer: loggedIn } = await client.customer.login({
1777
2100
  email: 'jane@example.com',
@@ -1792,9 +2115,9 @@ await client.customer.forgotPassword('jane@example.com') // sends token via webh
1792
2115
  await client.customer.resetPassword(token, 'newPassword123')`,
1793
2116
  cautions: [
1794
2117
  "customer.register/login/me are only available on Client (not ServerClient)",
1795
- "verificationRequired means no token is returned \u2014 user must verify email first",
2118
+ "registration creates a local customer account; add app-level verification if your project requires it",
1796
2119
  "updateProfile only accepts name, phone, and marketingConsent \u2014 not email or password",
1797
- "forgotPassword sends the token via tenant webhook, not directly to the client"
2120
+ "forgotPassword sends the token to configured tenant webhooks; your webhook handler owns email/SMS delivery"
1798
2121
  ],
1799
2122
  relatedResources: ["docs://sdk/customer-auth", "docs://sdk/getting-started"],
1800
2123
  relatedTools: []
@@ -1828,7 +2151,7 @@ const result = await client.collections.from('images').create(formData as unknow
1828
2151
  "Always set alt text for accessibility"
1829
2152
  ],
1830
2153
  relatedResources: ["docs://sdk/query-builder"],
1831
- relatedTools: ["create-collection"]
2154
+ relatedTools: ["query-collection", "get-collection-schema"]
1832
2155
  }
1833
2156
  },
1834
2157
  "bulk-operations": {
@@ -1864,7 +2187,7 @@ const removed = await client.collections.from('products').removeMany(
1864
2187
  "Very broad where clauses (or empty) will affect all documents in the collection"
1865
2188
  ],
1866
2189
  relatedResources: ["docs://sdk/query-builder"],
1867
- relatedTools: ["update-many-collection", "delete-many-collection"]
2190
+ relatedTools: ["query-collection", "get-collection-schema"]
1868
2191
  }
1869
2192
  }
1870
2193
  };
@@ -1878,8 +2201,8 @@ function getRecipe(goal, runtime = "both") {
1878
2201
  }
1879
2202
 
1880
2203
  // src/tools/sdk-get-recipe.ts
1881
- var schema31 = {
1882
- goal: z31.enum([
2204
+ var schema26 = {
2205
+ goal: z24.enum([
1883
2206
  "fetch-list",
1884
2207
  "fetch-by-id",
1885
2208
  "create-item",
@@ -1891,11 +2214,11 @@ var schema31 = {
1891
2214
  "file-upload",
1892
2215
  "bulk-operations"
1893
2216
  ]).describe("What the user wants to accomplish"),
1894
- runtime: z31.enum(["browser", "server", "both"]).default("both").describe("Target runtime environment"),
1895
- collection: z31.string().optional().describe("Specific collection name if applicable"),
1896
- includeExample: z31.boolean().default(true).describe("Whether to include a full code example")
2217
+ runtime: z24.enum(["browser", "server", "both"]).default("both").describe("Target runtime environment"),
2218
+ collection: z24.string().optional().describe("Specific collection name if applicable"),
2219
+ includeExample: z24.boolean().default(true).describe("Whether to include a full code example")
1897
2220
  };
1898
- var metadata31 = {
2221
+ var metadata26 = {
1899
2222
  name: "sdk-get-recipe",
1900
2223
  description: "Get a complete SDK code recipe for a specific task. Returns recommended approach, code example, and related documentation links. Use this FIRST when the user asks how to do something with the SDK.",
1901
2224
  annotations: {
@@ -1938,7 +2261,7 @@ function handler2({
1938
2261
  }
1939
2262
 
1940
2263
  // src/tools/sdk-search-docs.ts
1941
- import { z as z32 } from "zod";
2264
+ import { z as z25 } from "zod";
1942
2265
 
1943
2266
  // src/lib/sdk-doc-index.ts
1944
2267
  var docIndex = [
@@ -2040,8 +2363,8 @@ var docIndex = [
2040
2363
  // Customer Auth
2041
2364
  {
2042
2365
  title: "Customer Auth \u2014 Login and Register",
2043
- keywords: ["customer", "login", "register", "auth", "authentication", "customer auth", "email verification", "verificationRequired"],
2044
- summary: "client.customer.login({ email, password }) and register({ name, email, password }). If tenant requireEmailVerification is on, register returns verificationRequired: true.",
2366
+ keywords: ["customer", "login", "register", "auth", "authentication", "customer auth"],
2367
+ summary: "client.customer.login({ email, password }) and register({ name, email, password }).",
2045
2368
  resourceUri: "docs://sdk/customer-auth"
2046
2369
  },
2047
2370
  {
@@ -2067,7 +2390,7 @@ var docIndex = [
2067
2390
  {
2068
2391
  title: "Webhooks",
2069
2392
  keywords: ["webhook", "hmac", "signature", "WEBHOOK_SECRET", "server-to-server", "event"],
2070
- summary: "Tenant webhooks deliver server-to-server events (e.g. email verification token, password reset token). Signed with HMAC-SHA256 using PAYLOAD_SECRET.",
2393
+ summary: "Tenant webhooks deliver server-to-server events such as password reset tokens. Signed with HMAC-SHA256 using PAYLOAD_SECRET.",
2071
2394
  resourceUri: "docs://sdk/webhook"
2072
2395
  },
2073
2396
  // Order API
@@ -2113,11 +2436,11 @@ function searchDocs(query, limit = 5) {
2113
2436
  }
2114
2437
 
2115
2438
  // src/tools/sdk-search-docs.ts
2116
- var schema32 = {
2117
- query: z32.string().min(2).describe('Search keyword or phrase (e.g. "infinite scroll", "webhook", "customer login")'),
2118
- limit: z32.number().min(1).max(10).default(5).describe("Maximum results to return (1-10, default: 5)")
2439
+ var schema27 = {
2440
+ query: z25.string().min(2).describe('Search keyword or phrase (e.g. "infinite scroll", "webhook", "customer login")'),
2441
+ limit: z25.number().min(1).max(10).default(5).describe("Maximum results to return (1-10, default: 5)")
2119
2442
  };
2120
- var metadata32 = {
2443
+ var metadata27 = {
2121
2444
  name: "sdk-search-docs",
2122
2445
  description: "Search SDK documentation by keyword. Returns matching topics with summaries and resource links. Use when looking for specific SDK features or patterns.",
2123
2446
  annotations: {
@@ -2152,20 +2475,20 @@ function handler3({
2152
2475
  }
2153
2476
 
2154
2477
  // src/tools/sdk-get-auth-setup.ts
2155
- import { z as z33 } from "zod";
2156
- var schema33 = {
2157
- scenario: z33.enum([
2478
+ import { z as z26 } from "zod";
2479
+ var schema28 = {
2480
+ scenario: z26.enum([
2158
2481
  "browser-client",
2159
2482
  "server-client",
2160
2483
  "customer-auth",
2161
2484
  "mcp-connection",
2162
- "api-key-generation",
2485
+ "server-credentials",
2163
2486
  "webhook-verification"
2164
2487
  ]).describe("Authentication scenario")
2165
2488
  };
2166
- var metadata33 = {
2489
+ var metadata28 = {
2167
2490
  name: "sdk-get-auth-setup",
2168
- description: "Get the correct authentication setup for a specific scenario. Returns env var names, code snippets, and security notes.",
2491
+ description: "Get the current authentication setup for a specific scenario. Returns env var names, code snippets, and security notes.",
2169
2492
  annotations: {
2170
2493
  title: "Get Auth Setup",
2171
2494
  readOnlyHint: true,
@@ -2198,15 +2521,14 @@ const { data } = client.query.useQuery({ collection: 'products' })`,
2198
2521
 
2199
2522
  const client = createServerClient({
2200
2523
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
2201
- secretKey: process.env.SOFTWARE_SECRET_KEY! // usually sk01_..., sometimes pat01_...
2524
+ secretKey: process.env.SOFTWARE_SECRET_KEY!
2202
2525
  })
2203
2526
 
2204
2527
  // Full CRUD operations
2205
2528
  const result = await client.collections.from('products').create({ title: 'New Product' })`,
2206
2529
  notes: [
2207
- "ServerClient has full CRUD access \u2014 never expose the API key in browser",
2208
- "SOFTWARE_SECRET_KEY stores an opaque bearer token; backend services should prefer tenant API keys (sk01_...)",
2209
- "Browser-based CLI/init login flows may provision a user-scoped PAT (pat01_...) with a default tenant",
2530
+ "ServerClient has full CRUD access and must run only in trusted server code",
2531
+ "Store server credentials in environment variables and rotate them from the Console",
2210
2532
  "Use in API routes, server actions, or backend services only",
2211
2533
  "React Query hooks available for reads (useQuery, prefetchQuery, etc.) + mutations (useCreate, useUpdate, useRemove)"
2212
2534
  ]
@@ -2238,90 +2560,68 @@ client.customer.isAuthenticated()`,
2238
2560
  notes: [
2239
2561
  "Customer auth uses the browser Client (not ServerClient)",
2240
2562
  "JWT tokens are managed automatically by the SDK",
2241
- "Tenant may require email verification (requireEmailVerification setting)"
2563
+ "Registration creates a local customer account; add application-level verification if needed"
2242
2564
  ]
2243
2565
  },
2244
2566
  "mcp-connection": {
2245
2567
  title: "MCP Server Connection",
2246
- envVars: ["SOFTWARE_PUBLISHABLE_KEY", "SOFTWARE_SECRET_KEY"],
2568
+ envVars: [],
2247
2569
  code: `# Claude Code
2248
- claude mcp add --transport http \\
2249
- --header "x-api-key: $SOFTWARE_SECRET_KEY" \\
2250
- --header "x-publishable-key: $SOFTWARE_PUBLISHABLE_KEY" \\
2251
- 01software https://mcp.01.software/mcp
2570
+ claude mcp add --transport http 01software https://mcp.01.software/mcp
2252
2571
 
2253
- # Codex project-safe .codex/config.toml
2572
+ # Codex .codex/config.toml
2254
2573
  [mcp_servers.01software]
2255
2574
  url = "https://mcp.01.software/mcp"
2256
2575
 
2257
- [mcp_servers.01software.env_http_headers]
2258
- x-api-key = "SOFTWARE_SECRET_KEY"
2259
- x-publishable-key = "SOFTWARE_PUBLISHABLE_KEY"
2260
-
2261
- # Or use .mcp.json
2576
+ # Or use JSON clients that support OAuth discovery
2262
2577
  {
2263
2578
  "mcpServers": {
2264
2579
  "01software": {
2265
2580
  "type": "http",
2266
- "url": "https://mcp.01.software/mcp",
2267
- "headers": {
2268
- "x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
2269
- "x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
2270
- }
2581
+ "url": "https://mcp.01.software/mcp"
2271
2582
  }
2272
2583
  }
2273
2584
  }`,
2274
2585
  notes: [
2275
- "MCP accepts either a tenant API key (sk01_...) or a personal access token (pat01_...) in x-api-key",
2276
- "HTTP transport also requires x-publishable-key (or legacy x-client-key) for tenant routing, rate limits, and quota enforcement",
2277
- "Codex project scope is appropriate for repo-safe shared configuration, but keep raw sk01_/pat01_ values out of committed files",
2278
- "Use tenant API keys for shared service integrations; PATs are useful for user-scoped local workflows",
2279
- "Never commit raw bearer tokens to repo-local MCP config; prefer environment interpolation, client prompts, OS secret managers, or ignored local files",
2280
- "Avoid passing real tokens directly on shared-machine command lines because shell history and process listings can expose them",
2281
- "stdio transport: use `npx @01.software/cli mcp` with SOFTWARE_PUBLISHABLE_KEY and SOFTWARE_SECRET_KEY env vars"
2586
+ "HTTP MCP uses OAuth discovery and Authorization Code + PKCE",
2587
+ "Clients that cannot complete OAuth discovery are unsupported until a smoke test proves compatibility",
2588
+ "stdio transport remains a local CLI path and is separate from HTTP MCP OAuth discovery"
2282
2589
  ]
2283
2590
  },
2284
- "api-key-generation": {
2285
- title: "API Key Generation",
2591
+ "server-credentials": {
2592
+ title: "Server Credential Management",
2286
2593
  envVars: ["SOFTWARE_PUBLISHABLE_KEY", "SOFTWARE_SECRET_KEY"],
2287
- code: `# API keys are generated from the Console, not in code.
2288
- # Go to Console > Settings > API Keys and click "Create API Key".
2289
- # The generated key has the format: sk01_{40hex}
2290
- # Copy the publishable key from the same tenant.
2594
+ code: `# Server credentials are managed from the Console, not in code.
2595
+ # Copy both values from the same tenant.
2291
2596
 
2292
- # Use them together for MCP, CLI, and server SDK calls:
2293
- export SOFTWARE_PUBLISHABLE_KEY=pk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2597
+ # Use them together for CLI and server SDK calls.
2598
+ export SOFTWARE_PUBLISHABLE_KEY=pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2294
2599
  export SOFTWARE_SECRET_KEY=sk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`,
2295
2600
  notes: [
2296
- "API keys are sk01_{40hex} opaque bearer tokens",
2297
2601
  "The matching SOFTWARE_PUBLISHABLE_KEY is still required for tenant routing, rate limits, and quota enforcement",
2298
- "Browser-based CLI/init login may issue pat01_{40hex} personal access tokens for user-scoped workflows",
2299
- "Used for MCP and REST API authentication via x-api-key header or Authorization: Bearer",
2300
- "Generate keys from Console > Settings > API Keys \u2014 never derive them in code"
2602
+ "Used for REST/SDK authentication in trusted server contexts",
2603
+ "Manage credentials from the Console and rotate them on exposure"
2301
2604
  ]
2302
2605
  },
2303
2606
  "webhook-verification": {
2304
2607
  title: "Webhook Verification",
2305
2608
  envVars: ["WEBHOOK_SECRET"],
2306
- code: `import { handleWebhook } from '@01.software/sdk/webhook'
2609
+ code: `import { handleWebhook, createCustomerAuthWebhookHandler } from '@01.software/sdk/webhook'
2610
+
2611
+ const customerAuthHandler = createCustomerAuthWebhookHandler({
2612
+ passwordReset: sendPasswordResetEmail,
2613
+ })
2307
2614
 
2308
2615
  export async function POST(request: Request) {
2309
- return handleWebhook(request, async (event) => {
2310
- // event.collection, event.operation, event.data
2311
- switch (event.operation) {
2312
- case 'verification':
2313
- await sendVerificationEmail(event.data)
2314
- break
2315
- case 'password-reset':
2316
- await sendPasswordResetEmail(event.data)
2317
- break
2318
- }
2319
- }, { secret: process.env.WEBHOOK_SECRET! })
2616
+ return handleWebhook(request, customerAuthHandler, {
2617
+ secret: process.env.WEBHOOK_SECRET!,
2618
+ })
2320
2619
  }`,
2321
2620
  notes: [
2322
2621
  "handleWebhook() takes (request, handler, options) \u2014 handler receives the parsed event",
2323
2622
  "WEBHOOK_SECRET is set per-tenant in Console > Settings",
2324
- "handleWebhook() verifies HMAC-SHA256 signature automatically before calling handler"
2623
+ "handleWebhook() verifies HMAC-SHA256 signature automatically before calling handler",
2624
+ "createCustomerAuthWebhookHandler() is optional; it just routes auth events to your own email/SMS delivery code"
2325
2625
  ]
2326
2626
  }
2327
2627
  };
@@ -2340,14 +2640,14 @@ function handler4({
2340
2640
  }
2341
2641
 
2342
2642
  // src/tools/sdk-get-collection-pattern.ts
2343
- import { z as z34 } from "zod";
2344
- import { COLLECTIONS as COLLECTIONS9 } from "@01.software/sdk";
2345
- var schema34 = {
2346
- collection: z34.enum(COLLECTIONS9).describe("Collection name"),
2347
- operation: z34.enum(["read", "write", "full-crud"]).default("read").describe("What operations are needed"),
2348
- surface: z34.enum(["query-builder", "react-query", "server-api"]).default("query-builder").describe("Preferred API surface")
2643
+ import { z as z27 } from "zod";
2644
+ import { COLLECTIONS as COLLECTIONS4 } from "@01.software/sdk";
2645
+ var schema29 = {
2646
+ collection: z27.enum(COLLECTIONS4).describe("Collection name"),
2647
+ operation: z27.enum(["read", "write", "full-crud"]).default("read").describe("What operations are needed"),
2648
+ surface: z27.enum(["query-builder", "react-query", "server-api"]).default("query-builder").describe("Preferred API surface")
2349
2649
  };
2350
- var metadata34 = {
2650
+ var metadata29 = {
2351
2651
  name: "sdk-get-collection-pattern",
2352
2652
  description: "Get the recommended CRUD pattern for a specific collection. Returns code examples for the chosen API surface and operation type.",
2353
2653
  annotations: {
@@ -2514,7 +2814,6 @@ function handler5({
2514
2814
  relatedTools: [
2515
2815
  "query-collection",
2516
2816
  "get-collection-by-id",
2517
- ...operation !== "read" ? ["create-collection", "update-collection", "delete-collection"] : [],
2518
2817
  "get-collection-schema"
2519
2818
  ],
2520
2819
  relatedResources: [
@@ -2528,14 +2827,14 @@ function handler5({
2528
2827
  }
2529
2828
 
2530
2829
  // src/prompts/sdk-usage-guide.ts
2531
- import { z as z35 } from "zod";
2532
- var schema35 = {
2533
- goal: z35.string().describe('What the user wants to accomplish (e.g., "query product list", "create order")'),
2534
- runtime: z35.enum(["browser", "server"]).optional().describe("Target runtime: browser (React/Next.js client) or server (Node.js)"),
2535
- surface: z35.enum(["query-builder", "react-query", "customer-api", "server-api"]).optional().describe("Preferred API surface"),
2536
- collection: z35.string().optional().describe("Specific collection if relevant")
2830
+ import { z as z28 } from "zod";
2831
+ var schema30 = {
2832
+ goal: z28.string().describe('What the user wants to accomplish (e.g., "query product list", "create order")'),
2833
+ runtime: z28.enum(["browser", "server"]).optional().describe("Target runtime: browser (React/Next.js client) or server (Node.js)"),
2834
+ surface: z28.enum(["query-builder", "react-query", "customer-api", "server-api"]).optional().describe("Preferred API surface"),
2835
+ collection: z28.string().optional().describe("Specific collection if relevant")
2537
2836
  };
2538
- var metadata35 = {
2837
+ var metadata30 = {
2539
2838
  name: "sdk-usage-guide",
2540
2839
  title: "SDK Usage Guide",
2541
2840
  description: "Provides guidance on how to perform a specific task using the 01.software SDK",
@@ -2632,8 +2931,8 @@ await client.collections.from('products').remove('id')
2632
2931
  const { totalDocs } = await client.collections.from('products').count()
2633
2932
 
2634
2933
  // Metadata - generate Next.js Metadata from collection fields
2635
- // Auto-maps per-collection fields (e.g. posts: description\u2192description, thumbnail\u2192image)
2636
- const postMeta = await client.collections.from('posts').findMetadataById(id, { siteName: 'My Blog' })
2934
+ // Auto-maps per-collection fields (e.g. articles: description\u2192description, thumbnail\u2192image)
2935
+ const articleMeta = await client.collections.from('articles').findMetadataById(id, { siteName: 'My Blog' })
2637
2936
  const productMeta = await client.collections.from('products').findMetadata(
2638
2937
  { where: { slug: { equals: 'my-product' } } },
2639
2938
  { siteName: 'My Store' }
@@ -2672,14 +2971,14 @@ You can perform the "${goal}" task by following the patterns above.`;
2672
2971
  }
2673
2972
 
2674
2973
  // src/prompts/collection-query-help.ts
2675
- import { z as z36 } from "zod";
2676
- import { COLLECTIONS as COLLECTIONS10 } from "@01.software/sdk";
2677
- var schema36 = {
2678
- collection: z36.enum(COLLECTIONS10).describe("Collection name"),
2679
- operation: z36.enum(["find", "create", "update", "delete"]).describe("Operation to perform (find, create, update, delete)"),
2680
- filters: z36.string().optional().describe("Filter conditions (JSON string, optional)")
2974
+ import { z as z29 } from "zod";
2975
+ import { COLLECTIONS as COLLECTIONS5 } from "@01.software/sdk";
2976
+ var schema31 = {
2977
+ collection: z29.enum(COLLECTIONS5).describe("Collection name"),
2978
+ operation: z29.enum(["find", "create", "update", "delete"]).describe("Operation to perform (find, create, update, delete)"),
2979
+ filters: z29.string().optional().describe("Filter conditions (JSON string, optional)")
2681
2980
  };
2682
- var metadata36 = {
2981
+ var metadata31 = {
2683
2982
  name: "collection-query-help",
2684
2983
  title: "Collection Query Help",
2685
2984
  description: "Provides guidance on how to write queries for a specific collection",
@@ -2766,16 +3065,16 @@ ${operation === "find" ? `- Use \`where\` option for filtering (Payload query sy
2766
3065
  }
2767
3066
 
2768
3067
  // src/prompts/order-flow-guide.ts
2769
- import { z as z37 } from "zod";
2770
- var schema37 = {
2771
- scenario: z37.enum([
3068
+ import { z as z30 } from "zod";
3069
+ var schema32 = {
3070
+ scenario: z30.enum([
2772
3071
  "simple-order",
2773
3072
  "cart-checkout",
2774
3073
  "return-refund",
2775
3074
  "fulfillment-tracking"
2776
3075
  ]).describe("Order flow scenario")
2777
3076
  };
2778
- var metadata37 = {
3077
+ var metadata32 = {
2779
3078
  name: "order-flow-guide",
2780
3079
  title: "Order Flow Guide",
2781
3080
  description: "Provides step-by-step guidance for ecommerce order flows including creation, checkout, returns, and fulfillment.",
@@ -2790,8 +3089,8 @@ var SCENARIOS = {
2790
3089
  - Provide: orderNumber, customerSnapshot (email required), shippingAddress, orderItems, totalAmount
2791
3090
  - Optional: pgPaymentId (omit for free orders), shippingAmount, discountCode
2792
3091
 
2793
- 2. **Payment Confirmation** \u2192 \`update-order\` tool
2794
- - Update status to \`paid\` after payment gateway confirms
3092
+ 2. **Payment Confirmation** \u2192 \`update-transaction\` tool
3093
+ - Confirm provider payment with pgPaymentId, paymentKey, and amount
2795
3094
  - Stock is automatically adjusted (stock -= qty, reservedStock += qty)
2796
3095
 
2797
3096
  3. **Fulfillment** \u2192 \`create-fulfillment\` tool
@@ -2818,8 +3117,13 @@ const order = await client.commerce.orders.create({
2818
3117
  pgPaymentId: 'pay_xxx' // omit for free orders
2819
3118
  })
2820
3119
 
2821
- // 2. After payment confirmed
2822
- await client.commerce.orders.update({ orderNumber: 'ORD-240101-001', status: 'paid' })
3120
+ // 2. After payment confirmed by provider
3121
+ await client.commerce.orders.updateTransaction({
3122
+ pgPaymentId: 'pay_xxx',
3123
+ status: 'paid',
3124
+ paymentKey: 'payment_key_xxx',
3125
+ amount: 59800
3126
+ })
2823
3127
 
2824
3128
  // 3. Ship items
2825
3129
  await client.commerce.orders.createFulfillment({
@@ -2837,7 +3141,7 @@ await client.commerce.orders.createFulfillment({
2837
3141
  2. **Apply Discount** (optional) \u2192 \`apply-discount\` tool
2838
3142
  3. **Calculate Shipping** \u2192 \`calculate-shipping\` tool
2839
3143
  4. **Checkout** \u2192 \`checkout\` tool (converts cart to order)
2840
- 5. **Payment** \u2192 \`update-order\` or \`update-transaction\`
3144
+ 5. **Payment** \u2192 \`update-transaction\` for provider-verified paid transitions
2841
3145
 
2842
3146
  ### Key Points
2843
3147
  - Cart has a customer linked \u2014 auto-copied to order on checkout
@@ -2874,7 +3178,7 @@ const order = await client.commerce.orders.checkout({
2874
3178
  1. **Return with Refund** \u2192 \`return-with-refund\` tool
2875
3179
  - Handles return + stock restoration + transaction update in one call
2876
3180
  - Return immediately completed (bypasses FSM)
2877
- - Requires pgPaymentId to identify which transaction to refund
3181
+ - Requires pgPaymentId and paymentKey for provider-verified refund
2878
3182
 
2879
3183
  ### Key Points
2880
3184
  - Full refund: original transaction \u2192 \`canceled\`
@@ -2891,7 +3195,8 @@ await client.commerce.orders.returnWithRefund({
2891
3195
  reasonDetail: 'Product arrived damaged',
2892
3196
  returnItems: [{ orderItem: 'oi-id', quantity: 1 }],
2893
3197
  refundAmount: 29900,
2894
- pgPaymentId: 'pay_xxx'
3198
+ pgPaymentId: 'pay_xxx',
3199
+ paymentKey: 'payment_key_xxx'
2895
3200
  })
2896
3201
  \`\`\``,
2897
3202
  "fulfillment-tracking": `## Fulfillment & Tracking
@@ -2954,12 +3259,12 @@ ${SCENARIOS[scenario] || "Unknown scenario."}
2954
3259
  }
2955
3260
 
2956
3261
  // src/prompts/feature-setup-guide.ts
2957
- import { z as z38 } from "zod";
2958
- var schema38 = {
2959
- feature: z38.enum([
3262
+ import { z as z31 } from "zod";
3263
+ var schema33 = {
3264
+ feature: z31.enum([
2960
3265
  "ecommerce",
2961
3266
  "customers",
2962
- "posts",
3267
+ "articles",
2963
3268
  "documents",
2964
3269
  "playlists",
2965
3270
  "galleries",
@@ -2971,7 +3276,7 @@ var schema38 = {
2971
3276
  "community"
2972
3277
  ]).describe("Feature to get setup guide for")
2973
3278
  };
2974
- var metadata38 = {
3279
+ var metadata33 = {
2975
3280
  name: "feature-setup-guide",
2976
3281
  title: "Feature Setup Guide",
2977
3282
  description: "Setup checklist and remediation guide for a tenant feature. Load before using get-tenant-context to diagnose setup gaps.",
@@ -2984,8 +3289,8 @@ var FEATURES = {
2984
3289
 
2985
3290
  ### Required Collections (count > 0)
2986
3291
 
2987
- 1. **products** \u2014 Use \`create-collection\` with \`collection='products'\`
2988
- - Minimum fields: \`{ title, slug, status: 'active' }\`
3292
+ 1. **products** \u2014 Create via Console UI or SDK \`client.collections.from('products').create({ ... })\`
3293
+ - Minimum fields: \`{ title, slug, status: 'published', _status: 'published' }\`
2989
3294
 
2990
3295
  2. **product-variants** \u2014 At least 1 sellable variant per product
2991
3296
  - Minimum fields: \`{ product, title, price, stock }\`
@@ -3018,26 +3323,26 @@ customer-addresses
3018
3323
 
3019
3324
  ### Optional Collections
3020
3325
 
3021
- customer-groups \u2014 Use \`create-collection\` with \`collection='customer-groups'\`, \`{ title }\`
3326
+ customer-groups \u2014 Create via Console UI or SDK \`client.collections.from('customer-groups').create({ title })\`
3022
3327
 
3023
3328
  ### Config
3024
3329
 
3025
- - \`requireEmailVerification\` can be toggled in tenant settings
3330
+ - Customer registration creates a local account; add app-level verification if needed
3026
3331
  - Customer auth uses custom JWT (separate from Payload auth)`,
3027
- posts: `## Posts Setup Guide
3332
+ articles: `## Articles Setup Guide
3028
3333
 
3029
3334
  ### Required Collections (count > 0)
3030
3335
 
3031
- 1. **posts** \u2014 At least 1 post
3336
+ 1. **articles** \u2014 At least 1 article
3032
3337
  - Minimum fields: \`{ title, slug }\`
3033
3338
 
3034
- 2. **post-authors** \u2014 At least 1 author
3339
+ 2. **article-authors** \u2014 At least 1 author
3035
3340
  - Minimum fields: \`{ title, slug }\`
3036
- - Link authors to posts via the \`authors\` relationship field
3341
+ - Link authors to articles via the \`authors\` relationship field
3037
3342
 
3038
3343
  ### Optional Collections
3039
3344
 
3040
- post-categories, post-tags`,
3345
+ article-categories, article-tags`,
3041
3346
  documents: `## Documents Setup Guide
3042
3347
 
3043
3348
  ### Required Collections (count > 0)
@@ -3057,10 +3362,10 @@ document-categories`,
3057
3362
  ### Required Collections (count > 0)
3058
3363
 
3059
3364
  1. **playlists** \u2014 At least 1 playlist
3060
- - Minimum fields: \`{ title, slug }\`
3365
+ - Minimum fields: \`{ title, slug, status: 'published', _status: 'published' }\`
3061
3366
 
3062
3367
  2. **tracks** \u2014 At least 1 track
3063
- - Minimum fields: \`{ title }\`
3368
+ - Minimum fields: \`{ title, sourceUrl, status: 'published', _status: 'published' }\`
3064
3369
 
3065
3370
  3. **playlists.tracks** \u2014 Link at least 1 track from a playlist
3066
3371
  - Minimum fields: \`{ tracks: [trackId] }\`
@@ -3073,11 +3378,11 @@ playlist-categories, playlist-tags, track-categories, track-tags, track-assets`,
3073
3378
  ### Required Collections (count > 0)
3074
3379
 
3075
3380
  1. **galleries** \u2014 At least 1 gallery
3076
- - Minimum fields: \`{ title, slug }\`
3381
+ - Minimum fields: \`{ title, slug, status: 'published', _status: 'published' }\`
3077
3382
 
3078
3383
  2. **gallery-items** \u2014 At least 1 item per gallery
3079
3384
  - References \`images\` collection (non-upload)
3080
- - Minimum fields: \`{ gallery, image }\`
3385
+ - Minimum fields: \`{ gallery, image, _status: 'published' }\`
3081
3386
 
3082
3387
  ### Optional Collections
3083
3388
 
@@ -3087,7 +3392,7 @@ gallery-categories, gallery-tags`,
3087
3392
  ### Required Collections (count > 0)
3088
3393
 
3089
3394
  1. **links** \u2014 At least 1 link
3090
- - Minimum fields: \`{ title, slug, url }\`
3395
+ - Minimum fields: \`{ title, slug, url, status: 'published', _status: 'published' }\`
3091
3396
 
3092
3397
  ### Optional Collections
3093
3398
 
@@ -3147,7 +3452,7 @@ form-submissions \u2014 Auto-created when forms are submitted by end users`,
3147
3452
 
3148
3453
  ### Required Collections (count > 0)
3149
3454
 
3150
- 1. **threads** \u2014 At least 1 thread
3455
+ 1. **posts** \u2014 At least 1 post
3151
3456
  - Minimum fields: \`{ title, slug }\`
3152
3457
 
3153
3458
  2. **reaction-types** \u2014 At least 1 reaction type defined
@@ -3159,7 +3464,7 @@ comments, reactions, bookmarks, reports, community-bans
3159
3464
 
3160
3465
  ### Optional Collections
3161
3466
 
3162
- thread-categories`
3467
+ post-categories`
3163
3468
  };
3164
3469
  function featureSetupGuide({ feature }) {
3165
3470
  return `# Feature Setup Guide: ${feature}
@@ -3168,12 +3473,12 @@ ${FEATURES[feature] || "Unknown feature."}
3168
3473
 
3169
3474
  ## Related MCP Tools
3170
3475
  - \`get-tenant-context\` \u2014 check current collection counts and feature status
3171
- - \`create-collection\` \u2014 create required collection documents
3172
- - \`query-collection\` \u2014 verify existing documents in a collection`;
3476
+ - \`query-collection\` \u2014 verify existing documents in a collection
3477
+ - \`get-collection-schema\` \u2014 inspect tenant-aware fields before creating data via SDK or Console UI`;
3173
3478
  }
3174
3479
 
3175
3480
  // src/resources/(config)/app.ts
3176
- var metadata39 = {
3481
+ var metadata34 = {
3177
3482
  name: "app-config",
3178
3483
  title: "Application Config",
3179
3484
  description: "01.software SDK and MCP server configuration information"
@@ -3188,35 +3493,20 @@ function handler6() {
3188
3493
 
3189
3494
  ## Authentication
3190
3495
 
3191
- All HTTP requests require a bearer token and publishable key:
3496
+ HTTP MCP uses OAuth discovery and Authorization Code + PKCE.
3192
3497
 
3498
+ \`\`\`toml
3499
+ [mcp_servers.01software]
3500
+ url = "https://mcp.01.software/mcp"
3193
3501
  \`\`\`
3194
- x-api-key: <sk01_... or pat01_...>
3195
- x-publishable-key: <pk01_...>
3196
- \`\`\`
3197
-
3198
- \`x-client-key\` is accepted as a legacy alias for \`x-publishable-key\`.
3199
3502
 
3200
- ### Accepted Token Types
3503
+ ## Available Tools (29)
3201
3504
 
3202
- - \`sk01_{40hex}\` \u2014 tenant API key from Console > Settings > API Keys
3203
- - \`pat01_{40hex}\` \u2014 personal access token for user-scoped local workflows
3505
+ > Generic write tools (create/update/delete/update-many/delete-many) are intentionally absent. Use the dedicated workflow tools below or the SDK (\`client.collections.from(slug).create()\` / \`update()\` / \`remove()\` / \`updateMany()\` / \`removeMany()\`) for stateful mutations.
3204
3506
 
3205
- \`\`\`
3206
- SOFTWARE_SECRET_KEY=sk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3207
- SOFTWARE_PUBLISHABLE_KEY=pk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3208
- \`\`\`
3209
-
3210
- ## Available Tools (34)
3211
-
3212
- ### Generic CRUD (7)
3507
+ ### Generic Read (2)
3213
3508
  - \`query-collection\` - Query collection with filters, pagination, sorting
3214
3509
  - \`get-collection-by-id\` - Get single item by ID
3215
- - \`create-collection\` - Create new item
3216
- - \`update-collection\` - Update existing item
3217
- - \`delete-collection\` - Delete item (destructive)
3218
- - \`update-many-collection\` - Bulk update items matching filter
3219
- - \`delete-many-collection\` - Bulk delete items matching filter (destructive)
3220
3510
 
3221
3511
  ### Orders (7)
3222
3512
  - \`create-order\` - Create a new order with products and shipping
@@ -3274,70 +3564,86 @@ Rate limits depend on your tenant plan:
3274
3564
  }
3275
3565
 
3276
3566
  // src/resources/(collections)/schema.ts
3277
- import { COLLECTIONS as COLLECTIONS11 } from "@01.software/sdk";
3278
- var metadata40 = {
3567
+ import { COLLECTIONS as COLLECTIONS6 } from "@01.software/sdk";
3568
+ var metadata35 = {
3279
3569
  name: "collections-schema",
3280
3570
  title: "Collection Schema Info",
3281
3571
  description: "Available collections and their schema information"
3282
3572
  };
3573
+ var COLLECTIONS_BY_CATEGORY = {
3574
+ "Tenant Management": ["tenants", "tenant-metadata", "tenant-logos"],
3575
+ Products: [
3576
+ "products",
3577
+ "product-variants",
3578
+ "product-options",
3579
+ "product-option-values",
3580
+ "product-categories",
3581
+ "product-tags",
3582
+ "product-collections"
3583
+ ],
3584
+ Brands: ["brands", "brand-logos"],
3585
+ "Orders & Fulfillment": [
3586
+ "orders",
3587
+ "order-items",
3588
+ "transactions",
3589
+ "fulfillments",
3590
+ "fulfillment-items"
3591
+ ],
3592
+ "Shipping & Returns": ["returns", "return-items", "shipping-policies"],
3593
+ Customers: [
3594
+ "customers",
3595
+ "customer-profiles",
3596
+ "customer-addresses",
3597
+ "customer-groups"
3598
+ ],
3599
+ Carts: ["carts", "cart-items"],
3600
+ "Discounts & Promotions": ["discounts", "promotions"],
3601
+ Documents: ["documents", "document-categories", "document-types"],
3602
+ Articles: ["articles", "article-authors", "article-categories", "article-tags"],
3603
+ Community: [
3604
+ "posts",
3605
+ "comments",
3606
+ "reactions",
3607
+ "reaction-types",
3608
+ "bookmarks",
3609
+ "post-categories",
3610
+ "reports",
3611
+ "community-bans"
3612
+ ],
3613
+ Playlists: [
3614
+ "playlists",
3615
+ "tracks",
3616
+ "playlist-categories",
3617
+ "playlist-tags",
3618
+ "track-categories",
3619
+ "track-tags"
3620
+ ],
3621
+ Galleries: ["galleries", "gallery-items", "gallery-categories", "gallery-tags"],
3622
+ Links: ["links", "link-categories", "link-tags"],
3623
+ Canvas: [
3624
+ "canvases",
3625
+ "canvas-node-types",
3626
+ "canvas-edge-types",
3627
+ "canvas-categories",
3628
+ "canvas-tags",
3629
+ "canvas-nodes",
3630
+ "canvas-edges"
3631
+ ],
3632
+ Videos: ["videos", "video-categories", "video-tags"],
3633
+ "Live Streams": ["live-streams"],
3634
+ Images: ["images"],
3635
+ Forms: ["forms", "form-submissions"],
3636
+ Events: [
3637
+ "event-calendars",
3638
+ "events",
3639
+ "event-categories",
3640
+ "event-occurrences",
3641
+ "event-tags"
3642
+ ]
3643
+ };
3283
3644
  function handler7() {
3284
- const collectionsByCategory = {
3285
- "Tenant Management": ["tenants", "tenant-metadata", "tenant-logos"],
3286
- Products: [
3287
- "products",
3288
- "product-variants",
3289
- "product-options",
3290
- "product-categories",
3291
- "product-tags",
3292
- "product-collections"
3293
- ],
3294
- Brands: ["brands", "brand-logos"],
3295
- "Orders & Fulfillment": [
3296
- "orders",
3297
- "order-items",
3298
- "transactions",
3299
- "fulfillments",
3300
- "fulfillment-items"
3301
- ],
3302
- "Shipping & Returns": [
3303
- "returns",
3304
- "return-items",
3305
- "shipping-policies"
3306
- ],
3307
- Customers: ["customers", "customer-addresses", "customer-groups"],
3308
- Carts: ["carts", "cart-items"],
3309
- Discounts: ["discounts"],
3310
- Documents: ["documents", "document-categories", "document-types"],
3311
- "Posts (Blog)": ["posts", "post-categories", "post-tags"],
3312
- Playlists: [
3313
- "playlists",
3314
- "tracks",
3315
- "track-assets",
3316
- "playlist-categories",
3317
- "playlist-tags",
3318
- "track-categories",
3319
- "track-tags"
3320
- ],
3321
- Galleries: [
3322
- "galleries",
3323
- "gallery-items",
3324
- "gallery-categories",
3325
- "gallery-tags"
3326
- ],
3327
- Canvas: [
3328
- "canvases",
3329
- "canvas-node-types",
3330
- "canvas-edge-types",
3331
- "canvas-categories",
3332
- "canvas-tags"
3333
- ],
3334
- Videos: ["videos", "video-categories", "video-tags"],
3335
- "Live Streams": ["live-streams"],
3336
- Images: ["images"],
3337
- Forms: ["forms", "form-submissions"]
3338
- };
3339
- const categoryDocs = Object.entries(collectionsByCategory).map(([category, collections]) => {
3340
- const collectionList = collections.filter((c) => COLLECTIONS11.includes(c)).map((c) => `- **${c}**`).join("\n");
3645
+ const categoryDocs = Object.entries(COLLECTIONS_BY_CATEGORY).map(([category, collections]) => {
3646
+ const collectionList = collections.filter((c) => COLLECTIONS6.includes(c)).map((c) => `- **${c}**`).join("\n");
3341
3647
  return `## ${category}
3342
3648
  ${collectionList}`;
3343
3649
  }).join("\n\n");
@@ -3358,6 +3664,9 @@ Each collection supports the following operations:
3358
3664
  - \`updateMany(where, data)\` - Bulk update items matching filter
3359
3665
  - \`removeMany(where)\` - Bulk delete items matching filter
3360
3666
 
3667
+ Draft-enabled public collections expose only \`_status: 'published'\` rows to
3668
+ publishable-key reads unless server-side access explicitly includes drafts.
3669
+
3361
3670
  ## Query Examples
3362
3671
 
3363
3672
  ### Filtering
@@ -3379,11 +3688,11 @@ Each collection supports the following operations:
3379
3688
  }
3380
3689
  \`\`\`
3381
3690
 
3382
- Total available collections: ${COLLECTIONS11.length}`;
3691
+ Total available collections: ${COLLECTIONS6.length}`;
3383
3692
  }
3384
3693
 
3385
3694
  // src/resources/(docs)/getting-started.ts
3386
- var metadata41 = {
3695
+ var metadata36 = {
3387
3696
  name: "docs-getting-started",
3388
3697
  title: "Getting Started",
3389
3698
  description: "01.software SDK getting started guide"
@@ -3428,7 +3737,7 @@ const result = await client.collections.from('products').find({
3428
3737
  }
3429
3738
 
3430
3739
  // src/resources/(docs)/guides.ts
3431
- var metadata42 = {
3740
+ var metadata37 = {
3432
3741
  name: "docs-guides",
3433
3742
  title: "Guides",
3434
3743
  description: "01.software SDK usage guides"
@@ -3639,7 +3948,7 @@ For more detailed guides, see the [Guides page](/docs/guides).`;
3639
3948
  }
3640
3949
 
3641
3950
  // src/resources/(docs)/api.ts
3642
- var metadata43 = {
3951
+ var metadata38 = {
3643
3952
  name: "docs-api",
3644
3953
  title: "API Reference",
3645
3954
  description: "01.software SDK API reference documentation"
@@ -3859,7 +4168,7 @@ Customer authentication and profile management. Available on \`Client\` only (\`
3859
4168
  ### Authentication
3860
4169
  \`\`\`typescript
3861
4170
  // Register
3862
- const { customer, verificationRequired? } = await client.customer.register({
4171
+ const { customer } = await client.customer.register({
3863
4172
  name: 'John',
3864
4173
  email: 'john@example.com',
3865
4174
  password: 'password123',
@@ -3894,7 +4203,7 @@ const updated = await client.customer.updateProfile({
3894
4203
 
3895
4204
  ### Password
3896
4205
  \`\`\`typescript
3897
- // Forgot password (sends reset token via webhook)
4206
+ // Forgot password (sends reset token to configured tenant webhooks)
3898
4207
  await client.customer.forgotPassword(email)
3899
4208
 
3900
4209
  // Reset password with token
@@ -3904,11 +4213,6 @@ await client.customer.resetPassword(token, newPassword)
3904
4213
  await client.customer.changePassword(currentPassword, newPassword)
3905
4214
  \`\`\`
3906
4215
 
3907
- ### Email Verification
3908
- \`\`\`typescript
3909
- await client.customer.verifyEmail(token)
3910
- \`\`\`
3911
-
3912
4216
  ### Orders
3913
4217
  \`\`\`typescript
3914
4218
  const orders = await client.commerce.orders.listMine({
@@ -3930,7 +4234,7 @@ For more details, see the [full API documentation](/docs/api).`;
3930
4234
  }
3931
4235
 
3932
4236
  // src/resources/(docs)/query-builder.ts
3933
- var metadata44 = {
4237
+ var metadata39 = {
3934
4238
  name: "docs-query-builder",
3935
4239
  title: "Query Builder",
3936
4240
  description: "01.software SDK Query Builder API reference (client.collections.from)"
@@ -4087,7 +4391,7 @@ if (page1.hasNextPage) {
4087
4391
 
4088
4392
  \`\`\`typescript
4089
4393
  // Descending (newest first)
4090
- const result = await client.collections.from('posts').find({ sort: '-createdAt' })
4394
+ const result = await client.collections.from('articles').find({ sort: '-createdAt' })
4091
4395
 
4092
4396
  // Ascending
4093
4397
  const result2 = await client.collections.from('products').find({ sort: 'price' })
@@ -4124,7 +4428,7 @@ console.log(result.hasNextPage) // true
4124
4428
  }
4125
4429
 
4126
4430
  // src/resources/(docs)/react-query.ts
4127
- var metadata45 = {
4431
+ var metadata40 = {
4128
4432
  name: "docs-react-query",
4129
4433
  title: "React Query Hooks",
4130
4434
  description: "01.software SDK React Query hooks reference (client.query)"
@@ -4372,7 +4676,7 @@ export function ProductList() {
4372
4676
  }
4373
4677
 
4374
4678
  // src/resources/(docs)/server-api.ts
4375
- var metadata46 = {
4679
+ var metadata41 = {
4376
4680
  name: "docs-server-api",
4377
4681
  title: "Server-side API",
4378
4682
  description: "01.software SDK server-side API reference (client.commerce) for orders, fulfillments, returns, carts, and validation"
@@ -4382,19 +4686,19 @@ function handler13() {
4382
4686
 
4383
4687
  Server-side operations are available via \`client.commerce\` on \`ServerClient\`. Use \`createServerClient\` with both \`publishableKey\` and \`secretKey\`.
4384
4688
 
4385
- For backend services, prefer a tenant API key (\`sk01_...\`) in \`SOFTWARE_SECRET_KEY\`.
4386
- Browser-based CLI/init login flows may instead provision a user-scoped PAT (\`pat01_...\`) with a default tenant.
4387
-
4388
4689
  \`\`\`typescript
4389
4690
  import { createServerClient } from '@01.software/sdk'
4390
4691
 
4391
4692
  const client = createServerClient({
4392
4693
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
4393
- secretKey: process.env.SOFTWARE_SECRET_KEY!, // usually sk01_..., sometimes pat01_...
4694
+ secretKey: process.env.SOFTWARE_SECRET_KEY!,
4394
4695
  })
4395
4696
  \`\`\`
4396
4697
 
4397
- > Never expose \`SOFTWARE_SECRET_KEY\` in browser code. Use server components, API routes, or server actions only.
4698
+ Use server components, API routes, or server actions only. Never expose
4699
+ \`SOFTWARE_SECRET_KEY\` to browser code, client bundles, logs, or public
4700
+ repositories. If a secret key leaks, rotate it from the Console before deploying
4701
+ again.
4398
4702
 
4399
4703
  ## Order API
4400
4704
 
@@ -4513,7 +4817,7 @@ const ret = await client.commerce.orders.updateReturn({
4513
4817
  \`\`\`
4514
4818
 
4515
4819
  ### returnWithRefund()
4516
- Create a return and process refund in one atomic operation.
4820
+ Create a return and process a provider-verified refund in one atomic operation.
4517
4821
 
4518
4822
  \`\`\`typescript
4519
4823
  const result = await client.commerce.orders.returnWithRefund({
@@ -4525,6 +4829,7 @@ const result = await client.commerce.orders.returnWithRefund({
4525
4829
  ],
4526
4830
  refundAmount: 29900,
4527
4831
  pgPaymentId: 'toss-payment-id', // required
4832
+ paymentKey: 'toss-payment-key', // required for provider refund
4528
4833
  refundReceiptUrl?: 'https://...',
4529
4834
  })
4530
4835
  \`\`\`
@@ -4532,12 +4837,15 @@ const result = await client.commerce.orders.returnWithRefund({
4532
4837
  ## Transaction API
4533
4838
 
4534
4839
  ### updateTransaction()
4535
- Update a transaction status (after PG callback).
4840
+ Confirm or annotate a transaction. Paid transitions require provider
4841
+ verification; non-financial annotations can still update pending transactions.
4536
4842
 
4537
4843
  \`\`\`typescript
4538
4844
  const tx = await client.commerce.orders.updateTransaction({
4539
4845
  pgPaymentId: 'toss-payment-id',
4540
- status: 'paid', // paid | failed | canceled
4846
+ status: 'paid', // pending | paid | failed | canceled
4847
+ paymentKey: 'toss-payment-key', // required when status is paid
4848
+ amount: 29900, // required when status is paid
4541
4849
  })
4542
4850
  \`\`\`
4543
4851
 
@@ -4630,7 +4938,7 @@ const result = await client.commerce.shipping.calculate({
4630
4938
  }
4631
4939
 
4632
4940
  // src/resources/(docs)/customer-auth.ts
4633
- var metadata47 = {
4941
+ var metadata42 = {
4634
4942
  name: "docs-customer-auth",
4635
4943
  title: "Customer Auth API",
4636
4944
  description: "01.software SDK Customer Auth API reference (client.customer)"
@@ -4663,11 +4971,9 @@ const result = await client.customer.register({
4663
4971
  phone?: '+821012345678',
4664
4972
  })
4665
4973
  // result.customer - created customer object
4666
- // result.token? - JWT token (set if email verification not required)
4667
- // result.verificationRequired? - true if tenant requires email verification
4668
4974
  \`\`\`
4669
4975
 
4670
- When \`verificationRequired\` is true, no token is returned. The tenant's webhook receives a \`verificationToken\` to send to the customer.
4976
+ Registration creates a local customer account. Projects that need additional email verification should enforce it in application code.
4671
4977
 
4672
4978
  ### login()
4673
4979
  Authenticate with email and password.
@@ -4721,12 +5027,12 @@ const updated = await client.customer.updateProfile({
4721
5027
  ## Password
4722
5028
 
4723
5029
  ### forgotPassword()
4724
- Request a password reset. Sends reset token via tenant webhook.
5030
+ Request a password reset. Sends the reset token to configured tenant webhooks; your webhook handler owns delivery.
4725
5031
 
4726
5032
  \`\`\`typescript
4727
5033
  await client.customer.forgotPassword('john@example.com')
4728
5034
  // Rate limited: 5 requests/min per tenant+email
4729
- // Webhook receives: { resetPasswordToken, resetPasswordExpiry }
5035
+ // Webhook receives: { resetPasswordToken, resetPasswordExpiresAt }
4730
5036
  \`\`\`
4731
5037
 
4732
5038
  ### resetPassword()
@@ -4743,15 +5049,6 @@ Change password while authenticated (requires current password).
4743
5049
  await client.customer.changePassword('currentPassword', 'newPassword123')
4744
5050
  \`\`\`
4745
5051
 
4746
- ## Email Verification
4747
-
4748
- ### verifyEmail()
4749
- Verify email address using the token received via webhook.
4750
-
4751
- \`\`\`typescript
4752
- await client.customer.verifyEmail('verification-token')
4753
- \`\`\`
4754
-
4755
5052
  ## Orders
4756
5053
 
4757
5054
  ### listMine()
@@ -4797,12 +5094,7 @@ const client = createClient({
4797
5094
  async function handleRegister(email: string, password: string, name: string) {
4798
5095
  const result = await client.customer.register({ email, password, name })
4799
5096
 
4800
- if (result.verificationRequired) {
4801
- // Redirect to "check your email" page
4802
- return { status: 'verify-email' }
4803
- }
4804
-
4805
- // Token is automatically stored; customer is now logged in
5097
+ // Customer is created as a local account.
4806
5098
  return { status: 'success', customer: result.customer }
4807
5099
  }
4808
5100
 
@@ -4824,7 +5116,7 @@ async function loadProfile() {
4824
5116
  }
4825
5117
 
4826
5118
  // src/resources/(docs)/browser-vs-server.ts
4827
- var metadata48 = {
5119
+ var metadata43 = {
4828
5120
  name: "docs-browser-vs-server",
4829
5121
  title: "Client vs ServerClient",
4830
5122
  description: "When to use Client (createClient) vs ServerClient (createServerClient) in the 01.software SDK"
@@ -4904,7 +5196,11 @@ await client.commerce.orders.checkout({ ... })
4904
5196
 
4905
5197
  **Environment variables**:
4906
5198
  - \`SOFTWARE_PUBLISHABLE_KEY\` \u2014 publishable key (no NEXT_PUBLIC prefix, server-only)
4907
- - \`SOFTWARE_SECRET_KEY\` \u2014 opaque bearer token (server-only, never expose to browser). Backend services usually use \`sk01_...\`; browser-based CLI/init login can provision \`pat01_...\` with a default tenant.
5199
+ - \`SOFTWARE_SECRET_KEY\` \u2014 server credential
5200
+
5201
+ Never expose \`SOFTWARE_SECRET_KEY\` in browser code, client bundles, logs, or
5202
+ public repositories. If a secret key leaks, rotate it from the Console before
5203
+ deploying again.
4908
5204
 
4909
5205
  ## Decision Matrix
4910
5206
 
@@ -4970,15 +5266,16 @@ export function ProductList() {
4970
5266
 
4971
5267
  ## Security Rules
4972
5268
 
4973
- - Never import \`SOFTWARE_SECRET_KEY\` or \`SOFTWARE_PUBLISHABLE_KEY\` in files without a \`'use server'\` directive or that could be bundled for the browser.
5269
+ - Keep server credentials in server-only modules.
4974
5270
  - Only \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` is safe to use in client components.
4975
- - If you accidentally expose \`SOFTWARE_SECRET_KEY\` in a browser bundle, rotate the key immediately in the 01.software console.
5271
+ - Never import a module that reads \`SOFTWARE_SECRET_KEY\` from a client component.
5272
+ - Rotate any exposed secret key immediately from the Console.
4976
5273
 
4977
5274
  > Ecommerce note: product card pricing lives on \`products.listing.*\`, but authoritative sellable pricing still lives on \`product-variants.price\`.`;
4978
5275
  }
4979
5276
 
4980
5277
  // src/resources/(docs)/file-upload.ts
4981
- var metadata49 = {
5278
+ var metadata44 = {
4982
5279
  name: "docs-file-upload",
4983
5280
  title: "File Upload",
4984
5281
  description: "01.software SDK file upload patterns using the images collection"
@@ -5129,7 +5426,7 @@ The platform stores files in Cloudflare R2 and serves via CDN (\`cdn.01.software
5129
5426
  }
5130
5427
 
5131
5428
  // src/resources/(docs)/webhook.ts
5132
- var metadata50 = {
5429
+ var metadata45 = {
5133
5430
  name: "docs-webhook",
5134
5431
  title: "Webhooks",
5135
5432
  description: "01.software SDK webhook verification and event handling"
@@ -5137,27 +5434,23 @@ var metadata50 = {
5137
5434
  function handler17() {
5138
5435
  return `# Webhooks
5139
5436
 
5140
- The platform dispatches HMAC-SHA256 signed webhook events to your registered URLs for async operations (email verification, password reset, etc.).
5437
+ The platform dispatches HMAC-SHA256 signed webhook events to your registered URLs. Tenant developers own routing inside their webhook handler.
5141
5438
 
5142
5439
  ## Webhook Handling
5143
5440
 
5144
- Use the SDK \`handleWebhook\` helper to verify signatures and route events.
5441
+ Use the SDK \`handleWebhook\` helper to verify signatures. For customer auth events, use \`createCustomerAuthWebhookHandler\` to wire delivery behavior in your app.
5145
5442
 
5146
5443
  \`\`\`typescript
5147
- import { handleWebhook } from '@01.software/sdk/webhook'
5444
+ import { handleWebhook, createCustomerAuthWebhookHandler } from '@01.software/sdk/webhook'
5445
+
5446
+ const handler = createCustomerAuthWebhookHandler({
5447
+ passwordReset: async (data) => {
5448
+ await sendPasswordResetEmail(data)
5449
+ },
5450
+ })
5148
5451
 
5149
5452
  export async function POST(request: Request) {
5150
- return handleWebhook(request, async (event) => {
5151
- // event.collection, event.operation, event.data
5152
- switch (event.operation) {
5153
- case 'verification':
5154
- await sendVerificationEmail(event.data)
5155
- break
5156
- case 'password-reset':
5157
- await sendPasswordResetEmail(event.data)
5158
- break
5159
- }
5160
- }, {
5453
+ return handleWebhook(request, handler, {
5161
5454
  secret: process.env.WEBHOOK_SECRET!,
5162
5455
  })
5163
5456
  }
@@ -5169,19 +5462,17 @@ export async function POST(request: Request) {
5169
5462
 
5170
5463
  \`\`\`typescript
5171
5464
  // app/api/webhooks/route.ts
5172
- import { handleWebhook } from '@01.software/sdk/webhook'
5465
+ import { handleWebhook, createCustomerAuthWebhookHandler } from '@01.software/sdk/webhook'
5466
+
5467
+ const customerAuthHandler = createCustomerAuthWebhookHandler({
5468
+ passwordReset: sendPasswordResetEmail,
5469
+ })
5173
5470
 
5174
5471
  export async function POST(request: Request) {
5175
5472
  return handleWebhook(request, async (event) => {
5176
5473
  console.log('Webhook received:', event.collection, event.operation)
5177
5474
 
5178
- if (event.collection === 'customers') {
5179
- if (event.operation === 'verification') {
5180
- await sendVerificationEmail(event.data)
5181
- } else if (event.operation === 'password-reset') {
5182
- await sendPasswordResetEmail(event.data)
5183
- }
5184
- }
5475
+ await customerAuthHandler(event)
5185
5476
  }, {
5186
5477
  secret: process.env.WEBHOOK_SECRET!,
5187
5478
  })
@@ -5195,49 +5486,13 @@ All webhook events share this envelope:
5195
5486
  \`\`\`typescript
5196
5487
  {
5197
5488
  collection: string, // e.g. 'customers'
5198
- operation: string, // e.g. 'verification' | 'password-reset'
5489
+ operation: string, // e.g. 'password-reset'
5199
5490
  data: object, // event-specific payload
5200
5491
  }
5201
5492
  \`\`\`
5202
5493
 
5203
5494
  ## Event Types
5204
5495
 
5205
- ### Customer Email Verification
5206
-
5207
- Dispatched when a customer registers on a tenant with \`requireEmailVerification: true\`.
5208
-
5209
- \`\`\`typescript
5210
- {
5211
- collection: 'customers',
5212
- operation: 'verification',
5213
- data: {
5214
- customerId: string,
5215
- email: string,
5216
- name: string,
5217
- verificationToken: string, // raw token to include in verification link
5218
- }
5219
- }
5220
- \`\`\`
5221
-
5222
- **Usage**: Send the \`verificationToken\` to the customer's email. The customer calls \`client.customer.verifyEmail(token)\` to complete verification.
5223
-
5224
- \`\`\`typescript
5225
- // Example: send verification email
5226
- async function sendVerificationEmail(data: {
5227
- customerId: string
5228
- email: string
5229
- name: string
5230
- verificationToken: string
5231
- }) {
5232
- const verifyUrl = \`https://yourstore.com/verify-email?token=\${data.verificationToken}\`
5233
- await emailService.send({
5234
- to: data.email,
5235
- subject: 'Verify your email',
5236
- body: \`Click here to verify: \${verifyUrl}\`,
5237
- })
5238
- }
5239
- \`\`\`
5240
-
5241
5496
  ### Customer Password Reset
5242
5497
 
5243
5498
  Dispatched when a customer calls \`client.customer.forgotPassword(email)\`.
@@ -5251,7 +5506,7 @@ Dispatched when a customer calls \`client.customer.forgotPassword(email)\`.
5251
5506
  email: string,
5252
5507
  name: string,
5253
5508
  resetPasswordToken: string, // raw token to include in reset link
5254
- resetPasswordExpiry: string, // ISO 8601 expiry (1 hour from dispatch)
5509
+ resetPasswordExpiresAt: string, // ISO 8601 expiry (1 hour from dispatch)
5255
5510
  }
5256
5511
  }
5257
5512
  \`\`\`
@@ -5264,13 +5519,13 @@ async function sendPasswordResetEmail(data: {
5264
5519
  email: string
5265
5520
  name: string
5266
5521
  resetPasswordToken: string
5267
- resetPasswordExpiry: string
5522
+ resetPasswordExpiresAt: string
5268
5523
  }) {
5269
5524
  const resetUrl = \`https://yourstore.com/reset-password?token=\${data.resetPasswordToken}\`
5270
5525
  await emailService.send({
5271
5526
  to: data.email,
5272
5527
  subject: 'Reset your password',
5273
- body: \`Reset link (expires \${data.resetPasswordExpiry}): \${resetUrl}\`,
5528
+ body: \`Reset link (expires \${data.resetPasswordExpiresAt}): \${resetUrl}\`,
5274
5529
  })
5275
5530
  }
5276
5531
  \`\`\`
@@ -5285,28 +5540,54 @@ Configure webhook URLs in the 01.software console under Tenant Settings > Webhoo
5285
5540
  }
5286
5541
 
5287
5542
  // src/server.ts
5288
- function registerTool(server, schema39, meta, handler19) {
5543
+ var REGISTERED_TOOLS_BY_SERVER = /* @__PURE__ */ new WeakMap();
5544
+ function registerTool(server, schema34, meta, handler19) {
5545
+ let registered = REGISTERED_TOOLS_BY_SERVER.get(server);
5546
+ if (!registered) {
5547
+ registered = /* @__PURE__ */ new Set();
5548
+ REGISTERED_TOOLS_BY_SERVER.set(server, registered);
5549
+ }
5550
+ registered.add(meta.name);
5289
5551
  server.registerTool(
5290
5552
  meta.name,
5291
5553
  {
5292
5554
  description: meta.description,
5293
- inputSchema: schema39,
5555
+ inputSchema: schema34,
5294
5556
  annotations: meta.annotations
5295
5557
  },
5296
5558
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5297
5559
  async (params) => {
5560
+ const ctx = tenantAuthContext();
5561
+ if (ctx) {
5562
+ const decision = evaluateToolPolicy(meta.name, ctx.scopes);
5563
+ if (!decision.allowed) {
5564
+ const status = decision.reason === "insufficient_scope" ? 403 : 500;
5565
+ return {
5566
+ content: [
5567
+ {
5568
+ type: "text",
5569
+ text: toolError({
5570
+ status,
5571
+ reason: decision.reason,
5572
+ message: decision.message
5573
+ })
5574
+ }
5575
+ ]
5576
+ };
5577
+ }
5578
+ }
5298
5579
  const result = await handler19(params);
5299
5580
  return { content: [{ type: "text", text: result }] };
5300
5581
  }
5301
5582
  );
5302
5583
  }
5303
- function registerPrompt(server, schema39, meta, handler19) {
5584
+ function registerPrompt(server, schema34, meta, handler19) {
5304
5585
  server.registerPrompt(
5305
5586
  meta.name,
5306
5587
  {
5307
5588
  title: meta.title,
5308
5589
  description: meta.description,
5309
- argsSchema: schema39
5590
+ argsSchema: schema34
5310
5591
  },
5311
5592
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5312
5593
  (params) => ({
@@ -5333,80 +5614,248 @@ function registerStaticResource(server, uri, meta, handler19) {
5333
5614
  })
5334
5615
  );
5335
5616
  }
5336
- function createServer() {
5617
+ function createServer(options = {}) {
5618
+ const toolSurface = options.toolSurface ?? "full";
5337
5619
  const server = new McpServer({
5338
5620
  name: "01.software MCP Server",
5339
5621
  version: "0.1.0"
5340
5622
  });
5341
- registerTool(server, schema, metadata, queryCollection);
5342
- registerTool(server, schema2, metadata2, getCollectionById);
5343
- registerTool(server, schema3, metadata3, createCollection);
5344
- registerTool(server, schema4, metadata4, updateCollection);
5345
- registerTool(server, schema5, metadata5, deleteCollection);
5346
- registerTool(server, schema6, metadata6, deleteManyCollection);
5347
- registerTool(server, schema7, metadata7, updateManyCollection);
5348
- registerTool(server, schema8, metadata8, getOrder);
5349
- registerTool(server, schema9, metadata9, createOrder);
5350
- registerTool(server, schema10, metadata10, updateOrder);
5351
- registerTool(server, schema11, metadata11, checkout);
5352
- registerTool(server, schema12, metadata12, createFulfillment);
5353
- registerTool(server, schema13, metadata13, updateFulfillment);
5354
- registerTool(server, schema14, metadata14, updateTransaction);
5355
- registerTool(server, schema15, metadata15, createReturn);
5356
- registerTool(server, schema16, metadata16, updateReturn);
5357
- registerTool(server, schema17, metadata17, returnWithRefund);
5358
- registerTool(server, schema18, metadata18, addCartItem);
5359
- registerTool(server, schema19, metadata19, updateCartItem);
5360
- registerTool(server, schema20, metadata20, removeCartItem);
5361
- registerTool(server, schema21, metadata21, applyDiscount);
5362
- registerTool(server, schema22, metadata22, removeDiscount);
5363
- registerTool(server, schema23, metadata23, clearCart);
5364
- registerTool(server, schema24, metadata24, validateDiscount);
5365
- registerTool(server, schema25, metadata25, calculateShipping);
5366
- registerTool(server, schema26, metadata26, stockCheck);
5367
- registerTool(server, schema27, metadata27, getCollectionSchemaTool);
5368
- registerTool(server, schema28, metadata28, handler);
5369
- registerTool(server, schema29, metadata29, listConfigurableFields);
5370
- registerTool(server, schema30, metadata30, updateFieldConfig);
5371
- registerTool(server, schema31, metadata31, handler2);
5372
- registerTool(server, schema32, metadata32, handler3);
5373
- registerTool(server, schema33, metadata33, handler4);
5374
- registerTool(server, schema34, metadata34, handler5);
5375
- registerPrompt(server, schema35, metadata35, sdkUsageGuide);
5376
- registerPrompt(server, schema36, metadata36, collectionQueryHelp);
5377
- registerPrompt(server, schema37, metadata37, orderFlowGuide);
5378
- registerPrompt(server, schema38, metadata38, featureSetupGuide);
5379
- registerStaticResource(server, "config://app", metadata39, handler6);
5380
- registerStaticResource(server, "collections://schema", metadata40, handler7);
5381
- registerStaticResource(server, "docs://sdk/getting-started", metadata41, handler8);
5382
- registerStaticResource(server, "docs://sdk/guides", metadata42, handler9);
5383
- registerStaticResource(server, "docs://sdk/api", metadata43, handler10);
5384
- registerStaticResource(server, "docs://sdk/query-builder", metadata44, handler11);
5385
- registerStaticResource(server, "docs://sdk/react-query", metadata45, handler12);
5386
- registerStaticResource(server, "docs://sdk/server-api", metadata46, handler13);
5387
- registerStaticResource(server, "docs://sdk/customer-auth", metadata47, handler14);
5388
- registerStaticResource(server, "docs://sdk/browser-vs-server", metadata48, handler15);
5389
- registerStaticResource(server, "docs://sdk/file-upload", metadata49, handler16);
5390
- registerStaticResource(server, "docs://sdk/webhook", metadata50, handler17);
5623
+ if (toolSurface === "full") {
5624
+ registerTool(server, schema, metadata, queryCollection);
5625
+ registerTool(server, schema2, metadata2, getCollectionById);
5626
+ registerTool(server, schema3, metadata3, getOrder);
5627
+ registerTool(server, schema4, metadata4, createOrder);
5628
+ registerTool(server, schema5, metadata5, updateOrder);
5629
+ registerTool(server, schema6, metadata6, checkout);
5630
+ registerTool(server, schema7, metadata7, createFulfillment);
5631
+ registerTool(server, schema8, metadata8, updateFulfillment);
5632
+ registerTool(server, schema9, metadata9, updateTransaction);
5633
+ registerTool(server, schema10, metadata10, createReturn);
5634
+ registerTool(server, schema11, metadata11, updateReturn);
5635
+ registerTool(server, schema12, metadata12, returnWithRefund);
5636
+ registerTool(server, schema13, metadata13, addCartItem);
5637
+ registerTool(server, schema14, metadata14, updateCartItem);
5638
+ registerTool(server, schema15, metadata15, removeCartItem);
5639
+ registerTool(server, schema16, metadata16, applyDiscount);
5640
+ registerTool(server, schema17, metadata17, removeDiscount);
5641
+ registerTool(server, schema18, metadata18, clearCart);
5642
+ registerTool(server, schema19, metadata19, validateDiscount);
5643
+ registerTool(server, schema20, metadata20, calculateShipping);
5644
+ registerTool(server, schema21, metadata21, stockCheck);
5645
+ }
5646
+ registerTool(server, schema22, metadata22, getCollectionSchemaTool);
5647
+ registerTool(server, schema23, metadata23, handler);
5648
+ registerTool(server, schema24, metadata24, listConfigurableFields);
5649
+ registerTool(server, schema25, metadata25, updateFieldConfig);
5650
+ registerTool(server, schema26, metadata26, handler2);
5651
+ registerTool(server, schema27, metadata27, handler3);
5652
+ registerTool(server, schema28, metadata28, handler4);
5653
+ registerTool(server, schema29, metadata29, handler5);
5654
+ registerPrompt(server, schema30, metadata30, sdkUsageGuide);
5655
+ registerPrompt(server, schema31, metadata31, collectionQueryHelp);
5656
+ registerPrompt(server, schema32, metadata32, orderFlowGuide);
5657
+ registerPrompt(server, schema33, metadata33, featureSetupGuide);
5658
+ registerStaticResource(server, "config://app", metadata34, handler6);
5659
+ registerStaticResource(server, "collections://schema", metadata35, handler7);
5660
+ registerStaticResource(server, "docs://sdk/getting-started", metadata36, handler8);
5661
+ registerStaticResource(server, "docs://sdk/guides", metadata37, handler9);
5662
+ registerStaticResource(server, "docs://sdk/api", metadata38, handler10);
5663
+ registerStaticResource(server, "docs://sdk/query-builder", metadata39, handler11);
5664
+ registerStaticResource(server, "docs://sdk/react-query", metadata40, handler12);
5665
+ registerStaticResource(server, "docs://sdk/server-api", metadata41, handler13);
5666
+ registerStaticResource(server, "docs://sdk/customer-auth", metadata42, handler14);
5667
+ registerStaticResource(server, "docs://sdk/browser-vs-server", metadata43, handler15);
5668
+ registerStaticResource(server, "docs://sdk/file-upload", metadata44, handler16);
5669
+ registerStaticResource(server, "docs://sdk/webhook", metadata45, handler17);
5391
5670
  return server;
5392
5671
  }
5393
5672
 
5394
5673
  // src/auth.ts
5395
- function validateApiKey(apiKey) {
5396
- if (!apiKey || apiKey.length === 0) {
5397
- return { valid: false, error: "x-api-key header is required" };
5674
+ import { createPublicKey, verify as verifySignature } from "crypto";
5675
+ var ALLOWED_ALGORITHMS = /* @__PURE__ */ new Set(["RS256", "ES256"]);
5676
+ var DEFAULT_CLOCK_SKEW_SECONDS = 30;
5677
+ var DEFAULT_JWKS_URI = `${MCP_OAUTH_ISSUER}/.well-known/jwks.json`;
5678
+ var MAX_ACCESS_TOKEN_LIFETIME_SECONDS = 300;
5679
+ function invalid(errorDescription) {
5680
+ return { valid: false, error: "invalid_token", errorDescription };
5681
+ }
5682
+ function isProduction() {
5683
+ return process.env.NODE_ENV === "production";
5684
+ }
5685
+ function insufficientScope(errorDescription) {
5686
+ return { valid: false, error: "insufficient_scope", errorDescription };
5687
+ }
5688
+ function decodeBase64UrlJson(value) {
5689
+ try {
5690
+ return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
5691
+ } catch {
5692
+ return null;
5398
5693
  }
5399
- if (!apiKey.startsWith("sk01_") && !apiKey.startsWith("pat01_")) {
5400
- return {
5401
- valid: false,
5402
- error: "Invalid API key format. Expected sk01_ or pat01_ token."
5403
- };
5694
+ }
5695
+ function parseScopes(payload) {
5696
+ const rawScopes = typeof payload.scope === "string" ? payload.scope.split(/\s+/).filter(Boolean) : Array.isArray(payload.scp) ? payload.scp : [];
5697
+ return rawScopes.filter(
5698
+ (scope) => scope === MCP_SCOPES.read || scope === MCP_SCOPES.write
5699
+ );
5700
+ }
5701
+ function audienceMatches(aud, expected) {
5702
+ if (typeof aud === "string") return aud === expected;
5703
+ return Array.isArray(aud) && aud.includes(expected);
5704
+ }
5705
+ function verifyJwtSignature(alg, jwk, signingInput, signature) {
5706
+ const key = createPublicKey({ key: jwk, format: "jwk" });
5707
+ const input = Buffer.from(signingInput);
5708
+ if (alg === "RS256") {
5709
+ return verifySignature("RSA-SHA256", input, key, signature);
5710
+ }
5711
+ if (alg === "ES256") {
5712
+ return verifySignature("SHA256", input, { key, dsaEncoding: "ieee-p1363" }, signature);
5713
+ }
5714
+ return false;
5715
+ }
5716
+ var cachedRemoteJwks = null;
5717
+ function jwksUriFor(options) {
5718
+ const raw = isProduction() ? DEFAULT_JWKS_URI : options.jwksUri ?? DEFAULT_JWKS_URI;
5719
+ let url;
5720
+ try {
5721
+ url = new URL(raw);
5722
+ } catch {
5723
+ return null;
5724
+ }
5725
+ if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) {
5726
+ return null;
5727
+ }
5728
+ return url.toString();
5729
+ }
5730
+ async function remoteJwks(options = {}, forceRefresh = false) {
5731
+ const uri = jwksUriFor(options);
5732
+ if (!uri) return null;
5733
+ const now = Date.now();
5734
+ if (!forceRefresh && cachedRemoteJwks && cachedRemoteJwks.uri === uri && cachedRemoteJwks.expiresAt > now) {
5735
+ return cachedRemoteJwks.jwks;
5736
+ }
5737
+ const fetchImpl = options.fetchImpl ?? fetch;
5738
+ let response;
5739
+ try {
5740
+ response = await fetchImpl(uri, {
5741
+ headers: { Accept: "application/json" }
5742
+ });
5743
+ } catch {
5744
+ return null;
5745
+ }
5746
+ if (!response.ok) return null;
5747
+ const parsed = await response.json().catch(() => null);
5748
+ if (!parsed || !Array.isArray(parsed.keys)) return null;
5749
+ cachedRemoteJwks = {
5750
+ expiresAt: now + 3e5,
5751
+ jwks: parsed,
5752
+ uri
5753
+ };
5754
+ return parsed;
5755
+ }
5756
+ function validateAccessToken(token, options = {}) {
5757
+ const parts = token.split(".");
5758
+ if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
5759
+ return invalid("Bearer token must be a compact JWT");
5760
+ }
5761
+ const [encodedHeader, encodedPayload, encodedSignature] = parts;
5762
+ const header = decodeBase64UrlJson(encodedHeader);
5763
+ const payload = decodeBase64UrlJson(encodedPayload);
5764
+ if (!header || !payload) return invalid("Bearer token contains invalid JSON");
5765
+ if (typeof header.alg !== "string" || !ALLOWED_ALGORITHMS.has(header.alg)) {
5766
+ return invalid("Bearer token uses an unsupported signing algorithm");
5767
+ }
5768
+ if (typeof header.kid !== "string" || header.kid.length === 0) {
5769
+ return invalid("Bearer token is missing kid");
5770
+ }
5771
+ const jwks = options.jwks;
5772
+ if (!jwks) return invalid("JWKS is not configured");
5773
+ const jwk = jwks.keys.find((key) => key.kid === header.kid);
5774
+ if (!jwk) return invalid("Bearer token kid is unknown");
5775
+ const signingInput = `${encodedHeader}.${encodedPayload}`;
5776
+ const signature = Buffer.from(encodedSignature, "base64url");
5777
+ if (!verifyJwtSignature(header.alg, jwk, signingInput, signature)) {
5778
+ return invalid("Bearer token signature is invalid");
5779
+ }
5780
+ const issuer = options.issuer ?? MCP_OAUTH_ISSUER;
5781
+ if (payload.iss !== issuer) return invalid("Bearer token issuer is invalid");
5782
+ const audience = options.audience ?? MCP_RESOURCE_AUDIENCE;
5783
+ if (!audienceMatches(payload.aud, audience)) {
5784
+ return invalid("Bearer token audience is invalid");
5785
+ }
5786
+ if (typeof payload.iat !== "number") return invalid("Bearer token is missing iat");
5787
+ if (typeof payload.exp !== "number") return invalid("Bearer token is missing exp");
5788
+ if (payload.exp <= payload.iat) {
5789
+ return invalid("Bearer token lifetime is invalid");
5790
+ }
5791
+ if (payload.exp - payload.iat > MAX_ACCESS_TOKEN_LIFETIME_SECONDS) {
5792
+ return invalid("Bearer token lifetime exceeds 300 seconds");
5793
+ }
5794
+ const nowSeconds = Math.floor((options.now ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
5795
+ const leeway = options.clockSkewSeconds ?? DEFAULT_CLOCK_SKEW_SECONDS;
5796
+ if (payload.iat > nowSeconds + leeway) {
5797
+ return invalid("Bearer token iat is too far in the future");
5798
+ }
5799
+ if (typeof payload.nbf === "number" && payload.nbf > nowSeconds + leeway) {
5800
+ return invalid("Bearer token is not yet valid");
5404
5801
  }
5405
- return { valid: true };
5802
+ if (payload.exp < nowSeconds - leeway) return invalid("Bearer token is expired");
5803
+ const tenantId = payload[MCP_TENANT_CLAIM];
5804
+ if (typeof tenantId !== "string" || tenantId.length === 0) {
5805
+ return invalid("Bearer token tenant_id claim is invalid");
5806
+ }
5807
+ const tenantRole = payload[MCP_TENANT_ROLE_CLAIM];
5808
+ if (tenantRole !== "tenant-admin" && tenantRole !== "tenant-editor" && tenantRole !== "tenant-viewer") {
5809
+ return invalid("Bearer token tenant_role claim is invalid");
5810
+ }
5811
+ if (typeof payload.sub !== "string" || payload.sub.length === 0) {
5812
+ return invalid("Bearer token subject claim is invalid");
5813
+ }
5814
+ const scopes = parseScopes(payload);
5815
+ const requiredScopes = options.requiredScopes ?? [MCP_SCOPES.read];
5816
+ const missingScope = requiredScopes.find((scope) => !scopes.includes(scope));
5817
+ if (missingScope) return insufficientScope(`Bearer token is missing ${missingScope}`);
5818
+ return {
5819
+ valid: true,
5820
+ context: {
5821
+ tenantId,
5822
+ principalId: payload.sub,
5823
+ scopes,
5824
+ tenantRole,
5825
+ authMode: "oauth"
5826
+ }
5827
+ };
5828
+ }
5829
+ function shouldRefreshRemoteJwks(result) {
5830
+ return !result.valid && (result.errorDescription === "Bearer token kid is unknown" || result.errorDescription === "Bearer token signature is invalid");
5831
+ }
5832
+ async function validateBearerAuthorizationHeaderAsync(authorization, options) {
5833
+ if (!authorization) return invalid("Authorization Bearer token is required");
5834
+ const match = authorization.match(/^Bearer\s+(.+)$/i);
5835
+ if (!match) return invalid("Authorization header must use Bearer");
5836
+ if (options?.jwks) {
5837
+ return validateAccessToken(match[1], options);
5838
+ }
5839
+ const jwks = await remoteJwks(options);
5840
+ const result = validateAccessToken(match[1], {
5841
+ ...options,
5842
+ jwks: jwks ?? void 0
5843
+ });
5844
+ if (shouldRefreshRemoteJwks(result)) {
5845
+ const refreshedJwks = await remoteJwks(options, true);
5846
+ if (refreshedJwks) {
5847
+ return validateAccessToken(match[1], {
5848
+ ...options,
5849
+ jwks: refreshedJwks
5850
+ });
5851
+ }
5852
+ }
5853
+ return result;
5406
5854
  }
5407
5855
 
5408
5856
  // src/handler.ts
5409
5857
  var MAX_REQUEST_BODY_BYTES = 1024 * 1024;
5858
+ var MCP_ALLOWED_BROWSER_ORIGIN = "https://01.software";
5410
5859
  var METHOD_NOT_ALLOWED = JSON.stringify({
5411
5860
  jsonrpc: "2.0",
5412
5861
  error: {
@@ -5416,16 +5865,16 @@ var METHOD_NOT_ALLOWED = JSON.stringify({
5416
5865
  id: null
5417
5866
  });
5418
5867
  function setCors(res) {
5419
- res.setHeader("Access-Control-Allow-Origin", "*");
5868
+ res.setHeader("Access-Control-Allow-Origin", MCP_ALLOWED_BROWSER_ORIGIN);
5420
5869
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
5421
5870
  res.setHeader(
5422
5871
  "Access-Control-Allow-Headers",
5423
- "Content-Type, x-api-key, x-publishable-key, x-client-key, mcp-session-id"
5872
+ "Authorization, Content-Type, mcp-session-id"
5424
5873
  );
5425
5874
  res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id, Mcp-Protocol-Version");
5426
5875
  }
5427
- function getHeaderValue(headers2, name) {
5428
- const value = headers2[name.toLowerCase()];
5876
+ function getHeaderValue(headers, name) {
5877
+ const value = headers[name.toLowerCase()];
5429
5878
  if (Array.isArray(value)) return value[0];
5430
5879
  return value;
5431
5880
  }
@@ -5450,143 +5899,69 @@ var HOME_PAGE = `01.software MCP Server
5450
5899
  ======================
5451
5900
 
5452
5901
  MCP server for AI agents to interact with the 01.software API.
5453
- Manage content, products, orders, and more.
5902
+ Connect over HTTP with OAuth, or use the local CLI stdio transport for full
5903
+ server-key operations.
5454
5904
 
5455
5905
 
5456
5906
  Authentication
5457
5907
  --------------
5458
5908
 
5459
- All requests require both:
5460
- x-api-key opaque bearer token
5461
- x-publishable-key publishable key for routing, rate limits, and quota enforcement
5462
-
5463
- Use x-client-key as a legacy alias for x-publishable-key.
5464
-
5465
- Accepted formats:
5466
- sk01_{40hex} tenant API key (Console > Settings > API Keys)
5467
- pat01_{40hex} personal access token (user-scoped local workflow)
5468
- pk01_{...} publishable key
5469
-
5470
- export SOFTWARE_PUBLISHABLE_KEY=pk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5471
- export SOFTWARE_SECRET_KEY=sk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5472
-
5909
+ HTTP MCP uses OAuth discovery and Authorization Code + PKCE.
5910
+ Clients should connect to:
5473
5911
 
5474
5912
  Connect
5475
5913
  -------
5476
5914
 
5477
5915
  Claude Code:
5478
5916
 
5479
- claude mcp add --transport http \\
5480
- --header "x-api-key: $SOFTWARE_SECRET_KEY" \\
5481
- --header "x-publishable-key: $SOFTWARE_PUBLISHABLE_KEY" \\
5482
- 01software https://mcp.01.software/mcp
5917
+ claude mcp add --transport http 01software https://mcp.01.software/mcp
5483
5918
 
5484
- Codex (.codex/config.toml, project-safe when using env vars):
5919
+ Codex (.codex/config.toml):
5485
5920
 
5486
5921
  [mcp_servers.01software]
5487
5922
  url = "https://mcp.01.software/mcp"
5488
5923
 
5489
- [mcp_servers.01software.env_http_headers]
5490
- x-api-key = "SOFTWARE_SECRET_KEY"
5491
- x-publishable-key = "SOFTWARE_PUBLISHABLE_KEY"
5924
+ Cursor / Claude Desktop / other JSON clients:
5492
5925
 
5493
- // or .mcp.json
5494
5926
  {
5495
5927
  "mcpServers": {
5496
5928
  "01software": {
5497
5929
  "type": "http",
5498
- "url": "https://mcp.01.software/mcp",
5499
- "headers": {
5500
- "x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
5501
- "x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
5502
- }
5930
+ "url": "https://mcp.01.software/mcp"
5503
5931
  }
5504
5932
  }
5505
5933
  }
5506
5934
 
5507
- Cursor (.cursor/mcp.json):
5935
+ Windsurf (~/.codeium/windsurf/mcp_config.json):
5508
5936
 
5509
5937
  {
5510
5938
  "mcpServers": {
5511
5939
  "01software": {
5512
- "url": "https://mcp.01.software/mcp",
5513
- "headers": {
5514
- "x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
5515
- "x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
5516
- }
5940
+ "serverUrl": "https://mcp.01.software/mcp"
5517
5941
  }
5518
5942
  }
5519
5943
  }
5520
5944
 
5521
- Windsurf (~/.codeium/windsurf/mcp_config.json):
5945
+ CLI (stdio):
5522
5946
 
5523
- {
5524
- "mcpServers": {
5525
- "01software": {
5526
- "serverUrl": "https://mcp.01.software/mcp",
5527
- "headers": {
5528
- "x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
5529
- "x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
5530
- }
5531
- }
5532
- }
5533
- }
5947
+ npx @01.software/cli mcp
5534
5948
 
5535
- VS Code (.vscode/mcp.json):
5536
5949
 
5537
- {
5538
- "servers": {
5539
- "01software": {
5540
- "type": "http",
5541
- "url": "https://mcp.01.software/mcp",
5542
- "headers": {
5543
- "x-api-key": "\${input:01software-api-key}",
5544
- "x-publishable-key": "\${input:01software-publishable-key}"
5545
- }
5546
- }
5547
- }
5548
- }
5950
+ HTTP OAuth Tools
5951
+ ----------------
5549
5952
 
5550
- Claude Desktop (claude_desktop_config.json):
5953
+ Schema get-collection-schema
5954
+ Context get-tenant-context
5955
+ Field Config list-configurable-fields, update-field-config
5956
+ Guidance sdk-get-recipe, sdk-search-docs, sdk-get-auth-setup, sdk-get-collection-pattern
5551
5957
 
5552
- {
5553
- "mcpServers": {
5554
- "01software": {
5555
- "url": "https://mcp.01.software/mcp",
5556
- "headers": {
5557
- "x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
5558
- "x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
5559
- }
5560
- }
5561
- }
5562
- }
5958
+ Full Tool Surface
5959
+ -----------------
5563
5960
 
5564
- CLI (stdio):
5961
+ The local CLI stdio transport exposes CRUD, commerce, cart, validation, product,
5962
+ schema, context, field-config, and guidance tools:
5565
5963
 
5566
5964
  npx @01.software/cli mcp
5567
- # Reads SOFTWARE_SECRET_KEY=sk01_... or pat01_...
5568
- # Also reads SOFTWARE_PUBLISHABLE_KEY for CDN routing, rate limits, and quota enforcement
5569
-
5570
- Security: never commit raw sk01_... or pat01_... tokens to repo-local MCP
5571
- config files. Prefer client secret prompts, environment interpolation, OS secret
5572
- managers, or ignored local files. Avoid passing real tokens directly on
5573
- shared-machine command lines because shell history and process listings can
5574
- expose them.
5575
-
5576
-
5577
- Tools (34)
5578
- ----------
5579
-
5580
- CRUD query, get, create, update, delete, delete-many, update-many
5581
- Orders create-order, checkout, get-order, update-order, create-fulfillment, update-fulfillment, update-transaction
5582
- Returns create-return, update-return, return-with-refund
5583
- Cart add-cart-item, update-cart-item, remove-cart-item, clear-cart, apply-discount, remove-discount
5584
- Validation validate-discount, calculate-shipping
5585
- Products stock-check
5586
- Schema get-collection-schema
5587
- Context get-tenant-context
5588
- Field Config list-configurable-fields, update-field-config
5589
- Guidance sdk-get-recipe, sdk-search-docs, sdk-get-auth-setup, sdk-get-collection-pattern
5590
5965
 
5591
5966
  Prompts (4): sdk-usage-guide, collection-query-help, order-flow-guide, feature-setup-guide
5592
5967
  Resources (12): config, collections-schema, getting-started, guides, api, query-builder, react-query, server-api, customer-auth, browser-vs-server, file-upload, webhook
@@ -5600,6 +5975,19 @@ SDK https://01.software/docs/sdk/client
5600
5975
  API Reference https://01.software/docs/api/rest-api
5601
5976
  Console https://console.01.software
5602
5977
  `;
5978
+ var PROTECTED_RESOURCE_METADATA = JSON.stringify({
5979
+ resource: MCP_RESOURCE_AUDIENCE,
5980
+ authorization_servers: [MCP_OAUTH_ISSUER],
5981
+ scopes_supported: [MCP_SCOPES.read, MCP_SCOPES.write]
5982
+ });
5983
+ var SERVICE_JWKS_PATH = "/.well-known/service-jwks.json";
5984
+ function writeOAuthError(res, status, error, description) {
5985
+ res.writeHead(status, {
5986
+ "Content-Type": "application/json",
5987
+ "WWW-Authenticate": `Bearer resource_metadata="${MCP_PROTECTED_RESOURCE_METADATA_PATH}", error="${error}", error_description="${description}"`
5988
+ });
5989
+ res.end(JSON.stringify({ error, error_description: description }));
5990
+ }
5603
5991
  async function handler18(req, res) {
5604
5992
  setCors(res);
5605
5993
  if (req.method === "OPTIONS") {
@@ -5608,6 +5996,30 @@ async function handler18(req, res) {
5608
5996
  return;
5609
5997
  }
5610
5998
  if (req.method === "GET") {
5999
+ const pathname = new URL(req.url ?? "/", MCP_RESOURCE_AUDIENCE).pathname;
6000
+ if (pathname === MCP_PROTECTED_RESOURCE_METADATA_PATH) {
6001
+ res.setHeader("Access-Control-Allow-Origin", "*");
6002
+ res.writeHead(200, { "Content-Type": "application/json" });
6003
+ res.end(PROTECTED_RESOURCE_METADATA);
6004
+ return;
6005
+ }
6006
+ if (pathname === SERVICE_JWKS_PATH) {
6007
+ res.setHeader("Access-Control-Allow-Origin", "*");
6008
+ try {
6009
+ res.writeHead(200, {
6010
+ "Cache-Control": "public, max-age=60",
6011
+ "Content-Type": "application/json"
6012
+ });
6013
+ res.end(JSON.stringify(mcpServicePublicJwks()));
6014
+ } catch {
6015
+ res.writeHead(503, {
6016
+ "Cache-Control": "no-store",
6017
+ "Content-Type": "application/json"
6018
+ });
6019
+ res.end(JSON.stringify({ keys: [] }));
6020
+ }
6021
+ return;
6022
+ }
5611
6023
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
5612
6024
  res.end(HOME_PAGE);
5613
6025
  return;
@@ -5617,20 +6029,14 @@ async function handler18(req, res) {
5617
6029
  res.end(METHOD_NOT_ALLOWED);
5618
6030
  return;
5619
6031
  }
5620
- const apiKey = getHeaderValue(req.headers, "x-api-key");
5621
- const auth = validateApiKey(apiKey);
6032
+ const auth = await validateBearerAuthorizationHeaderAsync(
6033
+ getHeaderValue(req.headers, "authorization")
6034
+ );
5622
6035
  if (!auth.valid) {
5623
- res.writeHead(401, { "Content-Type": "application/json" });
5624
- res.end(JSON.stringify({ error: auth.error }));
5625
- return;
5626
- }
5627
- const publishableKey = getHeaderValue(req.headers, "x-publishable-key") ?? getHeaderValue(req.headers, "x-client-key");
5628
- if (!publishableKey) {
5629
- res.writeHead(401, { "Content-Type": "application/json" });
5630
- res.end(JSON.stringify({ error: "x-publishable-key header is required" }));
6036
+ writeOAuthError(res, auth.error === "insufficient_scope" ? 403 : 401, auth.error, auth.errorDescription);
5631
6037
  return;
5632
6038
  }
5633
- const server = createServer();
6039
+ const server = createServer({ toolSurface: "oauth" });
5634
6040
  const transport = new StreamableHTTPServerTransport({
5635
6041
  sessionIdGenerator: void 0
5636
6042
  });
@@ -5645,25 +6051,37 @@ async function handler18(req, res) {
5645
6051
  void close();
5646
6052
  });
5647
6053
  await server.connect(transport);
5648
- const headers2 = new Headers();
6054
+ const headers = new Headers();
5649
6055
  for (const [key, value] of Object.entries(req.headers)) {
5650
- if (typeof value === "string") headers2.set(key, value);
5651
- else if (Array.isArray(value)) headers2.set(key, value.join(", "));
6056
+ if (typeof value === "string") headers.set(key, value);
6057
+ else if (Array.isArray(value)) headers.set(key, value.join(", "));
5652
6058
  }
5653
- await requestContext.run({ headers: headers2 }, async () => {
6059
+ await requestContext.run({ headers, auth: auth.context }, async () => {
5654
6060
  try {
5655
6061
  const body = req.body ?? JSON.parse(await readBody(req));
5656
6062
  await transport.handleRequest(req, res, body);
5657
- } catch {
5658
- if (!res.headersSent) {
5659
- res.writeHead(500, { "Content-Type": "application/json" });
5660
- res.end(JSON.stringify({ error: "Internal server error" }));
5661
- }
6063
+ } catch (err) {
6064
+ writeRequestError(res, err);
5662
6065
  } finally {
5663
6066
  await close();
5664
6067
  }
5665
6068
  });
5666
6069
  }
6070
+ function writeRequestError(res, err) {
6071
+ if (res.headersSent) return;
6072
+ if (err instanceof SyntaxError) {
6073
+ res.writeHead(400, { "Content-Type": "application/json" });
6074
+ res.end(JSON.stringify({ error: "Invalid JSON body" }));
6075
+ return;
6076
+ }
6077
+ if (err instanceof Error && err.message === "Request body too large") {
6078
+ res.writeHead(413, { "Content-Type": "application/json" });
6079
+ res.end(JSON.stringify({ error: "Request body too large" }));
6080
+ return;
6081
+ }
6082
+ res.writeHead(500, { "Content-Type": "application/json" });
6083
+ res.end(JSON.stringify({ error: "Internal server error" }));
6084
+ }
5667
6085
  export {
5668
6086
  handler18 as default
5669
6087
  };