@plitzi/sdk-mcp 0.38.4 → 0.38.6

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 (77) hide show
  1. package/CHANGELOG.md +358 -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 +27 -4
  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/resources/connectors.d.ts +1 -1
  24. package/dist/modules/mcp/resources/core.js +2 -2
  25. package/dist/modules/mcp/resources/data.d.ts +8 -0
  26. package/dist/modules/mcp/resources/data.js +46 -0
  27. package/dist/modules/mcp/resources/primer.js +3 -3
  28. package/dist/modules/mcp/resources/register.js +12 -2
  29. package/dist/modules/mcp/resources/renderGuide.js +6 -6
  30. package/dist/modules/mcp/resources/router.js +3 -1
  31. package/dist/modules/mcp/resources/schema.js +1 -1
  32. package/dist/modules/mcp/server.d.ts +3 -3
  33. package/dist/modules/mcp/server.js +12 -8
  34. package/dist/modules/mcp/tools/apply/dispatch.js +9 -1
  35. package/dist/modules/mcp/tools/apply/index.d.ts +11 -2
  36. package/dist/modules/mcp/tools/apply/index.js +27 -1
  37. package/dist/modules/mcp/tools/index.d.ts +2 -2
  38. package/dist/modules/mcp/tools/index.js +2 -2
  39. package/dist/modules/mcp/tools/operations/connectors/index.d.ts +2 -2
  40. package/dist/modules/mcp/tools/operations/connectors/manifest.d.ts +1 -1
  41. package/dist/modules/mcp/tools/operations/connectors/patchConnector.d.ts +1 -1
  42. package/dist/modules/mcp/tools/operations/connectors/upsertConnector.d.ts +1 -1
  43. package/dist/modules/mcp/tools/operations/data/data.test.d.ts +1 -0
  44. package/dist/modules/mcp/tools/operations/data/index.d.ts +32 -0
  45. package/dist/modules/mcp/tools/operations/data/index.js +54 -0
  46. package/dist/modules/mcp/tools/operations/index.d.ts +26 -4
  47. package/dist/modules/mcp/tools/operations/index.js +4 -1
  48. package/dist/modules/mcp/tools/operations/schema/interactions/patchInteractionNode.d.ts +1 -0
  49. package/dist/modules/mcp/tools/operations/schema/interactions/upsertInteractionFlow.d.ts +1 -0
  50. package/dist/modules/mcp/tools/operations/schema/operations.d.ts +2 -0
  51. package/dist/modules/mcp/tools/operations/schema/shared.d.ts +1 -0
  52. package/dist/modules/mcp/tools/operations/schema/shared.js +2 -5
  53. package/dist/modules/mcp/tools/operations/schema/translator.js +1 -1
  54. package/dist/modules/mcp/tools/operations/schema/write.js +1 -1
  55. package/dist/modules/mcp/tools/operations/schemaIds.js +1 -2
  56. package/dist/modules/mcp/tools/operations/style/write.js +1 -1
  57. package/dist/modules/mcp/tools/preview.d.ts +2 -0
  58. package/dist/modules/mcp/tools/render.d.ts +2 -0
  59. package/dist/modules/mcp/tools/screenshot.d.ts +11 -2
  60. package/dist/modules/mcp/tools/shared/newSuggestions.d.ts +2 -2
  61. package/dist/modules/mcp/tools/shared/newSuggestions.js +10 -5
  62. package/dist/modules/mcp/tools/shared/newSuggestions.test.d.ts +1 -0
  63. package/dist/modules/mcp/tools/shared/tool.d.ts +7 -6
  64. package/dist/modules/mcp/tools/shared/validator/index.js +2 -2
  65. package/dist/modules/mcp/tools/tryFunction.js +1 -1
  66. package/dist/modules/mcp/tools/validate.d.ts +11 -2
  67. package/dist/modules/mcp/types/appTypes.d.ts +2 -2
  68. package/dist/modules/mcp/types/toolTypes.d.ts +5 -1
  69. package/package.json +7 -9
  70. package/dist/modules/ai/AIEngine.d.ts +0 -20
  71. package/dist/modules/ai/AIEngine.js +0 -120
  72. package/dist/modules/ai/index.d.ts +0 -4
  73. package/dist/modules/ai/toolkit.d.ts +0 -17
  74. package/dist/modules/ai/toolkit.js +0 -59
  75. package/dist/modules/mcp/helpers/agentPrompt.d.ts +0 -4
  76. package/dist/modules/mcp/helpers/agentPrompt.js +0 -123
  77. /package/dist/modules/{ai/toolkit.test.d.ts → mcp/helpers/callToolResult.test.d.ts} +0 -0
