Skip to content

Commit 6397ec1

Browse files
committed
Add way to pass fonts
1 parent c516cf0 commit 6397ec1

File tree

12 files changed

+323
-4
lines changed

12 files changed

+323
-4
lines changed

examples/customer-segmentation-server/src/mcp-app.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
:root {
33
color-scheme: light dark;
44

5+
/* Font families */
6+
--font-sans: system-ui, -apple-system, sans-serif;
7+
58
/* Background colors */
69
--color-background-primary: light-dark(#ffffff, #111827);
710
--color-background-secondary: light-dark(#f9fafb, #1f2937);
@@ -33,6 +36,7 @@
3336
html, body {
3437
margin: 0;
3538
padding: 0;
39+
font-family: var(--font-sans);
3640
color: var(--color-text-primary);
3741
overflow: hidden;
3842
}

examples/customer-segmentation-server/src/mcp-app.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
App,
66
PostMessageTransport,
77
applyHostStyleVariables,
8+
applyHostFonts,
89
applyDocumentTheme,
910
} from "@modelcontextprotocol/ext-apps";
1011
import { Chart, registerables } from "chart.js";
@@ -448,14 +449,17 @@ applyDocumentTheme(systemDark ? "dark" : "light");
448449
// Register handlers and connect
449450
app.onerror = log.error;
450451

451-
// Handle host context changes (theme and styles from host)
452+
// Handle host context changes (theme, styles, and fonts from host)
452453
app.onhostcontextchanged = (params) => {
453454
if (params.theme) {
454455
applyDocumentTheme(params.theme);
455456
}
456457
if (params.styles?.variables) {
457458
applyHostStyleVariables(params.styles.variables);
458459
}
460+
if (params.styles?.css?.fonts) {
461+
applyHostFonts(params.styles.css.fonts);
462+
}
459463
// Recreate chart to pick up new colors
460464
if (state.chart && (params.theme || params.styles?.variables)) {
461465
state.chart.destroy();
@@ -472,6 +476,9 @@ app.connect(new PostMessageTransport(window.parent)).then(() => {
472476
if (ctx?.styles?.variables) {
473477
applyHostStyleVariables(ctx.styles.variables);
474478
}
479+
if (ctx?.styles?.css?.fonts) {
480+
applyHostFonts(ctx.styles.css.fonts);
481+
}
475482
});
476483

477484
// Fetch data after connection

specification/draft/apps.mdx

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,11 @@ interface HostContext {
409409
styles?: {
410410
/** CSS variables for theming */
411411
variables?: Record<McpUiStyleVariableKey, string | undefined>;
412+
/** CSS blocks that apps can inject */
413+
css?: {
414+
/** CSS for font loading (@font-face rules or @import statements) */
415+
fonts?: string;
416+
};
412417
};
413418
/** How the UI is currently displayed */
414419
displayMode?: "inline" | "fullscreen" | "pip";
@@ -461,8 +466,11 @@ Example:
461466
"variables": {
462467
"--color-background-primary": "light-dark(#ffffff, #171717)",
463468
"--color-text-primary": "light-dark(#171717, #fafafa)",
464-
"--font-family-sans": "system-ui, sans-serif",
469+
"--font-sans": "Anthropic Sans, sans-serif",
465470
...
471+
},
472+
"css": {
473+
"fonts": "@font-face { font-family: \"Custom Font Name\"; src: url(\"https://...\"); }"
466474
}
467475
},
468476
"displayMode": "inline",
@@ -596,7 +604,46 @@ Example usage of standardized CSS variables:
596604
.container {
597605
background: var(--color-background-primary);
598606
color: var(--color-text-primary);
599-
font: var(--font-style-body);
607+
font-family: var(--font-sans);
608+
}
609+
```
610+
611+
#### Custom Fonts
612+
613+
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:
614+
615+
```typescript
616+
hostContext.styles.variables["--font-sans"] = '"Font Name", sans-serif';
617+
618+
// Self-hosted fonts
619+
hostContext.styles.css.fonts = `
620+
@font-face {
621+
font-family: "Font Name";
622+
src: url("https://url-where-font-is-hosted.com/.../Regular.otf") format("opentype");
623+
font-weight: 400;
624+
font-style: normal;
625+
font-display: swap;
626+
}
627+
@font-face {
628+
font-family: "Font Name";
629+
src: url("https://url-where-font-is-hosted.com/.../Medium.otf") format("opentype");
630+
font-weight: 500;
631+
font-style: medium;
632+
font-display: swap;
633+
}
634+
`;
635+
636+
// Google Fonts
637+
hostContext.styles.css.fonts = `
638+
@import url('https://fonts.googleapis.com/css2?family=Font+Name&display=swap');
639+
`;
640+
```
641+
642+
Apps can use the `applyHostFonts` utility to inject the font CSS into the document:
643+
644+
```typescript
645+
if (hostContext.styles?.css?.fonts) {
646+
applyHostFonts(hostContext.styles.css.fonts);
600647
}
601648
```
602649

src/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export { PostMessageTransport } from "./message-transport";
5151
export * from "./types";
5252
export {
5353
applyHostStyleVariables,
54+
applyHostFonts,
5455
getDocumentTheme,
5556
applyDocumentTheme,
5657
} from "./styles";

src/generated/schema.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,17 @@
585585
{}
586586
]
587587
}
588+
},
589+
"css": {
590+
"description": "CSS blocks that apps can inject.",
591+
"type": "object",
592+
"properties": {
593+
"fonts": {
594+
"description": "CSS for font loading (@font-face rules or",
595+
"type": "string"
596+
}
597+
},
598+
"additionalProperties": false
588599
}
589600
},
590601
"additionalProperties": false
@@ -1196,6 +1207,17 @@
11961207
{}
11971208
]
11981209
}
1210+
},
1211+
"css": {
1212+
"description": "CSS blocks that apps can inject.",
1213+
"type": "object",
1214+
"properties": {
1215+
"fonts": {
1216+
"description": "CSS for font loading (@font-face rules or",
1217+
"type": "string"
1218+
}
1219+
},
1220+
"additionalProperties": false
11991221
}
12001222
},
12011223
"additionalProperties": false
@@ -1319,6 +1341,17 @@
13191341
},
13201342
"additionalProperties": {}
13211343
},
1344+
"McpUiHostCss": {
1345+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1346+
"type": "object",
1347+
"properties": {
1348+
"fonts": {
1349+
"description": "CSS for font loading (@font-face rules or",
1350+
"type": "string"
1351+
}
1352+
},
1353+
"additionalProperties": false
1354+
},
13221355
"McpUiHostStyles": {
13231356
"$schema": "https://json-schema.org/draft/2020-12/schema",
13241357
"type": "object",
@@ -1640,6 +1673,17 @@
16401673
{}
16411674
]
16421675
}
1676+
},
1677+
"css": {
1678+
"description": "CSS blocks that apps can inject.",
1679+
"type": "object",
1680+
"properties": {
1681+
"fonts": {
1682+
"description": "CSS for font loading (@font-face rules or",
1683+
"type": "string"
1684+
}
1685+
},
1686+
"additionalProperties": false
16431687
}
16441688
},
16451689
"additionalProperties": false
@@ -2314,6 +2358,17 @@
23142358
{}
23152359
]
23162360
}
2361+
},
2362+
"css": {
2363+
"description": "CSS blocks that apps can inject.",
2364+
"type": "object",
2365+
"properties": {
2366+
"fonts": {
2367+
"description": "CSS for font loading (@font-face rules or",
2368+
"type": "string"
2369+
}
2370+
},
2371+
"additionalProperties": false
23172372
}
23182373
},
23192374
"additionalProperties": false

src/generated/schema.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export type McpUiToolCancelledNotificationSchemaInferredType = z.infer<
6363
typeof generated.McpUiToolCancelledNotificationSchema
6464
>;
6565

66+
export type McpUiHostCssSchemaInferredType = z.infer<
67+
typeof generated.McpUiHostCssSchema
68+
>;
69+
6670
export type McpUiHostStylesSchemaInferredType = z.infer<
6771
typeof generated.McpUiHostStylesSchema
6872
>;
@@ -189,6 +193,8 @@ expectType<spec.McpUiToolCancelledNotification>(
189193
expectType<McpUiToolCancelledNotificationSchemaInferredType>(
190194
{} as spec.McpUiToolCancelledNotification,
191195
);
196+
expectType<spec.McpUiHostCss>({} as McpUiHostCssSchemaInferredType);
197+
expectType<McpUiHostCssSchemaInferredType>({} as spec.McpUiHostCss);
192198
expectType<spec.McpUiHostStyles>({} as McpUiHostStylesSchemaInferredType);
193199
expectType<McpUiHostStylesSchemaInferredType>({} as spec.McpUiHostStyles);
194200
expectType<spec.McpUiResourceTeardownRequest>(

src/generated/schema.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,17 @@ export const McpUiToolCancelledNotificationSchema = z.object({
285285
}),
286286
});
287287

288+
/**
289+
* @description CSS blocks that can be injected by apps.
290+
*/
291+
export const McpUiHostCssSchema = z.object({
292+
/** @description CSS for font loading (@font-face rules or @import statements). Apps must apply using applyHostFonts(). */
293+
fonts: z
294+
.string()
295+
.optional()
296+
.describe("CSS for font loading (@font-face rules or"),
297+
});
298+
288299
/**
289300
* @description Style configuration for theming MCP apps.
290301
*/
@@ -293,6 +304,10 @@ export const McpUiHostStylesSchema = z.object({
293304
variables: McpUiStylesSchema.optional().describe(
294305
"CSS variables for theming the app.",
295306
),
307+
/** @description CSS blocks that apps can inject. */
308+
css: McpUiHostCssSchema.optional().describe(
309+
"CSS blocks that apps can inject.",
310+
),
296311
});
297312

298313
/**

src/react/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*
1111
* - {@link useApp} - React hook to create and connect an MCP App
1212
* - {@link useHostStyleVariables} - React hook to apply host style variables and theme
13+
* - {@link useHostFonts} - React hook to apply host fonts
1314
* - {@link useDocumentTheme} - React hook for reactive document theme
1415
* - {@link useAutoResize} - React hook for manual auto-resize control (rarely needed)
1516
*

0 commit comments

Comments
 (0)