@wootsup/mcp 0.1.0-rc.9 → 0.3.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 +148 -83
- package/README.md +36 -32
- 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/cli-hint.d.ts +22 -0
- package/dist/cli-hint.js +55 -0
- package/dist/cli-hint.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +163 -22
- package/dist/index.js.map +1 -1
- package/dist/install-skill.js +1 -1
- package/dist/modules/apimapper/cache.d.ts +2 -2
- package/dist/modules/apimapper/cache.js +119 -29
- package/dist/modules/apimapper/cache.js.map +1 -1
- package/dist/modules/apimapper/client.d.ts +102 -1
- package/dist/modules/apimapper/client.js +631 -297
- package/dist/modules/apimapper/client.js.map +1 -1
- package/dist/modules/apimapper/connections-format.d.ts +51 -0
- package/dist/modules/apimapper/connections-format.js +261 -0
- package/dist/modules/apimapper/connections-format.js.map +1 -0
- package/dist/modules/apimapper/connections-trim.d.ts +82 -0
- package/dist/modules/apimapper/connections-trim.js +224 -0
- package/dist/modules/apimapper/connections-trim.js.map +1 -0
- package/dist/modules/apimapper/connections.d.ts +14 -2
- package/dist/modules/apimapper/connections.js +612 -153
- 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-format.d.ts +21 -0
- package/dist/modules/apimapper/credentials-format.js +145 -0
- package/dist/modules/apimapper/credentials-format.js.map +1 -0
- package/dist/modules/apimapper/credentials.d.ts +12 -2
- package/dist/modules/apimapper/credentials.js +226 -73
- package/dist/modules/apimapper/credentials.js.map +1 -1
- package/dist/modules/apimapper/diagnose.d.ts +54 -2
- package/dist/modules/apimapper/diagnose.js +213 -12
- package/dist/modules/apimapper/diagnose.js.map +1 -1
- package/dist/modules/apimapper/elicitation.d.ts +54 -0
- package/dist/modules/apimapper/elicitation.js +90 -0
- package/dist/modules/apimapper/elicitation.js.map +1 -0
- package/dist/modules/apimapper/flows-format.d.ts +50 -0
- package/dist/modules/apimapper/flows-format.js +318 -0
- package/dist/modules/apimapper/flows-format.js.map +1 -0
- package/dist/modules/apimapper/flows.d.ts +13 -2
- package/dist/modules/apimapper/flows.js +312 -122
- package/dist/modules/apimapper/flows.js.map +1 -1
- package/dist/modules/apimapper/gateway/advanced-tool.d.ts +9 -0
- package/dist/modules/apimapper/gateway/advanced-tool.js +265 -0
- package/dist/modules/apimapper/gateway/advanced-tool.js.map +1 -0
- package/dist/modules/apimapper/gateway/capturing-server.d.ts +81 -0
- package/dist/modules/apimapper/gateway/capturing-server.js +87 -0
- package/dist/modules/apimapper/gateway/capturing-server.js.map +1 -0
- package/dist/modules/apimapper/gateway/essentials.d.ts +4 -0
- package/dist/modules/apimapper/gateway/essentials.js +35 -0
- package/dist/modules/apimapper/gateway/essentials.js.map +1 -0
- package/dist/modules/apimapper/gateway/test-support.d.ts +17 -0
- package/dist/modules/apimapper/gateway/test-support.js +43 -0
- package/dist/modules/apimapper/gateway/test-support.js.map +1 -0
- package/dist/modules/apimapper/get-skill.d.ts +3 -3
- package/dist/modules/apimapper/get-skill.js +47 -7
- package/dist/modules/apimapper/get-skill.js.map +1 -1
- package/dist/modules/apimapper/graph-builder.js +1 -1
- package/dist/modules/apimapper/graph-builder.js.map +1 -1
- package/dist/modules/apimapper/graph.d.ts +2 -2
- package/dist/modules/apimapper/graph.js +170 -35
- package/dist/modules/apimapper/graph.js.map +1 -1
- package/dist/modules/apimapper/index.d.ts +17 -1
- package/dist/modules/apimapper/index.js +68 -17
- package/dist/modules/apimapper/index.js.map +1 -1
- package/dist/modules/apimapper/inspect.d.ts +3 -2
- package/dist/modules/apimapper/inspect.js +97 -13
- package/dist/modules/apimapper/inspect.js.map +1 -1
- package/dist/modules/apimapper/library.d.ts +2 -2
- package/dist/modules/apimapper/library.js +665 -80
- package/dist/modules/apimapper/library.js.map +1 -1
- package/dist/modules/apimapper/license-format.d.ts +22 -0
- package/dist/modules/apimapper/license-format.js +149 -0
- package/dist/modules/apimapper/license-format.js.map +1 -0
- package/dist/modules/apimapper/license.d.ts +16 -2
- package/dist/modules/apimapper/license.js +62 -38
- package/dist/modules/apimapper/license.js.map +1 -1
- package/dist/modules/apimapper/local-sources.d.ts +2 -2
- package/dist/modules/apimapper/local-sources.js +44 -30
- package/dist/modules/apimapper/local-sources.js.map +1 -1
- package/dist/modules/apimapper/misc.d.ts +30 -2
- package/dist/modules/apimapper/misc.js +114 -49
- package/dist/modules/apimapper/misc.js.map +1 -1
- package/dist/modules/apimapper/node-schema.d.ts +52 -0
- package/dist/modules/apimapper/node-schema.js +70 -2
- package/dist/modules/apimapper/node-schema.js.map +1 -1
- package/dist/modules/apimapper/normalizers.d.ts +1 -0
- package/dist/modules/apimapper/normalizers.js +51 -0
- package/dist/modules/apimapper/normalizers.js.map +1 -1
- package/dist/modules/apimapper/onboarding.d.ts +78 -3
- package/dist/modules/apimapper/onboarding.js +428 -26
- package/dist/modules/apimapper/onboarding.js.map +1 -1
- package/dist/modules/apimapper/read-cache.d.ts +31 -2
- package/dist/modules/apimapper/read-cache.js +20 -6
- package/dist/modules/apimapper/read-cache.js.map +1 -1
- package/dist/modules/apimapper/render/_shared.d.ts +24 -0
- package/dist/modules/apimapper/render/_shared.js +84 -0
- package/dist/modules/apimapper/render/_shared.js.map +1 -0
- package/dist/modules/apimapper/render/dag.d.ts +18 -0
- package/dist/modules/apimapper/render/dag.js +70 -0
- package/dist/modules/apimapper/render/dag.js.map +1 -0
- package/dist/modules/apimapper/render/index.d.ts +2 -0
- package/dist/modules/apimapper/render/index.js +112 -0
- package/dist/modules/apimapper/render/index.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/chart-bar.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/chart-bar.js +70 -0
- package/dist/modules/apimapper/render/renderers/chart-bar.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/chart-line.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/chart-line.js +71 -0
- package/dist/modules/apimapper/render/renderers/chart-line.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/diff.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/diff.js +154 -0
- package/dist/modules/apimapper/render/renderers/diff.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/flow-diagram.d.ts +1 -0
- package/dist/modules/apimapper/render/renderers/flow-diagram.js +180 -0
- package/dist/modules/apimapper/render/renderers/flow-diagram.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/json-tree.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/json-tree.js +87 -0
- package/dist/modules/apimapper/render/renderers/json-tree.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/schema-diagram.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/schema-diagram.js +83 -0
- package/dist/modules/apimapper/render/renderers/schema-diagram.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/table.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/table.js +75 -0
- package/dist/modules/apimapper/render/renderers/table.js.map +1 -0
- package/dist/modules/apimapper/render/schemas.d.ts +23 -0
- package/dist/modules/apimapper/render/schemas.js +56 -0
- package/dist/modules/apimapper/render/schemas.js.map +1 -0
- package/dist/modules/apimapper/render/secret-masking.d.ts +5 -0
- package/dist/modules/apimapper/render/secret-masking.js +51 -0
- package/dist/modules/apimapper/render/secret-masking.js.map +1 -0
- package/dist/modules/apimapper/render/sidecar.d.ts +21 -0
- package/dist/modules/apimapper/render/sidecar.js +66 -0
- package/dist/modules/apimapper/render/sidecar.js.map +1 -0
- package/dist/modules/apimapper/render/token-cap.d.ts +21 -0
- package/dist/modules/apimapper/render/token-cap.js +57 -0
- package/dist/modules/apimapper/render/token-cap.js.map +1 -0
- package/dist/modules/apimapper/schema.d.ts +2 -2
- package/dist/modules/apimapper/schema.js +92 -33
- package/dist/modules/apimapper/schema.js.map +1 -1
- package/dist/modules/apimapper/settings-format.d.ts +23 -0
- package/dist/modules/apimapper/settings-format.js +135 -0
- package/dist/modules/apimapper/settings-format.js.map +1 -0
- package/dist/modules/apimapper/settings.d.ts +2 -2
- package/dist/modules/apimapper/settings.js +100 -42
- 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/skill-resources.d.ts +2 -2
- package/dist/modules/apimapper/skill-resources.js.map +1 -1
- package/dist/modules/apimapper/token-baseline.harness.d.ts +91 -0
- package/dist/modules/apimapper/token-baseline.harness.js +291 -0
- package/dist/modules/apimapper/token-baseline.harness.js.map +1 -0
- package/dist/modules/apimapper/tool-result.d.ts +46 -0
- package/dist/modules/apimapper/tool-result.js +63 -0
- package/dist/modules/apimapper/tool-result.js.map +1 -0
- package/dist/modules/apimapper/toolslist-size.d.ts +56 -0
- package/dist/modules/apimapper/toolslist-size.js +192 -0
- package/dist/modules/apimapper/toolslist-size.js.map +1 -0
- package/dist/modules/apimapper/types.d.ts +44 -8
- package/dist/modules/apimapper/types.js +26 -1
- package/dist/modules/apimapper/types.js.map +1 -1
- package/dist/modules/apimapper/use-profile.d.ts +21 -0
- package/dist/modules/apimapper/use-profile.js +56 -2
- package/dist/modules/apimapper/use-profile.js.map +1 -1
- package/dist/modules/apimapper/whitelist-drift.d.ts +85 -0
- package/dist/modules/apimapper/whitelist-drift.js +360 -0
- package/dist/modules/apimapper/whitelist-drift.js.map +1 -0
- package/dist/modules/apimapper/workflows.d.ts +2 -2
- package/dist/modules/apimapper/workflows.js +202 -20
- 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 +186 -0
- package/dist/modules/apimapper/yootheme-binding.js.map +1 -0
- package/dist/platform/index.d.ts +56 -0
- package/dist/platform/index.js +195 -7
- package/dist/platform/index.js.map +1 -1
- 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-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 +60 -0
- package/dist/setup-cli.js +155 -5
- package/dist/setup-cli.js.map +1 -1
- package/dist/sites/loader.d.ts +41 -0
- package/dist/sites/loader.js +119 -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 +1 -1
- package/dist/skill-instructions.js +5 -0
- 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 +22 -22
- package/docs/customgraph-internal-migration.md +4 -4
- package/docs/security.md +2 -21
- package/docs/tools.md +40 -12
- package/manifest.json +77 -70
- package/package.json +68 -60
- package/skills/apimapper/SKILL.md +53 -7
- package/skills/apimapper/reference/conditional-style-multi-items.md +114 -0
- package/skills/apimapper/reference/jmespath-pitfalls.md +108 -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 +99 -0
- package/skills/apimapper/reference/render.md +132 -0
- package/skills/apimapper/reference/troubleshooting.md +21 -1
- package/skills/apimapper/reference/yootheme.md +1 -1
- 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,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({ query: '<api-name>' }) # 2. search the full catalog by name
|
|
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.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Merge two sources on a shared key
|
|
2
|
+
|
|
3
|
+
Join items from two sources on a shared key, optionally filter the join, surface the result as one Source for YOOtheme.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- You have two APIs whose rows belong together but live behind separate endpoints (orders + customers, Calendly slots + Sheet schedule, GitHub issues + sprint planning).
|
|
8
|
+
- The keys you want to join on either (a) are already string-equal, or (b) need a small Transform to be made comparable (e.g. ISO datetime → weekday name).
|
|
9
|
+
|
|
10
|
+
## The canonical recipe
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Source-A ─┐
|
|
14
|
+
├─► Merge (mode: join, key: <field>) ─► Filter ─► Output
|
|
15
|
+
Source-B ─┘
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
1. Drag both Source nodes onto the canvas, wire them up.
|
|
19
|
+
2. Drag a Merge node between them. Set:
|
|
20
|
+
- `mode: join` — emit one row per Source-A item, with Source-B fields nested under `b`.
|
|
21
|
+
- `key`: the field name present on BOTH sides (e.g. `weekday`).
|
|
22
|
+
3. (Optional) drag a Filter node after Merge — typical predicate uses both A- and B-side fields.
|
|
23
|
+
4. Wire to Output (YOOtheme Source) and Publish.
|
|
24
|
+
|
|
25
|
+
## When the keys aren't string-equal — date-primitive bridging
|
|
26
|
+
|
|
27
|
+
This is the case Cold-AI #5 walk-through hit: Calendly emits `start_time` as ISO-UTC (`"2026-06-04T09:00:00+00:00"`), but the customer's Sheet schedule is keyed on weekday names (`"Wednesday"`). The keys aren't comparable as-is.
|
|
28
|
+
|
|
29
|
+
**Fix:** insert a Transform node BEFORE Merge on the Calendly side that derives a Sheet-compatible key.
|
|
30
|
+
|
|
31
|
+
### Maria's flow (Calendly + Google Sheet schedule)
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
Calendly available_times ─► Transform (project day+local_time) ─┐
|
|
35
|
+
├─► Merge (key: day) ─► Filter ─► Output
|
|
36
|
+
Google Sheet schedule ──────────────────────────────────────┘
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Step 1 — Transform on the Calendly side
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
[].{
|
|
43
|
+
day: date_weekday(start_time, 'Europe/Berlin'),
|
|
44
|
+
local_time: date_iso_to_time(start_time, 'Europe/Berlin'),
|
|
45
|
+
start_time: start_time
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Each Calendly row now carries:
|
|
50
|
+
|
|
51
|
+
| field | value |
|
|
52
|
+
|-------|-------|
|
|
53
|
+
| `day` | `"Wednesday"` — Sheet-comparable |
|
|
54
|
+
| `local_time` | `"09:00"` — comparable to Sheet's `open`/`close` |
|
|
55
|
+
| `start_time` | original ISO datetime (preserved for downstream rendering) |
|
|
56
|
+
|
|
57
|
+
#### Step 2 — Merge on `day`
|
|
58
|
+
|
|
59
|
+
Merge mode `join`, key `day`. Sheet row arrives as `b` on each Calendly row:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"day": "Wednesday",
|
|
64
|
+
"local_time": "09:00",
|
|
65
|
+
"start_time": "2026-06-03T07:00:00+00:00",
|
|
66
|
+
"b": { "weekday": "Wednesday", "open": "09:00", "close": "17:00" }
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Step 3 — Filter to slots inside business hours
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
[?time_in_window(local_time, b.open, b.close)]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`time_in_window` is left-closed, right-open ([from, to)) — a 17:00 close-time does NOT include a 17:00 slot. Cross-midnight windows (e.g. bar `22:00..02:00`) are also handled.
|
|
77
|
+
|
|
78
|
+
#### Step 4 — Output
|
|
79
|
+
|
|
80
|
+
Wire to a YOOtheme Output. Slots that survive Steps 1-3 become the rows of a YOOtheme Source named e.g. `BookableSlots`.
|
|
81
|
+
|
|
82
|
+
## Other date-bridge patterns
|
|
83
|
+
|
|
84
|
+
| Customer scenario | Source-A key | Source-B key | Bridge |
|
|
85
|
+
|-------------------|--------------|--------------|--------|
|
|
86
|
+
| HubSpot meetings ↔ office-hours | ISO datetime | weekday name | `date_weekday(@.start, tz)` |
|
|
87
|
+
| Stripe invoices ↔ month-rollup | ISO datetime | `"YYYY-MM"` | `substring(date_iso_to_date(@.created, tz), 0, 7)` |
|
|
88
|
+
| GitHub issues ↔ daily-standup notes | ISO datetime | `"YYYY-MM-DD"` | `date_iso_to_date(@.updated_at, tz)` |
|
|
89
|
+
|
|
90
|
+
## Pitfalls
|
|
91
|
+
|
|
92
|
+
- **Timezone matters.** A slot at `2026-06-04T23:30:00Z` is Wednesday in UTC but Thursday in Berlin. Always supply the second `tz` arg matching the customer's business timezone.
|
|
93
|
+
- **`null` rows propagate.** A Calendly row with a malformed `start_time` projects to `{day: null, local_time: null, start_time: <bad>}`. The Merge will silently emit it under a `null` key — pre-filter with `[?day]` if you need to drop them.
|
|
94
|
+
- **Depth limit.** Don't try to do the whole projection + merge + filter inside ONE expression. JMESPath caps depth at 10. Two transform nodes piped is cleaner and stays under the limit.
|
|
95
|
+
|
|
96
|
+
## See also
|
|
97
|
+
|
|
98
|
+
- `jmespath-pitfalls` — full pitfall catalogue + date-primitive function reference.
|
|
99
|
+
- `yootheme` — how the published Source appears in YOOtheme Builder.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Render
|
|
2
|
+
|
|
3
|
+
`apimapper_render` delivers a ready-to-display text visualization of API Mapper data — tables, charts, diagrams, trees, and diffs. It is a pure function: deterministic, no network, no side-effects. The returned text is ready to show the user as-is in any MCP client, and you are free to enrich it however your client allows (artifacts, inline images, diagram-markdown, charts — whatever renders best).
|
|
4
|
+
|
|
5
|
+
## The two-step pattern
|
|
6
|
+
|
|
7
|
+
`apimapper_render` does not fetch data. The pattern is always: fetch with the matching source tool, then pass that result as `data`.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
apimapper_render({ type, data, options? })
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Field | Shape | Notes |
|
|
14
|
+
|-------|-------|-------|
|
|
15
|
+
| `type` | one of the 7 viz-types below | required |
|
|
16
|
+
| `data` | shape depends on `type` | required |
|
|
17
|
+
| `options` | object — see Options | optional |
|
|
18
|
+
|
|
19
|
+
## The 7 viz-types
|
|
20
|
+
|
|
21
|
+
Each type expects a specific `data` shape and pairs with a prerequisite source tool.
|
|
22
|
+
|
|
23
|
+
### `table`
|
|
24
|
+
|
|
25
|
+
Renders an array of objects as an aligned text table.
|
|
26
|
+
|
|
27
|
+
- **`data`**: an array of objects — `[{ ... }, { ... }]`.
|
|
28
|
+
- **Fetch with**: `apimapper_connection_data` (live REST rows) or `apimapper_graph_preview` (rows after a pipeline runs) or `apimapper_flow_trace` (per-node row samples).
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
const rows = apimapper_connection_data({ connection_id: "conn_xyz" });
|
|
32
|
+
apimapper_render({ type: "table", data: rows });
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### `chart-bar`
|
|
36
|
+
|
|
37
|
+
Renders a horizontal bar chart.
|
|
38
|
+
|
|
39
|
+
- **`data`**: `{ labels: string[], values: number[] }` — `labels` and `values` are equal length.
|
|
40
|
+
- **Fetch with**: any tool that yields parallel label/value arrays — typically derived from `apimapper_connection_data` or `apimapper_graph_preview` rows.
|
|
41
|
+
|
|
42
|
+
### `chart-line`
|
|
43
|
+
|
|
44
|
+
Renders a line chart (sparkline-style trend).
|
|
45
|
+
|
|
46
|
+
- **`data`**: `{ labels: string[], values: number[] }` — equal length, same shape as `chart-bar`.
|
|
47
|
+
- **Fetch with**: time-series or sequential numeric data from `apimapper_connection_data` / `apimapper_graph_preview`.
|
|
48
|
+
|
|
49
|
+
### `schema-diagram`
|
|
50
|
+
|
|
51
|
+
Renders a source's field structure as a typed schema diagram.
|
|
52
|
+
|
|
53
|
+
- **`data`**: a schema profile carrying a `fields[]` array.
|
|
54
|
+
- **Fetch with**: `apimapper_schema_profile` — pass its result straight through.
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
const profile = apimapper_schema_profile({ connection_id: "conn_xyz" });
|
|
58
|
+
apimapper_render({ type: "schema-diagram", data: profile });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `flow-diagram`
|
|
62
|
+
|
|
63
|
+
Renders a flow's node/edge graph as an ASCII pipeline diagram (Source → Filter → Transform → Output).
|
|
64
|
+
|
|
65
|
+
- **`data`**: a flow object carrying `nodes[]` and `edges[]`.
|
|
66
|
+
- **Fetch with**: `apimapper_flow_get` — pass the returned flow object.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
const flow = apimapper_flow_get({ flow_id: "flow_abc" });
|
|
70
|
+
apimapper_render({ type: "flow-diagram", data: flow });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### `json-tree`
|
|
74
|
+
|
|
75
|
+
Renders any JSON value as an indented tree, with a depth cap.
|
|
76
|
+
|
|
77
|
+
- **`data`**: any JSON value — object, array, or primitive.
|
|
78
|
+
- **Fetch with**: any tool. Pass the fetched payload directly; this is the catch-all for inspecting an unfamiliar response shape.
|
|
79
|
+
|
|
80
|
+
### `diff`
|
|
81
|
+
|
|
82
|
+
Renders a side-by-side comparison of two datasets.
|
|
83
|
+
|
|
84
|
+
- **`data`**: `{ old: ..., new: ... }` — both halves are JSON values of any shape.
|
|
85
|
+
- **Fetch with**: fetch both datasets first, e.g. two `apimapper_schema_profile` calls to compare a source's schema before and after an upstream change.
|
|
86
|
+
|
|
87
|
+
## Options
|
|
88
|
+
|
|
89
|
+
`options` tunes the output per render. Every field is optional with a sensible default.
|
|
90
|
+
|
|
91
|
+
| Option | Applies to | Default | Effect |
|
|
92
|
+
|--------|-----------|---------|--------|
|
|
93
|
+
| `title` | all types | none | Title shown above the visualization (max 200 chars) |
|
|
94
|
+
| `max_rows` | `table`, `json-tree` | 50 | Caps rows/items rendered (1–500) |
|
|
95
|
+
| `columns` | `table` | auto-detect | Field whitelist — render only these columns |
|
|
96
|
+
| `depth` | `json-tree` | 3 | Depth cap for nested structures (1–8) |
|
|
97
|
+
| `mask_secrets` | `diff` | true | Masks values for sensitive field names |
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
apimapper_render({
|
|
101
|
+
type: "table",
|
|
102
|
+
data: rows,
|
|
103
|
+
options: { title: "Marketing OKRs", max_rows: 20, columns: ["title", "status"] }
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## The `_visualization` sidecar
|
|
108
|
+
|
|
109
|
+
Flow-mutation tools attach a flow diagram automatically — there is no separate call to make.
|
|
110
|
+
|
|
111
|
+
`apimapper_flow_create`, `apimapper_flow_update`, and `apimapper_graph_validate` each include a `_visualization` field in their response:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
_visualization: {
|
|
115
|
+
diagram: "<ready-to-show ASCII flow diagram>",
|
|
116
|
+
display_hint: "Show this flow diagram to the user."
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The `diagram` is a ready-to-show ASCII preview of the flow you just created or validated — surface it to the user directly. For flows larger than the inline preview, call `apimapper_render({ type: "flow-diagram", data: flow })` for the complete diagram.
|
|
121
|
+
|
|
122
|
+
The sidecar is opt-out via the `APIMAPPER_AUTO_VISUALIZE_FLOWS="false"` environment variable; it is on by default.
|
|
123
|
+
|
|
124
|
+
## Common pairings
|
|
125
|
+
|
|
126
|
+
| Goal | Sequence |
|
|
127
|
+
|------|----------|
|
|
128
|
+
| Show live connection rows | `apimapper_connection_data` → `apimapper_render` type `table` |
|
|
129
|
+
| Show a flow's pipeline shape | `apimapper_flow_get` → `apimapper_render` type `flow-diagram` |
|
|
130
|
+
| Show a source's field structure | `apimapper_schema_profile` → `apimapper_render` type `schema-diagram` |
|
|
131
|
+
| Inspect an unfamiliar response | any fetch → `apimapper_render` type `json-tree` |
|
|
132
|
+
| Compare schema before/after | two `apimapper_schema_profile` calls → `apimapper_render` type `diff` |
|
|
@@ -79,6 +79,26 @@ If `apimapper_diagnose` is unavailable in your version, run the manual checks be
|
|
|
79
79
|
### "Source not appearing in YOOtheme Builder"
|
|
80
80
|
- Flow is saved but not **published**. See `skill://apimapper/yootheme` step 4.
|
|
81
81
|
|
|
82
|
+
### "YOOtheme Builder source list missing newly-published flow"
|
|
83
|
+
|
|
84
|
+
Symptom: A flow was just published with `apimapper_flow_full_recompile_publish`, but
|
|
85
|
+
YOOtheme Builder's source dropdown (or `yootheme_builder_sources_list` from the
|
|
86
|
+
sibling MCP) doesn't show it. The schema cache went stale.
|
|
87
|
+
|
|
88
|
+
Fix (one of):
|
|
89
|
+
|
|
90
|
+
- `apimapper_cache_invalidate({ scope: "yootheme.schema" })` — explicit flush of the
|
|
91
|
+
YOOtheme GraphQL schema cache files (`schema-*.{php,gql,error.gql}`). Direct-file
|
|
92
|
+
surface, bypasses the InvalidationBus.
|
|
93
|
+
- `apimapper_flow_full_recompile_publish({ id: ..., autofix: true })` — re-publish
|
|
94
|
+
with autofix. Since 2.0.8 (Wave-12 F8) this clears the schema-`.php`/`.gql`/`.error.gql`
|
|
95
|
+
triplets correctly.
|
|
96
|
+
|
|
97
|
+
Root cause was a glob-pattern gap in `YOOthemeSchemaCacheBuster` pre-2.0.8 — the
|
|
98
|
+
`.gql` file was the source-of-truth for YT's `LoadSourceSchema::handle()`, but only
|
|
99
|
+
`.php` files were unlinked. The `yootheme.schema` scope is the manual escape hatch
|
|
100
|
+
for customers still on cached state from before the fix landed.
|
|
101
|
+
|
|
82
102
|
### "Source shows but fields are empty"
|
|
83
103
|
- Field-name case mismatch. YOOtheme expects lowercase keys.
|
|
84
104
|
- Fix: Transform projection should lowercase: `[*].{title: name, image: cover_url}`.
|
|
@@ -120,4 +140,4 @@ ssh deploy@dev.wootsup.com "tail -200 /var/www/dev.wootsup.com/joomla/administra
|
|
|
120
140
|
`apimapper_diagnose` first. If still stuck:
|
|
121
141
|
1. Capture the full request + response (use the tool's `raw=true` flag where available).
|
|
122
142
|
2. Note the platform, version (`apimapper_health`), license tier.
|
|
123
|
-
3. Open a support ticket at `
|
|
143
|
+
3. Open a support ticket at `wootsup.com/contact/` with that bundle.
|
|
@@ -81,7 +81,7 @@ Same flow can ALSO emit a Shortcode output (`output_type="shortcode"`). Use this
|
|
|
81
81
|
- Using outside YOOtheme (page builders, plugins)
|
|
82
82
|
- Quick previews in admin
|
|
83
83
|
|
|
84
|
-
Shortcode currently supports a subset of the YOOtheme render-time features.
|
|
84
|
+
Shortcode currently supports a subset of the YOOtheme render-time features. See `https://wootsup.com/docs/mcp/api-mapper/` (the shortcode subpage will land in a later release).
|
|
85
85
|
|
|
86
86
|
## Common pitfalls
|
|
87
87
|
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
export interface OAuthClientRegistration {
|
|
2
|
-
client_id: string;
|
|
3
|
-
client_id_issued_at: number;
|
|
4
|
-
redirect_uris: string[];
|
|
5
|
-
client_name?: string;
|
|
6
|
-
token_endpoint_auth_method: "none";
|
|
7
|
-
grant_types: ["authorization_code", "refresh_token"];
|
|
8
|
-
response_types: ["code"];
|
|
9
|
-
}
|
|
10
|
-
export interface IssueCodeArgs {
|
|
11
|
-
client_id: string;
|
|
12
|
-
redirect_uri: string;
|
|
13
|
-
code_challenge: string;
|
|
14
|
-
code_challenge_method: "S256" | "plain";
|
|
15
|
-
scope: string;
|
|
16
|
-
state?: string;
|
|
17
|
-
}
|
|
18
|
-
export interface ExchangeCodeArgs {
|
|
19
|
-
client_id: string;
|
|
20
|
-
code: string;
|
|
21
|
-
redirect_uri: string;
|
|
22
|
-
code_verifier: string;
|
|
23
|
-
}
|
|
24
|
-
export interface RefreshArgs {
|
|
25
|
-
client_id: string;
|
|
26
|
-
refresh_token: string;
|
|
27
|
-
}
|
|
28
|
-
export interface TokenResponse {
|
|
29
|
-
access_token: string;
|
|
30
|
-
token_type: "Bearer";
|
|
31
|
-
expires_in: number;
|
|
32
|
-
refresh_token: string;
|
|
33
|
-
scope: string;
|
|
34
|
-
}
|
|
35
|
-
export interface AccessTokenInfo {
|
|
36
|
-
client_id: string;
|
|
37
|
-
scope: string;
|
|
38
|
-
expires_at: number;
|
|
39
|
-
}
|
|
40
|
-
export interface OAuthProviderOptions {
|
|
41
|
-
/** Clock function returning unix seconds. Default: Date.now()/1000. */
|
|
42
|
-
now?: () => number;
|
|
43
|
-
/** Authorization code TTL (default 600s = 10 min). */
|
|
44
|
-
codeTtlSeconds?: number;
|
|
45
|
-
/** Access token TTL (default 86400s = 24 h). */
|
|
46
|
-
tokenTtlSeconds?: number;
|
|
47
|
-
/** Refresh token TTL (default 604800s = 7 d). */
|
|
48
|
-
refreshTtlSeconds?: number;
|
|
49
|
-
}
|
|
50
|
-
export interface OAuthProvider {
|
|
51
|
-
registerClient(args: {
|
|
52
|
-
redirect_uris: string[];
|
|
53
|
-
client_name?: string;
|
|
54
|
-
}): OAuthClientRegistration;
|
|
55
|
-
getClient(client_id: string): OAuthClientRegistration | null;
|
|
56
|
-
issueCode(args: IssueCodeArgs): string;
|
|
57
|
-
exchangeCode(args: ExchangeCodeArgs): TokenResponse;
|
|
58
|
-
refreshToken(args: RefreshArgs): TokenResponse;
|
|
59
|
-
verifyAccessToken(token: string): AccessTokenInfo | null;
|
|
60
|
-
sweep(): number;
|
|
61
|
-
stats(): {
|
|
62
|
-
clients: number;
|
|
63
|
-
codes: number;
|
|
64
|
-
tokens: number;
|
|
65
|
-
refreshes: number;
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
export declare function createOAuthProvider(options?: OAuthProviderOptions): OAuthProvider;
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
// src/auth/oauth-provider.ts — Phase 9.2.
|
|
2
|
-
//
|
|
3
|
-
// Minimal in-memory OAuth 2.0 Authorization-Code + PKCE + Dynamic Client
|
|
4
|
-
// Registration (DCR) provider for the remote HTTP MCP transport.
|
|
5
|
-
//
|
|
6
|
-
// References:
|
|
7
|
-
// - RFC 6749 (OAuth 2.0)
|
|
8
|
-
// - RFC 7636 (PKCE)
|
|
9
|
-
// - RFC 7591 (Dynamic Client Registration)
|
|
10
|
-
// - workers-oauth-provider (Cloudflare reference, in-memory variant)
|
|
11
|
-
//
|
|
12
|
-
// Storage backend is process-local Maps with TTL-based expiry — the
|
|
13
|
-
// hosted production service (mcp.wootsup.com) is expected to swap in a
|
|
14
|
-
// persistent backend, but for unit/integration tests + the local DXT
|
|
15
|
-
// bundle, in-memory is sufficient.
|
|
16
|
-
//
|
|
17
|
-
// Security notes:
|
|
18
|
-
// - Tokens are 32 random bytes hex-encoded (no JWT — keeps the surface
|
|
19
|
-
// tiny and prevents misuse of unverified payloads).
|
|
20
|
-
// - Authorization codes are 32 random bytes hex; single-use; bound to
|
|
21
|
-
// client_id + redirect_uri + code_challenge.
|
|
22
|
-
// - PKCE S256 is REQUIRED; "plain" is rejected.
|
|
23
|
-
// - On refresh, the old refresh token is invalidated (rotation).
|
|
24
|
-
import { randomBytes, createHash, timingSafeEqual } from "node:crypto";
|
|
25
|
-
// ── Helpers ────────────────────────────────────────────────────────────
|
|
26
|
-
function randomId(prefix) {
|
|
27
|
-
return `${prefix}${randomBytes(12).toString("hex")}`;
|
|
28
|
-
}
|
|
29
|
-
function randomOpaque() {
|
|
30
|
-
return randomBytes(32).toString("hex");
|
|
31
|
-
}
|
|
32
|
-
/** Constant-time compare to thwart timing oracles on token / verifier lookups. */
|
|
33
|
-
function safeEquals(a, b) {
|
|
34
|
-
const ba = Buffer.from(a);
|
|
35
|
-
const bb = Buffer.from(b);
|
|
36
|
-
if (ba.length !== bb.length)
|
|
37
|
-
return false;
|
|
38
|
-
return timingSafeEqual(ba, bb);
|
|
39
|
-
}
|
|
40
|
-
function s256(input) {
|
|
41
|
-
return createHash("sha256").update(input).digest("base64url");
|
|
42
|
-
}
|
|
43
|
-
// ── Factory ────────────────────────────────────────────────────────────
|
|
44
|
-
export function createOAuthProvider(options = {}) {
|
|
45
|
-
const now = options.now ?? (() => Math.floor(Date.now() / 1000));
|
|
46
|
-
const codeTtl = options.codeTtlSeconds ?? 600;
|
|
47
|
-
const tokenTtl = options.tokenTtlSeconds ?? 86_400;
|
|
48
|
-
const refreshTtl = options.refreshTtlSeconds ?? 7 * 86_400;
|
|
49
|
-
const clients = new Map();
|
|
50
|
-
const codes = new Map();
|
|
51
|
-
const tokens = new Map();
|
|
52
|
-
const refreshes = new Map();
|
|
53
|
-
function registerClient(args) {
|
|
54
|
-
if (!Array.isArray(args.redirect_uris) || args.redirect_uris.length === 0) {
|
|
55
|
-
throw new Error("invalid_redirect_uri: at least one redirect_uri required");
|
|
56
|
-
}
|
|
57
|
-
for (const uri of args.redirect_uris) {
|
|
58
|
-
if (typeof uri !== "string" || uri.length === 0) {
|
|
59
|
-
throw new Error("invalid_redirect_uri");
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
const reg = {
|
|
63
|
-
client_id: randomId("cli_"),
|
|
64
|
-
client_id_issued_at: now(),
|
|
65
|
-
redirect_uris: [...args.redirect_uris],
|
|
66
|
-
client_name: args.client_name,
|
|
67
|
-
token_endpoint_auth_method: "none", // public client (mobile/native/AI)
|
|
68
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
69
|
-
response_types: ["code"],
|
|
70
|
-
};
|
|
71
|
-
clients.set(reg.client_id, reg);
|
|
72
|
-
return reg;
|
|
73
|
-
}
|
|
74
|
-
function getClient(client_id) {
|
|
75
|
-
return clients.get(client_id) ?? null;
|
|
76
|
-
}
|
|
77
|
-
function issueCode(args) {
|
|
78
|
-
const client = clients.get(args.client_id);
|
|
79
|
-
if (!client)
|
|
80
|
-
throw new Error("unknown_client");
|
|
81
|
-
if (!client.redirect_uris.includes(args.redirect_uri)) {
|
|
82
|
-
throw new Error("invalid_redirect_uri");
|
|
83
|
-
}
|
|
84
|
-
if (!args.code_challenge || typeof args.code_challenge !== "string") {
|
|
85
|
-
throw new Error("invalid_request: code_challenge required");
|
|
86
|
-
}
|
|
87
|
-
if (args.code_challenge_method !== "S256") {
|
|
88
|
-
// Reject "plain" — S256 is mandatory for safety.
|
|
89
|
-
throw new Error("invalid_request: code_challenge_method must be S256");
|
|
90
|
-
}
|
|
91
|
-
const code = randomId("code_");
|
|
92
|
-
codes.set(code, {
|
|
93
|
-
client_id: args.client_id,
|
|
94
|
-
redirect_uri: args.redirect_uri,
|
|
95
|
-
code_challenge: args.code_challenge,
|
|
96
|
-
code_challenge_method: args.code_challenge_method,
|
|
97
|
-
scope: args.scope,
|
|
98
|
-
expires_at: now() + codeTtl,
|
|
99
|
-
used: false,
|
|
100
|
-
});
|
|
101
|
-
return code;
|
|
102
|
-
}
|
|
103
|
-
function exchangeCode(args) {
|
|
104
|
-
const rec = codes.get(args.code);
|
|
105
|
-
if (!rec)
|
|
106
|
-
throw new Error("invalid_grant: code not found");
|
|
107
|
-
if (rec.used)
|
|
108
|
-
throw new Error("invalid_grant: code already used");
|
|
109
|
-
if (rec.expires_at < now())
|
|
110
|
-
throw new Error("invalid_grant: code expired");
|
|
111
|
-
if (rec.client_id !== args.client_id) {
|
|
112
|
-
throw new Error("invalid_grant: client_id mismatch");
|
|
113
|
-
}
|
|
114
|
-
if (rec.redirect_uri !== args.redirect_uri) {
|
|
115
|
-
throw new Error("invalid_grant: redirect_uri mismatch");
|
|
116
|
-
}
|
|
117
|
-
const challengeFromVerifier = s256(args.code_verifier);
|
|
118
|
-
if (!safeEquals(challengeFromVerifier, rec.code_challenge)) {
|
|
119
|
-
throw new Error("invalid_grant: code_verifier mismatch");
|
|
120
|
-
}
|
|
121
|
-
rec.used = true;
|
|
122
|
-
const accessToken = randomOpaque();
|
|
123
|
-
const refreshToken = randomOpaque();
|
|
124
|
-
tokens.set(accessToken, {
|
|
125
|
-
client_id: rec.client_id,
|
|
126
|
-
scope: rec.scope,
|
|
127
|
-
expires_at: now() + tokenTtl,
|
|
128
|
-
});
|
|
129
|
-
refreshes.set(refreshToken, {
|
|
130
|
-
client_id: rec.client_id,
|
|
131
|
-
scope: rec.scope,
|
|
132
|
-
expires_at: now() + refreshTtl,
|
|
133
|
-
revoked: false,
|
|
134
|
-
});
|
|
135
|
-
return {
|
|
136
|
-
access_token: accessToken,
|
|
137
|
-
refresh_token: refreshToken,
|
|
138
|
-
token_type: "Bearer",
|
|
139
|
-
expires_in: tokenTtl,
|
|
140
|
-
scope: rec.scope,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
function refreshToken(args) {
|
|
144
|
-
const rec = refreshes.get(args.refresh_token);
|
|
145
|
-
if (!rec)
|
|
146
|
-
throw new Error("invalid_grant: refresh_token not found");
|
|
147
|
-
if (rec.revoked)
|
|
148
|
-
throw new Error("invalid_grant: refresh_token revoked");
|
|
149
|
-
if (rec.expires_at < now()) {
|
|
150
|
-
throw new Error("invalid_grant: refresh_token expired");
|
|
151
|
-
}
|
|
152
|
-
if (rec.client_id !== args.client_id) {
|
|
153
|
-
throw new Error("invalid_grant: client_id mismatch");
|
|
154
|
-
}
|
|
155
|
-
// Rotate: revoke the old refresh token, mint a new pair.
|
|
156
|
-
rec.revoked = true;
|
|
157
|
-
const accessToken = randomOpaque();
|
|
158
|
-
const newRefresh = randomOpaque();
|
|
159
|
-
tokens.set(accessToken, {
|
|
160
|
-
client_id: rec.client_id,
|
|
161
|
-
scope: rec.scope,
|
|
162
|
-
expires_at: now() + tokenTtl,
|
|
163
|
-
});
|
|
164
|
-
refreshes.set(newRefresh, {
|
|
165
|
-
client_id: rec.client_id,
|
|
166
|
-
scope: rec.scope,
|
|
167
|
-
expires_at: now() + refreshTtl,
|
|
168
|
-
revoked: false,
|
|
169
|
-
});
|
|
170
|
-
return {
|
|
171
|
-
access_token: accessToken,
|
|
172
|
-
refresh_token: newRefresh,
|
|
173
|
-
token_type: "Bearer",
|
|
174
|
-
expires_in: tokenTtl,
|
|
175
|
-
scope: rec.scope,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
function verifyAccessToken(token) {
|
|
179
|
-
const rec = tokens.get(token);
|
|
180
|
-
if (!rec)
|
|
181
|
-
return null;
|
|
182
|
-
if (rec.expires_at < now())
|
|
183
|
-
return null;
|
|
184
|
-
return {
|
|
185
|
-
client_id: rec.client_id,
|
|
186
|
-
scope: rec.scope,
|
|
187
|
-
expires_at: rec.expires_at,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
function sweep() {
|
|
191
|
-
const t = now();
|
|
192
|
-
let evicted = 0;
|
|
193
|
-
for (const [k, v] of codes) {
|
|
194
|
-
if (v.expires_at < t || v.used) {
|
|
195
|
-
codes.delete(k);
|
|
196
|
-
evicted++;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
for (const [k, v] of tokens) {
|
|
200
|
-
if (v.expires_at < t) {
|
|
201
|
-
tokens.delete(k);
|
|
202
|
-
evicted++;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
for (const [k, v] of refreshes) {
|
|
206
|
-
if (v.expires_at < t || v.revoked) {
|
|
207
|
-
refreshes.delete(k);
|
|
208
|
-
evicted++;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return evicted;
|
|
212
|
-
}
|
|
213
|
-
function stats() {
|
|
214
|
-
return {
|
|
215
|
-
clients: clients.size,
|
|
216
|
-
codes: codes.size,
|
|
217
|
-
tokens: tokens.size,
|
|
218
|
-
refreshes: refreshes.size,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
return {
|
|
222
|
-
registerClient,
|
|
223
|
-
getClient,
|
|
224
|
-
issueCode,
|
|
225
|
-
exchangeCode,
|
|
226
|
-
refreshToken,
|
|
227
|
-
verifyAccessToken,
|
|
228
|
-
sweep,
|
|
229
|
-
stats,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
//# sourceMappingURL=oauth-provider.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-provider.js","sourceRoot":"","sources":["../../src/auth/oauth-provider.ts"],"names":[],"mappings":"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"}
|
package/dist/server-http.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { type ModuleStatus } from "@getimo/mcp-toolkit";
|
|
4
|
-
import { type McpHandler, type HttpTransportServer } from "./transports/http.js";
|
|
5
|
-
import { type OAuthProvider } from "./auth/oauth-provider.js";
|
|
6
|
-
export interface BootOptions {
|
|
7
|
-
/** TCP port. Default: process.env.APIMAPPER_HTTP_PORT or 8901. */
|
|
8
|
-
port?: number;
|
|
9
|
-
/** Public URL hint for OAuth metadata. */
|
|
10
|
-
publicBaseUrl?: string;
|
|
11
|
-
/** Pluggable MCP handler — defaults to a stub that returns the original JSON-RPC id with an empty result. */
|
|
12
|
-
mcpHandler?: McpHandler;
|
|
13
|
-
}
|
|
14
|
-
export interface BootedHttpServer extends HttpTransportServer {
|
|
15
|
-
oauth: OAuthProvider;
|
|
16
|
-
}
|
|
17
|
-
/** Test-only — exposes the server + module load status for assertions. */
|
|
18
|
-
export declare function _testGetMcpServer(): {
|
|
19
|
-
server: McpServer | null;
|
|
20
|
-
modules: ModuleStatus[];
|
|
21
|
-
};
|
|
22
|
-
export declare function bootHttpServer(options?: BootOptions): Promise<BootedHttpServer>;
|