# 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
get_customer_basicVerifica que get_customer retorna dados completos de um cliente existenteget_customer1nameemailtrue
```
---
## 10 Perguntas de Evaluation por MCP
Para cada MCP novo, responder a estas 10 perguntas criando um teste para cada uma:
```xml
A tool {principal} retorna dados quando recebe input válido?Input mínimo válidoResposta não é erro, contém campos esperadosO que acontece com input inválido (ID negativo, campo obrigatório em falta)?Input inválido ou em faltaResposta tem isError=true, mensagem é accionávelO que retorna quando o recurso pedido não existe (ID=99999)?Recurso não encontradoMensagem clara "não encontrado", não retorna null silenciosoOperações com idempotentHint=true produzem o mesmo resultado em chamadas repetidas?Chamar a mesma tool create/update duas vezes com os mesmos parâmetrosSegundo resultado idêntico ao primeiro ou erro controladoTools com destructiveHint=true apagam realmente dados? O utilizador é avisado?Executar delete em recurso existenteDados apagados. Mensagem confirma acção irreversívelTools com readOnlyHint=true não modificam dados?Executar get/list e verificar estado antes e depoisEstado da BD/sistema idêntico antes e depois da chamadaTools de listagem com muitos resultados retornam dados paginados correctamente?Lista com 1000+ registos, pedir página 2Retorna subset correcto, metadados de paginação presentesChamadas sem credenciais válidas são rejeitadas?Remover variável de ambiente com API key e chamar toolErro claro de autenticação, não crash do servidorO campo structuredContent contém os mesmos dados que o texto Markdown?Comparar campos em structuredContent com conteúdo do textDados idênticos em ambos os formatosA tool responde em menos de 5 segundos em condições normais?Medir tempo de resposta em 10 chamadas consecutivasp95 < 5000ms, sem memory leaks após 100 chamadas
```
---
## 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,
assertions: {
notError?: boolean;
containsFields?: string[];
isError?: boolean;
}
): Promise {
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*