@iris-eval/mcp-server 0.4.0 → 0.4.1

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.
@@ -11,6 +11,7 @@
11
11
  * needs to manage the rule set programmatically.
12
12
  */
13
13
  import { z } from 'zod';
14
+ import { LOCAL_TENANT } from '../types/tenant.js';
14
15
  const inputSchema = {
15
16
  eval_type: z
16
17
  .enum(['completeness', 'relevance', 'safety', 'cost', 'custom'])
@@ -27,6 +28,8 @@ export function registerListRulesTool(server, customRuleStore) {
27
28
  description: [
28
29
  'Enumerate deployed custom evaluation rules from the local rule store.',
29
30
  '',
31
+ 'Sibling tools — deploy_rule adds custom rules, delete_rule removes them, evaluate_output runs them against agent output. log_trace / get_traces / delete_trace handle the trace lifecycle separately. list_rules is the READ path for the custom-rule store; nothing else exposes the inventory.',
32
+ '',
30
33
  'Behavior. Pure read of ~/.iris/custom-rules.json (in-memory cached; no disk read per call after server boot). No mutation, no external network. Tenant-scoped in Cloud tier; OSS returns all rules for the single local tenant. Rate-limited to 20 req/min on HTTP MCP, unlimited on stdio. Returns in <5ms.',
31
34
  '',
32
35
  'Output shape. Returns JSON: `{ "rules": [{ "id": "rule-XXXX", "name", "description?", "evalType", "severity", "definition": { type, config, weight? }, "enabled": boolean, "deployedAt": ISO timestamp, "sourceMomentId?": string }], "total": number, "enabled_count": number }`. Empty array + total=0 when no rules deployed.',
@@ -35,6 +38,8 @@ export function registerListRulesTool(server, customRuleStore) {
35
38
  '',
36
39
  "Don't use to count traces or evals (that's get_traces). Don't use to inspect built-in (non-custom) rules — those ship with the iris binary and are listed in docs/api-reference.md, not in the rule store. Don't use to deploy a rule (use deploy_rule); don't use to remove one (use delete_rule).",
37
40
  '',
41
+ 'Parameters. eval_type filter is exact-match against each rule\'s evalType field (no wildcards). enabled_only excludes rules that are deployed-but-disabled (toggled via the dashboard\'s rule-list affordance — there\'s no MCP toggle tool in v0.4). Both filters are AND-combined when both are set. Both are optional; with no filter, all rules return. Defaults: eval_type=undefined (no filter), enabled_only=false (returns all rules including disabled).',
42
+ '',
38
43
  "Error modes. Returns empty list if the rule store file doesn't exist (first run). Returns 429 if HTTP rate limit exceeded. Never throws on valid input.",
39
44
  ].join('\n'),
40
45
  inputSchema,
@@ -45,7 +50,10 @@ export function registerListRulesTool(server, customRuleStore) {
45
50
  openWorldHint: false,
46
51
  },
47
52
  }, async (args) => {
48
- let rules = customRuleStore.list();
53
+ // OSS: MCP tools operate under LOCAL_TENANT. Cloud multi-tenant
54
+ // exposure is a v0.5 architectural item (MCP SDK doesn't pass
55
+ // session/tenant context to tool handlers).
56
+ let rules = customRuleStore.list(LOCAL_TENANT);
49
57
  if (args.eval_type) {
50
58
  rules = rules.filter((r) => r.evalType === args.eval_type);
51
59
  }
@@ -31,17 +31,17 @@ const TokenUsageSchema = z.object({
31
31
  total_tokens: z.number().optional(),
32
32
  });
33
33
  const inputSchema = {
34
- agent_name: z.string().describe('Name of the agent'),
35
- framework: z.string().optional().describe('Agent framework name'),
36
- input: z.string().optional().describe('Agent input text'),
37
- output: z.string().optional().describe('Agent output text'),
38
- tool_calls: z.array(ToolCallSchema).optional().describe('Tool calls made during execution'),
39
- latency_ms: z.number().optional().describe('Total execution time in milliseconds'),
40
- token_usage: TokenUsageSchema.optional().describe('Token usage breakdown'),
41
- cost_usd: z.number().optional().describe('Total cost in USD'),
42
- metadata: z.record(z.unknown()).optional().describe('Arbitrary metadata'),
43
- spans: z.array(SpanSchema).optional().describe('Detailed execution spans'),
44
- timestamp: z.string().optional().describe('Trace timestamp (ISO 8601)'),
34
+ agent_name: z.string().describe('Agent name used for filtering in get_traces (e.g., "customer-support-bot")'),
35
+ framework: z.string().optional().describe('Agent framework identifier (e.g., langchain, autogen, custom)'),
36
+ input: z.string().optional().describe('Agent input text — the user prompt or upstream input that produced this output'),
37
+ output: z.string().optional().describe('Agent output text — what the agent produced (pass to evaluate_output for scoring)'),
38
+ tool_calls: z.array(ToolCallSchema).optional().describe('Tool calls made during execution (per-call latency, errors, input/output)'),
39
+ latency_ms: z.number().optional().describe('Total execution time in milliseconds (end-to-end agent latency)'),
40
+ token_usage: TokenUsageSchema.optional().describe('Token usage breakdown (prompt/completion/total — used for cost analysis)'),
41
+ cost_usd: z.number().optional().describe('Total cost in USD — overrides per-span aggregation when provided (treated as authoritative)'),
42
+ metadata: z.record(z.unknown()).optional().describe('Opaque key-value tags (e.g. {requestId, userId, env}) — queryable in dashboard, not via get_traces filters'),
43
+ spans: z.array(SpanSchema).optional().describe('Detailed execution spans (hierarchical span tree with timings, attributes, events)'),
44
+ timestamp: z.string().optional().describe('Trace timestamp (ISO 8601); defaults to now() when omitted'),
45
45
  };
46
46
  export function registerLogTraceTool(server, storage) {
47
47
  server.registerTool('log_trace', {
@@ -49,6 +49,8 @@ export function registerLogTraceTool(server, storage) {
49
49
  description: [
50
50
  'Persist a single agent execution trace (input, output, spans, tool calls, cost, latency, token usage).',
51
51
  '',
52
+ 'Sibling tools — evaluate_output runs heuristic scoring on the trace; evaluate_with_llm_judge runs semantic LLM-based scoring; verify_citations checks citation grounding; get_traces queries stored traces; delete_trace removes a single trace; list_rules / deploy_rule / delete_rule manage custom evaluation rules. log_trace is the WRITE path that records executions; everything else reads, scores, or manages around it.',
53
+ '',
52
54
  'Behavior. Writes one row to Iris storage (SQLite by default; Postgres in Cloud tier). When IRIS_OTEL_ENDPOINT is set, ALSO fires a best-effort async export to the configured OTLP/HTTP collector (Jaeger, Tempo, Datadog OTLP, OTEL Collector). The OTel export is fire-and-forget — its success does not affect the tool response; failures are logged but the trace is still stored locally. No authentication in stdio mode; HTTP mode requires Bearer token. Rate-limited to 20 req/min on HTTP MCP, unlimited on stdio. Not idempotent: each call mints a fresh trace_id, so resubmitting the same payload creates a duplicate trace.',
53
55
  '',
54
56
  'Output shape. Returns a JSON string: `{ "trace_id": "<32-hex>", "status": "stored" }`. The trace_id is the key you pass to evaluate_output or get_traces afterwards.',
@@ -57,6 +59,8 @@ export function registerLogTraceTool(server, storage) {
57
59
  '',
58
60
  'Don\'t use when you only need a transient log (use console logging). Don\'t use to update an existing trace — there is no update path in v0.4 (traces are immutable once stored).',
59
61
  '',
62
+ 'Parameters. agent_name is required; everything else is optional. token_usage and cost_usd are summary fields — if you ALSO pass spans with per-tool-call costs, the summary fields are treated as authoritative (no auto-aggregation). spans without an explicit start_time fall back to the trace timestamp; spans with an end_time get a duration_ms derived. metadata is opaque key-value (queryable in the dashboard, not via get_traces filters). tool_calls record per-tool latency + errors; missing latency_ms means "not reported," not "zero." Defaults: span.kind="INTERNAL", span.status_code="UNSET", timestamp=now() if omitted.',
63
+ '',
60
64
  'Error modes. Throws on missing agent_name. Throws on malformed span or tool_call objects (Zod rejects). Returns 500 on storage failure (disk full, DB locked). Never blocks on the agent — returns within ~50ms for typical payloads.',
61
65
  ].join('\n'),
62
66
  inputSchema,
@@ -53,6 +53,8 @@ export function registerVerifyCitationsTool(server, storage) {
53
53
  description: [
54
54
  'Extract citations from agent output, fetch the cited sources, and use an LLM judge to check whether each source supports the claim in context. Returns per-citation verdicts + an overall support ratio.',
55
55
  '',
56
+ 'Sibling tools — evaluate_with_llm_judge runs general semantic scoring (accuracy, helpfulness, correctness, faithfulness); this tool is specifically for citation grounding (does the cited source actually support the claim). evaluate_output\'s no_hallucination_markers heuristic detects FABRICATED-looking citations cheaply (free, no fetch); this tool resolves and verifies them (paid, opt-in fetch, SSRF-guarded). log_trace / get_traces handle trace I/O. verify_citations is the GROUNDING-CHECK path — narrowest in scope, deepest in rigor.',
57
+ '',
56
58
  '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.',
57
59
  '',
58
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 / resolved`; `null` when nothing resolvable was found.',
@@ -61,6 +63,8 @@ export function registerVerifyCitationsTool(server, storage) {
61
63
  "",
62
64
  "Don't use when the agent output has no citations at all (overall_score will be null; the tool degrades gracefully but a heuristic rule is cheaper). Don't use without allow_fetch=true or IRIS_CITATION_ALLOW_FETCH=1 — the tool refuses outbound HTTP unless explicitly enabled. Don't use with an open allowlist + untrusted output on the public internet; you are effectively running a user-directed fetcher. For stricter safety set IRIS_CITATION_DOMAINS to a curated list.",
63
65
  '',
66
+ 'Parameters. model is required; provider auto-detected from model name (override only for ambiguous IDs). allow_fetch=false by default — outbound HTTP is REFUSED unless explicitly true OR IRIS_CITATION_ALLOW_FETCH=1 env. domain_allowlist suffix-matches hostnames (e.g., "wikipedia.org" allows en.wikipedia.org); merged with IRIS_CITATION_DOMAINS env (UNION — either source permits). max_citations defaults 20, hard cap 50 (extras are skipped silently, NOT errored — check total_citations_found in the response if precise). max_cost_usd_total defaults $1.00 — the pipeline stops mid-citation when the next judge call would exceed the cap (returns partial verdicts). per_source_timeout_ms defaults 10000 (10s); per_source_max_bytes defaults 5MB (truncates at boundary, judges still run on truncated content). trace_id optional but recommended. Defaults: max_citations=20, max_cost_usd_total=$1.00, per_source_timeout_ms=10000, per_source_max_bytes=5242880, allow_fetch=false.',
67
+ '',
64
68
  '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).',
65
69
  ].join('\n'),
66
70
  inputSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iris-eval/mcp-server",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "The agent eval standard for MCP. Score every agent output for quality, safety, and cost.",
5
5
  "mcpName": "io.github.iris-eval/mcp-server",
6
6
  "type": "module",
@@ -25,6 +25,10 @@
25
25
  "test:e2e:ui": "playwright test --ui",
26
26
  "version:check": "bash scripts/check-version.sh",
27
27
  "version:sync": "node scripts/sync-versions.mjs",
28
+ "claims:capture-tests": "node scripts/claims/capture-tests.mjs",
29
+ "claims:generate": "node scripts/claims/generate.mjs",
30
+ "claims:check": "node scripts/claims/generate.mjs --check",
31
+ "claims:check-hardcoded": "node scripts/claims/check-no-hardcoded.mjs",
28
32
  "clean": "rm -rf dist coverage",
29
33
  "seed:demo": "tsx scripts/seed-demo-data.ts",
30
34
  "demo": "tsx scripts/demo.ts",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/iris-eval/mcp-server",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.4.0",
9
+ "version": "0.4.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@iris-eval/mcp-server",
14
- "version": "0.4.0",
14
+ "version": "0.4.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },