# anyray-connect

The on-ramp. A small, dependency-free Node CLI that points a developer's local
coding tools at the **Anyray gateway** through supported application config and
the developer's personal gateway key. The gateway stays the brain (routing,
spend store, content encryption, optimizer) — this only does capture/onboarding.

> The Anyray gateway you target may itself front an existing gateway (LiteLLM, OpenRouter, Portkey, homegrown) via the `anyray-upstream` provider — that's configured server-side on the gateway, with no client-side difference to `connect`.

```bash
npx anyray-connect@latest --enroll https://app.anyray.ai/enroll/enl_…
```

An `npx` run first copies its published runtime into a versioned directory under
`~/.anyray/bin`. Hooks, MCP registrations, and login services point at that
durable copy instead of npm's disposable `_npx` cache.

## Personal gateway keys (enrollment)

Every gateway `/v1` request requires a **personal gateway key** (`ark_...`). Enroll
before a real apply:

1. An org admin uses the console's **Users** page to create a passwordless
   enrollment link, or shares the org's SSO enrollment command.
2. The developer enrolls while connecting:

   ```bash
   npx anyray-connect@latest --enroll https://app.anyray.ai/enroll/enl_…
   # Enterprise SSO:
   npx anyray-connect@latest --sso https://app.anyray.ai/sso/tnt_…
   ```

Enrollment mints a personal key and binds its verified user/team to spend
attribution. The key lands in owner-only local state and the supported tool
credential stores; it is never printed in full. Tools talk directly to a current
gateway. A separate scheduled heartbeat signs a fresh, purpose-bound device
proof and extends that same raw key before expiry, so running tools do not need a
credential rewrite. The heartbeat never returns a replacement key.

Existing refresh-enabled Connect profiles continue routing directly after an
update. If the heartbeat route is unavailable, Connect reports a gateway
version or routing problem without treating the enrollment certificate as
invalid.

A dry run never consumes an enrollment link or writes configuration; without an
existing key it reports the selected installs that would be configured after
enrollment.

Already have a key? Set `ANYRAY_CLIENT_KEY` in the environment. It must be a
minted `ark_...` key; the old `anyray-placeholder` value cannot authenticate.

How the key is sent depends on the auth mode:

- **Org API mode**: the personal key is the `Authorization` bearer; the gateway
  selects the server-held provider credential after authenticating the developer.
- **`--subscription`**: the tool's seat OAuth token keeps the `Authorization`
  header, and the personal key rides the `x-anyray-api-key` header instead so
  the gateway can still identify the dev.

## Automatic coverage matrix

“Automatic” has two different meanings here: **route** means the request takes a supported path
through the gateway and can be optimized; **visibility** means content-free usage arrives through
the request path or an official vendor reporting API. A visibility connector does not pretend that
closed first-party traffic was routed.

