Skip to content

Latest commit

 

History

History

README.md

UI-Enabled MCP Server Example

This example demonstrates the MCP Apps Extension (SEP-1865) support in the PulseEngine MCP Framework with two UI implementations:

  1. Simple HTML Template (templates/greeting.html) - Basic interactive UI with vanilla JavaScript
  2. React + TypeScript UI (ui/) - Full-featured React app using @mcp-ui/client SDK

What is MCP Apps?

The MCP Apps Extension allows MCP servers to deliver interactive HTML user interfaces that can be displayed inline when tools are called. Instead of just returning text, servers can provide rich, interactive experiences.

Features Demonstrated

Server Side (Rust)

  • Tool with UI Link - The greet_with_ui tool references a UI resource via _meta.ui/resourceUri
  • UI Resources - HTML interface served with text/html+mcp MIME type
  • Resource URIs - Using the ui:// URI scheme for UI resources
  • Mixed Tools - Both UI-enabled and text-only tools in the same server

Client Side (React)

  • React Integration - Using @mcp-ui/client SDK with React hooks
  • Host Context - Receives theme, viewport, device capabilities, tool info
  • Bidirectional Communication - UI can call tools back on the server
  • Connection Management - Handles connection state and errors
  • Responsive Design - Mobile-friendly with dark mode support

Quick Start

Option 1: Simple HTML Template (No Build Required)

cargo run --bin ui-enabled-server

The server will serve the basic HTML template from templates/greeting.html.

Option 2: React UI (Recommended)

# 1. Build the React UI
./build-ui.sh

# 2. Run the server
cargo run --bin ui-enabled-server

The server will automatically serve the built React app from static/ if it exists, otherwise falls back to the simple template.

See UI_README.md for detailed React UI documentation.

Testing with MCP Inspector

# Install MCP Inspector
npx @modelcontextprotocol/inspector

# Run this server
cargo run --bin ui-enabled-server

In MCP Inspector:

  1. List tools - you'll see greet_with_ui with _meta.ui/resourceUri
  2. List resources - you'll see ui://greetings/interactive
  3. Read the resource - you'll get the HTML content
  4. Call the tool - the UI should be displayed (if client supports it)

Code Structure

ui-enabled-server/
├── src/
│   └── main.rs               # Rust MCP server backend
├── templates/
│   └── greeting.html         # Simple HTML template (fallback)
├── ui/                       # React UI application
│   ├── src/
│   │   ├── GreetingUI.tsx   # Main React component with MCP integration
│   │   ├── GreetingUI.css   # Component styles
│   │   ├── main.tsx         # React entry point
│   │   └── index.css        # Global styles
│   ├── index.html           # HTML shell
│   ├── package.json         # Node dependencies
│   ├── vite.config.ts       # Build configuration
│   └── tsconfig.json        # TypeScript config
├── static/                   # Built React UI (generated by build-ui.sh)
├── build-ui.sh              # UI build script
├── README.md                # This file
├── UI_README.md             # Detailed React UI documentation
└── Cargo.toml

Key Implementation Details

1. Tool with UI Metadata

Tool {
    name: "greet_with_ui".to_string(),
    description: "Greet someone with an interactive button UI".to_string(),
    // ... other fields ...
    _meta: Some(ToolMeta::with_ui_resource("ui://greetings/interactive")),
}

2. UI Resource Declaration

Resource::ui_resource(
    "ui://greetings/interactive",
    "Interactive Greeting UI",
    "Interactive HTML interface for greeting with a button",
)

3. Serving HTML Content

ResourceContents::html_ui(params.uri, html)

This automatically sets:

  • MIME type: text/html+mcp
  • Content: HTML string

React UI Features

The React implementation (ui/) demonstrates:

Using the window.mcp API

function GreetingUI() {
  const [context, setContext] = useState(null);

  useEffect(() => {
    if (window.mcp) {
      window.mcp.getContext().then(setContext);
    }
  }, []);

  // context provides: hostInfo, theme, displayMode, viewport,
  // locale, timezone, platform, device, tool

  // Call tools from the UI
  const result = await window.mcp.callTool({
    name: "greet_with_ui",
    arguments: { name: "Alice" },
  });
}

Host Context Integration

  • Displays host name, version, theme, display mode
  • Shows viewport dimensions and locale
  • Adapts to host theme (light/dark)
  • Shows connection status

Error Handling

  • Connection state management
  • Loading states during tool calls
  • User-friendly error messages
  • Input validation

Production Considerations

Security

  • UIs run in sandboxed iframes
  • Content Security Policy (CSP) controls network access
  • No direct DOM access to parent

Communication

  • Uses postMessage with JSON-RPC protocol
  • @mcp-ui/client SDK handles the protocol
  • Bidirectional: UI → Host tool calls, Host → UI context updates

Fallbacks

  • Always provide text content in tool results
  • Not all clients support UI rendering
  • Simple HTML template as graceful degradation

Next Steps

Building Your Own UI-Enabled Server

Server Side (Rust):

  1. Use Resource::ui_resource() to create UI resources
  2. Link tools to UIs with ToolMeta::with_ui_resource()
  3. Serve HTML with ResourceContents::html_ui()

Client Side (React):

  1. Install @mcp-ui/client: npm install @mcp-ui/client
  2. Use useMCPClient() hook to access MCP host
  3. Call client.callTool() to invoke server tools
  4. Access context for host information
  5. Build with Vite or your preferred bundler

Extending This Example

  • Add more tools with different UI patterns
  • Implement form validation and better UX
  • Add data visualization (charts, graphs)
  • Use external APIs (configure CSP)
  • Add state management (Redux, Zustand)
  • Implement real-time updates

Related Documentation

PulseEngine MCP Framework

MCP Apps Extension

MCP UI Client SDK

Testing

See TESTING.md for comprehensive testing instructions with MCP Inspector and manual JSON-RPC commands.