npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@nuwa-ai/ui-kit

v0.6.1

Published

Nuwa UI Kit for TypeScript/React

Readme

@nuwa-ai/ui-kit

Nuwa UI SDK for TypeScript/React - A powerful library for building iframe-based UI components with bidirectional communication between your Cap UI and the Nuwa Client.

This SDK enables two primary use cases:

  1. Nuwa Client Integration: Call Nuwa Client functions from your UI (send prompts, add selections, etc.)
  2. MCP Tool Exposure: Expose your UI functionality as MCP tools for AI agents to interact with

Together, these create a bidirectional connection where your Cap UI can both control the Nuwa Client and be controlled by AI.

Installation

# npm
npm install @nuwa-ai/ui-kit

# yarn
yarn add @nuwa-ai/ui-kit

# pnpm
pnpm add @nuwa-ai/ui-kit

# bun
bun add @nuwa-ai/ui-kit

🔗 Nuwa Client Integration

Connect to the Nuwa Client to call its functions from your Cap UI.

React Hook (Recommended)

import React, { useEffect, useState } from 'react';
import { NuwaProvider, useNuwa } from '@nuwa-ai/ui-kit';

function MyCapUI() {
  const { nuwa, connected, theme } = useNuwa();
  const [input, setInput] = useState('');

  // Load previously saved state (if any)
  useEffect(() => {
    let mounted = true;
    (async () => {
      const prev = await nuwa.getState<string | null>();
      if (mounted && typeof prev === 'string') setInput(prev);
    })();
    return () => { mounted = false; };
  }, [nuwa]);

  // Save state 
  const handleInputChange = async (e) => {
    const value = e.target.value;
    setInput(value);
    await nuwa.saveState(value);
  };

  const handleSendPrompt = async () => {
    await nuwa.sendPrompt('The user wants to do this');
  };

  const handleAIGeneration = async () => {
    const stream = nuwa.createAIStream({ prompt: 'Generate something amazing' });
    await stream.execute({
      onChunk: (chunk) => {
        if (chunk.type === 'content') setInput((prev) => prev + chunk.content);
      },
    });
  };

  return (
    <div className="p-4">
      <h1>My Cap UI</h1>
      <p>Connected: {String(connected)}</p>
      <input value={input} onChange={handleInputChange} />
      
      <button onClick={handleSendPrompt}>Send Prompt to AI</button>
      <button onClick={handleAIGeneration}>Let AI Generate</button>
    </div>
  );
}

export default function App() {
  return (
    <NuwaProvider
      autoHeight
      onConnected={() => console.log('Connected to Nuwa!')}
      onError={(err) => console.error('Connection error:', err)}
    >
      <MyCapUI />
    </NuwaProvider>
  );
}

Vanilla JavaScript

import { NuwaClient } from '@nuwa-ai/ui-kit';

const client = new NuwaClient();

// Connect before invoking methods
await client.connect();

// Send a prompt to the AI
await client.sendPrompt('Generate something amazing');

// Add a selection to the parent Nuwa client
await client.addSelection('Weather Data', 'Current temperature is 72°F');

// Save and retrieve state
await client.saveState({ userPreferences: { theme: 'dark' } });
const state = await client.getState();

// Stream AI responses (capId is optional)
const stream = client.createAIStream({
  prompt: 'Stream a poem line by line'
});

// Start and await until completion; observe live chunks via callbacks
const { result, error } = await stream.execute({
  onChunk: (chunk) => {
    if (chunk.type === 'content') console.log('chunk:', chunk.content);
  },
  onError: (err) => console.error('stream error:', err)
});
console.log('final result:', result);
if (error) console.warn('ended with error:', error);

// Abort when you are done (optional)
// stream.abort();

// Inspect live state anytime
console.log('status:', stream.status); // 'idle' | 'running' | 'completed' | 'error' | 'aborted'
console.log('error:', stream.error);   // Error | null
console.log('result:', stream.result); // string or array of chunks

Auto Height Adjustment

The SDK provides automatic height adjustment to ensure your iframe content is fully visible. Wrap your app with NuwaProvider and set autoHeight:

