Merge pull request #212 from jonathanhefner/video-resource-server-safeareainsets
Support `safeAreaInsets` + type-checking in video-resource-server
This commit is contained in:
commit
dadbb6ac3d
3 changed files with 35 additions and 17 deletions
|
|
@ -16,7 +16,7 @@
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env INPUT=mcp-app.html vite build",
|
"build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build",
|
||||||
"watch": "cross-env INPUT=mcp-app.html vite build --watch",
|
"watch": "cross-env INPUT=mcp-app.html vite build --watch",
|
||||||
"serve": "bun server.ts",
|
"serve": "bun server.ts",
|
||||||
"start": "cross-env NODE_ENV=development npm run build && npm run serve",
|
"start": "cross-env NODE_ENV=development npm run build && npm run serve",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
* Demonstrates fetching binary content (video) via MCP resources.
|
* Demonstrates fetching binary content (video) via MCP resources.
|
||||||
* The video is served as a base64 blob and converted to a data URI for playback.
|
* The video is served as a base64 blob and converted to a data URI for playback.
|
||||||
*/
|
*/
|
||||||
import { App } from "@modelcontextprotocol/ext-apps";
|
import { App, type McpUiHostContext } from "@modelcontextprotocol/ext-apps";
|
||||||
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { ReadResourceResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
import { ReadResourceResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import "./global.css";
|
import "./global.css";
|
||||||
|
|
@ -16,6 +16,7 @@ const log = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get element references
|
// Get element references
|
||||||
|
const mainEl = document.querySelector(".main") as HTMLElement;
|
||||||
const loadingEl = document.getElementById("loading")!;
|
const loadingEl = document.getElementById("loading")!;
|
||||||
const loadingTextEl = document.getElementById("loading-text")!;
|
const loadingTextEl = document.getElementById("loading-text")!;
|
||||||
const errorEl = document.getElementById("error")!;
|
const errorEl = document.getElementById("error")!;
|
||||||
|
|
@ -83,16 +84,13 @@ app.ontoolresult = async (result) => {
|
||||||
ReadResourceResultSchema,
|
ReadResourceResultSchema,
|
||||||
);
|
);
|
||||||
|
|
||||||
log.info(
|
|
||||||
"Resource received, blob size:",
|
|
||||||
resourceResult.contents[0]?.blob?.length,
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = resourceResult.contents[0];
|
const content = resourceResult.contents[0];
|
||||||
if (!content?.blob) {
|
if (!content || !("blob" in content)) {
|
||||||
throw new Error("Resource response did not contain blob data");
|
throw new Error("Resource response did not contain blob data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("Resource received, blob size:", content.blob.length);
|
||||||
|
|
||||||
showLoading("Converting to data URI...");
|
showLoading("Converting to data URI...");
|
||||||
|
|
||||||
const mimeType = content.mimeType || "video/mp4";
|
const mimeType = content.mimeType || "video/mp4";
|
||||||
|
|
@ -112,5 +110,21 @@ app.onerror = (err) => {
|
||||||
showError(err instanceof Error ? err.message : String(err));
|
showError(err instanceof Error ? err.message : String(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function handleHostContextChanged(ctx: McpUiHostContext) {
|
||||||
|
if (ctx.safeAreaInsets) {
|
||||||
|
mainEl.style.paddingTop = `${ctx.safeAreaInsets.top}px`;
|
||||||
|
mainEl.style.paddingRight = `${ctx.safeAreaInsets.right}px`;
|
||||||
|
mainEl.style.paddingBottom = `${ctx.safeAreaInsets.bottom}px`;
|
||||||
|
mainEl.style.paddingLeft = `${ctx.safeAreaInsets.left}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.onhostcontextchanged = handleHostContextChanged;
|
||||||
|
|
||||||
// Connect to host
|
// Connect to host
|
||||||
app.connect();
|
app.connect().then(() => {
|
||||||
|
const ctx = app.getHostContext();
|
||||||
|
if (ctx) {
|
||||||
|
handleHostContextChanged(ctx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "NodeNext",
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
"moduleResolution": "NodeNext",
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"noUnusedLocals": true,
|
||||||
"resolveJsonModule": true,
|
"noUnusedParameters": true,
|
||||||
"declaration": false,
|
"noFallthroughCasesInSwitch": true
|
||||||
"outDir": "dist",
|
|
||||||
"lib": ["ESNext", "DOM"]
|
|
||||||
},
|
},
|
||||||
"include": ["server.ts", "src/**/*"]
|
"include": ["src", "server.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue