Criar um projeto
De uma fonte de dados real a um app no ar, para um agente de código. Cada passo termina em algo que você pode conferir.
0. Pré-requisitos
- O servidor MCP conectado com
--scope user, e owhoamirespondendo (Primeiros passos com o MCP). - O CLI companion:
make clino checkout da plataforma, depoiscortex login.cortex whoami --jsonsai com 0. - Node 22+ no terminal onde o app vai ficar.
O comando
cortex. Ele não está no npm, enpx cortexinstala outro pacote com o mesmo nome: nunca rode. Omake clideixa o nosso em~/.local/bin.
1. Criar o projeto
Combine o slug (global) com a pessoa.
Pergunte a região à pessoa; nunca escolha por ela. region é obrigatória e permanente: o lake e o app rodam nela (southamerica-east1 para o Brasil, us-central1 para os Estados Unidos). Em dev, hoje, o runtime existe em us-central1; uma região sem runtime é recusada pelo create_project com um erro que diz quais regiões funcionam. Conte isso à pessoa e pergunte de novo; nunca tente outra região por conta própria.
create_project { slug: "acme-bi", name: "Acme BI", region: "us-central1" }
get_project { project_id } # até o status ser "active"2. Gerar o app
Só depois de o projeto estar active: o cortex.yaml nomeia o projeto pelo slug, e o deploy o procura.
cortex init bi-nextjs --dir acme-bi --slug acme-bi --region us-central1
cd acme-bi && npm install
npm run dev # http://localhost:3000, dados de exemplo, login simuladoO cortex init recebe a mesma região, e --region é obrigatório: precisa ser aquela em que o projeto foi criado (o get_project mostra).
O init escreve o template Next.js e o cortex.yaml (spec §8) com o seu slug, nome e região em project:. O template vem com dados de exemplo no formato exato de MetricQueryResult, então roda offline enquanto o projeto ainda está vazio.
3. Descrever os dados no cortex.yaml
sources: uma entrada por banco, pasta de arquivos ou API. Strings de conexão são referências a segredos (connection: { secret: ERP_DSN }), nunca valores.pipelines: o que roda, quando, em qual fuso.semantic.datasetsesemantic.metrics: o único lugar onde um número é definido.access.row_policieseaccess.column_masks: quem vê quais linhas e colunas.
Todo segredo citado no arquivo precisa existir antes: set_secret { project_id, name, value }. Depois aplique o arquivo inteiro:
apply_project_config { project_id, yaml: "<conteúdo do cortex.yaml>", dry_run: true }
apply_project_config { project_id, yaml: "<conteúdo do cortex.yaml>" }O primeiro apply também sincroniza o catálogo; antes dele, o run_sql responde que o catálogo ainda não foi sincronizado. Um ambiente protegido responde com um change set que a pessoa aprova (approve_change_set).
4. Carregar os dados
- Banco acessível pela internet:
add_sourcecomtype: "database", umconnector(postgres,mysql,mssql,oracle) e o nome do segredo;test_source; depoisrun_pipeline { project_id, pipeline: "<nome em pipelines>" }eget_run { run_id }até terminar. - Planilhas e CSVs:
upload_file, PUT dos bytes na URL assinada,finalize_upload,get_run. Para arquivos no disco da pessoa,cortex upload <arquivos|pasta> --jsonfaz os quatro e espera; ou deixe para o primeiro deploy (passo 6,--with-data). - Uma fonte dentro da rede da pessoa (um ERP local) não é alcançável pela nuvem hoje, e o
cortex ingest --localainda não foi implementado (sai com 9). Diga isso.
Confira: list_tables e sample_table mostram o que entrou. Informe as contagens de linhas.
5. Modelar e consultar
propose_model { project_id } devolve um change set; mostre, obtenha a aprovação, depois run_models { project_id }. Com as métricas de semantic.metrics aplicadas:
query_metrics { project_id, query: { metrics: ["revenue", "orders"], dimensions: ["orders.channel"], time_grain: "month" } }Todo número vem com o seu SQL. Mostre os dois.
6. Publicar
cortex deploy --json # production por padrão; --env preview para uma prévia.gitignore e .cortexignore decidem o que entra no pacote; arquivos .env* são sempre recusados. approval.required quer dizer ambiente protegido: aprove o change set com a pessoa e rode de novo. O comando espera o certificado de um endereço novo e imprime a URL. Confira o /healthz nela.
Primeiro deploy e dados locais. No primeiro deploy de um projeto (nenhum deployment em nenhum ambiente ainda), a pessoa decide se os arquivos de dados do disco dela sobem junto com o app. Pergunte antes de rodar o comando, ou chame create_deployment { project_id }: ele pergunta e devolve o comando a rodar, cortex deploy --with-data ou cortex deploy.
cortex deploy --with-data --jsonsobe antes todos os arquivos de dados locais (CSV, TSV, Excel.xlsx, Parquet), espera cada um carregar e só então publica o app. Os arquivos são as fontestype: filesdocortex.yamlcujopathexiste no disco; sem elas, todo arquivo de dados da pasta, com.gitignoree.cortexignoreaplicados. O que subiu como dado fica fora do pacote do app (--keep-data-in-appmanda também).--data <caminho|glob>[,...]escolhe os arquivos (o.gitignorenão se aplica a ele);--no-datapublica só o app.--env pr<N>manda os dados para essa prévia.- Sem flag e sem terminal (você, um agente), o comando nunca pergunta: publica só o app e imprime em stderr uma dica de
--with-data. O--jsontrazdata.decision,data.uploadsedata.run_ids. - Falha de dados para antes de publicar o app: sai com 7 quando um upload é recusado, com 1 e
run.failedquando uma carga falha. Leiaget_run_logs { run_id }, corrija o arquivo e rode de novo. .xlsnão é lido: salve como.xlsx.- Nunca sobem como dado, com qualquer flag ou glob:
.env*; chaves e arquivos de credencial (*service-account*.json,*credentials*.json,*.pem,*.key,*.p12,*.pfx,id_rsa*,id_ed25519*,.npmrc,.pypirc); arquivos de texto cujos primeiros 4 KiB têm uma chave privada ou um JSON de service account; links simbólicos que resolvem para fora da pasta (glob e varredura de pasta nunca seguem link). Cada um imprime uma linhawarning:em stderr e entra emrefused(data.refusednodeploy) com oreason. Nomeado sozinho, o comando sai com 7. Globs pulampackage.json,tsconfig*.jsone outros JSON de ferramenta.
O meu app está do tamanho certo?
Pergunte à plataforma, não chute. O get_app_metrics lê CPU, memória, instâncias, requisições, latência p50/p95, 5xx e cold starts do app de um ambiente, o size atual comparado ao teto do plano, e uma recommendation calculada a partir desses números:
get_app_metrics { project_id, env: "production", window: "24h" } # window: 1h, 24h ou 7drecommendation.advice | O que quer dizer | O que você faz |
|---|---|---|
ok | O tamanho aguenta a carga | Nada |
scale_up | O p95 de CPU ou de memória está alto | Proponha o recommendation.cortex_yaml |
scale_out | As instâncias ficam no máximo | Proponha o recommendation.cortex_yaml |
keep_warm | Cold starts pioram a latência | Proponha min_instances: 1; custa parado |
scale_down | Paga por um tamanho que não usa | Proponha o tamanho menor |
upgrade_plan | A solução está acima do teto do plano | Diga à pessoa qual plano; não aplique |
no_data | Nenhum tráfego no período | Abra o app, ou tente window: "7d" |
O tamanho fica no cortex.yaml, em app.resources (plan-default quando ausente):
app:
resources:
cpu: 2
memory: 1Gi
min_instances: 0Mostre à pessoa o recommendation.reason e o monthly_cost_delta_usd (só o número que a plataforma mandou; nunca calcule um), depois apply_project_config com dry_run: true e de novo sem ele. Um ambiente protegido responde com um change set para aprovar. O tamanho novo vale a partir do próximo deploy daquele ambiente. Um tamanho acima do teto do plano é recusado com limit.plan_required, e o hint diz qual plano permite. O console mostra os mesmos números na página do projeto (aba Máquina), só para leitura.
Pronto quando
whoami funciona, o cortex.yaml aplica, pelo menos um pipeline ou upload rodou, o query_metrics responde com o SQL, e a URL responde em /healthz.
Estado hoje (24/09/2026, dev)
- Funciona:
cortex login,whoami,init,deploy(com--with-data),upload; toda ferramenta do MCP emtools/list. - Ainda não:
cortex ingest --local,cortex devecortex config push/pullsaem com9e uma dica; use as ferramentas do MCP acima. - Novo em 25/09/2026:
get_app_metricse aregionobrigatória. Se otools/listainda não tiver oget_app_metrics, este ambiente não foi atualizado: diga isso, não chute o tamanho. - O dashboard e o chat do template ainda mostram dados de exemplo. Ligá-los ao
query_metricsé a próxima mudança do template; até lá, diga à pessoa que os números da página são exemplos.