@plitzi/sdk-server 0.32.9 → 0.32.11

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 +20 -0
  2. package/dist/core/http/dispatcher.js +45 -9
  3. package/dist/core/services/mcp.js +9 -7
  4. package/dist/helpers/serverLog.js +33 -0
  5. package/dist/index.js +2 -1
  6. package/dist/mcp.d.ts +2 -0
  7. package/dist/mcp.js +7 -0
  8. package/dist/modules/mcp/apps/example/view.tsx +70 -0
  9. package/dist/modules/mcp/apps/index.js +12 -0
  10. package/dist/modules/mcp/apps/render/index.js +16 -0
  11. package/dist/modules/mcp/apps/render/view.tsx +108 -0
  12. package/dist/modules/mcp/apps/shared/bundle.js +22 -0
  13. package/dist/modules/mcp/apps/shared/page.js +23 -0
  14. package/dist/modules/mcp/apps/shared/registerApp.js +27 -0
  15. package/dist/modules/mcp/apps/shared/resolve.js +5 -0
  16. package/dist/modules/mcp/apps/shared/shell.ejs +37 -0
  17. package/dist/modules/mcp/apps/shared/zodEnglishOnly.js +23 -0
  18. package/dist/modules/mcp/handler.js +8 -6
  19. package/dist/modules/mcp/helpers/log.js +20 -10
  20. package/dist/modules/mcp/resources/register.js +11 -5
  21. package/dist/modules/mcp/resources/renderGuide.js +17 -7
  22. package/dist/modules/mcp/server.js +21 -12
  23. package/dist/modules/mcp/tools/apply/index.js +1 -1
  24. package/dist/modules/mcp/tools/render.js +3 -3
  25. package/dist/modules/ssr/registerExternalPlugins.js +1 -1
  26. package/dist/modules/ssr/template.js +1 -1
  27. package/dist/plugins/compile.js +1 -1
  28. package/dist/plugins/copy.js +1 -1
  29. package/dist/plugins/manager.js +3 -1
  30. package/dist/plugins/validate.js +23 -0
  31. package/dist/src/core/http/dispatcher.test.d.ts +1 -0
  32. package/dist/src/core/http/types.d.ts +3 -0
  33. package/dist/src/core/requestParser.d.ts +3 -0
  34. package/dist/src/helpers/buildResponseHelpers.d.ts +2 -0
  35. package/dist/src/helpers/serverLog.d.ts +10 -0
  36. package/dist/src/index.d.ts +2 -1
  37. package/dist/src/mcp.d.ts +14 -0
  38. package/dist/src/modules/mcp/apps/apps.test.d.ts +1 -0
  39. package/dist/src/modules/mcp/apps/example/index.d.ts +8 -0
  40. package/dist/src/modules/mcp/apps/example/view.d.ts +1 -0
  41. package/dist/src/modules/mcp/apps/index.d.ts +8 -0
  42. package/dist/src/modules/mcp/apps/render/index.d.ts +3 -0
  43. package/dist/src/modules/mcp/apps/render/view.d.ts +1 -0
  44. package/dist/src/modules/mcp/apps/shared/bundle.d.ts +1 -0
  45. package/dist/src/modules/mcp/apps/shared/index.d.ts +2 -0
  46. package/dist/src/modules/mcp/apps/shared/page.d.ts +2 -0
  47. package/dist/src/modules/mcp/apps/shared/registerApp.d.ts +5 -0
  48. package/dist/src/modules/mcp/apps/shared/resolve.d.ts +1 -0
  49. package/dist/src/modules/mcp/apps/shared/zodEnglishOnly.d.ts +4 -0
  50. package/dist/src/modules/mcp/e2e/connector.test.d.ts +1 -0
  51. package/dist/src/modules/mcp/e2e/host.test.d.ts +1 -0
  52. package/dist/src/modules/mcp/e2e/index.d.ts +6 -0
  53. package/dist/src/modules/mcp/e2e/mcpEndpoint.d.ts +20 -0
  54. package/dist/src/modules/mcp/e2e/postMessageChannel.d.ts +22 -0
  55. package/dist/src/modules/mcp/e2e/renderingHost.d.ts +14 -0
  56. package/dist/src/modules/mcp/handler.d.ts +11 -7
  57. package/dist/src/modules/mcp/helpers/log.d.ts +3 -3
  58. package/dist/src/modules/mcp/index.d.ts +1 -0
  59. package/dist/src/modules/mcp/resources/register.d.ts +1 -1
  60. package/dist/src/modules/mcp/resources/renderGuide.d.ts +3 -2
  61. package/dist/src/modules/mcp/server.d.ts +4 -12
  62. package/dist/src/modules/mcp/tools/apply/index.d.ts +3 -3
  63. package/dist/src/modules/mcp/tools/operations/index.d.ts +6 -6
  64. package/dist/src/modules/mcp/tools/operations/schema/operations.d.ts +1 -1
  65. package/dist/src/modules/mcp/tools/operations/schema/settings/patchSettings.d.ts +1 -1
  66. package/dist/src/modules/mcp/tools/operations/style/operations.d.ts +2 -2
  67. package/dist/src/modules/mcp/tools/operations/style/shared.d.ts +1 -1
  68. package/dist/src/modules/mcp/tools/operations/style/variables/deleteStyleVariable.d.ts +1 -1
  69. package/dist/src/modules/mcp/tools/operations/style/variables/upsertStyleVariable.d.ts +1 -1
  70. package/dist/src/modules/mcp/tools/preview.d.ts +3 -3
  71. package/dist/src/modules/mcp/tools/render.d.ts +3 -3
  72. package/dist/src/modules/mcp/tools/screenshot.d.ts +3 -3
  73. package/dist/src/modules/mcp/tools/shared/tool.d.ts +4 -7
  74. package/dist/src/modules/mcp/tools/validate.d.ts +3 -3
  75. package/dist/src/modules/mcp/types/appTypes.d.ts +13 -0
  76. package/dist/src/modules/mcp/types/index.d.ts +1 -0
  77. package/dist/src/plugins/validate.d.ts +9 -0
  78. package/dist/src/plugins/validate.test.d.ts +1 -0
  79. package/package.json +13 -4
  80. package/dist/modules/mcp/resources/renderApp.js +0 -182
  81. package/dist/server.d.ts +0 -1
  82. package/dist/server.js +0 -107
  83. package/dist/src/modules/mcp/resources/renderApp.d.ts +0 -3
