@maxhealth.tech/prefab 0.3.5 → 0.3.7
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.
- package/CHANGELOG.md +50 -0
- package/dist/actions/mcp.js.map +1 -1
- package/dist/app.d.ts +2 -11
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +5 -11
- package/dist/app.js.map +1 -1
- package/dist/auto/comparison.d.ts.map +1 -1
- package/dist/auto/comparison.js +3 -3
- package/dist/auto/comparison.js.map +1 -1
- package/dist/auto/form.d.ts +2 -2
- package/dist/auto/form.js +2 -2
- package/dist/auto/index.d.ts.map +1 -1
- package/dist/auto/index.js +2 -1
- package/dist/auto/index.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/stringify.d.ts +23 -0
- package/dist/core/stringify.d.ts.map +1 -0
- package/dist/core/stringify.js +48 -0
- package/dist/core/stringify.js.map +1 -0
- package/dist/core/version.d.ts +24 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +24 -0
- package/dist/core/version.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/display.d.ts +7 -160
- package/dist/mcp/display.d.ts.map +1 -1
- package/dist/mcp/display.js +9 -170
- package/dist/mcp/display.js.map +1 -1
- package/dist/mcp/index.d.ts +9 -3
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +4 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/resource.d.ts +250 -0
- package/dist/mcp/resource.d.ts.map +1 -0
- package/dist/mcp/resource.js +282 -0
- package/dist/mcp/resource.js.map +1 -0
- package/dist/mcp/result.d.ts +42 -0
- package/dist/mcp/result.d.ts.map +1 -0
- package/dist/mcp/result.js +45 -0
- package/dist/mcp/result.js.map +1 -0
- package/dist/mcp/theme-bridge.d.ts +59 -0
- package/dist/mcp/theme-bridge.d.ts.map +1 -0
- package/dist/mcp/theme-bridge.js +106 -0
- package/dist/mcp/theme-bridge.js.map +1 -0
- package/dist/mcp/types.d.ts +96 -21
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js +18 -3
- package/dist/mcp/types.js.map +1 -1
- package/dist/prefab.css +21 -7
- package/dist/renderer/actions.d.ts.map +1 -1
- package/dist/renderer/actions.js +3 -2
- package/dist/renderer/actions.js.map +1 -1
- package/dist/renderer/app.js.map +1 -1
- package/dist/renderer/bridge.d.ts.map +1 -1
- package/dist/renderer/bridge.js +2 -1
- package/dist/renderer/bridge.js.map +1 -1
- package/dist/renderer/components/chart-helpers.d.ts.map +1 -1
- package/dist/renderer/components/chart-helpers.js +3 -2
- package/dist/renderer/components/chart-helpers.js.map +1 -1
- package/dist/renderer/components/chart-tooltip.d.ts.map +1 -1
- package/dist/renderer/components/chart-tooltip.js +3 -2
- package/dist/renderer/components/chart-tooltip.js.map +1 -1
- package/dist/renderer/components/charts.d.ts.map +1 -1
- package/dist/renderer/components/charts.js +4 -3
- package/dist/renderer/components/charts.js.map +1 -1
- package/dist/renderer/components/data.d.ts.map +1 -1
- package/dist/renderer/components/data.js +3 -2
- package/dist/renderer/components/data.js.map +1 -1
- package/dist/renderer/components/form.d.ts.map +1 -1
- package/dist/renderer/components/form.js +12 -11
- package/dist/renderer/components/form.js.map +1 -1
- package/dist/renderer/components/media.js +7 -1
- package/dist/renderer/components/media.js.map +1 -1
- package/dist/renderer/engine.d.ts.map +1 -1
- package/dist/renderer/engine.js +3 -2
- package/dist/renderer/engine.js.map +1 -1
- package/dist/renderer/index.js +34 -8
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/rx.d.ts.map +1 -1
- package/dist/renderer/rx.js +4 -3
- package/dist/renderer/rx.js.map +1 -1
- package/dist/renderer.auto.min.js +10 -10
- package/dist/renderer.min.js +10 -10
- package/dist/rx/collection.d.ts +4 -4
- package/dist/rx/collection.d.ts.map +1 -1
- package/dist/rx/collection.js +7 -6
- package/dist/rx/collection.js.map +1 -1
- package/dist/rx/pipes.d.ts +12 -3
- package/dist/rx/pipes.d.ts.map +1 -1
- package/dist/rx/pipes.js +12 -3
- package/dist/rx/pipes.js.map +1 -1
- package/dist/rx/rx.js.map +1 -1
- package/package.json +10 -9
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Apps resource helpers — register the prefab viewer as a `ui://` resource.
|
|
3
|
+
*
|
|
4
|
+
* Covers the three things every MCP Apps host checks and the two the
|
|
5
|
+
* 2026-07-28 protocol revision added:
|
|
6
|
+
* - the `text/html;profile=mcp-app` MIME type,
|
|
7
|
+
* - `_meta.ui` CSP / permissions on BOTH the listing and the content item,
|
|
8
|
+
* - the viewer HTML itself (CDN-pinned to this exact package version),
|
|
9
|
+
* - `ttlMs` / `cacheScope` on the read result (SEP-2549 `CacheableResult`),
|
|
10
|
+
* - the `io.modelcontextprotocol/ui` extension capability (SEP-2133).
|
|
11
|
+
*/
|
|
12
|
+
import type { McpCacheHint, McpResourceReadResult, McpTextResourceContents } from './types.js';
|
|
13
|
+
import { type ThemeBridge } from './theme-bridge.js';
|
|
14
|
+
/** CSP configuration for MCP Apps resources. */
|
|
15
|
+
export interface McpAppCsp {
|
|
16
|
+
/** Origins allowed for scripts, styles, images, fonts, media. */
|
|
17
|
+
resourceDomains?: string[];
|
|
18
|
+
/** Origins allowed for fetch/XHR/WebSocket. */
|
|
19
|
+
connectDomains?: string[];
|
|
20
|
+
/** Origins allowed for nested iframes. */
|
|
21
|
+
frameDomains?: string[];
|
|
22
|
+
/** Additional allowed base URIs. */
|
|
23
|
+
baseUriDomains?: string[];
|
|
24
|
+
}
|
|
25
|
+
/** Permission Policy requests for MCP Apps resources. */
|
|
26
|
+
export interface McpAppPermissions {
|
|
27
|
+
/** Request camera access (video capture, QR scanning). */
|
|
28
|
+
camera?: boolean;
|
|
29
|
+
/** Request microphone access (audio recording, voice input). */
|
|
30
|
+
microphone?: boolean;
|
|
31
|
+
/** Request geolocation access (location-aware apps, maps). */
|
|
32
|
+
geolocation?: boolean;
|
|
33
|
+
/** Request clipboard write access (copy-to-clipboard). */
|
|
34
|
+
clipboardWrite?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface ResourceMetaOptions {
|
|
37
|
+
/** CSP domains configuration. */
|
|
38
|
+
csp?: McpAppCsp;
|
|
39
|
+
/** Permission Policy requests (camera, mic, etc.). */
|
|
40
|
+
permissions?: McpAppPermissions;
|
|
41
|
+
}
|
|
42
|
+
/** Spec-compliant permissions shape: each granted permission is `{}`. */
|
|
43
|
+
interface McpAppPermissionsWire {
|
|
44
|
+
camera?: Record<string, never>;
|
|
45
|
+
microphone?: Record<string, never>;
|
|
46
|
+
geolocation?: Record<string, never>;
|
|
47
|
+
clipboardWrite?: Record<string, never>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Generate the `_meta` object for MCP Apps `ui://` resource registration.
|
|
51
|
+
*
|
|
52
|
+
* Includes CSP and Permission Policy configuration per the MCP Apps spec.
|
|
53
|
+
* Use on both the resource listing AND the content item (VS Code reads
|
|
54
|
+
* only the content item; other hosts may read either).
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const meta = resourceMeta({
|
|
59
|
+
* csp: { resourceDomains: ['https://cdn.jsdelivr.net'] },
|
|
60
|
+
* permissions: { camera: true },
|
|
61
|
+
* })
|
|
62
|
+
*
|
|
63
|
+
* server.registerResource('viewer', 'ui://my/viewer', {
|
|
64
|
+
* mimeType: 'text/html;profile=mcp-app',
|
|
65
|
+
* _meta: meta,
|
|
66
|
+
* cacheHint: { ttlMs: 86_400_000, cacheScope: 'public' },
|
|
67
|
+
* }, (uri) => Promise.resolve({
|
|
68
|
+
* contents: [{ uri: uri.toString(), mimeType: 'text/html;profile=mcp-app', text: html, _meta: meta }],
|
|
69
|
+
* ttlMs: 86_400_000,
|
|
70
|
+
* cacheScope: 'public',
|
|
71
|
+
* }))
|
|
72
|
+
*
|
|
73
|
+
* // The UI resource is associated with a tool on the tool DEFINITION, not
|
|
74
|
+
* // on its result:
|
|
75
|
+
* server.registerTool('browse', {
|
|
76
|
+
* title: 'Browse',
|
|
77
|
+
* inputSchema: schema,
|
|
78
|
+
* _meta: { ui: { resourceUri: 'ui://my/viewer' } },
|
|
79
|
+
* }, (args) => display(autoTable(rows)))
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function resourceMeta(options?: ResourceMetaOptions): {
|
|
83
|
+
ui: {
|
|
84
|
+
csp?: McpAppCsp;
|
|
85
|
+
permissions?: McpAppPermissionsWire;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
/** Default CSP meta for prefab apps using jsDelivr CDN. */
|
|
89
|
+
export declare const PREFAB_CDN_META: {
|
|
90
|
+
ui: {
|
|
91
|
+
csp?: McpAppCsp;
|
|
92
|
+
permissions?: McpAppPermissionsWire;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
/** Default URI for the prefab viewer resource. */
|
|
96
|
+
export declare const PREFAB_RESOURCE_URI = "ui://prefab/viewer";
|
|
97
|
+
/** MIME type required by MCP Apps hosts. */
|
|
98
|
+
export declare const MCP_APP_MIME = "text/html;profile=mcp-app";
|
|
99
|
+
/**
|
|
100
|
+
* Capability key for the MCP Apps extension (versioned independently of core).
|
|
101
|
+
*
|
|
102
|
+
* The identifier is `…/ui`, and the trap here is that `…/apps` looks right and is
|
|
103
|
+
* wrong. The normative source is the MCP Apps spec, which states "This extension is
|
|
104
|
+
* identified as: `io.modelcontextprotocol/ui`" in both the current `2026-01-26`
|
|
105
|
+
* revision and the draft, and the reference implementation agrees:
|
|
106
|
+
* `ext-apps/src/server/index.ts` exports `EXTENSION_ID = "io.modelcontextprotocol/ui"`.
|
|
107
|
+
*
|
|
108
|
+
* `io.modelcontextprotocol/apps` appears only as illustrative "e.g." text in the Rust
|
|
109
|
+
* and C# SDKs' generic `ServerCapabilities.extensions` doc comments (and their test
|
|
110
|
+
* fixtures), showing the SHAPE of the extensions map rather than naming this extension.
|
|
111
|
+
* Copying it from there declares the capability under a key no host looks up, which
|
|
112
|
+
* fails silently: rendering still works, because hosts fall back to detecting apps via
|
|
113
|
+
* `_meta.ui` plus the MIME type, so only the SEP-2133 declaration is lost.
|
|
114
|
+
*
|
|
115
|
+
* Duplicated as a literal rather than imported, because prefab ships zero dependencies.
|
|
116
|
+
*/
|
|
117
|
+
export declare const APPS_EXTENSION = "io.modelcontextprotocol/ui";
|
|
118
|
+
/**
|
|
119
|
+
* Default cache hint for the viewer resource.
|
|
120
|
+
*
|
|
121
|
+
* The HTML is a pure function of this package's `VERSION` (the CDN base pins
|
|
122
|
+
* the exact version), so it cannot change for a given server build — it is
|
|
123
|
+
* safely shared-cacheable. Without this the SDK falls back to the conservative
|
|
124
|
+
* `{ ttlMs: 0, cacheScope: 'private' }` and the viewer is re-fetched every time.
|
|
125
|
+
*/
|
|
126
|
+
export declare const DEFAULT_VIEWER_CACHE: Required<McpCacheHint>;
|
|
127
|
+
export interface RendererHtmlOptions {
|
|
128
|
+
/** Page title. @default 'Prefab' */
|
|
129
|
+
title?: string;
|
|
130
|
+
/** Additional `<script>` URLs to load after the renderer. */
|
|
131
|
+
scripts?: string[];
|
|
132
|
+
/** Additional `<link rel="stylesheet">` URLs. */
|
|
133
|
+
stylesheets?: string[];
|
|
134
|
+
/** Override CDN base URL (no trailing slash). @default jsdelivr CDN */
|
|
135
|
+
cdnBase?: string;
|
|
136
|
+
/**
|
|
137
|
+
* Inject a theme bridge stylesheet after `prefab.css`.
|
|
138
|
+
*
|
|
139
|
+
* `'vscode'` re-declares the tokens VS Code can supply with the
|
|
140
|
+
* `--vscode-*` variable first, dropping the MCP Apps `--color-*` layer that
|
|
141
|
+
* would otherwise shadow it, so the viewer follows the user's editor theme.
|
|
142
|
+
* Emitted before `stylesheets`, which stay the outermost override.
|
|
143
|
+
*/
|
|
144
|
+
themeBridge?: ThemeBridge;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Generate the HTML page for a prefab MCP Apps viewer resource.
|
|
148
|
+
*
|
|
149
|
+
* Returns the minimal HTML that loads `prefab.css` and `renderer.auto.min.js`
|
|
150
|
+
* from the CDN, plus any additional scripts/stylesheets you specify.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* import { rendererHtml } from '@maxhealth.tech/prefab/mcp'
|
|
155
|
+
* const html = rendererHtml()
|
|
156
|
+
* // or with extra scripts:
|
|
157
|
+
* const html = rendererHtml({ scripts: ['https://cdn.example.com/plugin.js'] })
|
|
158
|
+
* // inside VS Code, to follow the user's editor theme:
|
|
159
|
+
* const html = rendererHtml({ themeBridge: 'vscode' })
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export declare function rendererHtml(options?: RendererHtmlOptions): string;
|
|
163
|
+
export interface ViewerResourceOptions {
|
|
164
|
+
/** Resource URI. @default PREFAB_RESOURCE_URI */
|
|
165
|
+
uri?: string;
|
|
166
|
+
/** Resource title. @default 'Prefab Viewer' */
|
|
167
|
+
title?: string;
|
|
168
|
+
/** CSP configuration. @default { resourceDomains: ['https://cdn.jsdelivr.net'] } */
|
|
169
|
+
csp?: McpAppCsp;
|
|
170
|
+
/** Permission Policy requests. */
|
|
171
|
+
permissions?: McpAppPermissions;
|
|
172
|
+
/** Additional `<script>` URLs to load after the renderer. */
|
|
173
|
+
scripts?: string[];
|
|
174
|
+
/** Additional `<link rel="stylesheet">` URLs. */
|
|
175
|
+
stylesheets?: string[];
|
|
176
|
+
/** Override CDN base URL (no trailing slash). */
|
|
177
|
+
cdnBase?: string;
|
|
178
|
+
/**
|
|
179
|
+
* Inject a theme bridge stylesheet. `'vscode'` makes the viewer follow the
|
|
180
|
+
* user's editor theme. See {@link RendererHtmlOptions.themeBridge}.
|
|
181
|
+
*/
|
|
182
|
+
themeBridge?: ThemeBridge;
|
|
183
|
+
/**
|
|
184
|
+
* Cache fields for the `resources/read` result (SEP-2549).
|
|
185
|
+
* @default { ttlMs: 86_400_000, cacheScope: 'public' }
|
|
186
|
+
*/
|
|
187
|
+
cache?: McpCacheHint;
|
|
188
|
+
/**
|
|
189
|
+
* Declare the `io.modelcontextprotocol/ui` extension capability on the
|
|
190
|
+
* server (SEP-2133). Must happen before the server connects; a server that
|
|
191
|
+
* is already connected keeps its existing capabilities and a warning is
|
|
192
|
+
* logged. @default true
|
|
193
|
+
*/
|
|
194
|
+
declareCapability?: boolean;
|
|
195
|
+
}
|
|
196
|
+
/** Registration config accepted by both SDK generations. */
|
|
197
|
+
export interface ResourceConfig {
|
|
198
|
+
title?: string;
|
|
199
|
+
mimeType: string;
|
|
200
|
+
description?: string;
|
|
201
|
+
_meta?: Record<string, unknown>;
|
|
202
|
+
/** Per-resource cache hint (SDK v2; ignored by servers that do not read it). */
|
|
203
|
+
cacheHint?: McpCacheHint;
|
|
204
|
+
}
|
|
205
|
+
/** `resources/read` handler shape passed to the server — the viewer is always HTML text. */
|
|
206
|
+
export type ResourceReadHandler = (uri: URL) => Promise<McpResourceReadResult<McpTextResourceContents>>;
|
|
207
|
+
/** Capability bag carrying extension declarations. */
|
|
208
|
+
interface CapabilityDeclaration {
|
|
209
|
+
extensions?: Record<string, Record<string, unknown>>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* MCP server interface expected by registerViewerResource.
|
|
213
|
+
*
|
|
214
|
+
* Structural rather than an SDK import, so fastmcp and hand-rolled servers stay
|
|
215
|
+
* compatible. `registerResource` is preferred; `resource` is the v1 overload
|
|
216
|
+
* that v2 retired and is used only as a fallback.
|
|
217
|
+
*/
|
|
218
|
+
export interface McpServerLike {
|
|
219
|
+
registerResource?(name: string, uri: string, config: ResourceConfig, handler: ResourceReadHandler): unknown;
|
|
220
|
+
resource?(name: string, uri: string, config: ResourceConfig, handler: ResourceReadHandler): unknown;
|
|
221
|
+
/** Low-level server, where both SDK generations expose capability registration. */
|
|
222
|
+
server?: {
|
|
223
|
+
registerCapabilities?(capabilities: CapabilityDeclaration): void;
|
|
224
|
+
};
|
|
225
|
+
/** Some wrappers expose capability registration directly. */
|
|
226
|
+
registerCapabilities?(capabilities: CapabilityDeclaration): void;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Register the prefab viewer as a `ui://` resource on an MCP server.
|
|
230
|
+
*
|
|
231
|
+
* Handles the MIME type, CSP on both listing and content item, HTML generation,
|
|
232
|
+
* the `CacheableResult` fields and the Apps extension capability in one call.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* import { registerViewerResource, PREFAB_RESOURCE_URI, display } from '@maxhealth.tech/prefab/mcp'
|
|
237
|
+
*
|
|
238
|
+
* registerViewerResource(server)
|
|
239
|
+
*
|
|
240
|
+
* // The UI resource is attached to the tool DEFINITION via _meta.ui:
|
|
241
|
+
* server.registerTool('browse', {
|
|
242
|
+
* title: 'Browse patients',
|
|
243
|
+
* inputSchema: { query: z.string() },
|
|
244
|
+
* _meta: { ui: { resourceUri: PREFAB_RESOURCE_URI } },
|
|
245
|
+
* }, async (args) => display(autoTable(await search(args.query))))
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
export declare function registerViewerResource(server: McpServerLike, options?: ViewerResourceOptions): void;
|
|
249
|
+
export {};
|
|
250
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/mcp/resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Apps resource helpers — register the prefab viewer as a `ui://` resource.
|
|
3
|
+
*
|
|
4
|
+
* Covers the three things every MCP Apps host checks and the two the
|
|
5
|
+
* 2026-07-28 protocol revision added:
|
|
6
|
+
* - the `text/html;profile=mcp-app` MIME type,
|
|
7
|
+
* - `_meta.ui` CSP / permissions on BOTH the listing and the content item,
|
|
8
|
+
* - the viewer HTML itself (CDN-pinned to this exact package version),
|
|
9
|
+
* - `ttlMs` / `cacheScope` on the read result (SEP-2549 `CacheableResult`),
|
|
10
|
+
* - the `io.modelcontextprotocol/ui` extension capability (SEP-2133).
|
|
11
|
+
*/
|
|
12
|
+
import { VERSION } from '../app.js';
|
|
13
|
+
import { createLogger } from '../core/logger.js';
|
|
14
|
+
import { themeBridgeCss } from './theme-bridge.js';
|
|
15
|
+
const log = createLogger('mcp');
|
|
16
|
+
/**
|
|
17
|
+
* Generate the `_meta` object for MCP Apps `ui://` resource registration.
|
|
18
|
+
*
|
|
19
|
+
* Includes CSP and Permission Policy configuration per the MCP Apps spec.
|
|
20
|
+
* Use on both the resource listing AND the content item (VS Code reads
|
|
21
|
+
* only the content item; other hosts may read either).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const meta = resourceMeta({
|
|
26
|
+
* csp: { resourceDomains: ['https://cdn.jsdelivr.net'] },
|
|
27
|
+
* permissions: { camera: true },
|
|
28
|
+
* })
|
|
29
|
+
*
|
|
30
|
+
* server.registerResource('viewer', 'ui://my/viewer', {
|
|
31
|
+
* mimeType: 'text/html;profile=mcp-app',
|
|
32
|
+
* _meta: meta,
|
|
33
|
+
* cacheHint: { ttlMs: 86_400_000, cacheScope: 'public' },
|
|
34
|
+
* }, (uri) => Promise.resolve({
|
|
35
|
+
* contents: [{ uri: uri.toString(), mimeType: 'text/html;profile=mcp-app', text: html, _meta: meta }],
|
|
36
|
+
* ttlMs: 86_400_000,
|
|
37
|
+
* cacheScope: 'public',
|
|
38
|
+
* }))
|
|
39
|
+
*
|
|
40
|
+
* // The UI resource is associated with a tool on the tool DEFINITION, not
|
|
41
|
+
* // on its result:
|
|
42
|
+
* server.registerTool('browse', {
|
|
43
|
+
* title: 'Browse',
|
|
44
|
+
* inputSchema: schema,
|
|
45
|
+
* _meta: { ui: { resourceUri: 'ui://my/viewer' } },
|
|
46
|
+
* }, (args) => display(autoTable(rows)))
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function resourceMeta(options) {
|
|
50
|
+
const ui = {};
|
|
51
|
+
if (options?.csp) {
|
|
52
|
+
ui.csp = {
|
|
53
|
+
resourceDomains: options.csp.resourceDomains ?? [],
|
|
54
|
+
connectDomains: options.csp.connectDomains ?? [],
|
|
55
|
+
frameDomains: options.csp.frameDomains ?? [],
|
|
56
|
+
baseUriDomains: options.csp.baseUriDomains ?? [],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (options?.permissions) {
|
|
60
|
+
ui.permissions = {};
|
|
61
|
+
if (options.permissions.camera)
|
|
62
|
+
ui.permissions.camera = {};
|
|
63
|
+
if (options.permissions.microphone)
|
|
64
|
+
ui.permissions.microphone = {};
|
|
65
|
+
if (options.permissions.geolocation)
|
|
66
|
+
ui.permissions.geolocation = {};
|
|
67
|
+
if (options.permissions.clipboardWrite)
|
|
68
|
+
ui.permissions.clipboardWrite = {};
|
|
69
|
+
}
|
|
70
|
+
return { ui };
|
|
71
|
+
}
|
|
72
|
+
/** Default CSP meta for prefab apps using jsDelivr CDN. */
|
|
73
|
+
export const PREFAB_CDN_META = resourceMeta({
|
|
74
|
+
csp: { resourceDomains: ['https://cdn.jsdelivr.net'] },
|
|
75
|
+
});
|
|
76
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
77
|
+
/** Default URI for the prefab viewer resource. */
|
|
78
|
+
export const PREFAB_RESOURCE_URI = 'ui://prefab/viewer';
|
|
79
|
+
/** MIME type required by MCP Apps hosts. */
|
|
80
|
+
export const MCP_APP_MIME = 'text/html;profile=mcp-app';
|
|
81
|
+
/**
|
|
82
|
+
* Capability key for the MCP Apps extension (versioned independently of core).
|
|
83
|
+
*
|
|
84
|
+
* The identifier is `…/ui`, and the trap here is that `…/apps` looks right and is
|
|
85
|
+
* wrong. The normative source is the MCP Apps spec, which states "This extension is
|
|
86
|
+
* identified as: `io.modelcontextprotocol/ui`" in both the current `2026-01-26`
|
|
87
|
+
* revision and the draft, and the reference implementation agrees:
|
|
88
|
+
* `ext-apps/src/server/index.ts` exports `EXTENSION_ID = "io.modelcontextprotocol/ui"`.
|
|
89
|
+
*
|
|
90
|
+
* `io.modelcontextprotocol/apps` appears only as illustrative "e.g." text in the Rust
|
|
91
|
+
* and C# SDKs' generic `ServerCapabilities.extensions` doc comments (and their test
|
|
92
|
+
* fixtures), showing the SHAPE of the extensions map rather than naming this extension.
|
|
93
|
+
* Copying it from there declares the capability under a key no host looks up, which
|
|
94
|
+
* fails silently: rendering still works, because hosts fall back to detecting apps via
|
|
95
|
+
* `_meta.ui` plus the MIME type, so only the SEP-2133 declaration is lost.
|
|
96
|
+
*
|
|
97
|
+
* Duplicated as a literal rather than imported, because prefab ships zero dependencies.
|
|
98
|
+
*/
|
|
99
|
+
export const APPS_EXTENSION = 'io.modelcontextprotocol/ui';
|
|
100
|
+
/**
|
|
101
|
+
* Default cache hint for the viewer resource.
|
|
102
|
+
*
|
|
103
|
+
* The HTML is a pure function of this package's `VERSION` (the CDN base pins
|
|
104
|
+
* the exact version), so it cannot change for a given server build — it is
|
|
105
|
+
* safely shared-cacheable. Without this the SDK falls back to the conservative
|
|
106
|
+
* `{ ttlMs: 0, cacheScope: 'private' }` and the viewer is re-fetched every time.
|
|
107
|
+
*/
|
|
108
|
+
export const DEFAULT_VIEWER_CACHE = {
|
|
109
|
+
ttlMs: 86_400_000, // 24h
|
|
110
|
+
cacheScope: 'public',
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Runtime whitelist for {@link McpCacheScope}.
|
|
114
|
+
*
|
|
115
|
+
* Kept as a value (not just the union type) so the guard in `resolveCache` still
|
|
116
|
+
* rejects a bad scope handed over from untyped JS — a `!==` chain against the
|
|
117
|
+
* union narrows itself away and validates nothing.
|
|
118
|
+
*/
|
|
119
|
+
const CACHE_SCOPES = ['public', 'private'];
|
|
120
|
+
/** CDN base for the @maxhealth.tech/prefab package (exact version, never stale). */
|
|
121
|
+
function cdnBase() {
|
|
122
|
+
return `https://cdn.jsdelivr.net/npm/@maxhealth.tech/prefab@${VERSION}/dist`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Generate the HTML page for a prefab MCP Apps viewer resource.
|
|
126
|
+
*
|
|
127
|
+
* Returns the minimal HTML that loads `prefab.css` and `renderer.auto.min.js`
|
|
128
|
+
* from the CDN, plus any additional scripts/stylesheets you specify.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* import { rendererHtml } from '@maxhealth.tech/prefab/mcp'
|
|
133
|
+
* const html = rendererHtml()
|
|
134
|
+
* // or with extra scripts:
|
|
135
|
+
* const html = rendererHtml({ scripts: ['https://cdn.example.com/plugin.js'] })
|
|
136
|
+
* // inside VS Code, to follow the user's editor theme:
|
|
137
|
+
* const html = rendererHtml({ themeBridge: 'vscode' })
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export function rendererHtml(options) {
|
|
141
|
+
const title = options?.title ?? 'Prefab';
|
|
142
|
+
const base = options?.cdnBase ?? cdnBase();
|
|
143
|
+
// Order in <head> is load-bearing: prefab.css, then the bridge (which must win
|
|
144
|
+
// over it), then the caller's stylesheets as the outermost override.
|
|
145
|
+
const bridge = options?.themeBridge
|
|
146
|
+
? ` <style>\n${themeBridgeCss(options.themeBridge)}\n </style>\n`
|
|
147
|
+
: '';
|
|
148
|
+
const extraStyles = (options?.stylesheets ?? [])
|
|
149
|
+
.map(url => ` <link rel="stylesheet" crossorigin href="${escapeAttr(url)}">`)
|
|
150
|
+
.join('\n');
|
|
151
|
+
const extraScripts = (options?.scripts ?? [])
|
|
152
|
+
.map(url => ` <script crossorigin src="${escapeAttr(url)}"></script>`)
|
|
153
|
+
.join('\n');
|
|
154
|
+
return `<!doctype html>
|
|
155
|
+
<html lang="en">
|
|
156
|
+
<head>
|
|
157
|
+
<meta charset="UTF-8">
|
|
158
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
159
|
+
<title>${escapeHtml(title)}</title>
|
|
160
|
+
<link rel="stylesheet" crossorigin href="${base}/prefab.css">
|
|
161
|
+
${bridge}${extraStyles}</head>
|
|
162
|
+
<body>
|
|
163
|
+
<div id="root"></div>
|
|
164
|
+
<script crossorigin src="${base}/renderer.auto.min.js"></script>
|
|
165
|
+
${extraScripts}</body>
|
|
166
|
+
</html>`;
|
|
167
|
+
}
|
|
168
|
+
/** Reject values the SDK would silently discard in favour of `ttlMs: 0`. */
|
|
169
|
+
function resolveCache(hint) {
|
|
170
|
+
const ttlMs = hint?.ttlMs ?? DEFAULT_VIEWER_CACHE.ttlMs;
|
|
171
|
+
if (!Number.isSafeInteger(ttlMs) || ttlMs < 0) {
|
|
172
|
+
throw new RangeError(`cache.ttlMs must be a non-negative safe integer, got ${String(hint?.ttlMs)}`);
|
|
173
|
+
}
|
|
174
|
+
const cacheScope = hint?.cacheScope ?? DEFAULT_VIEWER_CACHE.cacheScope;
|
|
175
|
+
if (!CACHE_SCOPES.includes(cacheScope)) {
|
|
176
|
+
throw new RangeError(`cache.cacheScope must be 'public' or 'private', got ${String(hint?.cacheScope)}`);
|
|
177
|
+
}
|
|
178
|
+
return { ttlMs, cacheScope };
|
|
179
|
+
}
|
|
180
|
+
/** Declare the MCP Apps extension capability, tolerating servers that cannot. */
|
|
181
|
+
function declareAppsExtension(server) {
|
|
182
|
+
// Not named `declare` — that is a TS modifier keyword at statement position
|
|
183
|
+
// and some transpilers refuse to parse a call to a binding of that name.
|
|
184
|
+
const declareCaps = server.registerCapabilities?.bind(server)
|
|
185
|
+
?? server.server?.registerCapabilities?.bind(server.server);
|
|
186
|
+
if (declareCaps == null)
|
|
187
|
+
return;
|
|
188
|
+
try {
|
|
189
|
+
declareCaps({ extensions: { [APPS_EXTENSION]: {} } });
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
// Both SDK generations throw when called after connect. The server still
|
|
193
|
+
// serves the resource — it just does not advertise the extension.
|
|
194
|
+
log.warn(`could not declare the ${APPS_EXTENSION} capability (register the viewer before connecting):`, e);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Register the prefab viewer as a `ui://` resource on an MCP server.
|
|
199
|
+
*
|
|
200
|
+
* Handles the MIME type, CSP on both listing and content item, HTML generation,
|
|
201
|
+
* the `CacheableResult` fields and the Apps extension capability in one call.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* import { registerViewerResource, PREFAB_RESOURCE_URI, display } from '@maxhealth.tech/prefab/mcp'
|
|
206
|
+
*
|
|
207
|
+
* registerViewerResource(server)
|
|
208
|
+
*
|
|
209
|
+
* // The UI resource is attached to the tool DEFINITION via _meta.ui:
|
|
210
|
+
* server.registerTool('browse', {
|
|
211
|
+
* title: 'Browse patients',
|
|
212
|
+
* inputSchema: { query: z.string() },
|
|
213
|
+
* _meta: { ui: { resourceUri: PREFAB_RESOURCE_URI } },
|
|
214
|
+
* }, async (args) => display(autoTable(await search(args.query))))
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
export function registerViewerResource(server, options) {
|
|
218
|
+
const uri = options?.uri ?? PREFAB_RESOURCE_URI;
|
|
219
|
+
const title = options?.title ?? 'Prefab Viewer';
|
|
220
|
+
const cache = resolveCache(options?.cache);
|
|
221
|
+
// Merge CSP: always include jsdelivr for the default renderer
|
|
222
|
+
const csp = options?.csp
|
|
223
|
+
? {
|
|
224
|
+
resourceDomains: [...new Set([...(options.csp.resourceDomains ?? []), 'https://cdn.jsdelivr.net'])],
|
|
225
|
+
connectDomains: options.csp.connectDomains ?? [],
|
|
226
|
+
frameDomains: options.csp.frameDomains ?? [],
|
|
227
|
+
baseUriDomains: options.csp.baseUriDomains ?? [],
|
|
228
|
+
}
|
|
229
|
+
: { resourceDomains: ['https://cdn.jsdelivr.net'] };
|
|
230
|
+
// Add script origins to CSP resourceDomains
|
|
231
|
+
if (options?.scripts && options.scripts.length > 0) {
|
|
232
|
+
const scriptOrigins = options.scripts
|
|
233
|
+
.map(url => { try {
|
|
234
|
+
return new URL(url).origin;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return null;
|
|
238
|
+
} })
|
|
239
|
+
.filter((o) => o !== null);
|
|
240
|
+
csp.resourceDomains = [...new Set([...(csp.resourceDomains ?? []), ...scriptOrigins])];
|
|
241
|
+
}
|
|
242
|
+
const meta = resourceMeta({ csp, permissions: options?.permissions });
|
|
243
|
+
const html = rendererHtml({
|
|
244
|
+
title,
|
|
245
|
+
scripts: options?.scripts,
|
|
246
|
+
stylesheets: options?.stylesheets,
|
|
247
|
+
cdnBase: options?.cdnBase,
|
|
248
|
+
themeBridge: options?.themeBridge,
|
|
249
|
+
});
|
|
250
|
+
if (options?.declareCapability !== false)
|
|
251
|
+
declareAppsExtension(server);
|
|
252
|
+
// Extract name from URI: 'ui://prefab/viewer' -> 'prefab-viewer'
|
|
253
|
+
const name = uri.replace(/^ui:\/\//, '').replace(/\//g, '-');
|
|
254
|
+
const config = { title, mimeType: MCP_APP_MIME, _meta: meta, cacheHint: cache };
|
|
255
|
+
const handler = (resourceUri) => Promise.resolve({
|
|
256
|
+
contents: [{
|
|
257
|
+
uri: resourceUri.toString(),
|
|
258
|
+
mimeType: MCP_APP_MIME,
|
|
259
|
+
text: html,
|
|
260
|
+
_meta: meta,
|
|
261
|
+
}],
|
|
262
|
+
// Handler-provided cache fields take precedence over any server hint, and
|
|
263
|
+
// make the result spec-compliant on servers that fill in nothing.
|
|
264
|
+
ttlMs: cache.ttlMs,
|
|
265
|
+
cacheScope: cache.cacheScope,
|
|
266
|
+
});
|
|
267
|
+
// registerResource is the current API in both SDK generations; resource() is
|
|
268
|
+
// the v1 overload that v2 dropped.
|
|
269
|
+
const register = server.registerResource?.bind(server) ?? server.resource?.bind(server);
|
|
270
|
+
if (register == null) {
|
|
271
|
+
throw new TypeError('registerViewerResource: server exposes neither registerResource() nor resource()');
|
|
272
|
+
}
|
|
273
|
+
register(name, uri, config, handler);
|
|
274
|
+
}
|
|
275
|
+
// ── HTML escaping helpers ────────────────────────────────────────────────────
|
|
276
|
+
function escapeHtml(s) {
|
|
277
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
278
|
+
}
|
|
279
|
+
function escapeAttr(s) {
|
|
280
|
+
return s.replace(/&/g, '&').replace(/"/g, '"');
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/mcp/resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool-result envelope.
|
|
3
|
+
*
|
|
4
|
+
* Every prefab display helper returns the same shape: the wire JSON as a text
|
|
5
|
+
* content block plus `structuredContent` for MCP Apps hosts to hand to the
|
|
6
|
+
* iframe. Building that envelope in one place keeps the `display_*` builders
|
|
7
|
+
* identical by construction.
|
|
8
|
+
*
|
|
9
|
+
* `structuredContent` is typed through the generic rather than widened to
|
|
10
|
+
* `Record<string, unknown>`, so callers keep the wire type and no cast is
|
|
11
|
+
* needed anywhere (protocol revision 2026-07-28 loosened the field to any JSON
|
|
12
|
+
* value — SEP-2106).
|
|
13
|
+
*
|
|
14
|
+
* `resultType` (SEP-2322) is deliberately NOT set here. It is a wire-only
|
|
15
|
+
* discriminator owned by the SDK's protocol layer: `@modelcontextprotocol/server`
|
|
16
|
+
* stamps it at its 2026-era encode seam and strips it before results reach
|
|
17
|
+
* consumers, which is why its public result types do not declare it. The one
|
|
18
|
+
* handler-authored case is `resultType: 'input_required'` for multi-round-trip
|
|
19
|
+
* results — not something a display helper produces.
|
|
20
|
+
*/
|
|
21
|
+
import type { McpToolResult } from './types.js';
|
|
22
|
+
export interface ToolResultOptions {
|
|
23
|
+
/** Flag the result as failed (hosts render it as a tool error). */
|
|
24
|
+
isError?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Wrap a JSON payload as an MCP tool result.
|
|
28
|
+
*
|
|
29
|
+
* The payload is serialized into `content[0].text` (for hosts and models that
|
|
30
|
+
* read text) and passed through as `structuredContent` (for MCP Apps iframes,
|
|
31
|
+
* which receive it via `ui/notifications/tool-result`).
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { toolResult } from '@maxhealth.tech/prefab/mcp'
|
|
36
|
+
*
|
|
37
|
+
* // Returning pre-built wire JSON from your own tool handler:
|
|
38
|
+
* return toolResult(wireJson)
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function toolResult<T>(payload: T, options?: ToolResultOptions): McpToolResult<T>;
|
|
42
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/mcp/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAAC,CAAC,CAAC,CAOvF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool-result envelope.
|
|
3
|
+
*
|
|
4
|
+
* Every prefab display helper returns the same shape: the wire JSON as a text
|
|
5
|
+
* content block plus `structuredContent` for MCP Apps hosts to hand to the
|
|
6
|
+
* iframe. Building that envelope in one place keeps the `display_*` builders
|
|
7
|
+
* identical by construction.
|
|
8
|
+
*
|
|
9
|
+
* `structuredContent` is typed through the generic rather than widened to
|
|
10
|
+
* `Record<string, unknown>`, so callers keep the wire type and no cast is
|
|
11
|
+
* needed anywhere (protocol revision 2026-07-28 loosened the field to any JSON
|
|
12
|
+
* value — SEP-2106).
|
|
13
|
+
*
|
|
14
|
+
* `resultType` (SEP-2322) is deliberately NOT set here. It is a wire-only
|
|
15
|
+
* discriminator owned by the SDK's protocol layer: `@modelcontextprotocol/server`
|
|
16
|
+
* stamps it at its 2026-era encode seam and strips it before results reach
|
|
17
|
+
* consumers, which is why its public result types do not declare it. The one
|
|
18
|
+
* handler-authored case is `resultType: 'input_required'` for multi-round-trip
|
|
19
|
+
* results — not something a display helper produces.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a JSON payload as an MCP tool result.
|
|
23
|
+
*
|
|
24
|
+
* The payload is serialized into `content[0].text` (for hosts and models that
|
|
25
|
+
* read text) and passed through as `structuredContent` (for MCP Apps iframes,
|
|
26
|
+
* which receive it via `ui/notifications/tool-result`).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import { toolResult } from '@maxhealth.tech/prefab/mcp'
|
|
31
|
+
*
|
|
32
|
+
* // Returning pre-built wire JSON from your own tool handler:
|
|
33
|
+
* return toolResult(wireJson)
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function toolResult(payload, options) {
|
|
37
|
+
const result = {
|
|
38
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
39
|
+
structuredContent: payload,
|
|
40
|
+
};
|
|
41
|
+
if (options?.isError)
|
|
42
|
+
result.isError = true;
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/mcp/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AASH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,UAAU,CAAI,OAAU,EAAE,OAA2B;IACnE,MAAM,MAAM,GAAqB;QAC/B,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1D,iBAAiB,EAAE,OAAO;KAC3B,CAAA;IACD,IAAI,OAAO,EAAE,OAAO;QAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;IAC3C,OAAO,MAAM,CAAA;AACf,CAAC"}
|