@01.software/cli 0.9.0 → 0.10.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.
@@ -88,6 +88,285 @@ var MCP_CONSOLE_SERVICE_AUDIENCE = "https://api.01.software/internal/mcp";
88
88
  var MCP_CONSOLE_SERVICE_SCOPE = "console:mcp_proxy";
89
89
  var MCP_SERVICE_TOKEN_LIFETIME_SECONDS = 60;
90
90
 
91
+ // ../../packages/contracts/src/tenant/index.ts
92
+ import { z } from "zod";
93
+ var tenantFieldConfigStateSchema = z.object({
94
+ hiddenFields: z.array(z.string()),
95
+ isHidden: z.boolean()
96
+ }).strict();
97
+ var tenantContextQuerySchema = z.object({
98
+ counts: z.literal("true").optional()
99
+ }).strict();
100
+ var tenantContextToolInputSchema = z.object({
101
+ includeCounts: z.boolean().optional().default(false).describe(
102
+ "Include per-collection document counts and config status (bypasses cache, slower)"
103
+ )
104
+ }).strict();
105
+ var tenantContextResponseSchema = z.object({
106
+ tenant: z.object({
107
+ id: z.string(),
108
+ name: z.string(),
109
+ plan: z.string(),
110
+ planSource: z.string().optional(),
111
+ authoritative: z.boolean().optional(),
112
+ capabilityVersion: z.string().optional()
113
+ }).strict(),
114
+ features: z.array(z.string()),
115
+ collections: z.object({
116
+ active: z.array(z.string()),
117
+ inactive: z.array(z.string())
118
+ }).strict(),
119
+ fieldConfigs: z.record(z.string(), tenantFieldConfigStateSchema),
120
+ counts: z.record(z.string(), z.number()).optional(),
121
+ config: z.object({
122
+ webhookConfigured: z.boolean()
123
+ }).strict().optional()
124
+ }).strict();
125
+ var COLLECTION_SCHEMA_CONTRACT_VERSION = 1;
126
+ var collectionSchemaEndpointParamsSchema = z.object({
127
+ collectionSlug: z.string().min(1, "collectionSlug is required")
128
+ }).strict();
129
+ function createCollectionSchemaToolInputSchema(collections) {
130
+ return z.object({
131
+ collection: z.enum(collections).describe("Collection name (required)")
132
+ }).strict();
133
+ }
134
+ var collectionFieldOptionSchema = z.object({
135
+ label: z.string(),
136
+ value: z.string()
137
+ }).strict();
138
+ var collectionFieldSchema = z.lazy(
139
+ () => z.object({
140
+ name: z.string(),
141
+ path: z.string(),
142
+ type: z.string(),
143
+ required: z.literal(true).optional(),
144
+ unique: z.literal(true).optional(),
145
+ hasMany: z.literal(true).optional(),
146
+ relationTo: z.union([z.string(), z.array(z.string())]).optional(),
147
+ options: z.array(collectionFieldOptionSchema).optional(),
148
+ hidden: z.literal(true).optional(),
149
+ systemManaged: z.literal(true).optional(),
150
+ writable: z.boolean().optional(),
151
+ fields: z.array(collectionFieldSchema).optional()
152
+ }).strict()
153
+ );
154
+ var collectionSchemaResponseSchema = z.object({
155
+ contractVersion: z.literal(COLLECTION_SCHEMA_CONTRACT_VERSION),
156
+ mode: z.literal("effective"),
157
+ collection: z.object({
158
+ slug: z.string(),
159
+ timestamps: z.boolean(),
160
+ alwaysActive: z.boolean(),
161
+ feature: z.string().nullable(),
162
+ systemFields: z.array(z.string()),
163
+ visibility: z.object({
164
+ collectionHidden: z.boolean(),
165
+ hiddenFields: z.array(z.string())
166
+ }).strict(),
167
+ fields: z.array(collectionFieldSchema)
168
+ }).strict()
169
+ }).strict();
170
+
171
+ // ../../packages/contracts/src/ecommerce/index.ts
172
+ import { z as z2 } from "zod";
173
+ var transactionStatusSchema = z2.enum([
174
+ "pending",
175
+ "paid",
176
+ "failed",
177
+ "canceled"
178
+ ]);
179
+ var updateTransactionSchema = z2.object({
180
+ pgPaymentId: z2.string().min(1, "pgPaymentId is required").describe("PG payment ID (required)"),
181
+ status: transactionStatusSchema.describe(
182
+ "New transaction status (required)"
183
+ ),
184
+ paymentMethod: z2.string().optional().describe("Payment method (optional)"),
185
+ receiptUrl: z2.string().optional().describe("Receipt URL (optional)"),
186
+ paymentKey: z2.string().min(1).optional().describe("Provider payment key for verified paid confirmation"),
187
+ amount: z2.number().int().positive().optional().describe("Provider-confirmed amount for verified paid confirmation")
188
+ }).strict();
189
+ var UpdateTransactionSchema = updateTransactionSchema;
190
+ var returnReasonSchema = z2.enum([
191
+ "change_of_mind",
192
+ "defective",
193
+ "wrong_delivery",
194
+ "damaged",
195
+ "other"
196
+ ]);
197
+ var restockActionSchema = z2.enum(["return_to_stock", "discard"]);
198
+ var returnWithRefundItemSchema = z2.object({
199
+ orderItem: z2.union([z2.string(), z2.number()]).transform(String),
200
+ quantity: z2.number().int().positive("quantity must be a positive integer"),
201
+ restockAction: restockActionSchema.default("return_to_stock")
202
+ }).strict();
203
+ var returnWithRefundSchema = z2.object({
204
+ orderNumber: z2.string().min(1, "orderNumber is required").describe("Order number (required)"),
205
+ reason: returnReasonSchema.optional().describe("Return reason (optional)"),
206
+ reasonDetail: z2.string().optional().describe("Detailed reason text (optional)"),
207
+ returnItems: z2.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
208
+ refundAmount: z2.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
209
+ pgPaymentId: z2.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
210
+ paymentKey: z2.string().min(1).optional().describe("Provider payment key for verified refund"),
211
+ refundReceiptUrl: z2.string().optional().describe("Refund receipt URL (optional)")
212
+ }).strict();
213
+ var ReturnWithRefundSchema = returnWithRefundSchema;
214
+
215
+ // ../../packages/contracts/src/mcp/index.ts
216
+ var MCP_TOOL_CONTRACT = {
217
+ "query-collection": {
218
+ consoleRole: "tenant-viewer",
219
+ oauthScope: "mcp:read",
220
+ readOnly: true
221
+ },
222
+ "get-collection-by-id": {
223
+ consoleRole: "tenant-viewer",
224
+ oauthScope: "mcp:read",
225
+ readOnly: true
226
+ },
227
+ "get-order": {
228
+ consoleRole: "tenant-viewer",
229
+ oauthScope: "mcp:read",
230
+ readOnly: true
231
+ },
232
+ "stock-check": {
233
+ consoleRole: "tenant-viewer",
234
+ oauthScope: "mcp:read",
235
+ readOnly: true
236
+ },
237
+ "validate-discount": {
238
+ consoleRole: "tenant-viewer",
239
+ oauthScope: "mcp:read",
240
+ readOnly: true
241
+ },
242
+ "calculate-shipping": {
243
+ consoleRole: "tenant-viewer",
244
+ oauthScope: "mcp:read",
245
+ readOnly: true
246
+ },
247
+ "get-collection-schema": {
248
+ consoleRole: "tenant-viewer",
249
+ oauthScope: "mcp:read",
250
+ readOnly: true
251
+ },
252
+ "list-configurable-fields": {
253
+ consoleRole: "tenant-viewer",
254
+ oauthScope: "mcp:read",
255
+ readOnly: true
256
+ },
257
+ "get-tenant-context": {
258
+ consoleRole: "tenant-viewer",
259
+ oauthScope: "mcp:read",
260
+ readOnly: true
261
+ },
262
+ "add-cart-item": {
263
+ consoleRole: "tenant-editor",
264
+ oauthScope: "mcp:write",
265
+ readOnly: false
266
+ },
267
+ "update-cart-item": {
268
+ consoleRole: "tenant-editor",
269
+ oauthScope: "mcp:write",
270
+ readOnly: false
271
+ },
272
+ "remove-cart-item": {
273
+ consoleRole: "tenant-editor",
274
+ oauthScope: "mcp:write",
275
+ readOnly: false
276
+ },
277
+ "clear-cart": {
278
+ consoleRole: "tenant-editor",
279
+ oauthScope: "mcp:write",
280
+ readOnly: false
281
+ },
282
+ "apply-discount": {
283
+ consoleRole: "tenant-editor",
284
+ oauthScope: "mcp:write",
285
+ readOnly: false
286
+ },
287
+ "remove-discount": {
288
+ consoleRole: "tenant-editor",
289
+ oauthScope: "mcp:write",
290
+ readOnly: false
291
+ },
292
+ checkout: {
293
+ consoleRole: "tenant-admin",
294
+ oauthScope: "mcp:write",
295
+ readOnly: false
296
+ },
297
+ "create-order": {
298
+ consoleRole: "tenant-admin",
299
+ oauthScope: "mcp:write",
300
+ readOnly: false
301
+ },
302
+ "update-order": {
303
+ consoleRole: "tenant-admin",
304
+ oauthScope: "mcp:write",
305
+ readOnly: false
306
+ },
307
+ "create-fulfillment": {
308
+ consoleRole: "tenant-admin",
309
+ oauthScope: "mcp:write",
310
+ readOnly: false
311
+ },
312
+ "update-fulfillment": {
313
+ consoleRole: "tenant-admin",
314
+ oauthScope: "mcp:write",
315
+ readOnly: false
316
+ },
317
+ "create-return": {
318
+ consoleRole: "tenant-admin",
319
+ oauthScope: "mcp:write",
320
+ readOnly: false
321
+ },
322
+ "update-return": {
323
+ consoleRole: "tenant-admin",
324
+ oauthScope: "mcp:write",
325
+ readOnly: false
326
+ },
327
+ "return-with-refund": {
328
+ consoleRole: "tenant-admin",
329
+ oauthScope: "mcp:write",
330
+ readOnly: false
331
+ },
332
+ "update-transaction": {
333
+ consoleRole: "tenant-admin",
334
+ oauthScope: "mcp:write",
335
+ readOnly: false
336
+ },
337
+ "update-field-config": {
338
+ consoleRole: "tenant-admin",
339
+ oauthScope: "mcp:write",
340
+ readOnly: false
341
+ },
342
+ "sdk-get-recipe": {
343
+ consoleRole: "tenant-viewer",
344
+ oauthScope: "mcp:read",
345
+ readOnly: true
346
+ },
347
+ "sdk-search-docs": {
348
+ consoleRole: "tenant-viewer",
349
+ oauthScope: "mcp:read",
350
+ readOnly: true
351
+ },
352
+ "sdk-get-auth-setup": {
353
+ consoleRole: "tenant-viewer",
354
+ oauthScope: "mcp:read",
355
+ readOnly: true
356
+ },
357
+ "sdk-get-collection-pattern": {
358
+ consoleRole: "tenant-viewer",
359
+ oauthScope: "mcp:read",
360
+ readOnly: true
361
+ }
362
+ };
363
+ var MCP_TOOL_NAMES = Object.keys(
364
+ MCP_TOOL_CONTRACT
365
+ );
366
+ function isMcpToolName(toolName) {
367
+ return Object.prototype.hasOwnProperty.call(MCP_TOOL_CONTRACT, toolName);
368
+ }
369
+
91
370
  // src/tool-policy.ts
