docs/custom-mcp-servers.md
Rama main · réplica de lectura del repositorio original.
# 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`, `<token>`)
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*. While that test runs, the row says it's off
until it passes. One that doesn't answer stays off and says so plainly: *Saved,
but off: the test failed*, with the reason, and that bots can use it once it's
on. 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. A click on a server's switch in the
moment after a passing test turned it on keeps it on (*it passed its test a
moment ago*): it was aimed at the Off you saw, never at switching it straight
back off.
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.
A server you run as a command keeps running between a bot's tasks. A bot that
changes the server's code reloads it with `mcp_server` action `restart`, which
keeps the server's saved keys and sign-in; its next task starts the new code.
Turning the server off and on does the same. Removing it deletes its saved
keys, and a key the person entered on a card works once, so they would have to
enter it again.
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.
### Agentcard
In Plugins, find **Agentcard** beside Whop and click **Connect**. The card uses
Agentcard's official hosted MCP endpoint, `https://mcp.agentcard.sh/mcp`, and
opens Agentcard's browser sign-in with your own Agentcard account. No API key
is needed. After sign-in and tool discovery it enables the connection, and
**Disconnect** and **Bot access** work as they do for Whop.
OpenMausBot owns no cards, approvals or orders here. Agentcard keeps your cards
and handles purchase approvals; OpenMausBot stores no card details. A bot that
can use Agentcard can call all of its tools, including the ones that buy and
the ones that show card details, so choose which bots get it under **Bot
access**. Existing custom entries at the official HTTP endpoint are recognized
rather than replaced.
### Email, phone numbers and calls for your bots
Four cards give bots their own email, numbers and calls on accounts you own,
so each service bills you directly and OpenMausBot pays for none of it.
| Card | Official endpoint | Connects with |
| --- | --- | --- |
| **AgentMail**, email inboxes | `https://mcp.agentmail.to/mcp` | Browser sign-in |
| **AgentPhone**, US and Canadian numbers, texts and calls | `https://mcp.agentphone.ai/mcp` | Browser sign-in |
| **Vapi**, phone calls | `https://mcp.vapi.ai/mcp` | A private API key from the Vapi dashboard |
| **Telnyx**, numbers in many countries, texts and calls | `https://api.telnyx.com/v2/mcp` | An API key from the Telnyx portal |
Vapi and Telnyx have no browser sign-in, so their cards take a pasted key. The
key goes in the server's `Authorization: Bearer` header, never in its
address, on a server that stays off until its tools load with that key; a
refused key leaves it off and says why. Like every header value it is never
shown again. **Disconnect** on a key card deletes the server and its key.
Placing a call, buying a number and every Telnyx action (Telnyx reaches its
whole API through one tool) follow **Sending on your behalf**, the same as
sending a message. Choose which bots get each card under **Bot access**. To
have a bot answer new mail, texts or calls, add a webhook for that bot in
**Automations** and paste its address into the service.
### 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. A server that lists its tools before you sign in but refuses
tool calls the same way (an HTTP 401 pointing at those sign-in details) shows
**Sign in** too. Test first calls a tool name no server has. If the server
answers that without asking who is calling, as Gmail's MCP server does, Test
calls one of the server's own tools once, with no arguments: only a tool the
server marks read-only and not destructive, and that requires arguments, so it
has nothing to work on. Background checks never do this.
- **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:<port>/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 <your key>` — 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
`<data>/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/<bot>/<conversation>`) 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/<bot>`) or
its conversation folders is that bot's own: it starts only for that bot, and
the `config.json` entry says so with `"bot": "<bot id>"`. 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 `<data>/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 searched only on API models.** On other engines a
command server with hundreds of tools is still listed whole.
API-model bots take at most 128 tools in one turn, and every server of the
turn shares them with the built-in tools. When a bot's servers offer more,
the turn lists as many whole servers as fit, in the order they are set up
after the built-ins, and reaches the rest through one tool search of three
tools (`mcp_search_tools`, `mcp_describe_tool`, `mcp_call_tool`) that count
toward the 128. No server is left out for the limit; the chat says in one
line which servers were reached through the search. The bot's MCP servers
card in Access shows the last turn's count against the limit and those
servers, and a server's **Test** says when it alone has more tools than the
limit, or half of it or more.
### 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. On a Cloud whose bots run in their box, bots reach a URL server
through the box's credential broker, and so does Test (docs/bot-box.md). 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 `<cwd>/.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`. A command entry may
say `"type": "stdio"`, as other MCP clients write it; it changes nothing.
`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. Where bots
are boxed, a command server is tested in the box, as a bot's turn starts
it, and its configured variables pass the box's allow-list there, in the
test and in an API-model bot's turn alike. 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.