@wootsup/mcp 0.1.0-rc.1

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.
Files changed (135) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/LICENSE +21 -0
  3. package/README.md +179 -0
  4. package/SECURITY.md +163 -0
  5. package/dist/auth/keychain.d.ts +47 -0
  6. package/dist/auth/keychain.js +262 -0
  7. package/dist/auth/keychain.js.map +1 -0
  8. package/dist/auth/oauth-provider.d.ts +68 -0
  9. package/dist/auth/oauth-provider.js +232 -0
  10. package/dist/auth/oauth-provider.js.map +1 -0
  11. package/dist/auth/profiles.d.ts +52 -0
  12. package/dist/auth/profiles.js +200 -0
  13. package/dist/auth/profiles.js.map +1 -0
  14. package/dist/auth/token.d.ts +27 -0
  15. package/dist/auth/token.js +88 -0
  16. package/dist/auth/token.js.map +1 -0
  17. package/dist/index.d.ts +13 -0
  18. package/dist/index.js +137 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/install-skill.d.ts +23 -0
  21. package/dist/install-skill.js +73 -0
  22. package/dist/install-skill.js.map +1 -0
  23. package/dist/modules/apimapper/cache.d.ts +2 -0
  24. package/dist/modules/apimapper/cache.js +71 -0
  25. package/dist/modules/apimapper/cache.js.map +1 -0
  26. package/dist/modules/apimapper/client.d.ts +85 -0
  27. package/dist/modules/apimapper/client.js +523 -0
  28. package/dist/modules/apimapper/client.js.map +1 -0
  29. package/dist/modules/apimapper/connections.d.ts +2 -0
  30. package/dist/modules/apimapper/connections.js +406 -0
  31. package/dist/modules/apimapper/connections.js.map +1 -0
  32. package/dist/modules/apimapper/credential-sanitizer.d.ts +7 -0
  33. package/dist/modules/apimapper/credential-sanitizer.js +70 -0
  34. package/dist/modules/apimapper/credential-sanitizer.js.map +1 -0
  35. package/dist/modules/apimapper/credentials.d.ts +2 -0
  36. package/dist/modules/apimapper/credentials.js +258 -0
  37. package/dist/modules/apimapper/credentials.js.map +1 -0
  38. package/dist/modules/apimapper/diagnose.d.ts +18 -0
  39. package/dist/modules/apimapper/diagnose.js +305 -0
  40. package/dist/modules/apimapper/diagnose.js.map +1 -0
  41. package/dist/modules/apimapper/flows.d.ts +2 -0
  42. package/dist/modules/apimapper/flows.js +372 -0
  43. package/dist/modules/apimapper/flows.js.map +1 -0
  44. package/dist/modules/apimapper/get-skill.d.ts +4 -0
  45. package/dist/modules/apimapper/get-skill.js +88 -0
  46. package/dist/modules/apimapper/get-skill.js.map +1 -0
  47. package/dist/modules/apimapper/graph-builder.d.ts +47 -0
  48. package/dist/modules/apimapper/graph-builder.js +117 -0
  49. package/dist/modules/apimapper/graph-builder.js.map +1 -0
  50. package/dist/modules/apimapper/graph.d.ts +2 -0
  51. package/dist/modules/apimapper/graph.js +117 -0
  52. package/dist/modules/apimapper/graph.js.map +1 -0
  53. package/dist/modules/apimapper/index.d.ts +2 -0
  54. package/dist/modules/apimapper/index.js +43 -0
  55. package/dist/modules/apimapper/index.js.map +1 -0
  56. package/dist/modules/apimapper/inspect.d.ts +20 -0
  57. package/dist/modules/apimapper/inspect.js +86 -0
  58. package/dist/modules/apimapper/inspect.js.map +1 -0
  59. package/dist/modules/apimapper/library.d.ts +2 -0
  60. package/dist/modules/apimapper/library.js +237 -0
  61. package/dist/modules/apimapper/library.js.map +1 -0
  62. package/dist/modules/apimapper/license.d.ts +2 -0
  63. package/dist/modules/apimapper/license.js +142 -0
  64. package/dist/modules/apimapper/license.js.map +1 -0
  65. package/dist/modules/apimapper/local-sources.d.ts +2 -0
  66. package/dist/modules/apimapper/local-sources.js +123 -0
  67. package/dist/modules/apimapper/local-sources.js.map +1 -0
  68. package/dist/modules/apimapper/misc.d.ts +2 -0
  69. package/dist/modules/apimapper/misc.js +149 -0
  70. package/dist/modules/apimapper/misc.js.map +1 -0
  71. package/dist/modules/apimapper/node-schema.d.ts +217 -0
  72. package/dist/modules/apimapper/node-schema.js +218 -0
  73. package/dist/modules/apimapper/node-schema.js.map +1 -0
  74. package/dist/modules/apimapper/normalizers.d.ts +13 -0
  75. package/dist/modules/apimapper/normalizers.js +37 -0
  76. package/dist/modules/apimapper/normalizers.js.map +1 -0
  77. package/dist/modules/apimapper/onboarding.d.ts +51 -0
  78. package/dist/modules/apimapper/onboarding.js +201 -0
  79. package/dist/modules/apimapper/onboarding.js.map +1 -0
  80. package/dist/modules/apimapper/schema.d.ts +2 -0
  81. package/dist/modules/apimapper/schema.js +84 -0
  82. package/dist/modules/apimapper/schema.js.map +1 -0
  83. package/dist/modules/apimapper/settings.d.ts +2 -0
  84. package/dist/modules/apimapper/settings.js +157 -0
  85. package/dist/modules/apimapper/settings.js.map +1 -0
  86. package/dist/modules/apimapper/skill-resources.d.ts +4 -0
  87. package/dist/modules/apimapper/skill-resources.js +85 -0
  88. package/dist/modules/apimapper/skill-resources.js.map +1 -0
  89. package/dist/modules/apimapper/types.d.ts +111 -0
  90. package/dist/modules/apimapper/types.js +14 -0
  91. package/dist/modules/apimapper/types.js.map +1 -0
  92. package/dist/modules/apimapper/use-profile.d.ts +34 -0
  93. package/dist/modules/apimapper/use-profile.js +176 -0
  94. package/dist/modules/apimapper/use-profile.js.map +1 -0
  95. package/dist/modules/apimapper/workflows.d.ts +2 -0
  96. package/dist/modules/apimapper/workflows.js +301 -0
  97. package/dist/modules/apimapper/workflows.js.map +1 -0
  98. package/dist/platform/index.d.ts +71 -0
  99. package/dist/platform/index.js +377 -0
  100. package/dist/platform/index.js.map +1 -0
  101. package/dist/server-http.d.ts +22 -0
  102. package/dist/server-http.js +159 -0
  103. package/dist/server-http.js.map +1 -0
  104. package/dist/setup/detect-clients.d.ts +39 -0
  105. package/dist/setup/detect-clients.js +152 -0
  106. package/dist/setup/detect-clients.js.map +1 -0
  107. package/dist/setup/probe-handshake.d.ts +26 -0
  108. package/dist/setup/probe-handshake.js +159 -0
  109. package/dist/setup/probe-handshake.js.map +1 -0
  110. package/dist/setup/write-config.d.ts +25 -0
  111. package/dist/setup/write-config.js +247 -0
  112. package/dist/setup/write-config.js.map +1 -0
  113. package/dist/setup-cli.d.ts +49 -0
  114. package/dist/setup-cli.js +292 -0
  115. package/dist/setup-cli.js.map +1 -0
  116. package/dist/skill-instructions.d.ts +10 -0
  117. package/dist/skill-instructions.js +68 -0
  118. package/dist/skill-instructions.js.map +1 -0
  119. package/dist/transports/http.d.ts +29 -0
  120. package/dist/transports/http.js +267 -0
  121. package/dist/transports/http.js.map +1 -0
  122. package/dist/transports/stdio.d.ts +9 -0
  123. package/dist/transports/stdio.js +19 -0
  124. package/dist/transports/stdio.js.map +1 -0
  125. package/docs/architecture.md +140 -0
  126. package/docs/customgraph-internal-migration.md +210 -0
  127. package/docs/security.md +126 -0
  128. package/docs/tools.md +230 -0
  129. package/manifest.json +76 -0
  130. package/package.json +61 -0
  131. package/skills/apimapper/SKILL.md +57 -0
  132. package/skills/apimapper/reference/joomla.md +85 -0
  133. package/skills/apimapper/reference/oauth.md +94 -0
  134. package/skills/apimapper/reference/troubleshooting.md +123 -0
  135. package/skills/apimapper/reference/yootheme.md +96 -0
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@wootsup/mcp",
3
+ "version": "0.1.0-rc.1",
4
+ "type": "module",
5
+ "description": "API Mapper MCP Server — multi-platform (WordPress/Joomla), multi-AI-client, Stripe-style auth, bundled skills",
6
+ "license": "MIT",
7
+ "author": "WootsUp <hello@wootsup.com>",
8
+ "homepage": "https://wootsup.com/products/api-mapper",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/wootsup/api-mapper.git",
12
+ "directory": "packages/apimapper-mcp"
13
+ },
14
+ "bugs": "https://github.com/wootsup/api-mapper/issues",
15
+ "keywords": [
16
+ "mcp",
17
+ "model-context-protocol",
18
+ "api-mapper",
19
+ "wordpress",
20
+ "joomla",
21
+ "claude",
22
+ "chatgpt",
23
+ "cursor"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "bin": {
29
+ "apimapper-mcp": "./dist/index.js"
30
+ },
31
+ "files": [
32
+ "dist/",
33
+ "skills/",
34
+ "docs/",
35
+ "manifest.json",
36
+ "CHANGELOG.md",
37
+ "SECURITY.md"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc && chmod +x dist/index.js 2>/dev/null || true",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "dev": "tsx watch src/index.ts",
44
+ "typecheck": "tsc --noEmit",
45
+ "start:http": "node dist/server-http.js",
46
+ "build:dxt": "node scripts/build-dxt.js"
47
+ },
48
+ "dependencies": {
49
+ "@modelcontextprotocol/sdk": "^1.27.0",
50
+ "@getimo/mcp-toolkit": "^1.0.0",
51
+ "@napi-rs/keyring": "^1.3.0",
52
+ "@clack/prompts": "^0.10.0",
53
+ "zod": "^4.3.6"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^22.0.0",
57
+ "tsx": "^4.21.0",
58
+ "typescript": "^5.6.0",
59
+ "vitest": "^2.1.0"
60
+ }
61
+ }
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: apimapper
3
+ description: Use when working with API Mapper flows, connections, sources, credentials, or YOOtheme dynamic content on WordPress/Joomla — REST tools (`apimapper_*`), OAuth, Joomla com_ajax, Bearer auth.
4
+ version: 0.1.0
5
+ allowed-tools:
6
+ - apimapper_health
7
+ - apimapper_onboarding
8
+ - apimapper_get_skill
9
+ ---
10
+
11
+ # API Mapper
12
+
13
+ Bridge any REST API into YOOtheme via a saved flow (Source → Filter → Transform → Output). One server, one Bearer key, 74 MCP tools.
14
+
15
+ ## Quickstart
16
+
17
+ 1. **Set up MCP**
18
+ ```
19
+ npx -y @wootsup/mcp setup
20
+ ```
21
+ Interactive wizard: paste your MCP key, pick the AI client(s) to configure
22
+ (Claude Desktop, Cursor, Zed, Continue, Cline, Roo Code), wizard writes
23
+ the config files for you.
24
+
25
+ 2. **Generate a key**
26
+ - WordPress: click *API Mapper* in the WP admin menu to open the editor.
27
+ In the editor header (top-right), click the **⋮** (three-dot) menu →
28
+ *Settings*. In the Settings modal's left sidebar, click **MCP Access**
29
+ → *Generate new key*.
30
+ - Joomla: open *Components → API Mapper* — same editor loads. Use the
31
+ same **⋮** menu → *Settings* → **MCP Access** sidebar entry.
32
+
33
+ 3. **Verify + onboard**
34
+ ```
35
+ apimapper_health → status: "ok"
36
+ apimapper_onboarding → lists platform, existing flows
37
+ ```
38
+
39
+ ## Common Tools
40
+
41
+ | Tool | Use for |
42
+ |------|---------|
43
+ | `apimapper_connection_create` | New REST/OAuth source |
44
+ | `apimapper_flow_setup_with_sources` | One-shot connection+flow+publish |
45
+ | `apimapper_flow_compile` | Validate graph before publish |
46
+ | `apimapper_library_activate` | Drop a featured template |
47
+ | `apimapper_diagnose` | Triage 401/404/5xx |
48
+
49
+ For topic docs: `apimapper_get_skill topic="..."` or read `skill://apimapper/<topic>`.
50
+
51
+ ## Common Pitfalls
52
+
53
+ - **Source not in YOOtheme Builder** → flow is *saved* but not *published*. Click Publish. Published name (not "API Mapper") appears in YOOtheme Source dropdown.
54
+ - **Joomla 404 on `/wp-json/...`** → Joomla wraps in `com_ajax`. See `apimapper_get_skill topic="joomla"`.
55
+ - **OAuth expired** → use `apimapper_credential_link` to re-auth, NOT delete-and-recreate (loses `refresh_token`).
56
+
57
+ Full docs: `wootsup.com/docs/mcp/`
@@ -0,0 +1,85 @@
1
+ # Joomla-Specific Gotchas
2
+
3
+ API Mapper supports Joomla 5+ with feature parity to WordPress, but the transport differs. Most platform differences are absorbed by the MCP server — these notes cover the edge cases you may still hit.
4
+
5
+ ## Endpoint shape
6
+
7
+ WordPress uses `wp-json` REST routes:
8
+ ```
9
+ GET https://site.example/wp-json/api-mapper/v1/connections
10
+ ```
11
+
12
+ Joomla wraps everything in a `com_ajax` envelope:
13
+ ```
14
+ GET https://site.example/index.php?option=com_ajax&plugin=apimapper&format=json&task=connections.list
15
+ ```
16
+
17
+ The MCP server detects platform automatically (via `apimapper_health`) and rewrites paths. You should never need to construct the URL by hand. If you do (debugging with `curl`), check `apimapper_platform_parity` for the current mapping.
18
+
19
+ ## Auth tiers
20
+
21
+ Joomla has **three** auth paths, ordered by priority:
22
+
23
+ ### Tier 1 — Bearer fast-path
24
+ A short-lived Bearer token signed by API Mapper. This is what `apimapper_credential_create` uses for the MCP-to-plugin call. Fast (no Joomla session bootstrap), works headless.
25
+
26
+ Header: `Authorization: Bearer <token>`
27
+
28
+ ### Tier 2 — Joomla user session
29
+ For requests originating in the admin UI (Property Panel REST calls). Cookie-based. The MCP server does NOT use this — it's relevant only when debugging admin-UI bugs.
30
+
31
+ ### Tier 3 — OAuth helper trampoline
32
+ For OAuth callbacks (Google, Instagram, …). The OAuth provider POSTs to a public endpoint with a state token; API Mapper validates the state, swaps `code`→`access_token`, and stores into the credential record. The trampoline route is registered as a Joomla System Plugin event, NOT a com_ajax task — it must respond at a stable URL the provider can call back to.
33
+
34
+ If OAuth keeps failing with `state_mismatch`, check that Joomla's `Update site` URL setting matches the OAuth callback URL configured at the provider. A common cause is `www.` vs apex mismatch.
35
+
36
+ ## com_ajax response envelope
37
+
38
+ Joomla wraps successful responses:
39
+ ```json
40
+ { "success": true, "message": null, "messages": null, "data": [ <actual_response> ] }
41
+ ```
42
+
43
+ Errors return `success: false` with a `message`. The MCP server unwraps `data[0]` automatically — but if you hit a third-party tool that calls the Joomla endpoint directly (e.g. a custom integration), you must unwrap.
44
+
45
+ ## Plugin location
46
+
47
+ ```
48
+ /path/to/joomla/plugins/system/apimapper/
49
+ ├── apimapper.php # System plugin entry — NOT api-mapper-joomla.php
50
+ ├── services/ # Service registration
51
+ ├── src/ # PSR-4 autoloaded
52
+ └── language/
53
+ ```
54
+
55
+ Note: the directory is `apimapper` (no hyphen), entry file is `apimapper.php`. This is a Joomla naming requirement (lowercase, no hyphens in plugin element names).
56
+
57
+ ## Common pitfalls
58
+
59
+ 1. **404 on a `/wp-json/...` URL** — you're hitting WordPress paths against a Joomla site. Re-resolve via the MCP, never hardcode paths.
60
+ 2. **`source.init` returning an array instead of an object** — Joomla System Plugin events can pass either. Add `is_object($source)` check at the boundary.
61
+ 3. **OAuth callback fails silently** — Joomla's URL rewriting can swallow query parameters. Verify `index.php?option=com_ajax&...` URLs work both with and without SEF rewriting enabled.
62
+ 4. **`com_ajax` cache** — Joomla caches the response by URL. Add `&_=` cache-bust during development, or disable Joomla cache plugin for `com_ajax` calls.
63
+ 5. **Bearer token rejected** — the token must be tied to a Joomla user with `apimapper.access` permission. Check Joomla → Users → Groups → Permissions.
64
+ 6. **`AdminCacheStore` invalidation seems no-op** — Joomla's object cache adapter differs from WP's. Use `apimapper_cache_invalidate` (not raw cache calls) to ensure both tiers flush.
65
+
66
+ ## Deployment
67
+
68
+ Joomla extensions install as a `.zip` (system plugin). The MCP server bundles a single ZIP per release — `apimapper-mcp install` does not deploy the Joomla plugin; you install it via Joomla admin → Extensions → Install.
69
+
70
+ To verify the plugin is loaded:
71
+ ```
72
+ apimapper_health
73
+ # → { platform: "joomla", version: "2.0.7", plugin: "enabled" }
74
+ ```
75
+
76
+ If `plugin: "disabled"`, enable via Joomla admin → Extensions → Plugins → search "API Mapper".
77
+
78
+ ## Multi-platform development workflow
79
+
80
+ When developing a fix:
81
+ 1. Test on WordPress (`/deploy-extensions wp`).
82
+ 2. Test on Joomla (`/deploy-extensions joomla`).
83
+ 3. Run `apimapper_platform_parity` to verify the same flow produces identical YOOtheme schemas on both platforms.
84
+
85
+ Any divergence is a bug. Open an issue with the parity report attached.
@@ -0,0 +1,94 @@
1
+ # OAuth Sources
2
+
3
+ Wiring OAuth-protected sources (Pexels API key, Google Sheets/Drive, Instagram Graph, Facebook Pages, Notion, Airtable, …) through API Mapper.
4
+
5
+ ## When to use OAuth vs static key
6
+
7
+ | Auth shape | Examples | Credential type |
8
+ |------------|----------|-----------------|
9
+ | Static API key in header | Pexels, NewsAPI, OpenWeather | `bearer` or `apikey` |
10
+ | OAuth2 authorization-code | Google Sheets/Drive, Instagram, Facebook, Notion | `oauth2_authcode` |
11
+ | OAuth2 client-credentials | Spotify (catalog), some B2B APIs | `oauth2_clientcred` |
12
+ | Personal access token | GitHub, GitLab, Airtable | `bearer` |
13
+
14
+ If the API requires a user-consent screen and returns a `refresh_token`, you want `oauth2_authcode`. Everything else is `bearer`/`apikey`.
15
+
16
+ ## Wiring an OAuth source — full flow
17
+
18
+ The two-step pattern is **always**: create credential → bind credential to connection. Credentials are reusable across connections.
19
+
20
+ ### Step 1 — Create the credential
21
+
22
+ ```
23
+ apimapper_credential_create(
24
+ name="My Google Account",
25
+ type="oauth2_authcode",
26
+ provider="google",
27
+ scopes=["https://www.googleapis.com/auth/spreadsheets.readonly"]
28
+ )
29
+ ```
30
+
31
+ The tool returns a `credential_id` and an `authorize_url`. The caller (Claude / ChatGPT / Cursor) **must** surface the `authorize_url` to the user — the user clicks it, completes consent, the OAuth callback writes the `access_token` + `refresh_token` into the credential record.
32
+
33
+ If you skip surfacing the URL, the credential will stay in `pending` state and every subsequent call will return 401.
34
+
35
+ ### Step 2 — Begin authorization (when refresh failed)
36
+
37
+ If the credential is `expired` or the refresh token was revoked, re-trigger consent:
38
+
39
+ ```
40
+ apimapper_oauth_authorize_begin(credential_id="cred_abc123")
41
+ ```
42
+
43
+ Returns a new `authorize_url`. Same surface-it-to-the-user rule applies.
44
+
45
+ ### Step 3 — Create the connection bound to the credential
46
+
47
+ ```
48
+ apimapper_connection_create(
49
+ name="Sheets — Marketing OKRs",
50
+ source_type="google_sheets",
51
+ credential_id="cred_abc123",
52
+ config={ "spreadsheet_id": "1AbC...", "range": "OKRs!A1:F100" }
53
+ )
54
+ ```
55
+
56
+ `source_type` selects the upstream driver (REST request shape, response normalizer, schema profiler). `config` is driver-specific.
57
+
58
+ ### Step 4 — Verify
59
+
60
+ ```
61
+ apimapper_connection_test(connection_id="conn_xyz")
62
+ ```
63
+
64
+ Returns one of:
65
+ - `{ ok: true, sample: { ... } }` — green, you can build a flow on this
66
+ - `{ ok: false, error: "401", hint: "credential expired → call apimapper_oauth_authorize_begin" }`
67
+ - `{ ok: false, error: "scope_missing", hint: "credential lacks scope X → recreate with scopes=[...]" }`
68
+
69
+ ## Common pitfalls
70
+
71
+ 1. **Recreating credentials after expiry** — wipes the `refresh_token`. Always `apimapper_oauth_authorize_begin` first; only delete-and-recreate if the user revoked access on the provider side.
72
+ 2. **Hardcoding tokens into `connection.config.auth.token`** — bypasses the credential system, breaks rotation, fails on token expiry. Always use `credential_id`.
73
+ 3. **Forgetting to surface the `authorize_url`** — the user can't grant consent telepathically. Treat the URL as a required UI step, not a debug log line.
74
+ 4. **Scope drift** — provider adds new scopes (e.g. Google's `drive.readonly` split). Add them to the credential definition and re-authorize; pre-existing tokens lack them silently until a call returns 403.
75
+ 5. **Sharing a credential across users** — credentials are per-user (tied to the WordPress/Joomla user that created them). Cross-user share is not supported in 2.0.x.
76
+
77
+ ## Provider-specific notes
78
+
79
+ - **Google** (Sheets/Drive/Calendar/Tasks) — needs `access_type=offline` and `prompt=consent` to issue a refresh token. The MCP server adds these automatically. If you bypass the MCP and craft the URL by hand, you must add them.
80
+ - **Instagram Graph** — token expires every 60 days, refresh-token flow is non-standard (long-lived → exchange). API Mapper's refresh handler covers it; do not call the Meta endpoints directly.
81
+ - **Notion / Airtable** — issue personal access tokens, not full OAuth. Use `bearer` credential type and paste the token; no `authorize_url` step.
82
+ - **Pexels** — static API key; use `apikey` credential type. No consent screen.
83
+
84
+ ## Diagnose flow
85
+
86
+ ```
87
+ apimapper_credential_get(credential_id="cred_abc123")
88
+ # → { status: "expired", last_refresh_at: "2026-04-12T...", scopes: [...] }
89
+
90
+ apimapper_diagnose(connection_id="conn_xyz")
91
+ # → consolidates credential state + last connection_test + recent logs
92
+ ```
93
+
94
+ For schema/transform issues (not auth issues), see `skill://apimapper/troubleshooting`.
@@ -0,0 +1,123 @@
1
+ # Troubleshooting
2
+
3
+ Diagnostic checklist when API Mapper tools return errors or flows misbehave. Work top-down — the most common causes are at the top.
4
+
5
+ ## Quick triage
6
+
7
+ ```
8
+ apimapper_diagnose(connection_id="conn_xyz") # one-call summary
9
+ ```
10
+
11
+ `apimapper_diagnose` (Phase 8 composite tool) consolidates: credential state, last connection_test, recent error logs, license tier, schema profile. Use it FIRST before drilling into individual tools.
12
+
13
+ If `apimapper_diagnose` is unavailable in your version, run the manual checks below.
14
+
15
+ ## HTTP status codes
16
+
17
+ ### 401 Unauthorized
18
+
19
+ **Cause A — invalid Bearer**
20
+ - `apimapper_health` returns `auth: "MISSING"` or `auth: "invalid"`.
21
+ - Fix: regenerate the MCP access token in the plugin admin, update your client env.
22
+
23
+ **Cause B — credential expired (OAuth flow)**
24
+ - `apimapper_credential_get(credential_id="...")` shows `status: "expired"`.
25
+ - Fix: `apimapper_oauth_authorize_begin(credential_id="...")` → surface URL to user. Do NOT delete + recreate.
26
+
27
+ **Cause C — wrong scope**
28
+ - Connection-test returns `scope_missing`.
29
+ - Fix: recreate credential with extended `scopes=[...]`, re-authorize.
30
+
31
+ ### 403 Forbidden
32
+
33
+ - License tier doesn't allow the requested action (e.g. 2nd connection on Free tier, JMESPath transform on Free tier).
34
+ - Fix: `apimapper_license_status` to verify tier. Upgrade or stay within limits.
35
+
36
+ ### 404 Not Found
37
+
38
+ **Cause A — endpoint not registered**
39
+ - Plugin disabled or not installed.
40
+ - Fix: `apimapper_health` → check `plugin: "enabled"`. On WP: Plugins → activate. On Joomla: Extensions → Plugins → enable.
41
+
42
+ **Cause B — wrong platform path**
43
+ - Hitting `/wp-json/...` on Joomla or `index.php?option=...` on WordPress.
44
+ - Fix: let the MCP server resolve paths. If debugging manually, re-read `skill://apimapper/joomla`.
45
+
46
+ **Cause C — resource ID typo**
47
+ - `apimapper_flow_get(flow_id="flow_typo")` → 404.
48
+ - Fix: `apimapper_flow_list` to find the correct ID.
49
+
50
+ ### 422 / 400 Validation error
51
+
52
+ - The request body failed server-side validation.
53
+ - The error response includes a `validation_errors` array with field-level detail.
54
+ - Fix: check `connection.config` shape against `apimapper_local_source_schema` for the source type.
55
+
56
+ ### 429 Rate limited
57
+
58
+ - Upstream API rate-limited the call.
59
+ - API Mapper respects `Retry-After`; the breaker trips after repeated 429s.
60
+ - Fix: wait the period given in the error response. For long-term: enable per-connection caching with `cacheTtl`.
61
+
62
+ ### 5xx Server error
63
+
64
+ **Cause A — uncaught PHP exception**
65
+ - Check plugin logs: WP → `wp-content/debug.log`, Joomla → `administrator/logs/`.
66
+ - Sentry: search `wootsup` org → `api-mapper` project for the matching event.
67
+
68
+ **Cause B — upstream API outage**
69
+ - `apimapper_connection_test` against another known-good connection — if that also fails, it's network/server-side.
70
+ - Status pages: check the upstream provider.
71
+
72
+ **Cause C — schema introspection panic**
73
+ - Often shows as `e is not iterable` in JS console or `Source not iterable` in PHP logs.
74
+ - Cause: source returned a non-array where array was expected.
75
+ - Fix: inspect the actual upstream response via `apimapper_connection_data(connection_id, raw=true)`. Add a Transform projection if needed.
76
+
77
+ ## Flow-specific issues
78
+
79
+ ### "Source not appearing in YOOtheme Builder"
80
+ - Flow is saved but not **published**. See `skill://apimapper/yootheme` step 4.
81
+
82
+ ### "Source shows but fields are empty"
83
+ - Field-name case mismatch. YOOtheme expects lowercase keys.
84
+ - Fix: Transform projection should lowercase: `[*].{title: name, image: cover_url}`.
85
+
86
+ ### "Flow returns stale data"
87
+ - Cache hit. Force refresh: `apimapper_connection_data(connection_id="...", forceRefresh=true)`.
88
+ - Clear admin cache: `apimapper_cache_invalidate(scope="all")`.
89
+ - Clear YOOtheme schema cache (sibling MCP): `yootheme_clear_cache`.
90
+
91
+ ### "Flow worked yesterday, broken today"
92
+ - Most likely upstream API changed shape.
93
+ - `apimapper_flow_detect_schema(flow_id="...")` — compare against the schema stored at last publish.
94
+ - If drifted, re-publish (`apimapper_flow_full_recompile_publish`).
95
+
96
+ ## Credential-specific issues
97
+
98
+ | Symptom | Likely cause | Fix |
99
+ |---------|--------------|-----|
100
+ | `pending` forever | User never clicked `authorize_url` | Re-surface URL. Don't recreate. |
101
+ | `expired` after 1h | Refresh-token flow not wired | Check provider supports refresh; re-authorize with correct prompts |
102
+ | `revoked` | User revoked access on provider side | Re-authorize from scratch (delete + create OK here) |
103
+ | `error: invalid_scope` | Scope changed at provider | Recreate credential with new scopes |
104
+
105
+ ## Logs
106
+
107
+ ```
108
+ # WordPress
109
+ ssh deploy@dev.wootsup.com "tail -200 /var/www/dev.wootsup.com/wordpress/wp-content/debug.log | grep -i api-mapper"
110
+
111
+ # Joomla
112
+ ssh deploy@dev.wootsup.com "tail -200 /var/www/dev.wootsup.com/joomla/administrator/logs/everything.php"
113
+
114
+ # Sentry (filtered)
115
+ # → use sentry MCP: list_issues project=api-mapper
116
+ ```
117
+
118
+ ## When all else fails
119
+
120
+ `apimapper_diagnose` first. If still stuck:
121
+ 1. Capture the full request + response (use the tool's `raw=true` flag where available).
122
+ 2. Note the platform, version (`apimapper_health`), license tier.
123
+ 3. Open a support ticket at `docs.wootsup.com/support` with that bundle.
@@ -0,0 +1,96 @@
1
+ # Exposing API Mapper Sources to YOOtheme
2
+
3
+ API Mapper sources show up in the YOOtheme Builder Source dropdown ONLY when a flow ends in a YOOtheme Output node and the flow is **published** (not just saved). This is the most common "source isn't appearing" cause.
4
+
5
+ ## The publish pipeline
6
+
7
+ ```
8
+ Source ─► [Filter] ─► [Transform] ─► [Merge] ─► Output
9
+
10
+
11
+ ┌─────────────────────┐
12
+ │ YOOtheme Output (✓) │ → appears as YOOtheme Source
13
+ │ Shortcode Output │ → [api_mapper id="..."] usable in posts
14
+ └─────────────────────┘
15
+ ```
16
+
17
+ Both output types can coexist on the same flow.
18
+
19
+ ## Step-by-step
20
+
21
+ 1. **Build the flow** via `apimapper_flow_create` + `apimapper_app_add_node` calls, or one-shot via `apimapper_flow_setup_with_sources`.
22
+
23
+ 2. **Add a YOOtheme Output node** (NOT Shortcode):
24
+ ```
25
+ apimapper_app_configure_output(
26
+ flow_id="flow_abc",
27
+ output_type="yootheme",
28
+ source_name="Marketing Posts"
29
+ )
30
+ ```
31
+ The `source_name` is what appears in the YOOtheme Source dropdown. Keep it short and human-readable.
32
+
33
+ 3. **Compile** to validate schema + graph:
34
+ ```
35
+ apimapper_flow_compile(flow_id="flow_abc")
36
+ ```
37
+ This catches:
38
+ - Disconnected nodes
39
+ - Type drift between Transform output and Output input
40
+ - Missing required fields
41
+ - Filter expressions referencing fields that don't exist on the upstream
42
+
43
+ 4. **Publish**:
44
+ ```
45
+ apimapper_flow_full_recompile_publish(flow_id="flow_abc")
46
+ ```
47
+ This writes the flow into the YOOtheme Source registry and invalidates the YOOtheme schema cache.
48
+
49
+ 5. **Verify in YOOtheme Builder**:
50
+ - Open any YOOtheme page or template.
51
+ - Add a Grid / Slider / Dynamic element.
52
+ - "Source" dropdown → look for **the name you set in step 2** ("Marketing Posts"), NOT a "API Mapper" entry.
53
+
54
+ ## Schema for YOOtheme dynamic content
55
+
56
+ YOOtheme reads the published flow's output schema (introspected by `apimapper_flow_detect_schema`). Each field on the schema becomes a mappable property in the YOOtheme element editor (title, image, link, date, …).
57
+
58
+ To control which fields YOOtheme sees:
59
+ - Use a `Transform` node with an explicit JMESPath/expression projection.
60
+ - `[*].{title: name, image: cover.url, link: permalink}` is a typical canonical projection.
61
+ - The Visual Expression Builder in the UI produces equivalent expressions; both end up in the same compiled flow.
62
+
63
+ ## Where YOOtheme features live (NOT in the flow)
64
+
65
+ These belong in YOOtheme, not in API Mapper:
66
+
67
+ | Feature | Where to configure |
68
+ |---------|--------------------|
69
+ | Sort order at render time | YOOtheme element → `filter_order` |
70
+ | Filter at render time | YOOtheme element → `filter_reverse`, `filter` |
71
+ | Slice / limit at render time | YOOtheme element → `limit`, `offset` |
72
+ | Pagination | YOOtheme element + URL handling |
73
+ | Per-page query overrides | YOOtheme element bindings (`source`) |
74
+
75
+ API Mapper's job is to deliver typed clean JSON. The render-time slicing / sorting / filtering lives in YOOtheme. Don't reimplement it in a Transform node — it just bloats the flow and creates two truth sources.
76
+
77
+ ## Shortcode output (WordPress only)
78
+
79
+ Same flow can ALSO emit a Shortcode output (`output_type="shortcode"`). Use this for:
80
+ - Embedding in classic editor posts
81
+ - Using outside YOOtheme (page builders, plugins)
82
+ - Quick previews in admin
83
+
84
+ Shortcode currently supports a subset of the YOOtheme render-time features. Document at `docs.wootsup.com/mcp/yootheme/shortcode`.
85
+
86
+ ## Common pitfalls
87
+
88
+ 1. **Save vs Publish** — saving stores the flow in `wp_options` (or Joomla equivalent) but does NOT register it as a YOOtheme Source. Always publish.
89
+ 2. **Looking for "API Mapper" in the dropdown** — wrong. Sources appear under the **flow's published source_name**.
90
+ 3. **Stale YOOtheme schema cache** — after a major schema change (Transform projection updated, new field added), clear YOOtheme cache: `yootheme_clear_cache` (sibling MCP) or via WP-Admin → YOOtheme → Settings → Clear Cache.
91
+ 4. **Republishing without recompile** — schema drift won't propagate. Use `apimapper_flow_full_recompile_publish`, not `apimapper_flow_publish`.
92
+ 5. **Dynamic content fields show as empty** — usually case sensitivity. YOOtheme expects field names lowercase; the Transform projection must produce lowercase keys. Field-Cards in the UI handle this automatically.
93
+
94
+ ## Multi-platform behaviour
95
+
96
+ WordPress and Joomla produce identical YOOtheme schemas — the Source/Type registration goes through the same YOOtheme PHP API. But the underlying REST surface differs (see `skill://apimapper/joomla` for the Joomla envelope). Tools auto-detect the platform and route correctly.