When the Model Context Protocol (MCP) became the de facto standard for connecting LLMs to external tools, most implementations treated "browser access" as a simple HTTP request. Real agentic workflows need a full browser—JavaScript execution, persistent cookies, login states, and anti-bot fingerprinting—not just raw HTTP responses.
Anchor gives you that infrastructure, and wiring it up as an MCP server takes about 50 lines of Python.
Why Your MCP Browser Tool Needs More Than fetch()
The typical pattern for giving an LLM web access looks like this:
# Most "browser" MCP tools are just HTTP wrappers
async def browse(url: str) -> str:
response = requests.get(url)
return response.text # No JS, no auth, no cookies
This works for static pages. It fails for everything real: SPAs that need JavaScript to render content, pages behind login flows, sites that fingerprint headless scrapers, and any multi-step workflow that spans sequential pages.
Setting Up Anchor as an MCP Server
Install the dependencies:
pip install anchor-browser mcp
Here's a minimal MCP server that exposes a live Anchor browser session as a callable tool:
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
from anchor_browser import AnchorClient
app = Server("anchor-browser")
client = AnchorClient()
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="browse",
description="Navigate to a URL in a real browser and return page text",
inputSchema={
"type": "object",
"properties": {
"url": {"type": "string"},
"wait_for": {
"type": "string",
"description": "CSS selector to wait for before returning",
},
},
"required": ["url"],
},
),
types.Tool(
name="click",
description="Click an element on the current page",
inputSchema={
"type": "object",
"properties": {
"selector": {"type": "string", "description": "CSS selector"},
},
"required": ["selector"],
},
),
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
async with client.session() as session:
page = await session.new_page()
if name == "browse":
await page.goto(arguments["url"])
if "wait_for" in arguments:
await page.wait_for_selector(arguments["wait_for"])
content = await page.inner_text("body")
return [types.TextContent(type="text", text=content)]
elif name == "click":
await page.click(arguments["selector"])
return [types.TextContent(type="text", text="Clicked")]
async def main():
async with stdio_server() as (read_stream, write_stream):
await app.run(read_stream, write_stream, app.create_initialization_options())
asyncio.run(main())
Register the server in your Claude Desktop config:
{
"mcpServers": {
"anchor-browser": {
"command": "python",
"args": ["/path/to/anchor_mcp_server.py"]
}
}
}
Persisting Sessions Across Tool Calls
The pattern above creates a new session per tool call. For multi-step workflows—log in, navigate, extract data—you need session persistence:
_sessions: dict[str, any] = {}
async def get_or_create_session(session_id: str):
if session_id not in _sessions:
_sessions[session_id] = await client.create_session()
return _sessions[session_id]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
session_id = arguments.get("session_id", "default")
session = await get_or_create_session(session_id)
page = await session.get_current_page()
# ... rest of your tool logic
This lets an LLM build up browser state across multiple tool calls—authenticate on call one, navigate on call two, extract on call three—within the same persistent, isolated Anchor session.
Running It End-to-End
With the server registered, Claude treats your Anchor browser like any other tool. A prompt like "Check the pricing plans on linear.app and list them" triggers a real browser session with full JavaScript rendering, persistent cookies, and a human-like fingerprint—not a raw HTTP request that Linear's bot detection would block.
Because Anchor manages session isolation at the infrastructure level, you can run dozens of independent MCP clients simultaneously without sessions bleeding into each other. Each agent gets its own clean browser context.
Where to Go Next
Once the basic server is running, a few natural extensions open up:
- OmniConnect for auth — let Anchor handle credential injection so your MCP tools never touch secrets directly
- Anchor VPN — route sessions through specific geographic IPs from your MCP config
- Async session pools — spin up multiple browser contexts concurrently for high-throughput workflows
The MCP ecosystem is moving fast. Having a real browser as a first-class tool—not a thin HTTP wrapper—is the difference between agents that work and agents that stall on the first JavaScript-rendered page.
Start your first free Anchor session at anchorbrowser.io and have this MCP server running in minutes.



