Reflex speaks the Model Context Protocol, so any MCP server
— local (stdio) or remote (streamable-HTTP / SSE) — becomes a set of tools inside a session. Every
tool a connector exposes is registered as reflex tool named <server>__<tool>, and like every
other Reflex tool each call passes through the TypeSafe reflex gate (bash / edit / write
gating rules apply), so a connector that can send mail or delete a Jira ticket is still subject to
"ask before destructive / external side effects."
There are two ways to add a connector:
reflex connect <preset>— one command for the official, vendor-hosted remote MCP servers. No endpoints, no transport details, no hand-written JSON. This is the recommended path.- Edit
~/.reflex/mcp.jsondirectly (or usereflex web → Settings → Connectors) for anything else: a private stdio server, a self-hosted remote, a community server, etc.
Both write to the same file (~/.reflex/mcp.json, mode 0600), and both show up in /mcp inside a
session.
reflex connect # list presets, show which are enabled
reflex connect <id> # enable a connector
reflex connect remove <id> # disable + delete a connector| id | service | auth | transport |
|---|---|---|---|
gmail |
Google Gmail | OAuth | remote, via mcp-remote |
slack |
Slack | OAuth | remote, via mcp-remote |
atlassian |
Atlassian Jira + Confluence | OAuth | remote, via mcp-remote |
linear |
Linear | OAuth | remote, via mcp-remote |
linear-key |
Linear | API key | streamable-HTTP (no bridge) |
--readonly/--ro— where the service offers a read-only endpoint (Linear), use it. The token can never reach write APIs.--key <token>— forapi-keypresets, provide the token inline instead of prompting. Equivalent to exporting the preset's env var (e.g.LINEAR_API_KEY).
# OAuth remotes — a browser tab opens on first connect, token cached in ~/.mcp-auth/
reflex connect gmail
reflex connect slack
reflex connect atlassian
reflex connect linear
# read-only Linear
reflex connect linear --readonly
# Linear with a personal API key — no browser, no npx, talks streamable-HTTP directly
reflex connect linear-key --key lin_api_xxx
# or: export LINEAR_API_KEY=lin_api_xxx && reflex connect linear-keyAfter enabling, start a session and run /mcp to see the connector status and the tools it
loaded. The footer shows 🔌 n mcp (or 🔌 n mcp · k failed if a server didn't start).
Reflex's built-in MCP client speaks stdio (spawned server) and streamable-HTTP (fetch +
Mcp-Session-Id). It does not implement the OAuth 2.1 + dynamic client registration flow
that the official remote MCP servers require. Rather than ship a partial OAuth client, Reflex
bridges those remotes through the mcp-remote package:
reflex session ──stdio──▶ npx -y mcp-remote <url> ──HTTPS+OAuth──▶ vendor MCP server
│
└─ first run: opens browser, caches token in ~/.mcp-auth/
So an OAuth preset's server config is simply:
{
"gmail": {
"command": "npx",
"args": ["-y", "mcp-remote@latest", "https://mcp.google.com/gmail/mcp"],
"enabled": true
}
}Requirements: npx (ships with Node), a browser for the one-time consent, and write access to
~/.mcp-auth/ for the cached token. After the first run, sessions are silent.
The linear-key preset is the exception: Linear's MCP server also accepts a personal API key
as a bearer token over plain streamable-HTTP, so Reflex talks to it directly — no npx, no
browser, no bridge process:
{
"linear-key": {
"url": "https://mcp.linear.app/mcp",
"headers": { "Authorization": "Bearer lin_api_xxx" },
"enabled": true
}
}These are the official, vendor-hosted endpoints the presets target (defined in
src/extensions/mcp/presets.ts):
| service | endpoint |
|---|---|
| Gmail | https://mcp.google.com/gmail/mcp |
| Slack | https://mcp.slack.com/sse |
| Atlassian | https://mcp.atlassian.com/v1/sse |
| Linear | https://mcp.linear.app/mcp |
| Linear RO | https://mcp.linear.app/mcp/readonly |
Verified: Linear's endpoint and OAuth flow are confirmed against Linear's docs (
https://linear.app/docs/mcp). Slack and Atlassian return302/401to an unauthenticated request, i.e. alive and OAuth-gated, consistent with their published remote MCP servers. The Gmail endpoint is the commonly-cited one; Google's own MCP docs were not reachable from the build environment, so treat it as the likely-but-unverified URL and adjust inpresets.tsif it changes.
Anything not in the presets list is added directly to ~/.reflex/mcp.json:
{
"servers": {
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"enabled": true
},
"my-api": {
"url": "https://my-host/mcp",
"headers": { "Authorization": "Bearer ..." },
"enabled": true,
"tools": ["search", "fetch"]
}
}
}Supported fields per server (McpServerConfig):
command+args+env+cwd— stdio server (spawned lazily at session start).url+headers— streamable-HTTP / SSE remote.enabled— setfalseto keep the config but skip the server (defaulttrue).tools— optional allowlist; only these tools are registered.
You can also manage these from reflex web → Settings → Connectors.
- On
session_start, Reflex loads~/.reflex/mcp.jsonand connects everyenabledserver (lazy: stdio servers are spawned, HTTP servers are initialised withinitialize+tools/list). - Each tool the server exposes is registered as
reflextool<server>__<tool>. The tool's input schema is passed through, so the LLM sees and fills real parameters. - When the model calls one, Reflex runs it through the reflex gate first — the same Jev
questions as for
bash/edit/write. A connector call that sends an email, deletes a Jira issue, or posts to Slack is therefore subject to your risk appetite: low-risk reads auto-allow, external/irreversible writes ask. - Tool output (text + images) is returned to the model. Errors surface as failed tool calls.
Run /mcp in a session to see status, tool lists, and any connect failures.
| file | role |
|---|---|
src/extensions/mcp/client.ts |
minimal MCP client (stdio + streamable-HTTP), config load/save |
src/extensions/mcp/presets.ts |
the built-in connector presets + endpoint constants |
src/extensions/mcp/connect.ts |
reflex connect CLI (enable / remove / list) |
src/extensions/mcp/index.ts |
session wiring: connect all, register tools, /mcp command |
npxnot found — install Node ≥ 22.19 (it ships withnpx).- OAuth tab never completes — check
~/.mcp-auth/is writable and you're not headless; the bridge needs a browser for the first consent. Re-runreflex connect <id>to retry. 🔌 n mcp · k failed— run/mcpto see the per-server error. For stdio servers it's usually a missingcommand/package; for remotes, a stale token (delete~/.mcp-auth/<server>and reconnect) or a wrong endpoint.linear-key401 — the key is wrong, or you hit a--readonlyendpoint with a token that lacks read scope. Regenerate at Linear → Settings → API.