Skip to main content
Glama
README.md
# claude-connectors-mcp

An MCP server that lets Claude manage your **claude.ai connectors** from chat: list, add, update and remove
them, and set per-tool permissions (Always allow / Needs approval). It drives your own signed-in browser,
because claude.ai has no public API for this.

> **Read this first.** This tool automates claude.ai and uses its internal, undocumented calls. Anthropic's
> Consumer Terms of Service prohibit accessing the service "through automated or non-human means" except via an
> API key or where Anthropic permits it, and prohibit reverse engineering. Use it only on your own account, at low
> volume, at your own risk. It is not affiliated with or endorsed by Anthropic, and the calls can change at any
> time. Do **not** use your Claude Code login token for anything like this.

## Why a browser

`claude.ai/api` sits behind a Cloudflare browser check. Plain HTTP clients and headless browsers get
`403 cf-mitigated: challenge`, even with a valid session cookie. This tool does not try to get around that check.
Instead, every call runs `fetch()` **inside a normal Chromium window that you signed in to once**, so the request
is made by a real browser. A call takes about 3-5 seconds and the browser is closed afterwards (0 MB when idle).

## Tools

| Tool | What it does |
|---|---|
| `list_connectors` | Every connector: name, kind (custom/built-in), last recorded state, auth |
| `add_connector` | Create a custom connector (no sign-in, OAuth modes, or a request header such as `Authorization`) |
| `update_connector` | Rename / re-point / change auth. claude.ai has no edit call, so this removes and re-adds (rolls back if the add fails) |
| `remove_connector` | Delete a connector by name or id. Refuses protected names unless `force` is set |
| `check_connector` | Live reachability check of a connector's server, made from this machine |
| `get_permissions` | Per-tool permission of a connector |
| `set_permission` | Set always_allow / needs_approval for all or some tools |
| `fix_all_permissions` | Put every tool that claude.ai currently asks about back on Always allow (`dry_run` available) |

The recorded calls are documented in [API.md](API.md).

## Requirements

- **Linux only** (it uses `fcntl` and Wayland/X11 browser flags). Tested on Ubuntu x86_64 and Debian arm64; Windows is not supported and macOS is untested
- Python 3.10+ and `pip install -r requirements.txt` (Playwright; the browser itself is your system Chromium or
  Chrome, no download needed)
- Chromium or Chrome, and a desktop session (Wayland or X11) to show the window
- Node.js 18+ for the small bearer front door (`bearer-proxy.js`)
- A way to publish an HTTPS URL for the server (a Cloudflare Tunnel, a reverse proxy, ...)

## Install

```bash
git clone https://github.com/febin-micheal/claude-connectors-mcp && cd claude-connectors-mcp
pip install -r requirements.txt

# 1. sign in to claude.ai once, in the tool's own browser profile, then close the window
./start-browser.sh

# 2. check it works (a window opens for a few seconds)
python3 -c "import ccore, json; print(json.dumps(ccore.list_connectors(), indent=2))"

# 3. run the server (127.0.0.1 only) and its bearer front door
mkdir -p ~/.config/cc-mcp && chmod 700 ~/.config/cc-mcp
openssl rand -hex 32 | tr -d '\n' > ~/.config/cc-mcp/proxy-secret && chmod 600 ~/.config/cc-mcp/proxy-secret
python3 server.py 8781 &
node bearer-proxy.js 8782 8781 ~/.config/cc-mcp/proxy-secret &
```

Publish `http://127.0.0.1:8782` as an HTTPS URL, then add it in claude.ai (Settings, Connectors, Add custom
connector) with **sign-in: none** and a request header `Authorization: Bearer <contents of proxy-secret>`.
Systemd examples are in [`systemd/`](systemd/).

Once one instance is connected, it can add further instances itself with `add_connector`.

## Configuration

