Getting started with the Cortex MCP
Audience: a coding agent (Claude Code, Codex, Cursor) working for a human. Everything here is verifiable from a terminal; the only browser step is the human signing in.
1. Connect
The MCP server of dev is https://mcp.dev.infra.doublex.ai/mcp: Streamable HTTP, with OAuth. In Claude Code:
claude mcp add --scope user --transport http cortex https://mcp.dev.infra.doublex.ai/mcp--scope user registers the server for every folder. A cortex entry with local scope (what the command creates without the flag) wins over the user one, so if you ever added it wrong, remove it first: claude mcp remove cortex -s local.
On first use the server answers 401 with WWW-Authenticate: Bearer resource_metadata=...; the client discovers the authorization server through /.well-known/oauth-protected-resource and opens the browser for the human, who signs in with the same account as the console.
If cortex is listed but has no tools, or keeps asking for sign-in: in Claude Code type /mcp, choose cortex, then Authenticate.
Verify: call whoami. It answers with the organization, the plan and your scopes.
The
cortexcommand. It is not on npm, andnpx cortexinstalls an unrelated package with the same name: never run it. Build it from the platform checkout withmake cli, which leavescortexin~/.local/bin. You need it forcortex deploy, not to talk to the MCP.
2. Authenticate the terminal
cortex login # opens the browser; the issuer is discovered from the API
cortex login --pat <token> # CI, or when the browser cannot open
cortex whoami --jsonThe credential is validated before it is written to ~/.cortex/credentials.json with mode 0600. CORTEX_TOKEN in the environment wins over the file, so CI never needs a login step. Tokens are created on the console's Tokens page.
3. The tools
tools/list is always the truth. The groups, with the tools you will use first:
| Group | Tools |
|---|---|
| Account | whoami, list_projects, get_project, create_project, delete_project |
| Sources | upload_file, finalize_upload, add_source, test_source, list_sources, remove_source, set_secret |
| Pipelines | run_pipeline, get_run, get_run_logs, list_runs, schedule_pipeline, cancel_run |
| Explore | list_tables, describe_table, sample_table, profile_table |
| Query | run_sql, estimate_query, get_query_result |
| Modeling | get_project_config, apply_project_config, propose_model, run_models, list_models, get_change_set, list_change_sets, approve_change_set, reject_change_set |
| Semantics and AI | get_semantic_model, validate_semantic, query_metrics, explain_metric, ask, set_ai_provider, list_ai_providers |
| App | get_template, create_deployment, get_deployment, list_deployments, get_build_logs, get_app_logs, rollback_deployment, promote_deployment, set_env_var, list_env_vars, get_health, get_app_metrics |
| End users | invite_end_user, list_end_users, set_end_user_roles, set_end_user_attrs, disable_end_user |
| Budget | get_usage, get_budget, get_limits, set_spend_cap |
Most tools take a project_id (the ULID create_project returns, not the slug). Deploying from a folder is cortex deploy, which packs the folder and calls create_deployment for you.
4. Reading a result
Every result carries _meta["cortex/usage"] with cu_seconds, bytes_scanned, cost_estimate_usd and budget_remaining_usd. Watch it: cost is a requirement, not a report.
5. Reading an error
Errors are structured, in English, and tell you what to do:
{ "error": { "code": "limit.bytes_exceeded", "message": "query would scan 12 GB",
"hint": "Filter by order_date, or upgrade the plan.",
"details": { "bytes_estimated": 12884901888 } } }Common codes: auth.unauthenticated, auth.scope_missing, validation.config_invalid, not_found, conflict.slug_taken, limit.bytes_exceeded, limit.spend_cap, approval.required, upstream.unavailable.
The CLI maps them to exit codes: 0 ok, 2 usage, 3 auth, 4 not found, 5 conflict, 6 limit, 7 validation, 9 not implemented yet, 1 anything else.
6. Approvals
A change to a protected environment never applies silently. approve_change_set asks the human through MCP, with a summary and a diff. You wait for the answer; you do not work around it.
Next
Quickstart goes from sign-up to a published URL; Bootstrap a project is the same path for a real data source.