GitHubスター
3
ユーザー評価
未評価
フォーク
1
イシュー
0
閲覧数
1
お気に入り
0
mcp-ui-bridge
: LLM-Oriented Web Accessibility Bridge
mcp-ui-bridge
is a project dedicated to making web applications natively and equally accessible to both human users and Large Language Models (LLMs) through a single, unified development effort.
Available in both TypeScript/Node.js and Python implementations for maximum flexibility and integration with your existing tech stack.
It embodies the concept of LLM-Oriented Accessibility: a new paradigm for web interaction. Instead of forcing LLMs to interpret visual UIs (often unreliably) or requiring developers to build separate, limited APIs for them, LLM-Oriented Accessibility focuses on providing LLMs with a structured, text-based, and semantically rich understanding of a web application. This is achieved by instrumenting the web application with data-mcp-*
attributes that the mcp-ui-bridge
tool can then parse and expose via a Model Context Protocol (MCP) server.
The core philosophy is "Code Once, Serve All." Developers build their rich visual UI for humans as they normally would, and by adding these semantic attributes, the same application becomes fully understandable and operable by LLMs. This approach respects the strengths of both humans (visual intuition) and LLMs (text processing, automation) by providing each with an interface optimized for their needs, all stemming from a single source of truth: your application code.
Key Benefits of LLM-Oriented Accessibility with mcp-ui-bridge
Adopting this approach offers several powerful advantages:
- True Feature Parity (Code Once, Serve All): LLMs interact with the same application logic and features as human users because it is the same application. There's no need to develop and maintain a separate, often lagging, API for AI agents. New features become instantly accessible to both humans and LLMs once instrumented.
- Simplified and Future-Proof Development: Adding
data-mcp-*
attributes is a lightweight process that augments existing HTML. This significantly reduces the overhead compared to building bespoke LLM APIs. As the visual UI evolves, the LLM's understanding evolves with it, provided the semantic attributes are maintained. - Empowering LLMs as Advanced Testers: By providing a clear, structured view of the application state and available actions,
mcp-ui-bridge
inherently enables sophisticated automated testing by LLMs. An LLM can programmatically navigate, interact with elements, input data, and verify outcomes based on the semantic information provided, going far beyond traditional brittle UI automation scripts. - Enabling LLM-Assisted Development Workflows: Imagine an LLM in your IDE (like Cursor or a similar AI-powered coding assistant). With
mcp-ui-bridge
, this LLM can not only help you write code for a new feature but can then immediately interact with that feature on the live development server using the exposed MCP tools. It can perform test actions, report back on the UI state, and help you debug in a tight, iterative loop, significantly accelerating development and improving quality. - Enhanced LLM Reliability: By interacting with a structured, text-based representation, LLMs avoid the pitfalls of visual interpretation (misreading text, misinterpreting icons, errors with coordinates), leading to more reliable and predictable interactions.
- Language Flexibility: Choose the implementation that best fits your infrastructure - TypeScript/Node.js for JavaScript-heavy environments or Python for data science, AI/ML, and automation workflows.
Repository Structure
This repository contains implementations of the mcp-ui-bridge
tool in both TypeScript and Python, along with sample applications to demonstrate their capabilities:
Core Libraries:
/react-cli-mcp
: The original TypeScript/Node.js implementation ofmcp-ui-bridge
- Note on Naming: While the project is now called
mcp-ui-bridge
(as it's framework-agnostic), this directory retains its original namereact-cli-mcp
to avoid potential issues with GitHub repository history and continuity. The tool within this folder is themcp-ui-bridge
.
- Note on Naming: While the project is now called
/mcp-ui-bridge-python
: The Python implementation ofmcp-ui-bridge
with full feature parity
Sample Applications:
/frontend
: Contains a sample TodoMVC web application. This is the application thatmcp-ui-bridge
will interact with./backend
: Contains a simple mock backend server that the TodoMVCfrontend
application uses for its data persistence (if applicable to your frontend's setup).
Example Servers:
/mcp-external-server
: Pre-configured TypeScript application that uses thereact-cli-mcp
library to connect to your frontend (recommended way to run the TypeScript bridge)/mcp-external-server-python
: Pre-configured Python application that uses themcp-ui-bridge-python
library (recommended way to run the Python bridge)
Both implementations provide identical functionality and MCP tool interfaces, so you can choose based on your preferred language and existing infrastructure.
How It Works: A High-Level View
- Semantic Instrumentation: Developers annotate their web application's HTML elements with specific
data-mcp-*
attributes (e.g.,data-mcp-interactive-element
,data-mcp-element-label
,data-mcp-purpose
). These attributes provide semantic meaning about the UI's structure, interactive elements, and their purpose. DomParser
: This module, running within themcp-ui-bridge
tool, uses Playwright to connect to the target web application. It scans the live DOM for thedata-mcp-*
attributes and extracts a structured JSON representation of the page's interactive elements and display data.PlaywrightController
: When an LLM decides to perform an action, this module uses Playwright to execute those actions (e.g., clicks, typing, selections) reliably on the live web page.- MCP Server:
mcp-ui-bridge
runs an MCP server (usingFastMCP
) that exposes a standardized set of tools to an LLM:get_current_screen_data
: Allows the LLM to "see" the current state of the web page as structured data.get_current_screen_actions
: Provides the LLM with a list of suggested actions and command hints.send_command
: Enables the LLM to execute actions likeclick #id
ortype #id "text"
.
Instrumenting Your Frontend
To make your web application understandable and operable by mcp-ui-bridge
, you need to add data-mcp-*
attributes to your HTML elements. These attributes provide the semantic information that the bridge uses to interact with your UI.
Here are some common examples:
Key Attributes:
data-mcp-interactive-element="unique-id"
: Marks an element as interactive. The ID should be unique within the currently rendered view.data-mcp-element-type="<type>"
: Specifies the type of interactive element (e.g.,button
,text_input
,select
,checkbox
,radio
).data-mcp-element-label="<label>"
: A human-readable label for the element, often derived from visible text or anaria-label
. This helps the LLM understand the element's purpose.data-mcp-purpose="<description>"
: A more detailed description of what the element does or is for.data-mcp-value-source-prop="<prop>"
: For inputs, specifies which JavaScript property holds the element's current value (e.g.,"value"
,"checked"
).data-mcp-checked-prop="<prop>"
: For checkboxes or radio buttons, specifies the property indicating its checked state (usually"checked"
).data-mcp-radio-group-name="<name>"
: For radio buttons, specifies thename
attribute of the radio group they belong to. This is crucial for identifying and interacting with the correct group.data-mcp-region="<region-id>"
: Defines a logical section or container on the page.data-mcp-display-item-text
: Marks an element whoseinnerText
should be captured as display data.data-mcp-display-item-id="<unique-id>"
: A unique ID for a display item, allowing the LLM to reference specific pieces of text.data-mcp-navigates-to="<url_or_identifier>"
: Indicates that interacting with this element will cause a navigation.data-mcp-triggers-loading="true"
: Indicates that interacting with this element may trigger a loading state.
Example Snippets (React/JSX):
Simple Button:
<button data-mcp-interactive-element="submit-button" data-mcp-element-type="button" data-mcp-element-label="Submit Form" data-mcp-purpose="Submits the current form data." onClick="{handleSubmit}" > Submit </button>
Text Input:
<input type="text" id="username-input" data-mcp-interactive-element="username-field" data-mcp-element-type="text_input" data-mcp-element-label="Username" data-mcp-purpose="Enter your username." data-mcp-value-source-prop="value" // Assumes 'value' prop holds the input's content value={username} onChange={(e) => setUsername(e.target.value)} />
Checkbox:
<input type="checkbox" id="terms-checkbox" data-mcp-interactive-element="terms-checkbox" data-mcp-element-type="checkbox" data-mcp-element-label="Agree to Terms" data-mcp-purpose="Confirm agreement to terms and conditions." data-mcp-checked-prop="checked" // Assumes 'checked' prop holds the state checked={agreedToTerms} onChange={(e) => setAgreedToTerms(e.target.checked)} /> <label htmlFor="terms-checkbox">I agree to the terms and conditions</label>
Select Dropdown:
<select id="country-select" data-mcp-interactive-element="country-selector" data-mcp-element-type="select" data-mcp-element-label="Country Selector" data-mcp-purpose="Select your country of residence." data-mcp-value-source-prop="value" value={selectedCountry} onChange={(e) => setSelectedCountry(e.target.value)} > <option value="us" data-mcp-element-label="United States Option">United States</option> <option value="ca" data-mcp-element-label="Canada Option">Canada</option> <option value="gb" data-mcp-element-label="United Kingdom Option">United Kingdom</option> </select>
Radio Button Group:
<div role="radiogroup" aria-labelledby="payment-method-label"> <span id="payment-method-label" data-mcp-element-label="Payment Method Options" >Choose Payment Method:</span > <div className="form-check"> // Example class, not MCP related <input type="radio" id="cc-radio" name="paymentMethod" // HTML name attribute for grouping value="credit_card" data-mcp-interactive-element="payment-type-cc" data-mcp-element-type="radio" data-mcp-element-label="Credit Card Radio Button" data-mcp-radio-group-name="paymentMethod" // MCP attribute for identifying the group data-mcp-checked-prop="checked" checked={paymentType === 'credit_card'} onChange={(e) => setPaymentType(e.target.value)} /> <label htmlFor="cc-radio">Credit Card</label> </div> <div className="form-check"> <input type="radio" id="paypal-radio" name="paymentMethod" value="paypal" data-mcp-interactive-element="payment-type-paypal" data-mcp-element-type="radio" data-mcp-element-label="PayPal Radio Button" data-mcp-radio-group-name="paymentMethod" data-mcp-checked-prop="checked" checked={paymentType === 'paypal'} onChange={(e) => setPaymentType(e.target.value)} /> <label htmlFor="paypal-radio">PayPal</label> </div> </div>
Display Container/Region:
<div data-mcp-region="user-profile-card" data-mcp-purpose="Displays user profile information." > <h2 data-mcp-display-item-text data-mcp-display-item-id="user-name-display"> {user.name} </h2> <p data-mcp-display-item-text data-mcp-display-item-id="user-email-display"> {user.email} </p> </div>
By thoughtfully applying these attributes, you empower mcp-ui-bridge
to understand and interact with your application as effectively as a human user, enabling a new level of LLM-driven automation and assistance.
Getting Started: Running the Project
Prerequisites
- Node.js (v18.x or later recommended)
- npm (usually comes with Node.js)
- A web browser compatible with Playwright (e.g., Chromium, Firefox, WebKit - Playwright will download these if not found, but
npx playwright install
in thereact-cli-mcp
directory can pre-install them).
1. Running the Frontend (TodoMVC App)
The frontend is a standard Vite-based React application.
# Navigate to the frontend directory
cd frontend
# Install dependencies
npm install
# Start the development server (usually on http://localhost:5173)
npm run dev
Leave this terminal running.
2. Running the Backend (Mock Server for TodoMVC)
The sample TodoMVC application might require a mock backend for full functionality.
Option A: If it has its own server (e.g., Express)
# Navigate to the backend directory cd backend # Install dependencies (if any) npm install # Start the backend server (check its package.json for the correct command) npm start # or npm run dev, etc.
Option B: Using Visual Studio Code Debugger If the backend is set up to be run via a VS Code launch configuration:
- Open the project in Visual Studio Code.
- Navigate to the "Run and Debug" panel.
- Select the appropriate launch configuration for the backend (e.g., "Run Backend Server").
- Start debugging (usually F5).
Option C: If no separate backend server is needed by the frontend Some simple frontends (especially TodoMVC examples) might use browser
localStorage
and not require a separate backend. If so, you can skip this step.
(Please verify the exact setup of your /backend
and update these instructions if necessary. If it doesn't require running, state that clearly.)
3. Running mcp-ui-bridge
(the react-cli-mcp
tool)
This is the core tool that will connect to the running frontend.
# Navigate to the mcp-ui-bridge tool directory
cd react-cli-mcp
# Install dependencies
npm install
# Ensure Playwright browsers are installed (optional, but good practice)
# npx playwright install
# Build the project (compile TypeScript to JavaScript)
npm run build
# Start the mcp-ui-bridge server
npm run start
Environment Variables for mcp-ui-bridge
:
When running npm run start
for mcp-ui-bridge
, it will look for the following environment variables (you can set these in your terminal, in a .env
file via dotenv
, or by prefixing the start command):
MCP_TARGET_URL
: The URL of the web application you wantmcp-ui-bridge
to connect to. For the sample frontend, this will likely behttp://localhost:5173
.- Example:
MCP_TARGET_URL=http://localhost:5173 npm run start
- Example:
MCP_HEADLESS_BROWSER
: Set totrue
to run the Playwright browser in headless mode (no visible UI), orfalse
(default) to see the browser window.- Example:
MCP_HEADLESS_BROWSER=true MCP_TARGET_URL=http://localhost:5173 npm run start
- Example:
MCP_PORT
: The port on which themcp-ui-bridge
MCP server will listen (default:3000
).MCP_SSE_ENDPOINT
: The SSE endpoint for MCP communication (default:/mcp/sse
).MCP_SERVER_NAME
: Optional name for the MCP server.MCP_SERVER_VERSION
: Optional version for the MCP server (e.g., "1.0.3").
Once mcp-ui-bridge
starts successfully, it will connect to the MCP_TARGET_URL
, and an MCP client (like an LLM integrated with an MCP library) can connect to mcp-ui-bridge
(e.g., at http://localhost:3000/mcp/sse
) to start interacting with the web application.
4. Running the mcp-external-server
(Primary Way to Use the Bridge)
The mcp-external-server
is a pre-configured application that uses the react-cli-mcp
library to connect to your frontend. This is the recommended way to run the MCP bridge.
# Navigate to the mcp-external-server directory
cd mcp-external-server
# Install dependencies
npm install
# Start the server (uses ts-node-dev or nodemon for auto-reloading TypeScript changes)
npm run dev
The server will typically start on http://localhost:8070
.
Configuration for mcp-external-server
:
The primary configuration for mcp-external-server
is done within its src/index.ts
file, where McpServerOptions
are set. However, it's coded to respect the following environment variables (which can override the defaults in src/index.ts
):
MCP_TARGET_URL
: The URL of the web application (e.g.,http://localhost:5173
for the sample frontend).MCP_PORT
: The port for themcp-external-server
to listen on (default:8070
).MCP_HEADLESS_BROWSER
: Set totrue
for headless mode,false
(default) to see the browser.
Example: MCP_TARGET_URL=http://localhost:5173 npm run dev
The mcp-external-server
also includes a toy authentication mechanism in its src/index.ts
(the MANUALLY_ALLOW_CONNECTION
variable) for testing purposes.
5. Alternative: Running the Python Version
If you prefer Python, you can use the Python implementation instead:
# Navigate to the Python external server directory
cd mcp-external-server-python
# Create and activate a virtual environment (recommended)
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Start the Python MCP server
python src/main.py
The Python server will start on http://localhost:8080
by default.
Configuration for Python version:
The Python version can be configured using the same environment variables:
MCP_TARGET_URL
: The URL of the web application (e.g.,http://localhost:5173
)MCP_PORT
: The port for the server to listen on (default:8080
)MCP_HEADLESS_BROWSER
: Set to"true"
for headless mode,"false"
(default) to see the browser
Example: MCP_TARGET_URL=http://localhost:5173 python src/main.py
6. Using mcp-ui-bridge
with Cursor
To enable an LLM within Cursor (like the Cursor agent) to use the tools provided by mcp-ui-bridge
and interact with your running web application, follow these steps:
Prerequisites:
- Ensure your frontend application is running (e.g.,
npm run dev
in the/frontend
directory). - Ensure your backend server (if required by your frontend) is running.
- Ensure the
mcp-ui-bridge
server itself is running (e.g.,npm run start
in the/react-cli-mcp
directory after annpm run build
).
Configuration:
.cursor/mcp.json
File: This project includes a.cursor/mcp.json
file in the root directory, which defines the MCP server for Cursor. It typically looks like this:{ "mcpServers": { "react-app-mcp-agent": { // This key is the server name displayed in Cursor "transport": "sse", "url": "http://localhost:3000/mcp/sse", // Matches MCP_PORT and MCP_SSE_ENDPOINT defaults "description": "MCP server for interacting with the target web application via mcp-ui-bridge and Playwright." } } }
This file tells Cursor how to find and identify your local
mcp-ui-bridge
server.Enable in Cursor Settings:
- Open the Command Palette in Cursor (Ctrl+Shift+P on Windows/Linux, Cmd+Shift+P on macOS).
- Type
Cursor Settings
and select it to open Cursor's settings UI. - In the settings, find the section related to "Model Context Protocol (MCP)" or "MCP Servers".
- You should see an entry for your MCP server, likely named "react-app-mcp-agent" (or whatever key is used in
mcp.json
), listed as "Project Managed". - Enable this server using the toggle switch next to its name.
(You can add a screenshot here similar to the one provided in the issue, showing the MCP Servers settings in Cursor with the project-managed server enabled.)
Outcome:
Once enabled, the Cursor agent will have access to the following tools from mcp-ui-bridge
:
get_current_screen_data
get_current_screen_actions
send_command
This allows you to instruct Cursor to interact with your web application, test features, and assist in development in a tight iterative loop, as described in the "Key Benefits" section.
Using mcp-ui-bridge
as a Library
The mcp-ui-bridge
package is designed to be used as a library within your own projects, allowing you to create a custom MCP server that bridges your web application to LLMs. Choose the implementation that best fits your tech stack:
TypeScript/Node.js Implementation
Installation
Once published to npm:
npm install mcp-ui-bridge
For local development or if using it directly from this monorepo structure into a sibling project, you might use npm link
or specify a file path in your package.json
(referencing the react-cli-mcp
directory if that's where the source lives before publishing under the new name):
"dependencies": {
"mcp-ui-bridge": "file:../path/to/react-cli-mcp" // Adjust path as needed
}
Basic Usage
Here's a minimal example of how to use runMcpServer
:
// your-custom-mcp-server.ts
import {
runMcpServer,
McpServerOptions,
ClientAuthContext,
} from "mcp-ui-bridge";
async function startMyMcpBridge() {
const options: McpServerOptions = {
targetUrl: process.env.MY_APP_URL || "http://localhost:3000", // URL of your web application
port: Number(process.env.MY_MCP_BRIDGE_PORT) || 8090,
headlessBrowser: process.env.HEADLESS !== "false",
serverName: "My Custom MCP Bridge",
serverVersion: "1.0.0",
serverInstructions: "This bridge connects to My Awesome App.",
// Optional: Implement custom client authentication
authenticateClient: async (
context: ClientAuthContext
): Promise<boolean> => {
console.log(`Authentication attempt from IP: ${context.sourceIp}`);
const apiKey = context.headers["x-my-app-api-key"];
if (apiKey && apiKey === process.env.MY_EXPECTED_API_KEY) {
console.log("Client authenticated successfully.");
return true;
}
console.log("Client authentication failed.");
return false;
},
};
try {
await runMcpServer(options);
console.log(
`My Custom MCP Bridge started on port ${options.port}, targeting ${options.targetUrl}`
);
} catch (error) {
console.error("Failed to start My Custom MCP Bridge:", error);
process.exit(1);
}
}
startMyMcpBridge();
Python Implementation
Installation
For local development using the Python implementation:
# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install the Python mcp-ui-bridge library (adjust path as needed)
pip install -e ./mcp-ui-bridge-python
Basic Usage
Here's a minimal example using the Python implementation:
# your_custom_mcp_server.py
import asyncio
import os
from mcp_ui_bridge_python import run_mcp_server, McpServerOptions
async def authenticate_client(source_ip: str, headers: dict) -> bool:
"""Custom authentication logic"""
print(f"Authentication attempt from IP: {source_ip}")
api_key = headers.get("x-my-app-api-key")
if api_key and api_key == os.getenv("MY_EXPECTED_API_KEY"):
print("Client authenticated successfully.")
return True
print("Client authentication failed.")
return False
async def start_my_mcp_bridge():
options = McpServerOptions(
target_url=os.getenv("MY_APP_URL", "http://localhost:3000"),
port=int(os.getenv("MY_MCP_BRIDGE_PORT", "8090")),
headless_browser=os.getenv("HEADLESS", "false").lower() != "false",
server_name="My Custom MCP Bridge",
server_version="1.0.0",
server_instructions="This bridge connects to My Awesome App.",
# Optional: Implement custom client authentication
authenticate_client=authenticate_client
)
try:
await run_mcp_server(options)
print(f"My Custom MCP Bridge started on port {options.port}, targeting {options.target_url}")
except Exception as error:
print(f"Failed to start My Custom MCP Bridge: {error}")
exit(1)
if __name__ == "__main__":
asyncio.run(start_my_mcp_bridge())
Key Configuration Options
Both implementations support the same core options (with language-appropriate naming conventions):
targetUrl
/target_url
(string, required): The URL of the web application the MCP server will control.port
(number/int, optional): Port for the MCP server. Defaults to8080
if not set byMCP_PORT
env var or this option.headlessBrowser
/headless_browser
(boolean, optional): Whether to run Playwright in headless mode. Defaults tofalse
.ssePath
/sse_path
(string, optional): The path for the Server-Sent Events (SSE) endpoint. Defaults to/sse
.serverName
/server_name
(string, optional): Name of your MCP server.serverVersion
/server_version
(string, optional): Version of your MCP server.serverInstructions
/server_instructions
(string, optional): Instructions for the LLM on how to use this server or the target application.authenticateClient
/authenticate_client
(function, optional): Custom authentication function.- TypeScript:
async (context: ClientAuthContext) => Promise<boolean>
- Python:
async (source_ip: str, headers: dict) => bool
- Return
true
/True
to allow the connection,false
/False
to deny (results in a 401 response).
- TypeScript:
Using either library allows you to host this MCP bridge as part of a larger backend, deploy it as a standalone service, and integrate custom authentication seamlessly.
Instrumenting Your Frontend: The data-mcp-*
Attributes
The core philosophy of mcp-ui-bridge
is LLM-Oriented Accessibility. This is achieved by instrumenting your web application's HTML with specific data-mcp-*
attributes. These attributes provide semantic meaning, allowing the DomParser
within the mcp-ui-bridge
library to understand the structure, interactive elements, and purpose of your UI, and then convey this understanding to an LLM.
Here are the key data-mcp-*
attributes:
Attribute | Purpose | Value(s) / Notes | Example Usage |
---|---|---|---|
data-mcp-interactive-element |
Required for most interactive elements. Marks an element that the LLM can interact with. | Can be empty. If the element is not an input , button , select , a , or textarea , this value is used as its id . |
<div data-mcp-interactive-element="custom-button">Click Me</div> |
data-mcp-element-label |
Provides a human-readable label for the element. Crucial for LLM understanding. | String (e.g., "Submit Form", "User Name Input") | <input data-mcp-element-label="User Name Input"> |
data-mcp-element-type |
Explicitly defines the type of a custom interactive element (if not a standard HTML tag). | String (e.g., "custom-slider", "date-picker") | <div data-mcp-interactive-element data-mcp-element-type="star-rating">...</div> |
data-mcp-current-value |
For custom elements, explicitly provides their current value if not readable from standard properties. | String | <div data-mcp-current-value="75%">...</div> |
data-mcp-purpose |
Describes the high-level goal or function of the element or a region. | String (e.g., "submit-user-profile", "display-search-results") | <button data-mcp-purpose="submit-user-profile">Save</button> |
data-mcp-container-id |
Groups related items, often used for lists or collections of data. Parsed into structuredData . |
String (unique ID for the container) | <ul data-mcp-container-id="todo-list">...</ul> |
data-mcp-item-text |
Identifies the text content of an item within a data-mcp-container-id . Parsed into structuredData . |
String (extracted from the element's text content if not specified) | <li data-mcp-item-text>Buy milk</li> |
data-mcp-region-id |
Defines a semantic region on the page. Parsed into structuredData . |
String (unique ID for the region) | <section data-mcp-region-id="user-settings">...</section> |
data-mcp-navigates-to |
For <a> tags or custom navigation elements, indicates the destination URL or view. |
String (URL or logical view name) | <a data-mcp-navigates-to="/profile">Profile</a> |
data-mcp-updates-container |
Indicates which data-mcp-container-id or data-mcp-region-id this element might modify or refresh. |
String (ID of the container/region) | <button data-mcp-updates-container="todo-list">Add</button> |
data-mcp-controls-element |
Links an interactive element (like a button) to the element it primarily controls (like an input). | String (ID of the controlled element) | <button data-mcp-controls-element="username-input">...</button> |
data-mcp-is-disabled |
Explicitly marks an element as disabled if the standard disabled attribute isn't sufficient/used. |
"true" or "false" | <div data-mcp-is-disabled="true">Cannot click</div> |
data-mcp-is-readonly |
Explicitly marks an element as read-only. | "true" or "false" | <div data-mcp-is-readonly="true">View Only</div> |
data-mcp-is-checked |
For custom checkbox-like elements, explicitly provides their checked state. | "true" or "false" | <div data-mcp-is-checked="true">Subscribed</div> |
data-mcp-custom-state |
For elements with complex states beyond simple values, allows exposing a descriptive state string. | String (e.g., "expanded", "loading", "error") | <div data-mcp-custom-state="expanded">Details</div> |
By thoughtfully applying these attributes, you create a rich, semantic layer over your existing UI. The DomParser
uses this layer to generate the currentUrl
, structuredData
(from containers and regions), and interactiveElements
(with their labels, types, values, and purposes) that are sent to the LLM. This enables the LLM to "understand" the page contextually and perform actions accurately.
This instrumentation is the cornerstone of aligning your frontend with the mcp-ui-bridge
design philosophy: build for humans, annotate for LLMs, and serve both effectively from a single codebase.
Testing the Setup
Once all necessary components are running (Frontend, Backend (if any), and mcp-external-server
), you can test the setup:
- Connect with an MCP Client: Use Cursor (configured as described above) or another MCP client to connect to the
mcp-external-server
(e.g., athttp://localhost:8070/sse
). - Test Basic Interaction: Try using tools like
get_current_screen_data
orsend_command
to interact with your frontend application. - Test Authentication (Toy Implementation):
- The
mcp-external-server/src/index.ts
includes a simple authentication mechanism controlled by theMANUALLY_ALLOW_CONNECTION
variable. - To allow connections: Set
MANUALLY_ALLOW_CONNECTION = true;
inmcp-external-server/src/index.ts
. The MCP client should connect successfully. - To deny connections: Set
MANUALLY_ALLOW_CONNECTION = false;
inmcp-external-server/src/index.ts
. Thenodemon
process (if usingnpm run dev
) will restart the server. Attempts to connect with an MCP client should now fail (e.g., Cursor might show a connection error). - Check the console output of
mcp-external-server
for logs indicating authentication attempts and success/failure. This confirms theauthenticateClient
callback provided torunMcpServer
is working.
- The
This README.md
was co-authored by Santiago Calvo and Gemini 2.5 Pro.