# MCP Evaluation Guide

> Guia para criar e executar evaluations de servidores MCP.
> Baseado nos padrões do mcp-builder Anthropic.

---

## O que são Evaluations MCP?

Evaluations são testes estruturados que validam o comportamento de um MCP em cenários reais. Não testam apenas se as ferramentas existem — testam se produzem **resultados correctos, seguros e previsíveis**.

**Objectivos:**
- Garantir que cada tool faz o que a descrição promete
- Detectar regressões após alterações
- Validar edge cases e erros esperados
- Confirmar que annotations correspondem ao comportamento real

---

## Estrutura de uma Evaluation

Cada evaluation é definida em XML e contém:

```xml
<evaluation>
  <name>get_customer_basic</name>
  <description>Verifica que get_customer retorna dados completos de um cliente existente</description>
  <tool>get_customer</tool>
  <input>
    <customer_id>1</customer_id>
  </input>
  <expected>
    <contains>name</contains>
    <contains>email</contains>
    <not_error>true</not_error>
  </expected>
</evaluation>
```

---

## 10 Perguntas de Evaluation por MCP

Para cada MCP novo, responder a estas 10 perguntas criando um teste para cada uma:

```xml
<!-- 1. A tool mais básica funciona? -->
<evaluation id="1">
  <question>A tool {principal} retorna dados quando recebe input válido?</question>
  <scenario>Input mínimo válido</scenario>
  <assert>Resposta não é erro, contém campos esperados</assert>
</evaluation>

<!-- 2. Erros de input são tratados graciosamente? -->
<evaluation id="2">
  <question>O que acontece com input inválido (ID negativo, campo obrigatório em falta)?</question>
  <scenario>Input inválido ou em falta</scenario>
  <assert>Resposta tem isError=true, mensagem é accionável</assert>
</evaluation>

<!-- 3. O recurso inexistente é tratado correctamente? -->
<evaluation id="3">
  <question>O que retorna quando o recurso pedido não existe (ID=99999)?</question>
  <scenario>Recurso não encontrado</scenario>
  <assert>Mensagem clara "não encontrado", não retorna null silencioso</assert>
</evaluation>

<!-- 4. Operações de escrita são idempotentes quando declaradas? -->
<evaluation id="4">
  <question>Operações com idempotentHint=true produzem o mesmo resultado em chamadas repetidas?</question>
  <scenario>Chamar a mesma tool create/update duas vezes com os mesmos parâmetros</scenario>
  <assert>Segundo resultado idêntico ao primeiro ou erro controlado</assert>
</evaluation>

<!-- 5. Operações destrutivas têm confirmação ou são irreversíveis como declarado? -->
<evaluation id="5">
  <question>Tools com destructiveHint=true apagam realmente dados? O utilizador é avisado?</question>
  <scenario>Executar delete em recurso existente</scenario>
  <assert>Dados apagados. Mensagem confirma acção irreversível</assert>
</evaluation>

<!-- 6. Tools read-only não alteram estado? -->
<evaluation id="6">
  <question>Tools com readOnlyHint=true não modificam dados?</question>
  <scenario>Executar get/list e verificar estado antes e depois</scenario>
  <assert>Estado da BD/sistema idêntico antes e depois da chamada</assert>
</evaluation>

<!-- 7. Paginação e listas grandes funcionam? -->
<evaluation id="7">
  <question>Tools de listagem com muitos resultados retornam dados paginados correctamente?</question>
  <scenario>Lista com 1000+ registos, pedir página 2</scenario>
  <assert>Retorna subset correcto, metadados de paginação presentes</assert>
</evaluation>

<!-- 8. Autenticação/autorização é validada? -->
<evaluation id="8">
  <question>Chamadas sem credenciais válidas são rejeitadas?</question>
  <scenario>Remover variável de ambiente com API key e chamar tool</scenario>
  <assert>Erro claro de autenticação, não crash do servidor</assert>
</evaluation>

<!-- 9. structuredContent é consistente com content textual? -->
<evaluation id="9">
  <question>O campo structuredContent contém os mesmos dados que o texto Markdown?</question>
  <scenario>Comparar campos em structuredContent com conteúdo do text</scenario>
  <assert>Dados idênticos em ambos os formatos</assert>
</evaluation>

<!-- 10. Performance é aceitável? -->
<evaluation id="10">
  <question>A tool responde em menos de 5 segundos em condições normais?</question>
  <scenario>Medir tempo de resposta em 10 chamadas consecutivas</scenario>
  <assert>p95 < 5000ms, sem memory leaks após 100 chamadas</assert>
</evaluation>
```

