@comando.one/mcp-server 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +141 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -51,7 +51,7 @@ Ambos expõem o **mesmo conjunto de ferramentas** (131 curadas + `lookups` + `co
51
51
 
52
52
  ## Como funciona
53
53
 
54
- - **131 tools curadas** (gerados a partir do OpenAPI canônico, zero drift) cobrindo as operações de maior valor — `customers_list`, `invoices_create`/`invoices_delete`, `charges_create`, `nfse_emit`, `payouts_create`, `contracts_pause`/`contracts_resume`, `service_categories_create`, `reports_aging_actions`/`reports_cashflow_forecast`, `audit_timeline`, `documents_analyze` (lê boleto/guia/NF anexada e extrai valor, vencimento e emitente), `cost_centers_create`, etc.
54
+ - **134 tools curadas** (gerados a partir do OpenAPI canônico, zero drift) cobrindo as operações de maior valor — `customers_list`, `invoices_create`/`invoices_delete`, `charges_create`, `nfse_emit`, `payouts_create`, `contracts_pause`/`contracts_resume`, `service_categories_create`, `reports_aging_actions`/`reports_cashflow_forecast`, `audit_timeline`, `documents_analyze` (lê boleto/guia/NF anexada e extrai valor, vencimento e emitente), `cost_centers_create`, `invoices_settle_charge` (dá baixa numa parcela — registra o recebimento), etc.
55
55
  - **`comando_request`** — escape hatch genérico (`method`, `path`, `query`, `body`) que cobre 100% dos endpoints restantes.
56
56
  - **`lookups`** — catálogos read-only (centros de custo, condições/métodos de pagamento, naturezas, contas bancárias).
57
57
  - **`whoami`** — identidade, empresas acessíveis e scopes da chave.
package/dist/index.js CHANGED
@@ -144,6 +144,9 @@ var MANIFEST = [
144
144
  { operationId: "createInvoice", tool: "invoices_create", write: true },
145
145
  { operationId: "cancelInvoice", tool: "invoices_cancel", write: true, destructive: true },
146
146
  { operationId: "sendInvoiceSecondCopy", tool: "invoices_send", write: true },
147
+ { operationId: "setInvoiceAutoSend", tool: "invoices_set_auto_send", write: true },
148
+ { operationId: "listInvoiceCharges", tool: "invoices_list_charges" },
149
+ { operationId: "settleInvoiceCharge", tool: "invoices_settle_charge", write: true },
147
150
  { operationId: "generateContractInvoices", tool: "contracts_generate_invoices", write: true },
148
151
  // ----- Cobranças -----
149
152
  { operationId: "listCharges", tool: "charges_list" },
@@ -1154,13 +1157,13 @@ var SCHEMAS = {
1154
1157
  },
1155
1158
  InvoiceCreate: {
1156
1159
  type: "object",
1157
- required: ["customer_id"],
1160
+ required: ["customer_id", "due_date"],
1158
1161
  properties: {
1159
1162
  customer_id: { type: "string", format: "uuid" },
1160
1163
  title: { type: "string" },
1161
1164
  amount: { type: "number", description: "Ignorado se 'items' for enviado (calculado dos itens)" },
1162
1165
  status: { type: "string", enum: ["rascunho", "enviada", "aceita", "ativa"], default: "rascunho" },
1163
- due_date: { type: "string", format: "date" },
1166
+ due_date: { type: "string", format: "date", description: "Obrigat\xF3rio. A fatura nasce com uma parcela 1/1 neste vencimento \u2014 sem parcela ningu\xE9m cobra (o dispatch seleciona por parcela). O envio autom\xE1tico nasce desligado; para ligar use PATCH /v1/invoices/{id}/auto-send." },
1164
1167
  items: {
1165
1168
  type: "array",
1166
1169
  items: {
@@ -1224,7 +1227,7 @@ var SCHEMAS = {
1224
1227
  type: "object",
1225
1228
  required: ["motivo", "justificativa"],
1226
1229
  properties: {
1227
- motivo: { type: "integer", enum: [1, 2, 3, 4, 9], description: "1=n\xE3o realizada, 2=duplica\xE7\xE3o, 3=erro de emiss\xE3o, 4=servi\xE7o n\xE3o prestado, 9=outros" },
1230
+ motivo: { type: "integer", enum: [1, 2, 3, 4, 9], description: "1=erro na emiss\xE3o, 2=servi\xE7o n\xE3o prestado, 3=duplicidade de emiss\xE3o, 4=decis\xE3o judicial, 9=outros. Use o c\xF3digo pelo R\xD3TULO, n\xE3o pela ordem." },
1228
1231
  justificativa: { type: "string", minLength: 15, maxLength: 255 }
1229
1232
  }
1230
1233
  },
@@ -1827,7 +1830,7 @@ var OPENAPI_SPEC = {
1827
1830
  tags: ["Propostas"],
1828
1831
  summary: "Enviar proposta",
1829
1832
  operationId: "sendProposal",
1830
- description: "Marca a proposta como enviada.",
1833
+ description: "Apenas MUDA O STATUS da proposta para 'enviada'. N\xC3O dispara e-mail \u2014 n\xE3o existe envio de proposta por e-mail no sistema (conferido: email_schedules s\xF3 tem invoice e charge). Depois de chamar, n\xE3o afirme ao usu\xE1rio que a proposta foi enviada ao cliente.",
1831
1834
  security: [{ ApiKeyAuth: ["proposals:send"] }],
1832
1835
  responses: { "200": { description: "Proposta enviada", content: { "application/json": { schema: { $ref: "#/components/schemas/Proposal" } } } }, "404": errorResponse("Proposta n\xE3o encontrada"), "409": errorResponse("Proposta cancelada"), ...COMMON_ERRORS }
1833
1836
  }
@@ -2038,7 +2041,8 @@ var OPENAPI_SPEC = {
2038
2041
  tags: ["Contratos"],
2039
2042
  summary: "Gerar as faturas devidas do contrato",
2040
2043
  operationId: "generateContractInvoices",
2041
- description: "Gera as faturas em aberto do contrato conforme a periodicidade dele. A regra de compet\xEAncia (e a prote\xE7\xE3o contra duplicar fatura do mesmo per\xEDodo) \xE9 da rotina de gera\xE7\xE3o, n\xE3o deste endpoint.",
2044
+ description: "Gera fatura do contrato. Por padr\xE3o (mode=extra) cria uma fatura AVULSA fora do ciclo: n\xE3o avan\xE7a o agendamento, ent\xE3o chamar duas vezes gera DUAS faturas. Use mode=cycle para antecipar a fatura do ciclo corrente \u2014 \xE9 esse modo que avan\xE7a next_generation_date e evita duplicar a compet\xEAncia. mode=skip pula o ciclo sem gerar nada. Contrato cancelado ou suspenso \xE9 recusado.",
2045
+ requestBody: { required: false, content: { "application/json": { schema: { type: "object", properties: { mode: { type: "string", enum: ["extra", "cycle", "skip"], default: "extra", description: "extra = avulsa (repet\xEDvel) \xB7 cycle = fatura do ciclo, avan\xE7a o agendamento \xB7 skip = s\xF3 avan\xE7a" } } } } } },
2042
2046
  security: [{ ApiKeyAuth: ["invoices:create"] }],
2043
2047
  responses: {
2044
2048
  "200": {
@@ -2059,6 +2063,105 @@ var OPENAPI_SPEC = {
2059
2063
  }
2060
2064
  }
2061
2065
  },
2066
+ "/invoices/{id}/auto-send": {
2067
+ parameters: [{ name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } }],
2068
+ patch: {
2069
+ tags: ["Faturas"],
2070
+ summary: "Programar a cobran\xE7a autom\xE1tica",
2071
+ operationId: "setInvoiceAutoSend",
2072
+ description: "Liga ou desliga o envio AUTOM\xC1TICO da cobran\xE7a nas parcelas em aberto da fatura. Sem isso a cobran\xE7a nunca sai sozinha: o disparo di\xE1rio s\xF3 envia com `auto_send_enabled` ligado na parcela, e a fatura criada por API nasce desligada. `days_before` = quantos dias ANTES do vencimento disparar (0 = no dia). Desligar devolve o canal para `manual`.",
2073
+ security: [{ ApiKeyAuth: ["invoices:send"] }],
2074
+ requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: {
2075
+ enabled: { type: "boolean", default: true, description: "false desliga e volta o canal para manual" },
2076
+ channel: { type: "string", enum: ["manual", "email", "whatsapp", "ambos"], default: "email", description: '"ambos" = e-mail E WhatsApp' },
2077
+ days_before: { type: "integer", minimum: 0, maximum: 90, default: 0, description: "dias ANTES do vencimento" }
2078
+ } } } } },
2079
+ responses: {
2080
+ "200": { description: "Programa\xE7\xE3o aplicada", content: { "application/json": { schema: { type: "object", properties: {
2081
+ invoice_id: { type: "string" },
2082
+ auto_send_enabled: { type: "boolean" },
2083
+ send_channel: { type: "string" },
2084
+ send_days_before: { type: "integer" },
2085
+ parcelas_afetadas: { type: "integer" }
2086
+ } } } } },
2087
+ "400": errorResponse("channel ou days_before inv\xE1lidos"),
2088
+ "404": errorResponse("Fatura n\xE3o encontrada"),
2089
+ "409": errorResponse("Fatura sem parcela em aberto"),
2090
+ ...COMMON_ERRORS
2091
+ }
2092
+ }
2093
+ },
2094
+ "/invoices/{id}/charges": {
2095
+ parameters: [{ name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } }],
2096
+ get: {
2097
+ tags: ["Faturas"],
2098
+ summary: "Listar as parcelas da fatura",
2099
+ operationId: "listInvoiceCharges",
2100
+ description: "As PARCELAS (invoice_charges) da fatura, com quanto j\xE1 foi pago e quanto falta em cada uma. N\xE3o confunda com `/v1/charges`, que \xE9 a cobran\xE7a do GATEWAY (Pix/boleto emitido no banco): a parcela \xE9 o que manda no vencimento e no status de pagamento da fatura. Use esta rota para descobrir o `charge_id` antes de dar baixa.",
2101
+ security: [{ ApiKeyAuth: ["invoices:read"] }],
2102
+ responses: { "200": { description: "Parcelas da fatura", content: { "application/json": { schema: { type: "object", properties: {
2103
+ invoice_id: { type: "string" },
2104
+ invoice_number: { type: "string" },
2105
+ invoice_status: { type: "string" },
2106
+ invoice_payment_status: { type: "string" },
2107
+ total: { type: "integer" },
2108
+ charges: { type: "array", items: { type: "object", properties: {
2109
+ id: { type: "string" },
2110
+ sequence: { type: "integer" },
2111
+ description: { type: "string" },
2112
+ amount: { type: "number" },
2113
+ due_date: { type: "string", format: "date" },
2114
+ payment_status: { type: "string", enum: ["pendente", "parcial", "pago", "vencido"] },
2115
+ paid_amount: { type: "number", description: "soma j\xE1 baixada nesta parcela" },
2116
+ remaining_amount: { type: "number", description: "saldo a receber" },
2117
+ auto_send_enabled: { type: "boolean" },
2118
+ send_channel: { type: "string" }
2119
+ } } }
2120
+ } } } } }, "404": errorResponse("Fatura n\xE3o encontrada"), ...COMMON_ERRORS }
2121
+ }
2122
+ },
2123
+ "/invoices/{id}/charges/{charge_id}/pay": {
2124
+ parameters: [
2125
+ { name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } },
2126
+ { name: "charge_id", in: "path", required: true, schema: { type: "string", format: "uuid" }, description: "id da PARCELA (veja GET /v1/invoices/{id}/charges)" }
2127
+ ],
2128
+ post: {
2129
+ tags: ["Faturas"],
2130
+ summary: "Dar baixa em uma parcela",
2131
+ operationId: "settleInvoiceCharge",
2132
+ description: "Registra o recebimento de uma parcela: cria o pagamento e o aloca \xE0 parcela. A parcela e a fatura passam a `pago` (ou `parcial`) pelo pr\xF3prio banco. Sem `amount`, quita o saldo restante; sem `payment_date`, usa hoje (America/Sao_Paulo). Baixa PARCIAL \xE9 permitida; acima do saldo, n\xE3o. Campo de corpo que a rota n\xE3o conhece devolve 400 em vez de ser ignorado.",
2133
+ security: [{ ApiKeyAuth: ["invoices:settle"] }],
2134
+ requestBody: { required: false, content: { "application/json": { schema: { type: "object", properties: {
2135
+ amount: { type: "number", description: "Valor recebido. Omitido, quita o saldo restante da parcela." },
2136
+ payment_date: { type: "string", format: "date", description: "Data do recebimento (YYYY-MM-DD). Padr\xE3o: hoje." },
2137
+ receipt_method_id: { type: "string", format: "uuid", description: "Forma de RECEBIMENTO (GET /v1/payment-methods). Aceita tamb\xE9m o nome `payment_method_id`." },
2138
+ bank_account_id: { type: "string", format: "uuid", description: "Conta que recebeu (GET /v1/bank-accounts)." },
2139
+ notes: { type: "string", description: "Observa\xE7\xE3o livre do pagamento." }
2140
+ } } } } },
2141
+ responses: {
2142
+ "201": { description: "Baixa registrada", content: { "application/json": { schema: { type: "object", properties: {
2143
+ payment_id: { type: "string" },
2144
+ invoice_id: { type: "string" },
2145
+ invoice_number: { type: "string" },
2146
+ invoice_payment_status: { type: "string" },
2147
+ amount: { type: "number" },
2148
+ payment_date: { type: "string", format: "date" },
2149
+ charge: { type: "object", properties: {
2150
+ id: { type: "string" },
2151
+ sequence: { type: "integer" },
2152
+ amount: { type: "number" },
2153
+ paid_amount: { type: "number" },
2154
+ remaining_amount: { type: "number" },
2155
+ payment_status: { type: "string" }
2156
+ } }
2157
+ } } } } },
2158
+ "400": errorResponse("amount, payment_date ou campo de corpo inv\xE1lido"),
2159
+ "404": errorResponse("Fatura ou parcela n\xE3o encontrada"),
2160
+ "409": errorResponse("Fatura cancelada, parcela j\xE1 quitada ou valor acima do saldo"),
2161
+ ...COMMON_ERRORS
2162
+ }
2163
+ }
2164
+ },
2062
2165
  "/invoices/{id}/send": {
2063
2166
  parameters: [{ name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } }],
2064
2167
  post: {
@@ -2510,6 +2613,7 @@ var OPENAPI_SPEC = {
2510
2613
  { name: "include_shared", in: "query", schema: { type: "boolean", default: true }, description: "Incluir clientes compartilhados" },
2511
2614
  { name: "customer_id", in: "query", schema: { type: "string", format: "uuid" } },
2512
2615
  { name: "search", in: "query", schema: { type: "string" } },
2616
+ { name: "company_id", in: "query", schema: { type: "string", format: "uuid" }, description: "Restringe a UMA empresa: a sua, ou a dona de um cliente compartilhado. Aplicado SOBRE o escopo permitido \u2014 nunca amplia acesso." },
2513
2617
  { name: "query", in: "query", schema: { type: "string" }, description: "Apelido de 'search' \u2014 o c\xF3digo j\xE1 aceitava os dois nomes; agora est\xE1 documentado." }
2514
2618
  ],
2515
2619
  responses: { "200": { description: "Faturas em atraso + a\xE7\xF5es recomendadas", content: { "application/json": { schema: { type: "object", properties: {
@@ -2520,6 +2624,8 @@ var OPENAPI_SPEC = {
2520
2624
  total_invoiced_amount: { type: "number", description: "Soma do valor cheio das mesmas faturas, ignorando pagamentos parciais." },
2521
2625
  actions: { type: "array", items: { type: "object", properties: {
2522
2626
  invoice_id: { type: "string", format: "uuid" },
2627
+ company_id: { type: "string", format: "uuid", description: "Empresa DONA da fatura. Com clientes compartilhados a lista mistura empresas; sem este campo n\xE3o d\xE1 para saber de quem \xE9 cada linha." },
2628
+ company_name: { type: "string", nullable: true },
2523
2629
  invoice_number: { type: "string", nullable: true },
2524
2630
  title: { type: "string", nullable: true },
2525
2631
  customer_id: { type: "string", format: "uuid" },
@@ -2541,6 +2647,7 @@ var OPENAPI_SPEC = {
2541
2647
  tags: ["Relat\xF3rios"],
2542
2648
  summary: "Previs\xE3o de fluxo de caixa",
2543
2649
  operationId: "reportCashflowForecast",
2650
+ description: "Projeta o caixa dia a dia pelas parcelas em aberto. `total_inflow`/`total_outflow` e a `timeline` usam o **saldo devedor** de cada parcela (valor menos o que j\xE1 foi baixado) \u2014 \xE9 o que de fato entra e sai; `total_inflow_full`/`total_outflow_full` trazem o valor cheio. Parcela de fatura/compra **cancelada ou em rascunho** n\xE3o entra: o t\xEDtulo foi desfeito ou ainda n\xE3o foi emitido.",
2544
2651
  security: [{ ApiKeyAuth: ["finance:read"] }],
2545
2652
  parameters: [
2546
2653
  { name: "horizon_days", in: "query", schema: { type: "integer", minimum: 1, maximum: 120, default: 30 } },
@@ -2548,7 +2655,7 @@ var OPENAPI_SPEC = {
2548
2655
  { name: "include_receivables", in: "query", schema: { type: "boolean", default: true } },
2549
2656
  { name: "include_payables", in: "query", schema: { type: "boolean", default: true } }
2550
2657
  ],
2551
- responses: { "200": { description: "Proje\xE7\xE3o di\xE1ria de caixa (timeline)", content: { "application/json": { schema: { type: "object" } } } }, ...COMMON_ERRORS }
2658
+ responses: { "200": { description: "Proje\xE7\xE3o di\xE1ria de caixa (timeline). Cada linha de `receivables_preview`/`payables_preview` traz `amount` (cheio), `paid_amount` e `outstanding_amount` (o que falta).", content: { "application/json": { schema: { type: "object" } } } }, ...COMMON_ERRORS }
2552
2659
  }
2553
2660
  },
2554
2661
  // -----------------------------------------------------------------------
@@ -2574,7 +2681,7 @@ var OPENAPI_SPEC = {
2574
2681
  tags: ["NFS-e"],
2575
2682
  summary: "Emitir NFS-e",
2576
2683
  operationId: "emitNfse",
2577
- description: "Emite uma NFS-e a partir de uma fatura. O m\xE9todo (certificado/portal) vem da configura\xE7\xE3o da empresa. Pode levar alguns segundos.",
2684
+ description: "Emite uma NFS-e a partir de uma fatura. O m\xE9todo (certificado/portal) vem da configura\xE7\xE3o da empresa. Pode levar alguns segundos. **201** = autorizada ou em transmiss\xE3o; **422** = o fisco REJEITOU (o motivo vem em `message`) \u2014 nesse caso n\xE3o existe nota, n\xE3o afirme ao usu\xE1rio que foi emitida.",
2578
2685
  security: [{ ApiKeyAuth: ["nfse:emitir"] }],
2579
2686
  requestBody: { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/NfseEmit" } } } },
2580
2687
  responses: { "201": { description: "Emiss\xE3o solicitada", content: { "application/json": { schema: { type: "object", properties: { invoice_id: { type: "string" }, status: { type: "string" }, numero_nfse: { type: ["string", "null"] }, chave_acesso: { type: ["string", "null"] }, history_id: { type: ["string", "null"] } } } } } }, "400": errorResponse("invoice_id ausente"), "404": errorResponse("Fatura n\xE3o encontrada"), "422": errorResponse("NFS-e n\xE3o configurada"), "502": errorResponse("Erro do provedor de NFS-e"), ...COMMON_ERRORS }
@@ -2621,7 +2728,7 @@ var OPENAPI_SPEC = {
2621
2728
  tags: ["NFS-e"],
2622
2729
  summary: "URLs assinadas do XML e DANFSE",
2623
2730
  operationId: "getNfseFiles",
2624
- description: "Retorna URLs tempor\xE1rias (30 min) para download do XML e do PDF (DANFSE). Gera o PDF sob demanda se a nota estiver autorizada/cancelada.",
2731
+ description: "Retorna URLs tempor\xE1rias (30 min). `xml_signed_url` = XML autorizado. `danfse_signed_url` = DANFSe em PDF (gerada sob demanda a partir do XML se ainda n\xE3o existir). `danfse_html_signed_url` = HTML OFICIAL publicado pelo ADN, quando dispon\xEDvel \u2014 \xE9 outro documento, n\xE3o uma vers\xE3o pior do PDF. `danfse_formato` diz o que veio em `danfse_signed_url` (pdf|html).",
2625
2732
  security: [{ ApiKeyAuth: ["nfse:read"] }],
2626
2733
  responses: { "200": { description: "URLs assinadas", content: { "application/json": { schema: { type: "object", properties: { id: { type: "string" }, invoice_id: { type: "string" }, status: { type: "string" }, xml_signed_url: { type: ["string", "null"] }, danfse_signed_url: { type: ["string", "null"] }, signed_url_ttl_seconds: { type: "integer" } } } } } }, "404": errorResponse("NFS-e n\xE3o encontrada"), ...COMMON_ERRORS }
2627
2734
  }
@@ -3365,6 +3472,7 @@ var API_MODULES = [
3365
3472
  { key: "create", label: "Criar cobran\xE7a", description: "Gerar boletos, Pix e cobran\xE7as" },
3366
3473
  { key: "cancel", label: "Cancelar cobran\xE7a", description: "Cancelar cobran\xE7as em aberto" },
3367
3474
  { key: "send", label: "Enviar ao cliente", description: "Enviar segunda via da fatura por e-mail ao cliente" },
3475
+ { key: "settle", label: "Dar baixa", description: "Registrar o recebimento de uma parcela (baixa total ou parcial)" },
3368
3476
  { key: "delete", label: "Excluir fatura", description: "Excluir faturas permanentemente (itens e cobran\xE7as vinculadas)" }
3369
3477
  ]
3370
3478
  },
@@ -3759,6 +3867,27 @@ function buildCuratedTools(api, heldScopes) {
3759
3867
 
3760
3868
  // src/tools/request.ts
3761
3869
  var METHODS = ["GET", "POST", "PATCH", "PUT", "DELETE"];
3870
+ var ROTAS_DESTRUTIVAS = (() => {
3871
+ const out = [];
3872
+ for (const entry of MANIFEST) {
3873
+ if (!entry.destructive) continue;
3874
+ let desc;
3875
+ try {
3876
+ desc = describeOperation(entry.operationId);
3877
+ } catch {
3878
+ continue;
3879
+ }
3880
+ const padrao = desc.pathTemplate.split("/").map(
3881
+ (seg) => seg.startsWith("{") && seg.endsWith("}") ? "[^/]+" : seg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
3882
+ ).join("/");
3883
+ out.push({ method: desc.method.toUpperCase(), re: new RegExp(`^${padrao}/?$`) });
3884
+ }
3885
+ return out;
3886
+ })();
3887
+ function ehRotaDestrutiva(method, path) {
3888
+ const semQuery = path.split("?")[0];
3889
+ return ROTAS_DESTRUTIVAS.some((r) => r.method === method.toUpperCase() && r.re.test(semQuery));
3890
+ }
3762
3891
  function buildRequestTool(api) {
3763
3892
  return {
3764
3893
  name: "comando_request",
@@ -3782,7 +3911,7 @@ function buildRequestTool(api) {
3782
3911
  company_id: { type: "string", description: "Opcional: UUID da empresa (header X-Company-Id)." },
3783
3912
  idempotency_key: { type: "string", description: "Opcional: chave de idempot\xEAncia (POST)." },
3784
3913
  dry_run: { type: "boolean", description: "Se true, n\xE3o executa \u2014 s\xF3 mostra o que seria enviado." },
3785
- confirm: { type: "boolean", description: "Obrigat\xF3rio para DELETE: confirma a exclus\xE3o." }
3914
+ confirm: { type: "boolean", description: "Obrigat\xF3rio para DELETE e para qualquer rota destrutiva (cancelar, estornar, pagar, reverter)." }
3786
3915
  }
3787
3916
  },
3788
3917
  handler: async (args) => {
@@ -3791,13 +3920,13 @@ function buildRequestTool(api) {
3791
3920
  if (!METHODS.includes(method)) {
3792
3921
  return fail(`M\xE9todo inv\xE1lido: ${args.method}. Use um de: ${METHODS.join(", ")}.`);
3793
3922
  }
3794
- if (method === "DELETE" && args.confirm !== true && args.dry_run !== true) {
3923
+ let path = String(args.path ?? "");
3924
+ if (!path.startsWith("/")) return fail("path deve come\xE7ar com / (ex.: /customers).");
3925
+ if ((method === "DELETE" || ehRotaDestrutiva(method, path)) && args.confirm !== true && args.dry_run !== true) {
3795
3926
  return fail(
3796
3927
  "A\xE7\xE3o destrutiva bloqueada. Passe confirm: true para executar, ou dry_run: true para pr\xE9-visualizar."
3797
3928
  );
3798
3929
  }
3799
- let path = String(args.path ?? "");
3800
- if (!path.startsWith("/")) return fail("path deve come\xE7ar com / (ex.: /customers).");
3801
3930
  const queryArg = coerceObjectArg(args.query);
3802
3931
  if (queryArg) {
3803
3932
  const u = new URLSearchParams();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comando.one/mcp-server",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Servidor MCP (Model Context Protocol) para a API pública do Comando.One — opere seu ERP por linguagem natural.",
5
5
  "type": "module",
6
6
  "license": "MIT",