Merge pull request #159 from modelcontextprotocol/martinalong/mcp-apps/custom-fonts
[MCP Apps] Add way to pass custom fonts
This commit is contained in:
commit
242ec23856
12 changed files with 323 additions and 4 deletions
|
|
@ -2,6 +2,9 @@
|
||||||
:root {
|
:root {
|
||||||
color-scheme: light dark;
|
color-scheme: light dark;
|
||||||
|
|
||||||
|
/* Font families */
|
||||||
|
--font-sans: system-ui, -apple-system, sans-serif;
|
||||||
|
|
||||||
/* Background colors */
|
/* Background colors */
|
||||||
--color-background-primary: light-dark(#ffffff, #111827);
|
--color-background-primary: light-dark(#ffffff, #111827);
|
||||||
--color-background-secondary: light-dark(#f9fafb, #1f2937);
|
--color-background-secondary: light-dark(#f9fafb, #1f2937);
|
||||||
|
|
@ -33,6 +36,7 @@
|
||||||
html, body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
font-family: var(--font-sans);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {
|
||||||
App,
|
App,
|
||||||
PostMessageTransport,
|
PostMessageTransport,
|
||||||
applyHostStyleVariables,
|
applyHostStyleVariables,
|
||||||
|
applyHostFonts,
|
||||||
applyDocumentTheme,
|
applyDocumentTheme,
|
||||||
} from "@modelcontextprotocol/ext-apps";
|
} from "@modelcontextprotocol/ext-apps";
|
||||||
import { Chart, registerables } from "chart.js";
|
import { Chart, registerables } from "chart.js";
|
||||||
|
|
@ -448,7 +449,7 @@ applyDocumentTheme(systemDark ? "dark" : "light");
|
||||||
// Register handlers and connect
|
// Register handlers and connect
|
||||||
app.onerror = log.error;
|
app.onerror = log.error;
|
||||||
|
|
||||||
// Handle host context changes (theme and styles from host)
|
// Handle host context changes (theme, styles, and fonts from host)
|
||||||
app.onhostcontextchanged = (params) => {
|
app.onhostcontextchanged = (params) => {
|
||||||
if (params.theme) {
|
if (params.theme) {
|
||||||
applyDocumentTheme(params.theme);
|
applyDocumentTheme(params.theme);
|
||||||
|
|
@ -456,6 +457,9 @@ app.onhostcontextchanged = (params) => {
|
||||||
if (params.styles?.variables) {
|
if (params.styles?.variables) {
|
||||||
applyHostStyleVariables(params.styles.variables);
|
applyHostStyleVariables(params.styles.variables);
|
||||||
}
|
}
|
||||||
|
if (params.styles?.css?.fonts) {
|
||||||
|
applyHostFonts(params.styles.css.fonts);
|
||||||
|
}
|
||||||
// Recreate chart to pick up new colors
|
// Recreate chart to pick up new colors
|
||||||
if (state.chart && (params.theme || params.styles?.variables)) {
|
if (state.chart && (params.theme || params.styles?.variables)) {
|
||||||
state.chart.destroy();
|
state.chart.destroy();
|
||||||
|
|
@ -472,6 +476,9 @@ app.connect().then(() => {
|
||||||
if (ctx?.styles?.variables) {
|
if (ctx?.styles?.variables) {
|
||||||
applyHostStyleVariables(ctx.styles.variables);
|
applyHostStyleVariables(ctx.styles.variables);
|
||||||
}
|
}
|
||||||
|
if (ctx?.styles?.css?.fonts) {
|
||||||
|
applyHostFonts(ctx.styles.css.fonts);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch data after connection
|
// Fetch data after connection
|
||||||
|
|
|
||||||
|
|
@ -451,6 +451,11 @@ interface HostContext {
|
||||||
styles?: {
|
styles?: {
|
||||||
/** CSS variables for theming */
|
/** CSS variables for theming */
|
||||||
variables?: Record<McpUiStyleVariableKey, string | undefined>;
|
variables?: Record<McpUiStyleVariableKey, string | undefined>;
|
||||||
|
/** CSS blocks that apps can inject */
|
||||||
|
css?: {
|
||||||
|
/** CSS for font loading (@font-face rules or @import statements) */
|
||||||
|
fonts?: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
/** How the UI is currently displayed */
|
/** How the UI is currently displayed */
|
||||||
displayMode?: "inline" | "fullscreen" | "pip";
|
displayMode?: "inline" | "fullscreen" | "pip";
|
||||||
|
|
@ -503,8 +508,11 @@ Example:
|
||||||
"variables": {
|
"variables": {
|
||||||
"--color-background-primary": "light-dark(#ffffff, #171717)",
|
"--color-background-primary": "light-dark(#ffffff, #171717)",
|
||||||
"--color-text-primary": "light-dark(#171717, #fafafa)",
|
"--color-text-primary": "light-dark(#171717, #fafafa)",
|
||||||
"--font-family-sans": "system-ui, sans-serif",
|
"--font-sans": "Anthropic Sans, sans-serif",
|
||||||
...
|
...
|
||||||
|
},
|
||||||
|
"css": {
|
||||||
|
"fonts": "@font-face { font-family: \"Custom Font Name\"; src: url(\"https://...\"); }"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"displayMode": "inline",
|
"displayMode": "inline",
|
||||||
|
|
@ -638,7 +646,46 @@ Example usage of standardized CSS variables:
|
||||||
.container {
|
.container {
|
||||||
background: var(--color-background-primary);
|
background: var(--color-background-primary);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
font: var(--font-style-body);
|
font-family: var(--font-sans);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom Fonts
|
||||||
|
|
||||||
|
Hosts can provide custom fonts via `styles.css.fonts`, which can contain `@font-face` rules for self-hosted fonts, `@import` statements for font services like Google Fonts, or both:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
hostContext.styles.variables["--font-sans"] = '"Font Name", sans-serif';
|
||||||
|
|
||||||
|
// Self-hosted fonts
|
||||||
|
hostContext.styles.css.fonts = `
|
||||||
|
@font-face {
|
||||||
|
font-family: "Font Name";
|
||||||
|
src: url("https://url-where-font-is-hosted.com/.../Regular.otf") format("opentype");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Font Name";
|
||||||
|
src: url("https://url-where-font-is-hosted.com/.../Medium.otf") format("opentype");
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: medium;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Google Fonts
|
||||||
|
hostContext.styles.css.fonts = `
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Font+Name&display=swap');
|
||||||
|
`;
|
||||||
|
```
|
||||||
|
|
||||||
|
Apps can use the `applyHostFonts` utility to inject the font CSS into the document:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
if (hostContext.styles?.css?.fonts) {
|
||||||
|
applyHostFonts(hostContext.styles.css.fonts);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ export { PostMessageTransport } from "./message-transport";
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
export {
|
export {
|
||||||
applyHostStyleVariables,
|
applyHostStyleVariables,
|
||||||
|
applyHostFonts,
|
||||||
getDocumentTheme,
|
getDocumentTheme,
|
||||||
applyDocumentTheme,
|
applyDocumentTheme,
|
||||||
} from "./styles";
|
} from "./styles";
|
||||||
|
|
|
||||||
55
src/generated/schema.json
generated
55
src/generated/schema.json
generated
|
|
@ -585,6 +585,17 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"css": {
|
||||||
|
"description": "CSS blocks that apps can inject.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fonts": {
|
||||||
|
"description": "CSS for font loading (@font-face rules or",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|
@ -1196,6 +1207,17 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"css": {
|
||||||
|
"description": "CSS blocks that apps can inject.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fonts": {
|
||||||
|
"description": "CSS for font loading (@font-face rules or",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|
@ -1319,6 +1341,17 @@
|
||||||
},
|
},
|
||||||
"additionalProperties": {}
|
"additionalProperties": {}
|
||||||
},
|
},
|
||||||
|
"McpUiHostCss": {
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fonts": {
|
||||||
|
"description": "CSS for font loading (@font-face rules or",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
"McpUiHostStyles": {
|
"McpUiHostStyles": {
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
@ -1640,6 +1673,17 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"css": {
|
||||||
|
"description": "CSS blocks that apps can inject.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fonts": {
|
||||||
|
"description": "CSS for font loading (@font-face rules or",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|
@ -2314,6 +2358,17 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"css": {
|
||||||
|
"description": "CSS blocks that apps can inject.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fonts": {
|
||||||
|
"description": "CSS for font loading (@font-face rules or",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|
|
||||||
6
src/generated/schema.test.ts
generated
6
src/generated/schema.test.ts
generated
|
|
@ -63,6 +63,10 @@ export type McpUiToolCancelledNotificationSchemaInferredType = z.infer<
|
||||||
typeof generated.McpUiToolCancelledNotificationSchema
|
typeof generated.McpUiToolCancelledNotificationSchema
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type McpUiHostCssSchemaInferredType = z.infer<
|
||||||
|
typeof generated.McpUiHostCssSchema
|
||||||
|
>;
|
||||||
|
|
||||||
export type McpUiHostStylesSchemaInferredType = z.infer<
|
export type McpUiHostStylesSchemaInferredType = z.infer<
|
||||||
typeof generated.McpUiHostStylesSchema
|
typeof generated.McpUiHostStylesSchema
|
||||||
>;
|
>;
|
||||||
|
|
@ -197,6 +201,8 @@ expectType<spec.McpUiToolCancelledNotification>(
|
||||||
expectType<McpUiToolCancelledNotificationSchemaInferredType>(
|
expectType<McpUiToolCancelledNotificationSchemaInferredType>(
|
||||||
{} as spec.McpUiToolCancelledNotification,
|
{} as spec.McpUiToolCancelledNotification,
|
||||||
);
|
);
|
||||||
|
expectType<spec.McpUiHostCss>({} as McpUiHostCssSchemaInferredType);
|
||||||
|
expectType<McpUiHostCssSchemaInferredType>({} as spec.McpUiHostCss);
|
||||||
expectType<spec.McpUiHostStyles>({} as McpUiHostStylesSchemaInferredType);
|
expectType<spec.McpUiHostStyles>({} as McpUiHostStylesSchemaInferredType);
|
||||||
expectType<McpUiHostStylesSchemaInferredType>({} as spec.McpUiHostStyles);
|
expectType<McpUiHostStylesSchemaInferredType>({} as spec.McpUiHostStyles);
|
||||||
expectType<spec.McpUiResourceTeardownRequest>(
|
expectType<spec.McpUiResourceTeardownRequest>(
|
||||||
|
|
|
||||||
15
src/generated/schema.ts
generated
15
src/generated/schema.ts
generated
|
|
@ -285,6 +285,17 @@ export const McpUiToolCancelledNotificationSchema = z.object({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description CSS blocks that can be injected by apps.
|
||||||
|
*/
|
||||||
|
export const McpUiHostCssSchema = z.object({
|
||||||
|
/** @description CSS for font loading (@font-face rules or @import statements). Apps must apply using applyHostFonts(). */
|
||||||
|
fonts: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe("CSS for font loading (@font-face rules or"),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Style configuration for theming MCP apps.
|
* @description Style configuration for theming MCP apps.
|
||||||
*/
|
*/
|
||||||
|
|
@ -293,6 +304,10 @@ export const McpUiHostStylesSchema = z.object({
|
||||||
variables: McpUiStylesSchema.optional().describe(
|
variables: McpUiStylesSchema.optional().describe(
|
||||||
"CSS variables for theming the app.",
|
"CSS variables for theming the app.",
|
||||||
),
|
),
|
||||||
|
/** @description CSS blocks that apps can inject. */
|
||||||
|
css: McpUiHostCssSchema.optional().describe(
|
||||||
|
"CSS blocks that apps can inject.",
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
*
|
*
|
||||||
* - {@link useApp} - React hook to create and connect an MCP App
|
* - {@link useApp} - React hook to create and connect an MCP App
|
||||||
* - {@link useHostStyleVariables} - React hook to apply host style variables and theme
|
* - {@link useHostStyleVariables} - React hook to apply host style variables and theme
|
||||||
|
* - {@link useHostFonts} - React hook to apply host fonts
|
||||||
* - {@link useDocumentTheme} - React hook for reactive document theme
|
* - {@link useDocumentTheme} - React hook for reactive document theme
|
||||||
* - {@link useAutoResize} - React hook for manual auto-resize control (rarely needed)
|
* - {@link useAutoResize} - React hook for manual auto-resize control (rarely needed)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { App } from "../app";
|
import { App } from "../app";
|
||||||
import { applyDocumentTheme, applyHostStyleVariables } from "../styles";
|
import {
|
||||||
|
applyDocumentTheme,
|
||||||
|
applyHostFonts,
|
||||||
|
applyHostStyleVariables,
|
||||||
|
} from "../styles";
|
||||||
import { McpUiHostContext } from "../types";
|
import { McpUiHostContext } from "../types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,6 +58,7 @@ import { McpUiHostContext } from "../types";
|
||||||
*
|
*
|
||||||
* @see {@link applyHostStyleVariables} for the underlying styles function
|
* @see {@link applyHostStyleVariables} for the underlying styles function
|
||||||
* @see {@link applyDocumentTheme} for the underlying theme function
|
* @see {@link applyDocumentTheme} for the underlying theme function
|
||||||
|
* @see {@link useHostFonts} for applying host fonts
|
||||||
* @see {@link McpUiStyles} for available CSS variables
|
* @see {@link McpUiStyles} for available CSS variables
|
||||||
*/
|
*/
|
||||||
export function useHostStyleVariables(
|
export function useHostStyleVariables(
|
||||||
|
|
@ -94,3 +99,104 @@ export function useHostStyleVariables(
|
||||||
};
|
};
|
||||||
}, [app]);
|
}, [app]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React hook that applies host fonts from CSS.
|
||||||
|
*
|
||||||
|
* This hook listens to host context changes and automatically applies:
|
||||||
|
* - `styles.css.fonts` as a `<style>` tag for custom fonts
|
||||||
|
*
|
||||||
|
* The CSS can contain `@font-face` rules for self-hosted fonts,
|
||||||
|
* `@import` statements for Google Fonts or other font services, or both.
|
||||||
|
*
|
||||||
|
* The hook also applies fonts from the initial host context when
|
||||||
|
* the app first connects.
|
||||||
|
*
|
||||||
|
* @param app - The connected App instance, or null during initialization
|
||||||
|
* @param initialContext - Initial host context from the connection (optional).
|
||||||
|
* If provided, fonts will be applied immediately on mount.
|
||||||
|
*
|
||||||
|
* @example Basic usage with useApp
|
||||||
|
* ```tsx
|
||||||
|
* import { useApp } from '@modelcontextprotocol/ext-apps/react';
|
||||||
|
* import { useHostFonts } from '@modelcontextprotocol/ext-apps/react';
|
||||||
|
*
|
||||||
|
* function MyApp() {
|
||||||
|
* const { app, isConnected } = useApp({
|
||||||
|
* appInfo: { name: "MyApp", version: "1.0.0" },
|
||||||
|
* capabilities: {},
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // Automatically apply host fonts
|
||||||
|
* useHostFonts(app);
|
||||||
|
*
|
||||||
|
* return (
|
||||||
|
* <div style={{ fontFamily: 'var(--font-sans)' }}>
|
||||||
|
* Hello!
|
||||||
|
* </div>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example With initial context
|
||||||
|
* ```tsx
|
||||||
|
* const [hostContext, setHostContext] = useState<McpUiHostContext | null>(null);
|
||||||
|
*
|
||||||
|
* // ... get initial context from app.connect() result
|
||||||
|
*
|
||||||
|
* useHostFonts(app, hostContext);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @see {@link applyHostFonts} for the underlying fonts function
|
||||||
|
* @see {@link useHostStyleVariables} for applying style variables and theme
|
||||||
|
*/
|
||||||
|
export function useHostFonts(
|
||||||
|
app: App | null,
|
||||||
|
initialContext?: McpUiHostContext | null,
|
||||||
|
): void {
|
||||||
|
const initialApplied = useRef(false);
|
||||||
|
|
||||||
|
// Apply initial fonts once on mount
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialApplied.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (initialContext?.styles?.css?.fonts) {
|
||||||
|
applyHostFonts(initialContext.styles.css.fonts);
|
||||||
|
initialApplied.current = true;
|
||||||
|
}
|
||||||
|
}, [initialContext]);
|
||||||
|
|
||||||
|
// Listen for host context changes and apply updated fonts
|
||||||
|
useEffect(() => {
|
||||||
|
if (!app) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.onhostcontextchanged = (params) => {
|
||||||
|
if (params.styles?.css?.fonts) {
|
||||||
|
applyHostFonts(params.styles.css.fonts);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [app]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React hook that applies host styles, fonts, and theme.
|
||||||
|
*
|
||||||
|
* This is a convenience hook that combines {@link useHostStyleVariables} and
|
||||||
|
* {@link useHostFonts}. Use the individual hooks if you need more control.
|
||||||
|
*
|
||||||
|
* @param app - The connected App instance, or null during initialization
|
||||||
|
* @param initialContext - Initial host context from the connection (optional).
|
||||||
|
*
|
||||||
|
* @see {@link useHostStyleVariables} for style variables and theme only
|
||||||
|
* @see {@link useHostFonts} for fonts only
|
||||||
|
*/
|
||||||
|
export function useHostStyles(
|
||||||
|
app: App | null,
|
||||||
|
initialContext?: McpUiHostContext | null,
|
||||||
|
): void {
|
||||||
|
useHostStyleVariables(app, initialContext);
|
||||||
|
useHostFonts(app, initialContext);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -284,12 +284,22 @@ export interface McpUiToolCancelledNotification {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description CSS blocks that can be injected by apps.
|
||||||
|
*/
|
||||||
|
export interface McpUiHostCss {
|
||||||
|
/** @description CSS for font loading (@font-face rules or @import statements). Apps must apply using applyHostFonts(). */
|
||||||
|
fonts?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Style configuration for theming MCP apps.
|
* @description Style configuration for theming MCP apps.
|
||||||
*/
|
*/
|
||||||
export interface McpUiHostStyles {
|
export interface McpUiHostStyles {
|
||||||
/** @description CSS variables for theming the app. */
|
/** @description CSS variables for theming the app. */
|
||||||
variables?: McpUiStyles;
|
variables?: McpUiStyles;
|
||||||
|
/** @description CSS blocks that apps can inject. */
|
||||||
|
css?: McpUiHostCss;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -114,3 +114,68 @@ export function applyHostStyleVariables(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply host font CSS to the document.
|
||||||
|
*
|
||||||
|
* This function takes the `css.fonts` string from `McpUiHostContext.styles` and
|
||||||
|
* injects it as a `<style>` tag. The CSS can contain `@font-face` rules for
|
||||||
|
* self-hosted fonts, `@import` statements for Google Fonts or other font services,
|
||||||
|
* or a combination of both.
|
||||||
|
*
|
||||||
|
* The styles are only injected once. Subsequent calls will not create duplicate
|
||||||
|
* style tags.
|
||||||
|
*
|
||||||
|
* @param fontCss - CSS string containing @font-face rules and/or @import statements
|
||||||
|
*
|
||||||
|
* @example Apply fonts from host context
|
||||||
|
* ```typescript
|
||||||
|
* import { applyHostFonts } from '@modelcontextprotocol/ext-apps';
|
||||||
|
*
|
||||||
|
* app.onhostcontextchanged = (params) => {
|
||||||
|
* if (params.styles?.css?.fonts) {
|
||||||
|
* applyHostFonts(params.styles.css.fonts);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example Host providing self-hosted fonts
|
||||||
|
* ```typescript
|
||||||
|
* hostContext.styles.css.fonts = `
|
||||||
|
* @font-face {
|
||||||
|
* font-family: "Anthropic Sans";
|
||||||
|
* src: url("https://assets.anthropic.com/.../Regular.otf") format("opentype");
|
||||||
|
* font-weight: 400;
|
||||||
|
* }
|
||||||
|
* `;
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example Host providing Google Fonts
|
||||||
|
* ```typescript
|
||||||
|
* hostContext.styles.css.fonts = `
|
||||||
|
* @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
|
||||||
|
* `;
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example Use host fonts in CSS
|
||||||
|
* ```css
|
||||||
|
* body {
|
||||||
|
* font-family: "Anthropic Sans", sans-serif;
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @see {@link McpUiHostContext} for the full host context structure
|
||||||
|
*/
|
||||||
|
export function applyHostFonts(fontCss: string): void {
|
||||||
|
const styleId = "__mcp-host-fonts";
|
||||||
|
|
||||||
|
// Check if already loaded
|
||||||
|
if (document.getElementById(styleId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.id = styleId;
|
||||||
|
style.textContent = fontCss;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export {
|
||||||
type McpUiDisplayMode,
|
type McpUiDisplayMode,
|
||||||
type McpUiStyleVariableKey,
|
type McpUiStyleVariableKey,
|
||||||
type McpUiStyles,
|
type McpUiStyles,
|
||||||
|
type McpUiHostCss,
|
||||||
type McpUiHostStyles,
|
type McpUiHostStyles,
|
||||||
type McpUiOpenLinkRequest,
|
type McpUiOpenLinkRequest,
|
||||||
type McpUiOpenLinkResult,
|
type McpUiOpenLinkResult,
|
||||||
|
|
@ -72,6 +73,7 @@ import type {
|
||||||
export {
|
export {
|
||||||
McpUiThemeSchema,
|
McpUiThemeSchema,
|
||||||
McpUiDisplayModeSchema,
|
McpUiDisplayModeSchema,
|
||||||
|
McpUiHostCssSchema,
|
||||||
McpUiHostStylesSchema,
|
McpUiHostStylesSchema,
|
||||||
McpUiOpenLinkRequestSchema,
|
McpUiOpenLinkRequestSchema,
|
||||||
McpUiOpenLinkResultSchema,
|
McpUiOpenLinkResultSchema,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue