Build a country-by-country price index with an AI agent (one MCP server, no scraping stack)

javascript dev.to

Give an AI agent a browser and it can read the web. But it reads the web from exactly one address — whichever machine it happens to be running on.

That's fine until the thing you're asking about is country-specific. Public prices, currency, regional availability, localized search results — all of it comes back through a vantage point the agent never chose and can't move. The agent doesn't error. It just confidently reports the wrong country's answer.

I wanted an agent that could say "check this from Germany, now from the UK, now from the US" — and then prove it actually did. So I built an MCP server for it, and this post is the walkthrough: we'll build a small country-by-country price index, driven entirely by tool calls.

The gap this fills

If you've used Microsoft's Playwright MCP, you know it's a genuinely good tool — it drives a browser properly. But its proxy is a launch flag: you set it when the server starts, and every call in that session goes out the same door. It wasn't built for per-task geography, and that's a reasonable design choice.

Hosted "scraping" MCPs solve geography by routing through their own cloud and handing you JSON. The geography is real, but it's opaque — you don't pick the exit, and you can't verify where a request actually landed.

What I wanted sat in between: a local browser, a residential exit, and the country as an argument on the tool call — plus a way for the agent to check the exit before trusting the page.

Setup (2 minutes)

It's an npm package. Add it to your MCP client config — claude_desktop_config.json for Claude Desktop, ~/.cursor/mcp.json for Cursor:

{"mcpServers":{"aethyn-browser":{"command":"npx","args":["-y","aethyn-browser-mcp"],"env":{"AETHYN_USERNAME":"aethyn-XXXXX","AETHYN_PASSWORD":"your-proxy-password","AETHYN_DEFAULT_TIER":"premium"}}}}
Enter fullscreen mode Exit fullscreen mode

Chromium auto-installs on first run (~170 MB, once). The browser runs on your machine — nothing is hosted for you, and your credentials never leave your box.

Restart the client and the agent has 10 tools, all prefixed aethyn_:

Tool What it does
aethyn_launch_browser Open a proxied session in a chosen country
aethyn_navigate Go to a URL
aethyn_get_content Page content as text / html / markdown
aethyn_snapshot Accessibility tree with [ref=eNN] handles
aethyn_click / aethyn_type Interact by ref or selector
aethyn_check_exit_ip Verify the exit through the session's proxy
aethyn_new_identity Rotate to a fresh exit IP, clear cookies
aethyn_close Close the session
aethyn_list_countries Discover exit options at runtime

The build: a regional price index

The task: read a public product/pricing page from three countries and put the numbers side by side. Every step below is a real tool call the agent makes on its own.

Step 1 — let the agent discover its options

Rather than hard-coding a country list, the agent asks:

{"tool":"aethyn_list_countries","args":{"with_cities":false}}
Enter fullscreen mode Exit fullscreen mode

This matters more than it looks: the agent can now plan a loop over countries it knows are actually available, instead of guessing and failing.

Step 2 — one sticky session per country

For each country, launch a session. The session argument is the important one — it's your task id, and reusing it pins the same exit IP for the whole task:

{"tool":"aethyn_launch_browser","args":{"country":"de","session":"price_idx_de","lifetime_min":10,"tier":"premium"}}
Enter fullscreen mode Exit fullscreen mode

Note the token is lifetime_min (1–1440 minutes, default 10), not "ttl". It's how long that exit stays pinned to your session. Launch returns a session_id, which every other tool takes.

Step 3 — verify before you trust (the step people skip)

This is the one I'd argue is the whole point:

{"tool":"aethyn_check_exit_ip","args":{"session_id":"..."}}
Enter fullscreen mode Exit fullscreen mode
{"ip":"…","country":"DE","city":"…","org":"…","is_residential":true}
Enter fullscreen mode Exit fullscreen mode

It runs the check through the session's own proxy, so it tells you where the page you're about to load will actually come from. If the agent asked for de and the exit says something else, it can rotate and re-check instead of quietly reporting a German price it never saw.

(Fair warning: is_residential is a best-effort heuristic derived from the ASN/org — treat it as a signal, not a guarantee.)

Step 4 — read the page

{"tool":"aethyn_navigate","args":{"session_id":"...","url":"https://example-store.com/pricing"}}{"tool":"aethyn_get_content","args":{"session_id":"...","format":"markdown"}}
Enter fullscreen mode Exit fullscreen mode

markdown is usually the right format for feeding an LLM — it's the page, cleaned, without the script/style noise eating your context window.

Step 5 — close, repeat, aggregate

aethyn_close the session, then run the same sequence for uk and us with their own session ids. Because each country gets its own session, the identities never cross-contaminate.

What you actually type

The nice part is you don't orchestrate any of this by hand. You say:

"Using the aethyn-browser tools: list the available countries, then for de, uk, and us, open a sticky session, verify the exit IP is in the right country, load https://example-store.com/pricing, and pull the plan prices. Close each session when you're done. Put the results in a table with the currency, and flag any country where the exit IP didn't match what you asked for."

…and the agent runs the loop, checks its own work, and hands you the table. From there it's one more step to a chart or a nightly pipeline.

Two gotchas worth your time

1. The 407 that looks like a proxy failure but isn't.
AETHYN_DEFAULT_TIER defaults to premium, which uses HTTP port 2099. Elite uses 5499. If your account is Elite and you leave the default alone, the browser launches perfectly fine and then the proxy rejects auth with a 407. It looks like broken credentials; it's a port mismatch. Fix: set AETHYN_DEFAULT_TIER=elite, or pass tier: "elite" on the call.

Also note city and state targeting are Elite-only (port 5499). Country works on both tiers.

2. HTTP, not SOCKS5.
The server is HTTP-only by design — Chromium can't authenticate SOCKS5 proxies. If you've been burned by that before, this is why.

Bring your own proxy

It defaults to Aethyn, but it isn't locked to it. Point it at any HTTP proxy with PROXY_HOST, PROXY_PORT, PROXY_USERNAME, PROXY_PASSWORD, and a PROXY_USERNAME_TEMPLATE describing how your provider encodes targeting in the username:

PROXY_USERNAME_TEMPLATE="{username}-country-{country}[-city-{city}]-session-{session}-lifetime-{lifetime}"
Enter fullscreen mode Exit fullscreen mode

[optional] segments drop out when empty. For a fixed proxy with no geo targeting at all, the template is just {username}.

Where this stays honest

Worth being direct about the boundaries, because the "agents + browsers" space is full of overclaiming:

  • Public data only. Respect robots.txt, rate limits, and each site's terms. "The agent can reach it" isn't "the agent should."
  • There is no CAPTCHA-solving tool, and none is planned. If a challenge fires, that's a signal to back off — not to fight it.
  • No login/credential-wall automation.
  • Nothing here auto-detects a block. If a page comes back looking wrong, the agent reads the content, judges it, and calls aethyn_new_identity. That's a deliberate choice — I'd rather the model make that call visibly than have the tool silently paper over it.
  • A residential exit fixes your network vantage point. It doesn't fix reckless pacing or a client that looks robotic in every other way.

Try it

npx -y aethyn-browser-mcp
Enter fullscreen mode Exit fullscreen mode

Drop the config block in your MCP client, and your agent can pick a country on its very next tool call.

If you build something with it — or it breaks in an interesting way — I'd like to hear about it.

Source: dev.to

arrow_back Back to Tutorials