import React from 'react';
import { NuwaProvider } from '@nuwa-ai/ui-kit';
import App from './App';

export default function Root() {
  return (
    <NuwaProvider autoHeight>
      <App />
    </NuwaProvider>
  );
}

🤖 MCP Tool Exposure

Expose your UI functionality as MCP (Model Context Protocol) tools that AI agents can interact with. This creates a bidirectional communication channel where AI can control your UI.

Basic MCP Server Setup

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { PostMessageMCPTransport } from '@nuwa-ai/ui-kit';
import { z } from 'zod';

// Create MCP transport for iframe communication
const transport = new PostMessageMCPTransport();

// Initialize MCP server
const server = new McpServer({
  name: "my-cap-mcp",
  version: "1.0.0",
});

// Register a tool that AI can call
server.registerTool(
  "update_content",
  {
    title: "Update UI Content",
    description: "Update the content displayed in the UI",
    inputSchema: {
      content: z.string().describe("The new content to display"),
    },
  },
  ({ content }) => {
    // Your UI update logic here
    updateUIContent(content);
    
    return {
      content: [
        {
          type: "text",
          text: `Content updated successfully: ${content}`,
        },
      ],
    };
  },
);

// Connect server to transport
server.connect(transport);

Returning UI Resources from MCP Tools

You can return UI resources from your MCP tools to tell the client to render specific UI components for a tool execution:

import { createUIToolResult } from '@nuwa-ai/ui-kit';

server.registerTool(
  "show_dashboard",
  {
    title: "Show Dashboard",
    description: "Display the analytics dashboard UI",
    inputSchema: {
      period: z.string().describe("Time period for analytics"),
    },
  },
  ({ period }) => {
    // Return a UI resource that tells the client to render UI at the given path
    return createUIToolResult(
      `/dashboard?period=${period}`,
      "Analytics Dashboard",
      `Dashboard showing ${period} analytics data`
    );
  },
);

The client will receive this resource and construct the full URL using its known origin to render your UI component.

UI Resource Type:

interface UIResource {
  uri: string;                           // capui://Dashboard
  name: string;                          // "Analytics Dashboard"
  description?: string;                  // Optional description
  mimeType: "text/x-nuwa-capui-path";   // MIME type identifier
  path: string;                          // "/dashboard?period=week"
}

Helper Functions:

  • createUIResource(path, name?, description?) - Create a UI resource
  • createUIToolResult(path, name?, description?) - Create a complete tool result with UI resource

Complete Example with React Hook

import { useEffect, useState } from 'react';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { PostMessageMCPTransport, NuwaProvider, useNuwa } from '@nuwa-ai/ui-kit';
import { z } from 'zod';

function MyCapWithMCP() {
  const [content, setContent] = useState('Initial content');
  const { nuwa } = useNuwa();

  useEffect(() => {
    // Set up MCP server
    const transport = new PostMessageMCPTransport();

    const server = new McpServer({
      name: "content-editor-mcp",
      version: "1.0.0",
    });

    // Register tools for AI to interact with your UI
    server.registerTool(
      "update_content",
      {
        title: "Update Display Content",
        description: "Update the content shown in the UI",
        inputSchema: {
          content: z.string().describe("The content to display"),
        },
      },
      ({ content: newContent }) => {
        setContent(newContent);
        return {
          content: [{
            type: "text",
            text: `Content updated to: ${newContent}`,
          }],
        };
      },
    );

    server.registerTool(
      "get_content",
      {
        title: "Get Current Content",
        description: "Retrieve the current content from the UI",
        inputSchema: {},
      },
      () => ({
        content: [{
          type: "text",
          text: `Current content: ${content}`,
        }],
      }),
    );

    // Connect server
    server.connect(transport);

    return () => {
      server.close();
    };
  }, [content]);

  return (
    <div className="p-4">
      <h1>My Cap UI with MCP</h1>
      
      <div className="content-display">
        <h2>Current Content:</h2>
        <p>{content}</p>
      </div>
      
      <p className="text-sm text-gray-600">
        AI agents can now interact with this UI through MCP tools!
      </p>
    </div>
  );
}

