SDK
cloudbroker-sdk é o SDK oficial (Node.js, publicado no npm sem scope — conta npm free não permite pacote scoped). Cobre dois casos de uso: Token Intelligence (custo de IA) e Cost per Feature (custo de nuvem por trecho de código).
npm install cloudbroker-sdk
Gere uma API Key do tipo SDK em app.cloudbroker.app.br → Configurações → aba Conta → CloudBroker SDK (só admin cria; o valor completo aparece uma única vez — ver Administração). Uma key SDK só envia eventos de custo; ela é recusada nas rotas de leitura.
Token Intelligence — custo de IA
Envolve seu client OpenAI (openai), Anthropic (@anthropic-ai/sdk) ou Gemini (@google/genai) — o provedor é detectado pelo client que você passa. Todas as chamadas continuam funcionando exatamente igual, o SDK só observa e registra o usage em background.
import OpenAI from "openai";
import { CloudBrokerAI } from "cloudbroker-sdk";
const openai = CloudBrokerAI.wrap(new OpenAI(), {
apiKey: "cb-key-xxxx",
product: "checkout",
team: "mobile", // opcional
});
const res = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Olá!" }],
});
Funciona igual com Anthropic (@anthropic-ai/sdk), e com stream: true — o SDK observa os chunks conforme você consome e registra o usage ao final, sem alterar o fluxo original.
Para somar o custo de IA ao trecho de código na visão Custo por Código, passe cbLabel no segundo argumento (create(params, { cbLabel: "checkout:resumo-pedido" })) ou use o middleware Express. O cbLabel nunca é repassado ao provedor.
O custo de OpenAI e Anthropic é estimado por tabela de preço local.
- Anthropic: modelo fora da tabela sai com
costUSD: nulleunpriced: true, com os tokens preservados — "custo não estimado", não "custo zero". Snapshot com sufixo de data não listado usa o preço do modelo base e é marcado como aproximado (approxPricing). - OpenAI: modelo fora da tabela ainda recebe o preço do
gpt-4o— trate esse valor como aproximação.
Cache de prompt (Anthropic): os tokens de cache entram no custo com o multiplicador da Anthropic — escrita de 5 min a 1,25×, escrita de 1 h a 2×, leitura a 0,1× o preço de entrada — e vão separados no evento (cacheWriteTokens, cacheReadTokens).
Gemini (@google/genai)
import { GoogleGenAI } from "@google/genai";
import { CloudBrokerAI } from "cloudbroker-sdk";
const gemini = CloudBrokerAI.wrap(new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY }), {
apiKey: "cb-key-xxxx", product: "checkout", team: "mobile",
});
await gemini.models.generateContent({
model: "gemini-2.5-flash", contents: "Olá!",
cbLabel: "checkout:resumo-pedido", // opcional — removido antes de ir ao Google
});
generateContent e generateContentStream são instrumentados. O custo do Gemini não é input × preço + output × preço — o SDK trata as particularidades do usageMetadata:
- Cache:
promptTokenCountjá inclui os tokens servidos de cache (cachedContentTokenCount), que têm preço próprio, bem menor. Só o restante é cobrado a preço cheio de input. - Thinking:
thoughtsTokenCountvem separado da resposta e é cobrado como output. - Faixas: o 2.5 Pro muda de preço acima de 200k tokens de prompt; áudio na entrada tem preço próprio nos modelos que o diferenciam.
- O custo é estimado pela tabela do paid tier publicada pelo Google — não foi calibrado contra fatura real. Quem está no free tier vê o valor que pagaria.
- Modelo fora da tabela não recebe preço de outro modelo: o evento sai com
costUSD: nulleunpriced: true, preservando os tokens. Isso significa "custo não estimado", não "custo zero". Variantes com sufixo (ex.:-preview-…) usam o preço do modelo base e são marcadas como aproximadas (approxPricing).
Análise de custo de PR (trackDeploy)
Apesar do nome, trackDeploy não registra um deploy: ele envia os dados da PR para POST /api/analyze/pr, que faz a análise de custo da mudança e, para provider: "github" (o padrão), publica o evento deployment.created (uma PR analisada, que pode ser mergeada dias depois ou nunca). Fire-and-forget, como o resto do SDK.
openai.trackDeploy({
repo: "myapp", branch: "main", prTitle: "feat: add new feature",
prUrl: "https://github.com/org/myapp/pull/42", author: "john.doe",
prNumber: 42, filesChanged: 5, linesAdded: 120, linesRemoved: 30,
});
Para registrar um deploy real (evento deployment.completed), use o passo de CI que chama POST /api/deploys/completed — veja Registrando deploys.
Cost per Feature — custo de nuvem por linha de código
Envolve um client do provedor de nuvem e, a cada chamada, estima o custo (requests + transferência) e registra um evento.
- AWS (
wrapAWS): S3 e DynamoDB, regiãous-east-2. - Azure (
wrapAzure): Blob Storage e Cosmos DB, regiãoeastus2— ver Azure.
Armazenamento GB-mês e atribuição automática por stack-trace estão fora do escopo atual.
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { CloudBrokerCloud } from "cloudbroker-sdk";
const s3 = CloudBrokerCloud.wrapAWS(new S3Client({ region: "us-east-2" }), {
apiKey: "cb-key-xxxx", product: "checkout", team: "mobile",
});
await s3.send(new PutObjectCommand({ Bucket: "b", Key: "nf.pdf", Body: buf }), {
cbLabel: "upload-nota-fiscal",
});
Cada chamada gera um evento com service, operation, product, team, label, requests, bytesIn/bytesOut, costUSD estimado e latencyMs.
Atribuição automática via middleware Express
Em vez de passar cbLabel em cada .send(), use o middleware — toda chamada de nuvem feita durante a requisição herda um label automaticamente via AsyncLocalStorage:
import { CloudBrokerCloud } from "cloudbroker-sdk";
app.use(CloudBrokerCloud.expressMiddleware({
team: (req) => (req.path.startsWith("/api/admin") ? "Plataforma" : "Produto"),
product: (req) => req.path.split("/")[2] || "core",
// label omitido = padrão: "MÉTODO /caminho" com ids trocados por ":id"
}));
Prioridade: cbLabel/cbProduct/cbTeam explícito no .send() > contexto do middleware > valor estático do wrapAWS > null. O middleware vale igual para wrapAzure e para o wrapper de IA.
Sem label, o trecho é MÉTODO /caminho com os segmentos que parecem identificador trocados por :id — número, UUID, hex longo, e-mail, token longo com dígito e data. GET /pedidos/123 vira GET /pedidos/:id e GET /clientes/maria@empresa.com vira GET /clientes/:id; no máximo 6 segmentos e 120 caracteres.
Até a 0.9.2 o padrão era o caminho cru: o middleware roda no app.use, antes de o Express casar a rota, então cada id virava um trecho diferente e ia gravado no rótulo. Se você usa a 0.9.2 ou anterior sem label, atualize para a 0.9.3.
Para montar o seu próprio rótulo, reaproveite routeLabel(req) ou normalizePath(caminho), exportados pelo pacote. Não use req.path cru.
Custo por execução e versão do código
Cada evento do wrapAWS/wrapAzure é uma chamada de API, não uma execução do trecho. Só com chamadas não dá para saber se o código passou a chamar mais ou se veio mais tráfego. O ExecutionCounter (versão 0.8.0 ou mais nova) conta as execuções e fecha essa conta: custo por execução e chamadas por execução.
import { CloudBrokerCloud, ExecutionCounter, normalizePath } from "cloudbroker-sdk";
const execucoes = new ExecutionCounter({ apiKey: process.env.CLOUDBROKER_API_KEY });
app.use(CloudBrokerCloud.expressMiddleware({
product: "checkout",
label: (req) => normalizePath(req.path).split("/").slice(0, 3).join("/"), // prefixo da rota, ids viram ":id"
executions: execucoes, // 1 execução por requisição
}));
// Fora do Express (jobs, filas, workers):
await CloudBrokerCloud.trackExecution("job:fechamento-mensal", () => fecharMes(), {
product: "financeiro", executions: execucoes,
});
- A contagem fica em memória, agregada por trecho, app, versão e hora, e sai em lote a cada 60 s para
POST /api/cloud/executions. Nada de rede no caminho da requisição. - Rótulo de baixa cardinalidade: acima de 200 combinações pendentes, o excedente é descartado e somado em
execucoes.dropped. - Versão do código: defina
CLOUDBROKER_VERSION(ex.: o SHA do build) ou passeversionaoExecutionCountere aowrapAWS/wrapAzure. Ela vai nas execuções e nas chamadas, para comparar o trecho antes e depois de um deploy pela versão que de fato rodou. - Falhas e duração (
0.9.1): sem mudar o código, o middleware e otrackExecutioninformam quantas execuções terminaram, quantas falharam (requisição com status 5xx; callback que lançou ou Promise rejeitada) e a duração somada. Requisição abortada antes de responder conta como execução, mas não como terminada — por isso taxa de falha e duração média são calculadas só sobre as terminadas. Versões anteriores do SDK não informam esses campos, e eles aparecem como "não medido", nunca como "0 falhas". - No shutdown:
await execucoes.flush(); execucoes.stop();.
O FinOps Advisor usa essa contagem para responder se o custo de um trecho mudou com os deploys. Comparação por deploy só sai com pelo menos 24 h e 30 execuções de cada lado; abaixo disso a resposta diz "volume insuficiente", sem percentual.
Azure — Blob Storage e Cosmos DB
O Azure SDK não tem um .send() único: o wrapAzure intercepta a cadeia de sub-clients (getContainerClient → getBlockBlobClient, database → container → items) e rastreia só os métodos de data-plane conhecidos. O cbLabel vai no objeto de options nativo do método.
import { BlobServiceClient } from "@azure/storage-blob";
import { CosmosClient } from "@azure/cosmos";
import { CloudBrokerCloud } from "cloudbroker-sdk";
const blobSvc = CloudBrokerCloud.wrapAzure(
BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION),
{ apiKey: "cb-key-xxxx", product: "checkout", team: "mobile" },
);
await blobSvc.getContainerClient("docs").getBlockBlobClient("nf.pdf")
.uploadData(buf, { cbLabel: "upload-nota-fiscal" });
const cosmos = CloudBrokerCloud.wrapAzure(new CosmosClient(conn), {
apiKey: "cb-key-xxxx", product: "checkout",
});
await cosmos.database("loja").container("pedidos").items
.create({ id: "123" }, { cbLabel: "criar-pedido" });
No Cosmos DB, as RUs usadas são as reais devolvidas pelo servidor (requestCharge); o que é estimado é o preço por RU.
- Cosmos: preço por RU assume o modelo serverless. Em throughput provisionado (manual ou autoscale) a Azure cobra RU/s reservada — esse modelo não é representado, e o custo calculado não corresponde à fatura.
- Blob: assume tier Hot e redundância LRS. Cool/Archive e GRS/ZRS têm outro preço.
- Transferência de saída: cobrada sobre todo
bytesOut; para app na mesma região do Storage, superestima. - Região: só
eastus2tem tabela de preço. Em outra região o evento é registrado comcostUSD: null(billingUnit: "*-region-unpriced") — aparece no relatório, sem custo inventado. - Não calibrado contra fatura Azure real. Trate como ordem de grandeza até comparar com a reconciliação na sua conta.
Reconciliação com a fatura real
O custo estimado pelo SDK é comparado automaticamente contra o custo real do provedor (Cost Explorer na AWS, Cost Management no Azure) — a cobertura mede quanto do custo real os trechos instrumentados explicam (estimado sempre ≤ real, nunca o inverso).
Garantias
- Fire-and-forget: se o CloudBroker estiver fora do ar, sua aplicação não é afetada
- Zero latência adicional: tracking acontece em background
- Retry automático: eventos com falha são reenviados em 30s
- Reenvio não duplica custo (
0.9.2): cada evento recebe umeventId(ev_+ UUID v7) ao entrar na fila, e o retry reenvia o mesmo id. O CloudBroker reconhece o evento já gravado e não o conta de novo - Fila local: até 500 eventos em memória se o servidor estiver indisponível. Acima disso o evento é descartado e contado:
client.stats()devolveaiDropped/cloudDropped, e a contagem também vai ao CloudBroker no próximo lote - Versão do preço (
0.9.2): cada evento levapricingVersion, a versão da tabela de preço que estimou o custo
MCP
O cloudbroker-mcp é um servidor MCP (Model Context Protocol) que conecta seu assistente de IA — Claude Desktop, Cursor, ChatGPT — direto aos seus dados do CloudBroker, sem abrir a console.
{
"mcpServers": {
"cloudbroker": {
"command": "npx",
"args": ["-y", "cloudbroker-mcp"],
"env": { "CLOUDBROKER_API_KEY": "cb_live_xxx" }
}
}
}
São 23 tools (versão 0.10.0).
Leitura (17): get_cost_overview, list_apps, list_groups, get_forecast, get_ai_usage, get_recommendations, get_anomalies, analyze_cost_increase, compare_periods, get_deploys, list_deploys, get_top_services, get_saas_costs, get_daily_costs, list_findings, get_shadow_report, discovery_scan.
list_deploys lista os deploys que de fato aconteceram (registrados pelo passo de CI) e o que cada um mudou — ver Registrando deploys. get_deploys lista análises de PR com custo estimado, não deploys.
Escrita (6) — o cliente MCP pede aprovação a cada chamada, e o backend exige papel admin: set_budget, create_app, update_app, create_group, update_group, review_finding.
A autenticação é via API Key do tipo MCP (Configurações → aba Conta → API Key Pessoal (MCP)), enviada no header x-api-key — mesmos endpoints REST usados pela interface. Qualquer papel pode criar a sua; o que ela consegue fazer segue o papel atual de quem a criou (um viewer usa as tools de leitura, e as de escrita respondem 403). Uma key MCP é recusada nas rotas de ingestão do SDK. Detalhes em Administração.