@gaffer-sh/mcp 0.3.0 → 0.4.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/dist/index.js +648 -131
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -341,6 +341,112 @@ var GafferApiClient = class _GafferApiClient {
|
|
|
341
341
|
}
|
|
342
342
|
);
|
|
343
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* Get coverage summary for a project
|
|
346
|
+
*
|
|
347
|
+
* @param options - Query options
|
|
348
|
+
* @param options.projectId - The project ID (required)
|
|
349
|
+
* @param options.days - Analysis period in days (default: 30)
|
|
350
|
+
* @returns Coverage summary with trends and lowest coverage files
|
|
351
|
+
*/
|
|
352
|
+
async getCoverageSummary(options) {
|
|
353
|
+
if (!this.isUserToken()) {
|
|
354
|
+
throw new Error("getCoverageSummary requires a user API Key (gaf_).");
|
|
355
|
+
}
|
|
356
|
+
if (!options.projectId) {
|
|
357
|
+
throw new Error("projectId is required");
|
|
358
|
+
}
|
|
359
|
+
return this.request(
|
|
360
|
+
`/user/projects/${options.projectId}/coverage-summary`,
|
|
361
|
+
{
|
|
362
|
+
...options.days && { days: options.days }
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Get coverage files for a project with filtering
|
|
368
|
+
*
|
|
369
|
+
* @param options - Query options
|
|
370
|
+
* @param options.projectId - The project ID (required)
|
|
371
|
+
* @param options.filePath - Filter to specific file path
|
|
372
|
+
* @param options.minCoverage - Minimum coverage percentage
|
|
373
|
+
* @param options.maxCoverage - Maximum coverage percentage
|
|
374
|
+
* @param options.limit - Maximum number of results
|
|
375
|
+
* @param options.offset - Pagination offset
|
|
376
|
+
* @param options.sortBy - Sort by 'path' or 'coverage'
|
|
377
|
+
* @param options.sortOrder - Sort order 'asc' or 'desc'
|
|
378
|
+
* @returns List of files with coverage data
|
|
379
|
+
*/
|
|
380
|
+
async getCoverageFiles(options) {
|
|
381
|
+
if (!this.isUserToken()) {
|
|
382
|
+
throw new Error("getCoverageFiles requires a user API Key (gaf_).");
|
|
383
|
+
}
|
|
384
|
+
if (!options.projectId) {
|
|
385
|
+
throw new Error("projectId is required");
|
|
386
|
+
}
|
|
387
|
+
return this.request(
|
|
388
|
+
`/user/projects/${options.projectId}/coverage/files`,
|
|
389
|
+
{
|
|
390
|
+
...options.filePath && { filePath: options.filePath },
|
|
391
|
+
...options.minCoverage !== void 0 && { minCoverage: options.minCoverage },
|
|
392
|
+
...options.maxCoverage !== void 0 && { maxCoverage: options.maxCoverage },
|
|
393
|
+
...options.limit && { limit: options.limit },
|
|
394
|
+
...options.offset && { offset: options.offset },
|
|
395
|
+
...options.sortBy && { sortBy: options.sortBy },
|
|
396
|
+
...options.sortOrder && { sortOrder: options.sortOrder }
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Get risk areas (files with low coverage AND test failures)
|
|
402
|
+
*
|
|
403
|
+
* @param options - Query options
|
|
404
|
+
* @param options.projectId - The project ID (required)
|
|
405
|
+
* @param options.days - Analysis period in days (default: 30)
|
|
406
|
+
* @param options.coverageThreshold - Include files below this coverage (default: 80)
|
|
407
|
+
* @returns List of risk areas sorted by risk score
|
|
408
|
+
*/
|
|
409
|
+
async getCoverageRiskAreas(options) {
|
|
410
|
+
if (!this.isUserToken()) {
|
|
411
|
+
throw new Error("getCoverageRiskAreas requires a user API Key (gaf_).");
|
|
412
|
+
}
|
|
413
|
+
if (!options.projectId) {
|
|
414
|
+
throw new Error("projectId is required");
|
|
415
|
+
}
|
|
416
|
+
return this.request(
|
|
417
|
+
`/user/projects/${options.projectId}/coverage/risk-areas`,
|
|
418
|
+
{
|
|
419
|
+
...options.days && { days: options.days },
|
|
420
|
+
...options.coverageThreshold !== void 0 && { coverageThreshold: options.coverageThreshold }
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get a browser-navigable URL for viewing a test report
|
|
426
|
+
*
|
|
427
|
+
* @param options - Query options
|
|
428
|
+
* @param options.projectId - The project ID (required)
|
|
429
|
+
* @param options.testRunId - The test run ID (required)
|
|
430
|
+
* @param options.filename - Specific file to open (default: index.html)
|
|
431
|
+
* @returns URL with signed token for browser access
|
|
432
|
+
*/
|
|
433
|
+
async getReportBrowserUrl(options) {
|
|
434
|
+
if (!this.isUserToken()) {
|
|
435
|
+
throw new Error("getReportBrowserUrl requires a user API Key (gaf_).");
|
|
436
|
+
}
|
|
437
|
+
if (!options.projectId) {
|
|
438
|
+
throw new Error("projectId is required");
|
|
439
|
+
}
|
|
440
|
+
if (!options.testRunId) {
|
|
441
|
+
throw new Error("testRunId is required");
|
|
442
|
+
}
|
|
443
|
+
return this.request(
|
|
444
|
+
`/user/projects/${options.projectId}/reports/${options.testRunId}/browser-url`,
|
|
445
|
+
{
|
|
446
|
+
...options.filename && { filename: options.filename }
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
}
|
|
344
450
|
};
|
|
345
451
|
|
|
346
452
|
// src/tools/compare-test-metrics.ts
|
|
@@ -443,26 +549,206 @@ Use cases:
|
|
|
443
549
|
Tip: Use get_test_history first to find the commit SHAs or test run IDs you want to compare.`
|
|
444
550
|
};
|
|
445
551
|
|
|
446
|
-
// src/tools/
|
|
552
|
+
// src/tools/find-uncovered-failure-areas.ts
|
|
447
553
|
import { z as z2 } from "zod";
|
|
554
|
+
var findUncoveredFailureAreasInputSchema = {
|
|
555
|
+
projectId: z2.string().describe("Project ID to analyze. Required. Use list_projects to find project IDs."),
|
|
556
|
+
days: z2.number().int().min(1).max(365).optional().describe("Number of days to analyze for test failures (default: 30)"),
|
|
557
|
+
coverageThreshold: z2.number().min(0).max(100).optional().describe("Include files with coverage below this percentage (default: 80)")
|
|
558
|
+
};
|
|
559
|
+
var findUncoveredFailureAreasOutputSchema = {
|
|
560
|
+
hasCoverage: z2.boolean(),
|
|
561
|
+
hasTestResults: z2.boolean(),
|
|
562
|
+
riskAreas: z2.array(z2.object({
|
|
563
|
+
filePath: z2.string(),
|
|
564
|
+
coverage: z2.number(),
|
|
565
|
+
failureCount: z2.number(),
|
|
566
|
+
riskScore: z2.number(),
|
|
567
|
+
testNames: z2.array(z2.string())
|
|
568
|
+
})),
|
|
569
|
+
message: z2.string().optional()
|
|
570
|
+
};
|
|
571
|
+
async function executeFindUncoveredFailureAreas(client, input) {
|
|
572
|
+
const response = await client.getCoverageRiskAreas({
|
|
573
|
+
projectId: input.projectId,
|
|
574
|
+
days: input.days,
|
|
575
|
+
coverageThreshold: input.coverageThreshold
|
|
576
|
+
});
|
|
577
|
+
return {
|
|
578
|
+
hasCoverage: response.hasCoverage,
|
|
579
|
+
hasTestResults: response.hasTestResults,
|
|
580
|
+
riskAreas: response.riskAreas,
|
|
581
|
+
message: response.message
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
var findUncoveredFailureAreasMetadata = {
|
|
585
|
+
name: "find_uncovered_failure_areas",
|
|
586
|
+
title: "Find Uncovered Failure Areas",
|
|
587
|
+
description: `Find areas of code that have both low coverage AND test failures.
|
|
588
|
+
|
|
589
|
+
This cross-references test failures with coverage data to identify high-risk
|
|
590
|
+
areas in your codebase that need attention. Files are ranked by a "risk score"
|
|
591
|
+
calculated as: (100 - coverage%) \xD7 failureCount.
|
|
592
|
+
|
|
593
|
+
When using a user API Key (gaf_), you must provide a projectId.
|
|
594
|
+
Use list_projects first to find available project IDs.
|
|
595
|
+
|
|
596
|
+
Parameters:
|
|
597
|
+
- projectId: The project to analyze (required)
|
|
598
|
+
- days: Analysis period for test failures (default: 30)
|
|
599
|
+
- coverageThreshold: Include files below this coverage % (default: 80)
|
|
600
|
+
|
|
601
|
+
Returns:
|
|
602
|
+
- List of risk areas sorted by risk score (highest risk first)
|
|
603
|
+
- Each area includes: file path, coverage %, failure count, risk score, test names
|
|
604
|
+
|
|
605
|
+
Use this to prioritize which parts of your codebase need better test coverage.`
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
// src/tools/get-coverage-for-file.ts
|
|
609
|
+
import { z as z3 } from "zod";
|
|
610
|
+
var getCoverageForFileInputSchema = {
|
|
611
|
+
projectId: z3.string().describe("Project ID to get coverage for. Required. Use list_projects to find project IDs."),
|
|
612
|
+
filePath: z3.string().describe("File path to get coverage for. Can be exact path or partial match.")
|
|
613
|
+
};
|
|
614
|
+
var getCoverageForFileOutputSchema = {
|
|
615
|
+
hasCoverage: z3.boolean(),
|
|
616
|
+
files: z3.array(z3.object({
|
|
617
|
+
path: z3.string(),
|
|
618
|
+
lines: z3.object({
|
|
619
|
+
covered: z3.number(),
|
|
620
|
+
total: z3.number(),
|
|
621
|
+
percentage: z3.number()
|
|
622
|
+
}),
|
|
623
|
+
branches: z3.object({
|
|
624
|
+
covered: z3.number(),
|
|
625
|
+
total: z3.number(),
|
|
626
|
+
percentage: z3.number()
|
|
627
|
+
}),
|
|
628
|
+
functions: z3.object({
|
|
629
|
+
covered: z3.number(),
|
|
630
|
+
total: z3.number(),
|
|
631
|
+
percentage: z3.number()
|
|
632
|
+
})
|
|
633
|
+
})),
|
|
634
|
+
message: z3.string().optional()
|
|
635
|
+
};
|
|
636
|
+
async function executeGetCoverageForFile(client, input) {
|
|
637
|
+
const response = await client.getCoverageFiles({
|
|
638
|
+
projectId: input.projectId,
|
|
639
|
+
filePath: input.filePath,
|
|
640
|
+
limit: 10
|
|
641
|
+
// Return up to 10 matching files
|
|
642
|
+
});
|
|
643
|
+
return {
|
|
644
|
+
hasCoverage: response.hasCoverage,
|
|
645
|
+
files: response.files.map((f) => ({
|
|
646
|
+
path: f.path,
|
|
647
|
+
lines: f.lines,
|
|
648
|
+
branches: f.branches,
|
|
649
|
+
functions: f.functions
|
|
650
|
+
})),
|
|
651
|
+
message: response.message
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
var getCoverageForFileMetadata = {
|
|
655
|
+
name: "get_coverage_for_file",
|
|
656
|
+
title: "Get Coverage for File",
|
|
657
|
+
description: `Get coverage metrics for a specific file or files matching a path pattern.
|
|
658
|
+
|
|
659
|
+
When using a user API Key (gaf_), you must provide a projectId.
|
|
660
|
+
Use list_projects first to find available project IDs.
|
|
661
|
+
|
|
662
|
+
Parameters:
|
|
663
|
+
- projectId: The project to query (required)
|
|
664
|
+
- filePath: File path to search for (exact or partial match)
|
|
665
|
+
|
|
666
|
+
Returns:
|
|
667
|
+
- Line coverage (covered/total/percentage)
|
|
668
|
+
- Branch coverage (covered/total/percentage)
|
|
669
|
+
- Function coverage (covered/total/percentage)
|
|
670
|
+
|
|
671
|
+
Use this to check coverage for specific files or find files by path pattern.`
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
// src/tools/get-coverage-summary.ts
|
|
675
|
+
import { z as z4 } from "zod";
|
|
676
|
+
var getCoverageSummaryInputSchema = {
|
|
677
|
+
projectId: z4.string().describe("Project ID to get coverage for. Required. Use list_projects to find project IDs."),
|
|
678
|
+
days: z4.number().int().min(1).max(365).optional().describe("Number of days to analyze for trends (default: 30)")
|
|
679
|
+
};
|
|
680
|
+
var getCoverageSummaryOutputSchema = {
|
|
681
|
+
hasCoverage: z4.boolean(),
|
|
682
|
+
current: z4.object({
|
|
683
|
+
lines: z4.number(),
|
|
684
|
+
branches: z4.number(),
|
|
685
|
+
functions: z4.number()
|
|
686
|
+
}).optional(),
|
|
687
|
+
trend: z4.object({
|
|
688
|
+
direction: z4.enum(["up", "down", "stable"]),
|
|
689
|
+
change: z4.number()
|
|
690
|
+
}).optional(),
|
|
691
|
+
totalReports: z4.number(),
|
|
692
|
+
latestReportDate: z4.string().nullable().optional(),
|
|
693
|
+
lowestCoverageFiles: z4.array(z4.object({
|
|
694
|
+
path: z4.string(),
|
|
695
|
+
coverage: z4.number()
|
|
696
|
+
})).optional(),
|
|
697
|
+
message: z4.string().optional()
|
|
698
|
+
};
|
|
699
|
+
async function executeGetCoverageSummary(client, input) {
|
|
700
|
+
const response = await client.getCoverageSummary({
|
|
701
|
+
projectId: input.projectId,
|
|
702
|
+
days: input.days
|
|
703
|
+
});
|
|
704
|
+
return {
|
|
705
|
+
hasCoverage: response.hasCoverage,
|
|
706
|
+
current: response.current,
|
|
707
|
+
trend: response.trend,
|
|
708
|
+
totalReports: response.totalReports,
|
|
709
|
+
latestReportDate: response.latestReportDate,
|
|
710
|
+
lowestCoverageFiles: response.lowestCoverageFiles,
|
|
711
|
+
message: response.message
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
var getCoverageSummaryMetadata = {
|
|
715
|
+
name: "get_coverage_summary",
|
|
716
|
+
title: "Get Coverage Summary",
|
|
717
|
+
description: `Get the coverage metrics summary for a project.
|
|
718
|
+
|
|
719
|
+
When using a user API Key (gaf_), you must provide a projectId.
|
|
720
|
+
Use list_projects first to find available project IDs.
|
|
721
|
+
|
|
722
|
+
Returns:
|
|
723
|
+
- Current coverage percentages (lines, branches, functions)
|
|
724
|
+
- Trend direction (up, down, stable) and change amount
|
|
725
|
+
- Total number of coverage reports
|
|
726
|
+
- Latest report date
|
|
727
|
+
- Top 5 files with lowest coverage
|
|
728
|
+
|
|
729
|
+
Use this to understand your project's overall test coverage health.`
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
// src/tools/get-flaky-tests.ts
|
|
733
|
+
import { z as z5 } from "zod";
|
|
448
734
|
var getFlakyTestsInputSchema = {
|
|
449
|
-
projectId:
|
|
450
|
-
threshold:
|
|
451
|
-
limit:
|
|
452
|
-
days:
|
|
735
|
+
projectId: z5.string().optional().describe("Project ID to get flaky tests for. Required when using a user API Key (gaf_). Use list_projects to find project IDs."),
|
|
736
|
+
threshold: z5.number().min(0).max(1).optional().describe("Minimum flip rate to be considered flaky (0-1, default: 0.1 = 10%)"),
|
|
737
|
+
limit: z5.number().int().min(1).max(100).optional().describe("Maximum number of flaky tests to return (default: 50)"),
|
|
738
|
+
days: z5.number().int().min(1).max(365).optional().describe("Analysis period in days (default: 30)")
|
|
453
739
|
};
|
|
454
740
|
var getFlakyTestsOutputSchema = {
|
|
455
|
-
flakyTests:
|
|
456
|
-
name:
|
|
457
|
-
flipRate:
|
|
458
|
-
flipCount:
|
|
459
|
-
totalRuns:
|
|
460
|
-
lastSeen:
|
|
741
|
+
flakyTests: z5.array(z5.object({
|
|
742
|
+
name: z5.string(),
|
|
743
|
+
flipRate: z5.number(),
|
|
744
|
+
flipCount: z5.number(),
|
|
745
|
+
totalRuns: z5.number(),
|
|
746
|
+
lastSeen: z5.string()
|
|
461
747
|
})),
|
|
462
|
-
summary:
|
|
463
|
-
threshold:
|
|
464
|
-
totalFlaky:
|
|
465
|
-
period:
|
|
748
|
+
summary: z5.object({
|
|
749
|
+
threshold: z5.number(),
|
|
750
|
+
totalFlaky: z5.number(),
|
|
751
|
+
period: z5.number()
|
|
466
752
|
})
|
|
467
753
|
};
|
|
468
754
|
async function executeGetFlakyTests(client, input) {
|
|
@@ -502,22 +788,22 @@ specific tests are flaky and need investigation.`
|
|
|
502
788
|
};
|
|
503
789
|
|
|
504
790
|
// src/tools/get-project-health.ts
|
|
505
|
-
import { z as
|
|
791
|
+
import { z as z6 } from "zod";
|
|
506
792
|
var getProjectHealthInputSchema = {
|
|
507
|
-
projectId:
|
|
508
|
-
days:
|
|
793
|
+
projectId: z6.string().optional().describe("Project ID to get health for. Required when using a user API Key (gaf_). Use list_projects to find project IDs."),
|
|
794
|
+
days: z6.number().int().min(1).max(365).optional().describe("Number of days to analyze (default: 30)")
|
|
509
795
|
};
|
|
510
796
|
var getProjectHealthOutputSchema = {
|
|
511
|
-
projectName:
|
|
512
|
-
healthScore:
|
|
513
|
-
passRate:
|
|
514
|
-
testRunCount:
|
|
515
|
-
flakyTestCount:
|
|
516
|
-
trend:
|
|
517
|
-
period:
|
|
518
|
-
days:
|
|
519
|
-
start:
|
|
520
|
-
end:
|
|
797
|
+
projectName: z6.string(),
|
|
798
|
+
healthScore: z6.number(),
|
|
799
|
+
passRate: z6.number().nullable(),
|
|
800
|
+
testRunCount: z6.number(),
|
|
801
|
+
flakyTestCount: z6.number(),
|
|
802
|
+
trend: z6.enum(["up", "down", "stable"]),
|
|
803
|
+
period: z6.object({
|
|
804
|
+
days: z6.number(),
|
|
805
|
+
start: z6.string(),
|
|
806
|
+
end: z6.string()
|
|
521
807
|
})
|
|
522
808
|
};
|
|
523
809
|
async function executeGetProjectHealth(client, input) {
|
|
@@ -553,23 +839,77 @@ Returns:
|
|
|
553
839
|
Use this to understand the current state of your test suite.`
|
|
554
840
|
};
|
|
555
841
|
|
|
842
|
+
// src/tools/get-report-browser-url.ts
|
|
843
|
+
import { z as z7 } from "zod";
|
|
844
|
+
var getReportBrowserUrlInputSchema = {
|
|
845
|
+
projectId: z7.string().describe("Project ID the test run belongs to. Required. Use list_projects to find project IDs."),
|
|
846
|
+
testRunId: z7.string().describe("The test run ID to get the report URL for. Use list_test_runs to find test run IDs."),
|
|
847
|
+
filename: z7.string().optional().describe("Specific file to open (default: index.html or first HTML file)")
|
|
848
|
+
};
|
|
849
|
+
var getReportBrowserUrlOutputSchema = {
|
|
850
|
+
url: z7.string(),
|
|
851
|
+
filename: z7.string(),
|
|
852
|
+
testRunId: z7.string(),
|
|
853
|
+
expiresAt: z7.string(),
|
|
854
|
+
expiresInSeconds: z7.number()
|
|
855
|
+
};
|
|
856
|
+
async function executeGetReportBrowserUrl(client, input) {
|
|
857
|
+
const response = await client.getReportBrowserUrl({
|
|
858
|
+
projectId: input.projectId,
|
|
859
|
+
testRunId: input.testRunId,
|
|
860
|
+
filename: input.filename
|
|
861
|
+
});
|
|
862
|
+
return {
|
|
863
|
+
url: response.url,
|
|
864
|
+
filename: response.filename,
|
|
865
|
+
testRunId: response.testRunId,
|
|
866
|
+
expiresAt: response.expiresAt,
|
|
867
|
+
expiresInSeconds: response.expiresInSeconds
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
var getReportBrowserUrlMetadata = {
|
|
871
|
+
name: "get_report_browser_url",
|
|
872
|
+
title: "Get Report Browser URL",
|
|
873
|
+
description: `Get a browser-navigable URL for viewing a test report (Playwright, Vitest, etc.).
|
|
874
|
+
|
|
875
|
+
Returns a signed URL that can be opened directly in a browser without requiring
|
|
876
|
+
the user to log in. The URL expires after 30 minutes for security.
|
|
877
|
+
|
|
878
|
+
When using a user API Key (gaf_), you must provide both projectId and testRunId.
|
|
879
|
+
Use list_projects and list_test_runs to find available IDs.
|
|
880
|
+
|
|
881
|
+
Parameters:
|
|
882
|
+
- projectId: The project the test run belongs to (required)
|
|
883
|
+
- testRunId: The test run to view (required)
|
|
884
|
+
- filename: Specific file to open (optional, defaults to index.html)
|
|
885
|
+
|
|
886
|
+
Returns:
|
|
887
|
+
- url: Browser-navigable URL with signed token
|
|
888
|
+
- filename: The file being accessed
|
|
889
|
+
- expiresAt: ISO timestamp when the URL expires
|
|
890
|
+
- expiresInSeconds: Time until expiration
|
|
891
|
+
|
|
892
|
+
The returned URL can be shared with users who need to view the report.
|
|
893
|
+
Note: URLs expire after 30 minutes for security.`
|
|
894
|
+
};
|
|
895
|
+
|
|
556
896
|
// src/tools/get-report.ts
|
|
557
|
-
import { z as
|
|
897
|
+
import { z as z8 } from "zod";
|
|
558
898
|
var getReportInputSchema = {
|
|
559
|
-
testRunId:
|
|
899
|
+
testRunId: z8.string().describe("The test run ID to get report files for. Use list_test_runs to find test run IDs.")
|
|
560
900
|
};
|
|
561
901
|
var getReportOutputSchema = {
|
|
562
|
-
testRunId:
|
|
563
|
-
projectId:
|
|
564
|
-
projectName:
|
|
565
|
-
resultSchema:
|
|
566
|
-
files:
|
|
567
|
-
filename:
|
|
568
|
-
size:
|
|
569
|
-
contentType:
|
|
570
|
-
downloadUrl:
|
|
902
|
+
testRunId: z8.string(),
|
|
903
|
+
projectId: z8.string(),
|
|
904
|
+
projectName: z8.string(),
|
|
905
|
+
resultSchema: z8.string().optional(),
|
|
906
|
+
files: z8.array(z8.object({
|
|
907
|
+
filename: z8.string(),
|
|
908
|
+
size: z8.number(),
|
|
909
|
+
contentType: z8.string(),
|
|
910
|
+
downloadUrl: z8.string()
|
|
571
911
|
})),
|
|
572
|
-
urlExpiresInSeconds:
|
|
912
|
+
urlExpiresInSeconds: z8.number().optional()
|
|
573
913
|
};
|
|
574
914
|
async function executeGetReport(client, input) {
|
|
575
915
|
const response = await client.getReport(input.testRunId);
|
|
@@ -627,29 +967,29 @@ Use cases:
|
|
|
627
967
|
};
|
|
628
968
|
|
|
629
969
|
// src/tools/get-slowest-tests.ts
|
|
630
|
-
import { z as
|
|
970
|
+
import { z as z9 } from "zod";
|
|
631
971
|
var getSlowestTestsInputSchema = {
|
|
632
|
-
projectId:
|
|
633
|
-
days:
|
|
634
|
-
limit:
|
|
635
|
-
framework:
|
|
636
|
-
branch:
|
|
972
|
+
projectId: z9.string().describe("Project ID to get slowest tests for. Required. Use list_projects to find project IDs."),
|
|
973
|
+
days: z9.number().int().min(1).max(365).optional().describe("Analysis period in days (default: 30)"),
|
|
974
|
+
limit: z9.number().int().min(1).max(100).optional().describe("Maximum number of tests to return (default: 20)"),
|
|
975
|
+
framework: z9.string().optional().describe('Filter by test framework (e.g., "playwright", "vitest", "jest")'),
|
|
976
|
+
branch: z9.string().optional().describe('Filter by git branch name (e.g., "main", "develop")')
|
|
637
977
|
};
|
|
638
978
|
var getSlowestTestsOutputSchema = {
|
|
639
|
-
slowestTests:
|
|
640
|
-
name:
|
|
641
|
-
fullName:
|
|
642
|
-
filePath:
|
|
643
|
-
framework:
|
|
644
|
-
avgDurationMs:
|
|
645
|
-
p95DurationMs:
|
|
646
|
-
runCount:
|
|
979
|
+
slowestTests: z9.array(z9.object({
|
|
980
|
+
name: z9.string(),
|
|
981
|
+
fullName: z9.string(),
|
|
982
|
+
filePath: z9.string().optional(),
|
|
983
|
+
framework: z9.string().optional(),
|
|
984
|
+
avgDurationMs: z9.number(),
|
|
985
|
+
p95DurationMs: z9.number(),
|
|
986
|
+
runCount: z9.number()
|
|
647
987
|
})),
|
|
648
|
-
summary:
|
|
649
|
-
projectId:
|
|
650
|
-
projectName:
|
|
651
|
-
period:
|
|
652
|
-
totalReturned:
|
|
988
|
+
summary: z9.object({
|
|
989
|
+
projectId: z9.string(),
|
|
990
|
+
projectName: z9.string(),
|
|
991
|
+
period: z9.number(),
|
|
992
|
+
totalReturned: z9.number()
|
|
653
993
|
})
|
|
654
994
|
};
|
|
655
995
|
async function executeGetSlowestTests(client, input) {
|
|
@@ -707,28 +1047,28 @@ Use cases:
|
|
|
707
1047
|
};
|
|
708
1048
|
|
|
709
1049
|
// src/tools/get-test-history.ts
|
|
710
|
-
import { z as
|
|
1050
|
+
import { z as z10 } from "zod";
|
|
711
1051
|
var getTestHistoryInputSchema = {
|
|
712
|
-
projectId:
|
|
713
|
-
testName:
|
|
714
|
-
filePath:
|
|
715
|
-
limit:
|
|
1052
|
+
projectId: z10.string().optional().describe("Project ID to get test history for. Required when using a user API Key (gaf_). Use list_projects to find project IDs."),
|
|
1053
|
+
testName: z10.string().optional().describe("Exact test name to search for"),
|
|
1054
|
+
filePath: z10.string().optional().describe("File path containing the test"),
|
|
1055
|
+
limit: z10.number().int().min(1).max(100).optional().describe("Maximum number of results (default: 20)")
|
|
716
1056
|
};
|
|
717
1057
|
var getTestHistoryOutputSchema = {
|
|
718
|
-
history:
|
|
719
|
-
testRunId:
|
|
720
|
-
createdAt:
|
|
721
|
-
branch:
|
|
722
|
-
commitSha:
|
|
723
|
-
status:
|
|
724
|
-
durationMs:
|
|
725
|
-
message:
|
|
1058
|
+
history: z10.array(z10.object({
|
|
1059
|
+
testRunId: z10.string(),
|
|
1060
|
+
createdAt: z10.string(),
|
|
1061
|
+
branch: z10.string().optional(),
|
|
1062
|
+
commitSha: z10.string().optional(),
|
|
1063
|
+
status: z10.enum(["passed", "failed", "skipped", "pending"]),
|
|
1064
|
+
durationMs: z10.number(),
|
|
1065
|
+
message: z10.string().optional()
|
|
726
1066
|
})),
|
|
727
|
-
summary:
|
|
728
|
-
totalRuns:
|
|
729
|
-
passedRuns:
|
|
730
|
-
failedRuns:
|
|
731
|
-
passRate:
|
|
1067
|
+
summary: z10.object({
|
|
1068
|
+
totalRuns: z10.number(),
|
|
1069
|
+
passedRuns: z10.number(),
|
|
1070
|
+
failedRuns: z10.number(),
|
|
1071
|
+
passRate: z10.number().nullable()
|
|
732
1072
|
})
|
|
733
1073
|
};
|
|
734
1074
|
async function executeGetTestHistory(client, input) {
|
|
@@ -783,35 +1123,35 @@ Use this to investigate flaky tests or understand test stability.`
|
|
|
783
1123
|
};
|
|
784
1124
|
|
|
785
1125
|
// src/tools/get-test-run-details.ts
|
|
786
|
-
import { z as
|
|
1126
|
+
import { z as z11 } from "zod";
|
|
787
1127
|
var getTestRunDetailsInputSchema = {
|
|
788
|
-
testRunId:
|
|
789
|
-
projectId:
|
|
790
|
-
status:
|
|
791
|
-
limit:
|
|
792
|
-
offset:
|
|
1128
|
+
testRunId: z11.string().describe("The test run ID to get details for. Use list_test_runs to find test run IDs."),
|
|
1129
|
+
projectId: z11.string().describe("Project ID the test run belongs to. Required when using a user API Key (gaf_). Use list_projects to find project IDs."),
|
|
1130
|
+
status: z11.enum(["passed", "failed", "skipped"]).optional().describe("Filter tests by status. Returns only tests matching this status."),
|
|
1131
|
+
limit: z11.number().int().min(1).max(500).optional().describe("Maximum number of tests to return (default: 100, max: 500)"),
|
|
1132
|
+
offset: z11.number().int().min(0).optional().describe("Number of tests to skip for pagination (default: 0)")
|
|
793
1133
|
};
|
|
794
1134
|
var getTestRunDetailsOutputSchema = {
|
|
795
|
-
testRunId:
|
|
796
|
-
summary:
|
|
797
|
-
passed:
|
|
798
|
-
failed:
|
|
799
|
-
skipped:
|
|
800
|
-
total:
|
|
1135
|
+
testRunId: z11.string(),
|
|
1136
|
+
summary: z11.object({
|
|
1137
|
+
passed: z11.number(),
|
|
1138
|
+
failed: z11.number(),
|
|
1139
|
+
skipped: z11.number(),
|
|
1140
|
+
total: z11.number()
|
|
801
1141
|
}),
|
|
802
|
-
tests:
|
|
803
|
-
name:
|
|
804
|
-
fullName:
|
|
805
|
-
status:
|
|
806
|
-
durationMs:
|
|
807
|
-
filePath:
|
|
808
|
-
error:
|
|
1142
|
+
tests: z11.array(z11.object({
|
|
1143
|
+
name: z11.string(),
|
|
1144
|
+
fullName: z11.string(),
|
|
1145
|
+
status: z11.enum(["passed", "failed", "skipped"]),
|
|
1146
|
+
durationMs: z11.number().nullable(),
|
|
1147
|
+
filePath: z11.string().nullable(),
|
|
1148
|
+
error: z11.string().nullable()
|
|
809
1149
|
})),
|
|
810
|
-
pagination:
|
|
811
|
-
total:
|
|
812
|
-
limit:
|
|
813
|
-
offset:
|
|
814
|
-
hasMore:
|
|
1150
|
+
pagination: z11.object({
|
|
1151
|
+
total: z11.number(),
|
|
1152
|
+
limit: z11.number(),
|
|
1153
|
+
offset: z11.number(),
|
|
1154
|
+
hasMore: z11.boolean()
|
|
815
1155
|
})
|
|
816
1156
|
};
|
|
817
1157
|
async function executeGetTestRunDetails(client, input) {
|
|
@@ -866,24 +1206,101 @@ Note: For aggregate analytics like flaky test detection or duration trends,
|
|
|
866
1206
|
use get_test_history, get_flaky_tests, or get_slowest_tests instead.`
|
|
867
1207
|
};
|
|
868
1208
|
|
|
1209
|
+
// src/tools/get-untested-files.ts
|
|
1210
|
+
import { z as z12 } from "zod";
|
|
1211
|
+
var getUntestedFilesInputSchema = {
|
|
1212
|
+
projectId: z12.string().describe("Project ID to analyze. Required. Use list_projects to find project IDs."),
|
|
1213
|
+
maxCoverage: z12.number().min(0).max(100).optional().describe('Maximum coverage percentage to include (default: 10 for "untested")'),
|
|
1214
|
+
limit: z12.number().int().min(1).max(100).optional().describe("Maximum number of files to return (default: 20)")
|
|
1215
|
+
};
|
|
1216
|
+
var getUntestedFilesOutputSchema = {
|
|
1217
|
+
hasCoverage: z12.boolean(),
|
|
1218
|
+
files: z12.array(z12.object({
|
|
1219
|
+
path: z12.string(),
|
|
1220
|
+
lines: z12.object({
|
|
1221
|
+
covered: z12.number(),
|
|
1222
|
+
total: z12.number(),
|
|
1223
|
+
percentage: z12.number()
|
|
1224
|
+
}),
|
|
1225
|
+
branches: z12.object({
|
|
1226
|
+
covered: z12.number(),
|
|
1227
|
+
total: z12.number(),
|
|
1228
|
+
percentage: z12.number()
|
|
1229
|
+
}),
|
|
1230
|
+
functions: z12.object({
|
|
1231
|
+
covered: z12.number(),
|
|
1232
|
+
total: z12.number(),
|
|
1233
|
+
percentage: z12.number()
|
|
1234
|
+
})
|
|
1235
|
+
})),
|
|
1236
|
+
totalCount: z12.number(),
|
|
1237
|
+
message: z12.string().optional()
|
|
1238
|
+
};
|
|
1239
|
+
async function executeGetUntestedFiles(client, input) {
|
|
1240
|
+
const maxCoverage = input.maxCoverage ?? 10;
|
|
1241
|
+
const limit = input.limit ?? 20;
|
|
1242
|
+
const response = await client.getCoverageFiles({
|
|
1243
|
+
projectId: input.projectId,
|
|
1244
|
+
maxCoverage,
|
|
1245
|
+
limit,
|
|
1246
|
+
sortBy: "coverage",
|
|
1247
|
+
sortOrder: "asc"
|
|
1248
|
+
// Lowest coverage first
|
|
1249
|
+
});
|
|
1250
|
+
return {
|
|
1251
|
+
hasCoverage: response.hasCoverage,
|
|
1252
|
+
files: response.files.map((f) => ({
|
|
1253
|
+
path: f.path,
|
|
1254
|
+
lines: f.lines,
|
|
1255
|
+
branches: f.branches,
|
|
1256
|
+
functions: f.functions
|
|
1257
|
+
})),
|
|
1258
|
+
totalCount: response.pagination.total,
|
|
1259
|
+
message: response.message
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
var getUntestedFilesMetadata = {
|
|
1263
|
+
name: "get_untested_files",
|
|
1264
|
+
title: "Get Untested Files",
|
|
1265
|
+
description: `Get files with little or no test coverage.
|
|
1266
|
+
|
|
1267
|
+
Returns files sorted by coverage percentage (lowest first), filtered
|
|
1268
|
+
to only include files below a coverage threshold.
|
|
1269
|
+
|
|
1270
|
+
When using a user API Key (gaf_), you must provide a projectId.
|
|
1271
|
+
Use list_projects first to find available project IDs.
|
|
1272
|
+
|
|
1273
|
+
Parameters:
|
|
1274
|
+
- projectId: The project to analyze (required)
|
|
1275
|
+
- maxCoverage: Include files with coverage at or below this % (default: 10)
|
|
1276
|
+
- limit: Maximum number of files to return (default: 20, max: 100)
|
|
1277
|
+
|
|
1278
|
+
Returns:
|
|
1279
|
+
- List of files sorted by coverage (lowest first)
|
|
1280
|
+
- Each file includes line/branch/function coverage metrics
|
|
1281
|
+
- Total count of files matching the criteria
|
|
1282
|
+
|
|
1283
|
+
Use this to find files that need tests added.`
|
|
1284
|
+
};
|
|
1285
|
+
|
|
869
1286
|
// src/tools/list-projects.ts
|
|
870
|
-
import { z as
|
|
1287
|
+
import { z as z13 } from "zod";
|
|
871
1288
|
var listProjectsInputSchema = {
|
|
872
|
-
organizationId:
|
|
873
|
-
limit:
|
|
1289
|
+
organizationId: z13.string().optional().describe("Filter by organization ID (optional)"),
|
|
1290
|
+
limit: z13.number().int().min(1).max(100).optional().describe("Maximum number of projects to return (default: 50)")
|
|
874
1291
|
};
|
|
875
1292
|
var listProjectsOutputSchema = {
|
|
876
|
-
projects:
|
|
877
|
-
id:
|
|
878
|
-
name:
|
|
879
|
-
description:
|
|
880
|
-
organization:
|
|
881
|
-
id:
|
|
882
|
-
name:
|
|
883
|
-
slug:
|
|
1293
|
+
projects: z13.array(z13.object({
|
|
1294
|
+
id: z13.string(),
|
|
1295
|
+
name: z13.string(),
|
|
1296
|
+
description: z13.string().nullable().optional(),
|
|
1297
|
+
organization: z13.object({
|
|
1298
|
+
id: z13.string(),
|
|
1299
|
+
name: z13.string(),
|
|
1300
|
+
slug: z13.string()
|
|
884
1301
|
})
|
|
885
1302
|
})),
|
|
886
|
-
total:
|
|
1303
|
+
total: z13.number()
|
|
887
1304
|
};
|
|
888
1305
|
async function executeListProjects(client, input) {
|
|
889
1306
|
const response = await client.listProjects({
|
|
@@ -912,28 +1329,28 @@ Requires a user API Key (gaf_). Get one from Account Settings in the Gaffer dash
|
|
|
912
1329
|
};
|
|
913
1330
|
|
|
914
1331
|
// src/tools/list-test-runs.ts
|
|
915
|
-
import { z as
|
|
1332
|
+
import { z as z14 } from "zod";
|
|
916
1333
|
var listTestRunsInputSchema = {
|
|
917
|
-
projectId:
|
|
918
|
-
commitSha:
|
|
919
|
-
branch:
|
|
920
|
-
status:
|
|
921
|
-
limit:
|
|
1334
|
+
projectId: z14.string().optional().describe("Project ID to list test runs for. Required when using a user API Key (gaf_). Use list_projects to find project IDs."),
|
|
1335
|
+
commitSha: z14.string().optional().describe("Filter by commit SHA (exact or prefix match)"),
|
|
1336
|
+
branch: z14.string().optional().describe("Filter by branch name"),
|
|
1337
|
+
status: z14.enum(["passed", "failed"]).optional().describe('Filter by status: "passed" (no failures) or "failed" (has failures)'),
|
|
1338
|
+
limit: z14.number().int().min(1).max(100).optional().describe("Maximum number of test runs to return (default: 20)")
|
|
922
1339
|
};
|
|
923
1340
|
var listTestRunsOutputSchema = {
|
|
924
|
-
testRuns:
|
|
925
|
-
id:
|
|
926
|
-
commitSha:
|
|
927
|
-
branch:
|
|
928
|
-
passedCount:
|
|
929
|
-
failedCount:
|
|
930
|
-
skippedCount:
|
|
931
|
-
totalCount:
|
|
932
|
-
createdAt:
|
|
1341
|
+
testRuns: z14.array(z14.object({
|
|
1342
|
+
id: z14.string(),
|
|
1343
|
+
commitSha: z14.string().optional(),
|
|
1344
|
+
branch: z14.string().optional(),
|
|
1345
|
+
passedCount: z14.number(),
|
|
1346
|
+
failedCount: z14.number(),
|
|
1347
|
+
skippedCount: z14.number(),
|
|
1348
|
+
totalCount: z14.number(),
|
|
1349
|
+
createdAt: z14.string()
|
|
933
1350
|
})),
|
|
934
|
-
pagination:
|
|
935
|
-
total:
|
|
936
|
-
hasMore:
|
|
1351
|
+
pagination: z14.object({
|
|
1352
|
+
total: z14.number(),
|
|
1353
|
+
hasMore: z14.boolean()
|
|
937
1354
|
})
|
|
938
1355
|
};
|
|
939
1356
|
async function executeListTestRuns(client, input) {
|
|
@@ -1213,6 +1630,106 @@ async function main() {
|
|
|
1213
1630
|
}
|
|
1214
1631
|
}
|
|
1215
1632
|
);
|
|
1633
|
+
server.registerTool(
|
|
1634
|
+
getCoverageSummaryMetadata.name,
|
|
1635
|
+
{
|
|
1636
|
+
title: getCoverageSummaryMetadata.title,
|
|
1637
|
+
description: getCoverageSummaryMetadata.description,
|
|
1638
|
+
inputSchema: getCoverageSummaryInputSchema,
|
|
1639
|
+
outputSchema: getCoverageSummaryOutputSchema
|
|
1640
|
+
},
|
|
1641
|
+
async (input) => {
|
|
1642
|
+
try {
|
|
1643
|
+
const output = await executeGetCoverageSummary(client, input);
|
|
1644
|
+
return {
|
|
1645
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
1646
|
+
structuredContent: output
|
|
1647
|
+
};
|
|
1648
|
+
} catch (error) {
|
|
1649
|
+
return handleToolError(getCoverageSummaryMetadata.name, error);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
);
|
|
1653
|
+
server.registerTool(
|
|
1654
|
+
getCoverageForFileMetadata.name,
|
|
1655
|
+
{
|
|
1656
|
+
title: getCoverageForFileMetadata.title,
|
|
1657
|
+
description: getCoverageForFileMetadata.description,
|
|
1658
|
+
inputSchema: getCoverageForFileInputSchema,
|
|
1659
|
+
outputSchema: getCoverageForFileOutputSchema
|
|
1660
|
+
},
|
|
1661
|
+
async (input) => {
|
|
1662
|
+
try {
|
|
1663
|
+
const output = await executeGetCoverageForFile(client, input);
|
|
1664
|
+
return {
|
|
1665
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
1666
|
+
structuredContent: output
|
|
1667
|
+
};
|
|
1668
|
+
} catch (error) {
|
|
1669
|
+
return handleToolError(getCoverageForFileMetadata.name, error);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
);
|
|
1673
|
+
server.registerTool(
|
|
1674
|
+
findUncoveredFailureAreasMetadata.name,
|
|
1675
|
+
{
|
|
1676
|
+
title: findUncoveredFailureAreasMetadata.title,
|
|
1677
|
+
description: findUncoveredFailureAreasMetadata.description,
|
|
1678
|
+
inputSchema: findUncoveredFailureAreasInputSchema,
|
|
1679
|
+
outputSchema: findUncoveredFailureAreasOutputSchema
|
|
1680
|
+
},
|
|
1681
|
+
async (input) => {
|
|
1682
|
+
try {
|
|
1683
|
+
const output = await executeFindUncoveredFailureAreas(client, input);
|
|
1684
|
+
return {
|
|
1685
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
1686
|
+
structuredContent: output
|
|
1687
|
+
};
|
|
1688
|
+
} catch (error) {
|
|
1689
|
+
return handleToolError(findUncoveredFailureAreasMetadata.name, error);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
);
|
|
1693
|
+
server.registerTool(
|
|
1694
|
+
getUntestedFilesMetadata.name,
|
|
1695
|
+
{
|
|
1696
|
+
title: getUntestedFilesMetadata.title,
|
|
1697
|
+
description: getUntestedFilesMetadata.description,
|
|
1698
|
+
inputSchema: getUntestedFilesInputSchema,
|
|
1699
|
+
outputSchema: getUntestedFilesOutputSchema
|
|
1700
|
+
},
|
|
1701
|
+
async (input) => {
|
|
1702
|
+
try {
|
|
1703
|
+
const output = await executeGetUntestedFiles(client, input);
|
|
1704
|
+
return {
|
|
1705
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
1706
|
+
structuredContent: output
|
|
1707
|
+
};
|
|
1708
|
+
} catch (error) {
|
|
1709
|
+
return handleToolError(getUntestedFilesMetadata.name, error);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
);
|
|
1713
|
+
server.registerTool(
|
|
1714
|
+
getReportBrowserUrlMetadata.name,
|
|
1715
|
+
{
|
|
1716
|
+
title: getReportBrowserUrlMetadata.title,
|
|
1717
|
+
description: getReportBrowserUrlMetadata.description,
|
|
1718
|
+
inputSchema: getReportBrowserUrlInputSchema,
|
|
1719
|
+
outputSchema: getReportBrowserUrlOutputSchema
|
|
1720
|
+
},
|
|
1721
|
+
async (input) => {
|
|
1722
|
+
try {
|
|
1723
|
+
const output = await executeGetReportBrowserUrl(client, input);
|
|
1724
|
+
return {
|
|
1725
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
1726
|
+
structuredContent: output
|
|
1727
|
+
};
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
return handleToolError(getReportBrowserUrlMetadata.name, error);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
);
|
|
1216
1733
|
const transport = new StdioServerTransport();
|
|
1217
1734
|
await server.connect(transport);
|
|
1218
1735
|
}
|
package/package.json
CHANGED