# DeployRápido — Agent Setup

Catálogo completo de ferramentas MCP / full MCP tool catalog:
https://deployrapido.com.br/documentacao/mcp

Você é um agente de codificação (Cursor, Claude, ChatGPT, Codex, etc.) e vai
publicar o app do usuário no DeployRápido. Siga estes passos **na ordem**. Não
invente ferramentas: só use as descritas aqui.

You are an AI coding agent about to deploy this user's app on DeployRápido.
Follow these steps **in order**. Do not invent tools: use only the ones below.

---

## Fale com a pessoa / Talk to the person

Esses clientes **não são desenvolvedores**. Depois de **cada** chamada de
ferramenta, diga em voz alta o campo `tellTheUser` (traduza se a pessoa
escreveu em inglês) **antes** da próxima ferramenta — não pule em silêncio.
Quando existir link público do app, mencione-o. Banco e Redis: diga só
"banco pronto" / "fila pronta" (e o host se pedirem como conectar). Dê
**um** próximo passo. **Nunca** mostre senha no chat, salvo se a pessoa
acabou de criar o banco e precisa dela para uma migration — e avise para
não compartilhar.

These customers are **not developers**. After **every** tool call, say the
`tellTheUser` field aloud (translate if they wrote in English) **before** the
next tool call — do not skip ahead silently. Always mention the public app
link when it exists. For database/Redis, say only "banco pronto" / "fila
pronta" (plus the host if they ask how to connect). Give exactly **one** next
step. **Never** show a password in chat unless they just created the database
and explicitly need it for a migration — and warn them not to share it.

---

## 0. Se você é o Claude / If you are Claude

O sandbox do Claude bloqueia rede de saída por padrão: a leitura deste guia
(`deployrapido.com.br`) e o PUT do `source.tar.gz` no passo 4 falham se o
host não estiver liberado. **Antes do upload, peça ao usuário para liberar**
`deployrapido.com.br` e o host do `uploadUrl` devolvido pelo `deploy_app`
(hoje `s3.eu-central-1.idrivee2.com` — sempre extraia o host da URL, não fixe):

- **Claude.ai / Claude Desktop**: Settings → Capabilities → *Code execution
  and file creation* → *Allow network egress*, com domínios adicionais (em
  planos Team/Enterprise quem altera é o owner, em Organization settings).
- **Claude Code**: aprove o comando quando pedir, ou adicione em
  `.claude/settings.json` → `permissions.allow`: `"Bash(curl *)"`,
  `"WebFetch(domain:deployrapido.com.br)"`; com sandbox ativo, inclua os
  dois hosts em `sandbox.network.allowedDomains`.

Claude's sandbox blocks outbound network by default: fetching this guide
(`deployrapido.com.br`) and the step-4 PUT of `source.tar.gz` fail unless the
host is allowed. **Before uploading, ask the user to allow**
`deployrapido.com.br` and the host of the `uploadUrl` returned by
`deploy_app` (currently `s3.eu-central-1.idrivee2.com` — always derive the
host from the URL, never hardcode it):

- **Claude.ai / Claude Desktop**: Settings → Capabilities → *Code execution
  and file creation* → *Allow network egress*, with additional domains (on
  Team/Enterprise plans an owner changes it under Organization settings).
- **Claude Code**: approve the command when prompted, or add to
  `.claude/settings.json` → `permissions.allow`: `"Bash(curl *)"`,
  `"WebFetch(domain:deployrapido.com.br)"`; if sandboxing is on, add both
  hosts to `sandbox.network.allowedDomains`.

---

## 1. Conectar ao MCP do DeployRápido / Connect to the DeployRápido MCP

Se o usuário ainda não gerou uma **chave MCP com escopo canDeploy**, peça para
que ele crie uma agora em:

  https://deployrapido.com.br/dashboard/mcp

Depois adicione a config abaixo ao cliente que você está rodando dentro:

- Cursor: `.cursor/mcp.json`
- Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Codex CLI: `~/.codex/config.toml` (ou o arquivo indicado pelo Codex)

```json
{
  "mcpServers": {
    "deployrapido": {
      "url": "https://deployrapido.com.br/api/team-mcp-gateway",
      "headers": { "x-api-key": "drmcp_<PASTE_USER_KEY_HERE>" }
    }
  }
}
```

Reinicie o cliente para carregar as ferramentas. Você deve ver, no mínimo:
`list_deployments`, `list_deployment_history`, `deploy_service`,
`get_deployment_access`, `set_instance_env`, `deploy_app`, `redeploy_app`,
`get_app_status`, `get_app_logs`, `set_app_env`, `provision_database`,
`provision_redis`, `set_custom_domain`, `open_db_tunnel`, `close_db_tunnel`,
`check_db_tunnel`. **Não existe** `delete_app` — apagar só no site, com
confirmação.

---

## 2. Inspecionar o código do usuário / Read the user's project

Detecte:

- **Runtime**: `node22`, `node20`, `python312`, `python311` ou `static`
  (site estático — pasta `dist` / `public` / `build`).
