Iris

oficial

Servidor de avaliação e observabilidade de agentes nativo do MCP com registro de rastreamento, avaliação de qualidade de saída, rastreamento de custos, 12 regras de avaliação integradas, painel em tempo real e detecção de PII

O que você pode fazer com Iris MCP?

  • Registrar e avaliar execuções do agente — Peça ao seu assistente para registrar uma tarefa no Iris e obtenha pontuações determinísticas de qualidade, segurança e custo na saída.
  • Consultar histórico de rastreamento — Recupere execuções de agentes armazenadas com suporte a filtros, paginação e intervalos de tempo para revisar o desempenho passado.
  • Comparar execuções ao longo do tempo — Analise duas execuções nas mesmas perguntas lado a lado para identificar regressões ou melhorias no comportamento do agente.
  • Pontuar a qualidade da saída — Avalie qualquer texto com base em 25 regras integradas que cobrem completude, relevância, segurança e custo, com detecção de PII e injeção de prompt.
  • Executar o painel de demonstração — Inicie um banco de dados de demonstração pré-carregado com falhas e vereditos de exemplo para explorar o mecanismo de pontuação do Iris localmente.

Documentação

Iris — pare de lançar agentes no achismo

Glama Score Install in Cursor Install in VS Code npm version GitHub stars CI OpenSSF Scorecard OpenSSF Best Practices License: MIT Docker PulseMCP mcp.so

O Iris pontua cada execução de agente por qualidade, segurança e custo — na sua máquina, sem SDK e sem conta. A maioria dos projetos de agentes verifica qualidade rodando alguns prompts memorizados e avaliando a saída visualmente. O Iris substitui isso por números que você pode auditar: as execuções dos seus agentes caem em um banco de dados SQLite no seu disco, 25 regras integradas pontuam deterministicamente — PII, injeção de prompt, marcadores de alucinação, limites de custo e as próprias chamadas de ferramenta do agente — grátis, sem chamadas de LLM, e um juiz LLM opcional com limite rígido de custo por avaliação cuida das questões semânticas. Toda regra é inspecionável e editável, porque um juiz que você não pode auditar é só achismo com um número em cima. Licença MIT, sem telemetria. Nada sai da sua máquina a menos que você ative uma destas opções: um endpoint OpenTelemetry (IRIS_OTEL_ENDPOINT), que exporta traces para o coletor que você nomear; o juiz LLM com sua própria chave, que envia o texto julgado para esse provedor, e cuja verificação de citação busca as páginas que uma saída cita; ou um webhook, que envia ids, o veredito e nomes de regras, nunca o texto, para o endereço que você definir.

Requer Node.js 22.13 ou posterior. Verifique com node --version.

The demo: Failures, a failure opened, two runs compared

O banco de dados de demonstração, gravado por scripts/demo-media.mts; a fonte é demo.mp4. Uma imagem estática: dashboard-overview.png.

Uma falha na tela em 60 segundos

Sem configuração de agente, sem config — um comando:

npx @iris-eval/mcp-server --demo

Isso popula um banco de dados de demonstração — cinco agentes pequenos, duas semanas de execuções, todo veredito vindo do próprio mecanismo — e serve o dashboard contra ele em http://localhost:6920 (seu navegador abre automaticamente na primeira execução). O dashboard cai em Failures: o que falhou, do pior ao mais recente, cada cartão nomeando a regra e sua evidência. Vale clicar — um vazamento de PII pego pelas regras de segurança, uma diretiva oculta em um post de fórum que o resumidor obedeceu, um número que o documento-fonte nunca disse, duas execuções nas mesmas doze perguntas comparadas com um intervalo (Runs), uma regra customizada implantada e uma pausada com suas linhas de auditoria, e uma pontuação de juiz LLM reprovada com sua justificativa.

Os dados de demonstração vivem em seu próprio banco (demo.db no seu diretório home do Iris — ~/.iris no macOS/Linux, %USERPROFILE%\.iris no Windows) e nunca se misturam com seus traces reais. Remova tudo com um comando:

npx @iris-eval/mcp-server --demo-clear

Conecte seu próprio agente

Primeiro, prove que a instalação funciona nesta máquina — roda offline e não abre nada seu:

npx @iris-eval/mcp-server --self-test   # exit 0 = healthy

Depois adicione o Iris ao seu cliente MCP. Um comando grava o arquivo de configuração do próprio cliente, mantém todos os outros servidores nele e fixa a versão que você executou:

npx -y @iris-eval/mcp-server install claude-code

Os clientes: claude-code, claude-desktop, cursor, windsurf, continue, vscode, cline, zed, codex, gemini. install --list mostra os encontrados nesta máquina, o Iris que cada um executa e o arquivo que ele lê; install <client> --uninstall remove o Iris novamente. Todos os clientes compartilham um banco de dados, então após uma atualização mova todos de uma vez com install --upgrade (Atualizando). Reinicie o cliente para carregá-lo.

Claude Desktop: um clique. Cada release a partir de 0.20.0 anexa iris-eval.mcpb, um MCP Bundle: baixe o mais recente, abra-o, e o Claude Desktop mostra um diálogo de instalação. Nada nele é obrigatório — uma chave Anthropic ou OpenAI para o juiz LLM é opcional, e o dashboard é um interruptor que começa desligado. O bundle contém o pacote npm e suas dependências, então nada mais precisa ser instalado: o Claude Desktop o executa sob o Node que ele acompanha quando esse Node é 22.13 ou mais novo (Claude Desktop 1.1.6679 acompanha 24.13), e o Iris armazena traces com o SQLite embutido do Node, no mesmo ~/.iris que toda outra instalação usa. As notas de release mostram como verificar sua assinatura e atestado de build.

Ele roda em qualquer cliente MCP, e cada cliente que ele nomeia tem uma linha com o que foi realmente verificado. Verificado em toda execução de CI: Claude Code, Gemini CLI — o cliente real inicia o Iris a partir da configuração que o instalador escreveu e reporta que conectou (claude mcp list, gemini mcp list), em Linux, macOS e Windows; os hooks do plugin de captura do Claude Code também são acionados pelos scripts reais. Afirmado a partir da documentação MCP de cada cliente — o instalador escreve o formato de configuração que o cliente documenta, e esse escritor é testado no formato; ninguém do lado do Iris viu a conexão: Claude Desktop, Cursor, Devin Desktop (Windsurf), Continue, VS Code, Cline, Zed, OpenAI Codex CLI. Cada linha com sua fonte e a data em que foi lida: https://iris-eval.com/clients. Manualmente, um bloco, dashboard incluído:

{
  "mcpServers": {
    "iris-eval": {
      "command": "npx",
      "args": ["-y", "@iris-eval/mcp-server", "--dashboard"]
    }
  }
}

Seu cliente lista as doze ferramentas do Iris na conexão, e o dashboard serve em http://localhost:6920. Agora cole isto no seu agente:

Registre essa última tarefa no Iris e avalie a saída.

O trace cai no dashboard com suas pontuações. Prefere o servidor MCP sem interface? Remova --dashboard dos argumentos — você pode abrir o mesmo dashboard a qualquer momento com npx @iris-eval/mcp-server --dashboard.

Uma coisa que vale saber de antemão: ferramentas MCP são chamadas quando o modelo decide chamá-las. O Iris não intercepta seu agente, então traces são registrados quando seu agente pede para registrá-los — seja porque você mandou, seja porque seu código chama as ferramentas diretamente. Peça ao seu agente para "registrar isso no Iris e avaliar" e ele fará. Se você quer captura que não dependa da escolha do modelo, POST /api/v1/traces faz exatamente isso — seu código envia o trace por HTTP puro, sem modelo no meio (veja docs/http-ingest.md). A CLI e os hooks de host no roadmap serão clientes leves sobre o mesmo endpoint.

Captura por HTTP (sem modelo no meio)

O endpoint de ingestão vive na porta do dashboard — 6920 por padrão, não a porta do transporte MCP — e existe apenas enquanto o dashboard está rodando. Passe --dashboard (ou defina IRIS_DASHBOARD=true); --transport http sozinho não o inicia, e uma requisição à porta de transporte retorna 404. Com o dashboard de pé, qualquer coisa que possa enviar uma requisição HTTP pode registrar um trace — e opcionalmente rodar as avaliações determinísticas na mesma requisição. GET /api/v1/capabilities na mesma porta diz o que este servidor pode julgar, o que cada regra precisa, o estado do juiz com os passos que o habilitam e os limites — o mesmo objeto que o recurso MCP iris://capabilities serve — então um chamador HTTP tem o enquadramento que um cliente MCP recebe no initialize:

curl -s -X POST "http://127.0.0.1:6920/api/v1/traces" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_name": "support-bot",
    "input": "What is the refund policy?",
    "output": "Refunds are available within 30 days of purchase.",
    "evaluate": true,
    "eval_type": "safety"
  }'

Retorna 201 com o trace_id armazenado e o resultado da avaliação (no modo --demo o endpoint recusa escritas com 403, então dados de demonstração nunca se misturam com os seus). O endpoint aceita o mesmo corpo que a ferramenta log_trace e fica atrás da mesma pilha de middleware que o resto do dashboard: bind de loopback e a proteção contra DNS-rebinding por padrão, mais autenticação Bearer quando você define uma. Dois fatos simples sobre ele: aceita escritas não autenticadas a menos que o Iris tenha sido iniciado com --api-key (ou IRIS_API_KEY) — o bind de loopback é o que o mantém na sua máquina por padrão, então defina uma chave antes de vincular além do loopback; e o que ele armazena é verbatim — input e output caem em iris.db exatamente como enviados, incluindo qualquer texto que no_pii venha a sinalizar. Contrato completo, referência de campos e semântica de erros: docs/http-ingest.md.

Capture cada turno do Claude Code (opcional)

/plugin marketplace add iris-eval/mcp-server
/plugin install iris-eval-capture@iris-eval

Um segundo plugin, instalado separadamente: três hooks registram o prompt de cada turno, chamadas de ferramenta e resposta final e os entregam a iris-eval ingest, em modo destacado, com spans críticos redigidos no texto de avaliação armazenado — captura que não depende do modelo decidir chamar uma ferramenta. Nunca registra um turno que o modelo já registrou, nunca imprime, nunca bloqueia, nunca envia nada para lugar nenhum. Instalar iris-eval sozinho não muda nada no seu loop de turnos. Limites e remoção: claude-plugin-capture/README.md.

Python

pip install iris-eval
from iris_eval import IrisClient
iris = IrisClient()                       # IRIS_URL, or the running dashboard's runtime.json
iris.evaluate_output("…", input="…", agent_name="support-bot")["verdict"]   # {"state": "pass", "basis": "clean", "by": []}

Um cliente leve sobre a API HTTP do servidor 0.16.0 e posterior, versionado separadamente — iris_eval.__version__ e a página do PyPI carregam seu número, que não é o do servidor: log_trace(), evaluate_output(), get_traces(), get_trace(), health(), capabilities(), síncrono e assíncrono, respostas tipadas, a própria frase do servidor sobre uma recusa — e um plugin pytest: uma fixture iris e assert_iris(output, expect="pass") que afirma o estado do veredito. packages/python/README.md.

Registre toda chamada OpenAI e Anthropic

from iris_eval import wrap_openai
client = wrap_openai(OpenAI(), agent_name="support-bot")   # every call: a GenAI span to POST /v1/traces, scored
import { wrapOpenAI } from '@iris-eval/sdk';
const openai = wrapOpenAI(new OpenAI(), { agentName: 'support-bot' });

Envolva o cliente do provedor uma vez e cada chamada de modelo vira um span GenAI OpenTelemetry enviado à porta OTLP, armazenado com sua entrada, saída, uso de tokens e chamadas de ferramenta, e pontuado: captura que não depende do modelo chamar uma ferramenta. wrap_openai / wrap_anthropic no cliente Python; wrapOpenAI, wrapAnthropic e irisMiddleware para o Vercel AI SDK em @iris-eval/sdk. Ambos ainda não publicados (o próximo release iris-eval no PyPI; @iris-eval/sdk é construído da fonte até seu primeiro release npm). Streams, os helpers de stream dos SDKs e chamadas de ferramenta são cobertos, o cliente original não é alterado, e o Iris fora do ar nunca quebra uma chamada — packages/sdk/README.md, packages/python/README.md.

Pontue toda execução LangChain e LangGraph

from iris_eval.langchain import IrisCallbackHandler
graph.invoke(inputs, config={"callbacks": [IrisCallbackHandler(agent_name="support-bot")]})

Cada execução de nível superior vira um trace (a execução, suas chamadas de modelo, suas chamadas de ferramenta e seus nós de grafo como spans GenAI) com sua entrada, saída, chamadas de ferramenta, uso de tokens e um veredito. Python no cliente (próximo release, ainda não publicado no PyPI), JavaScript como @iris-eval/langchain (ainda não publicado no npm). Ambos são comprovados em CI contra um app LangGraph real com um modelo roteirizado; a exportação OpenTelemetry do próprio LangSmith é comprovada da mesma forma — docs/otel-recipes.md.

Um portão de CI, sem servidor necessário

npx -y @iris-eval/mcp-server ingest --file traces.ndjson --evaluate --fail-on detector_veto

Ou a GitHub Action (0.16.0), que reprova o job nos vereditos que você nomear, escreve o recibo no resumo do job e o publica como um comentário de pull request atualizado no lugar: uses: iris-eval/mcp-server/.github/actions/gate@v0.19.0 com traces: traces.ndjson — docs/ci-gate.md. Uma quarta porta (0.15.0): POST /v1/traces na porta do dashboard recebe o JSON ou protobuf OTLP/HTTP que sua instrumentação OpenTelemetry já emite (o exportador do SDK Python fala apenas protobuf, então esta também é a porta para Python), e cada trace OTLP se torna um trace Iris com seus spans — docs/otel-integration.md; uma receita por framework (Pydantic AI, Google ADK, LangGraph via LangSmith, CrewAI, o OpenAI Agents SDK em Python e JavaScript, LlamaIndex, AutoGen, Microsoft Agent Framework, Semantic Kernel, o Vercel AI SDK e Mastra), cada uma comprovada por um fixture, em docs/otel-recipes.md. ingest lê um trace JSON (ou NDJSON, um por linha) de stdin ou de um arquivo, armazena-o, avalia-o exatamente sob as regras que evaluate_output executa, imprime uma linha JSON por trace com o veredito e sua base, e sai com código 1 quando um veredito corresponde a --fail-on. --dataset <id|label> restringe esse portão às chaves de caso em um dataset (POST /api/v1/datasets promove as chaves de caso de uma execução em um), para que um job falhe apenas nos casos que você escolheu. A receita completa, os códigos de saída e as oito bases estão em docs/ci-gate.md.

Escreva uma regra como código

eval.plugins em config.json carrega regras que você escreveu — um módulo ES cuja exportação padrão é { name, kind, mechanism, version, needs, evaluate(ctx) } — fixado pelo sha256 do arquivo, de modo que um arquivo alterado desde a fixação recusa a inicialização em vez de executar. Um plugin carregado dispara como um integrado e aparece em list_rules sob plugins. O contrato, a receita de hash e o que um plugin pode retornar: docs/plugins.md.

Use o motor no seu próprio processo

O motor de avaliação é importável — sem servidor, sem banco de dados, sem modelo:

import { EvalEngine, defaultConfig } from '@iris-eval/mcp-server/engine';

const engine = new EvalEngine(defaultConfig.eval.defaultThreshold, defaultConfig.eval.ruleThresholds, defaultConfig.eval);
const result = await engine.evaluateAll({ output: answer, input: prompt, toolCalls, costUsd });
result.verdict.state;       // 'pass' | 'fail' | 'unknown', with result.verdict.basis and result.interpretations

O mesmo motor, as mesmas regras e o mesmo compositor que o servidor executa; builtInRules(), createCustomRule(), compose() e os leitores de precisão publicada são exportados ao lado dele.

Um cliente tipado para a rota HTTP

import { createClient } from '@iris-eval/mcp-server/client';

const iris = createClient({ baseUrl: 'http://127.0.0.1:6920', apiKey: process.env.IRIS_API_KEY });
const { trace_id, evaluation } = await iris.logTrace({ agent_name: 'support-bot', input, output, tool_calls, evaluate: true });
evaluation?.verdict?.state;  // the same object evaluate_output returns

Um corpo em cada porta: é o que log_trace e iris-eval ingest aceitam. Uma recusa lança IrisClientError com a própria frase e status do servidor. Ambos os subcaminhos são verificados a partir de um tarball empacotado em cada build.

Verifique sua instalação

npx @iris-eval/mcp-server --self-test   # offline diagnostic; exit 0 = healthy, 1 = a check failed
npx @iris-eval/mcp-server --version     # prints the bare version, e.g. 1.2.3

