@solumflow-app/crm-mcp-server 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
6
6
  import { createClient } from "@solumflow-app/crm-client";
7
7
 
8
8
  // package.json
9
- var version = "0.11.0";
9
+ var version = "0.12.0";
10
10
 
11
11
  // src/catalog-migration-tools.ts
12
12
  import { z as z8 } from "zod";
@@ -4476,8 +4476,77 @@ function describeForm(form) {
4476
4476
  return `- ${form.name} (/${form.slug}, id ${form.id}): ${form.status}`;
4477
4477
  }
4478
4478
 
4479
- // src/order-tools.ts
4479
+ // src/hosting-order-page-tools.ts
4480
4480
  import { z as z16 } from "zod";
4481
+ function registerHostingOrderPageTools(server, client) {
4482
+ server.registerTool(
4483
+ "get_hosting_order_page",
4484
+ {
4485
+ title: "Read the hosting order page",
4486
+ description: 'The hosting order page: open or closed, the packages on it and the ones that could be, the quick extensions, the sites it may stand on, why it would refuse to open, its link, and the snippet that puts it on a site (or <CrmHostingOrder account="\u2026" /> from @solumflow-app/crm-client/react on a React site).',
4487
+ annotations: { readOnlyHint: true, idempotentHint: true }
4488
+ },
4489
+ async () => {
4490
+ try {
4491
+ const page2 = await client.getHostingOrderPage();
4492
+ return answer(describe(page2), page2);
4493
+ } catch (error) {
4494
+ return refusal(error, "hosting-order:manage");
4495
+ }
4496
+ }
4497
+ );
4498
+ server.registerTool(
4499
+ "update_hosting_order_page",
4500
+ {
4501
+ title: "Set up the hosting order page",
4502
+ description: "Changes the hosting order page; only what is sent changes. `packages` is the whole list in the order the page shows it (product ids from get_hosting_order_page candidates), `allowedOrigins` the whole list of sites the snippet may stand on -- add the site before placing the snippet. `enabled: true` opens real payments. It is refused while the page cannot deliver what it sells; the refusal names what the account owner has to fix in the app themselves (a registrar login, the renewal round still in shadow mode) -- pass that on, do not retry.",
4503
+ inputSchema: {
4504
+ enabled: z16.boolean().optional(),
4505
+ quickTlds: z16.array(z16.string().max(30)).max(12).optional().describe("Extensions without a dot, such as nl, com, be."),
4506
+ defaultPeriod: z16.enum(["month", "year"]).optional(),
4507
+ packages: z16.array(
4508
+ z16.object({
4509
+ productId: z16.string().uuid(),
4510
+ service: z16.enum(["hosting", "email"]),
4511
+ featured: z16.boolean().optional(),
4512
+ badge: z16.string().max(40).nullable().optional(),
4513
+ highlights: z16.array(z16.string().max(80)).max(4).optional()
4514
+ })
4515
+ ).max(24).optional(),
4516
+ allowedOrigins: z16.array(z16.string().max(255)).max(20).optional().describe(
4517
+ "Origins such as https://example.com: no path, lower case."
4518
+ )
4519
+ },
4520
+ annotations: { idempotentHint: true }
4521
+ },
4522
+ async (input) => {
4523
+ try {
4524
+ const page2 = await client.updateHostingOrderPage(input);
4525
+ return answer(`Saved. ${describe(page2)}`, page2);
4526
+ } catch (error) {
4527
+ return refusal(error, "hosting-order:manage");
4528
+ }
4529
+ }
4530
+ );
4531
+ }
4532
+ function describe(page2) {
4533
+ const lines = [
4534
+ `${page2.enabled ? "Open" : "Closed"}: ${page2.url}`,
4535
+ `Packages on the page: ${page2.packages.length} of ${page2.candidates.length}.`,
4536
+ page2.allowedOrigins.length ? `Sites it may stand on: ${page2.allowedOrigins.join(", ")}.` : "No sites yet: add the site to allowedOrigins before placing the snippet."
4537
+ ];
4538
+ if (page2.refusal) {
4539
+ lines.push(`It cannot open now: ${page2.refusal.message}`);
4540
+ }
4541
+ lines.push(
4542
+ "Snippet (the site needs its CSP to allow the app and js.stripe.com):",
4543
+ codeBlock(page2.embed)
4544
+ );
4545
+ return lines.join("\n");
4546
+ }
4547
+
4548
+ // src/order-tools.ts
4549
+ import { z as z17 } from "zod";
4481
4550
  function registerOrderTools(server, client) {
4482
4551
  server.registerTool(
4483
4552
  "get_order",
@@ -4485,7 +4554,7 @@ function registerOrderTools(server, client) {
4485
4554
  title: "Read one order",
4486
4555
  description: "One order by its number, its id or its Stripe PaymentIntent (pi_\u2026): payment status, whether it was sent, the lines and the totals. Live, never cached. It does not say who bought it or where it went, and it cannot change, ship or refund an order: that is done in the app.",
4487
4556
  inputSchema: {
4488
- ref: z16.string().min(1).max(200).describe("The order number, its id, or a pi_\u2026 PaymentIntent id.")
4557
+ ref: z17.string().min(1).max(200).describe("The order number, its id, or a pi_\u2026 PaymentIntent id.")
4489
4558
  },
4490
4559
  annotations: { readOnlyHint: true, idempotentHint: true }
4491
4560
  },
@@ -4514,16 +4583,16 @@ function describeOrder(order) {
4514
4583
  }
4515
4584
 
4516
4585
  // src/payment-page-tools.ts
4517
- import { z as z17 } from "zod";
4518
- var id4 = z17.string().uuid();
4519
- var slug = z17.string().min(2).max(64).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/).describe("Lowercase letters, digits and hyphens.");
4586
+ import { z as z18 } from "zod";
4587
+ var id4 = z18.string().uuid();
4588
+ var slug = z18.string().min(2).max(64).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/).describe("Lowercase letters, digits and hyphens.");
4520
4589
  function registerPaymentPageTools(server, client) {
4521
4590
  server.registerTool(
4522
4591
  "list_payment_pages",
4523
4592
  {
4524
4593
  title: "List payment pages",
4525
4594
  description: "The account\u2019s payment pages, paged by id; archived ones only with archived: true.",
4526
- inputSchema: { archived: z17.boolean().optional(), ...page },
4595
+ inputSchema: { archived: z18.boolean().optional(), ...page },
4527
4596
  annotations: { readOnlyHint: true, idempotentHint: true }
4528
4597
  },
4529
4598
  async (input) => {
@@ -4566,7 +4635,7 @@ ${blockers}`, page2);
4566
4635
  title: "Make a payment page",
4567
4636
  description: "A draft payment page for one of the account\u2019s products. Nothing is live until a person publishes it in the app. Without priceId the buyer picks from the product\u2019s active price options.",
4568
4637
  inputSchema: {
4569
- name: z17.string().min(1).max(120),
4638
+ name: z18.string().min(1).max(120),
4570
4639
  slug: slug.optional(),
4571
4640
  productId: id4,
4572
4641
  priceId: id4.optional()
@@ -4588,13 +4657,13 @@ ${blockers}`, page2);
4588
4657
  description: "Only what is sent changes. content takes whole blocks: hero {headline, subheadline}, bullets {title, items}, quote {text, author, role}, guarantee {title, body}, faq {items: [{question, answer}]}, trust {items}, paymentOptions {recommendedPriceId, note}; a block left out stays. settings takes termsUrl, requireFields, excludedPaymentMethods (what is switched OFF), thankYouMode, redirectUrl, createInvoice, maxQuantity, customFields, tagOnPurchase. status goes back to draft (off the web) or archived; publishing is done in the app. A new slug breaks links already sent.",
4589
4658
  inputSchema: {
4590
4659
  id: id4,
4591
- name: z17.string().min(1).max(120).optional(),
4660
+ name: z18.string().min(1).max(120).optional(),
4592
4661
  slug: slug.optional(),
4593
4662
  productId: id4.optional(),
4594
4663
  priceId: id4.nullable().optional(),
4595
- status: z17.enum(["draft", "archived"]).optional(),
4596
- content: z17.record(z17.string(), z17.unknown()).optional(),
4597
- settings: z17.record(z17.string(), z17.unknown()).optional()
4664
+ status: z18.enum(["draft", "archived"]).optional(),
4665
+ content: z18.record(z18.string(), z18.unknown()).optional(),
4666
+ settings: z18.record(z18.string(), z18.unknown()).optional()
4598
4667
  },
4599
4668
  annotations: { idempotentHint: true }
4600
4669
  },
@@ -4613,9 +4682,9 @@ function describePage(page2) {
4613
4682
  }
4614
4683
 
4615
4684
  // src/placement-tools.ts
4616
- import { z as z18 } from "zod";
4685
+ import { z as z19 } from "zod";
4617
4686
  var REACT_HINT = "On a React or Next.js site, prefer the components in `@solumflow-app/crm-client/react` (CrmForm, CrmBooking, CrmChatWidget, CrmSiteTracking) over pasting HTML.";
4618
- var origin = z18.string().regex(/^https?:\/\/[a-zA-Z0-9.-]+(:[0-9]{1,5})?$/).describe("Scheme, host and optional port; no path: https://www.example.com");
4687
+ var origin = z19.string().regex(/^https?:\/\/[a-zA-Z0-9.-]+(:[0-9]{1,5})?$/).describe("Scheme, host and optional port; no path: https://www.example.com");
4619
4688
  function registerPlacementTools(server, client) {
4620
4689
  server.registerTool(
4621
4690
  "list_forms",
@@ -4688,13 +4757,13 @@ function registerPlacementTools(server, client) {
4688
4757
  title: "Make or change a chat widget",
4689
4758
  description: "Makes the chat widget at this address when there is none, or changes it. Sets its name, whether it is live, and which sites it may run on. How it looks and what it says is done in the app\u2019s builder. Sites are added and removed, never replaced. Careful: an empty list means ANY site, so adding the first origin narrows the widget to that site only, and removing an origin takes the chat off a site that may be live.",
4690
4759
  inputSchema: {
4691
- slug: z18.string().regex(/^[a-z][a-z0-9-]*$/).max(100).describe(
4760
+ slug: z19.string().regex(/^[a-z][a-z0-9-]*$/).max(100).describe(
4692
4761
  'The widget\u2019s address on this account, such as "website-chat".'
4693
4762
  ),
4694
- name: z18.string().min(1).max(200).optional().describe("Required when the widget does not exist yet."),
4695
- status: z18.enum(["draft", "published"]).optional().describe("Only a published widget answers on a site."),
4696
- addAllowedOrigins: z18.array(origin).max(20).optional(),
4697
- removeAllowedOrigins: z18.array(origin).max(20).optional()
4763
+ name: z19.string().min(1).max(200).optional().describe("Required when the widget does not exist yet."),
4764
+ status: z19.enum(["draft", "published"]).optional().describe("Only a published widget answers on a site."),
4765
+ addAllowedOrigins: z19.array(origin).max(20).optional(),
4766
+ removeAllowedOrigins: z19.array(origin).max(20).optional()
4698
4767
  },
4699
4768
  annotations: {
4700
4769
  readOnlyHint: false,
@@ -4742,9 +4811,9 @@ ${describeChatWidget(widget)}`,
4742
4811
  title: "Set up or change the tracking script",
4743
4812
  description: "Sets website tracking up when it is not there, adds or removes the hosts it counts visits from, and changes the consent it waits for. An empty list of hosts counts nothing, so add the host of every site the script is placed on. Hosts are added and removed, never replaced.",
4744
4813
  inputSchema: {
4745
- addAllowedHosts: z18.array(z18.string()).max(20).optional().describe('Hosts such as "www.example.com" or "*.example.com".'),
4746
- removeAllowedHosts: z18.array(z18.string()).max(20).optional(),
4747
- consentSignal: z18.enum([
4814
+ addAllowedHosts: z19.array(z19.string()).max(20).optional().describe('Hosts such as "www.example.com" or "*.example.com".'),
4815
+ removeAllowedHosts: z19.array(z19.string()).max(20).optional(),
4816
+ consentSignal: z19.enum([
4748
4817
  "ad_storage",
4749
4818
  "analytics_storage",
4750
4819
  "personalization_storage",
@@ -4805,14 +4874,14 @@ function describeSiteTracking(site) {
4805
4874
  }
4806
4875
 
4807
4876
  // src/record-tools.ts
4808
- import { z as z19 } from "zod";
4809
- var recordId = z19.string().uuid().describe("The id of the record.");
4810
- var values = z19.record(z19.string(), z19.unknown()).describe(
4877
+ import { z as z20 } from "zod";
4878
+ var recordId = z20.string().uuid().describe("The id of the record.");
4879
+ var values = z20.record(z20.string(), z20.unknown()).describe(
4811
4880
  "Field values by field slug, as list_record_lists names them. A field marked sensitive is refused."
4812
4881
  );
4813
- var tags = z19.object({
4814
- add: z19.array(z19.string().uuid()).max(50).optional(),
4815
- remove: z19.array(z19.string().uuid()).max(50).optional()
4882
+ var tags = z20.object({
4883
+ add: z20.array(z20.string().uuid()).max(50).optional(),
4884
+ remove: z20.array(z20.string().uuid()).max(50).optional()
4816
4885
  }).optional().describe("Tag ids to put on or take off; list_tags names them.");
4817
4886
  function registerRecordTools(server, client) {
4818
4887
  server.registerTool(
@@ -4837,8 +4906,8 @@ function registerRecordTools(server, client) {
4837
4906
  title: "Search the records of one list",
4838
4907
  description: "The records of one list, by its slug, paged. q searches the name and every value but a sensitive one. Records whose processing is restricted are left out.",
4839
4908
  inputSchema: {
4840
- list: z19.string().min(1).max(64).describe("The slug of the list."),
4841
- q: z19.string().max(200).optional(),
4909
+ list: z20.string().min(1).max(64).describe("The slug of the list."),
4910
+ q: z20.string().max(200).optional(),
4842
4911
  ...page
4843
4912
  },
4844
4913
  annotations: { readOnlyHint: true, idempotentHint: true }
@@ -4878,7 +4947,7 @@ function registerRecordTools(server, client) {
4878
4947
  title: "Make a record",
4879
4948
  description: "A new record in a list (left out: a person, where the account puts new contacts). A person whose e-mail or phone is already known is not made again: the answer gives only their id (matched), nothing on them changes, and get_record reads them; use update_record to change them. Automations that start on a new record run as they do for one made in the app.",
4880
4949
  inputSchema: {
4881
- list: z19.string().min(1).max(64).optional(),
4950
+ list: z20.string().min(1).max(64).optional(),
4882
4951
  values,
4883
4952
  tags
4884
4953
  }
@@ -4904,7 +4973,7 @@ function registerRecordTools(server, client) {
4904
4973
  id: recordId,
4905
4974
  values: values.optional(),
4906
4975
  tags,
4907
- ownerId: z19.string().uuid().nullable().optional()
4976
+ ownerId: z20.string().uuid().nullable().optional()
4908
4977
  },
4909
4978
  annotations: { idempotentHint: true }
4910
4979
  },
@@ -4968,7 +5037,7 @@ function registerRecordTools(server, client) {
4968
5037
  description: "A note on a record\u2019s timeline, shown as written through the API with the name of this key.",
4969
5038
  inputSchema: {
4970
5039
  recordId,
4971
- body: z19.string().min(1).max(2e4)
5040
+ body: z20.string().min(1).max(2e4)
4972
5041
  }
4973
5042
  },
4974
5043
  async ({ recordId: id7, body }) => {
@@ -4986,12 +5055,12 @@ function registerRecordTools(server, client) {
4986
5055
  title: "Add a contact, or find the one already known",
4987
5056
  description: "A person by e-mail address. Somebody already known by that address is matched rather than made twice (created: false), and the answer is their id. A field left out is never cleared on a contact already known.",
4988
5057
  inputSchema: {
4989
- email: z19.string().email().max(320),
4990
- firstName: z19.string().trim().min(1).max(200).optional(),
4991
- lastName: z19.string().trim().min(1).max(200).optional(),
4992
- phone: z19.string().trim().min(1).max(50).optional(),
4993
- company: z19.string().trim().min(1).max(200).optional(),
4994
- object: z19.string().trim().min(1).max(64).optional().describe(
5058
+ email: z20.string().email().max(320),
5059
+ firstName: z20.string().trim().min(1).max(200).optional(),
5060
+ lastName: z20.string().trim().min(1).max(200).optional(),
5061
+ phone: z20.string().trim().min(1).max(50).optional(),
5062
+ company: z20.string().trim().min(1).max(200).optional(),
5063
+ object: z20.string().trim().min(1).max(64).optional().describe(
4995
5064
  "The slug of the list for people a new contact goes into; left out, the account\u2019s own choice."
4996
5065
  )
4997
5066
  },
@@ -5073,20 +5142,20 @@ function registerServerInfoTool(server, info) {
5073
5142
  }
5074
5143
 
5075
5144
  // src/shop-setup-tools.ts
5076
- import { z as z20 } from "zod";
5077
- var id5 = z20.string().uuid();
5078
- var cents2 = z20.number().int().min(0).max(99999999);
5079
- var region = z20.object({
5080
- countryCode: z20.string().regex(/^[A-Z]{2}$/).describe("Two capital letters, ISO 3166 (NL, BE, DE)."),
5081
- postcodeFrom: z20.string().max(20).nullable().default(null).describe("With postcodeTo: only this range of the country."),
5082
- postcodeTo: z20.string().max(20).nullable().default(null)
5145
+ import { z as z21 } from "zod";
5146
+ var id5 = z21.string().uuid();
5147
+ var cents2 = z21.number().int().min(0).max(99999999);
5148
+ var region = z21.object({
5149
+ countryCode: z21.string().regex(/^[A-Z]{2}$/).describe("Two capital letters, ISO 3166 (NL, BE, DE)."),
5150
+ postcodeFrom: z21.string().max(20).nullable().default(null).describe("With postcodeTo: only this range of the country."),
5151
+ postcodeTo: z21.string().max(20).nullable().default(null)
5083
5152
  });
5084
- var discount = z20.object({
5085
- name: z20.string().min(1).max(120).describe("What the team sees it as."),
5086
- code: z20.string().max(40).nullable().describe(
5153
+ var discount = z21.object({
5154
+ name: z21.string().min(1).max(120).describe("What the team sees it as."),
5155
+ code: z21.string().max(40).nullable().describe(
5087
5156
  "What a buyer types (upper-cased), or null for one that applies on its own."
5088
5157
  ),
5089
- effect: z20.enum([
5158
+ effect: z21.enum([
5090
5159
  "tier_price",
5091
5160
  "percent_off_lines",
5092
5161
  "amount_off_lines",
@@ -5096,19 +5165,19 @@ var discount = z20.object({
5096
5165
  ]).describe(
5097
5166
  "tier_price: a lower price from tierMinQuantity of one product; *_lines: off each matching line; *_order: once off the whole order; free_shipping: no postage."
5098
5167
  ),
5099
- percent: z20.number().nullable().describe("For a percentage: above 0, at most 100."),
5100
- amountCents: z20.number().int().nullable().describe("For an amount, in currency, as the account enters prices."),
5101
- currency: z20.string().regex(/^[A-Z]{3}$/).nullable().describe("Required beside amountCents (EUR)."),
5102
- tierMinQuantity: z20.number().int().nullable(),
5103
- minSubtotalCents: z20.number().int().nullable().describe("Only from this basket value, before discount."),
5104
- productIds: z20.array(id5).max(500).describe("Empty: every product."),
5105
- categoryIds: z20.array(id5).max(100),
5106
- objectIds: z20.array(id5).max(20),
5107
- startsAt: z20.string().datetime({ offset: true }).nullable(),
5108
- endsAt: z20.string().datetime({ offset: true }).nullable(),
5109
- maxUses: z20.number().int().positive().nullable(),
5110
- maxUsesPerEmail: z20.number().int().positive().nullable(),
5111
- combinable: z20.boolean().describe(
5168
+ percent: z21.number().nullable().describe("For a percentage: above 0, at most 100."),
5169
+ amountCents: z21.number().int().nullable().describe("For an amount, in currency, as the account enters prices."),
5170
+ currency: z21.string().regex(/^[A-Z]{3}$/).nullable().describe("Required beside amountCents (EUR)."),
5171
+ tierMinQuantity: z21.number().int().nullable(),
5172
+ minSubtotalCents: z21.number().int().nullable().describe("Only from this basket value, before discount."),
5173
+ productIds: z21.array(id5).max(500).describe("Empty: every product."),
5174
+ categoryIds: z21.array(id5).max(100),
5175
+ objectIds: z21.array(id5).max(20),
5176
+ startsAt: z21.string().datetime({ offset: true }).nullable(),
5177
+ endsAt: z21.string().datetime({ offset: true }).nullable(),
5178
+ maxUses: z21.number().int().positive().nullable(),
5179
+ maxUsesPerEmail: z21.number().int().positive().nullable(),
5180
+ combinable: z21.boolean().describe(
5112
5181
  "Whether it may count beside another discount on the same order."
5113
5182
  )
5114
5183
  });
@@ -5161,8 +5230,8 @@ function registerShopSetupTools(server, client) {
5161
5230
  description: "A zone is a name and the countries (or postcode ranges) it covers. Without id a new zone; with one, that zone renamed and its places replaced by these. Answers the zone id, for upsert_shipping_method.",
5162
5231
  inputSchema: {
5163
5232
  id: id5.optional().describe("The zone to change; leave out for a new one."),
5164
- name: z20.string().min(1).max(80),
5165
- regions: z20.array(region).max(300).default([])
5233
+ name: z21.string().min(1).max(80),
5234
+ regions: z21.array(region).max(300).default([])
5166
5235
  },
5167
5236
  annotations: { idempotentHint: true }
5168
5237
  },
@@ -5186,14 +5255,14 @@ function registerShopSetupTools(server, client) {
5186
5255
  inputSchema: {
5187
5256
  id: id5.optional(),
5188
5257
  zoneId: id5,
5189
- kind: z20.enum(["flat_rate", "free_shipping", "local_pickup"]),
5190
- name: z20.string().min(1).max(80).describe("What the buyer chooses."),
5191
- enabled: z20.boolean().default(true),
5258
+ kind: z21.enum(["flat_rate", "free_shipping", "local_pickup"]),
5259
+ name: z21.string().min(1).max(80).describe("What the buyer chooses."),
5260
+ enabled: z21.boolean().default(true),
5192
5261
  amountCents: cents2.default(0),
5193
5262
  noClassAmountCents: cents2.default(0),
5194
5263
  taxRateId: id5.nullish(),
5195
5264
  freeThresholdCents: cents2.nullish(),
5196
- classRates: z20.array(z20.object({ classId: id5, amountCents: cents2 })).max(100).default([])
5265
+ classRates: z21.array(z21.object({ classId: id5, amountCents: cents2 })).max(100).default([])
5197
5266
  },
5198
5267
  annotations: { idempotentHint: true }
5199
5268
  },
@@ -5214,7 +5283,7 @@ function registerShopSetupTools(server, client) {
5214
5283
  {
5215
5284
  title: "List discounts",
5216
5285
  description: "The account discounts, newest first, at most 200: codes, the ones that apply on their own, and tier prices, with every condition and how many paid orders used each. Archived ones only with archived: true.",
5217
- inputSchema: { archived: z20.boolean().optional() },
5286
+ inputSchema: { archived: z21.boolean().optional() },
5218
5287
  annotations: { readOnlyHint: true, idempotentHint: true }
5219
5288
  },
5220
5289
  async ({ archived }) => {
@@ -5256,7 +5325,7 @@ function registerShopSetupTools(server, client) {
5256
5325
  description: "Stops a discount from giving anything to a new cart or order; orders that used it keep it. archived: false brings it back. Deleting is left to a person in the app.",
5257
5326
  inputSchema: {
5258
5327
  id: id5,
5259
- archived: z20.boolean().default(true)
5328
+ archived: z21.boolean().default(true)
5260
5329
  },
5261
5330
  annotations: { idempotentHint: true }
5262
5331
  },
@@ -5321,9 +5390,9 @@ function registerShopSetupTools(server, client) {
5321
5390
  description: "Point the account at the site revalidation route (with the SDK: https://<site>/api/solumflow/revalidate) so its cached pages follow a product the moment it changes. Without id a new address: the answer carries its signing key once -- put it in the site environment as SOLUMFLOW_WEBHOOK_KEY, it cannot be read back. https only, never a private network.",
5322
5391
  inputSchema: {
5323
5392
  id: id5.optional(),
5324
- name: z20.string().min(1).max(80),
5325
- url: z20.string().url().max(2048),
5326
- events: z20.array(z20.string().min(1).max(60)).min(1).max(20).describe(
5393
+ name: z21.string().min(1).max(80),
5394
+ url: z21.string().url().max(2048),
5395
+ events: z21.array(z21.string().min(1).max(60)).min(1).max(20).describe(
5327
5396
  "For a shop: product.changed, product.deleted, category.changed."
5328
5397
  )
5329
5398
  },
@@ -5359,7 +5428,7 @@ function describeDiscount(entry) {
5359
5428
  }
5360
5429
 
5361
5430
  // src/shop-test-order-tool.ts
5362
- import { z as z21 } from "zod";
5431
+ import { z as z22 } from "zod";
5363
5432
  var PAID_POLLS = 10;
5364
5433
  var PAID_POLL_MS = 1500;
5365
5434
  function registerShopTestOrderTool(server, client, options = {}) {
@@ -5370,25 +5439,25 @@ function registerShopTestOrderTool(server, client, options = {}) {
5370
5439
  title: "Place and pay a test order",
5371
5440
  description: "Proves the shop end to end in Stripe test mode: puts one product in a cart, chooses postage, places the order, pays it with Stripe test card 4242, and reads the order back until it is paid (the invoice and the confirmation mail follow from there). Refused when the account is live -- no real money can move through this tool. Needs a key with shop:manage, cart:write, orders:write and orders:read. Pass `extras` (products from the product\u2019s own `extras` list) and `options` (by option key: a text, true, or a choice key) to prove a product with an extra or a surcharge option; the cart must show them, priced by the server, before the order is placed. Never send an amount.",
5372
5441
  inputSchema: {
5373
- email: z21.string().email().describe(
5442
+ email: z22.string().email().describe(
5374
5443
  "Where the confirmation goes: the shop owner, never a customer."
5375
5444
  ),
5376
- productId: z21.string().uuid().optional().describe(
5445
+ productId: z22.string().uuid().optional().describe(
5377
5446
  "The product to buy; leave out for the first one in stock."
5378
5447
  ),
5379
- countryCode: z21.string().regex(/^[A-Z]{2}$/).default("NL").describe("Where it is posted, for a product that is posted."),
5380
- postalCode: z21.string().max(20).optional(),
5381
- extras: z21.array(
5382
- z21.object({
5383
- productId: z21.string().uuid(),
5384
- quantity: z21.number().int().min(1).max(99).default(1)
5448
+ countryCode: z22.string().regex(/^[A-Z]{2}$/).default("NL").describe("Where it is posted, for a product that is posted."),
5449
+ postalCode: z22.string().max(20).optional(),
5450
+ extras: z22.array(
5451
+ z22.object({
5452
+ productId: z22.string().uuid(),
5453
+ quantity: z22.number().int().min(1).max(99).default(1)
5385
5454
  })
5386
5455
  ).max(10).optional().describe(
5387
5456
  "Extras to take with the product, each a product from its `extras`; for one that follows its line the quantity is per piece."
5388
5457
  ),
5389
- options: z21.record(
5390
- z21.string().regex(/^[a-z0-9_]{1,40}$/),
5391
- z21.union([z21.string().max(2e3), z21.boolean()])
5458
+ options: z22.record(
5459
+ z22.string().regex(/^[a-z0-9_]{1,40}$/),
5460
+ z22.union([z22.string().max(2e3), z22.boolean()])
5392
5461
  ).optional().describe(
5393
5462
  "The choices for the product\u2019s options, by option key: a text, true for a ticked box, or the key of a choice."
5394
5463
  )
@@ -5515,9 +5584,9 @@ function registerShopTestOrderTool(server, client, options = {}) {
5515
5584
  }
5516
5585
 
5517
5586
  // src/task-tools.ts
5518
- import { z as z22 } from "zod";
5519
- var id6 = z22.string().uuid();
5520
- var moment2 = z22.string().datetime({ offset: true }).describe(
5587
+ import { z as z23 } from "zod";
5588
+ var id6 = z23.string().uuid();
5589
+ var moment2 = z23.string().datetime({ offset: true }).describe(
5521
5590
  "An ISO moment with its offset, such as 2026-11-01T09:00:00+01:00."
5522
5591
  );
5523
5592
  var OUTSIDE_PROJECTS = "Only tasks outside projects: a project\u2019s tasks are seen differently per member and are worked on in the app.";
@@ -5528,7 +5597,7 @@ function registerTaskTools(server, client) {
5528
5597
  title: "List tasks",
5529
5598
  description: `Tasks paged by id: open and in progress unless status says otherwise; recordId, dealId and assigneeId narrow them. ${OUTSIDE_PROJECTS}`,
5530
5599
  inputSchema: {
5531
- status: z22.enum(["open", "in_progress", "done", "all"]).optional(),
5600
+ status: z23.enum(["open", "in_progress", "done", "all"]).optional(),
5532
5601
  recordId: id6.optional(),
5533
5602
  dealId: id6.optional(),
5534
5603
  assigneeId: id6.optional(),
@@ -5574,14 +5643,14 @@ function registerTaskTools(server, client) {
5574
5643
  title: "Make a task",
5575
5644
  description: `A task on a contact (recordId), on a deal (dealId; with recordId one of that contact\u2019s deals), or on nothing, optionally due, prioritised and assigned to members. ${OUTSIDE_PROJECTS}`,
5576
5645
  inputSchema: {
5577
- title: z22.string().min(1).max(500),
5578
- description: z22.string().max(2e4).nullable().optional(),
5646
+ title: z23.string().min(1).max(500),
5647
+ description: z23.string().max(2e4).nullable().optional(),
5579
5648
  dueAt: moment2.nullable().optional(),
5580
- priority: z22.enum(["low", "normal", "high"]).optional(),
5649
+ priority: z23.enum(["low", "normal", "high"]).optional(),
5581
5650
  recordId: id6.nullable().optional(),
5582
5651
  dealId: id6.nullable().optional(),
5583
5652
  parentTaskId: id6.nullable().optional(),
5584
- assigneeIds: z22.array(id6).max(20).optional()
5653
+ assigneeIds: z23.array(id6).max(20).optional()
5585
5654
  }
5586
5655
  },
5587
5656
  async (input) => {
@@ -5600,12 +5669,12 @@ function registerTaskTools(server, client) {
5600
5669
  description: `Changes only what is sent; status done completes it. assigneeIds replaces who works on it. ${OUTSIDE_PROJECTS}`,
5601
5670
  inputSchema: {
5602
5671
  id: id6,
5603
- title: z22.string().min(1).max(500).optional(),
5604
- description: z22.string().max(2e4).nullable().optional(),
5672
+ title: z23.string().min(1).max(500).optional(),
5673
+ description: z23.string().max(2e4).nullable().optional(),
5605
5674
  dueAt: moment2.nullable().optional(),
5606
- priority: z22.enum(["low", "normal", "high"]).optional(),
5607
- status: z22.enum(["open", "in_progress", "done"]).optional(),
5608
- assigneeIds: z22.array(id6).max(20).optional()
5675
+ priority: z23.enum(["low", "normal", "high"]).optional(),
5676
+ status: z23.enum(["open", "in_progress", "done"]).optional(),
5677
+ assigneeIds: z23.array(id6).max(20).optional()
5609
5678
  },
5610
5679
  annotations: { idempotentHint: true }
5611
5680
  },
@@ -5626,7 +5695,7 @@ function registerTaskTools(server, client) {
5626
5695
  inputSchema: {
5627
5696
  from: moment2,
5628
5697
  to: moment2,
5629
- kind: z22.enum(["appointment", "block", "all"]).optional(),
5698
+ kind: z23.enum(["appointment", "block", "all"]).optional(),
5630
5699
  contactRecordId: id6.optional(),
5631
5700
  ...page
5632
5701
  },
@@ -5675,14 +5744,14 @@ function describeAppointment(appointment) {
5675
5744
  }
5676
5745
 
5677
5746
  // src/tool-definitions-version.ts
5678
- import { z as z23 } from "zod";
5747
+ import { z as z24 } from "zod";
5679
5748
  import { createHash } from "crypto";
5680
5749
  function toolDefinitionsHash(tools) {
5681
5750
  const definitions = [...tools].sort((left, right) => left.name.localeCompare(right.name)).map(({ name, config }) => ({
5682
5751
  name,
5683
5752
  title: config.title ?? null,
5684
5753
  description: config.description ?? null,
5685
- inputSchema: config.inputSchema ? z23.toJSONSchema(z23.object(config.inputSchema), {
5754
+ inputSchema: config.inputSchema ? z24.toJSONSchema(z24.object(config.inputSchema), {
5686
5755
  io: "input",
5687
5756
  unrepresentable: "any"
5688
5757
  }) : null,
@@ -5730,7 +5799,7 @@ function holdToolRegistrations(real) {
5730
5799
  description: `${config.description ?? ""} Definitions version ${version2}: send it as schemaHash and the answer tells you when these tool definitions are older than the server.`,
5731
5800
  inputSchema: {
5732
5801
  ...config.inputSchema,
5733
- schemaHash: z23.string().max(64).optional().describe(
5802
+ schemaHash: z24.string().max(64).optional().describe(
5734
5803
  `The definitions version named in this tool's description (${version2}).`
5735
5804
  )
5736
5805
  }
@@ -5801,6 +5870,7 @@ async function main() {
5801
5870
  registerEmailTemplateTools(held.server, client);
5802
5871
  registerFormDefinitionTools(held.server, client);
5803
5872
  registerConsentBannerTools(held.server, client);
5873
+ registerHostingOrderPageTools(held.server, client);
5804
5874
  registerTaskTools(held.server, client);
5805
5875
  held.publish();
5806
5876
  await server.connect(new StdioServerTransport());