MCP
Give ChatGPT, Claude, Cursor and other MCP clients a cloud browser, a shell and files as tools: sign in with one click, use an API key, run it over stdio, or host it yourself.
The Boxline MCP server gives ChatGPT, Claude, Cursor and other Model Context Protocol (MCP) clients an isolated cloud machine as tools. The model can browse, run commands in the shell and work with files on that one machine. Nothing runs on your own computer except, if you choose stdio, the small server that talks to the API.
Set it up
There are four ways to connect. The first needs no API key. The tools are the same in all of them, except that an app you connect with a Boxline sign-in gets the set without saved credentials.
Connect ChatGPT, Claude or Claude Code (no API key)
Choose Connect in ChatGPT or Claude, sign in to Boxline, pick a project and allow it. The app gets a short-lived token instead of a key: an access token that lasts 1 hour, and a refresh token that is replaced every time it is used. In Claude Code, this opens your browser to sign in:
claude mcp add --transport http boxline https://mcp.boxline.dev/mcpThe steps, what a connected app can and cannot do, and how to revoke it under Account → Connected apps are in Connect Boxline to ChatGPT, Claude and Claude Code. There is also a Boxline plugin with five skills.
Hosted, with an API key
Boxline runs the server for you at https://mcp.boxline.dev/mcp, over Streamable HTTP. You need an API key: create one just for MCP in the console. Every request carries it as Authorization: Bearer <API key>. Use this for saved credentials, for clients that cannot sign in, and for scripts. In Claude Code:
claude mcp add --transport http boxline https://mcp.boxline.dev/mcp \ --header "Authorization: Bearer your-api-key"Cursor, and other clients whose config takes a URL and headers:
{ "mcpServers": { "boxline": { "url": "https://mcp.boxline.dev/mcp", "headers": { "Authorization": "Bearer your-api-key" } } }}- The hosted server keeps no key of its own and never logs one. It calls the API with the key (or the sign-in token) of the request it is serving, so it can do only what that key or token can.
- Over HTTP,
session_createandsession_resumeleave out the live view URL. Anyone who has that link can control the browser. - Request bodies are limited to 4 MiB.
- Claude Desktop starts servers from its config file, so use stdio below. Other clients: MCP setup.
On your computer (stdio)
The client starts the server with npx -y @boxline/mcp and talks to it over stdio. You need Node 20 or newer and two environment variables: BOXLINE_API_KEY (your API key) and BOXLINE_API_URL (the API address, https://api.boxline.dev). In Claude Code:
claude mcp add boxline \ -e BOXLINE_API_KEY=your-api-key \ -e BOXLINE_API_URL=https://api.boxline.dev \ -- npx -y @boxline/mcpOpen source (MIT licence): github.com/Boxline-dev/mcp. The package on npm: @boxline/mcp.
Claude Desktop, Cursor and other clients: see MCP setup. Over stdio, the result of session_create includes the live view URL.
Host it yourself
npx -y @boxline/mcp --http serves the same tools over HTTP at POST /mcp, for clients that connect to a URL. Run it behind HTTPS (a reverse proxy or load balancer), and list the host name clients use in MCP_ALLOWED_HOSTS:
MCP_ALLOWED_HOSTS=mcp.example.com \BOXLINE_API_URL=https://api.boxline.dev \npx -y @boxline/mcp --http| Variable | Default | What it does |
|---|---|---|
MCP_PORT | 8081 | The port to listen on. 0 picks a free one. |
MCP_HOST | 0.0.0.0 | The address to bind. |
BOXLINE_API_URL | https://api.boxline.dev | The API the server calls. |
MCP_ALLOWED_HOSTS | none | Comma-separated host names the server answers to, such as mcp.example.com (no ports). It guards against DNS rebinding. localhost, 127.0.0.1 and [::1] always pass. Another Host, or a browser Origin that is not one of them, gets 403. * turns both checks off: do not use it on a server that is open to the internet. |
MCP_TOOLS | full | directory leaves out browser_login, credentials_list and every credential input (credential and field on browser_type, credentials on session_create). It is for a server listed in a public connector directory. |
MCP_MAX_IN_FLIGHT | 32 | How many requests run at once. More get 503. |
MCP_TOKEN_CACHE_SECONDS | 60 | How long the check of a token against the API is remembered, by the token’s hash. 0 checks every request. |
MCP_RESOURCE, MCP_AUTH_SERVER | none | Both or neither. They turn on OAuth sign-in discovery: this server’s public URL, and the address of the authorization server. Boxline’s own sign-in issues tokens for the hosted endpoint only, so leave them unset on a server you host against the Boxline cloud. |
MCP_OPENAI_CHALLENGE | none | The token that OpenAI’s domain check expects at /.well-known/openai-apps-challenge. |
Each caller sends their own API key, and your server holds none, so a caller can do only what their key allows. Before it serves a request, your server asks the API whether the token is good. See what the HTTP server checks.
How it works
Call session_create first. It returns a sessionId, and every tool that works in a session takes it as a required sessionId. There is no default session: the server keeps nothing between calls, so the model carries the id from one call to the next, and session_list finds an id again if the model loses it. session_create, session_list, credentials_listand the web_* tools need no session.
session_create {} -> { "sessionId": "6c1f3c9e-1d2a-4f67-9b0e-1b2c3d4e5f60", "workspace": "/workspace", "shell": true }browser_navigate { "sessionId": "6c1f3c9e-1d2a-4f67-9b0e-1b2c3d4e5f60", "url": "https://example.com" }A session is a browser only, a shell only, or both: see the three kinds of session. session_create takes browser and shell, both true unless you say otherwise. If your plan has no shells and you did not ask for one, you get a browser-only session. For a shell with no browser, set browser to false: the session has no live view, and the browser_* tools answer browser_disabled.
session_create{ "browser": false, "shell": true }shell_exec{ "sessionId": "SESSION_ID", "command": "python3 -c 'print(6*7)'" }- A stopped session. The tools that act on a session (clicks, typing, steps, commands, writing and deleting files) resume it first, as it was. The tools that read the browser (
browser_read,browser_screenshot) do not, because a resume starts a machine and billing: they say the session is stopped and tell the model to call session_resume.files_listandfiles_readneed no resume: they read what a stopped session saved, without starting a machine. - Several steps in one call.
browser_acttakes a list of steps, each a sentence such as “click the Sign in link” or an exact action. A plain-English step is carried out by a model and billed like a step of an agent run, so it needs a plan that includes plain-English steps. - Annotations. Every tool has a title and read-only, destructive and open-world hints, so your client can decide what to ask you about. Each tool’s page lists them.
- Protocol. The server speaks MCP revision 2026-07-28. Clients that speak the 2025 protocol keep working.
Ask in plain words. For example:
- “Open example.com and tell me what the page is for.”
- “Download the CSV on that page and give me the total of the amount column.”
- “Open a session with a residential proxy in Germany and check the price there.”
Tools
The server has 26 tools. There are no tools that start an agent run: an MCP client is already an agent, and it drives the session itself. Agent runs are for the API, the SDKs, the CLI and the console.
Sessions
| Tool | What it does |
|---|---|
session_create | Start a new session and get its sessionId. |
session_list | Find your sessions again. |
session_stop | Stop a session and keep it. |
session_resume | Bring a stopped session back. |
session_delete | Delete a session for good. |
Browser
| Tool | What it does |
|---|---|
browser_navigate | Open a URL in the session's browser. |
browser_click | Click an element or a point on the page. |
browser_type | Type text, or a saved credential. |
browser_press | Press a key, a key combination, or several in a row. |
browser_act | Do one or more steps in the browser, in plain English or as exact actions. |
browser_login | Sign in with a saved password, in one call. |
browser_computer | Run one computer-use action from a model, and see the screen. |
browser_screenshot | Take a screenshot of the current tab. |
browser_read | Read the current page as text. |
Shell
| Tool | What it does |
|---|---|
shell_exec | Run a bash command in the session's shell. |
Files
| Tool | What it does |
|---|---|
files_list | List files in the workspace. |
files_read | Read a text file from the workspace. |
files_write | Write a text file into the workspace. |
files_delete | Delete a file or a folder from the workspace. |
Credentials
| Tool | What it does |
|---|---|
credentials_list | List the saved credentials (never their values). |
Web
| Tool | What it does |
|---|---|
web_fetch | Fetch a page without a session. |
web_search | Search the web, with no session. |
web_screenshot | Screenshot a web page, with no session. |
Crawl and extract
| Tool | What it does |
|---|---|
web_crawl_start | Crawl a site from a start URL, with no session. |
web_crawl_get | Get a crawl's state and its next pages. |
web_extract | Get JSON from up to 10 pages, with no session. |
Sign in without showing a password
Save a website password as a credential in the console, with the CLI or with an SDK. Then ask the model to sign in. It calls credentials_list to see the names (never a value), and browser_type with credential and field to fill each box:
{ "sessionId": "<id>", "credential": "SHOP", "field": "username", "selector": "#email" }{ "sessionId": "<id>", "credential": "SHOP", "field": "password", "selector": "#password" }{ "sessionId": "<id>", "credential": "SHOP", "field": "otp", "selector": "#code" }To sign in in one call, the model uses browser_login with the credential’s name and, if it helps, the sign-in page:
{ "sessionId": "<id>", "credential": "SHOP", "url": "https://shop.example.com/login" }A short run inside the session types the credential on its own sites only. The model sees neither the password, nor a 2FA code, nor a sign-in link, and the answer is the page it ended on: Signed in with SHOP: Your account (https://shop.example.com/account). It counts as an agent run, so it needs a plan with password credentials and uses the project’s default model. See Sign in in one call.
- The platform types the value into the field itself, only on the sites the credential was saved for. The model, the chat and the logs never see it.
otpis the current 2FA code, for a password with a 2FA key. If the credential’scodeSourceispushorurl(the site emails or texts the code),otpwaits for the code your own system sends. - Afterwards, the session hides it wherever a result would show it:
browser_read, the page’s elements, an error and shell output show%SHOP.password%instead, and a screenshot covers the field. - This hides a value; it does not protect it. It is a guard against showing a credential by accident, not a boundary. Code that runs in the page or the session (an
evaluatestep ofbrowser_act, a command inshell_exec) can still read a field and print it, for example reversed or in base64. Use a password only in sessions whose pages and scripts you trust, and save it with the narrowest sites. - Codes from your own system. When
credentials_listshowscodeSourcepushorurl,browser_login(orbrowser_typewithotp) waits for the code or sign-in link, up to the credential’s code timeout (5 minutes by default). Ask the model to tell you when it waits. You then send the code with the CLI (boxline credentials push-code SHOP) or an SDK, never through the chat. See 2FA codes and sign-in links. - No tool creates or changes a credential, on purpose: that would send a password through the chat.
session_createcan start from a saved browser profile (profile, andpersistProfileto save new logins back) and exportcredentialsinto the shell as environment variables, for those that allow it. Anything that runs in the shell can read them, so export only what a command needs.
Browse through a proxy
session_create takes three optional proxy inputs, so you can ask the model to “open a session with a residential proxy in Germany”: proxyType ("residential" or "datacenter"), proxyCountry (two letters) and proxyCity (residential only, needs a country).
The session keeps one IP, and only the browser uses the proxy: shell commands connect directly. Without these inputs, a session has no proxy. US states, your own proxy and the shell option are available through the API and SDKs.
Use it safely
- Your API key sits in the client’s config file, as an environment variable for stdio or as the
Authorizationheader for the hosted server. Use a key just for MCP, keep the file out of shared repositories, and revoke the key when you stop using it. - An app you connect with a Boxline sign-in holds tokens, not a key. Revoke apps you no longer use under Account → Connected apps, and read how to use it safely.
- If you host the server yourself, put it behind HTTPS and list your host name in
MCP_ALLOWED_HOSTS. Every request carries a key, and the key is sent in a header: never run it over plain HTTP outside your own machine. - Web pages can contain text written to mislead a model. Watch what the model does with pages you do not trust, and do not give it sessions that are signed in to accounts it does not need.
- Anything you type into the chat, the model sees. Do not paste passwords into it; save them as credentials and let the model use them by name, or sign in by hand in the live view.
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.