@solumflow-app/crm-mcp-server 0.6.0 → 0.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/dist/index.js +35 -12
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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.
|
|
9
|
+
var version = "0.8.0";
|
|
10
10
|
|
|
11
11
|
// src/catalog-migration-tools.ts
|
|
12
12
|
import { z as z4 } from "zod";
|
|
@@ -38,6 +38,11 @@ var CatalogFieldSlugSchema = z.string().max(63).regex(FIELD_SLUG, {
|
|
|
38
38
|
)
|
|
39
39
|
});
|
|
40
40
|
var cents = z.number().int().min(0).max(MAX_AMOUNT_CENTS);
|
|
41
|
+
var CatalogCategoryPlacementSchema = z.object({
|
|
42
|
+
slug: CatalogWebSlugSchema,
|
|
43
|
+
/** Lowest first; equal places stand in id order. */
|
|
44
|
+
position: z.number().int().min(-1e6).max(1e6).optional()
|
|
45
|
+
}).strict();
|
|
41
46
|
var text = (max) => z.string().trim().max(max).nullable().optional();
|
|
42
47
|
var CatalogPriceSchema = z.object({
|
|
43
48
|
/** The price's own id at the source, so a renamed price stays itself. */
|
|
@@ -199,8 +204,13 @@ var CatalogProductUpsertSchema = z.object({
|
|
|
199
204
|
socialTitle: text(300),
|
|
200
205
|
socialDescription: text(1e3),
|
|
201
206
|
socialImage: CatalogFileRefSchema.nullable().optional(),
|
|
202
|
-
/**
|
|
203
|
-
|
|
207
|
+
/**
|
|
208
|
+
* Replaces the product's categories, by slug, the first one leading. An
|
|
209
|
+
* entry may be `{ slug, position }` instead, to also set where the
|
|
210
|
+
* product stands on that category's page (`?sort=position`, lowest
|
|
211
|
+
* first); a plain slug leaves a stored place as it is.
|
|
212
|
+
*/
|
|
213
|
+
categories: z.array(z.union([CatalogWebSlugSchema, CatalogCategoryPlacementSchema])).max(50).optional(),
|
|
204
214
|
/**
|
|
205
215
|
* Make a category that does not exist yet instead of refusing. Off unless
|
|
206
216
|
* asked for, so a typo cannot become a category on a customer's website.
|
|
@@ -499,6 +509,13 @@ function more(nextCursor) {
|
|
|
499
509
|
function money(cents2, currency2) {
|
|
500
510
|
return `${(cents2 / 100).toFixed(2)} ${currency2.toUpperCase()}`;
|
|
501
511
|
}
|
|
512
|
+
function fieldsNotOnPageNote(slugs) {
|
|
513
|
+
if (!slugs?.length) {
|
|
514
|
+
return "";
|
|
515
|
+
}
|
|
516
|
+
const count = slugs.length === 1 ? "1 public field has" : `${slugs.length} public fields have`;
|
|
517
|
+
return ` ${count} a value but ${slugs.length === 1 ? "is" : "are"} not on this product's page: ${slugs.join(", ")}; send them in publicFields to show them.`;
|
|
518
|
+
}
|
|
502
519
|
|
|
503
520
|
// src/catalog-tools.ts
|
|
504
521
|
var objectInput = {
|
|
@@ -589,7 +606,7 @@ function registerCatalogTools(server, client) {
|
|
|
589
606
|
"get_product",
|
|
590
607
|
{
|
|
591
608
|
title: "Read one product",
|
|
592
|
-
description: "One product as its page needs it: name, prices, pictures, categories, variants and `fields`, keyed by field slug. Published products only unless you pass `status` (needs
|
|
609
|
+
description: "One product as its page needs it: name, prices, pictures, categories, variants and `fields`, keyed by field slug. Published products only unless you pass `status` (needs a server key), and a published one may be a minute old: to read back exactly what you just wrote, use get_product_by_ref. Answers that it is not found for an unknown product, or one not in the status asked. " + CONTENT_BLOCKS,
|
|
593
610
|
inputSchema: { slugOrId, currency, status: productStatus },
|
|
594
611
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
595
612
|
},
|
|
@@ -619,7 +636,7 @@ function registerCatalogTools(server, client) {
|
|
|
619
636
|
"list_products",
|
|
620
637
|
{
|
|
621
638
|
title: "List the products",
|
|
622
|
-
description: "One page of the catalogue: name, address (slug), lowest price, whether it is in stock, its first picture and the public fields a listing shows. Narrow it to one category\u2019s whole branch by its id or address, or by the reference upsert_category recorded (`categorySource` + `categoryRef`), to one catalogue by the slug of its object, or to the product carrying an article number (`sku`). Read get_product for everything a product page needs. Published products only, and may be a minute old; `status` lists concepts, scheduled and archived ones too (needs
|
|
639
|
+
description: "One page of the catalogue: name, address (slug), lowest price, whether it is in stock, its first picture and the public fields a listing shows. Narrow it to one category\u2019s whole branch by its id or address, or by the reference upsert_category recorded (`categorySource` + `categoryRef`), to one catalogue by the slug of its object, or to the product carrying an article number (`sku`). Read get_product for everything a product page needs. Published products only, and may be a minute old; `status` lists concepts, scheduled and archived ones too (needs a server key). `sort` orders the published ones: -publishedAt (newest first, the default), publishedAt, name, or position -- the seller\u2019s own order on the page of `category`, which it needs. Each item carries `sku`, `publishedAt` and `stockQuantity` (null when not counted).",
|
|
623
640
|
inputSchema: {
|
|
624
641
|
...page,
|
|
625
642
|
category: z3.string().regex(
|
|
@@ -636,7 +653,10 @@ function registerCatalogTools(server, client) {
|
|
|
636
653
|
'One catalogue, by the slug of its object, such as "product".'
|
|
637
654
|
),
|
|
638
655
|
currency,
|
|
639
|
-
status: productStatus
|
|
656
|
+
status: productStatus,
|
|
657
|
+
sort: z3.enum(["-publishedAt", "publishedAt", "name", "position"]).optional().describe(
|
|
658
|
+
"The order: newest first by default; position needs `category`."
|
|
659
|
+
)
|
|
640
660
|
},
|
|
641
661
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
642
662
|
},
|
|
@@ -664,7 +684,7 @@ function registerCatalogTools(server, client) {
|
|
|
664
684
|
"list_categories",
|
|
665
685
|
{
|
|
666
686
|
title: "List the categories",
|
|
667
|
-
description: 'The categories of the catalogue, each with its path from the top and how many products are in it. Left without `parent`, every category comes back, which is what building a menu in one go needs; "root" asks for the top level, a category id for its children. Published categories only unless you pass `status` (draft or all, needs
|
|
687
|
+
description: 'The categories of the catalogue, each with its path from the top and how many products are in it. Left without `parent`, every category comes back, which is what building a menu in one go needs; "root" asks for the top level, a category id for its children. Published categories only unless you pass `status` (draft or all, needs a server key).',
|
|
668
688
|
inputSchema: {
|
|
669
689
|
...page,
|
|
670
690
|
parent: z3.union([z3.literal("root"), z3.string().uuid()]).optional().describe('"root" or the id of a category.'),
|
|
@@ -688,7 +708,7 @@ function registerCatalogTools(server, client) {
|
|
|
688
708
|
"get_category",
|
|
689
709
|
{
|
|
690
710
|
title: "Read one category page",
|
|
691
|
-
description: "One category as its page needs it: name, description, the texts for search engines and social media, and the first page of its products. Pass the nextCursor back to page through the products. A category that is not on the website yet needs `status` (draft or all,
|
|
711
|
+
description: "One category as its page needs it: name, description, the texts for search engines and social media, and the first page of its products. Pass the nextCursor back to page through the products. A category that is not on the website yet needs `status` (draft or all, a server key); its products are the published ones.",
|
|
692
712
|
inputSchema: {
|
|
693
713
|
slugOrId: z3.string().min(1).max(200).describe("The address (slug) or the id of the category."),
|
|
694
714
|
...page,
|
|
@@ -852,7 +872,7 @@ function registerCatalogMigrationTools(server, client) {
|
|
|
852
872
|
(file) => file.status === "pending"
|
|
853
873
|
);
|
|
854
874
|
return answer(
|
|
855
|
-
`${product.name} (${product.slug || "no address"}), ${product.status} (${product.availability}); ${Object.keys(product.fields).length} fields, ${product.categories.length} categories, ${product.images.length} pictures; files: ${pending.length} pending, ${refused.length} refused${refused.length ? ` (${refused.map((file) => `${file.ref}: ${file.reason}`).join("; ")})` : ""}
|
|
875
|
+
`${product.name} (${product.slug || "no address"}), ${product.status} (${product.availability}); ${Object.keys(product.fields).length} fields, ${product.categories.length} categories, ${product.images.length} pictures; files: ${pending.length} pending, ${refused.length} refused${refused.length ? ` (${refused.map((file) => `${file.ref}: ${file.reason}`).join("; ")})` : ""}.` + fieldsNotOnPageNote(product.fieldsNotOnPage),
|
|
856
876
|
product
|
|
857
877
|
);
|
|
858
878
|
} catch (error) {
|
|
@@ -886,12 +906,15 @@ function registerCatalogMigrationTools(server, client) {
|
|
|
886
906
|
target(object2)
|
|
887
907
|
);
|
|
888
908
|
const count = (kind) => outcomes.filter((outcome) => outcome.outcome === kind).length;
|
|
909
|
+
const offPage = new Set(
|
|
910
|
+
outcomes.flatMap((outcome) => outcome.product?.fieldsNotOnPage ?? [])
|
|
911
|
+
);
|
|
889
912
|
const refusals = outcomes.filter((outcome) => outcome.outcome === "refused").map(
|
|
890
913
|
(outcome) => `- ${outcome.externalRef}: ${outcome.error?.message ?? "refused"}` + (outcome.error?.fields.length ? ` (${outcome.error.fields.map((field) => `${field.path}: ${field.message}`).join("; ")})` : "")
|
|
891
914
|
);
|
|
892
915
|
return answer(
|
|
893
916
|
[
|
|
894
|
-
`${count("created")} made, ${count("changed")} changed, ${count("refused")} refused, ${count("skipped")} skipped
|
|
917
|
+
`${count("created")} made, ${count("changed")} changed, ${count("refused")} refused, ${count("skipped")} skipped.` + (offPage.size ? ` Some products hold public fields that are not on their page (${[...offPage].join(", ")}); send them in publicFields to show them.` : ""),
|
|
895
918
|
...refusals
|
|
896
919
|
].join("\n"),
|
|
897
920
|
outcomes
|
|
@@ -949,7 +972,7 @@ function registerCatalogMigrationTools(server, client) {
|
|
|
949
972
|
"get_category_by_ref",
|
|
950
973
|
{
|
|
951
974
|
title: "Find a category by its reference",
|
|
952
|
-
description: 'The category upsert_category recorded with this `source` and `externalRef` -- the id of a series in your own system -- also after its address was changed in the app. A published category unless you pass `status`: "draft" finds a concept or scheduled one, "all" any (needs
|
|
975
|
+
description: 'The category upsert_category recorded with this `source` and `externalRef` -- the id of a series in your own system -- also after its address was changed in the app. A published category unless you pass `status`: "draft" finds a concept or scheduled one, "all" any (needs a server key).',
|
|
953
976
|
inputSchema: {
|
|
954
977
|
source: CatalogSourceSchema,
|
|
955
978
|
externalRef: CatalogExternalRefSchema,
|
|
@@ -1061,7 +1084,7 @@ function registerCatalogWriteTools(server, client) {
|
|
|
1061
1084
|
);
|
|
1062
1085
|
const verb = product.created ? "Made" : "Changed";
|
|
1063
1086
|
return answer(
|
|
1064
|
-
`${verb} ${product.name} (${product.slug ?? "no address"}), ${product.availability}; ${product.variants.length} variants; pictures ${product.images}
|
|
1087
|
+
`${verb} ${product.name} (${product.slug ?? "no address"}), ${product.availability}; ${product.variants.length} variants; pictures ${product.images}.` + fieldsNotOnPageNote(product.fieldsNotOnPage),
|
|
1065
1088
|
product
|
|
1066
1089
|
);
|
|
1067
1090
|
} catch (error) {
|