Docs
Dashboard
SDKs / MCP serverBeta

Setup

Add the hosted or the local MCP server to Claude Desktop, Claude Code, Cursor or any other MCP client, what it checks over HTTP, and how a client signs people in with OAuth.

The MCP server runs in two ways. Boxline hosts it at https://mcp.boxline.dev/mcp, or your client starts it on your computer and talks to it over stdio. It is the same set of tools either way, and either way the server talks to the API over HTTPS. You can also host it yourself. To connect ChatGPT, Claude or Claude Code with a Boxline sign-in and no API key, read Connect Boxline to ChatGPT, Claude and Claude Code.

Open source (MIT licence): github.com/Boxline-dev/mcp. The package on npm: @boxline/mcp.

It speaks MCP revision 2026-07-28. Clients that speak the 2025 protocol keep working.

Hosted server

Connect to https://mcp.boxline.dev/mcp with Streamable HTTP, and send a token as Authorization: Bearer <token> on every request. The token is an API key (create one just for MCP in the console), or the access token an app got when you connected it with a Boxline sign-in (see Sign in with OAuth).

The server checks the token with the API before it serves anything. A missing, expired, revoked or unknown token gets 401, and an app then refreshes the token or asks you to sign in again.

Shell
claude mcp add --transport http boxline https://mcp.boxline.dev/mcp \  --header "Authorization: Bearer your-api-key"

Cursor, and other clients that take a URL and headers in their config, use the same two values:

.cursor/mcp.json
{  "mcpServers": {    "boxline": {      "url": "https://mcp.boxline.dev/mcp",      "headers": { "Authorization": "Bearer your-api-key" }    }  }}

Claude Desktop starts servers from its config file, so it uses the stdio setup below. See Over HTTP for what the hosted server checks and limits.

Stdio: what it needs

VariableValue
BOXLINE_API_KEYYour API key. Create one just for MCP in the console.
BOXLINE_API_URLThe API address, https://api.boxline.dev.

The command is npx -y @boxline/mcp, so the computer needs Node 20 or later.

Claude Desktop

Claude Desktop starts the server on your computer (stdio). Add it to claude_desktop_config.json and restart the app. On macOS the file is in ~/Library/Application Support/Claude/; on Windows, in %APPDATA%\Claude\.

claude_desktop_config.json
{  "mcpServers": {    "boxline": {      "command": "npx",      "args": ["-y", "@boxline/mcp"],      "env": {        "BOXLINE_API_KEY": "your-api-key",        "BOXLINE_API_URL": "https://api.boxline.dev"      }    }  }}

Claude Code

For the hosted server, use the command above. To start the server on your computer instead:

Shell
claude mcp add boxline \  -e BOXLINE_API_KEY=your-api-key \  -e BOXLINE_API_URL=https://api.boxline.dev \  -- npx -y @boxline/mcp

Run claude mcp list to check that it is there.

Cursor

The hosted block above and this one go in .cursor/mcp.json in your project, or in ~/.cursor/mcp.json to turn it on everywhere. To start the server on your computer instead:

.cursor/mcp.json
{  "mcpServers": {    "boxline": {      "command": "npx",      "args": ["-y", "@boxline/mcp"],      "env": {        "BOXLINE_API_KEY": "your-api-key",        "BOXLINE_API_URL": "https://api.boxline.dev"      }    }  }}

Other clients

If the client can connect to a Streamable HTTP server and send a header, use the hosted server above. Otherwise start npx -y @boxline/mcp as a stdio server with both environment variables set.

Sessions: no default session

The server keeps nothing between calls. session_create returns a sessionId, and every tool that works in a session takes it as a required sessionId. A call without it is refused before anything reaches the API.

  • session_create, session_list, credentials_list, web_fetch, web_search, web_screenshot, web_extract, web_crawl_start and web_crawl_get take no sessionId.
  • Tools that act on a session (browser_login, browser_navigate, browser_click, browser_type, browser_press, browser_computer, browser_act, shell_exec, files_write and files_delete) resume a stopped session first, as it was.
  • Tools that read the browser (browser_screenshot and browser_read) never resume a session, because a resume starts a machine and billing. On a stopped session they answer that it is stopped and to call session_resume, then retry.
  • Tools that work on the session as it is (session_stop, session_resume, session_delete, files_list and files_read): the stop, resume and delete tools, and the two that read files, which read a stopped session’s saved files without starting a machine.
  • A session without a browser (browser false in session_create) is a shell-only session: the browser_* tools answer browser_disabled, and the shell, file and session tools work as usual. A session without a shell answers shell_disabled to shell_exec.
  • Lost a sessionId? session_list finds it again.
  • A session has no proxy unless session_create asks for one. Without timeout, its time is your plan’s longest session, at most 30 minutes (15 minutes if the server cannot read your plan).

