Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/build-plugin/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Before writing a single file, understand and explore the API. **Use `AskUserQues
- **Time-range control** — does the endpoint accept a queryable time range at all (a `from`/`to`, `start`/`end`, or `period` parameter), or only return a fixed snapshot / current values?
- **Data granularity** — when the endpoint _does_ accept a range, the finest interval it aggregates at: **per-event/raw**, **hourly**, **daily**, or **monthly**. Read it off the API docs (aggregation windows, `granularity`/`interval` params, the minimum queryable range).
5. **Understand pagination** — Cursor/next-token, or offset/limit? Separate concern from response transformation.
6. **Note the auth pattern** — API key in header, Bearer token, OAuth2, Basic auth? Determine from the docs.
6. **Note the auth pattern** — API key in header, Bearer token, OAuth2, Basic auth, JWT Bearer (signed-JWT auth)? Determine from the docs.

---

Expand Down
42 changes: 42 additions & 0 deletions .claude/skills/build-plugin/references/metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,45 @@ OAuth URLs and scopes support `{{fieldName}}` expressions — useful when the au
"oauth2AuthUrl": "https://{{accountId}}.example.com/oauth/authorize",
"oauth2Scope": "read {{role ? 'role:' + role : ''}}"
```

**JWT Bearer** (HMAC — `HS256`/`HS384`/`HS512`; distinct from the static "Bearer token" pattern above — this signs a fresh JWT and attaches it to every request):

```json
"authMode": "jwtBearer",
"jwtAlgorithm": "HS256",
"jwtSecret": "{{jwtSecret}}",
"jwtPayload": {
"iss": "my-integration",
"sub": "{{accountId}}",
"iat": "{{Math.floor(Date.now() / 1000) - 60}}",
"exp": "{{Math.floor(Date.now() / 1000) + 600}}"
}
```

**JWT Bearer** (asymmetric — `RS256`/`RS384`/`RS512`, `PS256`/`PS384`/`PS512`, `ES256`/`ES384`/`ES512`):

```json
"authMode": "jwtBearer",
"jwtAlgorithm": "RS256",
"jwtPrivateKey": "{{jwtPrivateKey}}",
"jwtPayload": {
"iss": "my-integration",
"iat": "{{Math.floor(Date.now() / 1000) - 60}}",
"exp": "{{Math.floor(Date.now() / 1000) + 600}}"
}
```

Set exactly one of `jwtSecret` (HMAC algorithms) or `jwtPrivateKey` — a PEM-encoded private key — for every other algorithm; never both. There's no caching/refresh step to configure — unlike OAuth2, a fresh JWT is signed on every request.

**Advanced JWT options:**

```json
"jwtTokenLocation": "queryParam", // "header" (default) or "queryParam"
// Prefer "header"; query params can leak the token via logs/referrers.
"jwtQueryParamName": "token", // required when jwtTokenLocation is "queryParam" — the query param the JWT is sent as
Comment thread
andrewmumblebee marked this conversation as resolved.
"jwtSecretIsBase64": true, // set when jwtSecret is base64-encoded rather than plain text
"jwtHeaderPrefix": "Bearer ", // text prepended to the token when sent as a header; defaults to "Bearer " (include the trailing space if overriding)
"jwtHeaders": { "kid": "my-key-id" } // extra claims merged into the JWT's own header (not the HTTP request header), e.g. a key ID
```

`jwtPayload` and `jwtHeaders` are plain JSON objects; any string value can itself be a `{{ ... }}` expression, evaluated fresh on every request — this is how `iat`/`exp` stay current without any extra logic.
2 changes: 1 addition & 1 deletion .claude/skills/build-plugin/references/ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ The `validation` object accepts [react-hook-form](https://react-hook-form.com/do
}
```

**`password`** — masked text; **use for any API key, token, secret, or password field**:
**`password`** — masked text; **use for any API key, token, secret, or password field**

```json
{ "type": "password", "name": "apiKey", "label": "API Key" }
Expand Down
Loading