Improve consistency and organization of examples for AI learning

- Add `.gitignore` files to all 19 examples (`node_modules/`, `dist/`)
- Replace custom `log` objects with standard `console` methods
- Add `ontoolcancelled` handler to all `basic-server-*` examples
- Add host styling integration (`applyDocumentTheme`, `applyHostFonts`,
  `applyHostStyleVariables`) to non-React `basic-server-*` examples
- Add section headers for better code organization in feature examples
- Update READMEs with Build System section explaining single-file
  bundling

These changes make examples more consistent and easier to adapt.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jonathan Hefner 2026-01-12 17:06:44 -06:00
commit 4140565b77
4 changed files with 449 additions and 363 deletions

2
examples/threejs-server/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules/
dist/

View file

@ -16,6 +16,10 @@ import path from "node:path";
import { z } from "zod";
import { startServer } from "./server-utils.js";
// =============================================================================
// Constants
// =============================================================================
const DIST_DIR = path.join(import.meta.dirname, "dist");
// Default code example for the Three.js widget
@ -135,6 +139,10 @@ animate();
const resourceUri = "ui://threejs/mcp-app.html";
// =============================================================================
// Server Setup
// =============================================================================
/**
* Creates a new MCP server instance with tools and resources registered.
* Each HTTP session needs its own server instance because McpServer only supports one transport.

View file

@ -12,7 +12,9 @@ import { createRoot } from "react-dom/client";
import ThreeJSApp from "./threejs-app.tsx";
import "./global.css";
const APP_INFO = { name: "Three.js Widget", version: "1.0.0" };
// =============================================================================
// Types
// =============================================================================
/**
* Props passed to the widget component.
@ -37,6 +39,10 @@ export interface WidgetProps<TToolInput = Record<string, unknown>> {
sendLog: App["sendLog"];
}
// =============================================================================
// MCP App Wrapper
// =============================================================================
function McpAppWrapper() {
const [toolInputs, setToolInputs] = useState<Record<string, unknown> | null>(
null,
@ -49,7 +55,7 @@ function McpAppWrapper() {
const [hostContext, setHostContext] = useState<McpUiHostContext | null>(null);
const { app, error } = useApp({
appInfo: APP_INFO,
appInfo: { name: "Three.js Widget", version: "1.0.0" },
capabilities: {},
onAppCreated: (app) => {
// Complete tool input (streaming finished)

View file

@ -12,11 +12,21 @@ import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";
import type { WidgetProps } from "./mcp-app-wrapper.tsx";
// =============================================================================
// Types
// =============================================================================
interface ThreeJSToolInput {
code?: string;
height?: number;
}
type ThreeJSAppProps = WidgetProps<ThreeJSToolInput>;
// =============================================================================
// Constants
// =============================================================================
// Default demo code shown when no code is provided
const DEFAULT_THREEJS_CODE = `const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
@ -52,7 +62,9 @@ function animate() {
}
animate();`;
type ThreeJSAppProps = WidgetProps<ThreeJSToolInput>;
// =============================================================================
// Streaming Preview
// =============================================================================
const SHIMMER_STYLE = `
@keyframes shimmer {
@ -119,7 +131,10 @@ function LoadingShimmer({ height, code }: { height: number; code?: string }) {
);
}
// Context object passed to user code
// =============================================================================
// Three.js Execution
// =============================================================================
const threeContext = {
THREE,
OrbitControls,
@ -145,6 +160,10 @@ async function executeThreeCode(
await fn(threeContext, canvas, width, height);
}
// =============================================================================
// Main Component
// =============================================================================
export default function ThreeJSApp({
toolInputs,
toolInputsPartial,