Files
Emanuel Almeida 6b3a6f2698 feat: refactor 30+ skills to Anthropic progressive disclosure pattern
- All SKILL.md files now <500 lines (avg reduction 69%)
- Detailed content extracted to references/ subdirectories
- Frontmatter standardised: only name + description (Anthropic standard)
- New skills: brand-guidelines, spec-coauthor, report-templates, skill-creator
- Design skills: anti-slop guidelines, premium-proposals reference
- Removed non-standard frontmatter fields (triggers, version, author, category)

Plugins affected: infraestrutura, marketing, dev-tools, crm-ops, gestao,
core-tools, negocio, perfex-dev, wordpress, design-media

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:05:03 +00:00

8.2 KiB

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:

<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:

<!-- 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)

# 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)

// 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:

{
  "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

# .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