| Tool                       | Route setup                                                                                                                                                                                                                                                                         | Spend visibility                                                                                                                                                                                                          | Boundary                                                                                                                                                                                                                                                                          |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Claude Code**            | **Automatic** — writes the supported `env` block in `~/.claude/settings.json`.                                                                                                                                                                                                      | **Automatic** — API-key and Anthropic seat traffic is attributed on the gateway path.                                                                                                                                     | Seat OAuth passes through unchanged; provider routing applies only to API-key traffic.                                                                                                                                                                                            |
| **Codex app / CLI / IDE**  | **Automatic** — all three share `~/.codex/config.toml`; Connect makes the `anyray` provider the default without replacing foreign config.                                                                                                                                           | **Automatic** — API-key and ChatGPT-seat traffic is attributed on the gateway path.                                                                                                                                       | Retrieval is automatic. Source-side tool-output trim remains unavailable: Codex's blocking `PostToolUse` response rejects an already-completed tool promise in code mode, which can make a successful side effect look failed. Connect removes its retired hook rather than risk duplicate execution. Hosted tools and Codex cloud/web tasks also stay outside local hooks. |
| **Cursor**                 | **Automatic for native Team Claude/Opus context hooks by default**: Connect leaves inference and either password or SSO-backed Team sign-in inside Cursor, then installs user-level Shell/MCP hooks plus the retrieval MCP server. The separate OpenAI BYOK custom-model lane requires explicit `--org`; its exact custom-endpoint cells have a reversible backup and queue while Cursor is open. | Hook savings are content-free. BYOK requests that Cursor sends to the custom endpoint are visible on the gateway path. Native inference has no gateway trace. Cursor's docs conflict on Team Admin API availability, so test an `admin:*` key against `/teams/filtered-usage-events`; Anyray can import native usage only when that call succeeds. | No Anthropic key is added in native mode. On macOS/Linux, recognized simple test/search Shell commands run through an exit-code-preserving output reducer; object-valued MCP results use `updated_mcp_tool_output`. Windows keeps MCP handling but skips Shell rewriting. Read/Write, complex Shell, native prompts/responses, and cloud agents remain outside this user-level seam. The opt-in BYOK lane needs public TLS and an upstream API key; its exact Agent wire behavior varies by Cursor release and it cannot consume the Cursor Team entitlement. |
| **OpenCode**               | **Automatic** for direct API providers and its Copilot provider.                                                                                                                                                                                                                    | **Automatic** for those routed lanes.                                                                                                                                                                                     | Restart OpenCode afterward. Built-in ChatGPT OAuth exposes no endpoint override; use Codex for that seat.                                                                                                                                                                         |
| **VS Code Copilot Chat**   | **Automatic** on VS Code 1.121+ — Connect installs and configures an explicit Anyray chat provider through VS Code's supported language-model-provider API.                                                                                                                         | **Automatic** for gateway API-key mode. Copilot-seat/native automatic reporting requires Copilot Business or Enterprise plus the official GitHub connector; the current Anyray connector does not cover Individual plans. | The developer selects an “Anyray · …” chat model. Native Copilot Chat and inline completions remain GitHub traffic. VS Code profiles scope extensions: Connect installs into every profile registered at apply time (config is shared); a profile created later needs a re-run or the official **Apply Extension to all Profiles** action. |
| **JetBrains AI Assistant** | **Automatic** on IDE 2025.3.2+ with enabled JetBrains AI 253.30387.147+ — registers pinned Claude and Codex ACP launchers in `~/.jetbrains/acp.json`.                                                                                                                               | **Automatic** on those ACP carriers; native Copilot reporting requires Copilot Business or Enterprise plus the official GitHub connector.                                                                                 | Connect proves both versions from local JetBrains metadata before writing. Native JetBrains AI and Copilot traffic is untouched. A carrier may request its normal provider sign-in on first launch.                                                                               |
| **Claude Desktop**         | **Automatic** on Windows and on macOS builds whose config seam Connect verifies locally at apply time (version floor + app-bundle byte-check, so a routine Desktop update stays automatic). Both routing and the retrieval MCP server are applied through one adapter; unverifiable macOS builds use the in-app flow. | **Automatic** for routed third-party Gateway traffic, including the supported macOS subscription-helper lane. | On verified macOS builds, subscription selection uses the developer's Claude Code credential: the local helper reads it from the login Keychain on demand; setup never reads it, and the helper never persists or logs it. Windows remains API-billed and fails closed when subscription is selected. The first-party Desktop lane stays direct. Desktop has no supported pre-transcript tool-result replacement hook, so source-side trim is unavailable. Machine-level Windows policy takes priority. |
| **Devin Desktop**          | **Automatic registry write only** for pinned Claude and Codex ACP agents; route activation is not automatic.                                                                                                                                                                        | **Automatic** once requests use those carriers; the official enterprise consumption connector covers native Devin usage.                                                                                                  | Devin requires its documented one-time vendor UI agent-enable action and an app restart. Native Devin traffic is not redirected.                                                                                                                                                  |
| **Shell / SDK env**        | **Automatic** — writes a managed, idempotent shell-profile block.                                                                                                                                                                                                                   | **Automatic** on the gateway request path.                                                                                                                                                                                | Subscription seats are not applicable to generic SDK traffic.                                                                                                                                                                                                                     |