@@ -4,7 +4,7 @@ import { resourceErrorMessage } from "./canonical.js";
4
4
  import { envelope, jsonContents } from "./envelope.js";
5
5
  import { readResource } from "./router.js";
6
6
  import { registerRenderResources } from "./renderGuide.js";
7
- import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp";
7
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  //#region src/modules/mcp/resources/register.ts
9
9
  /** Register every resource on the MCP server: fixed listings plus templated per-item reads. The space is
10
10
  * loaded lazily via getSpace, so listing resources never touches the store — only reading one does. */
@@ -21,19 +21,25 @@ var registerResources = (server, getSpace, env, log) => {
21
21
  throw error;
22
22
  }
23
23
  };
24
+ const emitStatic = (uri, read) => {
25
+ const start = performance.now();
26
+ const result = read();
27
+ log.resourceRead(uri, performance.now() - start);
28
+ return result;
29
+ };
24
30
  server.registerResource("Guide", "plitzi://guide", {
25
31
  description: "How to read and write this space with mcp-ai",
26
32
  mimeType: "text/markdown"
27
- }, () => ({ contents: [{
33
+ }, () => emitStatic("plitzi://guide", () => ({ contents: [{
28
34
  uri: "plitzi://guide",
29
35
  mimeType: "text/markdown",
30
36
  text: guideText
31
- }] }));
37
+ }] })));
32
38
  server.registerResource("CSS properties", "plitzi://css-properties", {
33
39
  description: "Valid kebab-case CSS property keys",
34
40
  mimeType: "application/json"
35
- }, () => jsonContents("plitzi://css-properties", envelope(cssProperties)));
36
- registerRenderResources(server);
41
+ }, () => emitStatic("plitzi://css-properties", () => jsonContents("plitzi://css-properties", envelope(cssProperties))));
42
+ registerRenderResources(server, log);
37
43
  const fixed = [
38
44
  [
39
45
  "Primer",
@@ -168,19 +168,29 @@ connects that source to a descendant's field.
168
168
  The tool returns \`rendered: false\` with \`errors: [{ path, message, hint }]\`. Read the hint, fix that one op, and
169
169
  retry — you never lose the rest of the batch.
170
170
  `;
171
- var registerRenderResources = (server) => {
171
+ var registerRenderResources = (server, log) => {
172
172
  server.registerResource("Render guide", RENDER_GUIDE_URI, {
173
173
  description: "How to author a plitzi_render widget: operations, element types, styling, examples.",
174
174
  mimeType: "text/markdown"
175
- }, () => ({ contents: [{
176
- uri: RENDER_GUIDE_URI,
177
- mimeType: "text/markdown",
178
- text: renderGuideText
179
- }] }));
175
+ }, () => {
176
+ const start = performance.now();
177
+ const contents = { contents: [{
178
+ uri: RENDER_GUIDE_URI,
179
+ mimeType: "text/markdown",
180
+ text: renderGuideText
181
+ }] };
182
+ log.resourceRead(RENDER_GUIDE_URI, performance.now() - start);
183
+ return contents;
184
+ });
180
185
  server.registerResource("Render element types", RENDER_TYPES_URI, {
181
186
  description: "Built-in element types a plitzi_render widget can use, with descriptions.",
182
187
  mimeType: "application/json"
183
- }, () => jsonContents(RENDER_TYPES_URI, envelope(renderTypes())));
188
+ }, () => {
189
+ const start = performance.now();
190
+ const contents = jsonContents(RENDER_TYPES_URI, envelope(renderTypes()));
191
+ log.resourceRead(RENDER_TYPES_URI, performance.now() - start);
192
+ return contents;
193
+ });
184
194
  };
