OpenAI SDK
OpenAI exposes two paths for putting GPT behind a Browserless-powered agent loop:
- Direct integration with the Responses API. One
responses.createcall, MCP server as atool. Lightest possible shape. - Managed integration with the OpenAI Agents SDK. An
Agentclass, arun()helper, built-in tracing and handoffs. Better when you outgrow a single prompt.
Both routes call the same hosted MCP server at https://mcp.browserless.io/mcp. This page walks through the same toy task — GET /hn returning the top 10 Hacker News stories as JSON — at both levels of ceremony.
- A Browserless API token (available in your account dashboard)
- An OpenAI API key
- Node.js 20.6 or later (for the built-in
--env-fileflag)
Setting environment variables
- .env file
- Command line
OPENAI_API_KEY=your-openai-key
BROWSERLESS_API_KEY=your-browserless-token
PORT=3000
export OPENAI_API_KEY=your-openai-key
export BROWSERLESS_API_KEY=your-browserless-token
export PORT=3000
Direct integration: Responses API
The Responses API runs the tool-calling loop server-side and accepts the hosted MCP server as a single tool entry. No SDK, no state machine — one request, one response.
Install
npm init -y
npm install openai express
server.js
import express from "express";
import OpenAI from "openai";
const PORT = process.env.PORT ?? 3000;
const OPENAI_API_KEY = process.env.OPENAI_API_KEY || "";
const BROWSERLESS_API_KEY = process.env.BROWSERLESS_API_KEY || "";
const INSTRUCTIONS =
"You are a news scraper. Use the browserless tools to load the Hacker News front page and return the top 10 stories as a JSON array. Each item must have the shape { rank, title, url, points }. Reply with ONLY the JSON array — no markdown fences, no commentary.";
const app = express();
const client = new OpenAI({ apiKey: OPENAI_API_KEY });
app.get("/hn", async (_req, res) => {
const response = await client.responses.create({
model: "gpt-5.5",
instructions: INSTRUCTIONS,
tools: [
{
type: "mcp",
server_label: "browserless",
server_url: "https://mcp.browserless.io/mcp",
headers: {
Authorization: `Bearer ${BROWSERLESS_API_KEY}`,
},
require_approval: "never",
},
],
input: "Fetch the top 10 stories from https://news.ycombinator.com",
});
res.type("application/json").send(response.output_text);
});
app.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`);
});
Add "type": "module" to your package.json so the import statements work.
Run it
node --env-file=.env server.js
curl http://localhost:3000/hn
# → [
# { "rank": 1, "title": "Show HN: …", "url": "https://…", "points": 412 },
# …
# ]
How it works
- Express receives
GET /hn. - The server calls
responses.createwith themcptool pointing athttps://mcp.browserless.io/mcp. - OpenAI fetches the MCP tool list, the model picks the right Browserless tool (typically
browserless_smartscraper), and OpenAI executes the call server-side againsthttps://news.ycombinator.com. - The scraped page comes back to the model, which extracts the top 10 stories into the JSON shape requested by
instructions. response.output_textis piped straight to the HTTP response — one-shot, no persistence.
Multi-turn state
For a chat-style endpoint, persist the response ID per session and pass it on the next call:
const response = await client.responses.create({
model: "gpt-5.5",
previous_response_id: lastResponseId, // from the previous turn
tools: [/* same mcp tool block — must be re-sent every call */],
input: userMessage,
});
lastResponseId = response.id;
OpenAI persists the conversation server-side under that ID, so you only store the string.
Managed integration: Agents SDK
The OpenAI Agents SDK wraps the Responses API in an Agent abstraction with built-in tracing, handoffs to other agents, and richer multi-turn ergonomics. The MCP tool config moves from a raw tools[] entry to the hostedMcpTool helper.
Reach for this when you want a longer-running agent process, multi-agent orchestration, or just structured tracing of every tool call.
Install
npm install @openai/agents express
server.js
import express from "express";
import { Agent, run, hostedMcpTool, setDefaultOpenAIKey, setTracingExportApiKey } from "@openai/agents";
const PORT = process.env.PORT ?? 3000;
const OPENAI_API_KEY = process.env.OPENAI_API_KEY || "";
const BROWSERLESS_API_KEY = process.env.BROWSERLESS_API_KEY || "";
const INSTRUCTIONS =
"You are a news scraper. Use the browserless tools to load the Hacker News front page and return the top 10 stories as a JSON array. Each item must have the shape { rank, title, url, points }. Reply with ONLY the JSON array — no markdown fences, no commentary.";
setDefaultOpenAIKey(OPENAI_API_KEY);
setTracingExportApiKey(OPENAI_API_KEY);
const app = express();
const agent = new Agent({
name: "HN Scraper",
model: "gpt-5.5",
instructions: INSTRUCTIONS,
tools: [
hostedMcpTool({
serverLabel: "browserless",
serverUrl: "https://mcp.browserless.io/mcp",
headers: {
Authorization: `Bearer ${BROWSERLESS_API_KEY}`,
},
requireApproval: "never",
}),
],
});
app.get("/hn", async (_req, res) => {
const result = await run(
agent,
"Fetch the top 10 stories from https://news.ycombinator.com",
);
res.type("application/json").send(result.finalOutput ?? "[]");
});
app.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`);
});
Run it
node --env-file=.env server.js
curl http://localhost:3000/hn
What you get over the raw Responses path
- Tracing. Every tool call, retry, and handoff is exported to OpenAI's traces dashboard out of the box.
- Handoffs. Define multiple
Agentinstances and let the model route between them (e.g. one for scraping, one for summarization). - Session helpers. Pass
previousResponseIdor accumulate input arrays without managing the wire format yourself. - Guardrails. Built-in input/output validation hooks for production use.
Trade-off: another dependency, and an extra abstraction between you and the underlying Responses API.
Stealth, CAPTCHAs, and regional endpoints
Both paths route through the same hosted MCP server, so stealth mode, automatic CAPTCHA solving, and residential proxies all apply.
To pin to a regional endpoint, add the x-browserless-api-url header alongside the bearer:
headers: {
Authorization: `Bearer ${BROWSERLESS_API_KEY}`,
"x-browserless-api-url": "https://production-lon.browserless.io",
},
See the MCP setup page for the full list of configuration options.
Troubleshooting
The request hangs forever
The default value for require_approval (Responses API) / requireApproval (Agents SDK) is "always", which makes the API return mcp_approval_request items and wait for you to approve each tool call. Set it to "never" on the MCP tool config so the loop runs to completion.
"Unknown tool type: mcp"
The mcp tool type requires a current model. Use gpt-5.5 (or later); older completion-only models reject the entry.
Tools "disappear" between calls (Responses API)
OpenAI does not persist tool configuration server-side on the raw Responses API. Re-send the full tools[] array on every responses.create call, even when using previous_response_id. The Agents SDK handles this for you because tools are bound to the Agent instance.
401 from the MCP server
The bearer is sent fresh on every call and never stored. Double-check that BROWSERLESS_API_KEY is loaded into the process and that the Authorization: Bearer … header is on the MCP tool config, not on the outer request.
Use /mcp, not /sse
The Browserless MCP endpoint is https://mcp.browserless.io/mcp (Streamable HTTP). The legacy /sse path will not work with either the Responses API's MCP client or the Agents SDK's hostedMcpTool.