Turn Any Website Into an MCP Tool with Anchor Browser

Technical Dive
by Idan Raman

Model Context Protocol (MCP) has become the default way to connect AI agents to external tools. Every major framework supports it. Infrastructure providers are shipping MCP servers for databases, Slack, GitHub, and dozens of SaaS APIs. The pattern is clean: expose a capability as a tool, and any agent can call it.

But there's a problem. Most of the web doesn't have an API.

LinkedIn company pages, legacy ERP portals, government procurement systems, competitor pricing pages — they're all sitting behind a browser, not a REST endpoint. If your agent needs data or actions from those surfaces, you're writing Playwright scripts from scratch or giving up entirely.

This post shows how to bridge that gap: wrap Anchor Browser in a custom MCP server so any authenticated, anti-bot-proof browser session becomes a first-class tool your agents can call.

Why Anchor + MCP?

Anchor handles the hard infrastructure layer — session isolation, residential proxies, CAPTCHA resolution, and persistent authentication via OmniConnect. What it doesn't do out of the box is expose those sessions as MCP tools. That's the 50-line wrapper we're about to write.

The result: your Claude Desktop setup, your OpenAI Agents pipeline, or any MCP-compatible framework can now call get_page_text or search_site the same way it calls any other tool — and a real, managed browser will handle the request.

Setup

Install the dependencies:

pip install anchorbrowser mcp playwright
playwright install chromium

Export your keys:

export ANCHOR_API_KEY="your-anchor-key"

Building the MCP Server

The server below exposes two tools: one that visits a URL and returns its visible text, and one that fills a search box and returns the results page. Both create a fresh Anchor session per call, so every invocation is isolated.

import os, asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
from anchorbrowser import Anchorbrowser

app    = Server("anchor-browser")
anchor = Anchorbrowser(api_key=os.environ["ANCHOR_API_KEY"])

@app.list_tools()
async def list_tools():
    return [
        Tool(
            name="get_page_text",
            description="Navigate to a URL and return all visible text.",
            inputSchema={
                "type": "object",
                "properties": {
                    "url": {"type": "string", "description": "Page URL to visit"},
                },
                "required": ["url"],
            },
        ),
        Tool(
            name="search_site",
            description="Type a query into the site's search box and return the results page text.",
            inputSchema={
                "type": "object",
                "properties": {
                    "url":   {"type": "string", "description": "Site homepage URL"},
                    "query": {"type": "string", "description": "Search term"},
                },
                "required": ["url", "query"],
            },
        ),
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    session_response = anchor.sessions.create(
        session={
            "proxy":   {"active": True, "country_code": "us"},
            "timeout": {"max_duration": 3, "idle_timeout": 1},
        }
    )
    session_id = session_response.data.id

    with anchor.browser.connect(session_id) as browser:
        page = browser.contexts[0].pages[0]

        if name == "get_page_text":
            page.goto(arguments["url"])
            page.wait_for_load_state("networkidle")
            text = page.inner_text("body")
            return [TextContent(type="text", text=text[:8000])]

        if name == "search_site":
            page.goto(arguments["url"])
            page.wait_for_load_state("networkidle")
            page.get_by_role("searchbox").fill(arguments["query"])
            page.keyboard.press("Enter")
            page.wait_for_load_state("networkidle")
            text = page.inner_text("body")
            return [TextContent(type="text", text=text[:8000])]

if __name__ == "__main__":
    asyncio.run(stdio_server(app))

Save this as anchor_mcp_server.py.

Connecting to Claude Desktop

Add the server to your claude_desktop_config.json:

{
  "mcpServers": {
    "anchor-browser": {
      "command": "python",
      "args": ["/absolute/path/to/anchor_mcp_server.py"],
      "env": {
        "ANCHOR_API_KEY": "your-key-here"
      }
    }
  }
}

Restart Claude Desktop. The tools appear immediately. Ask: "What are the top three plans on competitor.com/pricing?" — Claude drives a real, clean browser session to find the answer.

Using it with the OpenAI Agents SDK

The same server works anywhere MCP runs. Here's the pattern for OpenAI Agents:

import asyncio, os
from agents import Agent, Runner
from agents.mcp import MCPServerStdio

async def main():
    async with MCPServerStdio(
        command="python",
        args=["anchor_mcp_server.py"],
        env={"ANCHOR_API_KEY": os.environ["ANCHOR_API_KEY"]},
    ) as server:
        agent = Agent(
            name="web-research-agent",
            model="gpt-4o",
            mcp_servers=[server],
        )
        result = await Runner.run(
            agent,
            "Find the current pricing tiers at example-competitor.com"
        )
        print(result.final_output)

asyncio.run(main())

What This Unlocks

Once any browser workflow is an MCP tool, your agents can:

  • Monitor competitors — pull pricing, feature pages, or changelog entries on a schedule
  • Interact with legacy systems — submit forms, navigate portals, and extract data from tools that will never ship an API
  • Maintain authenticated sessions — OmniConnect handles re-login, MFA, and session recovery so your agent doesn't get locked out mid-task
  • Scale without friction — each tool call spins up an isolated Anchor session; run dozens in parallel without IP collisions

The MCP layer stays thin. Anchor handles the infrastructure. Your agent gets a clean tool interface to the whole web.

Get started

Sign up at anchorbrowser.io, grab your API key, and adapt the server above to your first use case. The full SDK reference lives at docs.anchorbrowser.io.

Building something interesting on top of Anchor + MCP? We'd love to hear about it — reach out or join the community.

Stay ahead in browser automation

We respect your inbox. Privacy policy

Welcome aboard! Thanks for signing up
Oops! Something went wrong while submitting the form.