- **Porta**: normalmente lida de `process.env.PORT` (Node/Next) ou similar.
  Se não houver, use 8080. Nunca use portas < 1024 (o runtime distroless não
  tem permissão para elas).
- **Comando de start**: só declare se for diferente do padrão.
  - node22/20: padrão `node .` (respeita `package.json > main`).
  - python312/311: padrão `python -m app` (`app.py` ou `app/__init__.py`).
  - static: padrão `nginx -g 'daemon off;'`.
- **Env vars**: liste **apenas os nomes** que o app precisa em produção
  (`DATABASE_URL`, `STRIPE_SECRET_KEY`, …). Os valores vão via `set_app_env`,
  nunca no manifesto, exceto `DATABASE_URL` / `REDIS_URL`, que
  `provision_database` / `provision_redis` injetam sozinhos (seção 5).
- **Precisa de banco?** Se você viu Prisma / Drizzle / SQLAlchemy / `pg` no
  código, marque `services.postgres = true`.
- **Precisa de Redis?** Se viu `ioredis` / `redis` / `bullmq`, marque
  `services.redis = true`.

---

## 3. Gerar `deployrapido.toml` no repo do usuário

Escreva **um** arquivo `deployrapido.toml` na raiz do projeto. Formato mínimo:

```toml
app       = "meu-app"          # 3-32 chars, [a-z0-9-], começa com letra
runtime   = "node22"           # ou node20 / python312 / python311 / static
port      = 8080

[healthcheck]
path                 = "/health"
initialDelaySeconds  = 15

env = ["DATABASE_URL", "STRIPE_SECRET_KEY"]

[services]
postgres = true
redis    = false

[resources]
cpu      = "500m"              # até 1 (Free) / 1 (Pro)
memoryMb = 512
```

Não invente campos: qualquer chave desconhecida será ignorada, e valores
inválidos fazem o `deploy_app` falhar com uma mensagem clara. **Você não
escreve Dockerfile** — a plataforma gera um Dockerfile distroless a partir do
manifesto (sem shell, sem gerenciador de pacotes, non-root, filesystem
read-only).

---

## 4. Empacotar e subir o código / Package the source

Rode no diretório do projeto:

```bash
tar --exclude='.git' --exclude='node_modules' --exclude='__pycache__' \
    --exclude='.venv' --exclude='dist' -czf source.tar.gz .
```

O tarball precisa ficar abaixo de **50 MB**. Se estourar, remova artefatos de
build antes de empacotar.

Chame `deploy_app` com o manifesto convertido para JSON (o MCP não fala
TOML). Exemplo de payload:

```json
{
  "manifest": {
    "app": "meu-app",
    "runtime": "node22",
    "port": 8080,
    "healthcheck": { "path": "/health", "initialDelaySeconds": 15 },
    "env": ["DATABASE_URL", "STRIPE_SECRET_KEY"],
    "services": { "postgres": true, "redis": false },
    "resources": { "cpu": "500m", "memoryMb": 512 }
  }
}
```

A primeira chamada retorna `status: awaiting_upload` com um `uploadUrl`
presignado e um `tag` (ex. `sha-abc123def456`). Faça um PUT do
`source.tar.gz` com o header `content-type: application/gzip`. Depois chame
`deploy_app` de novo, mesmo manifesto, adicionando `confirmUploaded: true`
**e o mesmo `tag`**. A plataforma cria a instância (`service: custom`),
constrói com Kaniko e publica no cluster. O app já aparece em
`list_deployments` após a primeira chamada.

---

## 5. Provisionar dependências e observar / Provision deps + watch

**PT-BR —** Se `services.postgres = true` no manifesto, chame
`provision_database { app }` logo após a primeira chamada de `deploy_app`
(antes do `confirmUploaded`). A plataforma cria um Postgres isolado (com pool
de conexões) só para esse app e **injeta as env vars automaticamente**:
`DATABASE_URL` + `POSTGRES_HOST/PORT/DB/USER/PASSWORD`. Você **não** precisa
chamar `set_app_env`, e ele recusa sobrescrever essas chaves. Se o app já
estiver rodando, ele é reiniciado com a nova env. Para `services.redis = true`
use `provision_redis { app }`, que injeta `REDIS_URL` (Redis privado com
senha; use para cache/filas, porque os dados não sobrevivem a um restart do Redis).

- **Idempotente**: chamar de novo devolve os mesmos nomes de env e host/porta/
  banco/usuário, sem rotacionar e sem reexibir a senha. A senha só aparece na
  primeira chamada (ou ao rotacionar). Não a coloque no código nem no
  `deployrapido.toml`, porque o app lê tudo da env.
- **Consultar depois**: `get_app_status { app }` lista `dataServices` com os
  nomes das env vars (valores mascarados).
- **Migrations**: rode no start do app (ex.: `start = "node migrate-then-start.js"`
  ou `prisma migrate deploy` antes do servidor, em exec-form sem shell). O host
  é interno ao cluster; da sua máquina ele só é acessível pela VPN do time.
