@hartl-services/medusa-contracts 1.5.0 → 1.7.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 CHANGED
@@ -28,6 +28,7 @@ Consumer subpaths:
28
28
  - `@hartl-services/medusa-contracts/types/plugin-features-store`
29
29
  - `@hartl-services/medusa-contracts/types/plugin-features-admin`
30
30
  - `@hartl-services/medusa-contracts/types/tax-compliance-store`
31
+ - `@hartl-services/medusa-contracts/types/order-assistant`
31
32
  - `@hartl-services/medusa-contracts/schemas/affiliate`
32
33
  - `@hartl-services/medusa-contracts/schemas/batch-management`
33
34
  - `@hartl-services/medusa-contracts/schemas/documents`
@@ -39,6 +40,7 @@ Consumer subpaths:
39
40
  - `@hartl-services/medusa-contracts/schemas/checkout`
40
41
  - `@hartl-services/medusa-contracts/schemas/plugin-features`
41
42
  - `@hartl-services/medusa-contracts/schemas/tax-compliance`
43
+ - `@hartl-services/medusa-contracts/schemas/order-assistant`
42
44
  - `@hartl-services/medusa-contracts/paths/affiliate`
43
45
  - `@hartl-services/medusa-contracts/paths/batch-management`
44
46
  - `@hartl-services/medusa-contracts/paths/documents`
@@ -49,6 +51,7 @@ Consumer subpaths:
49
51
  - `@hartl-services/medusa-contracts/paths/checkout`
50
52
  - `@hartl-services/medusa-contracts/paths/plugin-features`
51
53
  - `@hartl-services/medusa-contracts/paths/tax-compliance`
54
+ - `@hartl-services/medusa-contracts/paths/order-assistant`
52
55
 
53
56
  Server-only event subpaths (see "Server-only event contracts" below):
54
57
 
@@ -70,6 +73,17 @@ Packing-list DTOs are exported only from `types/documents-admin`. Use
70
73
  `packingListsPaths` from `paths/documents` with the consuming Admin's configured
71
74
  Medusa SDK; there is deliberately no packing-list Store contract or client.
72
75
 
76
+ The order assistant (`order-assistant`) is not a JSON REST route: `paths` holds
77
+ the MCP endpoint (`/mcp`), `schemas` the input and output schemas of its four
78
+ tools, the tool names and the order metadata keys the assistant writes, and
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. 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).
86
+
73
87
  File Gateway responses are binary and have no JSON response DTO. Public paths
74
88
  come from `paths/files`. Private gateway URLs are opaque, signed server results
75
89
  and must not be assembled by a consumer.
