quality-rollup
Aggregates data quality status across a table, test suite, or organization. Shows pass rate, top failing cases, and status breakdown for quick assessment of quality issues.
Instructions
Aggregated DQ status across a scope (table / test suite / org-wide). Lists test cases under the scope, buckets them by current status (Success/Failed/Aborted/Queued), surfaces the top failing cases (with fqn/result/timestamp), reports passRatePct + most-recent-run timestamp. Replaces the recursive list-test-cases + list-test-case-results walk LLMs do for 'what's broken in ?'. Provide ONE of entityLink / tableFqn / testSuiteId / testSuiteFqn (or none for org-wide).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| entityLink | No | OM entityLink, e.g. '<#E::table::svc.db.schema.orders>' | |
| tableFqn | No | Convenience: build entityLink from a table fully-qualified name | |
| testSuiteId | No | Test Suite UUID — restricts to cases belonging to this suite | |
| testSuiteFqn | No | Test Suite FQN — resolved to UUID before listing | |
| limit | No | Maximum test cases to inspect (default 100, max 500) | |
| topFailingLimit | No | Number of top failing cases to surface in the response (default 5) |
Implementation Reference
- src/tools/quality-rollup.ts:49-138 (handler)The main handler function for the quality-rollup tool. Accepts scope parameters (entityLink, tableFqn, testSuiteId, testSuiteFqn) to filter test cases, fetches them from OpenMetadata's /dataQuality/testCases endpoint, buckets them by status, computes pass rate, and surfaces top failing cases sorted by recency.
export async function qualityRollup(params: z.infer<typeof qualityRollupSchema>) { const caveats: string[] = []; // Build the entityLink from tableFqn convenience. let entityLink = params.entityLink; if (!entityLink && params.tableFqn) { entityLink = `<#E::table::${params.tableFqn}>`; } // Resolve suite FQN → UUID when needed. let testSuiteId = params.testSuiteId; if (!testSuiteId && params.testSuiteFqn) { try { const suite = await omClient.get<{ id?: string }>( `/dataQuality/testSuites/name/${encodeURIComponent(params.testSuiteFqn)}`, ); testSuiteId = suite.id; } catch (err) { caveats.push(`testSuite lookup failed: ${err instanceof Error ? err.message : String(err)}`); } } const query: Record<string, string | number | boolean | undefined> = { limit: params.limit, fields: "testSuite,testCaseStatus,testCaseResult", }; if (entityLink) query.entityLink = entityLink; if (testSuiteId) query.testSuiteId = testSuiteId; const { listing } = await aggregate( { listing: () => omClient.get<{ data?: TestCaseSummary[]; paging?: { total?: number } }>( "/dataQuality/testCases", query, ), }, caveats, ); const cases = listing?.data ?? []; const total = listing?.paging?.total ?? cases.length; // Bucket by current status. const counts: Record<string, number> = {}; let mostRecentTs: number | null = null; for (const c of cases) { const s = statusOf(c); counts[s] = (counts[s] ?? 0) + 1; const ts = c.testCaseResult?.timestamp; if (typeof ts === "number" && (mostRecentTs == null || ts > mostRecentTs)) { mostRecentTs = ts; } } // Top failing cases (Failed first, then Aborted, ordered by recency). const failing = cases .filter((c) => /Fail|Abort|Error/i.test(statusOf(c))) .sort((a, b) => (b.testCaseResult?.timestamp ?? 0) - (a.testCaseResult?.timestamp ?? 0)) .slice(0, params.topFailingLimit) .map((c) => ({ name: c.name ?? null, fqn: c.fullyQualifiedName ?? null, status: statusOf(c), result: c.testCaseResult?.result ?? null, lastRun: c.testCaseResult?.timestamp ?? null, testSuiteFqn: c.testSuite?.fullyQualifiedName ?? null, })); const success = (counts["Success"] ?? 0); const passRate = cases.length > 0 ? Math.round((success / cases.length) * 1000) / 10 : null; let scopeLabel: string; if (entityLink) scopeLabel = `entityLink=${entityLink}`; else if (testSuiteId) scopeLabel = `testSuite=${params.testSuiteFqn ?? testSuiteId}`; else scopeLabel = "org-wide"; return { scope: { entityLink, testSuiteId, testSuiteFqn: params.testSuiteFqn ?? null, tableFqn: params.tableFqn ?? null, label: scopeLabel }, summary: { casesInspected: cases.length, casesAvailable: total, truncated: total > cases.length, counts, passRatePct: passRate, mostRecentRunTs: mostRecentTs, }, topFailing: failing, caveats, }; } - src/tools/quality-rollup.ts:21-34 (schema)Zod schema defining the input parameters for quality-rollup: entityLink, tableFqn, testSuiteId, testSuiteFqn (scope filters), limit (max 500), and topFailingLimit (max 50).
export const qualityRollupSchema = z.object({ entityLink: z.string().optional() .describe("OM entityLink, e.g. '<#E::table::svc.db.schema.orders>'"), tableFqn: z.string().optional() .describe("Convenience: build entityLink from a table fully-qualified name"), testSuiteId: z.string().optional() .describe("Test Suite UUID — restricts to cases belonging to this suite"), testSuiteFqn: z.string().optional() .describe("Test Suite FQN — resolved to UUID before listing"), limit: z.coerce.number().int().min(1).max(500).optional().default(100) .describe("Maximum test cases to inspect (default 100, max 500)"), topFailingLimit: z.coerce.number().int().min(1).max(50).optional().default(5) .describe("Number of top failing cases to surface in the response (default 5)"), }).describe(SCOPE_DESCRIPTION); - src/index.ts:488-490 (registration)Registration of the quality-rollup tool in the MCP server using the tool() function, with its schema shape and handler wrapped via wrapToolHandler.
tool("quality-rollup", "Aggregated DQ status across a scope (table / test suite / org-wide). Lists test cases under the scope, buckets them by current status (Success/Failed/Aborted/Queued), surfaces the top failing cases (with fqn/result/timestamp), reports passRatePct + most-recent-run timestamp. Replaces the recursive list-test-cases + list-test-case-results walk LLMs do for 'what's broken in <scope>?'. Provide ONE of entityLink / tableFqn / testSuiteId / testSuiteFqn (or none for org-wide).", qualityRollupSchema.shape, wrapToolHandler(qualityRollup)); - src/tools/quality-rollup.ts:45-47 (helper)Helper function that extracts the test case status from a TestCaseSummary, falling back through testCaseResult.testCaseStatus, then testCaseStatus, then 'Unknown'.
function statusOf(tc: TestCaseSummary): string { return tc.testCaseResult?.testCaseStatus ?? tc.testCaseStatus ?? "Unknown"; } - src/index.ts:36-36 (registration)Import of qualityRollup and its schema from the quality-rollup module into the main server entry point.
import { qualityRollupSchema, qualityRollup, runTestSuiteSchema, runTestSuite } from "./tools/quality-rollup.js";