@wix/mcp 1.0.81 → 1.0.83
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 +847 -4
- package/build/cjs/index.cjs +900 -20
- package/build/cjs/toolkit.cjs +844 -4
- 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 +33 -2
- package/build/dts/config/param-descriptions.d.ts +21 -0
- package/build/dts/config/param-descriptions.js +21 -0
- 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/import-claude-design-tool/index.js +2 -2
- package/build/dts/index.d.ts +2 -1
- package/build/dts/index.js +3 -1
- package/build/dts/site-widget-tools/constants.d.ts +12 -5
- package/build/dts/site-widget-tools/constants.js +20 -11
- package/build/dts/site-widget-tools/site-builder-tool/index.js +1 -1
- package/build/dts/site-widget-tools/site-creation-tool/index.js +3 -2
- package/build/dts/site-widget-tools/suggested-domain-tool/index.js +25 -5
- 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/wix-mcp-server.d.ts +2 -0
- package/build/dts/wix-mcp-server.js +5 -0
- package/build/esm/index.js +892 -20
- package/build/esm/toolkit.js +839 -4
- package/package.json +2 -2
package/build/cjs/index.cjs
CHANGED
|
@@ -20873,13 +20873,17 @@ __export(index_exports, {
|
|
|
20873
20873
|
DEFAULT_OPENAI_SECURITY_SCHEMES: () => DEFAULT_OPENAI_SECURITY_SCHEMES,
|
|
20874
20874
|
EDIT_SITE_TOOL_NAME: () => EDIT_SITE_TOOL_NAME,
|
|
20875
20875
|
EXECUTE_FUNCTION_EXPRESSION_HINT: () => EXECUTE_FUNCTION_EXPRESSION_HINT,
|
|
20876
|
+
EXECUTE_GRAPHQL_TOOL_NAME: () => EXECUTE_GRAPHQL_TOOL_NAME,
|
|
20876
20877
|
GET_SUGGESTED_DOMAINS_TOOL_NAME: () => GET_SUGGESTED_DOMAINS_TOOL_NAME,
|
|
20878
|
+
GRAPHQL_TOOL_NAMES: () => GRAPHQL_TOOL_NAMES,
|
|
20877
20879
|
IMPORT_CLAUDE_DESIGN_TOOL_NAME: () => IMPORT_CLAUDE_DESIGN_TOOL_NAME,
|
|
20880
|
+
INSPECT_GRAPHQL_TOOL_NAME: () => INSPECT_GRAPHQL_TOOL_NAME,
|
|
20878
20881
|
MENU_CONTENT_THRESHOLD: () => MENU_CONTENT_THRESHOLD,
|
|
20879
20882
|
OPENAI_WIDGET_MIME_TYPE: () => OPENAI_WIDGET_MIME_TYPE,
|
|
20880
20883
|
OPEN_AI_SITE_WIDGET_CSP: () => OPEN_AI_SITE_WIDGET_CSP,
|
|
20881
20884
|
PULL_SITE_JOB_TOOL_NAME: () => PULL_SITE_JOB_TOOL_NAME,
|
|
20882
20885
|
RECIPES_INDEX_URL: () => RECIPES_INDEX_URL,
|
|
20886
|
+
SEARCH_GRAPHQL_TOOL_NAME: () => SEARCH_GRAPHQL_TOOL_NAME,
|
|
20883
20887
|
SEARCH_SITE_TEMPLATES_TOOL_NAME: () => SEARCH_SITE_TEMPLATES_TOOL_NAME,
|
|
20884
20888
|
SITE_BUILDER_TOOL_NAME: () => SITE_BUILDER_TOOL_NAME,
|
|
20885
20889
|
SITE_CREATION_COPY: () => SITE_CREATION_COPY,
|
|
@@ -20892,6 +20896,7 @@ __export(index_exports, {
|
|
|
20892
20896
|
VALID_DOCS_TOOLS: () => VALID_DOCS_TOOLS,
|
|
20893
20897
|
WIXEL_ICON_PNG_URL: () => WIXEL_ICON_PNG_URL,
|
|
20894
20898
|
WIXEL_ICON_SVG_URL: () => WIXEL_ICON_SVG_URL,
|
|
20899
|
+
WIX_GRAPHQL_ENDPOINT: () => WIX_GRAPHQL_ENDPOINT,
|
|
20895
20900
|
WIX_SITE_CREATION_TOOL_NAME: () => WIX_SITE_CREATION_TOOL_NAME,
|
|
20896
20901
|
WixFetchError: () => WixFetchError,
|
|
20897
20902
|
WixMcpServer: () => WixMcpServer,
|
|
@@ -20908,6 +20913,7 @@ __export(index_exports, {
|
|
|
20908
20913
|
addGetWixelAssetTool: () => addGetWixelAssetTool,
|
|
20909
20914
|
addGetWixelJobStatusTool: () => addGetWixelJobStatusTool,
|
|
20910
20915
|
addGetWixelProjectAssetsTool: () => addGetWixelProjectAssetsTool,
|
|
20916
|
+
addGraphqlTools: () => addGraphqlTools,
|
|
20911
20917
|
addImportClaudeDesignTool: () => addImportClaudeDesignTool,
|
|
20912
20918
|
addListWixelEventsTool: () => addListWixelEventsTool,
|
|
20913
20919
|
addMarkWixelJobAsFulfilledTool: () => addMarkWixelJobAsFulfilledTool,
|
|
@@ -20917,6 +20923,7 @@ __export(index_exports, {
|
|
|
20917
20923
|
addSendWixelMessageTool: () => addSendWixelMessageTool,
|
|
20918
20924
|
addSiteCreationWidgetTools: () => addSiteCreationWidgetTools,
|
|
20919
20925
|
addStartWixelSessionTool: () => addStartWixelSessionTool,
|
|
20926
|
+
addSuggestedDomainTool: () => addSuggestedDomainTool,
|
|
20920
20927
|
addSupportTool: () => addSupportTool,
|
|
20921
20928
|
addWixSiteCreationTool: () => addWixSiteCreationTool,
|
|
20922
20929
|
addWixelSessionTools: () => addWixelSessionTools,
|
|
@@ -20941,6 +20948,7 @@ __export(index_exports, {
|
|
|
20941
20948
|
mergeConfig: () => mergeConfig,
|
|
20942
20949
|
normalizeMenuContent: () => normalizeMenuContent,
|
|
20943
20950
|
parseDirectChildren: () => parseDirectChildren,
|
|
20951
|
+
resetGraphqlSchemaCache: () => resetGraphqlSchemaCache,
|
|
20944
20952
|
rethrowIfUnauthorized: () => rethrowIfUnauthorized,
|
|
20945
20953
|
summarizeLargeMenu: () => summarizeLargeMenu,
|
|
20946
20954
|
toWidget: () => toWidget,
|
|
@@ -36094,6 +36102,11 @@ var WixMcpServer = class extends McpServer {
|
|
|
36094
36102
|
isMcpAppsInMcpEnabled() {
|
|
36095
36103
|
return this.mcpAppsInMcpEnabled;
|
|
36096
36104
|
}
|
|
36105
|
+
/** Microsoft 365 Copilot ("sydney" at runtime) or Copilot Cowork ("copilot"). */
|
|
36106
|
+
isMicrosoftCopilotClient() {
|
|
36107
|
+
const clientName = this.getClientName?.()?.toLowerCase() ?? "";
|
|
36108
|
+
return clientName.includes("sydney") || clientName.includes("copilot");
|
|
36109
|
+
}
|
|
36097
36110
|
shouldRenderMcpApps() {
|
|
36098
36111
|
const clientName = this.getClientName?.()?.toLowerCase();
|
|
36099
36112
|
const isOpenAi = clientName?.includes("openai") ?? false;
|
|
@@ -37221,6 +37234,8 @@ var SITE_CREATION_COPY = {
|
|
|
37221
37234
|
var SITE_GUIDANCE = dedent_default`
|
|
37222
37235
|
A new-site request has two things to settle: what the site is for, and how to build it. The how is AI generation vs. starting from a template, so naming templates, a template id or Wix Studio (Wix Studio is also the template side) already settles it.
|
|
37223
37236
|
|
|
37237
|
+
One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool. Send them to https://www.wix.com/headless/drop?utm_campaign=mcp — they drag the files in and get a live Wix-hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
|
|
37238
|
+
|
|
37224
37239
|
Settle what the site is for first: its purpose, topic, business type, audience, or main visitor action. Naming a method is not a description of the site, and neither is the business or industry on its own.
|
|
37225
37240
|
If you can't tell yet, ask the preferred question below and nothing else — one question, no creation-method question in the same reply, and no internal tool names.
|
|
37226
37241
|
Once the purpose is clear, unless the user already named a method, ${SITE_CREATION_COPY.unstatedMethod.nonWidget}. A method they named is settled — never re-open it. Never default to AI or to templates, and never ask the user to choose between Harmony and Wix Studio; infer that from their words.
|
|
@@ -37553,7 +37568,8 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
37553
37568
|
After calling, you MUST speak the message provided in the tool result to the user as your reply — do not stay silent and do not replace it with a one-line pointer to the widget. Write out ALL of it, word for word: its opening line (the "Ready to set up your … site?" question INCLUDING "Choose your path in the widget above:"), BOTH of its option bullets, and its "Next up:" line, each on its own line and in that order. Do not summarize, shorten, drop any line, invent your own opener, rename the options, or add an intro or outro.
|
|
37554
37569
|
`,
|
|
37555
37570
|
"import-claude-design-from-url": dedent_default`
|
|
37556
|
-
Import a design into Wix
|
|
37571
|
+
Import a design created in Claude's design tool into Wix. Takes the signed claudeusercontent.com URL Claude Design produces for its self-contained HTML bundle (all images, fonts, and styles inlined); any other host is rejected. Creates a live Wix-hosted site and returns its URL.
|
|
37572
|
+
NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool: send the user to https://www.wix.com/headless/drop?utm_campaign=mcp, where they upload the files and get a live hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
|
|
37557
37573
|
`,
|
|
37558
37574
|
RunWixCliCommand: dedent_default`
|
|
37559
37575
|
Executes Wix CLI commands for Wix app development and management.
|
|
@@ -37950,10 +37966,37 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
37950
37966
|
|
|
37951
37967
|
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\`.
|
|
37952
37968
|
|
|
37969
|
+
`,
|
|
37970
|
+
SearchWixGraphQLOperations: dedent_default`
|
|
37971
|
+
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\`.
|
|
37972
|
+
|
|
37973
|
+
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.
|
|
37974
|
+
|
|
37975
|
+
When to prefer GraphQL over the REST tools:
|
|
37976
|
+
- 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.
|
|
37977
|
+
- Picking exactly the fields you need, so responses stay small.
|
|
37978
|
+
- Answering "how many / list / show" questions: a query root plus \`pageInfo\` (\`hasNext\`, and \`total\` where the API provides it).
|
|
37979
|
+
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.
|
|
37980
|
+
|
|
37981
|
+
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.
|
|
37982
|
+
Results list \`kind name(arg: InputType): ReturnType\` plus a one-line description; pass the chosen name to InspectWixGraphQLSchema next.
|
|
37983
|
+
`,
|
|
37984
|
+
InspectWixGraphQLSchema: dedent_default`
|
|
37985
|
+
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.
|
|
37986
|
+
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.
|
|
37987
|
+
`,
|
|
37988
|
+
ExecuteWixGraphQL: dedent_default`
|
|
37989
|
+
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.
|
|
37990
|
+
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.
|
|
37991
|
+
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.
|
|
37992
|
+
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.
|
|
37993
|
+
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.
|
|
37994
|
+
Set \`hasMutations: true\` for any document containing a mutation root field.
|
|
37953
37995
|
`,
|
|
37954
37996
|
CreateWixBusinessGuide: dedent_default`
|
|
37955
37997
|
Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
|
|
37956
37998
|
Call this ONLY when the user explicitly asks for a headless site, a Wix Classic Editor site, or manual/advanced account/API-based site creation — these don't need the site's purpose first. For normal site-creation ${SITE_CREATION_COPY.normalCreationRoute.nonWidget}; for a specific template id, call \`CreateSiteFromTemplate\`.
|
|
37999
|
+
Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool — send them to https://www.wix.com/headless/drop?utm_campaign=mcp to upload the files and get a live hosted site (give that URL exactly, keeping the utm_campaign=mcp parameter). This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
|
|
37957
38000
|
|
|
37958
38001
|
## What This Tool Contains
|
|
37959
38002
|
|
|
@@ -38068,7 +38111,8 @@ function mergeConfig(base, overrides) {
|
|
|
38068
38111
|
dynamicContextEnabled: overrides.dynamicContextEnabled ?? base.dynamicContextEnabled,
|
|
38069
38112
|
partnerContextEnabled: overrides.partnerContextEnabled ?? base.partnerContextEnabled,
|
|
38070
38113
|
usePlatformizedSearch: overrides.usePlatformizedSearch ?? base.usePlatformizedSearch,
|
|
38071
|
-
skillsPrOverrideEnabled: overrides.skillsPrOverrideEnabled ?? base.skillsPrOverrideEnabled
|
|
38114
|
+
skillsPrOverrideEnabled: overrides.skillsPrOverrideEnabled ?? base.skillsPrOverrideEnabled,
|
|
38115
|
+
graphqlEnabled: overrides.graphqlEnabled ?? base.graphqlEnabled
|
|
38072
38116
|
};
|
|
38073
38117
|
}
|
|
38074
38118
|
var defaultConfig = {
|
|
@@ -38419,7 +38463,16 @@ var known_apps_default = {
|
|
|
38419
38463
|
"1480c568-5cbd-9392-5604-1148f5faffa0": "Promote SEO",
|
|
38420
38464
|
"ff5d6eb1-65e4-4f9a-8b14-64d34c12cc2e": "Wix Rentals"
|
|
38421
38465
|
};
|
|
38422
|
-
var
|
|
38466
|
+
var DYNAMIC_CONTEXT_PATH = "/_api/dynamic-context/v1/dynamic-context";
|
|
38467
|
+
var DYNAMIC_CONTEXT_URL = `https://dev.wix.com${DYNAMIC_CONTEXT_PATH}`;
|
|
38468
|
+
var isWixHost = (hostname2) => hostname2 === "wix.com" || hostname2.endsWith(".wix.com");
|
|
38469
|
+
var dynamicContextUrl = () => {
|
|
38470
|
+
if (typeof window === "undefined") {
|
|
38471
|
+
return DYNAMIC_CONTEXT_URL;
|
|
38472
|
+
}
|
|
38473
|
+
const hostname2 = window.location?.hostname;
|
|
38474
|
+
return typeof hostname2 === "string" && isWixHost(hostname2) ? DYNAMIC_CONTEXT_PATH : DYNAMIC_CONTEXT_URL;
|
|
38475
|
+
};
|
|
38423
38476
|
var COUNT_SITES_URL = "https://www.wixapis.com/site-list/v2/sites/count";
|
|
38424
38477
|
var MAX_SITES_FOR_DYNAMIC_CONTEXT = 10;
|
|
38425
38478
|
var DYNAMIC_CONTEXT_PETRI_OVERRIDE = "shouldReturnDynamicContextInMCP#B";
|
|
@@ -38471,7 +38524,7 @@ async function getDynamicContext(auth, httpClient6, filter, options) {
|
|
|
38471
38524
|
if (filter?.siteId) body.site_id = filter.siteId;
|
|
38472
38525
|
else if (filter?.siteName) body.site_name = filter.siteName;
|
|
38473
38526
|
const dynamicContextPromise = httpClient6.request({
|
|
38474
|
-
url:
|
|
38527
|
+
url: dynamicContextUrl(),
|
|
38475
38528
|
method: "POST",
|
|
38476
38529
|
headers: {
|
|
38477
38530
|
...accountHeaders,
|
|
@@ -38665,6 +38718,27 @@ var paramDescriptions = {
|
|
|
38665
38718
|
mimeType: 'MIME type of the image (e.g. "image/jpeg", "image/png", "image/webp"). Required when imageBase64 does not include a data URL prefix.',
|
|
38666
38719
|
displayName: "Optional display name for the file in Media Manager. If not provided, uses the original filename."
|
|
38667
38720
|
},
|
|
38721
|
+
SearchWixGraphQLOperations: {
|
|
38722
|
+
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.',
|
|
38723
|
+
kind: 'Restrict to "query" (reads), "mutation" (writes \u2014 note a few reads such as search/list calls are exposed as mutations), or "all" (default).',
|
|
38724
|
+
maxResults: "Maximum number of operations to return (default 10, max 50).",
|
|
38725
|
+
reason: "One sentence describing the original user request and why you are looking for a GraphQL operation."
|
|
38726
|
+
},
|
|
38727
|
+
InspectWixGraphQLSchema: {
|
|
38728
|
+
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.',
|
|
38729
|
+
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`.',
|
|
38730
|
+
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.",
|
|
38731
|
+
reason: "One sentence describing the original user request and why you need this part of the GraphQL schema."
|
|
38732
|
+
},
|
|
38733
|
+
ExecuteWixGraphQL: {
|
|
38734
|
+
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.",
|
|
38735
|
+
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.',
|
|
38736
|
+
operationName: "Operation to run when the document defines more than one named operation.",
|
|
38737
|
+
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.",
|
|
38738
|
+
scope: '"site" (default when siteId is given) runs with a site-scoped credential; "account" runs account-level operations (sites, account settings) and ignores siteId.',
|
|
38739
|
+
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.",
|
|
38740
|
+
reason: "One sentence explaining the original user request and why you are running this GraphQL operation to complete it."
|
|
38741
|
+
},
|
|
38668
38742
|
RunWixCliCommand: {
|
|
38669
38743
|
appPath: "The full path to the Wix CLI app to run the command on.",
|
|
38670
38744
|
extensionType: "The type of extension to generate. You MUST provide this for commands that generate extensions.",
|
|
@@ -40932,9 +41006,18 @@ var SITE_CREATION_CUSTOM_ACTIONS_GUIDANCE = `2\u20133 specific, high-value setup
|
|
|
40932
41006
|
* Events / Weddings / Real Estate: "add event schedules, display listings, and manage RSVP forms"
|
|
40933
41007
|
* General / Unspecified: "${SITE_CREATION_DEFAULT_ACTIONS}"
|
|
40934
41008
|
If none of these patterns fit the site, infer 2\u20133 actions that suit what that kind of business would manage on its site.`;
|
|
40935
|
-
function buildSiteCreationSelectorMessage(siteType, customActions) {
|
|
41009
|
+
function buildSiteCreationSelectorMessage(siteType, customActions, { plain = false } = {}) {
|
|
40936
41010
|
const type = (siteType ?? "").trim() || "new";
|
|
40937
41011
|
const actions = (customActions ?? "").trim() || SITE_CREATION_DEFAULT_ACTIONS;
|
|
41012
|
+
const body = `Ready to set up your ${type} site? Choose your path in the widget above:
|
|
41013
|
+
|
|
41014
|
+
* **Build with AI** for a custom, tailor-made layout
|
|
41015
|
+
* **Pick a template** to start with a pre-designed layout
|
|
41016
|
+
|
|
41017
|
+
**Next up:** Once your site is created, you can customize it in Wix or ask me to ${actions}\u2014right here in the chat!`;
|
|
41018
|
+
if (plain) {
|
|
41019
|
+
return body;
|
|
41020
|
+
}
|
|
40938
41021
|
return `You are the assistant replying to the user \u2014 say this message out loud in chat (do NOT stay silent, and do NOT just point at the widget). Your reply is only complete if it includes ALL of the following, word for word, each on its own line and in this order:
|
|
40939
41022
|
1. "Ready to set up your ${type} site? Choose your path in the widget above:" \u2014 keep the "Choose your path in the widget above:" part.
|
|
40940
41023
|
2. "* **Build with AI** for a custom, tailor-made layout"
|
|
@@ -40943,12 +41026,7 @@ function buildSiteCreationSelectorMessage(siteType, customActions) {
|
|
|
40943
41026
|
|
|
40944
41027
|
Copy those four lines as written; do not summarize, shorten, reorder, or reword any of them. Here is the full message to send:
|
|
40945
41028
|
|
|
40946
|
-
|
|
40947
|
-
|
|
40948
|
-
* **Build with AI** for a custom, tailor-made layout
|
|
40949
|
-
* **Pick a template** to start with a pre-designed layout
|
|
40950
|
-
|
|
40951
|
-
**Next up:** Once your site is created, you can customize it in Wix or ask me to ${actions}\u2014right here in the chat!`;
|
|
41029
|
+
${body}`;
|
|
40952
41030
|
}
|
|
40953
41031
|
var OPENAI_WIDGET_MIME_TYPE = "text/html+skybridge";
|
|
40954
41032
|
var SITE_WIDGET_MCP_CSP = {
|
|
@@ -47909,7 +47987,7 @@ function addCreateSiteWidgetTool(server, authStrategy, options) {
|
|
|
47909
47987
|
`The prompt to build the site. If not provided, the user will be asked to provide a prompt.`
|
|
47910
47988
|
),
|
|
47911
47989
|
suggestedSiteName: external_exports.string().optional().describe(
|
|
47912
|
-
"
|
|
47990
|
+
"You must provide this on every call, including updates to an existing job, in English. A short name for the site: the business name the user gave, or one you derive from the site prompt."
|
|
47913
47991
|
)
|
|
47914
47992
|
},
|
|
47915
47993
|
_meta: toolDefinitionMeta,
|
|
@@ -48066,6 +48144,11 @@ async function buildHarmonySite({
|
|
|
48066
48144
|
|
|
48067
48145
|
// src/site-widget-tools/suggested-domain-tool/index.ts
|
|
48068
48146
|
var WIX_APIS_BASE_URL3 = "https://www.wixapis.com";
|
|
48147
|
+
var DEFAULT_SITE_NAME = /^my site(\s+\d+)?$/i;
|
|
48148
|
+
var NON_LATIN_LETTER = new RegExp("(?![\\p{Script=Latin}])\\p{L}", "u");
|
|
48149
|
+
function isSearchableSiteName(siteName) {
|
|
48150
|
+
return !DEFAULT_SITE_NAME.test(siteName.trim()) && !NON_LATIN_LETTER.test(siteName);
|
|
48151
|
+
}
|
|
48069
48152
|
async function fetchSiteDisplayName(siteAuthorization, siteId) {
|
|
48070
48153
|
const response = await fetch(
|
|
48071
48154
|
`${WIX_APIS_BASE_URL3}/site-properties/v4/properties`,
|
|
@@ -48090,7 +48173,15 @@ function addSuggestedDomainTool(server, authStrategy) {
|
|
|
48090
48173
|
GET_SUGGESTED_DOMAINS_TOOL_NAME,
|
|
48091
48174
|
{
|
|
48092
48175
|
title: "Get Suggested Domains",
|
|
48093
|
-
description:
|
|
48176
|
+
description: dedent_default`
|
|
48177
|
+
Suggests available domain names for a business, brand, or Wix site.
|
|
48178
|
+
|
|
48179
|
+
Call this when the user asks for domain name ideas, wants to know which domains are available, or wants a domain for a new or existing site.
|
|
48180
|
+
|
|
48181
|
+
Provide either a free-text \`query\` (the user's own words, e.g. "pancakes business", "modern yoga studio") OR a \`siteId\` to base the suggestions on that site's name. At least one of them is required.
|
|
48182
|
+
|
|
48183
|
+
Present the returned domains to the user as a short list. This tool only suggests domains — it does not purchase or connect them.
|
|
48184
|
+
`,
|
|
48094
48185
|
inputSchema: {
|
|
48095
48186
|
query: zod_default.string().optional().describe(
|
|
48096
48187
|
"Free-text keywords, business idea, or brand concept to base suggestions on. Use the user's own words."
|
|
@@ -48110,10 +48201,11 @@ function addSuggestedDomainTool(server, authStrategy) {
|
|
|
48110
48201
|
},
|
|
48111
48202
|
_meta: {
|
|
48112
48203
|
securitySchemes: [{ type: "oauth2", scopes: ["offline_access"] }],
|
|
48113
|
-
|
|
48204
|
+
// Model-visible so hosts (e.g. Microsoft 365 Copilot) can call it directly and show it
|
|
48205
|
+
// in their debug info; 'app' keeps it callable from the site-builder widget.
|
|
48206
|
+
ui: { visibility: ["app", "model"] },
|
|
48114
48207
|
"openai/toolInvocation/invoking": "Fetching suggested domains",
|
|
48115
|
-
"openai/toolInvocation/invoked": "Suggested domains retrieved"
|
|
48116
|
-
"openai/visibility": "private"
|
|
48208
|
+
"openai/toolInvocation/invoked": "Suggested domains retrieved"
|
|
48117
48209
|
}
|
|
48118
48210
|
},
|
|
48119
48211
|
async (args, { panorama }) => {
|
|
@@ -48161,7 +48253,9 @@ function addSuggestedDomainTool(server, authStrategy) {
|
|
|
48161
48253
|
structuredContent: { domains: [] }
|
|
48162
48254
|
};
|
|
48163
48255
|
}
|
|
48164
|
-
resolvedQuery
|
|
48256
|
+
if (isSearchableSiteName(siteName) || !resolvedQuery) {
|
|
48257
|
+
resolvedQuery = siteName;
|
|
48258
|
+
}
|
|
48165
48259
|
} catch (error2) {
|
|
48166
48260
|
panorama.logger().error(
|
|
48167
48261
|
`[getSuggestedDomains] Error fetching site name for siteId: ${siteId}`,
|
|
@@ -48619,7 +48713,8 @@ function addWixSiteCreationTool(server, _authStrategy, options) {
|
|
|
48619
48713
|
};
|
|
48620
48714
|
const message = buildSiteCreationSelectorMessage(
|
|
48621
48715
|
args.query,
|
|
48622
|
-
args.customActions
|
|
48716
|
+
args.customActions,
|
|
48717
|
+
{ plain: server.isMicrosoftCopilotClient() }
|
|
48623
48718
|
);
|
|
48624
48719
|
return withJsonContent({
|
|
48625
48720
|
content: [
|
|
@@ -51403,10 +51498,14 @@ function addImportClaudeDesignTool(server, authStrategy, options) {
|
|
|
51403
51498
|
try {
|
|
51404
51499
|
designUrl = new URL(args.url);
|
|
51405
51500
|
} catch {
|
|
51406
|
-
return errorResult(
|
|
51501
|
+
return errorResult(
|
|
51502
|
+
"Invalid design URL \u2014 this tool only accepts the claudeusercontent.com URL produced by Claude Design. For site files the user provided themselves, do not retry: send the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload them and get a live hosted site."
|
|
51503
|
+
);
|
|
51407
51504
|
}
|
|
51408
51505
|
if (designUrl.protocol !== "https:" || !designUrl.hostname.endsWith(CLAUDE_USER_CONTENT_HOST_SUFFIX)) {
|
|
51409
|
-
return errorResult(
|
|
51506
|
+
return errorResult(
|
|
51507
|
+
"Invalid design URL \u2014 this tool only accepts the claudeusercontent.com URL produced by Claude Design. For site files the user provided themselves, do not retry: send the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload them and get a live hosted site."
|
|
51508
|
+
);
|
|
51410
51509
|
}
|
|
51411
51510
|
const accountHeaders = await authStrategy.getAccountAuthHeaders();
|
|
51412
51511
|
const wixstroOptions = { baseUrl: wixstroBaseUrl, accountHeaders };
|
|
@@ -51508,6 +51607,765 @@ function truncateResponse(content, maxChars = DEFAULT_MAX_CHARS) {
|
|
|
51508
51607
|
Response was ~${estimatedTokens.toLocaleString()} tokens (limit: ${limitTokens.toLocaleString()}). Refine your code to return less data.`;
|
|
51509
51608
|
}
|
|
51510
51609
|
|
|
51610
|
+
// src/graphql/constants.ts
|
|
51611
|
+
var WIX_GRAPHQL_ENDPOINT = "https://www.wixapis.com/graphql/alpha";
|
|
51612
|
+
var SEARCH_GRAPHQL_TOOL_NAME = "SearchWixGraphQLOperations";
|
|
51613
|
+
var INSPECT_GRAPHQL_TOOL_NAME = "InspectWixGraphQLSchema";
|
|
51614
|
+
var EXECUTE_GRAPHQL_TOOL_NAME = "ExecuteWixGraphQL";
|
|
51615
|
+
var GRAPHQL_TOOL_NAMES = [
|
|
51616
|
+
SEARCH_GRAPHQL_TOOL_NAME,
|
|
51617
|
+
INSPECT_GRAPHQL_TOOL_NAME,
|
|
51618
|
+
EXECUTE_GRAPHQL_TOOL_NAME
|
|
51619
|
+
];
|
|
51620
|
+
|
|
51621
|
+
// src/graphql/introspection.ts
|
|
51622
|
+
var TYPE_REF_FRAGMENT = `
|
|
51623
|
+
fragment TR on __Type { kind name ofType { kind name ofType { kind name ofType { kind name } } } }`;
|
|
51624
|
+
var ROOT_OPERATIONS_QUERY = `{
|
|
51625
|
+
__schema {
|
|
51626
|
+
queryType { fields { name description args { name type { ...TR } } type { ...TR } } }
|
|
51627
|
+
mutationType { fields { name description args { name type { ...TR } } type { ...TR } } }
|
|
51628
|
+
}
|
|
51629
|
+
}${TYPE_REF_FRAGMENT}`;
|
|
51630
|
+
var TYPE_DETAILS_FRAGMENT = `
|
|
51631
|
+
fragment TD on __Type {
|
|
51632
|
+
kind name description
|
|
51633
|
+
fields { name description type { ...TR } args { name type { ...TR } } }
|
|
51634
|
+
inputFields { name description type { ...TR } defaultValue }
|
|
51635
|
+
enumValues { name description }
|
|
51636
|
+
possibleTypes { name }
|
|
51637
|
+
}`;
|
|
51638
|
+
var ROOT_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
51639
|
+
var rootCache;
|
|
51640
|
+
var typeCache = /* @__PURE__ */ new Map();
|
|
51641
|
+
function resetGraphqlSchemaCache() {
|
|
51642
|
+
rootCache = void 0;
|
|
51643
|
+
typeCache.clear();
|
|
51644
|
+
}
|
|
51645
|
+
var RETRY_DELAYS_MS = [800, 2e3, 4e3];
|
|
51646
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
51647
|
+
async function fetchWithRetry(fetchImpl, url, init, delays = RETRY_DELAYS_MS) {
|
|
51648
|
+
let attempt = 0;
|
|
51649
|
+
for (; ; ) {
|
|
51650
|
+
const resp = await fetchImpl(url, init);
|
|
51651
|
+
if (resp.status < 500 || attempt >= delays.length) return resp;
|
|
51652
|
+
await sleep(delays[attempt++]);
|
|
51653
|
+
}
|
|
51654
|
+
}
|
|
51655
|
+
async function postIntrospection(query, options) {
|
|
51656
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
51657
|
+
const endpoint = options.endpoint ?? WIX_GRAPHQL_ENDPOINT;
|
|
51658
|
+
const resp = await fetchWithRetry(
|
|
51659
|
+
fetchImpl,
|
|
51660
|
+
endpoint,
|
|
51661
|
+
{
|
|
51662
|
+
method: "POST",
|
|
51663
|
+
headers: {
|
|
51664
|
+
"Content-Type": "application/json",
|
|
51665
|
+
Accept: "application/json"
|
|
51666
|
+
},
|
|
51667
|
+
body: JSON.stringify({ query })
|
|
51668
|
+
},
|
|
51669
|
+
options.retryDelaysMs
|
|
51670
|
+
);
|
|
51671
|
+
const text = await resp.text();
|
|
51672
|
+
if (!resp.ok) {
|
|
51673
|
+
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);
|
|
51674
|
+
throw new Error(
|
|
51675
|
+
`GraphQL introspection failed (${resp.status} ${resp.statusText}): ${body}`
|
|
51676
|
+
);
|
|
51677
|
+
}
|
|
51678
|
+
const json = JSON.parse(text);
|
|
51679
|
+
if (json.errors?.length && !json.data) {
|
|
51680
|
+
throw new Error(
|
|
51681
|
+
`GraphQL introspection returned errors: ${JSON.stringify(json.errors).slice(0, 500)}`
|
|
51682
|
+
);
|
|
51683
|
+
}
|
|
51684
|
+
return json.data;
|
|
51685
|
+
}
|
|
51686
|
+
function getRootOperations(options = {}) {
|
|
51687
|
+
const now = Date.now();
|
|
51688
|
+
if (rootCache && now - rootCache.fetchedAt < ROOT_CACHE_TTL_MS) {
|
|
51689
|
+
return rootCache.promise;
|
|
51690
|
+
}
|
|
51691
|
+
const promise = postIntrospection(ROOT_OPERATIONS_QUERY, options).then(
|
|
51692
|
+
(data) => {
|
|
51693
|
+
const toOps = (fields, kind) => (fields ?? []).map((f2) => ({
|
|
51694
|
+
kind,
|
|
51695
|
+
name: f2.name,
|
|
51696
|
+
description: f2.description ?? "",
|
|
51697
|
+
args: f2.args ?? [],
|
|
51698
|
+
type: f2.type
|
|
51699
|
+
}));
|
|
51700
|
+
return [
|
|
51701
|
+
...toOps(data?.__schema?.queryType?.fields, "query"),
|
|
51702
|
+
...toOps(data?.__schema?.mutationType?.fields, "mutation")
|
|
51703
|
+
];
|
|
51704
|
+
}
|
|
51705
|
+
);
|
|
51706
|
+
promise.catch(() => {
|
|
51707
|
+
if (rootCache?.promise === promise) rootCache = void 0;
|
|
51708
|
+
});
|
|
51709
|
+
rootCache = { promise, fetchedAt: now };
|
|
51710
|
+
return promise;
|
|
51711
|
+
}
|
|
51712
|
+
var GRAPHQL_NAME = /^[_A-Za-z][_0-9A-Za-z]*$/;
|
|
51713
|
+
var TYPE_BATCH_SIZE = 10;
|
|
51714
|
+
async function getTypes(names, options = {}) {
|
|
51715
|
+
const unique = [...new Set(names)].filter((n) => GRAPHQL_NAME.test(n));
|
|
51716
|
+
const missing = unique.filter((n) => !typeCache.has(n));
|
|
51717
|
+
for (let start = 0; start < missing.length; start += TYPE_BATCH_SIZE) {
|
|
51718
|
+
const chunk = missing.slice(start, start + TYPE_BATCH_SIZE);
|
|
51719
|
+
const selections = chunk.map((n, i) => `t${i}: __type(name: "${n}") { ...TD }`).join("\n");
|
|
51720
|
+
const query = `{
|
|
51721
|
+
${selections}
|
|
51722
|
+
}${TYPE_DETAILS_FRAGMENT}${TYPE_REF_FRAGMENT}`;
|
|
51723
|
+
const batch = postIntrospection(query, options);
|
|
51724
|
+
chunk.forEach((n, i) => {
|
|
51725
|
+
const p2 = batch.then(
|
|
51726
|
+
(data) => data?.[`t${i}`] ?? null
|
|
51727
|
+
);
|
|
51728
|
+
p2.catch(() => {
|
|
51729
|
+
if (typeCache.get(n) === p2) typeCache.delete(n);
|
|
51730
|
+
});
|
|
51731
|
+
typeCache.set(n, p2);
|
|
51732
|
+
});
|
|
51733
|
+
await batch.catch(() => void 0);
|
|
51734
|
+
}
|
|
51735
|
+
const result = /* @__PURE__ */ new Map();
|
|
51736
|
+
await Promise.all(
|
|
51737
|
+
unique.map(async (n) => {
|
|
51738
|
+
result.set(n, await typeCache.get(n));
|
|
51739
|
+
})
|
|
51740
|
+
);
|
|
51741
|
+
return result;
|
|
51742
|
+
}
|
|
51743
|
+
function renderTypeRef(ref) {
|
|
51744
|
+
if (!ref) return "Unknown";
|
|
51745
|
+
if (ref.kind === "NON_NULL") return `${renderTypeRef(ref.ofType)}!`;
|
|
51746
|
+
if (ref.kind === "LIST") return `[${renderTypeRef(ref.ofType)}]`;
|
|
51747
|
+
return ref.name ?? "Unknown";
|
|
51748
|
+
}
|
|
51749
|
+
function namedType(ref) {
|
|
51750
|
+
let cur = ref;
|
|
51751
|
+
while (cur && (cur.kind === "NON_NULL" || cur.kind === "LIST")) {
|
|
51752
|
+
cur = cur.ofType ?? null;
|
|
51753
|
+
}
|
|
51754
|
+
return cur?.name ?? null;
|
|
51755
|
+
}
|
|
51756
|
+
var BUILTIN_SCALARS = /* @__PURE__ */ new Set([
|
|
51757
|
+
"String",
|
|
51758
|
+
"Int",
|
|
51759
|
+
"Float",
|
|
51760
|
+
"Boolean",
|
|
51761
|
+
"ID",
|
|
51762
|
+
"JSON"
|
|
51763
|
+
]);
|
|
51764
|
+
function isBuiltinScalar(name) {
|
|
51765
|
+
return !!name && BUILTIN_SCALARS.has(name);
|
|
51766
|
+
}
|
|
51767
|
+
function oneLine(text, max) {
|
|
51768
|
+
if (!text) return "";
|
|
51769
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
51770
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
51771
|
+
}
|
|
51772
|
+
function renderField(field, isInput) {
|
|
51773
|
+
const args = !isInput && field.args?.length ? `(${field.args.map((a2) => `${a2.name}: ${renderTypeRef(a2.type)}`).join(", ")})` : "";
|
|
51774
|
+
const def = isInput && field.defaultValue != null ? ` = ${field.defaultValue}` : "";
|
|
51775
|
+
const comment = field.description ? ` # ${oneLine(field.description, 140)}` : "";
|
|
51776
|
+
return ` ${field.name}${args}: ${renderTypeRef(field.type)}${def}${comment}`;
|
|
51777
|
+
}
|
|
51778
|
+
function renderType(type) {
|
|
51779
|
+
const references = /* @__PURE__ */ new Set();
|
|
51780
|
+
const collect = (ref) => {
|
|
51781
|
+
const n = namedType(ref);
|
|
51782
|
+
if (n && !isBuiltinScalar(n) && n !== type.name) references.add(n);
|
|
51783
|
+
};
|
|
51784
|
+
const header = type.description ? `# ${oneLine(type.description, 200)}
|
|
51785
|
+
` : "";
|
|
51786
|
+
let body;
|
|
51787
|
+
switch (type.kind) {
|
|
51788
|
+
case "ENUM":
|
|
51789
|
+
body = `enum ${type.name} {
|
|
51790
|
+
${(type.enumValues ?? []).map(
|
|
51791
|
+
(v) => ` ${v.name}${v.description ? ` # ${oneLine(v.description, 100)}` : ""}`
|
|
51792
|
+
).join("\n")}
|
|
51793
|
+
}`;
|
|
51794
|
+
break;
|
|
51795
|
+
case "INPUT_OBJECT":
|
|
51796
|
+
(type.inputFields ?? []).forEach((f2) => collect(f2.type));
|
|
51797
|
+
body = `input ${type.name} {
|
|
51798
|
+
${(type.inputFields ?? []).map((f2) => renderField(f2, true)).join("\n")}
|
|
51799
|
+
}`;
|
|
51800
|
+
break;
|
|
51801
|
+
case "UNION":
|
|
51802
|
+
(type.possibleTypes ?? []).forEach((p2) => references.add(p2.name));
|
|
51803
|
+
body = `union ${type.name} = ${(type.possibleTypes ?? []).map((p2) => p2.name).join(" | ")}`;
|
|
51804
|
+
break;
|
|
51805
|
+
case "SCALAR":
|
|
51806
|
+
body = `scalar ${type.name}`;
|
|
51807
|
+
break;
|
|
51808
|
+
default: {
|
|
51809
|
+
(type.fields ?? []).forEach((f2) => {
|
|
51810
|
+
collect(f2.type);
|
|
51811
|
+
f2.args?.forEach((a2) => collect(a2.type));
|
|
51812
|
+
});
|
|
51813
|
+
const keyword = type.kind === "INTERFACE" ? "interface" : "type";
|
|
51814
|
+
body = `${keyword} ${type.name} {
|
|
51815
|
+
${(type.fields ?? []).map((f2) => renderField(f2, false)).join("\n")}
|
|
51816
|
+
}`;
|
|
51817
|
+
}
|
|
51818
|
+
}
|
|
51819
|
+
return { text: header + body, references: [...references] };
|
|
51820
|
+
}
|
|
51821
|
+
function renderOperationSignature(op) {
|
|
51822
|
+
const args = op.args.map((a2) => `${a2.name}: ${renderTypeRef(a2.type)}`).join(", ");
|
|
51823
|
+
return `${op.kind === "query" ? "Query" : "Mutation"}.${op.name}(${args}): ${renderTypeRef(op.type)}`;
|
|
51824
|
+
}
|
|
51825
|
+
async function expandTypes(rootTypeNames, options = {}) {
|
|
51826
|
+
const depth = Math.max(1, Math.min(options.depth ?? 2, 5));
|
|
51827
|
+
const maxTypes = options.maxTypes ?? 40;
|
|
51828
|
+
const rendered = [];
|
|
51829
|
+
const unknown2 = [];
|
|
51830
|
+
const seen = /* @__PURE__ */ new Set();
|
|
51831
|
+
let frontier = [...new Set(rootTypeNames)].filter((n) => !isBuiltinScalar(n));
|
|
51832
|
+
let level = 1;
|
|
51833
|
+
let overflow = [];
|
|
51834
|
+
while (frontier.length > 0 && level <= depth) {
|
|
51835
|
+
const room = maxTypes - seen.size;
|
|
51836
|
+
if (room <= 0) {
|
|
51837
|
+
overflow = frontier;
|
|
51838
|
+
break;
|
|
51839
|
+
}
|
|
51840
|
+
const batch = frontier.slice(0, room);
|
|
51841
|
+
overflow = frontier.slice(room);
|
|
51842
|
+
batch.forEach((n) => seen.add(n));
|
|
51843
|
+
const types = await getTypes(batch, options);
|
|
51844
|
+
const next = /* @__PURE__ */ new Set();
|
|
51845
|
+
for (const name of batch) {
|
|
51846
|
+
const type = types.get(name);
|
|
51847
|
+
if (!type) {
|
|
51848
|
+
unknown2.push(name);
|
|
51849
|
+
continue;
|
|
51850
|
+
}
|
|
51851
|
+
const { text, references } = renderType(type);
|
|
51852
|
+
rendered.push(text);
|
|
51853
|
+
references.forEach((r) => {
|
|
51854
|
+
if (!seen.has(r)) next.add(r);
|
|
51855
|
+
});
|
|
51856
|
+
}
|
|
51857
|
+
frontier = [...next];
|
|
51858
|
+
level++;
|
|
51859
|
+
}
|
|
51860
|
+
const unexpanded = [.../* @__PURE__ */ new Set([...overflow, ...frontier])].filter(
|
|
51861
|
+
(n) => !seen.has(n) && !isBuiltinScalar(n)
|
|
51862
|
+
);
|
|
51863
|
+
return { rendered, unexpanded, unknown: unknown2 };
|
|
51864
|
+
}
|
|
51865
|
+
|
|
51866
|
+
// src/graphql/search.ts
|
|
51867
|
+
function tokenize(text) {
|
|
51868
|
+
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);
|
|
51869
|
+
}
|
|
51870
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
51871
|
+
"a",
|
|
51872
|
+
"an",
|
|
51873
|
+
"the",
|
|
51874
|
+
"of",
|
|
51875
|
+
"for",
|
|
51876
|
+
"to",
|
|
51877
|
+
"in",
|
|
51878
|
+
"on",
|
|
51879
|
+
"with",
|
|
51880
|
+
"and",
|
|
51881
|
+
"or",
|
|
51882
|
+
"by",
|
|
51883
|
+
"all",
|
|
51884
|
+
"my",
|
|
51885
|
+
"me",
|
|
51886
|
+
"i",
|
|
51887
|
+
"from",
|
|
51888
|
+
"is",
|
|
51889
|
+
"are",
|
|
51890
|
+
"be",
|
|
51891
|
+
"it",
|
|
51892
|
+
"this",
|
|
51893
|
+
"that",
|
|
51894
|
+
"wix",
|
|
51895
|
+
"api",
|
|
51896
|
+
"graphql"
|
|
51897
|
+
]);
|
|
51898
|
+
function stem(token) {
|
|
51899
|
+
if (token.length > 4 && token.endsWith("ies"))
|
|
51900
|
+
return `${token.slice(0, -3)}y`;
|
|
51901
|
+
if (token.length > 3 && token.endsWith("es") && !token.endsWith("ses"))
|
|
51902
|
+
return token.slice(0, -1);
|
|
51903
|
+
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss"))
|
|
51904
|
+
return token.slice(0, -1);
|
|
51905
|
+
return token;
|
|
51906
|
+
}
|
|
51907
|
+
var VERB_SYNONYMS = {
|
|
51908
|
+
list: ["query", "list", "search"],
|
|
51909
|
+
find: ["query", "search", "get"],
|
|
51910
|
+
fetch: ["get", "query"],
|
|
51911
|
+
read: ["get", "query"],
|
|
51912
|
+
show: ["get", "query"],
|
|
51913
|
+
count: ["count", "query"],
|
|
51914
|
+
add: ["create", "add"],
|
|
51915
|
+
new: ["create"],
|
|
51916
|
+
insert: ["create"],
|
|
51917
|
+
edit: ["update"],
|
|
51918
|
+
change: ["update"],
|
|
51919
|
+
modify: ["update"],
|
|
51920
|
+
set: ["update"],
|
|
51921
|
+
remove: ["delete", "remove"],
|
|
51922
|
+
destroy: ["delete"],
|
|
51923
|
+
cancel: ["cancel", "delete"],
|
|
51924
|
+
customer: ["contact", "member"],
|
|
51925
|
+
client: ["contact", "member"],
|
|
51926
|
+
item: ["product", "item"],
|
|
51927
|
+
inventory: ["inventory", "stock"],
|
|
51928
|
+
stock: ["inventory", "stock"],
|
|
51929
|
+
post: ["post", "blog"],
|
|
51930
|
+
article: ["post", "blog"],
|
|
51931
|
+
appointment: ["booking", "session"],
|
|
51932
|
+
collection: ["collection", "category", "data"],
|
|
51933
|
+
cms: ["data", "collection"],
|
|
51934
|
+
store: ["stores", "store"]
|
|
51935
|
+
};
|
|
51936
|
+
function expandQueryTokens(tokens) {
|
|
51937
|
+
const out = /* @__PURE__ */ new Set();
|
|
51938
|
+
for (const raw of tokens) {
|
|
51939
|
+
if (STOP_WORDS.has(raw)) continue;
|
|
51940
|
+
const s2 = stem(raw);
|
|
51941
|
+
out.add(s2);
|
|
51942
|
+
for (const syn of VERB_SYNONYMS[raw] ?? VERB_SYNONYMS[s2] ?? []) {
|
|
51943
|
+
out.add(stem(syn));
|
|
51944
|
+
}
|
|
51945
|
+
}
|
|
51946
|
+
return out;
|
|
51947
|
+
}
|
|
51948
|
+
var READ_VERBS = /* @__PURE__ */ new Set([
|
|
51949
|
+
"list",
|
|
51950
|
+
"get",
|
|
51951
|
+
"query",
|
|
51952
|
+
"find",
|
|
51953
|
+
"fetch",
|
|
51954
|
+
"read",
|
|
51955
|
+
"show",
|
|
51956
|
+
"retrieve",
|
|
51957
|
+
"search",
|
|
51958
|
+
"count",
|
|
51959
|
+
"view",
|
|
51960
|
+
"see"
|
|
51961
|
+
]);
|
|
51962
|
+
var WRITE_VERBS = /* @__PURE__ */ new Set([
|
|
51963
|
+
"create",
|
|
51964
|
+
"add",
|
|
51965
|
+
"new",
|
|
51966
|
+
"insert",
|
|
51967
|
+
"update",
|
|
51968
|
+
"edit",
|
|
51969
|
+
"change",
|
|
51970
|
+
"modify",
|
|
51971
|
+
"set",
|
|
51972
|
+
"delete",
|
|
51973
|
+
"remove",
|
|
51974
|
+
"destroy",
|
|
51975
|
+
"cancel",
|
|
51976
|
+
"bulk",
|
|
51977
|
+
"merge",
|
|
51978
|
+
"publish",
|
|
51979
|
+
"send"
|
|
51980
|
+
]);
|
|
51981
|
+
var isVersionToken = (t) => t === "v" || /^\d+$/.test(t);
|
|
51982
|
+
function stripMarkdown(text) {
|
|
51983
|
+
return text.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/[`*_]/g, "");
|
|
51984
|
+
}
|
|
51985
|
+
function firstSentence(text, max = 220) {
|
|
51986
|
+
const flat = stripMarkdown(text).replace(/\s+/g, " ").trim();
|
|
51987
|
+
const cut = flat.search(/(?<=[.!?])\s/);
|
|
51988
|
+
const sentence = cut > 0 ? flat.slice(0, cut) : flat;
|
|
51989
|
+
return sentence.length > max ? `${sentence.slice(0, max - 1)}\u2026` : sentence;
|
|
51990
|
+
}
|
|
51991
|
+
var indexCache = /* @__PURE__ */ new WeakMap();
|
|
51992
|
+
function indexOperations(ops) {
|
|
51993
|
+
let indexed = indexCache.get(ops);
|
|
51994
|
+
if (!indexed) {
|
|
51995
|
+
indexed = ops.map((op) => ({
|
|
51996
|
+
op,
|
|
51997
|
+
nameTokens: new Set(tokenize(op.name).map(stem)),
|
|
51998
|
+
descTokens: new Set(tokenize(op.description.slice(0, 600)).map(stem))
|
|
51999
|
+
}));
|
|
52000
|
+
indexCache.set(ops, indexed);
|
|
52001
|
+
}
|
|
52002
|
+
return indexed;
|
|
52003
|
+
}
|
|
52004
|
+
function searchOperations(ops, query, options = {}) {
|
|
52005
|
+
const { kind = "all", maxResults = 10 } = options;
|
|
52006
|
+
const rawTokens = tokenize(query);
|
|
52007
|
+
const terms = expandQueryTokens(rawTokens);
|
|
52008
|
+
const originalTerms = new Set(
|
|
52009
|
+
rawTokens.filter((t) => !STOP_WORDS.has(t)).map(stem)
|
|
52010
|
+
);
|
|
52011
|
+
const exactName = query.trim();
|
|
52012
|
+
const hasWriteVerb = rawTokens.some((t) => WRITE_VERBS.has(t));
|
|
52013
|
+
const nounTokens = rawTokens.filter(
|
|
52014
|
+
(t) => !STOP_WORDS.has(t) && !READ_VERBS.has(t) && !WRITE_VERBS.has(t)
|
|
52015
|
+
);
|
|
52016
|
+
const pluralIntent = nounTokens.length > 0 && nounTokens.some((t) => stem(t) !== t);
|
|
52017
|
+
const readIntent = !hasWriteVerb;
|
|
52018
|
+
const matches = [];
|
|
52019
|
+
for (const { op, nameTokens, descTokens } of indexOperations(ops)) {
|
|
52020
|
+
if (kind !== "all" && op.kind !== kind) continue;
|
|
52021
|
+
let score = 0;
|
|
52022
|
+
let originalHits = 0;
|
|
52023
|
+
const matchedNameTokens = /* @__PURE__ */ new Set();
|
|
52024
|
+
for (const term of terms) {
|
|
52025
|
+
const isOriginal = originalTerms.has(term);
|
|
52026
|
+
if (op.kind === "query" && readIntent && READ_VERBS.has(term)) {
|
|
52027
|
+
score += isOriginal ? 4 : 2;
|
|
52028
|
+
if (isOriginal) originalHits++;
|
|
52029
|
+
if (nameTokens.has(term)) matchedNameTokens.add(term);
|
|
52030
|
+
continue;
|
|
52031
|
+
}
|
|
52032
|
+
if (nameTokens.has(term)) {
|
|
52033
|
+
matchedNameTokens.add(term);
|
|
52034
|
+
score += isOriginal ? 4 : 2;
|
|
52035
|
+
if (isOriginal) originalHits++;
|
|
52036
|
+
continue;
|
|
52037
|
+
}
|
|
52038
|
+
let prefixHit = false;
|
|
52039
|
+
for (const nt2 of nameTokens) {
|
|
52040
|
+
if (term.length >= 3 && (nt2.startsWith(term) || term.startsWith(nt2))) {
|
|
52041
|
+
prefixHit = true;
|
|
52042
|
+
matchedNameTokens.add(nt2);
|
|
52043
|
+
break;
|
|
52044
|
+
}
|
|
52045
|
+
}
|
|
52046
|
+
if (prefixHit) {
|
|
52047
|
+
score += isOriginal ? 2 : 1;
|
|
52048
|
+
if (isOriginal) originalHits++;
|
|
52049
|
+
continue;
|
|
52050
|
+
}
|
|
52051
|
+
if (descTokens.has(term)) {
|
|
52052
|
+
score += 1;
|
|
52053
|
+
if (isOriginal) originalHits++;
|
|
52054
|
+
}
|
|
52055
|
+
}
|
|
52056
|
+
if (score <= 0 && op.name !== exactName) continue;
|
|
52057
|
+
if (op.name === exactName) score += 100;
|
|
52058
|
+
if (originalTerms.size > 0 && originalHits === originalTerms.size) {
|
|
52059
|
+
score += 3;
|
|
52060
|
+
}
|
|
52061
|
+
const lastToken = tokenize(op.name).filter((t) => !isVersionToken(t)).pop();
|
|
52062
|
+
if (lastToken && nounTokens.length > 0) {
|
|
52063
|
+
const pluralOp = stem(lastToken) !== lastToken;
|
|
52064
|
+
score += pluralOp === pluralIntent ? 0.5 : -0.5;
|
|
52065
|
+
}
|
|
52066
|
+
if (/DoNotCall/i.test(op.name)) score -= 2;
|
|
52067
|
+
for (const nt2 of nameTokens) {
|
|
52068
|
+
if (!isVersionToken(nt2) && !matchedNameTokens.has(nt2)) score -= 0.3;
|
|
52069
|
+
}
|
|
52070
|
+
if (readIntent && op.kind === "mutation" && [...nameTokens].some((t) => WRITE_VERBS.has(t))) {
|
|
52071
|
+
score -= 6;
|
|
52072
|
+
}
|
|
52073
|
+
if (op.kind === "query" && readIntent) score += 1;
|
|
52074
|
+
const version3 = op.name.match(/V(\d+)/);
|
|
52075
|
+
if (version3) score += Math.min(Number(version3[1]), 5) * 0.1;
|
|
52076
|
+
if (!op.description) score -= 0.5;
|
|
52077
|
+
score -= op.name.length / 200;
|
|
52078
|
+
if (score <= 0) continue;
|
|
52079
|
+
matches.push({
|
|
52080
|
+
operation: op.name,
|
|
52081
|
+
kind: op.kind,
|
|
52082
|
+
signature: `${op.name}(${op.args.map((a2) => `${a2.name}: ${renderTypeRef(a2.type)}`).join(", ")}): ${renderTypeRef(op.type)}`,
|
|
52083
|
+
description: firstSentence(op.description),
|
|
52084
|
+
inputType: op.args[0] ? namedType(op.args[0].type) : null,
|
|
52085
|
+
returnType: namedType(op.type),
|
|
52086
|
+
score: Math.round(score * 100) / 100
|
|
52087
|
+
});
|
|
52088
|
+
}
|
|
52089
|
+
matches.sort(
|
|
52090
|
+
(a2, b2) => b2.score - a2.score || a2.operation.localeCompare(b2.operation)
|
|
52091
|
+
);
|
|
52092
|
+
return matches.slice(0, Math.max(1, Math.min(maxResults, 50)));
|
|
52093
|
+
}
|
|
52094
|
+
|
|
52095
|
+
// src/graphql/index.ts
|
|
52096
|
+
var textResult = (text) => ({
|
|
52097
|
+
content: [{ type: "text", text }]
|
|
52098
|
+
});
|
|
52099
|
+
var errorResult2 = (text) => ({
|
|
52100
|
+
isError: true,
|
|
52101
|
+
content: [{ type: "text", text }]
|
|
52102
|
+
});
|
|
52103
|
+
var SCHEMA_MAX_RESPONSE_CHARS = 48e3;
|
|
52104
|
+
function addGraphqlTools(server, options = {}) {
|
|
52105
|
+
const { authStrategy, disableTools = [], toolDescriptions } = options;
|
|
52106
|
+
const getDescription = (name) => toolDescriptions?.[name] ?? defaultToolDescriptions[name];
|
|
52107
|
+
if (!disableTools.includes(SEARCH_GRAPHQL_TOOL_NAME)) {
|
|
52108
|
+
addSearchGraphqlTool(
|
|
52109
|
+
server,
|
|
52110
|
+
getDescription(SEARCH_GRAPHQL_TOOL_NAME),
|
|
52111
|
+
options
|
|
52112
|
+
);
|
|
52113
|
+
}
|
|
52114
|
+
if (!disableTools.includes(INSPECT_GRAPHQL_TOOL_NAME)) {
|
|
52115
|
+
addInspectGraphqlTool(
|
|
52116
|
+
server,
|
|
52117
|
+
getDescription(INSPECT_GRAPHQL_TOOL_NAME),
|
|
52118
|
+
options
|
|
52119
|
+
);
|
|
52120
|
+
}
|
|
52121
|
+
if (authStrategy && !disableTools.includes(EXECUTE_GRAPHQL_TOOL_NAME)) {
|
|
52122
|
+
addExecuteGraphqlTool(
|
|
52123
|
+
server,
|
|
52124
|
+
getDescription(EXECUTE_GRAPHQL_TOOL_NAME),
|
|
52125
|
+
authStrategy,
|
|
52126
|
+
options
|
|
52127
|
+
);
|
|
52128
|
+
}
|
|
52129
|
+
}
|
|
52130
|
+
function introspectionOptions(options) {
|
|
52131
|
+
return {
|
|
52132
|
+
endpoint: options.endpoint,
|
|
52133
|
+
fetchImpl: options.fetchImpl,
|
|
52134
|
+
retryDelaysMs: options.retryDelaysMs
|
|
52135
|
+
};
|
|
52136
|
+
}
|
|
52137
|
+
function addSearchGraphqlTool(server, description, options) {
|
|
52138
|
+
const d2 = desc("SearchWixGraphQLOperations");
|
|
52139
|
+
server.tool(
|
|
52140
|
+
SEARCH_GRAPHQL_TOOL_NAME,
|
|
52141
|
+
description,
|
|
52142
|
+
{
|
|
52143
|
+
query: external_exports.string().describe(d2.query),
|
|
52144
|
+
kind: external_exports.enum(["query", "mutation", "all"]).optional().describe(d2.kind),
|
|
52145
|
+
maxResults: external_exports.number().int().min(1).max(50).optional().describe(d2.maxResults),
|
|
52146
|
+
reason: external_exports.string().describe(d2.reason)
|
|
52147
|
+
},
|
|
52148
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
52149
|
+
async ({ query, kind, maxResults, reason }) => {
|
|
52150
|
+
logger2.log(
|
|
52151
|
+
`[${SEARCH_GRAPHQL_TOOL_NAME}] query="${query}" kind=${kind ?? "all"} reason="${reason}"`
|
|
52152
|
+
);
|
|
52153
|
+
try {
|
|
52154
|
+
const ops = await getRootOperations(introspectionOptions(options));
|
|
52155
|
+
const matches = searchOperations(ops, query, {
|
|
52156
|
+
kind: kind ?? "all",
|
|
52157
|
+
maxResults: maxResults ?? 10
|
|
52158
|
+
});
|
|
52159
|
+
if (matches.length === 0) {
|
|
52160
|
+
return textResult(
|
|
52161
|
+
`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.`
|
|
52162
|
+
);
|
|
52163
|
+
}
|
|
52164
|
+
const lines = matches.map(
|
|
52165
|
+
(m2, i) => `${i + 1}. ${m2.kind} ${m2.signature}
|
|
52166
|
+
${m2.description || "(no description)"}`
|
|
52167
|
+
);
|
|
52168
|
+
const text = [
|
|
52169
|
+
`Top ${matches.length} of ${ops.length} Wix GraphQL root operations for "${query}":`,
|
|
52170
|
+
"",
|
|
52171
|
+
...lines,
|
|
52172
|
+
"",
|
|
52173
|
+
`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.`
|
|
52174
|
+
].join("\n");
|
|
52175
|
+
return textResult(text);
|
|
52176
|
+
} catch (err) {
|
|
52177
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
52178
|
+
logger2.log(`[${SEARCH_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
52179
|
+
return errorResult2(`GraphQL schema search error: ${message}`);
|
|
52180
|
+
}
|
|
52181
|
+
}
|
|
52182
|
+
);
|
|
52183
|
+
}
|
|
52184
|
+
function addInspectGraphqlTool(server, description, options) {
|
|
52185
|
+
const d2 = desc("InspectWixGraphQLSchema");
|
|
52186
|
+
const maxChars = options.maxResponseSize ?? SCHEMA_MAX_RESPONSE_CHARS;
|
|
52187
|
+
server.tool(
|
|
52188
|
+
INSPECT_GRAPHQL_TOOL_NAME,
|
|
52189
|
+
description,
|
|
52190
|
+
{
|
|
52191
|
+
operation: external_exports.string().optional().describe(d2.operation),
|
|
52192
|
+
typeNames: external_exports.array(external_exports.string()).optional().describe(d2.typeNames),
|
|
52193
|
+
depth: external_exports.number().int().min(1).max(4).optional().describe(d2.depth),
|
|
52194
|
+
reason: external_exports.string().describe(d2.reason)
|
|
52195
|
+
},
|
|
52196
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
52197
|
+
async ({ operation, typeNames, depth, reason }) => {
|
|
52198
|
+
logger2.log(
|
|
52199
|
+
`[${INSPECT_GRAPHQL_TOOL_NAME}] operation=${operation ?? "-"} typeNames=${JSON.stringify(typeNames ?? [])} depth=${depth ?? 2} reason="${reason}"`
|
|
52200
|
+
);
|
|
52201
|
+
if (!operation && !(typeNames && typeNames.length > 0)) {
|
|
52202
|
+
return errorResult2(
|
|
52203
|
+
"Pass `operation` (a root query/mutation name from SearchWixGraphQLOperations) and/or `typeNames` (type names to expand)."
|
|
52204
|
+
);
|
|
52205
|
+
}
|
|
52206
|
+
try {
|
|
52207
|
+
const io2 = introspectionOptions(options);
|
|
52208
|
+
const sections = [];
|
|
52209
|
+
const roots = [];
|
|
52210
|
+
if (operation) {
|
|
52211
|
+
const ops = await getRootOperations(io2);
|
|
52212
|
+
const op = ops.find((o) => o.name === operation);
|
|
52213
|
+
if (!op) {
|
|
52214
|
+
const suggestions = searchOperations(ops, operation, {
|
|
52215
|
+
maxResults: 5
|
|
52216
|
+
});
|
|
52217
|
+
return errorResult2(
|
|
52218
|
+
`No root operation named "${operation}". ${suggestions.length ? `Did you mean: ${suggestions.map((s2) => `${s2.kind} ${s2.operation}`).join(", ")}? Use SearchWixGraphQLOperations to discover the exact name.` : "Use SearchWixGraphQLOperations to discover the exact name."}`
|
|
52219
|
+
);
|
|
52220
|
+
}
|
|
52221
|
+
sections.push(
|
|
52222
|
+
`# ${renderOperationSignature(op)}`,
|
|
52223
|
+
op.description ? op.description.trim().split("\n").map((l) => `# ${l}`).join("\n") : "# (no description)"
|
|
52224
|
+
);
|
|
52225
|
+
op.args.forEach((a2) => {
|
|
52226
|
+
const n = namedType(a2.type);
|
|
52227
|
+
if (n) roots.push(n);
|
|
52228
|
+
});
|
|
52229
|
+
const ret = namedType(op.type);
|
|
52230
|
+
if (ret) roots.push(ret);
|
|
52231
|
+
}
|
|
52232
|
+
if (typeNames?.length) roots.push(...typeNames);
|
|
52233
|
+
const { rendered, unexpanded, unknown: unknown2 } = await expandTypes(roots, {
|
|
52234
|
+
...io2,
|
|
52235
|
+
depth: depth ?? 2
|
|
52236
|
+
});
|
|
52237
|
+
sections.push(...rendered);
|
|
52238
|
+
if (unknown2.length) {
|
|
52239
|
+
sections.push(
|
|
52240
|
+
`# Unknown type names (not in the schema): ${unknown2.join(", ")}`
|
|
52241
|
+
);
|
|
52242
|
+
}
|
|
52243
|
+
if (unexpanded.length) {
|
|
52244
|
+
sections.push(
|
|
52245
|
+
`# Not expanded (pass any of these in \`typeNames\` to read them): ${unexpanded.join(", ")}`
|
|
52246
|
+
);
|
|
52247
|
+
}
|
|
52248
|
+
sections.push(
|
|
52249
|
+
"# 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."
|
|
52250
|
+
);
|
|
52251
|
+
return textResult(truncateResponse(sections.join("\n\n"), maxChars));
|
|
52252
|
+
} catch (err) {
|
|
52253
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
52254
|
+
logger2.log(`[${INSPECT_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
52255
|
+
return errorResult2(`GraphQL schema inspection error: ${message}`);
|
|
52256
|
+
}
|
|
52257
|
+
}
|
|
52258
|
+
);
|
|
52259
|
+
}
|
|
52260
|
+
function addExecuteGraphqlTool(server, description, authStrategy, options) {
|
|
52261
|
+
const d2 = desc("ExecuteWixGraphQL");
|
|
52262
|
+
const endpoint = options.endpoint ?? WIX_GRAPHQL_ENDPOINT;
|
|
52263
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
52264
|
+
server.tool(
|
|
52265
|
+
EXECUTE_GRAPHQL_TOOL_NAME,
|
|
52266
|
+
description,
|
|
52267
|
+
{
|
|
52268
|
+
query: external_exports.string().describe(d2.query),
|
|
52269
|
+
variables: external_exports.record(external_exports.unknown()).optional().describe(d2.variables),
|
|
52270
|
+
operationName: external_exports.string().optional().describe(d2.operationName),
|
|
52271
|
+
siteId: external_exports.string().optional().describe(d2.siteId),
|
|
52272
|
+
scope: external_exports.enum(["site", "account"]).optional().describe(d2.scope),
|
|
52273
|
+
hasMutations: external_exports.boolean().describe(d2.hasMutations),
|
|
52274
|
+
reason: external_exports.string().describe(d2.reason)
|
|
52275
|
+
},
|
|
52276
|
+
{ readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
52277
|
+
async ({ query, variables, operationName, siteId, scope, hasMutations, reason }, extra) => {
|
|
52278
|
+
const effectiveScope = scope ?? (siteId ? "site" : "account");
|
|
52279
|
+
logger2.log(
|
|
52280
|
+
`[${EXECUTE_GRAPHQL_TOOL_NAME}] scope=${effectiveScope} siteId=${siteId ?? "none"} hasMutations=${hasMutations} operationName=${operationName ?? "-"} reason="${reason}"`
|
|
52281
|
+
);
|
|
52282
|
+
if (effectiveScope === "site" && !siteId) {
|
|
52283
|
+
return errorResult2(
|
|
52284
|
+
'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.'
|
|
52285
|
+
);
|
|
52286
|
+
}
|
|
52287
|
+
if (!query.trim()) {
|
|
52288
|
+
return errorResult2("`query` must be a non-empty GraphQL document.");
|
|
52289
|
+
}
|
|
52290
|
+
const authHeaders = effectiveScope === "site" ? await authStrategy.getSiteAuthHeaders(siteId) : await authStrategy.getAccountAuthHeaders();
|
|
52291
|
+
try {
|
|
52292
|
+
const init = {
|
|
52293
|
+
method: "POST",
|
|
52294
|
+
headers: {
|
|
52295
|
+
"Content-Type": "application/json",
|
|
52296
|
+
Accept: "application/json",
|
|
52297
|
+
...authHeaders
|
|
52298
|
+
},
|
|
52299
|
+
body: JSON.stringify({
|
|
52300
|
+
query,
|
|
52301
|
+
...variables ? { variables } : {},
|
|
52302
|
+
...operationName ? { operationName } : {}
|
|
52303
|
+
})
|
|
52304
|
+
};
|
|
52305
|
+
const resp = hasMutations ? await fetchImpl(endpoint, init) : await fetchWithRetry(
|
|
52306
|
+
fetchImpl,
|
|
52307
|
+
endpoint,
|
|
52308
|
+
init,
|
|
52309
|
+
options.retryDelaysMs
|
|
52310
|
+
);
|
|
52311
|
+
const requestId = resp.headers?.get("x-wix-request-id") ?? void 0;
|
|
52312
|
+
extra?.setToolBiParams?.({ siteId, requestId });
|
|
52313
|
+
const text = await resp.text();
|
|
52314
|
+
if (!resp.ok) {
|
|
52315
|
+
await authStrategy.enforceValidWixToken?.({
|
|
52316
|
+
status: resp.status,
|
|
52317
|
+
response: { status: resp.status }
|
|
52318
|
+
});
|
|
52319
|
+
const body2 = /^\s*<(!doctype|html)/i.test(text) ? "(HTML error page)" : text.slice(0, 2e3);
|
|
52320
|
+
const hasObjectVariables = !!variables && Object.values(variables).some(
|
|
52321
|
+
(v) => v !== null && typeof v === "object"
|
|
52322
|
+
);
|
|
52323
|
+
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.` : "";
|
|
52324
|
+
return errorResult2(
|
|
52325
|
+
`Wix GraphQL request failed: ${resp.status} ${resp.statusText}.${requestId ? ` request id: ${requestId}.` : ""}${hint}
|
|
52326
|
+
${body2}`
|
|
52327
|
+
);
|
|
52328
|
+
}
|
|
52329
|
+
let json;
|
|
52330
|
+
try {
|
|
52331
|
+
json = JSON.parse(text);
|
|
52332
|
+
} catch {
|
|
52333
|
+
return errorResult2(
|
|
52334
|
+
`Wix GraphQL returned a non-JSON body:
|
|
52335
|
+
${text.slice(0, 2e3)}`
|
|
52336
|
+
);
|
|
52337
|
+
}
|
|
52338
|
+
const hasData = json.data != null && Object.values(json.data).some((v) => v !== null && v !== void 0);
|
|
52339
|
+
const errors = json.errors ?? [];
|
|
52340
|
+
if (!hasData && errors.length > 0) {
|
|
52341
|
+
const permissionDenied = errors.some(
|
|
52342
|
+
(e) => e.extensions?.message === "PERMISSION_DENIED"
|
|
52343
|
+
);
|
|
52344
|
+
return errorResult2(
|
|
52345
|
+
[
|
|
52346
|
+
`Wix GraphQL returned errors and no data${requestId ? ` (request id: ${requestId})` : ""}:`,
|
|
52347
|
+
JSON.stringify(errors, null, 2).slice(0, 4e3),
|
|
52348
|
+
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.'
|
|
52349
|
+
].join("\n")
|
|
52350
|
+
);
|
|
52351
|
+
}
|
|
52352
|
+
const body = { data: json.data };
|
|
52353
|
+
if (errors.length > 0) {
|
|
52354
|
+
body.errors = errors;
|
|
52355
|
+
body.note = "Partial result: some root fields failed (see errors[].path). Fields present in data succeeded.";
|
|
52356
|
+
}
|
|
52357
|
+
return textResult(truncateResponse(body, options.maxResponseSize));
|
|
52358
|
+
} catch (err) {
|
|
52359
|
+
rethrowIfUnauthorized(err);
|
|
52360
|
+
await authStrategy.enforceValidWixToken?.(err);
|
|
52361
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
52362
|
+
logger2.log(`[${EXECUTE_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
52363
|
+
return errorResult2(`Wix GraphQL execution error: ${message}`);
|
|
52364
|
+
}
|
|
52365
|
+
}
|
|
52366
|
+
);
|
|
52367
|
+
}
|
|
52368
|
+
|
|
51511
52369
|
// src/code-mode/validation.ts
|
|
51512
52370
|
var EXECUTE_FUNCTION_EXPRESSION_HINT = 'ExecuteWixAPI code must be a function expression, not a script body. Pass code like: async () => { const response = await wix.request({ scope: "site", method: "GET", url: "https://www.wixapis.com/..." }); return response.data; }. Do not write top-level const/await/return outside the function and do not invoke the function yourself.';
|
|
51513
52371
|
function validateExecuteCode(code) {
|
|
@@ -51993,6 +52851,20 @@ function createWixToolkit(options) {
|
|
|
51993
52851
|
maxResponseSize
|
|
51994
52852
|
});
|
|
51995
52853
|
}
|
|
52854
|
+
const requestedGraphqlTools = GRAPHQL_TOOL_NAMES.filter(
|
|
52855
|
+
(name) => requestedTools.has(name)
|
|
52856
|
+
);
|
|
52857
|
+
if (requestedGraphqlTools.length > 0) {
|
|
52858
|
+
addGraphqlTools(server, {
|
|
52859
|
+
authStrategy,
|
|
52860
|
+
disableTools: [
|
|
52861
|
+
...disableTools ?? [],
|
|
52862
|
+
...GRAPHQL_TOOL_NAMES.filter((n) => !requestedTools.has(n))
|
|
52863
|
+
],
|
|
52864
|
+
toolDescriptions: effectiveToolDescriptions,
|
|
52865
|
+
maxResponseSize
|
|
52866
|
+
});
|
|
52867
|
+
}
|
|
51996
52868
|
} else {
|
|
51997
52869
|
addApiCallTool(
|
|
51998
52870
|
server,
|
|
@@ -52179,13 +53051,17 @@ function createWixToolkit(options) {
|
|
|
52179
53051
|
DEFAULT_OPENAI_SECURITY_SCHEMES,
|
|
52180
53052
|
EDIT_SITE_TOOL_NAME,
|
|
52181
53053
|
EXECUTE_FUNCTION_EXPRESSION_HINT,
|
|
53054
|
+
EXECUTE_GRAPHQL_TOOL_NAME,
|
|
52182
53055
|
GET_SUGGESTED_DOMAINS_TOOL_NAME,
|
|
53056
|
+
GRAPHQL_TOOL_NAMES,
|
|
52183
53057
|
IMPORT_CLAUDE_DESIGN_TOOL_NAME,
|
|
53058
|
+
INSPECT_GRAPHQL_TOOL_NAME,
|
|
52184
53059
|
MENU_CONTENT_THRESHOLD,
|
|
52185
53060
|
OPENAI_WIDGET_MIME_TYPE,
|
|
52186
53061
|
OPEN_AI_SITE_WIDGET_CSP,
|
|
52187
53062
|
PULL_SITE_JOB_TOOL_NAME,
|
|
52188
53063
|
RECIPES_INDEX_URL,
|
|
53064
|
+
SEARCH_GRAPHQL_TOOL_NAME,
|
|
52189
53065
|
SEARCH_SITE_TEMPLATES_TOOL_NAME,
|
|
52190
53066
|
SITE_BUILDER_TOOL_NAME,
|
|
52191
53067
|
SITE_CREATION_COPY,
|
|
@@ -52198,6 +53074,7 @@ function createWixToolkit(options) {
|
|
|
52198
53074
|
VALID_DOCS_TOOLS,
|
|
52199
53075
|
WIXEL_ICON_PNG_URL,
|
|
52200
53076
|
WIXEL_ICON_SVG_URL,
|
|
53077
|
+
WIX_GRAPHQL_ENDPOINT,
|
|
52201
53078
|
WIX_SITE_CREATION_TOOL_NAME,
|
|
52202
53079
|
WixFetchError,
|
|
52203
53080
|
WixMcpServer,
|
|
@@ -52214,6 +53091,7 @@ function createWixToolkit(options) {
|
|
|
52214
53091
|
addGetWixelAssetTool,
|
|
52215
53092
|
addGetWixelJobStatusTool,
|
|
52216
53093
|
addGetWixelProjectAssetsTool,
|
|
53094
|
+
addGraphqlTools,
|
|
52217
53095
|
addImportClaudeDesignTool,
|
|
52218
53096
|
addListWixelEventsTool,
|
|
52219
53097
|
addMarkWixelJobAsFulfilledTool,
|
|
@@ -52223,6 +53101,7 @@ function createWixToolkit(options) {
|
|
|
52223
53101
|
addSendWixelMessageTool,
|
|
52224
53102
|
addSiteCreationWidgetTools,
|
|
52225
53103
|
addStartWixelSessionTool,
|
|
53104
|
+
addSuggestedDomainTool,
|
|
52226
53105
|
addSupportTool,
|
|
52227
53106
|
addWixSiteCreationTool,
|
|
52228
53107
|
addWixelSessionTools,
|
|
@@ -52247,6 +53126,7 @@ function createWixToolkit(options) {
|
|
|
52247
53126
|
mergeConfig,
|
|
52248
53127
|
normalizeMenuContent,
|
|
52249
53128
|
parseDirectChildren,
|
|
53129
|
+
resetGraphqlSchemaCache,
|
|
52250
53130
|
rethrowIfUnauthorized,
|
|
52251
53131
|
summarizeLargeMenu,
|
|
52252
53132
|
toWidget,
|