@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.
- package/CHANGELOG.md +91 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/SECURITY.md +163 -0
- package/dist/auth/keychain.d.ts +47 -0
- package/dist/auth/keychain.js +262 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/oauth-provider.d.ts +68 -0
- package/dist/auth/oauth-provider.js +232 -0
- package/dist/auth/oauth-provider.js.map +1 -0
- package/dist/auth/profiles.d.ts +52 -0
- package/dist/auth/profiles.js +200 -0
- package/dist/auth/profiles.js.map +1 -0
- package/dist/auth/token.d.ts +27 -0
- package/dist/auth/token.js +88 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/dist/install-skill.d.ts +23 -0
- package/dist/install-skill.js +73 -0
- package/dist/install-skill.js.map +1 -0
- package/dist/modules/apimapper/cache.d.ts +2 -0
- package/dist/modules/apimapper/cache.js +71 -0
- package/dist/modules/apimapper/cache.js.map +1 -0
- package/dist/modules/apimapper/client.d.ts +85 -0
- package/dist/modules/apimapper/client.js +523 -0
- package/dist/modules/apimapper/client.js.map +1 -0
- package/dist/modules/apimapper/connections.d.ts +2 -0
- package/dist/modules/apimapper/connections.js +406 -0
- package/dist/modules/apimapper/connections.js.map +1 -0
- package/dist/modules/apimapper/credential-sanitizer.d.ts +7 -0
- package/dist/modules/apimapper/credential-sanitizer.js +70 -0
- package/dist/modules/apimapper/credential-sanitizer.js.map +1 -0
- package/dist/modules/apimapper/credentials.d.ts +2 -0
- package/dist/modules/apimapper/credentials.js +258 -0
- package/dist/modules/apimapper/credentials.js.map +1 -0
- package/dist/modules/apimapper/diagnose.d.ts +18 -0
- package/dist/modules/apimapper/diagnose.js +305 -0
- package/dist/modules/apimapper/diagnose.js.map +1 -0
- package/dist/modules/apimapper/flows.d.ts +2 -0
- package/dist/modules/apimapper/flows.js +372 -0
- package/dist/modules/apimapper/flows.js.map +1 -0
- package/dist/modules/apimapper/get-skill.d.ts +4 -0
- package/dist/modules/apimapper/get-skill.js +88 -0
- package/dist/modules/apimapper/get-skill.js.map +1 -0
- package/dist/modules/apimapper/graph-builder.d.ts +47 -0
- package/dist/modules/apimapper/graph-builder.js +117 -0
- package/dist/modules/apimapper/graph-builder.js.map +1 -0
- package/dist/modules/apimapper/graph.d.ts +2 -0
- package/dist/modules/apimapper/graph.js +117 -0
- package/dist/modules/apimapper/graph.js.map +1 -0
- package/dist/modules/apimapper/index.d.ts +2 -0
- package/dist/modules/apimapper/index.js +43 -0
- package/dist/modules/apimapper/index.js.map +1 -0
- package/dist/modules/apimapper/inspect.d.ts +20 -0
- package/dist/modules/apimapper/inspect.js +86 -0
- package/dist/modules/apimapper/inspect.js.map +1 -0
- package/dist/modules/apimapper/library.d.ts +2 -0
- package/dist/modules/apimapper/library.js +237 -0
- package/dist/modules/apimapper/library.js.map +1 -0
- package/dist/modules/apimapper/license.d.ts +2 -0
- package/dist/modules/apimapper/license.js +142 -0
- package/dist/modules/apimapper/license.js.map +1 -0
- package/dist/modules/apimapper/local-sources.d.ts +2 -0
- package/dist/modules/apimapper/local-sources.js +123 -0
- package/dist/modules/apimapper/local-sources.js.map +1 -0
- package/dist/modules/apimapper/misc.d.ts +2 -0
- package/dist/modules/apimapper/misc.js +149 -0
- package/dist/modules/apimapper/misc.js.map +1 -0
- package/dist/modules/apimapper/node-schema.d.ts +217 -0
- package/dist/modules/apimapper/node-schema.js +218 -0
- package/dist/modules/apimapper/node-schema.js.map +1 -0
- package/dist/modules/apimapper/normalizers.d.ts +13 -0
- package/dist/modules/apimapper/normalizers.js +37 -0
- package/dist/modules/apimapper/normalizers.js.map +1 -0
- package/dist/modules/apimapper/onboarding.d.ts +51 -0
- package/dist/modules/apimapper/onboarding.js +201 -0
- package/dist/modules/apimapper/onboarding.js.map +1 -0
- package/dist/modules/apimapper/schema.d.ts +2 -0
- package/dist/modules/apimapper/schema.js +84 -0
- package/dist/modules/apimapper/schema.js.map +1 -0
- package/dist/modules/apimapper/settings.d.ts +2 -0
- package/dist/modules/apimapper/settings.js +157 -0
- package/dist/modules/apimapper/settings.js.map +1 -0
- package/dist/modules/apimapper/skill-resources.d.ts +4 -0
- package/dist/modules/apimapper/skill-resources.js +85 -0
- package/dist/modules/apimapper/skill-resources.js.map +1 -0
- package/dist/modules/apimapper/types.d.ts +111 -0
- package/dist/modules/apimapper/types.js +14 -0
- package/dist/modules/apimapper/types.js.map +1 -0
- package/dist/modules/apimapper/use-profile.d.ts +34 -0
- package/dist/modules/apimapper/use-profile.js +176 -0
- package/dist/modules/apimapper/use-profile.js.map +1 -0
- package/dist/modules/apimapper/workflows.d.ts +2 -0
- package/dist/modules/apimapper/workflows.js +301 -0
- package/dist/modules/apimapper/workflows.js.map +1 -0
- package/dist/platform/index.d.ts +71 -0
- package/dist/platform/index.js +377 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/server-http.d.ts +22 -0
- package/dist/server-http.js +159 -0
- package/dist/server-http.js.map +1 -0
- package/dist/setup/detect-clients.d.ts +39 -0
- package/dist/setup/detect-clients.js +152 -0
- package/dist/setup/detect-clients.js.map +1 -0
- package/dist/setup/probe-handshake.d.ts +26 -0
- package/dist/setup/probe-handshake.js +159 -0
- package/dist/setup/probe-handshake.js.map +1 -0
- package/dist/setup/write-config.d.ts +25 -0
- package/dist/setup/write-config.js +247 -0
- package/dist/setup/write-config.js.map +1 -0
- package/dist/setup-cli.d.ts +49 -0
- package/dist/setup-cli.js +292 -0
- package/dist/setup-cli.js.map +1 -0
- package/dist/skill-instructions.d.ts +10 -0
- package/dist/skill-instructions.js +68 -0
- package/dist/skill-instructions.js.map +1 -0
- package/dist/transports/http.d.ts +29 -0
- package/dist/transports/http.js +267 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/stdio.d.ts +9 -0
- package/dist/transports/stdio.js +19 -0
- package/dist/transports/stdio.js.map +1 -0
- package/docs/architecture.md +140 -0
- package/docs/customgraph-internal-migration.md +210 -0
- package/docs/security.md +126 -0
- package/docs/tools.md +230 -0
- package/manifest.json +76 -0
- package/package.json +61 -0
- package/skills/apimapper/SKILL.md +57 -0
- package/skills/apimapper/reference/joomla.md +85 -0
- package/skills/apimapper/reference/oauth.md +94 -0
- package/skills/apimapper/reference/troubleshooting.md +123 -0
- 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.
|