@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
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# JMESPath Cookbook
|
|
2
|
+
|
|
3
|
+
Copy-paste recipes for the Transform node. Every recipe below is verified
|
|
4
|
+
against the actual API Mapper JMESPath engine (same one the Transform pipeline
|
|
5
|
+
runs). For the trap list and date/number primitive reference, see
|
|
6
|
+
`jmespath-pitfalls`. For joining two sources, see `merge-two-sources-on-key`.
|
|
7
|
+
|
|
8
|
+
> **Iterate with `apimapper_jmespath_test`.** Before you wire an expression into
|
|
9
|
+
> a Transform node, dry-run it: `apimapper_jmespath_test({ expression, sample_rows })`
|
|
10
|
+
> returns `{ result, row_count }`, and a bad expression returns the SAME
|
|
11
|
+
> teaching hint (arithmetic, depth-split) you'd hit at flow time — but instantly,
|
|
12
|
+
> with no flow to clean up. Grab real rows with `apimapper_connection_data` first.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## If / else — `conditional()` (preferred) or the `(cond && X) || Y` ternary
|
|
17
|
+
|
|
18
|
+
JMESPath has no native `if/else` statement. API Mapper ships a `conditional()`
|
|
19
|
+
primitive AND the classic short-circuit ternary; pick whichever reads cleaner.
|
|
20
|
+
|
|
21
|
+
### `conditional(condition, then_value [, else_value])`
|
|
22
|
+
|
|
23
|
+
```jmespath
|
|
24
|
+
[*].{
|
|
25
|
+
name: name,
|
|
26
|
+
status: conditional(stock > `0`, 'in stock', 'sold out')
|
|
27
|
+
}
|
|
28
|
+
# {stock:5} → "in stock" {stock:0} → "sold out"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Returns `then_value` when `condition` is truthy, `else_value` (default `null`
|
|
32
|
+
when omitted) when falsy. Truthiness mirrors the engine: `false`, `null`,
|
|
33
|
+
`''`, `[]`, `{}` are falsy; **everything else — including `0` — is truthy**.
|
|
34
|
+
|
|
35
|
+
It is flatter and more readable than the ternary for a simple two-way branch,
|
|
36
|
+
and it **side-steps the ternary's falsy-`X` trap**: `conditional(cond, '', Y)`
|
|
37
|
+
returns the empty string verbatim, whereas `(cond && '') || Y` would wrongly
|
|
38
|
+
fall through to `Y`. (It is not "free" against the depth cap — a function call
|
|
39
|
+
still costs +1 nesting level — but for the simple case it reads better.)
|
|
40
|
+
|
|
41
|
+
### Ternary — `(cond && X) || Y`
|
|
42
|
+
|
|
43
|
+
The short-circuit idiom: when `cond` is truthy the `&&` yields `X`; otherwise
|
|
44
|
+
the `||` falls through to `Y`.
|
|
45
|
+
|
|
46
|
+
```jmespath
|
|
47
|
+
[*].{
|
|
48
|
+
name: name,
|
|
49
|
+
status: (stock > `0` && 'in stock') || 'sold out'
|
|
50
|
+
}
|
|
51
|
+
# {stock:5} → "in stock" {stock:0} → "sold out"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Gotcha:** if `X` itself can be falsy (e.g. the string `''`, `0`, `false`), the
|
|
55
|
+
`|| Y` branch fires even when `cond` was true. Keep `X` a non-empty constant,
|
|
56
|
+
use `conditional()` instead, or restructure. For a numeric comparison on a
|
|
57
|
+
string field, cast first: `(to_number(price) > \`100\` && 'premium') || 'standard'`.
|
|
58
|
+
You can chain ternaries for >2 branches (`(a && 'x') || (b && 'y') || 'z'`);
|
|
59
|
+
`conditional()` covers the two-way case.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## String composition — `join`
|
|
64
|
+
|
|
65
|
+
`join(separator, array_of_strings)` concatenates. All elements must be strings —
|
|
66
|
+
cast numbers with `to_string()`.
|
|
67
|
+
|
|
68
|
+
```jmespath
|
|
69
|
+
# "A - 5"
|
|
70
|
+
[*].join(' - ', [name, to_string(stock)])
|
|
71
|
+
|
|
72
|
+
# Prefix a currency symbol onto a formatted number
|
|
73
|
+
[*].{price: join('', ['$', format_number(price)])}
|
|
74
|
+
# {price:"890000"} → "$890,000"
|
|
75
|
+
|
|
76
|
+
# Build a full address from parts
|
|
77
|
+
[*].{label: join(', ', [street, city, country])}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
To concatenate with no separator, pass `''` as the first arg.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Empty / filled field as a condition (result-pattern)
|
|
85
|
+
|
|
86
|
+
To branch on whether a field is present-and-non-empty, reuse the ternary —
|
|
87
|
+
an empty string, `null`, or `[]` is falsy:
|
|
88
|
+
|
|
89
|
+
```jmespath
|
|
90
|
+
[*].{
|
|
91
|
+
name: name,
|
|
92
|
+
has_tags: (tags && 'yes') || 'no'
|
|
93
|
+
}
|
|
94
|
+
# {tags:["x"]} → "yes" {tags:[]} → "no" {missing} → "no"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
To FILTER to only the filled rows:
|
|
98
|
+
|
|
99
|
+
```jmespath
|
|
100
|
+
[?image_url] # keeps rows whose image_url is non-empty
|
|
101
|
+
[?length(tags) > `0`] # keeps rows with at least one tag
|
|
102
|
+
[?contains(keys(@), 'price')] # keeps rows where the KEY exists (any value)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
(See pitfall #8 in `jmespath-pitfalls` for empty-array vs missing-key.)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Sorting — `sort_by`
|
|
110
|
+
|
|
111
|
+
`sort_by(array, &field)` sorts ascending by a field. Wrap in `reverse(...)` for
|
|
112
|
+
descending. The `&` makes an expression-reference (don't forget it).
|
|
113
|
+
|
|
114
|
+
```jmespath
|
|
115
|
+
# Ascending by price
|
|
116
|
+
sort_by([*], &price)
|
|
117
|
+
|
|
118
|
+
# Descending by date, then take the first 3
|
|
119
|
+
reverse(sort_by([*], &published_at))[:3]
|
|
120
|
+
|
|
121
|
+
# Sort, then project just the names
|
|
122
|
+
sort_by([*], &stock)[*].name
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`sort_by` needs a comparable key. If the field is a numeric STRING, sorting is
|
|
126
|
+
lexical (`"100" < "20"`); normalise upstream or accept string order.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Coordinate keys for a Map element
|
|
131
|
+
|
|
132
|
+
YOOtheme's Map element binds a `coordinates` array `[lat, lng]`. Build it inline:
|
|
133
|
+
|
|
134
|
+
```jmespath
|
|
135
|
+
[*].{
|
|
136
|
+
title: title,
|
|
137
|
+
coordinates: [lat, lng]
|
|
138
|
+
}
|
|
139
|
+
# → [{ title: "HQ", coordinates: [52.5, 13.4] }]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
If lat/lng arrive as strings, cast: `coordinates: [to_number(lat), to_number(lng)]`.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Number formatting — `format_number`
|
|
147
|
+
|
|
148
|
+
`format_number(value [, decimals=0 [, decimal_sep='.' [, thousands_sep=',']]])`
|
|
149
|
+
groups thousands. Accepts numbers OR numeric strings; returns `null` on
|
|
150
|
+
non-numeric input.
|
|
151
|
+
|
|
152
|
+
```jmespath
|
|
153
|
+
# 890000 → "890,000"
|
|
154
|
+
[*].{name: name, price: format_number(price)}
|
|
155
|
+
|
|
156
|
+
# Two decimals: 1234.5 → "1,234.50"
|
|
157
|
+
format_number(`1234.5`, `2`)
|
|
158
|
+
|
|
159
|
+
# European grouping: 1234567.89 → "1.234.567,89"
|
|
160
|
+
format_number(price, `2`, ',', '.')
|
|
161
|
+
|
|
162
|
+
# Guard a column with stray non-numeric rows
|
|
163
|
+
[*].coalesce_empty(format_number(price), 'n/a')
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Date primitives — weekday / time / date / window
|
|
169
|
+
|
|
170
|
+
JMESPath has no native date support; API Mapper ships four primitives. The
|
|
171
|
+
optional last arg is an IANA timezone (`'Europe/Berlin'`, `'America/New_York'`,
|
|
172
|
+
`'UTC'`); omit it to keep the offset already in the string. All return `null`
|
|
173
|
+
(or `false` for `time_in_window`) on malformed input.
|
|
174
|
+
|
|
175
|
+
```jmespath
|
|
176
|
+
# Calendly slot → Sheet-comparable weekday + local time
|
|
177
|
+
[*].{
|
|
178
|
+
day: date_weekday(start_time, 'Europe/Berlin'), # "Wednesday"
|
|
179
|
+
local_time: date_iso_to_time(start_time, 'Europe/Berlin'), # "09:30"
|
|
180
|
+
ymd: date_iso_to_date(start_time, 'Europe/Berlin') # "2026-06-03"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Keep only slots inside business hours
|
|
184
|
+
[?time_in_window(date_iso_to_time(start_time, 'Europe/Berlin'), '09:00', '17:00')]
|
|
185
|
+
|
|
186
|
+
# Cross-midnight window (10pm..2am): from > to is supported
|
|
187
|
+
[?time_in_window(local_time, '22:00', '02:00')]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
See `merge-two-sources-on-key` for using these to join a weekday-name source
|
|
191
|
+
against an ISO-datetime source.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Two-Transform split against the depth limit
|
|
196
|
+
|
|
197
|
+
Expression depth is capped at **10** levels of bracket / pipe nesting. A pipe
|
|
198
|
+
`|` resets the depth counter, so the universal fix for a "too deeply nested"
|
|
199
|
+
422 is to **split one Transform into two piped together** — either inside one
|
|
200
|
+
expression with a pipe, or as two separate Transform nodes wired in series.
|
|
201
|
+
|
|
202
|
+
```jmespath
|
|
203
|
+
# Too deep — slice + multi-level hash in one breath
|
|
204
|
+
[1:][*].{day: [0], hours: {open: [1], close: [2], extra: {note: [3]}}}
|
|
205
|
+
|
|
206
|
+
# Fixed — pipe resets, second stage starts fresh and shallow
|
|
207
|
+
[1:] | [*].{day: [0], hours: {open: [1], close: [2]}}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
If you hit the cap, also reconsider whether you need the nesting at all — flat
|
|
211
|
+
`[*].{a: x, b: y}` bucket shapes are almost always enough for a YOOtheme
|
|
212
|
+
binding. (Full walk-through: `jmespath-pitfalls` → "Depth-cap warning".)
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## GIBT-ES-NICHT — things JMESPath does NOT have (and the workaround)
|
|
217
|
+
|
|
218
|
+
Stop trying these — they raise a syntax error (now annotated with a hint) or
|
|
219
|
+
silently misbehave. Use the workaround.
|
|
220
|
+
|
|
221
|
+
| You want… | Does NOT exist | Workaround |
|
|
222
|
+
|-----------|----------------|------------|
|
|
223
|
+
| Arithmetic `a + b`, `a - b`, `a * b`, `a / b` | No arithmetic operators at all. `price + tax` is a **syntax error** (annotated *"JMESPath has no arithmetic operators"*). | Compute upstream (in the API, a spreadsheet, or an n8n step), or in a YOOtheme element. For display-only concatenation use `join('', [a, b])`. |
|
|
224
|
+
| Split a string `split(s, ',')` | No `split`. | Pre-split upstream, or if the API returns an array use it directly. For a CSV cell, request a structured endpoint instead. |
|
|
225
|
+
| Regex match / replace | No regex. | Use `contains(@, 'substr')`, `starts_with`, `ends_with` for membership. For replace, normalise the data upstream. |
|
|
226
|
+
| `if / else` / `switch` | No statement forms. | `conditional(cond, X, Y)` (flat, two-way) or the ternary `(cond && X) || Y` (above). Chain ternaries for >2 branches: `(a && 'x') || (b && 'y') || 'z'`. |
|
|
227
|
+
| Uppercase / lowercase | No case functions. | Normalise casing upstream (the source API or a Transform-feeding step). |
|
|
228
|
+
| Sum / count-with-condition | No `sum`/aggregate over a predicate. | `length([?cond])` counts matching rows. For a real sum, aggregate upstream. |
|
|
229
|
+
| Date math (`+ 1 day`) | No date arithmetic. | Use the date primitives for extraction/formatting/windowing only; do calendar math upstream. |
|
|
230
|
+
|
|
231
|
+
When in doubt, paste the expression into `apimapper_jmespath_test` — a failing
|
|
232
|
+
expression tells you exactly which of these you tripped over.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## See also
|
|
237
|
+
|
|
238
|
+
- `jmespath-pitfalls` — the 9 traps + primitive reference + depth-cap detail.
|
|
239
|
+
- `merge-two-sources-on-key` — joining two sources, with date-primitive bridging.
|
|
240
|
+
- `conditional-style-multi-items` — per-row YOOtheme styles from a row field.
|
|
241
|
+
- `yootheme-source-to-builder-handoff` — where the empty/filled string fields you build here drive a theme layout's `_condition` (`'!'`/`'!!'`) in the `#parent` repeater form.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# JMESPath Pitfalls
|
|
2
|
+
|
|
3
|
+
The nine traps that bite every Transform JMESPath expression in API Mapper, plus the date-primitive functions added in 2.0.8 that finally make weekday-name ↔ ISO-datetime joins tractable.
|
|
4
|
+
|
|
5
|
+
## The 9 traps
|
|
6
|
+
|
|
7
|
+
1. **Top-level vs item-level scope.** A flat `[].name` projects across items; bare `name` reads the wrapper object. Mixing the two is the most common cause of `null` everywhere.
|
|
8
|
+
2. **Implicit numeric coercion.** `[?price > 100]` reads `price` as a string when the API returns a string. Cast first with `to_number(price) > \`100\`` or normalize upstream.
|
|
9
|
+
3. **`@` vs nothing.** Inside multi-select hashes the `@` refers to the current item — `{name: @.name}` is shorthand. Forgetting `@` inside a filter expression (`[?@.x == 'y']`) is silently parsed as a function call.
|
|
10
|
+
4. **String-quoted literals.** JSON literals use backticks: `\`100\``, `\`true\``, `\`null\``. Single-quoted strings (`'red'`) are raw strings. Mixing them up yields lexer errors that the engine reports far from the actual typo.
|
|
11
|
+
5. **`null` propagation through pipes.** `items | [].name | [?contains(@, 'foo')]` returns `null` (not `[]`) when an item has no `name`. Wrap with `coalesce_empty(@, '')` or filter `not_null(@)`.
|
|
12
|
+
6. **`length()` on null.** Vendor `length(null)` throws. Use `length(@.items || \`[]\`)` to default.
|
|
13
|
+
7. **Backslash-escapes inside raw strings.** Single-quoted strings do NOT honour `\n` / `\t`. To embed a literal `'` use `'\\''` (close, escape, open).
|
|
14
|
+
8. **Empty-array vs missing-key.** `[?tags]` keeps items whose `tags` is non-empty AND non-null. To match items with the *key* present regardless of value, use `[?contains(keys(@), 'tags')]`.
|
|
15
|
+
9. **Multi-projection collapse on header-skip slices.** `[1:][*].{weekday: [0], open: [1]}` looks like "slice off the header row, then map each remaining row into a hash", but JMESPath parses `[1:][*]` as a *chained multi-projection* that flattens row boundaries before the hash runs. The fix is one pipe: `[1:] | [*].{weekday: [0], open: [1]}`. See "Symptom / Cause / Fix" below.
|
|
16
|
+
|
|
17
|
+
### Pitfall 9 in detail (header-skip slice → hash projection)
|
|
18
|
+
|
|
19
|
+
**Symptom.** Output schema auto-detection returns flat columns prefixed with the row index, like:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
0_weekday, 0_open, 0_close,
|
|
23
|
+
1_weekday, 1_open, 1_close,
|
|
24
|
+
2_weekday, 2_open, 2_close, ...
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Each row also reads as `null` in the actual data preview. Every binding in the Multi-Items element lands on the wrapper object instead of the row.
|
|
28
|
+
|
|
29
|
+
**Cause.** `[1:][*]` is two consecutive projections. JMESPath flattens them: the inner array index dissolves, so the trailing `.{weekday: [0], ...}` operates on the wrapper object's positional slots, not on each row. The auto-detected schema reflects that flattened shape.
|
|
30
|
+
|
|
31
|
+
**Fix.** Insert a pipe between the slice and the hash projection so the second stage starts fresh:
|
|
32
|
+
|
|
33
|
+
```jmespath
|
|
34
|
+
# Working — pipe resets the projection so [*] iterates row-by-row
|
|
35
|
+
[1:] | [*].{
|
|
36
|
+
weekday: [0],
|
|
37
|
+
open: [1],
|
|
38
|
+
close: [2]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This recipe is the canonical shape for "Google Sheets export with a header row" sources. If you find yourself debugging `0_*` / `1_*` / `2_*` column names in the YOOtheme source field picker, this is almost always the cause.
|
|
43
|
+
|
|
44
|
+
## Date-primitive functions (new in 2.0.8)
|
|
45
|
+
|
|
46
|
+
JMESPath has no built-in date support. API Mapper ships four custom date primitives so weekday-name ↔ ISO-datetime joins (Calendly → Sheet schedule, HubSpot meeting-times → office-hours, etc.) become possible without exporting to a sidecar service.
|
|
47
|
+
|
|
48
|
+
| Function | Signature | Returns |
|
|
49
|
+
|----------|-----------|---------|
|
|
50
|
+
| `date_weekday` | `date_weekday(iso_string [, tz])` | English weekday name `"Monday"`..`"Sunday"`, or `null` on bad input |
|
|
51
|
+
| `date_iso_to_time` | `date_iso_to_time(iso_string [, tz])` | `"HH:MM"` 24-hour time, or `null` |
|
|
52
|
+
| `date_iso_to_date` | `date_iso_to_date(iso_string [, tz])` | `"YYYY-MM-DD"` calendar date, or `null` |
|
|
53
|
+
| `time_in_window` | `time_in_window(time, from, to)` | `true` if `time` is in `[from, to)`. Supports cross-midnight when `from > to`. `false` on any malformed input |
|
|
54
|
+
|
|
55
|
+
### Timezone rules
|
|
56
|
+
|
|
57
|
+
- `tz` is an IANA timezone string (`"Europe/Berlin"`, `"America/New_York"`, `"UTC"`).
|
|
58
|
+
- If `tz` is omitted, the offset already encoded in the input string is preserved. A Calendly payload `"2026-06-04T09:00:00+02:00"` returns `"09:00"` from `date_iso_to_time(@, )` (no second arg) — i.e. the customer's local time.
|
|
59
|
+
- If `tz` IS supplied, the datetime is shifted into that zone first.
|
|
60
|
+
- An unknown timezone (typo, mistaken country code) returns `null`. This is the same graceful-failure mode as malformed datetimes — one bad row never crashes the flow.
|
|
61
|
+
|
|
62
|
+
### The Maria recipe (Calendly + Sheet weekday-join)
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
# Source A — Calendly available_times
|
|
66
|
+
[].{
|
|
67
|
+
day: date_weekday(start_time, 'Europe/Berlin'),
|
|
68
|
+
local_time: date_iso_to_time(start_time, 'Europe/Berlin'),
|
|
69
|
+
start_time: start_time
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Now each Calendly slot carries a Sheet-comparable `day` (e.g. `"Wednesday"`) AND a comparable `local_time` (e.g. `"09:30"`). See `merge-two-sources-on-key` for the full join pattern.
|
|
74
|
+
|
|
75
|
+
### Slot-against-business-hours filter
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
[?time_in_window(date_iso_to_time(start_time, 'Europe/Berlin'),
|
|
79
|
+
'09:00', '17:00')]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Cross-midnight windows (bars, support shifts):
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
[?time_in_window(local_time, '22:00', '02:00')] # 10pm..2am
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Number formatting — `format_number()`
|
|
89
|
+
|
|
90
|
+
JMESPath has no arithmetic and no number-format support, so a price field
|
|
91
|
+
arrives as raw digits and renders as `$890000` instead of `$890,000`. The
|
|
92
|
+
`format_number()` primitive (new in 2.0.13) groups thousands and fixes
|
|
93
|
+
decimals — the number-format analogue of the date primitives above.
|
|
94
|
+
|
|
95
|
+
| Signature | Returns |
|
|
96
|
+
|-----------|---------|
|
|
97
|
+
| `format_number(value [, decimals [, decimal_sep [, thousands_sep]]])` | grouped string, or `null` on non-numeric input |
|
|
98
|
+
|
|
99
|
+
- `value` — a number OR a numeric string (Sheets/REST often deliver `"1250000"`). Anything non-numeric (including `true`/arrays/empty string) → `null`.
|
|
100
|
+
- `decimals` — digits after the decimal separator (default `0`). Rounds half-up.
|
|
101
|
+
- `decimal_sep` — default `"."`.
|
|
102
|
+
- `thousands_sep` — default `","`.
|
|
103
|
+
|
|
104
|
+
```jmespath
|
|
105
|
+
# US dollars, no cents — the SunWest case
|
|
106
|
+
[*].{name: name, price_display: format_number(price)}
|
|
107
|
+
# 890000 → "890,000"
|
|
108
|
+
# 1450000 → "1,450,000"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```jmespath
|
|
112
|
+
# Two decimals
|
|
113
|
+
format_number(`1234.5`, `2`) # "1,234.50"
|
|
114
|
+
|
|
115
|
+
# European grouping (1.234.567,89)
|
|
116
|
+
format_number(price, `2`, ',', '.')
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Prefix the currency symbol in the same expression with `join`:
|
|
120
|
+
|
|
121
|
+
```jmespath
|
|
122
|
+
[*].{price: join('', ['$', format_number(price)])} # "$890,000"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Because `format_number` returns `null` on bad input, wrap it with
|
|
126
|
+
`coalesce_empty` when a column may contain stray non-numeric rows:
|
|
127
|
+
|
|
128
|
+
```jmespath
|
|
129
|
+
coalesce_empty(format_number(price), 'n/a')
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
> Reminder: JMESPath still has **no arithmetic operators** (`+ - * /`). If you
|
|
133
|
+
> try `price + tax` the engine raises a syntax error now annotated with
|
|
134
|
+
> *"JMESPath has no arithmetic operators"*. Compute totals upstream, in a
|
|
135
|
+
> YOOtheme element, or concatenate strings with `join('', [a, b])`.
|
|
136
|
+
|
|
137
|
+
## Logic primitive — `conditional()`
|
|
138
|
+
|
|
139
|
+
JMESPath has no `if/else` statement. The classic workaround is the
|
|
140
|
+
short-circuit ternary `(cond && X) || Y`, but it has a sharp edge: when `X` is
|
|
141
|
+
itself falsy (`''`, `0`, `false`) the `|| Y` branch fires even though `cond` was
|
|
142
|
+
true. The `conditional()` primitive is a flat, readable if/else without that
|
|
143
|
+
trap.
|
|
144
|
+
|
|
145
|
+
| Signature | Returns |
|
|
146
|
+
|-----------|---------|
|
|
147
|
+
| `conditional(condition, then_value [, else_value])` | `then_value` when `condition` is truthy; `else_value` (default `null`) when falsy |
|
|
148
|
+
|
|
149
|
+
Truthiness mirrors the engine: `false`, `null`, `''` (empty string), `[]`
|
|
150
|
+
(empty array) and `{}` (empty object) are falsy — **everything else, including
|
|
151
|
+
`0`, is truthy**.
|
|
152
|
+
|
|
153
|
+
```jmespath
|
|
154
|
+
# per-row in-stock badge from a stock field
|
|
155
|
+
[*].{name: name, in_stock: conditional(stock > `0`, 'in stock', 'sold out')}
|
|
156
|
+
# {stock:5} → "in stock" {stock:0} → "sold out"
|
|
157
|
+
|
|
158
|
+
# returns a falsy then-value verbatim — no ternary fall-through
|
|
159
|
+
[*].{label: conditional(is_featured, '', 'standard')}
|
|
160
|
+
# is_featured truthy → "" (the ternary (is_featured && '') || 'standard' would
|
|
161
|
+
# have wrongly returned "standard")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`conditional()` is **flatter and more readable** than the ternary for a simple
|
|
165
|
+
two-way branch — but it does NOT dodge the depth cap: a function call still
|
|
166
|
+
costs `+1` nesting level. For >2 branches, chain ternaries
|
|
167
|
+
(`(a && 'x') || (b && 'y') || 'z'`); `conditional()` covers the two-way case.
|
|
168
|
+
|
|
169
|
+
## Depth-cap warning
|
|
170
|
+
|
|
171
|
+
JMESPath expression depth is capped at **10 levels** of bracket / pipe nesting. Above that, the engine returns `expression too deeply nested (N levels)`.
|
|
172
|
+
|
|
173
|
+
**As of 2.0.8 this is caught at flow-write time** (HTTP 422 on `flow_create` / `flow_update`) with per-node `jmespathErrors`. The response payload looks like:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"success": false,
|
|
178
|
+
"error": "1 JMESPath expression exceeded the depth limit of 10",
|
|
179
|
+
"jmespathErrors": { "t1": "JMESPath expression too deeply nested (13 levels). Maximum: 10" },
|
|
180
|
+
"status": 422
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
If you hit this, the cure is almost always to split the expression into TWO transform nodes piped together — that resets the depth counter and is easier to debug.
|
|
185
|
+
|
|
186
|
+
## See also
|
|
187
|
+
|
|
188
|
+
- `merge-two-sources-on-key` — joining two sources on a shared key, with date-primitive bridging.
|
|
189
|
+
- `troubleshooting` — broader error triage.
|
|
@@ -70,7 +70,7 @@ Joomla extensions install as a `.zip` (system plugin). The MCP server bundles a
|
|
|
70
70
|
To verify the plugin is loaded:
|
|
71
71
|
```
|
|
72
72
|
apimapper_health
|
|
73
|
-
# → { platform: "joomla", version: "2.0.
|
|
73
|
+
# → { platform: "joomla", version: "2.0.13", plugin: "enabled" }
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
If `plugin: "disabled"`, enable via Joomla admin → Extensions → Plugins → search "API Mapper".
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Library template discovery
|
|
2
|
+
|
|
3
|
+
Inspect a library template's contract BEFORE you activate it, so the connection
|
|
4
|
+
lands fully configured the first time.
|
|
5
|
+
|
|
6
|
+
## Why this matters
|
|
7
|
+
|
|
8
|
+
The library is the mandatory first stop for any new API (see the library-first
|
|
9
|
+
guard below). But before you call `apimapper_library_activate`, you want to know
|
|
10
|
+
what the template actually declares: which base URL and endpoints it ships, which
|
|
11
|
+
auth scheme it needs, and which placeholder values (`extra_fields`) you must pass.
|
|
12
|
+
Activating blind is the single most common cause of an empty source that is hard
|
|
13
|
+
to debug from downstream tool calls.
|
|
14
|
+
|
|
15
|
+
## The discovery sequence
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
apimapper_library_featured({}) # 1. the curated short-list
|
|
19
|
+
apimapper_library_list({ search: '<api-name>' }) # 2. search the full catalog by name (param is `search`, 3+ chars)
|
|
20
|
+
apimapper_library_connection_detail({ id: '<id>' }) # 3. read the template's full contract
|
|
21
|
+
apimapper_credential_list({}) # 4. find a matching credential (auth-protected templates)
|
|
22
|
+
apimapper_library_activate({ id, credential_id, extra_fields }) # 5. activate, fully configured
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Steps 1-2 are routed first-class; step 3 routes through the gateway:
|
|
26
|
+
`apimapper_advanced({ tool: 'apimapper_library_connection_detail', arguments: { id } })`.
|
|
27
|
+
|
|
28
|
+
## What `library_connection_detail` tells you
|
|
29
|
+
|
|
30
|
+
Read these fields off the returned template before activating:
|
|
31
|
+
|
|
32
|
+
| Field | What to do with it |
|
|
33
|
+
|-------|--------------------|
|
|
34
|
+
| `base_url` | The upstream host. Confirms which API the template targets. |
|
|
35
|
+
| `endpoints` | The named requests the template ships (list/detail/search). The activated connection exposes these. |
|
|
36
|
+
| `auth` scheme | `none`, `bearer`, `api_key`, or `oauth`. Tells you whether step 4 (credential) is required. |
|
|
37
|
+
| `extra_fields` | The placeholder values YOU must supply at activation time (e.g. `spreadsheet_id` for Google Sheets, `api_key` for Pexels). Required `extra_fields` left unset land a broken connection. |
|
|
38
|
+
|
|
39
|
+
## Then activate, not create
|
|
40
|
+
|
|
41
|
+
Once you have read the contract:
|
|
42
|
+
|
|
43
|
+
- **Auth-protected template** (Google Sheets, Notion, Airtable, Pexels,
|
|
44
|
+
OpenWeatherMap, Calendly, GitHub, ...): call `apimapper_credential_list({})`
|
|
45
|
+
first, then pass the right `credential_id` explicitly to `library_activate`.
|
|
46
|
+
The activation auto-links a credential only when exactly ONE matching one
|
|
47
|
+
exists for the provider; otherwise it returns a structured
|
|
48
|
+
`credential_required` / `credential_ambiguous` error listing the candidates.
|
|
49
|
+
- Pass any required `extra_fields` the template declared in step 3.
|
|
50
|
+
|
|
51
|
+
## The library-first guard (why you discover instead of hand-rolling)
|
|
52
|
+
|
|
53
|
+
`apimapper_connection_create` is the fallback for niche or unknown APIs ONLY. The
|
|
54
|
+
server enforces this: a custom `connection_create` whose endpoint host is covered
|
|
55
|
+
by a curated template is rejected with HTTP 409 `error_code:
|
|
56
|
+
library_template_available`. The error names the template and the exact
|
|
57
|
+
`apimapper_library_activate({ id })` call to run instead, with a structured
|
|
58
|
+
`library_suggestion`. Always inspect-then-activate before reaching for a custom
|
|
59
|
+
connection.
|
|
60
|
+
|
|
61
|
+
## Cross-references
|
|
62
|
+
|
|
63
|
+
- `apimapper_get_skill({ topic: 'getting-started' })` — Step 1 (mandatory): check the library first.
|
|
64
|
+
- `apimapper_get_skill({ topic: 'oauth' })` — wiring OAuth-protected templates.
|
|
65
|
+
- `apimapper_library_connection_detail` — the tool this topic is about.
|