--self-test primeiro cria seu home Iris se estiver ausente e verifica se é gravável (saída 1, nomeando o caminho, se não for), relata onde está o índice de busca do seu banco de dados (inteiro, quantos traces um build em segundo plano indexou até agora, ou sem FTS5 neste SQLite), lê o schema do seu banco de dados (saída 1, com a correção, quando esta versão ou um cliente MCP fixado em uma versão mais antiga não puder abri-lo), então executa suas verificações — round-trip de armazenamento, um SSN plantado e uma injeção plantada capturados pelas regras de segurança, inicialização do dashboard, a proteção contra DNS-rebinding — dentro de um home temporário isolado. Seu banco de dados real é apenas lido, nunca alterado. Tudo o que Iris escreve vive sob um diretório, seu home Iris: ~/.iris por padrão (%USERPROFILE%\.iris no Windows), ou onde IRIS_HOME apontar. É onde iris.db, config.json, custom-rules.json, audit.log, preferences.json e os arquivos de demonstração vivem; aponte IRIS_HOME para um diretório temporário para experimentar Iris sem tocar em seus dados reais.

Configuração por ferramenta
ClienteStatusO que isso significaLeia
Claude Codeverificadoum teste dirige o cliente real em cada execução de CI2026-09-25
Claude Desktopreivindicadoo instalador escreve a forma que o cliente documenta, e esse escritor é testado na forma; ninguém do lado Iris o viu conectar2026-09-25
Cursorreivindicadoo instalador escreve a forma que o cliente documenta, e esse escritor é testado na forma; ninguém do lado Iris o viu conectar2026-09-25
Devin Desktop (Windsurf)reivindicadoo instalador escreve a forma que o cliente documenta, e esse escritor é testado na forma; ninguém do lado Iris o viu conectar2026-09-25
Continuereivindicadoo instalador escreve a forma que o cliente documenta, e esse escritor é testado na forma; ninguém do lado Iris o viu conectar2026-09-25
VS Codereivindicadoo instalador escreve a forma que o cliente documenta, e esse escritor é testado na forma; ninguém do lado Iris o viu conectar2026-09-25
Clinereivindicadoo instalador escreve a forma que o cliente documenta, e esse escritor é testado na forma; ninguém do lado Iris o viu conectar2026-09-25
Zedreivindicadoo instalador escreve a forma que o cliente documenta, e esse escritor é testado na forma; ninguém do lado Iris o viu conectar2026-09-25
OpenAI Codex CLIreivindicadoo instalador escreve a forma que o cliente documenta, e esse escritor é testado na forma; ninguém do lado Iris o viu conectar2026-09-25
Gemini CLIverificadoum teste dirige o cliente real em cada execução de CI2026-09-25

Cada linha com o que foi verificado: iris-eval.com/clients. Nenhum cliente é chamado de suportado sem uma linha.

npx -y @iris-eval/mcp-server install <client> escreve cada um desses para você. À mão, por cliente:

Claude Desktop

Edite seu arquivo de configuração MCP:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Adicione a configuração JSON acima e reinicie o Claude Desktop.

Claude Code

claude mcp add --transport stdio iris-eval -- npx -y @iris-eval/mcp-server

Em seguida, reinicie a sessão (/clear ou relance) para que as ferramentas carreguem.

Nota para Windows: Não use o wrapper cmd /c — ele causa problemas de parsing de caminho. O comando npx funciona diretamente.

Cursor

Adicione a configuração JSON acima a ~/.cursor/mcp.json (todo projeto) ou .cursor/mcp.json em um workspace, com "type": "stdio" na entrada iris-eval — os docs do Cursor a marcam como obrigatória.

Devin Desktop (Windsurf)

Adicione a configuração JSON acima a mcp_config.json: ~/.config/devin/mcp_config.json no macOS e Linux, %APPDATA%\devin\mcp_config.json no Windows.

Continue

Salve a configuração JSON acima como seu próprio arquivo na pasta mcpServers do Continue: ~/.continue/mcpServers/iris-eval.json (todo workspace) ou .continue/mcpServers/iris-eval.json em um.

VS Code (MCP nativo)

Adicione a .vscode/mcp.json no seu workspace (nota: VS Code usa servers, não mcpServers):

{
  "servers": {
    "iris-eval": {
      "command": "npx",
      "args": ["-y", "@iris-eval/mcp-server"]
    }
  }
}

Cline

Abra o painel de Servidores MCP do Cline → Configure MCP Servers, e adicione a configuração JSON mcpServers acima a cline_mcp_settings.json (~/.cline/data/settings/cline_mcp_settings.json, compartilhado pelo Cline no VS Code, JetBrains e CLI).

Zed

Adicione ao Zed settings.json:

{
  "context_servers": {
    "iris-eval": {
      "command": "npx",
      "args": ["-y", "@iris-eval/mcp-server"],
      "env": {}
    }
  }
}

OpenAI Codex CLI

Adicione a ~/.codex/config.toml:

[mcp_servers.iris-eval]
command = "npx"
args = ["-y", "@iris-eval/mcp-server"]

Gemini CLI

Adicione a configuração JSON mcpServers acima a ~/.gemini/settings.json. O Gemini CLI conecta-se a servidores MCP apenas em pastas que ele confia: se gemini mcp list mostrar iris-eval como Desabilitado, execute /permissions nessa pasta.

Qualquer outra coisa que fale MCP

Iris é um servidor MCP stdio padrão — um comando npx @iris-eval/mcp-server, sem SDK, sem mudanças de código. Se seu cliente suporta MCP, ele suporta Iris. Formatos de configuração de cliente mudam; em caso de dúvida, verifique os docs MCP do seu cliente e aponte-o para esse comando.

Outros Métodos de Instalação

# Global install (recommended for persistent data and faster startup)
npm install -g @iris-eval/mcp-server
iris-eval --dashboard

# Docker — two servers, two ports: 3000 = MCP HTTP transport,
# 6920 = dashboard (which also serves the POST /api/v1/traces ingest endpoint).
# The image binds 0.0.0.0 inside the container, so a key is required (see Production).
# The volume is the Iris home: the database, deployed rules and audit log persist in it.
docker run -p 3000:3000 -p 6920:6920 -v iris-data:/data \
  -e IRIS_API_KEY="$(openssl rand -hex 32)" ghcr.io/iris-eval/mcp-server

Dica: Instalação global (npm install -g) armazena traces persistentemente em ~/.iris/iris.db. Com npx, os traces persistem no mesmo local, mas a inicialização é mais lenta devido à resolução de pacotes.

O Que Você Obtém

Registro de TracesÁrvores de spans hierárquicas com latência por chamada de ferramenta, uso de tokens e custo em USD. Armazenado em SQLite, consultável instantaneamente.
Avaliação de Saída25 regras integradas em 4 categorias: completude, relevância, segurança, custo. Detecção de PII (21 padrões: SSN, cartão de crédito, telefone, e-mail, IBAN, data de nascimento, MRN, IP, chave de API, passaporte, além de tokens AWS/Slack/SendGrid/GitHub/Google/npm/DigitalOcean, credenciais dentro de URLs, atribuições com nomes de segredo, blocos de chave privada PEM e frases-semente; data de nascimento, número de registro médico, passaporte e frase-semente disparam apenas ao lado de seu rótulo, por design), detecção de injeção de prompt (38 padrões, frase + estrutural), detecção de saída simulada, detecção de alucinação (25 sinais de fabricação/contradição fundamentados em contexto — passe input para fundamentá-los contra o material de origem do agente), e seis regras de trajetória que leem o que o agente FEZ: uma chamada de ferramenta falha não reconhecida, uma repetida (por chamada, por sequência repetida, ou por alvo quando você envia tools), uma chamada cujos argumentos o próprio JSON Schema da ferramenta rejeita e o agente nunca tentou novamente, um arquivo, diretório ou URL que a resposta cita mas que não aparece em nada que o agente leu, uma instrução que chegou dentro de um RESULTADO DE FERRAMENTA e foi então obedecida por uma chamada posterior, e uma tarefa que levou mais chamadas de ferramenta do que seu orçamento de etapas. Uma trajetória pode chegar como tool_calls ou como spans TOOL do OpenTelemetry. Adicione regras personalizadas com schemas Zod.
LLM-como-JuizPontuação semântica opcional via Anthropic ou OpenAI — traga sua própria chave de API. Sete modelos. Com IRIS_RELEVANCE_JUDGE_MODEL definido, answers_the_ask pergunta ao juiz relevance e reprova uma resposta fora do tópico; sem ele, a regra lê a pergunta lexicalmente e aconselha. Limite de custo rígido por avaliação (IRIS_LLM_JUDGE_MAX_COST_USD_PER_EVAL, padrão $0,25), preço por avaliação divulgado no resultado.
Visibilidade de CustoCusto agregado em todos os agentes em qualquer janela de tempo. Defina limites de orçamento. Seja sinalizado quando agentes gastarem demais. Um trace que envia contagens de tokens e um modelo, mas sem custo (a maioria dos traces OpenTelemetry e de frameworks), é precificado pelo preço de tabela do modelo e marcado como estimado em todos os lugares em que aparece; pricing.models em config.json precifica modelos que a tabela integrada não cobre — docs/cost.md.
Dashboard WebInterface em modo escuro em tempo real que aterrissa nas falhas, piores e mais recentes primeiro — visualização de traces com busca de texto completo sobre o texto de cada trace, resultados de avaliação, detalhamentos de custo e uma paleta de comandos (⌘K) que busca suas próprias regras, traces e avaliações.
Local-primeiroTudo vive em SQLite no seu disco. Sem conta, sem cadastro, sem telemetria. HTTP de saída acontece apenas onde você opta: sua própria chave de juiz LLM, busca de citações, um exportador OTel que você configura ou um webhook que você define.

