@nodegx/mcp 0.0.0-stage → 0.1.0
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/LICENSE +674 -0
- package/README.md +301 -2
- package/bin/noodl-mcp.js +2 -0
- package/dist/cloud-bundle.cjs +117812 -0
- package/dist/kit-extract.cjs +96049 -0
- package/dist/kit-extract.inputs.json +488 -0
- package/dist/node-catalog.json +64846 -0
- package/dist/noodl-mcp.cjs +250972 -0
- package/package.json +45 -4
- package/src/index.ts +14 -0
package/README.md
CHANGED
|
@@ -1,3 +1,302 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nodegx/mcp — NodeGX MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes
|
|
4
|
+
NodeGX (OpenNoodl) **v2 project directories** to external AI agents: open,
|
|
5
|
+
inspect, author and validate projects programmatically — no editor process, no
|
|
6
|
+
whole-project context.
|
|
7
|
+
|
|
8
|
+
The design goal (Phase 13, Gate G1): *an agent can author a correct new page
|
|
9
|
+
having read only that page's parent, the relevant slice of the node catalog,
|
|
10
|
+
and the validator's feedback — never the entire project.*
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
The simplest start is `npm create nodegx my-apps`. It writes one file, `my-apps/.mcp.json`. That file tells Claude Code
|
|
15
|
+
to start this server when you run `claude` in the folder.
|
|
16
|
+
|
|
17
|
+
Any other MCP client can start the server itself over stdio:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx -y @nodegx/mcp --allow-writes
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
With no project folder the server starts in bootstrap mode (below). Put a project folder before `--allow-writes` to
|
|
24
|
+
serve that project. The rest of this README runs the server from a checkout of the NodeGX repository, where the same
|
|
25
|
+
server is `node <path-to>/noodl-mcp.cjs`.
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
**The easiest way to get this right is not to type it.** NodeGX's
|
|
30
|
+
**Settings → Editor → Connect an AI agent** has a Copy button that emits the command below with
|
|
31
|
+
your installation's path and your open project's path already filled in, and a per-project server
|
|
32
|
+
name (MCP-001). Everything here is the same thing, written out.
|
|
33
|
+
|
|
34
|
+
There is no URL and nothing to start: this is a **stdio** server that your MCP client spawns
|
|
35
|
+
itself. Point it at a project directory and it serves that project.
|
|
36
|
+
|
|
37
|
+
**With no project directory it still starts** (BST-001), in *bootstrap mode*: `list_projects`,
|
|
38
|
+
`create_project`, `list_examples`, `get_example` and nothing else. That is the mode for a machine
|
|
39
|
+
that has no projects yet — the agent finds what is already there, or scopes one with you and
|
|
40
|
+
creates it, and you then register a server against that directory. See
|
|
41
|
+
[Bootstrap mode](#bootstrap-mode-no-project-yet) below.
|
|
42
|
+
|
|
43
|
+
### Client configuration
|
|
44
|
+
|
|
45
|
+
Claude Code:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
claude mcp add --scope user nodegx-<project-slug> -- node <path-to>/noodl-mcp.cjs <project-dir> --allow-writes
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Two details that are not decoration:
|
|
52
|
+
|
|
53
|
+
- **`nodegx-<project-slug>`, not `nodegx`.** The server is bound to the project path in argv at
|
|
54
|
+
spawn time and never rebinds, so one registration is one project. A fixed name means the second
|
|
55
|
+
project you add silently replaces the first. The editor slugs the project **directory basename**
|
|
56
|
+
for this.
|
|
57
|
+
- **`--scope user`.** `claude mcp add` defaults to `local`, which ties the registration to the
|
|
58
|
+
directory you happened to run it in — from anywhere else it looks as though it vanished.
|
|
59
|
+
|
|
60
|
+
Generic MCP host config, for clients that are not Claude Code:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"mcpServers": {
|
|
65
|
+
"nodegx-<project-slug>": {
|
|
66
|
+
"command": "node",
|
|
67
|
+
"args": ["<path-to>/noodl-mcp.cjs", "<project-dir>", "--allow-writes"]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Drop `--allow-writes` for a read-only server.
|
|
74
|
+
|
|
75
|
+
### Bootstrap mode (no project yet)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
claude mcp add --scope user nodegx-bootstrap -- node <path-to>/noodl-mcp.cjs --allow-writes
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Omit the project directory and the server starts anyway, advertising four tools:
|
|
82
|
+
|
|
83
|
+
| Tool | |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `list_projects` | The NodeGX projects this machine has opened, read from the launcher's own recent-projects list and never written to. **Call this first** when the user talks about their app as something that already exists |
|
|
86
|
+
| `create_project` | Scope an app in conversation, then write the project, its `docs/` and a build plan that is returned but not run |
|
|
87
|
+
| `list_examples`, `get_example` | What a NodeGX graph actually looks like, without needing a project to read |
|
|
88
|
+
|
|
89
|
+
Everything else — reading components, authoring, validation, rendering, the backend — is **absent
|
|
90
|
+
from `tools/list`**, not present-and-erroring, because a model calls what it is shown.
|
|
91
|
+
|
|
92
|
+
⚠️ **`--allow-writes` is required in this mode**, and the server refuses to start without it:
|
|
93
|
+
`create_project` is a write, and with no project there is nothing to read, so a read-only bootstrap
|
|
94
|
+
server would start, appear connected, and be unable to do the one thing it exists for.
|
|
95
|
+
|
|
96
|
+
⚠️ **Creating a project does not repoint this server.** The project is written to disk and the
|
|
97
|
+
server stays unbound; register a second server against the new directory to author in it. (BST-002
|
|
98
|
+
is the task that makes the live server bind itself.)
|
|
99
|
+
|
|
100
|
+
### Where the bundle is
|
|
101
|
+
|
|
102
|
+
Shipped inside the app by MCP-002:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
macOS /Applications/NodeGX.app/Contents/Resources/noodl-mcp/noodl-mcp.cjs
|
|
106
|
+
Windows %LOCALAPPDATA%\Programs\NodeGX\resources\noodl-mcp\noodl-mcp.cjs
|
|
107
|
+
Linux /opt/NodeGX/resources/noodl-mcp/noodl-mcp.cjs (deb)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
A Linux AppImage mounts itself at a different path on every launch, so there is no fixed answer
|
|
111
|
+
there — take the path from the settings section, which asks the running app.
|
|
112
|
+
|
|
113
|
+
### Contributors, working in a checkout
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# build both MCP bundles once
|
|
117
|
+
npm run build:sidecars
|
|
118
|
+
|
|
119
|
+
# read-only (default)
|
|
120
|
+
node packages/noodl-mcp/dist/noodl-mcp.cjs /path/to/project
|
|
121
|
+
|
|
122
|
+
# with authoring tools
|
|
123
|
+
node packages/noodl-mcp/dist/noodl-mcp.cjs /path/to/project --allow-writes
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The `dist/noodl-mcp.cjs` bundle is self-contained (format engines and the
|
|
127
|
+
enriched node catalog are compiled in) — only Node ≥ 18 is required.
|
|
128
|
+
|
|
129
|
+
### The project has to be v2
|
|
130
|
+
|
|
131
|
+
The target must be a **v2 project directory** (`nodegx.project.json` /
|
|
132
|
+
`components/_registry.json`). Legacy monolithic `project.json` projects are
|
|
133
|
+
detected and refused with a pointer to the editor's migration — and the settings
|
|
134
|
+
section performs the same check before it offers you a command, so a legacy
|
|
135
|
+
project is a message on screen rather than a spawn failure in your terminal.
|
|
136
|
+
|
|
137
|
+
## Component identity
|
|
138
|
+
|
|
139
|
+
| Form | Example | Where |
|
|
140
|
+
|------|---------|-------|
|
|
141
|
+
| `path` | `Pages/Home` | canonical tool identifier, registry keys, directories |
|
|
142
|
+
| `legacyName` | `/Pages/Home` | `component.json`'s `path` field; **the `type` of a node that instantiates the component** |
|
|
143
|
+
|
|
144
|
+
Every tool accepts either form; responses carry both.
|
|
145
|
+
|
|
146
|
+
## Tools
|
|
147
|
+
|
|
148
|
+
### Read (always available)
|
|
149
|
+
|
|
150
|
+
| Tool | Purpose |
|
|
151
|
+
|------|---------|
|
|
152
|
+
| `get_project_info` | Metadata, settings, root component, routes, style names, stats, server mode. Call first. |
|
|
153
|
+
| `list_components` | Registry rows (path, legacyName, type, counts). Filters: `type`, `path_prefix`. |
|
|
154
|
+
| `get_component` | Full graph of one component + `revision` token. `include_usages` lists instantiations. |
|
|
155
|
+
| `search_project` | Locate nodes by `node_type` and/or `text` across components without reading graphs. |
|
|
156
|
+
| `explain_component` | Structured description: visual tree, logic nodes, data flow, component refs. |
|
|
157
|
+
| `list_node_types` | Compact catalog rows with one-line summaries. Filters: `category`, `query`, `visual_only`. |
|
|
158
|
+
| `get_node_type` | Full enriched entries (ports + semantics, whenToUse, runtime behavior, example ids) for up to 8 named types. |
|
|
159
|
+
| `list_examples` / `get_example` | Browse and fetch validated example graph fragments. |
|
|
160
|
+
| `validate_component` / `validate_project` | SUB-006 semantic diagnostics on demand (`strict` promotes unknown types to errors). |
|
|
161
|
+
| `drive_page` | P109 ISL-021 (deferred, `explore` group) — render the project from disk in a headless browser and run a step list on it (each call a fresh browser): `tap`, `type` (appends; `replace` types over; a field's placeholder is a target), `key`, `wait_for`, `read` (text, an app Variable, localStorage, the URL, an expression; with `expect`), `navigate`, `viewport`, `screenshot`, `seed_storage` (pairs, or an `edit` of a saved value in place; written again at the next load before the app runs, so the app's own save cannot overwrite it), `clear_storage`, `stub_route` (answers a path with canned JSON or text; stubs at the head of the list meet the page's first load; `read: {stub}` gives hits and the last body). One row per step and a PASSED/FAILED headline; a tap is a real pointer press, refused when something covers the target (naming what a finger would hit); the first failing step stops the run. Reports whether the page was visible and had WebGL2. Runner: `scripts/devtools/drive-steps.js`, beside the render harness (`NODEGX_DRIVE_CLI` overrides). |
|
|
162
|
+
| `list_projects` | BST-006 — the NodeGX projects this machine has opened, from the launcher's own list (read, never written). Legacy ones are marked rather than dropped; directories that no longer exist are counted, not listed. Resident in [bootstrap mode](#bootstrap-mode-no-project-yet); deferred to the `project` group when a project is bound, because a bound server cannot be repointed. |
|
|
163
|
+
|
|
164
|
+
### Backend permissions — read (always available)
|
|
165
|
+
|
|
166
|
+
Unlike every other tool, these talk to a **running** `nodegx-backend` over HTTP
|
|
167
|
+
(discovered under `~/.noodl/backends/`, admin credential read from the backend's
|
|
168
|
+
own `secrets.json`). See [BAK-003](../../docs/runtime/BACKEND-ACCESS-CONTROL.md).
|
|
169
|
+
|
|
170
|
+
| Tool | Purpose |
|
|
171
|
+
|------|---------|
|
|
172
|
+
| `list_backends` | Configured local backends: id, name, port, reachable, whether the admin credential is present. |
|
|
173
|
+
| `list_backend_processes` | What is actually running, from the durable spawn records (AAQ-011/F13): pid, port, which spawner owns it (this server or the editor), and whether that owner is still alive. `reap: true` also stops the ones no live owner claims. |
|
|
174
|
+
| `get_backend_permissions` | Full access-control config: CLPs, creator-owns, function/file rules, whether enforcement is active. |
|
|
175
|
+
| `list_backend_roles` / `list_backend_api_keys` | Roles + members; keys (names, scopes, status — never secrets). |
|
|
176
|
+
| `check_backend_access` | Server-side dry run: does a hypothetical principal get `find`/`get`/… on a collection, or call a function? Names the deciding rule. Verify a change took effect without a live session. |
|
|
177
|
+
| `get_backend_ops_config` | Operational config (BAK-009): rate-limit policy per route class, trusted proxies, log level, CORS origins, audit retention, metrics. Read this when a call comes back 429 — it says which class the route is in and what its budget is. |
|
|
178
|
+
| `query_backend_audit` | The privileged-action trail: permission/role/key edits, schema changes, backups and restores, config edits, admin logins and failures — with actor, origin, outcome, and the request id that ties each to the access log. |
|
|
179
|
+
|
|
180
|
+
### Author + backend permissions — write (only with `--allow-writes`)
|
|
181
|
+
|
|
182
|
+
| Tool | Purpose |
|
|
183
|
+
|------|---------|
|
|
184
|
+
| `create_project` | AIX-012 — a whole new project at a directory you name, from a scope you agreed with your user: the v2 skeleton, `docs/` (BRIEF, ARCHITECTURE, CONVENTIONS, and the initial-scope decision record) and a build plan that is **returned but not run**. Takes no store — the directory it writes is by definition not the one this server is pointed at — which is what makes it the point of [bootstrap mode](#bootstrap-mode-no-project-yet). |
|
|
185
|
+
| `create_component` | New component from nodes/connections. Validated before writing. |
|
|
186
|
+
| `update_component` | One component change: full `set` replacement **or** a batch of `operations` (`add_node`, `update_node`, `remove_node`, `add_connection`, `remove_connection`, `set_visual_roots`, `set_ports`, `set_component_info`). Supports `if_revision`. |
|
|
187
|
+
| `delete_component` | Refuses while referenced (lists usages) unless `force`. |
|
|
188
|
+
| `set_project_settings` | P109 ISL-016 (deferred, `project` group) — the app's `name`, its project settings (`htmlTitle`, `navigationPathType`, `bodyScroll`, `headCode`, the two Repeater switches) and its `home`: the component the project starts from, resolved as the editor's "Make home" resolves it (its first top-level visual node that is not a Page). **Overwrites** and answers `was` → `now` per field; an unknown setting or a component with no visual root is refused by name and nothing is written; a request that changes nothing writes nothing. `get_project_info.rootComponent` reads the home from `rootNodeId`, as the editor does. |
|
|
189
|
+
| `create_plan` / `stage_plan_operation` / `apply_plan` / `discard_plan` | AIX-011 project-scope staging: declare a multi-component change as an ordered plan (creates before the updates that use them), stage each operation's graph **in memory** — validated against the project *plus* the plan's other staged operations, so an update may instantiate a sibling create — then `apply_plan` writes the complete set at once. Skips are the explicit partial apply and must be dependency-closed; discarding (or never applying) leaves the project byte-identical. Doc operations are plannable but refuse to apply until the project-docs write path (AIX-009) lands. Plans live in the server process. |
|
|
190
|
+
| `provision_backend` / `stop_backend` | AAQ-011/F13 — **create, start and bind a backend for this project**, pre-seeding the collections you name, so an external agent can build a full-stack app end to end. Reuses this project's backend of the same name rather than making a second, adopts it if already running, and refuses (changing nothing) if the project already points elsewhere. ⚠️ The one effect here that is not undoable, which is why it is a tool of its own and not a plan operation: `apply_plan` promises all-or-nothing, and a running process and a created database are neither. Lifecycle: the backend carries `--parent-pid`, the owner heartbeats a durable record, and a `noodl-mcp` that is killed outright has its backends reaped by the next one to start. |
|
|
191
|
+
| `set_collection_permissions` / `reset_collection_permissions` | Lock/open a collection (per-op rules + creator-owns), or revert to defaults. Rejected with the reason on a malformed rule or a system collection. |
|
|
192
|
+
| `create_backend_role` / `delete_backend_role` / `assign_role_user` | Manage roles and membership (`assign_role_user` with `remove: true` revokes). |
|
|
193
|
+
| `create_backend_api_key` / `revoke_backend_api_key` | Issue a scoped key (secret returned once) or revoke one. |
|
|
194
|
+
|
|
195
|
+
**Write policy.** Every write is validated first — JSON-schema structure, then
|
|
196
|
+
the semantic validator with unknown node types promoted to errors (pass
|
|
197
|
+
`allow_unknown_types: true` for module-provided nodes). New errors reject the
|
|
198
|
+
write with diagnostics that carry a `suggestion` and valid `alternatives`;
|
|
199
|
+
nothing is written on rejection. Warnings are written and reported.
|
|
200
|
+
Pre-existing errors in a component never block editing it ("don't make it
|
|
201
|
+
worse" gate).
|
|
202
|
+
|
|
203
|
+
**Concurrency.** Optimistic: pass the `revision` from `get_component` as
|
|
204
|
+
`if_revision` to fail cleanly on drift; the server additionally re-stats files
|
|
205
|
+
before writing and refuses if anything changed since it read them. Writes are
|
|
206
|
+
atomic (temp file + rename). This is conflict *detection*, not locking — keep
|
|
207
|
+
projects under git and avoid pointing an editor and a writing agent at the
|
|
208
|
+
same component simultaneously.
|
|
209
|
+
|
|
210
|
+
## Example session
|
|
211
|
+
|
|
212
|
+
Goal: *add a Settings page with a back button, linked from Home.*
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
→ get_project_info {}
|
|
216
|
+
← { name: "Demo App", rootComponent: { path: "App" }, mode: "read-write", … }
|
|
217
|
+
|
|
218
|
+
→ list_components { "type": "page" }
|
|
219
|
+
← { components: [ { path: "Pages/Home", legacyName: "/Pages/Home", … } ] }
|
|
220
|
+
|
|
221
|
+
→ get_component { "path": "Pages/Home" }
|
|
222
|
+
← { revision: "a4a9260622c4", nodes: [ …Page → Group → Text/Button…,
|
|
223
|
+
{ id: "nav", type: "RouterNavigate" } ], connections: [ btn.onClick → nav.navigate ] }
|
|
224
|
+
|
|
225
|
+
→ get_node_type { "type_names": ["Page", "RouterNavigate", "net.noodl.controls.button"] }
|
|
226
|
+
← ports with semantics, whenToUse, example ids
|
|
227
|
+
|
|
228
|
+
→ create_component
|
|
229
|
+
{ "path": "Pages/Settings",
|
|
230
|
+
"nodes": [
|
|
231
|
+
{ "id": "page", "type": "Page", "parameters": { "title": "Settings" } },
|
|
232
|
+
{ "id": "layout", "type": "Group", "parent": "page" },
|
|
233
|
+
{ "id": "title", "type": "Text", "parent": "layout", "parameters": { "text": "Settings" } },
|
|
234
|
+
{ "id": "back", "type": "net.noodl.controls.button", "parent": "layout", "parameters": { "label": "Back" } },
|
|
235
|
+
{ "id": "nav", "type": "RouterNavigate", "parameters": { "target": "/Pages/Home" } } ],
|
|
236
|
+
"connections": [ { "fromId": "back", "fromProperty": "onClick", "toId": "nav", "toProperty": "navigate" } ] }
|
|
237
|
+
← { created: "Pages/Settings", legacyName: "/Pages/Settings", revision: "…",
|
|
238
|
+
validation: { summary: { errors: 0, warnings: 0 } } }
|
|
239
|
+
|
|
240
|
+
→ update_component (wire Home → Settings; one reviewable batch)
|
|
241
|
+
{ "path": "Pages/Home", "if_revision": "a4a9260622c4",
|
|
242
|
+
"operations": [
|
|
243
|
+
{ "op": "add_node", "node": { "id": "settingsBtn", "type": "net.noodl.controls.button",
|
|
244
|
+
"parent": "layout", "parameters": { "label": "Settings" } } },
|
|
245
|
+
{ "op": "add_node", "node": { "id": "navSettings", "type": "RouterNavigate",
|
|
246
|
+
"parameters": { "target": "/Pages/Settings" } } },
|
|
247
|
+
{ "op": "add_connection", "connection": { "fromId": "settingsBtn",
|
|
248
|
+
"fromProperty": "onClick", "toId": "navSettings", "toProperty": "navigate" } } ] }
|
|
249
|
+
← { updated: "Pages/Home", revision: "…", applied: [ … ], validation: clean }
|
|
250
|
+
|
|
251
|
+
→ validate_project { "strict": true }
|
|
252
|
+
← { summary: { errors: 0, … } }
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
A rejected write teaches instead of failing opaquely:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
→ create_component { "path": "X", "nodes": [ { "id": "g", "type": "Grup" } ] }
|
|
259
|
+
← isError: { error: { code: "validation-failed",
|
|
260
|
+
details: { readable: [ "ERROR [unknown-node-type] … did you mean `Group`?" ],
|
|
261
|
+
newErrors: [ { code: "unknown-node-type", suggestion: "Group", … } ] } } }
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Architecture
|
|
265
|
+
|
|
266
|
+
- `src/project/ProjectStore.ts` — v2 directory access, registry maintenance,
|
|
267
|
+
revisions, drift detection, atomic writes.
|
|
268
|
+
- `src/graph.ts` — hierarchy reconciliation (`parent`/`children` double
|
|
269
|
+
bookkeeping) and the batched update operations.
|
|
270
|
+
- `src/validate.ts` — write gate: schemas → semantic validator → baseline diff.
|
|
271
|
+
- `src/catalog.ts` — projections of the enriched node catalog.
|
|
272
|
+
- `src/describe.ts` — `explain_component`.
|
|
273
|
+
- `src/tools/*.ts` — MCP tool registrations; `src/server.ts`, `src/cli.ts`.
|
|
274
|
+
|
|
275
|
+
Format logic is **not duplicated**: the io engines, schemas and validator are
|
|
276
|
+
imported from `noodl-editor`'s pure modules (`src/editor/src/{io,schemas,validation}`)
|
|
277
|
+
and bundled by esbuild — the same single source of truth the editor uses (see
|
|
278
|
+
`docs/DESIGN.md` for the full decision record and tool-surface rationale).
|
|
279
|
+
|
|
280
|
+
## Development
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
npm test --workspace @nodegx/mcp # jest (29 tests, incl. end-to-end MCP client)
|
|
284
|
+
npm run typecheck --workspace @nodegx/mcp
|
|
285
|
+
npm run build --workspace @nodegx/mcp # esbuild → dist/noodl-mcp.cjs
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Current limitations
|
|
289
|
+
|
|
290
|
+
- File-based only; no live sync with a running editor (a bridge is future work —
|
|
291
|
+
the editor holds its own in-memory state and can overwrite MCP writes on save).
|
|
292
|
+
- No project-level settings/styles/routes writes, asset upload, or model files in v1.
|
|
293
|
+
- Read-side supports v2 directories only; migrate legacy projects with the editor first.
|
|
294
|
+
- `get_node_type` with several types can exceed an MCP host's tool-result cap
|
|
295
|
+
(~126 KB for 7 types in the SUB-010 demo, and the host's "overflow saved to a
|
|
296
|
+
file" fallback is useless to a filesystem-less agent). Ask for 1–2 types per
|
|
297
|
+
call until a compact/ports-only projection is added (SUB-010 friction report).
|
|
298
|
+
|
|
299
|
+
## Licence
|
|
300
|
+
|
|
301
|
+
The server is licensed GPL-3.0-only because it bundles code from the NodeGX editor. The source is in
|
|
302
|
+
[The-Low-Code-Foundation/NodeGX](https://github.com/The-Low-Code-Foundation/NodeGX), under `packages/noodl-mcp`.
|
package/bin/noodl-mcp.js
ADDED