refactor: use MCP resource types for ui/download-file params
Replace custom {filename, content, mimeType, encoding} params with
standard MCP resource types: EmbeddedResource for inline content and
ResourceLink for host-resolved references.
New params shape:
params: { contents: (EmbeddedResource | ResourceLink)[] }
This reuses existing MCP primitives instead of inventing new ones,
and enables hosts to show resource 'bubbles' or resolve ResourceLink
URIs via resources/read on the MCP server.
This commit is contained in:
parent
494c73e7ca
commit
3757271641
3 changed files with 366 additions and 125 deletions
204
src/generated/schema.json
generated
204
src/generated/schema.json
generated
|
|
@ -92,33 +92,187 @@
|
|||
"params": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"filename": {
|
||||
"type": "string",
|
||||
"description": "Suggested filename for the download."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "File content — text or base64-encoded binary."
|
||||
},
|
||||
"mimeType": {
|
||||
"type": "string",
|
||||
"description": "MIME type of the file (e.g. \"image/svg+xml\", \"application/json\")."
|
||||
},
|
||||
"encoding": {
|
||||
"description": "Content encoding. Defaults to \"utf-8\". Use \"base64\" for binary content.",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"const": "utf-8"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"const": "base64"
|
||||
}
|
||||
]
|
||||
"contents": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "resource"
|
||||
},
|
||||
"resource": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
"type": "string"
|
||||
},
|
||||
"_meta": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["uri", "text"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
"type": "string"
|
||||
},
|
||||
"_meta": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"blob": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["uri", "blob"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"audience": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["user", "assistant"]
|
||||
}
|
||||
},
|
||||
"priority": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"lastModified": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"_meta": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["type", "resource"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"icons": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"src": {
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
"type": "string"
|
||||
},
|
||||
"sizes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"type": "string",
|
||||
"enum": ["light", "dark"]
|
||||
}
|
||||
},
|
||||
"required": ["src"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
"type": "string"
|
||||
},
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"audience": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["user", "assistant"]
|
||||
}
|
||||
},
|
||||
"priority": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"lastModified": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"_meta": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "resource_link"
|
||||
}
|
||||
},
|
||||
"required": ["name", "uri", "type"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Resource contents to download — embedded (inline data) or linked (host fetches). Uses standard MCP resource types."
|
||||
}
|
||||
},
|
||||
"required": ["filename", "content", "mimeType"],
|
||||
"required": ["contents"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
|
|
|
|||
20
src/generated/schema.test.ts
generated
20
src/generated/schema.test.ts
generated
|
|
@ -35,10 +35,6 @@ export type McpUiOpenLinkResultSchemaInferredType = z.infer<
|
|||
typeof generated.McpUiOpenLinkResultSchema
|
||||
>;
|
||||
|
||||
export type McpUiDownloadFileRequestSchemaInferredType = z.infer<
|
||||
typeof generated.McpUiDownloadFileRequestSchema
|
||||
>;
|
||||
|
||||
export type McpUiDownloadFileResultSchemaInferredType = z.infer<
|
||||
typeof generated.McpUiDownloadFileResultSchema
|
||||
>;
|
||||
|
|
@ -131,6 +127,10 @@ export type McpUiClientCapabilitiesSchemaInferredType = z.infer<
|
|||
typeof generated.McpUiClientCapabilitiesSchema
|
||||
>;
|
||||
|
||||
export type McpUiDownloadFileRequestSchemaInferredType = z.infer<
|
||||
typeof generated.McpUiDownloadFileRequestSchema
|
||||
>;
|
||||
|
||||
export type McpUiMessageRequestSchemaInferredType = z.infer<
|
||||
typeof generated.McpUiMessageRequestSchema
|
||||
>;
|
||||
|
|
@ -187,12 +187,6 @@ expectType<spec.McpUiOpenLinkResult>(
|
|||
expectType<McpUiOpenLinkResultSchemaInferredType>(
|
||||
{} as spec.McpUiOpenLinkResult,
|
||||
);
|
||||
expectType<spec.McpUiDownloadFileRequest>(
|
||||
{} as McpUiDownloadFileRequestSchemaInferredType,
|
||||
);
|
||||
expectType<McpUiDownloadFileRequestSchemaInferredType>(
|
||||
{} as spec.McpUiDownloadFileRequest,
|
||||
);
|
||||
expectType<spec.McpUiDownloadFileResult>(
|
||||
{} as McpUiDownloadFileResultSchemaInferredType,
|
||||
);
|
||||
|
|
@ -307,6 +301,12 @@ expectType<spec.McpUiClientCapabilities>(
|
|||
expectType<McpUiClientCapabilitiesSchemaInferredType>(
|
||||
{} as spec.McpUiClientCapabilities,
|
||||
);
|
||||
expectType<spec.McpUiDownloadFileRequest>(
|
||||
{} as McpUiDownloadFileRequestSchemaInferredType,
|
||||
);
|
||||
expectType<McpUiDownloadFileRequestSchemaInferredType>(
|
||||
{} as spec.McpUiDownloadFileRequest,
|
||||
);
|
||||
expectType<spec.McpUiMessageRequest>(
|
||||
{} as McpUiMessageRequestSchemaInferredType,
|
||||
);
|
||||
|
|
|
|||
59
src/generated/schema.ts
generated
59
src/generated/schema.ts
generated
|
|
@ -5,8 +5,10 @@ import { z } from "zod";
|
|||
import {
|
||||
ContentBlockSchema,
|
||||
CallToolResultSchema,
|
||||
EmbeddedResourceSchema,
|
||||
ImplementationSchema,
|
||||
RequestIdSchema,
|
||||
ResourceLinkSchema,
|
||||
ToolSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
|
||||
|
|
@ -161,41 +163,6 @@ export const McpUiOpenLinkResultSchema = z
|
|||
})
|
||||
.passthrough();
|
||||
|
||||
/**
|
||||
* @description Request to download a file through the host.
|
||||
*
|
||||
* Sent from the View to the Host when the app wants to trigger a file download.
|
||||
* Since MCP Apps run in sandboxed iframes where direct downloads are blocked,
|
||||
* this provides a host-mediated mechanism for file exports.
|
||||
* The host SHOULD show a confirmation dialog before initiating the download.
|
||||
*
|
||||
* @see {@link app!App.downloadFile `App.downloadFile`} for the method that sends this request
|
||||
*/
|
||||
export const McpUiDownloadFileRequestSchema = z.object({
|
||||
method: z.literal("ui/download-file"),
|
||||
params: z.object({
|
||||
/** @description Suggested filename for the download. */
|
||||
filename: z.string().describe("Suggested filename for the download."),
|
||||
/** @description File content — text or base64-encoded binary. */
|
||||
content: z
|
||||
.string()
|
||||
.describe("File content \u2014 text or base64-encoded binary."),
|
||||
/** @description MIME type of the file (e.g. "image/svg+xml", "application/json"). */
|
||||
mimeType: z
|
||||
.string()
|
||||
.describe(
|
||||
'MIME type of the file (e.g. "image/svg+xml", "application/json").',
|
||||
),
|
||||
/** @description Content encoding. Defaults to "utf-8". Use "base64" for binary content. */
|
||||
encoding: z
|
||||
.union([z.literal("utf-8"), z.literal("base64")])
|
||||
.optional()
|
||||
.describe(
|
||||
'Content encoding. Defaults to "utf-8". Use "base64" for binary content.',
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Result from a file download request.
|
||||
* @see {@link McpUiDownloadFileRequest `McpUiDownloadFileRequest`}
|
||||
|
|
@ -755,6 +722,28 @@ export const McpUiClientCapabilitiesSchema = z.object({
|
|||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Request to download a file through the host.
|
||||
*
|
||||
* Sent from the View to the Host when the app wants to trigger a file download.
|
||||
* Since MCP Apps run in sandboxed iframes where direct downloads are blocked,
|
||||
* this provides a host-mediated mechanism for file exports.
|
||||
* The host SHOULD show a confirmation dialog before initiating the download.
|
||||
*
|
||||
* @see {@link app!App.downloadFile `App.downloadFile`} for the method that sends this request
|
||||
*/
|
||||
export const McpUiDownloadFileRequestSchema = z.object({
|
||||
method: z.literal("ui/download-file"),
|
||||
params: z.object({
|
||||
/** @description Resource contents to download — embedded (inline data) or linked (host fetches). Uses standard MCP resource types. */
|
||||
contents: z
|
||||
.array(z.union([EmbeddedResourceSchema, ResourceLinkSchema]))
|
||||
.describe(
|
||||
"Resource contents to download \u2014 embedded (inline data) or linked (host fetches). Uses standard MCP resource types.",
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Request to send a message to the host's chat interface.
|
||||
* @see {@link app!App.sendMessage `App.sendMessage`} for the method that sends this request
|
||||
|
|
|
|||
Loading…
Reference in a new issue