---
layout: docs
title: MCP UI - Build MCP apps with Flowbite
description: Learn how create and install an MCP application that can be used to build apps for ChatGPT, Claude, Gemini, and any other MCP client and leverage the UI components from Flowbite
group: getting-started
toc: true

previous: HUGO
previousLink: getting-started/hugo/
next: Configuration
nextLink: customize/configuration/
---

MCP UI is the standard SDK that you can use to deliver MCP apps which can be used as applications for ChatGPT, Gemini, Claude, and other MCP clients like Cursor or Windsurf. [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro) (MCP) is part of the Agentic AI Foundation donated by Anthropic in 2025 and it is one of the fastest growing open-source projects in the AI category with over 100 million monthly SDK downloads.

In this guide you will learn how to create an MCP app using Flowbite and Skybridge and start developing application for ChatGPT, Claude, Gemini and any other AI client that uses the MCP standard. After setting up the project you'll be able to integrate any UI component and theme from the Flowbite documentation inside your MCP application.

We decided to use the [Skybridge](https://github.com/alpic-ai/skybridge) framework to build MCP apps and we are using the UI components from [Flowbite](https://flowbite.com/).

<iframe width="100%" class="my-8 rounded-lg shadow-lg yt-video" src="https://www.youtube.com/embed/t9KRwktrZyk?si=eo2EOgOJd0ha1NWI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

## Create new MCP app

The first step is to create a new MCP application and start developing locally:

1. Download or clone the [MCP UI Starter](https://github.com/themesberg/mcp-ui-starter/) repository:

{{< code lang="bash" >}}
git clone https://github.com/themesberg/mcp-ui-starter.git
{{< /code >}}

2. Install dependencies using NPM, PNPM, Yarn or Bun:

{{< code lang="bash" >}}
npm install
{{< /code >}}

3. Run a local development server:

{{< code lang="bash" >}}
npm run dev --use-forwarded-host
{{< /code >}}

This command will run a local server on `http://localhost:3000` and will create the following:

- the main MCP server on the `/mcp` endpoint
- a collection of widgets built with Flowbite and React used as tools

## Connect with NGROK

In order to expose the server to AI clients such as ChatGPT, Gemini or Claude we need to host the MCP server.

Install `ngrok` on your computer using Homebrew and run the following command:

{{< code lang="bash" >}}
ngrok http 3000
{{< /code >}}

This will host the MCP server on a URL similar to this one:

{{< code lang="bash" >}}
# this is just an example
https://3785c5ddc4b6.ngrok-free.app/mcp
{{< /code >}}

You will now be able to use this URL to create an application for ChatGPT, Claude, Gemini, and for any MCP clients.

Don't forget to add the `/mcp` endpoint to the URL generated by NGROK.

## Install on AI providers

Use the following guides to connect your MCP app to major AI providers like ChatGPT, Claude, and Gemini.

### ChatGPT apps

Make sure that you have a paid plan to create an application on ChatGPT.

1. Go to Settings > Connectors
2. Scroll down and click on "Advanced Settings"
3. Enable Developer mode
4. Go back to the Settings > Connectors page, and click on "Create in the Browser Connectors"
5. Add a custom connector with the MCP Server URL: `[NGROK_FORWARDING_URL]/mcp`
6. Click on "Create to add the MCP server as a Connector"
7. To use your connector in the chat, click "+"" then "More" and select it.

### Claude Web

Make sure that you have a paid plan to create an application on Claude.

1. Go to Settings > Connectors
2. Find the "Connectors" section
3. Click on "Add custom connector" at the bottom of the section
4. Add your connector's remote MCP server URL: [NGROK_FORWARDING_URL]/mcp
5. Finish configuring your connector and click "Add"
6. To enable connectors, use the "Search and tools button" on the lower left of the chat interface.

### Gemini CLI

Run the following command in your terminal:

{{< code lang="bash" >}}
gemini mcp add --transport http <server-name> "[NGROK_FORWARDING_URL]/mcp"
{{< /code >}}

Use `/mcp` in the Gemini CLI terminal to view your recently added MCP server status and discovered tools.

### Cursor

Add your MCP server to Cursor by opening the `mcp.json` file and configure it using `mcpServers`.

{{< code lang="bash" >}}
{
  "mcpServers": {
    "<server-name>": {
      "type": "http",
      "url": "[NGROK_FORWARDING_URL]/mcp"
    }
  }
}
{{< /code >}}

### VS Code

To add your MCP server to VS Code you need to open the `.vscode/mcp.json` file and configure `servers`.

{{< code lang="bash" >}}
{
  "servers": {
    "<server-name>": {
      "type": "http",
      "url": "[NGROK_FORWARDING_URL]/mcp"
    }
  }
}
{{< /code >}}

### Claude Code

MCP servers are stored at `~/.claude.json` in Claude Code. Use the CLI to add your MCP app:

{{< code lang="bash" >}}
claude mcp add --transport http <server-name> "[NGROK_FORWARDING_URL]/mcp"
{{< /code >}}

### Mistral AI

1. Open the side panel and expand Intelligence > Connectors
2. Click "+ Add Connector" on the right side of the page
3. In the MCP Connectors directory, click the "Custom MCP Connector tab"
4. Enter a name for the connector and the following server URL: `[NGROK_FORWARDING_URL]/mcp`
5. Finish configuring your connector and click "Create"
6. To use the connector, click the "Tools" button below the chat input and enable it in the "Connectors" section.

### Codex

MCP servers in Codex are located at `~/.codex/config.toml` and you can install your MCP app using the CLI:

{{< code lang="bash" >}}
codex mcp add <server-name> --url "[NGROK_FORWARDING_URL]/mcp"
{{< /code >}}

## Create a widget

Creating a new widget means setting up the server side where we can set up the input data that comes from the AI client (which is the user prompt itself) and the web component which is the front-end widget where we show the output (such as a chart, data table, or just text).

### Server component

If you want to create a new widget yourself, then first create a new file inside the `server/src/widgets` folder and add the following code that creates a basic server side tool which returns a string as output:

{{< code lang="javascript" icon="file" file="widgets/basic-text-server.tsx" >}}
import { z } from "zod";

// Basic Answer widget configuration
export const basicTextWidget = {
  name: "basic-text" as const,
  metadata: {
    description: "Basic Text",
  },
  toolConfig: {
    description: "Show a text message based on a question.",
    inputSchema: {
      question: z.string().describe("The user's question."),
    },
  },
  handler: async () => {
    try {
      const answer = "Hello, world!";
      return {
        structuredContent: { answer },
        content: [],
        isError: false,
      };
    } catch (error) {
      return {
        content: [{ type: "text" as const, text: `Error: ${error}` }],
        isError: true,
      };
    }
  },
};
{{< /code >}}

### Web component

Then create the front-end part of the widget inside the `web/src/widgets` folder where you can use React code to create the markup of the widget and use the server data, which in this case is a basic text coming from our server component:

{{< code lang="javascript" icon="file" file="web/src/widgets/basic-text.tsx" >}}
import "@/index.css";

import { mountWidget } from "skybridge/web";
import { useToolInfo } from "../helpers";

function BasicTextWidget() {
  const { input, output } = useToolInfo<"basic-text">();
  if (!output) {
    return (
      <div role="status">
          <svg aria-hidden="true" className="w-8 h-8 text-neutral-tertiary animate-spin fill-brand" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
              <path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
          </svg>
          <span className="sr-only">Loading...</span>
      </div>
    );
  }

  return (
    <div className="p-4 space-y-2">
      <p className="text-body"><strong>Question:</strong> {input.question}</p>
      <p className="text-body"><strong>Answer:</strong> {output.answer}</p>
    </div>
  );
}

export default BasicTextWidget;

mountWidget(<BasicTextWidget />);
{{< /code >}}

Finally, register the widget in the `server.ts` file:

{{< code lang="javascript" icon="file" file="server.ts" >}}
import { McpServer } from "skybridge/server";
import { basicTextWidget } from "./widgets/basic-text-server.js";

const server = new McpServer(
  {
    name: "mcp-ui-components",
    version: "0.0.1",
  },
  { capabilities: {} }
)
  .registerWidget(
    basicTextWidget.name,
    basicTextWidget.metadata,
    basicTextWidget.toolConfig,
    basicTextWidget.handler
  )
{{< /code >}}

## Customize theming

Flowbite allows you to easily customize the appearance of the UI components from the MCP apps by using the [theming options](https://flowbite.com/docs/customize/theming/) based on CSS variables from Tailwind.

Select one of the predefined themes from Flowbite or customize the variables yourself in the `index.css` file:

{{< code lang="css" icon="file" file="index.css" >}}
/* choose one of the following */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
@import "flowbite/src/themes/default";

/* MINIMAL THEME
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap');
@import "flowbite/src/themes/minimal";
*/

/* ENTERPRISE THEME
@import url('https://fonts.googleapis.com/css2?family=STIX+Two+Text:ital,wght@0,400..700;1,400..700&display=swap');
@import "flowbite/src/themes/enterprise";
*/

/* PLAYFUL THEME
@import url('https://fonts.googleapis.com/css2?family=Shantell+Sans:ital,wght@0,300..800;1,300..800&display=swap');
@import "flowbite/src/themes/playful";
*/

/* MONO THEME
@import url('https://fonts.googleapis.com/css2?family=Google+Sans+Code:ital,wght@0,300..800;1,300..800&display=swap');
@import "flowbite/src/themes/mono";
*/
{{< /code >}}

<!-- ## Build for production

## Host your MCP app -->