@hartl-services/medusa-contracts 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/email/schemas.d.ts +27 -4
- package/dist/email/schemas.js +9 -0
- package/dist/esm/email/schemas.mjs +9 -0
- package/dist/esm/order-assistant/paths.mjs +26 -0
- package/dist/esm/order-assistant/schemas.mjs +203 -1
- package/dist/order-assistant/paths.d.ts +26 -0
- package/dist/order-assistant/paths.js +26 -0
- package/dist/order-assistant/schemas.d.ts +178 -0
- package/dist/order-assistant/schemas.js +204 -1
- package/dist/order-assistant/types.d.ts +17 -1
- package/dist/plugin-features/schemas.d.ts +36 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,7 +77,12 @@ The order assistant (`order-assistant`) is not a JSON REST route: `paths` holds
|
|
|
77
77
|
the MCP endpoint (`/mcp`), `schemas` the input and output schemas of its four
|
|
78
78
|
tools, the tool names and the order metadata keys the assistant writes, and
|
|
79
79
|
`types` their inferred types. A storefront never calls it; the consumer is an
|
|
80
|
-
MCP client (an AI chatbot) and the base's own tests.
|
|
80
|
+
MCP client (an AI chatbot) and the base's own tests. Since 1.7.0 the same
|
|
81
|
+
subpaths also hold the assistant's OAuth sign-in: `paths.oauth` (discovery,
|
|
82
|
+
registration, authorization, token), `paths.admin` and `paths.adminPages`
|
|
83
|
+
(consent page and connected chatbots in the Admin), the request and response
|
|
84
|
+
schemas of those endpoints, `ORDER_ASSISTANT_FEATURES` and
|
|
85
|
+
`ORDER_ASSISTANT_OAUTH` (scope and lifetimes).
|
|
81
86
|
|
|
82
87
|
File Gateway responses are binary and have no JSON response DTO. Public paths
|
|
83
88
|
come from `paths/files`. Private gateway URLs are opaque, signed server results
|
package/dist/email/schemas.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const EmailKindSchema: z.ZodEnum<{
|
|
|
15
15
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
16
16
|
"user.invite": "user.invite";
|
|
17
17
|
"system.credential_expiry.admin": "system.credential_expiry.admin";
|
|
18
|
+
"order.picked_up": "order.picked_up";
|
|
18
19
|
}>;
|
|
19
20
|
export declare const EmailMessageStatusSchema: z.ZodEnum<{
|
|
20
21
|
unknown: "unknown";
|
|
@@ -61,7 +62,7 @@ export declare const EMAIL_ATTACHMENT_MAX_PDF_BYTES: number;
|
|
|
61
62
|
* Customer mails an Admin can attach library documents (terms, legal notices,
|
|
62
63
|
* flyers) to. Account, Shop and affiliate mails never carry them.
|
|
63
64
|
*/
|
|
64
|
-
export declare const EMAIL_ATTACHABLE_KINDS: readonly ["order.received", "order.shipped", "order.canceled", "order.return_requested", "order.return_received", "order.refunded"];
|
|
65
|
+
export declare const EMAIL_ATTACHABLE_KINDS: readonly ["order.received", "order.shipped", "order.picked_up", "order.canceled", "order.return_requested", "order.return_received", "order.refunded"];
|
|
65
66
|
export declare const EmailAttachableKindSchema: z.ZodEnum<{
|
|
66
67
|
"order.received": "order.received";
|
|
67
68
|
"order.shipped": "order.shipped";
|
|
@@ -69,6 +70,7 @@ export declare const EmailAttachableKindSchema: z.ZodEnum<{
|
|
|
69
70
|
"order.return_requested": "order.return_requested";
|
|
70
71
|
"order.return_received": "order.return_received";
|
|
71
72
|
"order.refunded": "order.refunded";
|
|
73
|
+
"order.picked_up": "order.picked_up";
|
|
72
74
|
}>;
|
|
73
75
|
export declare const ListAdminEmailMessagesQuerySchema: z.ZodObject<{
|
|
74
76
|
kind: z.ZodOptional<z.ZodEnum<{
|
|
@@ -87,6 +89,7 @@ export declare const ListAdminEmailMessagesQuerySchema: z.ZodObject<{
|
|
|
87
89
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
88
90
|
"user.invite": "user.invite";
|
|
89
91
|
"system.credential_expiry.admin": "system.credential_expiry.admin";
|
|
92
|
+
"order.picked_up": "order.picked_up";
|
|
90
93
|
}>>;
|
|
91
94
|
status: z.ZodOptional<z.ZodEnum<{
|
|
92
95
|
unknown: "unknown";
|
|
@@ -117,7 +120,8 @@ export declare const CreateAdminEmailAttachmentDocumentSchema: z.ZodObject<{
|
|
|
117
120
|
"order.return_requested": "order.return_requested";
|
|
118
121
|
"order.return_received": "order.return_received";
|
|
119
122
|
"order.refunded": "order.refunded";
|
|
120
|
-
|
|
123
|
+
"order.picked_up": "order.picked_up";
|
|
124
|
+
}>>, z.ZodTransform<("order.received" | "order.shipped" | "order.canceled" | "order.return_requested" | "order.return_received" | "order.refunded" | "order.picked_up")[], ("order.received" | "order.shipped" | "order.canceled" | "order.return_requested" | "order.return_received" | "order.refunded" | "order.picked_up")[]>>;
|
|
121
125
|
}, z.core.$strict>;
|
|
122
126
|
export declare const UpdateAdminEmailAttachmentDocumentSchema: z.ZodObject<{
|
|
123
127
|
name: z.ZodString;
|
|
@@ -128,7 +132,8 @@ export declare const UpdateAdminEmailAttachmentDocumentSchema: z.ZodObject<{
|
|
|
128
132
|
"order.return_requested": "order.return_requested";
|
|
129
133
|
"order.return_received": "order.return_received";
|
|
130
134
|
"order.refunded": "order.refunded";
|
|
131
|
-
|
|
135
|
+
"order.picked_up": "order.picked_up";
|
|
136
|
+
}>>, z.ZodTransform<("order.received" | "order.shipped" | "order.canceled" | "order.return_requested" | "order.return_received" | "order.refunded" | "order.picked_up")[], ("order.received" | "order.shipped" | "order.canceled" | "order.return_requested" | "order.return_received" | "order.refunded" | "order.picked_up")[]>>>;
|
|
132
137
|
}, z.core.$strict>;
|
|
133
138
|
export declare const CreateAdminEmailAttachmentVersionSchema: z.ZodObject<{
|
|
134
139
|
request_id: z.ZodString;
|
|
@@ -154,6 +159,7 @@ export declare const AdminEmailMessageSchema: z.ZodObject<{
|
|
|
154
159
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
155
160
|
"user.invite": "user.invite";
|
|
156
161
|
"system.credential_expiry.admin": "system.credential_expiry.admin";
|
|
162
|
+
"order.picked_up": "order.picked_up";
|
|
157
163
|
}>;
|
|
158
164
|
status: z.ZodEnum<{
|
|
159
165
|
unknown: "unknown";
|
|
@@ -258,6 +264,7 @@ export declare const AdminEmailAttachmentDocumentSchema: z.ZodObject<{
|
|
|
258
264
|
"order.return_requested": "order.return_requested";
|
|
259
265
|
"order.return_received": "order.return_received";
|
|
260
266
|
"order.refunded": "order.refunded";
|
|
267
|
+
"order.picked_up": "order.picked_up";
|
|
261
268
|
}>>;
|
|
262
269
|
versions: z.ZodArray<z.ZodObject<{
|
|
263
270
|
id: z.ZodString;
|
|
@@ -289,6 +296,7 @@ export declare const ListAdminEmailMessagesResponseSchema: z.ZodObject<{
|
|
|
289
296
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
290
297
|
"user.invite": "user.invite";
|
|
291
298
|
"system.credential_expiry.admin": "system.credential_expiry.admin";
|
|
299
|
+
"order.picked_up": "order.picked_up";
|
|
292
300
|
}>;
|
|
293
301
|
status: z.ZodEnum<{
|
|
294
302
|
unknown: "unknown";
|
|
@@ -360,6 +368,7 @@ export declare const GetAdminEmailMessageResponseSchema: z.ZodObject<{
|
|
|
360
368
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
361
369
|
"user.invite": "user.invite";
|
|
362
370
|
"system.credential_expiry.admin": "system.credential_expiry.admin";
|
|
371
|
+
"order.picked_up": "order.picked_up";
|
|
363
372
|
}>;
|
|
364
373
|
status: z.ZodEnum<{
|
|
365
374
|
unknown: "unknown";
|
|
@@ -466,6 +475,7 @@ export declare const ResendAdminEmailMessageResponseSchema: z.ZodObject<{
|
|
|
466
475
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
467
476
|
"user.invite": "user.invite";
|
|
468
477
|
"system.credential_expiry.admin": "system.credential_expiry.admin";
|
|
478
|
+
"order.picked_up": "order.picked_up";
|
|
469
479
|
}>;
|
|
470
480
|
status: z.ZodEnum<{
|
|
471
481
|
unknown: "unknown";
|
|
@@ -526,6 +536,7 @@ export declare const ListAdminEmailAttachmentDocumentsResponseSchema: z.ZodObjec
|
|
|
526
536
|
"order.return_requested": "order.return_requested";
|
|
527
537
|
"order.return_received": "order.return_received";
|
|
528
538
|
"order.refunded": "order.refunded";
|
|
539
|
+
"order.picked_up": "order.picked_up";
|
|
529
540
|
}>>;
|
|
530
541
|
versions: z.ZodArray<z.ZodObject<{
|
|
531
542
|
id: z.ZodString;
|
|
@@ -551,6 +562,7 @@ export declare const AdminEmailAttachmentDocumentResponseSchema: z.ZodObject<{
|
|
|
551
562
|
"order.return_requested": "order.return_requested";
|
|
552
563
|
"order.return_received": "order.return_received";
|
|
553
564
|
"order.refunded": "order.refunded";
|
|
565
|
+
"order.picked_up": "order.picked_up";
|
|
554
566
|
}>>;
|
|
555
567
|
versions: z.ZodArray<z.ZodObject<{
|
|
556
568
|
id: z.ZodString;
|
|
@@ -590,7 +602,7 @@ export declare const AdminDraftOrderConfirmationResponseSchema: z.ZodObject<{
|
|
|
590
602
|
* Mails whose content an Admin edits as a block template. Every other kind
|
|
591
603
|
* keeps its code template.
|
|
592
604
|
*/
|
|
593
|
-
export declare const EMAIL_EDITABLE_KINDS: readonly ["order.received", "order.received.admin", "order.shipped", "order.canceled", "order.return_requested", "order.return_received", "order.refunded", "auth.password_reset", "auth.admin_password_reset", "auth.verification", "auth.login_code", "user.invite", "affiliate.consignment_report_due", "affiliate.application_submitted.admin"];
|
|
605
|
+
export declare const EMAIL_EDITABLE_KINDS: readonly ["order.received", "order.received.admin", "order.shipped", "order.picked_up", "order.canceled", "order.return_requested", "order.return_received", "order.refunded", "auth.password_reset", "auth.admin_password_reset", "auth.verification", "auth.login_code", "user.invite", "affiliate.consignment_report_due", "affiliate.application_submitted.admin"];
|
|
594
606
|
export declare const EmailEditableKindSchema: z.ZodEnum<{
|
|
595
607
|
"order.received": "order.received";
|
|
596
608
|
"order.received.admin": "order.received.admin";
|
|
@@ -606,6 +618,7 @@ export declare const EmailEditableKindSchema: z.ZodEnum<{
|
|
|
606
618
|
"affiliate.consignment_report_due": "affiliate.consignment_report_due";
|
|
607
619
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
608
620
|
"user.invite": "user.invite";
|
|
621
|
+
"order.picked_up": "order.picked_up";
|
|
609
622
|
}>;
|
|
610
623
|
/** Server-rendered blocks with the mail's real data. */
|
|
611
624
|
export declare const EmailTemplateDynamicBlockSchema: z.ZodEnum<{
|
|
@@ -658,6 +671,12 @@ export declare const EMAIL_TEMPLATE_CATALOG: {
|
|
|
658
671
|
readonly placeholders: readonly ["shop_name", "order_number", "customer_name"];
|
|
659
672
|
readonly required_blocks: readonly [];
|
|
660
673
|
};
|
|
674
|
+
readonly "order.picked_up": {
|
|
675
|
+
readonly blocks: readonly ["details", "item_list"];
|
|
676
|
+
readonly conditions: readonly ["invoice_attached"];
|
|
677
|
+
readonly placeholders: readonly ["shop_name", "order_number", "customer_name"];
|
|
678
|
+
readonly required_blocks: readonly [];
|
|
679
|
+
};
|
|
661
680
|
readonly "order.canceled": {
|
|
662
681
|
readonly blocks: readonly ["details", "order_summary"];
|
|
663
682
|
readonly conditions: readonly ["cancellation_attached"];
|
|
@@ -1798,6 +1817,7 @@ export declare const AdminEmailTemplateSummarySchema: z.ZodObject<{
|
|
|
1798
1817
|
"affiliate.consignment_report_due": "affiliate.consignment_report_due";
|
|
1799
1818
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
1800
1819
|
"user.invite": "user.invite";
|
|
1820
|
+
"order.picked_up": "order.picked_up";
|
|
1801
1821
|
}>;
|
|
1802
1822
|
customized: z.ZodBoolean;
|
|
1803
1823
|
code_override: z.ZodBoolean;
|
|
@@ -1820,6 +1840,7 @@ export declare const ListAdminEmailTemplatesResponseSchema: z.ZodObject<{
|
|
|
1820
1840
|
"affiliate.consignment_report_due": "affiliate.consignment_report_due";
|
|
1821
1841
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
1822
1842
|
"user.invite": "user.invite";
|
|
1843
|
+
"order.picked_up": "order.picked_up";
|
|
1823
1844
|
}>;
|
|
1824
1845
|
customized: z.ZodBoolean;
|
|
1825
1846
|
code_override: z.ZodBoolean;
|
|
@@ -1843,6 +1864,7 @@ export declare const AdminEmailTemplateResponseSchema: z.ZodObject<{
|
|
|
1843
1864
|
"affiliate.consignment_report_due": "affiliate.consignment_report_due";
|
|
1844
1865
|
"affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
|
|
1845
1866
|
"user.invite": "user.invite";
|
|
1867
|
+
"order.picked_up": "order.picked_up";
|
|
1846
1868
|
}>;
|
|
1847
1869
|
customized: z.ZodBoolean;
|
|
1848
1870
|
code_override: z.ZodBoolean;
|
|
@@ -2400,6 +2422,7 @@ export declare const SetAdminEmailAttachmentMailKindSchema: z.ZodObject<{
|
|
|
2400
2422
|
"order.return_requested": "order.return_requested";
|
|
2401
2423
|
"order.return_received": "order.return_received";
|
|
2402
2424
|
"order.refunded": "order.refunded";
|
|
2425
|
+
"order.picked_up": "order.picked_up";
|
|
2403
2426
|
}>;
|
|
2404
2427
|
attached: z.ZodBoolean;
|
|
2405
2428
|
}, z.core.$strict>;
|
package/dist/email/schemas.js
CHANGED
|
@@ -19,6 +19,7 @@ exports.EmailKindSchema = zod_1.z.enum([
|
|
|
19
19
|
"affiliate.application_submitted.admin",
|
|
20
20
|
"user.invite",
|
|
21
21
|
"system.credential_expiry.admin",
|
|
22
|
+
"order.picked_up",
|
|
22
23
|
]);
|
|
23
24
|
exports.EmailMessageStatusSchema = zod_1.z.enum([
|
|
24
25
|
"pending",
|
|
@@ -73,6 +74,7 @@ const NullableWireDateSchema = primitives_1.WireDateSchema.nullable();
|
|
|
73
74
|
exports.EMAIL_ATTACHABLE_KINDS = [
|
|
74
75
|
"order.received",
|
|
75
76
|
"order.shipped",
|
|
77
|
+
"order.picked_up",
|
|
76
78
|
"order.canceled",
|
|
77
79
|
"order.return_requested",
|
|
78
80
|
"order.return_received",
|
|
@@ -257,6 +259,7 @@ exports.EMAIL_EDITABLE_KINDS = [
|
|
|
257
259
|
"order.received",
|
|
258
260
|
"order.received.admin",
|
|
259
261
|
"order.shipped",
|
|
262
|
+
"order.picked_up",
|
|
260
263
|
"order.canceled",
|
|
261
264
|
"order.return_requested",
|
|
262
265
|
"order.return_received",
|
|
@@ -326,6 +329,12 @@ exports.EMAIL_TEMPLATE_CATALOG = {
|
|
|
326
329
|
placeholders: ["shop_name", "order_number", "customer_name"],
|
|
327
330
|
required_blocks: [],
|
|
328
331
|
},
|
|
332
|
+
"order.picked_up": {
|
|
333
|
+
blocks: ["details", "item_list"],
|
|
334
|
+
conditions: ["invoice_attached"],
|
|
335
|
+
placeholders: ["shop_name", "order_number", "customer_name"],
|
|
336
|
+
required_blocks: [],
|
|
337
|
+
},
|
|
329
338
|
"order.canceled": {
|
|
330
339
|
blocks: ["details", "order_summary"],
|
|
331
340
|
conditions: ["cancellation_attached"],
|
|
@@ -16,6 +16,7 @@ export const EmailKindSchema = z.enum([
|
|
|
16
16
|
"affiliate.application_submitted.admin",
|
|
17
17
|
"user.invite",
|
|
18
18
|
"system.credential_expiry.admin",
|
|
19
|
+
"order.picked_up",
|
|
19
20
|
]);
|
|
20
21
|
export const EmailMessageStatusSchema = z.enum([
|
|
21
22
|
"pending",
|
|
@@ -70,6 +71,7 @@ const NullableWireDateSchema = WireDateSchema.nullable();
|
|
|
70
71
|
export const EMAIL_ATTACHABLE_KINDS = [
|
|
71
72
|
"order.received",
|
|
72
73
|
"order.shipped",
|
|
74
|
+
"order.picked_up",
|
|
73
75
|
"order.canceled",
|
|
74
76
|
"order.return_requested",
|
|
75
77
|
"order.return_received",
|
|
@@ -254,6 +256,7 @@ export const EMAIL_EDITABLE_KINDS = [
|
|
|
254
256
|
"order.received",
|
|
255
257
|
"order.received.admin",
|
|
256
258
|
"order.shipped",
|
|
259
|
+
"order.picked_up",
|
|
257
260
|
"order.canceled",
|
|
258
261
|
"order.return_requested",
|
|
259
262
|
"order.return_received",
|
|
@@ -323,6 +326,12 @@ export const EMAIL_TEMPLATE_CATALOG = {
|
|
|
323
326
|
placeholders: ["shop_name", "order_number", "customer_name"],
|
|
324
327
|
required_blocks: [],
|
|
325
328
|
},
|
|
329
|
+
"order.picked_up": {
|
|
330
|
+
blocks: ["details", "item_list"],
|
|
331
|
+
conditions: ["invoice_attached"],
|
|
332
|
+
placeholders: ["shop_name", "order_number", "customer_name"],
|
|
333
|
+
required_blocks: [],
|
|
334
|
+
},
|
|
326
335
|
"order.canceled": {
|
|
327
336
|
blocks: ["details", "order_summary"],
|
|
328
337
|
conditions: ["cancellation_attached"],
|
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The shop's MCP endpoint for the order assistant (Streamable HTTP,
|
|
3
3
|
* stateless). Outside `/admin` and `/store`: it authenticates on its own.
|
|
4
|
+
*
|
|
5
|
+
* `oauth`: the OAuth 2.1 endpoints an MCP client (claude.ai, ChatGPT) uses
|
|
6
|
+
* to sign an Admin user in; the well-known paths are fixed by RFC 9728 and
|
|
7
|
+
* RFC 8414. `admin`: the Admin API behind the consent page and the list of
|
|
8
|
+
* connected chatbots. `adminPages`: Admin pages, relative to the Admin root.
|
|
4
9
|
*/
|
|
5
10
|
export const orderAssistantPaths = {
|
|
6
11
|
mcp: "/mcp",
|
|
12
|
+
oauth: {
|
|
13
|
+
protectedResourceMetadata: "/.well-known/oauth-protected-resource",
|
|
14
|
+
/** Same document; clients probe the path of the MCP endpoint first. */
|
|
15
|
+
protectedResourceMetadataForMcp: "/.well-known/oauth-protected-resource/mcp",
|
|
16
|
+
authorizationServerMetadata: "/.well-known/oauth-authorization-server",
|
|
17
|
+
register: "/mcp/oauth/register",
|
|
18
|
+
authorize: "/mcp/oauth/authorize",
|
|
19
|
+
token: "/mcp/oauth/token",
|
|
20
|
+
},
|
|
21
|
+
admin: {
|
|
22
|
+
authorization: (authorizationId) => `/admin/order-assistant/authorizations/${encodeURIComponent(authorizationId)}`,
|
|
23
|
+
connections: "/admin/order-assistant/connections",
|
|
24
|
+
connection: (connectionId) => `/admin/order-assistant/connections/${encodeURIComponent(connectionId)}`,
|
|
25
|
+
},
|
|
26
|
+
adminPages: {
|
|
27
|
+
/**
|
|
28
|
+
* The request id is a path segment on purpose: the Admin login returns
|
|
29
|
+
* to the path it came from and drops the query string.
|
|
30
|
+
*/
|
|
31
|
+
authorize: (authorizationId) => `/order-assistant/authorize/${encodeURIComponent(authorizationId)}`,
|
|
32
|
+
},
|
|
7
33
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "@medusajs/framework/zod";
|
|
2
|
-
import { NonEmptyStringSchema } from "../shared/primitives.mjs";
|
|
2
|
+
import { NonEmptyStringSchema, WireDateSchema } from "../shared/primitives.mjs";
|
|
3
3
|
/** Plugin key and container key of the order assistant in the base. */
|
|
4
4
|
export const ORDER_ASSISTANT_PLUGIN_KEY = "order_assistant";
|
|
5
5
|
/** Tool names as the MCP client sees them. */
|
|
@@ -181,3 +181,205 @@ export const CreateDraftOrderOutputSchema = z.object({
|
|
|
181
181
|
/** Path of the draft in the Admin, relative to the backend URL. */
|
|
182
182
|
admin_path: z.string(),
|
|
183
183
|
});
|
|
184
|
+
// --- Anmeldung per OAuth (MCP authorization, OAuth 2.1) --------------------
|
|
185
|
+
/** Feature keys of the order assistant's plugin toggles. */
|
|
186
|
+
export const ORDER_ASSISTANT_FEATURES = {
|
|
187
|
+
oauth: "oauth",
|
|
188
|
+
};
|
|
189
|
+
export const ORDER_ASSISTANT_OAUTH = {
|
|
190
|
+
/** The one scope: use the assistant's tools as the signed-in Admin user. */
|
|
191
|
+
scope: "order_assistant",
|
|
192
|
+
accessTokenTtlSeconds: 3600,
|
|
193
|
+
/** A connection ends when its refresh token stays unused this long. */
|
|
194
|
+
connectionIdleDays: 30,
|
|
195
|
+
/** How long the consent page may stay open. */
|
|
196
|
+
authorizationRequestTtlSeconds: 600,
|
|
197
|
+
/** How long the client has to redeem the code after the consent. */
|
|
198
|
+
authorizationCodeTtlSeconds: 120,
|
|
199
|
+
maxRedirectUris: 10,
|
|
200
|
+
maxUriLength: 2000,
|
|
201
|
+
maxClientNameLength: 120,
|
|
202
|
+
maxStateLength: 2000,
|
|
203
|
+
};
|
|
204
|
+
const OAUTH_LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
|
|
205
|
+
/** `http://localhost`, `http://127.0.0.1` or `http://[::1]`, any port. */
|
|
206
|
+
export const isOAuthLoopbackRedirectUri = (value) => {
|
|
207
|
+
try {
|
|
208
|
+
const url = new URL(value);
|
|
209
|
+
return (url.protocol === "http:" && OAUTH_LOOPBACK_HOSTS.includes(url.hostname));
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* A redirect URI a client may register: https, or http on a loopback address
|
|
217
|
+
* (native clients such as Claude Code), without credentials or fragment.
|
|
218
|
+
*/
|
|
219
|
+
export const OAuthRedirectUriSchema = z
|
|
220
|
+
.string()
|
|
221
|
+
.max(ORDER_ASSISTANT_OAUTH.maxUriLength)
|
|
222
|
+
.refine((value) => {
|
|
223
|
+
let url;
|
|
224
|
+
try {
|
|
225
|
+
url = new URL(value);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
if (url.hash || url.username || url.password) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
return url.protocol === "https:" || isOAuthLoopbackRedirectUri(value);
|
|
234
|
+
}, {
|
|
235
|
+
message: "A redirect URI must use https, or http on a loopback address, and carry no fragment.",
|
|
236
|
+
});
|
|
237
|
+
/**
|
|
238
|
+
* Dynamic client registration request (RFC 7591). Unknown metadata is
|
|
239
|
+
* ignored; the requested authentication method, grant and response types are
|
|
240
|
+
* accepted as sent and answered with what the server actually supports.
|
|
241
|
+
*/
|
|
242
|
+
export const OAuthClientRegistrationRequestSchema = z.object({
|
|
243
|
+
redirect_uris: z
|
|
244
|
+
.array(OAuthRedirectUriSchema)
|
|
245
|
+
.min(1)
|
|
246
|
+
.max(ORDER_ASSISTANT_OAUTH.maxRedirectUris),
|
|
247
|
+
client_name: z
|
|
248
|
+
.string()
|
|
249
|
+
.trim()
|
|
250
|
+
.min(1)
|
|
251
|
+
.max(ORDER_ASSISTANT_OAUTH.maxClientNameLength)
|
|
252
|
+
.optional(),
|
|
253
|
+
});
|
|
254
|
+
export const OAuthClientRegistrationResponseSchema = z.object({
|
|
255
|
+
client_id: z.string(),
|
|
256
|
+
/** Seconds since the epoch. */
|
|
257
|
+
client_id_issued_at: z.number(),
|
|
258
|
+
client_name: z.string().optional(),
|
|
259
|
+
redirect_uris: z.array(z.string()),
|
|
260
|
+
/** Public clients only: PKCE instead of a client secret. */
|
|
261
|
+
token_endpoint_auth_method: z.literal("none"),
|
|
262
|
+
grant_types: z.array(z.string()),
|
|
263
|
+
response_types: z.array(z.string()),
|
|
264
|
+
scope: z.string(),
|
|
265
|
+
});
|
|
266
|
+
/** Query of the authorization endpoint, before the client is looked up. */
|
|
267
|
+
export const OAuthAuthorizeQuerySchema = z.object({
|
|
268
|
+
client_id: z.string().min(1).max(200),
|
|
269
|
+
redirect_uri: z.string().min(1).max(ORDER_ASSISTANT_OAUTH.maxUriLength),
|
|
270
|
+
response_type: z.string().max(100).optional(),
|
|
271
|
+
code_challenge: z.string().max(200).optional(),
|
|
272
|
+
code_challenge_method: z.string().max(20).optional(),
|
|
273
|
+
state: z.string().max(ORDER_ASSISTANT_OAUTH.maxStateLength).optional(),
|
|
274
|
+
scope: z.string().max(1000).optional(),
|
|
275
|
+
resource: z.string().max(ORDER_ASSISTANT_OAUTH.maxUriLength).optional(),
|
|
276
|
+
});
|
|
277
|
+
/** Body of the token endpoint (`application/x-www-form-urlencoded`). */
|
|
278
|
+
export const OAuthTokenRequestSchema = z.discriminatedUnion("grant_type", [
|
|
279
|
+
z.object({
|
|
280
|
+
grant_type: z.literal("authorization_code"),
|
|
281
|
+
code: z.string().min(1).max(200),
|
|
282
|
+
/** RFC 7636: 43 to 128 unreserved characters. */
|
|
283
|
+
code_verifier: z.string().regex(/^[A-Za-z0-9\-._~]{43,128}$/),
|
|
284
|
+
client_id: z.string().min(1).max(200),
|
|
285
|
+
redirect_uri: z.string().max(ORDER_ASSISTANT_OAUTH.maxUriLength).optional(),
|
|
286
|
+
resource: z.string().max(ORDER_ASSISTANT_OAUTH.maxUriLength).optional(),
|
|
287
|
+
}),
|
|
288
|
+
z.object({
|
|
289
|
+
grant_type: z.literal("refresh_token"),
|
|
290
|
+
refresh_token: z.string().min(1).max(200),
|
|
291
|
+
client_id: z.string().min(1).max(200),
|
|
292
|
+
resource: z.string().max(ORDER_ASSISTANT_OAUTH.maxUriLength).optional(),
|
|
293
|
+
}),
|
|
294
|
+
]);
|
|
295
|
+
export const OAuthTokenResponseSchema = z.object({
|
|
296
|
+
access_token: z.string(),
|
|
297
|
+
token_type: z.literal("Bearer"),
|
|
298
|
+
/** Seconds until the access token expires. */
|
|
299
|
+
expires_in: z.number(),
|
|
300
|
+
/** Replaces the refresh token that was sent; the old one stops working. */
|
|
301
|
+
refresh_token: z.string(),
|
|
302
|
+
scope: z.string(),
|
|
303
|
+
});
|
|
304
|
+
export const OAuthErrorCodeSchema = z.enum([
|
|
305
|
+
"invalid_request",
|
|
306
|
+
"invalid_client",
|
|
307
|
+
"invalid_grant",
|
|
308
|
+
"invalid_target",
|
|
309
|
+
"invalid_client_metadata",
|
|
310
|
+
"invalid_redirect_uri",
|
|
311
|
+
"unsupported_grant_type",
|
|
312
|
+
"unsupported_response_type",
|
|
313
|
+
"access_denied",
|
|
314
|
+
"temporarily_unavailable",
|
|
315
|
+
]);
|
|
316
|
+
/** Error body of the registration and token endpoints (RFC 6749, RFC 7591). */
|
|
317
|
+
export const OAuthErrorResponseSchema = z.object({
|
|
318
|
+
error: OAuthErrorCodeSchema,
|
|
319
|
+
error_description: z.string(),
|
|
320
|
+
});
|
|
321
|
+
/** RFC 9728: where the MCP endpoint's authorization server is. */
|
|
322
|
+
export const OAuthProtectedResourceMetadataSchema = z.object({
|
|
323
|
+
resource: z.string(),
|
|
324
|
+
authorization_servers: z.array(z.string()),
|
|
325
|
+
bearer_methods_supported: z.array(z.string()),
|
|
326
|
+
scopes_supported: z.array(z.string()),
|
|
327
|
+
resource_name: z.string(),
|
|
328
|
+
});
|
|
329
|
+
/** RFC 8414: the authorization server's endpoints and capabilities. */
|
|
330
|
+
export const OAuthAuthorizationServerMetadataSchema = z.object({
|
|
331
|
+
issuer: z.string(),
|
|
332
|
+
authorization_endpoint: z.string(),
|
|
333
|
+
token_endpoint: z.string(),
|
|
334
|
+
registration_endpoint: z.string(),
|
|
335
|
+
response_types_supported: z.array(z.string()),
|
|
336
|
+
grant_types_supported: z.array(z.string()),
|
|
337
|
+
code_challenge_methods_supported: z.array(z.string()),
|
|
338
|
+
token_endpoint_auth_methods_supported: z.array(z.string()),
|
|
339
|
+
scopes_supported: z.array(z.string()),
|
|
340
|
+
/** RFC 9207: every authorization response carries `iss`. */
|
|
341
|
+
authorization_response_iss_parameter_supported: z.boolean(),
|
|
342
|
+
});
|
|
343
|
+
/** What the consent page shows about a pending sign-in request. */
|
|
344
|
+
export const AdminOrderAssistantAuthorizationSchema = z.object({
|
|
345
|
+
id: z.string(),
|
|
346
|
+
/** Name the chatbot registered with; `null` when it gave none. */
|
|
347
|
+
client_name: z.string().nullable(),
|
|
348
|
+
/** Host the browser is sent back to after the decision. */
|
|
349
|
+
redirect_host: z.string(),
|
|
350
|
+
/** True when the chatbot runs on this computer (loopback redirect). */
|
|
351
|
+
redirect_is_loopback: z.boolean(),
|
|
352
|
+
/** True when the host belongs to a chatbot service the shop knows. */
|
|
353
|
+
redirect_is_known: z.boolean(),
|
|
354
|
+
expires_at: WireDateSchema,
|
|
355
|
+
});
|
|
356
|
+
export const AdminOrderAssistantAuthorizationResponseSchema = z.object({
|
|
357
|
+
authorization: AdminOrderAssistantAuthorizationSchema,
|
|
358
|
+
});
|
|
359
|
+
export const DecideOrderAssistantAuthorizationSchema = z
|
|
360
|
+
.object({ approve: z.boolean() })
|
|
361
|
+
.strict();
|
|
362
|
+
export const DecideOrderAssistantAuthorizationResponseSchema = z.object({
|
|
363
|
+
/** Where the browser must go next: the chatbot's redirect URI. */
|
|
364
|
+
redirect_to: z.string(),
|
|
365
|
+
});
|
|
366
|
+
/** One chatbot an Admin user connected through OAuth. */
|
|
367
|
+
export const AdminOrderAssistantConnectionSchema = z.object({
|
|
368
|
+
id: z.string(),
|
|
369
|
+
client_name: z.string().nullable(),
|
|
370
|
+
redirect_host: z.string(),
|
|
371
|
+
user_id: z.string(),
|
|
372
|
+
/** `null` when the Admin user no longer exists. */
|
|
373
|
+
user_email: z.string().nullable(),
|
|
374
|
+
created_at: WireDateSchema,
|
|
375
|
+
last_used_at: WireDateSchema.nullable(),
|
|
376
|
+
/** When the connection ends unless the chatbot uses it before. */
|
|
377
|
+
expires_at: WireDateSchema,
|
|
378
|
+
});
|
|
379
|
+
export const AdminOrderAssistantConnectionsResponseSchema = z.object({
|
|
380
|
+
connections: z.array(AdminOrderAssistantConnectionSchema),
|
|
381
|
+
});
|
|
382
|
+
export const DeleteOrderAssistantConnectionResponseSchema = z.object({
|
|
383
|
+
id: z.string(),
|
|
384
|
+
deleted: z.literal(true),
|
|
385
|
+
});
|
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The shop's MCP endpoint for the order assistant (Streamable HTTP,
|
|
3
3
|
* stateless). Outside `/admin` and `/store`: it authenticates on its own.
|
|
4
|
+
*
|
|
5
|
+
* `oauth`: the OAuth 2.1 endpoints an MCP client (claude.ai, ChatGPT) uses
|
|
6
|
+
* to sign an Admin user in; the well-known paths are fixed by RFC 9728 and
|
|
7
|
+
* RFC 8414. `admin`: the Admin API behind the consent page and the list of
|
|
8
|
+
* connected chatbots. `adminPages`: Admin pages, relative to the Admin root.
|
|
4
9
|
*/
|
|
5
10
|
export declare const orderAssistantPaths: {
|
|
6
11
|
readonly mcp: "/mcp";
|
|
12
|
+
readonly oauth: {
|
|
13
|
+
readonly protectedResourceMetadata: "/.well-known/oauth-protected-resource";
|
|
14
|
+
/** Same document; clients probe the path of the MCP endpoint first. */
|
|
15
|
+
readonly protectedResourceMetadataForMcp: "/.well-known/oauth-protected-resource/mcp";
|
|
16
|
+
readonly authorizationServerMetadata: "/.well-known/oauth-authorization-server";
|
|
17
|
+
readonly register: "/mcp/oauth/register";
|
|
18
|
+
readonly authorize: "/mcp/oauth/authorize";
|
|
19
|
+
readonly token: "/mcp/oauth/token";
|
|
20
|
+
};
|
|
21
|
+
readonly admin: {
|
|
22
|
+
readonly authorization: (authorizationId: string) => string;
|
|
23
|
+
readonly connections: "/admin/order-assistant/connections";
|
|
24
|
+
readonly connection: (connectionId: string) => string;
|
|
25
|
+
};
|
|
26
|
+
readonly adminPages: {
|
|
27
|
+
/**
|
|
28
|
+
* The request id is a path segment on purpose: the Admin login returns
|
|
29
|
+
* to the path it came from and drops the query string.
|
|
30
|
+
*/
|
|
31
|
+
readonly authorize: (authorizationId: string) => string;
|
|
32
|
+
};
|
|
7
33
|
};
|
|
@@ -4,7 +4,33 @@ exports.orderAssistantPaths = void 0;
|
|
|
4
4
|
/**
|
|
5
5
|
* The shop's MCP endpoint for the order assistant (Streamable HTTP,
|
|
6
6
|
* stateless). Outside `/admin` and `/store`: it authenticates on its own.
|
|
7
|
+
*
|
|
8
|
+
* `oauth`: the OAuth 2.1 endpoints an MCP client (claude.ai, ChatGPT) uses
|
|
9
|
+
* to sign an Admin user in; the well-known paths are fixed by RFC 9728 and
|
|
10
|
+
* RFC 8414. `admin`: the Admin API behind the consent page and the list of
|
|
11
|
+
* connected chatbots. `adminPages`: Admin pages, relative to the Admin root.
|
|
7
12
|
*/
|
|
8
13
|
exports.orderAssistantPaths = {
|
|
9
14
|
mcp: "/mcp",
|
|
15
|
+
oauth: {
|
|
16
|
+
protectedResourceMetadata: "/.well-known/oauth-protected-resource",
|
|
17
|
+
/** Same document; clients probe the path of the MCP endpoint first. */
|
|
18
|
+
protectedResourceMetadataForMcp: "/.well-known/oauth-protected-resource/mcp",
|
|
19
|
+
authorizationServerMetadata: "/.well-known/oauth-authorization-server",
|
|
20
|
+
register: "/mcp/oauth/register",
|
|
21
|
+
authorize: "/mcp/oauth/authorize",
|
|
22
|
+
token: "/mcp/oauth/token",
|
|
23
|
+
},
|
|
24
|
+
admin: {
|
|
25
|
+
authorization: (authorizationId) => `/admin/order-assistant/authorizations/${encodeURIComponent(authorizationId)}`,
|
|
26
|
+
connections: "/admin/order-assistant/connections",
|
|
27
|
+
connection: (connectionId) => `/admin/order-assistant/connections/${encodeURIComponent(connectionId)}`,
|
|
28
|
+
},
|
|
29
|
+
adminPages: {
|
|
30
|
+
/**
|
|
31
|
+
* The request id is a path segment on purpose: the Admin login returns
|
|
32
|
+
* to the path it came from and drops the query string.
|
|
33
|
+
*/
|
|
34
|
+
authorize: (authorizationId) => `/order-assistant/authorize/${encodeURIComponent(authorizationId)}`,
|
|
35
|
+
},
|
|
10
36
|
};
|