MCP Bling
by dgsidney
README.md
# MCP Bling
> Servidor **MCP (Model Context Protocol) remoto** que expõe a [API v3 do Bling](https://developer.bling.com.br/home) a agentes de IA — multi-tenant, stateless e rodando na edge com **Cloudflare Workers**.




**Stack:** TypeScript · [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol) · Cloudflare Workers · Hono · Zod
Ferramenta de **desenvolvimento/debug**: o time aponta o MCP para a conta de **qualquer cliente** (passando o `access_token` daquele cliente) e trabalha com dados reais — NF-e, etiquetas, particularidades — direto de um assistente de IA, sem o cliente precisar reautenticar nada.
## Destaques de engenharia
- 🔑 **Modelo read-through de token** — o Worker é *stateless* e nunca guarda nem renova credenciais. Decisão deliberada: o Bling rotaciona o `refresh_token` a cada refresh, então renovar aqui invalidaria o token do app em produção do cliente. O MCP só repassa um `access_token` já válido.
- 🔒 **Somente-leitura por padrão** — as tools de escrita nem aparecem para o agente; habilitar escrita exige um header explícito, e há um **kill-switch global** (`FORCE_READ_ONLY`) no Worker.
- 🧩 **CRUD genérico tipado** — 6 tools cobrem **~40 módulos** do Bling (produtos, pedidos, NF-e/NFC-e/NFS-e, estoque, financeiro…) via um enum `recurso`, em vez de dezenas de tools redundantes. Uma "escotilha" (`bling_request`) cobre sub-rotas especiais.
- ☁️ **Edge nativo** — deploy global em Cloudflare Workers, transporte Streamable HTTP, custom domain e observabilidade habilitada.
## Sumário
- [Modelo de auth](#modelo-de-auth)
- [Endpoints](#endpoints)
- [Arquivos](#arquivos)
- [Setup](#setup)
- [Uso (Claude Code / VS Code)](#uso-claude-code--vs-code)
- [Tools disponíveis](#tools-disponíveis)
- [Modo somente-leitura (padrão)](#modo-somente-leitura-padrão)
- [Hardening (próximos passos sugeridos)](#hardening-próximos-passos-sugeridos)
---
- **Transporte:** Streamable HTTP (`/mcp`)
- **Auth do chamador:** service token compartilhado (`Authorization: Bearer …`)
- **Token do cliente:** header `X-Bling-Access-Token` (pass-through, **somente leitura** — o MCP nunca renova)
- **Stateless:** sem Durable Objects, sem KV, sem credenciais do Bling no Worker
> **Produção:** `https://mcp-bling.bconnector.com.br` — endpoint MCP em `…/mcp`.
## Modelo de auth
A API v3 do Bling é **OAuth 2.0** (a apikey da v2 foi desativada em ago/2024). Cada app de vocês
já guarda e renova o token de cada cliente. O MCP **não gerencia token nenhum**: o chamador envia
um `access_token` válido e o MCP só o repassa para a API do Bling.
```
você (Claude Code) ──(Bearer SERVICE_TOKEN + X-Bling-Access-Token)──► MCP Worker ──► API Bling do cliente
```
### Por que o MCP não renova o token
O Bling **rotaciona o `refresh_token` a cada refresh**. Como cada app de vocês tem a própria cópia
do token do cliente, se o MCP renovasse, **invalidaria o token do app em produção** daquele cliente.
Por isso o MCP é **read-through**: recebe um `access_token` já válido (vence em ~6h) e nunca renova.
Quem renova continua sendo o app dono do token.
## Endpoints
| Método | Rota | Auth | Descrição |
|---|---|---|---|
| GET | `/` | — | Landing/health |
| POST | `/mcp` | `Bearer SERVICE_TOKEN` + `X-Bling-Access-Token` | Endpoint MCP (Streamable HTTP) |
## Arquivos
| Arquivo | Papel |
|---|---|
| `src/index.ts` | Worker: valida o service token e monta o handler MCP por requisição |
| `src/tools.ts` | Tools MCP (CRUD genérico por `recurso` + escotilha) |
| `src/bling-client.ts` | `createRequester(accessToken)` — chamadas à API com o token recebido |
## Setup
```bash
npm install
npx wrangler secret put SERVICE_TOKEN # token compartilhado entre quem usa o MCP
npm run deploy
```
Para dev local: copie `.dev.vars.example` para `.dev.vars` e preencha o `SERVICE_TOKEN`.
## Uso (Claude Code / VS Code)
Pegue o **`access_token` vivo** do cliente-alvo (aquele que o app daquele cliente já usa) e configure:
```json
{
"mcpServers": {
"bling": {
"type": "http",
"url": "https://mcp-bling.bconnector.com.br/mcp",
"headers": {
"Authorization": "Bearer ${BLING_SERVICE_TOKEN}",
"X-Bling-Access-Token": "${BLING_ACCESS_TOKEN}"
}
}
}
}
```
> Use `${...}` para o Claude Code expandir variáveis de ambiente — assim você **não commita
> segredo**. Defina `BLING_SERVICE_TOKEN` (o service token) e `BLING_ACCESS_TOKEN` (o token do
> cliente do momento) no ambiente. O `access_token` vence em ~6h; quando expirar, atualize a env var
> com um novo (pego do app daquele cliente) e reconecte (`/mcp`).
**Testar no Inspector:** `npx @modelcontextprotocol/inspector` → Transport `Streamable HTTP`,
URL `…/mcp`, e em Headers adicione `Authorization: Bearer <SERVICE_TOKEN>` e
`X-Bling-Access-Token: <token>`.
## Tools disponíveis
São **6 tools**: 5 genéricas tipadas (CRUD) parametrizadas pelo `recurso`, + uma escotilha.
| Tool | Descrição |
|---|---|
| `bling_listar` | Lista (paginado) registros de um recurso, com `filtros` de query |
| `bling_obter` | Detalhe de um registro por ID |
| `bling_criar` | Cria um registro (POST) |
| `bling_atualizar` | Atualiza um registro (PUT, ou PATCH com `parcial=true`) |
| `bling_excluir` | Remove um registro (DELETE) |
| `bling_request` | **Escotilha**: sub-rotas/endpoints especiais (`/estoques/saldos`, etc.) |
O parâmetro **`recurso`** é um enum que cobre ~40 módulos do Bling (produtos, pedidos-vendas,
pedidos-compras, contatos, contas-pagar, contas-receber, contas-contabeis, caixas, nfe, nfce,
nfse, estoques, depositos, empresas, categorias-produtos, categorias-receitas-despesas,
canais-venda, formas-pagamentos, naturezas-operacoes, logisticas[-objetos/-remessas/-servicos],
ordens-producao, produtos-[estruturas/fornecedores/variacoes/lotes], propostas-comerciais,
anuncios[-categorias], grupos-produtos, campos-customizados, vendedores, notificacoes, borderos,
situacoes[-modulos/-transicoes], contratos, usuarios). O mapa recurso→path está em `src/tools.ts`.
Sincronizado com o changelog da API do Bling até **ago/2026** (novas APIs de caixas e bancos,
controle de lotes, anúncios/marketplaces e NFS-e; ver notas no `bling_request` sobre as *breaking
changes* de pedido de compra e documentos referenciados em notas fiscais).
## Modo somente-leitura (padrão)
Você opera sobre a conta de **produção** do cliente, então o MCP é **somente-leitura por padrão**:
expõe apenas `bling_listar`, `bling_obter` e `bling_request` (limitado a GET). As tools de escrita
nem aparecem.
Para habilitar escrita (`bling_criar`/`bling_atualizar`/`bling_excluir` e métodos não-GET no
`bling_request`), envie o header na conexão:
```
X-Bling-Allow-Write: true
```
**Kill-switch global:** defina o secret/var `FORCE_READ_ONLY=true` no Worker para travar tudo em
somente-leitura, ignorando o header (`npx wrangler secret put FORCE_READ_ONLY`).
> Mesmo com escrita habilitada: `bling_criar`/`bling_atualizar`/`bling_excluir` **alteram dados
> reais** do cliente. Use com cuidado.
## Hardening (próximos passos sugeridos)
- **Chave por dev/app** em vez de service token único (auditoria/revogação).
- **Espelho de token central (Supabase)**: cada app grava o `access_token` atual numa tabela
read-only; o MCP passa a ler por `X-Tenant-Id` e você escolhe o cliente sem colar token na mão.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues