@plitzi/sdk-mcp 0.38.3 → 0.38.5

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +540 -0
  2. package/README.md +38 -12
  3. package/dist/createServer.d.ts +1 -1
  4. package/dist/createServer.js +1 -1
  5. package/dist/index.d.ts +3 -4
  6. package/dist/index.js +1 -4
  7. package/dist/modules/mcp/apps/render/styles.d.ts +4 -8
  8. package/dist/modules/mcp/apps/render/styles.js +16 -47
  9. package/dist/modules/mcp/apps/render/view/index.tsx +1 -1
  10. package/dist/modules/mcp/catalogs/index.js +2 -2
  11. package/dist/modules/mcp/catalogs/pluginCatalog.js +2 -2
  12. package/dist/modules/mcp/e2e/mcpEndpoint.d.ts +2 -1
  13. package/dist/modules/mcp/handler.d.ts +2 -2
  14. package/dist/modules/mcp/helpers/callToolResult.d.ts +2 -0
  15. package/dist/modules/mcp/helpers/callToolResult.js +4 -0
  16. package/dist/modules/mcp/helpers/guide.js +68 -14
  17. package/dist/modules/mcp/helpers/index.d.ts +1 -1
  18. package/dist/modules/mcp/helpers/space.d.ts +4 -1
  19. package/dist/modules/mcp/helpers/space.js +1 -0
  20. package/dist/modules/mcp/helpers/uris.d.ts +2 -0
  21. package/dist/modules/mcp/helpers/uris.js +3 -1
  22. package/dist/modules/mcp/index.d.ts +1 -1
  23. package/dist/modules/mcp/localScreenshotClient.d.ts +3 -0
  24. package/dist/modules/mcp/localScreenshotClient.js +2 -0
  25. package/dist/modules/mcp/resources/connectors.d.ts +1 -1
  26. package/dist/modules/mcp/resources/core.js +2 -2
  27. package/dist/modules/mcp/resources/data.d.ts +8 -0
  28. package/dist/modules/mcp/resources/data.js +46 -0
  29. package/dist/modules/mcp/resources/explain.js +1 -1
  30. package/dist/modules/mcp/resources/primer.js +3 -3
  31. package/dist/modules/mcp/resources/register.js +12 -2
  32. package/dist/modules/mcp/resources/renderGuide.js +6 -6
  33. package/dist/modules/mcp/resources/router.js +3 -1
  34. package/dist/modules/mcp/resources/schema.js +1 -1
  35. package/dist/modules/mcp/server.d.ts +3 -3
  36. package/dist/modules/mcp/server.js +12 -8
  37. package/dist/modules/mcp/tools/apply/dispatch.js +9 -1
  38. package/dist/modules/mcp/tools/apply/index.d.ts +12 -2
  39. package/dist/modules/mcp/tools/apply/index.js +27 -1
  40. package/dist/modules/mcp/tools/index.d.ts +2 -2
  41. package/dist/modules/mcp/tools/index.js +2 -2
  42. package/dist/modules/mcp/tools/operations/connectors/index.d.ts +2 -2
  43. package/dist/modules/mcp/tools/operations/connectors/manifest.d.ts +1 -1
  44. package/dist/modules/mcp/tools/operations/connectors/patchConnector.d.ts +1 -1
  45. package/dist/modules/mcp/tools/operations/connectors/upsertConnector.d.ts +1 -1
  46. package/dist/modules/mcp/tools/operations/data/data.test.d.ts +1 -0
  47. package/dist/modules/mcp/tools/operations/data/index.d.ts +32 -0
  48. package/dist/modules/mcp/tools/operations/data/index.js +54 -0
  49. package/dist/modules/mcp/tools/operations/index.d.ts +30 -4
  50. package/dist/modules/mcp/tools/operations/index.js +4 -1
  51. package/dist/modules/mcp/tools/operations/schema/elements/patchElement.d.ts +1 -0
  52. package/dist/modules/mcp/tools/operations/schema/elements/patchElement.js +5 -2
  53. package/dist/modules/mcp/tools/operations/schema/elements/upsertElement.js +1 -0
  54. package/dist/modules/mcp/tools/operations/schema/interactions/patchInteractionNode.d.ts +1 -0
  55. package/dist/modules/mcp/tools/operations/schema/interactions/upsertInteractionFlow.d.ts +1 -0
  56. package/dist/modules/mcp/tools/operations/schema/operations.d.ts +3 -0
  57. package/dist/modules/mcp/tools/operations/schema/shared.d.ts +5 -0
  58. package/dist/modules/mcp/tools/operations/schema/shared.js +13 -7
  59. package/dist/modules/mcp/tools/operations/schema/translator.js +1 -1
  60. package/dist/modules/mcp/tools/operations/schema/write.js +3 -2
  61. package/dist/modules/mcp/tools/operations/schemaIds.js +3 -3
  62. package/dist/modules/mcp/tools/operations/style/write.js +1 -1
  63. package/dist/modules/mcp/tools/preview.d.ts +3 -0
  64. package/dist/modules/mcp/tools/render.d.ts +3 -0
  65. package/dist/modules/mcp/tools/screenshot.d.ts +12 -2
  66. package/dist/modules/mcp/tools/shared/newSuggestions.d.ts +2 -2
  67. package/dist/modules/mcp/tools/shared/newSuggestions.js +10 -5
  68. package/dist/modules/mcp/tools/shared/newSuggestions.test.d.ts +1 -0
  69. package/dist/modules/mcp/tools/shared/tool.d.ts +7 -6
  70. package/dist/modules/mcp/tools/shared/validator/index.js +2 -2
  71. package/dist/modules/mcp/tools/tryFunction.js +1 -1
  72. package/dist/modules/mcp/tools/validate.d.ts +12 -2
  73. package/dist/modules/mcp/types/appTypes.d.ts +2 -2
  74. package/dist/modules/mcp/types/toolTypes.d.ts +5 -1
  75. package/package.json +7 -9
  76. package/dist/modules/ai/AIEngine.d.ts +0 -20
  77. package/dist/modules/ai/AIEngine.js +0 -120
  78. package/dist/modules/ai/index.d.ts +0 -4
  79. package/dist/modules/ai/toolkit.d.ts +0 -17
  80. package/dist/modules/ai/toolkit.js +0 -59
  81. package/dist/modules/mcp/helpers/agentPrompt.d.ts +0 -4
  82. package/dist/modules/mcp/helpers/agentPrompt.js +0 -123
  83. /package/dist/modules/{ai/toolkit.test.d.ts → mcp/helpers/callToolResult.test.d.ts} +0 -0
package/README.md CHANGED
@@ -5,10 +5,10 @@ edit a space, the tool engine behind it, the widget renderer, and the draft-prev
5
5
 
6
6
  Connecting an agent and what it can do once connected: [AI agents and the MCP server](../../docs/en/mcp.md).
7
7
 
8
- It is a sibling of [`@plitzi/sdk-server`](../server/README.md), not a layer on top of it. That package serves
9
- pages; this one serves agents. They share only the HTTP kernel, which this package imports from
10
- `@plitzi/sdk-server/kernel` — a narrow entry that carries the dispatcher and the transports and nothing else, so
11
- a page-only deployment installs none of this and an MCP deployment loads no page renderer.
8
+ It depends on [`@plitzi/sdk-server`](../server/README.md), never the other way round: that package serves pages,
9
+ this one serves agents. It imports only that package's narrow entries — the HTTP kernel (`/kernel`, the dispatcher
10
+ and the transports), OAuth (`/oauth`) and, for draft preview alone, the renderer (`/ssr`) — so a page-only
11
+ deployment installs none of this and a dedicated MCP server loads no page renderer.
12
12
 