@@ -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 };
@@ -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,
@@ -178,6 +179,8 @@ Never download a whole tree you do not need.
178
179
  action may be built from.
179
180
  - \`plitzi://functions/{env}\` — the space's **functions**, its own server code: which files, and the tasks, routes and
180
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.
181
184
  - \`plitzi://changes/{env}\` — the space's **change history**, read-only and newest first: who made each change (a
182
185
  person in the builder, an agent, the autofix), when, and every element, class, token or font it touched, whole, before
183
186
  and after. \`/{id}\` for the history of one of them — how you find what changed an element you are asked about, or
@@ -249,6 +252,11 @@ space already had is not repeated on every batch — and none of them blocks any
249
252
  \`states\`/\`ancestors\`, the SDK (reduced motion, the theme toggle's icons) or the space's notification look says.
250
253
  - \`heavy-animation\` — keyframes that animate a size, a position, a blur, a shadow, or a colour in a loop: they stutter
251
254
  while the page loads. Lighter, not shorter — see **Motion — good practices**.
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.
252
260
  \`plitzi://explain/<code>\` explains each. Take one unless the copies are about to diverge; one left on purpose is
253
261
  quieted on an element it names — \`quiet: ["repeated-shape"]\` on \`upsertElement\`/\`patchElement\` (\`null\` removes it)
254
262
  — and is not offered again, here, in the builder or in authoring. Suggestions' codes only.
@@ -424,7 +432,8 @@ fixed shape under *Connectors* — \`.records\` or \`.record\`, \`.pageInfo\`,
424
432
  response: \`.data\` (the parsed body — an API answering \`{ "items": [...] }\` is read as
425
433
  \`apiContainer_<name>.data.items\`) and \`.status\` (the HTTP status) — and a \`"server"\` provider with only a
426
434
  \`query\` (a JSON file the deployment serves) answers in that same shape, so moving it to the server changes no
427
- binding. Both add \`.isLoading\`, \`.isEmpty\`,
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\`,
428
437
  \`.hasError\` and \`.errorMessage\`.
429
438
 
430
439
  **Source scope — a source is visible to the provider's DESCENDANTS only.** An element source named
@@ -471,7 +480,8 @@ you — never wire them by hand. Each step also has an \`enabled\` flag (see dis
471
480
  **Each step reads the page as it is when it runs**: a \`when\` or a \`{{ state.x }}\` after a \`setState\` sees the new
472
481
  value, and one after a \`delay\` sees what changed meanwhile. To act on the value from BEFORE a write, put the reading
473
482
  step first. **A trigger fired again while its flow runs is ignored** (no double submit); set \`"whileRunning": "queue"\`
474
- 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).
475
485
  **Keyboard shortcuts** are a trigger every element has: \`onKey\` with param \`keys\` — \`"f"\`, \`"shift+f"\`,
476
486
  \`"mod+k"\` (⌘ on a Mac, Ctrl elsewhere), \`"plus, ="\`, \`"escape"\`. Heard on the whole page while the element is
477
487
  mounted, ignored while someone types in a field; the flow reads the key pressed as \`{{ <trigger id>.key }}\`.
@@ -673,7 +683,7 @@ returns **no records** rather than the whole collection — a URL that addressed
673
683
  **Paging** is the \`pagination\` prop: \`"none"\`, \`"url"\` (the page number rides the query string, so pages are
674
684
  shareable and indexable — prefer it) or \`"append"\` (a "load more" list). Give each list on a page its own
675
685
  \`pageParam\` so two lists page independently. The provider also offers the \`loadMore\` and \`goToPage\` callbacks to
676
- interaction flows, and \`performQuery\` to refetch.
686
+ interaction flows, \`performQuery\` to refetch, and \`cancelQuery\` to drop a request in flight (a STOP).
677
687
 
678
688
  **Writing back** (a form, a "delete" button): declare the endpoint under \`endpoints.write\`, then call the provider's
679
689
  **\`writeRecord\`** callback from an interaction flow — nodeType \`callback\`, \`elementId\` the apiContainer's ref,
@@ -732,6 +742,19 @@ any other (\`<namespace>.<action>\`), listed in \`plitzi://actions/{env}/tasks\`
732
742
  The live site runs what the space was last published with, never the draft. Running functions is part of the paid
733
743
  plans: on another plan they save, and a run answers why it did not start — tell the user rather than work around it.
734
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
+
735
758
  ## Realtime channels
736
759
  Pages that see each other — cursors, presence, a shared board, a game. Three parts, all needed:
737
760
 
@@ -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';
@@ -10,7 +10,7 @@ export declare const connectorSummaries: (space: Space) => {
10
10
  operators: string[];
11
11
  pagination: import('@plitzi/sdk-shared').ConnectorPagination | undefined;
12
12
  credential: string | undefined;
13
- fields: Record<string, "number" | "file" | "text" | "color" | "switch" | "email" | "date" | "richText" | "image" | "multiImage" | "video" | "link" | "phone" | "option"> | undefined;
13
+ fields: Record<string, "number" | "file" | "text" | "color" | "image" | "switch" | "email" | "date" | "richText" | "multiImage" | "video" | "link" | "phone" | "option"> | undefined;
14
14
  }[];
15
15
  };
16
16
  /** Connector reads: the space's connector listing and one full manifest. Undefined when the URI belongs to
@@ -1,7 +1,7 @@
1
- import { buildTypeRegistry } from "../catalogs/registry.js";
2
- import { cssProperties, cssShorthands } from "../catalogs/index.js";
3
1
  import "../helpers/uris.js";
4
2
  import { guideText } from "../helpers/guide.js";
3
+ import { buildTypeRegistry } from "../catalogs/registry.js";
4
+ import { cssProperties, cssShorthands } from "../catalogs/index.js";
5
5
  import { envelope } from "./envelope.js";
6
6
  //#region src/modules/mcp/resources/core.ts
7
7
  /** Space-independent singletons: the usage guide, the observed type registry and the CSS property catalog.
@@ -0,0 +1,8 @@
1
+ import { Space } from '../helpers';
2
+ import { Env, ResourceEnvelope } from '../types';
3
+ /**
4
+ * The space's own data: JSON its server providers read (`query: '/data/<file>'`, `runtime: 'server'`), never served.
5
+ * The listing says which files there are, so an agent reads the one it needs; a file reads whole — parsed, so its
6
+ * shape is what a binding onto it reads.
7
+ */
8
+ export declare const readDataResource: (space: Space, env: Env, uri: string) => ResourceEnvelope<unknown> | null | undefined;
@@ -0,0 +1,46 @@
1
+ import { afterPrefix, dataFileUri, dataUri } from "../helpers/uris.js";
2
+ import { envelope } from "./envelope.js";
3
+ //#region src/modules/mcp/resources/data.ts
4
+ /**
5
+ * The space's own data: JSON its server providers read (`query: '/data/<file>'`, `runtime: 'server'`), never served.
6
+ * The listing says which files there are, so an agent reads the one it needs; a file reads whole — parsed, so its
7
+ * shape is what a binding onto it reads.
8
+ */
9
+ var readDataResource = (space, env, uri) => {
10
+ if (uri === dataUri(env)) {
11
+ if (!space.data) return envelope({
12
+ available: false,
13
+ reason: "This deployment keeps no space data"
14
+ });
15
+ const { files, version } = space.data;
16
+ return envelope({
17
+ available: true,
18
+ version,
19
+ files: Object.entries(files).map(([path, content]) => ({
20
+ path,
21
+ query: `/data/${path}`,
22
+ bytes: content.length
23
+ }))
24
+ });
25
+ }
26
+ const path = afterPrefix(uri, dataFileUri(env, ""));
27
+ if (path === void 0) return;
28
+ const content = space.data && Object.hasOwn(space.data.files, path) ? space.data.files[path] : void 0;
29
+ if (content === void 0) return null;
30
+ try {
31
+ return envelope({
32
+ path,
33
+ query: `/data/${path}`,
34
+ content: JSON.parse(content)
35
+ });
36
+ } catch {
37
+ return envelope({
38
+ path,
39
+ query: `/data/${path}`,
40
+ text: content,
41
+ problem: "It is not JSON"
42
+ });
43
+ }
44
+ };
45
+ //#endregion
46
+ export { readDataResource };
@@ -1,9 +1,9 @@
1
- import { buildDataSourceCatalog, buildInteractionCatalog } from "../catalogs/observed.js";
2
- import { buildTypeRegistry } from "../catalogs/registry.js";
3
- import { cssProperties, cssShorthands } from "../catalogs/index.js";
4
1
  import { componentSummariesToAI } from "../helpers/components.js";
5
2
  import { componentsUri, connectorsUri, dataSourcesUri, defsUri, flagsUri, foldersUri, fontsUri, interactionsUri, layoutsUri, pagesUri, primerUri, schemaVarsUri, settingsUri, styleVarsUri, typesUri } from "../helpers/uris.js";
6
3
  import { guideQuickstart } from "../helpers/guide.js";
4
+ import { buildDataSourceCatalog, buildInteractionCatalog } from "../catalogs/observed.js";
5
+ import { buildTypeRegistry } from "../catalogs/registry.js";
6
+ import { cssProperties, cssShorthands } from "../catalogs/index.js";
7
7
  import { envelope } from "./envelope.js";
8
8
  import { connectorSummaries } from "./connectors.js";
9
9
  import { definitionRefs, fontsToAI, styleVariablesToAI } from "../tools/operations/style/translator.js";
@@ -1,6 +1,6 @@
1
- import { cssProperties, cssShorthands } from "../catalogs/index.js";
2
1
  import { changesUri } from "../helpers/uris.js";
3
2
  import { guideText } from "../helpers/guide.js";
3
+ import { cssProperties, cssShorthands } from "../catalogs/index.js";
4
4
  import { envelope, jsonContents } from "./envelope.js";
5
5
  import { resourceErrorMessage } from "./canonical.js";
6
6
  import { registerRenderResources } from "./renderGuide.js";
@@ -36,7 +36,7 @@ var registerResources = (server, getSpace, env, log, hasSpace, readChanges) => {
36
36
  return result;
37
37
  };
38
38
  server.registerResource("Guide", "plitzi://guide", {
39
- description: "How to read and write this space with mcp-ai",
39
+ description: "How to read and write this space through this server",
40
40
  mimeType: "text/markdown"
41
41
  }, () => emitStatic("plitzi://guide", () => ({ contents: [{
42
42
  uri: "plitzi://guide",
@@ -157,6 +157,11 @@ var registerResources = (server, getSpace, env, log, hasSpace, readChanges) => {
157
157
  `plitzi://functions/${env}`,