| Variable | Default | Meaning |
|---|---|---|
| `CC_PROFILE` | `~/.cc-mcp/browser-profile` | Browser profile that holds your claude.ai login |
| `CC_CHROME` | auto-detected | Path to Chromium / Chrome |
| `CC_OZONE` | `wayland` | `wayland` or `x11` (X11 needs `DISPLAY` set) |
| `CC_PROTECTED_NAMES` | `cc` | Comma-separated connector names `remove_connector` and `update_connector` refuse to touch |
| `CC_SERVER_NAME` | `cc` | Name the MCP server reports |
| `~/.config/cc-mcp/keeper-exclude` | none | One connector name per line that `fix_all_permissions` and the keeper skip |

## How it behaves

- One browser at a time (a lock file), **one tab only**: it reuses the window's single tab, loads the tiny
  `robots.txt` page and makes the calls from there. It closes extra tabs in its own profile and never touches
  your normal browsers.
- If a window with the tool's profile is already open (for example the one you signed in with), it attaches to it
  and leaves it open; otherwise it starts its own and quits it when done.
- Typical memory while a call runs is a few hundred MB; nothing runs between calls.

## Permissions fall back, and the keeper

claude.ai stores "Always allow" under a key built from each tool's current definition. When an MCP server changes
a tool's definition, or adds a tool, that tool goes back to "Needs approval", and a long chat can stall waiting for
a click. `fix_all_permissions` re-applies Always allow to everything that fell back. `keeper.py` runs the same fix
from a timer (`systemd/cc-keeper.*`).

The keeper is **optional and off by default**. It makes about 40 requests per run, so a short interval is a
conspicuous automated pattern. If you use it, use a long interval. Consider leaving this server's own write tools
(`add_connector`, `update_connector`, `remove_connector`) on "Needs approval".

## Security notes

- `server.py` has no login of its own. It listens on `127.0.0.1` only and relies on the bearer front door for access.
  Anyone who can call it can add and remove your connectors, so keep the secret private and never expose port 8781.
- While the tool's browser window is open, its debug port (`--remote-debugging-port`, bound to `127.0.0.1`) lets any
  local process drive your signed-in claude.ai session. The tool's own windows live only for a few seconds; a window
  you leave open from `start-browser.sh` stays reachable until you close it.
- `check_connector` sends an MCP `initialize` request to each connector's registered address from the machine that
  runs the server. It follows redirects. That is fine for your own connectors; keep untrusted callers away from the server.
- The server log (`~/.local/state/cc_mcp.log`) records tool names, connector names and URL **hosts** only. Header values
  and URL paths are never logged.
- Ids are accepted only as 8 or more hex characters (as shown by `list_connectors`, a full id with dashes also works),
  so a short typo can never match a different connector.

## Limits

- `state` in `list_connectors` is only what claude.ai last recorded; connectors that use a stored header show
  `unreported` until used. Use `check_connector` for a live check.
- `update_connector` removes and re-adds, so it is **not atomic**: if the process is killed between the two steps the
  connector is gone. It rolls back after a failed add, except for connectors that use a stored header. It resets the
  connector's tool permissions; OAuth sign-in is finished in claude.ai Settings; stored header values can't be read
  back, so updating a header connector needs the value again.
- Blocking a tool ("Blocked") and Connect / Disconnect are not covered.
- If Cloudflare shows a check in the window, pass it once by hand; calls fail with a clear message until then.

## Tests

`python3 tests/test_core.py` (or `pytest`) runs the checks that need no browser: install modes, URL validation,
permission parsing, id matching and log masking.

## Files

`ccore.py` (the calls) · `server.py` (MCP server) · `keeper.py` (optional) · `bearer-proxy.js` (front door) ·
`start-browser.sh` (sign-in helper) · `API.md` (recorded calls) · `systemd/` (example units) · `tests/`

## License

MIT, see [LICENSE](LICENSE).

Maintenance

ActivityMaintained
ResponsivenessNo issues