MCP Browser Server for React Native Web
 
A Model Context Protocol (MCP) browser automation server specifically designed to work with React Native Web applications. Features multi-tab support, screenshot capabilities, and persistent browser sessions.
The Problem
React Native Web uses a gesture responder system that listens for mousedown/mouseup events instead of standard click events. This breaks standard browser automation tools like Playwright's locator.click() method.
The Solution
This MCP server uses coordinate-based clicks with Playwright's low-level page.mouse API, which properly triggers the mouse events that React Native Web components listen for.
Features
- Coordinate-based clicking - Uses
page.mouse.down()/page.mouse.up()instead of synthetic clicks - Multiple element finding strategies - CSS selector, text content, testID, or exact coordinates
- Custom page snapshots - Shows interactive elements with positions (not reliant on accessibility tree)
- Screenshot support - Returns base64-encoded PNG images that AI agents can view
- Multi-tab management - Create, switch between, and close browser tabs
- Persistent sessions - Browser stays open between tool calls for continuous interaction
- Full browser control - Navigate, type, scroll, press keys, evaluate JavaScript
Installation
From npm
npm install @nizarius/mcp-rnw-browser
npx playwright install chromium
From source
git clone https://github.com/nizarius/mcp-rnw-browser.git
cd mcp-rnw-browser
npm install
npm run build
npx playwright install chromium
Configuration for Cursor
Add to your Cursor MCP settings (~/.cursor/mcp.json or Cursor Settings > MCP):
Using npm package (recommended)
{
"mcpServers": {
"rnw-browser": {
"command": "npx",
"args": ["@nizarius/mcp-rnw-browser"]
}
}
}
Using local installation
{
"mcpServers": {
"rnw-browser": {
"command": "node",
"args": ["/path/to/mcp-rnw-browser/dist/index.js"]
}
}
}
Available Tools
Navigation
rnw_navigate
Navigate to a URL. Creates a browser session if none exists. ``json { "url": "http://localhost:8081" } `` Returns: Page snapshot with all interactive elements.
Screenshots & Snapshots
rnw_snapshot
Get a text snapshot of interactive elements on the page with their positions. Returns element tags, text content, testIDs, roles, positions, and center coordinates for clicking. ``json // No parameters required {} `` Returns: Text list of all interactive elements with their positions.
rnw_screenshot
Take a screenshot and return as a base64-encoded PNG image. ```json // Viewport screenshot (default) {}
// Full page screenshot (captures entire scrollable area) { "fullPage": true }
// Element screenshot (captures specific element) { "selector": "#my-component" } ``` Returns: Base64-encoded PNG image that AI agents can view directly.
Interactions
rnw_click
Click on an element using React Native Web compatible mouse events (mousedown/mouseup). ```json // By CSS selector { "selector": "button.submit", "findBy": "css" }
// By text content (partial match) { "selector": "Sign In", "findBy": "text" }
// By testID (data-testid attribute) { "selector": "login-button", "findBy": "testid" }
// By exact coordinates (useful when element detection fails) { "x": 500, "y": 300, "findBy": "coordinates" } ``` Returns: Click coordinates and updated page snapshot.
rnw_type
Type text into a focused element or find an element first and type into it. ```json // Type into currently focused element { "text": "Hello World" }
// Find element first, then type (clicks to focus) { "text": "Hello World", "selector": "input", "findBy": "css" }
// Type and press Enter (e.g., for search/submit) { "text": "Hello World", "selector": "input", "findBy": "css", "pressEnter": true } ``` Returns: Confirmation of typed text.
rnw_scroll
Scroll the page or a specific scrollable element. ```json // Scroll page down by 300 pixels { "direction": "down", "amount": 300 }
// Scroll page up { "direction": "up", "amount": 500 }
// Scroll within a specific container { "direction": "down", "amount": 200, "selector": ".scroll-container" } `` Parameters: direction (up/down/left/right), amount (pixels, default: 300), selector` (optional).
Returns: Updated page snapshot.
rnw_wait
Wait for a specified time or until an element appears on the page. ```json // Wait for 1 second (1000ms) { "time": 1000 }
// Wait for element to appear (with 5s timeout) { "selector": "button.loaded", "findBy": "css" }
// Wait for text to appear { "selector": "Loading complete", "findBy": "text" } ``` Returns: Confirmation when wait completes or error if timeout.
rnw_press_key
Press a keyboard key. Useful for navigation, form submission, or triggering shortcuts. ```json // Press Enter { "key": "Enter" }
// Press Escape { "key": "Escape" }
// Press arrow keys { "key": "ArrowDown" }
// Press Tab to move focus { "key": "Tab" } ``` Common keys: Enter, Escape, Tab, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Backspace, Delete, Space.
Returns: Confirmation of key pressed.
rnw_evaluate
Execute JavaScript in the browser context. Useful for debugging, reading state, or performing custom interactions. ```json // Get page title { "script": "document.title" }
// Get current URL { "script": "window.location.href" }
// Read localStorage value { "script": "localStorage.getItem('authToken')" }
// Get element count { "script": "document.querySelectorAll('button').length" }
// Trigger custom action { "script": "window.scrollTo(0, document.body.scrollHeight)" } ``` Returns: JSON-stringified result of the script execution.
Tab Management
rnw_tabs_list
List all open browser tabs with their index, title, and URL. ``json // No parameters required {} `` Returns: List of all tabs showing index, active status, title, and URL.
rnw_tabs_new
Create a new browser tab and optionally navigate to a URL. The new tab becomes the active tab. ```json // Create empty new tab (about:blank) {}
// Create new tab and navigate to URL { "url": "http://localhost:8081/settings" } ``` Returns: New tab index and page snapshot.
rnw_tabs_select
Switch to a specific tab by index (0-based). Use rnw_tabs_list to see available tabs. ```json // Switch to second tab { "index": 1 }
// Switch to first tab { "index": 0 } ``` Returns: Snapshot of the selected tab's page.
rnw_tabs_close
Close a browser tab. If the closed tab was active, switches to the nearest remaining tab. ```json // Close current active tab {}
// Close specific tab by index { "index": 2 } ``` Returns: Confirmation and snapshot of the new active tab.
Session Management
rnw_session_status
Get current browser session status. Useful for checking if a session is active before performing actions. ``json // No parameters required {} `` Returns:
isRunning: Whether browser is activetabCount: Number of open tabscurrentTabIndex: Index of active tabcurrentUrl: URL of active tabviewport: Browser window dimensions (width x height)
rnw_close
Close the browser and end the session. All tabs are closed and resources are released. ``json // No parameters required {} `` Returns: Confirmation that browser session has ended.
How It Works
Standard Playwright Click (Doesn't work with RNW)
// This dispatches a synthetic 'click' event that RNW ignores
await element.click();
This MCP Server's Click (Works with RNW)
// This triggers real mousedown/mouseup events that RNW responds to
await page.mouse.move(x, y);
await page.mouse.down();
await page.mouse.up();
Example Usage with AI Agent
Agent: Let me navigate to your React Native Web app, take a screenshot, and click the login button.
> rnw_navigate { "url": "http://localhost:8081" }
Page loaded. I can see the following interactive elements:
[0] button testid="login-button"
text: "Sign In"
center: (640, 400)
> rnw_screenshot {}
[Returns PNG image of the page]
> rnw_click { "selector": "login-button", "findBy": "testid" }
Clicked at (640, 400). The login form is now visible.
> rnw_tabs_new { "url": "http://localhost:8081/settings" }
Created new tab [1] and navigated to settings page.
> rnw_tabs_list {}
Open Tabs (2):
[0] Home - http://localhost:8081/
[1] (active) Settings - http://localhost:8081/settings
Continuous Session Workflow
The browser session persists across tool calls, enabling:
- Multi-step interactions - Navigate, screenshot, interact, screenshot again
- Visual verification - Take screenshots to verify UI state after actions
- Multi-page workflows - Open multiple tabs for complex testing scenarios
- Debugging - Use
rnw_evaluateto inspect page state
Agent: I'll test the multi-step form submission.
> rnw_navigate { "url": "http://localhost:8081/form" }
> rnw_screenshot {} // Verify initial state
> rnw_type { "text": "John Doe", "selector": "[data-testid='name-input']", "findBy": "css" }
> rnw_screenshot {} // Verify text entered
> rnw_click { "selector": "Submit", "findBy": "text" }
> rnw_screenshot {} // Verify submission result
> rnw_close {} // End session when done
Troubleshooting
Elements not found
- Make sure your React Native Web components have
testIDprops set - Use
rnw_snapshotto see available elements - Try using text content with
findBy: "text"
Clicks not registering
- Ensure the element is visible and not covered by another element
- Try increasing the delay with
rnw_waitbefore clicking - Use coordinates directly if element finding fails
Screenshots not working
- Ensure the browser session is active (
rnw_session_status) - For element screenshots, verify the selector matches a visible element
Version History
v2.0.0
- Added screenshot support with base64 image return
- Added multi-tab management (list, new, select, close)
- Added session status tool
- Improved tool response format
- Persistent browser sessions
v1.0.0
- Initial release with RNW-compatible clicking
- Basic navigation, snapshot, and interaction tools
License
MIT











