@com55/node-red-ai-access 0.1.1 → 0.2.0-beta.2
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 +74 -0
- package/README.md +84 -26
- package/ai-access.html +1 -1
- package/dist/plugin.cjs +753 -145
- package/dist/plugin.d.cts +82 -17
- package/docs/api/openapi.yaml +50 -0
- package/docs/mcp-clients.md +34 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,78 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to
|
|
7
7
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## Unreleased
|
|
10
|
+
|
|
11
|
+
## [0.2.0-beta.2] - 2026-10-28
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- OAuth route collision check no longer counts disabled `http in` nodes or nodes
|
|
16
|
+
on disabled flows; collisions are re-evaluated from scratch on every deploy,
|
|
17
|
+
and an enabled `http in` node is reported once (not again as an Express "bound
|
|
18
|
+
dispatch" route).
|
|
19
|
+
- OAuth route collision check ignores `http in` nodes in subflow templates
|
|
20
|
+
without a running instance, compares real mounted paths (`httpAdminRoot` /
|
|
21
|
+
`httpNodeRoot`), and matches paths case-insensitively like Express.
|
|
22
|
+
- `GET /mcp/api/v1/oauth/status` no longer exposes absolute `httpStatic`
|
|
23
|
+
filesystem paths to users without full (`*`) permission.
|
|
24
|
+
- Editor sidebar: OAuth lifetime number inputs and unit dropdowns sit on one
|
|
25
|
+
line, including the narrow Node-RED 5 sidebar.
|
|
26
|
+
- Editor sidebar: OAuth route collisions are shown as a readable bulleted list,
|
|
27
|
+
one route per line with paths in `<code>`; `http in` nodes link to the node in
|
|
28
|
+
the editor.
|
|
29
|
+
- Editor sidebar: Public URL "Check" result is shown as a clear green/red
|
|
30
|
+
status; loopback notes and the trust proxy state are moved into a muted,
|
|
31
|
+
collapsible "About the check" section with human-readable wording.
|
|
32
|
+
- OAuth route collision check also honours `httpStaticRoot` (and per-entry
|
|
33
|
+
`httpStatic` roots).
|
|
34
|
+
- `/mcp` page Access links: the area under the form now always shows generic
|
|
35
|
+
usage (with a `<YOUR_TOKEN>` placeholder) for the currently selected channel
|
|
36
|
+
and updates when the channel changes. The real token and client setup are
|
|
37
|
+
shown once in a modal right after creation; closing it removes the token from
|
|
38
|
+
the page.
|
|
39
|
+
- Access link create/list responses are sent with `Cache-Control: no-store` and
|
|
40
|
+
`Pragma: no-cache`.
|
|
41
|
+
- Access link tokens are kept in memory as a SHA-256 hash only (the raw token is
|
|
42
|
+
no longer retained after creation).
|
|
43
|
+
|
|
44
|
+
### Breaking
|
|
45
|
+
|
|
46
|
+
- **Access tokens can no longer be viewed or copied after creation:**
|
|
47
|
+
`GET /mcp/api/v1/links` no longer returns `token` (rest/mcp) or `url`
|
|
48
|
+
(get_link), and the links table only offers Revoke. Revoke and create a new
|
|
49
|
+
token instead.
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- Editor sidebar: short "How to use MCP OAuth" help in the OAuth section.
|
|
54
|
+
- `GET /mcp/api/v1/oauth/status` returns structured `details` (path, source,
|
|
55
|
+
node id/name) for each collision.
|
|
56
|
+
|
|
57
|
+
## [0.2.0-beta.1] - 2026-10-28
|
|
58
|
+
|
|
59
|
+
First pre-release of 0.2.0, published under the npm `beta` dist-tag.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- Node-RED settings and sidebar for configuring AI Access (#91).
|
|
64
|
+
- MCP OAuth authorization (#99): dynamic client registration (DCR), PKCE, an
|
|
65
|
+
owner consent page, and sliding refresh tokens with defaults of 1 hour (access
|
|
66
|
+
token), 30 days (refresh idle) and 90 days (refresh maximum).
|
|
67
|
+
- Route-collision check for the OAuth and metadata endpoints.
|
|
68
|
+
- MCP OAuth 2.1 authorization server and protected resource support with PKCE
|
|
69
|
+
S256, dynamic client registration, resource-bound tokens, rotating refresh
|
|
70
|
+
tokens, and `read`/`propose` scopes. Existing access-link tokens still work.
|
|
71
|
+
- Sidebar controls for OAuth lifetimes and client limits, registered-client
|
|
72
|
+
revocation, route-collision and metadata-fallback warnings, and a Public URL
|
|
73
|
+
warning when enabling OAuth, plus a refresh-token maximum lifetime (default 90
|
|
74
|
+
days, 0 = unlimited).
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
|
|
78
|
+
- CI now tests against a Node-RED 4 and Node-RED 5 matrix.
|
|
79
|
+
- Requires Node.js >= 20.19.
|
|
80
|
+
|
|
9
81
|
## [0.1.1] - 2026-10-17
|
|
10
82
|
|
|
11
83
|
### Added
|
|
@@ -384,5 +456,7 @@ Requires Node.js 20+.
|
|
|
384
456
|
auth, 413 limits) served at `GET /mcp/api/v1/openapi.yaml`; README install,
|
|
385
457
|
config, `/mcp` usage, and AI quickstart.
|
|
386
458
|
|
|
459
|
+
[0.2.0-beta.1]:
|
|
460
|
+
https://github.com/com55/node-red-ai-access/compare/v0.1.1...v0.2.0-beta.1
|
|
387
461
|
[0.1.1]: https://github.com/com55/node-red-ai-access/compare/v0.1.0...v0.1.1
|
|
388
462
|
[0.1.0]: https://github.com/com55/node-red-ai-access/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Node-RED AI Access
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](https://nodejs.org/)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](https://github.com/com55/node-red-ai-access/actions)
|
|
7
7
|
|
|
@@ -24,9 +24,10 @@ Derived from
|
|
|
24
24
|
|
|
25
25
|
## Install (Node-RED plugin)
|
|
26
26
|
|
|
27
|
-
Requirements: **Node-RED 4.x or newer**, Node.js 20
|
|
28
|
-
|
|
29
|
-
opt in (see
|
|
27
|
+
Requirements: **Node-RED 4.x or newer**, Node.js 20.19+ (tested: Node-RED 4.1 on
|
|
28
|
+
Node 20/22, Node-RED 5.0 on Node 22/24), and **`adminAuth` enabled**
|
|
29
|
+
(recommended). Without `adminAuth`, AI features stay off unless you opt in (see
|
|
30
|
+
Configuration).
|
|
30
31
|
|
|
31
32
|
1. Install into your Node-RED user directory (or via the palette):
|
|
32
33
|
|
|
@@ -80,6 +81,13 @@ module.exports = {
|
|
|
80
81
|
// Browser origins allowed to call /mcp/a, /mcp/api, and /mcp/rpc
|
|
81
82
|
// cross-origin (default: none — no CORS headers are sent)
|
|
82
83
|
// corsAllowedOrigins: ['https://app.example.com'],
|
|
84
|
+
// MCP OAuth (disabled by default; publicBaseUrl is the issuer)
|
|
85
|
+
// oauth: {
|
|
86
|
+
// enabled: true,
|
|
87
|
+
// accessTokenTtlSec: 3600,
|
|
88
|
+
// refreshIdleTtlSec: 2592000,
|
|
89
|
+
// dcrMaxClients: 20,
|
|
90
|
+
// },
|
|
83
91
|
},
|
|
84
92
|
};
|
|
85
93
|
```
|
|
@@ -89,8 +97,10 @@ module.exports = {
|
|
|
89
97
|
Access-link `url`, proposal `reviewUrl`, and `accessLinksUrl` (in
|
|
90
98
|
`token_expired`) are absolute URLs built by one shared helper:
|
|
91
99
|
|
|
92
|
-
1. `aiAccess.publicBaseUrl`, when set,
|
|
93
|
-
|
|
100
|
+
1. Effective `aiAccess.publicBaseUrl`, when set, overrides the request origin.
|
|
101
|
+
The sidebar/settings API accepts an HTTPS origin without a path, query,
|
|
102
|
+
fragment, or credentials (HTTP localhost / loopback is allowed for
|
|
103
|
+
development). An empty value uses the request origin.
|
|
94
104
|
2. Otherwise, when Express `trust proxy` is enabled (for example
|
|
95
105
|
`httpServerOptions: { 'trust proxy': true }`), the first `X-Forwarded-Proto`
|
|
96
106
|
and `X-Forwarded-Host` values are used.
|
|
@@ -117,34 +127,82 @@ affected by this setting.
|
|
|
117
127
|
By default the AI-facing endpoints (`/mcp/a/*`, `/mcp/api/*`, `/mcp/rpc`) send
|
|
118
128
|
**no** CORS headers, overriding Node-RED's built-in
|
|
119
129
|
`Access-Control-Allow-Origin: *` for those paths. To allow a browser app, list
|
|
120
|
-
its origin in `aiAccess.corsAllowedOrigins`
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
`
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
its origin in `aiAccess.corsAllowedOrigins` via the AI Access sidebar or
|
|
131
|
+
settings API. Changes apply immediately. The API rejects any invalid origin in
|
|
132
|
+
the supplied list (including paths, credentials, queries, and fragments). For a
|
|
133
|
+
listed `Origin` the response echoes it in `Access-Control-Allow-Origin` with
|
|
134
|
+
`Vary: Origin`, and `OPTIONS` preflight returns `204` with allowed methods
|
|
135
|
+
(`GET, POST, DELETE, OPTIONS`) and headers (`Authorization`, `Content-Type`,
|
|
136
|
+
`Accept`, `Mcp-Session-Id`, `Mcp-Protocol-Version`, `Last-Event-ID`). Origins
|
|
137
|
+
match case-insensitively and the configured origin is echoed; malformed origins
|
|
138
|
+
are rejected. Exposed headers include `Mcp-Session-Id`, `WWW-Authenticate`,
|
|
139
|
+
`Retry-After`, and the `RateLimit*` headers. Other origins get no CORS headers.
|
|
129
140
|
|
|
130
141
|
### Settings precedence
|
|
131
142
|
|
|
132
143
|
Effective values are resolved **without restart** in this order (highest wins):
|
|
133
144
|
|
|
134
|
-
1.
|
|
145
|
+
1. **`settings.js`** — keys present in the `aiAccess` block are locked
|
|
146
|
+
2. **Disk** — `<userDir>/.ai-access/config.json` (editor sidebar **AI Access**
|
|
135
147
|
tab, or `PUT /mcp/api/v1/settings`)
|
|
136
|
-
2. **`settings.js`** — `aiAccess` block (install / bootstrap defaults)
|
|
137
148
|
3. **Built-in defaults** — as documented above
|
|
138
149
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
150
|
+
This is a breaking precedence change in 0.2.0: settings.js now wins over disk.
|
|
151
|
+
Legacy `settings.json` is read when `config.json` is absent; the next save
|
|
152
|
+
writes `config.json`. Locked fields show a lock indicator and disabled input.
|
|
153
|
+
PUT ignores locked keys while applying unlocked ones; GET and PUT return
|
|
154
|
+
per-field `sources` (`settings.js`, `disk`, or `default`) and the precedence
|
|
155
|
+
order.
|
|
156
|
+
|
|
157
|
+
The editor right-hand sidebar tab (**AI Access**, next to Information / Help /
|
|
158
|
+
Debug / Configuration nodes / Context Data) lets `*` admins change
|
|
159
|
+
enable/disable, default deploy type, access-link TTL, maximum active links,
|
|
160
|
+
Public URL, CORS origins, and the three rate limits. `read` users see the same
|
|
161
|
+
values read-only. Express's effective `trust proxy` setting is displayed
|
|
162
|
+
read-only. Public URL, CORS, and rate limits apply immediately; changing limits
|
|
163
|
+
starts fresh rate-limit buckets. A button opens the `/mcp` page. When
|
|
144
164
|
`adminAuth` is off, the tab exposes the `allowWithoutAdminAuth` opt-in with a
|
|
145
165
|
clear local-only warning.
|
|
146
166
|
|
|
147
|
-
|
|
167
|
+
The sidebar also configures MCP OAuth, shows registered clients with revoke
|
|
168
|
+
buttons, and reports route collisions and the `httpNodeRoot: false` metadata
|
|
169
|
+
fallback. Enabling OAuth with no Public URL or an HTTP non-loopback URL shows a
|
|
170
|
+
warning confirmation; it does not block the change. OAuth fields set in
|
|
171
|
+
`settings.js` are locked in the sidebar.
|
|
172
|
+
|
|
173
|
+
### MCP OAuth
|
|
174
|
+
|
|
175
|
+
OAuth is disabled by default. Set `aiAccess.publicBaseUrl` to the public HTTPS
|
|
176
|
+
origin before enabling it; localhost and loopback HTTP are supported for local
|
|
177
|
+
development. Node-RED serves authorization, token, dynamic client registration,
|
|
178
|
+
and revocation under `/mcp/oauth/*`. It publishes authorization server metadata
|
|
179
|
+
at `/.well-known/oauth-authorization-server` and protected resource metadata at
|
|
180
|
+
`/.well-known/oauth-protected-resource/mcp` and
|
|
181
|
+
`/.well-known/oauth-protected-resource`. The issuer is `publicBaseUrl` and the
|
|
182
|
+
resource indicator is `{publicBaseUrl}/mcp`. When `httpNodeRoot` is false,
|
|
183
|
+
metadata is available under the admin `/mcp` path instead; the sidebar displays
|
|
184
|
+
this fallback. Route collisions disable the affected OAuth endpoints and appear
|
|
185
|
+
in the sidebar.
|
|
186
|
+
|
|
187
|
+
Clients use authorization code with PKCE S256 and can request `read` or
|
|
188
|
+
`propose`. Consent uses the Node-RED editor login. Access tokens last one hour
|
|
189
|
+
by default; refresh tokens expire after 30 days of inactivity and rotate on each
|
|
190
|
+
use. Reusing a rotated refresh token revokes its token family. Tokens are stored
|
|
191
|
+
as SHA-256 hashes in `<userDir>/.ai-access/oauth.json`. Access-link `aia_`
|
|
192
|
+
bearer tokens continue to work. See [MCP client setup](docs/mcp-clients.md).
|
|
193
|
+
|
|
194
|
+
Public URL **Check** uses `POST /mcp/api/v1/settings/check-public-url` and
|
|
195
|
+
requires `*` permission because it performs an outbound request. It validates
|
|
196
|
+
the form URL, fetches only `{publicBaseUrl}{httpAdminRoot}/mcp/api/v1`
|
|
197
|
+
(discovery), times out after 5 seconds, and never follows redirects. The result
|
|
198
|
+
reports the HTTP status or a connection/timeout error; it does not save the
|
|
199
|
+
form. Loopback checks use Node-RED's own listening port; behind a reverse proxy,
|
|
200
|
+
use the internal Node-RED port. Unix-socket listeners are not supported. HTTPS
|
|
201
|
+
checks using a self-signed certificate fail because TLS verification remains
|
|
202
|
+
enabled. Saving a Public URL with a private/internal IP literal is allowed, but
|
|
203
|
+
the settings response and sidebar show a warning.
|
|
204
|
+
|
|
205
|
+
Storage lives under **`<userDir>/.ai-access`** (`config.json`,
|
|
148
206
|
`proposals/*.json`, and append-only `logs/audit.jsonl`). Directories use mode
|
|
149
207
|
`0700`, files `0600`. Proposal JSON is capped at **1_500_000 bytes**; oversized
|
|
150
208
|
proposal JSON or audit lines return HTTP **413** with `code: payload_too_large`
|
|
@@ -229,9 +287,9 @@ a `code`. `token_expired` also includes `expiredAt` and `accessLinksUrl` (the
|
|
|
229
287
|
|
|
230
288
|
Access links are **in memory** (default TTL **4h**, max 10). Caps: **get_link ≤
|
|
231
289
|
24h**, **rest/mcp ≤ 30d**. Creating a link returns the raw `token` plus a
|
|
232
|
-
self-contained `url` for `get_link` (`/mcp/a/<token>`).
|
|
233
|
-
|
|
234
|
-
|
|
290
|
+
self-contained `url` for `get_link` (`/mcp/a/<token>`). The token is shown only
|
|
291
|
+
once: `GET /links` never returns `token` or `url`, so to give access again
|
|
292
|
+
revoke the link and create a new one. After a restart every old token is
|
|
235
293
|
invalid.
|
|
236
294
|
|
|
237
295
|
Call `list_flows` or `get_flow` first and pass the returned `rev` as
|
package/ai-access.html
CHANGED