---

## Como Executar Evaluations

### Método Manual (MCP Inspector)

```bash
# Instalar MCP Inspector
npx @modelcontextprotocol/inspector

# Conectar ao MCP local
# Interface web em http://localhost:5173
# Testar cada tool manualmente
```

### Método Automatizado (Script TypeScript)

```typescript
// eval/run-evals.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

interface EvalResult {
  id: string;
  passed: boolean;
  duration: number;
  error?: string;
}

async function runEval(
  client: Client,
  toolName: string,
  input: Record<string, unknown>,
  assertions: {
    notError?: boolean;
    containsFields?: string[];
    isError?: boolean;
  }
): Promise<EvalResult> {
  const start = Date.now();

  try {
    const result = await client.callTool({ name: toolName, arguments: input });
    const duration = Date.now() - start;

    // Verificar assertions
    if (assertions.notError && result.isError) {
      return { id: toolName, passed: false, duration, error: 'Esperava sucesso, recebeu erro' };
    }

    if (assertions.isError && !result.isError) {
      return { id: toolName, passed: false, duration, error: 'Esperava erro, recebeu sucesso' };
    }

    if (assertions.containsFields) {
      const text = result.content[0]?.text || '';
      for (const field of assertions.containsFields) {
        if (!text.includes(field)) {
          return { id: toolName, passed: false, duration, error: `Campo "${field}" não encontrado` };
        }
      }
    }

    return { id: toolName, passed: true, duration };
  } catch (err) {
    return {
      id: toolName,
      passed: false,
      duration: Date.now() - start,
      error: err instanceof Error ? err.message : String(err)
    };
  }
}

// Executar todas as evaluations
async function main() {
  const transport = new StdioClientTransport({
    command: 'node',
    args: ['dist/index.js']
  });

  const client = new Client({ name: 'eval-client', version: '1.0.0' });
  await client.connect(transport);

  const results: EvalResult[] = [];

  // Eval 1: Tool básica
  results.push(await runEval(client, 'get_customer', { customer_id: 1 }, {
    notError: true,
    containsFields: ['name', 'email']
  }));

  // Eval 2: Input inválido
  results.push(await runEval(client, 'get_customer', { customer_id: -1 }, {
    isError: true
  }));

  // Eval 3: Recurso inexistente
  results.push(await runEval(client, 'get_customer', { customer_id: 99999 }, {
    isError: true
  }));

  // Sumário
  const passed = results.filter(r => r.passed).length;
  const total = results.length;
  console.log(`\nEvaluations: ${passed}/${total} passou`);
  results.filter(r => !r.passed).forEach(r => {
    console.log(`  FALHOU ${r.id}: ${r.error}`);
  });

  await client.close();
  process.exit(passed === total ? 0 : 1);
}

main().catch(console.error);
```

**Adicionar ao package.json:**
```json
{
  "scripts": {
    "eval": "tsx eval/run-evals.ts",
    "eval:ci": "npm run build && npm run eval"
  }
}
```

---

## Checklist de Evaluations (pré-deploy)

- [ ] Eval 1 passa: tool principal com input válido
- [ ] Eval 2 passa: input inválido retorna erro accionável
- [ ] Eval 3 passa: recurso inexistente retorna erro claro
- [ ] Eval 4 passa: idempotência verificada (se aplicável)
- [ ] Eval 5 passa: operações destrutivas confirmadas
- [ ] Eval 6 passa: read-only não altera estado
- [ ] Eval 7 passa: paginação funciona (se aplicável)
- [ ] Eval 8 passa: auth inválida rejeitada graciosamente
- [ ] Eval 9 passa: structuredContent consistente
- [ ] Eval 10 passa: p95 < 5000ms

---

## Integração CI/CD

```yaml
# .gitea/workflows/eval.yml
name: MCP Evaluations
on: [push, pull_request]

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm ci
      - run: npm run eval:ci
```

---

*evaluation-guide.md v1.0 | 2026-03-10*