Every tool also has a title and read-only, destructive and open-world annotations. Read-only: session_list, browser_screenshot, browser_read, files_list, files_read, credentials_list, web_fetch, web_search, web_screenshot, web_extract and web_crawl_get. Destructive: session_delete, shell_exec, files_write and files_delete (files_write overwrites). Open-world: everything that reaches the web, which is every tool except session_create, session_list, session_stop, session_resume, session_delete, files_list, files_read, files_write, files_delete, credentials_list and web_crawl_get. Each tool’s page has its own.

Over HTTP

The hosted server and a server you start with --http behave the same way. See Host it yourself for the command and its settings.

  • Stateless. Each request is served on its own: no Mcp-Session-Id, nothing remembered on the server. Any number of copies behind a load balancer can serve any request, and a restart loses nothing. GET /mcp and DELETE /mcp answer 405.
  • Your token, per request. The server holds no key and never logs a token. It builds its API client from the token of each request, and checks the token with the API first. The answer, good or refused, is remembered for a minute by the token’s hash, so a token that was just revoked can take up to a minute to be refused here.
  • No live view URL. session_create and session_resume leave out liveUrl over HTTP, because anyone who has that link can control the browser.
  • Long calls stay open. A long tool call sends a keep-alive comment every 15 seconds, so a load balancer’s idle timeout does not cut it.
  • Health check. GET /healthz answers 200 ok on any host, with no key.
  • Discovery. GET /.well-known/oauth-protected-resource (also under /mcp) tells an OAuth client which authorization server to use, with no token. It is 404 on a server where OAuth is not set up.
CheckAnswer
A path other than /mcp, /healthz and the /.well-known/ pages404
A Host not in MCP_ALLOWED_HOSTS, or a browser Origin that is not one of those hosts (clients that send no Origin pass)403
No Authorization: Bearer token401, with WWW-Authenticate: Bearer. Where OAuth is set up, it also names the discovery page: resource_metadata="…"
A body over 4 MiB413
More requests at once than MCP_MAX_IN_FLIGHT (32 by default)503
A token the API refuses: expired, revoked or unknown401. Where OAuth is set up, WWW-Authenticate also has error="invalid_token"

Only a 401 from the API refuses a token. Any other answer, such as the API being down, lets the request through, and the tool call reports the problem.

Sign in with OAuth

A client that supports OAuth can sign a person in to Boxline instead of asking for an API key. ChatGPT and Claude do this when someone chooses Connect. Boxline is the authorization server, at https://api.boxline.dev. This section is for people who build or review a client. To connect an app, read Connect Boxline to ChatGPT, Claude and Claude Code.

  • OAuth 2.1 authorization code with PKCE. Only S256 is accepted.
  • Public clients only: there are no client secrets, and token_endpoint_auth_method is none.
  • A client registers itself (dynamic client registration, RFC 7591). Client ID Metadata Documents are not supported.
  • If a client sends a resource (RFC 8707), it must be https://mcp.boxline.dev/mcp. Responses carry iss (RFC 9207).

The flow

  1. Call the MCP endpoint with no token. The answer is 401 with WWW-Authenticate: Bearer resource_metadata="…", scope="boxline openid email offline_access" (RFC 9728).
  2. Read the protected resource metadata it points to. It names the authorization server. Then read that server’s metadata at https://api.boxline.dev/.well-known/oauth-authorization-server (RFC 8414; /.well-known/openid-configuration answers too).
  3. Register once with POST /oauth/register.
  4. Send the person’s browser to /oauth/authorize with a PKCE challenge. They sign in to Boxline, pick a project and allow it. The browser returns to your redirect URI with code, state and iss, or with error=access_denied if they deny.
  5. Call POST /oauth/token with the code and your PKCE verifier. You get an access token and a refresh token, and an id_token when the scope includes openid.
  6. Call the MCP endpoint with Authorization: Bearer <access token>. Before it expires, call /oauth/token with the refresh token for a new pair.