158
158
  "The space’s own server code: which files it is, and the tasks, routes and hosts it declares — its tasks are steps like any server task"
159
159
  ],
160
+ [
161
+ "Data",
162
+ `plitzi://data/${env}`,
163
+ "The space’s own data: JSON files its server providers read as `/data/<file>`, never served — which files there are"
164
+ ],
160
165
  [
161
166
  "Style definitions (schema alias)",
162
167
  `plitzi://schema/${env}/definitions`,
@@ -237,6 +242,11 @@ var registerResources = (server, getSpace, env, log, hasSpace, readChanges) => {
237
242
  `plitzi://functions/${env}/{+path}`,
238
243
  "One file of the space’s functions, whole"
239
244
  ],
245
+ [
246
+ "Data file",
247
+ `plitzi://data/${env}/{+path}`,
248
+ "One file of the space’s data, parsed"
249
+ ],
240
250
  [
241
251
  "Style definition",
242
252
  `plitzi://definitions/${env}/{ref}`,
@@ -197,9 +197,9 @@ and \`patchDefinition\` still changes only some CSS of one class.
197
197
 
198
198
  ## Fit the panel — go wide, stay short
199
199
 
200
- The widget renders in a **side panel** (Claude Desktop, ChatGPT, the Plitzi builder), so it gets a usable width but
201
- very little height: everything past the first screenful costs the user a scroll. Height is the scarce resource —
202
- spend width instead.
200
+ The widget renders in a **side panel** (Claude Desktop, ChatGPT), so it gets a usable width but very little
201
+ height: everything past the first screenful costs the user a scroll. Height is the scarce resource — spend width
202
+ instead.
203
203
 
204
204
  Plain containers are blocks, so doing nothing stacks children **vertically** and produces exactly the tall,
205
205
  half-empty widget to avoid. Choose the axis every time:
@@ -228,9 +228,9 @@ it — and reach for \`max-width\` only to stop one lone card from stretching ac
228
228
 
229
229
  ## Match the host theme — never hardcode a light palette
230
230
 
231
- The widget is embedded in the host's own UI (Claude Desktop, ChatGPT, the builder), and that UI **may be in dark
232
- mode**. A widget painted with fixed light colours is the most common way to ship something unusable: a white card
233
- in a dark chat glares, and — worse — text left at a dark default disappears against the host's dark background.
231
+ The widget is embedded in the host's own UI (Claude Desktop, ChatGPT), and that UI **may be in dark mode**. A
232
+ widget painted with fixed light colours is the most common way to ship something unusable: a white card in a dark
233
+ chat glares, and — worse — text left at a dark default disappears against the host's dark background.
234
234
 
235
235
  The host publishes its palette as CSS variables on the page, so use them for every colour, with a
236
236
  \`light-dark(<light>, <dark>)\` fallback for hosts that send none:
@@ -4,6 +4,7 @@ import { readActionResource } from "./actions.js";
4
4
  import { canonicalUri } from "./canonical.js";
5
5
  import { readConnectorResource } from "./connectors.js";
6
6
  import { readCoreResource } from "./core.js";
7
+ import { readDataResource } from "./data.js";
7
8
  import { readFunctionsResource } from "./functions.js";
8
9
  import { readPrimerResource } from "./primer.js";
9
10
  import { readRenderResource } from "./renderGuide.js";
@@ -18,7 +19,8 @@ var resolvers = [
18
19
  readStyleResource,
19
20
  readConnectorResource,
20
21
  readActionResource,
21
- readFunctionsResource
22
+ readFunctionsResource,
23
+ readDataResource
22
24
  ];
23
25
  /** Resolve a resource URI to its versioned envelope, or null if unknown / not found. */
24
26
  var readResource = (space, env, rawUri) => {
@@ -1,7 +1,7 @@
1
- import { buildDataSourceCatalog, buildInteractionCatalog } from "../catalogs/observed.js";
2
1
  import { componentSummariesToAI, componentView, findComponentByRef } from "../helpers/components.js";
3
2
  import { findElementByRef, findRootByRef } from "../helpers/space.js";
4
3
  import { afterPrefix, componentsUri, dataSourcesUri, elementUri, flagsUri, folderUri, foldersUri, interactionsUri, layoutsUri, pageUri, pagesUri, schemaVarsUri, settingsUri } from "../helpers/uris.js";
4
+ import { buildDataSourceCatalog, buildInteractionCatalog } from "../catalogs/observed.js";
5
5
  import { envelope } from "./envelope.js";
6
6
  import { elementView, folderRefToAI, foldersToAI, layoutSummariesToAI, pageSkeletonToAI, pageStylesToAI, pageSummariesToAI, schemaVariablesToAI, settingsToAI } from "../tools/operations/schema/translator.js";
7
7
  import { treeOf } from "@plitzi/sdk-schema/helpers/components";
@@ -20,10 +20,10 @@ export interface McpServerContext {
20
20
  /** Structured request-log sink. When set, every tool call and resource read emits an McpLogEvent to it (the
21
21
  * consumer renders them); otherwise logging falls back to the console when MCP_DEBUG=1. */
22
22
  logger?: ServerLogger;
23
- /** May the plitzi_render view paint from tool arguments the host is still streaming (see `mcpAi.renderStreaming`)?
24
- * Defaults to true. */
23
+ /** May the plitzi_render view paint from tool arguments the host is still streaming (see
24
+ * `McpOptions.renderStreaming`)? Defaults to true. */
25
25
  renderStreaming?: boolean;
26
- /** Where a rendered widget loads everything external from (see `mcpAi.proxy`). It reaches two places: the render
26
+ /** Where a rendered widget loads everything external from (see `McpOptions.proxy`). It reaches two places: the render
27
27
  * tool, which rewrites the URLs the agent authored, and the App's CSP, which declares the origin those URLs
28
28
  * point at. Absent → neither happens, and a widget's resources load only where the host allows their origin. */
29
29
  proxy?: ResourceProxy;
@@ -1,11 +1,11 @@
1
1
  import { registerApps } from "./apps/index.js";
2
+ import { isCallToolResult } from "./helpers/callToolResult.js";
2
3
  import { NoSpaceError, ReadOnlyGrantError, emptySpace, emptySpaceMessage, noSpaceErrorCode, readOnlyGrantErrorCode } from "./helpers/space.js";
3
4
  import { serverInstructions, widgetsOnlyInstructions } from "./helpers/guide.js";
4
5
  import { createMcpLog } from "./helpers/log.js";
5
6
  import { proxyForTool } from "./proxy/config.js";
6
7
  import { registerResources } from "./resources/register.js";
7
8
  import { tools } from "./tools/index.js";
8
- import { isCallToolResult } from "../ai/toolkit.js";
9
9
  import { randomUUID } from "node:crypto";
10
10
  import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
11
11
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -31,14 +31,15 @@ var createMcpServer = async ({ adapters, getGrant, preview, screenshot, logger,
31
31
  };
32
32
  const loadSpace = async () => {
33
33
  const id = requireSpaceId();
34
- const [schema, style, catalog, connectors, actions, actionTasks, functions] = await Promise.all([
34
+ const [schema, style, catalog, connectors, actions, actionTasks, functions, data] = await Promise.all([
35
35
  adapters.getSchema?.(id, MCP_ENV),
36
36
  adapters.getStyle?.(id, MCP_ENV),
37
37
  adapters.getComponentCatalog?.(id, MCP_ENV).catch(() => void 0),
38
38
  adapters.getConnectors?.(id).catch(() => void 0),
39
39
  adapters.getActions?.(id).catch(() => void 0),
40
40
  adapters.getActionTasks?.(id).catch(() => void 0),
41
- adapters.getFunctions?.(id).catch(() => void 0)
41
+ adapters.getFunctions?.(id).catch(() => void 0),
42
+ adapters.getData?.(id).catch(() => void 0)
42
43
  ]);
43
44
  if (!schema || !style) throw new Error(emptySpaceMessage);
44
45
  return {
@@ -48,10 +49,11 @@ var createMcpServer = async ({ adapters, getGrant, preview, screenshot, logger,
48
49
  connectors: connectors ?? [],
49
50
  actions: actions ?? [],
50
51
  actionTasks,
51
- ...functions ? { functions } : {}
52
+ ...functions ? { functions } : {},
53
+ ...data ? { data } : {}
52
54
  };
53
55
  };
54
- const { saveSchema, saveStyle, saveConnector, deleteConnector, saveAction, deleteAction, saveFunctions, tryFunction } = adapters;
56
+ const { saveSchema, saveStyle, saveConnector, deleteConnector, saveAction, deleteAction, saveFunctions, tryFunction, saveData } = adapters;
55
57
  const write = {
56
58
  userId: grant?.userId,
57
59
  batch: randomUUID()
@@ -63,13 +65,14 @@ var createMcpServer = async ({ adapters, getGrant, preview, screenshot, logger,
63
65
  deleteConnector: deleteConnector ? (id) => deleteConnector(requireWritableSpaceId(), id) : void 0,
64
66
  saveAction: saveAction ? (entry) => saveAction(requireWritableSpaceId(), entry) : void 0,
65
67
  deleteAction: deleteAction ? (id) => deleteAction(requireWritableSpaceId(), id) : void 0,
66
- saveFunctions: saveFunctions ? (files, base) => saveFunctions(requireWritableSpaceId(), files, base, write) : void 0
68
+ saveFunctions: saveFunctions ? (files, base) => saveFunctions(requireWritableSpaceId(), files, base, write) : void 0,
69
+ saveData: saveData ? (files, base) => saveData(requireWritableSpaceId(), files, base, write) : void 0
67
70
  };
68
71
  let spacePromise;
69
72
  const getSpace = () => spacePromise ??= loadSpace();
70
73
  const server = new McpServer({
71
74
  name: "plitzi-mcp",
72
- version: "0.38.4"
75
+ version: "0.38.6"
73
76
  }, { instructions: hasSpace ? serverInstructions : widgetsOnlyInstructions });
74
77
  const { getChanges } = adapters;
75
78
  registerResources(server, getSpace, MCP_ENV, log, hasSpace, getChanges ? (query) => getChanges(requireSpaceId(), MCP_ENV, query) : void 0);
@@ -126,7 +129,8 @@ var createMcpServer = async ({ adapters, getGrant, preview, screenshot, logger,
126
129
  const config = {
127
130
  title: tool.title,
128
131
  description: tool.description,
129
- inputSchema: tool.inputShape
132
+ inputSchema: tool.inputShape,
133
+ annotations: { readOnlyHint: tool.access === "read" }
130
134
  };
131
135
  if (tool.ui) {
132
136
  registerAppTool(server, tool.name, {
@@ -7,6 +7,7 @@ import { upsertAction } from "../operations/actions/upsertAction.js";
7
7
  import { deleteConnector } from "../operations/connectors/deleteConnector.js";
8
8
  import { patchConnector } from "../operations/connectors/patchConnector.js";
9
9
  import { upsertConnector } from "../operations/connectors/upsertConnector.js";
10
+ import { deleteDataFile, upsertDataFile } from "../operations/data/index.js";
10
11
  import { deleteFunctionFile, upsertFunctionFile } from "../operations/functions/index.js";
11
12
  import { deleteBinding } from "../operations/schema/bindings/deleteBinding.js";
12
13
  import { patchBinding } from "../operations/schema/bindings/patchBinding.js";
@@ -113,6 +114,8 @@ var executeOp = (space, env, op) => {
113
114
  case "deleteAction": return deleteAction(space, env, op);
114
115
  case "upsertFunctionFile": return upsertFunctionFile(space, env, op);
115
116
  case "deleteFunctionFile": return deleteFunctionFile(space, env, op);
117
+ case "upsertDataFile": return upsertDataFile(space, env, op);
118
+ case "deleteDataFile": return deleteDataFile(space, env, op);
116
119
  default: return fail("type", `Unknown operation "${op.type}"`, "See the Operation union");
117
120
  }
118
121
  };
@@ -132,7 +135,8 @@ var applyOperations = (space, env, ops) => {
132
135
  deletedConnectors: [],
133
136
  changedActions: [],
134
137
  deletedActions: [],
135
- changedFunctions: false
138
+ changedFunctions: false,
139
+ changedData: false
136
140
  };
137
141
  const stale = /* @__PURE__ */ new Set();
138
142
  const elements = /* @__PURE__ */ new Set();
@@ -173,6 +177,10 @@ var applyOperations = (space, env, ops) => {
173
177
  case "deleteFunctionFile":
174
178
  outcome.changedFunctions = true;
175
179
  break;
180
+ case "upsertDataFile":
181
+ case "deleteDataFile":
182
+ outcome.changedData = true;
183
+ break;
176
184
  default: if (isStyleOp(op.type)) outcome.changedStyle = true;
177
185
  else outcome.changedSchema = true;
178
186
  }