13
13
  ## Installation
14
14
 
@@ -75,16 +75,42 @@ The server is stateless: it resolves the space per request and reads and writes
75
75
  | `getOfflineData(spaceId, env, rev)` | for preview | Read side of draft-preview. Only the preview endpoint calls it. |
76
76
  | `getComponentCatalog(spaceId, env)` | no | The element types the space's plugins add, for the catalog resources. |
77
77
  | `getConnectors` / `saveConnector` / `deleteConnector` | for connectors | The space's connector manifests, read and written by the connector operations. |
78
- | `getActions` / `getActionTasks` / `saveAction` / `deleteAction` | for actions | The space's server actions, and the catalog of tasks a step may run — the deployment's and the space's own functions, each with its `origin`. |
78
+ | `getActions` / `getActionTasks` / `saveAction` / `deleteAction` | for actions | The space's server actions, and the catalog of tasks a step may run — the deployment's, the space's own functions and its plugins' server halves, each with its `origin` (`deployment`, `space` or `plugin`). |
79
79
  | `getFunctions` / `saveFunctions` / `tryFunction` | for functions | The space's functions: read for `plitzi://functions/{env}`, saved (built and checked, refused from an older copy) by `upsertFunctionFile` / `deleteFunctionFile`, and run by `plitzi_try_function`. |
80
+ | `getData` / `saveData` | for data | The space's data — JSON its server providers read as `/data/<file>`: read for `plitzi://data/{env}`, saved whole (each file checked, refused from an older copy) by `upsertDataFile` / `deleteDataFile`. |
80
81
 
81
82
  Schema and style are read as **separate documents** on purpose: `getOfflineData` is SSR-shaped and strips
82
83
  `style.platform`, which the style resources need.
83
84
 
85
+ `createJsonAdapters({ offlineData })` from `@plitzi/sdk-server` offers `getSchema` and `getStyle` over a
86
+ `{ schema, style }` JSON and — when given a path — `saveSchema` and `saveStyle`, each writing its document back into
87
+ the file, so an MCP can run over it with a `getGrant` of yours beside it. It comes from the package root, which loads
88
+ the page server too; a dedicated MCP that keeps its weight down writes those four itself, as
89
+ [self-hosting/06](../../examples/self-hosting/06-mcp-server) does.
90
+
84
91
  Without a `getGrant` that resolves, the server still answers its public surface — the handshake, the tool list,
85
92
  the guide, the CSS-property catalog — and asks for a space only when a tool or resource needs one. A grant that
86
93
  resolves with `canWrite: false` reads everything and is refused at every write tool.
87
94
 
