@iris-eval/mcp-server 0.4.6 → 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 +3 -3
- package/dist/config/index.js +18 -1
- package/dist/custom-rule-store.js +22 -8
- 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 +81 -15
- 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 +74 -5
- 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.js +0 -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 +1337 -26
- package/dist/index.js +196 -18
- package/dist/self-test.d.ts +18 -0
- package/dist/self-test.js +329 -0
- package/dist/storage/sqlite-adapter.d.ts +2 -0
- package/dist/storage/sqlite-adapter.js +65 -9
- 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/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/write-atomic.d.ts +2 -0
- package/dist/utils/write-atomic.js +34 -2
- package/package.json +3 -2
- package/server.json +3 -3
- package/dist/dashboard/assets/index-B4Aw6ozt.css +0 -1
- package/dist/dashboard/assets/index-ChcHJDDJ.js +0 -10
|
@@ -3,6 +3,7 @@ import { LOCAL_TENANT } from '../types/tenant.js';
|
|
|
3
3
|
import { verifyCitations } from '../eval/citation-verify/verifier.js';
|
|
4
4
|
import { findPricing } from '../eval/llm-judge/pricing.js';
|
|
5
5
|
import { generateEvalId } from '../utils/ids.js';
|
|
6
|
+
import { strictInput } from './strict-input.js';
|
|
6
7
|
const inputSchema = {
|
|
7
8
|
output: z.string().min(1).describe('The agent output containing citations to verify'),
|
|
8
9
|
model: z
|
|
@@ -57,7 +58,7 @@ export function registerVerifyCitationsTool(server, storage) {
|
|
|
57
58
|
'',
|
|
58
59
|
'Behavior. Three-phase pipeline: (1) regex extraction of [N] numbered refs, (Author, Year) parentheticals, bare URLs, and DOIs (in-process, no network); (2) SSRF-guarded fetch of URL + DOI citations, with scheme allowlist, private/link-local/cloud-metadata IP blocking, optional domain allowlist (IRIS_CITATION_DOMAINS), 10s timeout, 5MB body cap, manual redirect chase (max 3, re-checked), in-process LRU cache; (3) per-citation LLM judge call asking "does this source support this claim?" with a 256-token verdict. Opt-in via allow_fetch=true or IRIS_CITATION_ALLOW_FETCH=1 — Iris refuses outbound HTTP by default. Cost-capped across the entire call by max_cost_usd_total (default $1.00) — the pipeline stops when the cap would be exceeded. Rate-limited to 20 req/min on HTTP MCP. Writes one eval_result row tagged with per-citation provenance.',
|
|
59
60
|
'',
|
|
60
|
-
'Output shape. Returns JSON: `{ "id": "<uuid>", "overall_score": 0..1|null, "passed": boolean, "total_citations_found": number, "total_resolved": number, "total_supported": number, "total_cost_usd": number, "citations": [{ "citation": { "raw", "kind", "identifier", "offset_start", "offset_end" }, "resolve_status": "ok"|"skipped"|"error", "resolve_error"?, "source"?: { "url", "status", "content_type", "bytes_fetched", "truncated" }, "judge"?: { "supported", "confidence", "rationale", "cost_usd", "latency_ms", "input_tokens", "output_tokens" } }] }`. `overall_score = supported /
|
|
61
|
+
'Output shape. Returns JSON: `{ "id": "<uuid>", "overall_score": 0..1|null, "passed": boolean, "total_citations_found": number, "total_resolved": number, "total_judged": number, "total_supported": number, "total_cost_usd": number, "citations": [{ "citation": { "raw", "kind", "identifier", "offset_start", "offset_end" }, "resolve_status": "ok"|"skipped"|"error", "resolve_error"?, "source"?: { "url", "status", "content_type", "bytes_fetched", "truncated" }, "judge"?: { "supported", "confidence", "rationale", "cost_usd", "latency_ms", "input_tokens", "output_tokens" } }] }`. `overall_score = supported / judged`; `null` when nothing was judged (no resolvable citations, or every judge call failed). Infrastructure failures (cost cap, judge timeout/error, malformed verdict) leave a citation resolved-but-unjudged — reported per-citation via resolve_error, never scored as unsupported.',
|
|
61
62
|
'',
|
|
62
63
|
'Use when the output makes factual claims backed by [1]-style references, DOIs, or URLs and you want to separate "cited correctly" from "cited and wrong" from "cited but unresolvable". Particularly useful for research/legal/medical agents where fabricated citations are the dominant failure mode.',
|
|
63
64
|
"",
|
|
@@ -67,7 +68,7 @@ export function registerVerifyCitationsTool(server, storage) {
|
|
|
67
68
|
'',
|
|
68
69
|
'Error modes. Throws when the API key env var is missing. Throws "Unknown model" on unsupported model IDs. Per-citation errors are collected (resolve_error.kind = bad_scheme / ssrf / not_allowed_domain / timeout / too_large / bad_status / redirect_loop / not_text / fetch_disabled / malformed_judge_response / cost_cap_reached / unresolvable_kind) and returned in the response rather than thrown. An empty output or output with zero extractable citations returns overall_score=null + passed=true (nothing to fail).',
|
|
69
70
|
].join('\n'),
|
|
70
|
-
inputSchema,
|
|
71
|
+
inputSchema: strictInput(inputSchema),
|
|
71
72
|
annotations: {
|
|
72
73
|
readOnlyHint: false, // Writes eval_result + spends money
|
|
73
74
|
destructiveHint: false, // Creates data; doesn't overwrite/delete
|
|
@@ -109,11 +110,11 @@ export function registerVerifyCitationsTool(server, storage) {
|
|
|
109
110
|
passed: result.passed,
|
|
110
111
|
score,
|
|
111
112
|
message: result.overallScore === null
|
|
112
|
-
? `No
|
|
113
|
-
: `${result.totalSupported}/${result.
|
|
113
|
+
? `No citations judged (found ${result.totalCitationsFound}, resolved ${result.totalResolved}, judged 0)`
|
|
114
|
+
: `${result.totalSupported}/${result.totalJudged} judged sources supported the output`,
|
|
114
115
|
},
|
|
115
116
|
],
|
|
116
|
-
suggestions: result.passed ? [] : [`Only ${result.totalSupported}/${result.
|
|
117
|
+
suggestions: result.passed ? [] : [`Only ${result.totalSupported}/${result.totalJudged} judged sources actually supported the claim.`],
|
|
117
118
|
rules_evaluated: 1,
|
|
118
119
|
rules_skipped: 0,
|
|
119
120
|
insufficient_data: result.overallScore === null,
|
|
@@ -128,6 +129,7 @@ export function registerVerifyCitationsTool(server, storage) {
|
|
|
128
129
|
passed: result.passed,
|
|
129
130
|
total_citations_found: result.totalCitationsFound,
|
|
130
131
|
total_resolved: result.totalResolved,
|
|
132
|
+
total_judged: result.totalJudged,
|
|
131
133
|
total_supported: result.totalSupported,
|
|
132
134
|
total_cost_usd: result.totalCostUsd,
|
|
133
135
|
citations: result.citations.map((c) => ({
|
package/dist/transport/http.js
CHANGED
|
@@ -56,9 +56,31 @@ export async function createHttpTransport(mcpServer, config, logger) {
|
|
|
56
56
|
* `127.0.0.1:0` and reject every real request with a 403 that looks
|
|
57
57
|
* exactly like an attack. Routes are registered immediately after, and
|
|
58
58
|
* the port is not discoverable by any client until this function returns.
|
|
59
|
+
*
|
|
60
|
+
* The callback MUST inspect its error argument. Express 5 wires the
|
|
61
|
+
* listen callback as `server.once('error', done)` as well as the
|
|
62
|
+
* listening callback — so on EADDRINUSE it is invoked WITH the error.
|
|
63
|
+
* Ignoring that argument resolved this promise on a server that never
|
|
64
|
+
* bound: the caller then logged "HTTP transport listening on <port>"
|
|
65
|
+
* while another process owned the port, and the process idled forever.
|
|
66
|
+
* A CI health poll got 200 from the OTHER instance and shipped
|
|
67
|
+
* evaluations to a stranger's database. A bind failure must reject,
|
|
68
|
+
* name the port, and take the process down nonzero.
|
|
59
69
|
*/
|
|
60
|
-
const httpServer = await new Promise((resolve) => {
|
|
61
|
-
const server = app.listen(config.transport.port, config.transport.host, () =>
|
|
70
|
+
const httpServer = await new Promise((resolve, reject) => {
|
|
71
|
+
const server = app.listen(config.transport.port, config.transport.host, (err) => {
|
|
72
|
+
if (err) {
|
|
73
|
+
const bind = `${config.transport.host}:${config.transport.port}`;
|
|
74
|
+
const code = err.code;
|
|
75
|
+
reject(code === 'EADDRINUSE'
|
|
76
|
+
? new Error(`HTTP transport failed to start: port ${config.transport.port} is already in use ` +
|
|
77
|
+
`(EADDRINUSE on ${bind}). Another process — possibly another iris instance — owns it. ` +
|
|
78
|
+
`Pass --port <other> (or set IRIS_PORT) or stop the other process.`)
|
|
79
|
+
: new Error(`HTTP transport failed to bind ${bind}: ${err.message}`));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
resolve(server);
|
|
83
|
+
});
|
|
62
84
|
});
|
|
63
85
|
const address = httpServer.address();
|
|
64
86
|
const port = typeof address === 'object' && address ? address.port : config.transport.port;
|
|
@@ -120,3 +120,15 @@ export interface MomentQueryResult {
|
|
|
120
120
|
limit: number;
|
|
121
121
|
offset: number;
|
|
122
122
|
}
|
|
123
|
+
export interface RankedFailure extends DecisionMoment {
|
|
124
|
+
/** Severity × recency-decay blend, 0-1. Higher = shown first. */
|
|
125
|
+
rankScore: number;
|
|
126
|
+
}
|
|
127
|
+
export interface FailureQueryResult {
|
|
128
|
+
failures: RankedFailure[];
|
|
129
|
+
/** How many recent traces were scanned to build the list. */
|
|
130
|
+
scanned: number;
|
|
131
|
+
/** Total traces matching the filter (pre-scan-cap). */
|
|
132
|
+
total: number;
|
|
133
|
+
limit: number;
|
|
134
|
+
}
|
package/dist/types/eval.d.ts
CHANGED
|
@@ -4,6 +4,18 @@ export interface EvalRule {
|
|
|
4
4
|
description: string;
|
|
5
5
|
evalType: EvalType;
|
|
6
6
|
weight: number;
|
|
7
|
+
/**
|
|
8
|
+
* Hard-fail marker. When a critical rule FAILS (and was not skipped), the
|
|
9
|
+
* overall eval reports passed=false regardless of the weighted score.
|
|
10
|
+
*
|
|
11
|
+
* Exists because the weighted average routinely outvotes a genuine
|
|
12
|
+
* violation: an output leaking a real SSN failed no_pii while the other
|
|
13
|
+
* safety rules passed, scoring ~0.765 — above the 0.7 threshold — so the
|
|
14
|
+
* one field every CI gate reads said passed:true about the product's
|
|
15
|
+
* flagship failure scenario. The score stays a quality gradient; `passed`
|
|
16
|
+
* is the verdict, and a critical violation must never be averaged away.
|
|
17
|
+
*/
|
|
18
|
+
critical?: boolean;
|
|
7
19
|
evaluate(context: EvalContext): EvalRuleResult;
|
|
8
20
|
}
|
|
9
21
|
export interface EvalContext {
|
|
@@ -23,6 +35,17 @@ export interface EvalContext {
|
|
|
23
35
|
costUsd?: number;
|
|
24
36
|
metadata?: Record<string, unknown>;
|
|
25
37
|
customConfig?: Record<string, unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* Per-evaluation regex circuit breaker, initialized by the engine (never
|
|
40
|
+
* by callers). Each sandbox budget breach increments `breaches`; once it
|
|
41
|
+
* reaches the cap, remaining regex rules in the SAME evaluation skip
|
|
42
|
+
* without running. Bounds how long a single hostile output can stall a
|
|
43
|
+
* request: without it, N regex rules × (budget + worker respawn) of
|
|
44
|
+
* main-thread stall scale linearly with N.
|
|
45
|
+
*/
|
|
46
|
+
regexBudget?: {
|
|
47
|
+
breaches: number;
|
|
48
|
+
};
|
|
26
49
|
}
|
|
27
50
|
export interface EvalRuleResult {
|
|
28
51
|
ruleName: string;
|
|
@@ -31,6 +54,8 @@ export interface EvalRuleResult {
|
|
|
31
54
|
message: string;
|
|
32
55
|
skipped?: boolean;
|
|
33
56
|
skipReason?: string;
|
|
57
|
+
configInvalid?: boolean;
|
|
58
|
+
budgetExceeded?: boolean;
|
|
34
59
|
}
|
|
35
60
|
export interface EvalResult {
|
|
36
61
|
id: string;
|
|
@@ -46,6 +71,13 @@ export interface EvalResult {
|
|
|
46
71
|
rules_evaluated?: number;
|
|
47
72
|
rules_skipped?: number;
|
|
48
73
|
insufficient_data?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Names of critical rules that failed (present only when non-empty).
|
|
76
|
+
* Any entry here forces passed=false regardless of the weighted score —
|
|
77
|
+
* this field is how a caller tells "failed the quality bar" apart from
|
|
78
|
+
* "committed a hard violation".
|
|
79
|
+
*/
|
|
80
|
+
critical_failures?: string[];
|
|
49
81
|
}
|
|
50
82
|
export type CustomRuleType = 'regex_match' | 'regex_no_match' | 'min_length' | 'max_length' | 'contains_keywords' | 'excludes_keywords' | 'json_schema' | 'cost_threshold';
|
|
51
83
|
export interface CustomRuleDefinition {
|
package/dist/types/query.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface TraceQueryResult {
|
|
|
23
23
|
limit: number;
|
|
24
24
|
offset: number;
|
|
25
25
|
}
|
|
26
|
-
export type EvalStatsPeriod = '24h' | '7d' | '30d' | 'all';
|
|
26
|
+
export type EvalStatsPeriod = '24h' | '2d' | '7d' | '14d' | '30d' | '60d' | '90d' | '180d' | 'all';
|
|
27
27
|
export interface EvalStats {
|
|
28
28
|
passRate: number;
|
|
29
29
|
avgScore: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync, renameSync, unlinkSync } from 'node:fs';
|
|
1
|
+
import { mkdirSync, writeFileSync, renameSync, unlinkSync, chmodSync, existsSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { randomBytes } from 'node:crypto';
|
|
4
4
|
/*
|
|
@@ -28,6 +28,38 @@ import { randomBytes } from 'node:crypto';
|
|
|
28
28
|
*/
|
|
29
29
|
const TRANSIENT_RENAME_ERRORS = new Set(['EPERM', 'EACCES', 'EBUSY']);
|
|
30
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
|
+
}
|
|
31
63
|
function sleepSync(ms) {
|
|
32
64
|
// Synchronous by necessity — writeAtomic is sync, and making it async
|
|
33
65
|
// would ripple through every caller for a Windows-only edge case.
|
|
@@ -36,7 +68,7 @@ function sleepSync(ms) {
|
|
|
36
68
|
export function writeAtomic(targetPath, contents) {
|
|
37
69
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
38
70
|
const tmp = `${targetPath}.tmp.${process.pid}.${randomBytes(6).toString('hex')}`;
|
|
39
|
-
writeFileSync(tmp, contents, 'utf-8');
|
|
71
|
+
writeFileSync(tmp, contents, { encoding: 'utf-8', mode: OWNER_ONLY_FILE_MODE });
|
|
40
72
|
let lastError;
|
|
41
73
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
42
74
|
try {
|
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%}}
|