@cyanheads/mcp-ts-core 0.13.10 → 0.13.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 (104) hide show
  1. package/AGENTS.md +6 -3
  2. package/CLAUDE.md +6 -3
  3. package/README.md +29 -3
  4. package/biome.json +1 -1
  5. package/changelog/0.13.x/0.13.11.md +113 -0
  6. package/dist/cli/app-render.d.ts +11 -0
  7. package/dist/cli/app-render.d.ts.map +1 -0
  8. package/dist/cli/app-render.js +150 -0
  9. package/dist/cli/app-render.js.map +1 -0
  10. package/dist/cli/init.d.ts +2 -1
  11. package/dist/cli/init.d.ts.map +1 -1
  12. package/dist/cli/init.js +10 -2
  13. package/dist/cli/init.js.map +1 -1
  14. package/dist/core/context.d.ts +3 -2
  15. package/dist/core/context.d.ts.map +1 -1
  16. package/dist/core/context.js +25 -16
  17. package/dist/core/context.js.map +1 -1
  18. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
  19. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +18 -7
  20. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
  21. package/dist/services/mirror/sqlite/handle.d.ts +11 -5
  22. package/dist/services/mirror/sqlite/handle.d.ts.map +1 -1
  23. package/dist/services/mirror/sqlite/handle.js +46 -8
  24. package/dist/services/mirror/sqlite/handle.js.map +1 -1
  25. package/dist/services/mirror/sqlite/sqliteMirrorStore.d.ts +6 -1
  26. package/dist/services/mirror/sqlite/sqliteMirrorStore.d.ts.map +1 -1
  27. package/dist/services/mirror/sqlite/sqliteMirrorStore.js.map +1 -1
  28. package/dist/testing/apps/browser.d.ts +27 -0
  29. package/dist/testing/apps/browser.d.ts.map +1 -0
  30. package/dist/testing/apps/browser.js +162 -0
  31. package/dist/testing/apps/browser.js.map +1 -0
  32. package/dist/testing/apps/cdp-pipe.d.ts +79 -0
  33. package/dist/testing/apps/cdp-pipe.d.ts.map +1 -0
  34. package/dist/testing/apps/cdp-pipe.js +200 -0
  35. package/dist/testing/apps/cdp-pipe.js.map +1 -0
  36. package/dist/testing/apps/csp.d.ts +17 -0
  37. package/dist/testing/apps/csp.d.ts.map +1 -0
  38. package/dist/testing/apps/csp.js +50 -0
  39. package/dist/testing/apps/csp.js.map +1 -0
  40. package/dist/testing/apps/host-pages.d.ts +47 -0
  41. package/dist/testing/apps/host-pages.d.ts.map +1 -0
  42. package/dist/testing/apps/host-pages.js +138 -0
  43. package/dist/testing/apps/host-pages.js.map +1 -0
  44. package/dist/testing/apps/index.d.ts +167 -0
  45. package/dist/testing/apps/index.d.ts.map +1 -0
  46. package/dist/testing/apps/index.js +36 -0
  47. package/dist/testing/apps/index.js.map +1 -0
  48. package/dist/testing/apps/partial-json.d.ts +14 -0
  49. package/dist/testing/apps/partial-json.d.ts.map +1 -0
  50. package/dist/testing/apps/partial-json.js +71 -0
  51. package/dist/testing/apps/partial-json.js.map +1 -0
  52. package/dist/testing/apps/run.d.ts +13 -0
  53. package/dist/testing/apps/run.d.ts.map +1 -0
  54. package/dist/testing/apps/run.js +659 -0
  55. package/dist/testing/apps/run.js.map +1 -0
  56. package/dist/utils/index.d.ts +1 -1
  57. package/dist/utils/index.d.ts.map +1 -1
  58. package/dist/utils/index.js +1 -1
  59. package/dist/utils/index.js.map +1 -1
  60. package/dist/utils/internal/logger.d.ts +21 -1
  61. package/dist/utils/internal/logger.d.ts.map +1 -1
  62. package/dist/utils/internal/logger.js +31 -14
  63. package/dist/utils/internal/logger.js.map +1 -1
  64. package/dist/utils/internal/performance.d.ts +3 -1
  65. package/dist/utils/internal/performance.d.ts.map +1 -1
  66. package/dist/utils/internal/performance.js +8 -3
  67. package/dist/utils/internal/performance.js.map +1 -1
  68. package/dist/utils/security/sanitization.d.ts +16 -13
  69. package/dist/utils/security/sanitization.d.ts.map +1 -1
  70. package/dist/utils/security/sanitization.js +74 -33
  71. package/dist/utils/security/sanitization.js.map +1 -1
  72. package/dist/utils/telemetry/attributes.d.ts +7 -1
  73. package/dist/utils/telemetry/attributes.d.ts.map +1 -1
  74. package/dist/utils/telemetry/attributes.js +7 -1
  75. package/dist/utils/telemetry/attributes.js.map +1 -1
  76. package/framework-skills/add-app-tool/SKILL.md +2 -1
  77. package/framework-skills/add-test/SKILL.md +15 -15
  78. package/framework-skills/add-tool/SKILL.md +2 -2
  79. package/framework-skills/api-config/SKILL.md +11 -2
  80. package/framework-skills/api-context/SKILL.md +2 -2
  81. package/framework-skills/api-mirror/SKILL.md +4 -3
  82. package/framework-skills/api-telemetry/SKILL.md +6 -4
  83. package/framework-skills/api-testing/SKILL.md +5 -1
  84. package/framework-skills/api-utils/SKILL.md +3 -3
  85. package/framework-skills/code-simplifier/SKILL.md +2 -2
  86. package/framework-skills/field-test/SKILL.md +52 -7
  87. package/framework-skills/git-wrapup/SKILL.md +2 -2
  88. package/framework-skills/orchestrations/SKILL.md +1 -1
  89. package/framework-skills/orchestrations/workflows/greenfield-build.md +2 -2
  90. package/framework-skills/polish-docs-meta/SKILL.md +4 -3
  91. package/framework-skills/polish-docs-meta/references/server-json.md +24 -26
  92. package/framework-skills/release-and-publish/SKILL.md +2 -2
  93. package/framework-skills/release-pr-review/SKILL.md +5 -5
  94. package/framework-skills/setup/SKILL.md +2 -2
  95. package/package.json +37 -22
  96. package/scripts/devcheck.ts +43 -21
  97. package/scripts/lint-packaging.ts +91 -1
  98. package/scripts/prune-musl-packages.ts +146 -0
  99. package/templates/.github/workflows/codeql.yml +10 -1
  100. package/templates/Dockerfile +13 -4
  101. package/templates/_.dockerignore +3 -5
  102. package/templates/_.gitignore +4 -4
  103. package/templates/package.json +4 -4
  104. package/templates/server.json +7 -21
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @fileoverview The two loopback origins of the headless MCP Apps host. The host origin
3
+ * serves a page holding the sandbox iframe; the sandbox origin serves the sandbox proxy
4
+ * page with the view's CSP as an HTTP header. The proxy announces
5
+ * `ui/notifications/sandbox-proxy-ready`; on `ui/notifications/sandbox-resource-ready` it
6
+ * creates the inner view iframe and writes the view HTML into it (an about:blank child
7
+ * inherits the proxy's origin and CSP); it relays every other message both ways except a
8
+ * `ui/notifications/sandbox-*` one, a name the spec reserves for host and proxy.
9
+ *
10
+ * Both iframes carry the view's permissions-policy `allow` value, set before each frame
11
+ * loads: a frame can only delegate a feature its own frame was granted, and writing the
12
+ * view into the inner frame is not a navigation, so its policy is fixed when it is created.
13
+ *
14
+ * The host-side relay is not part of the host page: it runs in an isolated world
15
+ * ({@link HOST_WORLD}) installed over CDP, and reports through a binding only that world
16
+ * can see, so the view cannot reach the channel to Node.
17
+ * @module src/testing/apps/host-pages
18
+ */
19
+ import http from 'node:http';
20
+ /** The isolated world the host relay runs in. */
21
+ export const HOST_WORLD = '__mcpAppsHost';
22
+ /** Binding the relay calls with each message from the sandbox, as JSON. */
23
+ export const RECEIVE_BINDING = '__mcpAppsHostReceive';
24
+ /** Function the relay defines in its world; Node calls it with each message for the view. */
25
+ export const SEND_FUNCTION = '__mcpAppsHostSend';
26
+ /** Function the relay defines to set the sandbox iframe's height in CSS pixels. */
27
+ export const RESIZE_FUNCTION = '__mcpAppsHostResize';
28
+ /** The id of the sandbox iframe in the host page. */
29
+ export const SANDBOX_FRAME_ID = 'mcp-apps-sandbox';
30
+ /** Start both origins. `csp` is sent with the sandbox page. */
31
+ export async function startHostPages(csp, options) {
32
+ let sandboxOrigin = '';
33
+ let hostOrigin = '';
34
+ const host = await listen((req, res) => {
35
+ if (req.url !== '/')
36
+ return notFound(res);
37
+ send(res, hostPage(sandboxOrigin, options), {});
38
+ });
39
+ const sandbox = await listen((req, res) => {
40
+ if (req.url !== '/sandbox.html')
41
+ return notFound(res);
42
+ send(res, sandboxPage(hostOrigin, options.allow), { 'content-security-policy': csp });
43
+ });
44
+ hostOrigin = `http://127.0.0.1:${host.address().port}`;
45
+ sandboxOrigin = `http://127.0.0.1:${sandbox.address().port}`;
46
+ return {
47
+ hostUrl: `${hostOrigin}/`,
48
+ sandboxOrigin,
49
+ relayScript: relayScript(sandboxOrigin),
50
+ close: async () => {
51
+ await Promise.all([stop(host), stop(sandbox)]);
52
+ },
53
+ };
54
+ }
55
+ function listen(handler) {
56
+ return new Promise((resolve, reject) => {
57
+ const server = http.createServer(handler);
58
+ server.once('error', reject);
59
+ server.listen(0, '127.0.0.1', () => resolve(server));
60
+ });
61
+ }
62
+ function stop(server) {
63
+ server.closeAllConnections();
64
+ return new Promise((resolve) => server.close(() => resolve()));
65
+ }
66
+ function send(res, body, headers) {
67
+ res.writeHead(200, {
68
+ 'content-type': 'text/html; charset=utf-8',
69
+ 'cache-control': 'no-store',
70
+ ...headers,
71
+ });
72
+ res.end(body);
73
+ }
74
+ function notFound(res) {
75
+ res.writeHead(404, { 'content-type': 'text/plain' });
76
+ res.end('not found');
77
+ }
78
+ function hostPage(sandboxOrigin, { allow, background, height, width }) {
79
+ return `<!doctype html>
80
+ <html><head><meta charset="utf-8"><title>MCP Apps host</title>
81
+ <style>html,body{margin:0;padding:0;background:${background}}iframe{display:block;border:0}</style>
82
+ </head><body>
83
+ <iframe id="${SANDBOX_FRAME_ID}" src="${sandboxOrigin}/sandbox.html" sandbox="allow-scripts allow-same-origin allow-forms"${allow ? ` allow="${allow}"` : ''} style="width:${width}px;height:${height}px"></iframe>
84
+ </body></html>`;
85
+ }
86
+ function sandboxPage(hostOrigin, allow) {
87
+ return `<!doctype html>
88
+ <html><head><meta charset="utf-8"><style>html,body{margin:0;padding:0;height:100%;overflow:hidden}iframe{display:block;border:0;width:100%;height:100%}</style></head>
89
+ <body><script>
90
+ (() => {
91
+ const HOST_ORIGIN = ${JSON.stringify(hostOrigin)};
92
+ const ALLOW = ${JSON.stringify(allow)};
93
+ const reserved = (data) => typeof data?.method === 'string' && data.method.startsWith('ui/notifications/sandbox-');
94
+ let inner;
95
+ window.addEventListener('message', (event) => {
96
+ const data = event.data;
97
+ if (event.source === window.parent) {
98
+ if (event.origin !== HOST_ORIGIN) return;
99
+ if (data?.method === 'ui/notifications/sandbox-resource-ready') {
100
+ inner = document.createElement('iframe');
101
+ inner.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
102
+ if (ALLOW) inner.setAttribute('allow', ALLOW);
103
+ document.body.appendChild(inner);
104
+ const doc = inner.contentDocument;
105
+ doc.open();
106
+ doc.write(String(data.params.html));
107
+ doc.close();
108
+ return;
109
+ }
110
+ if (!reserved(data) && inner?.contentWindow) inner.contentWindow.postMessage(data, '*');
111
+ } else if (inner && event.source === inner.contentWindow) {
112
+ if (event.origin !== location.origin || reserved(data)) return;
113
+ window.parent.postMessage(data, HOST_ORIGIN);
114
+ }
115
+ });
116
+ window.parent.postMessage({ jsonrpc: '2.0', method: 'ui/notifications/sandbox-proxy-ready', params: {} }, HOST_ORIGIN);
117
+ })();
118
+ </script></body></html>`;
119
+ }
120
+ function relayScript(sandboxOrigin) {
121
+ return `(() => {
122
+ if (window.top !== window) return;
123
+ const SANDBOX_ORIGIN = ${JSON.stringify(sandboxOrigin)};
124
+ const frame = () => document.getElementById(${JSON.stringify(SANDBOX_FRAME_ID)});
125
+ window.addEventListener('message', (event) => {
126
+ const sandbox = frame();
127
+ if (!sandbox || event.source !== sandbox.contentWindow || event.origin !== SANDBOX_ORIGIN) return;
128
+ globalThis[${JSON.stringify(RECEIVE_BINDING)}](JSON.stringify(event.data));
129
+ });
130
+ globalThis[${JSON.stringify(SEND_FUNCTION)}] = (json) => {
131
+ frame().contentWindow.postMessage(JSON.parse(json), SANDBOX_ORIGIN);
132
+ };
133
+ globalThis[${JSON.stringify(RESIZE_FUNCTION)}] = (height) => {
134
+ frame().style.height = height + 'px';
135
+ };
136
+ })();`;
137
+ }
138
+ //# sourceMappingURL=host-pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-pages.js","sourceRoot":"","sources":["../../../src/testing/apps/host-pages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * @fileoverview Headless MCP Apps host. `renderAppTool` connects to a server as a client
3
+ * advertising `io.modelcontextprotocol/ui`, calls a tool, loads its `ui://` resource into
4
+ * headless `chrome-headless-shell` inside the double-iframe sandbox and CSP the MCP Apps
5
+ * specification prescribes, plays the host half of the protocol through the official
6
+ * `AppBridge`, runs scripted steps against the view, and returns a report.
7
+ *
8
+ * Import from `@cyanheads/mcp-ts-core/testing/apps`. The optional peers
9
+ * `@modelcontextprotocol/client` and `@modelcontextprotocol/ext-apps` are loaded on first
10
+ * use; importing this subpath without them succeeds.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { renderAppTool } from '@cyanheads/mcp-ts-core/testing/apps';
15
+ *
16
+ * const run = await renderAppTool({
17
+ * server: { command: 'bun', args: ['run', 'dist/index.js'] },
18
+ * tool: 'my_app_tool',
19
+ * arguments: { query: 'probe' },
20
+ * steps: [{ click: '#action-btn' }, { screenshot: 'after-click' }],
21
+ * });
22
+ * ```
23
+ * @module src/testing/apps
24
+ */
25
+ /** How to reach the server: a stdio command, or a Streamable HTTP endpoint. */
26
+ export type AppServerTarget = {
27
+ args?: string[];
28
+ command: string;
29
+ cwd?: string;
30
+ /** The server's environment. Default: the MCP client's inherited-variable allowlist. */
31
+ env?: Record<string, string>;
32
+ } | {
33
+ headers?: Record<string, string>;
34
+ url: string;
35
+ };
36
+ /** One scripted action against the view, run after the tool result is delivered. */
37
+ export type AppRenderStep = {
38
+ click: string;
39
+ } | {
40
+ fill: string;
41
+ value: string;
42
+ } | {
43
+ timeoutMs?: number;
44
+ waitFor: string;
45
+ } | {
46
+ evaluate: string;
47
+ } | {
48
+ screenshot: string;
49
+ };
50
+ /** Display modes a host can offer a view. */
51
+ export type AppDisplayMode = 'inline' | 'fullscreen' | 'pip';
52
+ /** The host the view sees. */
53
+ export interface AppHostOptions {
54
+ /** Display modes the host accepts on `ui/request-display-mode`. Default: `['inline']`. */
55
+ availableDisplayModes?: AppDisplayMode[];
56
+ /** The initial display mode. Default: `'inline'`. */
57
+ displayMode?: AppDisplayMode;
58
+ /** Fixed container height in CSS pixels. Default: follows the view's size-changed reports. */
59
+ height?: number;
60
+ locale?: string;
61
+ /** Send `tool-input-partial` notifications (growing prefixes of the arguments) before `tool-input`. */
62
+ streamInput?: boolean;
63
+ /** CSS custom properties passed as `styles.variables`. */
64
+ styles?: Record<string, string>;
65
+ /** Default: `'light'`. */
66
+ theme?: 'light' | 'dark';
67
+ timeZone?: string;
68
+ /** Container width in CSS pixels. Default: 720. */
69
+ width?: number;
70
+ }
71
+ /** Options for one render. */
72
+ export interface RenderAppToolOptions {
73
+ arguments?: Record<string, unknown>;
74
+ /** Path to a `chrome-headless-shell` executable; overrides `MCP_APPS_BROWSER_PATH` and the cache. */
75
+ browserPath?: string;
76
+ host?: AppHostOptions;
77
+ /** Where screenshots are written. Default: a new directory under the OS temp dir. */
78
+ outDir?: string;
79
+ server: AppServerTarget;
80
+ steps?: AppRenderStep[];
81
+ /** How long to wait for the view's `initialized` and for each step. Default: 15000 ms. */
82
+ timeoutMs?: number;
83
+ tool: string;
84
+ }
85
+ /** A message between the view and the host, as it crossed the sandbox. */
86
+ export interface AppRenderMessage {
87
+ /** Milliseconds since the run started. */
88
+ at: number;
89
+ direction: 'view-to-host' | 'host-to-view';
90
+ message: Record<string, unknown>;
91
+ }
92
+ /** An uncaught exception or console error. */
93
+ export interface AppRenderError {
94
+ column?: number;
95
+ /** The frame it came from: the view, the sandbox proxy, or the host page. */
96
+ frame: 'view' | 'sandbox' | 'host' | 'unknown';
97
+ line?: number;
98
+ message: string;
99
+ source: 'exception' | 'console';
100
+ url?: string;
101
+ }
102
+ /** A Content Security Policy violation in the sandbox or the view. */
103
+ export interface AppCspViolation {
104
+ /** The blocked URL, or `eval` / `inline` / `wasm-eval` for a blocked script evaluation. */
105
+ blockedURI: string;
106
+ directive: string;
107
+ line?: number;
108
+ sourceURL?: string;
109
+ violationType: string;
110
+ }
111
+ /** The outcome of one step. */
112
+ export interface AppRenderStepResult {
113
+ error?: string;
114
+ ok: boolean;
115
+ /** Path of the PNG a screenshot step wrote. */
116
+ screenshot?: string;
117
+ step: AppRenderStep;
118
+ /** The value an evaluate step returned. */
119
+ value?: unknown;
120
+ }
121
+ /** What one render observed. */
122
+ export interface AppRenderReport {
123
+ /** The view's `appInfo` from `ui/initialize`. */
124
+ appInfo?: {
125
+ name: string;
126
+ version: string;
127
+ };
128
+ arguments: Record<string, unknown>;
129
+ browser: {
130
+ executable: string;
131
+ pid: number;
132
+ profileDir: string;
133
+ };
134
+ /** The CSP header the sandbox applied to the view. */
135
+ csp: string;
136
+ cspViolations: AppCspViolation[];
137
+ durationMs: number;
138
+ errors: AppRenderError[];
139
+ /** A failure after the browser was reached, such as the view never initializing. */
140
+ failure?: string;
141
+ /** The view completed `ui/initialize`. */
142
+ initialized: boolean;
143
+ messages: AppRenderMessage[];
144
+ resourceUri: string;
145
+ screenshots: string[];
146
+ /** The last `ui/notifications/size-changed`. */
147
+ size?: {
148
+ height?: number;
149
+ width?: number;
150
+ };
151
+ steps: AppRenderStepResult[];
152
+ /** The view's rendered text after the steps. */
153
+ text: string;
154
+ tool: string;
155
+ /** Why the tool call failed at the protocol level; the view then received `tool-cancelled`. */
156
+ toolError?: string;
157
+ toolResult?: Record<string, unknown>;
158
+ }
159
+ /**
160
+ * Render an app tool's view in a headless MCP Apps host and report what happened.
161
+ * Resolves with the report whenever the run reached the browser, even when the view
162
+ * failed. Throws only on setup failures: a missing optional peer, no browser, the server
163
+ * unreachable, the tool missing, or its UI resource absent, unreadable, or declaring a
164
+ * `_meta.ui.csp` entry that is not a plain origin.
165
+ */
166
+ export declare function renderAppTool(options: RenderAppToolOptions): Promise<AppRenderReport>;
167
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/testing/apps/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @fileoverview Headless MCP Apps host. `renderAppTool` connects to a server as a client
3
+ * advertising `io.modelcontextprotocol/ui`, calls a tool, loads its `ui://` resource into
4
+ * headless `chrome-headless-shell` inside the double-iframe sandbox and CSP the MCP Apps
5
+ * specification prescribes, plays the host half of the protocol through the official
6
+ * `AppBridge`, runs scripted steps against the view, and returns a report.
7
+ *
8
+ * Import from `@cyanheads/mcp-ts-core/testing/apps`. The optional peers
9
+ * `@modelcontextprotocol/client` and `@modelcontextprotocol/ext-apps` are loaded on first
10
+ * use; importing this subpath without them succeeds.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { renderAppTool } from '@cyanheads/mcp-ts-core/testing/apps';
15
+ *
16
+ * const run = await renderAppTool({
17
+ * server: { command: 'bun', args: ['run', 'dist/index.js'] },
18
+ * tool: 'my_app_tool',
19
+ * arguments: { query: 'probe' },
20
+ * steps: [{ click: '#action-btn' }, { screenshot: 'after-click' }],
21
+ * });
22
+ * ```
23
+ * @module src/testing/apps
24
+ */
25
+ /**
26
+ * Render an app tool's view in a headless MCP Apps host and report what happened.
27
+ * Resolves with the report whenever the run reached the browser, even when the view
28
+ * failed. Throws only on setup failures: a missing optional peer, no browser, the server
29
+ * unreachable, the tool missing, or its UI resource absent, unreadable, or declaring a
30
+ * `_meta.ui.csp` entry that is not a plain origin.
31
+ */
32
+ export async function renderAppTool(options) {
33
+ const { runAppRender } = await import('./run.js');
34
+ return runAppRender(options);
35
+ }
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/apps/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAmIH;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA6B;IAC/D,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IAClD,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @fileoverview Growing prefixes of a tool's argument JSON for `ui/notifications/tool-input-partial`
3
+ * streaming. Each prefix of the serialized arguments is closed into a valid JSON object:
4
+ * an open string is terminated, open containers are closed, and a trailing token that
5
+ * cannot be completed (a dangling key, a partial literal) is backed off.
6
+ * @module src/testing/apps/partial-json
7
+ */
8
+ /**
9
+ * Partial argument objects for growing prefixes of `JSON.stringify(args)`, in order. Every
10
+ * entry is a plain object; consecutive duplicates are dropped and the complete arguments
11
+ * are never included (those go out as `tool-input`).
12
+ */
13
+ export declare function partialArguments(args: Record<string, unknown>): Record<string, unknown>[];
14
+ //# sourceMappingURL=partial-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial-json.d.ts","sourceRoot":"","sources":["../../../src/testing/apps/partial-json.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAazF"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @fileoverview Growing prefixes of a tool's argument JSON for `ui/notifications/tool-input-partial`
3
+ * streaming. Each prefix of the serialized arguments is closed into a valid JSON object:
4
+ * an open string is terminated, open containers are closed, and a trailing token that
5
+ * cannot be completed (a dangling key, a partial literal) is backed off.
6
+ * @module src/testing/apps/partial-json
7
+ */
8
+ /** At most this many partials are produced for one argument object. */
9
+ const MAX_PARTIALS = 8;
10
+ /**
11
+ * Partial argument objects for growing prefixes of `JSON.stringify(args)`, in order. Every
12
+ * entry is a plain object; consecutive duplicates are dropped and the complete arguments
13
+ * are never included (those go out as `tool-input`).
14
+ */
15
+ export function partialArguments(args) {
16
+ const json = JSON.stringify(args);
17
+ const count = Math.min(MAX_PARTIALS, json.length - 1);
18
+ const partials = [];
19
+ let previous = '';
20
+ for (let i = 1; i <= count; i++) {
21
+ const cut = Math.max(1, Math.floor((json.length * i) / (count + 1)));
22
+ const closed = closePrefix(json.slice(0, cut));
23
+ if (closed === undefined || closed === previous || closed === json)
24
+ continue;
25
+ previous = closed;
26
+ partials.push(JSON.parse(closed));
27
+ }
28
+ return partials;
29
+ }
30
+ /** The longest closing of `prefix` (backing off one character at a time) that parses as an object. */
31
+ function closePrefix(prefix) {
32
+ for (let end = prefix.length; end > 0; end--) {
33
+ const candidate = close(prefix.slice(0, end));
34
+ try {
35
+ const value = JSON.parse(candidate);
36
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
37
+ return JSON.stringify(value);
38
+ }
39
+ }
40
+ catch {
41
+ // Not closable at this length; back off.
42
+ }
43
+ }
44
+ return undefined;
45
+ }
46
+ /** `text` with its open string and open containers closed. */
47
+ function close(text) {
48
+ const closers = [];
49
+ let inString = false;
50
+ let escaped = false;
51
+ for (const ch of text) {
52
+ if (inString) {
53
+ if (escaped)
54
+ escaped = false;
55
+ else if (ch === '\\')
56
+ escaped = true;
57
+ else if (ch === '"')
58
+ inString = false;
59
+ }
60
+ else if (ch === '"')
61
+ inString = true;
62
+ else if (ch === '{')
63
+ closers.push('}');
64
+ else if (ch === '[')
65
+ closers.push(']');
66
+ else if (ch === '}' || ch === ']')
67
+ closers.pop();
68
+ }
69
+ return `${text}${inString ? '"' : ''}${closers.reverse().join('')}`;
70
+ }
71
+ //# sourceMappingURL=partial-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial-json.js","sourceRoot":"","sources":["../../../src/testing/apps/partial-json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview One headless MCP Apps host run, loaded on demand by `renderAppTool`.
3
+ * Node holds the MCP client and the official `AppBridge`; the bridge's transport relays
4
+ * JSON-RPC through the host page's isolated-world relay into the sandbox proxy and the
5
+ * view. CDP supplies the instrumentation: exceptions and console errors from
6
+ * `Runtime`, CSP violations from `Audits` issues, frames from `Page`. The optional peers
7
+ * are imported here, inside the run, never at module load.
8
+ * @module src/testing/apps/run
9
+ */
10
+ import type { AppRenderReport, RenderAppToolOptions } from './index.js';
11
+ /** Run one render. See `renderAppTool`. */
12
+ export declare function runAppRender(options: RenderAppToolOptions): Promise<AppRenderReport>;
13
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/testing/apps/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2CH,OAAO,KAAK,EAGV,eAAe,EAIf,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAsBpB,2CAA2C;AAC3C,wBAAsB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CA6F1F"}