@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.
- package/README.md +7 -3
- package/dist/config/defaults.js +17 -1
- package/dist/config/index.js +8 -0
- package/dist/dashboard/assets/{index-CshLgDRB.js → index-DTA8DzF_.js} +1 -1
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/routes/rules.d.ts +8 -11
- package/dist/dashboard/routes/rules.js +9 -16
- package/dist/dashboard/routes/traces.js +14 -2
- package/dist/dashboard/validation.d.ts +4 -4
- package/dist/dashboard/validation.js +6 -2
- package/dist/eval/criticality.d.ts +67 -0
- package/dist/eval/criticality.js +154 -0
- package/dist/eval/engine.d.ts +33 -2
- package/dist/eval/engine.js +64 -8
- package/dist/eval/rules/cost.d.ts +9 -0
- package/dist/eval/rules/cost.js +97 -1
- package/dist/eval/rules/relevance.d.ts +13 -0
- package/dist/eval/rules/relevance.js +185 -21
- package/dist/eval/rules/safety.d.ts +13 -1
- package/dist/eval/rules/safety.js +273 -21
- package/dist/eval/rules/trajectory.d.ts +91 -0
- package/dist/eval/rules/trajectory.js +297 -0
- package/dist/index.js +1 -1
- package/dist/self-test.js +1 -1
- package/dist/server.js +1 -1
- package/dist/tools/evaluate-output.js +38 -23
- package/dist/tools/index.js +1 -1
- package/dist/tools/list-rules.d.ts +2 -1
- package/dist/tools/list-rules.js +22 -4
- package/dist/tools/log-trace.d.ts +8 -1
- package/dist/tools/log-trace.js +19 -4
- package/dist/tools/strict-input.js +2 -2
- package/dist/tools/trace-link.d.ts +10 -0
- package/dist/tools/trace-link.js +13 -1
- package/dist/types/config.d.ts +14 -0
- package/dist/types/eval.d.ts +39 -7
- package/package.json +8 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
[](https://www.pulsemcp.com/servers/iris-eval)
|
|
14
14
|
[](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,
|
|
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** |
|
|
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
|
-
|
|
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
|
|
package/dist/config/defaults.js
CHANGED
|
@@ -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
|
-
|
|
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',
|
package/dist/config/index.js
CHANGED
|
@@ -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
|
}
|