MCP UI - Build MCP apps with Flowbite
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
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 (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 framework to build MCP apps and we are using the UI components from Flowbite.
Create new MCP app #
The first step is to create a new MCP application and start developing locally:
- Download or clone the MCP UI Starter repository:
- Terminal
git clone https://github.com/themesberg/mcp-ui-starter.git
- Install dependencies using NPM, PNPM, Yarn or Bun:
- Terminal
npm install
- Run a local development server:
- Terminal
npm run dev --use-forwarded-host
This command will run a local server on http://localhost:3000 and will create the following:
- the main MCP server on the
/mcpendpoint - 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:
- Terminal
ngrok http 3000
This will host the MCP server on a URL similar to this one:
- Terminal
# this is just an example
https://3785c5ddc4b6.ngrok-free.app/mcp
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.
- Go to Settings > Connectors
- Scroll down and click on “Advanced Settings”
- Enable Developer mode
- Go back to the Settings > Connectors page, and click on “Create in the Browser Connectors”
- Add a custom connector with the MCP Server URL:
[NGROK_FORWARDING_URL]/mcp - Click on “Create to add the MCP server as a Connector”
- 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.
- Go to Settings > Connectors
- Find the “Connectors” section
- Click on “Add custom connector” at the bottom of the section
- Add your connector’s remote MCP server URL: [NGROK_FORWARDING_URL]/mcp
- Finish configuring your connector and click “Add”
- 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:
- Terminal
gemini mcp add --transport http <server-name> "[NGROK_FORWARDING_URL]/mcp"
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.
- Terminal
{
"mcpServers": {
"<server-name>": {
"type": "http",
"url": "[NGROK_FORWARDING_URL]/mcp"
}
}
}
VS Code #
To add your MCP server to VS Code you need to open the .vscode/mcp.json file and configure servers.
- Terminal
{
"servers": {
"<server-name>": {
"type": "http",
"url": "[NGROK_FORWARDING_URL]/mcp"
}
}
}
Claude Code #
MCP servers are stored at ~/.claude.json in Claude Code. Use the CLI to add your MCP app:
- Terminal
claude mcp add --transport http <server-name> "[NGROK_FORWARDING_URL]/mcp"
Mistral AI #
- Open the side panel and expand Intelligence > Connectors
- Click “+ Add Connector” on the right side of the page
- In the MCP Connectors directory, click the “Custom MCP Connector tab”
- Enter a name for the connector and the following server URL:
[NGROK_FORWARDING_URL]/mcp - Finish configuring your connector and click “Create”
- 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:
- Terminal
codex mcp add <server-name> --url "[NGROK_FORWARDING_URL]/mcp"
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:
- 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,
};
}
},
};
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:
- 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 />);
Finally, register the widget in the server.ts 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
)
Customize theming #
Flowbite allows you to easily customize the appearance of the UI components from the MCP apps by using the theming options based on CSS variables from Tailwind.
Select one of the predefined themes from Flowbite or customize the variables yourself in the index.css 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=Shantell+Sans:ital,wght@0,300..800;1,300..800&display=swap');
@import "flowbite/src/themes/enterprise";
*/
/* PLAYFUL 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/playful";
*/
/* MONO THEME
@import "flowbite/src/themes/mono";
*/