The real provider key is never written here — it stays server-side in the
gateway (`ANYRAY_PROVIDER_KEY_*`), which swaps it in per request. Connect itself
only writes routing and attribution configuration; it never reads or persists
prompt/response content. Routed requests are handled by the gateway under the
deployment's configured content mode.

These adapters use application-owned configuration only. Cursor native subscription mode writes no
model credential or base URL: it owns only its user hooks and one MCP entry. Cursor's separate BYOK
route is a supported UI setting, but Cursor publishes no managed-config contract for it; Connect's
best-effort automation targets only its exact journaled settings cells. Existing malformed values
fail closed. Missing rows are seeded as first use, so a future release moving the keys cannot be
distinguished automatically; a scheduled repo probe (`connect/scripts/probe-cursor-shape.mjs`)
checks each latest Cursor build still ships the managed cell names, and the UI remains the fallback
when the override disappears.
Claude Desktop similarly publishes its third-party inference UI, not a managed contract for the
local config-library files. The macOS write is gated by a local seam check — the installed app
bundle must still ship the known config-library shape (a scheduled probe also watches new
releases for drift) — is journaled, and fails closed to the supported UI on every unverifiable
version.
On Windows, Connect writes Claude's per-user policy and installs a key-free credential helper.
Restart Claude after first setup. Later enrolled-key renewals need no policy update or restart.
That Windows helper resolves only the personal Anyray gateway key; Windows has no supported
subscription-credential source in Connect, so subscription selection fails closed instead of
silently switching the developer to API billing. On a verified macOS build, a selected Claude
subscription installs a different local helper that reads the developer's Claude Code credential
from the login Keychain at request time and combines it with Anyray pass-through headers. The
credential is delivered from that helper to Claude Desktop; the setup process never reads it, and
the helper never persists or logs it. If the Keychain credential is unavailable, setup fails closed. The
first-party Claude Desktop subscription endpoint itself remains direct.
On supported automatic per-user lanes, the adapter also registers `anyray_retrieve` and
`anyray_recall` in the active third-party app configuration. Those tools recover
gateway-externalized context, but they do not trim Desktop tool results. Claude Desktop has no
supported pre-transcript replacement hook, and native tools and
connectors do not all traverse user-configured stdio MCP servers, so Connect does not interpose on
foreign MCP entries or copy their command, environment, or credentials into a broker.
Connect changes only settings and files it owns, and rolls back incomplete setup. Normal `npx`
setup saves the stable Connect copy the helper needs; direct temporary-only runs stop before making
changes.

`anyray-connect desktop` uses that complete lifecycle for Claude Desktop alone: enrollment,
durable runtime, credential renewal, self-update, drift healing, migration, and status/doctor all
share the same managed-tool record. `desktop disconnect` performs an ownership-safe scoped revert.
Use the existing `anyray-connect migrate` command when you want to run the local-history copy
immediately; setup otherwise queues it automatically.

On a user-facing macOS setup, Connect also points the administrator to Claude Desktop's supported
**Developer → Configure Third-Party Inference** window. The Anyray connection is already selected
there. Use **Connectors & extensions** to add and test approved remote MCP URLs, and **Plugins &
skills** to add approved Git marketplaces. **Apply locally** tests one machine. For Jamf, replace
the per-user credential-helper path with the stable helper path packaged by IT, then use Claude's
**Export → .mobileconfig** action. No connector JSON or plist editing is required. The window is
read-only on an MDM-managed machine, so author and export on an unmanaged staging Mac.

Connector URLs come from Claude's connector directory, the connector vendor, or the customer's
internal remote MCP service. OAuth client registration comes from that vendor or the customer's
IdP. Plugin marketplaces come from approved Git repositories; skills come from those plugins or the
system `org-plugins` deployment. A claude.ai organization connector grant, OAuth token, or account
session cannot be exported or copied into the third-party profile. Each user authorizes the rebuilt
connector normally.

The adapters do **not** install a certificate authority, change DNS/NAT, or modify the
operating-system proxy.
When a first-party client has no supported endpoint seam, Connect leaves it
untouched and names a config-native alternative.

