Auth
The Extentos auth model — anonymous-first by design, with a lazy device-code flow (OAuth 2.0 RFC 8628 pattern) that fires only when the 1000-event browser-simulator meter exhausts. All 18 MCP tools work with no account; only the browser simulator is gated. The completeAuthLink MCP tool polls the backend after createSimulatorSession returns auth_required, persists the token to ~/.extentos/auth.json, and the original tool call retries automatically. No manual token paste, no upfront signup wall, no payment ever.
The Extentos auth model is anonymous-first by design. All 18 MCP tools, code generation, validation, on-device LocalSimTransport simulation, and real-hardware testing through RealMetaTransport work with no account, no email, no payment — forever. Only the browser simulator at extentos.com/s carries a 1000-event meter, and only when that meter exhausts does the device-code flow fire to link a free email-only account. The agent handles the entire flow programmatically via the completeAuthLink MCP tool — the developer signs up once in their browser, the bearer token persists to ~/.extentos/auth.json, and the original createSimulatorSession call retries automatically. No manual token paste, no upfront signup wall, no payment at any tier. This page covers the identity states, the device-code mechanics, the CLI subcommands, the state files, and the env-var dials.
Identity states
Extentos has three identity states, each unlocking a defined slice of functionality:
| State | Identifier | What you can do | Cost |
|---|---|---|---|
| No account | installId (per-machine, at ~/.extentos/install_id) | All 18 MCP tools. LocalSimTransport. Code generation. Validation. Real-hardware testing through RealMetaTransport (your own Meta credentials). 1000-event lifetime meter on BrowserSimTransport browser simulator. | Free forever |
| Free account | accountId (linked via device-code flow) | Everything above, no event meter on the browser simulator. Email + ToS acceptance only. | Free, no payment |
| (Future) Paid | n/a at launch | Reserved for post-launch evolution if Extentos ever introduces paid features. No paid tier exists at launch. | n/a |
The installId is anonymous and never tied to your identity. The accountId is created by the developer signing up at the verification URL during the device-code flow. There's no upfront signup wall — the agent and the simulator work end-to-end on the anonymous tier first.
See pricing for the full free-tier breakdown.
The device-code flow
Extentos uses the OAuth 2.0 Device Authorization Grant (RFC 8628) — the standard pattern for headless / CLI scenarios where the application can't perform a browser redirect. AI coding agents are exactly that case: the agent runs in a terminal, the developer's browser is somewhere else.
Sequence diagram
Developer Agent MCP server Backend Verification URL (browser)
│ │ │ │ │
│ "build app" │ │ │ │
│─────────────▶│ │ │ │
│ │ │ │ │
│ │ createSimulator- │ │ │
│ │ Session(...) │ │ │
│ │───────────────────▶│ │ │
│ │ │ POST /api/.../ │ │
│ │ │ session │ │
│ │ │─────────────────▶│ │
│ │ │ │ │
│ │ │ auth_required, │ │
│ │ │ deviceCode, │ │
│ │ │ userCode, │ │
│ │ │ verificationUrl│ │
│ │ │◀─────────────────│ │
│ │ { status: │ │ │
│ │ "auth_required" │ │ │
│ │ verificationUrl, │ │ │
│ │ userCode, │ │ │
│ │ deviceCode } │ │ │
│ │◀───────────────────│ │ │
│ │ │ │ │
│ │ open browser │ │ │
│ │ at verificationUrl │ │ │
│ │─────────────────────────────────────────────────────────▶ │
│ │ │
│ │ completeAuthLink({ deviceCode }) │
│ │───────────────────▶│ │ │
│ │ │ poll /api/.../ │ │
│ │ │ token │ │
│ │ │◀────────────────▶│ pending, pending │
│ │ │
│ signs up at verificationUrl ────────────────────────────────────────────▶│
│ │ confirms signup │
│ │◀───────────────────│
│ │ │ │ │
│ │ │ poll → │ │
│ │ │ authToken, │ │
│ │ │ accountId, │ │
│ │ │ tier │ │
│ │ │◀─────────────────│ │
│ │ persist to │ │ │
│ │ ~/.extentos/ │ │ │
│ │ auth.json │ │ │
│ │ │ │ │
│ │ retry │ │ │
│ │ createSimulator- │ │ │
│ │ Session │ │ │
│ │───────────────────▶│ │ │
│ │ │ uses bearer token │ │
│ │ session ready │ │ │
│ │◀───────────────────│ │ │Backend endpoints
Verified from mcp-server/src/cli/login.ts and mcp-server/src/tools/handlers/completeAuthLink.ts:
| Endpoint | Method | Purpose |
|---|---|---|
/api/auth/device/code | POST | Issue a device code. Body: { scope: "account_full" }. Returns { deviceCode, userCode, verificationUrl, expiresInSeconds, pollIntervalSeconds } |
/api/auth/device/token | POST | Poll for completion. Body: { deviceCode }. Returns { pending: true, pollIntervalSeconds } while waiting; { authToken, accountId, tier, linkedAt, expiresAt } on approval |
Defaults and limits
| Field | Default | Notes |
|---|---|---|
| Device-code window | 600 seconds (10 min) | Backend returns the actual expiresInSeconds; completeAuthLink enforces a max of 600 |
| Poll interval | 5 seconds | Backend may bump this via pollIntervalSeconds in poll responses; agent honors |
completeAuthLink.maxWaitSeconds | 600 | Caller can lower; capped at 600 by the handler |
completeAuthLink.pollIntervalSeconds | 5 | Range 1-30 (clamped) |
HTTP status codes
| Status | What it means | Retryable |
|---|---|---|
200 (pending: true) | User hasn't finished signup yet — keep polling | yes (continue) |
200 (with authToken) | Approved — token issued | done |
403 | User declined the signup prompt at the verification page | yes (after asking the user to retry) |
404 | Unknown deviceCode (never existed or already consumed) | no (need a fresh code) |
410 | Device-code window elapsed (10-min default) | yes (need a fresh code) |
| Other / network failure | Backend unreachable | yes (after the network issue resolves) |
The completeAuthLink MCP tool
The agent-facing primitive for the device-code flow. After createSimulatorSession returns status: "auth_required", the agent calls completeAuthLink with the returned deviceCode.
Parameters
| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
deviceCode | string | yes | n/a | From the auth_required response |
maxWaitSeconds | integer | no | 600 | 1-600 |
pollIntervalSeconds | integer | no | 5 | 1-30 |
Response on success
{
"linked": true,
"tier": "free_account",
"accountId": "acc_...",
"linkedAt": "2026-05-01T12:34:56Z",
"expiresAt": "2027-05-01T12:34:56Z",
"authTokenFile": {
"path": "/home/dev/.extentos/auth.json",
"written": true,
"error": null
},
"pollCount": 3,
"summary": "Account linked (tier: free_account). Re-invoke createSimulatorSession to use the new bearer token."
}The pollCount lets the agent know how long it took. The summary is the agent-facing reminder.
Error codes
| Code | Cause | Retryable |
|---|---|---|
unknown_device_code | Backend doesn't recognize the deviceCode | no — call createSimulatorSession again for a fresh code |
device_code_expired | 10-min window elapsed | yes — call createSimulatorSession again |
device_code_denied | User declined at the verification page | yes — ask the developer to retry and accept |
backend_unreachable | Network failure or backend down | yes |
backend_invalid_response | Malformed poll body | no — version mismatch |
auth_timeout | Polled past maxWaitSeconds without approval | yes — the developer hasn't finished signup yet |
Telemetry events fire on every flow outcome: account.login_initiated, account.login_completed (with outcome success | code_expired | user_denied | network_error | polling_aborted), and account.linked (only on success). Tagged with installId, no PII.
CLI subcommands
extentos-mcp is a developer CLI in addition to the MCP server. The auth-related subcommands:
| Subcommand | What it does | When to use |
|---|---|---|
login | Manually trigger the device-code flow. Initiates the flow against /api/auth/device/code, prints the userCode + verificationUrl, auto-opens the browser, polls until completion. | When the developer wants to link before the meter exhausts (rare — the agent does this automatically when needed) |
logout | Delete ~/.extentos/auth.json. Install returns to the anonymous tier (the installId survives). | When switching machines, debugging auth state, or revoking the link |
whoami | Print install state — installId, accountId (if linked), tier, meter remaining, auth expiry | Diagnosing auth issues, before filing bug reports |
Run any of them with:
npx @extentos/mcp-server@latest login
npx @extentos/mcp-server@latest logout
npx @extentos/mcp-server@latest whoamiThe login command output looks like:
Extentos account linking
User code: ABCD-1234
Verification URL: https://extentos.com/auth/device
Opening browser… (if it doesn't open, paste the URL above)
Polling for up to 600s. Ctrl-C to cancel.State files
Auth state lives in ~/.extentos/ by default (overridable with EXTENTOS_CONFIG_DIR):
| File | Contents | Created by |
|---|---|---|
install_id | A per-machine UUID, never expires | First MCP server start |
auth.json | { authToken, accountId, linkedAt, expiresAt, scope } after linking | Successful completeAuthLink (or extentos-mcp login) |
consent | Telemetry consent state (accepted / declined / unset) | First privacy notice acceptance or extentos-mcp accept-privacy / decline-privacy |
logout deletes auth.json only. install_id persists — wiping the entire ~/.extentos/ directory creates a fresh anonymous identity with a fresh meter, which is one of the abuse vectors flagged in docs/mcp/ACCOUNTS_AND_PRICING.md (acceptable at MVP scale because it requires the developer to wipe the agent's MCP context each time).
Environment variables
| Variable | Default | Purpose |
|---|---|---|
EXTENTOS_BACKEND_URL | Production backend | Override the backend URL the auth endpoints hit. Used for local backend development. |
EXTENTOS_CONFIG_DIR | ~/.extentos | Override the config / auth / consent directory |
EXTENTOS_NO_AUTO_OPEN | unset | Set to 1 to disable browser auto-open. The verification URL is still printed; the developer pastes it manually. Useful for headless / SSH / cloud-hosted-agent environments. |
Browser auto-open
When the device-code flow fires, the MCP server attempts to open the verification URL in the developer's default browser via:
| Platform | Command |
|---|---|
| macOS | open <url> |
| Windows | cmd.exe /c start "" <url> |
| Linux | xdg-open <url> |
If auto-open fails (no GUI, no default handler, sandboxed environment), the URL is still printed to the agent's tool response and the CLI's stdout. The developer copies it into their browser. Set EXTENTOS_NO_AUTO_OPEN=1 to skip the auto-open attempt entirely.
Telemetry consent (separate from auth)
Telemetry consent is a separate mechanism from authentication. The MCP server emits anonymous usage telemetry (tool calls, install events, no source code or PII) tagged with the per-machine installId. Consent is consent-default with first-run notice — the same pattern used by Vercel CLI, Astro, and Vite.
The consent state lives at ~/.extentos/consent. It's set by:
| Action | Effect |
|---|---|
| First MCP tool call after install | Privacy notice injected once into the response (PRIVACY_NOTICE constant in mcp-server/src/index.ts); telemetry stays enabled by default |
extentos-mcp accept-privacy | Records explicit consent |
extentos-mcp decline-privacy | Disables telemetry upload — events still emit locally for debugging but never leave the machine |
Setting EXTENTOS_TELEMETRY=0 | Same as decline-privacy for the current shell, but doesn't persist to the consent file |
Telemetry consent has nothing to do with whether you're authenticated. A no-account install can opt out of telemetry; a linked account can opt out too. The two systems are independent.
Frequently asked questions
Why anonymous-first?
The MCP server is the agent's tool surface. If we required signup before the agent could call any tool, time-to-first-prototype would suffer — the developer would have to interrupt the agent flow to create an account. Anonymous-first means the agent and developer can prove value first; the account gate fires only when there's already engagement to convert.
Why device-code instead of OAuth redirect?
OAuth redirect requires the application to be a web app that can register a callback URL and receive a redirect. An AI coding agent isn't a web app — it's a process running in the developer's terminal. Device-code (RFC 8628) is the OAuth flow designed for exactly this case: the device shows the user a code and a URL, the user authorizes on a separate browser, the device polls to learn the result. No redirect, no callback URL, no web server.
Can I use Extentos without ever signing up?
Yes — until the 1000-event browser-simulator meter exhausts. After that, the simulator stops working until you link a free account; the rest of Extentos (MCP tools, code generation, real-hardware testing through your own Meta credentials, on-device LocalSimTransport) keeps working forever, no account.
Does signing up cost anything?
No. The free account tier is the only tier that exists at launch. No payment, no card, no upfront commitment. Email + ToS acceptance only.
What if the device-code expires before I finish signup?
Call createSimulatorSession again to get a fresh deviceCode. The backend issues a new code with a fresh 10-minute window. Your prior install state is preserved.
What if I'm on a headless machine (cloud agent, SSH session)?
Set EXTENTOS_NO_AUTO_OPEN=1. The verification URL prints to the agent's response; the developer copies it to their local browser. The polling continues in the headless environment regardless of where the URL was opened.
How do I rotate or revoke my auth token?
extentos-mcp logout deletes ~/.extentos/auth.json locally. To revoke server-side, log in to your Extentos account at extentos.com and revoke the device. The next API call from the revoked install will get a 401 and prompt re-linking.
Is the auth token a secret?
Yes — treat it like any other API token. It's stored at ~/.extentos/auth.json with file-mode 0600 (owner read/write only). Don't commit it to version control. Don't share it across machines.
Is the installId a secret?
No — it's a per-machine anonymous identifier. Other apps on the same machine can read ~/.extentos/install_id; the same value is sent to api.extentos.com on every tool call as the telemetry key. It's not a credential.
What happens if ~/.extentos/auth.json is corrupted?
The MCP server treats it as not-linked and returns to the anonymous tier (with the existing installId's remaining meter). Run extentos-mcp login to re-link.
Related
- Pricing — what the free tier and the 1000-event meter actually cover
completeAuthLinktool — the MCP tool reference (full input/output schema)- MCP server overview — the broader 18-tool catalog
- Quickstart with an AI agent — installing the server in the first place
Search tools
The Extentos MCP server's search tool — searchDocs (search Extentos documentation by topic or keyword). Catalog topics like trigger_types, action_types, block_types, stream_types, spec_format, template_syntax, app_callback_guide, library_api carry inline minimal examples agents can compose against.
Supported agents
Per-host capability differences for AI coding agents that work with Extentos. What it's actually like running Extentos inside Claude Code (terminal, filesystem-wide reach, simultaneous iOS + Android edits) vs Cursor or Windsurf (VS Code workspace, both platforms work if both folders are open) vs Cline (similar but more click-approval) vs Android Studio's Gemini or Xcode AI features (single-platform, no MCP support — can't run Extentos at all). Project scope, MCP integration maturity, browser auto-open behavior, and the URL-bake fallback path documented per host.