Add binary blob resource pattern to patterns guide

Document how to serve binary content (e.g., video) via MCP resources
as base64-encoded blobs. Includes server-side registration using the
`blob` field and client-side fetching with conversion to data URIs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jonathan Hefner 2026-01-28 14:34:54 -06:00
commit 13615613d4
No known key found for this signature in database
GPG key ID: 0DA40AFD21043691
2 changed files with 105 additions and 2 deletions

View file

@ -249,6 +249,53 @@ try {
}
```
## Serving binary blobs via resources
Binary content (e.g., video) can be served via MCP resources as base64-encoded blobs. The server returns the data in the `blob` field of the resource content, and the App fetches it via `resources/read` for use in the browser.
**Server-side**: Register a resource that returns binary data in the `blob` field:
<!-- prettier-ignore -->
```ts source="./patterns.tsx#binaryBlobResourceServer"
server.registerResource(
"Video",
new ResourceTemplate("video://{id}", { list: undefined }),
{
description: "Video data served as base64 blob",
mimeType: "video/mp4",
},
async (uri, { id }): Promise<ReadResourceResult> => {
// Fetch or load your binary data
const idString = Array.isArray(id) ? id[0] : id;
const buffer = await getVideoData(idString);
const blob = Buffer.from(buffer).toString("base64");
return { contents: [{ uri: uri.href, mimeType: "video/mp4", blob }] };
},
);
```
**Client-side**: Fetch the resource and convert the base64 blob to a data URI:
<!-- prettier-ignore -->
```ts source="./patterns.tsx#binaryBlobResourceClient"
const result = await app.request(
{ method: "resources/read", params: { uri: `video://${videoId}` } },
ReadResourceResultSchema,
);
const content = result.contents[0];
if (!content || !("blob" in content)) {
throw new Error("Resource did not contain blob data");
}
const videoEl = document.querySelector("video")!;
videoEl.src = `data:${content.mimeType!};base64,${content.blob}`;
```
> [!NOTE]
> For a full example that implements this pattern, see: [`examples/video-resource-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/video-resource-server).
## Adapting to host context (theme, styling, fonts, and safe areas)
The host provides context about its environment via {@link types!McpUiHostContext `McpUiHostContext`}. Use this to adapt your app's appearance and layout:

View file

@ -14,12 +14,19 @@ import {
applyHostStyleVariables,
} from "../src/styles.js";
import { randomUUID } from "node:crypto";
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import type {
CallToolResult,
ReadResourceResult,
} from "@modelcontextprotocol/sdk/types.js";
import { ReadResourceResultSchema } from "@modelcontextprotocol/sdk/types.js";
import type { McpUiHostContext } from "../src/types.js";
import { useEffect, useState } from "react";
import { useApp } from "../src/react/index.js";
import { registerAppTool } from "../src/server/index.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
McpServer,
ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
/**
@ -213,6 +220,53 @@ function chunkedDataClient(app: App, resourceId: string) {
//#endregion chunkedDataClient
}
/**
* Example: Serving binary blobs via resources (server-side)
*/
function binaryBlobResourceServer(
server: McpServer,
getVideoData: (id: string) => Promise<ArrayBuffer>,
) {
//#region binaryBlobResourceServer
server.registerResource(
"Video",
new ResourceTemplate("video://{id}", { list: undefined }),
{
description: "Video data served as base64 blob",
mimeType: "video/mp4",
},
async (uri, { id }): Promise<ReadResourceResult> => {
// Fetch or load your binary data
const idString = Array.isArray(id) ? id[0] : id;
const buffer = await getVideoData(idString);
const blob = Buffer.from(buffer).toString("base64");
return { contents: [{ uri: uri.href, mimeType: "video/mp4", blob }] };
},
);
//#endregion binaryBlobResourceServer
}
/**
* Example: Serving binary blobs via resources (client-side)
*/
async function binaryBlobResourceClient(app: App, videoId: string) {
//#region binaryBlobResourceClient
const result = await app.request(
{ method: "resources/read", params: { uri: `video://${videoId}` } },
ReadResourceResultSchema,
);
const content = result.contents[0];
if (!content || !("blob" in content)) {
throw new Error("Resource did not contain blob data");
}
const videoEl = document.querySelector("video")!;
videoEl.src = `data:${content.mimeType!};base64,${content.blob}`;
//#endregion binaryBlobResourceClient
}
/**
* Example: Adapting to host context (theme, CSS variables, fonts, safe areas)
*/
@ -406,6 +460,8 @@ void pollingVanillaJs;
void pollingReact;
void chunkedDataServer;
void chunkedDataClient;
void binaryBlobResourceServer;
void binaryBlobResourceClient;
void hostContextVanillaJs;
void hostContextReact;
void persistViewStateServer;