185
195
  //#endregion
186
196
  export { RENDER_GUIDE_URI, RENDER_TYPES_URI, registerRenderResources };
@@ -1,18 +1,19 @@
1
+ import { registerApps } from "./apps/index.js";
1
2
  import { emptySpace, emptySpaceMessage, unauthorizedSpaceMessage } from "./helpers/space.js";
2
3
  import { serverInstructions } from "./helpers/guide.js";
3
4
  import { createMcpLog } from "./helpers/log.js";
4
5
  import { registerResources } from "./resources/register.js";
5
- import { registerRenderApp } from "./resources/renderApp.js";
6
6
  import { tools } from "./tools/index.js";
7
7
  import { isCallToolResult } from "../ai/toolkit.js";
8
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp";
8
+ import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
9
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
10
  //#region src/modules/mcp/server.ts
10
11
  var MCP_ENV = "main";
11
12
  var asText = (data) => ({ content: [{
12
13
  type: "text",
13
14
  text: JSON.stringify(data)
14
15
  }] });
15
- var createMcpServer = ({ adapters, getSpaceId, preview, screenshot, logger, renderApp }) => {
16
+ var createMcpServer = ({ adapters, getSpaceId, preview, screenshot, logger }) => {
16
17
  const log = createMcpLog(logger);
17
18
  let spaceIdPromise;
18
19
  const requireSpaceId = () => spaceIdPromise ??= getSpaceId().then((id) => {
@@ -42,10 +43,10 @@ var createMcpServer = ({ adapters, getSpaceId, preview, screenshot, logger, rend
42
43
  const getSpace = () => spacePromise ??= loadSpace();
43
44
  const server = new McpServer({
44
45
  name: "plitzi-mcp",
45
- version: "0.32.9"
46
+ version: "0.32.11"
46
47
  }, { instructions: serverInstructions });
47
48
  registerResources(server, getSpace, MCP_ENV, log);
48
- if (renderApp) registerRenderApp(server, renderApp.sdkBase, renderApp.devMode ?? false);
49
+ registerApps(server);
49
50
  const toolContext = async () => ({
50
51
  space: await getSpace(),
51
52
  env: MCP_ENV,
@@ -63,12 +64,7 @@ var createMcpServer = ({ adapters, getSpaceId, preview, screenshot, logger, rend
63
64
  });
64
65
  for (const tool of tools) {
65
66
  if (tool.requires === "screenshot" && !screenshot) continue;
66
- server.registerTool(tool.name, {
67
- title: tool.title,
68
- description: tool.description,
69
- inputSchema: tool.inputShape,
70
- ...tool.ui && renderApp ? { _meta: { ui: tool.ui } } : {}
71
- }, async (args) => {
67
+ const run = async (args) => {
72
68
  const start = performance.now();
73
69
  try {
74
70
  const result = await tool.execute(args, tool.spaceless ? spacelessContext() : await toolContext());
@@ -78,7 +74,20 @@ var createMcpServer = ({ adapters, getSpaceId, preview, screenshot, logger, rend
78
74
  log.toolCall(tool.name, args, performance.now() - start, error);
79
75
  throw error;
80
76
  }
81
- });
77
+ };
78
+ const config = {
79
+ title: tool.title,
80
+ description: tool.description,
81
+ inputSchema: tool.inputShape
82
+ };
83
+ if (tool.ui) {
84
+ registerAppTool(server, tool.name, {
85
+ ...config,
86
+ _meta: { ui: tool.ui }
87
+ }, run);
88
+ continue;
89
+ }
90
+ server.registerTool(tool.name, config, run);
82
91
  }
83
92
  return server;
84
93
  };
@@ -6,7 +6,7 @@ import { defineTool } from "../shared/tool.js";
6
6
  import { validateOperations } from "../shared/validator/index.js";
7
7
  import { auditResources } from "../shared/validator/audit.js";
8
8
  import { z } from "zod";
9
- import { validateSchema } from "@plitzi/sdk-schema";
9
+ import { validateSchema } from "@plitzi/sdk-schema/helpers/schemaValidator";
10
10
  //#region src/modules/mcp/tools/apply/index.ts
11
11
  var applyShape = {
12
12
  environment,
@@ -1,12 +1,12 @@
1
+ import { RENDER_APP_URI } from "../apps/render/index.js";
1
2
  import { emptySpace } from "../helpers/space.js";
2
- import { RENDER_APP_URI } from "../resources/renderApp.js";
3
3
  import { operations } from "./operations/index.js";
4
4
  import { applyOperations } from "./apply/dispatch.js";
5
5
  import { defineTool } from "./shared/tool.js";
6
6
  import { validateOperations } from "./shared/validator/index.js";
7
7
  import { auditResources } from "./shared/validator/audit.js";
8
- import { validateSchema } from "@plitzi/sdk-schema";
9
- import { generateCache } from "@plitzi/sdk-style";
8
+ import { validateSchema } from "@plitzi/sdk-schema/helpers/schemaValidator";
9
+ import { generateCache } from "@plitzi/sdk-style/StyleHelper";
10
10
  //#region src/modules/mcp/tools/render.ts
11
11
  var HOST_PAGE_REF = "render";
12
12
  var seedSpace = () => {
@@ -1,6 +1,6 @@
1
+ import path from "node:path";
1
2
  import crypto from "node:crypto";
2
3
  import fs from "node:fs/promises";
3
- import path from "node:path";
4
4
  //#region src/modules/ssr/registerExternalPlugins.ts
5
5
  var MANIFEST_TTL_MS = 600 * 1e3;
6
6
  var manifestCache = /* @__PURE__ */ new Map();
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
- import fs from "node:fs";
3
2
  import { fileURLToPath } from "node:url";
3
+ import fs from "node:fs";
4
4
  import ejs from "ejs";
5
5
  //#region src/modules/ssr/template.ts
6
6
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -1,6 +1,6 @@
1
- import fs from "node:fs/promises";
2
1
  import path from "node:path";
3
2
  import esbuild from "esbuild";
3
+ import fs from "node:fs/promises";
4
4
  //#region src/plugins/compile.ts
5
5
  var EXTERNAL = [
6
6
  "react",
@@ -1,5 +1,5 @@
1
- import fs from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import fs from "node:fs/promises";
3
3
  //#region src/plugins/copy.ts
4
4
  var copyPlugin = async (src, destDir, filename) => {
5
5
  const dest = path.join(destDir, filename);
@@ -1,8 +1,9 @@
1
1
  import { compilePlugin } from "./compile.js";
2
2
  import { copyPlugin } from "./copy.js";
3
3
  import { detectAction, isComponentSource } from "./detect.js";
4
- import fs from "node:fs/promises";
4
+ import { assertPluginSources } from "./validate.js";
5
5
  import path from "node:path";
6
+ import fs from "node:fs/promises";
6
7
  //#region src/plugins/manager.ts
7
8
  var META_FILE = "meta.json";
8
9
  var DEFAULT_TTL_MS = 10080 * 60 * 1e3;
@@ -19,6 +20,7 @@ var PluginManager = class {
19
20
  nameIndex = /* @__PURE__ */ new Map();
20
21
  devMode = false;
21
22
  constructor(plugins, cacheDir, ttlMs, devMode = false) {
23
+ assertPluginSources(plugins);
22
24
  this.plugins = plugins;
23
25
  this.outputDir = path.resolve(process.cwd(), cacheDir ?? DEFAULT_CACHE_DIR);
24
26
  this.ttlMs = ttlMs ?? DEFAULT_TTL_MS;
@@ -0,0 +1,23 @@
1
+ import { isComponentSource } from "./detect.js";
2
+ import fs from "node:fs";
3
+ //#region src/plugins/validate.ts
4
+ var isRemote = (js) => js.startsWith("http://") || js.startsWith("https://");
5
+ /** The file a plugin source needs on disk, or null when nothing local is involved — a remote URL (fetched and
6
+ * cached at request time) or a component handed in directly. */
7
+ var localSourcePath = (source) => {
8
+ if (isComponentSource(source) || isRemote(source.js)) return null;
9
+ return source.js;
10
+ };
11
+ /** Fail at boot on a plugin whose entry file is missing. A plugin entry is a plain string path in the server
12
+ * config, so nothing — not tsc, not eslint — notices when that file is moved or deleted; without this check the
13
+ * server starts happily and only dies on the first render that needs the plugin, with an esbuild resolve error
14
+ * far from its cause. */
15
+ var assertPluginSources = (plugins) => {
16
+ const missing = Object.entries(plugins).map(([name, source]) => ({
17
+ name,
18
+ file: localSourcePath(source)
19
+ })).filter((entry) => entry.file !== null && !fs.existsSync(entry.file)).map((entry) => ` ${entry.name}: ${entry.file ?? ""}`);
20
+ if (missing.length > 0) throw new Error(`Plugin entry file not found (declared in the server config):\n${missing.join("\n")}`);
21
+ };
22
+ //#endregion
23
+ export { assertPluginSources, localSourcePath };
@@ -0,0 +1 @@
1
+ export {};
@@ -10,6 +10,9 @@ export interface BaseContext {
10
10
  res: SSRResponseHelpers;
11
11
  config: SSRServerConfig;
12
12
  port: number;
13
+ /** Set by the stage that answers when the path alone does not identify the work (the MCP endpoint serves every
14
+ * JSON-RPC method on the same URL). The dispatcher folds it into the access log. */
15
+ operation?: string;
13
16
  }
14
17
  export interface SSRContext extends BaseContext {
15
18
  renderFn: SSRTemplateFn;
@@ -1,4 +1,7 @@
1
1
  import { SSRRequest } from '@plitzi/sdk-shared';
2
2
  import { IncomingMessage } from 'node:http';
3
3
  export declare const parseRequest: (raw: IncomingMessage) => SSRRequest;
4
+ /** The public origin the request was addressed to (proxy-aware via x-forwarded-proto, port included), or an empty
5
+ * string when the request carries no usable authority. */
6
+ export declare const requestOrigin: (req: SSRRequest) => string;
4
7
  export declare const readRawBody: (raw: IncomingMessage) => Promise<string>;
@@ -1,6 +1,8 @@
1
1
  import { SSRResponseHelpers } from '@plitzi/sdk-shared';
2
2
  export type RawResponse = {
3
3
  headersSent: boolean;
4
+ /** The status actually written to the wire — the access log reads it once the response is out. */
5
+ statusCode: number;
4
6
  setHeader(name: string, value: string | number | readonly string[]): unknown;
5
7
  getHeaders(): Record<string, string | number | readonly string[]>;
6
8
  writeHead(statusCode: number, headers?: Record<string, string | number | readonly string[]>): unknown;
@@ -0,0 +1,10 @@
1
+ import { ServerLogEvent, ServerLogger } from '@plitzi/sdk-shared';
2
+ /** One line for any {@link ServerLogEvent}: an HTTP request reads as an access-log line
3
+ * (`[SSR] GET /pricing 200 12ms ok`), the MCP events as what happened inside one
4
+ * (`[mcp] tools/call plitzi_apply {operations:[3]} 41ms ok`). Events arrive PII-free — the dispatcher strips
5
+ * query values and collects no headers, cookies or IPs, and tool args are summarised by shape — so rendering is
6
+ * a pure format. */
7
+ export declare const renderLogEvent: (event: ServerLogEvent) => string;
8
+ /** A drop-in `SSRServerConfig.logger` for consumers that just want the log on the console. Consumers with their
9
+ * own logging stack should pass their own sink instead and read the structured event. */
10
+ export declare const consoleLogger: ServerLogger;
@@ -1,5 +1,6 @@
1
1
  export { createServer, createSSRServer, createMCPServer, resolveServices } from './core/createServer';
2
2
  export { registerHealthCheck, buildHealthPayload } from './core/health';
3
+ export { consoleLogger, renderLogEvent } from './helpers/serverLog';
3
4
  export type { HealthCheckApp, HealthIdentity } from './core/health';
4
5
  export { createJsonAdapters } from './adapters/jsonAdapters';
5
6
  export { AIEngine, toolResponseOk, toolResponseErr, zodToJsonSchema, getAllowedModes, bindTools, isToolActive, resolveToolHandler, isCallToolResult, toolResponseFromResult } from './modules/ai';
@@ -8,7 +9,7 @@ export { createPreview, createMemoryDraftStore, PREVIEW_TOKEN_PARAM } from './mo
8
9
  export { apply, search, read, validate, applyShape, searchShape, readShape, validateShape, operation, tools } from './modules/mcp/tools';
9
10
  export type { JsonAdaptersConfig } from './adapters/jsonAdapters';
10
11
  export type { ResolvedServices } from './core/createServer';
11
- export type { McpServerContext, PreviewClient, PreviewRequestBody, PreviewResult, HttpPreviewClientConfig, ScreenshotClient, ScreenshotImage, ScreenshotResult, Viewport, HttpScreenshotClientConfig } from './modules/mcp';
12
+ export type { McpServerContext, McpRequestOptions, PreviewClient, PreviewRequestBody, PreviewResult, HttpPreviewClientConfig, ScreenshotClient, ScreenshotImage, ScreenshotResult, Viewport, HttpScreenshotClientConfig } from './modules/mcp';
12
13
  export type { ApplyInput, SearchInput, SearchResponse, SearchHit, ReadInput, ReadResponse, ReadHit, ValidateInput, WriteResponse, WriteElement, Persisters, Operation, ToolDef, ToolContext } from './modules/mcp/tools';
13
14
  export type { Space } from './modules/mcp/helpers';
14
15
  export type { Env } from './modules/mcp/types';
@@ -0,0 +1,14 @@
1
+ /** The MCP role's entry point: everything an MCP deployment wires, and nothing else.
2
+ *
3
+ * Import it as `@plitzi/sdk-server/mcp`. The package root is a barrel over every service (SSR, RSC, plugins, the
4
+ * AI engine…), so pulling `createMCPServer` from there makes the process load that whole graph — including the
5
+ * React element packages its types come from — where this entry loads only the MCP server. */
6
+ export { createMCPServer } from './core/server/mcpServer';
7
+ export { consoleLogger, renderLogEvent } from './helpers/serverLog';
8
+ export { createMcpServer, handleMcp, serveMcp, readMcpBody } from './modules/mcp/handler';
9
+ export { createHttpPreviewClient } from './modules/mcp/previewClient';
10
+ export { createHttpScreenshotClient } from './modules/mcp/screenshotClient';
11
+ export type { McpRequestOptions } from './modules/mcp/handler';
12
+ export type { McpServerContext } from './modules/mcp/server';
13
+ export type { PreviewClient, ScreenshotClient } from './modules/mcp/types';
14
+ export type { HealthIdentity } from './core/health';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { McpApp } from '../../types';
2
+ /** The reference app: the smallest definition that works, kept as the template a new app is copied from.
3
+ *
4
+ * It is deliberately NOT in the `apps` list — nothing renders it, since no tool points at it — so it costs a
5
+ * real deployment nothing while the test suite still builds it, which is what keeps it from rotting. To turn it
6
+ * into a real app: add it to `apps` in ../index.ts and give a tool `ui: { resourceUri: EXAMPLE_APP_URI }`. */
7
+ export declare const EXAMPLE_APP_URI = "ui://plitzi/example.html";
8
+ export declare const exampleApp: McpApp;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { McpApp } from '../types';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ /** Every MCP App this server serves. A new one is a folder beside `render/` (its definition + its view) and one
4
+ * line here — the tool that opens it points at its `uri` through `ui: { resourceUri }`. Start from `example/`,
5
+ * which is that same shape stripped to the minimum. */
6
+ export declare const apps: McpApp[];
7
+ export declare const registerApps: (server: McpServer) => void;
8
+ export { RENDER_APP_URI } from './render';
@@ -0,0 +1,3 @@
1
+ import { McpApp } from '../../types';
2
+ export declare const RENDER_APP_URI = "ui://plitzi/render.html";
3
+ export declare const renderApp: McpApp;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const bundle: (entry: string) => Promise<string>;
@@ -0,0 +1,2 @@
1
+ export { registerApp } from './registerApp';
2
+ export { require } from './resolve';
@@ -0,0 +1,2 @@
1
+ import { McpApp } from '../../types';
2
+ export declare const page: (app: McpApp) => Promise<string>;
@@ -0,0 +1,5 @@
1
+ import { McpApp } from '../../types';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ /** Serves the app as a self-contained page: no import map, no asset mounts, no cross-origin fetches, so the
4
+ * strictest host sandbox runs it and no deployment has to serve anything extra. */
5
+ export declare const registerApp: (server: McpServer, app: McpApp) => void;
@@ -0,0 +1 @@
1
+ export declare const require: NodeJS.Require;
@@ -0,0 +1,4 @@
1
+ import { Plugin } from 'esbuild';
2
+ /** Drops zod's ~40 translations (194 KB): they are re-exported as a namespace, which no bundler can tree-shake.
3
+ * English survives because zod imports `locales/en.js` directly, not through this index. */
4
+ export declare const zodEnglishOnly: Plugin;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ /** An MCP Apps host in two halves, so the App can be exercised without Claude Desktop or ChatGPT: the CONNECTOR
2
+ * (a real MCP client over Streamable HTTP) and the RENDERER (the ui:// page in a DOM, driven by AppBridge). */
3
+ export { readAppPage, startMcpEndpoint } from './mcpEndpoint';
4
+ export { startRenderingHost } from './renderingHost';
5
+ export type { AppPage, McpEndpoint } from './mcpEndpoint';
6
+ export type { RenderingHost } from './renderingHost';
@@ -0,0 +1,20 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ /** A live MCP endpoint on loopback — the same handler a deployment serves, transport and all. */
3
+ export interface McpEndpoint {
4
+ url: string;
5
+ client: Client;
6
+ /** Raw fetch, for the probes a connector fires outside the JSON-RPC session. */
7
+ request: (init?: RequestInit & {
8
+ path?: string;
9
+ }) => Promise<Response>;
10
+ close: () => Promise<void>;
11
+ }
12
+ export interface AppPage {
13
+ html: string;
14
+ mimeType?: string;
15
+ meta?: Record<string, unknown>;
16
+ }
17
+ /** Start the endpoint and connect a real MCP client to it, as a remote connector does. */
18
+ export declare const startMcpEndpoint: () => Promise<McpEndpoint>;
19
+ /** Read a ui:// resource. A page that came back as a blob is a bug a host would meet as a blank frame. */
20
+ export declare const readAppPage: (endpoint: McpEndpoint, uri: string) => Promise<AppPage>;
@@ -0,0 +1,22 @@
1
+ import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
2
+ import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
3
+ import { JSDOM } from 'jsdom';
4
+ export interface ParentStub {
5
+ postMessage: (message: unknown) => void;
6
+ attach: (deliver: (message: unknown) => void) => void;
7
+ }
8
+ /** Stands in for the window an iframe posts to. It BUFFERS: the page starts parsing the moment the DOM is built,
9
+ * so without the queue its `ui/initialize` is dropped and the handshake silently never completes. */
10
+ export declare const parentStub: () => ParentStub;
11
+ /** The host end of that channel, as a transport an AppBridge can connect to. */
12
+ export declare class HostBridgeTransport implements Transport {
13
+ private readonly window;
14
+ private readonly parent;
15
+ onmessage?: (message: JSONRPCMessage) => void;
16
+ onclose?: () => void;
17
+ onerror?: (error: Error) => void;
18
+ constructor(window: JSDOM['window'], parent: ParentStub);
19
+ start(): Promise<void>;
20
+ send(message: JSONRPCMessage): Promise<void>;
21
+ close(): Promise<void>;
22
+ }
@@ -0,0 +1,14 @@
1
+ import { AppBridge } from '@modelcontextprotocol/ext-apps/app-bridge';
2
+ import { JSDOM } from 'jsdom';
3
+ import { McpUiHostContext, McpUiToolResultNotification } from '@modelcontextprotocol/ext-apps';
4
+ export interface RenderingHost {
5
+ bridge: AppBridge;
6
+ window: JSDOM['window'];
7
+ /** Deliver a tool result and let the App paint before assertions run. */
8
+ showResult: (result: McpUiToolResultNotification['params']) => Promise<void>;
9
+ text: () => string;
10
+ close: () => void;
11
+ }
12
+ /** Load a ui:// page into a DOM and complete the MCP Apps handshake against it, through the official AppBridge.
13
+ * Resolves once the App is connected, so a test can push a tool result straight away. */
14
+ export declare const startRenderingHost: (html: string, hostContext?: McpUiHostContext) => Promise<RenderingHost>;
@@ -1,12 +1,16 @@
1
1
  import { createMcpServer } from './server';
2
2
  import { PreviewClient, ScreenshotClient } from './types';
3
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
4
- import { SSRAdapters, SSRRequest, McpLogger } from '@plitzi/sdk-shared';
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { SSRAdapters, SSRRequest, ServerLogger } from '@plitzi/sdk-shared';
5
5
  import { IncomingMessage, ServerResponse } from 'node:http';
6
+ /** Per-request wiring the MCP service does not resolve itself: the renderer clients and the log sink. Everything
7
+ * here is optional — the service degrades feature by feature. */
8
+ export type McpRequestOptions = {
9
+ preview?: PreviewClient;
10
+ screenshot?: ScreenshotClient;
11
+ logger?: ServerLogger;
12
+ };
6
13
  export declare const readMcpBody: (req: IncomingMessage) => Promise<unknown>;
7
- export declare const serveMcp: (raw: IncomingMessage, res: ServerResponse, server: McpServer) => Promise<void>;
8
- export declare const handleMcp: (raw: IncomingMessage, res: ServerResponse, req: SSRRequest, adapters: SSRAdapters, preview?: PreviewClient, screenshot?: ScreenshotClient, logger?: McpLogger, renderApp?: {
9
- sdkBase: string;
10
- devMode?: boolean;
11
- }) => Promise<void>;
14
+ export declare const serveMcp: (raw: IncomingMessage, res: ServerResponse, server: McpServer) => Promise<string | undefined>;
15
+ export declare const handleMcp: (raw: IncomingMessage, res: ServerResponse, req: SSRRequest, adapters: SSRAdapters, options?: McpRequestOptions) => Promise<string | undefined>;
12
16
  export { createMcpServer };
@@ -1,8 +1,8 @@
1
- import { McpLogger } from '@plitzi/sdk-shared';
1
+ import { ServerLogger } from '@plitzi/sdk-shared';
2
2
  export interface McpLog {
3
3
  toolCall(name: string, args: unknown, ms: number, error?: unknown): void;
4
4
  resourceRead(uri: string, ms: number, error?: unknown): void;
5
5
  }
6
- /** Build the request-log sink for one MCP server. Dispatches structured events to the consumer's `logger` when
6
+ /** Build the protocol-log sink for one MCP server. Dispatches structured events to the consumer's `logger` when
7
7
  * provided; otherwise renders to the console when MCP_DEBUG=1; otherwise a no-op. */
8
- export declare const createMcpLog: (logger?: McpLogger) => McpLog;
8
+ export declare const createMcpLog: (logger?: ServerLogger) => McpLog;
@@ -4,6 +4,7 @@ export { apply, validate, search, read, validateOperations, operation, tools } f
4
4
  export { computeVersion, buildAgentGuide } from './helpers';
5
5
  export { createHttpPreviewClient } from './previewClient';
6
6
  export { createHttpScreenshotClient } from './screenshotClient';
7
+ export type { McpRequestOptions } from './handler';
7
8
  export type { HttpPreviewClientConfig } from './previewClient';
8
9
  export type { HttpScreenshotClientConfig } from './screenshotClient';
9
10
  export type { McpServerContext } from './server';
@@ -1,6 +1,6 @@
1
1
  import { McpLog, Space } from '../helpers';
2
2
  import { Env } from '../types';
3
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  /** Register every resource on the MCP server: fixed listings plus templated per-item reads. The space is
5
5
  * loaded lazily via getSpace, so listing resources never touches the store — only reading one does. */
6
6
  export declare const registerResources: (server: McpServer, getSpace: () => Promise<Space>, env: Env, log: McpLog) => void;
@@ -1,4 +1,5 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
1
+ import { McpLog } from '../helpers';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
3
  export declare const RENDER_GUIDE_URI = "plitzi://render/guide";
3
4
  export declare const RENDER_TYPES_URI = "plitzi://render/types";
4
- export declare const registerRenderResources: (server: McpServer) => void;
5
+ export declare const registerRenderResources: (server: McpServer, log: McpLog) => void;
@@ -1,6 +1,6 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { PreviewClient, ScreenshotClient } from './types';
3
- import { SSRAdapters, McpLogger } from '@plitzi/sdk-shared';
3
+ import { SSRAdapters, ServerLogger } from '@plitzi/sdk-shared';
4
4
  /** The MCP service is stateless: every request resolves its own `spaceId` (from the request JWT) and reads the
5
5
  * space fresh through the adapters — schema and style are two documents, read/written independently. Both the
6
6
  * spaceId and the space itself resolve lazily, so the public surface (handshake, tools-list, resources-list,
@@ -17,14 +17,6 @@ export interface McpServerContext {
17
17
  screenshot?: ScreenshotClient;
18
18
  /** Structured request-log sink. When set, every tool call and resource read emits an McpLogEvent to it (the
19
19
  * consumer renders them); otherwise logging falls back to the console when MCP_DEBUG=1. */
20
- logger?: McpLogger;
21
- /** MCP Apps: the interactive render view for plitzi_render. `sdkBase` is this server's absolute origin, which
22
- * serves the Plitzi SDK bundle under /sdk-assets; the iframe imports it and renders the widget client-side.
23
- * `devMode` picks the SDK vendor bundle name (dev vs prod split) to match the served bundle. Absent → the
24
- * ui:// view is not registered (the tool still returns its text summary + offlineData). */
25
- renderApp?: {
26
- sdkBase: string;
27
- devMode?: boolean;
28
- };
20
+ logger?: ServerLogger;
29
21
  }
30
- export declare const createMcpServer: ({ adapters, getSpaceId, preview, screenshot, logger, renderApp }: McpServerContext) => McpServer;
22
+ export declare const createMcpServer: ({ adapters, getSpaceId, preview, screenshot, logger }: McpServerContext) => McpServer;
@@ -179,8 +179,8 @@ export declare const applyShape: {
179
179
  userProvider: z.ZodOptional<z.ZodEnum<{
180
180
  "": "";
181
181
  custom: "custom";
182
- basic: "basic";
183
182
  auth0: "auth0";
183
+ basic: "basic";
184
184
  }>>;
185
185
  auth0Domain: z.ZodOptional<z.ZodString>;
186
186
  auth0ClientId: z.ZodOptional<z.ZodString>;
@@ -395,9 +395,9 @@ export declare const applyShape: {
395
395
  type: z.ZodLiteral<"upsertStyleVariable">;
396
396
  category: z.ZodEnum<{
397
397
  color: "color";
398
- custom: "custom";
399
398
  spacing: "spacing";
400
399
  shadow: "shadow";
400
+ custom: "custom";
401
401
  }>;
402
402
  name: z.ZodString;
403
403
  value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodObject<{
@@ -409,9 +409,9 @@ export declare const applyShape: {
409
409
  type: z.ZodLiteral<"deleteStyleVariable">;
410
410
  category: z.ZodEnum<{
411
411
  color: "color";
412
- custom: "custom";
413
412
  spacing: "spacing";
414
413
  shadow: "shadow";
414
+ custom: "custom";
415
415
  }>;
416
416
  name: z.ZodString;
417
417
  }, z.core.$strip>], "type">>;