OpenConnector
by cheese-zj
README.md
# OpenConnector
One local connector hub for all your AI agents.
Today every AI product has its own connector store. You sign in to Gmail in Claude, again in ChatGPT, again in Cursor, and each one is tied to a single account. OpenConnector moves the connectors out of the agent:
- You connect your services (Google, GitHub, Slack, Notion, Linear, ...) **once**, in OpenConnector.
- You can connect **several accounts per service** and switch between them freely.
- Every agent gets **its own token** and **its own policy** (which services, which accounts, which tools, read-only or not).
- Agents talk to **one endpoint**: an MCP server (Streamable HTTP or stdio) or a plain REST API.
- Optionally, a **cheap model filters large results** before they reach the expensive agent.
Everything runs locally. Credentials stay on your machine, encrypted at rest.
```
Claude Code ─┐ ┌─ Google (Gmail, Calendar, Drive) ── work@ / personal@
Codex ───────┤ MCP / REST ├─ GitHub ── you / bot
Cursor ──────┼──────────────▶ OpenConnector ─┼─ Slack
Your agent ──┘ per-agent token ├─ Notion
+ policy + filter └─ Linear
```
## Status
Early, `v0.1.0`. The hub, the five providers, the desktop console and the CLI work and are tested against mocked APIs. No provider has been tested against its live API yet. See [Development log](#development-log).
## Quick start
Requirements: Go 1.26+, Bun 1.2+.
```bash
bun install
bun run dev
```
This opens the desktop console, which also runs the server. `bun run build` packages a `.app` and a `.dmg` into `build/`.
Headless use works from the same binary. With any argument, the binary runs as a CLI instead of opening a window:
```bash
go build -o openconnector .
./openconnector accounts connect notion --key ntn_...
./openconnector agents create claude-code --allow notion --read-only
./openconnector serve
```
### CLI
| Command | Purpose |
|---|---|
| `serve [--addr host:port]` | Run the MCP and REST server. |
| `mcp [--token T] [--url U]` | Stdio MCP proxy to the running server. The token can also come from `OPENCONNECTOR_TOKEN`. |
| `providers`, `providers configure <id> --client-id X [--client-secret Y]` | List providers. Set the OAuth client of a provider. |
| `accounts`, `accounts connect <provider> [--label L] [--key K] [--services a,b]` | List accounts. Connect one by OAuth (opens the browser) or by key. `--services` limits Google to, for example, `gmail,calendar`. |
| `accounts remove <id>`, `accounts default <provider> <id>`, `accounts rename <id> <label>` | Manage accounts. |
| `agents`, `agents create <name> [--read-only] [--allow provider[:tool,...]]...` | List agents. Create one; without `--allow` it may use everything. |
| `agents delete <id>`, `agents rotate <id>` | Delete an agent. Issue a new token. |
| `tools --agent <id>`, `call --agent <id> <tool> [json]` | Show or call what an agent sees. |
| `settings`, `settings set [...]` | Show or change the listen address, callback port and filter. |
| `audit [--limit n]` | Show recent tool calls. |
Put `--json` before the command for machine-readable output.
### Connect an agent
1. In the console, open **Agents** and create an agent. Copy its token (shown once).
2. Point your agent at the MCP endpoint:
```bash
claude mcp add --transport http openconnector http://127.0.0.1:7777/mcp --header "Authorization: Bearer oc_..."
```
For clients that only speak stdio:
```json
{
"mcpServers": {
"openconnector": {
"command": "openconnector",
"args": ["mcp", "--token", "oc_..."]
}
}
}
```
## Concepts
| Term | Meaning |
|---|---|
| Provider | An integration, such as `google` or `github`. It declares its auth method and its tools. |
| Account | One signed-in identity of a provider. A provider can have many accounts. |
| Agent | A client of OpenConnector. It has a bearer token and a policy. |
| Policy | Which providers, accounts and tools an agent may use, and whether it is read-only. |
| Default account | The account a tool uses when the call does not name one. Set per provider, and optionally overridden per agent. |
| Filter | An optional cheap model that reduces a tool result to what the agent asked for. |
### Tools an agent sees
Provider tools are exposed as `<provider>_<tool>`, for example `google_gmail_search` or `github_list_issues`. Every provider tool also accepts two optional arguments:
- `account`: the account ID, label or identity (such as an email address) to use for this call.
- `result_filter`: a plain-language instruction. When a filter model is set, the result is reduced to what matches.
Two meta tools are always present:
- `connector_accounts`: list the accounts this agent may use, and which one is active per provider.
- `connector_use_account`: switch the active account of a provider for the rest of the session.
### REST API
| Method | Path | Body |
|---|---|---|
| `GET` | `/v1/tools` | |
| `POST` | `/v1/tools/{name}` | the tool arguments as JSON |
| `GET` | `/healthz` | |
All requests need `Authorization: Bearer <agent token>`.
## Providers
| Provider | Auth | Notes |
|---|---|---|
| `google` | One-click sign-in (OAuth 2.0) | Pick any of Gmail, Calendar and Drive when you connect |
| `github` | One-click sign-in (OAuth 2.0) or personal access token | Repos, issues, pull requests, search |
| `slack` | Bot or user token | Channels, history, search, post |
| `notion` | Internal integration token | Search, pages, databases |
| `linear` | Personal API key | Issues, teams, projects |
### Connecting is one click
Click **Connect** next to Google, tick the services you want, and sign in to Google in your browser. That is all. Each account remembers which services you allowed. Agents only see a tool when one of their accounts allowed its service. Click **Add access** on an account to allow more later.
This works because a release of OpenConnector ships a built-in OAuth client. Users never open Google Cloud.
### Built-in OAuth clients (for maintainers)
A build finds its built-in clients in the first `oauth-clients.json` it reads from:
1. the path in `OPENCONNECTOR_OAUTH_CLIENTS`
2. the data directory
3. the app's `resources/` directory (copied into the `.app` by `mygo build`)
4. next to the executable
```json
{
"google": { "clientId": "....apps.googleusercontent.com", "clientSecret": "..." },
"github": { "clientId": "...", "clientSecret": "..." }
}
```
Both paths are in `.gitignore`. For a desktop app, Google and GitHub do not treat the client secret as confidential, because anyone can extract it from the app. PKCE protects each sign-in.
To create the Google client once:
1. In the Google Cloud console, create a project and enable the Gmail, Google Calendar and Google Drive APIs.
2. Configure the OAuth consent screen: app name OpenConnector, user type External, and the scopes `gmail.modify`, `calendar` and `drive.readonly`.
3. Create an OAuth client ID of type **Desktop app**. Desktop clients accept any loopback port, so no redirect URI is needed.
4. Put the ID and secret in `resources/oauth-clients.json` and run `bun run build`.
Until Google verifies the app, sign-in shows a "Google hasn't verified this app" warning, and at most 100 users can sign in. While the consent screen is in "Testing" status, refresh tokens expire after 7 days, so switch it to "In production" even before verification. Gmail and Drive scopes are restricted, so verification also needs a yearly security assessment. Calendar alone only needs the lighter sensitive-scope review.
A user can still use their own client: open **Advanced** on the provider card. Tokens always refresh with the client that issued them, so switching clients does not break connected accounts.
## Architecture
```
main.go desktop app (mygo) or CLI, chosen by arguments
internal/core shared types and interfaces: Provider, Store, Filter, Registry
internal/store JSON state + AES-GCM encrypted credentials + audit log
internal/auth OAuth 2.0 loopback flow (PKCE), token refresh, API key clients
internal/gateway policy checks, account resolution, sessions, filter, audit
internal/filter OpenAI-compatible and Anthropic filter backends
internal/server MCP server (Streamable HTTP), REST API, stdio proxy
internal/console admin service used by the desktop UI and the CLI
internal/app wires everything together for one data directory
internal/cli command-line interface
internal/providers/* one package per provider
src/ desktop UI (TypeScript, Vite), calls the console through mygo bindings
```
### Data
The data directory is `~/Library/Application Support/OpenConnector` on macOS, `$XDG_CONFIG_HOME/openconnector` on Linux and `%AppData%\OpenConnector` on Windows. Set `OPENCONNECTOR_HOME` to use another directory.
| File | Content |
|---|---|
| `state.json` | accounts, agents (token hashes only), provider configs without secrets, settings |
| `secrets.enc` | credentials and client secrets, AES-256-GCM |
| `audit.jsonl` | one line per tool call |
The encryption key lives in the system keychain. Where no keychain exists, or when `OPENCONNECTOR_KEYCHAIN=0`, it lives in `master.key` (mode `0600`).
### Security model
- The server binds to `127.0.0.1` by default.
- Agent tokens are random 192-bit values. Only their SHA-256 hash is stored.
- Each call is checked against the agent policy before any credential is loaded.
- Read-only agents only see tools marked read-only.
- Every call is written to the audit log, without arguments or results.
## Development
```bash
go test ./...
bun run typecheck
bun run dev
```
Conventions:
- No code comments. Names and small functions carry the meaning.
- This README is the only document. Keep it current.
- Conventional Commits (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`).
## Development log
### 2026-10-17: v0.1.0
Done:
- Core contracts in `internal/core`. Every other package was built against them in parallel, then merged.
- Store: JSON state, AES-256-GCM secrets, audit log with rotation, reload when another process writes.
- Auth: OAuth 2.0 loopback flow with PKCE, token refresh that persists, API key clients. A personal access token also works on an OAuth provider.
- Gateway: per-agent policy, read-only mode, account resolution (argument, session, agent default, global default, oldest), session switching, result filter, size cap, audit.
- Server: MCP over Streamable HTTP with one MCP session per agent session, REST API, stdio proxy, Host and Origin checks against DNS rebinding.
- Providers: Google (15 tools), GitHub (14), Slack (8), Notion (7), Linear (9).
- Console, CLI and desktop UI (Overview, Accounts, Agents, Activity, Settings).
- Tests: unit tests per package and an end-to-end test with two agents and two accounts over real MCP. `go test -race ./...` passes. `tsc` passes. `mygo build` produces the app.
Known limits:
- No provider is tested against its live API. Highest risk: Linear GraphQL filter shapes, Google free/busy response keys.
- Two processes that write the store at the same moment can lose one write. There is no cross-process file lock.
- The stdio proxy does not relay server-initiated messages, and does not re-initialise after the server drops a session.
- Notion Markdown import is flat: no nested lists, and inline formatting stays literal.
- The keychain may ask for access again after each rebuild of the binary on macOS. Set `OPENCONNECTOR_KEYCHAIN=0` during development.
Next:
- Live smoke test of each provider with real accounts.
- Tray or menu bar mode, so the server keeps running with the window closed.
- More providers (Outlook, Jira, Discord, Todoist, local files).
- A "smart" meta tool that routes a plain-language request to the right tools with the cheap model.
- Agent-scoped OAuth (MCP authorisation spec) so clients can sign in without copying a token.
### 2026-10-17: one-click connect
- Providers can declare services. Google offers Gmail, Calendar and Drive, and an account records what the user granted.
- The gateway hides tools whose service no allowed account granted, and picks an account that granted it.
- Built-in OAuth clients load from `oauth-clients.json`. A user's own client overrides them; tokens refresh with the client that issued them.
- The Accounts page connects in one click. Own-client setup moved under **Advanced**.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues