@wootsup/mcp 0.1.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +157 -83
- package/README.md +31 -27
- package/SECURITY.md +15 -6
- package/dist/auth/keychain.d.ts +27 -1
- package/dist/auth/keychain.js +48 -2
- package/dist/auth/keychain.js.map +1 -1
- package/dist/catalog/build-catalog.d.ts +31 -0
- package/dist/catalog/build-catalog.js +68 -0
- package/dist/catalog/build-catalog.js.map +1 -0
- package/dist/cli-hint.d.ts +22 -0
- package/dist/cli-hint.js +55 -0
- package/dist/cli-hint.js.map +1 -0
- package/dist/index.js +129 -22
- package/dist/index.js.map +1 -1
- package/dist/install-skill.js +1 -1
- package/dist/modules/apimapper/auto-layout.d.ts +21 -0
- package/dist/modules/apimapper/auto-layout.js +54 -0
- package/dist/modules/apimapper/auto-layout.js.map +1 -0
- package/dist/modules/apimapper/cache.js +25 -17
- package/dist/modules/apimapper/cache.js.map +1 -1
- package/dist/modules/apimapper/client.d.ts +115 -4
- package/dist/modules/apimapper/client.js +699 -304
- package/dist/modules/apimapper/client.js.map +1 -1
- package/dist/modules/apimapper/connections-format.d.ts +31 -1
- package/dist/modules/apimapper/connections-format.js +97 -5
- package/dist/modules/apimapper/connections-format.js.map +1 -1
- package/dist/modules/apimapper/connections.d.ts +9 -7
- package/dist/modules/apimapper/connections.js +449 -127
- package/dist/modules/apimapper/connections.js.map +1 -1
- package/dist/modules/apimapper/credential-sanitizer.d.ts +5 -0
- package/dist/modules/apimapper/credential-sanitizer.js +60 -1
- package/dist/modules/apimapper/credential-sanitizer.js.map +1 -1
- package/dist/modules/apimapper/credentials.js +105 -61
- package/dist/modules/apimapper/credentials.js.map +1 -1
- package/dist/modules/apimapper/diagnose.js +21 -2
- package/dist/modules/apimapper/diagnose.js.map +1 -1
- package/dist/modules/apimapper/elicitation.d.ts +29 -0
- package/dist/modules/apimapper/elicitation.js +62 -0
- package/dist/modules/apimapper/elicitation.js.map +1 -1
- package/dist/modules/apimapper/example-extract.d.ts +13 -0
- package/dist/modules/apimapper/example-extract.js +111 -0
- package/dist/modules/apimapper/example-extract.js.map +1 -0
- package/dist/modules/apimapper/filter-operators.d.ts +24 -0
- package/dist/modules/apimapper/filter-operators.js +103 -0
- package/dist/modules/apimapper/filter-operators.js.map +1 -0
- package/dist/modules/apimapper/flows-format.js +92 -22
- package/dist/modules/apimapper/flows-format.js.map +1 -1
- package/dist/modules/apimapper/flows.d.ts +8 -7
- package/dist/modules/apimapper/flows.js +275 -120
- package/dist/modules/apimapper/flows.js.map +1 -1
- package/dist/modules/apimapper/gateway/advanced-read-tool.d.ts +9 -0
- package/dist/modules/apimapper/gateway/advanced-read-tool.js +172 -0
- package/dist/modules/apimapper/gateway/advanced-read-tool.js.map +1 -0
- package/dist/modules/apimapper/gateway/advanced-tool.js +66 -106
- package/dist/modules/apimapper/gateway/advanced-tool.js.map +1 -1
- package/dist/modules/apimapper/gateway/collect-module-tools.d.ts +17 -0
- package/dist/modules/apimapper/gateway/collect-module-tools.js +44 -0
- package/dist/modules/apimapper/gateway/collect-module-tools.js.map +1 -0
- package/dist/modules/apimapper/gateway/essentials.d.ts +1 -1
- package/dist/modules/apimapper/gateway/essentials.js +21 -2
- package/dist/modules/apimapper/gateway/essentials.js.map +1 -1
- package/dist/modules/apimapper/gateway/gateway-shared.d.ts +21 -0
- package/dist/modules/apimapper/gateway/gateway-shared.js +124 -0
- package/dist/modules/apimapper/gateway/gateway-shared.js.map +1 -0
- package/dist/modules/apimapper/gateway/test-support.d.ts +1 -17
- package/dist/modules/apimapper/gateway/test-support.js +4 -33
- package/dist/modules/apimapper/gateway/test-support.js.map +1 -1
- package/dist/modules/apimapper/get-skill-cores.d.ts +4 -0
- package/dist/modules/apimapper/get-skill-cores.js +220 -0
- package/dist/modules/apimapper/get-skill-cores.js.map +1 -0
- package/dist/modules/apimapper/get-skill.d.ts +1 -1
- package/dist/modules/apimapper/get-skill.js +74 -9
- package/dist/modules/apimapper/get-skill.js.map +1 -1
- package/dist/modules/apimapper/graph-builder.d.ts +85 -2
- package/dist/modules/apimapper/graph-builder.js +151 -15
- package/dist/modules/apimapper/graph-builder.js.map +1 -1
- package/dist/modules/apimapper/graph.js +152 -48
- package/dist/modules/apimapper/graph.js.map +1 -1
- package/dist/modules/apimapper/index.js +27 -13
- package/dist/modules/apimapper/index.js.map +1 -1
- package/dist/modules/apimapper/jmespath-test.d.ts +4 -0
- package/dist/modules/apimapper/jmespath-test.js +152 -0
- package/dist/modules/apimapper/jmespath-test.js.map +1 -0
- package/dist/modules/apimapper/library.js +553 -88
- package/dist/modules/apimapper/library.js.map +1 -1
- package/dist/modules/apimapper/license.js +12 -36
- package/dist/modules/apimapper/license.js.map +1 -1
- package/dist/modules/apimapper/list-footer.d.ts +27 -0
- package/dist/modules/apimapper/list-footer.js +57 -0
- package/dist/modules/apimapper/list-footer.js.map +1 -0
- package/dist/modules/apimapper/local-sources.js +100 -57
- package/dist/modules/apimapper/local-sources.js.map +1 -1
- package/dist/modules/apimapper/mcp-client-identity.d.ts +32 -0
- package/dist/modules/apimapper/mcp-client-identity.js +70 -0
- package/dist/modules/apimapper/mcp-client-identity.js.map +1 -0
- package/dist/modules/apimapper/merge-constants.d.ts +6 -0
- package/dist/modules/apimapper/merge-constants.js +26 -0
- package/dist/modules/apimapper/merge-constants.js.map +1 -0
- package/dist/modules/apimapper/misc.js +13 -27
- package/dist/modules/apimapper/misc.js.map +1 -1
- package/dist/modules/apimapper/node-schema.d.ts +52 -2
- package/dist/modules/apimapper/node-schema.js +95 -4
- package/dist/modules/apimapper/node-schema.js.map +1 -1
- package/dist/modules/apimapper/onboarding.d.ts +59 -1
- package/dist/modules/apimapper/onboarding.js +231 -28
- package/dist/modules/apimapper/onboarding.js.map +1 -1
- package/dist/modules/apimapper/read-cache.d.ts +16 -3
- package/dist/modules/apimapper/read-cache.js +59 -4
- package/dist/modules/apimapper/read-cache.js.map +1 -1
- package/dist/modules/apimapper/render/index.js +26 -5
- package/dist/modules/apimapper/render/index.js.map +1 -1
- package/dist/modules/apimapper/resource-id.d.ts +13 -0
- package/dist/modules/apimapper/resource-id.js +69 -0
- package/dist/modules/apimapper/resource-id.js.map +1 -0
- package/dist/modules/apimapper/schema.js +9 -18
- package/dist/modules/apimapper/schema.js.map +1 -1
- package/dist/modules/apimapper/settings.js +49 -52
- package/dist/modules/apimapper/settings.js.map +1 -1
- package/dist/modules/apimapper/sites-tools.d.ts +29 -0
- package/dist/modules/apimapper/sites-tools.js +165 -0
- package/dist/modules/apimapper/sites-tools.js.map +1 -0
- package/dist/modules/apimapper/tool-result.d.ts +66 -0
- package/dist/modules/apimapper/tool-result.js +125 -0
- package/dist/modules/apimapper/tool-result.js.map +1 -0
- package/dist/modules/apimapper/toolslist-size.d.ts +12 -11
- package/dist/modules/apimapper/toolslist-size.js +34 -21
- package/dist/modules/apimapper/toolslist-size.js.map +1 -1
- package/dist/modules/apimapper/types.d.ts +34 -0
- package/dist/modules/apimapper/types.js +1 -1
- package/dist/modules/apimapper/types.js.map +1 -1
- package/dist/modules/apimapper/whitelist-drift.d.ts +85 -0
- package/dist/modules/apimapper/whitelist-drift.js +375 -0
- package/dist/modules/apimapper/whitelist-drift.js.map +1 -0
- package/dist/modules/apimapper/workflows.js +302 -58
- package/dist/modules/apimapper/workflows.js.map +1 -1
- package/dist/modules/apimapper/yootheme-binding.d.ts +35 -0
- package/dist/modules/apimapper/yootheme-binding.js +267 -0
- package/dist/modules/apimapper/yootheme-binding.js.map +1 -0
- package/dist/platform/index.d.ts +56 -0
- package/dist/platform/index.js +158 -2
- package/dist/platform/index.js.map +1 -1
- package/dist/proxy/bridge.d.ts +35 -0
- package/dist/proxy/bridge.js +129 -0
- package/dist/proxy/bridge.js.map +1 -0
- package/dist/proxy/mode.d.ts +9 -0
- package/dist/proxy/mode.js +20 -0
- package/dist/proxy/mode.js.map +1 -0
- package/dist/setup/detect-clients.d.ts +40 -1
- package/dist/setup/detect-clients.js +148 -1
- package/dist/setup/detect-clients.js.map +1 -1
- package/dist/setup/probe-auth.d.ts +51 -0
- package/dist/setup/probe-auth.js +141 -0
- package/dist/setup/probe-auth.js.map +1 -0
- package/dist/setup/probe-handshake.js +40 -7
- package/dist/setup/probe-handshake.js.map +1 -1
- package/dist/setup/remove-config.d.ts +8 -0
- package/dist/setup/remove-config.js +145 -0
- package/dist/setup/remove-config.js.map +1 -0
- package/dist/setup/uninstall.d.ts +34 -0
- package/dist/setup/uninstall.js +147 -0
- package/dist/setup/uninstall.js.map +1 -0
- package/dist/setup-cli.d.ts +16 -0
- package/dist/setup-cli.js +63 -1
- package/dist/setup-cli.js.map +1 -1
- package/dist/sites/loader.d.ts +48 -0
- package/dist/sites/loader.js +134 -0
- package/dist/sites/loader.js.map +1 -0
- package/dist/sites/schema.d.ts +69 -0
- package/dist/sites/schema.js +71 -0
- package/dist/sites/schema.js.map +1 -0
- package/dist/sites/secret-resolver.d.ts +47 -0
- package/dist/sites/secret-resolver.js +150 -0
- package/dist/sites/secret-resolver.js.map +1 -0
- package/dist/skill-instructions.d.ts +14 -1
- package/dist/skill-instructions.js +35 -6
- package/dist/skill-instructions.js.map +1 -1
- package/dist/transports/stdio.js +4 -4
- package/dist/transports/stdio.js.map +1 -1
- package/dist/uninstall-skill.d.ts +27 -0
- package/dist/uninstall-skill.js +89 -0
- package/dist/uninstall-skill.js.map +1 -0
- package/docs/architecture.md +21 -21
- package/docs/customgraph-internal-migration.md +4 -4
- package/docs/security.md +2 -21
- package/docs/tools.md +40 -12
- package/manifest.json +77 -79
- package/package.json +69 -65
- package/skills/apimapper/SKILL.md +128 -7
- package/skills/apimapper/reference/conditional-style-multi-items.md +114 -0
- package/skills/apimapper/reference/dynamize-existing-layout.md +158 -0
- package/skills/apimapper/reference/jmespath-cookbook.md +241 -0
- package/skills/apimapper/reference/jmespath-pitfalls.md +189 -0
- package/skills/apimapper/reference/joomla.md +1 -1
- package/skills/apimapper/reference/library-template-discovery.md +65 -0
- package/skills/apimapper/reference/merge-two-sources-on-key.md +204 -0
- package/skills/apimapper/reference/oauth.md +143 -52
- package/skills/apimapper/reference/troubleshooting.md +22 -2
- package/skills/apimapper/reference/yootheme-source-to-builder-handoff.md +348 -0
- package/skills/apimapper/reference/yootheme.md +75 -44
- package/dist/auth/oauth-provider.d.ts +0 -68
- package/dist/auth/oauth-provider.js +0 -232
- package/dist/auth/oauth-provider.js.map +0 -1
- package/dist/server-http.d.ts +0 -22
- package/dist/server-http.js +0 -159
- package/dist/server-http.js.map +0 -1
- package/dist/transports/http.d.ts +0 -29
- package/dist/transports/http.js +0 -267
- package/dist/transports/http.js.map +0 -1
|
@@ -4,6 +4,8 @@ import { request, hintFor } from "./client.js";
|
|
|
4
4
|
import { unwrapEntity } from "./envelope.js";
|
|
5
5
|
import { buildFlowGraph } from "./graph-builder.js";
|
|
6
6
|
import { normalizeMergeStrategy } from "./normalizers.js";
|
|
7
|
+
import { MERGE_STRATEGIES_INPUT, MERGE_JOIN_TYPES } from "./merge-constants.js";
|
|
8
|
+
import { FILTER_OPERATORS } from "./filter-operators.js";
|
|
7
9
|
/**
|
|
8
10
|
* F-37 (W1.22): matches the default labels emitted by
|
|
9
11
|
* `apimapper_flow_change_merge_strategy`. User-customised labels (which
|
|
@@ -14,8 +16,30 @@ import { normalizeMergeStrategy } from "./normalizers.js";
|
|
|
14
16
|
* casings count as user-custom and are preserved as-is.
|
|
15
17
|
*/
|
|
16
18
|
const DEFAULT_MERGE_LABEL_RE = /^Merge: (Concat|Join)$/;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
// F25 (2026-06-10): a local-source descriptor (CMS-native WordPress posts /
|
|
20
|
+
// Joomla articles) — queried straight from the platform DB, no Connection.
|
|
21
|
+
// Mirrors the `local-source` node-schema (`data.contentType`, limit, filters).
|
|
22
|
+
const LocalSourceSpec = z.object({
|
|
23
|
+
content_type: z
|
|
24
|
+
.string()
|
|
25
|
+
.min(1)
|
|
26
|
+
.describe('Fully-qualified content type, e.g. "wordpress/post", "wordpress/page", "joomla/article". ' +
|
|
27
|
+
'The bare "post" is rejected at save time — always qualify with the platform prefix.'),
|
|
28
|
+
limit: z.number().int().min(1).optional().describe("Max items (clamped to tier max server-side)"),
|
|
29
|
+
filters: z
|
|
30
|
+
.record(z.string(), z.unknown())
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('Server-side filters (e.g. {"category":"news"})'),
|
|
33
|
+
});
|
|
34
|
+
const SourceSpec = z
|
|
35
|
+
.object({
|
|
36
|
+
// F25: `connection` is now OPTIONAL — a source is EITHER a connection
|
|
37
|
+
// source OR a local source. The .superRefine below enforces exactly-one.
|
|
38
|
+
connection: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Connection ID (API source). Use apimapper_connection_list to find."),
|
|
42
|
+
local_source: LocalSourceSpec.optional().describe("Local-source descriptor (CMS-native source). Mutually exclusive with `connection`."),
|
|
19
43
|
endpoint: z.string().optional().describe('Endpoint id (e.g., "Scheduled Events")'),
|
|
20
44
|
template_fields: z
|
|
21
45
|
.record(z.string(), z.string())
|
|
@@ -23,16 +47,102 @@ const SourceSpec = z.object({
|
|
|
23
47
|
.describe('Template field values (e.g., {"user_uri":"...","spreadsheet_id":"..."})'),
|
|
24
48
|
items_path: z.string().optional().describe("Override items_path"),
|
|
25
49
|
label: z.string().optional().describe("Display label for the source node"),
|
|
50
|
+
})
|
|
51
|
+
.superRefine((s, ctx) => {
|
|
52
|
+
const hasConnection = typeof s.connection === "string" && s.connection !== "";
|
|
53
|
+
const hasLocal = !!s.local_source;
|
|
54
|
+
if (!hasConnection && !hasLocal) {
|
|
55
|
+
ctx.addIssue({
|
|
56
|
+
code: z.ZodIssueCode.custom,
|
|
57
|
+
message: "each source must set either `connection` (API source) or `local_source` (CMS source)",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (hasConnection && hasLocal) {
|
|
61
|
+
ctx.addIssue({
|
|
62
|
+
code: z.ZodIssueCode.custom,
|
|
63
|
+
message: "a source must set `connection` OR `local_source`, not both",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
26
66
|
});
|
|
67
|
+
// A8/F45 (2026-06-10): the merge join key is the make-or-break input of the
|
|
68
|
+
// two-source task, and a wrong/incomparable key silently yields 0 rows. The
|
|
69
|
+
// describe()s steer the agent to DISCOVER the key by sampling both sources
|
|
70
|
+
// (apimapper_connection_data) before choosing it, and name the
|
|
71
|
+
// connection_data sampling + the merge-two-sources-on-key topic explicitly so
|
|
72
|
+
// the contract is learnable at the chokepoint, not only inside a skill topic.
|
|
27
73
|
const MergeSpec = z.object({
|
|
28
|
-
strategy
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
74
|
+
// F156 — strategy + joinType enums come from the shared merge-constants so
|
|
75
|
+
// the composite path and the raw node-schema path can never drift. 'append'
|
|
76
|
+
// is the customer-facing alias normalized to 'concat' downstream.
|
|
77
|
+
strategy: z.enum(MERGE_STRATEGIES_INPUT).describe('Merge strategy ("append" alias for "concat")'),
|
|
78
|
+
// A8/F45/F149 — surface the joinKey DISCOVERY hint right at the chokepoint,
|
|
79
|
+
// not only in the merge-two-sources-on-key skill topic. A cold agent has no
|
|
80
|
+
// idea WHICH field is the shared key; point it at connection_data to inspect
|
|
81
|
+
// the rows and call out the same-type requirement.
|
|
82
|
+
joinKey: z
|
|
83
|
+
.string()
|
|
84
|
+
.optional()
|
|
85
|
+
.describe("Left-side join key field (required for strategy=join). This is the field PRESENT IN BOTH " +
|
|
86
|
+
"sources' rows on which they join. DISCOVER it first: call apimapper_connection_data on " +
|
|
87
|
+
"EACH source and find a field whose values overlap and are the SAME type/format on both " +
|
|
88
|
+
"sides (a string \"P-100\" never equals a number 42). If the keys differ in shape, add a " +
|
|
89
|
+
"Transform before the merge to make them comparable (e.g. date-primitive transforms for a " +
|
|
90
|
+
"weekday name ↔ ISO date). See apimapper_get_skill({ topic: 'merge-two-sources-on-key' }) " +
|
|
91
|
+
"→ 'Discovering the join key'."),
|
|
92
|
+
joinKeyRight: z
|
|
93
|
+
.string()
|
|
94
|
+
.optional()
|
|
95
|
+
.describe("Right-side join key field (defaults to joinKey). Set this when the two sources name the " +
|
|
96
|
+
"same value differently (e.g. left 'property_id' vs right 'propertyId'). Sample both with " +
|
|
97
|
+
"apimapper_connection_data to confirm the values actually match."),
|
|
98
|
+
// F156 — full SQL-style join kinds (left/right/inner/outer), matching the
|
|
99
|
+
// raw node-schema mergeNodeSchema. The old composite enum only had
|
|
100
|
+
// left/inner, so an agent that learned joinType:'outer' on the raw graph
|
|
101
|
+
// path got a hard reject here.
|
|
102
|
+
joinType: z.enum(MERGE_JOIN_TYPES).default("left").describe("Join type (left/right/inner/outer)"),
|
|
103
|
+
});
|
|
104
|
+
// F53/F54 (2026-06-10): one-shot filter. `operator` is a CLOSED enum so the
|
|
105
|
+
// schema itself advertises the valid vocabulary — a cold agent reaching for
|
|
106
|
+
// `greater_than` / `>` / `<` is rejected at the zod boundary with the full set
|
|
107
|
+
// named, instead of shipping a flow that silently yields 0 rows at render.
|
|
108
|
+
// Mirrors FilterOperators::ALL (PHP) and FILTER_OPERATOR_VALUES (admin-ui).
|
|
109
|
+
const FILTER_OPERATOR_ENUM = z.enum(FILTER_OPERATORS);
|
|
110
|
+
const FilterConditionSpec = z.object({
|
|
111
|
+
field: z.string().min(1).describe('Field to filter on (e.g., "price", "media_type", "user.role")'),
|
|
112
|
+
operator: FILTER_OPERATOR_ENUM.describe('Comparison operator. Numeric: gt/gte/lt/lte. Text: equals/not_equals/contains/' +
|
|
113
|
+
'starts_with/ends_with. Set: in/not_in. Presence: exists/not_exists/empty/not_empty. ' +
|
|
114
|
+
'Symbolic operators (">", "<") are NOT accepted — use "gt"/"lt".'),
|
|
115
|
+
value: z
|
|
116
|
+
.union([z.string(), z.number(), z.boolean(), z.null(), z.array(z.union([z.string(), z.number()]))])
|
|
117
|
+
.optional()
|
|
118
|
+
.describe('Value to compare against. Omit for value-less operators (exists/not_exists/empty/not_empty; ' +
|
|
119
|
+
"for empty/not_empty, 0 and '0' are NOT empty). " +
|
|
120
|
+
'Numeric operators auto-coerce formatted strings like "$1,250,000".'),
|
|
121
|
+
});
|
|
122
|
+
// Accepts EITHER an inline single condition `{ field, operator, value }` OR a
|
|
123
|
+
// `{ conditions: [...], logic }` list. buildFlowGraph()/normalizeFilterSpec()
|
|
124
|
+
// re-validates and normalises both shapes.
|
|
125
|
+
const FilterSpec = z.object({
|
|
126
|
+
field: z.string().min(1).optional().describe("Inline single-condition field (shorthand)"),
|
|
127
|
+
operator: FILTER_OPERATOR_ENUM.optional().describe("Inline single-condition operator (shorthand)"),
|
|
128
|
+
value: z
|
|
129
|
+
.union([z.string(), z.number(), z.boolean(), z.null(), z.array(z.union([z.string(), z.number()]))])
|
|
130
|
+
.optional()
|
|
131
|
+
.describe("Inline single-condition value (shorthand)"),
|
|
132
|
+
conditions: z.array(FilterConditionSpec).optional().describe("Explicit condition list (for 2+ conditions)"),
|
|
133
|
+
logic: z.enum(["and", "or"]).optional().describe('Combinator across conditions; defaults to "and"'),
|
|
32
134
|
});
|
|
33
135
|
const TransformSpec = z.object({
|
|
34
136
|
expression: z.string().default("[*]").describe('JMESPath expression (e.g., "[*]" or "[*].{title:name,id:id}")'),
|
|
35
137
|
});
|
|
138
|
+
// F181 (2026-06-12): a transform is EITHER one TransformSpec OR an array of
|
|
139
|
+
// them. The JMESPath depth cap (10 levels of bracket/pipe nesting) forces a
|
|
140
|
+
// deeply-nested reshape to be SPLIT into two (or more) Transform nodes piped
|
|
141
|
+
// together — each node's pipe resets the depth counter. The composite builder
|
|
142
|
+
// chains an array as source(s) → [merge →] [filter →] t1 → t2 → … → output. A
|
|
143
|
+
// single object and a one-element array produce an identical graph; an empty
|
|
144
|
+
// array is treated as no transform at all.
|
|
145
|
+
const TransformChainSpec = z.union([TransformSpec, z.array(TransformSpec)]);
|
|
36
146
|
const OutputSpec = z.object({
|
|
37
147
|
type: z.enum(["yootheme", "shortcode"]).default("yootheme").describe("Output type"),
|
|
38
148
|
name: z.string().min(1).describe("Source name shown in YOOtheme dropdown"),
|
|
@@ -42,23 +152,67 @@ export function registerWorkflowTools(server) {
|
|
|
42
152
|
// ── apimapper_flow_setup_with_sources ──────────────────────────────
|
|
43
153
|
server.registerTool("apimapper_flow_setup_with_sources", {
|
|
44
154
|
title: "Setup Multi-Source Flow (Composite)",
|
|
45
|
-
description: "Compose a complete flow from connections in one call.
|
|
155
|
+
description: "Compose a complete flow from connections AND/OR CMS-native local sources in one call. " +
|
|
156
|
+
"Sequence: " +
|
|
46
157
|
"(1) build graph via buildFlowGraph() — pure function, deterministic layout. " +
|
|
47
158
|
"(2) POST /flows (creates row). " +
|
|
48
159
|
"(3) POST /flows/{id}/compile (publishes to YOOtheme). " +
|
|
49
|
-
"Returns flow_id + per-step status. On partial failure, returns cleanup hint." +
|
|
50
|
-
"
|
|
160
|
+
"Returns flow_id + per-step status. On partial failure, returns cleanup hint. " +
|
|
161
|
+
"Each source is EITHER an API connection (`connection`) OR a local source " +
|
|
162
|
+
"(`local_source: { content_type, limit?, filters? }`) — never both. Local " +
|
|
163
|
+
"content_type must be fully-qualified (e.g. 'wordpress/post', 'joomla/article'); " +
|
|
164
|
+
"a bare 'post' is rejected at save time." +
|
|
165
|
+
"\n\nConnection-source example:\n apimapper_flow_setup_with_sources({ name: 'Pexels + IG Gallery', sources: [{ connection: 'con_pexels' }, { connection: 'con_instagram' }], merge: { strategy: 'concat' }, output: { type: 'yootheme', name: 'Gallery' } })" +
|
|
166
|
+
"\n\nLocal-source example:\n apimapper_flow_setup_with_sources({ name: 'Latest Posts', sources: [{ local_source: { content_type: 'wordpress/post', limit: 12 } }], output: { type: 'yootheme', name: 'Posts' } })" +
|
|
167
|
+
"\n\nFilter example (one-shot — no raw graph editing needed):\n apimapper_flow_setup_with_sources({ name: 'Warm EU Cities', sources: [{ connection: 'con_weather' }], filter: { field: 'temp', operator: 'gt', value: 10 }, output: { type: 'yootheme', name: 'Warm' } })" +
|
|
168
|
+
"\n\nFilter operators: equals, not_equals, contains, starts_with, ends_with, gt, gte, lt, lte, exists, not_exists, empty, not_empty, in, not_in. " +
|
|
169
|
+
"Use the named operators (gt/lt), NOT symbols (>/<). Numeric operators auto-coerce formatted strings ('$1,250,000'). " +
|
|
170
|
+
"The filter runs AFTER merge and BEFORE transform." +
|
|
171
|
+
"\n\nMulti-stage reshape? Pass `transform` as an ARRAY of stages — each stage gets its own depth budget." +
|
|
172
|
+
"\n\nMulti-transform example (JMESPath depth cap → split into chained transforms):\n apimapper_flow_setup_with_sources({ name: 'Opening Hours', sources: [{ connection: 'con_hours' }], transform: [{ expression: '[1:]' }, { expression: '[*].{day: [0], hours: [1]}' }], output: { type: 'yootheme', name: 'Hours' } })" +
|
|
173
|
+
"\nPass `transform` as an ARRAY to chain N transform nodes (t1 → t2 → …). The pipe between chained nodes resets the JMESPath depth counter, so a reshape that overflows the depth cap (10) in one expression succeeds when split across two. A single `{ expression }` object still works for the one-transform case.",
|
|
51
174
|
inputSchema: {
|
|
52
175
|
name: z.string().min(1).describe('Flow name (appears in YOOtheme dropdown)'),
|
|
53
176
|
description: z.string().optional().describe("Optional description"),
|
|
54
|
-
sources: z
|
|
177
|
+
sources: z
|
|
178
|
+
.array(SourceSpec)
|
|
179
|
+
.min(1)
|
|
180
|
+
.describe("1..N sources — each a connection source OR a local_source (CMS-native)"),
|
|
55
181
|
merge: MergeSpec.optional().describe("Merge config — required if sources.length >= 2"),
|
|
56
|
-
|
|
182
|
+
filter: FilterSpec.optional().describe("Optional Filter node (F54). Inline `{field, operator, value}` or `{conditions:[...], logic}`. " +
|
|
183
|
+
"Runs after merge, before transform. Unknown operators are rejected hard."),
|
|
184
|
+
transform: TransformChainSpec.optional().describe("Optional Transform node(s). Multi-stage reshape? Pass `transform` as an ARRAY of stages — " +
|
|
185
|
+
"each stage gets its own depth budget. A single `{ expression }` covers the one-reshape case; " +
|
|
186
|
+
"an ARRAY CHAINs multiple transforms (t1 → t2 → …). Chaining is the remedy for the " +
|
|
187
|
+
"JMESPath depth cap (10): split one over-nested expression into two shallow ones — the " +
|
|
188
|
+
"pipe between chained nodes resets the depth counter. " +
|
|
189
|
+
"See apimapper_get_skill({ topic: 'jmespath-cookbook' }) → 'Two-Transform split'."),
|
|
57
190
|
output: OutputSpec.describe("Output node config"),
|
|
191
|
+
// Phase 2.7 (change protocol): this composite builds a whole flow, so
|
|
192
|
+
// the action is IMPLICIT (`flow.created`) — only `summary` is needed;
|
|
193
|
+
// the handler defaults the action. Threaded into the create POST body
|
|
194
|
+
// as `change: { summary }` (conditional-spread when present).
|
|
195
|
+
summary: z
|
|
196
|
+
.string()
|
|
197
|
+
.max(280)
|
|
198
|
+
.optional()
|
|
199
|
+
.describe("Optional one-line summary of this flow for the change history " +
|
|
200
|
+
'(e.g., "Set up a merged Pexels + Instagram gallery").'),
|
|
58
201
|
compile: z.boolean().default(true).describe("Compile after save (= publish to YOOtheme)"),
|
|
202
|
+
autofix: z
|
|
203
|
+
.boolean()
|
|
204
|
+
.default(true)
|
|
205
|
+
.describe("Defaults to true. When compile:true, auto-repair empty-output-schema compile errors " +
|
|
206
|
+
"by calling detect-schema then retrying compile so the new source compiles and " +
|
|
207
|
+
"appears. Pass false to opt out (verbatim old behaviour — no detect-schema retry). " +
|
|
208
|
+
"Mirrors `flow_full_recompile_publish` autofix."),
|
|
59
209
|
},
|
|
60
210
|
annotations: creating({ title: "Setup Flow with Sources", openWorld: true }),
|
|
61
|
-
}, async ({ name, description, sources, merge, transform, output, compile }, extra) => {
|
|
211
|
+
}, async ({ name, description, sources, merge, filter, transform, output, compile, autofix, summary }, extra) => {
|
|
212
|
+
// C (2026-06-04): autofix defaults to true. Treat `undefined` (omitted
|
|
213
|
+
// arg / unit-test harness that bypasses the schema default) as the
|
|
214
|
+
// on-default; only an explicit `autofix:false` opts out.
|
|
215
|
+
const autofixEnabled = autofix !== false;
|
|
62
216
|
// W3.5 — progress side-channel. `null` when the caller sent no
|
|
63
217
|
// progressToken; `progress?.report(...)` then no-ops. Total steps:
|
|
64
218
|
// build-graph + create + (compile when requested).
|
|
@@ -71,6 +225,7 @@ export function registerWorkflowTools(server) {
|
|
|
71
225
|
graph = buildFlowGraph({
|
|
72
226
|
sources,
|
|
73
227
|
merge: merge,
|
|
228
|
+
filter: filter,
|
|
74
229
|
transform: transform,
|
|
75
230
|
output: output,
|
|
76
231
|
});
|
|
@@ -78,8 +233,8 @@ export function registerWorkflowTools(server) {
|
|
|
78
233
|
catch (e) {
|
|
79
234
|
return formatResult({
|
|
80
235
|
error: e instanceof Error ? e.message : String(e),
|
|
81
|
-
context: { source_count: sources.length, has_merge: !!merge },
|
|
82
|
-
hint: "Check the validation rules in buildFlowGraph — typically missing merge for multi-source,
|
|
236
|
+
context: { source_count: sources.length, has_merge: !!merge, has_filter: !!filter },
|
|
237
|
+
hint: "Check the validation rules in buildFlowGraph — typically missing merge for multi-source, missing joinKey for strategy=join, or an invalid filter operator (use gt/lt, not >/<).",
|
|
83
238
|
}, true);
|
|
84
239
|
}
|
|
85
240
|
// Step 1: create
|
|
@@ -95,6 +250,9 @@ export function registerWorkflowTools(server) {
|
|
|
95
250
|
nodes: graph.nodes,
|
|
96
251
|
edges: graph.edges,
|
|
97
252
|
viewport: graph.viewport,
|
|
253
|
+
// Phase 2.7 — implicit action (`flow.created`); attach `change:
|
|
254
|
+
// { summary }` only when supplied so the body is unchanged otherwise.
|
|
255
|
+
...(summary !== undefined ? { change: { summary } } : {}),
|
|
98
256
|
}),
|
|
99
257
|
});
|
|
100
258
|
const createdFlow = unwrapEntity(createR.data, "flow");
|
|
@@ -104,7 +262,10 @@ export function registerWorkflowTools(server) {
|
|
|
104
262
|
status: createR.status,
|
|
105
263
|
errorCode: createR.errorCode,
|
|
106
264
|
context: { name, stage: "create" },
|
|
107
|
-
|
|
265
|
+
// F83: pass the create error message so a JMESPath depth-limit 422
|
|
266
|
+
// (classifies to "unknown") routes to the two-transform-split
|
|
267
|
+
// remedy + jmespath-cookbook pointer, NOT the generic health hint.
|
|
268
|
+
hint: hintFor(createR.errorCode, createR.error),
|
|
108
269
|
}, true);
|
|
109
270
|
}
|
|
110
271
|
const flowId = createdFlow.id;
|
|
@@ -116,9 +277,29 @@ export function registerWorkflowTools(server) {
|
|
|
116
277
|
// does not include the full flow object.
|
|
117
278
|
let compiled = false;
|
|
118
279
|
let compileError;
|
|
280
|
+
const autofixActions = [];
|
|
119
281
|
if (compile) {
|
|
120
282
|
await progress?.report(2, totalSteps, "Compiling and publishing to YOOtheme…");
|
|
121
|
-
|
|
283
|
+
let compileR = await request(`/flows/${encodeURIComponent(flowId)}/compile`, { method: "POST" });
|
|
284
|
+
// F5 (W9b, 2026-05-29): autofix mirror of flow_full_recompile_publish.
|
|
285
|
+
// CODE_EMPTY_SCHEMA = 4 (PHP FlowCompilerException constant, frozen).
|
|
286
|
+
// On empty-schema error, call detect-schema then retry compile. This
|
|
287
|
+
// makes the composite tool self-healing on the most common compile
|
|
288
|
+
// failure (newly created flow has no output schema yet).
|
|
289
|
+
const COMPILER_CODE_EMPTY_SCHEMA = 4;
|
|
290
|
+
if (!compileR.success &&
|
|
291
|
+
autofixEnabled &&
|
|
292
|
+
compileR.errorBody?.compiler_error_code === COMPILER_CODE_EMPTY_SCHEMA) {
|
|
293
|
+
autofixActions.push("Detected empty output schema, running detect-schema");
|
|
294
|
+
const detectR = await request(`/flows/${encodeURIComponent(flowId)}/detect-schema`, { method: "POST" });
|
|
295
|
+
if (detectR.success) {
|
|
296
|
+
autofixActions.push("Schema detected, retrying compile");
|
|
297
|
+
compileR = await request(`/flows/${encodeURIComponent(flowId)}/compile`, { method: "POST" });
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
autofixActions.push(`Schema detection failed: ${detectR.error ?? "unknown"}; cannot retry compile`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
122
303
|
if (!compileR.success) {
|
|
123
304
|
compileError = compileR.error;
|
|
124
305
|
steps.push(`2. Compile FAILED: ${compileR.error}`);
|
|
@@ -132,7 +313,8 @@ export function registerWorkflowTools(server) {
|
|
|
132
313
|
}
|
|
133
314
|
else {
|
|
134
315
|
compiled = true;
|
|
135
|
-
|
|
316
|
+
const autofixSuffix = autofixActions.length > 0 ? " (autofix applied)" : "";
|
|
317
|
+
steps.push(`2. Compiled — flow visible in YOOtheme dropdown${autofixSuffix}`);
|
|
136
318
|
}
|
|
137
319
|
}
|
|
138
320
|
}
|
|
@@ -142,10 +324,11 @@ export function registerWorkflowTools(server) {
|
|
|
142
324
|
flow_id: flowId,
|
|
143
325
|
compiled: false,
|
|
144
326
|
compile_error: compileError,
|
|
327
|
+
autofix_actions: autofixActions.length > 0 ? autofixActions : undefined,
|
|
145
328
|
steps,
|
|
146
329
|
next_action: "investigate_compile_error",
|
|
147
|
-
cleanup: `
|
|
148
|
-
hint:
|
|
330
|
+
cleanup: `Roll back the orphan via apimapper_advanced({ tool: "apimapper_flow_delete", arguments: { id: "${flowId}", confirm: true } }), OR fix node config and recompile via apimapper_flow_full_recompile_publish.`,
|
|
331
|
+
hint: 'Compile errors usually mean missing joinKey, invalid JMESPath, or missing connection. Inspect node data via apimapper_advanced({ tool: "apimapper_flow_get", arguments: { id } }).',
|
|
149
332
|
}, true);
|
|
150
333
|
}
|
|
151
334
|
await progress?.report(totalSteps, totalSteps, compiled ? "Flow created and published" : "Flow created");
|
|
@@ -158,8 +341,8 @@ export function registerWorkflowTools(server) {
|
|
|
158
341
|
merge_strategy: merge ? normalizeMergeStrategy(merge.strategy) : null,
|
|
159
342
|
steps,
|
|
160
343
|
next: compiled
|
|
161
|
-
? "
|
|
162
|
-
: "
|
|
344
|
+
? 'Test items via apimapper_advanced({ tool: "apimapper_graph_preview", arguments: { flow_id } }), or bind in the YOOtheme Builder.'
|
|
345
|
+
: "Publish via apimapper_flow_full_recompile_publish.",
|
|
163
346
|
}, false, { maxChars: 3000 });
|
|
164
347
|
});
|
|
165
348
|
// ── apimapper_flow_change_merge_strategy ───────────────────────────
|
|
@@ -171,14 +354,24 @@ export function registerWorkflowTools(server) {
|
|
|
171
354
|
"\n\nExample:\n apimapper_flow_change_merge_strategy({ flow_id: 'flow_Z2fLg70M84', strategy: 'join', joinKey: 'id' })",
|
|
172
355
|
inputSchema: {
|
|
173
356
|
flow_id: z.string().describe("Flow ID. Use apimapper_flow_list to find."),
|
|
174
|
-
|
|
357
|
+
// F156 — shared enums (see merge-constants.ts) keep this in lock-step
|
|
358
|
+
// with the raw node-schema + flow_setup_with_sources MergeSpec.
|
|
359
|
+
strategy: z.enum(MERGE_STRATEGIES_INPUT).describe("New merge strategy"),
|
|
175
360
|
joinKey: z.string().optional().describe("Left-side join key (required for strategy=join)"),
|
|
176
361
|
joinKeyRight: z.string().optional().describe("Right-side join key (defaults to joinKey)"),
|
|
177
|
-
joinType: z.enum(
|
|
362
|
+
joinType: z.enum(MERGE_JOIN_TYPES).optional().describe("Join type override (left/right/inner/outer)"),
|
|
363
|
+
// Phase 2.7 (change protocol): the action is IMPLICIT (`merge.changed`)
|
|
364
|
+
// — only `summary` is needed; the handler defaults the action.
|
|
365
|
+
// Threaded into the PUT body as `change: { summary }`.
|
|
366
|
+
summary: z
|
|
367
|
+
.string()
|
|
368
|
+
.max(280)
|
|
369
|
+
.optional()
|
|
370
|
+
.describe('Optional one-line summary of this merge change (e.g., "Switched merge to join on id").'),
|
|
178
371
|
compile: z.boolean().default(true).describe("Compile after save"),
|
|
179
372
|
},
|
|
180
373
|
annotations: mutating({ title: "Change Flow Merge Strategy", openWorld: true }),
|
|
181
|
-
}, async ({ flow_id, strategy, joinKey, joinKeyRight, joinType, compile }) => {
|
|
374
|
+
}, async ({ flow_id, strategy, joinKey, joinKeyRight, joinType, compile, summary }) => {
|
|
182
375
|
const getR = await request(`/flows/${encodeURIComponent(flow_id)}`);
|
|
183
376
|
if (!getR.success || !getR.data) {
|
|
184
377
|
return formatResult({
|
|
@@ -196,7 +389,7 @@ export function registerWorkflowTools(server) {
|
|
|
196
389
|
return formatResult({
|
|
197
390
|
error: "flow.nodes is not an array",
|
|
198
391
|
context: { flow_id, type: typeof flow.nodes },
|
|
199
|
-
hint:
|
|
392
|
+
hint: 'Flow data shape unexpected. Inspect via apimapper_advanced({ tool: "apimapper_flow_get", arguments: { id: flow_id } }).',
|
|
200
393
|
}, true);
|
|
201
394
|
}
|
|
202
395
|
const mergeNode = flow.nodes.find((n) => n.type === "merge");
|
|
@@ -238,7 +431,12 @@ export function registerWorkflowTools(server) {
|
|
|
238
431
|
// `(putR.data as any)?.flow` before reading fields.
|
|
239
432
|
const putR = await request(`/flows/${encodeURIComponent(flow_id)}`, {
|
|
240
433
|
method: "PUT",
|
|
241
|
-
body: JSON.stringify({
|
|
434
|
+
body: JSON.stringify({
|
|
435
|
+
nodes: flow.nodes,
|
|
436
|
+
// Phase 2.7 — implicit action (`merge.changed`); attach `change:
|
|
437
|
+
// { summary }` only when supplied so the body is unchanged otherwise.
|
|
438
|
+
...(summary !== undefined ? { change: { summary } } : {}),
|
|
439
|
+
}),
|
|
242
440
|
});
|
|
243
441
|
if (!putR.success) {
|
|
244
442
|
return formatResult({
|
|
@@ -246,7 +444,9 @@ export function registerWorkflowTools(server) {
|
|
|
246
444
|
status: putR.status,
|
|
247
445
|
errorCode: putR.errorCode,
|
|
248
446
|
context: { flow_id, stage: "update" },
|
|
249
|
-
|
|
447
|
+
// F83: a depth-limit 422 can surface on this write path too — route
|
|
448
|
+
// the message to the two-transform-split remedy.
|
|
449
|
+
hint: hintFor(putR.errorCode, putR.error),
|
|
250
450
|
}, true);
|
|
251
451
|
}
|
|
252
452
|
let compiled = false;
|
|
@@ -268,10 +468,10 @@ export function registerWorkflowTools(server) {
|
|
|
268
468
|
compiled: false,
|
|
269
469
|
compile_error: compileError,
|
|
270
470
|
next_action: "investigate_compile_error_or_revert",
|
|
271
|
-
cleanup: "Option A: fix the merge config (e.g., correct joinKey field name) and
|
|
272
|
-
|
|
471
|
+
cleanup: "Option A: fix the merge config (e.g., correct joinKey field name) and recompile via apimapper_flow_full_recompile_publish. " +
|
|
472
|
+
'Option B: revert via apimapper_advanced({ tool: "apimapper_flow_change_merge_strategy" }) back to the previous strategy. ' +
|
|
273
473
|
"The flow PUT succeeded — node data is updated but the flow won't appear in YOOtheme until compile passes.",
|
|
274
|
-
hint:
|
|
474
|
+
hint: 'Compile errors usually mean wrong joinKey field name. Inspect node data via apimapper_advanced({ tool: "apimapper_flow_get", arguments: { id: flow_id } }).',
|
|
275
475
|
}, true);
|
|
276
476
|
}
|
|
277
477
|
return formatResult({
|
|
@@ -298,20 +498,30 @@ export function registerWorkflowTools(server) {
|
|
|
298
498
|
// so the source appears in YOOtheme immediately without a second MCP
|
|
299
499
|
// package (matches audit recommendation A1 P1).
|
|
300
500
|
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
501
|
+
// C (2026-06-04): the default is now `autofix:true`. Observed live in a
|
|
502
|
+
// Claude Desktop session: when the AI omits `autofix`, the old default
|
|
503
|
+
// (false) meant the YOOtheme schema cache was never flushed, so the source
|
|
504
|
+
// did NOT appear in the YOOtheme builder after publish. Flipping the default
|
|
505
|
+
// makes the autofix path (detect-schema retry + flush-yt) the normal path,
|
|
506
|
+
// so a plain `apimapper_flow_full_recompile_publish({id})` reliably publishes
|
|
507
|
+
// a visible source. `autofix:false` is now an explicit opt-out that restores
|
|
508
|
+
// the verbatim v2.0.7 behaviour (compile + admin-cache only, no detect-schema
|
|
509
|
+
// retry, no YT flush). The handler treats `autofix !== false` as enabled, so
|
|
510
|
+
// an omitted/undefined value behaves identically to the schema default at the
|
|
511
|
+
// real SDK call boundary. The PHP layer surfaces `compiler_error_code` on
|
|
512
|
+
// compile-error responses (FlowHandler.php) so this branch reads a numeric
|
|
513
|
+
// class rather than string-matching messages.
|
|
305
514
|
server.registerTool("apimapper_flow_full_recompile_publish", {
|
|
306
515
|
title: "Full Recompile + Cache Clear (Composite)",
|
|
307
|
-
description: "Compile a flow + invalidate admin cache
|
|
308
|
-
"to ensure the flow + schema are fresh. Each step's success
|
|
309
|
-
"and a cache-clear failure does NOT silently downgrade compile success. " +
|
|
310
|
-
"
|
|
311
|
-
"(empty output schema → re-detect; then retry compile)
|
|
312
|
-
"YOOtheme schema cache so the source becomes visible in YOOtheme
|
|
313
|
-
"
|
|
314
|
-
"
|
|
516
|
+
description: "Compile a flow + invalidate admin cache + flush the YOOtheme schema cache in one call. " +
|
|
517
|
+
"Use after upstream API changes to ensure the flow + schema are fresh. Each step's success " +
|
|
518
|
+
"is surfaced separately, and a cache-clear failure does NOT silently downgrade compile success. " +
|
|
519
|
+
"autofix defaults to true: it auto-repairs common compile errors " +
|
|
520
|
+
"(empty output schema → re-detect; then retry compile) AND flushes the " +
|
|
521
|
+
"YOOtheme schema cache so the source becomes visible in the YOOtheme builder — " +
|
|
522
|
+
"no second MCP package needed. Pass autofix:false to opt out (compile + admin-cache " +
|
|
523
|
+
"only, no detect-schema retry, no YT flush — the verbatim v2.0.7 behaviour)." +
|
|
524
|
+
"\n\nExample:\n apimapper_flow_full_recompile_publish({ id: 'flow_Z2fLg70M84' })",
|
|
315
525
|
inputSchema: {
|
|
316
526
|
// L-6 (W3F-5): accept either `id` (canonical, matches every other
|
|
317
527
|
// flow_* tool) or `flow_id` (legacy alias kept so existing callers
|
|
@@ -327,13 +537,24 @@ export function registerWorkflowTools(server) {
|
|
|
327
537
|
.describe("Deprecated alias for `id` — accepted for back-compat with pre-W3F-5 callers."),
|
|
328
538
|
autofix: z
|
|
329
539
|
.boolean()
|
|
330
|
-
.default(
|
|
331
|
-
.describe("When true: on empty-schema compile error, call detect-schema and retry
|
|
332
|
-
"On compile success, also flush the YOOtheme schema cache so the source
|
|
333
|
-
"in the YOOtheme builder.
|
|
540
|
+
.default(true)
|
|
541
|
+
.describe("When true (default): on empty-schema compile error, call detect-schema and retry " +
|
|
542
|
+
"compile. On compile success, also flush the YOOtheme schema cache so the source " +
|
|
543
|
+
"surfaces in the YOOtheme builder. Pass false to opt out — compile + admin-cache " +
|
|
544
|
+
"only, no detect-schema retry, no YT flush (the verbatim v2.0.7 behaviour)."),
|
|
545
|
+
// Phase 2.7 (change protocol): recompile = (re)publish, so the action
|
|
546
|
+
// is IMPLICIT (`flow.published`) — only `summary` is needed; the handler
|
|
547
|
+
// defaults the action. Attached to the compile POST body as
|
|
548
|
+
// `change: { summary }` (the compile POST otherwise has no body).
|
|
549
|
+
summary: z
|
|
550
|
+
.string()
|
|
551
|
+
.max(280)
|
|
552
|
+
.optional()
|
|
553
|
+
.describe('Optional one-line summary of this publish for the change history ' +
|
|
554
|
+
'(e.g., "Republished after refreshing the upstream schema").'),
|
|
334
555
|
},
|
|
335
556
|
annotations: mutating({ title: "Full Recompile & Publish Flow", openWorld: true }),
|
|
336
|
-
}, async ({ id, flow_id: flowIdAlias, autofix }, extra) => {
|
|
557
|
+
}, async ({ id, flow_id: flowIdAlias, autofix, summary }, extra) => {
|
|
337
558
|
// L-6 (W3F-5): resolve the dual-name input — `id` (canonical) takes
|
|
338
559
|
// priority over the legacy `flow_id` alias when both are present.
|
|
339
560
|
const flow_id = id ?? flowIdAlias;
|
|
@@ -346,20 +567,36 @@ export function registerWorkflowTools(server) {
|
|
|
346
567
|
const steps = [];
|
|
347
568
|
const errors = [];
|
|
348
569
|
const autofixActions = [];
|
|
349
|
-
//
|
|
570
|
+
// C (2026-06-04): autofix defaults to true. Treat `undefined` (caller
|
|
571
|
+
// omitted the arg, and the unit-test harness bypasses the schema default)
|
|
572
|
+
// identically to the on-default, so only an explicit `autofix:false`
|
|
573
|
+
// opts out. This keeps the handler faithful to the documented default
|
|
574
|
+
// whether or not the SDK applied the Zod default before dispatch.
|
|
575
|
+
const autofixEnabled = autofix !== false;
|
|
576
|
+
// W3.5: progress side-channel. autofix adds a third stage
|
|
350
577
|
// (YOOtheme schema cache flush) so it's a 3-step pipeline.
|
|
351
|
-
const totalSteps =
|
|
578
|
+
const totalSteps = autofixEnabled ? 3 : 2;
|
|
352
579
|
const progress = extra ? createProgressReporter(extra) : null;
|
|
353
580
|
await progress?.report(0, totalSteps, "Recompiling flow…");
|
|
354
581
|
// Stage 1: compile (with optional autofix retry)
|
|
355
|
-
|
|
582
|
+
// Phase 2.7 — implicit action (`flow.published`). Attach `change:
|
|
583
|
+
// { summary }` only when supplied so the body is unchanged otherwise. The
|
|
584
|
+
// autofix-retry compile below does NOT re-attach it (one publish = one
|
|
585
|
+
// ChangeEvent; the first compile carries the summary).
|
|
586
|
+
let compileR = await request(`/flows/${encodeURIComponent(flow_id)}/compile`, summary !== undefined
|
|
587
|
+
? {
|
|
588
|
+
method: "POST",
|
|
589
|
+
body: JSON.stringify({ change: { summary } }),
|
|
590
|
+
headers: { "Content-Type": "application/json" },
|
|
591
|
+
}
|
|
592
|
+
: { method: "POST" });
|
|
356
593
|
// W2-3 autofix: on CODE_EMPTY_SCHEMA (4), call detect-schema then retry.
|
|
357
594
|
// PHP FlowCompilerException::CODE_EMPTY_SCHEMA = 4 (constant frozen for
|
|
358
595
|
// wire-stability across plugin versions; same number on WP + Joomla
|
|
359
596
|
// because both share src/modules/core/src/Flow/FlowCompilerException.php).
|
|
360
597
|
const COMPILER_CODE_EMPTY_SCHEMA = 4;
|
|
361
598
|
if (!compileR.success &&
|
|
362
|
-
|
|
599
|
+
autofixEnabled &&
|
|
363
600
|
compileR.errorBody?.compiler_error_code === COMPILER_CODE_EMPTY_SCHEMA) {
|
|
364
601
|
autofixActions.push("Detected empty output schema, running detect-schema");
|
|
365
602
|
const detectR = await request(`/flows/${encodeURIComponent(flow_id)}/detect-schema`, { method: "POST" });
|
|
@@ -377,9 +614,11 @@ export function registerWorkflowTools(server) {
|
|
|
377
614
|
status: compileR.status,
|
|
378
615
|
errorCode: compileR.errorCode,
|
|
379
616
|
compiler_error_code: compileR.errorBody?.compiler_error_code,
|
|
380
|
-
context: { flow_id, stage: "compile", autofix:
|
|
617
|
+
context: { flow_id, stage: "compile", autofix: autofixEnabled },
|
|
381
618
|
autofix_actions: autofixActions.length > 0 ? autofixActions : undefined,
|
|
382
|
-
|
|
619
|
+
// F83: the depth-limit overflow can surface late at compile /
|
|
620
|
+
// detect-schema time too — route its message to the split remedy.
|
|
621
|
+
hint: hintFor(compileR.errorCode, compileR.error),
|
|
383
622
|
}, true);
|
|
384
623
|
}
|
|
385
624
|
steps.push("1. Flow compiled");
|
|
@@ -406,7 +645,7 @@ export function registerWorkflowTools(server) {
|
|
|
406
645
|
// {flushed: false, error: <msg>} in the body when the buster threw.
|
|
407
646
|
// We treat the request-level success/failure as authoritative and
|
|
408
647
|
// record any flushed:false as a non-fatal warning step.
|
|
409
|
-
if (
|
|
648
|
+
if (autofixEnabled) {
|
|
410
649
|
await progress?.report(2, totalSteps, "Flushing YOOtheme schema cache…");
|
|
411
650
|
const ytR = await request("/cache/flush-yt", { method: "POST" });
|
|
412
651
|
if (ytR.success && ytR.data?.flushed !== false) {
|
|
@@ -434,11 +673,16 @@ export function registerWorkflowTools(server) {
|
|
|
434
673
|
errors: ok ? undefined : errors,
|
|
435
674
|
autofix_actions: autofixActions.length > 0 ? autofixActions : undefined,
|
|
436
675
|
next: ok
|
|
437
|
-
?
|
|
676
|
+
? autofixEnabled
|
|
438
677
|
? "Flow recompiled, admin cache invalidated, and YOOtheme schema cache flushed. The source should now be visible in the YOOtheme builder."
|
|
439
|
-
: "Flow is fresh in both admin + render layers. YOOtheme schema cache
|
|
440
|
-
:
|
|
441
|
-
},
|
|
678
|
+
: "Flow is fresh in both admin + render layers. You passed autofix:false, so the YOOtheme schema cache was NOT cleared by this tool; re-run without autofix:false (the default flushes it) or call yootheme_clear_cache via the YOOtheme MCP if the source does not appear in the builder."
|
|
679
|
+
: 'Compile succeeded but a cache step had issues. Manually invalidate via apimapper_advanced({ tool: "apimapper_cache_invalidate", arguments: { scope: "all" } }) if downstream still sees stale data.',
|
|
680
|
+
},
|
|
681
|
+
// Wave-5 F7 fix (2026-05-29): formatResult's 2nd positional is `isError`.
|
|
682
|
+
// Pre-fix we passed `ok` here, which inverted the meaning — ok:true was
|
|
683
|
+
// tagged as `<error>` envelope. Pass `!ok` so success-with-no-errors
|
|
684
|
+
// renders as success and partial (errors.length > 0) renders as error.
|
|
685
|
+
!ok, { maxChars: 2500 });
|
|
442
686
|
});
|
|
443
687
|
}
|
|
444
688
|
//# sourceMappingURL=workflows.js.map
|