Skip to main content
MCP specifications supports returning resources along with a tool result. We leveraged this mechanism and defined a resource schema for UI resources. When a tool returns a UI resource, the client will render an iframe that embeds the UI described by this UI resource.

UI Resource Schema

Here’s the UI resource schema:
interface UIResource {
	type: "resource";
	resource: {
		uri: string;
		name?: string;
		description?: string;
		mimeType: "text/x-nuwa-cap-ui-url"; // we use this MIME type to identify the UI resource
		text: string; // this is the URL to your UI app
	};
}

MCP UI Example

Here’s an example of an UI app that can be returned as a Nuwa-Compatible UI resource. You can find the full example here: mcp-ui-demo
app.tsx
import { NuwaProvider } from "@nuwa-ai/ui-kit";
import React, { useEffect, useState } from "react";
import { Weather, type WeatherAtLocation } from "@/components/weather";

function WeatherContent() {
  const [weatherData, setWeatherData] = useState<WeatherAtLocation | null>(
    null,
  );

  useEffect(() => {
    const searchParams = new URLSearchParams(window.location.search);
    const latitude = searchParams.get("latitude") ?? "22.3356";
    const longitude = searchParams.get("longitude") ?? "114.1847";
    const getWeather = async () => {
      const response = await fetch(
        `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m&hourly=temperature_2m&daily=sunrise,sunset&timezone=auto`,
      );

      const weatherData = (await response.json()) as WeatherAtLocation;
      setWeatherData(weatherData);
    };

    getWeather();
  }, []);

  return (
    <div className="p-4">
      {weatherData ? (
        <Weather weatherAtLocation={weatherData} />
      ) : (
        <div>Loading weather...</div>
      )}
    </div>
  );
}

export default function App() {
  return (
    <NuwaProvider> {/* wrap your app with NuwaProvider so the auto height adjustment works */}
      <React.Suspense fallback={<div>Loading weather...</div>}>
        <WeatherContent />
      </React.Suspense>
    </NuwaProvider>
  );
}

createUIResource

The @nuwa-ai/ui-kit provides a helper function for constructing a UI resource to return it as part of the MCP content:
import { createUIResource } from '@nuwa-ai/ui-kit';

mcpServer.freeTool({
  name: "render_ui_demo_weather_card",
  description: "Call this tool to return the Demo Weather Card UI",
  parameters: {
    longitude: z
      .number()
      .describe("The longitude of the city to get the weather for"),
    latitude: z
      .number()
      .describe("The latitude of the city to get the weather for"),
  },
  async execute({ longitude, latitude }) {
    const UIResource = createUIResource({
      url: `http://localhost:3000?longitude=${longitude}&latitude=${latitude}`,
    });
    return {
      content: [UIResource, { type: "text", text: "Rendered the weather card UI!" }],
    };
  },
});

Nuwa Client Functions in MCP UI Resource

When your UI app is rendered as a Nuwa-Compatible UI resource, returned by the MCP server, some of the Nuwa Client functions are not supported: Supported
  • auto connection
  • auto height adjustment
  • theme sync
  • sendPrompt
NOT Supported
  • addSelection
  • saveState/getState
  • createAIStream