Codex supports MCP servers configured in ~/.codex/config.toml. browser.city ships a hosted MCP server, so you can give Codex real browser tools without running infrastructure locally.
1) Create an API key
Get a browser.city API key from your dashboard and export it:
Set BROWSERCITY_API_KEY in your environment.
2) Add browser.city to ~/.codex/config.toml
Add an MCP server entry (remote HTTP):
[mcp_servers.browsercity]
url = "https://mcp.browser.city/mcp"
bearer_token_env_var = "BROWSERCITY_API_KEY"
If you prefer explicit headers, Codex also supports http_headers = { Authorization = "Bearer ${BROWSERCITY_API_KEY}" }.
3) Use it inside Codex
Once configured, Codex will discover browser.city tools (for example: browser_open, browser_navigate, browser_snapshot, browser_click, browser_markdown).
Try a prompt like:
Open a browser with browser.city, navigate to
https://example.com, and return the page as markdown.
4) Practical patterns (what actually ships)
Fast extraction (best for RAG / pipelines)
Use the Request API when you don’t need interaction:
const res = await fetch("https://api.browser.city/v1/requests", { method: "POST", headers: { Authorization: `Bearer ${process.env.BROWSERCITY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ url: "https://example.com", markdown: true }),}).then((r) => r.json());console.log(res.content);import osimport requestsapi_key = os.environ["BROWSERCITY_API_KEY"]res = requests.post( "https://api.browser.city/v1/requests", headers={"Authorization": f"Bearer {api_key}"}, json={"url": "https://example.com", "markdown": True},).json()print(res["content"])using System.Net.Http.Headers;using System.Net.Http.Json;var apiKey = Environment.GetEnvironmentVariable("BROWSERCITY_API_KEY")!;var http = new HttpClient();http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);var res = await http.PostAsJsonAsync( "https://api.browser.city/v1/requests", new { url = "https://example.com", markdown = true });Console.WriteLine(await res.Content.ReadAsStringAsync());import java.net.URI;import java.net.http.*;public class Request { public static void main(String[] args) throws Exception { var apiKey = System.getenv("BROWSERCITY_API_KEY"); var http = HttpClient.newHttpClient(); var req = HttpRequest.newBuilder() .uri(URI.create("https://api.browser.city/v1/requests")) .header("Authorization", "Bearer %s".formatted(apiKey)) .POST(HttpRequest.BodyPublishers.ofString( "{\"url\":\"https://example.com\",\"markdown\":true}")) .build(); var res = http.send(req, HttpResponse.BodyHandlers.ofString()); System.out.println(res.body()); }}
Long-running workflows (logins, multi-step flows)
Use sessions + Playwright (keep your existing Playwright code):
import { chromium } from "playwright";const api = "https://api.browser.city/v1/sessions";const auth = { Authorization: `Bearer ${process.env.BROWSERCITY_API_KEY}` };const { endpoint, token, id } = await fetch(api, { method: "POST", headers: { ...auth, "Content-Type": "application/json" }, body: JSON.stringify({ browser: "chromium", egress: { mode: "managed", proxyType: "residential", country: "US" },}) }).then((r) => r.json());try { const browser = await chromium.connect(endpoint, { headers: { Authorization: `Bearer ${token}` } }); const page = browser.contexts().at(0)!.pages().at(0)!; await page.goto("https://example.com");} finally { await fetch(`${api}/${id}`, { method: "DELETE", headers: auth });}import os, requestsfrom playwright.sync_api import sync_playwrightapi = "https://api.browser.city/v1/sessions"auth = {"Authorization": f"Bearer {os.environ['BROWSERCITY_API_KEY']}"}session = requests.post(api, headers=auth, json={"browser": "chromium", "egress": {"mode": "managed", "proxyType": "residential", "country": "US"}}).json()endpoint = session['endpoint']token = session['token']session_id = session['id']playwright = Nonetry: playwright = sync_playwright().start() browser = playwright.chromium.connect( endpoint, headers={"Authorization": f"Bearer {token}"}, ) page = browser.contexts[0].pages[0] page.goto("https://example.com")finally: try: requests.delete(f"{api}/{session_id}", headers=auth) finally: if playwright: playwright.stop()using Microsoft.Playwright;using System.Net.Http.Headers;using System.Net.Http.Json;var http = new HttpClient();http.DefaultRequestHeaders.Authorization = new( "Bearer", Environment.GetEnvironmentVariable("BROWSERCITY_API_KEY"));var session = await (await http.PostAsJsonAsync( "https://api.browser.city/v1/sessions", new { browser = "chromium" })) .Content.ReadFromJsonAsync<Session>();var (id, endpoint, token) = session!;IPlaywright? pw = null;try { pw = await Playwright.CreateAsync(); var browser = await pw.Chromium.ConnectAsync(endpoint, new() { Headers = new() { ["Authorization"] = $"Bearer {token}" } }); var page = browser.Contexts[0].Pages[0]; await page.GotoAsync("https://example.com");} finally { try { if (session is not null) await http.DeleteAsync($"https://api.browser.city/v1/sessions/{id}"); } finally { pw?.Dispose(); }}record Session(string Id, string Endpoint, string Token);import com.fasterxml.jackson.databind.ObjectMapper;import com.microsoft.playwright.*;import java.net.URI;import java.net.http.*;import java.util.Map;public class Session { public static void main(String[] args) throws Exception { var key = System.getenv("BROWSERCITY_API_KEY"); var http = HttpClient.newHttpClient(); var req = HttpRequest.newBuilder(URI.create("https://api.browser.city/v1/sessions")) .header("Authorization", "Bearer %s".formatted(key)) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString("{\"browser\":\"chromium\"}")) .build(); var created = new ObjectMapper().readValue( http.send(req, HttpResponse.BodyHandlers.ofString()).body(), CreatedSession.class); var id = created.id(); var endpoint = created.endpoint(); var token = created.token(); Playwright pw = null; try { pw = Playwright.create(); var browser = pw.chromium().connect(endpoint, new BrowserType.ConnectOptions().setHeaders(Map.of("Authorization", "Bearer %s".formatted(token)))); var page = browser.contexts().get(0).pages().get(0); page.navigate("https://example.com"); } finally { try { http.send(HttpRequest.newBuilder(URI.create("https://api.browser.city/v1/sessions/" + id)) .header("Authorization", "Bearer %s".formatted(key)).DELETE().build(), HttpResponse.BodyHandlers.discarding()); } finally { if (pw != null) pw.close(); } } } record CreatedSession(String id, String endpoint, String token) {}}