# Bring your own MCP servers Open **Plugins → MCP servers → Add server** to give your bots tools from an MCP server you trust. Paste what you were given into the one box: the server's link, a `claude mcp add …` or `claude mcp add-json …` command, an `npx`, `uvx` or `docker run` line, or an `mcpServers` JSON block. OpenMausBot works out the name, the connection, the headers and the environment, and asks only for what the paste left out: a value that was a variable from your terminal (`$CF_ID`, `${CF_ID}`) or a placeholder (`YOUR_API_KEY`, ``) is never saved as written. **Edit details** shows the whole form: - **Connect to a URL** — a remote server: its address and, if it needs a token, a header row (`Authorization`, with the value `Bearer …`). Most servers speak **Streamable HTTP**; pick **SSE** only for an older server that documents the `/sse` endpoint. - **Run a command** — a local stdio server: the executable, each argument on its own line, and its environment variables as name and value rows. **Add** saves the server, connects to it (it starts the command or reaches the address, completes the MCP handshake and lists the tools) and turns it on when it answers: *Connected: 12 tools*. One that doesn't answer stays off with the reason. When it refused the keys (HTTP 401 or 403), the line names the headers to check and **Fix the keys** asks for just their values, then tests again; when it couldn't be reached or timed out, **Try again** tests again. One that wants you to sign in opens its sign-in page. Compatible bots get it on their next task; no app restart is needed. On a Team Cloud everyone on the team can use a server an Admin adds; **Only for me**, offered for a server with one key, keeps that key as the Admin's own. Header and environment values are write-only: typed like passwords, never shown again, and a saved one left blank is kept. Tokens for URL servers go in headers, never in the address. **Behind Cloudflare Access or another gateway:** add the headers the gateway checks (for Cloudflare Access, a service token's `CF-Access-Client-Id` and `CF-Access-Client-Secret`), or paste your `claude mcp add --transport http … --header "…"` command and fill in the values it took from your terminal. ### Whop In Plugins, find **Whop** alongside Gmail, Slack and the other apps and click **Connect**. The built-in card uses Whop's official hosted MCP endpoint and opens Whop's browser sign-in; no API key, local CLI or Composio connection is needed. After successful sign-in and tool discovery it enables the connection. A cancelled or failed first setup remains off and can be retried without adding another server. Open **Bot access** on the Whop card to reach each bot's existing Access settings. Bots set to use every enabled MCP server inherit the connection; bots with an explicit selection must include its server name. The next task uses the selection. Whop also appears in the **Connected** filter. **Disconnect** disables the connection and signs out; the saved entry remains available to reconnect. Existing custom Whop entries at the official HTTP endpoint are recognized rather than replaced. Whop currently requests admin access across businesses your Whop account can manage, and its own consequential-action confirmations still apply. See the [official Whop MCP documentation](https://github.com/whopio/whop-mcp-server). This is an optional connected tool, not a change to OMB's subscription billing. ### Servers that ask you to sign in Some URL servers take no token; they want you to sign in with your browser (OAuth). When **Test** gets an HTTP 401 that points to the server's sign-in details (RFC 9728 protected-resource metadata, then the authorization server's own metadata), the server shows **Needs sign-in** and a **Sign in** button. Sign in opens the provider's page in your browser, which returns to OpenMausBot when you approve. The server then shows **Signed in**, and Test lists its tools. - **Which servers work.** The authorization server must support PKCE with S256. If it lets apps register themselves (dynamic client registration), OpenMausBot registers a fresh app for each sign-in. If it only accepts an app registered in advance, add that app as described below. A 401 without sign-in details keeps the old message: check the address and headers. - **Where you can sign in.** Anywhere you use OpenMausBot: on the computer it runs on, or in a browser on another computer (My Cloud, a hosted workspace, a server of your own). See below. - **Bots never sign in.** A server that needs sign-in is left out of every turn until someone signs in from this page. A signed-in server gets `Authorization: Bearer …` in place of any `Authorization` header you set, so every engine that reaches URL servers can use it. - **Tokens are kept apart.** They live in `~/.openmausbot/mcp-oauth.json` (owner-only), never in `config.json` or an API response, and are left out of workspace backups. They belong to the server's address: changing the address or removing the server drops them. **Sign out** revokes them when the server offers that, then forgets them. - **Refresh.** A token is refreshed shortly before it expires. If the server refuses the refresh, the server goes back to **Needs sign-in**. #### Signing in from another computer On the computer OpenMausBot runs on, your browser returns to it at `127.0.0.1`. In a browser on another computer that address is your own computer, where OpenMausBot is not, so sign-in comes back another way: - **Over https (My Cloud, a hosted workspace, a server behind https).** When the server lets apps register themselves, your browser returns to the address you are using OpenMausBot at, at `/mcp-oauth/callback`, and the tab says **Signed in. You can close this tab and return to OpenMausBot.** Nothing to copy. Whop works this way. The address is the server's public address (a Cloud or hosted workspace's own, a connected domain, or `OMB_PUBLIC_URL`) when you are on it, or the https address your browser is on when none is set. If you reach the server at an https address it does not list, sign-in returns to its listed public address instead. - **Otherwise, paste one address.** For an app registered in advance (it always returns to its registered `127.0.0.1` address), a server that will not register OpenMausBot for your https address, or a server reached over plain http, the sign-in panel says: *After you approve, your browser shows a page that can't load. Copy that page's full address and paste it here.* Copy the **entire URL from the address bar**, paste it into **Redirect URL**, and choose **Complete sign-in**. The page that can't load is expected: it refers to your computer, while OpenMausBot is on the server. No port forwarding is needed. On the computer OpenMausBot runs on, the same paste step is under **Signing in from another computer?** for an SSH tunnel or a browser elsewhere. **Open sign-in page** reopens the approval page if your browser blocked the first attempt. Keep the redirect URL private. Complete the flow in the same OMB browser/session that started it, within five minutes. Cancellation or logging out ends the pending flow; a URL cannot be reused, and a return to the https address works once, for that sign-in only. #### Servers that need an app registered in advance Corporate identity providers (Microsoft Entra ID, for example) and some hosted servers do not let apps register themselves. Register an app with the provider, then edit the server and fill in **Sign-in app**: - **Client ID** of that app. With a client ID set, OpenMausBot signs in as that app and never registers one. - **Client secret**, only for a confidential app. It is write-only like a header value: kept in `config.json`, never shown again, and sent only to the authorization server's token endpoint (HTTP Basic, or in the form when the server lists only `client_secret_post`). It is never copied into `mcp-oauth.json`. Leave it blank to keep the saved one; **Remove saved secret** drops it. - **Scopes**, separated by spaces. They replace the scopes the server advertises; add `offline_access` when your provider needs it to issue a refresh token. After you save, the editor shows the redirect URI to register with the app: `http://127.0.0.1:/mcp-oauth/callback`, where the port is fixed for the server's address. Sign-in with a registered app always uses that port; if another program holds it, sign-in stops and says so. Changing the client ID or the scopes signs the server out; a new secret for the same app does not. ### Example: give your bots web search A good first URL server is You.com's search server, because the free profile needs no token at all. Add a URL server with the address ``` https://api.you.com/mcp?profile=free ``` leave the headers empty, and press **Add** — the handshake completes and the server advertises `you-search` (web search) and `you-discover` (a directory of other MCP servers). The free profile is read-only and rate-limited to 100 searches a day; there is no key, so none is stored. It turns on as soon as it answers, and every compatible bot can search the web on its next task — `you-search` results arrive like any other tool result, through the approval cards. If the free limits are too small, [you.com/platform](https://you.com/platform) issues an API key with a higher quota and the `you-contents` tool for full-page extraction. Add the same address without `?profile=free` and one header, `Authorization` with the value `Bearer ` — the key is kept write-only like every other header value. `you-research` (multi-step cited reports) is served by its own dedicated server, `https://api.you.com/mcp/research` — add it the same way, with the same header. ### Import and choose tools per bot The paste box also takes an `mcpServers` JSON block, a VS Code `servers` block, a server-name map, or a single entry — commands and URL servers alike, in the shape Claude Code, Cursor and Claude Desktop write — and several `claude mcp add` commands, one per line. Each server is added, connected and turned on when it answers, like a single one; a name already taken gets a number (`notes-2`), so nothing here is overwritten. An `enabled` in the paste is ignored. The desktop app's first-run tour does the same for you in its **Bring what you already use** step (`server/setup-import.ts`). On the computer running OpenMausBot, and only for the desktop app's own window there, it reads the `mcpServers` of Claude Code (only the top-level key of `~/.claude.json`), Claude Desktop, Cursor (`~/.cursor/mcp.json`) and Gemini CLI (`~/.gemini/settings.json`), and the `[mcp_servers.*]` tables of Codex's `config.toml`. Each server goes through the import parser, switched off; one already here by command or address is left alone, a different server under a name already taken is skipped and named in the result, a name OpenMausBot keeps for itself (`browser`, `computer`, `data`…) gets an `mcp-` prefix, a trailing comma is accepted, and the list stops at its limit. One entry or file it cannot read drops only itself, and a kind that cannot be saved is reported as failed while the other still comes in. With **Also use my Claude Code MCP servers** on, Claude Code's own servers are not offered: Claude bots already run them. Token variables (Codex `bearer_token_env_var`) are not resolved: add the token as a header when you test the server. The step also brings in the person's own skills (`~/.claude/skills`, `~/.codex/skills`). Each one is copied once, read-only except on Windows, into `/imported-skills` (`server/imported-skills.ts`), and every bot uses that copy on every engine that works with files: the bot the tour sets up, and any bot made later with New bot, the API, a template, a Chief of Staff team or a package. Each turn lists them after the bot's own skills, with a budget of their own, links them into the task folder's `.claude`, `.agents` and `.grok` skills, and runs them as `/name`. A bot's own or library skill of the same name wins; **Bot Settings → Skills** shows the rest as coming from this computer, with a switch for that bot. A skill the scan flags (a download piped into a shell, invisible characters, a long encoded blob, in SKILL.md or any file beside it) comes in switched off for every bot: read it there, then switch it on for that bot; it is off again if it is brought in with other files. Inside a skill folder it copies text files only, never links, files with another hard link, credential or session files (such as `token.json`, `session.json`, `password.txt`, `secrets/`, `*.env`) or a file holding a provider key, and counts what it leaves out. A skill folder that is a link is followed only while it stays in the home folder, out of the folders macOS guards (Documents, Desktop, Downloads, Library and the media folders) and out of the tools' own folders outside their skills. A copy whose files change afterwards is left out until it is brought in again. A turn whose engine already loads the folder a skill came from leaves the copy out, so nothing is listed twice: Claude Code with **Also use my Claude Code skills** on, and Codex on the person's own `CODEX_HOME`. The copies travel with backups and Copy to My Cloud. The step never opens credential, session or history files, and it is skipped when there is nothing new. Open a bot’s **Tools → Access → MCP servers** to narrow the enabled global servers offered to it. Existing bots keep all enabled global servers until you choose a subset; switching them all off means none. **Use every enabled server** restores the default, including future additions. Stop all of that bot’s running turns before changing this selection; the next direct or channel turn gets the new list. The list does not filter project-local `.mcp.json` files and is not a shell sandbox. Individual tool approvals depend on the engine and approval mode. ### A bot's own servers, and servers that don't start A bot can add a command server with `add_mcp_server` (saved switched off, as always). A conversation folder (`task-workspaces//`) goes when its conversation does, so when the command, its arguments or its environment point into one of the bot's conversation folders, those files are first copied into the bot's own folder under `tools/`, and the server runs from the copy. Where bots are boxed (a Cloud), each bot's turn sees only its own folders. There a server whose files are in the bot's own folder (`workspaces/`) or its conversation folders is that bot's own: it starts only for that bot, and the `config.json` entry says so with `"bot": ""`. A command that uses another bot's files is refused there. On a desktop every bot reaches every folder, so such a server stays every bot's. Servers you add in Settings stay every bot's. The first time bots run boxed after an update, a server no bot owns whose files are in the folders of one existing bot becomes that bot's own the same way, once, and you hear about it once: "The Blender tool only works for Iris, so it's off for your other bots." Each enabled command server is also started once for each bot that uses it, the way that bot's AI starts it (in the bot's box on a Cloud, from its own folder), when it is added, changed or turned on, before a bot's first turn with it, and again whenever the bot's AI reports it did not connect. A server that doesn't start for a bot is left out of that bot's turns, so its failure doesn't follow every session or tool search; you hear about it once, the server's row in Settings says who it is off for, and it is tried again on its own every half hour (one that started is checked again every six hours). The checks are kept in `/mcp-health.json`. ### Servers with hundreds of tools Some servers offer more tools than a model can usefully read at once: Whop's lists 425. Claude Code, and Codex signed in with its own account, look tools up as they need them, so they get every server whole, as before. Other engines would read every tool's description on every message, and the API-model engines stop at 128 tools. So for bots on API models, on Pi, and on Codex with a ChatGPT plan, a big URL server shows up as three tools: one that searches its tools, one that reads a tool's exact inputs, and one that runs a tool by name. The search tool says what the server covers, area by area. A server counts as big when it has more than 40 tools, or more than about 100,000 characters of tool descriptions (roughly where Claude Code starts searching on its own). Smaller servers are listed as they always were. There is nothing to configure. A bot's tool selection still holds: it can find and run only the tools you chose for it, and an approval card names the tool being run, never the search. A big URL server also gets 30 seconds to start on a bot's turn, like the Test button gives it; command servers keep eight. Known limits: - **Codex with a ChatGPT plan, on Approve for me, asks about every call.** Codex lets a server's read-only tools run without asking, but here every tool runs through the one tool that runs them all, which cannot carry each tool's own read-only hint. Each Whop call shows a card, naming the tool. - **Command servers are not searched yet.** Only URL servers are. A command server with hundreds of tools is still listed whole, and API-model bots refuse more than 128 tools in one turn. ### Which engines reach which servers | Server | Claude Code bots | Codex bots | ACP bots (Cursor, Grok, Kimi, …) | API-model bots | | --- | --- | --- | --- | --- | | Command (stdio) | yes, through the result gate | yes | yes | yes | | URL, Streamable HTTP | yes | yes | yes | yes | | URL, SSE | yes | yes | yes | yes | Every engine except Claude Code reaches URL servers through OpenMausBot's own connector, the same one **Test** uses, so a server that passes Test works in chat too. Some servers refuse any handshake field they do not know, and the engines' own MCP clients add such fields: a bot connecting by itself got "Tool not found" for every tool of a server that passed Test. Claude Code still connects by itself, since its handshake has only fields the MCP spec defines. Through the connector, a server's own error for a call (a missing argument, an unknown id) reaches the bot with its code and words, so it can fix the call; the server's address and header values are removed from it first, and connection failures stay generic. A tool call may take as long as the engine allows (the connector adds no deadline of its own, except for the tool search used by bots that cannot search tools themselves, two minutes). An HTTP 401 tells the bot to send the message again (a sign-in is refreshed when a message starts) and, if that fails, to sign in again or check the server's header values in Plugins → MCP servers. ## What a Claude bot sees, and the "Also use my Claude Code MCP servers" switch A bot on the Claude engine gets the tools and instructions its owner gave it: the servers above, its integrations (computer, browser, agents, phone), and its own project's `/.mcp.json`. By default it does **not** inherit this machine's Claude Code setup — the MCP servers and claude.ai connectors in your user or local Claude config, your skills and agents, your hooks, and your personal `~/.claude/CLAUDE.md`. Those were being mounted into every turn of every bot (one measured desktop added 407 tools, ~10k tokens per model call) and were reachable by the bot. Codex bots, by contrast, have always read the MCP servers in `~/.codex/config.toml`, which is why the two engines looked different. If you want Claude bots to see your own Claude Code MCP servers too, switch on **Also use my Claude Code MCP servers** at the top of Plugins → MCP servers. With it on, every Claude bot also loads the servers and connectors from your Claude Code config on every message; skills, hooks and the personal `CLAUDE.md` still stay out. More tools means more tokens per message, so keep it off unless you need those servers — the recommended way to give a bot a server is still this page or the bot project's `.mcp.json`. **Also use my Claude Code skills**, just below it, gives Claude bots the skills in your own `~/.claude/skills` and nothing else from your setup. OpenMausBot keeps one link to that folder (in its data folder, `claude-user-skills/`) and passes it with `--add-dir`, so each skill loads as it is on disk, scripts and reference files included, and Claude Code picks up your edits within the session. Your hooks, MCP servers and `CLAUDE.md` stay out, so a bot with a tool selection keeps working. In `config.json` it is `"features": { "claudeUserSkills": true }`. Skills in a bot project's own `.claude/skills` load either way. The link folder is left out of backups and removed when the switch goes off; a skill brought in from `~/.claude/skills` is left out of a Claude turn while the switch is on, since Claude already has the original. The switch drops the CLI flag `--strict-mcp-config` (Claude Code 1.0.60+) while keeping `--setting-sources project` (1.0.122+). The environment variable `OMB_CLAUDE_INHERIT_USER_CONFIG=1` on the OpenMausBot process remains the full escape hatch back to the old launch: it restores everything, for every Claude bot, until you remove it. The harness also picks the session's compaction window with `--autocompact` (2.1.122+). OpenMausBot reads `claude --version` whenever it lists engines (app load, the Engines page, after an update) and only passes each flag to a CLI that accepts it, so an older CLI keeps working — without the controls it predates — and the Engines page shows an update notice with the exact command. `claude update` clears it. ## When your organization manages MCP servers If this computer is connected to an organization (Settings → Organization) and its Admin turns off custom MCP servers, only servers whose name or address is on the organization's approved list reach bots. Other configured servers stay in your list, marked **Managed by** your organization, but bots do not get them, and the "Also use my Claude Code MCP servers" switch has no effect. **Add server** accepts only approved servers. Nothing is written to `config.json`; disconnecting the organization restores the list as you configured it. With no organization connection, none of this applies. Address entries are HTTPS only. The host is compared label by label, where `*` stands for one or more whole labels (`https://*.example.com/mcp` matches `https://a.example.com/mcp`, never `https://evil.test/x.example.com/mcp`), and the path separately, where `*` matches anything. Limits: a personal **Codex** engine also loads MCP servers from your own `~/.codex/config.toml`, which OpenMausBot does not filter. A server here that `config.toml` already runs, with the same command, arguments and environment or the same address and headers (as one brought in from there is), is not mounted a second time on a turn without a tool selection. An organization that must block those can allow only company models, or leave personal Codex off its engine list. Company Codex uses its own separate home, without your `config.toml`. ## Advanced: edit the file The same registry lives in `~/.openmausbot/config.json`: ```json { "mcpServers": { "notes": { "command": "npx", "args": ["-y", "@example/notes-mcp"], "env": { "NOTES_TOKEN": "…" } }, "docs": { "type": "http", "url": "https://mcp.example.com/mcp", "headers": { "Authorization": "Bearer …" } }, "corp": { "type": "http", "url": "https://mcp.corp.example/mcp", "oauth": { "clientId": "…", "clientSecret": "…", "scopes": ["api://corp-mcp/read", "offline_access"] } } }, "features": { "claudeUserMcp": false } } ``` `type` is `http` (Streamable HTTP, the default) or `sse`. `oauth` is the optional sign-in app; `clientSecret` and `scopes` may be left out. If you edit the file by hand, restart OpenMausBot. Every bot whose engine can mount custom MCP servers gets the enabled tools on its next task. ## Rules that keep this safe - **Permission cards by default.** Custom servers are never pre-approved: on Claude their tools route through the permission broker into Allow/Deny cards; on Codex they keep the on-request approval policy; ACP engines relay the agent's own permission asks. Built-ins stay pre-quieted — only *your* servers ask. - **Reserved names are refused** (`computer`, `agents`, `composio`, `browser`, `phone`, `dweb`, `ogb`, …) so a custom entry can never shadow a built-in tool surface. Names are lowercase letters/digits/`_`/`-`, max 32 chars, starting with a letter. - **Environment names are checked.** `OMB_*` and `OGB_*` are refused: Codex hands MCP children their variables from one shared environment, and those names carry OpenMausBot's own routing and bearers. `ELECTRON_RUN_AS_NODE` is accepted as `1` only — the value OpenMausBot's own proxies use — so a server bundled inside an Electron app (its binary as the command) can run as node; any other value would overwrite what the built-in proxies need. - **One bad entry never takes the fleet down.** Invalid entries are skipped with a logged reason; the rest still mount. - **Credentials are write-only in the UI.** The API returns environment and header names, never their values. Leaving an existing value blank keeps it saved; removing its line deletes it. - **Credentials stay off argv.** `env` values travel in the child environment (Codex argv carries env *names* only; Claude uses the private 0600 mcp-config file; ACP passes them in the session payload with the wire log redacted). Header values do the same: OpenMausBot's connector reads them from a private environment record (Codex names only the record's variable on argv; ACP gets it in the session payload), Claude Code from the 0600 file. They do persist as plaintext in the 0600 config file — prefer tokens scoped to the one server. Codex gives its MCP servers their variables from the same environment it runs the bot's shell commands in, so each variable a server needs there is excluded from that shell; a value you already had in your own environment, like a proxy setting, stays. - **Proxies.** Where OpenMausBot's own remote proxy connects to a URL server, an `https://` server goes through your `HTTPS_PROXY` / `HTTP_PROXY` (CONNECT), with this computer's loopback names always added to `NO_PROXY` (`[::1]` included). An `http://` server goes through your `http_proxy` / `HTTP_PROXY`, sent by the connector itself in absolute form (Node's own env-proxy switch stays off, `NODE_USE_ENV_PROXY=0`, since Node 24's fetch hangs on a plain http request sent through it); loopback and `NO_PROXY` hosts are reached directly. Proxies set only in macOS System Settings are not read. - **Testing is bounded.** A command is stopped after the handshake (or eight seconds), its output is capped, and its stderr is never sent to the UI. It inherits none of OpenMausBot's workspace or provider credentials; only the environment variables configured for that MCP server are added. A test reads at most 32 MB (Whop's tool list alone is 1.2 MB), and a URL test reports only the HTTP status of a refusal. - **Addresses are checked.** A URL server needs a full `http://` or `https://` address with no credentials in it; header names must be valid HTTP field names and values a single line. - **The result gate covers commands.** An oversized tool result from a stdio server never reaches the model whole (`OMB_MCP_RESULT_BUDGET`, 8,000 characters by default). A JSON result is *shaped*: the gate keeps the whole payload on its side, shows the model its shape (keys, array sizes, item keys) and the first records, and lists one read-only tool beside the server's own, `query_result`, that runs a JMESPath query over the held result and returns only what it selects. Answers go through the same budget, so no path returns the whole payload, and the tool is pre-allowed for Claude because it reads nothing the server has not already returned. Held results live as long as the session's MCP connection (64 MB per server, oldest first). A result that is not JSON is cut to the budget as before. A URL server's results arrive untrimmed. - `"enabled": false` parks an entry without deleting it.