Endpoints

EndpointWhat it takes
POST /oauth/registerJSON with redirect_uris (1 to 10, required) and client_name (up to 100 characters). The answer is 201 with a client_id. 60 requests an hour per IP address.
GET /oauth/authorizeresponse_type=code, client_id, redirect_uri, code_challenge and code_challenge_method=S256. Optional: state (up to 512 characters), scope, resource and nonce. 60 requests per 15 minutes per IP address.
POST /oauth/tokenA form body (JSON works too). grant_type=authorization_code with code, redirect_uri, client_id and code_verifier, or grant_type=refresh_token with refresh_token and client_id. 300 requests per 15 minutes per IP address, and 600 per client.
POST /oauth/revoketoken, an access or a refresh token. It ends the whole connection. The answer is always 200.
GET /oauth/userinfoAuthorization: Bearer with an access token that has openid. Returns sub, and with email also email, email_verified and name.
GET /oauth/jwksThe public key that signs the id_token (ES256).

A redirect URI must be https, or http on localhost or 127.0.0.1, and it is compared exactly. For a registered loopback address only, the port may differ, as Claude Code needs. A URI has no fragment or user name, and is at most 2048 characters. A registration that goes unused for 30 days, and has no live connection, is deleted.

ScopeWhat it allows
boxlineUse Boxline for the person: browser and shell sessions and their files, in the project they chose. The default. Without it, a token reaches nothing.
openidKnow which Boxline account this is: an id_token, and /oauth/userinfo.
emailSee the person’s email address and name.
offline_accessAccepted for compatibility. Refresh tokens are always issued, whether or not a client asks for this scope.

Tokens

TokenLastsNotes
Authorization code60 seconds, onceUsed twice, it ends the connection its first use made.
Access token, bxo_…1 hourThe Bearer token for the MCP server.
Refresh token, bxr_…30 days from when it was issuedReplaced on every use. An old one used again ends the whole connection, and so does a refresh after the person lost their role in the project.
id_token1 hourA JWT signed with ES256, with the openid scope.
  • Errors follow RFC 6749: 400 with error and error_description (such as invalid_request, invalid_grant and invalid_scope), 401 invalid_client for an unknown client, and 429 with Retry-After. An error never says whether a token was wrong or dead.
  • What a token can reach. An access token works only for the routes the tools use, in the project the person chose, with their role. Anything else gets 403 oauth_route_forbidden, a request that names a saved credential gets oauth_credentials_refused, and a token without the boxline scope gets insufficient_scope. The server gives it the directory tools: no browser_login and no credentials_list.
  • The person can end a connection at any time under Settings → Connected apps. See Connected apps.

Tools

The server has 26 tools. Each one has a page with its inputs, an example call and what it returns.

ToolWhat it does
session_createStart a new session and get its sessionId.
session_listFind your sessions again.
session_stopStop a session and keep it.
session_resumeBring a stopped session back.
session_deleteDelete a session for good.
browser_loginSign in with a saved password, in one call.
browser_navigateOpen a URL in the session's browser.
browser_clickClick an element or a point on the page.
browser_typeType text, or a saved credential.
browser_pressPress a key, a key combination, or several in a row.
browser_computerRun one computer-use action from a model, and see the screen.
browser_actDo one or more steps in the browser, in plain English or as exact actions.
browser_screenshotTake a screenshot of the current tab.
browser_readRead the current page as text.
shell_execRun a bash command in the session's shell.
files_listList files in the workspace.
files_readRead a text file from the workspace.
files_writeWrite a text file into the workspace.
files_deleteDelete a file from the workspace.
credentials_listList the saved credentials (never their values).
web_fetchFetch a page without a session.
web_searchSearch the web, with no session.
web_screenshotScreenshot a web page, with no session.
web_extractGet JSON from up to 10 pages, with no session.
web_crawl_startCrawl a site from a start URL, with no session.
web_crawl_getGet a crawl's state and its next pages.

Results longer than 40,000 characters are cut, with a note at the end. A failed call comes back marked as an error, with the API’s error code and message.

These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.