Skip to main content
StefanBrain exposes a remote MCP server over streamable HTTP:
This page documents the StefanBrain product MCP, which can run StefanBrain tools and complete work. It is separate from the read-only documentation MCP at https://docs.stefanbrain.com/mcp.
Use OAuth for connector interfaces (claude.ai, Claude Desktop, ChatGPT) and for coding assistants that support MCP OAuth, such as Claude Code. Use an API key for clients that send a fixed header (Cursor, Windsurf, Codex) and for SDKs and scripts. Both bill the plan pool on MCP.

Use ask_stefanbrain

ask_stefanbrain is the main MCP tool. In your assistant, say use StefanBrain or use SB, then describe the work. Your assistant calls the tool; StefanBrain completes the turn on its servers and returns the response. You can use it to:
  • Request ad angles, copy, reviews, strategy, research, or another complete work product.
  • Send up to 10 image URLs through image_urls for JPEG, PNG, WebP, or GIF inputs.
  • Upload local images, PDFs, documents, and spreadsheets through POST /api/developers/v1/files, then pass the returned upload_... identifiers in file_ids. The upload needs an unscoped key or a key with the runs scope. A key scoped only to mcp gets 403 api_key_scope_forbidden; call stage_file instead.
  • Call stage_file when the MCP client cannot send raw HTTP. Stage a public URL or base64-encoded bytes, then pass its upload_id in file_ids.
  • Include a video or web-page URL in message; StefanBrain detects it without a separate URL field.
Follow-up asks continue the same StefanBrain chat. To start a fresh one, the assistant passes chat: "new". Uploads for file_ids expire after 24 hours. One ask accepts at most 10 attachments across image_urls and file_ids, and its message can be up to 32,000 characters. Each image URL must be a public JPEG, PNG, WebP, or GIF of 15 MB or less. Inside an ask or an Agent Run, StefanBrain never sends email or changes schedules, whichever lane pays for the turn. prepare_launch only stages a draft. Open the reply’s app_url to finish those in the app. Calling send_email directly sends immediately. Leave ask_stefanbrain.brain unset. It is for StefanBrain admins. For any other account, the call fails with an error that tells you to omit it. Use a direct tool when you need its raw result. Use ask_stefanbrain when StefanBrain should plan and complete the work. ask_stefanbrain has no JSON-schema option; it returns free text in answer. For schema-constrained JSON, start an Agent Run with output_config.format.

Read the answer

ask_stefanbrain waits up to about 55 seconds for the turn. A turn that finishes in that window returns its answer inline. Otherwise the call returns a parked result with family: "mcp_ask", a run_… run_id, and status queued or running. Poll get_job_status with that family and run_id about every 10 seconds, then call get_job_result when status is succeeded, failed, or cancelled. Set the client’s tool timeout above 60 seconds. Clients that send an MCP progressToken get progress notifications while the ask waits. Use cancel_job to stop a queued or running mcp_ask job. An ask returns answer, chat, and app_url, plus steps and produced_documents when the turn has them. A parked ask’s get_job_result returns the same fields, with status and last_error. produced_documents holds the text files the turn wrote, up to 16,000 characters each and 48,000 in total. answer repeats the steps and files for clients that read only text.

Handle a busy chat

An ask uses on_busy: "reject" by default. If the target chat has an active turn, the result is a chat_busy receipt with the active run details. Only on_busy: "reject" is supported. Poll the blocking run and retry after it finishes, or use a fresh chat. If StefanBrain is briefly at capacity when an ask starts, the call returns a retryable error (upstream_unavailable in the tool result on MCP, 429 runtime_capacity_busy with Retry-After on REST). Retry after a few seconds. Use a different session label for independent parallel work. Queue only when the ask needs the existing chat state.

Retry an ask safely

Pass idempotency_key before retrying an ask whose response may have been lost. A repeated key returns the original turn and run_id without another charge. Ask deduplication holds for the rest of the same UTC day. Keep the same project, session, or explicit chat on every retry.

Billing and transport behavior