MCP Transport Options

const transport = new PostMessageMCPTransport({
  targetWindow: window.parent,     // Target window (usually parent)
  targetOrigin: "*",               // Target origin (* for dev, specific domain for prod)
  allowedOrigins: ["*"],           // Allowed origins for security
  debug: true,                     // Enable debug logging
  timeout: 10000,                  // Request timeout in ms
  securityPolicy: {
    enforceOriginValidation: true, // Enforce origin validation
    maxMessageSize: 1024 * 1024,   // Max message size (1MB)
    rateLimits: [                  // Rate limiting
      { windowMs: 60000, maxRequests: 100 }
    ]
  }
});

🚀 Complete Example

For a full working example, see Nuwa Caps:

📁 nuwa-caps

API Reference

NuwaProvider(props) and useNuwa()

Use NuwaProvider to set up the connection and wrap your UI; call useNuwa() inside to access the client and state.

NuwaProvider props:

  • autoHeight?: boolean - Auto-adjust parent iframe height (default true)
  • allowedOrigins?: string[] - Allowed origins (default ["*"])
  • timeout?: number - Connection timeout in ms (default 2000)
  • debug?: boolean - Enable debug logging
  • methodTimeout?, methodTimeouts?, methodRetries?, methodRetriesMap? - Per-call behavior
  • streamTimeout?, streamRetries?, streamBufferSize? - Streaming config
  • onConnected?(), onError?(error) - Lifecycle callbacks
  • className?, style? - Provider container props (dark class toggles automatically with theme)

useNuwa() returns:

  • nuwa: NuwaClient - The client instance
  • theme: 'light' | 'dark' - Current theme pushed from parent
  • connected: boolean - Connection state

NuwaClient Class

Constructor Options:

  • Same as NuwaProvider client options (allowedOrigins, timeout, debug, methodTimeout(s), retry maps, stream settings)

Methods:

  • connect(): Promise<void> - Establish connection with parent
  • sendPrompt(prompt: string): Promise<void> - Send a prompt to parent
  • setHeight(height: string | number): Promise<void> - Set iframe height
  • addSelection(label: string, message: string | Record<string, any>): Promise<void> - Add selection to parent
  • saveState<T>(state: T): Promise<void> - Save state data to parent
  • getState<T>(): Promise<T | null> - Retrieve state data from parent
  • createAIStream<T>(request: StreamAIRequest<T>): StreamHandle<T> - Factory for starting a server-side stream. Call await execute({ onChunk?, onError? }) to run until completion; the promise resolves to { result, error }. Inspect live status, error, result and use abort() to cancel.
  • reconnect(): Promise<void> - Reconnect to parent if connection is lost
  • disconnect(): void - Disconnect from parent
  • getStats() - Get connection statistics

Note: useNuwa() exposes connected. You can also rely on onConnected/onError callbacks in the provider for lifecycle events.

Streaming API

// Basic
const s1 = nuwa.createAIStream({ prompt: 'Stream summary' });
const { result: res1, error: err1 } = await s1.execute({
  onChunk: (chunk) => {
    if (chunk.type === 'content') console.log(chunk.content);
  }
});
console.log('status:', s1.status);
console.log('result:', res1);
if (err1) console.warn('error:', err1);

// With explicit error callback
const s2 = nuwa.createAIStream({ prompt: 'Stream structured output' });
const { result: res2, error: err2 } = await s2.execute({
  onChunk: (chunk) => {
    if (chunk.type === 'content') console.log('content:', chunk.content);
  },
  onError: (err) => console.error('stream error (live):', err)
});

Note: Callbacks passed to execute() are handled locally by the SDK and are not sent to the parent window, as functions cannot be cloned across postMessage.

Requirements

  • React >= 16.8.0 (for React integration)
  • Modern browser with iframe support
  • Parent window must be configured to receive messages

License

MIT

Contributing

This package is part of the Nuwa AI ecosystem. Contributions are welcome!


Built with ❤️ by the Nuwa AI team