@iris-eval/mcp-server 0.4.5 → 0.5.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 +94 -44
- package/dist/audit-log-reader.d.ts +0 -2
- package/dist/audit-log-reader.js +5 -5
- package/dist/config/defaults.js +3 -3
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +32 -6
- package/dist/custom-rule-store.js +100 -38
- package/dist/dashboard/assets/index-BZZt8bVh.js +10 -0
- package/dist/dashboard/assets/index-UffZ-aEJ.css +1 -0
- package/dist/dashboard/fonts/jetbrains-mono-cyrillic-ext.woff2 +0 -0
- package/dist/dashboard/fonts/jetbrains-mono-cyrillic.woff2 +0 -0
- package/dist/dashboard/fonts/jetbrains-mono-greek.woff2 +0 -0
- package/dist/dashboard/fonts/jetbrains-mono-latin-ext.woff2 +0 -0
- package/dist/dashboard/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/dist/dashboard/fonts/jetbrains-mono-vietnamese.woff2 +0 -0
- package/dist/dashboard/fonts/manrope-cyrillic-ext.woff2 +0 -0
- package/dist/dashboard/fonts/manrope-cyrillic.woff2 +0 -0
- package/dist/dashboard/fonts/manrope-greek.woff2 +0 -0
- package/dist/dashboard/fonts/manrope-latin-ext.woff2 +0 -0
- package/dist/dashboard/fonts/manrope-latin.woff2 +0 -0
- package/dist/dashboard/fonts/manrope-vietnamese.woff2 +0 -0
- package/dist/dashboard/fonts/space-grotesk-latin-ext.woff2 +0 -0
- package/dist/dashboard/fonts/space-grotesk-latin.woff2 +0 -0
- package/dist/dashboard/fonts/space-grotesk-vietnamese.woff2 +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/routes/failures.d.ts +3 -0
- package/dist/dashboard/routes/failures.js +76 -0
- package/dist/dashboard/routes/index.d.ts +1 -0
- package/dist/dashboard/routes/index.js +1 -0
- package/dist/dashboard/routes/preferences.js +7 -2
- package/dist/dashboard/routes/rules.js +32 -14
- package/dist/dashboard/routes/traces.d.ts +12 -1
- package/dist/dashboard/routes/traces.js +90 -2
- package/dist/dashboard/seed-demo-data.d.ts +49 -0
- package/dist/dashboard/seed-demo-data.js +1080 -0
- package/dist/dashboard/server.js +124 -16
- package/dist/dashboard/validation.d.ts +74 -0
- package/dist/dashboard/validation.js +31 -2
- package/dist/eval/citation-verify/resolve.js +29 -0
- package/dist/eval/citation-verify/verifier.d.ts +1 -0
- package/dist/eval/citation-verify/verifier.js +12 -4
- package/dist/eval/engine.d.ts +15 -1
- package/dist/eval/engine.js +99 -15
- package/dist/eval/failure-rank.d.ts +14 -0
- package/dist/eval/failure-rank.js +44 -0
- package/dist/eval/rules/custom.d.ts +29 -1
- package/dist/eval/rules/custom.js +155 -19
- package/dist/eval/rules/regex-budget.d.ts +5 -0
- package/dist/eval/rules/regex-budget.js +100 -0
- package/dist/eval/rules/regex-sandbox.d.ts +26 -0
- package/dist/eval/rules/regex-sandbox.js +131 -0
- package/dist/eval/rules/relevance.d.ts +0 -2
- package/dist/eval/rules/relevance.js +6 -68
- package/dist/eval/rules/safety.d.ts +10 -0
- package/dist/eval/rules/safety.js +1377 -30
- package/dist/index.js +206 -19
- package/dist/middleware/rebinding-guard.d.ts +21 -0
- package/dist/middleware/rebinding-guard.js +77 -0
- package/dist/preferences.js +5 -10
- package/dist/self-test.d.ts +18 -0
- package/dist/self-test.js +329 -0
- package/dist/storage/migrations/005-normalize-created-at.d.ts +3 -0
- package/dist/storage/migrations/005-normalize-created-at.js +34 -0
- package/dist/storage/migrations/index.js +8 -1
- package/dist/storage/sqlite-adapter.d.ts +2 -0
- package/dist/storage/sqlite-adapter.js +93 -13
- package/dist/tools/delete-rule.d.ts +2 -1
- package/dist/tools/delete-rule.js +13 -4
- package/dist/tools/delete-trace.js +2 -1
- package/dist/tools/deploy-rule.d.ts +2 -1
- package/dist/tools/deploy-rule.js +29 -7
- package/dist/tools/evaluate-output.js +36 -9
- package/dist/tools/evaluate-with-llm-judge.js +2 -1
- package/dist/tools/get-traces.js +6 -2
- package/dist/tools/index.js +2 -2
- package/dist/tools/list-rules.js +2 -1
- package/dist/tools/log-trace.d.ts +51 -0
- package/dist/tools/log-trace.js +14 -2
- package/dist/tools/strict-input.d.ts +2 -0
- package/dist/tools/strict-input.js +35 -0
- package/dist/tools/verify-citations.js +7 -5
- package/dist/transport/http.js +24 -2
- package/dist/types/config.d.ts +7 -0
- package/dist/types/decision-moment.d.ts +12 -0
- package/dist/types/eval.d.ts +32 -0
- package/dist/types/query.d.ts +1 -1
- package/dist/utils/iris-home.d.ts +1 -0
- package/dist/utils/iris-home.js +21 -0
- package/dist/utils/write-atomic.d.ts +3 -0
- package/dist/utils/write-atomic.js +96 -0
- package/package.json +3 -2
- package/server.json +3 -3
- package/dist/dashboard/assets/index-B4Aw6ozt.css +0 -1
- package/dist/dashboard/assets/index-CIKsbEhq.js +0 -10
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, renameSync, unlinkSync, chmodSync, existsSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
/*
|
|
5
|
+
* Atomic file write: write a temp file, then rename it over the target.
|
|
6
|
+
*
|
|
7
|
+
* This lives in one place because it was duplicated verbatim in
|
|
8
|
+
* preferences.ts and custom-rule-store.ts, and both copies carried the same
|
|
9
|
+
* two Windows bugs.
|
|
10
|
+
*
|
|
11
|
+
* 1. The temp path was `${targetPath}.tmp.${process.pid}` — keyed on the
|
|
12
|
+
* PROCESS, not the call. Two concurrent writes to the same target inside
|
|
13
|
+
* one process (which is exactly what a vitest file does) therefore raced
|
|
14
|
+
* on a single temp path: one call renamed it away while the other was
|
|
15
|
+
* still writing, and the loser got
|
|
16
|
+
* EPERM: operation not permitted, rename '...preferences.json.tmp.38468'
|
|
17
|
+
* Observed twice in one session, on different suites. A random suffix
|
|
18
|
+
* makes each call's temp file its own.
|
|
19
|
+
*
|
|
20
|
+
* 2. Even with unique names, Windows can briefly deny a rename while a
|
|
21
|
+
* virus scanner or indexer holds the file. POSIX rename() has no such
|
|
22
|
+
* behaviour, so this never reproduces on CI. A few short retries turn a
|
|
23
|
+
* transient lock into a small delay instead of a lost write.
|
|
24
|
+
*
|
|
25
|
+
* The retry is deliberately narrow: only the error codes Windows raises for
|
|
26
|
+
* transient sharing violations. Anything else (ENOSPC, EROFS, a bad path)
|
|
27
|
+
* still throws immediately rather than being retried into a slow failure.
|
|
28
|
+
*/
|
|
29
|
+
const TRANSIENT_RENAME_ERRORS = new Set(['EPERM', 'EACCES', 'EBUSY']);
|
|
30
|
+
const MAX_ATTEMPTS = 5;
|
|
31
|
+
/*
|
|
32
|
+
* Owner-only (0600). These files hold agent inputs and outputs verbatim —
|
|
33
|
+
* and a tool whose job is detecting PII necessarily stores the PII it found.
|
|
34
|
+
* Node's default is 0666 before umask, so a typical umask leaves them 0644:
|
|
35
|
+
* world-readable to every local account on a shared POSIX host. The mode is
|
|
36
|
+
* a no-op on Windows (ACL inheritance governs there), which is exactly why
|
|
37
|
+
* local testing never surfaces it.
|
|
38
|
+
*
|
|
39
|
+
* Set at creation on the temp file, so the bytes are never briefly readable
|
|
40
|
+
* between write and chmod; rename preserves the mode.
|
|
41
|
+
*/
|
|
42
|
+
export const OWNER_ONLY_FILE_MODE = 0o600;
|
|
43
|
+
/*
|
|
44
|
+
* Narrow an EXISTING file to owner-only. Two cases need this, because a mode
|
|
45
|
+
* passed at write time only applies when the write creates the file:
|
|
46
|
+
* - files written before this change (every install that predates it),
|
|
47
|
+
* - files a library creates for us (better-sqlite3 opens iris.db, and WAL
|
|
48
|
+
* mode adds iris.db-wal / iris.db-shm on first write).
|
|
49
|
+
* Best-effort by design: a missing file, a read-only mount, or a
|
|
50
|
+
* non-POSIX filesystem must never take the server down over permissions.
|
|
51
|
+
*/
|
|
52
|
+
export function ensureOwnerOnly(...paths) {
|
|
53
|
+
for (const p of paths) {
|
|
54
|
+
try {
|
|
55
|
+
if (existsSync(p))
|
|
56
|
+
chmodSync(p, OWNER_ONLY_FILE_MODE);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Best effort — see above.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function sleepSync(ms) {
|
|
64
|
+
// Synchronous by necessity — writeAtomic is sync, and making it async
|
|
65
|
+
// would ripple through every caller for a Windows-only edge case.
|
|
66
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
67
|
+
}
|
|
68
|
+
export function writeAtomic(targetPath, contents) {
|
|
69
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
70
|
+
const tmp = `${targetPath}.tmp.${process.pid}.${randomBytes(6).toString('hex')}`;
|
|
71
|
+
writeFileSync(tmp, contents, { encoding: 'utf-8', mode: OWNER_ONLY_FILE_MODE });
|
|
72
|
+
let lastError;
|
|
73
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
74
|
+
try {
|
|
75
|
+
renameSync(tmp, targetPath);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
lastError = err;
|
|
80
|
+
const code = err?.code;
|
|
81
|
+
if (!code || !TRANSIENT_RENAME_ERRORS.has(code))
|
|
82
|
+
break;
|
|
83
|
+
sleepSync(10 * (attempt + 1));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Don't leave the temp file behind on a genuine failure — a stray
|
|
87
|
+
// `preferences.json.tmp.1234.ab12cd` next to the real file is confusing
|
|
88
|
+
// and never cleaned up otherwise.
|
|
89
|
+
try {
|
|
90
|
+
unlinkSync(tmp);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Best effort; the original error is the one worth reporting.
|
|
94
|
+
}
|
|
95
|
+
throw lastError;
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iris-eval/mcp-server",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Stop shipping agents on vibes. Score every agent output for quality, safety, and cost.",
|
|
5
5
|
"mcpName": "io.github.iris-eval/mcp-server",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"test:coverage": "vitest run --coverage",
|
|
23
23
|
"test:integration": "vitest run tests/integration/",
|
|
24
24
|
"test:e2e": "playwright test",
|
|
25
|
+
"test:uat": "node tests/uat/run-uat.mjs",
|
|
25
26
|
"test:e2e:ui": "playwright test --ui",
|
|
26
27
|
"version:check": "bash scripts/check-version.sh",
|
|
27
28
|
"version:sync": "node scripts/sync-versions.mjs",
|
package/server.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.iris-eval/mcp-server",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Stop shipping agents on vibes. Score every agent output for quality, safety, and cost.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/iris-eval/mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.5.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@iris-eval/mcp-server",
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.5.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@import "https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500..700&family=Manrope:wght@400..700&family=JetBrains+Mono:wght@400..700&display=swap";:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--iris-50:#f0fdfa;--iris-100:#ccfbf1;--iris-200:#99f6e4;--iris-300:#5eead4;--iris-400:#2dd4bf;--iris-500:#14b8a6;--iris-600:#0d9488;--iris-700:#0f766e;--iris-800:#115e59;--iris-900:#134e4a;--iris-950:#042f2e;--eval-pass:#22c55e;--eval-warn:#eab308;--eval-fail:#ef4444;--eval-tool:#3b82f6;--eval-llm:#a855f7;--eval-skipped:#71717a}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}:root,[data-theme=dark]{--bg-base:#050508;--bg-raised:#08080e;--bg-surface:#0d0d15;--bg-card:#101018;--bg-card-hover:#16161f;--border-subtle:#ffffff0d;--border-default:#ffffff14;--border-strong:#ffffff24;--border-glow:#14b8a680;--text-primary:#f0f0f5;--text-secondary:#9494a8;--text-muted:#5e5e72;--text-accent:var(--iris-400);--glow-primary:#14b8a61f;--glow-strong:#14b8a640;--shadow-sm:0 1px 2px #0000004d;--shadow-md:0 4px 6px #0006;--shadow-lg:0 10px 15px #00000080}[data-theme=light]{--bg-base:#fafcfc;--bg-raised:#f1f5f5;--bg-surface:#e8eded;--bg-card:#fff;--bg-card-hover:#f4f8f8;--border-subtle:#0000000a;--border-default:#00000014;--border-strong:#00000024;--border-glow:#0d948859;--text-primary:#0a0f0e;--text-secondary:#3d5250;--text-muted:#7a908e;--text-accent:var(--iris-700);--glow-primary:#0d94880f;--glow-strong:#0d94881f;--shadow-sm:0 1px 2px #0000000f;--shadow-md:0 4px 6px #00000014;--shadow-lg:0 10px 15px #0000001a}:root{--font-display:"Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-body:"Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", ui-monospace, monospace;--font-sans:var(--font-body);--text-caption-xs:11px;--text-caption:12px;--text-body-sm:13px;--text-body:14px;--text-body-lg:15px;--text-heading-sm:16px;--text-heading:20px;--text-display-sm:28px;--text-display:40px;--font-size-xs:var(--text-caption);--font-size-sm:var(--text-body-sm);--font-size-base:var(--text-body);--font-size-lg:var(--text-body-lg);--font-size-xl:var(--text-heading-sm);--font-size-2xl:var(--text-heading);--font-size-3xl:var(--text-display-sm);--leading-body:1.5;--leading-heading:1.2;--leading-display:1.1;--leading-mono:1.4;--space-0_5:2px;--space-1:4px;--space-1_5:6px;--space-2:8px;--space-2_5:10px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;--space-10:40px;--space-12:48px;--space-16:64px;--space-20:80px;--space-24:96px}:root,[data-density=compact]{--density-row:32px;--density-padding:var(--space-3);--density-body:var(--text-body-sm)}[data-density=comfortable]{--density-row:44px;--density-padding:var(--space-4);--density-body:var(--text-body)}:root{--sidebar-width-expanded:256px;--sidebar-width-collapsed:64px;--header-height:56px;--page-toolbar-height:40px;--radius-xs:4px;--radius-sm:6px;--radius:8px;--radius-lg:12px;--radius-xl:16px;--radius-pill:999px;--border-radius:var(--radius);--border-radius-sm:var(--radius-xs);--border-radius-lg:var(--radius-lg);--transition-instant:.1s ease;--transition-fast:.15s ease;--transition-base:.2s ease;--transition-slow:.3s ease;--ease-iris:cubic-bezier(.25, .4, .25, 1);--bg-primary:var(--bg-base);--bg-secondary:var(--bg-raised);--bg-tertiary:var(--bg-surface);--bg-hover:var(--bg-card-hover);--border-color:var(--border-default);--accent-primary:var(--iris-500);--accent-primary-hover:var(--iris-400);--accent-success:var(--eval-pass);--accent-error:var(--eval-fail);--accent-warning:var(--eval-warn);--accent-tool:var(--eval-tool);--accent-llm:var(--eval-llm)}html{transition:background-color var(--transition-base), color var(--transition-base)}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{width:100%;height:100%}body{font-family:var(--font-body);font-size:var(--text-body);color:var(--text-primary);background-color:var(--bg-base);line-height:var(--leading-body);-webkit-font-smoothing:antialiased;font-feature-settings:"cv11", "ss01"}h1,h2,h3,h4,.display{font-family:var(--font-display);letter-spacing:-.01em;font-weight:600}a{color:var(--text-accent);text-decoration:none}a:hover{color:var(--iris-300)}button{cursor:pointer;font-family:inherit}input,select{font-family:inherit;font-size:inherit}code,pre{font-family:var(--font-mono)}a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,[role=button]:focus-visible{outline:2px solid var(--iris-500);outline-offset:2px;border-radius:var(--radius-xs)}.iris-sr-reveal{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.iris-sr-reveal:focus-within{width:auto;height:auto;margin:var(--space-3) 0 0 0;padding:var(--space-3) var(--space-4);clip:auto;white-space:normal;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--iris-500);border-radius:var(--radius-sm);position:static;overflow:visible}.iris-sr-reveal:focus-within>li{padding:var(--space-1) 0;list-style:none}::selection{background:var(--iris-600);color:#fff}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--bg-base)}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:var(--radius-xs)}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}html{scrollbar-color:var(--border-strong) transparent;scrollbar-width:thin}@keyframes pulse-ring{0%{opacity:.5;transform:scale(1)}to{opacity:0;transform:scale(2.5)}}.pulse-dot{position:relative}.pulse-dot:after{content:"";background:var(--iris-500);border-radius:50%;animation:2s ease-out infinite pulse-ring;position:absolute;inset:-2px}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}@media (width<=767px){aside[aria-label=Main\ navigation]{width:160px}main{overflow-x:auto}}@media print{body{color:#000!important;background:#fff!important}aside[aria-label=Main\ navigation],header,[role=region][aria-label=Welcome],[role=region][aria-label=Bulk\ actions],[role=status],[role=dialog]{display:none!important}body,#root,main{height:auto!important;overflow:visible!important}main{padding:0!important}tr,pre,code{page-break-inside:avoid}h1,h2,h3{page-break-after:avoid}[aria-label*=violation],[aria-label*=spike],[aria-label*=collision],[aria-label*=Pass],[aria-label*=Fail]{border:1px solid #000!important}a{color:#000!important;text-decoration:underline!important}a[href^=http]:after{content:" (" attr(href) ")";color:#555;font-size:80%}}
|