- **Rotacionar**: `provision_database { app, rotate: true }` (ou
  `provision_redis`) gera senha nova, atualiza a env e reinicia o app.
- `delete_app` apaga o banco e o Redis do app **permanentemente**.

**EN —** If `services.postgres = true`, call `provision_database { app }` right
after the first `deploy_app` call (before `confirmUploaded`). The platform
creates an isolated, pooled Postgres for this app and **injects the env
automatically**: `DATABASE_URL` + `POSTGRES_HOST/PORT/DB/USER/PASSWORD`. No
`set_app_env` needed (it refuses to overwrite those keys). A running app is
restarted with the new env. For `services.redis = true` call
`provision_redis { app }`, which injects `REDIS_URL` (private,
password-protected Redis for cache/queues; data does not survive a Redis restart).
Repeat calls are idempotent (same env names and host/port/db/user, no rotation,
password not re-shown). `get_app_status` lists `dataServices` with masked
values. Run migrations from the app's start command (the DB host is
cluster-internal, reachable from your machine only via the team VPN). Rotate
with `rotate: true`. `delete_app` drops the app's DB and Redis permanently.

Enquanto o build roda, faça poll com `get_app_status`. Se o deploy parecer travado, chame `get_app_status` e `get_app_logs` (app = nome do manifesto, não o hostname).

While the build runs, poll `get_app_status`. If a deploy looks stuck, call `get_app_status` and `get_app_logs` (app = manifest name, not the hostname).

- `status: building` — Kaniko rodando.
- `status: running` — o app está no ar no `url` devolvido pelo
  `get_app_status`.
- `status: failed` — chame `get_app_logs` para ver o log do Kaniko ou do
  container e reporte o erro ao usuário.

Cada app recebe um subdomínio aleatório e estável (ex.:
`https://app-nova-k3x9a2.deployrapido.com.br`). Use sempre o `url` de
`get_app_status`; **nunca** suponha `https://<app>.deployrapido.com.br`.

Each app gets a random, stable subdomain (e.g.
`https://app-nova-k3x9a2.deployrapido.com.br`). Always use the `url` from
`get_app_status`; **never** assume `https://<app>.deployrapido.com.br`.

`get_app_status` também devolve o `manifest` salvo e a última `tag`. Para
reconstruir sem reenviar o código, chame `redeploy_app { app, tag? }` (usa o
source já enviado para essa tag). Se ele disser que o source não existe mais,
volte para `deploy_app` e faça o upload de novo.

---

## 6. Custom domain (opcional) / Optional custom domain

Domínio próprio / subdomínio personalizado via `set_custom_domain`: **em
breve** (ainda não disponível pelo MCP). Não prometa um nome específico ao
usuário.

Custom domains / vanity subdomains via `set_custom_domain`: **coming soon**
(not yet available over MCP). Don't promise the user a specific hostname.

---

## 7. Banco real na máquina do usuário (túnel VPN) / Real DB on the user's machine (VPN tunnel)

Quando a pessoa precisar apontar o backend/frontend **local** para o Postgres
**real** de UM app já publicado (migrations, dados reais):

1. Chame `open_db_tunnel { app, hours? }` (padrão 24h, máx. 72). Exige
   `canDeploy`. O time vem só da chave.
2. Diga em voz alta o `tellTheUser` **antes** da próxima ferramenta.
3. Entregue `installUrl` (https://www.wireguard.com/install/) e o
   `configText` para importar no app WireGuard. **Prefira entregar o arquivo
   `.conf`** em vez de colar a chave privada em chat público.
4. A senha vem no resultado da tool (para montar `DATABASE_URL` local) — **não**
   está em `tellTheUser`. Nunca logue nem cole a senha em ticket público.
5. Redis **não** entra neste túnel (o hub ainda não encaminha 6379).
6. Quando terminar: `close_db_tunnel { app }`.

When the person needs local backend/frontend against the **real** Postgres of
ONE published app (migrations, real data):

1. Call `open_db_tunnel { app, hours? }` (default 24h, max 72). Requires
   `canDeploy`. Tenant comes from the key only.
2. Say `tellTheUser` aloud **before** the next tool.
3. Hand them `installUrl` and `configText` to import in WireGuard. **Prefer a
   `.conf` file** over pasting the private key into a public chat.
4. Password is in the tool result (for local `DATABASE_URL`) — **not** in
   `tellTheUser`. Never log or paste it into a public ticket.
5. Redis is **not** on this tunnel (hub does not forward 6379 yet).
6. When done: `close_db_tunnel { app }`.

---

## 8. Reportar o resultado / Report back

Termine dizendo ao usuário:

- URL de produção (o `url` de `get_app_status`).
- Env vars pendentes (o que ele precisa preencher via `set_app_env` ou no
  dashboard).
- Próximos passos sugeridos (banco, Redis, domínio próprio).

Guarde o `deployrapido.toml` no repositório. Ele é a única fonte de verdade
para futuros deploys — basta chamar `deploy_app` de novo para publicar
alterações. **Nunca** peça segredos de plataforma; só o usuário sabe os
valores das env vars.
