@teatak/mcp-server-browser
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@@teatak/mcp-server-browserSubmit the feedback form with the message: "Great job!""
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@teatak/mcp-server-browser
An MCP (Model Context Protocol) server that runs in the browser.
Register tools and prompts on a web page; expose them to a local MCP client (such as an agent daemon or sidecar process) over WebSocket. The browser acts as the MCP server — your tool handlers run client-side and the agent calls into them.
Why a "browser-side server"?
In the usual MCP topology, servers run as local processes and expose filesystem / database / API tools. This package flips that: the browser exposes capabilities to the agent. Useful when you want the agent to:
Drive a UI you're rendering (a canvas, a chart, a form).
Call into APIs that are only reachable from the user's browser session (authenticated SaaS, page-scoped APIs).
Get human-in-the-loop confirmation through DOM affordances.
At the wire level the browser dials a WebSocket to the agent; at the MCP
protocol level the browser is the server (handles tools/list,
tools/call, prompts/list, etc.).
Related MCP server: izan.io
How this differs from browser-automation MCP servers
If you've seen packages like @playwright/mcp,
BrowserMCP/mcp, browserbase/mcp-server-browserbase,
or chrome-devtools-mcp, those go in the opposite
direction from this one.
Browser-automation MCP servers |
| |
Where the MCP server runs | A local Node process (or cloud) | The browser page itself |
Who defines the tools | The package author (fixed set) | You — the page registers its own tools |
Browser's role | Target of automation (driven by agent) | Active producer of capabilities |
Typical tools |
| Anything your page can do — UI rendering, page-scoped APIs, etc. |
Bridge | Chrome extension / CDP / Playwright |
|
Short version: those packages give an agent a browser. This package lets your browser app give an agent custom tools.
The two patterns compose — you can use Playwright MCP to let an agent drive a page and have the same page expose its own MCP server (via this package) for higher-level domain operations.
Install
npm install @teatak/mcp-server-browserQuick start
import { createServer } from "@teatak/mcp-server-browser";
const server = createServer({
endpoint: "ws://127.0.0.1:9669/mcp/ws",
serverInfo: { name: "my-page", version: "1.0.0" },
});
server.registerTool({
name: "demo.echo",
description: "Echo back whatever the caller passed.",
inputSchema: {
type: "object",
properties: { text: { type: "string" } },
required: ["text"],
},
handler: async ({ text }) => ({ ok: true, text }),
});
server.connect();Tool metadata
Since 0.0.2, tool definitions may include MCP's _meta extension
object. It is passed through unchanged in tools/list, so clients can carry
private namespaced metadata without adding non-standard top-level fields.
server.registerTool({
name: "demo.echo",
description: "Echo back whatever the caller passed.",
inputSchema: { type: "object", properties: {} },
_meta: {
"example.com/tier": "lite",
},
handler: async () => ({ ok: true }),
});The other side — a minimal Go agent
The snippet above is only half the picture. Here's the matching MCP
client side — a Go program that accepts the WebSocket from the browser
and drives it over plain JSON-RPC 2.0. No external MCP library required;
the only dependency is gorilla/websocket.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
type rpcMessage struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id,omitempty"`
Method string `json:"method,omitempty"`
Params json.RawMessage `json:"params,omitempty"`
Result json.RawMessage `json:"result,omitempty"`
Error *struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"error,omitempty"`
}
// Single-flight roundtrip — sends one request and reads the next frame as
// its response. For concurrent calls, track pending requests by `id` in a
// sync.Map and dispatch from a dedicated read loop.
func roundtrip(conn *websocket.Conn, id int, method string, params any) (json.RawMessage, error) {
p, _ := json.Marshal(params)
idRaw, _ := json.Marshal(id)
if err := conn.WriteJSON(rpcMessage{
JSONRPC: "2.0", ID: idRaw, Method: method, Params: p,
}); err != nil {
return nil, err
}
var resp rpcMessage
if err := conn.ReadJSON(&resp); err != nil {
return nil, err
}
if resp.Error != nil {
return nil, fmt.Errorf("rpc %d: %s", resp.Error.Code, resp.Error.Message)
}
return resp.Result, nil
}
var upgrader = websocket.Upgrader{
// Tighten in production: pin Origin and validate a session token.
CheckOrigin: func(r *http.Request) bool { return true },
}
func main() {
http.HandleFunc("/mcp/ws", func(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
defer conn.Close()
// 1. Handshake.
if _, err := roundtrip(conn, 1, "initialize", map[string]any{
"protocolVersion": "2025-03-26",
"clientInfo": map[string]any{"name": "demo-agent", "version": "0.1"},
"capabilities": map[string]any{},
}); err != nil {
log.Printf("initialize: %v", err)
return
}
// 2. Discover what the page exposes.
tools, err := roundtrip(conn, 2, "tools/list", struct{}{})
if err != nil {
log.Printf("tools/list: %v", err)
return
}
log.Printf("browser exposes: %s", tools)
// 3. Invoke one.
result, err := roundtrip(conn, 3, "tools/call", map[string]any{
"name": "demo.echo",
"arguments": map[string]any{"text": "hello from go"},
})
if err != nil {
log.Printf("tools/call: %v", err)
return
}
log.Printf("result: %s", result)
})
log.Println("listening on ws://127.0.0.1:9669/mcp/ws")
log.Fatal(http.ListenAndServe("127.0.0.1:9669", nil))
}Run this next to the Quick start snippet above: the page dials in, gets
initialized, and has its demo.echo tool called once. From here a
real agent typically grows a pending-request map keyed by id for
concurrent calls, a hub holding multiple browser sessions (one per tab),
and a notifications/tools/list_changed handler so the tool set can be
hot-reloaded as the page registers new tools.
Authentication
This package is unopinionated about auth. The browser's WebSocket
constructor only exposes two knobs (url and protocols); any auth scheme
ultimately rides on one of those. Instead of baking in a specific mechanism,
the library exposes a createSocket factory and lets you decide.
The factory is called on every (re)connect — perfect for short-lived tokens.
No auth (default)
createServer({
endpoint: "ws://127.0.0.1:9669/mcp/ws",
serverInfo: { name: "demo", version: "1.0.0" },
});Bearer token in URL
createServer({
endpoint: "ws://127.0.0.1:9669/mcp/ws",
serverInfo: { name: "demo", version: "1.0.0" },
createSocket: ({ endpoint }) =>
new WebSocket(`${endpoint}?token=${encodeURIComponent(TOKEN)}`),
});Bearer token in Sec-WebSocket-Protocol
Avoids tokens leaking into logs / browser history.
createServer({
endpoint: "ws://127.0.0.1:9669/mcp/ws",
serverInfo: { name: "demo", version: "1.0.0" },
createSocket: ({ endpoint }) =>
new WebSocket(endpoint, ["mcp.v1", `bearer.${TOKEN}`]),
});The MCP client side should validate the subprotocol on upgrade and echo the chosen one back.
Fresh token per connection
createServer({
endpoint: "ws://127.0.0.1:9669/mcp/ws",
serverInfo: { name: "demo", version: "1.0.0" },
createSocket: async ({ endpoint, attempt }) => {
const token = await fetch("/mcp/session-token").then((r) => r.text());
return new WebSocket(endpoint, [`bearer.${token}`]);
},
});attempt is 0 on the first connect and increments on each reconnect, in
case you want to short-circuit retries after some bound.
A note on threat model
Localhost WebSocket endpoints are not protected by the browser's
same-origin policy — any tab on the user's machine can dial ws://127.0.0.1.
For real deployments the MCP client side should pair token validation with
an Origin header allowlist.
Entry points
Import path | What's there |
| High-level |
| Raw |
| Wire-level JSON-RPC / MCP types and constants. |
Prompts
In addition to tools, this package supports a lightweight prompts capability
— a chunk of guidance text that the MCP client should append to its LLM
system instruction. Compared to MCP's standard prompts, this variant is
deliberately simpler: no arguments, no prompts/get round-trip — content
is delivered inline in prompts/list.
server.registerPrompt({
name: "ui-render-table.usage",
description: "Constraints for the ui_render_table tool.",
content: `When calling ui_render_table, only pass rows from real data. Never invent values.`,
});Status
Pre-1.0. API may evolve. Tested against MCP protocol version 2025-03-26.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Live browser debugging for AI assistants — DOM, console, network via MCP.
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
The Remote MCP server acts as a standardized bridge between LLM applications (like Claude, ChatGPT, and Cursor) and external services, enabling AI agents to access external tools and resources. Its primary capability is providing a centralized search tool to discover other MCP servers and their respective tools. Unlike local implementations, it runs remotely with OAuth authentication and permission controls for security.
Hyperbrowser MCP — wraps the Hyperbrowser AI-agent browsing API
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP bridge server that allows AI tools to call functions and execute code directly within a web browser tab. It enables developers to register custom browser-side tools with full access to the DOM, web APIs, and local application state.4 npmApache 2.0
- FlicenseNot gradedqualityCmaintenanceTurns your Chrome browser into an MCP server, allowing AI clients to control browser actions like clicking, typing, navigating, and data extraction through custom JavaScript tools.28-
- FlicenseNot gradedqualityDmaintenanceEnables websites to expose JavaScript functions as MCP tools, allowing AI agents to interact with the browser environment via a protocol and Chrome extension.1,098-
- AlicenseNot gradedqualityBmaintenanceMCP server that lets agents drive your real Chrome browser with existing logins and sessions via an outbound-only WebSocket extension. It exposes Playwright-compatible browser tools for navigation, clicking, typing, and snapshots.1Apache 2.0