@wix/mcp 1.0.82 → 1.0.84
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 +3 -1
- package/build/bin-standalone.js +836 -2
- package/build/cjs/index.cjs +1468 -173
- package/build/cjs/toolkit.cjs +838 -2
- package/build/dts/bin.d.ts +1 -1
- package/build/dts/bin.js +13 -1
- package/build/dts/config/default-config.d.ts +1 -0
- package/build/dts/config/default-config.js +28 -1
- package/build/dts/config/param-descriptions.d.ts +21 -0
- package/build/dts/config/param-descriptions.js +21 -0
- package/build/dts/docs/get-to-know.js +17 -1
- package/build/dts/graphql/constants.d.ts +8 -0
- package/build/dts/graphql/constants.js +11 -0
- package/build/dts/graphql/index.d.ts +30 -0
- package/build/dts/graphql/index.js +259 -0
- package/build/dts/graphql/introspection.d.ts +106 -0
- package/build/dts/graphql/introspection.js +282 -0
- package/build/dts/graphql/search.d.ts +25 -0
- package/build/dts/graphql/search.js +277 -0
- package/build/dts/index.d.ts +2 -1
- package/build/dts/index.js +3 -1
- package/build/dts/site-widget-tools/suggested-domain-tool/index.js +21 -8
- package/build/dts/toolkit-entry.d.cts +12 -0
- package/build/dts/toolkit-entry.d.ts +12 -0
- package/build/dts/toolkit-entry.js +4 -0
- package/build/dts/toolkit.js +14 -0
- package/build/dts/wixel/wixel-session-tools.d.ts +43 -0
- package/build/dts/wixel/wixel-session-tools.js +701 -198
- package/build/esm/index.js +1460 -173
- package/build/esm/toolkit.js +833 -2
- package/package.json +2 -2
package/build/bin-standalone.js
CHANGED
|
@@ -72384,6 +72384,32 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
72384
72384
|
|
|
72385
72385
|
Call this when the user provides a specific template by \`metaSiteId\` or \`templateId\`. Use this only when the user gives a valid \`metaSiteId\` or \`templateId\` directly. To browse or choose a template first, call \`SearchSiteTemplates\`.
|
|
72386
72386
|
|
|
72387
|
+
`,
|
|
72388
|
+
SearchWixGraphQLOperations: dedent_default`
|
|
72389
|
+
Discover Wix GraphQL operations by intent (ranked keyword search over the root fields). Wix exposes ONE GraphQL schema over all Wix public APIs (Developer Preview) at POST https://www.wixapis.com/graphql/alpha: about 100 query roots and 580 mutation roots named <app><Service><Version><Verb/Entity>, e.g. \`storesProductsV3V3Products\`, \`crmContactsV4Contacts\`, \`crmContactsV4CreateContact\`, \`ecomOrdersV1SearchOrders\`.
|
|
72390
|
+
|
|
72391
|
+
GraphQL flow (3 steps, keep to it): SearchWixGraphQLOperations (find the operation) -> InspectWixGraphQLSchema (read its exact input and return types) -> ExecuteWixGraphQL (run it). Never write an operation or field name you did not see in one of these results.
|
|
72392
|
+
|
|
72393
|
+
When to prefer GraphQL over the REST tools:
|
|
72394
|
+
- Reading several related entities in ONE round-trip: related entities are resolved fields (product -> inventoryItems / collections, order -> fulfillments, booking -> contact), and independent root fields (products + contacts + orders) can share one request.
|
|
72395
|
+
- Picking exactly the fields you need, so responses stay small.
|
|
72396
|
+
- Answering "how many / list / show" questions: a query root plus \`pageInfo\` (\`hasNext\`, and \`total\` where the API provides it).
|
|
72397
|
+
Prefer the REST tools when a recipe or code example already gives you the exact REST call, for uploads/media, or when the GraphQL schema has no matching operation.
|
|
72398
|
+
|
|
72399
|
+
Conventions: query roots take \`queryInput\`; list roots (plural names) take \`queryInput: { query: { paging | cursorPaging, filter, sort } }\` and return \`{ items, pageInfo }\`; singular roots take the entity id. \`filter\`/\`sort\` are a JSON scalar in REST filter syntax (e.g. {"info.name.first": {"$eq": "Ada"}}) — pass them as variables. Mutations take \`input\`; a few read operations (search/list) are exposed as mutations, so search kind "all" unless you are sure.
|
|
72400
|
+
Results list \`kind name(arg: InputType): ReturnType\` plus a one-line description; pass the chosen name to InspectWixGraphQLSchema next.
|
|
72401
|
+
`,
|
|
72402
|
+
InspectWixGraphQLSchema: dedent_default`
|
|
72403
|
+
Read the exact Wix GraphQL schema for an operation and its types, as compact SDL with field descriptions. Pass \`operation\` (a root query/mutation name from SearchWixGraphQLOperations) to get its arguments, input type and return type expanded \`depth\` levels (default 2). Types that were referenced but not expanded are listed at the end — pass the ones you need in \`typeNames\` (optionally together with \`operation\`) instead of raising depth for everything.
|
|
72404
|
+
Use it before every ExecuteWixGraphQL call that introduces a new operation or new fields: read the INPUT type before writing a mutation or filter, and the RETURN type before writing the selection set. GraphQL rejects unknown fields and arguments, and every object-typed field needs its own selection set. \`JSON\` scalar fields (filter, sort, free-form data) accept any JSON — pass them as variables. Never guess a type or field name; if a validation error names a field, come back here.
|
|
72405
|
+
`,
|
|
72406
|
+
ExecuteWixGraphQL: dedent_default`
|
|
72407
|
+
Execute a GraphQL query or mutation against the Wix GraphQL API (POST https://www.wixapis.com/graphql/alpha) with the user's credentials. Authentication is injected — never add Authorization or site/account headers.
|
|
72408
|
+
Site targeting: site-level operations (anything that reads or writes inside a site: products, inventory, orders, contacts, bookings, CMS data, blog, members ...) require \`siteId\`; resolve it with GetSiteContext or ListWixSites first. Use \`scope: "account"\` only for account-level operations.
|
|
72409
|
+
Grounding: every root field, argument and selected field must come from SearchWixGraphQLOperations / InspectWixGraphQLSchema results in this conversation — never from memory. If the response says "Cannot query field" or "Unknown argument", re-inspect the schema and fix the name; do not retry with another guess.
|
|
72410
|
+
Compose: put independent reads in one document as several root fields, and use related-entity fields (e.g. \`inventoryItems\`, \`collections\`, \`fulfillments\`) instead of extra round-trips. Select only the fields you need; every object field needs a selection set. Variables: use them for \`JSON\` scalars (filter, sort) and plain scalars (String, Int, ID), declared in the document (e.g. \`query Q($f: JSON) { ... filter: $f ... }\`). Write mutation inputs and any object that contains an enum INLINE as GraphQL literals with enum values unquoted (e.g. \`input: { product: { name: "Mug", productType: PHYSICAL } }\`) — this endpoint fails with 503 when an enum value arrives inside an object-typed variable. For lists page with \`cursorPaging\` or \`paging\` exactly as the input type declares (never the other one), and request \`pageInfo { hasNext total }\`; \`total\` is only populated with offset \`paging\`, so for a cursor-paged list count by walking pages or use a count operation.
|
|
72411
|
+
Results: each root field fails independently — \`errors[].path\` names the failing field and \`errors[].extensions.message\` the cause (e.g. PERMISSION_DENIED); \`data\` still holds the fields that succeeded. Mutations return the created/updated entity — read the IDs from it and do not re-query to verify. Large results are truncated: narrow the selection or page size instead of re-running.
|
|
72412
|
+
Set \`hasMutations: true\` for any document containing a mutation root field.
|
|
72387
72413
|
`,
|
|
72388
72414
|
CreateWixBusinessGuide: dedent_default`
|
|
72389
72415
|
Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
|
|
@@ -73072,6 +73098,27 @@ var paramDescriptions = {
|
|
|
73072
73098
|
mimeType: 'MIME type of the image (e.g. "image/jpeg", "image/png", "image/webp"). Required when imageBase64 does not include a data URL prefix.',
|
|
73073
73099
|
displayName: "Optional display name for the file in Media Manager. If not provided, uses the original filename."
|
|
73074
73100
|
},
|
|
73101
|
+
SearchWixGraphQLOperations: {
|
|
73102
|
+
query: 'Free-text intent or entity, e.g. "products with inventory", "create contact", "orders", "blog posts", "booking services". Operation names encode app + service + version + verb/entity (e.g. storesProductsV3V3Products, crmContactsV4CreateContact), so a noun plus a verb (query/get/create/update/delete) ranks best. You may also pass an exact operation name.',
|
|
73103
|
+
kind: 'Restrict to "query" (reads), "mutation" (writes \u2014 note a few reads such as search/list calls are exposed as mutations), or "all" (default).',
|
|
73104
|
+
maxResults: "Maximum number of operations to return (default 10, max 50).",
|
|
73105
|
+
reason: "One sentence describing the original user request and why you are looking for a GraphQL operation."
|
|
73106
|
+
},
|
|
73107
|
+
InspectWixGraphQLSchema: {
|
|
73108
|
+
operation: 'Exact root query or mutation name from SearchWixGraphQLOperations (e.g. "crmContactsV4Contacts"). Returns its arguments, input type and return type as compact SDL, expanded `depth` levels.',
|
|
73109
|
+
typeNames: 'Type names to expand, e.g. ones listed as "Not expanded" in a previous result, or a nested input/object/enum type you need before writing the request. Can be combined with `operation`.',
|
|
73110
|
+
depth: "How many levels of referenced types to expand (1-4, default 2). Level 1 is the given types, level 2 the types their fields reference, and so on. Keep it small and expand specific types with typeNames instead of dumping everything.",
|
|
73111
|
+
reason: "One sentence describing the original user request and why you need this part of the GraphQL schema."
|
|
73112
|
+
},
|
|
73113
|
+
ExecuteWixGraphQL: {
|
|
73114
|
+
query: "The GraphQL document (a query or a mutation). Every root field, argument and selected field MUST come from SearchWixGraphQLOperations / InspectWixGraphQLSchema results in this conversation, never from memory. Declare variables for filters, sort and large inputs, e.g. `query Q($f: JSON) { crmContactsV4Contacts(queryInput: { query: { filter: $f, paging: { limit: 50 } } }) { items { id } pageInfo { hasNext } } }`. Object-typed fields need a selection set. You can put several independent root fields in one document.",
|
|
73115
|
+
variables: 'Variables object for the document, as a JSON object (not a string). Use it for JSON-scalar filters/sort (REST filter syntax, e.g. {"info.name.first": {"$eq": "Ada"}}) and plain scalars. Do NOT pass object inputs that contain enum values (product types, statuses, registration types ...) through variables \u2014 write those inline in the document with the enum unquoted; the endpoint returns 503 otherwise.',
|
|
73116
|
+
operationName: "Operation to run when the document defines more than one named operation.",
|
|
73117
|
+
siteId: "The Wix site to run against. Required for site-level operations (anything reading or writing inside a site \u2014 products, orders, contacts, bookings, CMS, blog, members ...). Resolve it with GetSiteContext or ListWixSites; never pass a guessed value.",
|
|
73118
|
+
scope: '"site" (default when siteId is given) runs with a site-scoped credential; "account" runs account-level operations (sites, account settings) and ignores siteId.',
|
|
73119
|
+
hasMutations: "Whether this document creates, updates, deletes or otherwise mutates Wix data. True for any mutation root field (in any operation of the document) even when the intent is inspection; false only for pure queries. This flag alone decides whether a transient server error may be retried, so never mark a document that writes as false.",
|
|
73120
|
+
reason: "One sentence explaining the original user request and why you are running this GraphQL operation to complete it."
|
|
73121
|
+
},
|
|
73075
73122
|
RunWixCliCommand: {
|
|
73076
73123
|
appPath: "The full path to the Wix CLI app to run the command on.",
|
|
73077
73124
|
extensionType: "The type of extension to generate. You MUST provide this for commands that generate extensions.",
|
|
@@ -73306,7 +73353,16 @@ ${skillsContent}`
|
|
|
73306
73353
|
] : await Promise.all(
|
|
73307
73354
|
baseDocs.filter((doc) => doc.url === RECIPES_INDEX_URL).map(fetchDoc)
|
|
73308
73355
|
);
|
|
73309
|
-
const
|
|
73356
|
+
const recipeNotes = !skillsContent?.includes("Dashboard Navigation") ? [] : [
|
|
73357
|
+
await fetchDoc({
|
|
73358
|
+
action: "text",
|
|
73359
|
+
title: "Recipe Notes",
|
|
73360
|
+
content: dedent_default`
|
|
73361
|
+
- **Navigation recipes** (the *Dashboard Navigation* family) build direct manage.wix.com URLs. If your environment provides dedicated navigation tools, use those to perform the actual navigation — opening a recipe-built URL forces a full page refresh. Use the recipe URLs for composing a link to hand the user, or for navigating when no dedicated tool exists.
|
|
73362
|
+
`
|
|
73363
|
+
})
|
|
73364
|
+
];
|
|
73365
|
+
const contents = [...baseContents, ...recipesContents, ...recipeNotes];
|
|
73310
73366
|
const dynamicContext = await dynamicContextPromise;
|
|
73311
73367
|
const parts = [
|
|
73312
73368
|
...contents.filter(
|
|
@@ -86592,6 +86648,775 @@ function createAccountFileAuthStrategy({
|
|
|
86592
86648
|
};
|
|
86593
86649
|
}
|
|
86594
86650
|
|
|
86651
|
+
// src/code-mode/truncate-response.ts
|
|
86652
|
+
var CHARS_PER_TOKEN = 4;
|
|
86653
|
+
var DEFAULT_MAX_TOKENS = 6e3;
|
|
86654
|
+
var DEFAULT_MAX_CHARS = DEFAULT_MAX_TOKENS * CHARS_PER_TOKEN;
|
|
86655
|
+
function truncateResponse(content, maxChars = DEFAULT_MAX_CHARS) {
|
|
86656
|
+
const text = typeof content === "string" ? content : JSON.stringify(content);
|
|
86657
|
+
const effectiveMax = Math.min(maxChars, MAX_ALLOWED_RESPONSE_CHARS);
|
|
86658
|
+
if (!text || text.length <= effectiveMax) {
|
|
86659
|
+
return text ?? "";
|
|
86660
|
+
}
|
|
86661
|
+
const truncated = text.slice(0, effectiveMax);
|
|
86662
|
+
const estimatedTokens = Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
86663
|
+
const limitTokens = Math.ceil(effectiveMax / CHARS_PER_TOKEN);
|
|
86664
|
+
return `${truncated}
|
|
86665
|
+
|
|
86666
|
+
--- TRUNCATED ---
|
|
86667
|
+
Response was ~${estimatedTokens.toLocaleString()} tokens (limit: ${limitTokens.toLocaleString()}). Refine your code to return less data.`;
|
|
86668
|
+
}
|
|
86669
|
+
|
|
86670
|
+
// src/graphql/constants.ts
|
|
86671
|
+
var WIX_GRAPHQL_ENDPOINT = "https://www.wixapis.com/graphql/alpha";
|
|
86672
|
+
var SEARCH_GRAPHQL_TOOL_NAME = "SearchWixGraphQLOperations";
|
|
86673
|
+
var INSPECT_GRAPHQL_TOOL_NAME = "InspectWixGraphQLSchema";
|
|
86674
|
+
var EXECUTE_GRAPHQL_TOOL_NAME = "ExecuteWixGraphQL";
|
|
86675
|
+
|
|
86676
|
+
// src/graphql/introspection.ts
|
|
86677
|
+
var TYPE_REF_FRAGMENT = `
|
|
86678
|
+
fragment TR on __Type { kind name ofType { kind name ofType { kind name ofType { kind name } } } }`;
|
|
86679
|
+
var ROOT_OPERATIONS_QUERY = `{
|
|
86680
|
+
__schema {
|
|
86681
|
+
queryType { fields { name description args { name type { ...TR } } type { ...TR } } }
|
|
86682
|
+
mutationType { fields { name description args { name type { ...TR } } type { ...TR } } }
|
|
86683
|
+
}
|
|
86684
|
+
}${TYPE_REF_FRAGMENT}`;
|
|
86685
|
+
var TYPE_DETAILS_FRAGMENT = `
|
|
86686
|
+
fragment TD on __Type {
|
|
86687
|
+
kind name description
|
|
86688
|
+
fields { name description type { ...TR } args { name type { ...TR } } }
|
|
86689
|
+
inputFields { name description type { ...TR } defaultValue }
|
|
86690
|
+
enumValues { name description }
|
|
86691
|
+
possibleTypes { name }
|
|
86692
|
+
}`;
|
|
86693
|
+
var ROOT_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
86694
|
+
var rootCache;
|
|
86695
|
+
var typeCache = /* @__PURE__ */ new Map();
|
|
86696
|
+
var RETRY_DELAYS_MS = [800, 2e3, 4e3];
|
|
86697
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
86698
|
+
async function fetchWithRetry(fetchImpl, url, init2, delays = RETRY_DELAYS_MS) {
|
|
86699
|
+
let attempt = 0;
|
|
86700
|
+
for (; ; ) {
|
|
86701
|
+
const resp = await fetchImpl(url, init2);
|
|
86702
|
+
if (resp.status < 500 || attempt >= delays.length) return resp;
|
|
86703
|
+
await sleep(delays[attempt++]);
|
|
86704
|
+
}
|
|
86705
|
+
}
|
|
86706
|
+
async function postIntrospection(query, options) {
|
|
86707
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
86708
|
+
const endpoint = options.endpoint ?? WIX_GRAPHQL_ENDPOINT;
|
|
86709
|
+
const resp = await fetchWithRetry(
|
|
86710
|
+
fetchImpl,
|
|
86711
|
+
endpoint,
|
|
86712
|
+
{
|
|
86713
|
+
method: "POST",
|
|
86714
|
+
headers: {
|
|
86715
|
+
"Content-Type": "application/json",
|
|
86716
|
+
Accept: "application/json"
|
|
86717
|
+
},
|
|
86718
|
+
body: JSON.stringify({ query })
|
|
86719
|
+
},
|
|
86720
|
+
options.retryDelaysMs
|
|
86721
|
+
);
|
|
86722
|
+
const text = await resp.text();
|
|
86723
|
+
if (!resp.ok) {
|
|
86724
|
+
const body = /^\s*<(!doctype|html)/i.test(text) ? "(HTML error page \u2014 the endpoint is temporarily unavailable; try again in a moment)" : text.slice(0, 500);
|
|
86725
|
+
throw new Error(
|
|
86726
|
+
`GraphQL introspection failed (${resp.status} ${resp.statusText}): ${body}`
|
|
86727
|
+
);
|
|
86728
|
+
}
|
|
86729
|
+
const json = JSON.parse(text);
|
|
86730
|
+
if (json.errors?.length && !json.data) {
|
|
86731
|
+
throw new Error(
|
|
86732
|
+
`GraphQL introspection returned errors: ${JSON.stringify(json.errors).slice(0, 500)}`
|
|
86733
|
+
);
|
|
86734
|
+
}
|
|
86735
|
+
return json.data;
|
|
86736
|
+
}
|
|
86737
|
+
function getRootOperations(options = {}) {
|
|
86738
|
+
const now = Date.now();
|
|
86739
|
+
if (rootCache && now - rootCache.fetchedAt < ROOT_CACHE_TTL_MS) {
|
|
86740
|
+
return rootCache.promise;
|
|
86741
|
+
}
|
|
86742
|
+
const promise = postIntrospection(ROOT_OPERATIONS_QUERY, options).then(
|
|
86743
|
+
(data) => {
|
|
86744
|
+
const toOps = (fields, kind) => (fields ?? []).map((f) => ({
|
|
86745
|
+
kind,
|
|
86746
|
+
name: f.name,
|
|
86747
|
+
description: f.description ?? "",
|
|
86748
|
+
args: f.args ?? [],
|
|
86749
|
+
type: f.type
|
|
86750
|
+
}));
|
|
86751
|
+
return [
|
|
86752
|
+
...toOps(data?.__schema?.queryType?.fields, "query"),
|
|
86753
|
+
...toOps(data?.__schema?.mutationType?.fields, "mutation")
|
|
86754
|
+
];
|
|
86755
|
+
}
|
|
86756
|
+
);
|
|
86757
|
+
promise.catch(() => {
|
|
86758
|
+
if (rootCache?.promise === promise) rootCache = void 0;
|
|
86759
|
+
});
|
|
86760
|
+
rootCache = { promise, fetchedAt: now };
|
|
86761
|
+
return promise;
|
|
86762
|
+
}
|
|
86763
|
+
var GRAPHQL_NAME = /^[_A-Za-z][_0-9A-Za-z]*$/;
|
|
86764
|
+
var TYPE_BATCH_SIZE = 10;
|
|
86765
|
+
async function getTypes(names, options = {}) {
|
|
86766
|
+
const unique = [...new Set(names)].filter((n2) => GRAPHQL_NAME.test(n2));
|
|
86767
|
+
const missing = unique.filter((n2) => !typeCache.has(n2));
|
|
86768
|
+
for (let start = 0; start < missing.length; start += TYPE_BATCH_SIZE) {
|
|
86769
|
+
const chunk = missing.slice(start, start + TYPE_BATCH_SIZE);
|
|
86770
|
+
const selections = chunk.map((n2, i2) => `t${i2}: __type(name: "${n2}") { ...TD }`).join("\n");
|
|
86771
|
+
const query = `{
|
|
86772
|
+
${selections}
|
|
86773
|
+
}${TYPE_DETAILS_FRAGMENT}${TYPE_REF_FRAGMENT}`;
|
|
86774
|
+
const batch = postIntrospection(query, options);
|
|
86775
|
+
chunk.forEach((n2, i2) => {
|
|
86776
|
+
const p = batch.then(
|
|
86777
|
+
(data) => data?.[`t${i2}`] ?? null
|
|
86778
|
+
);
|
|
86779
|
+
p.catch(() => {
|
|
86780
|
+
if (typeCache.get(n2) === p) typeCache.delete(n2);
|
|
86781
|
+
});
|
|
86782
|
+
typeCache.set(n2, p);
|
|
86783
|
+
});
|
|
86784
|
+
await batch.catch(() => void 0);
|
|
86785
|
+
}
|
|
86786
|
+
const result = /* @__PURE__ */ new Map();
|
|
86787
|
+
await Promise.all(
|
|
86788
|
+
unique.map(async (n2) => {
|
|
86789
|
+
result.set(n2, await typeCache.get(n2));
|
|
86790
|
+
})
|
|
86791
|
+
);
|
|
86792
|
+
return result;
|
|
86793
|
+
}
|
|
86794
|
+
function renderTypeRef(ref) {
|
|
86795
|
+
if (!ref) return "Unknown";
|
|
86796
|
+
if (ref.kind === "NON_NULL") return `${renderTypeRef(ref.ofType)}!`;
|
|
86797
|
+
if (ref.kind === "LIST") return `[${renderTypeRef(ref.ofType)}]`;
|
|
86798
|
+
return ref.name ?? "Unknown";
|
|
86799
|
+
}
|
|
86800
|
+
function namedType(ref) {
|
|
86801
|
+
let cur = ref;
|
|
86802
|
+
while (cur && (cur.kind === "NON_NULL" || cur.kind === "LIST")) {
|
|
86803
|
+
cur = cur.ofType ?? null;
|
|
86804
|
+
}
|
|
86805
|
+
return cur?.name ?? null;
|
|
86806
|
+
}
|
|
86807
|
+
var BUILTIN_SCALARS = /* @__PURE__ */ new Set([
|
|
86808
|
+
"String",
|
|
86809
|
+
"Int",
|
|
86810
|
+
"Float",
|
|
86811
|
+
"Boolean",
|
|
86812
|
+
"ID",
|
|
86813
|
+
"JSON"
|
|
86814
|
+
]);
|
|
86815
|
+
function isBuiltinScalar(name) {
|
|
86816
|
+
return !!name && BUILTIN_SCALARS.has(name);
|
|
86817
|
+
}
|
|
86818
|
+
function oneLine(text, max) {
|
|
86819
|
+
if (!text) return "";
|
|
86820
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
86821
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
86822
|
+
}
|
|
86823
|
+
function renderField(field, isInput) {
|
|
86824
|
+
const args = !isInput && field.args?.length ? `(${field.args.map((a2) => `${a2.name}: ${renderTypeRef(a2.type)}`).join(", ")})` : "";
|
|
86825
|
+
const def = isInput && field.defaultValue != null ? ` = ${field.defaultValue}` : "";
|
|
86826
|
+
const comment = field.description ? ` # ${oneLine(field.description, 140)}` : "";
|
|
86827
|
+
return ` ${field.name}${args}: ${renderTypeRef(field.type)}${def}${comment}`;
|
|
86828
|
+
}
|
|
86829
|
+
function renderType(type) {
|
|
86830
|
+
const references = /* @__PURE__ */ new Set();
|
|
86831
|
+
const collect = (ref) => {
|
|
86832
|
+
const n2 = namedType(ref);
|
|
86833
|
+
if (n2 && !isBuiltinScalar(n2) && n2 !== type.name) references.add(n2);
|
|
86834
|
+
};
|
|
86835
|
+
const header = type.description ? `# ${oneLine(type.description, 200)}
|
|
86836
|
+
` : "";
|
|
86837
|
+
let body;
|
|
86838
|
+
switch (type.kind) {
|
|
86839
|
+
case "ENUM":
|
|
86840
|
+
body = `enum ${type.name} {
|
|
86841
|
+
${(type.enumValues ?? []).map(
|
|
86842
|
+
(v) => ` ${v.name}${v.description ? ` # ${oneLine(v.description, 100)}` : ""}`
|
|
86843
|
+
).join("\n")}
|
|
86844
|
+
}`;
|
|
86845
|
+
break;
|
|
86846
|
+
case "INPUT_OBJECT":
|
|
86847
|
+
(type.inputFields ?? []).forEach((f) => collect(f.type));
|
|
86848
|
+
body = `input ${type.name} {
|
|
86849
|
+
${(type.inputFields ?? []).map((f) => renderField(f, true)).join("\n")}
|
|
86850
|
+
}`;
|
|
86851
|
+
break;
|
|
86852
|
+
case "UNION":
|
|
86853
|
+
(type.possibleTypes ?? []).forEach((p) => references.add(p.name));
|
|
86854
|
+
body = `union ${type.name} = ${(type.possibleTypes ?? []).map((p) => p.name).join(" | ")}`;
|
|
86855
|
+
break;
|
|
86856
|
+
case "SCALAR":
|
|
86857
|
+
body = `scalar ${type.name}`;
|
|
86858
|
+
break;
|
|
86859
|
+
default: {
|
|
86860
|
+
(type.fields ?? []).forEach((f) => {
|
|
86861
|
+
collect(f.type);
|
|
86862
|
+
f.args?.forEach((a2) => collect(a2.type));
|
|
86863
|
+
});
|
|
86864
|
+
const keyword = type.kind === "INTERFACE" ? "interface" : "type";
|
|
86865
|
+
body = `${keyword} ${type.name} {
|
|
86866
|
+
${(type.fields ?? []).map((f) => renderField(f, false)).join("\n")}
|
|
86867
|
+
}`;
|
|
86868
|
+
}
|
|
86869
|
+
}
|
|
86870
|
+
return { text: header + body, references: [...references] };
|
|
86871
|
+
}
|
|
86872
|
+
function renderOperationSignature(op) {
|
|
86873
|
+
const args = op.args.map((a2) => `${a2.name}: ${renderTypeRef(a2.type)}`).join(", ");
|
|
86874
|
+
return `${op.kind === "query" ? "Query" : "Mutation"}.${op.name}(${args}): ${renderTypeRef(op.type)}`;
|
|
86875
|
+
}
|
|
86876
|
+
async function expandTypes(rootTypeNames, options = {}) {
|
|
86877
|
+
const depth = Math.max(1, Math.min(options.depth ?? 2, 5));
|
|
86878
|
+
const maxTypes = options.maxTypes ?? 40;
|
|
86879
|
+
const rendered = [];
|
|
86880
|
+
const unknown2 = [];
|
|
86881
|
+
const seen = /* @__PURE__ */ new Set();
|
|
86882
|
+
let frontier = [...new Set(rootTypeNames)].filter((n2) => !isBuiltinScalar(n2));
|
|
86883
|
+
let level = 1;
|
|
86884
|
+
let overflow = [];
|
|
86885
|
+
while (frontier.length > 0 && level <= depth) {
|
|
86886
|
+
const room = maxTypes - seen.size;
|
|
86887
|
+
if (room <= 0) {
|
|
86888
|
+
overflow = frontier;
|
|
86889
|
+
break;
|
|
86890
|
+
}
|
|
86891
|
+
const batch = frontier.slice(0, room);
|
|
86892
|
+
overflow = frontier.slice(room);
|
|
86893
|
+
batch.forEach((n2) => seen.add(n2));
|
|
86894
|
+
const types = await getTypes(batch, options);
|
|
86895
|
+
const next = /* @__PURE__ */ new Set();
|
|
86896
|
+
for (const name of batch) {
|
|
86897
|
+
const type = types.get(name);
|
|
86898
|
+
if (!type) {
|
|
86899
|
+
unknown2.push(name);
|
|
86900
|
+
continue;
|
|
86901
|
+
}
|
|
86902
|
+
const { text, references } = renderType(type);
|
|
86903
|
+
rendered.push(text);
|
|
86904
|
+
references.forEach((r) => {
|
|
86905
|
+
if (!seen.has(r)) next.add(r);
|
|
86906
|
+
});
|
|
86907
|
+
}
|
|
86908
|
+
frontier = [...next];
|
|
86909
|
+
level++;
|
|
86910
|
+
}
|
|
86911
|
+
const unexpanded = [.../* @__PURE__ */ new Set([...overflow, ...frontier])].filter(
|
|
86912
|
+
(n2) => !seen.has(n2) && !isBuiltinScalar(n2)
|
|
86913
|
+
);
|
|
86914
|
+
return { rendered, unexpanded, unknown: unknown2 };
|
|
86915
|
+
}
|
|
86916
|
+
|
|
86917
|
+
// src/graphql/search.ts
|
|
86918
|
+
function tokenize(text) {
|
|
86919
|
+
return text.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-zA-Z])([0-9])/g, "$1 $2").replace(/([0-9])([a-zA-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 0);
|
|
86920
|
+
}
|
|
86921
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
86922
|
+
"a",
|
|
86923
|
+
"an",
|
|
86924
|
+
"the",
|
|
86925
|
+
"of",
|
|
86926
|
+
"for",
|
|
86927
|
+
"to",
|
|
86928
|
+
"in",
|
|
86929
|
+
"on",
|
|
86930
|
+
"with",
|
|
86931
|
+
"and",
|
|
86932
|
+
"or",
|
|
86933
|
+
"by",
|
|
86934
|
+
"all",
|
|
86935
|
+
"my",
|
|
86936
|
+
"me",
|
|
86937
|
+
"i",
|
|
86938
|
+
"from",
|
|
86939
|
+
"is",
|
|
86940
|
+
"are",
|
|
86941
|
+
"be",
|
|
86942
|
+
"it",
|
|
86943
|
+
"this",
|
|
86944
|
+
"that",
|
|
86945
|
+
"wix",
|
|
86946
|
+
"api",
|
|
86947
|
+
"graphql"
|
|
86948
|
+
]);
|
|
86949
|
+
function stem(token) {
|
|
86950
|
+
if (token.length > 4 && token.endsWith("ies"))
|
|
86951
|
+
return `${token.slice(0, -3)}y`;
|
|
86952
|
+
if (token.length > 3 && token.endsWith("es") && !token.endsWith("ses"))
|
|
86953
|
+
return token.slice(0, -1);
|
|
86954
|
+
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss"))
|
|
86955
|
+
return token.slice(0, -1);
|
|
86956
|
+
return token;
|
|
86957
|
+
}
|
|
86958
|
+
var VERB_SYNONYMS = {
|
|
86959
|
+
list: ["query", "list", "search"],
|
|
86960
|
+
find: ["query", "search", "get"],
|
|
86961
|
+
fetch: ["get", "query"],
|
|
86962
|
+
read: ["get", "query"],
|
|
86963
|
+
show: ["get", "query"],
|
|
86964
|
+
count: ["count", "query"],
|
|
86965
|
+
add: ["create", "add"],
|
|
86966
|
+
new: ["create"],
|
|
86967
|
+
insert: ["create"],
|
|
86968
|
+
edit: ["update"],
|
|
86969
|
+
change: ["update"],
|
|
86970
|
+
modify: ["update"],
|
|
86971
|
+
set: ["update"],
|
|
86972
|
+
remove: ["delete", "remove"],
|
|
86973
|
+
destroy: ["delete"],
|
|
86974
|
+
cancel: ["cancel", "delete"],
|
|
86975
|
+
customer: ["contact", "member"],
|
|
86976
|
+
client: ["contact", "member"],
|
|
86977
|
+
item: ["product", "item"],
|
|
86978
|
+
inventory: ["inventory", "stock"],
|
|
86979
|
+
stock: ["inventory", "stock"],
|
|
86980
|
+
post: ["post", "blog"],
|
|
86981
|
+
article: ["post", "blog"],
|
|
86982
|
+
appointment: ["booking", "session"],
|
|
86983
|
+
collection: ["collection", "category", "data"],
|
|
86984
|
+
cms: ["data", "collection"],
|
|
86985
|
+
store: ["stores", "store"]
|
|
86986
|
+
};
|
|
86987
|
+
function expandQueryTokens(tokens) {
|
|
86988
|
+
const out = /* @__PURE__ */ new Set();
|
|
86989
|
+
for (const raw of tokens) {
|
|
86990
|
+
if (STOP_WORDS.has(raw)) continue;
|
|
86991
|
+
const s = stem(raw);
|
|
86992
|
+
out.add(s);
|
|
86993
|
+
for (const syn of VERB_SYNONYMS[raw] ?? VERB_SYNONYMS[s] ?? []) {
|
|
86994
|
+
out.add(stem(syn));
|
|
86995
|
+
}
|
|
86996
|
+
}
|
|
86997
|
+
return out;
|
|
86998
|
+
}
|
|
86999
|
+
var READ_VERBS = /* @__PURE__ */ new Set([
|
|
87000
|
+
"list",
|
|
87001
|
+
"get",
|
|
87002
|
+
"query",
|
|
87003
|
+
"find",
|
|
87004
|
+
"fetch",
|
|
87005
|
+
"read",
|
|
87006
|
+
"show",
|
|
87007
|
+
"retrieve",
|
|
87008
|
+
"search",
|
|
87009
|
+
"count",
|
|
87010
|
+
"view",
|
|
87011
|
+
"see"
|
|
87012
|
+
]);
|
|
87013
|
+
var WRITE_VERBS = /* @__PURE__ */ new Set([
|
|
87014
|
+
"create",
|
|
87015
|
+
"add",
|
|
87016
|
+
"new",
|
|
87017
|
+
"insert",
|
|
87018
|
+
"update",
|
|
87019
|
+
"edit",
|
|
87020
|
+
"change",
|
|
87021
|
+
"modify",
|
|
87022
|
+
"set",
|
|
87023
|
+
"delete",
|
|
87024
|
+
"remove",
|
|
87025
|
+
"destroy",
|
|
87026
|
+
"cancel",
|
|
87027
|
+
"bulk",
|
|
87028
|
+
"merge",
|
|
87029
|
+
"publish",
|
|
87030
|
+
"send"
|
|
87031
|
+
]);
|
|
87032
|
+
var isVersionToken = (t) => t === "v" || /^\d+$/.test(t);
|
|
87033
|
+
function stripMarkdown(text) {
|
|
87034
|
+
return text.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/[`*_]/g, "");
|
|
87035
|
+
}
|
|
87036
|
+
function firstSentence(text, max = 220) {
|
|
87037
|
+
const flat = stripMarkdown(text).replace(/\s+/g, " ").trim();
|
|
87038
|
+
const cut = flat.search(/(?<=[.!?])\s/);
|
|
87039
|
+
const sentence = cut > 0 ? flat.slice(0, cut) : flat;
|
|
87040
|
+
return sentence.length > max ? `${sentence.slice(0, max - 1)}\u2026` : sentence;
|
|
87041
|
+
}
|
|
87042
|
+
var indexCache = /* @__PURE__ */ new WeakMap();
|
|
87043
|
+
function indexOperations(ops) {
|
|
87044
|
+
let indexed = indexCache.get(ops);
|
|
87045
|
+
if (!indexed) {
|
|
87046
|
+
indexed = ops.map((op) => ({
|
|
87047
|
+
op,
|
|
87048
|
+
nameTokens: new Set(tokenize(op.name).map(stem)),
|
|
87049
|
+
descTokens: new Set(tokenize(op.description.slice(0, 600)).map(stem))
|
|
87050
|
+
}));
|
|
87051
|
+
indexCache.set(ops, indexed);
|
|
87052
|
+
}
|
|
87053
|
+
return indexed;
|
|
87054
|
+
}
|
|
87055
|
+
function searchOperations(ops, query, options = {}) {
|
|
87056
|
+
const { kind = "all", maxResults = 10 } = options;
|
|
87057
|
+
const rawTokens = tokenize(query);
|
|
87058
|
+
const terms = expandQueryTokens(rawTokens);
|
|
87059
|
+
const originalTerms = new Set(
|
|
87060
|
+
rawTokens.filter((t) => !STOP_WORDS.has(t)).map(stem)
|
|
87061
|
+
);
|
|
87062
|
+
const exactName = query.trim();
|
|
87063
|
+
const hasWriteVerb = rawTokens.some((t) => WRITE_VERBS.has(t));
|
|
87064
|
+
const nounTokens = rawTokens.filter(
|
|
87065
|
+
(t) => !STOP_WORDS.has(t) && !READ_VERBS.has(t) && !WRITE_VERBS.has(t)
|
|
87066
|
+
);
|
|
87067
|
+
const pluralIntent = nounTokens.length > 0 && nounTokens.some((t) => stem(t) !== t);
|
|
87068
|
+
const readIntent = !hasWriteVerb;
|
|
87069
|
+
const matches = [];
|
|
87070
|
+
for (const { op, nameTokens, descTokens } of indexOperations(ops)) {
|
|
87071
|
+
if (kind !== "all" && op.kind !== kind) continue;
|
|
87072
|
+
let score = 0;
|
|
87073
|
+
let originalHits = 0;
|
|
87074
|
+
const matchedNameTokens = /* @__PURE__ */ new Set();
|
|
87075
|
+
for (const term of terms) {
|
|
87076
|
+
const isOriginal = originalTerms.has(term);
|
|
87077
|
+
if (op.kind === "query" && readIntent && READ_VERBS.has(term)) {
|
|
87078
|
+
score += isOriginal ? 4 : 2;
|
|
87079
|
+
if (isOriginal) originalHits++;
|
|
87080
|
+
if (nameTokens.has(term)) matchedNameTokens.add(term);
|
|
87081
|
+
continue;
|
|
87082
|
+
}
|
|
87083
|
+
if (nameTokens.has(term)) {
|
|
87084
|
+
matchedNameTokens.add(term);
|
|
87085
|
+
score += isOriginal ? 4 : 2;
|
|
87086
|
+
if (isOriginal) originalHits++;
|
|
87087
|
+
continue;
|
|
87088
|
+
}
|
|
87089
|
+
let prefixHit = false;
|
|
87090
|
+
for (const nt of nameTokens) {
|
|
87091
|
+
if (term.length >= 3 && (nt.startsWith(term) || term.startsWith(nt))) {
|
|
87092
|
+
prefixHit = true;
|
|
87093
|
+
matchedNameTokens.add(nt);
|
|
87094
|
+
break;
|
|
87095
|
+
}
|
|
87096
|
+
}
|
|
87097
|
+
if (prefixHit) {
|
|
87098
|
+
score += isOriginal ? 2 : 1;
|
|
87099
|
+
if (isOriginal) originalHits++;
|
|
87100
|
+
continue;
|
|
87101
|
+
}
|
|
87102
|
+
if (descTokens.has(term)) {
|
|
87103
|
+
score += 1;
|
|
87104
|
+
if (isOriginal) originalHits++;
|
|
87105
|
+
}
|
|
87106
|
+
}
|
|
87107
|
+
if (score <= 0 && op.name !== exactName) continue;
|
|
87108
|
+
if (op.name === exactName) score += 100;
|
|
87109
|
+
if (originalTerms.size > 0 && originalHits === originalTerms.size) {
|
|
87110
|
+
score += 3;
|
|
87111
|
+
}
|
|
87112
|
+
const lastToken = tokenize(op.name).filter((t) => !isVersionToken(t)).pop();
|
|
87113
|
+
if (lastToken && nounTokens.length > 0) {
|
|
87114
|
+
const pluralOp = stem(lastToken) !== lastToken;
|
|
87115
|
+
score += pluralOp === pluralIntent ? 0.5 : -0.5;
|
|
87116
|
+
}
|
|
87117
|
+
if (/DoNotCall/i.test(op.name)) score -= 2;
|
|
87118
|
+
for (const nt of nameTokens) {
|
|
87119
|
+
if (!isVersionToken(nt) && !matchedNameTokens.has(nt)) score -= 0.3;
|
|
87120
|
+
}
|
|
87121
|
+
if (readIntent && op.kind === "mutation" && [...nameTokens].some((t) => WRITE_VERBS.has(t))) {
|
|
87122
|
+
score -= 6;
|
|
87123
|
+
}
|
|
87124
|
+
if (op.kind === "query" && readIntent) score += 1;
|
|
87125
|
+
const version3 = op.name.match(/V(\d+)/);
|
|
87126
|
+
if (version3) score += Math.min(Number(version3[1]), 5) * 0.1;
|
|
87127
|
+
if (!op.description) score -= 0.5;
|
|
87128
|
+
score -= op.name.length / 200;
|
|
87129
|
+
if (score <= 0) continue;
|
|
87130
|
+
matches.push({
|
|
87131
|
+
operation: op.name,
|
|
87132
|
+
kind: op.kind,
|
|
87133
|
+
signature: `${op.name}(${op.args.map((a2) => `${a2.name}: ${renderTypeRef(a2.type)}`).join(", ")}): ${renderTypeRef(op.type)}`,
|
|
87134
|
+
description: firstSentence(op.description),
|
|
87135
|
+
inputType: op.args[0] ? namedType(op.args[0].type) : null,
|
|
87136
|
+
returnType: namedType(op.type),
|
|
87137
|
+
score: Math.round(score * 100) / 100
|
|
87138
|
+
});
|
|
87139
|
+
}
|
|
87140
|
+
matches.sort(
|
|
87141
|
+
(a2, b) => b.score - a2.score || a2.operation.localeCompare(b.operation)
|
|
87142
|
+
);
|
|
87143
|
+
return matches.slice(0, Math.max(1, Math.min(maxResults, 50)));
|
|
87144
|
+
}
|
|
87145
|
+
|
|
87146
|
+
// src/graphql/index.ts
|
|
87147
|
+
var textResult = (text) => ({
|
|
87148
|
+
content: [{ type: "text", text }]
|
|
87149
|
+
});
|
|
87150
|
+
var errorResult = (text) => ({
|
|
87151
|
+
isError: true,
|
|
87152
|
+
content: [{ type: "text", text }]
|
|
87153
|
+
});
|
|
87154
|
+
var SCHEMA_MAX_RESPONSE_CHARS = 48e3;
|
|
87155
|
+
function addGraphqlTools(server2, options = {}) {
|
|
87156
|
+
const { authStrategy, disableTools = [], toolDescriptions } = options;
|
|
87157
|
+
const getDescription = (name) => toolDescriptions?.[name] ?? defaultToolDescriptions[name];
|
|
87158
|
+
if (!disableTools.includes(SEARCH_GRAPHQL_TOOL_NAME)) {
|
|
87159
|
+
addSearchGraphqlTool(
|
|
87160
|
+
server2,
|
|
87161
|
+
getDescription(SEARCH_GRAPHQL_TOOL_NAME),
|
|
87162
|
+
options
|
|
87163
|
+
);
|
|
87164
|
+
}
|
|
87165
|
+
if (!disableTools.includes(INSPECT_GRAPHQL_TOOL_NAME)) {
|
|
87166
|
+
addInspectGraphqlTool(
|
|
87167
|
+
server2,
|
|
87168
|
+
getDescription(INSPECT_GRAPHQL_TOOL_NAME),
|
|
87169
|
+
options
|
|
87170
|
+
);
|
|
87171
|
+
}
|
|
87172
|
+
if (authStrategy && !disableTools.includes(EXECUTE_GRAPHQL_TOOL_NAME)) {
|
|
87173
|
+
addExecuteGraphqlTool(
|
|
87174
|
+
server2,
|
|
87175
|
+
getDescription(EXECUTE_GRAPHQL_TOOL_NAME),
|
|
87176
|
+
authStrategy,
|
|
87177
|
+
options
|
|
87178
|
+
);
|
|
87179
|
+
}
|
|
87180
|
+
}
|
|
87181
|
+
function introspectionOptions(options) {
|
|
87182
|
+
return {
|
|
87183
|
+
endpoint: options.endpoint,
|
|
87184
|
+
fetchImpl: options.fetchImpl,
|
|
87185
|
+
retryDelaysMs: options.retryDelaysMs
|
|
87186
|
+
};
|
|
87187
|
+
}
|
|
87188
|
+
function addSearchGraphqlTool(server2, description, options) {
|
|
87189
|
+
const d = desc("SearchWixGraphQLOperations");
|
|
87190
|
+
server2.tool(
|
|
87191
|
+
SEARCH_GRAPHQL_TOOL_NAME,
|
|
87192
|
+
description,
|
|
87193
|
+
{
|
|
87194
|
+
query: external_exports.string().describe(d.query),
|
|
87195
|
+
kind: external_exports.enum(["query", "mutation", "all"]).optional().describe(d.kind),
|
|
87196
|
+
maxResults: external_exports.number().int().min(1).max(50).optional().describe(d.maxResults),
|
|
87197
|
+
reason: external_exports.string().describe(d.reason)
|
|
87198
|
+
},
|
|
87199
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
87200
|
+
async ({ query, kind, maxResults, reason }) => {
|
|
87201
|
+
logger2.log(
|
|
87202
|
+
`[${SEARCH_GRAPHQL_TOOL_NAME}] query="${query}" kind=${kind ?? "all"} reason="${reason}"`
|
|
87203
|
+
);
|
|
87204
|
+
try {
|
|
87205
|
+
const ops = await getRootOperations(introspectionOptions(options));
|
|
87206
|
+
const matches = searchOperations(ops, query, {
|
|
87207
|
+
kind: kind ?? "all",
|
|
87208
|
+
maxResults: maxResults ?? 10
|
|
87209
|
+
});
|
|
87210
|
+
if (matches.length === 0) {
|
|
87211
|
+
return textResult(
|
|
87212
|
+
`No GraphQL operations matched "${query}". Try other words for the entity (e.g. "contact" instead of "customer", "product" instead of "item") or the verb (query/get/create/update/delete), or search a single noun.`
|
|
87213
|
+
);
|
|
87214
|
+
}
|
|
87215
|
+
const lines = matches.map(
|
|
87216
|
+
(m, i2) => `${i2 + 1}. ${m.kind} ${m.signature}
|
|
87217
|
+
${m.description || "(no description)"}`
|
|
87218
|
+
);
|
|
87219
|
+
const text = [
|
|
87220
|
+
`Top ${matches.length} of ${ops.length} Wix GraphQL root operations for "${query}":`,
|
|
87221
|
+
"",
|
|
87222
|
+
...lines,
|
|
87223
|
+
"",
|
|
87224
|
+
`Next: call ${INSPECT_GRAPHQL_TOOL_NAME} with \`operation\` set to the chosen name to read its exact input type and return type before writing the request.`
|
|
87225
|
+
].join("\n");
|
|
87226
|
+
return textResult(text);
|
|
87227
|
+
} catch (err) {
|
|
87228
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
87229
|
+
logger2.log(`[${SEARCH_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
87230
|
+
return errorResult(`GraphQL schema search error: ${message}`);
|
|
87231
|
+
}
|
|
87232
|
+
}
|
|
87233
|
+
);
|
|
87234
|
+
}
|
|
87235
|
+
function addInspectGraphqlTool(server2, description, options) {
|
|
87236
|
+
const d = desc("InspectWixGraphQLSchema");
|
|
87237
|
+
const maxChars = options.maxResponseSize ?? SCHEMA_MAX_RESPONSE_CHARS;
|
|
87238
|
+
server2.tool(
|
|
87239
|
+
INSPECT_GRAPHQL_TOOL_NAME,
|
|
87240
|
+
description,
|
|
87241
|
+
{
|
|
87242
|
+
operation: external_exports.string().optional().describe(d.operation),
|
|
87243
|
+
typeNames: external_exports.array(external_exports.string()).optional().describe(d.typeNames),
|
|
87244
|
+
depth: external_exports.number().int().min(1).max(4).optional().describe(d.depth),
|
|
87245
|
+
reason: external_exports.string().describe(d.reason)
|
|
87246
|
+
},
|
|
87247
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
87248
|
+
async ({ operation, typeNames, depth, reason }) => {
|
|
87249
|
+
logger2.log(
|
|
87250
|
+
`[${INSPECT_GRAPHQL_TOOL_NAME}] operation=${operation ?? "-"} typeNames=${JSON.stringify(typeNames ?? [])} depth=${depth ?? 2} reason="${reason}"`
|
|
87251
|
+
);
|
|
87252
|
+
if (!operation && !(typeNames && typeNames.length > 0)) {
|
|
87253
|
+
return errorResult(
|
|
87254
|
+
"Pass `operation` (a root query/mutation name from SearchWixGraphQLOperations) and/or `typeNames` (type names to expand)."
|
|
87255
|
+
);
|
|
87256
|
+
}
|
|
87257
|
+
try {
|
|
87258
|
+
const io = introspectionOptions(options);
|
|
87259
|
+
const sections = [];
|
|
87260
|
+
const roots = [];
|
|
87261
|
+
if (operation) {
|
|
87262
|
+
const ops = await getRootOperations(io);
|
|
87263
|
+
const op = ops.find((o2) => o2.name === operation);
|
|
87264
|
+
if (!op) {
|
|
87265
|
+
const suggestions = searchOperations(ops, operation, {
|
|
87266
|
+
maxResults: 5
|
|
87267
|
+
});
|
|
87268
|
+
return errorResult(
|
|
87269
|
+
`No root operation named "${operation}". ${suggestions.length ? `Did you mean: ${suggestions.map((s) => `${s.kind} ${s.operation}`).join(", ")}? Use SearchWixGraphQLOperations to discover the exact name.` : "Use SearchWixGraphQLOperations to discover the exact name."}`
|
|
87270
|
+
);
|
|
87271
|
+
}
|
|
87272
|
+
sections.push(
|
|
87273
|
+
`# ${renderOperationSignature(op)}`,
|
|
87274
|
+
op.description ? op.description.trim().split("\n").map((l) => `# ${l}`).join("\n") : "# (no description)"
|
|
87275
|
+
);
|
|
87276
|
+
op.args.forEach((a2) => {
|
|
87277
|
+
const n2 = namedType(a2.type);
|
|
87278
|
+
if (n2) roots.push(n2);
|
|
87279
|
+
});
|
|
87280
|
+
const ret = namedType(op.type);
|
|
87281
|
+
if (ret) roots.push(ret);
|
|
87282
|
+
}
|
|
87283
|
+
if (typeNames?.length) roots.push(...typeNames);
|
|
87284
|
+
const { rendered, unexpanded, unknown: unknown2 } = await expandTypes(roots, {
|
|
87285
|
+
...io,
|
|
87286
|
+
depth: depth ?? 2
|
|
87287
|
+
});
|
|
87288
|
+
sections.push(...rendered);
|
|
87289
|
+
if (unknown2.length) {
|
|
87290
|
+
sections.push(
|
|
87291
|
+
`# Unknown type names (not in the schema): ${unknown2.join(", ")}`
|
|
87292
|
+
);
|
|
87293
|
+
}
|
|
87294
|
+
if (unexpanded.length) {
|
|
87295
|
+
sections.push(
|
|
87296
|
+
`# Not expanded (pass any of these in \`typeNames\` to read them): ${unexpanded.join(", ")}`
|
|
87297
|
+
);
|
|
87298
|
+
}
|
|
87299
|
+
sections.push(
|
|
87300
|
+
"# Notes: `JSON` scalar fields (filter, sort, free-form data) take any JSON \u2014 pass them as variables. Every object-typed field in a selection needs its own { ... } selection set. Only fields listed above exist. Paging: use exactly the paging style the input type declares \u2014 `paging` (offset) or `cursorPaging` (cursor) \u2014 never the other one. `pageInfo.total` is only populated with offset paging; with cursor paging, count by walking pages (`pageInfo.hasNext` + cursor) or use a dedicated count operation if one exists."
|
|
87301
|
+
);
|
|
87302
|
+
return textResult(truncateResponse(sections.join("\n\n"), maxChars));
|
|
87303
|
+
} catch (err) {
|
|
87304
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
87305
|
+
logger2.log(`[${INSPECT_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
87306
|
+
return errorResult(`GraphQL schema inspection error: ${message}`);
|
|
87307
|
+
}
|
|
87308
|
+
}
|
|
87309
|
+
);
|
|
87310
|
+
}
|
|
87311
|
+
function addExecuteGraphqlTool(server2, description, authStrategy, options) {
|
|
87312
|
+
const d = desc("ExecuteWixGraphQL");
|
|
87313
|
+
const endpoint = options.endpoint ?? WIX_GRAPHQL_ENDPOINT;
|
|
87314
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
87315
|
+
server2.tool(
|
|
87316
|
+
EXECUTE_GRAPHQL_TOOL_NAME,
|
|
87317
|
+
description,
|
|
87318
|
+
{
|
|
87319
|
+
query: external_exports.string().describe(d.query),
|
|
87320
|
+
variables: external_exports.record(external_exports.unknown()).optional().describe(d.variables),
|
|
87321
|
+
operationName: external_exports.string().optional().describe(d.operationName),
|
|
87322
|
+
siteId: external_exports.string().optional().describe(d.siteId),
|
|
87323
|
+
scope: external_exports.enum(["site", "account"]).optional().describe(d.scope),
|
|
87324
|
+
hasMutations: external_exports.boolean().describe(d.hasMutations),
|
|
87325
|
+
reason: external_exports.string().describe(d.reason)
|
|
87326
|
+
},
|
|
87327
|
+
{ readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
87328
|
+
async ({ query, variables, operationName, siteId, scope, hasMutations, reason }, extra) => {
|
|
87329
|
+
const effectiveScope = scope ?? (siteId ? "site" : "account");
|
|
87330
|
+
logger2.log(
|
|
87331
|
+
`[${EXECUTE_GRAPHQL_TOOL_NAME}] scope=${effectiveScope} siteId=${siteId ?? "none"} hasMutations=${hasMutations} operationName=${operationName ?? "-"} reason="${reason}"`
|
|
87332
|
+
);
|
|
87333
|
+
if (effectiveScope === "site" && !siteId) {
|
|
87334
|
+
return errorResult(
|
|
87335
|
+
'A `siteId` is required for site-level GraphQL operations (anything that reads or writes inside a site). Resolve it with GetSiteContext or ListWixSites, or set scope: "account" for account-level operations.'
|
|
87336
|
+
);
|
|
87337
|
+
}
|
|
87338
|
+
if (!query.trim()) {
|
|
87339
|
+
return errorResult("`query` must be a non-empty GraphQL document.");
|
|
87340
|
+
}
|
|
87341
|
+
const authHeaders = effectiveScope === "site" ? await authStrategy.getSiteAuthHeaders(siteId) : await authStrategy.getAccountAuthHeaders();
|
|
87342
|
+
try {
|
|
87343
|
+
const init2 = {
|
|
87344
|
+
method: "POST",
|
|
87345
|
+
headers: {
|
|
87346
|
+
"Content-Type": "application/json",
|
|
87347
|
+
Accept: "application/json",
|
|
87348
|
+
...authHeaders
|
|
87349
|
+
},
|
|
87350
|
+
body: JSON.stringify({
|
|
87351
|
+
query,
|
|
87352
|
+
...variables ? { variables } : {},
|
|
87353
|
+
...operationName ? { operationName } : {}
|
|
87354
|
+
})
|
|
87355
|
+
};
|
|
87356
|
+
const resp = hasMutations ? await fetchImpl(endpoint, init2) : await fetchWithRetry(
|
|
87357
|
+
fetchImpl,
|
|
87358
|
+
endpoint,
|
|
87359
|
+
init2,
|
|
87360
|
+
options.retryDelaysMs
|
|
87361
|
+
);
|
|
87362
|
+
const requestId = resp.headers?.get("x-wix-request-id") ?? void 0;
|
|
87363
|
+
extra?.setToolBiParams?.({ siteId, requestId });
|
|
87364
|
+
const text = await resp.text();
|
|
87365
|
+
if (!resp.ok) {
|
|
87366
|
+
await authStrategy.enforceValidWixToken?.({
|
|
87367
|
+
status: resp.status,
|
|
87368
|
+
response: { status: resp.status }
|
|
87369
|
+
});
|
|
87370
|
+
const body2 = /^\s*<(!doctype|html)/i.test(text) ? "(HTML error page)" : text.slice(0, 2e3);
|
|
87371
|
+
const hasObjectVariables = !!variables && Object.values(variables).some(
|
|
87372
|
+
(v) => v !== null && typeof v === "object"
|
|
87373
|
+
);
|
|
87374
|
+
const hint = resp.status >= 500 ? hasObjectVariables ? ` The Wix GraphQL endpoint (Developer Preview) returns 503 when an object-typed variable carries an enum value. Rewrite the document with the input INLINE as a GraphQL literal (enum values unquoted, e.g. productType: PHYSICAL), keep variables only for JSON scalars and plain scalars, and retry once. If it still fails and REST tools (ExecuteWixAPI / CallWixSiteAPI) are available, use the equivalent REST method.` : ` The Wix GraphQL endpoint (Developer Preview) failed for this operation. Do not retry the same document more than once. If REST tools (ExecuteWixAPI / CallWixSiteAPI) are available, complete the task through the equivalent REST method instead; otherwise report the failure with the request id.` : "";
|
|
87375
|
+
return errorResult(
|
|
87376
|
+
`Wix GraphQL request failed: ${resp.status} ${resp.statusText}.${requestId ? ` request id: ${requestId}.` : ""}${hint}
|
|
87377
|
+
${body2}`
|
|
87378
|
+
);
|
|
87379
|
+
}
|
|
87380
|
+
let json;
|
|
87381
|
+
try {
|
|
87382
|
+
json = JSON.parse(text);
|
|
87383
|
+
} catch {
|
|
87384
|
+
return errorResult(
|
|
87385
|
+
`Wix GraphQL returned a non-JSON body:
|
|
87386
|
+
${text.slice(0, 2e3)}`
|
|
87387
|
+
);
|
|
87388
|
+
}
|
|
87389
|
+
const hasData = json.data != null && Object.values(json.data).some((v) => v !== null && v !== void 0);
|
|
87390
|
+
const errors = json.errors ?? [];
|
|
87391
|
+
if (!hasData && errors.length > 0) {
|
|
87392
|
+
const permissionDenied = errors.some(
|
|
87393
|
+
(e) => e.extensions?.message === "PERMISSION_DENIED"
|
|
87394
|
+
);
|
|
87395
|
+
return errorResult(
|
|
87396
|
+
[
|
|
87397
|
+
`Wix GraphQL returned errors and no data${requestId ? ` (request id: ${requestId})` : ""}:`,
|
|
87398
|
+
JSON.stringify(errors, null, 2).slice(0, 4e3),
|
|
87399
|
+
permissionDenied ? "PERMISSION_DENIED usually means the operation is site-level and was called without the right `siteId`, the app behind it is not installed on the site, or the credential lacks that permission scope." : 'Validation errors ("Cannot query field ...", "Unknown argument ...") mean a name was guessed \u2014 re-read the exact fields with InspectWixGraphQLSchema and retry.'
|
|
87400
|
+
].join("\n")
|
|
87401
|
+
);
|
|
87402
|
+
}
|
|
87403
|
+
const body = { data: json.data };
|
|
87404
|
+
if (errors.length > 0) {
|
|
87405
|
+
body.errors = errors;
|
|
87406
|
+
body.note = "Partial result: some root fields failed (see errors[].path). Fields present in data succeeded.";
|
|
87407
|
+
}
|
|
87408
|
+
return textResult(truncateResponse(body, options.maxResponseSize));
|
|
87409
|
+
} catch (err) {
|
|
87410
|
+
rethrowIfUnauthorized(err);
|
|
87411
|
+
await authStrategy.enforceValidWixToken?.(err);
|
|
87412
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
87413
|
+
logger2.log(`[${EXECUTE_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
87414
|
+
return errorResult(`Wix GraphQL execution error: ${message}`);
|
|
87415
|
+
}
|
|
87416
|
+
}
|
|
87417
|
+
);
|
|
87418
|
+
}
|
|
87419
|
+
|
|
86595
87420
|
// src/bin.ts
|
|
86596
87421
|
var PUBLIC_TOOLS = [...VALID_DOCS_TOOLS];
|
|
86597
87422
|
var EXPERIMENTAL_TOOLS = [
|
|
@@ -86600,7 +87425,8 @@ var EXPERIMENTAL_TOOLS = [
|
|
|
86600
87425
|
"GET_TO_KNOW_WIX",
|
|
86601
87426
|
"CREATE_WIX_BUSINESS_GUIDE",
|
|
86602
87427
|
"VELO_README",
|
|
86603
|
-
"WIX_API_THROUGH_FS"
|
|
87428
|
+
"WIX_API_THROUGH_FS",
|
|
87429
|
+
"GRAPHQL"
|
|
86604
87430
|
];
|
|
86605
87431
|
var DEFAULT_TOOLS = [...VALID_DOCS_TOOLS];
|
|
86606
87432
|
var parsedArgs = (0, import_minimist.default)(process.argv.slice(2));
|
|
@@ -86727,6 +87553,7 @@ if (cliTools.length > 0) {
|
|
|
86727
87553
|
}
|
|
86728
87554
|
}
|
|
86729
87555
|
}
|
|
87556
|
+
var cliAccountAuthStrategy;
|
|
86730
87557
|
if (isWixCliAuth) {
|
|
86731
87558
|
const accountFile = path9.join(homedir3(), ".wix", "auth", "account.json");
|
|
86732
87559
|
if (!existsSync3(accountFile)) {
|
|
@@ -86739,6 +87566,13 @@ if (isWixCliAuth) {
|
|
|
86739
87566
|
const tokens = new TokenStore({ accountFile });
|
|
86740
87567
|
const accountAuthStrategy = createAccountFileAuthStrategy({ tokens });
|
|
86741
87568
|
addApiCallTool(server, accountAuthStrategy);
|
|
87569
|
+
cliAccountAuthStrategy = accountAuthStrategy;
|
|
87570
|
+
}
|
|
87571
|
+
if (activeTools.includes("GRAPHQL")) {
|
|
87572
|
+
logger2.log("Adding GraphQL tools", {
|
|
87573
|
+
execute: Boolean(cliAccountAuthStrategy)
|
|
87574
|
+
});
|
|
87575
|
+
addGraphqlTools(server, { authStrategy: cliAccountAuthStrategy });
|
|
86742
87576
|
}
|
|
86743
87577
|
try {
|
|
86744
87578
|
const portals = parsedArgs["portals"]?.split(",") || [];
|