Para onde isso vai a seguir: o mapa de capacidades — toda pergunta que Iris pode receber sobre cada assunto, com o que tem e o que falta — e as três trilhas.

Medido, não reivindicado

Toda regra integrada tem precisão, recall e F1 publicados com intervalos de confiança de 95%, medidos em um corpus rotulado que vive neste repositório (proof/corpus/) e é regenerado com um único comando — npm run proof — offline, sem chave e sem modelo no processo. Esses números são de dois tipos diferentes, e a página nunca os soma: algumas regras são medidas contra rótulos que um modelo deu ao ler a própria falha, o que mede a detecção; as demais são verificadas contra sua própria definição documentada, aplicada de forma independente, o que mostra que o código implementa sua fórmula e não diz nada sobre se a fórmula captura a falha. proof/RESULTS.md e a página de prova marcam cada regra. A CI reexecuta a medição em cada pull request e falha se os números confirmados diferirem do que o código produz, então uma regra não pode mudar sem que seus números mudem junto. Os números estão em iris-eval.com/proof e em proof/RESULTS.md; como o corpus foi criado, o que ele não é e como ler um intervalo estão em docs/proof.md. O corpus é sintético e rotulado por modelo — uma rotulação humana cega está pendente, e a página diz isso; node proof/blind-sample.mjs gera a amostra reproduzível que resolverá isso.

Ferramentas MCP

O Iris registra doze ferramentas que qualquer agente compatível com MCP pode invocar — ciclo de vida de traces e regras, comparação entre execuções, LLM-como-juiz e verificação semântica de citações:

  • log_trace — Registra uma execução de agente com spans, chamadas de ferramenta, uso de tokens e custo; passe evaluate: true para pontuar na mesma chamada
  • evaluate_output — Pontua a qualidade da saída contra regras de completude, relevância, segurança e custo (heurísticas, determinísticas, gratuitas)
  • get_traces — Consulta traces armazenados com filtragem, paginação e suporte a intervalo de tempo, e encontra a execução em que o agente disse algo com q: busca de texto completo sobre entrada, saída, valores de chamadas de ferramenta e metadados, ranqueada, com as palavras correspondentes marcadas
  • list_rules — Enumera regras de avaliação personalizadas implantadas (somente leitura)
  • deploy_rule — Registra uma nova regra de avaliação personalizada para que ela seja acionada em todo evaluate_output dessa categoria
  • delete_rule — Remove uma regra personalizada implantada (destrutiva, idempotente)
  • delete_trace — Remove um único trace armazenado por ID (destrutivo, com escopo por locatário)
  • evaluate_with_llm_judge — Avaliação semântica via LLM (Anthropic ou OpenAI). Sete modelos: precisão, utilidade, segurança, correção, fidelidade, tarefa_concluída, relevância. Com limite de custo, preço por avaliação divulgado. Traga sua própria chave de API (IRIS_ANTHROPIC_API_KEY ou IRIS_OPENAI_API_KEY) — o Iris não faz proxy nem retransmite chamadas de LLM.
  • verify_citations — Extrai citações da saída (numeradas, autor-ano, URLs, DOIs), busca as fontes por trás de um resolvedor protegido contra SSRF e com lista de domínios permitidos, e usa um juiz LLM para verificar se cada fonte realmente sustenta a afirmação citada. HTTP de saída opcional. Mesmo requisito de BYOK que evaluate_with_llm_judge.
  • compare_runs — Uma mudança piorou o agente? Compara duas execuções de avaliações armazenadas: um teste exato pareado quando as execuções compartilham chaves de caso, um intervalo sobre a diferença caso contrário, um honesto "não é possível dizer" com o número de casos que seriam necessários, ou "equivalente dentro de uma margem". Cada regra carrega seu próprio teste unilateral, corrigido em conjunto (Benjamini–Hochberg) para que vinte regras não possam fabricar uma regressão
  • compare_traces — Com que confiabilidade o agente responde à mesma pergunta? Taxas de aprovação por caso com intervalos, casos instáveis primeiro, e uma taxa geral que respeita repetições
  • evaluate_runs — Reavalia cada trace de uma execução sob as regras atuais em uma nova execução, para que uma mudança de regras nunca seja lida como uma mudança do agente

Ative o juiz LLM (opcional; as regras determinísticas nunca precisam dele)

  1. Obtenha uma chave de API da Anthropic ou da OpenAI.
  2. Coloque-a no ambiente do processo que executa o Iris, não apenas no seu shell. Claude Code, Claude Desktop, Cursor e a maioria dos clientes MCP: o bloco "env" da entrada iris-eval na sua configuração MCP — "iris-eval": { "command": "npx", "args": ["-y", "@iris-eval/mcp-server"], "env": { "IRIS_ANTHROPIC_API_KEY": "sk-ant-..." } } (IRIS_OPENAI_API_KEY para uma chave da OpenAI). Docker: -e IRIS_ANTHROPIC_API_KEY=... no comando run. HTTP ou CI: exporte antes de iniciar o iris-eval.
  3. Reinicie a sessão MCP. Um processo em execução nunca vê uma variável definida depois que ele iniciou.
  4. Confirme de dentro do seu cliente: leia iris://capabilities — judge.enabled deve ser true lá. Uma chave exportada no seu shell não é passada para o processo que seu cliente inicia, a menos que a configuração dele a liste. Em uma máquina, npx @iris-eval/mcp-server --self-test imprime a linha do juiz para aquele shell, e GET /api/v1/health informa judge.enabled em um dashboard em execução.
  5. Proteção de gastos: cada chamada é limitada por IRIS_LLM_JUDGE_MAX_COST_USD_PER_EVAL (padrão 0,25 USD) e recusada antes de qualquer gasto se o pior caso exceder esse valor. O Iris chama o provedor diretamente com sua chave e nunca a usa como proxy.
  6. Opcional: defina IRIS_RELEVANCE_JUDGE_MODEL para um ID de modelo com preço (claude-haiku-4-5, por exemplo) para que answers_the_ask peça ao juiz se cada resposta aborda sua pergunta, e reprove uma fora do assunto. Isso é uma chamada de juiz por avaliação que carrega uma entrada, na sua chave e sob o limite acima; a chave sozinha nunca ativa isso. Cada chamada envia essa entrada e saída ao provedor do modelo, com os dados pessoais e credenciais dos flags no_pii substituídos primeiro (IRIS_RELEVANCE_JUDGE_REDACT=off os envia como estão). Isso gasta no máximo IRIS_RELEVANCE_JUDGE_DAILY_BUDGET_USD por dia UTC (padrão 1 USD) e faz no máximo IRIS_RELEVANCE_JUDGE_MAX_CALLS_PER_REQUEST chamadas por requisição (padrão 20); além de qualquer um desses limites, answers_the_ask lê a pergunta lexicalmente e diz o porquê.

Quando IRIS_OTEL_ENDPOINT está configurado, chamadas de log_trace também emitem uma exportação OTLP/HTTP JSON de melhor esforço para qualquer coletor OpenTelemetry (Jaeger, Grafana Tempo, Datadog OTLP, Honeycomb, etc). Veja docs/otel-integration.md.

Como passed é decidido

evaluate_output retorna tanto um flag score quanto um flag passed — eles respondem a perguntas diferentes:

  • score (0..1) é a média ponderada entre as regras que foram executadas — um gradiente de qualidade.
  • passed é o veredito de liberar/não liberar, e a pontuação nunca é consultada para ele. Um compositor lê cada regra pelo tipo de afirmação que ela faz: uma política que você configurou bloqueia; um detector crítico veta; uma verificação crítica que foi solicitada e não pôde responder torna o veredito desconhecido (passed: false) em vez de limpo; cada detector restante se combina em uma probabilidade de que a saída seja ruim, ponderada contra a razão de perda que você declara em eval.falsePassCost (padrão 1, então o corte é 0,5). verdict.basis nomeia a camada que decidiu e verdict.by as regras, e verdict.also lista toda camada posterior que também teria decidido; interpretations[] diz por que uma regra que falhou não decidiu e qual configuração mudaria isso, e nomeia qualquer pergunta que não foi julgada e a entrada que permitiria que fosse.

