diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 7d8dd09..a5e1f30 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "neptune-dxp", "description": "MCP server bridging Claude Code to the Neptune DXP API — tables, scripts, and apps.", - "version": "0.0.5", + "version": "0.0.6", "author": { "name": "Neptune Software" }, diff --git a/skills/manage-webapps/SKILL.md b/skills/manage-webapps/SKILL.md index c352737..4bfb98d 100644 --- a/skills/manage-webapps/SKILL.md +++ b/skills/manage-webapps/SKILL.md @@ -1,53 +1,144 @@ --- name: manage-webapps -description: Create, update, inspect, or delete Neptune DXP Web Apps (code-first apps served as static files, typically a compiled React or Vue build) via the MCP tools `list_webapps`, `get_webapp`, `save_webapp`, `delete_webapp`. Use when the user wants a code-first web app rather than a drag-and-drop App Designer app. Trigger phrases include "create a web app", "new react app", "new vue app", "list web apps", "update the webapp", "delete the web app". +description: Create, update, inspect, or delete Neptune DXP Web Apps — code-first apps served as static files at /webapp/ — via the MCP tools `list_webapps`, `get_webapp`, `save_webapp`, `delete_webapp`. Use when the user wants a code-first web app (React/Vue/vanilla SPA, dashboard, landing page) rather than a drag-and-drop App Designer app, or wants to change its files, make it public, or remove it. Trigger phrases include "create a web app", "new react app", "new vue app", "build me a dashboard web app", "put these files in the web app", "list web apps", "update the webapp", "delete the web app". Read this BEFORE composing any `save_webapp` payload — how you deliver the files decides whether the app renders or serves a blank page. --- # Managing Neptune DXP Web Apps via MCP -A Web App is a **code-first** app that Planet 9 serves as **plain static files** from a shared route, `/webapp/[/]`. At runtime it's just a static file host — no server-side framework runtime, no SSR, no per-app server (every web app shares the same route). The files you upload are typically a compiled **React** or **Vue** build, but the framework only matters when you build it (off-platform); what you upload and what gets served is framework-agnostic HTML/JS/CSS. It's the code-first alternative to a drag-and-drop App Designer (UI5) app. Stored in the `webapp` table; the MCP tools are the same records the Cockpit's Web App editor uses. See `dxp-overview` for the App vs Web App distinction. +A Web App is a **static file host**. Neptune DXP - Open Edition serves exactly the bytes you store: `runtime.page` at `/webapp/`, and each `runtime.assets[]` entry at `/webapp/`. Nothing is compiled, bundled or rendered server-side — `type: "react" | "vue"` is a label that changes nothing about hosting. Anything a browser can run from plain HTML/CSS/JS works; anything needing a build (JSX, TypeScript, Vue SFCs, npm imports) must be built **before** it is stored. `save_webapp` writes the same records the Cockpit's **Web App Manager** edits, and content is served immediately — there is no activate step. + +## The decision that determines success: how the bytes get in + +`runtime.page` is stored and served as **plain text**. Each asset's `source` is decoded as **base64 at serve time** — whatever you put there, valid or not. Nothing validates it on the way in, and `get_webapp` never gives it back, so a mistake is invisible until a browser loads the page. + +A base64 string missing (or gaining) one character decodes correctly up to that point and **as garbage from there to the end of the file** — the save succeeds, the app shows a blank page, and the console reports `Unterminated string constant` or `Unexpected token`. A `source` that isn't base64 at all is stored verbatim and served as a handful of meaningless bytes. + +Choose the path by **who produces the bytes**: + +| You are… | Do this | Never do this | +|---|---|---| +| A model composing the app in the conversation (no shell) | **Single-file app**: all HTML, `
", + "assets": [] + } +}}) +``` + +- Plain HTML/CSS/JS renders as-is. For React or Vue without a build, load the UMD bundles from a CDN and use `React.createElement` / the Vue options API — or add Babel standalone and write JSX in an inline `