@com55/node-red-ai-access 0.0.0-stage → 0.1.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 +388 -0
- package/LICENSE +22 -0
- package/README.md +333 -2
- package/ai-access.cjs +8 -0
- package/ai-access.html +34 -0
- package/dist/plugin.cjs +5487 -0
- package/dist/plugin.d.cts +707 -0
- package/docs/README.md +28 -0
- package/docs/api/openapi.yaml +2589 -0
- package/docs/errors.md +83 -0
- package/docs/mcp-clients.md +78 -0
- package/docs/mcp-tools.md +148 -0
- package/docs/releasing.md +82 -0
- package/package.json +135 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Node-RED AI Access will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to
|
|
7
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
|
+
|
|
9
|
+
## [0.1.1] - 2026-10-17
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `/status` (REST and MCP `get_status`) now includes `openapiJson` in
|
|
14
|
+
`caller.baseUrls` for `rest` tokens, pointing at the served
|
|
15
|
+
`GET /mcp/api/v1/openapi.json`.
|
|
16
|
+
- Optional `aiAccess.corsAllowedOrigins` setting (settings.js): list of browser
|
|
17
|
+
origins allowed to call the AI-facing endpoints cross-origin, with
|
|
18
|
+
`Vary: Origin` and `OPTIONS` preflight support. Origins match
|
|
19
|
+
case-insensitively and the configured canonical origin is echoed; malformed
|
|
20
|
+
origins are rejected. `WWW-Authenticate`, `Retry-After`, and the `RateLimit*`
|
|
21
|
+
headers are exposed to allowed origins.
|
|
22
|
+
- Optional `aiAccess.publicBaseUrl` setting (settings.js) that sets the public
|
|
23
|
+
base URL for absolute links (access-link `url`, proposal `reviewUrl`,
|
|
24
|
+
`accessLinksUrl`), overriding `Host` and `X-Forwarded-*`.
|
|
25
|
+
- The MCP client snippets in the GET-link guide use the public origin
|
|
26
|
+
(`publicBaseUrl`, else the validated request origin) instead of the
|
|
27
|
+
`https://YOUR-NODE-RED` placeholder, which remains only when the origin cannot
|
|
28
|
+
be determined.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Behavior change:** every response on the GET access-link channel
|
|
33
|
+
(`/mcp/a/<token>/*`), including the markdown guide and JSON success and error
|
|
34
|
+
bodies, now defaults to `Content-Type: text/html; charset=utf-8`, because some
|
|
35
|
+
AI web fetchers only read `text/html`. The HTML is a minimal document with the
|
|
36
|
+
same markdown or pretty-printed JSON, fully escaped in one `<pre>`, with safe
|
|
37
|
+
relative `./…` links linkified (no scripts, no external resources). Status
|
|
38
|
+
codes and data are unchanged. The previous representation is returned when
|
|
39
|
+
`Accept` explicitly lists `text/markdown` or `application/json`, or
|
|
40
|
+
`?format=md` / `?format=json` is set; an explicit `Accept: text/plain` returns
|
|
41
|
+
the raw text as `text/plain`. All `/mcp/a/*` responses carry `Vary: Accept`,
|
|
42
|
+
`Cache-Control: no-store`, `Referrer-Policy: no-referrer`,
|
|
43
|
+
`X-Robots-Tag: noindex, nofollow`, and `X-Content-Type-Options: nosniff`; HTML
|
|
44
|
+
adds a restrictive `Content-Security-Policy`. REST and MCP are unchanged.
|
|
45
|
+
- **Behavior change:** the AI-facing endpoints (`/mcp/a/*`, `/mcp/api/*`,
|
|
46
|
+
`/mcp/rpc`) no longer send `Access-Control-Allow-Origin: *` (inherited from
|
|
47
|
+
Node-RED's admin app). No CORS headers are sent unless the request `Origin` is
|
|
48
|
+
listed in `aiAccess.corsAllowedOrigins`. Only browser-based callers are
|
|
49
|
+
affected; server-side clients are not.
|
|
50
|
+
- Attempts to create, list, or revoke access links under a GET access link
|
|
51
|
+
(`/mcp/a/<token>/links`) now return **403** `link_create_not_supported`
|
|
52
|
+
instead of 405. Error registry, `docs/errors.md`, OpenAPI, and get-link docs
|
|
53
|
+
updated.
|
|
54
|
+
- `/status` (GET link, REST, and MCP `get_status`) no longer includes
|
|
55
|
+
`storageDir` for access-link callers; it remains in admin-session responses
|
|
56
|
+
(shown on the `/mcp` page).
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- Proposal titles no longer repeat the flow name when a long `<title> in <flow>`
|
|
61
|
+
was capped at 80 characters.
|
|
62
|
+
- GET-link HTML no longer linkifies template paths such as `./nodes/<id>`.
|
|
63
|
+
|
|
64
|
+
- **Security:** absolute URLs no longer copy unvalidated `X-Forwarded-Proto` /
|
|
65
|
+
`X-Forwarded-Host` (or `Host`) values. The scheme must be exactly `http` or
|
|
66
|
+
`https` (else the connection scheme is used); hosts must be a DNS name, IPv4,
|
|
67
|
+
or bracketed IPv6 literal with an optional port 1-65535, with no userinfo,
|
|
68
|
+
path, query, fragment, or whitespace. Invalid forwarded hosts fall back to
|
|
69
|
+
`Host`; an invalid `Host` falls back to a relative path.
|
|
70
|
+
|
|
71
|
+
- Absolute URLs (access-link `url`, proposal `reviewUrl`, `accessLinksUrl`) used
|
|
72
|
+
`http://` behind a TLS-terminating proxy. With Express `trust proxy` enabled
|
|
73
|
+
the first `X-Forwarded-Proto` value is now used explicitly; without it, set
|
|
74
|
+
`aiAccess.publicBaseUrl`. Forwarded headers stay ignored when `trust proxy` is
|
|
75
|
+
off.
|
|
76
|
+
|
|
77
|
+
## [0.1.0] - 2026-10-16
|
|
78
|
+
|
|
79
|
+
First public release of **Node-RED AI Access**: an admin-gated Node-RED 4 plugin
|
|
80
|
+
that lets AI agents read flows and submit reviewable change proposals over REST,
|
|
81
|
+
MCP, and GET access links, with scoped, expiring tokens, a review UI, conflict
|
|
82
|
+
detection, an audit log, and an error-code registry shared by all channels.
|
|
83
|
+
Requires Node.js 20+.
|
|
84
|
+
|
|
85
|
+
### Release
|
|
86
|
+
|
|
87
|
+
- Initial independent release of **Node-RED AI Access**
|
|
88
|
+
(`@com55/node-red-ai-access`), derived from
|
|
89
|
+
[ziv-daniel/node-red-mcp](https://github.com/ziv-daniel/node-red-mcp).
|
|
90
|
+
- Package metadata, Docker image names, documentation, and CI retargeted to
|
|
91
|
+
`com55/node-red-ai-access`.
|
|
92
|
+
- GitHub Actions CI runs on `ubuntu-latest` for all events.
|
|
93
|
+
- Requires Node.js `>=20`; CI tests Node 20, 22, and 23.
|
|
94
|
+
- `@modelcontextprotocol/sdk` raised to `^1.31.0`.
|
|
95
|
+
- Published package is a minified production build without source maps;
|
|
96
|
+
`prepublishOnly` runs quality checks, tests, and the production build. The
|
|
97
|
+
whole `docs/` folder ships; the OpenAPI spec exists once
|
|
98
|
+
(`docs/api/openapi.yaml`) and is served at `GET /mcp/api/v1/openapi.yaml`.
|
|
99
|
+
- MCP `list_proposals` `status: []` (or `""`) means no status filter; an invalid
|
|
100
|
+
status member returns the same `invalid_filter` message on every channel.
|
|
101
|
+
|
|
102
|
+
### Fixed (pre-release review)
|
|
103
|
+
|
|
104
|
+
- `RateLimit-*` headers on token requests report the per-token bucket
|
|
105
|
+
(`300;w=60` by default) on every route; requests without a token report the
|
|
106
|
+
per-IP bucket.
|
|
107
|
+
- Spec also served as JSON at `GET /mcp/api/v1/openapi.json`.
|
|
108
|
+
- Proposal responses include `storedTitle` (the title as saved).
|
|
109
|
+
- `duplicate_node_id` checks the whole resulting flows file: the tab id, nodes
|
|
110
|
+
on other tabs, config nodes, and subflows.
|
|
111
|
+
- A single validation error returns its specific message at the top level.
|
|
112
|
+
- Hints come from the error registry on every channel (including
|
|
113
|
+
`node_already_exists` and a top-level `validation_failed` hint); `errors[]`
|
|
114
|
+
entries no longer carry `statusCode`.
|
|
115
|
+
- `status` list filter accepts several values (comma-separated or repeated on
|
|
116
|
+
REST/GET; array or comma string on MCP); any invalid value is
|
|
117
|
+
`invalid_filter`. Repeating a single-value filter is `invalid_filter`.
|
|
118
|
+
- `mine=false` returns only proposals created by other links.
|
|
119
|
+
- GET guide lists every error code, warnings, statuses (including `superseded`),
|
|
120
|
+
and `autoClosedCount`.
|
|
121
|
+
|
|
122
|
+
### Changed
|
|
123
|
+
|
|
124
|
+
- List filters are validated on GET, REST, and MCP: bad `status`, `limit` (not
|
|
125
|
+
1–200; no longer clamped), `offset`, or `mine` return **400** `invalid_filter`
|
|
126
|
+
naming the parameter and allowed values. MCP argument schema errors return the
|
|
127
|
+
same JSON body (`invalid_filter` / `invalid_arguments`) instead of raw Zod
|
|
128
|
+
output.
|
|
129
|
+
- List responses on every channel share `total`, `offset`, `limit` (when set),
|
|
130
|
+
`hasMore`, and `nextOffset` (offset-based pagination).
|
|
131
|
+
- Several validation errors in one proposal return **400** `validation_failed`
|
|
132
|
+
(`N validation errors`, all listed in `errors[]`); a single error keeps its
|
|
133
|
+
own code (e.g. **404** `node_not_found`).
|
|
134
|
+
- Out-of-range or non-positive `ttlSeconds` returns **400** `invalid_ttl` with a
|
|
135
|
+
hint stating the per-kind caps (was `access_link_invalid`).
|
|
136
|
+
- `staleOnOlderRev` removed (stale peers are auto-closed on read); responses
|
|
137
|
+
include `autoClosedCount` when a request auto-closed pending proposals.
|
|
138
|
+
- `minutesRemaining` rounds up; `/status` also reports `secondsRemaining`.
|
|
139
|
+
- `token_expired` includes `expiredAt` and `accessLinksUrl`; GET-link 401s
|
|
140
|
+
include `WWW-Authenticate`.
|
|
141
|
+
|
|
142
|
+
### Added
|
|
143
|
+
|
|
144
|
+
- Duplicate node ids in a full tab or batch are rejected with
|
|
145
|
+
`duplicate_node_id`.
|
|
146
|
+
- Central error-code registry (`src/plugin/error-registry.ts`) generating
|
|
147
|
+
`docs/errors.md`, the GET guide error table, and the OpenAPI `ErrorCode` list
|
|
148
|
+
(`yarn generate:errors`; CI staleness check `yarn check:errors`).
|
|
149
|
+
- Channel onboarding on `/mcp`: REST copies a ready-to-paste AI prompt (base
|
|
150
|
+
URL, Bearer, OpenAPI + discovery, human-approve note, and a stop-and-ask-for-
|
|
151
|
+
GET-link line when custom headers are unavailable); MCP shows a setup panel
|
|
152
|
+
with Claude Desktop / Cursor / VS Code / Claude Code CLI / Streamable HTTP
|
|
153
|
+
tabs (each with its own copy button); GET link still copies the URL. Docs:
|
|
154
|
+
`docs/README.md`, `docs/mcp-clients.md`, `docs/errors.md`, generated
|
|
155
|
+
`docs/mcp-tools.md` (`yarn generate:mcp-tools` / CI stale check).
|
|
156
|
+
- Access-link TTL UX: `/mcp` expiry dropdown (15m / 1h / 4h / 24h / 7d /
|
|
157
|
+
Custom); `get_link` capped at 24h (7d hidden; switching to GET link with
|
|
158
|
+
expiry above 24h — including Custom — resets to 1 hour with a short note);
|
|
159
|
+
`rest` / `mcp` capped at 30d with a red warning above 24h. Server enforces the
|
|
160
|
+
same caps. Status for access-link callers includes `expiresAt` and
|
|
161
|
+
`minutesRemaining`. Known-expired tokens return **401** `token_expired`
|
|
162
|
+
(distinct from `access_link_invalid`).
|
|
163
|
+
- Auto-close stale pending proposals: `expired` (live rev moved) and
|
|
164
|
+
`superseded` (another proposal on the same flow was approved).
|
|
165
|
+
- Conflict trim: `conflictingProposals` lists same-rev or overlapping peers
|
|
166
|
+
only; other older-rev peers appear as `staleOnOlderRev` count. List items omit
|
|
167
|
+
`conflictingProposals`.
|
|
168
|
+
- GET withdraw requires `confirm=1` (prefetch-safe).
|
|
169
|
+
- Proposal list pagination and filters on GET/REST/MCP: `status`, `flowId`,
|
|
170
|
+
`limit`/`offset` (response `total`; max limit 200). MCP tool renamed to
|
|
171
|
+
`list_proposals` (`list_my_proposals` kept as alias).
|
|
172
|
+
- Proposal visibility: every access-link token can list/read **all** proposals
|
|
173
|
+
with `mine: true|false`; filter with `?mine=true` / MCP `list_proposals`
|
|
174
|
+
`{ mine: true }`.
|
|
175
|
+
- Cross-token conflict warnings on submit/read: `conflictingProposals` with
|
|
176
|
+
optional `overlappingNodeIds` / `overlappingFields` (replaces private
|
|
177
|
+
`otherLinkSiblingCount`).
|
|
178
|
+
- Withdraw own pending proposals (`withdrawn` status): GET
|
|
179
|
+
`…/proposals/<id>/withdraw?confirm=1`, REST `POST /proposals/{id}/withdraw`,
|
|
180
|
+
MCP `withdraw_proposal` (+ `/mcp` pill + audit `proposal.withdrawn`).
|
|
181
|
+
- Docs: Known issues (per-token rate limits stack), 1.5MB cap on every channel,
|
|
182
|
+
canonical REST/MCP `payload.nodes` op shapes.
|
|
183
|
+
- Phase 2C: GET `/mcp/a/<token>` cannot mint tokens — explicit **405**
|
|
184
|
+
`link_create_not_supported` on `/mcp/a/<token>/links`; OpenAPI documents
|
|
185
|
+
`AccessLinkKind`, `CreateLinkRequest.kind`, `wrong_token_kind` /
|
|
186
|
+
`invalid_link_kind`, and that get-link has no create-link operation; discovery
|
|
187
|
+
advertises admin-only `POST /links` mint and `getLink.canMintTokens: false`.
|
|
188
|
+
|
|
189
|
+
### Fixed
|
|
190
|
+
|
|
191
|
+
- Access-link rate limits share one per-token bucket across GET
|
|
192
|
+
`/mcp/a/<token>`, REST Bearer, and MCP (same limiter instances + unified token
|
|
193
|
+
key).
|
|
194
|
+
- Layered-read JSON responses always set `X-Content-Type-Options: nosniff` and
|
|
195
|
+
never echo path/query/exception text into error bodies (CodeQL XSS / HTML).
|
|
196
|
+
|
|
197
|
+
### Changed
|
|
198
|
+
|
|
199
|
+
- `/mcp` human-UX pass: status dashboard (AI on, active links, pending count,
|
|
200
|
+
expiry in hours), Pending/Decided proposal filters with tab badge, compact
|
|
201
|
+
decided cards, plain one-line diff summaries, stale dismiss + large-delete
|
|
202
|
+
typed confirm, Approve & deploy with deploy-type Advanced help, reject dialog,
|
|
203
|
+
access-link TTL presets (1h/4h/24h), Created/Last used link table, audit
|
|
204
|
+
labels without event codes, sticky tab nav, sign-in gate (no silent bounce),
|
|
205
|
+
and sidebar TTL hours + Review deep link to `/mcp#proposals`.
|
|
206
|
+
|
|
207
|
+
### Added
|
|
208
|
+
|
|
209
|
+
- Targeted `replace_text` op for string fields (`template`, `func`, …): unique
|
|
210
|
+
`find` or 1-based `occurrence`; errors `400 text_not_found` /
|
|
211
|
+
`409 ambiguous_match` (with `matchCount`). Available via GET propose
|
|
212
|
+
(`node=&field=&find=&replace=`), patch JSON, and REST/MCP `submit_proposal`
|
|
213
|
+
(`payload.nodes` without a full tab). Multiple ops apply in order. AI guide
|
|
214
|
+
recommends it for small edits to long fields and documents a last-resort
|
|
215
|
+
placeholder→paste workflow when content exceeds draft limits.
|
|
216
|
+
- Layered reads on GET link, REST, and MCP (same auth/rate limits):
|
|
217
|
+
`GET …/flows/{id}?summary=1` (node id/type/name/wires + bytes, no field
|
|
218
|
+
bodies); `GET …/nodes/{id}` (one full node + flowId);
|
|
219
|
+
`GET …/nodes/{id}/field/{name}?lines=A-B` (optional line range +
|
|
220
|
+
`totalLines`/`bytes`); `GET …/search?q=&flow=` (hits with node/field/lines/
|
|
221
|
+
snippet; capped with `truncated:true`). Recommended loop: summary → search →
|
|
222
|
+
read line range → `replace_text` → propose. MCP tools: `get_flow` (`summary`),
|
|
223
|
+
`get_node`, `get_node_field`, `search_flows`.
|
|
224
|
+
|
|
225
|
+
### Changed
|
|
226
|
+
|
|
227
|
+
- `/mcp/api` and `/mcp/a` use dual in-memory rate limits (per IP and per access
|
|
228
|
+
token) with configurable `aiAccess.rateLimitWindowMs` / `rateLimitMaxPerIp` /
|
|
229
|
+
`rateLimitMaxPerToken` (generous defaults: 600/min IP, 300/min token).
|
|
230
|
+
Exceeded limits return **429** `{ code: 'rate_limited', retryAfter }` and a
|
|
231
|
+
`Retry-After` header.
|
|
232
|
+
- Proposal `reviewUrl` is now an **absolute** URL from the request Host/protocol
|
|
233
|
+
(`X-Forwarded-*` only when Express `trust proxy` is set). Responses also
|
|
234
|
+
include relative `reviewPath` (`/mcp#proposal=<id>`).
|
|
235
|
+
- `displayName` datetime includes an explicit UTC offset and is documented as
|
|
236
|
+
**server local time** (for AI clients). The `/mcp` UI never shows that server
|
|
237
|
+
string: proposal headings and audit labels rebuild the title and format
|
|
238
|
+
`createdAt` / `at` with `Intl.DateTimeFormat` in the **browser** timezone.
|
|
239
|
+
`createdAt` remains ISO-8601.
|
|
240
|
+
- `/mcp` is served with `Cache-Control: no-store` so plugin upgrades do not keep
|
|
241
|
+
serving a cached admin client (inline CSS/JS).
|
|
242
|
+
- `/mcp` location hash reflects the active tab (`#status`, `#links`,
|
|
243
|
+
`#proposals`, `#audit`) via `history.replaceState`. `#proposal=<id>` is only
|
|
244
|
+
used while focusing a proposal deep link and is replaced with `#proposals`
|
|
245
|
+
after the scroll/highlight is consumed (so reload / returning to Proposals
|
|
246
|
+
does not re-trigger).
|
|
247
|
+
|
|
248
|
+
### Fixed
|
|
249
|
+
|
|
250
|
+
- Side-by-side (split) diffs for large field replaces pair remove/add runs so
|
|
251
|
+
the AFTER column is visible instead of a long blank placeholder stack.
|
|
252
|
+
- Unknown flow ids return **404** `flow_not_found` (with hint to use ids from
|
|
253
|
+
`GET ./flows`) instead of **502** `upstream_error` on RedAdapter.
|
|
254
|
+
- Invalid draft chunk base64url (e.g. `@@@`) returns **400**
|
|
255
|
+
`invalid_chunk_data` and does not store the seq.
|
|
256
|
+
- `set.wires` is allowed in patch/propose when targets exist in the resulting
|
|
257
|
+
flow (validated); id/type/z stay protected.
|
|
258
|
+
- Added/removed node diffs include `wires` (readable) and `x`/`y`.
|
|
259
|
+
- `draft/new` checks live rev immediately (**409** `version_mismatch`); stale
|
|
260
|
+
submit responses set `draftDeleted: false` when the draft was kept.
|
|
261
|
+
- REST `Bearer bogus` / short tokens return `access_link_invalid` (with a
|
|
262
|
+
truncated-URL hint when applicable), not `access_link_missing`.
|
|
263
|
+
- Proposal diffs default to compact `format=summary` (changed lines + context +
|
|
264
|
+
byte sizes); `format=full` restores complete text.
|
|
265
|
+
- Re-submitting a draft returns **409** `draft_already_submitted` with
|
|
266
|
+
`proposalId`; malformed sha256 returns **400** `invalid_sha256`.
|
|
267
|
+
- Propose/draft responses warn with `siblingsOnSameRev` when other pending
|
|
268
|
+
proposals share the same flow revision.
|
|
269
|
+
|
|
270
|
+
### Added
|
|
271
|
+
|
|
272
|
+
- Human-friendly proposals: optional `title` (≤80 chars) on all submit paths
|
|
273
|
+
(REST, MCP `submit_proposal`, GET `propose`, draft new/submit); responses
|
|
274
|
+
always include `title`, `displayName` (`title ·` server-local datetime), and
|
|
275
|
+
`reviewUrl` (`/mcp#proposal=<id>`). AI guides tell clients to cite
|
|
276
|
+
`displayName` + `reviewUrl` (not the raw id). `/mcp` list headings and audit
|
|
277
|
+
details use `displayName`; deep links open/scroll/highlight the proposal.
|
|
278
|
+
|
|
279
|
+
### Changed
|
|
280
|
+
|
|
281
|
+
- Node added/removed diff blocks use a readable summary sentence, show key
|
|
282
|
+
fields (name, wires by node name, code/HTML), and hide the rest under “All
|
|
283
|
+
properties”. HTML templates offer a sandboxed iframe Preview toggle (`sandbox`
|
|
284
|
+
with no `allow-scripts`, `srcdoc`) plus the code view.
|
|
285
|
+
- `/mcp` Access Links: Copy link on each active row and on create (brief Copied
|
|
286
|
+
state + clipboard fallback); empty states; relative timestamps with absolute
|
|
287
|
+
on hover; confirm before revoke/approve/reject. List API includes `url` for
|
|
288
|
+
active in-memory links (raw `token` still create-only). Diff lines wrap in
|
|
289
|
+
unified and side-by-side views.
|
|
290
|
+
|
|
291
|
+
### Fixed
|
|
292
|
+
|
|
293
|
+
- `/mcp` proposal diffs default to side-by-side on wide screens (unified below
|
|
294
|
+
the narrow breakpoint) and remember the toggle in `localStorage`; short
|
|
295
|
+
single-line fields in Node added/removed blocks stay inline (only long
|
|
296
|
+
multi-line text collapses).
|
|
297
|
+
- Proposal diffs compare against the **base flow snapshot** stored at submit
|
|
298
|
+
(`baseFlow`), so later approvals no longer make unrelated nodes look
|
|
299
|
+
“removed”. Pending+stale still warns and may include `vsCurrent`;
|
|
300
|
+
approved/rejected/failed never show the approve-would-fail banner.
|
|
301
|
+
- Added/removed nodes in the `/mcp` UI render as single “Node added” / “Node
|
|
302
|
+
removed” blocks (properties collapsed; long text expandable).
|
|
303
|
+
- Unknown paths under `/mcp/a/*` return JSON `{ code: not_found }` without
|
|
304
|
+
echoing the access-link token (no HTML `Cannot GET`).
|
|
305
|
+
- Draft `append` `totalBytes` counts **decoded** chunk bytes vs `maxBytes` (not
|
|
306
|
+
base64url character length).
|
|
307
|
+
- Field-mode `draft/new` validates that `node`/`field` exist on the live flow
|
|
308
|
+
before upload (`mode=json` may add/remove nodes and skips that check).
|
|
309
|
+
- Proposal diffs for added/removed nodes include all properties; long text
|
|
310
|
+
fields (`template`, `func`, …) are line-diffed as all-added / all-removed.
|
|
311
|
+
- Draft append “no gaps” hint no longer reads like the upload is finished; error
|
|
312
|
+
hints are code-specific (e.g. `chunk_conflict` vs stale rev).
|
|
313
|
+
- `/mcp` audit log actor cells expose the full label via `title` when truncated;
|
|
314
|
+
sidebar AI Access TTL/deploy labels stack above their inputs.
|
|
315
|
+
- `/mcp` page uses a wide responsive container (`max-width` ~1400px / 95vw);
|
|
316
|
+
proposal diffs default to side-by-side on wide screens with a unified toggle;
|
|
317
|
+
long code/diff blocks scroll horizontally with monospace line numbers.
|
|
318
|
+
|
|
319
|
+
### Added
|
|
320
|
+
|
|
321
|
+
- GET patch / `mode=json` drafts support `nodes:[{op:'add'|'remove'|'set',...}]`
|
|
322
|
+
(plus full `replace_flow`); docs cover exact JSON shapes and ~4–5 KB raw chunk
|
|
323
|
+
guidance.
|
|
324
|
+
- Self-contained GET access links at `/mcp/a/<token>` for chat AIs that can only
|
|
325
|
+
HTTP GET (markdown docs; scoped status/flows/proposals/propose/drafts; chunked
|
|
326
|
+
drafts for large fields; `Cache-Control: no-store`; per-link rate limit; token
|
|
327
|
+
redaction in paths). Create-link responses include the full `url`; default
|
|
328
|
+
link TTL is **4 hours** (configurable).
|
|
329
|
+
|
|
330
|
+
### Fixed
|
|
331
|
+
|
|
332
|
+
- Shared-read REST routes (`/flows`, `/proposals`, …) return
|
|
333
|
+
`access_link_missing` / `access_link_invalid` with Bearer `WWW-Authenticate`
|
|
334
|
+
(not passport `auth_required` / realm=`Users`) when an access-link token is
|
|
335
|
+
missing or invalid.
|
|
336
|
+
- `GET /flows/{id}` always includes `flow.type` (defaults to `tab`) so REST
|
|
337
|
+
matches OpenAPI `FlowTab` and MCP `get_flow`.
|
|
338
|
+
- Unknown paths under `/mcp/api/v1` return JSON `{ code: not_found }` instead of
|
|
339
|
+
HTML `Cannot GET`.
|
|
340
|
+
|
|
341
|
+
### Added
|
|
342
|
+
|
|
343
|
+
- `GET /mcp/api/v1` unauthenticated discovery index (endpoints, auth scheme,
|
|
344
|
+
OpenAPI URL, MCP endpoint).
|
|
345
|
+
- `GET /status` accepts access-link Bearer and returns caller label, expiry,
|
|
346
|
+
permissions, and base URLs.
|
|
347
|
+
- OpenAPI “Quick start for access-link clients” with example
|
|
348
|
+
`CreateProposalRequest`; `/mcp` **Copy link for AI** includes API base + docs
|
|
349
|
+
hint with the token.
|
|
350
|
+
|
|
351
|
+
### Removed
|
|
352
|
+
|
|
353
|
+
- Legacy standalone MCP server (Express/OAuth/SSE entrypoints, Docker images,
|
|
354
|
+
Playwright e2e against `/health`, and related dependencies). The Node-RED
|
|
355
|
+
plugin (`/mcp`, `/mcp/api/v1`, `/mcp/rpc`) is the supported surface.
|
|
356
|
+
|
|
357
|
+
### Added
|
|
358
|
+
|
|
359
|
+
- Node-RED plugin scaffold (`ai-access.cjs` / `dist/plugin.cjs`) with `GET /mcp`
|
|
360
|
+
admin page and `GET /mcp/api/v1/status`.
|
|
361
|
+
- `aiAccess` settings hook (`allowWithoutAdminAuth` and reserved fields for
|
|
362
|
+
later link TTL, max links, and deployment type).
|
|
363
|
+
- Optional `node-red` peerDependency (`>=4.0.0`).
|
|
364
|
+
- MCP Streamable HTTP endpoint at `POST /mcp/rpc` (access-link Bearer tools
|
|
365
|
+
mirroring the AI-facing REST API).
|
|
366
|
+
- Flow runtime adapter (`RedAdapter` via `RED.runtime`, `HttpAdapter` Admin API
|
|
367
|
+
fallback, factory selection) with rev-checked `updateFlow` (default deployment
|
|
368
|
+
type `nodes`).
|
|
369
|
+
- In-memory access-link store and admin routes under `/mcp/api/v1/links`
|
|
370
|
+
(default TTL 24h, max 10 active, token shown once; Bearer middleware for AI
|
|
371
|
+
clients).
|
|
372
|
+
- On-disk proposal store and audit log under `<userDir>/.ai-access`, plus REST
|
|
373
|
+
routes for proposals, flows (with rev), and logs. Approve deploys via
|
|
374
|
+
FlowAdapter with rev checks; concurrent approve guarded.
|
|
375
|
+
- Trusted write helper for `.ai-access` (call-site file-write suppressions
|
|
376
|
+
removed; sole trust-boundary annotations live in the helper): directory mode
|
|
377
|
+
`0o700`, file mode `0o600`, and size caps on proposal JSON and audit appends
|
|
378
|
+
(`413` when exceeded).
|
|
379
|
+
- `/mcp` admin UI for access links (TTL, show-once token, revoke), proposals
|
|
380
|
+
(summary, approve with deploy type, reject, rev-conflict messages), audit log,
|
|
381
|
+
and status. Read-only for `read` users; mutating controls for `*` only.
|
|
382
|
+
Optional `deploymentType` on approve; status exposes link/deploy defaults.
|
|
383
|
+
- OpenAPI 3.1 spec for `/mcp/api/v1` (status, links, flows, proposals, logs,
|
|
384
|
+
auth, 413 limits) served at `GET /mcp/api/v1/openapi.yaml`; README install,
|
|
385
|
+
config, `/mcp` usage, and AI quickstart.
|
|
386
|
+
|
|
387
|
+
[0.1.1]: https://github.com/com55/node-red-ai-access/compare/v0.1.0...v0.1.1
|
|
388
|
+
[0.1.0]: https://github.com/com55/node-red-ai-access/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) ziv-daniel
|
|
4
|
+
Copyright (c) 2026 com55
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|