Violaçõs genuínas de segurança falham de forma rígida. Por padrão, no_pii, no_injection_patterns e no_blocklist_words são regras críticas: se uma falhar, a avaliação reporta passed: false não importa quão bem as outras regras pontuaram, e a resposta nomeia os culpados em critical_failures. Um SSN vazado não pode ser diluído por média. Quais regras integradas são críticas é uma configuração de implantação (eval.criticalRules / eval.nonCriticalRules); cada resultado de regra carrega o flag critical efetivo e criticalSource, e list_rules reporta o quadro que este servidor aplica. Regras personalizadas implantadas com severity: "high" ou "critical" falham de forma rígida da mesma maneira; severidades low/medium afetam apenas a pontuação. Um limite para conhecer, declarado da mesma forma em todas as superfícies: uma regra crítica que pulou (contexto ausente, definição quebrada ou um regex morto no orçamento do sandbox) não julgou a saída e não veta — cada uma dessas regras é nomeada em critical_skipped. Um portão que deve falhar fechado trata um critical_skipped não vazio como desconhecido, não limpo, e pode tratar qualquer pulo de budgetExceeded em rule_results da mesma forma.

Para portões de CI: se você omitir eval_type, todo pacote é executado — completude, relevância, segurança, custo e quaisquer regras personalizadas — e a resposta diz eval_type: "all" com um note de que o padrão foi executado, além de um mapa categories por pacote. Um pacote sem nada para julgar (custo sem cost_usd, relevância sem input) reporta passed: null lá — não avaliado, não falhando — e nunca conta para o veredito. A resposta sempre ecoa o eval_type que foi executado, para que seu portão possa verificar a cobertura; use passed para o veredito e nomeie um pacote apenas quando quiser uma execução mais restrita.

Criando uma regra personalizada

Duas maneiras de adicionar uma regra. Regras inline acompanham uma única chamada de evaluate_output (custom_rules, até 10 por chamada); elas são acionadas junto com o pacote eval_type que você escolheu, ou sozinhas com eval_type: "custom". Regras implantadas são registradas uma vez com deploy_rule, persistem em custom-rules.json sob seu home do Iris e são acionadas em todo evaluate_output futuro de seu evalType. A definição tem a mesma forma de qualquer maneira:

CampoObrigatórioO que é
namesim1–80 caracteres; aparece como ruleName nos resultados
typesimum de regex_match · regex_no_match · min_length · max_length · contains_keywords · excludes_keywords · json_schema · cost_threshold
configsimas chaves para esse tipo: pattern (+ flags opcional) para os dois tipos de regex · min_length / max_length (uma contagem de caracteres) · keywords (+ threshold opcional, 0–1, padrão 1 = todos devem aparecer) para os dois tipos de palavra-chave · {} para json_schema · max_cost em USD para cost_threshold
weightnãopeso na pontuação; padrão 1

deploy_rule envolve a definição com name, um description opcional, evalType (completeness · relevance · safety · cost · custom) e severity. A severidade diz o que uma falha significa: low/medium apenas reduzem a pontuação; high/critical falham a avaliação de forma rígida — passed: false, a regra nomeada em critical_failures — independentemente da pontuação ponderada. Uma regra que pula (uma regra de cost_threshold sem cost_usd, ou um regex morto no orçamento de 100 ms do sandbox) não julgou a saída e é listada em critical_skipped em vez disso. Implante uma regra crítica que proíbe hostnames internos em qualquer coisa que o agente diga:

{
  "name": "no_internal_hostnames",
  "description": "Output must not mention internal hostnames.",
  "evalType": "safety",
  "severity": "critical",
  "definition": {
    "name": "no_internal_hostnames",
    "type": "regex_no_match",
    "config": { "pattern": "\\b[a-z0-9-]+\\.internal\\.example\\b", "flags": "i" }
  }
}

A resposta é a regra persistida — guarde o id para delete_rule:

{ "rule": { "id": "rule-588823d0", "name": "no_internal_hostnames", "evalType": "safety", "severity": "critical", "enabled": true, "version": 1, "definition": { "…": "…" } } }

A partir do próximo evaluate_output com eval_type: "safety", uma saída que mencione db-primary.internal.example retorna passed: false com critical_failures: ["no_internal_hostnames"] — mesmo que todas as cinco regras de segurança integradas tenham passado e a pontuação ponderada seja 0,895. Padrões de regex devem passar por uma verificação de ReDoS no momento da implantação e sempre são executados em um worker de sandbox sob um prazo rígido de 100 ms. list_rules mostra o que está implantado; o compositor de regras do dashboard constrói a mesma forma a partir de uma falha em que você clicou. Referência completa, pontuação por tipo e exemplos práticos: docs/custom-rules.md.

Esquemas completos de ferramentas e configuração: iris-eval.com

Recursos hospedados

O Iris é executado inteiramente na sua máquina hoje, e tudo o que ele faz é gratuito e licenciado sob MIT, sem limites e sem conta. Armazenamento hospedado, histórico compartilhado de equipe e alertas estão em consideração, não em construção. Não há preços e nada para comprar. Se o histórico compartilhado fosse útil para você, a lista de espera é como descobrimos se vale a pena construir — isso não compromete você com nada.

Dois compromissos permanecem independentemente: nada que é gratuito hoje será colocado atrás de um paywall e nenhuma certificação de conformidade será reivindicada antes de ser obtida.

Exemplos

Comunidade

Configuração e Segurança

Argumentos de CLI

FlagPadrãoDescrição
--transportstdioTipo de transporte: stdio ou http
--port3000Porta do transporte HTTP
--db-path~/.iris/iris.dbCaminho do banco de dados SQLite
--config~/.iris/config.jsonCaminho do arquivo de configuração
--api-key—Chave de API para autenticação HTTP (transporte e dashboard, incluindo POST /api/v1/traces)
--dashboardfalseAtiva o dashboard web. Também é a única forma de o endpoint de ingestão POST /api/v1/traces iniciar — ele nunca inicia implicitamente com --transport http
--dashboard-port6920Porta do dashboard
--dashboard-host127.0.0.1Endereço de bind do dashboard. Loopback por padrão — o dashboard não é autenticado a menos que --api-key esteja definido, então vincular além do loopback expõe todo o seu histórico de traces
--demofalseSemeia um banco de dados de demonstração (separado dos seus traces reais) e serve o dashboard contra ele
--demo-clearfalseExclui o banco de dados de demonstração e sai
--self-testfalseExecuta o diagnóstico de instalação offline em um home temporário isolado e sai (0 = saudável, 1 = uma verificação falhou). Ele também lê o banco de dados configurado, somente leitura, e falha quando esta versão ou um cliente MCP fixado não consegue abri-lo
--purgefalseExclui todos os traces, spans e avaliações armazenados do banco de dados configurado, compacta o arquivo e trunca o log de write-ahead para que o texto excluído não permaneça no disco, e sai. Regras implantadas, o log de auditoria e preferências são mantidos. Não é reversível. Pare qualquer servidor Iris em execução primeiro — o arquivo é compactado no local. Recusa-se a combinar com --demo, --demo-clear ou --self-test
--version—Imprime a versão simples (ex.: 1.2.3) na saída padrão e sai com 0. Não lê nada sob o seu home do Iris

Três comandos recebem seus próprios argumentos e saem: iris-eval ingest carrega traces de um arquivo ou stdin (Um gate de CI, sem servidor necessário), iris-eval export traces|evaluations --format csv|jsonl escreve o que está armazenado, filtrado como as listas do dashboard, na saída padrão ou --out (docs/api-reference.md), e iris-eval install <client> escreve o Iris na configuração de um cliente MCP — --uninstall o remove, --list mostra os clientes encontrados nesta máquina e o Iris que cada um executa, --upgrade move cada cliente que executa o Iris para esta versão (Conecte seu próprio agente, Atualizando). Nenhum inicia um servidor.

config.json é validado quando o Iris inicia. Uma chave que o Iris não lê — um erro de digitação como eval.critcalRules, uma chave de outra ferramenta — ou um valor do tipo errado recusa a inicialização com uma frase nomeando a chave completa, a chave que ele provavelmente quis dizer ou o tipo que ele queria. Nada no arquivo é silenciosamente ignorado.

Variáveis de Ambiente