JetBrains and Devin ACP carriers require Node.js with npm/npx on the endpoint. Connect resolves
the absolute `npx` launcher during setup and stores only that non-secret path in its durable ACP
launcher, so an IDE's reduced GUI `PATH` does not create a false-success install. JetBrains setup
also fails closed unless local `product-info.json`, the AI Assistant plugin descriptor, and the
enabled-plugin state prove IDE 2025.3.2+ with JetBrains AI 253.30387.147+. Custom IDE/plugin paths
that are not represented by those local metadata locations are left for the supported in-IDE ACP
flow. For JetBrains Remote Development, run Connect in the remote backend user's shell: that is
where the plugin and ACP process run, and configuring only the local frontend does not configure
the backend. JetBrains currently does not support ACP in WSL. A key supplied
through `ANYRAY_CLIENT_KEY` is usable by these durable helpers but cannot be refreshed on this
machine; rerun Connect with a replacement key when the administrator rotates or expires it.

## Desktop fleet configuration

Claude Desktop 1.10628.0 and newer can use a stable MDM trust anchor and fetch its effective
configuration from an organization-hosted bootstrap service. This avoids redeploying MDM for each
gateway, model, credential, or egress-policy update:

```bash
anyray-connect desktop fleet bootstrap macos \
  https://config.example.com/claude/bootstrap claude-bootstrap.mobileconfig \
  --oidc '{"clientId":"synthetic-desktop-client","issuer":"https://login.example.com/tenant/v2.0","scopes":"openid offline_access synthetic-desktop-client/.default"}'
```

The command emits only `bootstrapEnabled`, `bootstrapUrl`, optional `bootstrapOidc`, and an optional
deployment organization UUID. It does not host the authenticated bootstrap endpoint. Claude polls
it every 30 minutes; changed responses apply on the next Claude Desktop launch without an MDM
redeployment. Each response replaces the allowed managed configuration wholesale, so omitted keys
are unset. `inferenceCredentialHelper` is MDM-only and cannot come from the bootstrap response.
Any recognized managed key other than Claude's three app-behavior-only keys
(`disableAutoUpdates`, `autoUpdaterEnforcementHours`, and `disableWslSessions`) makes the managed
source authoritative, so Connect reports it and skips local healing. Unknown preference names and
configuration-profile metadata are ignored, matching Claude's reader. On Claude Desktop 1.19367.0
and newer, Windows treats an active HKLM policy as the whole configuration and does not merge HKCU
into it.

The generated fleet artifacts configure inference only. For a complete static macOS policy, use
Claude Desktop's configuration window to add managed MCP connectors and plugin marketplaces, test
them, and export the `.mobileconfig`. The local Anyray retrieval registration lives in the user's
third-party app config and is not automatically included in that export. Treat managed retrieval as
partial unless the exported policy contains an equivalent tested `managedMcpServers` entry. The CLI
artifact remains useful for inference-only automation, but it is not a connector catalogue or a
replacement for Claude's policy editor.

### Static credential-helper policy

Claude Desktop and Codex fleet artifacts contain no static personal key. They require an
explicitly deployed local helper; artifact generation fails when its absolute helper path is
missing.

First deploy the standalone binary to a stable absolute path, then install the helper on each
endpoint. This system-installed copy is externally owned: update it through the organization's
software distribution rather than Connect's per-user self-updater.

```bash
sudo /usr/local/bin/anyray-connect desktop helper --write --platform posix \
  --bin /usr/local/bin/anyray-connect
```

```powershell
& "C:\Program Files\Anyray\anyray-connect.exe" desktop helper --write `
  --platform windows --bin "C:\Program Files\Anyray\anyray-connect.exe"
```

The default destinations are `/usr/local/bin/anyray-credential-helper` and
`C:\Program Files\Anyray\anyray-credential-helper.cmd`. `--write` verifies the runtime and writes
atomically (mode `0755` on POSIX); `--print` emits only wrapper bytes for MDM packaging. The wrapper
never uses `PATH` and contains no key. Each signed-in user must already be enrolled.

Generate a policy only after choosing the matching helper path:

```bash
anyray-connect desktop fleet static macos \
  /usr/local/bin/anyray-credential-helper claude.mobileconfig \
  --gateway https://gw.example.com