95
+ ## Options
96
+
97
+ What MCP itself serves is `McpOptions` (`src/options.ts`), the **second** argument — to `createServer` here, and to
98
+ `mcpExtensions(options)` for a page server — never a section of the server config, which a page-only deployment
99
+ types its renderer with.
100
+
101
+ | Option | Default | Purpose |
102
+ |---|---|---|
103
+ | `path` | `/mcp` | Where MCP answers inside a server that also serves pages. A dedicated MCP server owns its whole origin and ignores it. |
104
+ | `renderStreaming` | `true` | Whether the `plitzi_render` view paints from the tool arguments while the host still streams them. `false` keeps the view blank until the finished widget arrives. |
105
+ | `proxy` | off | `McpProxyOptions`: with a `secret`, every external URL a render authored is rewritten to this server's signed endpoint (`path`, default `/__proxy`) and fetched here — the origins a widget needs cannot be declared ahead. `baseUrl`, `maxBytes` (8 MiB), `ttl` (7 days), `tools` (`['plitzi_render']`), `enabled`. |
106
+ | `previewClient` | off | `{ url, secret? }`: the SSR `/preview` endpoint, for an MCP server that runs apart from the renderer. Without it the preview tools report `PREVIEW_UNAVAILABLE`. |
107
+ | `screenshot` | off | `{ serviceUrl, renderBaseUrl }`: the browser service `plitzi_screenshot` renders through, and the SSR base it navigates to. Without it the tool is not registered. |
108
+ | `oauth` | off | OAuth 2.1 for remote connectors — see [OAuth](#oauth). |
109
+
110
+ ```ts
111
+ createServer({ adapters }, { screenshot: { serviceUrl, renderBaseUrl }, proxy: { secret } });
112
+ ```
113
+
88
114
  ## Tools
89
115
 
90
116
  | Tool | Access | What it does |
@@ -98,11 +124,14 @@ resolves with `canWrite: false` reads everything and is refused at every write t
98
124
  | `plitzi_render` | read | Render a self-contained UI widget, offline, with no space |
99
125
  | `plitzi_try_function` | write | Run one task of the space's functions against the draft, in the sandbox: its value, logs and error |
100
126
 
127
+ The server advertises each tool's access as its `readOnlyHint` annotation, so a host can run a read without asking
128
+ and ask before a write.
129
+
101
130
  Reads follow a filesystem model: list cheap, read one item in detail on demand. Agents are told never to
102
131
  hand-build a URI — every write and search response hands back the URI to use next.
103
132
 
104
133
  The tool functions are exported directly (`apply`, `search`, `read`, `validate`, `tools`), so a consumer can run
105
- them in-process and wrap them as its own agent's tools instead of speaking MCP over HTTP.
134
+ them in-process instead of speaking MCP over HTTP.
106
135
 
107
136
  ## Draft preview
108
137
 
@@ -118,16 +147,13 @@ engine's job, not the renderer's.
118
147
 
119
148
  ## OAuth
120
149
 
121
- Opt-in and inert unless configured. With `oauth` set, an unauthenticated JSON-RPC call gets a `401` plus a
150
+ Opt-in and inert unless configured. With `oauth` set (an [option](#options), not part of the server config), an unauthenticated JSON-RPC call gets a `401` plus a
122
151
  `WWW-Authenticate` challenge pointing at the discovery document — which is what makes a host such as Claude
123
152
  Desktop start the flow. Without it, the server stays anonymous, discovery answers `404`, and every caller reaches
124
153
  the public surface.
125
154
 
126
155
  ```ts
127
- createServer({
128
- adapters,
129
- oauth: { adapters: oauthAdapters, issuer, guest: { target: widgetsOnlyTarget } }
130
- });
156
+ createServer({ adapters }, { oauth: { adapters: oauthAdapters, issuer, guest: { target: widgetsOnlyTarget } } });
131
157
  ```
132
158
 
133
159
  Publish the connector URL **with a path** (`https://host/mcp`), not the bare origin.
@@ -177,7 +203,7 @@ MCP server, [self-hosting/07](../../examples/self-hosting/07-ssr-preview) is the
177
203
  | Import | Carries |
178
204
  |---|---|
179
205
  | `@plitzi/sdk-mcp/server` | The dedicated MCP server and its clients. What an MCP deployment wires. |
180
- | `@plitzi/sdk-mcp` | Everything above plus the pipeline stages, `createPreview`, the tool engine and the AI engine. |
206
+ | `@plitzi/sdk-mcp` | Everything above plus the pipeline stages, `createPreview` and the tool engine. |
181
207
 
182
208
  The split is about weight, not taste: ESM re-exports load eagerly, so the barrel pulls the draft-preview path and
183
209
  the renderer it reaches into. A `no-restricted-imports` rule and a test in `src/packageBoundary.test.ts` keep this
@@ -1,6 +1,6 @@
1
1
  import { McpOptions } from './options';
2
2
  import { SSRServer, SSRServerConfig } from '@plitzi/sdk-shared';
3
- /** The server this package makes: the lean mcp-ai pipeline over the bare context — no render template, caches
3
+ /** The server this package makes: the lean MCP pipeline over the bare context — no render template, caches
4
4
  * or plugin manager. `config` is the HTTP server's (port, TLS, health, logger, adapters); `options` is what MCP
5
5
  * itself serves, and lives here rather than in that config because the server does not need to know. Pair it with
6
6
  * the MCP adapters (getGrant + getSchema/getStyle/saveSchema/saveStyle) — the page adapters are not required, and
@@ -5,7 +5,7 @@ var noPlugins = {
5
5
  register: () => void 0,
6
6
  invalidate: () => Promise.resolve()
7
7
  };
8
- /** The server this package makes: the lean mcp-ai pipeline over the bare context — no render template, caches
8
+ /** The server this package makes: the lean MCP pipeline over the bare context — no render template, caches
9
9
  * or plugin manager. `config` is the HTTP server's (port, TLS, health, logger, adapters); `options` is what MCP
10
10
  * itself serves, and lives here rather than in that config because the server does not need to know. Pair it with
11
11
  * the MCP adapters (getGrant + getSchema/getStyle/saveSchema/saveStyle) — the page adapters are not required, and
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- /** Everything this package offers: the MCP server, the tool engine that backs it, the AI engine a consumer wraps
2
- * its own agent around, and the stages that mount all of it inside somebody else's page server.
1
+ /** Everything this package offers: the MCP server, the tool engine that backs it, and the stages that mount all of
2
+ * it inside somebody else's page server.
3
3
  *
4
4
  * A dedicated MCP deployment wants `@plitzi/sdk-mcp/server` instead — this barrel also pulls the draft-preview
5
5
  * endpoint, which reaches into the SSR renderer. */
@@ -10,8 +10,7 @@ export { createOAuthGuardStage, createOAuthStage } from '@plitzi/sdk-server/oaut
10
10
  export { previewStage } from './stages/preview';
11
11
  export { createWidgetProxyStage } from './stages/proxy';
12
12
  export { createPreview } from './preview/createPreview';
13
- export { AIEngine, toolResponseOk, toolResponseErr, zodToJsonSchema, getAllowedModes, bindTools, isToolActive, resolveToolHandler, isCallToolResult, toolResponseFromResult } from './modules/ai';
14
- export { createMcpServer, handleMcp, serveMcp, readMcpBody, buildAgentGuide, createHttpPreviewClient, createHttpScreenshotClient, createLocalScreenshotClient, resolveLocalBrowser, outlineOfSnapshot, outlineOfTree, unnamedControls } from './modules/mcp';
13
+ export { createMcpServer, handleMcp, serveMcp, readMcpBody, createHttpPreviewClient, createHttpScreenshotClient, createLocalScreenshotClient, resolveLocalBrowser, outlineOfSnapshot, outlineOfTree, unnamedControls } from './modules/mcp';
15
14
  export { apply, search, read, validate, applyShape, searchShape, readShape, validateShape, operation, tools } from './modules/mcp/tools';
16
15
  export type { McpServerContext, McpRequestOptions, PreviewClient, PreviewRequestBody, PreviewResult, HttpPreviewClientConfig, ScreenshotClient, ScreenshotImage, ScreenshotResult, AccessibilityNode, AccessibilityOutline, CaptureView, UnnamedControl, Viewport, HttpScreenshotClientConfig } from './modules/mcp';
17
16
  export type { ApplyInput, SearchInput, SearchResponse, SearchHit, ReadInput, ReadResponse, ReadHit, ValidateInput, WriteResponse, WriteElement, Persisters, Operation, ToolDef, ToolContext } from './modules/mcp/tools';
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { buildAgentGuide } from "./modules/mcp/helpers/agentPrompt.js";
2
1
  import { operation } from "./modules/mcp/tools/operations/index.js";
3
2
  import { apply, applyShape } from "./modules/mcp/tools/apply/index.js";
4
3
  import { read, readShape } from "./modules/mcp/tools/read.js";
@@ -6,7 +5,6 @@ import { outlineOfSnapshot, outlineOfTree, unnamedControls } from "./modules/mcp
6
5
  import { search, searchShape } from "./modules/mcp/tools/search.js";
7
6
  import { validate, validateShape } from "./modules/mcp/tools/validate.js";
8
7
  import { tools } from "./modules/mcp/tools/index.js";
9
- import { bindTools, getAllowedModes, isCallToolResult, isToolActive, resolveToolHandler, toolResponseErr, toolResponseFromResult, toolResponseOk, zodToJsonSchema } from "./modules/ai/toolkit.js";
10
8
  import { createMcpServer } from "./modules/mcp/server.js";
11
9
  import { handleMcp, readMcpBody, serveMcp } from "./modules/mcp/handler.js";
12
10
  import { createHttpPreviewClient } from "./modules/mcp/previewClient.js";
@@ -17,7 +15,6 @@ import { previewStage } from "./stages/preview.js";
17
15
  import { createWidgetProxyStage } from "./stages/proxy.js";
18
16
  import { buildMCPPipeline, mcpExtensions } from "./pipeline.js";
19
17
  import { createServer } from "./createServer.js";
20
- import AIEngine from "./modules/ai/AIEngine.js";
21
18
  import { createLocalScreenshotClient, resolveLocalBrowser } from "./modules/mcp/localScreenshotClient.js";
22
19
  import { createOAuthGuardStage, createOAuthStage } from "@plitzi/sdk-server/oauth";
23
- export { AIEngine, apply, applyShape, bindTools, buildAgentGuide, buildMCPPipeline, createHttpPreviewClient, createHttpScreenshotClient, createLocalScreenshotClient, createMcpOnlyStage, createMcpServer, createMcpStage, createOAuthGuardStage, createOAuthStage, createPreview, createServer, createWidgetProxyStage, getAllowedModes, handleMcp, isCallToolResult, isToolActive, mcpExtensions, operation, outlineOfSnapshot, outlineOfTree, previewStage, read, readMcpBody, readShape, resolveLocalBrowser, resolveToolHandler, search, searchShape, serveMcp, toolResponseErr, toolResponseFromResult, toolResponseOk, tools, unnamedControls, validate, validateShape, zodToJsonSchema };
20
+ export { apply, applyShape, buildMCPPipeline, createHttpPreviewClient, createHttpScreenshotClient, createLocalScreenshotClient, createMcpOnlyStage, createMcpServer, createMcpStage, createOAuthGuardStage, createOAuthStage, createPreview, createServer, createWidgetProxyStage, handleMcp, mcpExtensions, operation, outlineOfSnapshot, outlineOfTree, previewStage, read, readMcpBody, readShape, resolveLocalBrowser, search, searchShape, serveMcp, tools, unnamedControls, validate, validateShape };
@@ -1,10 +1,6 @@
1
- /** Lifts every @font-face block out of a stylesheet. Brace counting rather than a CSS parser: a @font-face body is
2
- * plain declarations, and base64 payloads and url() values carry no braces of their own. */
3
- export declare const splitFontFaces: (css: string) => {
4
- base: string;
5
- fonts: string;
6
- };
7
- /** What the page shell inlines: the SDK stylesheet without the icon fonts. */
1
+ /** The icon sheet with each font it names inlined: the widget's sandbox reaches no origin to fetch one from. */
2
+ export declare const inlineIconFonts: (css: string, readFont: (file: string) => Buffer) => string;
3
+ /** What the page shell inlines. */
8
4
  export declare const widgetCss: () => string;
9
- /** The icon fonts, for the renders that draw one. */
5
+ /** Font Awesome, fonts and all, for the renders that draw an icon. */
10
6
  export declare const iconFontCss: () => string;
@@ -2,53 +2,22 @@ import { require } from "../shared/resolve.js";
2
2
  import path from "node:path";
3
3
  import { readFileSync } from "node:fs";
4
4
  //#region src/modules/mcp/apps/render/styles.ts
5
- /** The SDK stylesheet, split in two.
5
+ /** The SDK's stylesheets, as a widget gets them.
6
6
  *
7
7
  * Everything the page shell inlines is paid for by EVERY widget: the host loads the ui:// page into a fresh
8
- * sandboxed iframe and parses it whole before anything paints, which is the "Rendering…" the user waits through.
9
- * Three of those @font-face blocks are Font Awesome shipped as base64 woff2 — ~330 KB, close to a fifth of the
10
- * page — and only a widget that authored a `fontAwesome` element has any use for them.
11
- *
12
- * So the shell takes the base, and the fonts travel in the tool result of the renders that actually draw an icon
13
- * (see tools/render.ts). A widget without icons never downloads a glyph; one with icons looks exactly the same as
14
- * before. */
15
- /** Where the closing brace of the block opened at `open` is, so a nested one cannot end it early. Returns the
16
- * string's length when the CSS is truncated mid-block, which drops that tail rather than mixing it into the base. */
17
- var blockEnd = (css, open) => {
18
- let depth = 0;
19
- for (let at = open; at < css.length; at++) if (css[at] === "{") depth++;
20
- else if (css[at] === "}") {
21
- depth--;
22
- if (depth === 0) return at + 1;
23
- }
24
- return css.length;
25
- };
26
- /** Lifts every @font-face block out of a stylesheet. Brace counting rather than a CSS parser: a @font-face body is
27
- * plain declarations, and base64 payloads and url() values carry no braces of their own. */
28
- var splitFontFaces = (css) => {
29
- const base = [];
30
- const fonts = [];
31
- let cursor = 0;
32
- for (let at = css.indexOf("@font-face", cursor); at !== -1; at = css.indexOf("@font-face", cursor)) {
33
- const open = css.indexOf("{", at);
34
- if (open === -1) break;
35
- const end = blockEnd(css, open);
36
- base.push(css.slice(cursor, at));
37
- fonts.push(css.slice(at, end));
38
- cursor = end;
39
- }
40
- base.push(css.slice(cursor));
41
- return {
42
- base: base.join(""),
43
- fonts: fonts.join("")
44
- };
45
- };
46
- var stylesheet = () => readFileSync(path.join(path.dirname(require.resolve("@plitzi/plitzi-sdk")), "plitzi-sdk.css"), "utf-8");
47
- var split;
48
- var sdkStyles = () => split ??= splitFontFaces(stylesheet());
49
- /** What the page shell inlines: the SDK stylesheet without the icon fonts. */
50
- var widgetCss = () => sdkStyles().base;
51
- /** The icon fonts, for the renders that draw one. */
52
- var iconFontCss = () => sdkStyles().fonts;
8
+ * sandboxed iframe and parses it whole before anything paints, which is the "Rendering…" the user waits through. So
9
+ * the shell takes only `plitzi-sdk.css`, and Font Awesome — its rules in `plitzi-sdk-icons.css`, its fonts files
10
+ * beside it — travels in the tool result of the renders that actually draw an icon (see tools/render.ts). A widget
11
+ * without icons never downloads a glyph. */
12
+ var sdkDist = () => path.dirname(require.resolve("@plitzi/plitzi-sdk"));
13
+ var FONT_URL = /url\((webfonts\/[\w.-]+\.woff2)\)/g;
14
+ /** The icon sheet with each font it names inlined: the widget's sandbox reaches no origin to fetch one from. */
15
+ var inlineIconFonts = (css, readFont) => css.replace(FONT_URL, (_match, file) => `url(data:font/woff2;base64,${readFont(file).toString("base64")})`);
16
+ var base;
17
+ var icons;
18
+ /** What the page shell inlines. */
19
+ var widgetCss = () => base ??= readFileSync(path.join(sdkDist(), "plitzi-sdk.css"), "utf-8");
20
+ /** Font Awesome, fonts and all, for the renders that draw an icon. */
21
+ var iconFontCss = () => icons ??= inlineIconFonts(readFileSync(path.join(sdkDist(), "plitzi-sdk-icons.css"), "utf-8"), (file) => readFileSync(path.join(sdkDist(), file)));
53
22
  //#endregion
54
- export { iconFontCss, splitFontFaces, widgetCss };
23
+ export { iconFontCss, inlineIconFonts, widgetCss };
@@ -34,7 +34,7 @@ const ErrorPanel = ({ title, details }: { title: string; details: string }) => (
34
34
  * wholesale when the result lands. A host that streams nothing paints none of this and keeps the static text. */
35
35
  const SKELETON_BAR_WIDTHS = ['92%', '78%', '85%', '64%', '88%', '72%'];
36
36
 
37
- // The deployment's own switch (mcpAi.renderStreaming), handed over by the page. Absent means on: a page built
37
+ // The deployment's own switch (McpOptions.renderStreaming), handed over by the page. Absent means on: a page built
38
38
  // before the setting existed carries none, and a host that streams no arguments paints the same either way.
39
39
  const streamingEnabled =
40
40
  (globalThis as { __PLITZI_VIEW__?: { streaming?: boolean } }).__PLITZI_VIEW__?.streaming !== false;
@@ -1,4 +1,4 @@
1
1
  import { buildDataSourceCatalog, buildInteractionCatalog, observedDataSources, observedInteractionActions } from "./observed.js";
2
2
  import { buildTypeRegistry } from "./registry.js";
3
- import { BUILTIN_ELEMENT_CALLBACKS as BUILTIN_ELEMENT_CALLBACKS$1, BUILTIN_GLOBAL_CALLBACKS as BUILTIN_GLOBAL_CALLBACKS$1, BUILTIN_UTILITIES as BUILTIN_UTILITIES$1, applyBuiltinCallback, applyElementCallback, applyUtility, cssProperties as cssProperties$1, cssShorthands, elementCatalog as elementCatalog$1, elementTypeNames, expandShorthand, expandShorthandPatch, getElementCallback, getGlobalCallback, getUtility, hiddenParams, isCssProperty, reconcileParams, suggestCssProperty } from "@plitzi/sdk-authoring";
4
- export { BUILTIN_ELEMENT_CALLBACKS$1 as BUILTIN_ELEMENT_CALLBACKS, BUILTIN_GLOBAL_CALLBACKS$1 as BUILTIN_GLOBAL_CALLBACKS, BUILTIN_UTILITIES$1 as BUILTIN_UTILITIES, applyBuiltinCallback, applyElementCallback, applyUtility, buildDataSourceCatalog, buildInteractionCatalog, buildTypeRegistry, cssProperties$1 as cssProperties, cssShorthands, elementCatalog$1 as elementCatalog, elementTypeNames, expandShorthand, expandShorthandPatch, getElementCallback, getGlobalCallback, getUtility, hiddenParams, isCssProperty, observedDataSources, observedInteractionActions, reconcileParams, suggestCssProperty };
3
+ import { applyBuiltinCallback, applyElementCallback, applyUtility, cssProperties as cssProperties$1, cssShorthands, elementCatalog as elementCatalog$1, elementTypeNames, expandShorthand, expandShorthandPatch, getElementCallback, getGlobalCallback, getUtility, hiddenParams, isCssProperty, reconcileParams, suggestCssProperty } from "@plitzi/sdk-authoring";
4
+ export { applyBuiltinCallback, applyElementCallback, applyUtility, buildDataSourceCatalog, buildInteractionCatalog, buildTypeRegistry, cssProperties$1 as cssProperties, cssShorthands, elementCatalog$1 as elementCatalog, elementTypeNames, expandShorthand, expandShorthandPatch, getElementCallback, getGlobalCallback, getUtility, hiddenParams, isCssProperty, observedDataSources, observedInteractionActions, reconcileParams, suggestCssProperty };
@@ -10,8 +10,8 @@
10
10
  *
11
11
  * The BUILT-IN types are not here: their structural half (attributes, style selectors, default styles) comes from
12
12
  * `@plitzi/sdk-elements`, which this package deliberately does not depend on — a React element library is the
13
- * heaviest possible import for a server that renders nothing. Deployments merge those in themselves; the curated
14
- * semantics for them live in `builtinComponents.ts`.
13
+ * heaviest possible import for a server that renders nothing. Deployments merge those in themselves; what each one is
14
+ * for is `elementCatalog`, read off the declarations by `@plitzi/sdk-authoring`.
15
15
  */
16
16
  var MANIFEST_TTL_MS = 6e5;
17
17
  var manifestCache = /* @__PURE__ */ new Map();
@@ -18,7 +18,8 @@ export interface McpEndpointOptions {
18
18
  /** Attach a space to the connection, the way an authorized connector's token does — the server then offers its
19
19
  * editing surface. Omitted, the endpoint is the guest one: no space, so only what works without one. */
20
20
  spaceId?: number;
21
- /** The deployment switch a consumer sets as `mcpAi.renderStreaming`. Omitted, the server's own default stands. */
21
+ /** The deployment switch a consumer sets as `McpOptions.renderStreaming`. Omitted, the server's own default
22
+ * stands. */
22
23
  renderStreaming?: boolean;
23
24
  }
24
25
  /** Start the endpoint and connect a real MCP client to it, as a remote connector does. */
@@ -10,9 +10,9 @@ export type McpRequestOptions = {
10
10
  preview?: PreviewClient;
11
11
  screenshot?: ScreenshotClient;
12
12
  logger?: ServerLogger;
13
- /** Deployment switch for the plitzi_render view (see `mcpAi.renderStreaming`). Defaults to true. */
13
+ /** Deployment switch for the plitzi_render view (see `McpOptions.renderStreaming`). Defaults to true. */
14
14
  renderStreaming?: boolean;
15
- /** Where a rendered widget loads everything external from (see `mcpAi.proxy`). Absent → the URLs an agent
15
+ /** Where a rendered widget loads everything external from (see `McpOptions.proxy`). Absent → the URLs an agent
16
16
  * authored travel to the host exactly as written. */
17
17
  proxy?: ResourceProxy;
18
18
  };
@@ -0,0 +1,2 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ export declare const isCallToolResult: (result: unknown) => result is CallToolResult;
@@ -0,0 +1,4 @@
1
+ //#region src/modules/mcp/helpers/callToolResult.ts
2
+ var isCallToolResult = (result) => typeof result === "object" && result !== null && Array.isArray(result.content);
3
+ //#endregion
4
+ export { isCallToolResult };
@@ -1,4 +1,4 @@
1
- import { BUILTIN_TRANSFORMERS, GLOBAL_SOURCES } from "@plitzi/sdk-authoring";
1
+ import { BUILTIN_TRANSFORMERS, GLOBAL_SOURCES, MOTION_ENTERS, MOTION_LOOPS } from "@plitzi/sdk-authoring";
2
2
  //#region src/modules/mcp/helpers/guide.ts
3
3
  var GLOBALS = GLOBAL_SOURCES.map((source) => `\`${source}\``).join(", ");
4
4
  var OTHER_TRANSFORMERS = Object.keys(BUILTIN_TRANSFORMERS).filter((action) => action !== "twigTemplate").map((action) => `\`${action}\``).join(", ");
@@ -61,7 +61,8 @@ computed for you). Node types: \`callback\` (an element's own callback — \`ele
61
61
  (category/key/value/revertOnFinish) ≠ global \`setState\` (source \`state\`, key/type/value). To turn a step off use
62
62
  \`patchInteractionNode {enabled:false}\` — \`deleteInteraction\` removes it (destructive; confirm first). Any param
63
63
  **value** can be a binding token \`{{ source }}\` (e.g. notification \`content: "{{ list_<name>.item.name }}"\`).
64
- Steps read the page as it is when they run; a refiring trigger is ignored unless \`whileRunning: "queue"\`; keyboard
64
+ Steps read the page as it is when they run; a refiring trigger is ignored unless \`whileRunning: "queue"\` (or
65
+ \`"latest"\`: the newest firing stops the running one); keyboard
65
66
  shortcuts are the \`onKey\` trigger (\`keys: "shift+f, escape"\`).
66
67
 
67
68
  **CMS / API integrations** (\`upsertConnector\`): a **connector** is a manifest declaring a provider's base URL,
@@ -88,6 +89,12 @@ button/link needs words (an icon-only button a \`title\`), every field a \`label
88
89
  image an \`alt\` or \`decorative: true\`; clicks go on a \`button\`/\`link\`, never a container; headings step down one
89
90
  level at a time. \`plitzi_screenshot view:"accessibility"\` shows the tree and what has no name.
90
91
 
92
+ **Everything the space holds is public.** Its pages, variables, attributes, flow params and mock data ship to every
93
+ visitor's browser, and a file on a public bucket of its CDN is served to anyone who has its address. A key, a token or
94
+ data only some visitors may read never goes in any of them: a secret is a credential the owner attaches (a connector, an
95
+ action or a function names it, never carries it), and data for some visitors only comes from a \`runtime: "server"\`
96
+ provider or an action that checks who is asking.
97
+
91
98
  **Touched elements must be malformation-free.** Editing an element also checks its CURRENT stored content and BLOCKS
92
99
  the save on any \`Pre-existing malformation in element …\` error (a broken transformer, a malformed step, an attribute
93
100
  it never reads) — even parts you did not touch. These are NOT from your change (the message says so); fix them in the
@@ -172,6 +179,8 @@ Never download a whole tree you do not need.
172
179
  action may be built from.
173
180
  - \`plitzi://functions/{env}\` — the space's **functions**, its own server code: which files, and the tasks, routes and
174
181
  hosts they declare. \`plitzi://functions/{env}/{+path}\` reads one file whole. See *Functions* below.
182
+ - \`plitzi://data/{env}\` — the space's **data**: JSON files its server providers read, never served.
183
+ \`plitzi://data/{env}/{+path}\` reads one, parsed. See *Data* below.
175
184
  - \`plitzi://changes/{env}\` — the space's **change history**, read-only and newest first: who made each change (a
176
185
  person in the builder, an agent, the autofix), when, and every element, class, token or font it touched, whole, before
177
186
  and after. \`/{id}\` for the history of one of them — how you find what changed an element you are asked about, or
@@ -234,7 +243,8 @@ space already had is not repeated on every batch — and none of them blocks any
234
243
  page being shown: the \`current\` state marks it (see *Pages & folders*).
235
244
  - \`repeated-shape\` — one structure written again with other words: a **component** with props
236
245
  (\`upsertComponent { fromRef }\` turns one copy into it), or, for sibling rows of data, one \`repeatElement\`/\`list\`.
237
- A few cards a person rewords by hand can stay cards.
246
+ A few cards a person rewords by hand can stay cards; siblings that read different data or keep different state are
247
+ controls written alike, and are not offered.
238
248
  - \`content-attribute\` — a \`button\` or \`link\` whose children are only a \`text\` and/or a \`fontAwesome\`: its words
239
249
  are its own \`content\` and the icon its \`icon\`. What the text's class adds (\`white-space: nowrap\`) moves to the
240
250
  box's class — never the class itself; the icon's class goes in the \`icon\` slot.
@@ -242,7 +252,14 @@ space already had is not repeated on every batch — and none of them blocks any
242
252
  \`states\`/\`ancestors\`, the SDK (reduced motion, the theme toggle's icons) or the space's notification look says.
243
253
  - \`heavy-animation\` — keyframes that animate a size, a position, a blur, a shadow, or a colour in a loop: they stutter
244
254
  while the page loads. Lighter, not shorter — see **Motion — good practices**.
245
- \`plitzi://explain/<code>\` explains each. Take one unless the copies are about to diverge.
255
+ - \`unused-class\`, \`unused-token\`, \`unused-component\` — declared and read by nothing: remove it
256
+ (\`deleteDefinition\`, \`deleteStyleVariable\`, \`deleteComponent\`), or use it where it was meant to go. Said
257
+ when a batch leaves one behind.
258
+ - \`literal-colour\` — a class painted from the palette typing out a token's light value: \`var(--token)\` when it
259
+ should follow the scheme; a token of one value when it must stay the same in both.
260
+ \`plitzi://explain/<code>\` explains each. Take one unless the copies are about to diverge; one left on purpose is
261
+ quieted on an element it names — \`quiet: ["repeated-shape"]\` on \`upsertElement\`/\`patchElement\` (\`null\` removes it)
262
+ — and is not offered again, here, in the builder or in authoring. Suggestions' codes only.
246
263
 
247
264
  ## Readers: resources vs plitzi_search vs plitzi_read (do not confuse them)
248
265
  Three ways to read, each for a different moment — pick by what you have in hand:
@@ -269,7 +286,7 @@ runtime wires by it — there is no second key and nothing to translate. Creatin
269
286
  That name is the **wiring key**: a provider registers its data source as \`<type>_<name>\`, so a \`source\` you write
270
287
  against a name resolves to that element at runtime. Rules for a **new** name (both enforced; a violation fails the
271
288
  batch):
272
- - Charset \`[A-Za-z0-9_-]\`, **starting with a letter** (e.g. \`"products-api"\`, \`"food_item"\`). A \`.\` would split
289
+ - Charset \`[A-Za-z0-9_-]\`, **starting with a letter** and not ending in \`-\` (e.g. \`"products-api"\`, \`"food_item"\`). A \`.\` would split
273
290
  the \`<type>_<name>.<field>\` source path and the interaction target lookup, so **no dots**. An **underscore is
274
291
  allowed**: the FIRST \`_\` separates \`<type>\` from the name and element types are camelCase with none, so
275
292
  underscores inside a name are unambiguous (\`list_food_item\` → type \`list\`, name \`food_item\`).
@@ -281,7 +298,8 @@ batch):
281
298
  **Where an element renders** is its \`runtime\`, settable on \`upsertElement\`/\`patchElement\`: \`"shared"\` (the
282
299
  default, both sides), \`"client"\` (browser only) or \`"server"\` (SSR only). It matters for one thing above all — an
283
300
  \`apiContainer\` reads through a **connector** only when it is \`"server"\` (see *Connectors*). An element read
284
- reports it only when it is set.
301
+ reports it only when it is set. The page server resolves \`"server"\` elements of a page and its layouts only, never
302
+ inside a component — one there is refused; put the provider on the page and hand the component its rows as a prop.
285
303
 
286
304
  **Renaming** (\`patchElement\` with \`rename\`) moves the one key: the parent's child list, every binding source and
287
305
  every interaction target across the space that named the old one is repointed with it, so nothing comes unwired.
@@ -412,7 +430,10 @@ provider's records into a list element:
412
430
  **What an \`apiContainer\` publishes** depends on where it reads. Through a connector (\`runtime: "server"\`) it is the
413
431
  fixed shape under *Connectors* — \`.records\` or \`.record\`, \`.pageInfo\`, … With a browser \`query\` it is the
414
432
  response: \`.data\` (the parsed body — an API answering \`{ "items": [...] }\` is read as
415
- \`apiContainer_<name>.data.items\`) and \`.status\` (the HTTP status). Both add \`.isLoading\`, \`.isEmpty\`,
433
+ \`apiContainer_<name>.data.items\`) and \`.status\` (the HTTP status) — and a \`"server"\` provider with only a
434
+ \`query\` (a JSON file the deployment serves) answers in that same shape, so moving it to the server changes no
435
+ binding. Fed by a server **action**, it is the action's output at the root (\`apiContainer_<name>.items\`, never
436
+ \`.data.items\`). All add \`.isLoading\` (true while asked again too), \`.isEmpty\`,
416
437
  \`.hasError\` and \`.errorMessage\`.
417
438
 
418
439
  **Source scope — a source is visible to the provider's DESCENDANTS only.** An element source named
@@ -435,7 +456,7 @@ the API needs a token or the content should be in the HTML), or a browser-side \
435
456
  \`{ action, params }\`; the runtime runs them in order and resolves each by its \`action\` alone, so an **unknown
436
457
  action is silently skipped** and the raw value passes through. Use the **exact** action names from
437
458
  \`plitzi://data-sources\` (\`transformers\`). The most common is **\`twigTemplate\`** to format a value — the incoming
438
- value is the **\`{{source}}\`** token (NOT \`{{value}}\`); \`{{sourceTo}}\` is the field's previous value. Example —
459
+ value is the **\`{{source}}\`** token (NOT \`{{value}}\`) — undefined until the source has a value, so \`{{ source|default('…') }}\` covers the wait; \`{{sourceTo}}\` is the field's previous value. Example —
439
460
  show a number with units:
440
461
  \`{ "type": "upsertBinding", "pageRef": "home", "ref": "food-item-time", "category": "attributes",
441
462
  "binding": { "to": "content", "source": "list_food-list.item.cookTimeMinutes",
@@ -459,7 +480,8 @@ you — never wire them by hand. Each step also has an \`enabled\` flag (see dis
459
480
  **Each step reads the page as it is when it runs**: a \`when\` or a \`{{ state.x }}\` after a \`setState\` sees the new
460
481
  value, and one after a \`delay\` sees what changed meanwhile. To act on the value from BEFORE a write, put the reading
461
482
  step first. **A trigger fired again while its flow runs is ignored** (no double submit); set \`"whileRunning": "queue"\`
462
- on the trigger node for a stream of events that must each run in order (\`"parallel"\` runs them at once).
483
+ on the trigger node for a stream of events that must each run in order (\`"parallel"\` runs them at once,
484
+ \`"latest"\` stops the running flow — its server action or request too — for a search as you type).
463
485
  **Keyboard shortcuts** are a trigger every element has: \`onKey\` with param \`keys\` — \`"f"\`, \`"shift+f"\`,
464
486
  \`"mod+k"\` (⌘ on a Mac, Ctrl elsewhere), \`"plus, ="\`, \`"escape"\`. Heard on the whole page while the element is
465
487
  mounted, ignored while someone types in a field; the flow reads the key pressed as \`{{ <trigger id>.key }}\`.
@@ -538,6 +560,20 @@ Discover valid actions in \`plitzi://interactions/{env}\`: \`actions\` = observe
538
560
  node type and valid params per action. An element read lists its flows as ordered nodes (each with its \`id\` and
539
561
  \`enabled\`), so a follow-up patch/delete needs no extra read.
540
562
 
563
+ ## What is public — and where a secret goes
564
+ A space is served to anyone who opens it, and everything it is made of goes with it:
565
+
566
+ - **The documents.** Every page, layout and component, every schema and style variable, every attribute, binding,
567
+ flow param and \`mockData\` reaches the visitor's browser with the page. Hiding an element (\`visible: false\`, a
568
+ flag, a condition) does not remove it from what is sent.
569
+ - **The files.** A file on one of the space's public buckets is on the internet at its address, signed in or not —
570
+ for pictures, fonts and data every visitor may see.
571
+
572
+ So a secret — an API key, a token, a password — never goes in a document or a public file. It is a **credential**,
573
+ created by the space owner in the builder, which a connector, an action or a function **names** and never carries.
574
+ Data only some visitors may read comes from a \`runtime: "server"\` provider or a server action whose \`access\` checks
575
+ who is asking — never from a public file or a document the page filters on the client.
576
+
541
577
  ## Connectors — CMS and API integrations
542
578
  A **connector** is how a space reads real content from Strapi, WordPress, Contentful, Directus or any REST service.
543
579
  It is **a manifest, not code**: you declare the base URL, the endpoints, the auth template and the filter operators,
@@ -647,7 +683,7 @@ returns **no records** rather than the whole collection — a URL that addressed
647
683
  **Paging** is the \`pagination\` prop: \`"none"\`, \`"url"\` (the page number rides the query string, so pages are
648
684
  shareable and indexable — prefer it) or \`"append"\` (a "load more" list). Give each list on a page its own
649
685
  \`pageParam\` so two lists page independently. The provider also offers the \`loadMore\` and \`goToPage\` callbacks to
650
- interaction flows, and \`performQuery\` to refetch.
686
+ interaction flows, \`performQuery\` to refetch, and \`cancelQuery\` to drop a request in flight (a STOP).
651
687
 
652
688
  **Writing back** (a form, a "delete" button): declare the endpoint under \`endpoints.write\`, then call the provider's
653
689
  **\`writeRecord\`** callback from an interaction flow — nodeType \`callback\`, \`elementId\` the apiContainer's ref,
@@ -706,6 +742,19 @@ any other (\`<namespace>.<action>\`), listed in \`plitzi://actions/{env}/tasks\`
706
742
  The live site runs what the space was last published with, never the draft. Running functions is part of the paid
707
743
  plans: on another plan they save, and a run answers why it did not start — tell the user rather than work around it.
708
744
 
745
+ ## Data
746
+ JSON the space's pages read on the server and never serve — a catalog, prices, a menu: what a page needs whole and
747
+ nobody edits element by element.
748
+
749
+ - **Read** it as a provider does: \`apiContainer\` with \`runtime: "server"\` and \`query: "/data/products.json"\` reads
750
+ \`products.json\` of \`plitzi://data/{env}\`, and the page arrives with it. A browser provider reading \`/data/…\` is
751
+ refused — the data is the server's.
752
+ - **Write** it with \`upsertDataFile { path, content }\` (the whole file, as JSON text; \`deleteDataFile { path }\`
753
+ removes one): a path of plain segments ending in \`.json\`. Saved whole, with the batch: a newer copy (somebody saved
754
+ since you read it) refuses it — read it again.
755
+ - **What a provider reads is in the page it renders**: data a page must not carry belongs in a server action, which
756
+ answers only what is shown. The live site reads what the space was last published with, never the draft.
757
+
709
758
  ## Realtime channels
710
759
  Pages that see each other — cursors, presence, a shared board, a game. Three parts, all needed:
711
760
 
@@ -926,8 +975,10 @@ Space-level configuration lives in \`plitzi://settings/{env}\` and is edited wit
926
975
  ## Motion — good practices
927
976
  A preset first: an element's \`motion\` field (\`upsertElement\`/\`patchElement\`) —
928
977
  \`{ "enter": "fade-up", "on": "view" }\`, \`"stagger": 60\` for its children one by one, \`"loop": "float"\` — is
929
- played by the SDK, cheap and stilled for reduced motion. Enters: fade, fade-up, fade-down, slide-left, slide-right,
930
- scale; loops: float, pulse, spin, sway. Beyond them, keyframes go in \`customCss\` (\`patchSettings\`), a
978
+ played by the SDK, cheap and stilled for reduced motion. \`on\`: \`load\` (default), \`view\` (once, the first time it
979
+ comes into view, then it stays) or \`scroll\` (follows the scroll both ways — it fades back out as the reader scrolls
980
+ up past it; only for an effect that is about the scroll itself). Enters: ${MOTION_ENTERS.join(", ")}; loops:
981
+ ${MOTION_LOOPS.join(", ")}. Beyond them, keyframes go in \`customCss\` (\`patchSettings\`), a
931
982
  definition names them (\`animation\`). Only \`opacity\` and
932
983
  \`transform\` (\`translate\`, \`scale\`, \`rotate\`) animate off the main thread: they stay smooth while the page
933
984
  hydrates and on a slow phone. Anything else repaints or lays the page out again on every frame, and freezes while the
@@ -940,7 +991,8 @@ page's scripts run.
940
991
  pulsing glow is a static gradient whose \`opacity\` or \`scale\` changes.
941
992
  - Decoration that must animate something else (a custom property, \`background-position\`) starts paused and runs
942
993
  once the page is hydrated: \`.glow { animation: glow 8s linear infinite paused; } [data-hydrated] .glow {
943
- animation-play-state: running; }\` in \`customCss\`.
994
+ animation-play-state: running; }\` in \`customCss\`. That is for loops: an entrance of its own that plays once
995
+ (\`transform\`/\`opacity\` only) plays from the first paint with \`backwards\`, never gated on \`[data-hydrated]\`.
944
996
  - Entrances: \`opacity\` + a small \`translateY\`, under ~600ms, staggered by tens of ms.
945
997
  - One slow ambient loop per screen at most — not one per card: \`infinite\` runs on every visitor's battery.
946
998
  - Name what transitions (\`transition: transform 200ms, opacity 200ms\`), never \`all\`; \`will-change\` only on the
@@ -962,7 +1014,9 @@ tree names nothing is invisible to both, however it looks. So build every page t
962
1014
  no agent finds them. An empty backdrop that closes a panel is the exception (the panel's close button and Escape
963
1015
  are the ways everyone has).
964
1016
  - **State is said, not only styled.** A toggle sets \`ariaPressed\`, a button that opens a panel \`ariaExpanded\`
965
- (bind them like any attribute) — an "active" class alone is invisible to both readers.
1017
+ (bind them like any attribute) — an "active" class alone is invisible to both readers. A button that shows and
1018
+ hides a panel names it in \`controls\` (\`aria-controls\`, the panel's anchor), and words that change while a
1019
+ visitor reads them — a count, a total, a status — sit in a \`container\` with \`live: "polite"\` (\`aria-live\`).
966
1020
  - **A dropdown opens from a \`button\`** among its children, outside the popup (an avatar, a "⋯": the button holds it and
967
1021
  has a \`title\`). The dropdown marks it and moves the focus in and out; a box or an icon alone opens nothing from a
968
1022
  keyboard.
@@ -1,4 +1,4 @@
1
- export * from './agentPrompt';
1
+ export * from './callToolResult';
2
2
  export * from './components';
3
3
  export * from './computeVersion';
4
4
  export * from './space';
@@ -1,5 +1,5 @@
1
1
  import { AIElementDetail } from '../types';
2
- import { ActionEntry, ActionTaskDescriptor, ComponentCatalog, ConnectorEntry, Element, FunctionsDraft, PageFolder, Schema, Style } from '@plitzi/sdk-shared';
2
+ import { ActionEntry, ActionTaskDescriptor, ComponentCatalog, ConnectorEntry, DataDraft, Element, FunctionsDraft, PageFolder, Schema, Style } from '@plitzi/sdk-shared';
3
3
  /** The working view the tools read and mutate: the two Plitzi schemas (elements + style), which the platform
4
4
  * stores and persists as separate documents (Space model / Style model), plus the space's `connectors` — a third
5
5
  * store (one row per connector) that a provider element points into by identifier. `catalog` is read-only
@@ -16,6 +16,9 @@ export interface Space {
16
16
  /** The space's own functions — their source files, which copy of them this is, and what they declare. Absent when
17
17
  * the deployment offers no functions: then there is nothing to read and every function op is refused. */
18
18
  functions?: FunctionsDraft;
19
+ /** The space's own data — JSON its server providers read (`/data/<file>`), never served — and which copy of it this
20
+ * is. Absent when the deployment offers no data: then there is nothing to read and every data op is refused. */
21
+ data?: DataDraft;
19
22
  catalog?: ComponentCatalog;
20
23
  /**
21
24
  * The whole document, when `schema` is a view of one tree of it — a component's, which an op addressed to a
@@ -6,6 +6,7 @@ var cloneSpace = (space) => ({
6
6
  connectors: structuredClone(space.connectors),
7
7
  actions: structuredClone(space.actions),
8
8
  ...space.functions ? { functions: structuredClone(space.functions) } : {},
9
+ ...space.data ? { data: structuredClone(space.data) } : {},
9
10
  ...space.actionTasks ? { actionTasks: space.actionTasks } : {},
10
11
  ...space.catalog ? { catalog: space.catalog } : {}
11
12
  });
@@ -28,6 +28,8 @@ export declare const actionUri: (env: Env, ref: string) => string;
28
28
  export declare const actionTasksUri: (env: Env) => string;
29
29
  export declare const functionsUri: (env: Env) => string;
30
30
  export declare const functionFileUri: (env: Env, path: string) => string;
31
+ export declare const dataUri: (env: Env) => string;
32
+ export declare const dataFileUri: (env: Env, path: string) => string;
31
33
  export declare const foldersUri: (env: Env) => string;
32
34
  export declare const folderUri: (env: Env, ref: string) => string;
33
35
  export declare const defsUri: (env: Env) => string;
@@ -28,6 +28,8 @@ var actionUri = (env, ref) => `plitzi://actions/${env}/${ref}`;
28
28
  var actionTasksUri = (env) => `plitzi://actions/${env}/tasks`;
29
29
  var functionsUri = (env) => `plitzi://functions/${env}`;
30
30
  var functionFileUri = (env, path) => `plitzi://functions/${env}/${path}`;
31
+ var dataUri = (env) => `plitzi://data/${env}`;
32
+ var dataFileUri = (env, path) => `plitzi://data/${env}/${path}`;
31
33
  var foldersUri = (env) => `plitzi://folders/${env}`;
32
34
  var folderUri = (env, ref) => `plitzi://folders/${env}/${ref}`;
33
35
  var defsUri = (env) => `plitzi://definitions/${env}`;
@@ -62,4 +64,4 @@ var itemTemplates = (env) => [
62
64
  * family and extracts its ref in one step, without repeating the prefix string for both the startsWith and slice. */
63
65
  var afterPrefix = (uri, prefix) => uri.startsWith(prefix) ? uri.slice(prefix.length) : void 0;
64
66
  //#endregion
65
- export { actionTasksUri, actionUri, actionsUri, afterPrefix, aliasedRoots, changesUri, componentsUri, connectorPresetsUri, connectorUri, connectorsUri, cssPropertiesUri, dataSourcesUri, defUri, defsUri, elementUri, flagsUri, folderUri, foldersUri, fontUri, fontsUri, functionFileUri, functionsUri, globalUri, globalsUri, guideUri, idUri, idsUri, interactionsUri, itemTemplates, layoutsUri, pageUri, pagesUri, primerUri, schemaVarsUri, settingsUri, styleVarUri, styleVarsUri, typesUri };
67
+ export { actionTasksUri, actionUri, actionsUri, afterPrefix, aliasedRoots, changesUri, componentsUri, connectorPresetsUri, connectorUri, connectorsUri, cssPropertiesUri, dataFileUri, dataSourcesUri, dataUri, defUri, defsUri, elementUri, flagsUri, folderUri, foldersUri, fontUri, fontsUri, functionFileUri, functionsUri, globalUri, globalsUri, guideUri, idUri, idsUri, interactionsUri, itemTemplates, layoutsUri, pageUri, pagesUri, primerUri, schemaVarsUri, settingsUri, styleVarUri, styleVarsUri, typesUri };
@@ -1,7 +1,7 @@
1
1
  export { createMcpServer, handleMcp, serveMcp, readMcpBody } from './handler';
2
2
  export { readResource, resourceVersion, registerResources, buildTypeRegistry, cssProperties } from './resources';
3
3
  export { apply, validate, search, read, validateOperations, operation, tools } from './tools';
4
- export { computeVersion, buildAgentGuide } from './helpers';
4
+ export { computeVersion } from './helpers';
5
5
  export { createHttpPreviewClient } from './previewClient';
6
6
  export { createLocalScreenshotClient, resolveLocalBrowser } from './localScreenshotClient';
7
7
  export { createHttpScreenshotClient } from './screenshotClient';