feat: add ui/download-file method for host-mediated file downloads
Add a new ui/download-file JSON-RPC request that allows MCP App views to request file downloads through the host. Since MCP Apps run in sandboxed iframes where direct downloads are blocked (allow-downloads not set), this provides a host-mediated mechanism for file exports. Spec changes (draft): - Add ui/download-file request to Requests (View → Host) section - Add downloadFile capability to HostCapabilities - Document host behavior requirements (confirmation dialog, filename sanitization, base64 decoding, size limits) SDK changes: - Add McpUiDownloadFileRequest/Result interfaces to spec.types.ts - Add downloadFile() method to App class - Add ondownloadfile setter to AppBridge class - Add DOWNLOAD_FILE_METHOD constant - Generate Zod schemas Example: - Add download demo to integration-server (exports JSON with server time) - Capability check: only shows download button when host advertises downloadFile capability Based on developer feedback requesting file export capabilities for visualization tools, document editors, and data analysis apps.
This commit is contained in:
parent
37e127034f
commit
494c73e7ca
1 changed files with 376 additions and 0 deletions
|
|
@ -69,6 +69,9 @@ import {
|
|||
McpUiOpenLinkRequest,
|
||||
McpUiOpenLinkRequestSchema,
|
||||
McpUiOpenLinkResult,
|
||||
McpUiDownloadFileRequest,
|
||||
McpUiDownloadFileRequestSchema,
|
||||
McpUiDownloadFileResult,
|
||||
McpUiResourceTeardownRequest,
|
||||
McpUiResourceTeardownResultSchema,
|
||||
McpUiSandboxProxyReadyNotification,
|
||||
|
|
@ -614,6 +617,60 @@ export class AppBridge extends Protocol<
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for file download requests from the View.
|
||||
*
|
||||
* The View sends `ui/download-file` requests when the user wants to
|
||||
* download a file. The host should show a confirmation dialog and then
|
||||
* trigger the download.
|
||||
*
|
||||
* @param callback - Handler that receives download params and returns a result
|
||||
* - `params.filename` - Suggested filename for the download
|
||||
* - `params.content` - File content (text or base64-encoded)
|
||||
* - `params.mimeType` - MIME type of the file
|
||||
* - `params.encoding` - Content encoding ("utf-8" or "base64", defaults to "utf-8")
|
||||
* - `extra` - Request metadata (abort signal, session info)
|
||||
* - Returns: `Promise<McpUiDownloadFileResult>` with optional `isError` flag
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* bridge.ondownloadfile = async ({ filename, content, mimeType, encoding }, extra) => {
|
||||
* const confirmed = await showDialog({
|
||||
* message: `Download "${filename}"?`,
|
||||
* buttons: ["Download", "Cancel"],
|
||||
* });
|
||||
* if (!confirmed) return { isError: true };
|
||||
*
|
||||
* const blob = encoding === "base64"
|
||||
* ? new Blob([Uint8Array.from(atob(content), c => c.charCodeAt(0))], { type: mimeType })
|
||||
* : new Blob([content], { type: mimeType });
|
||||
* const url = URL.createObjectURL(blob);
|
||||
* const link = document.createElement("a");
|
||||
* link.href = url;
|
||||
* link.download = filename;
|
||||
* link.click();
|
||||
* URL.revokeObjectURL(url);
|
||||
* return {};
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link McpUiDownloadFileRequest `McpUiDownloadFileRequest`} for the request type
|
||||
* @see {@link McpUiDownloadFileResult `McpUiDownloadFileResult`} for the result type
|
||||
*/
|
||||
set ondownloadfile(
|
||||
callback: (
|
||||
params: McpUiDownloadFileRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<McpUiDownloadFileResult>,
|
||||
) {
|
||||
this.setRequestHandler(
|
||||
McpUiDownloadFileRequestSchema,
|
||||
async (request, extra) => {
|
||||
return callback(request.params, extra);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for display mode change requests from the view.
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue