@gleanwork/mcp-server-tester 1.1.0 → 2.0.0-beta.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.
@@ -94,6 +94,80 @@ interface FieldRemovalSanitizer {
94
94
  */
95
95
  type SnapshotSanitizer = BuiltInSanitizer | RegexSanitizer | FieldRemovalSanitizer;
96
96
 
97
+ /**
98
+ * Built-in judge rubrics matching Glean EvalV2's named judge types.
99
+ * Use these for consistent, standardized evaluations across teams.
100
+ *
101
+ * All built-in rubrics use a 5-point scale: 0.0 / 0.25 / 0.5 / 0.75 / 1.0
102
+ */
103
+ type BuiltInRubric = 'correctness' | 'completeness' | 'groundedness' | 'instruction-following' | 'conciseness';
104
+ /** A rubric specification: either a built-in named rubric or custom text. */
105
+ type RubricSpec = BuiltInRubric | {
106
+ text: string;
107
+ };
108
+
109
+ /** Valid LLM judge provider kinds. */
110
+ type ProviderKind = 'anthropic' | 'vertex-anthropic' | 'anthropic-agent-sdk' | 'openai' | 'google';
111
+
112
+ type StoredArtifactKind = 'eval-runner-result' | 'eval-run-summary' | 'reporter-run' | 'eval-run-comparison' | 'server-comparison';
113
+ interface StoredEvalArtifactMetadata {
114
+ datasetName?: string;
115
+ gitHash?: string;
116
+ branch?: string;
117
+ runNumber?: string;
118
+ trigger?: string;
119
+ packageVersion?: string;
120
+ toolOverrideVariantId?: string;
121
+ mcpHostModel?: string;
122
+ judgeModel?: string;
123
+ labels?: Record<string, string>;
124
+ [key: string]: unknown;
125
+ }
126
+ interface StoredEvalArtifact<T> {
127
+ schemaVersion: 1;
128
+ kind: StoredArtifactKind;
129
+ id: string;
130
+ createdAt: string;
131
+ metadata: StoredEvalArtifactMetadata;
132
+ data: T;
133
+ }
134
+ interface StoredArtifactSummary {
135
+ kind: StoredArtifactKind;
136
+ id: string;
137
+ createdAt: string;
138
+ metadata: StoredEvalArtifactMetadata;
139
+ }
140
+ interface ListStoredArtifactsOptions {
141
+ limit?: number;
142
+ }
143
+ interface EvalResultStore {
144
+ saveArtifact<T>(artifact: StoredEvalArtifact<T>): Promise<void>;
145
+ loadArtifact<T>(kind: StoredArtifactKind, id: string): Promise<StoredEvalArtifact<T>>;
146
+ loadLatestArtifact<T>(kind: StoredArtifactKind): Promise<StoredEvalArtifact<T> | null>;
147
+ listArtifacts(kind: StoredArtifactKind, options?: ListStoredArtifactsOptions): Promise<StoredArtifactSummary[]>;
148
+ }
149
+ interface FileEvalResultStoreConfig {
150
+ provider: 'file';
151
+ dir: string;
152
+ }
153
+ interface GCSEvalResultStoreConfig {
154
+ provider: 'gcs';
155
+ bucket: string;
156
+ prefix?: string;
157
+ }
158
+ type EvalResultStoreConfig = FileEvalResultStoreConfig | GCSEvalResultStoreConfig;
159
+ type EvalResultStoreLike = EvalResultStore | EvalResultStoreConfig;
160
+
161
+ interface HostEvent {
162
+ kind: 'tool_call' | 'skill' | 'command' | 'subagent';
163
+ source: 'mcp' | 'host';
164
+ name: string;
165
+ server?: string;
166
+ arguments?: Record<string, unknown>;
167
+ output?: string;
168
+ id?: string;
169
+ }
170
+
97
171
  /**
98
172
  * Tool call validators for mcp_host simulation results.
99
173
  *
@@ -104,6 +178,9 @@ type SnapshotSanitizer = BuiltInSanitizer | RegexSanitizer | FieldRemovalSanitiz
104
178
  interface ToolCallExpectation {
105
179
  calls: Array<{
106
180
  name: string;
181
+ kind?: HostEvent['kind'];
182
+ source?: HostEvent['source'];
183
+ server?: string;
107
184
  arguments?: Record<string, unknown>;
108
185
  required?: boolean;
109
186
  }>;
@@ -116,21 +193,6 @@ interface ToolCallCountOptions {
116
193
  exact?: number;
117
194
  }
118
195
 
119
- /**
120
- * Built-in judge rubrics matching Glean EvalV2's named judge types.
121
- * Use these for consistent, standardized evaluations across teams.
122
- *
123
- * All built-in rubrics use a 5-point scale: 0.0 / 0.25 / 0.5 / 0.75 / 1.0
124
- */
125
- type BuiltInRubric = 'correctness' | 'completeness' | 'groundedness' | 'instruction-following' | 'conciseness';
126
- /** A rubric specification: either a built-in named rubric or custom text. */
127
- type RubricSpec = BuiltInRubric | {
128
- text: string;
129
- };
130
-
131
- /** Valid LLM judge provider kinds. */
132
- type ProviderKind = 'anthropic' | 'vertex-anthropic' | 'anthropic-agent-sdk' | 'openai' | 'google';
133
-
134
196
  /**
135
197
  * Matcher Types
136
198
  *
@@ -141,6 +203,8 @@ type ProviderKind = 'anthropic' | 'vertex-anthropic' | 'anthropic-agent-sdk' | '
141
203
  * Options for the LLM judge matcher
142
204
  */
143
205
  interface JudgeMatcherOptions {
206
+ /** Plugin options, parsed by the registered judge's schema. */
207
+ options?: Record<string, unknown>;
144
208
  /** Reference response to compare against */
145
209
  reference?: unknown;
146
210
  /** Score threshold for passing (default: 0.7) */
@@ -154,7 +218,7 @@ interface JudgeMatcherOptions {
154
218
  /**
155
219
  * Name of a registered custom judge executor.
156
220
  * When set, the named judge handles the entire evaluation pipeline
157
- * and its `pass` result is authoritative.
221
+ * and its score is compared with passingThreshold.
158
222
  */
159
223
  judge?: string;
160
224
  }
@@ -354,55 +418,6 @@ interface PredicateResult {
354
418
  */
355
419
  type ToolPredicate = (response: unknown, text: string) => boolean | PredicateResult | Promise<boolean | PredicateResult>;
356
420
 
357
- type StoredArtifactKind = 'eval-runner-result' | 'reporter-run' | 'eval-run-comparison' | 'server-comparison';
358
- interface StoredEvalArtifactMetadata {
359
- datasetName?: string;
360
- gitHash?: string;
361
- branch?: string;
362
- runNumber?: string;
363
- trigger?: string;
364
- packageVersion?: string;
365
- toolOverrideVariantId?: string;
366
- mcpHostModel?: string;
367
- judgeModel?: string;
368
- labels?: Record<string, string>;
369
- [key: string]: unknown;
370
- }
371
- interface StoredEvalArtifact<T> {
372
- schemaVersion: 1;
373
- kind: StoredArtifactKind;
374
- id: string;
375
- createdAt: string;
376
- metadata: StoredEvalArtifactMetadata;
377
- data: T;
378
- }
379
- interface StoredArtifactSummary {
380
- kind: StoredArtifactKind;
381
- id: string;
382
- createdAt: string;
383
- metadata: StoredEvalArtifactMetadata;
384
- }
385
- interface ListStoredArtifactsOptions {
386
- limit?: number;
387
- }
388
- interface EvalResultStore {
389
- saveArtifact<T>(artifact: StoredEvalArtifact<T>): Promise<void>;
390
- loadArtifact<T>(kind: StoredArtifactKind, id: string): Promise<StoredEvalArtifact<T>>;
391
- loadLatestArtifact<T>(kind: StoredArtifactKind): Promise<StoredEvalArtifact<T> | null>;
392
- listArtifacts(kind: StoredArtifactKind, options?: ListStoredArtifactsOptions): Promise<StoredArtifactSummary[]>;
393
- }
394
- interface FileEvalResultStoreConfig {
395
- provider: 'file';
396
- dir: string;
397
- }
398
- interface GCSEvalResultStoreConfig {
399
- provider: 'gcs';
400
- bucket: string;
401
- prefix?: string;
402
- }
403
- type EvalResultStoreConfig = FileEvalResultStoreConfig | GCSEvalResultStoreConfig;
404
- type EvalResultStoreLike = EvalResultStore | EvalResultStoreConfig;
405
-
406
421
  /**
407
422
  * Reporter-specific type definitions
408
423
  *
@@ -488,6 +503,7 @@ declare class MCPReporter implements Reporter {
488
503
  private allResults;
489
504
  private conformanceChecks;
490
505
  private serverCapabilities;
506
+ private variantExperiment;
491
507
  constructor(options?: MCPEvalReporterConfig);
492
508
  private log;
493
509
  private logError;
@@ -31,6 +31,7 @@ function sumUsage(a, b) {
31
31
  }
32
32
  var KIND_DIRS = {
33
33
  "eval-runner-result": "eval-runs",
34
+ "eval-run-summary": "eval-summaries",
34
35
  "reporter-run": "reporter-runs",
35
36
  "eval-run-comparison": "comparisons/eval-runs",
36
37
  "server-comparison": "comparisons/servers"
@@ -247,6 +248,7 @@ var MCPReporter = class {
247
248
  allResults = [];
248
249
  conformanceChecks = [];
249
250
  serverCapabilities = [];
251
+ variantExperiment;
250
252
  constructor(options = {}) {
251
253
  this.config = {
252
254
  outputDir: options.outputDir ?? ".mcp-test-results",
@@ -318,6 +320,22 @@ var MCPReporter = class {
318
320
  );
319
321
  }
320
322
  }
323
+ const experimentAttachment = result.attachments.find(
324
+ (a) => a.name === "mcp-variant-experiment" && a.contentType === "application/json"
325
+ );
326
+ const experimentContent = experimentAttachment ? await this.getAttachmentContent(experimentAttachment) : null;
327
+ if (experimentContent) {
328
+ try {
329
+ this.variantExperiment = JSON.parse(
330
+ experimentContent
331
+ );
332
+ } catch (error) {
333
+ this.logError(
334
+ `[MCP Reporter] Failed to parse variant-experiment attachment for "${test.title}":`,
335
+ error
336
+ );
337
+ }
338
+ }
321
339
  const conformanceAttachment = result.attachments.find(
322
340
  (a) => a.name === "mcp-conformance-checks" && a.contentType === "application/json"
323
341
  );
@@ -504,7 +522,8 @@ var MCPReporter = class {
504
522
  },
505
523
  results: this.allResults,
506
524
  conformanceChecks: this.conformanceChecks.length > 0 ? this.conformanceChecks : void 0,
507
- serverCapabilities: this.serverCapabilities.length > 0 ? this.serverCapabilities : void 0
525
+ serverCapabilities: this.serverCapabilities.length > 0 ? this.serverCapabilities : void 0,
526
+ variantExperiment: this.variantExperiment
508
527
  };
509
528
  }
510
529
  async loadHistoricalData() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/usageUtils.ts","../../src/evals/resultStore.ts","../../src/reporters/mcpReporter.ts"],"names":["readFile","mkdir","join","__filename","__dirname","writeFile","readdir"],"mappings":"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","file":"mcpReporter.js","sourcesContent":["import type { UsageMetrics } from '../types/index.js';\n\nfunction optionalSum(a?: number, b?: number): number | undefined {\n if (a === undefined && b === undefined) return undefined;\n return (a ?? 0) + (b ?? 0);\n}\n\nexport function sumUsage(\n a: UsageMetrics | undefined,\n b: UsageMetrics | undefined\n): UsageMetrics | undefined {\n if (!a && !b) return undefined;\n if (!a) return b ? { ...b } : undefined;\n if (!b) return { ...a };\n return {\n inputTokens: a.inputTokens + b.inputTokens,\n outputTokens: a.outputTokens + b.outputTokens,\n totalCostUsd: a.totalCostUsd + b.totalCostUsd,\n durationMs: a.durationMs + b.durationMs,\n durationApiMs: optionalSum(a.durationApiMs, b.durationApiMs),\n cacheReadInputTokens: optionalSum(\n a.cacheReadInputTokens,\n b.cacheReadInputTokens\n ),\n cacheCreationInputTokens: optionalSum(\n a.cacheCreationInputTokens,\n b.cacheCreationInputTokens\n ),\n };\n}\n","import { mkdir, readFile, readdir, writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nexport type StoredArtifactKind =\n | 'eval-runner-result'\n | 'reporter-run'\n | 'eval-run-comparison'\n | 'server-comparison';\n\nexport interface StoredEvalArtifactMetadata {\n datasetName?: string;\n gitHash?: string;\n branch?: string;\n runNumber?: string;\n trigger?: string;\n packageVersion?: string;\n toolOverrideVariantId?: string;\n mcpHostModel?: string;\n judgeModel?: string;\n labels?: Record<string, string>;\n [key: string]: unknown;\n}\n\nexport interface StoredEvalArtifact<T> {\n schemaVersion: 1;\n kind: StoredArtifactKind;\n id: string;\n createdAt: string;\n metadata: StoredEvalArtifactMetadata;\n data: T;\n}\n\nexport interface StoredArtifactSummary {\n kind: StoredArtifactKind;\n id: string;\n createdAt: string;\n metadata: StoredEvalArtifactMetadata;\n}\n\nexport interface ListStoredArtifactsOptions {\n limit?: number;\n}\n\nexport interface EvalResultStore {\n saveArtifact<T>(artifact: StoredEvalArtifact<T>): Promise<void>;\n loadArtifact<T>(\n kind: StoredArtifactKind,\n id: string\n ): Promise<StoredEvalArtifact<T>>;\n loadLatestArtifact<T>(\n kind: StoredArtifactKind\n ): Promise<StoredEvalArtifact<T> | null>;\n listArtifacts(\n kind: StoredArtifactKind,\n options?: ListStoredArtifactsOptions\n ): Promise<StoredArtifactSummary[]>;\n}\n\nexport interface FileEvalResultStoreConfig {\n provider: 'file';\n dir: string;\n}\n\nexport interface GCSEvalResultStoreConfig {\n provider: 'gcs';\n bucket: string;\n prefix?: string;\n}\n\nexport type EvalResultStoreConfig =\n | FileEvalResultStoreConfig\n | GCSEvalResultStoreConfig;\n\nexport type EvalResultStoreLike = EvalResultStore | EvalResultStoreConfig;\n\nconst KIND_DIRS: Record<StoredArtifactKind, string> = {\n 'eval-runner-result': 'eval-runs',\n 'reporter-run': 'reporter-runs',\n 'eval-run-comparison': 'comparisons/eval-runs',\n 'server-comparison': 'comparisons/servers',\n};\n\nexport function createEvalResultStore(\n config: EvalResultStoreConfig\n): EvalResultStore {\n if (config.provider === 'file') {\n return new FileEvalResultStore(config);\n }\n return new GCSEvalResultStore(config);\n}\n\nexport function resolveEvalResultStore(\n store: EvalResultStoreLike\n): EvalResultStore {\n return isEvalResultStore(store) ? store : createEvalResultStore(store);\n}\n\nexport function isEvalResultStore(value: unknown): value is EvalResultStore {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'saveArtifact' in value &&\n 'loadArtifact' in value &&\n 'loadLatestArtifact' in value &&\n 'listArtifacts' in value\n );\n}\n\nexport function createStoredEvalArtifact<T>(options: {\n kind: StoredArtifactKind;\n data: T;\n id?: string;\n metadata?: StoredEvalArtifactMetadata;\n createdAt?: string;\n}): StoredEvalArtifact<T> {\n const createdAt = options.createdAt ?? new Date().toISOString();\n return {\n schemaVersion: 1,\n kind: options.kind,\n id: options.id ?? createDefaultArtifactId(createdAt),\n createdAt,\n metadata: {\n ...defaultEnvironmentMetadata(),\n ...(options.metadata ?? {}),\n },\n data: options.data,\n };\n}\n\nexport function createDefaultArtifactId(timestamp = new Date().toISOString()) {\n const safeTimestamp = timestamp.replace(/[:.]/g, '-');\n const runNumber = process.env.GITHUB_RUN_NUMBER;\n const sha = process.env.GITHUB_SHA?.slice(0, 12);\n const suffix = runNumber ?? sha;\n return suffix ? `${safeTimestamp}-${suffix}` : safeTimestamp;\n}\n\nexport function defaultEnvironmentMetadata(): StoredEvalArtifactMetadata {\n return {\n ...(process.env.GITHUB_SHA !== undefined && {\n gitHash: process.env.GITHUB_SHA,\n }),\n ...(process.env.GITHUB_REF_NAME !== undefined && {\n branch: process.env.GITHUB_REF_NAME,\n }),\n ...(process.env.GITHUB_RUN_NUMBER !== undefined && {\n runNumber: process.env.GITHUB_RUN_NUMBER,\n }),\n ...(process.env.GITHUB_EVENT_NAME !== undefined && {\n trigger: process.env.GITHUB_EVENT_NAME,\n }),\n };\n}\n\nexport class FileEvalResultStore implements EvalResultStore {\n private readonly dir: string;\n\n constructor(config: FileEvalResultStoreConfig) {\n this.dir = config.dir;\n }\n\n async saveArtifact<T>(artifact: StoredEvalArtifact<T>): Promise<void> {\n const artifactDir = join(this.dir, KIND_DIRS[artifact.kind]);\n await mkdir(artifactDir, { recursive: true });\n const serialized = JSON.stringify(artifact, null, 2);\n await writeFile(\n join(artifactDir, `${artifact.id}.json`),\n serialized,\n 'utf8'\n );\n await writeFile(join(artifactDir, 'latest.json'), serialized, 'utf8');\n }\n\n async loadArtifact<T>(\n kind: StoredArtifactKind,\n id: string\n ): Promise<StoredEvalArtifact<T>> {\n const raw = await readFile(\n join(this.dir, KIND_DIRS[kind], `${id}.json`),\n 'utf8'\n );\n return JSON.parse(raw) as StoredEvalArtifact<T>;\n }\n\n async loadLatestArtifact<T>(\n kind: StoredArtifactKind\n ): Promise<StoredEvalArtifact<T> | null> {\n try {\n const raw = await readFile(\n join(this.dir, KIND_DIRS[kind], 'latest.json'),\n 'utf8'\n );\n return JSON.parse(raw) as StoredEvalArtifact<T>;\n } catch (error) {\n if (isMissingFileError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n async listArtifacts(\n kind: StoredArtifactKind,\n options: ListStoredArtifactsOptions = {}\n ): Promise<StoredArtifactSummary[]> {\n let files: string[];\n try {\n files = await readdir(join(this.dir, KIND_DIRS[kind]));\n } catch (error) {\n if (isMissingFileError(error)) {\n return [];\n }\n throw error;\n }\n\n const summaries = await Promise.all(\n files\n .filter((f) => f.endsWith('.json') && f !== 'latest.json')\n .map(async (file) => {\n const raw = await readFile(\n join(this.dir, KIND_DIRS[kind], file),\n 'utf8'\n );\n return toSummary(JSON.parse(raw) as StoredEvalArtifact<unknown>);\n })\n );\n\n return summaries\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n .slice(0, options.limit);\n }\n}\n\ntype GCSStorageConstructor = new () => {\n bucket(name: string): GCSBucket;\n};\n\ninterface GCSBucket {\n file(name: string): GCSFile;\n getFiles(options: { prefix: string }): Promise<[GCSFile[]]>;\n}\n\ninterface GCSFile {\n name: string;\n save(\n data: string,\n options?: { contentType?: string; resumable?: boolean; validation?: false }\n ): Promise<unknown>;\n download(): Promise<[Buffer]>;\n exists(): Promise<[boolean]>;\n}\n\nexport class GCSEvalResultStore implements EvalResultStore {\n private readonly bucketName: string;\n private readonly prefix: string;\n private storage: { bucket(name: string): GCSBucket } | undefined;\n\n constructor(config: GCSEvalResultStoreConfig) {\n this.bucketName = config.bucket;\n this.prefix = trimSlashes(config.prefix ?? '');\n }\n\n async saveArtifact<T>(artifact: StoredEvalArtifact<T>): Promise<void> {\n const bucket = await this.getBucket();\n const serialized = JSON.stringify(artifact, null, 2);\n await bucket\n .file(\n this.objectPath(\n artifact.kind,\n `${encodeURIComponent(artifact.id)}.json`\n )\n )\n .save(serialized, {\n contentType: 'application/json',\n resumable: false,\n validation: false,\n });\n await bucket\n .file(this.objectPath(artifact.kind, 'latest.json'))\n .save(serialized, {\n contentType: 'application/json',\n resumable: false,\n validation: false,\n });\n }\n\n async loadArtifact<T>(\n kind: StoredArtifactKind,\n id: string\n ): Promise<StoredEvalArtifact<T>> {\n const bucket = await this.getBucket();\n const file = bucket.file(\n this.objectPath(kind, `${encodeURIComponent(id)}.json`)\n );\n const [buffer] = await file.download();\n return JSON.parse(buffer.toString('utf8')) as StoredEvalArtifact<T>;\n }\n\n async loadLatestArtifact<T>(\n kind: StoredArtifactKind\n ): Promise<StoredEvalArtifact<T> | null> {\n const bucket = await this.getBucket();\n const file = bucket.file(this.objectPath(kind, 'latest.json'));\n const [exists] = await file.exists();\n if (!exists) return null;\n\n const [buffer] = await file.download();\n return JSON.parse(buffer.toString('utf8')) as StoredEvalArtifact<T>;\n }\n\n async listArtifacts(\n kind: StoredArtifactKind,\n options: ListStoredArtifactsOptions = {}\n ): Promise<StoredArtifactSummary[]> {\n const bucket = await this.getBucket();\n const [files] = await bucket.getFiles({\n prefix: this.objectPath(kind, ''),\n });\n\n const summaries = await Promise.all(\n files\n .filter(\n (f) => f.name.endsWith('.json') && !f.name.endsWith('/latest.json')\n )\n .map(async (file) => {\n const [buffer] = await file.download();\n return toSummary(\n JSON.parse(buffer.toString('utf8')) as StoredEvalArtifact<unknown>\n );\n })\n );\n\n return summaries\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n .slice(0, options.limit);\n }\n\n private async getBucket(): Promise<GCSBucket> {\n if (!this.storage) {\n const moduleName = '@google-cloud/storage';\n let Storage: GCSStorageConstructor;\n try {\n const mod = (await import(moduleName)) as {\n Storage: GCSStorageConstructor;\n };\n Storage = mod.Storage;\n } catch (error) {\n throw new Error(\n 'GCS result storage requires the optional `@google-cloud/storage` package. ' +\n 'Install it and authenticate with Application Default Credentials via GOOGLE_APPLICATION_CREDENTIALS.\\n' +\n `Original error: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n this.storage = new Storage();\n }\n return this.storage.bucket(this.bucketName);\n }\n\n private objectPath(kind: StoredArtifactKind, filename: string): string {\n const parts = [this.prefix, KIND_DIRS[kind], filename].filter(Boolean);\n return parts.join('/');\n }\n}\n\nfunction toSummary(\n artifact: StoredEvalArtifact<unknown>\n): StoredArtifactSummary {\n return {\n kind: artifact.kind,\n id: artifact.id,\n createdAt: artifact.createdAt,\n metadata: artifact.metadata,\n };\n}\n\nfunction trimSlashes(value: string): string {\n return value.replace(/^\\/+|\\/+$/g, '');\n}\n\nfunction isMissingFileError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { code?: string }).code === 'ENOENT'\n );\n}\n","import type {\n Reporter,\n FullConfig,\n Suite,\n TestCase,\n TestResult,\n FullResult,\n} from '@playwright/test/reporter';\nimport { mkdir, writeFile, readdir, readFile, unlink, cp } from 'fs/promises';\nimport { join, resolve, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport type { MCPEvalReporterConfig } from './types.js';\nimport type { AuthType } from '../types/index.js';\nimport type {\n MCPEvalRunData,\n MCPEvalHistoricalSummary,\n MCPConformanceResultData,\n MCPServerCapabilitiesData,\n EvalCaseResult,\n MCPConformanceCheck,\n} from '../types/reporter.js';\nimport type { UsageMetrics } from '../types/index.js';\nimport { sumUsage } from '../utils/usageUtils.js';\nimport {\n createStoredEvalArtifact,\n resolveEvalResultStore,\n} from '../evals/resultStore.js';\n\ntype ResolvedReporterConfig = Required<\n Omit<\n MCPEvalReporterConfig,\n 'resultStore' | 'runId' | 'runMetadata' | 'redactStoredResponses'\n >\n> &\n Pick<\n MCPEvalReporterConfig,\n 'resultStore' | 'runId' | 'runMetadata' | 'redactStoredResponses'\n >;\n\n/**\n * Custom Playwright reporter for MCP eval results\n *\n * Generates HTML reports with historical tracking\n *\n * @example\n * ```typescript\n * // playwright.config.ts\n * export default defineConfig({\n * reporter: [\n * ['@gleanwork/mcp-server-tester/reporters/mcpReporter', {\n * outputDir: '.mcp-test-results',\n * historyLimit: 10\n * }]\n * ]\n * });\n * ```\n */\nexport default class MCPReporter implements Reporter {\n private config: ResolvedReporterConfig;\n private startTime: number = 0;\n private allResults: Array<EvalCaseResult> = [];\n private conformanceChecks: Array<MCPConformanceResultData> = [];\n private serverCapabilities: Array<MCPServerCapabilitiesData> = [];\n\n constructor(options: MCPEvalReporterConfig = {}) {\n this.config = {\n outputDir: options.outputDir ?? '.mcp-test-results',\n autoOpen: options.autoOpen ?? false,\n historyLimit: options.historyLimit ?? 10,\n quiet: options.quiet ?? false,\n includeAutoTracking: options.includeAutoTracking ?? true,\n resultStore: options.resultStore,\n runId: options.runId,\n runMetadata: options.runMetadata,\n // Default true to match the eval-runner store path. Stored artifacts\n // omit response bodies by default; opt in by passing\n // `redactStoredResponses: false` if you need full responses for\n // debugging or history comparison. Keeping this consistent across\n // both write paths prevents users from getting a mix of\n // redacted/non-redacted artifacts depending on which code path wrote\n // them.\n redactStoredResponses: options.redactStoredResponses ?? true,\n };\n }\n\n private log(message: string): void {\n if (!this.config.quiet) {\n console.log(message);\n }\n }\n\n private logError(message: string, error?: unknown): void {\n if (!this.config.quiet) {\n console.error(message, error ?? '');\n }\n }\n\n /**\n * Reads attachment content from either in-memory body (Playwright < 1.43)\n * or from the file path on disk (Playwright ≥ 1.43, which writes large\n * attachments to disk and exposes path instead of body).\n */\n private async getAttachmentContent(attachment: {\n body?: Buffer;\n path?: string;\n }): Promise<string | null> {\n if (attachment.body) return attachment.body.toString('utf-8');\n if (attachment.path) {\n try {\n return await readFile(attachment.path, 'utf-8');\n } catch {\n return null;\n }\n }\n return null;\n }\n\n async onBegin(_config: FullConfig, _suite: Suite): Promise<void> {\n this.startTime = Date.now();\n this.allResults = [];\n this.conformanceChecks = [];\n this.serverCapabilities = [];\n\n // Ensure output directory exists\n await mkdir(this.config.outputDir, { recursive: true });\n }\n\n async onTestEnd(test: TestCase, result: TestResult): Promise<void> {\n // Strategy 1: Extract MCP eval results from runEvalDataset() attachments\n const evalAttachment = result.attachments.find(\n (a) =>\n a.name === 'mcp-test-results' && a.contentType === 'application/json'\n );\n\n let hasEvalDataset = false;\n\n const evalContent = evalAttachment\n ? await this.getAttachmentContent(evalAttachment)\n : null;\n if (evalContent) {\n try {\n const evalResults = JSON.parse(evalContent) as {\n caseResults: Array<EvalCaseResult>;\n };\n\n // Trust the data from the attachment - evalRunner now includes\n // authType and project from the mcp fixture (Playwright is source of truth)\n this.allResults.push(...evalResults.caseResults);\n hasEvalDataset = true;\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse eval results from test \"${test.title}\":`,\n error\n );\n }\n }\n\n // Strategy 2: Extract conformance check results\n // These are created by runConformanceChecks() when testInfo is passed\n const conformanceAttachment = result.attachments.find(\n (a) =>\n a.name === 'mcp-conformance-checks' &&\n a.contentType === 'application/json'\n );\n\n const conformanceContent = conformanceAttachment\n ? await this.getAttachmentContent(conformanceAttachment)\n : null;\n if (conformanceContent) {\n try {\n const conformanceData = JSON.parse(conformanceContent) as {\n operation: string;\n pass: boolean;\n checks: MCPConformanceCheck[];\n serverInfo?: { name?: string; version?: string };\n toolCount: number;\n authType?: AuthType;\n project?: string;\n };\n\n // Only push if checks array is valid\n if (Array.isArray(conformanceData.checks)) {\n this.conformanceChecks.push({\n testTitle: test.title,\n pass: conformanceData.pass,\n checks: conformanceData.checks,\n serverInfo: conformanceData.serverInfo,\n toolCount: conformanceData.toolCount,\n authType: conformanceData.authType,\n project: conformanceData.project,\n });\n }\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse conformance check attachment for \"${test.title}\":`,\n error\n );\n }\n }\n\n // Strategy 2b: Extract server capabilities from mcp-list-tools attachments\n // These are created by createMCPFixture().listTools()\n const listToolsAttachment = result.attachments.find(\n (a) => a.name === 'mcp-list-tools' && a.contentType === 'application/json'\n );\n\n const listToolsContent = listToolsAttachment\n ? await this.getAttachmentContent(listToolsAttachment)\n : null;\n if (listToolsContent) {\n try {\n const listToolsData = JSON.parse(listToolsContent) as {\n operation: string;\n toolCount: number;\n tools: Array<{ name: string; description?: string }>;\n };\n\n // Only push if tools array is valid\n if (Array.isArray(listToolsData.tools)) {\n this.serverCapabilities.push({\n testTitle: test.title,\n tools: listToolsData.tools,\n toolCount: listToolsData.toolCount,\n // Note: authType and project are available from the mcp fixture\n // but not currently included in the listTools attachment\n });\n }\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse mcp-list-tools attachment for \"${test.title}\":`,\n error\n );\n }\n }\n\n // Strategy 3: Extract MCP tool calls from auto-tracking attachments\n // These are created by createMCPFixture()\n // Skip if:\n // - This test already has eval dataset results (to avoid duplicates)\n // - Auto-tracking is disabled in config\n if (hasEvalDataset || !this.config.includeAutoTracking) {\n return;\n }\n\n const mcpCallAttachments = result.attachments.filter(\n (a) =>\n a.name &&\n a.name.startsWith('mcp-call-') &&\n a.contentType === 'application/json'\n );\n\n for (const attachment of mcpCallAttachments) {\n const callContent = await this.getAttachmentContent(attachment);\n if (!callContent) continue;\n\n try {\n // Attachment now includes authType and project from the mcp fixture\n const callData = JSON.parse(callContent) as {\n operation: string;\n toolName: string;\n args: Record<string, unknown>;\n result: unknown;\n durationMs: number;\n isError: boolean;\n authType?: AuthType;\n project?: string;\n };\n\n const suiteName = test.parent?.title || 'Uncategorized Tests';\n const testPassed = result.status === 'passed';\n\n const syntheticResult: EvalCaseResult = {\n id: test.title,\n datasetName: suiteName,\n toolName: callData.toolName,\n source: 'test',\n pass: testPassed,\n response: callData.result,\n error: !testPassed ? 'Test failed' : undefined,\n expectations: {},\n authType: callData.authType,\n project: callData.project,\n durationMs: callData.durationMs,\n };\n\n this.allResults.push(syntheticResult);\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse MCP call attachment \"${attachment.name}\":`,\n error\n );\n }\n }\n }\n\n async onEnd(_result: FullResult): Promise<void> {\n const endTime = Date.now();\n const durationMs = endTime - this.startTime;\n\n // Skip if no eval results collected\n if (this.allResults.length === 0) {\n this.log('[MCP Reporter] No MCP eval results found in test run');\n return;\n }\n\n // Build run data\n const runData = this.buildRunData(durationMs);\n\n // Load historical data\n const historical = await this.loadHistoricalData();\n\n // Add current run to historical\n historical.push({\n timestamp: runData.timestamp,\n total: runData.metrics.total,\n passed: runData.metrics.passed,\n failed: runData.metrics.failed,\n passRate: runData.metrics.passRate,\n durationMs: runData.durationMs,\n });\n\n // Save current run data\n await this.saveRunData(runData);\n\n // Clean up old runs\n await this.cleanupOldRuns();\n\n // Save current run to external storage, if configured. This is additive:\n // local report generation should still work if remote storage is down.\n await this.saveRunDataToStore(runData);\n\n // Generate report using copy + inject pattern\n const reportDir = join(this.config.outputDir, 'latest');\n await this.generateReport(runData, historical, reportDir);\n\n const reportPath = join(reportDir, 'index.html');\n this.log(`\\n[MCP Reporter] Report generated: ${reportPath}`);\n this.log(\n `[MCP Reporter] Results: ${runData.metrics.passed}/${runData.metrics.total} passed (${(runData.metrics.passRate * 100).toFixed(1)}%)`\n );\n\n // Auto-open browser if configured and not in CI\n if (this.config.autoOpen && !process.env.CI) {\n await this.openReport(reportPath);\n }\n }\n\n private async generateReport(\n runData: MCPEvalRunData,\n historical: Array<MCPEvalHistoricalSummary>,\n outputDir: string\n ): Promise<void> {\n // Get the UI dist path (relative to this file)\n // In ESM, we need to use import.meta.url\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const uiDistPath = join(__dirname, 'ui-dist');\n\n // Step 1: Copy pre-built UI template\n await mkdir(outputDir, { recursive: true });\n await cp(uiDistPath, outputDir, { recursive: true, force: true });\n\n // Step 2: Inject test data as JavaScript\n const dataScript = `window.MCP_EVAL_DATA = ${JSON.stringify(\n {\n runData,\n historical,\n },\n null,\n 2\n )};`;\n\n await writeFile(join(outputDir, 'data.js'), dataScript, 'utf-8');\n }\n\n private buildRunData(durationMs: number): MCPEvalRunData {\n const total = this.allResults.length;\n const datasetBreakdown: Record<string, number> = {};\n const expectationBreakdown = {\n exact: 0,\n schema: 0,\n textContains: 0,\n regex: 0,\n snapshot: 0,\n judge: 0,\n error: 0,\n size: 0,\n toolsTriggered: 0,\n toolCallCount: 0,\n };\n\n let passed = 0;\n for (const r of this.allResults) {\n if (r.pass) passed++;\n\n const datasetName = r.datasetName || 'Unknown Dataset';\n datasetBreakdown[datasetName] = (datasetBreakdown[datasetName] || 0) + 1;\n\n if (r.expectations.exact) expectationBreakdown.exact++;\n if (r.expectations.schema) expectationBreakdown.schema++;\n if (r.expectations.textContains) expectationBreakdown.textContains++;\n if (r.expectations.regex) expectationBreakdown.regex++;\n if (r.expectations.snapshot) expectationBreakdown.snapshot++;\n if (r.expectations.judge) expectationBreakdown.judge++;\n if (r.expectations.error) expectationBreakdown.error++;\n if (r.expectations.size) expectationBreakdown.size++;\n if (r.expectations.toolsTriggered) expectationBreakdown.toolsTriggered++;\n if (r.expectations.toolCallCount) expectationBreakdown.toolCallCount++;\n }\n\n const failed = total - passed;\n\n const totalHostUsage = this.allResults.reduce(\n (acc, r) => sumUsage(acc, r.hostUsage),\n undefined as UsageMetrics | undefined\n );\n\n return {\n timestamp: new Date().toISOString(),\n durationMs,\n environment: {\n ci: !!process.env.CI,\n node: process.version,\n platform: process.platform,\n },\n metrics: {\n total,\n passed,\n failed,\n passRate: passed / total,\n datasetBreakdown,\n expectationBreakdown,\n totalHostUsage,\n },\n results: this.allResults,\n conformanceChecks:\n this.conformanceChecks.length > 0 ? this.conformanceChecks : undefined,\n serverCapabilities:\n this.serverCapabilities.length > 0\n ? this.serverCapabilities\n : undefined,\n };\n }\n\n private async loadHistoricalData(): Promise<Array<MCPEvalHistoricalSummary>> {\n const storeHistorical = await this.loadHistoricalDataFromStore();\n if (storeHistorical) {\n return storeHistorical;\n }\n\n try {\n const files = await readdir(this.config.outputDir);\n const runFiles = files\n .filter((f) => f.startsWith('run-') && f.endsWith('.json'))\n .sort()\n .slice(-(this.config.historyLimit - 1)); // Keep most recent, leave room for current run\n\n const historical: Array<MCPEvalHistoricalSummary> = [];\n\n for (const file of runFiles) {\n try {\n const content = await readFile(\n join(this.config.outputDir, file),\n 'utf-8'\n );\n const runData = JSON.parse(content) as MCPEvalRunData;\n\n historical.push({\n timestamp: runData.timestamp,\n total: runData.metrics.total,\n passed: runData.metrics.passed,\n failed: runData.metrics.failed,\n passRate: runData.metrics.passRate,\n durationMs: runData.durationMs,\n });\n } catch (error) {\n this.logError(`[MCP Reporter] Failed to load ${file}:`, error);\n }\n }\n\n return historical;\n } catch {\n return [];\n }\n }\n\n private async saveRunData(runData: MCPEvalRunData): Promise<void> {\n const filename = `run-${runData.timestamp.replace(/:/g, '-')}.json`;\n const filepath = join(this.config.outputDir, filename);\n\n await writeFile(filepath, JSON.stringify(runData, null, 2), 'utf-8');\n }\n\n private async saveRunDataToStore(runData: MCPEvalRunData): Promise<void> {\n if (!this.config.resultStore) {\n return;\n }\n\n try {\n const store = resolveEvalResultStore(this.config.resultStore);\n await store.saveArtifact(\n createStoredEvalArtifact({\n kind: 'reporter-run',\n id: this.config.runId,\n data: this.config.redactStoredResponses\n ? redactResponses(runData)\n : runData,\n metadata: {\n ...(this.config.runMetadata ?? {}),\n },\n })\n );\n } catch (error) {\n this.logError(\n '[MCP Reporter] Failed to save run to result store:',\n error\n );\n }\n }\n\n private async loadHistoricalDataFromStore(): Promise<Array<MCPEvalHistoricalSummary> | null> {\n if (!this.config.resultStore) {\n return null;\n }\n\n try {\n const store = resolveEvalResultStore(this.config.resultStore);\n const summaries = await store.listArtifacts('reporter-run', {\n limit: this.config.historyLimit - 1,\n });\n\n const historical: Array<MCPEvalHistoricalSummary> = [];\n for (const summary of summaries.reverse()) {\n const artifact = await store.loadArtifact<MCPEvalRunData>(\n 'reporter-run',\n summary.id\n );\n historical.push(toHistoricalSummary(artifact.data));\n }\n return historical;\n } catch (error) {\n this.logError(\n '[MCP Reporter] Failed to load history from result store:',\n error\n );\n return null;\n }\n }\n\n private async cleanupOldRuns(): Promise<void> {\n try {\n const files = await readdir(this.config.outputDir);\n const runFiles = files\n .filter((f) => f.startsWith('run-') && f.endsWith('.json'))\n .sort()\n .reverse();\n\n // Keep only historyLimit most recent runs\n const toDelete = runFiles.slice(this.config.historyLimit);\n\n for (const file of toDelete) {\n await unlink(join(this.config.outputDir, file));\n }\n } catch (error) {\n this.logError('[MCP Reporter] Failed to cleanup old runs:', error);\n }\n }\n\n private async openReport(reportPath: string): Promise<void> {\n try {\n // Dynamic import to avoid bundling issues\n const { default: open } = await import('open');\n const absolutePath = resolve(reportPath);\n\n await open(absolutePath);\n this.log('[MCP Reporter] Opened report in browser');\n } catch (error) {\n this.logError('[MCP Reporter] Failed to open report:', error);\n this.log(`[MCP Reporter] Open manually: file://${resolve(reportPath)}`);\n }\n }\n}\n\nfunction toHistoricalSummary(\n runData: MCPEvalRunData\n): MCPEvalHistoricalSummary {\n return {\n timestamp: runData.timestamp,\n total: runData.metrics.total,\n passed: runData.metrics.passed,\n failed: runData.metrics.failed,\n passRate: runData.metrics.passRate,\n durationMs: runData.durationMs,\n };\n}\n\nfunction redactResponses<T>(value: T): T {\n return JSON.parse(\n JSON.stringify(value, (key, currentValue: unknown) =>\n key === 'response' ? undefined : currentValue\n )\n ) as T;\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/usageUtils.ts","../../src/evals/resultStore.ts","../../src/reporters/mcpReporter.ts"],"names":["readFile","mkdir","join","__filename","__dirname","writeFile","readdir"],"mappings":"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","file":"mcpReporter.js","sourcesContent":["import type { UsageMetrics } from '../types/index.js';\n\nfunction optionalSum(a?: number, b?: number): number | undefined {\n if (a === undefined && b === undefined) return undefined;\n return (a ?? 0) + (b ?? 0);\n}\n\nexport function sumUsage(\n a: UsageMetrics | undefined,\n b: UsageMetrics | undefined\n): UsageMetrics | undefined {\n if (!a && !b) return undefined;\n if (!a) return b ? { ...b } : undefined;\n if (!b) return { ...a };\n return {\n inputTokens: a.inputTokens + b.inputTokens,\n outputTokens: a.outputTokens + b.outputTokens,\n totalCostUsd: a.totalCostUsd + b.totalCostUsd,\n durationMs: a.durationMs + b.durationMs,\n durationApiMs: optionalSum(a.durationApiMs, b.durationApiMs),\n cacheReadInputTokens: optionalSum(\n a.cacheReadInputTokens,\n b.cacheReadInputTokens\n ),\n cacheCreationInputTokens: optionalSum(\n a.cacheCreationInputTokens,\n b.cacheCreationInputTokens\n ),\n };\n}\n","import { mkdir, readFile, readdir, writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nexport type StoredArtifactKind =\n | 'eval-runner-result'\n | 'eval-run-summary'\n | 'reporter-run'\n | 'eval-run-comparison'\n | 'server-comparison';\n\nexport interface StoredEvalArtifactMetadata {\n datasetName?: string;\n gitHash?: string;\n branch?: string;\n runNumber?: string;\n trigger?: string;\n packageVersion?: string;\n toolOverrideVariantId?: string;\n mcpHostModel?: string;\n judgeModel?: string;\n labels?: Record<string, string>;\n [key: string]: unknown;\n}\n\nexport interface StoredEvalArtifact<T> {\n schemaVersion: 1;\n kind: StoredArtifactKind;\n id: string;\n createdAt: string;\n metadata: StoredEvalArtifactMetadata;\n data: T;\n}\n\nexport interface StoredArtifactSummary {\n kind: StoredArtifactKind;\n id: string;\n createdAt: string;\n metadata: StoredEvalArtifactMetadata;\n}\n\nexport interface ListStoredArtifactsOptions {\n limit?: number;\n}\n\nexport interface EvalResultStore {\n saveArtifact<T>(artifact: StoredEvalArtifact<T>): Promise<void>;\n loadArtifact<T>(\n kind: StoredArtifactKind,\n id: string\n ): Promise<StoredEvalArtifact<T>>;\n loadLatestArtifact<T>(\n kind: StoredArtifactKind\n ): Promise<StoredEvalArtifact<T> | null>;\n listArtifacts(\n kind: StoredArtifactKind,\n options?: ListStoredArtifactsOptions\n ): Promise<StoredArtifactSummary[]>;\n}\n\nexport interface FileEvalResultStoreConfig {\n provider: 'file';\n dir: string;\n}\n\nexport interface GCSEvalResultStoreConfig {\n provider: 'gcs';\n bucket: string;\n prefix?: string;\n}\n\nexport type EvalResultStoreConfig =\n | FileEvalResultStoreConfig\n | GCSEvalResultStoreConfig;\n\nexport type EvalResultStoreLike = EvalResultStore | EvalResultStoreConfig;\n\nconst KIND_DIRS: Record<StoredArtifactKind, string> = {\n 'eval-runner-result': 'eval-runs',\n 'eval-run-summary': 'eval-summaries',\n 'reporter-run': 'reporter-runs',\n 'eval-run-comparison': 'comparisons/eval-runs',\n 'server-comparison': 'comparisons/servers',\n};\n\nexport function createEvalResultStore(\n config: EvalResultStoreConfig\n): EvalResultStore {\n if (config.provider === 'file') {\n return new FileEvalResultStore(config);\n }\n return new GCSEvalResultStore(config);\n}\n\nexport function resolveEvalResultStore(\n store: EvalResultStoreLike\n): EvalResultStore {\n return isEvalResultStore(store) ? store : createEvalResultStore(store);\n}\n\nexport function isEvalResultStore(value: unknown): value is EvalResultStore {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'saveArtifact' in value &&\n 'loadArtifact' in value &&\n 'loadLatestArtifact' in value &&\n 'listArtifacts' in value\n );\n}\n\nexport function createStoredEvalArtifact<T>(options: {\n kind: StoredArtifactKind;\n data: T;\n id?: string;\n metadata?: StoredEvalArtifactMetadata;\n createdAt?: string;\n}): StoredEvalArtifact<T> {\n const createdAt = options.createdAt ?? new Date().toISOString();\n return {\n schemaVersion: 1,\n kind: options.kind,\n id: options.id ?? createDefaultArtifactId(createdAt),\n createdAt,\n metadata: {\n ...defaultEnvironmentMetadata(),\n ...(options.metadata ?? {}),\n },\n data: options.data,\n };\n}\n\nexport function createDefaultArtifactId(timestamp = new Date().toISOString()) {\n const safeTimestamp = timestamp.replace(/[:.]/g, '-');\n const runNumber = process.env.GITHUB_RUN_NUMBER;\n const sha = process.env.GITHUB_SHA?.slice(0, 12);\n const suffix = runNumber ?? sha;\n return suffix ? `${safeTimestamp}-${suffix}` : safeTimestamp;\n}\n\nexport function defaultEnvironmentMetadata(): StoredEvalArtifactMetadata {\n return {\n ...(process.env.GITHUB_SHA !== undefined && {\n gitHash: process.env.GITHUB_SHA,\n }),\n ...(process.env.GITHUB_REF_NAME !== undefined && {\n branch: process.env.GITHUB_REF_NAME,\n }),\n ...(process.env.GITHUB_RUN_NUMBER !== undefined && {\n runNumber: process.env.GITHUB_RUN_NUMBER,\n }),\n ...(process.env.GITHUB_EVENT_NAME !== undefined && {\n trigger: process.env.GITHUB_EVENT_NAME,\n }),\n };\n}\n\nexport class FileEvalResultStore implements EvalResultStore {\n private readonly dir: string;\n\n constructor(config: FileEvalResultStoreConfig) {\n this.dir = config.dir;\n }\n\n async saveArtifact<T>(artifact: StoredEvalArtifact<T>): Promise<void> {\n const artifactDir = join(this.dir, KIND_DIRS[artifact.kind]);\n await mkdir(artifactDir, { recursive: true });\n const serialized = JSON.stringify(artifact, null, 2);\n await writeFile(\n join(artifactDir, `${artifact.id}.json`),\n serialized,\n 'utf8'\n );\n await writeFile(join(artifactDir, 'latest.json'), serialized, 'utf8');\n }\n\n async loadArtifact<T>(\n kind: StoredArtifactKind,\n id: string\n ): Promise<StoredEvalArtifact<T>> {\n const raw = await readFile(\n join(this.dir, KIND_DIRS[kind], `${id}.json`),\n 'utf8'\n );\n return JSON.parse(raw) as StoredEvalArtifact<T>;\n }\n\n async loadLatestArtifact<T>(\n kind: StoredArtifactKind\n ): Promise<StoredEvalArtifact<T> | null> {\n try {\n const raw = await readFile(\n join(this.dir, KIND_DIRS[kind], 'latest.json'),\n 'utf8'\n );\n return JSON.parse(raw) as StoredEvalArtifact<T>;\n } catch (error) {\n if (isMissingFileError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n async listArtifacts(\n kind: StoredArtifactKind,\n options: ListStoredArtifactsOptions = {}\n ): Promise<StoredArtifactSummary[]> {\n let files: string[];\n try {\n files = await readdir(join(this.dir, KIND_DIRS[kind]));\n } catch (error) {\n if (isMissingFileError(error)) {\n return [];\n }\n throw error;\n }\n\n const summaries = await Promise.all(\n files\n .filter((f) => f.endsWith('.json') && f !== 'latest.json')\n .map(async (file) => {\n const raw = await readFile(\n join(this.dir, KIND_DIRS[kind], file),\n 'utf8'\n );\n return toSummary(JSON.parse(raw) as StoredEvalArtifact<unknown>);\n })\n );\n\n return summaries\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n .slice(0, options.limit);\n }\n}\n\ntype GCSStorageConstructor = new () => {\n bucket(name: string): GCSBucket;\n};\n\ninterface GCSBucket {\n file(name: string): GCSFile;\n getFiles(options: { prefix: string }): Promise<[GCSFile[]]>;\n}\n\ninterface GCSFile {\n name: string;\n save(\n data: string,\n options?: { contentType?: string; resumable?: boolean; validation?: false }\n ): Promise<unknown>;\n download(): Promise<[Buffer]>;\n exists(): Promise<[boolean]>;\n}\n\nexport class GCSEvalResultStore implements EvalResultStore {\n private readonly bucketName: string;\n private readonly prefix: string;\n private storage: { bucket(name: string): GCSBucket } | undefined;\n\n constructor(config: GCSEvalResultStoreConfig) {\n this.bucketName = config.bucket;\n this.prefix = trimSlashes(config.prefix ?? '');\n }\n\n async saveArtifact<T>(artifact: StoredEvalArtifact<T>): Promise<void> {\n const bucket = await this.getBucket();\n const serialized = JSON.stringify(artifact, null, 2);\n await bucket\n .file(\n this.objectPath(\n artifact.kind,\n `${encodeURIComponent(artifact.id)}.json`\n )\n )\n .save(serialized, {\n contentType: 'application/json',\n resumable: false,\n validation: false,\n });\n await bucket\n .file(this.objectPath(artifact.kind, 'latest.json'))\n .save(serialized, {\n contentType: 'application/json',\n resumable: false,\n validation: false,\n });\n }\n\n async loadArtifact<T>(\n kind: StoredArtifactKind,\n id: string\n ): Promise<StoredEvalArtifact<T>> {\n const bucket = await this.getBucket();\n const file = bucket.file(\n this.objectPath(kind, `${encodeURIComponent(id)}.json`)\n );\n const [buffer] = await file.download();\n return JSON.parse(buffer.toString('utf8')) as StoredEvalArtifact<T>;\n }\n\n async loadLatestArtifact<T>(\n kind: StoredArtifactKind\n ): Promise<StoredEvalArtifact<T> | null> {\n const bucket = await this.getBucket();\n const file = bucket.file(this.objectPath(kind, 'latest.json'));\n const [exists] = await file.exists();\n if (!exists) return null;\n\n const [buffer] = await file.download();\n return JSON.parse(buffer.toString('utf8')) as StoredEvalArtifact<T>;\n }\n\n async listArtifacts(\n kind: StoredArtifactKind,\n options: ListStoredArtifactsOptions = {}\n ): Promise<StoredArtifactSummary[]> {\n const bucket = await this.getBucket();\n const [files] = await bucket.getFiles({\n prefix: this.objectPath(kind, ''),\n });\n\n const summaries = await Promise.all(\n files\n .filter(\n (f) => f.name.endsWith('.json') && !f.name.endsWith('/latest.json')\n )\n .map(async (file) => {\n const [buffer] = await file.download();\n return toSummary(\n JSON.parse(buffer.toString('utf8')) as StoredEvalArtifact<unknown>\n );\n })\n );\n\n return summaries\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n .slice(0, options.limit);\n }\n\n private async getBucket(): Promise<GCSBucket> {\n if (!this.storage) {\n const moduleName = '@google-cloud/storage';\n let Storage: GCSStorageConstructor;\n try {\n const mod = (await import(moduleName)) as {\n Storage: GCSStorageConstructor;\n };\n Storage = mod.Storage;\n } catch (error) {\n throw new Error(\n 'GCS result storage requires the optional `@google-cloud/storage` package. ' +\n 'Install it and authenticate with Application Default Credentials via GOOGLE_APPLICATION_CREDENTIALS.\\n' +\n `Original error: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n this.storage = new Storage();\n }\n return this.storage.bucket(this.bucketName);\n }\n\n private objectPath(kind: StoredArtifactKind, filename: string): string {\n const parts = [this.prefix, KIND_DIRS[kind], filename].filter(Boolean);\n return parts.join('/');\n }\n}\n\nfunction toSummary(\n artifact: StoredEvalArtifact<unknown>\n): StoredArtifactSummary {\n return {\n kind: artifact.kind,\n id: artifact.id,\n createdAt: artifact.createdAt,\n metadata: artifact.metadata,\n };\n}\n\nfunction trimSlashes(value: string): string {\n return value.replace(/^\\/+|\\/+$/g, '');\n}\n\nfunction isMissingFileError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { code?: string }).code === 'ENOENT'\n );\n}\n","import type {\n Reporter,\n FullConfig,\n Suite,\n TestCase,\n TestResult,\n FullResult,\n} from '@playwright/test/reporter';\nimport { mkdir, writeFile, readdir, readFile, unlink, cp } from 'fs/promises';\nimport { join, resolve, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport type { MCPEvalReporterConfig } from './types.js';\nimport type { AuthType } from '../types/index.js';\nimport type {\n MCPEvalRunData,\n MCPEvalHistoricalSummary,\n MCPConformanceResultData,\n MCPServerCapabilitiesData,\n MCPVariantExperimentData,\n EvalCaseResult,\n MCPConformanceCheck,\n} from '../types/reporter.js';\nimport type { UsageMetrics } from '../types/index.js';\nimport { sumUsage } from '../utils/usageUtils.js';\nimport {\n createStoredEvalArtifact,\n resolveEvalResultStore,\n} from '../evals/resultStore.js';\n\ntype ResolvedReporterConfig = Required<\n Omit<\n MCPEvalReporterConfig,\n 'resultStore' | 'runId' | 'runMetadata' | 'redactStoredResponses'\n >\n> &\n Pick<\n MCPEvalReporterConfig,\n 'resultStore' | 'runId' | 'runMetadata' | 'redactStoredResponses'\n >;\n\n/**\n * Custom Playwright reporter for MCP eval results\n *\n * Generates HTML reports with historical tracking\n *\n * @example\n * ```typescript\n * // playwright.config.ts\n * export default defineConfig({\n * reporter: [\n * ['@gleanwork/mcp-server-tester/reporters/mcpReporter', {\n * outputDir: '.mcp-test-results',\n * historyLimit: 10\n * }]\n * ]\n * });\n * ```\n */\nexport default class MCPReporter implements Reporter {\n private config: ResolvedReporterConfig;\n private startTime: number = 0;\n private allResults: Array<EvalCaseResult> = [];\n private conformanceChecks: Array<MCPConformanceResultData> = [];\n private serverCapabilities: Array<MCPServerCapabilitiesData> = [];\n private variantExperiment: MCPVariantExperimentData | undefined;\n\n constructor(options: MCPEvalReporterConfig = {}) {\n this.config = {\n outputDir: options.outputDir ?? '.mcp-test-results',\n autoOpen: options.autoOpen ?? false,\n historyLimit: options.historyLimit ?? 10,\n quiet: options.quiet ?? false,\n includeAutoTracking: options.includeAutoTracking ?? true,\n resultStore: options.resultStore,\n runId: options.runId,\n runMetadata: options.runMetadata,\n // Default true to match the eval-runner store path. Stored artifacts\n // omit response bodies by default; opt in by passing\n // `redactStoredResponses: false` if you need full responses for\n // debugging or history comparison. Keeping this consistent across\n // both write paths prevents users from getting a mix of\n // redacted/non-redacted artifacts depending on which code path wrote\n // them.\n redactStoredResponses: options.redactStoredResponses ?? true,\n };\n }\n\n private log(message: string): void {\n if (!this.config.quiet) {\n console.log(message);\n }\n }\n\n private logError(message: string, error?: unknown): void {\n if (!this.config.quiet) {\n console.error(message, error ?? '');\n }\n }\n\n /**\n * Reads attachment content from either in-memory body (Playwright < 1.43)\n * or from the file path on disk (Playwright ≥ 1.43, which writes large\n * attachments to disk and exposes path instead of body).\n */\n private async getAttachmentContent(attachment: {\n body?: Buffer;\n path?: string;\n }): Promise<string | null> {\n if (attachment.body) return attachment.body.toString('utf-8');\n if (attachment.path) {\n try {\n return await readFile(attachment.path, 'utf-8');\n } catch {\n return null;\n }\n }\n return null;\n }\n\n async onBegin(_config: FullConfig, _suite: Suite): Promise<void> {\n this.startTime = Date.now();\n this.allResults = [];\n this.conformanceChecks = [];\n this.serverCapabilities = [];\n\n // Ensure output directory exists\n await mkdir(this.config.outputDir, { recursive: true });\n }\n\n async onTestEnd(test: TestCase, result: TestResult): Promise<void> {\n // Strategy 1: Extract MCP eval results from runEvalDataset() attachments\n const evalAttachment = result.attachments.find(\n (a) =>\n a.name === 'mcp-test-results' && a.contentType === 'application/json'\n );\n\n let hasEvalDataset = false;\n\n const evalContent = evalAttachment\n ? await this.getAttachmentContent(evalAttachment)\n : null;\n if (evalContent) {\n try {\n const evalResults = JSON.parse(evalContent) as {\n caseResults: Array<EvalCaseResult>;\n };\n\n // Trust the data from the attachment - evalRunner now includes\n // authType and project from the mcp fixture (Playwright is source of truth)\n this.allResults.push(...evalResults.caseResults);\n hasEvalDataset = true;\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse eval results from test \"${test.title}\":`,\n error\n );\n }\n }\n\n // Strategy 1b: Extract variant-experiment summary (runVariantExperiment)\n const experimentAttachment = result.attachments.find(\n (a) =>\n a.name === 'mcp-variant-experiment' &&\n a.contentType === 'application/json'\n );\n const experimentContent = experimentAttachment\n ? await this.getAttachmentContent(experimentAttachment)\n : null;\n if (experimentContent) {\n try {\n this.variantExperiment = JSON.parse(\n experimentContent\n ) as MCPVariantExperimentData;\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse variant-experiment attachment for \"${test.title}\":`,\n error\n );\n }\n }\n\n // Strategy 2: Extract conformance check results\n // These are created by runConformanceChecks() when testInfo is passed\n const conformanceAttachment = result.attachments.find(\n (a) =>\n a.name === 'mcp-conformance-checks' &&\n a.contentType === 'application/json'\n );\n\n const conformanceContent = conformanceAttachment\n ? await this.getAttachmentContent(conformanceAttachment)\n : null;\n if (conformanceContent) {\n try {\n const conformanceData = JSON.parse(conformanceContent) as {\n operation: string;\n pass: boolean;\n checks: MCPConformanceCheck[];\n serverInfo?: { name?: string; version?: string };\n toolCount: number;\n authType?: AuthType;\n project?: string;\n };\n\n // Only push if checks array is valid\n if (Array.isArray(conformanceData.checks)) {\n this.conformanceChecks.push({\n testTitle: test.title,\n pass: conformanceData.pass,\n checks: conformanceData.checks,\n serverInfo: conformanceData.serverInfo,\n toolCount: conformanceData.toolCount,\n authType: conformanceData.authType,\n project: conformanceData.project,\n });\n }\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse conformance check attachment for \"${test.title}\":`,\n error\n );\n }\n }\n\n // Strategy 2b: Extract server capabilities from mcp-list-tools attachments\n // These are created by createMCPFixture().listTools()\n const listToolsAttachment = result.attachments.find(\n (a) => a.name === 'mcp-list-tools' && a.contentType === 'application/json'\n );\n\n const listToolsContent = listToolsAttachment\n ? await this.getAttachmentContent(listToolsAttachment)\n : null;\n if (listToolsContent) {\n try {\n const listToolsData = JSON.parse(listToolsContent) as {\n operation: string;\n toolCount: number;\n tools: Array<{ name: string; description?: string }>;\n };\n\n // Only push if tools array is valid\n if (Array.isArray(listToolsData.tools)) {\n this.serverCapabilities.push({\n testTitle: test.title,\n tools: listToolsData.tools,\n toolCount: listToolsData.toolCount,\n // Note: authType and project are available from the mcp fixture\n // but not currently included in the listTools attachment\n });\n }\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse mcp-list-tools attachment for \"${test.title}\":`,\n error\n );\n }\n }\n\n // Strategy 3: Extract MCP tool calls from auto-tracking attachments\n // These are created by createMCPFixture()\n // Skip if:\n // - This test already has eval dataset results (to avoid duplicates)\n // - Auto-tracking is disabled in config\n if (hasEvalDataset || !this.config.includeAutoTracking) {\n return;\n }\n\n const mcpCallAttachments = result.attachments.filter(\n (a) =>\n a.name &&\n a.name.startsWith('mcp-call-') &&\n a.contentType === 'application/json'\n );\n\n for (const attachment of mcpCallAttachments) {\n const callContent = await this.getAttachmentContent(attachment);\n if (!callContent) continue;\n\n try {\n // Attachment now includes authType and project from the mcp fixture\n const callData = JSON.parse(callContent) as {\n operation: string;\n toolName: string;\n args: Record<string, unknown>;\n result: unknown;\n durationMs: number;\n isError: boolean;\n authType?: AuthType;\n project?: string;\n };\n\n const suiteName = test.parent?.title || 'Uncategorized Tests';\n const testPassed = result.status === 'passed';\n\n const syntheticResult: EvalCaseResult = {\n id: test.title,\n datasetName: suiteName,\n toolName: callData.toolName,\n source: 'test',\n pass: testPassed,\n response: callData.result,\n error: !testPassed ? 'Test failed' : undefined,\n expectations: {},\n authType: callData.authType,\n project: callData.project,\n durationMs: callData.durationMs,\n };\n\n this.allResults.push(syntheticResult);\n } catch (error) {\n this.logError(\n `[MCP Reporter] Failed to parse MCP call attachment \"${attachment.name}\":`,\n error\n );\n }\n }\n }\n\n async onEnd(_result: FullResult): Promise<void> {\n const endTime = Date.now();\n const durationMs = endTime - this.startTime;\n\n // Skip if no eval results collected\n if (this.allResults.length === 0) {\n this.log('[MCP Reporter] No MCP eval results found in test run');\n return;\n }\n\n // Build run data\n const runData = this.buildRunData(durationMs);\n\n // Load historical data\n const historical = await this.loadHistoricalData();\n\n // Add current run to historical\n historical.push({\n timestamp: runData.timestamp,\n total: runData.metrics.total,\n passed: runData.metrics.passed,\n failed: runData.metrics.failed,\n passRate: runData.metrics.passRate,\n durationMs: runData.durationMs,\n });\n\n // Save current run data\n await this.saveRunData(runData);\n\n // Clean up old runs\n await this.cleanupOldRuns();\n\n // Save current run to external storage, if configured. This is additive:\n // local report generation should still work if remote storage is down.\n await this.saveRunDataToStore(runData);\n\n // Generate report using copy + inject pattern\n const reportDir = join(this.config.outputDir, 'latest');\n await this.generateReport(runData, historical, reportDir);\n\n const reportPath = join(reportDir, 'index.html');\n this.log(`\\n[MCP Reporter] Report generated: ${reportPath}`);\n this.log(\n `[MCP Reporter] Results: ${runData.metrics.passed}/${runData.metrics.total} passed (${(runData.metrics.passRate * 100).toFixed(1)}%)`\n );\n\n // Auto-open browser if configured and not in CI\n if (this.config.autoOpen && !process.env.CI) {\n await this.openReport(reportPath);\n }\n }\n\n private async generateReport(\n runData: MCPEvalRunData,\n historical: Array<MCPEvalHistoricalSummary>,\n outputDir: string\n ): Promise<void> {\n // Get the UI dist path (relative to this file)\n // In ESM, we need to use import.meta.url\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const uiDistPath = join(__dirname, 'ui-dist');\n\n // Step 1: Copy pre-built UI template\n await mkdir(outputDir, { recursive: true });\n await cp(uiDistPath, outputDir, { recursive: true, force: true });\n\n // Step 2: Inject test data as JavaScript\n const dataScript = `window.MCP_EVAL_DATA = ${JSON.stringify(\n {\n runData,\n historical,\n },\n null,\n 2\n )};`;\n\n await writeFile(join(outputDir, 'data.js'), dataScript, 'utf-8');\n }\n\n private buildRunData(durationMs: number): MCPEvalRunData {\n const total = this.allResults.length;\n const datasetBreakdown: Record<string, number> = {};\n const expectationBreakdown = {\n exact: 0,\n schema: 0,\n textContains: 0,\n regex: 0,\n snapshot: 0,\n judge: 0,\n error: 0,\n size: 0,\n toolsTriggered: 0,\n toolCallCount: 0,\n };\n\n let passed = 0;\n for (const r of this.allResults) {\n if (r.pass) passed++;\n\n const datasetName = r.datasetName || 'Unknown Dataset';\n datasetBreakdown[datasetName] = (datasetBreakdown[datasetName] || 0) + 1;\n\n if (r.expectations.exact) expectationBreakdown.exact++;\n if (r.expectations.schema) expectationBreakdown.schema++;\n if (r.expectations.textContains) expectationBreakdown.textContains++;\n if (r.expectations.regex) expectationBreakdown.regex++;\n if (r.expectations.snapshot) expectationBreakdown.snapshot++;\n if (r.expectations.judge) expectationBreakdown.judge++;\n if (r.expectations.error) expectationBreakdown.error++;\n if (r.expectations.size) expectationBreakdown.size++;\n if (r.expectations.toolsTriggered) expectationBreakdown.toolsTriggered++;\n if (r.expectations.toolCallCount) expectationBreakdown.toolCallCount++;\n }\n\n const failed = total - passed;\n\n const totalHostUsage = this.allResults.reduce(\n (acc, r) => sumUsage(acc, r.hostUsage),\n undefined as UsageMetrics | undefined\n );\n\n return {\n timestamp: new Date().toISOString(),\n durationMs,\n environment: {\n ci: !!process.env.CI,\n node: process.version,\n platform: process.platform,\n },\n metrics: {\n total,\n passed,\n failed,\n passRate: passed / total,\n datasetBreakdown,\n expectationBreakdown,\n totalHostUsage,\n },\n results: this.allResults,\n conformanceChecks:\n this.conformanceChecks.length > 0 ? this.conformanceChecks : undefined,\n serverCapabilities:\n this.serverCapabilities.length > 0\n ? this.serverCapabilities\n : undefined,\n variantExperiment: this.variantExperiment,\n };\n }\n\n private async loadHistoricalData(): Promise<Array<MCPEvalHistoricalSummary>> {\n const storeHistorical = await this.loadHistoricalDataFromStore();\n if (storeHistorical) {\n return storeHistorical;\n }\n\n try {\n const files = await readdir(this.config.outputDir);\n const runFiles = files\n .filter((f) => f.startsWith('run-') && f.endsWith('.json'))\n .sort()\n .slice(-(this.config.historyLimit - 1)); // Keep most recent, leave room for current run\n\n const historical: Array<MCPEvalHistoricalSummary> = [];\n\n for (const file of runFiles) {\n try {\n const content = await readFile(\n join(this.config.outputDir, file),\n 'utf-8'\n );\n const runData = JSON.parse(content) as MCPEvalRunData;\n\n historical.push({\n timestamp: runData.timestamp,\n total: runData.metrics.total,\n passed: runData.metrics.passed,\n failed: runData.metrics.failed,\n passRate: runData.metrics.passRate,\n durationMs: runData.durationMs,\n });\n } catch (error) {\n this.logError(`[MCP Reporter] Failed to load ${file}:`, error);\n }\n }\n\n return historical;\n } catch {\n return [];\n }\n }\n\n private async saveRunData(runData: MCPEvalRunData): Promise<void> {\n const filename = `run-${runData.timestamp.replace(/:/g, '-')}.json`;\n const filepath = join(this.config.outputDir, filename);\n\n await writeFile(filepath, JSON.stringify(runData, null, 2), 'utf-8');\n }\n\n private async saveRunDataToStore(runData: MCPEvalRunData): Promise<void> {\n if (!this.config.resultStore) {\n return;\n }\n\n try {\n const store = resolveEvalResultStore(this.config.resultStore);\n await store.saveArtifact(\n createStoredEvalArtifact({\n kind: 'reporter-run',\n id: this.config.runId,\n data: this.config.redactStoredResponses\n ? redactResponses(runData)\n : runData,\n metadata: {\n ...(this.config.runMetadata ?? {}),\n },\n })\n );\n } catch (error) {\n this.logError(\n '[MCP Reporter] Failed to save run to result store:',\n error\n );\n }\n }\n\n private async loadHistoricalDataFromStore(): Promise<Array<MCPEvalHistoricalSummary> | null> {\n if (!this.config.resultStore) {\n return null;\n }\n\n try {\n const store = resolveEvalResultStore(this.config.resultStore);\n const summaries = await store.listArtifacts('reporter-run', {\n limit: this.config.historyLimit - 1,\n });\n\n const historical: Array<MCPEvalHistoricalSummary> = [];\n for (const summary of summaries.reverse()) {\n const artifact = await store.loadArtifact<MCPEvalRunData>(\n 'reporter-run',\n summary.id\n );\n historical.push(toHistoricalSummary(artifact.data));\n }\n return historical;\n } catch (error) {\n this.logError(\n '[MCP Reporter] Failed to load history from result store:',\n error\n );\n return null;\n }\n }\n\n private async cleanupOldRuns(): Promise<void> {\n try {\n const files = await readdir(this.config.outputDir);\n const runFiles = files\n .filter((f) => f.startsWith('run-') && f.endsWith('.json'))\n .sort()\n .reverse();\n\n // Keep only historyLimit most recent runs\n const toDelete = runFiles.slice(this.config.historyLimit);\n\n for (const file of toDelete) {\n await unlink(join(this.config.outputDir, file));\n }\n } catch (error) {\n this.logError('[MCP Reporter] Failed to cleanup old runs:', error);\n }\n }\n\n private async openReport(reportPath: string): Promise<void> {\n try {\n // Dynamic import to avoid bundling issues\n const { default: open } = await import('open');\n const absolutePath = resolve(reportPath);\n\n await open(absolutePath);\n this.log('[MCP Reporter] Opened report in browser');\n } catch (error) {\n this.logError('[MCP Reporter] Failed to open report:', error);\n this.log(`[MCP Reporter] Open manually: file://${resolve(reportPath)}`);\n }\n }\n}\n\nfunction toHistoricalSummary(\n runData: MCPEvalRunData\n): MCPEvalHistoricalSummary {\n return {\n timestamp: runData.timestamp,\n total: runData.metrics.total,\n passed: runData.metrics.passed,\n failed: runData.metrics.failed,\n passRate: runData.metrics.passRate,\n durationMs: runData.durationMs,\n };\n}\n\nfunction redactResponses<T>(value: T): T {\n return JSON.parse(\n JSON.stringify(value, (key, currentValue: unknown) =>\n key === 'response' ? undefined : currentValue\n )\n ) as T;\n}\n"]}