anyray-connect desktop fleet static windows \
  "C:\Program Files\Anyray\anyray-credential-helper.cmd" claude.reg \
  --gateway https://gw.example.com

anyray-connect desktop fleet static linux \
  /usr/local/bin/anyray-credential-helper managed-settings.json \
  --gateway https://gw.example.com

anyray-connect desktop codex --mobileconfig codex.mobileconfig \
  --gateway https://gw.example.com \
  --helper-path /usr/local/bin/anyray-credential-helper
```

Deploy the Linux result to `/etc/claude-desktop/managed-settings.json`. Static policy changes need a
regenerate/redeploy; routine enrolled-key renewal does not, because the helper resolves the current
key at request time.

Claude consumes the helper's `print-key --json` output. Codex's supported command-auth contract
expects a raw bearer token, so its generated config selects the wrapper's private raw-token mode;
on Windows it invokes the `.cmd` through the system `cmd.exe` instead of relying on shell lookup.

## Common flags

```
--gateway <url>   Gateway origin (default: $ANYRAY_GATEWAY_URL or http://localhost:8787)
--user <id>       Attribute spend to this user (default: git email, else OS user)
--team <id>       Attribute spend to this team
--tools <ids>     Subset: claude-code, codex, cursor, copilot, opencode,
                  jetbrains, claude-desktop, windsurf (Devin Desktop), shell-env
--enroll <link>   Passwordless enrollment link; mints the personal gateway key.
--sso <link>      Enterprise SSO enrollment; mints the same kind of key after
                  the developer signs in with the configured identity provider.
--subscription    Seat-billed (subscription) auth: pass the tool's own OAuth
                  token through the gateway instead of the org API key.
                  Honored directly by Claude Code (Anthropic seats) and Codex
                  (ChatGPT seats), by Claude Desktop's helper on verified macOS
                  builds when the Claude Code Keychain credential is readable,
                  and by supported Copilot provider routes. Cursor is the
                  explicit exception: AUTO already keeps its native Team
                  credential inside Cursor and installs only the supported local
                  hooks/MCP seam; --org is required for Cursor BYOK. Claude
                  Desktop on Windows fails closed for
                  this selection. Unsupported first-party endpoints are left
                  untouched. With a personal gateway key (--enroll / --sso /
                  $ANYRAY_CLIENT_KEY), the key rides x-anyray-api-key while a
                  routed seat token stays the bearer.
--dry-run         Show what would change without writing
--yes, -y         Skip confirmation (scripted/fleet rollout)
--revert          Undo a previous run: unpick every tool config we wrote and
                  clear the stored enrollment. Reconnecting needs --sso (or a
                  fresh --enroll link). Restart any editor that was running.
```

Without a TTY (CI, piped stdin) the CLI exits 1 instead of prompting — pass
`--yes` for any scripted run, including `--revert` (piping `y` no longer works).

`--revert` clears the enrollment on purpose. Background renewal can heartbeat or re-mint and
repair managed tool config for as long as a valid cert is on disk, so leaving it behind lets an
editor that is still running restore the config the revert just removed.

`--revert` cannot be combined with `--sso` or `--enroll`: those flags have opposite
effects. To set up a new machine, run `anyray-connect --sso <link> --yes` without
`--revert`.

## Adding a tool

Adapters live in `src/tools/` and are registered in `src/tools/index.ts`
(`REGISTRY`) — same open/closed seam as the optimizer's strategy registry. Write
an adapter implementing `ToolAdapter` (`detect` / `apply` / `revert`) and add one
line to the registry. Nothing in the CLI orchestrator changes.

## Develop

```bash
# From the monorepo root; Connect's build embeds the sibling VS Code extension.
corepack pnpm install
cd connect
npm run dev -- --dry-run     # run from source
npm test                     # node --test
npm run build                # build/check bundled VSIX, then tsc → dist/
```
