knowledge-base/records/plugin-dev/KB-PLUGIN-024-mcprotocol-delete-item-silent-success-bug.md
Rodolpho Lopes f072b1cde7 docs: marca bugs do mcprotocol como resolvidos em v1.2.0
KB-024 (silent-success do delete): ambas correções implementadas — makeRequest
propaga 4xx/5xx (commit 09ce92c) e glpi_projecttask_delete via ORM (0ca94fc).
KB-023 (InputValidation): arquivos agora realmente implementados; corrige itens
do rascunho que não foram feitos (buildInputFromArgs, project_update estendido).
KB-026 (snapshot): atualizado para v1.2.0 — 19 tools, ProjectTask CRUD, métricas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:16:02 +00:00

166 lines
6.5 KiB
Markdown

---
id: KB-PLUGIN-024
title: mcprotocol — Bug duplo no glpi_delete_item (sucesso silencioso + ProjectTask não exposto em v2)
domain: plugin-dev
tags:
- mcp
- bug
- glpi
- rest-api
- delete
- error-handling
status: active
severity: high
created_at: 2026-05-26
updated_at: 2026-06-22
applies_to:
- glpi-11
- mcprotocol-plugin
related_records:
- KB-PLUGIN-019
- KB-PLUGIN-016
---
# mcprotocol — Bug duplo no glpi_delete_item (sucesso silencioso + ProjectTask não exposto em v2)
## Resumo
Ao tentar deletar uma `ProjectTask` via `glpi_delete_item`, a tool retorna sucesso ao cliente mas o item **continua existindo no banco**. Investigação revelou dois bugs sobrepostos.
## Evidência
Comando que reproduz:
```json
{"method":"tools/call","params":{
"name":"glpi_delete_item",
"arguments":{"itemtype":"ProjectTask","id":30,"force_purge":true}
}}
```
Resposta MCP: aparenta sucesso (sem JSON-RPC error).
Banco: registro ID 30 segue em `glpi_projecttasks` com `is_deleted=0`.
## Causa raiz #1 — REST API v2 do GLPI não expõe ProjectTask
Verificado por probes:
| Endpoint | HTTP |
|---|---|
| `GET /api.php/v2/Project` | 200 ✅ |
| `GET /api.php/v2/ProjectTask` | 404 ❌ |
| `DELETE /api.php/v2/Project/4/ProjectTask/30` | 404 ❌ |
| `DELETE /api.php/v2/Project/ProjectTask/30` | 404 ❌ |
| `DELETE /api.php/v2/Assistance/ProjectTask/30` | 404 ❌ |
`ProjectTask` simplesmente **não está no roteamento público da v2** desta build do GLPI 11. Não há caminho documentado. Pode ser limitação da própria release (não confirmado se evolui em versões futuras).
## Causa raiz #2 — `makeRequest` não valida HTTP status
Em `src/Server.php`, o handler de `glpi_delete_item` faz:
```php
$response = $this->makeRequest('DELETE', "/$itemtype/{$args['id']}$purge");
break;
```
O método `makeRequest()` retorna o body da resposta sem checar o código HTTP. Quando a API retorna `{"status":"ERROR_ITEM_NOT_FOUND"}` com HTTP 404, esse JSON vira o `content[0].text` da resposta MCP — que do ponto de vista do JSON-RPC parece sucesso (não tem campo `error` no envelope JSON-RPC).
O cliente (Python, LLM, etc.) que checa apenas `if "error" in response` é enganado.
## Impacto
- **Alto:** silenciosamente perde operações de delete sem alertar o usuário/LLM
- LLM acredita que removeu, segue trabalhando com base nessa premissa falsa
- Afeta qualquer itemtype que não esteja na v2 do GLPI (ProjectTask confirmado; outros podem estar afetados — `KnowbaseItem`, `Document_Item`, etc., precisam ser testados)
## Mitigações temporárias
1. **Não usar `glpi_delete_item` para ProjectTask** — usar SQL direto ou criar tool de domínio (`glpi_projecttask_delete`) com ORM `(new ProjectTask())->delete(['id' => $id], true)`.
2. **Cliente checa `applied`** — tools de domínio do plugin retornam `applied`. Para tools genéricas, o cliente deve fazer `GET` após `DELETE` pra confirmar remoção (verificação ativa).
## Correções recomendadas
### Correção #1 (rápida, alta prioridade) — Validar HTTP status no `makeRequest`
Em `src/Server.php`, modificar `makeRequest` para propagar 4xx/5xx como exception:
```php
private function makeRequest(string $method, string $path, ?array $body = null): array {
// ... cURL setup ...
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$decoded = json_decode($response, true) ?? [];
if ($httpCode >= 400) {
$msg = $decoded['title'] ?? $decoded['status'] ?? 'Unknown error';
$detail = $decoded['detail'] ?? '';
throw new \Exception("GLPI API {$method} {$path} → HTTP {$httpCode}: {$msg}" . ($detail ? " ({$detail})" : ''));
}
return $decoded;
}
```
Assim qualquer 404/422/500 da API vira erro JSON-RPC visível para o cliente.
### Correção #2 (evolução) — Criar tools de domínio para ProjectTask
Adicionar em `src/ProjectTaskTools.php`:
```php
'glpi_projecttask_delete' => [
'description' => 'Remove uma tarefa de projeto. Bypassa REST API v2 (não exposta).',
'inputSchema' => [...],
'handler' => [self::class, 'handleDelete']
],
```
Handler usa ORM diretamente (não a REST API):
```php
public static function handleDelete(array $args) {
$task = new \ProjectTask();
if (!$task->can($args['id'], DELETE)) throw new \Exception("Acesso Negado.");
if (!$task->delete(['id' => $args['id']], (bool)($args['force_purge'] ?? true))) {
throw new \Exception("Erro ao remover tarefa.");
}
return ['status'=>'success', 'id'=>$args['id'], 'message'=>'Tarefa removida.'];
}
```
### Correção #3 (defensiva) — Probe na lista de tools
Documentar em `glpi_delete_item` quais itemtypes são conhecidamente quebrados na v2. Ou no startup do plugin, fazer um `OPTIONS` em itemtypes comuns e marcar quais funcionam.
## Como reproduzir
```bash
# 1. criar uma ProjectTask
curl -s -X POST .../mcp.php -H "Authorization: Bearer $TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"glpi_projecttask_create","arguments":{"projects_id":4,"name":"X"}}}'
# 2. tentar deletar (parece sucesso)
curl -s -X POST .../mcp.php -H "Authorization: Bearer $TOKEN" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"glpi_delete_item","arguments":{"itemtype":"ProjectTask","id":<NEW_ID>,"force_purge":true}}}'
# 3. confirmar que o registro continua
docker exec glpi11-mariadb mariadb -uglpi -pglpi_local_dev glpi \
-e "SELECT id,name,is_deleted FROM glpi_projecttasks WHERE id=<NEW_ID>;"
```
## Status
-**RESOLVIDO em 2026-06-22** (plugin v1.2.0, repo dev `origin`).
- ✅ Correção #1 (`makeRequest` propaga 4xx/5xx) — commit `09ce92c`. `makeRequest`
agora lança exception em HTTP ≥400, capturada no `callTool` e devolvida como erro
JSON-RPC visível. Resolve o silent-success de TODAS as tools genéricas REST.
- ✅ Correção #2 (`glpi_projecttask_delete` via ORM) — commit `0ca94fc`. Implementada
em `src/ProjectTaskTools.php` junto com get/create/update, todas via ORM `\ProjectTask`.
- 🔲 Correção #3 (probe/documentação de itemtypes quebrados na v2) — segue no backlog.
## Lições
1. **Tools wrapper REST nunca confiam em corpo da resposta sem checar HTTP status** — sempre validar.
2. **Tools de domínio (ORM) são mais robustas** — não dependem do estado da REST API, usam o GLPI diretamente em PHP.
3. **`applied` em response** ajuda o cliente confiar — mas não substitui checagem ativa pós-delete.
4. **REST API v2 do GLPI tem buracos** — não cobre todos os itemtypes. Validar caso a caso antes de assumir que `glpi_*_item` genérico funciona.