@@ -40,6 +40,7 @@ export declare const EmailSafeStatusCodeSchema: z.ZodEnum<{
40
40
  connection_failed: "connection_failed";
41
41
  invalid_content: "invalid_content";
42
42
  lease_expired: "lease_expired";
43
+ no_recipient: "no_recipient";
43
44
  not_configured: "not_configured";
44
45
  notifications_disabled: "notifications_disabled";
45
46
  outcome_unknown: "outcome_unknown";
@@ -179,6 +180,7 @@ export declare const AdminEmailMessageSchema: z.ZodObject<{
179
180
  connection_failed: "connection_failed";
180
181
  invalid_content: "invalid_content";
181
182
  lease_expired: "lease_expired";
183
+ no_recipient: "no_recipient";
182
184
  not_configured: "not_configured";
183
185
  notifications_disabled: "notifications_disabled";
184
186
  outcome_unknown: "outcome_unknown";
@@ -217,6 +219,7 @@ export declare const AdminEmailAttemptSchema: z.ZodObject<{
217
219
  connection_failed: "connection_failed";
218
220
  invalid_content: "invalid_content";
219
221
  lease_expired: "lease_expired";
222
+ no_recipient: "no_recipient";
220
223
  not_configured: "not_configured";
221
224
  notifications_disabled: "notifications_disabled";
222
225
  outcome_unknown: "outcome_unknown";
@@ -312,6 +315,7 @@ export declare const ListAdminEmailMessagesResponseSchema: z.ZodObject<{
312
315
  connection_failed: "connection_failed";
313
316
  invalid_content: "invalid_content";
314
317
  lease_expired: "lease_expired";
318
+ no_recipient: "no_recipient";
315
319
  not_configured: "not_configured";
316
320
  notifications_disabled: "notifications_disabled";
317
321
  outcome_unknown: "outcome_unknown";
@@ -382,6 +386,7 @@ export declare const GetAdminEmailMessageResponseSchema: z.ZodObject<{
382
386
  connection_failed: "connection_failed";
383
387
  invalid_content: "invalid_content";
384
388
  lease_expired: "lease_expired";
389
+ no_recipient: "no_recipient";
385
390
  not_configured: "not_configured";
386
391
  notifications_disabled: "notifications_disabled";
387
392
  outcome_unknown: "outcome_unknown";
@@ -420,6 +425,7 @@ export declare const GetAdminEmailMessageResponseSchema: z.ZodObject<{
420
425
  connection_failed: "connection_failed";
421
426
  invalid_content: "invalid_content";
422
427
  lease_expired: "lease_expired";
428
+ no_recipient: "no_recipient";
423
429
  not_configured: "not_configured";
424
430
  notifications_disabled: "notifications_disabled";
425
431
  outcome_unknown: "outcome_unknown";
@@ -486,6 +492,7 @@ export declare const ResendAdminEmailMessageResponseSchema: z.ZodObject<{
486
492
  connection_failed: "connection_failed";
487
493
  invalid_content: "invalid_content";
488
494
  lease_expired: "lease_expired";
495
+ no_recipient: "no_recipient";
489
496
  not_configured: "not_configured";
490
497
  notifications_disabled: "notifications_disabled";
491
498
  outcome_unknown: "outcome_unknown";
@@ -44,6 +44,7 @@ exports.EmailSafeStatusCodeSchema = zod_1.z.enum([
44
44
  "connection_failed",
45
45
  "invalid_content",
46
46
  "lease_expired",
47
+ "no_recipient",
47
48
  "not_configured",
48
49
  "notifications_disabled",
49
50
  "outcome_unknown",
@@ -41,6 +41,7 @@ export const EmailSafeStatusCodeSchema = z.enum([
41
41
  "connection_failed",
42
42
  "invalid_content",
43
43
  "lease_expired",
44
+ "no_recipient",
44
45
  "not_configured",
45
46
  "notifications_disabled",
46
47
  "outcome_unknown",
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The shop's MCP endpoint for the order assistant (Streamable HTTP,
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.
9
+ */
10
+ export const orderAssistantPaths = {
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
+ },
33
+ };
@@ -0,0 +1,385 @@
1
+ import { z } from "@medusajs/framework/zod";
2
+ import { NonEmptyStringSchema, WireDateSchema } from "../shared/primitives.mjs";
3
+ /** Plugin key and container key of the order assistant in the base. */
4
+ export const ORDER_ASSISTANT_PLUGIN_KEY = "order_assistant";
5
+ /** Tool names as the MCP client sees them. */
6
+ export const ORDER_ASSISTANT_TOOLS = {
7
+ searchCustomers: "kunden_suchen",
8
+ searchProducts: "produkte_suchen",
9
+ listShippingOptions: "versandarten_auflisten",
10
+ createDraftOrder: "bestellentwurf_anlegen",
11
+ };
12
+ /** How the order reached the shop; stored in the order metadata. */
13
+ export const OrderAssistantSourceSchema = z.enum(["fax", "telefon"]);
14
+ /** Order metadata keys the assistant writes. */
15
+ export const ORDER_ASSISTANT_METADATA = {
16
+ source: "order_source",
17
+ note: "order_note",
18
+ };
19
+ export const ORDER_ASSISTANT_LIMITS = {
20
+ customers: 10,
21
+ variants: 20,
22
+ items: 50,
23
+ noteLength: 2000,
24
+ };
25
+ const CountryCodeSchema = z
26
+ .string()
27
+ .trim()
28
+ .toLowerCase()
29
+ .regex(/^[a-z]{2}$/)
30
+ .describe("Land als ISO-3166-1-alpha-2-Code, z. B. at oder de.");
31
+ const OptionalTextSchema = z.string().trim().min(1).max(255).optional();
32
+ export const OrderAssistantAddressSchema = z
33
+ .object({
34
+ first_name: NonEmptyStringSchema.max(255),
35
+ last_name: NonEmptyStringSchema.max(255),
36
+ company: OptionalTextSchema.describe("Firma oder Praxis, falls angegeben."),
37
+ address_1: NonEmptyStringSchema.max(255).describe("Straße und Hausnummer."),
38
+ address_2: OptionalTextSchema.describe("Adresszusatz."),
39
+ postal_code: NonEmptyStringSchema.max(20),
40
+ city: NonEmptyStringSchema.max(255),
41
+ country_code: CountryCodeSchema,
42
+ phone: OptionalTextSchema,
43
+ })
44
+ .strict();
45
+ export const SearchCustomersInputSchema = z
46
+ .object({
47
+ query: OptionalTextSchema.describe("Name, Firma, Telefonnummer oder E-Mail-Adresse (auch Teile davon)."),
48
+ postal_code: OptionalTextSchema.describe("PLZ einer Adresse der Person."),
49
+ city: OptionalTextSchema.describe("Ort einer Adresse der Person."),
50
+ })
51
+ .strict()
52
+ .refine((value) => Boolean(value.query ?? value.postal_code ?? value.city), { message: "Mindestens ein Suchfeld ist nötig." });
53
+ export const OrderAssistantCustomerAddressSchema = z.object({
54
+ first_name: z.string().nullable(),
55
+ last_name: z.string().nullable(),
56
+ company: z.string().nullable(),
57
+ address_1: z.string().nullable(),
58
+ address_2: z.string().nullable(),
59
+ postal_code: z.string().nullable(),
60
+ city: z.string().nullable(),
61
+ country_code: z.string().nullable(),
62
+ phone: z.string().nullable(),
63
+ });
64
+ export const OrderAssistantCustomerSchema = z.object({
65
+ id: z.string(),
66
+ first_name: z.string().nullable(),
67
+ last_name: z.string().nullable(),
68
+ company_name: z.string().nullable(),
69
+ email: z.string().nullable(),
70
+ phone: z.string().nullable(),
71
+ /** Registered shop account (true) or a customer record without login. */
72
+ has_account: z.boolean(),
73
+ addresses: z.array(OrderAssistantCustomerAddressSchema),
74
+ });
75
+ export const SearchCustomersOutputSchema = z.object({
76
+ customers: z.array(OrderAssistantCustomerSchema),
77
+ });
78
+ export const SearchProductsInputSchema = z
79
+ .object({
80
+ query: NonEmptyStringSchema.max(255).describe("Produktname oder Artikelnummer (auch Teile davon)."),
81
+ country_code: CountryCodeSchema.describe("Lieferland als ISO-Code; bestimmt Preis und Währung."),
82
+ })
83
+ .strict();
84
+ export const OrderAssistantVariantSchema = z.object({
85
+ variant_id: z.string(),
86
+ product_title: z.string(),
87
+ variant_title: z.string().nullable(),
88
+ sku: z.string().nullable(),
89
+ /** Price per unit in major currency units (33.5 = 33,50); null without a price. */
90
+ unit_price: z.number().nullable(),
91
+ currency_code: z.string(),
92
+ /** Available quantity; null when the variant's stock is not managed. */
93
+ available_quantity: z.number().nullable(),
94
+ /** False when the variant cannot be ordered (no price or no stock). */
95
+ orderable: z.boolean(),
96
+ });
97
+ export const SearchProductsOutputSchema = z.object({
98
+ variants: z.array(OrderAssistantVariantSchema),
99
+ });
100
+ export const ListShippingOptionsInputSchema = z
101
+ .object({
102
+ country_code: CountryCodeSchema.describe("Lieferland als ISO-Code."),
103
+ })
104
+ .strict();
105
+ export const OrderAssistantShippingOptionSchema = z.object({
106
+ id: z.string(),
107
+ name: z.string(),
108
+ /** Flat price in major currency units; null when none is set for the currency. */
109
+ amount: z.number().nullable(),
110
+ currency_code: z.string(),
111
+ });
112
+ export const ListShippingOptionsOutputSchema = z.object({
113
+ shipping_options: z.array(OrderAssistantShippingOptionSchema),
114
+ });
115
+ export const OrderAssistantNewCustomerSchema = z
116
+ .object({
117
+ first_name: NonEmptyStringSchema.max(255),
118
+ last_name: NonEmptyStringSchema.max(255),
119
+ company_name: OptionalTextSchema,
120
+ email: z
121
+ .string()
122
+ .trim()
123
+ .toLowerCase()
124
+ .email()
125
+ .max(255)
126
+ .optional()
127
+ .describe("Nur angeben, wenn eine E-Mail-Adresse bekannt ist."),
128
+ phone: OptionalTextSchema,
129
+ })
130
+ .strict();
131
+ export const CreateDraftOrderInputSchema = z
132
+ .object({
133
+ customer_id: z
134
+ .string()
135
+ .trim()
136
+ .min(1)
137
+ .optional()
138
+ .describe("ID einer bestehenden Kund:in aus kunden_suchen."),
139
+ new_customer: OrderAssistantNewCustomerSchema.optional().describe("Neue Kund:in ohne Konto; nur wenn kunden_suchen niemanden findet."),
140
+ shipping_address: OrderAssistantAddressSchema,
141
+ billing_address: OrderAssistantAddressSchema.optional().describe("Rechnungsadresse; ohne Angabe gilt die Lieferadresse."),
142
+ items: z
143
+ .array(z
144
+ .object({
145
+ variant_id: NonEmptyStringSchema.describe("ID der Variante aus produkte_suchen."),
146
+ quantity: z.number().int().positive().max(9999),
147
+ })
148
+ .strict())
149
+ .min(1)
150
+ .max(ORDER_ASSISTANT_LIMITS.items),
151
+ shipping_option_id: NonEmptyStringSchema.describe("ID der Versandart aus versandarten_auflisten."),
152
+ source: OrderAssistantSourceSchema.describe("Wie die Bestellung eingegangen ist."),
153
+ note: z
154
+ .string()
155
+ .trim()
156
+ .min(1)
157
+ .max(ORDER_ASSISTANT_LIMITS.noteLength)
158
+ .optional()
159
+ .describe("Interne Notiz, z. B. Unklarheiten auf dem Fax."),
160
+ })
161
+ .strict()
162
+ .refine((value) => Boolean(value.customer_id) !== Boolean(value.new_customer), { message: "Genau eines von customer_id und new_customer ist nötig." });
163
+ export const OrderAssistantDraftItemSchema = z.object({
164
+ title: z.string(),
165
+ sku: z.string().nullable(),
166
+ quantity: z.number(),
167
+ unit_price: z.number(),
168
+ total: z.number(),
169
+ });
170
+ export const CreateDraftOrderOutputSchema = z.object({
171
+ draft_order_id: z.string(),
172
+ display_id: z.number().nullable(),
173
+ customer_id: z.string(),
174
+ /** True when the assistant created the customer record for this draft. */
175
+ customer_created: z.boolean(),
176
+ currency_code: z.string(),
177
+ items: z.array(OrderAssistantDraftItemSchema),
178
+ shipping_total: z.number(),
179
+ tax_total: z.number(),
180
+ total: z.number(),
181
+ /** Path of the draft in the Admin, relative to the backend URL. */
182
+ admin_path: z.string(),
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
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The shop's MCP endpoint for the order assistant (Streamable HTTP,
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.
9
+ */
10
+ export declare const orderAssistantPaths: {
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
+ };
33
+ };
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.orderAssistantPaths = void 0;
4
+ /**
5
+ * The shop's MCP endpoint for the order assistant (Streamable HTTP,
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.
12
+ */
13
+ exports.orderAssistantPaths = {
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
+ },
36
+ };