For standard member accounts, MCP calls use the plan’s monthly pool instead of the API wallet. This applies to OAuth tokens and API keys used on MCP. Contracted partner accounts can have plan-pool traffic billed to the API wallet under their agreement. account_status does not show which lane applies; confirm it with your StefanBrain contact.
  • Use MCP for interactive work inside an assistant.
  • Use the REST API for integrations, pipelines, and automated or machine-scale traffic.
The MCP server is stateless and does not issue an Mcp-Session-Id. Each client message is one authenticated POST, so reconnecting is safe. A standalone GET request returns 405 by design; the server does not send server-initiated messages.

Connect Codex

Export the API key in your environment:
Add the server to ~/.codex/config.toml:
Keep the key out of committed configuration. The configuration above reads it from STEFANBRAIN_API_KEY instead of storing the secret in the file.

Connect Claude Code

Claude Code opens the StefanBrain OAuth flow. Remove --scope user when the server should apply only to the current project. To use an API key instead, send it as a header:

Connect Cursor

Add this to .cursor/mcp.json for one project or ~/.cursor/mcp.json globally:

Connect Windsurf

Add the server to ~/.codeium/windsurf/mcp_config.json. Windsurf uses serverUrl, not url.

Connect an OAuth client

StefanBrain operates an OAuth 2.1 authorization server for the MCP endpoint. In claude.ai, Claude Desktop, ChatGPT, or another custom-connector interface:
  1. Add a custom connector.
  2. Paste https://stefanbrain.com/api/developers/v1/mcp as the connector URL.
  3. Sign in to StefanBrain if requested.
  4. Select Allow access.
Connecting requires an active StefanBrain plan or trial. Without one, the approval page shows Developer access required, and API-key calls return 403 developer_access_forbidden. You do not need an API key, Client ID, or advanced settings for this flow. Disconnect the connector in the client to revoke its access. Access tokens last one hour and refresh automatically. Refresh tokens rotate on use and expire 60 days after issue. StefanBrain has no in-app list of connected OAuth clients. If a previously configured connector reports that it cannot register with StefanBrain’s sign-in service, remove it and add it again.

Connect another streamable-HTTP client

Most remote MCP clients accept this shape:
Header clients can send x-api-key: stefan_sk_... instead of Authorization: Bearer. Clients that only support stdio can bridge with mcp-remote:

Available tool areas

The MCP surface serves the supported developer-tool catalog. tools/list is authoritative for the tools available to the authenticated user in the current deployment. Tool availability can depend on account permissions, connector access, and deployment configuration. Do not treat this summary as an exhaustive static list.
  • Ask StefanBrain: ask_stefanbrain
  • Chats, projects, products, and brands: search_chats, read_chat, list_chats, list_projects, get_project_context, manage_projects, manage_products, and manage_brands
  • Research: web_search, web_extract, agentcore_browser, website_traffic, amazon_search, amazon_product, google_trends, reddit_search, reddit_thread, and scrape_social_comments
  • Meta: get_meta_ad reads one public Ad Library ad as an asynchronous job. meta_ad_accounts_search and meta_ads_insights are served directly and need a connected Meta account.
  • Connected accounts: Connector tools are not served directly over MCP. Ask for connector work with ask_stefanbrain, which runs a full StefanBrain turn with your connected accounts.
  • Copy, creative, and research jobs (asynchronous): review_copy, find_angles, create_images, edit_image, review_funnel, and research_shortform
  • Jobs: get_job_status, get_job_result, and cancel_job
  • Files: stage_file for a public URL or base64 payload that should become an upload_... id
  • Launch: upload_creative adds one image (a public URL, a staged upload_... id, or a file path in the chat) to your Launch library and returns its staticCreativeId; prepare_launch stages a Meta launch draft that you review and launch in the app
  • Email: send_email sends real email immediately from a fixed StefanBrain sender, to up to 5 recipients per email and 50 recipients per 24 hours
  • Account: read-only account_status. When api_wallet.reconciliation_required is true, wallet figures are provisional and REST requests billed to the API wallet are held for billing review.
  • Feedback: send_feedback tells the StefanBrain team where a tool or endpoint got in your way, and list_feedback returns your reports with the team’s replies. Both are free. See Send feedback.