92
371
  var READ_ONLY_ANNOTATION = {
93
372
  readOnly: true,
@@ -344,14 +623,14 @@ var TOOL_POLICY_MANIFEST = {
344
623
  }
345
624
  };
346
625
  function evaluateToolPolicy(toolName, scopes) {
347
- const entry = TOOL_POLICY_MANIFEST[toolName];
348
- if (!entry) {
626
+ if (!isMcpToolName(toolName)) {
349
627
  return {
350
628
  allowed: false,
351
629
  reason: "tool_policy_missing",
352
630
  message: `No tool-policy entry for ${toolName}`
353
631
  };
354
632
  }
633
+ const entry = TOOL_POLICY_MANIFEST[toolName];
355
634
  if (!scopes.includes(entry.oauthScope)) {
356
635
  return {
357
636
  allowed: false,
@@ -362,17 +641,12 @@ function evaluateToolPolicy(toolName, scopes) {
362
641
  return { allowed: true, entry };
363
642
  }
364
643
 
365
- // src/tools/query-collection.ts
366
- import { z } from "zod";
644
+ // src/lib/mcp-telemetry.ts
645
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
646
+ import { randomUUID as randomUUID2 } from "crypto";
367
647
 
368
- // src/lib/client.ts
369
- import {
370
- CollectionClient,
371
- CommunityClient,
372
- ModerationApi,
373
- ServerCommerceClient,
374
- createServerClient
375
- } from "@01.software/sdk";
648
+ // src/lib/console-api.ts
649
+ import { createHash } from "crypto";
376
650
 
377
651
  // src/service-auth.ts
378
652
  import { createPrivateKey, randomUUID, sign as signBytes } from "crypto";
@@ -501,43 +775,209 @@ function signMcpServiceToken(context) {
501
775
  return `${signingInput}.${signature.toString("base64url")}`;
502
776
  }
503
777
 
504
- // src/lib/client.ts
778
+ // src/lib/console-api.ts
779
+ var BASE_URL = process.env.SOFTWARE_API_URL || "http://localhost:3000";
780
+ var TIMEOUT_MS = 5e3;
505
781
  var MISSING_HTTP_AUTH_CONTEXT_ERROR = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
506
- function getClient() {
782
+ function resolveAuthHeaderContext() {
507
783
  const oauthContext = tenantAuthContext();
508
784
  if (oauthContext) {
509
- const serviceToken = signMcpServiceToken(oauthContext);
510
- const client = {
511
- lastRequestId: null,
512
- commerce: void 0,
513
- collections: void 0,
514
- community: void 0
515
- };
516
- const onRequestId = (id) => {
517
- client.lastRequestId = id;
785
+ return {
786
+ apiKey: signMcpServiceToken(oauthContext),
787
+ mode: "oauth"
518
788
  };
519
- client.commerce = new ServerCommerceClient({
520
- secretKey: serviceToken,
521
- onRequestId
522
- });
523
- client.collections = new CollectionClient(
524
- "",
525
- serviceToken,
526
- void 0,
527
- void 0,
528
- onRequestId
789
+ }
790
+ if (hasRequestContext()) throw new Error(MISSING_HTTP_AUTH_CONTEXT_ERROR);
791
+ return {
792
+ apiKey: process.env.SOFTWARE_SECRET_KEY,
793
+ mode: "stdio",
794
+ publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY ?? process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY
795
+ };
796
+ }
797
+ function resolveApiKey() {
798
+ const { apiKey } = resolveAuthHeaderContext();
799
+ if (!apiKey || typeof apiKey !== "string") {
800
+ throw new Error(
801
+ "Authentication required. Set SOFTWARE_SECRET_KEY for stdio transport."
529
802
  );
530
- const community = new CommunityClient({ secretKey: serviceToken });
531
- const moderation = new ModerationApi({ secretKey: serviceToken, onRequestId });
532
- client.community = Object.assign(community, {
533
- moderation: {
534
- banCustomer: moderation.banCustomer.bind(moderation),
535
- unbanCustomer: moderation.unbanCustomer.bind(moderation)
536
- }
803
+ }
804
+ return apiKey;
805
+ }
806
+ function buildAuthHeaders(apiKey) {
807
+ const { mode, publishableKey } = resolveAuthHeaderContext();
808
+ const headers = {
809
+ Authorization: `Bearer ${apiKey}`
810
+ };
811
+ if (mode === "stdio" && publishableKey) {
812
+ headers["X-Publishable-Key"] = publishableKey;
813
+ }
814
+ return headers;
815
+ }
816
+ function extractErrorMessage(body) {
817
+ if (!body || typeof body !== "object") return void 0;
818
+ const b = body;
819
+ if (typeof b.error === "string") return b.error;
820
+ if (Array.isArray(b.errors) && b.errors[0]?.message) {
821
+ return String(b.errors[0].message);
822
+ }
823
+ if (typeof b.message === "string") return b.message;
824
+ return void 0;
825
+ }
826
+ async function consoleGet(path, apiKey) {
827
+ const authHeaders = buildAuthHeaders(apiKey);
828
+ const controller = new AbortController();
829
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
830
+ try {
831
+ const res = await fetch(`${BASE_URL}${path}`, {
832
+ headers: authHeaders,
833
+ signal: controller.signal
537
834
  });
538
- return client;
835
+ if (!res.ok) {
836
+ const body = await res.json().catch(() => ({}));
837
+ const msg = extractErrorMessage(body);
838
+ throw new Error(msg || `Console GET ${path} failed: ${res.status}`);
839
+ }
840
+ return res.json();
841
+ } finally {
842
+ clearTimeout(timeoutId);
539
843
  }
540
- if (hasRequestContext()) throw new Error(MISSING_HTTP_AUTH_CONTEXT_ERROR);
844
+ }
845
+ async function consolePost(path, body, apiKey) {
846
+ const authHeaders = buildAuthHeaders(apiKey);
847
+ const controller = new AbortController();
848
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
849
+ try {
850
+ const res = await fetch(`${BASE_URL}${path}`, {
851
+ method: "POST",
852
+ headers: { ...authHeaders, "Content-Type": "application/json" },
853
+ body: JSON.stringify(body),
854
+ signal: controller.signal
855
+ });
856
+ if (!res.ok) {
857
+ const errBody = await res.json().catch(() => ({}));
858
+ const msg = extractErrorMessage(errBody);
859
+ throw new Error(msg || `Console POST ${path} failed: ${res.status}`);
860
+ }
861
+ return res.json();
862
+ } finally {
863
+ clearTimeout(timeoutId);
864
+ }
865
+ }
866
+ async function consolePostTelemetry(path, body, apiKey) {
867
+ const authHeaders = buildAuthHeaders(apiKey);
868
+ const controller = new AbortController();
869
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
870
+ try {
871
+ const res = await fetch(`${BASE_URL}${path}`, {
872
+ method: "POST",
873
+ headers: { ...authHeaders, "Content-Type": "application/json" },
874
+ body: JSON.stringify(body),
875
+ signal: controller.signal
876
+ });
877
+ if (!res.ok) {
878
+ const errBody = await res.json().catch(() => ({}));
879
+ const msg = extractErrorMessage(errBody);
880
+ throw new Error(msg || `Console POST ${path} failed: ${res.status}`);
881
+ }
882
+ } finally {
883
+ clearTimeout(timeoutId);
884
+ }
885
+ }
886
+
887
+ // src/lib/mcp-telemetry.ts
888
+ var TELEMETRY_ENDPOINT = "/api/tenants/mcp-telemetry";
889
+ var FLUSH_TIMEOUT_MS = 1500;
890
+ var telemetryContext = new AsyncLocalStorage2();
891
+ function createMcpTelemetrySummary(transport) {
892
+ return {
893
+ sessionId: randomUUID2(),
894
+ startedAtMs: Date.now(),
895
+ successfulWriteCount: 0,
896
+ toolCallCount: 0,
897
+ toolCounts: /* @__PURE__ */ new Map(),
898
+ transport
899
+ };
900
+ }
901
+ function currentMcpTelemetrySummary() {
902
+ return telemetryContext.getStore();
903
+ }
904
+ function runWithMcpTelemetry(summary, fn) {
905
+ return telemetryContext.run(summary, fn);
906
+ }
907
+ function recordMcpToolResult(params) {
908
+ if (!isMcpToolName(params.toolName)) return;
909
+ const toolName = params.toolName;
910
+ params.summary.toolCallCount += 1;
911
+ params.summary.toolCounts.set(
912
+ toolName,
913
+ (params.summary.toolCounts.get(toolName) ?? 0) + 1
914
+ );
915
+ if (!MCP_TOOL_CONTRACT[toolName].readOnly && toolResultSucceeded(params.resultText)) {
916
+ params.summary.successfulWriteCount += 1;
917
+ }
918
+ }
919
+ function toMcpTelemetryBody(summary) {
920
+ if (summary.toolCallCount <= 0) return null;
921
+ const durationMs = summary.transport === "http" ? Math.max(0, summary.durationMs ?? Date.now() - summary.startedAtMs) : void 0;
922
+ return {
923
+ converted: summary.successfulWriteCount > 0,
924
+ ...durationMs !== void 0 ? { durationMs } : {},
925
+ sessionId: summary.sessionId,
926
+ successfulWriteCount: summary.successfulWriteCount,
927
+ toolCallCount: summary.toolCallCount,
928
+ toolCounts: Object.fromEntries(summary.toolCounts),
929
+ transport: summary.transport
930
+ };
931
+ }
932
+ async function flushMcpTelemetrySummary(summary) {
933
+ const body = toMcpTelemetryBody(summary);
934
+ if (!body) return;
935
+ await swallow(
936
+ withTimeout(async () => {
937
+ const apiKey = resolveApiKey();
938
+ await consolePostTelemetry(TELEMETRY_ENDPOINT, body, apiKey);
939
+ }, FLUSH_TIMEOUT_MS)
940
+ );
941
+ }
942
+ function toolResultSucceeded(resultText) {
943
+ if (!resultText) return false;
944
+ try {
945
+ const parsed = JSON.parse(resultText);
946
+ return parsed.success === true;
947
+ } catch {
948
+ return false;
949
+ }
950
+ }
951
+ async function withTimeout(fn, timeoutMs) {
952
+ let timer;
953
+ await Promise.race([
954
+ fn(),
955
+ new Promise((resolve) => {
956
+ timer = setTimeout(resolve, timeoutMs);
957
+ })
958
+ ]);
959
+ if (timer) clearTimeout(timer);
960
+ }
961
+ async function swallow(promise) {
962
+ try {
963
+ await promise;
964
+ } catch {
965
+ }
966
+ }
967
+
968
+ // src/tools/query-collection.ts
969
+ import { z as z3 } from "zod";
970
+
971
+ // src/lib/client.ts
972
+ import { createServerClient } from "@01.software/sdk";
973
+ var MISSING_HTTP_AUTH_CONTEXT_ERROR2 = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
974
+ var HTTP_OAUTH_SDK_CLIENT_ERROR = "MCP HTTP OAuth requests cannot use SDK-backed tools. Use reviewed Console service endpoints for OAuth transport.";
975
+ function getClient() {
976
+ const oauthContext = tenantAuthContext();
977
+ if (oauthContext) {
978
+ throw new Error(HTTP_OAUTH_SDK_CLIENT_ERROR);
979
+ }
980
+ if (hasRequestContext()) throw new Error(MISSING_HTTP_AUTH_CONTEXT_ERROR2);
541
981
  const secretKey = process.env.SOFTWARE_SECRET_KEY;
542
982
  const publishableKey = process.env.SOFTWARE_PUBLISHABLE_KEY || process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY;
543
983
  if (!secretKey) {
@@ -560,15 +1000,18 @@ function getClient() {
560
1000
  }
561
1001
 
562
1002
  // src/tools/query-collection.ts
563
- import { COLLECTIONS } from "@01.software/sdk";
1003
+ import { SERVER_COLLECTIONS } from "@01.software/sdk";
564
1004
  var schema = {
565
- collection: z.enum(COLLECTIONS).describe("Collection name (required)"),
566
- where: z.string().optional().describe(
1005
+ collection: z3.enum(SERVER_COLLECTIONS).describe("Collection name (required)"),
1006
+ where: z3.string().optional().describe(
567
1007
  `Filter conditions (JSON string, optional). Pass the Payload query condition object as a JSON string. Example: '{"title":{"equals":"Product name"}}'`
568
1008
  ),
569
- limit: z.number().min(1).max(100).default(10).describe("Maximum number of items to return (1-100, default: 10)."),
570
- page: z.number().optional().describe("Page number (optional). Starts from 1. Used for pagination."),
571
- sort: z.string().regex(/^-?[a-zA-Z0-9_.]+$/, 'Sort must be a field name, optionally prefixed with "-" for descending').optional().describe(
1009
+ limit: z3.number().min(1).max(100).default(10).describe("Maximum number of items to return (1-100, default: 10)."),
1010
+ page: z3.number().optional().describe("Page number (optional). Starts from 1. Used for pagination."),
1011
+ sort: z3.string().regex(
1012
+ /^-?[a-zA-Z0-9_.]+$/,
1013
+ 'Sort must be a field name, optionally prefixed with "-" for descending'
1014
+ ).optional().describe(
572
1015
  'Sort field (optional). Use "fieldName" for ascending or "-fieldName" for descending. Example: "createdAt" or "-createdAt"'
573
1016
  )
574
1017
  };
@@ -622,11 +1065,11 @@ async function queryCollection({
622
1065
  }
623
1066
 
624
1067
  // src/tools/get-collection-by-id.ts
625
- import { z as z2 } from "zod";
626
- import { COLLECTIONS as COLLECTIONS2 } from "@01.software/sdk";
1068
+ import { z as z4 } from "zod";
1069
+ import { SERVER_COLLECTIONS as SERVER_COLLECTIONS2 } from "@01.software/sdk";
627
1070
  var schema2 = {
628
- collection: z2.enum(COLLECTIONS2).describe("Collection name (required)"),
629
- id: z2.string().min(1).describe("Item ID (required)")
1071
+ collection: z4.enum(SERVER_COLLECTIONS2).describe("Collection name (required)"),
1072
+ id: z4.string().min(1).describe("Item ID (required)")
630
1073
  };
631
1074
  var metadata2 = {
632
1075
  name: "get-collection-by-id",
@@ -652,9 +1095,9 @@ async function getCollectionById({
652
1095
  }
653
1096
 
654
1097
  // src/tools/get-order.ts
655
- import { z as z3 } from "zod";
1098
+ import { z as z5 } from "zod";
656
1099
  var schema3 = {
657
- orderNumber: z3.string().min(1).describe("Order number to look up (required)")
1100
+ orderNumber: z5.string().min(1).describe("Order number to look up (required)")
658
1101
  };
659
1102
  var metadata3 = {
660
1103
  name: "get-order",
@@ -684,24 +1127,24 @@ async function getOrder({
684
1127
  }
685
1128
 
686
1129
  // src/tools/create-order.ts
687
- import { z as z4 } from "zod";
1130
+ import { z as z6 } from "zod";
688
1131
  var schema4 = {
689
- pgPaymentId: z4.string().optional().describe("PG payment ID (optional \u2014 omit for free orders)"),
690
- orderNumber: z4.string().min(1).describe("Unique order number (required)"),
691
- customerSnapshot: z4.object({
692
- name: z4.string().optional().describe("Customer name"),
693
- email: z4.string().describe("Customer email (required)"),
694
- phone: z4.string().optional().describe("Customer phone")
1132
+ pgPaymentId: z6.string().optional().describe("PG payment ID (optional \u2014 omit for free orders)"),
1133
+ orderNumber: z6.string().min(1).describe("Unique order number (required)"),
1134
+ customerSnapshot: z6.object({
1135
+ name: z6.string().optional().describe("Customer name"),
1136
+ email: z6.string().describe("Customer email (required)"),
1137
+ phone: z6.string().optional().describe("Customer phone")
695
1138
  }).describe("Customer snapshot at time of order (required)"),
696
- shippingAddress: z4.record(z4.string(), z4.unknown()).describe(
1139
+ shippingAddress: z6.record(z6.string(), z6.unknown()).describe(
697
1140
  "Shipping address object (required). Fields: postalCode, address1, address2, deliveryMessage, recipientName, phone"
698
1141
  ),
699
- orderItems: z4.array(z4.record(z4.string(), z4.unknown())).describe(
1142
+ orderItems: z6.array(z6.record(z6.string(), z6.unknown())).describe(
700
1143
  "Array of order item objects (required). Each: { product, variant, option, quantity, unitPrice?, totalPrice? }"
701
1144
  ),
702
- totalAmount: z4.number().nonnegative().describe("Total order amount (required, min 0)"),
703
- shippingAmount: z4.number().nonnegative().optional().describe("Shipping amount (optional, default 0)"),
704
- discountCode: z4.string().optional().describe("Discount code to apply (optional)")
1145
+ totalAmount: z6.number().nonnegative().describe("Total order amount (required, min 0)"),
1146
+ shippingAmount: z6.number().nonnegative().optional().describe("Shipping amount (optional, default 0)"),
1147
+ discountCode: z6.string().optional().describe("Discount code to apply (optional)")
705
1148
  };
706
1149
  var metadata4 = {
707
1150
  name: "create-order",
@@ -726,10 +1169,10 @@ async function createOrder(params) {
726
1169
  }
727
1170
 
728
1171
  // src/tools/update-order.ts
729
- import { z as z5 } from "zod";
1172
+ import { z as z7 } from "zod";
730
1173
  var schema5 = {
731
- orderNumber: z5.string().min(1).describe("Order number (required)"),
732
- status: z5.enum([
1174
+ orderNumber: z7.string().min(1).describe("Order number (required)"),
1175
+ status: z7.enum([
733
1176
  "pending",
734
1177
  "paid",
735
1178
  "failed",
@@ -766,15 +1209,15 @@ async function updateOrder({
766
1209
  }
767
1210
 
768
1211
  // src/tools/checkout.ts
769
- import { z as z6 } from "zod";
1212
+ import { z as z8 } from "zod";
770
1213
  var schema6 = {
771
- cartId: z6.string().min(1).describe("Cart ID to convert to order (required)"),
772
- pgPaymentId: z6.string().optional().describe("PG payment ID (optional \u2014 omit for free orders)"),
773
- orderNumber: z6.string().min(1).describe("Unique order number (required)"),
774
- customerSnapshot: z6.record(z6.string(), z6.unknown()).describe(
1214
+ cartId: z8.string().min(1).describe("Cart ID to convert to order (required)"),
1215
+ pgPaymentId: z8.string().optional().describe("PG payment ID (optional \u2014 omit for free orders)"),
1216
+ orderNumber: z8.string().min(1).describe("Unique order number (required)"),
1217
+ customerSnapshot: z8.record(z8.string(), z8.unknown()).describe(
775
1218
  "Customer snapshot object (required). Fields: { name?, email, phone? }"
776
1219
  ),
777
- discountCode: z6.string().optional().describe("Discount code to apply (optional)")
1220
+ discountCode: z8.string().optional().describe("Discount code to apply (optional)")
778
1221
  };
779
1222
  var metadata6 = {
780
1223
  name: "checkout",
@@ -799,17 +1242,17 @@ async function checkout(params) {
799
1242
  }
800
1243
 
801
1244
  // src/tools/create-fulfillment.ts
802
- import { z as z7 } from "zod";
1245
+ import { z as z9 } from "zod";
803
1246
  var schema7 = {
804
- orderNumber: z7.string().min(1).describe("Order number (required)"),
805
- carrier: z7.string().optional().describe("Shipping carrier name (optional)"),
806
- trackingNumber: z7.string().optional().describe(
1247
+ orderNumber: z9.string().min(1).describe("Order number (required)"),
1248
+ carrier: z9.string().optional().describe("Shipping carrier name (optional)"),
1249
+ trackingNumber: z9.string().optional().describe(
807
1250
  'Tracking number (optional). Setting carrier + tracking triggers "shipped" status'
808
1251
  ),
809
- items: z7.array(
810
- z7.object({
811
- orderItem: z7.string().min(1).describe("Order item ID"),
812
- quantity: z7.number().int().positive().describe("Quantity to fulfill")
1252
+ items: z9.array(
1253
+ z9.object({
1254
+ orderItem: z9.string().min(1).describe("Order item ID"),
1255
+ quantity: z9.number().int().positive().describe("Quantity to fulfill")
813
1256
  })
814
1257
  ).describe("Array of items to fulfill (required)")
815
1258
  };
@@ -844,16 +1287,16 @@ async function createFulfillment({
844
1287
  }
845
1288
 
846
1289
  // src/tools/update-fulfillment.ts
847
- import { z as z8 } from "zod";
1290
+ import { z as z10 } from "zod";
848
1291
  var schema8 = {
849
- fulfillmentId: z8.string().min(1).describe("Fulfillment ID (required)"),
850
- status: z8.enum(["packed", "shipped", "delivered", "failed"]).describe(
1292
+ fulfillmentId: z10.string().min(1).describe("Fulfillment ID (required)"),
1293
+ status: z10.enum(["packed", "shipped", "delivered", "failed"]).describe(
851
1294
  "New fulfillment status (required). FSM: pending\u2192packed/shipped/failed, packed\u2192shipped/failed, shipped\u2192delivered/failed"
852
1295
  ),
853
- carrier: z8.string().optional().describe(
1296
+ carrier: z10.string().optional().describe(
854
1297
  "Shipping carrier (optional, changeable only in pending/packed status)"
855
1298
  ),
856
- trackingNumber: z8.string().optional().describe(
1299
+ trackingNumber: z10.string().optional().describe(
857
1300
  "Tracking number (optional, changeable only in pending/packed status)"
858
1301
  )
859
1302
  };
@@ -887,131 +1330,6 @@ async function updateFulfillment({
887
1330
  }
888
1331
  }
889
1332
 
890
- // ../../packages/contracts/src/tenant/index.ts
891
- import { z as z9 } from "zod";
892
- var tenantFieldConfigStateSchema = z9.object({
893
- hiddenFields: z9.array(z9.string()),
894
- isHidden: z9.boolean()
895
- }).strict();
896
- var tenantContextQuerySchema = z9.object({
897
- counts: z9.literal("true").optional()
898
- }).strict();
899
- var tenantContextToolInputSchema = z9.object({
900
- includeCounts: z9.boolean().optional().default(false).describe(
901
- "Include per-collection document counts and config status (bypasses cache, slower)"
902
- )
903
- }).strict();
904
- var tenantContextResponseSchema = z9.object({
905
- tenant: z9.object({
906
- id: z9.string(),
907
- name: z9.string(),
908
- plan: z9.string(),
909
- planSource: z9.string().optional(),
910
- authoritative: z9.boolean().optional(),
911
- capabilityVersion: z9.string().optional(),
912
- isDevMode: z9.boolean()
913
- }).strict(),
914
- features: z9.array(z9.string()),
915
- collections: z9.object({
916
- active: z9.array(z9.string()),
917
- inactive: z9.array(z9.string())
918
- }).strict(),
919
- fieldConfigs: z9.record(z9.string(), tenantFieldConfigStateSchema),
920
- counts: z9.record(z9.string(), z9.number()).optional(),
921
- config: z9.object({
922
- webhookConfigured: z9.boolean()
923
- }).strict().optional()
924
- }).strict();
925
- var COLLECTION_SCHEMA_CONTRACT_VERSION = 1;
926
- var collectionSchemaEndpointParamsSchema = z9.object({
927
- collectionSlug: z9.string().min(1, "collectionSlug is required")
928
- }).strict();
929
- function createCollectionSchemaToolInputSchema(collections) {
930
- return z9.object({
931
- collection: z9.enum(collections).describe("Collection name (required)")
932
- }).strict();
933
- }
934
- var collectionFieldOptionSchema = z9.object({
935
- label: z9.string(),
936
- value: z9.string()
937
- }).strict();
938
- var collectionFieldSchema = z9.lazy(
939
- () => z9.object({
940
- name: z9.string(),
941
- path: z9.string(),
942
- type: z9.string(),
943
- required: z9.literal(true).optional(),
944
- unique: z9.literal(true).optional(),
945
- hasMany: z9.literal(true).optional(),
946
- relationTo: z9.union([z9.string(), z9.array(z9.string())]).optional(),
947
- options: z9.array(collectionFieldOptionSchema).optional(),
948
- hidden: z9.literal(true).optional(),
949
- systemManaged: z9.literal(true).optional(),
950
- writable: z9.boolean().optional(),
951
- fields: z9.array(collectionFieldSchema).optional()
952
- }).strict()
953
- );
954
- var collectionSchemaResponseSchema = z9.object({
955
- contractVersion: z9.literal(COLLECTION_SCHEMA_CONTRACT_VERSION),
956
- mode: z9.literal("effective"),
957
- collection: z9.object({
958
- slug: z9.string(),
959
- timestamps: z9.boolean(),
960
- alwaysActive: z9.boolean(),
961
- feature: z9.string().nullable(),
962
- systemFields: z9.array(z9.string()),
963
- visibility: z9.object({
964
- collectionHidden: z9.boolean(),
965
- hiddenFields: z9.array(z9.string())
966
- }).strict(),
967
- fields: z9.array(collectionFieldSchema)
968
- }).strict()
969
- }).strict();
970
-
971
- // ../../packages/contracts/src/ecommerce/index.ts
972
- import { z as z10 } from "zod";
973
- var transactionStatusSchema = z10.enum([
974
- "pending",
975
- "paid",
976
- "failed",
977
- "canceled"
978
- ]);
979
- var updateTransactionSchema = z10.object({
980
- pgPaymentId: z10.string().min(1, "pgPaymentId is required").describe("PG payment ID (required)"),
981
- status: transactionStatusSchema.describe(
982
- "New transaction status (required)"
983
- ),
984
- paymentMethod: z10.string().optional().describe("Payment method (optional)"),
985
- receiptUrl: z10.string().optional().describe("Receipt URL (optional)"),
986
- paymentKey: z10.string().min(1).optional().describe("Provider payment key for verified paid confirmation"),
987
- amount: z10.number().int().positive().optional().describe("Provider-confirmed amount for verified paid confirmation")
988
- }).strict();
989
- var UpdateTransactionSchema = updateTransactionSchema;
990
- var returnReasonSchema = z10.enum([
991
- "change_of_mind",
992
- "defective",
993
- "wrong_delivery",
994
- "damaged",
995
- "other"
996
- ]);
997
- var restockActionSchema = z10.enum(["return_to_stock", "discard"]);
998
- var returnWithRefundItemSchema = z10.object({
999
- orderItem: z10.union([z10.string(), z10.number()]).transform(String),
1000
- quantity: z10.number().int().positive("quantity must be a positive integer"),
1001
- restockAction: restockActionSchema.default("return_to_stock")
1002
- }).strict();
1003
- var returnWithRefundSchema = z10.object({
1004
- orderNumber: z10.string().min(1, "orderNumber is required").describe("Order number (required)"),
1005
- reason: returnReasonSchema.optional().describe("Return reason (optional)"),
1006
- reasonDetail: z10.string().optional().describe("Detailed reason text (optional)"),
1007
- 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)"),
1008
- refundAmount: z10.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
1009
- pgPaymentId: z10.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
1010
- paymentKey: z10.string().min(1).optional().describe("Provider payment key for verified refund"),
1011
- refundReceiptUrl: z10.string().optional().describe("Refund receipt URL (optional)")
1012
- }).strict();
1013
- var ReturnWithRefundSchema = returnWithRefundSchema;
1014
-
1015
1333
  // src/tools/update-transaction.ts
1016
1334
  var schema9 = UpdateTransactionSchema.shape;
1017
1335
  var metadata9 = {
@@ -1449,97 +1767,7 @@ async function stockCheck({
1449
1767
  }
1450
1768
 
1451
1769
  // src/tools/get-collection-schema.ts
1452
- import { COLLECTIONS as COLLECTIONS3 } from "@01.software/sdk";
1453
-
1454
- // src/lib/console-api.ts
1455
- import { createHash } from "crypto";
1456
- var BASE_URL = process.env.SOFTWARE_API_URL || "http://localhost:3000";
1457
- var TIMEOUT_MS = 5e3;
1458
- var MISSING_HTTP_AUTH_CONTEXT_ERROR2 = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
1459
- function resolveAuthHeaderContext() {
1460
- const oauthContext = tenantAuthContext();
1461
- if (oauthContext) {
1462
- return {
1463
- apiKey: signMcpServiceToken(oauthContext),
1464
- mode: "oauth"
1465
- };
1466
- }
1467
- if (hasRequestContext()) throw new Error(MISSING_HTTP_AUTH_CONTEXT_ERROR2);
1468
- return {
1469
- apiKey: process.env.SOFTWARE_SECRET_KEY,
1470
- mode: "stdio",
1471
- publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY ?? process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY
1472
- };
1473
- }
1474
- function resolveApiKey() {
1475
- const { apiKey } = resolveAuthHeaderContext();
1476
- if (!apiKey || typeof apiKey !== "string") {
1477
- throw new Error(
1478
- "Authentication required. Set SOFTWARE_SECRET_KEY for stdio transport."
1479
- );
1480
- }
1481
- return apiKey;
1482
- }
1483
- function buildAuthHeaders(apiKey) {
1484
- const { mode, publishableKey } = resolveAuthHeaderContext();
1485
- const headers = {
1486
- Authorization: `Bearer ${apiKey}`
1487
- };
1488
- if (mode === "stdio" && publishableKey) {
1489
- headers["X-Publishable-Key"] = publishableKey;
1490
- }
1491
- return headers;
1492
- }
1493
- function extractErrorMessage(body) {
1494
- if (!body || typeof body !== "object") return void 0;
1495
- const b = body;
1496
- if (typeof b.error === "string") return b.error;
1497
- if (Array.isArray(b.errors) && b.errors[0]?.message) {
1498
- return String(b.errors[0].message);
1499
- }
1500
- if (typeof b.message === "string") return b.message;
1501
- return void 0;
1502
- }
1503
- async function consoleGet(path, apiKey) {
1504
- const authHeaders = buildAuthHeaders(apiKey);
1505
- const controller = new AbortController();
1506
- const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
1507
- try {
1508
- const res = await fetch(`${BASE_URL}${path}`, {
1509
- headers: authHeaders,
1510
- signal: controller.signal
1511
- });
1512
- if (!res.ok) {
1513
- const body = await res.json().catch(() => ({}));
1514
- const msg = extractErrorMessage(body);
1515
- throw new Error(msg || `Console GET ${path} failed: ${res.status}`);
1516
- }
1517
- return res.json();
1518
- } finally {
1519
- clearTimeout(timeoutId);
1520
- }
1521
- }
1522
- async function consolePost(path, body, apiKey) {
1523
- const authHeaders = buildAuthHeaders(apiKey);
1524
- const controller = new AbortController();
1525
- const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
1526
- try {
1527
- const res = await fetch(`${BASE_URL}${path}`, {
1528
- method: "POST",
1529
- headers: { ...authHeaders, "Content-Type": "application/json" },
1530
- body: JSON.stringify(body),
1531
- signal: controller.signal
1532
- });
1533
- if (!res.ok) {
1534
- const errBody = await res.json().catch(() => ({}));
1535
- const msg = extractErrorMessage(errBody);
1536
- throw new Error(msg || `Console POST ${path} failed: ${res.status}`);
1537
- }
1538
- return res.json();
1539
- } finally {
1540
- clearTimeout(timeoutId);
1541
- }
1542
- }
1770
+ import { SERVER_COLLECTIONS as SERVER_COLLECTIONS3 } from "@01.software/sdk";
1543
1771
 
1544
1772
  // src/lib/collection-schema.ts
1545
1773
  async function getCollectionSchema(collection) {
@@ -1552,7 +1780,7 @@ async function getCollectionSchema(collection) {
1552
1780
  }
1553
1781
 
1554
1782
  // src/tools/get-collection-schema.ts
1555
- var schema22 = createCollectionSchemaToolInputSchema(COLLECTIONS3).shape;
1783
+ var schema22 = createCollectionSchemaToolInputSchema(SERVER_COLLECTIONS3).shape;
1556
1784
  var metadata22 = {
1557
1785
  name: "get-collection-schema",
1558
1786
  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.",
@@ -2272,7 +2500,7 @@ var docIndex = [
2272
2500
  {
2273
2501
  title: "Browser Client vs Server Client",
2274
2502
  keywords: ["browser", "server", "publishable key", "secret key", "createClient", "createServerClient", "NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY", "SOFTWARE_PUBLISHABLE_KEY", "SOFTWARE_SECRET_KEY", "pk01_", "sk01_", "pat01_", "read-only", "full crud"],
2275
- summary: "createClient() for browser with publishableKey (read-only pk01_), createServerClient() for server with SOFTWARE_SECRET_KEY (usually sk01_, sometimes pat01_ in browser-auth-scoped flows). Never expose SECRET_KEY to the browser.",
2503
+ summary: "createClient() for browser with publishableKey (read-only pk01_), createServerClient() for server with matching SOFTWARE_PUBLISHABLE_KEY + SOFTWARE_SECRET_KEY (usually sk01_, sometimes pat01_ in scoped flows). Never expose SECRET_KEY to the browser.",
2276
2504
  resourceUri: "docs://sdk/getting-started"
2277
2505
  },
2278
2506
  // Query Builder
@@ -2638,9 +2866,9 @@ function handler4({
2638
2866
 
2639
2867
  // src/tools/sdk-get-collection-pattern.ts
2640
2868
  import { z as z27 } from "zod";
2641
- import { COLLECTIONS as COLLECTIONS4 } from "@01.software/sdk";
2869
+ import { COLLECTIONS, SERVER_COLLECTIONS as SERVER_COLLECTIONS4 } from "@01.software/sdk";
2642
2870
  var schema29 = {
2643
- collection: z27.enum(COLLECTIONS4).describe("Collection name"),
2871
+ collection: z27.enum(SERVER_COLLECTIONS4).describe("Collection name"),
2644
2872
  operation: z27.enum(["read", "write", "full-crud"]).default("read").describe("What operations are needed"),
2645
2873
  surface: z27.enum(["query-builder", "react-query", "server-api"]).default("query-builder").describe("Preferred API surface")
2646
2874
  };
@@ -2655,7 +2883,15 @@ var metadata29 = {
2655
2883
  }
2656
2884
  };
2657
2885
  function generatePattern(collection, operation, surface) {
2886
+ const isPublicCollection = COLLECTIONS.includes(
2887
+ collection
2888
+ );
2658
2889
  if (surface === "react-query") {
2890
+ if (!isPublicCollection) {
2891
+ throw new Error(
2892
+ `${collection} is server-only. Use surface="server-api" with createServerClient().`
2893
+ );
2894
+ }
2659
2895
  const parts2 = [];
2660
2896
  if (operation === "read") {
2661
2897
  parts2.push(
@@ -2767,17 +3003,29 @@ function generatePattern(collection, operation, surface) {
2767
3003
  }
2768
3004
  const parts = [];
2769
3005
  if (operation === "read" || operation === "full-crud") {
2770
- parts.push(
2771
- `// Read with any client (Client or ServerClient)`,
2772
- `const result = await client.collections.from('${collection}').find({`,
2773
- ` where: { status: { equals: 'published' } },`,
2774
- ` limit: 10,`,
2775
- ` sort: '-createdAt'`,
2776
- `})`,
2777
- ``,
2778
- `const item = await client.collections.from('${collection}').findById(id)`,
2779
- `const { totalDocs } = await client.collections.from('${collection}').count()`
2780
- );
3006
+ if (isPublicCollection) {
3007
+ parts.push(
3008
+ `// Read with any client (Client or ServerClient)`,
3009
+ `const result = await client.collections.from('${collection}').find({`,
3010
+ ` where: { status: { equals: 'published' } },`,
3011
+ ` limit: 10,`,
3012
+ ` sort: '-createdAt'`,
3013
+ `})`,
3014
+ ``,
3015
+ `const item = await client.collections.from('${collection}').findById(id)`,
3016
+ `const { totalDocs } = await client.collections.from('${collection}').count()`
3017
+ );
3018
+ } else {
3019
+ parts.push(
3020
+ `// Server-only collection: use ServerClient`,
3021
+ `const result = await serverClient.collections.from('${collection}').find({`,
3022
+ ` limit: 10,`,
3023
+ ` sort: '-createdAt'`,
3024
+ `})`,
3025
+ ``,
3026
+ `const item = await serverClient.collections.from('${collection}').findById(id)`
3027
+ );
3028
+ }
2781
3029
  }
2782
3030
  if (operation === "write" || operation === "full-crud") {
2783
3031
  parts.push(
@@ -2792,6 +3040,7 @@ function generatePattern(collection, operation, surface) {
2792
3040
  code: parts.join("\n"),
2793
3041
  notes: [
2794
3042
  "Query Builder works with both Client and ServerClient for reads",
3043
+ !isPublicCollection ? "This collection is server-only" : "",
2795
3044
  operation !== "read" ? "Write operations require ServerClient" : ""
2796
3045
  ].filter(Boolean)
2797
3046
  };
@@ -2969,9 +3218,9 @@ You can perform the "${goal}" task by following the patterns above.`;
2969
3218
 
2970
3219
  // src/prompts/collection-query-help.ts
2971
3220
  import { z as z29 } from "zod";
2972
- import { COLLECTIONS as COLLECTIONS5 } from "@01.software/sdk";
3221
+ import { COLLECTIONS as COLLECTIONS2, SERVER_COLLECTIONS as SERVER_COLLECTIONS5 } from "@01.software/sdk";
2973
3222
  var schema31 = {
2974
- collection: z29.enum(COLLECTIONS5).describe("Collection name"),
3223
+ collection: z29.enum(SERVER_COLLECTIONS5).describe("Collection name"),
2975
3224
  operation: z29.enum(["find", "create", "update", "delete"]).describe("Operation to perform (find, create, update, delete)"),
2976
3225
  filters: z29.string().optional().describe("Filter conditions (JSON string, optional)")
2977
3226
  };
@@ -2992,6 +3241,11 @@ Filter conditions:
2992
3241
  \`\`\`json
2993
3242
  ${filters}
2994
3243
  \`\`\`` : "";
3244
+ const isPublicCollection = COLLECTIONS2.includes(
3245
+ collection
3246
+ );
3247
+ const readClientName = isPublicCollection ? "client" : "serverClient";
3248
+ const readClientNote = isPublicCollection ? "Client or ServerClient" : "ServerClient only";
2995
3249
  return `How to perform "${operation}" operation on "${collection}" collection:${filterExample}
2996
3250
 
2997
3251
  ## Collection: ${collection}
@@ -3002,26 +3256,26 @@ ${filters}
3002
3256
  \`\`\`typescript
3003
3257
  import { createClient, createServerClient } from '@01.software/sdk'
3004
3258
 
3005
- // Client (read-only)
3259
+ // Client (read-only public collections)
3006
3260
  const client = createClient({
3007
3261
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!
3008
3262
  })
3009
3263
 
3010
- // Server client (full CRUD)
3264
+ // Server client (server/public collections, full CRUD)
3011
3265
  const serverClient = createServerClient({
3012
3266
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
3013
3267
  secretKey: process.env.SOFTWARE_SECRET_KEY!
3014
3268
  })
3015
3269
 
3016
- ${operation === "find" ? `// Query ${collection} collection with Query Builder
3270
+ ${operation === "find" ? `// Query ${collection} collection with Query Builder (${readClientNote})
3017
3271
  // find() returns PayloadFindResponse with docs array and pagination
3018
- const result = await client.collections.from('${collection}').find(${filters ? `{
3272
+ const result = await ${readClientName}.collections.from('${collection}').find(${filters ? `{
3019
3273
  where: ${filters}
3020
3274
  }` : ""})
3021
3275
  // result.docs - array of items
3022
3276
  // result.totalDocs, result.page, result.totalPages, result.hasNextPage, ...
3023
3277
 
3024
- // Using React Hook
3278
+ ${isPublicCollection ? `// Using React Hook
3025
3279
  const { data, isLoading, error } = client.query.useQuery({
3026
3280
  collection: '${collection}',
3027
3281
  options: { limit: 10 }
@@ -3031,19 +3285,19 @@ const { data, isLoading, error } = client.query.useQuery({
3031
3285
  const { data } = client.query.useSuspenseQuery({
3032
3286
  collection: '${collection}',
3033
3287
  options: { limit: 10 }
3034
- })` : operation === "create" ? `// Create ${collection} item (ServerClient only)
3288
+ })` : `// React hooks are browser/public only and do not support '${collection}'.`}` : operation === "create" ? `// Create ${collection} item (ServerClient only)
3035
3289
  // create() returns PayloadMutationResponse with doc and message
3036
- const result = await serverClient.from('${collection}').create({
3290
+ const result = await serverClient.collections.from('${collection}').create({
3037
3291
  // Enter fields
3038
3292
  })
3039
3293
  // result.doc - created item, result.message` : operation === "update" ? `// Update ${collection} item (ServerClient only)
3040
3294
  // update() returns PayloadMutationResponse with doc and message
3041
- const result = await serverClient.from('${collection}').update(id, {
3295
+ const result = await serverClient.collections.from('${collection}').update(id, {
3042
3296
  // Fields to update
3043
3297
  })
3044
3298
  // result.doc - updated item, result.message` : `// Delete ${collection} item (ServerClient only)
3045
3299
  // remove() returns the deleted document directly
3046
- await serverClient.from('${collection}').remove(id)`}
3300
+ await serverClient.collections.from('${collection}').remove(id)`}
3047
3301
  \`\`\`
3048
3302
 
3049
3303
  ### Useful Tips
@@ -3051,7 +3305,7 @@ await serverClient.from('${collection}').remove(id)`}
3051
3305
  ${operation === "find" ? `- Use \`where\` option for filtering (Payload query syntax)
3052
3306
  - Use \`limit\` and \`page\` for pagination
3053
3307
  - Use \`sort\` for sorting (prefix with "-" for descending)
3054
- - React Query hooks: useQuery, useSuspenseQuery, useInfiniteQuery
3308
+ - ${isPublicCollection ? "React Query hooks: useQuery, useSuspenseQuery, useInfiniteQuery" : "React Query hooks are browser/public only; use ServerClient for this collection"}
3055
3309
  - Cache utilities: invalidateQueries, prefetchQuery, getQueryData, setQueryData` : operation === "create" ? `- Requires ServerClient with secretKey
3056
3310
  - Check required fields
3057
3311
  - TypeScript recommended for type safety` : operation === "update" ? `- Requires ServerClient with secretKey
@@ -3287,7 +3541,7 @@ var FEATURES = {
3287
3541
  ### Required Collections (count > 0)
3288
3542
 
3289
3543
  1. **products** \u2014 Create via Console UI or SDK \`client.collections.from('products').create({ ... })\`
3290
- - Minimum fields: \`{ title, slug, status: 'published', _status: 'published' }\`
3544
+ - Minimum fields: \`{ title, slug, status: 'published' }\`
3291
3545
 
3292
3546
  2. **product-variants** \u2014 At least 1 sellable variant per product
3293
3547
  - Minimum fields: \`{ product, title, price, stock }\`
@@ -3320,7 +3574,8 @@ customer-addresses
3320
3574
 
3321
3575
  ### Optional Collections
3322
3576
 
3323
- customer-groups \u2014 Create via Console UI or SDK \`client.collections.from('customer-groups').create({ title })\`
3577
+ - customer-groups \u2014 Console/server-scoped segmentation for VIP coupons and campaigns. Use \`createServerClient().collections.from('customer-groups')\`.
3578
+ - customer-profile-lists \u2014 Public profile display/ranking lists for storefronts. Browser reads may use \`client.collections.from('customer-profile-lists')\`.
3324
3579
 
3325
3580
  ### Config
3326
3581
 
@@ -3331,10 +3586,10 @@ customer-groups \u2014 Create via Console UI or SDK \`client.collections.from('c
3331
3586
  ### Required Collections (count > 0)
3332
3587
 
3333
3588
  1. **articles** \u2014 At least 1 article
3334
- - Minimum fields: \`{ title, slug }\`
3589
+ - Minimum fields: \`{ title, slug, status: 'published' }\`
3335
3590
 
3336
3591
  2. **article-authors** \u2014 At least 1 author
3337
- - Minimum fields: \`{ title, slug }\`
3592
+ - Minimum fields: \`{ title, slug, status: 'published' }\`
3338
3593
  - Link authors to articles via the \`authors\` relationship field
3339
3594
 
3340
3595
  ### Optional Collections
@@ -3349,7 +3604,7 @@ article-categories, article-tags`,
3349
3604
 
3350
3605
  2. **document-types** \u2014 At least 1 type
3351
3606
  - Minimum fields: \`{ title, slug }\`
3352
- - Link document type via \`documentType\` relationship field
3607
+ - Link document type via \`type\` relationship field
3353
3608
 
3354
3609
  ### Optional Collections
3355
3610
 
@@ -3359,10 +3614,10 @@ document-categories`,
3359
3614
  ### Required Collections (count > 0)
3360
3615
 
3361
3616
  1. **playlists** \u2014 At least 1 playlist
3362
- - Minimum fields: \`{ title, slug, status: 'published', _status: 'published' }\`
3617
+ - Minimum fields: \`{ title, slug, status: 'published' }\`
3363
3618
 
3364
3619
  2. **tracks** \u2014 At least 1 track
3365
- - Minimum fields: \`{ title, sourceUrl, status: 'published', _status: 'published' }\`
3620
+ - Minimum fields: \`{ title, sourceUrl, status: 'published' }\`
3366
3621
 
3367
3622
  3. **playlists.tracks** \u2014 Link at least 1 track from a playlist
3368
3623
  - Minimum fields: \`{ tracks: [trackId] }\`
@@ -3375,11 +3630,11 @@ playlist-categories, playlist-tags, track-categories, track-tags, track-assets`,
3375
3630
  ### Required Collections (count > 0)
3376
3631
 
3377
3632
  1. **galleries** \u2014 At least 1 gallery
3378
- - Minimum fields: \`{ title, slug, status: 'published', _status: 'published' }\`
3633
+ - Minimum fields: \`{ title, slug, status: 'published' }\`
3379
3634
 
3380
3635
  2. **gallery-items** \u2014 At least 1 item per gallery
3381
3636
  - References \`images\` collection (non-upload)
3382
- - Minimum fields: \`{ gallery, image, _status: 'published' }\`
3637
+ - Minimum fields: \`{ gallery, image, status: 'published' }\`
3383
3638
 
3384
3639
  ### Optional Collections
3385
3640
 
@@ -3389,7 +3644,7 @@ gallery-categories, gallery-tags`,
3389
3644
  ### Required Collections (count > 0)
3390
3645
 
3391
3646
  1. **links** \u2014 At least 1 link
3392
- - Minimum fields: \`{ title, slug, url, status: 'published', _status: 'published' }\`
3647
+ - Minimum fields: \`{ title, slug, url, status: 'published' }\`
3393
3648
 
3394
3649
  ### Optional Collections
3395
3650
 
@@ -3461,9 +3716,11 @@ comments, reactions, bookmarks, reports, community-bans
3461
3716
 
3462
3717
  ### Optional Collections
3463
3718
 
3464
- post-categories`
3719
+ post-categories, customer-profile-lists`
3465
3720
  };
3466
- function featureSetupGuide({ feature }) {
3721
+ function featureSetupGuide({
3722
+ feature
3723
+ }) {
3467
3724
  return `# Feature Setup Guide: ${feature}
3468
3725
 
3469
3726
  ${FEATURES[feature] || "Unknown feature."}
@@ -3486,7 +3743,8 @@ function handler6() {
3486
3743
  ## Server Info
3487
3744
  - **Name**: 01.software MCP Server
3488
3745
  - **Version**: 0.1.0
3489
- - **Transport**: HTTP (Streamable)
3746
+ - **Hosted transport**: HTTP (Streamable)
3747
+ - **Local transport**: stdio through \`npx @01.software/cli mcp\`
3490
3748
 
3491
3749
  ## Authentication
3492
3750
 
@@ -3497,42 +3755,9 @@ HTTP MCP uses OAuth discovery and Authorization Code + PKCE.
3497
3755
  url = "https://mcp.01.software/mcp"
3498
3756
  \`\`\`
3499
3757
 
3500
- ## Available Tools (29)
3501
-
3502
- > 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.
3503
-
3504
- ### Generic Read (2)
3505
- - \`query-collection\` - Query collection with filters, pagination, sorting
3506
- - \`get-collection-by-id\` - Get single item by ID
3507
-
3508
- ### Orders (7)
3509
- - \`create-order\` - Create a new order with products and shipping
3510
- - \`get-order\` - Get order details by order number
3511
- - \`update-order\` - Update order status
3512
- - \`checkout\` - Convert cart to order
3513
- - \`create-fulfillment\` - Create fulfillment for order items
3514
- - \`update-fulfillment\` - Update fulfillment status, carrier, and tracking
3515
- - \`update-transaction\` - Update transaction status
3516
-
3517
- ### Returns (3)
3518
- - \`create-return\` - Create a return request
3519
- - \`update-return\` - Update return status
3520
- - \`return-with-refund\` - Process return with refund atomically
3521
-
3522
- ### Cart (6)
3523
- - \`add-cart-item\` - Add item to cart
3524
- - \`update-cart-item\` - Update cart item quantity
3525
- - \`remove-cart-item\` - Remove item from cart
3526
- - \`apply-discount\` - Apply discount code to cart
3527
- - \`remove-discount\` - Remove discount from cart
3528
- - \`clear-cart\` - Remove all items from cart
3758
+ ## Hosted HTTP OAuth Tools (8)
3529
3759
 
3530
- ### Validation (2)
3531
- - \`validate-discount\` - Validate discount code
3532
- - \`calculate-shipping\` - Calculate shipping fee
3533
-
3534
- ### Product (1)
3535
- - \`stock-check\` - Check product option stock availability
3760
+ The hosted HTTP MCP endpoint at https://mcp.01.software/mcp exposes only these OAuth-safe tools:
3536
3761
 
3537
3762
  ### Schema (1)
3538
3763
  - \`get-collection-schema\` - Get authoritative tenant-aware collection schema
@@ -3550,6 +3775,16 @@ url = "https://mcp.01.software/mcp"
3550
3775
  - \`sdk-get-auth-setup\` - Get framework-specific auth setup guidance
3551
3776
  - \`sdk-get-collection-pattern\` - Get collection-specific usage patterns
3552
3777
 
3778
+ ## Local CLI Stdio Surface (29)
3779
+
3780
+ For trusted local server-key workflows, start the stdio server:
3781
+
3782
+ \`\`\`bash
3783
+ npx @01.software/cli mcp
3784
+ \`\`\`
3785
+
3786
+ Local stdio can expose generic read, order, return, cart, validation, stock, schema, tenant context, field config, and guidance tools. Generic collection write tools (create/update/delete/update-many/delete-many) are intentionally absent on every transport; use the SDK server client for generic writes.
3787
+
3553
3788
  ## Rate Limits
3554
3789
 
3555
3790
  Rate limits depend on your tenant plan:
@@ -3561,7 +3796,7 @@ Rate limits depend on your tenant plan:
3561
3796
  }
3562
3797
 
3563
3798
  // src/resources/(collections)/schema.ts
3564
- import { COLLECTIONS as COLLECTIONS6 } from "@01.software/sdk";
3799
+ import { COLLECTIONS as COLLECTIONS3 } from "@01.software/sdk";
3565
3800
  var metadata35 = {
3566
3801
  name: "collections-schema",
3567
3802
  title: "Collection Schema Info",
@@ -3590,13 +3825,18 @@ var COLLECTIONS_BY_CATEGORY = {
3590
3825
  Customers: [
3591
3826
  "customers",
3592
3827
  "customer-profiles",
3593
- "customer-addresses",
3594
- "customer-groups"
3828
+ "customer-profile-lists",
3829
+ "customer-addresses"
3595
3830
  ],
3596
3831
  Carts: ["carts", "cart-items"],
3597
3832
  "Discounts & Promotions": ["discounts", "promotions"],
3598
3833
  Documents: ["documents", "document-categories", "document-types"],
3599
- Articles: ["articles", "article-authors", "article-categories", "article-tags"],
3834
+ Articles: [
3835
+ "articles",
3836
+ "article-authors",
3837
+ "article-categories",
3838
+ "article-tags"
3839
+ ],
3600
3840
  Community: [
3601
3841
  "posts",
3602
3842
  "comments",
@@ -3615,7 +3855,12 @@ var COLLECTIONS_BY_CATEGORY = {
3615
3855
  "track-categories",
3616
3856
  "track-tags"
3617
3857
  ],
3618
- Galleries: ["galleries", "gallery-items", "gallery-categories", "gallery-tags"],
3858
+ Galleries: [
3859
+ "galleries",
3860
+ "gallery-items",
3861
+ "gallery-categories",
3862
+ "gallery-tags"
3863
+ ],
3619
3864
  Links: ["links", "link-categories", "link-tags"],
3620
3865
  Canvas: [
3621
3866
  "canvases",
@@ -3640,7 +3885,7 @@ var COLLECTIONS_BY_CATEGORY = {
3640
3885
  };
3641
3886
  function handler7() {
3642
3887
  const categoryDocs = Object.entries(COLLECTIONS_BY_CATEGORY).map(([category, collections]) => {
3643
- const collectionList = collections.filter((c) => COLLECTIONS6.includes(c)).map((c) => `- **${c}**`).join("\n");
3888
+ const collectionList = collections.filter((c) => COLLECTIONS3.includes(c)).map((c) => `- **${c}**`).join("\n");
3644
3889
  return `## ${category}
3645
3890
  ${collectionList}`;
3646
3891
  }).join("\n\n");
@@ -3661,8 +3906,9 @@ Each collection supports the following operations:
3661
3906
  - \`updateMany(where, data)\` - Bulk update items matching filter
3662
3907
  - \`removeMany(where)\` - Bulk delete items matching filter
3663
3908
 
3664
- Draft-enabled public collections expose only \`_status: 'published'\` rows to
3665
- publishable-key reads unless server-side access explicitly includes drafts.
3909
+ Status-managed public collections expose only \`status: 'published'\` rows to
3910
+ publishable-key reads unless server-side access explicitly includes
3911
+ unpublished statuses.
3666
3912
 
3667
3913
  ## Query Examples
3668
3914
 
@@ -3685,7 +3931,7 @@ publishable-key reads unless server-side access explicitly includes drafts.
3685
3931
  }
3686
3932
  \`\`\`
3687
3933
 
3688
- Total available collections: ${COLLECTIONS6.length}`;
3934
+ Total available collections: ${COLLECTIONS3.length}`;
3689
3935
  }
3690
3936
 
3691
3937
  // src/resources/(docs)/getting-started.ts
@@ -5573,8 +5819,25 @@ function registerTool(server, schema34, meta, handler18) {
5573
5819
  };
5574
5820
  }
5575
5821
  }
5576
- const result = await handler18(params);
5577
- return { content: [{ type: "text", text: result }] };
5822
+ const activeSummary = currentMcpTelemetrySummary();
5823
+ const ownSummary = activeSummary || hasRequestContext() ? null : createMcpTelemetrySummary("stdio");
5824
+ const summary = activeSummary ?? ownSummary;
5825
+ let result = null;
5826
+ try {
5827
+ result = await handler18(params);
5828
+ return { content: [{ type: "text", text: result }] };
5829
+ } finally {
5830
+ if (summary) {
5831
+ recordMcpToolResult({
5832
+ resultText: result,
5833
+ summary,
5834
+ toolName: meta.name
5835
+ });
5836
+ }
5837
+ if (ownSummary) {
5838
+ void flushMcpTelemetrySummary(ownSummary);
5839
+ }
5840
+ }
5578
5841
  }
5579
5842
  );
5580
5843
  }
@@ -5676,6 +5939,9 @@ export {
5676
5939
  MCP_SCOPES,
5677
5940
  requestContext,
5678
5941
  mcpServicePublicJwks,
5942
+ createMcpTelemetrySummary,
5943
+ runWithMcpTelemetry,
5944
+ flushMcpTelemetrySummary,
5679
5945
  createServer
5680
5946
  };
5681
- //# sourceMappingURL=chunk-GJOQ4SE2.js.map
5947
+ //# sourceMappingURL=chunk-CADO6WG6.js.map