Skip to main content
Profiles are an experimental feature. The integrations commands (alias int) are hidden by default — run machine0 config set USE_EXPERIMENTAL_FEATURES=true to enable them.
Integrations belong to a profile and connect your machine0 account to external services, storing the resulting credentials encrypted. Four integrations are available in every profile — claude-code (Claude Code OAuth credentials), codex (OpenAI Codex OAuth credentials — VMs created with the profile get a logged-in codex plus the profile’s MCP endpoint preconfigured), github (a GitHub App installation for gh and git push/clone — you pick exactly which repositories during connect), and machine0-cli (a machine0 API key so the machine0 CLI works inside a VM) — and you can add any remote HTTP MCP server as a custom integration (for example Linear’s official server at https://mcp.linear.app/mcp). Each is connected per profile. Credentials are stored encrypted at rest. OAuth connections open your browser for the provider’s consent screen; claude-code also asks you to paste the Claude callback URL or code#state back into the CLI, which exchanges the code with Anthropic locally before uploading the encrypted credential payload to machine0. codex works the same way but captures the browser callback automatically on localhost:1455 (the same loopback codex login uses) — if the callback can’t reach your terminal (running the CLI over SSH, or the port is busy), paste the full callback URL from the browser’s address bar instead; the OpenAI token exchange also runs locally. machine0-cli connects instantly (it mints an API key server-side, visible in your account’s token list as machine0-cli-integration).
Disconnecting an integration deletes machine0’s stored credentials, but does not remove credentials already injected into running VMs, suspend snapshots, or custom images baked from them. For codex in particular, ~/.codex/auth.json on a VM keeps working (it self-refreshes) until you log out of codex on the VM or destroy it — and an image created from a codex-authenticated VM carries that file.

integrations ls

List a profile’s integrations (built-in and custom) with their connection status and when each was last checked.
An integration is only ever connected or disconnected: Plain disconnected covers two cases; the reason differs, so read the row’s message rather than assuming it always means “run connect”. The detail view (integrations get <profile> <integration>) shows configured: no when the server has no OAuth credentials for the provider. The Last checked column shows when each integration’s credentials were last verified, in days (“today”, “3d ago”). It is set when the integration connects and refreshed by every integrations check; for MCP integrations that authenticate with OAuth, the server also verifies (and silently refreshes) the stored tokens on a periodic background sweep, so Last checked can advance without you running anything — and a token the provider has revoked or expired is detected and cleared in the background too, flipping the integration to disconnected before you next use it. --json carries the full timestamp. -- means never checked — for custom MCP servers that need no authentication this is permanent, since there are no stored credentials to record a check against (the server is still probed and its tools still served).

integrations get

Show one integration’s details — its status, which external account is connected, description, and the last check’s outcome. For MCP integrations the CLI also probes the server live and lists the prompts and tools it exposes through the profile’s MCP endpoint (with any prefix and tool whitelist applied), and the attributes include the configured prefix (-- when unset) and tool whitelist. The live probe updates the integration’s stored health exactly like integrations check does — including the silent OAuth token refresh, and removal of dead credentials when the refresh is rejected upstream. Built-in integrations keep the fast attribute-only view.

integrations new

Add a remote HTTP MCP server to a profile as a custom integration. Aliased as integrations create and integrations add. Integration names follow the same rules as machine names (letters, digits, and dashes; max 63 characters), and built-in names and close aliases (claude-code, claude, anthropic, codex, openai, chatgpt, github, machine0-cli, and machine0) are reserved. Each profile holds up to 50 integrations, and the same name can exist in different profiles. Only HTTP(S) MCP servers are supported, and URLs must be public — private, loopback, and link-local addresses are rejected.

integrations update

Update a custom MCP integration’s settings. Built-in integrations can’t be updated. Pass "" to any flag to clear it.
How the two compose:
  • The whitelist matches the server’s original (unprefixed) tool names, case-sensitively — list admits list_pages and list_users but not my_list or List_all. Changing the prefix never changes what the whitelist admits.
  • Because entries are prefixes, the upstream can later add a new tool that matches one and it will be proxied automatically — even a full tool name as an entry also admits future extensions of it (delete admits a later delete_all). There is no exact-match mode; pick entries as specific as possible, and re-run integrations check after upstream changes to see exactly what is exposed.
  • Entries are limited to letters, digits, dots, underscores, and hyphens (max 128 characters). A tool whose name uses other characters (search$web, non-ASCII names) can only be admitted through a shorter prefix entry that stops before the first unsupported character.
  • The prefix applies to prompts too; the whitelist applies only to tools.
  • integrations check and MCP clients connected to the profile endpoint see exactly the same prefixed, filtered names.
  • A whitelist that matches nothing (e.g. a sentinel entry like none) blocks every tool from that server.
  • The prefix keeps names valid MCP identifiers, but a very long upstream name plus a prefix can still exceed a client’s own name limits.

integrations rm

Remove a custom integration from a profile, deleting its stored credentials. Built-in integrations can’t be removed — use disconnect instead.

integrations connect

Connect (authenticate) an integration in a profile. OAuth providers open your browser for the consent screen; once you approve, the CLI reports success. claude-code requires one extra manual step: paste the callback URL or code#state shown by Claude back into the CLI; the CLI performs the Anthropic token exchange locally, then stores the resulting credentials with machine0. codex opens the same ChatGPT consent screen codex login uses and captures the callback automatically on localhost:1455 — no pasting needed unless the browser can’t reach the terminal’s machine (CLI over SSH, port in use), in which case paste the callback URL from the address bar; its token exchange also runs locally. machine0-cli connects immediately without a browser, and so do custom MCP servers that don’t require auth.
Re-running connect on an already-connected integration asks for confirmation, then replaces the stored credentials with a fresh grant. Connecting the same integration in another profile is independent — each profile holds its own credentials.

GitHub repository access

Connecting github for the first time installs the machine0 GitHub App on your account or an organization, and GitHub’s install screen lets you grant access to all repositories or only the repositories you select. The stored token can only reach what the installation covers.
  • Change repo access anytime at github.com/settings/installations — changes take effect immediately, no reconnect needed. VMs already running with the profile’s credentials follow along, since the token’s access tracks the installation.
  • Add another organization without reconnecting. Once the app is installed anywhere, re-running connect completes without showing GitHub’s install screen again. To grant access to an additional org or account, use the install link that a passing integrations check prints (github.com/apps/<app>/installations/new) — the stored credentials reach new installations automatically, so nothing else is needed afterwards.
  • Organizations may require admin approval. If your org does, the install becomes a request and the browser page tells you how to finish: either re-run machine0 integrations connect after an admin approves it, or — when the page says authorization is already complete — just run machine0 integrations check after approval. Until the admin approves, integrations check fails because the credentials reach no repositories.
  • Access is limited to accounts and orgs where the app is installed. (The previous flow’s read:org scope covered every org you belonged to; the App model does not.)
  • gh gist is not supported — GitHub Apps have no access to the Gist API. Use a personal access token inside the VM if you need gists.

integrations disconnect

Disconnect an integration, deleting its stored credentials in that profile. Built-in providers also get best-effort revocation on the provider side; custom MCP credentials are simply deleted. The integration shows as disconnected until you reconnect. Other profiles’ connections are unaffected. Disconnecting does not reach into VMs that were already provisioned with the profile — see the warning at the top of this page: credentials injected into running VMs, suspend snapshots, and custom images stay there until you remove them on the VM or destroy it.

integrations check

Check integration connections — one integration, or the whole profile at once. Exits non-zero when a check fails, so it’s scriptable. (integrations inspect works as an alias; integrations test still works for one more release.)

Checking one integration

The output shows a status line (this run’s result) and a message line explaining it. Status collapses to two values: connected when the check passes, disconnected when it fails. When the run flips a previously-connected integration to failing, the status reads connected -> disconnected in red with the message in red too. The outcome is remembered: after a failed check the integration shows disconnected in red in integrations ls (with the failure and time on the detail view) until a check passes or you reconnect. claude-code’s and codex’s checks run locally in the CLI and report their outcomes back the same way. For MCP servers, the server’s prompts and tools are listed (PROMPTS above TOOLS), with any configured prefix and tool whitelist applied — exactly the names MCP clients see through the profile endpoint. One exception for OAuth-connected MCP servers: an expired access token is refreshed silently during the check, and if the server rejects the refresh (the token was revoked upstream), machine0 removes the dead credentials — the integration returns to disconnected and needs integrations connect again. What each integration’s live check does:

Checking the whole profile

Omitting the integration checks every integration in parallel, updating each one’s stored status and last-checked time, then lists all the prompts and tools the profile’s MCP endpoint exposes (PROMPTS above TOOLS) — one row per entry with the integration it comes from. The profile’s own prompts are listed first, attributed to profile; per-integration prefixes and tool whitelists are applied. If two servers expose the same name, the endpoint serves the one from the last-listed integration; the other rows are marked (shadowed) — and a profile prompt always wins over a same-named upstream prompt. Each row’s status is connected (green), connected -> disconnected (red, when this run flipped a previously-connected integration to failing), or disconnected. A disconnected row is red when it fails the run and plain when it’s benign (a built-in never connected, or a provider not configured on this server). A red row without the arrow — a custom MCP server failing, or an integration that was already failing before this run — still fails the run: --json returns the full per-integration array with real timestamps (the claude-code entry’s timestamp comes from the CLI’s local check, since that check runs on your machine). Each entry also carries a previousStatus field ("connected" or "disconnected") — the integration’s status before this run — so a script can detect a connected→disconnected flip. One freshness nuance for scripting: the profile-wide form returns each integration’s lastCheck* fields as updated by this run, while the single-integration form shows the attributes as they were before its check ran — the status field is the live answer in both.