The web, Amazon, Google Trends, website-traffic, and Reddit tools appear only when their provider is configured. Every tool also accepts an optional context string, up to 2,000 characters, recorded in the account owner’s audit log. Not served over MCP: StefanBrain’s knowledge corpus (knowledge_search, knowledge_read; use ask_stefanbrain instead), chat-only tools (chat_constraints, share_chat, request_connector_connect, collect_web_artifacts, capture_funnel), manage_automations (change schedules in the app), and the admin-only generate_hooks. Direct video analysis, cuts, and transcript tools are also excluded. Put a hosted video URL in an ask_stefanbrain message when the agent should read it. Handshake, tools/list, and job status, result, and cancel calls do not count against request limits. Each stage_file, send_feedback, or list_feedback call counts as a request but never draws on the plan pool or wallet. Calls that start work count and draw on the plan pool.

Poll a job

Submit tools return runId and status: "running". Call get_job_status with {"family": "<family>", "run_id": "<runId>"} plus the project and session you submitted with. When status is succeeded, failed, or cancelled, call get_job_result. For a submit-tool job, get_job_result returns a result of {kind: "files", summary, files: [{path}]} for files saved in the job’s chat, or {kind: "inline", summary, data}. A failed job carries its reason in error. Open that chat in StefanBrain, or list its files with GET /api/developers/v1/files?chat_id=… and fetch each download_url. Find the chat’s id with list_chats.

Stage a file inside an MCP client

Use one input lane for each stage_file call:
Or send local bytes when the client supports base64 tool arguments:
The result includes upload_id, filename, content type, byte count, and expires_at. Uploads last 24 hours and can be reused across asks. stage_file stages one file per call, up to 100 MB. MCP requests over 150 MB are refused with 413 request_too_large; host large files and pass url. Audio and video files are rejected by both staging lanes. Host those files and include the URL in the ask message.

Work in a chat or project

Each tool call runs in a real chat in your account. Clients that support custom headers can pin a default location:
Without either header, StefanBrain uses the account’s Developer API chat. A project header gives that project its own Developer API chat. Connector clients that cannot set headers can address work through tool arguments:
  • project accepts a proj_... id or project UUID.
  • session creates or reuses a Developer API — <label> chat inside the resolved project.
  • ask_stefanbrain.chat accepts a concrete chat id or new.
  • get_job_status, get_job_result, and cancel_job accept the same project and session used by the submit call.
Precedence is: There is no session header. Session labels use 1–128 characters from A–Z, a–z, 0–9, _, ., :, or -. Repeat the same project and session when polling a job or retrying an idempotent submit. Lookups for submit-tool jobs are chat-scoped, so a different address cannot find the job. Parked asks (mcp_ask) resolve by account, but keep the same address anyway. A poll with a wrong or unused session label also creates an empty Developer API — <label> chat. Use list_projects to find accessible proj_... identifiers. Use get_project_context to retrieve a project’s instructions, maintained digest, file roster, and relevant indexed excerpts for a query. Pass a roster file_id to get a temporary download URL for the original file. When the MCP connection is already pinned to a project, get_project_context can omit its project argument and use that pin. An inaccessible project returns 404 project_not_found. A chat and project that do not match return 409 chat_project_mismatch. A typical coding-assistant flow is:

Send feedback

The server instructions tell your assistant to call send_feedback when a StefanBrain tool or endpoint fails, returns the wrong thing, or lacks something it needs. Send one report per problem, with the tool, the error text, and the run or chat id. The StefanBrain team reads every report and replies. list_feedback returns the account’s reports, newest first, each with its status and the team’s reply. If the team replied to this account’s reports in the last 14 days, the server instructions on the next connection end with up to 3 of those replies. Each account can send 60 reports in any rolling hour. See Agent feedback for the fields, statuses, and errors.

Errors

A request-level failure returns an HTTP status and an error object with message, type, and code: A tool failure returns isError: true with the message in content and no structuredContent. Messages for invalid arguments and unknown tools begin with MCP error -32602. A busy chat is not an error. ask_stefanbrain returns status: "chat_busy" with active_run_id and hint.suggestion (wait_and_retry or use_own_chat). See Authentication for key scopes, Rate limits for limit codes, and Pricing for the difference between MCP and REST billing.