Cada variável que --help documenta. Flags de CLI têm precedência sobre variáveis de ambiente quando ambas estão definidas.

VariávelDescrição
IRIS_TRANSPORTTipo de transporte (stdio ou http)
IRIS_HOSTEndereço de bind do transporte HTTP (padrão 127.0.0.1)
IRIS_PORTPorta do transporte HTTP (1-65535, padrão 3000)
IRIS_HOMEDiretório para todos os arquivos por usuário: config.json, iris.db, custom-rules.json, audit.log, preferences.json (padrão ~/.iris)
IRIS_DB_PATHCaminho do banco de dados SQLite (substitui IRIS_HOME apenas para o banco de dados)
IRIS_SQLITE_DRIVERQual driver SQLite mantém o banco de dados: native (better-sqlite3, o padrão) ou node (o node:sqlite embutido do Node, Node 22.13+). Não definido: nativo, e quando o módulo nativo não pode carregar (ou é uma compilação que abortaria neste Node) o Iris avisa uma vez e recai para o embutido
IRIS_SEARCH_BUDGET_MSQuanto tempo uma busca de trace (q) pode ler antes de responder com as correspondências encontradas até agora e search.complete: false, em milissegundos (50 a 60000, padrão 1000). Uma busca segura outras solicitações enquanto lê, então este também é o máximo que pode fazê-las esperar. Também storage.searchBudgetMs em config.json
IRIS_SEARCH_INDEXon (o padrão) ou off. off não mantém índice de texto completo dos traces: uma escrita armazena o trace e nada mais, e uma busca de trace (q) lê os próprios traces dentro de IRIS_SEARCH_BUDGET_MS, do mais novo ao mais antigo, então em um armazenamento grande pode responder com parte das correspondências (search.complete: false). Desativá-lo apaga o índice que o banco de dados mantinha; ativá-lo novamente constrói um novo em segundo plano. Também storage.searchIndex em config.json
IRIS_LOG_LEVELNível de log: debug, info, warn, error
IRIS_DASHBOARDtrue/1/yes/on ativa o dashboard web; false/0/no/off o desativa (também substitui dashboard.enabled em config.json)
IRIS_DASHBOARD_PORTPorta do dashboard (1-65535, padrão 6920)
IRIS_WEBHOOK_URLO receptor do webhook que dispara em um momento — mesclado sobre notify.webhook em config.json (docs/webhooks.md)
IRIS_WEBHOOK_SECRETA chave de assinatura do webhook (qualquer string, ou whsec_ + base64); o formato iris recusa-se a executar sem uma
IRIS_DASHBOARD_HOSTEndereço de bind do dashboard (padrão 127.0.0.1)
IRIS_API_KEYChave de API para autenticação HTTP. Necessária para vincular o transporte HTTP ou o dashboard além do loopback (0.0.0.0, um endereço LAN, um contêiner): sem ela o servidor recusa-se a iniciar
IRIS_API_KEY_FILECaminho para um arquivo cujo conteúdo aparado é a chave de API — o padrão de arquivo secreto que Docker e Kubernetes montam, para que a chave nunca fique em um bloco de ambiente. Defina esta ou IRIS_API_KEY, não ambas
IRIS_ALLOW_UNAUTHENTICATEDDefina como 1 para executar um bind não-loopback com nenhuma chave de propósito (remove a recusa; a rede é então seu limite)
IRIS_ALLOWED_ORIGINSLista de permissões de origens separadas por vírgula. Dashboard: cabeçalhos CORS (suporta globs, ex.: http://localhost:*). Transporte HTTP: lista de permissões Origin de correspondência exata para proteção contra DNS-rebinding (globs ignorados; as origens de loopback do próprio servidor são sempre permitidas)
IRIS_NO_AUTO_LAUNCHDefina como 1 para desativar o auto-lançamento do dashboard na primeira execução
IRIS_ANTHROPIC_API_KEYNecessário por evaluate_with_llm_judge + verify_citations com provider=anthropic
IRIS_OPENAI_API_KEYNecessário por evaluate_with_llm_judge + verify_citations com provider=openai
IRIS_LLM_JUDGE_MAX_COST_USD_PER_EVALLimite de custo rígido por chamada de juiz LLM (padrão 0.25)
IRIS_RELEVANCE_JUDGE_MODELUm id de modelo de juiz com preço (ex.: claude-haiku-4-5). Quando definido, com a chave desse provedor, answers_the_ask pergunta a este juiz LLM em cada avaliação que carrega uma entrada e limita com base em seu veredito de relevância — uma chamada de juiz por avaliação, sob o limite de custo acima e os dois limites abaixo. A entrada e a saída de cada uma dessas avaliações são enviadas ao provedor desse modelo (Anthropic ou OpenAI) na sua chave, com os dados pessoais e credenciais que no_pii sinaliza substituídos primeiro. Não definido (o padrão), answers_the_ask lê a pergunta lexicalmente e aconselha, e nada é enviado (docs/llm-as-judge.md)
IRIS_RELEVANCE_JUDGE_DAILY_BUDGET_USDO que o juiz de relevância pode gastar por dia UTC, por tenant (padrão 1). Mantido no banco de dados, então um reinício não o redefine. Uma chamada é feita apenas se seu pior caso couber no que resta; além disso, answers_the_ask lê a pergunta lexicalmente e judge.withheld é daily_budget. 0 interrompe todas as chamadas
IRIS_RELEVANCE_JUDGE_MAX_CALLS_PER_REQUESTChamadas de juiz de relevância que uma solicitação pode fazer (padrão 20): um lote OTLP ou uma re-pontuação evaluate_runs julga seus primeiros 20 traces e lê o resto lexicalmente, com judge.withheld: "request_cap"
IRIS_RELEVANCE_JUDGE_REDACTon (padrão): cada span que no_pii sinaliza (dados pessoais e credenciais) na entrada e saída é substituído por um marcador [REDACTED:<kind>#<n>] antes de serem enviados ao juiz de relevância. off os envia como estão
IRIS_CITATION_ALLOW_FETCHDefina como 1 para permitir HTTP de saída em verify_citations (desativado por padrão)
IRIS_CITATION_DOMAINSLista de permissões de hostnames separadas por vírgula para verify_citations (correspondência de sufixo)
IRIS_OTEL_ENDPOINTAtiva exportação de traces JSON OTLP/HTTP de melhor esforço para esta URL de coletor
IRIS_OTEL_SERVICE_NAMEAtributo de recurso service.name para exportação OTel (padrão iris-eval)
IRIS_OTEL_HEADERSCabeçalhos k=v separados por vírgula para exportação OTel (ex.: authorization=Bearer abc)
IRIS_OTEL_TIMEOUT_MSTempo limite por exportação (padrão 15000)
RATE_LIMIT_SALTApenas API da lista de espera do site — necessária quando o site iris-eval.com está implantado; o servidor nunca a lê

Segurança

Ao usar transporte HTTP, o Iris inclui:

  • Autenticação por chave de API com comparação de tempo constante (Bearer para clientes de API; login no navegador no dashboard via ?key=)
  • CORS restrito a localhost por padrão
  • Limitação de taxa por endereço de cliente e minuto: 600 solicitações à API do dashboard (security.rateLimit.api) e 20 ao endpoint MCP (security.rateLimit.mcp), ambos definidos em config.json; uma solicitação MCP acima do limite recebe um erro JSON-RPC que nomeia a chave
  • Cabeçalhos de segurança Helmet
  • Validação de entrada Zod em todas as rotas
  • Regex seguro contra ReDoS para regras de avaliação personalizadas
  • Um limite de tamanho de solicitação de 1MB em cada transporte (security.requestSizeLimit): HTTP responde 413, stdio responde um erro JSON-RPC e mantém a sessão aberta
# Production deployment
iris-eval --transport http --port 3000 --api-key "$(openssl rand -hex 32)" --dashboard

Com uma chave definida, os clientes de API — clientes MCP, SDKs de captura, POST /api/v1/traces — enviam Authorization: Bearer <key>. Para abrir o dashboard em um navegador, acrescente a chave uma vez a qualquer URL do dashboard, http://localhost:6920/?key=<api key>: o Iris a troca por um cookie de sessão HttpOnly, SameSite=Lax e redireciona para a mesma página com a chave removida da barra de endereço. Uma página aberta sem sessão mostra um formulário de login que faz a mesma troca. A chave nunca é armazenada no navegador, e as sessões vivem apenas no processo do servidor (no máximo 256 ativas por vez; um login que as encontre todas ativas é recusado em vez de remover uma).

Produção

Várias chaves e rotação sem lacuna. security.apiKeys em config.json contém qualquer número de chaves adicionais, cada uma com um id e exatamente um de keyFile (um arquivo cujo conteúdo aparado é a chave) ou keyHash (o sha256 hex da chave, para que o arquivo de configuração não contenha segredo — printf %s "$KEY" | openssl dgst -sha256), e um expiresAt opcional (ISO 8601) após o qual ela para de corresponder naquele instante. Para rotacionar: adicione a nova chave, mova seus clientes, remova a chave antiga. As chaves em config.json e em arquivos de chave entram em vigor sem reinicialização (0.20.0): a cada requisição, o servidor verifica se config.json ou um arquivo de chave que ele nomeia mudou e, se mudou, relê as chaves antes de responder. Remover uma chave de security.apiKeys, ou excluir seu arquivo de chave, a revoga na próxima requisição: essa requisição é recusada, e toda sessão de navegador aberta com ela é desconectada. Um config.json que não pode ser lido (por exemplo, gravado pela metade) falha de forma segura e, até ser corrigido, apenas uma chave de IRIS_API_KEY ou --api-key é aceita. A chave em IRIS_API_KEY ou --api-key em si, e se a autenticação está ativa ou não, ainda mudam apenas em uma reinicialização. Toda chave autentica até ser removida ou expirar, tanto no caminho Bearer quanto no login do navegador; o log de inicialização nomeia os ids. security.rateLimit.mcpKeyBy: "apiKey" conta o orçamento por minuto do endpoint MCP por chave em vez de por endereço de cliente, para que vários agentes atrás de um endereço tenham cada um seu próprio minuto.

O Iris recusa-se a iniciar quando o transporte HTTP ou o dashboard está vinculado além do loopback — 0.0.0.0, um endereço LAN, um contêiner — sem chave de API, e diz isso em uma frase nomeando IRIS_API_KEY. Isso inclui um docker run simples da imagem, que vincula 0.0.0.0 dentro do contêiner porque o loopback é inacessível por meio de uma porta publicada. Loopback sem chave continua funcionando (com um aviso no transporte HTTP): o limite da máquina é o controle de exposição ali.

# The image: pass a key
docker run -p 3000:3000 -p 6920:6920 -v iris-data:/data \
  -e IRIS_API_KEY="$(openssl rand -hex 32)" ghcr.io/iris-eval/mcp-server

# Compose: the file requires the variable and refuses before the container starts
IRIS_API_KEY="$(openssl rand -hex 32)" docker compose up

# A network you have already fenced some other way: run open, on purpose
IRIS_ALLOW_UNAUTHENTICATED=1 iris-eval --transport http --dashboard

Aberto por design, em um servidor com chave: GET /health no transporte e GET /api/v1/health no dashboard respondem sem chave e fora de todo limite de taxa, em um único formato: status, versão, tempo de atividade, o driver SQLite, checks para armazenamento, o arquivo de regras implantadas e as migrações (aplicadas contra as conhecidas), o estado do índice de busca (search: pronto, ou até onde uma construção chegou como parcela dos traces), e se uma chave de juiz está presente — nunca a chave, nunca um trace, nunca uma contagem deles. status é ok apenas quando todas as verificações são; caso contrário, é degraded com HTTP 503, que o HEALTHCHECK da própria imagem Docker lê. Todo o resto precisa de Authorization: Bearer <key> ou de uma sessão de navegador. A retenção é executada em todo servidor: traces e avaliações mais antigos que retention.days (padrão 30) são excluídos na inicialização, assim que o servidor está respondendo, e a cada retention.sweepIntervalHours, em etapas curtas que nunca fazem uma requisição esperar muito; --self-test imprime a política desta instalação, e iris://capabilities / GET /api/v1/capabilities a carregam como retention.

Um webhook dispara em um momento (0.16.0): notify.webhook em config.json (ou IRIS_WEBHOOK_URL e IRIS_WEBHOOK_SECRET) nomeia um receptor, e o Iris publica uma mensagem assinada quando um veredito falha, uma detecção crítica veta, um custo é um valor atípico, a taxa de falha de uma regra muda ou um caso é respondido de ambas as formas pela primeira vez — ids, o veredito, as regras e os números, nunca o texto do agente. Assinada do jeito Standard Webhooks e do jeito GitHub ao mesmo tempo, repetida com backoff, com resfriamento por agente e regra, nunca no caminho da avaliação; corpos de Slack e Discord embutidos. docs/webhooks.md.

Seus dados no disco

Tudo o que o Iris armazena vive sob seu diretório inicial do Iris (~/.iris, ou IRIS_HOME). iris.db mantém o input e o output de cada trace verbatim — incluindo qualquer texto que no_pii venha a sinalizar; a detecção não remove nem edita a menos que você peça: storage.redact: "critical_spans" em config.json armazena a saída de cada avaliação com os spans sinalizados por um detector crítico substituídos por [REDACTED:<pattern>] (desativado por padrão; os offsets de evidência ainda indexam o texto que o chamador viu). storage.synchronous define quando uma gravação chega ao disco: normal (o padrão) sincroniza o log de write-ahead em cada checkpoint, então uma falha do Iris não perde nada e o arquivo não pode ser corrompido, mas uma queda de energia ou uma falha do sistema operacional pode desfazer as gravações desde a última sincronização; full sincroniza cada commit e os mantém através de ambos, a cerca de 1,5 ms a mais por gravação. Na inicialização, e a cada retention.sweepIntervalHours (padrão 24, 0 desativa o temporizador) depois disso, traces e avaliações mais antigos que retention.days (padrão 30, 0 desativa, definido em config.json) são excluídos e o log de write-ahead é submetido a checkpoint. Excluir um trace — por delete_trace ou pela varredura — apaga o texto de toda avaliação vinculada a ele (a saída, o texto esperado e as mensagens de regra) e carimba erased_at; o veredito, as pontuações e os offsets de evidência permanecem. Cada exclusão faz checkpoint do log de write-ahead antes de retornar, então o texto excluído não fica legível em iris.db ou iris.db-wal (se uma busca está lendo o arquivo naquele momento, ou outro processo está lendo ou gravando nele, a exclusão retorna sem esperar e o texto sai do arquivo assim que termina). Para remover tudo agora, pare o servidor e execute --purge: ele exclui todo trace, span e avaliação armazenados, compacta o banco de dados e trunca o log de write-ahead para que o texto desapareça do disco, e mantém suas regras implantadas, log de auditoria e preferências. Antes de uma versão aplicar uma migração a um iris.db existente, ela copia o arquivo ao lado dele (iris.db.<from>-to-<to>.<time>.bak, somente do proprietário, os três mais recentes mantidos; Downgrading): a cópia contém os traces como estavam, então a varredura de retenção exclui um mais antigo que retention.days e --purge os exclui todos. O servidor faz a cópia e as migrações depois de responder ao seu cliente, em uma thread própria: chamadas de ferramenta, leituras de recurso e requisições HTTP que chegam enquanto isso esperam por elas, no máximo 30 s cada, e então são recusadas com uma frase dizendo o que o servidor está fazendo (IRIS_STORAGE_ERROR, repetível; HTTP 503 com Retry-After). Health responde durante todo o processo e diz o que a atualização está fazendo. A partir de 0.19.0, com 100.000 traces que são cada um um loop de agente, a cópia e as migrações levaram cerca de 6 s. iris-eval ingest, --purge e --self-test ainda atualizam antes de fazer qualquer outra coisa.

O Iris não criptografa seus dados em repouso. iris.db e seus arquivos de log de write-ahead são criados somente do proprietário (modo 600), e o diretório inicial do Iris é criado no modo 700 (no Windows, ACLs de arquivo governam em vez disso). O banco de dados não armazena chaves de provedor de LLM: IRIS_ANTHROPIC_API_KEY e IRIS_OPENAI_API_KEY são lidos do ambiente e nunca gravados no disco. Ele armazena entradas e saídas de trace verbatim, então coloque o diretório inicial do Iris em um disco ou volume criptografado (FileVault, BitLocker, LUKS ou um volume de nuvem criptografado para o mount /data da imagem Docker).

Uma exportação — o botão Export nas páginas Traces e Evaluations do dashboard, GET /api/v1/traces/export e /api/v1/evaluations/export, ou iris-eval export — carrega esse texto armazenado como está, igual ao que o dashboard mostra: entrada e saída de trace verbatim, saída de avaliação com storage.redact aplicado. Trate um arquivo exportado como o banco de dados de onde veio.

Solução de problemas

Primeiro passo: execute o autoteste

npx @iris-eval/mcp-server --self-test

Ele verifica armazenamento, as avaliações determinísticas e o dashboard em um diretório inicial temporário isolado e imprime um veredito por etapa — a saída de falha nomeia a etapa quebrada. Código de saída 0 significa que a instalação está saudável.

O Iris não inicia / ERR_MODULE_NOT_FOUND

Você pode ter uma versão mais antiga em cache. Limpe o cache do npx e tente novamente:

npx --yes @iris-eval/mcp-server@latest

Ou instale globalmente para evitar problemas de cache por completo:

npm install -g @iris-eval/mcp-server@latest

npm install --ignore-scripts quebrou o binding do SQLite

O Iris armazena traces com better-sqlite3, um módulo nativo que busca ou compila seu binding em um script de instalação. Se esse script foi pulado — --ignore-scripts na linha de comando, ignore-scripts=true em um .npmrc (comum em máquinas corporativas) ou um espelho de registro que remove postinstall — a inicialização falha com um despejo longo "Could not locate the bindings file" listando uma dúzia de caminhos que tentou. Reconstrua esse único módulo:

npm rebuild better-sqlite3
# for a global install:
npm rebuild -g better-sqlite3

Ferramentas não aparecendo no Claude Code

Ferramentas MCP só carregam no início da sessão. Após adicionar iris-eval, reinicie a sessão com /clear ou relance o terminal.

Verificação de versão

npx @iris-eval/mcp-server --version

A primeira linha de log de inicialização também a carrega (Starting Iris MCP server vX.Y.Z), e --self-test a imprime em seu resumo. Para uma instalação global, npm ls -g @iris-eval/mcp-server mostra a versão instalada.

Atualização

Todo cliente MCP em uma máquina compartilha um banco de dados, ~/.iris/iris.db, e install fixa cada cliente na versão que gravou sua configuração. Quando uma versão muda o esquema do banco de dados, o primeiro processo dessa versão a abrir o arquivo o atualiza e, a partir daí, um cliente ainda fixado em uma versão mais antiga recusa-se a iniciar. Então mova todo cliente em um único passo, antes ou logo após atualizar:

npx -y @iris-eval/mcp-server@latest install --upgrade

Ele encontra toda configuração de cliente nesta máquina que executa o Iris, move cada fixação para essa versão (mantendo o que você adicionou à entrada, como --dashboard ou um bloco env), deixa intacta uma fixação em uma versão mais nova e uma entrada que executa algo diferente do pacote npm, e lista o que fez. Reinicie os clientes que nomeia. install --list mostra qual Iris cada cliente executa.

Duas instalações vivem fora desses arquivos: a extensão do Claude Desktop (iris-eval.mcpb) move quando você abre um bundle mais novo, e os plugins do Claude Code com claude plugin marketplace update iris-eval e depois claude plugin update iris-eval@iris-eval (e claude plugin update iris-eval-capture@iris-eval para o plugin de captura).

Atualizando de 0.19.x para 0.20.0. 0.20.0 adiciona o índice de busca e outras adições ao banco de dados (migrações 015 e posteriores). Assim que qualquer processo 0.20.0 abrir ~/.iris/iris.db (a extensão do Claude Desktop, npx iris-eval ou npx @iris-eval/mcp-server sem versão), um cliente fixado em 0.19.x para com This database was migrated by a newer Iris (…) — migration(s) 015-trace-search, … are unknown to v0.19.0. Upgrade Iris, …. Essa mensagem vem de 0.19.x e não pode mudar; a correção é o comando acima. Antes da atualização, 0.20.0 copia o arquivo ao lado dele, então voltar também é possível (abaixo).

Uma inicialização que atualiza o banco de dados imprime o que fez no stderr: a cópia que tirou, quais versões mais antigas não podem mais abrir o arquivo e qualquer cliente nesta máquina fixado em uma delas, com o comando. --self-test lê o banco de dados sem alterá-lo e diz o mesmo antes de você iniciar qualquer coisa.

Para uma instalação global, npm update -g @iris-eval/mcp-server, depois iris-eval install --upgrade.

Downgrading

Uma versão que atualizou o banco de dados o copia primeiro, ao lado dele: iris.db.<from>-to-<to>.<time>.bak no seu diretório Iris (<from> é a versão que alterou o esquema do arquivo por último, <to> a que o atualizou; a linha de inicialização imprimiu o caminho exato). Para reverter:

  1. Pare todos os clientes MCP e qualquer outro processo Iris que use o banco de dados.
  2. Mantenha o arquivo atualizado, caso você volte: renomeie iris.db para iris.db.upgraded e exclua iris.db-wal e iris.db-shm se eles existirem.
  3. Copie o backup para iris.db: cp ~/.iris/iris.db.0.19.0-to-0.20.0.<time>.bak ~/.iris/iris.db.
  4. Fixe cada cliente de volta na versão mais antiga: npx -y @iris-eval/mcp-server@0.19.0 install <client> para cada um (install --upgrade nunca move um cliente de volta).

Os rastros armazenados após a atualização estão em iris.db.upgraded, não no backup. Se nenhuma cópia foi feita (a linha de inicialização explica o motivo, por exemplo, disco cheio), a versão mais antiga não consegue abrir o arquivo atualizado, e o caminho a seguir é install --upgrade.

O driver de armazenamento

Em uma plataforma sem better-sqlite3 pré-compilado, a instalação ainda é bem-sucedida. better-sqlite3 é uma dependência opcional: quando o npm não consegue baixar um binário pré-compilado para o seu Node e plataforma nem compilar um (compilar requer Python e um kit de ferramentas C++ — as ferramentas de build C++ do Visual Studio no Windows), o npm imprime o erro de build, pula o módulo e termina a instalação. O Iris então roda no SQLite embutido do Node e avisa: a inicialização imprime uma linha no stderr explicando o motivo, e --self-test mostra driver node: better-sqlite3 is not installed …. Para recuperar o driver nativo, instale-o onde exista um pré-build ou um kit de ferramentas (npm install better-sqlite3 no projeto; para uma instalação global, instale o Iris novamente com npm install -g @iris-eval/mcp-server assim que um kit de ferramentas estiver disponível). O CI instala o servidor empacotado com o build nativo forçado a falhar em cada mudança e exige que a instalação termine e o autoteste armazene e leia um rastro no embutido.

O Iris mantém tudo em um único arquivo SQLite, aberto por better-sqlite3 — um addon nativo que é baixado ou compilado para o seu Node e plataforma. Quando esse módulo não consegue carregar, o Iris recorre ao SQLite embutido do Node (node:sqlite, Node 22.13 ou posterior) com um aviso no stderr, então um pré-build ausente é um início mais lento, não um travamento. Ele faz o mesmo, antes de carregá-lo, para um better-sqlite3 compilado na sua máquina contra cabeçalhos do Node 24.19 ou posterior: em todas as versões 24.x até agora, esse binário aborta o processo inteiro na primeira vez que libera uma declaração (Assertion failed: (env) != nullptr, nodejs/node#65446), e npm rebuild better-sqlite3 o substitui pelo binário pré-compilado, que é seguro. IRIS_SQLITE_DRIVER=node escolhe o embutido de propósito, native proíbe o fallback. O embutido é aberto com o carregamento de extensões desativado e trusted_schema desativado; o Node imprime sua própria linha ExperimentalWarning: SQLite is an experimental feature no stderr quando carrega, e o Iris não a silencia. --self-test e GET /health nomeiam o driver em uso; cada número na página de prova foi medido no driver nativo, e a suíte de testes roda em ambos no CI.

Versão do Node.js

O Iris requer Node.js 22.13 ou posterior. O Node 20 atingiu o fim da vida útil em 2026-04-30 e não é suportado; o Node 18 saiu em abril de 2025.

O mínimo é 22.13 em vez de 22.0 porque 22.13.0 é a primeira versão que inclui node:sqlite. Isso a torna a primeira versão em que toda instalação suportada do Iris tem um segundo driver de armazenamento: quando o addon nativo better-sqlite3 não carrega, o Iris recorre ao SQLite embutido do Node em vez de falhar ao iniciar. Abaixo de 22.13 — e no Node 20, por toda a sua vida — havia apenas um driver, e um pré-build ausente era um início travado.

node --version  # Must be v22.13.0 or newer

Windows: cmd /c não é necessário

O /doctor do Claude Code pode sugerir envolver o npx com cmd /c. Isso não é necessário e causa problemas de análise de caminho. Use npx diretamente:

# Correct
claude mcp add --transport stdio iris-eval -- npx -y @iris-eval/mcp-server

# Wrong (causes /c to be parsed as a path)
claude mcp add --transport stdio iris-eval -- cmd /c "npx -y @iris-eval/mcp-server"

Se o Iris for útil para você, considere dar uma estrela no repositório — isso ajuda outras pessoas a encontrá-lo.

Star on GitHub

Licenciado sob MIT.