@iris-eval/mcp-server 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +7 -3
  2. package/dist/config/defaults.js +17 -1
  3. package/dist/config/index.js +8 -0
  4. package/dist/dashboard/assets/{index-CshLgDRB.js → index-DTA8DzF_.js} +1 -1
  5. package/dist/dashboard/index.html +1 -1
  6. package/dist/dashboard/routes/rules.d.ts +8 -11
  7. package/dist/dashboard/routes/rules.js +9 -16
  8. package/dist/dashboard/routes/traces.js +14 -2
  9. package/dist/dashboard/validation.d.ts +4 -4
  10. package/dist/dashboard/validation.js +6 -2
  11. package/dist/eval/criticality.d.ts +67 -0
  12. package/dist/eval/criticality.js +154 -0
  13. package/dist/eval/engine.d.ts +33 -2
  14. package/dist/eval/engine.js +64 -8
  15. package/dist/eval/rules/cost.d.ts +9 -0
  16. package/dist/eval/rules/cost.js +97 -1
  17. package/dist/eval/rules/relevance.d.ts +13 -0
  18. package/dist/eval/rules/relevance.js +185 -21
  19. package/dist/eval/rules/safety.d.ts +13 -1
  20. package/dist/eval/rules/safety.js +273 -21
  21. package/dist/eval/rules/trajectory.d.ts +91 -0
  22. package/dist/eval/rules/trajectory.js +297 -0
  23. package/dist/index.js +1 -1
  24. package/dist/self-test.js +1 -1
  25. package/dist/server.js +1 -1
  26. package/dist/tools/evaluate-output.js +38 -23
  27. package/dist/tools/index.js +1 -1
  28. package/dist/tools/list-rules.d.ts +2 -1
  29. package/dist/tools/list-rules.js +22 -4
  30. package/dist/tools/log-trace.d.ts +8 -1
  31. package/dist/tools/log-trace.js +19 -4
  32. package/dist/tools/strict-input.js +2 -2
  33. package/dist/tools/trace-link.d.ts +10 -0
  34. package/dist/tools/trace-link.js +13 -1
  35. package/dist/types/config.d.ts +14 -0
  36. package/dist/types/eval.d.ts +39 -7
  37. package/package.json +8 -1
  38. package/server.json +2 -2
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![PulseMCP](https://img.shields.io/badge/PulseMCP-Listed-blue?style=flat-square)](https://www.pulsemcp.com/servers/iris-eval)
14
14
  [![mcp.so](https://img.shields.io/badge/mcp.so-Listed-blue?style=flat-square)](https://mcp.so/server/iris/iris-eval)
15
15
 
16
- **Iris scores every agent run for quality, safety, and cost — on your machine, with no SDK and no account.** Most agent projects check quality by running a few remembered prompts and eyeballing the output. Iris replaces that with numbers you can audit: your agent's runs land in a SQLite database on your disk, 13 built-in rules score them deterministically — PII, prompt injection, hallucination markers, cost thresholds — free, with no LLM calls, and an optional LLM judge with a hard per-eval cost cap handles the semantic questions. Every rule is inspectable and editable, because a judge you can't audit is just vibes with a number on it. MIT licensed, no telemetry; your traces never leave your machine.
16
+ **Iris scores every agent run for quality, safety, and cost — on your machine, with no SDK and no account.** Most agent projects check quality by running a few remembered prompts and eyeballing the output. Iris replaces that with numbers you can audit: your agent's runs land in a SQLite database on your disk, 15 built-in rules score them deterministically — PII, prompt injection, hallucination markers, cost thresholds, and the agent's own tool calls — free, with no LLM calls, and an optional LLM judge with a hard per-eval cost cap handles the semantic questions. Every rule is inspectable and editable, because a judge you can't audit is just vibes with a number on it. MIT licensed, no telemetry; your traces never leave your machine.
17
17
 
18
18
  **Requires Node.js 20 or later.** Check with `node --version`.
19
19
 
@@ -185,7 +185,7 @@ docker run -p 3000:3000 -p 6920:6920 -v iris-data:/data ghcr.io/iris-eval/mcp-se
185
185
  | | |
186
186
  |---|---|
187
187
  | **Trace Logging** | Hierarchical span trees with per-tool-call latency, token usage, and cost in USD. Stored in SQLite, queryable instantly. |
188
- | **Output Evaluation** | 13 built-in rules across 4 categories: completeness, relevance, safety, cost. PII detection (19 patterns: SSN, credit card, phone, email, IBAN, DOB, MRN, IP, API key, passport, plus AWS/Slack/SendGrid/GitHub/Google/npm/DigitalOcean tokens, PEM private-key blocks and seed phrases), prompt injection (37 patterns, phrase + structural), stub-output detection, hallucination detection (25 context-grounded fabrication/contradiction signals — pass `input` to ground them against the agent's source material). Add custom rules with Zod schemas. |
188
+ | **Output Evaluation** | 15 built-in rules across 4 categories: completeness, relevance, safety, cost. PII detection (19 patterns: SSN, credit card, phone, email, IBAN, DOB, MRN, IP, API key, passport, plus AWS/Slack/SendGrid/GitHub/Google/npm/DigitalOcean tokens, PEM private-key blocks and seed phrases), prompt injection (37 patterns, phrase + structural), stub-output detection, hallucination detection (25 context-grounded fabrication/contradiction signals — pass `input` to ground them against the agent's source material), and two trajectory rules that read the agent's `tool_calls`: an unacknowledged failed tool call, and a repeated one. Add custom rules with Zod schemas. |
189
189
  | **LLM-as-Judge** | Optional semantic scoring via Anthropic or OpenAI — bring your own API key. Five templates. Hard per-eval cost cap (`IRIS_LLM_JUDGE_MAX_COST_USD_PER_EVAL`, default $0.25), per-eval pricing disclosed in the result. |
190
190
  | **Cost Visibility** | Aggregate cost across all agents over any time window. Set budget thresholds. Get flagged when agents overspend. |
191
191
  | **Web Dashboard** | Real-time dark-mode UI that lands on the failures, worst and newest first — trace visualization, eval results, cost breakdowns, and a command palette (⌘K) that searches your own rules, traces, and evals. |
@@ -193,6 +193,10 @@ docker run -p 3000:3000 -p 6920:6920 -v iris-data:/data ghcr.io/iris-eval/mcp-se
193
193
 
194
194
  Where this is going next: [the roadmap](https://github.com/iris-eval/mcp-server/blob/main/docs/roadmap.md).
195
195
 
196
+ ### Measured, not claimed
197
+
198
+ Every built-in rule has a published precision, recall and F1 with 95% confidence intervals, measured on a labelled corpus that lives in this repository (`proof/corpus/`) and regenerates with one command — `npm run proof` — offline, with no key and no model in the loop. CI re-runs the measurement on every pull request and fails if the committed numbers differ from what the code produces, so a rule cannot change without its numbers changing with it. The numbers are on [iris-eval.com/proof](https://iris-eval.com/proof) and in [`proof/RESULTS.md`](https://github.com/iris-eval/mcp-server/blob/main/proof/RESULTS.md); how the corpus was made, what it is not, and how to read an interval are in [docs/proof.md](https://github.com/iris-eval/mcp-server/blob/main/docs/proof.md). The corpus is synthetic and model-labelled — a human blind label is pending, and the page says so.
199
+
196
200
  ## MCP Tools
197
201
 
198
202
  Iris registers nine tools that any MCP-compatible agent can invoke — full rule + trace lifecycle + LLM-as-judge + semantic citation verification:
@@ -218,7 +222,7 @@ When `IRIS_OTEL_ENDPOINT` is configured, `log_trace` calls also emit a best-effo
218
222
 
219
223
  Genuine safety violations hard-fail. `no_pii`, `no_injection_patterns`, and `no_blocklist_words` are **critical rules**: if one fails, the eval reports `passed: false` no matter how well the other rules scored, and the response names the culprits in `critical_failures`. A leaked SSN can't be averaged away. Custom rules deployed with `severity: "high"` or `"critical"` hard-fail the same way; `low`/`medium` severities only affect the score. One boundary to know: a critical rule that **skipped** (missing context, or any other cause of a skip) has not judged the output and does not veto — it is listed in `critical_skipped`, and `rule_results` shows every skip and its reason, so a gate that must fail closed on non-verdicts can.
220
224
 
221
- One gotcha for CI gates: if you omit `eval_type`, the default `completeness` bundle runs — **safety rules don't**. The response echoes `eval_type` (plus a `note` when it was defaulted) so your gate can verify which bundle actually ran. Key on `passed` for the verdict and `eval_type: "safety"` for coverage.
225
+ For CI gates: if you omit `eval_type`, **every bundle runs**completeness, relevance, safety, cost and any custom rules and the response says `eval_type: "all"` with a `note` that the default ran, plus a per-bundle `categories` map. A bundle with nothing to judge (cost without `cost_usd`, relevance without `input`) reports `passed: null` there — not evaluated, not failing — and never counts toward the verdict. The response always echoes the `eval_type` that ran, so your gate can verify coverage; key on `passed` for the verdict and name a bundle only when you want a narrower run.
222
226
 
223
227
  ### Authoring a custom rule
224
228
 
@@ -38,10 +38,26 @@ export const defaultConfig = {
38
38
  min_output_length: 50,
39
39
  min_sentences: 2,
40
40
  keyword_overlap: 0.35,
41
- topic_consistency: 0.10,
41
+ // Share of content-bearing sentences that must connect to the input's
42
+ // topic (a third — see topic_consistency in src/eval/rules/relevance.ts).
43
+ topic_consistency: 0.33,
42
44
  cost_threshold: 0.10,
43
45
  max_token_ratio: 5,
46
+ // Identical tool calls tolerated before no_tool_loop fires. Three
47
+ // allows a legitimate retry-with-backoff; the fourth is a loop.
48
+ max_tool_repeats: 3,
44
49
  },
50
+ /*
51
+ * Which built-in rules veto is a deployment's call, and the shipped
52
+ * answer is "the three the rules themselves declare" — no_pii,
53
+ * no_injection_patterns, no_blocklist_words. Both lists start empty so
54
+ * the default behaviour is exactly the rules' own declarations; a
55
+ * config.json that sets either one REPLACES the empty list (arrays are
56
+ * not deep-merged). See src/eval/criticality.ts for why this is
57
+ * configurable at all, and /proof for the error rates to decide with.
58
+ */
59
+ criticalRules: [],
60
+ nonCriticalRules: [],
45
61
  },
46
62
  logging: {
47
63
  level: 'info',
@@ -1,6 +1,7 @@
1
1
  import { readFileSync, mkdirSync, existsSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
3
  import { defaultConfig } from './defaults.js';
4
+ import { assertValidCriticality } from '../eval/criticality.js';
4
5
  import { irisHome } from '../utils/iris-home.js';
5
6
  /*
6
7
  * Owner-only (0700) for the iris home directory, matching the 0600 the data
@@ -172,5 +173,12 @@ export function loadConfig(cliArgs) {
172
173
  config = deepMerge(config, envConfig);
173
174
  config = deepMerge(config, argsConfig);
174
175
  ensureIrisDirectory(dirname(config.storage.path), 'the database directory (IRIS_DB_PATH / --db-path)');
176
+ /*
177
+ * Fail at STARTUP on a bad rule name, before a single evaluation runs.
178
+ * A typo in eval.criticalRules that quietly did nothing would leave an
179
+ * operator believing their deploy gate exists when it does not — the same
180
+ * "detection that reports an all-clear" failure the veto exists to stop.
181
+ */
182
+ assertValidCriticality(config.eval);
175
183
  return config;
176
184
  }