@kryptosai/mcp-observatory 0.3.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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +228 -0
  3. package/dist/src/adapters/http.d.ts +5 -0
  4. package/dist/src/adapters/http.js +54 -0
  5. package/dist/src/adapters/http.js.map +1 -0
  6. package/dist/src/adapters/local-process.d.ts +19 -0
  7. package/dist/src/adapters/local-process.js +63 -0
  8. package/dist/src/adapters/local-process.js.map +1 -0
  9. package/dist/src/checks/base.d.ts +28 -0
  10. package/dist/src/checks/base.js +42 -0
  11. package/dist/src/checks/base.js.map +1 -0
  12. package/dist/src/checks/list-check.d.ts +16 -0
  13. package/dist/src/checks/list-check.js +78 -0
  14. package/dist/src/checks/list-check.js.map +1 -0
  15. package/dist/src/checks/prompts.d.ts +2 -0
  16. package/dist/src/checks/prompts.js +11 -0
  17. package/dist/src/checks/prompts.js.map +1 -0
  18. package/dist/src/checks/resources.d.ts +2 -0
  19. package/dist/src/checks/resources.js +163 -0
  20. package/dist/src/checks/resources.js.map +1 -0
  21. package/dist/src/checks/tools-invoke.d.ts +2 -0
  22. package/dist/src/checks/tools-invoke.js +107 -0
  23. package/dist/src/checks/tools-invoke.js.map +1 -0
  24. package/dist/src/checks/tools.d.ts +2 -0
  25. package/dist/src/checks/tools.js +12 -0
  26. package/dist/src/checks/tools.js.map +1 -0
  27. package/dist/src/cli.d.ts +2 -0
  28. package/dist/src/cli.js +266 -0
  29. package/dist/src/cli.js.map +1 -0
  30. package/dist/src/diff.d.ts +2 -0
  31. package/dist/src/diff.js +98 -0
  32. package/dist/src/diff.js.map +1 -0
  33. package/dist/src/discovery.d.ts +7 -0
  34. package/dist/src/discovery.js +116 -0
  35. package/dist/src/discovery.js.map +1 -0
  36. package/dist/src/index.d.ts +9 -0
  37. package/dist/src/index.js +10 -0
  38. package/dist/src/index.js.map +1 -0
  39. package/dist/src/reporters/common.d.ts +7 -0
  40. package/dist/src/reporters/common.js +63 -0
  41. package/dist/src/reporters/common.js.map +1 -0
  42. package/dist/src/reporters/html.d.ts +2 -0
  43. package/dist/src/reporters/html.js +228 -0
  44. package/dist/src/reporters/html.js.map +1 -0
  45. package/dist/src/reporters/markdown.d.ts +2 -0
  46. package/dist/src/reporters/markdown.js +231 -0
  47. package/dist/src/reporters/markdown.js.map +1 -0
  48. package/dist/src/reporters/terminal.d.ts +2 -0
  49. package/dist/src/reporters/terminal.js +110 -0
  50. package/dist/src/reporters/terminal.js.map +1 -0
  51. package/dist/src/runner.d.ts +5 -0
  52. package/dist/src/runner.js +138 -0
  53. package/dist/src/runner.js.map +1 -0
  54. package/dist/src/schema-diff.d.ts +6 -0
  55. package/dist/src/schema-diff.js +73 -0
  56. package/dist/src/schema-diff.js.map +1 -0
  57. package/dist/src/storage.d.ts +6 -0
  58. package/dist/src/storage.js +32 -0
  59. package/dist/src/storage.js.map +1 -0
  60. package/dist/src/types.d.ts +120 -0
  61. package/dist/src/types.js +5 -0
  62. package/dist/src/types.js.map +1 -0
  63. package/dist/src/utils/failure-diagnosis.d.ts +2 -0
  64. package/dist/src/utils/failure-diagnosis.js +90 -0
  65. package/dist/src/utils/failure-diagnosis.js.map +1 -0
  66. package/dist/src/utils/ids.d.ts +2 -0
  67. package/dist/src/utils/ids.js +13 -0
  68. package/dist/src/utils/ids.js.map +1 -0
  69. package/dist/src/utils/schema-stub.d.ts +14 -0
  70. package/dist/src/utils/schema-stub.js +61 -0
  71. package/dist/src/utils/schema-stub.js.map +1 -0
  72. package/dist/src/validate.d.ts +4 -0
  73. package/dist/src/validate.js +97 -0
  74. package/dist/src/validate.js.map +1 -0
  75. package/dist/src/version.d.ts +1 -0
  76. package/dist/src/version.js +21 -0
  77. package/dist/src/version.js.map +1 -0
  78. package/package.json +76 -0
  79. package/schemas/diff-artifact.schema.json +132 -0
  80. package/schemas/run-artifact.schema.json +245 -0
@@ -0,0 +1,98 @@
1
+ import { diffSchemas } from "./schema-diff.js";
2
+ import { SCHEMA_VERSION, STATUS_RANK } from "./types.js";
3
+ function toEntry(base, head) {
4
+ const source = head ?? base;
5
+ if (source === undefined) {
6
+ throw new Error("Expected at least one check result when building a diff entry.");
7
+ }
8
+ return {
9
+ id: source.id,
10
+ capability: source.capability,
11
+ fromStatus: base?.status,
12
+ toStatus: head?.status,
13
+ message: head?.message ??
14
+ base?.message ??
15
+ "No additional diagnostic message was recorded."
16
+ };
17
+ }
18
+ export function diffArtifacts(base, head) {
19
+ const baseChecks = new Map(base.checks.map((check) => [check.id, check]));
20
+ const headChecks = new Map(head.checks.map((check) => [check.id, check]));
21
+ const checkIds = Array.from(new Set([...baseChecks.keys(), ...headChecks.keys()]));
22
+ const regressions = [];
23
+ const recoveries = [];
24
+ const unchanged = [];
25
+ const added = [];
26
+ const removed = [];
27
+ for (const checkId of checkIds) {
28
+ const baseCheck = baseChecks.get(checkId);
29
+ const headCheck = headChecks.get(checkId);
30
+ if (baseCheck === undefined && headCheck !== undefined) {
31
+ added.push(toEntry(undefined, headCheck));
32
+ continue;
33
+ }
34
+ if (baseCheck !== undefined && headCheck === undefined) {
35
+ removed.push(toEntry(baseCheck, undefined));
36
+ continue;
37
+ }
38
+ if (baseCheck === undefined || headCheck === undefined) {
39
+ continue;
40
+ }
41
+ if (baseCheck.status === headCheck.status) {
42
+ unchanged.push(toEntry(baseCheck, headCheck));
43
+ continue;
44
+ }
45
+ if (STATUS_RANK[headCheck.status] < STATUS_RANK[baseCheck.status]) {
46
+ regressions.push(toEntry(baseCheck, headCheck));
47
+ continue;
48
+ }
49
+ recoveries.push(toEntry(baseCheck, headCheck));
50
+ }
51
+ // Schema drift detection
52
+ const schemaDrift = [];
53
+ for (const checkId of checkIds) {
54
+ const baseCheck = baseChecks.get(checkId);
55
+ const headCheck = headChecks.get(checkId);
56
+ if (baseCheck === undefined || headCheck === undefined)
57
+ continue;
58
+ const baseSchemas = extractSchemas(baseCheck);
59
+ const headSchemas = extractSchemas(headCheck);
60
+ if (baseSchemas === undefined && headSchemas === undefined)
61
+ continue;
62
+ const drift = diffSchemas(checkId, baseSchemas ?? {}, headSchemas ?? {});
63
+ schemaDrift.push(...drift);
64
+ }
65
+ const summary = {
66
+ regressions: regressions.length,
67
+ recoveries: recoveries.length,
68
+ unchanged: unchanged.length,
69
+ added: added.length,
70
+ removed: removed.length,
71
+ schemaDriftCount: schemaDrift.length > 0 ? schemaDrift.length : undefined,
72
+ gate: regressions.length > 0 ? "fail" : "pass"
73
+ };
74
+ return {
75
+ artifactType: "diff",
76
+ schemaVersion: SCHEMA_VERSION,
77
+ gate: summary.gate,
78
+ baseRunId: base.runId,
79
+ headRunId: head.runId,
80
+ createdAt: new Date().toISOString(),
81
+ summary,
82
+ regressions,
83
+ recoveries,
84
+ unchanged,
85
+ added,
86
+ removed,
87
+ schemaDrift: schemaDrift.length > 0 ? schemaDrift : undefined,
88
+ };
89
+ }
90
+ function extractSchemas(check) {
91
+ for (const ev of check.evidence) {
92
+ if (ev.schemas !== undefined) {
93
+ return ev.schemas;
94
+ }
95
+ }
96
+ return undefined;
97
+ }
98
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { TargetConfig } from "./types.js";
2
+ interface DiscoveredTarget {
3
+ source: string;
4
+ config: TargetConfig;
5
+ }
6
+ export declare function scanForTargets(configPath?: string): Promise<DiscoveredTarget[]>;
7
+ export {};
@@ -0,0 +1,116 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ function defaultConfigPaths() {
5
+ const home = os.homedir();
6
+ const paths = [];
7
+ // Claude Code user config
8
+ paths.push(path.join(home, ".claude.json"));
9
+ // Claude Desktop (macOS)
10
+ if (process.platform === "darwin") {
11
+ paths.push(path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"));
12
+ }
13
+ // Claude Desktop (Windows)
14
+ if (process.platform === "win32") {
15
+ const appData = process.env["APPDATA"];
16
+ if (appData) {
17
+ paths.push(path.join(appData, "Claude", "claude_desktop_config.json"));
18
+ }
19
+ }
20
+ // Current directory configs
21
+ paths.push(path.join(process.cwd(), ".claude.json"));
22
+ paths.push(path.join(process.cwd(), ".mcp.json"));
23
+ return paths;
24
+ }
25
+ async function tryReadJson(filePath) {
26
+ try {
27
+ const content = await readFile(filePath, "utf8");
28
+ return JSON.parse(content);
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ function extractTargets(data, source) {
35
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
36
+ return [];
37
+ }
38
+ const obj = data;
39
+ const servers = obj["mcpServers"];
40
+ if (typeof servers !== "object" || servers === null || Array.isArray(servers)) {
41
+ return [];
42
+ }
43
+ const results = [];
44
+ for (const [name, entry] of Object.entries(servers)) {
45
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
46
+ continue;
47
+ }
48
+ const serverEntry = entry;
49
+ const command = serverEntry["command"];
50
+ const args = serverEntry["args"];
51
+ // HTTP/SSE server (has url field)
52
+ const url = serverEntry["url"];
53
+ if (typeof url === "string" && url.length > 0) {
54
+ results.push({
55
+ source,
56
+ config: {
57
+ targetId: name,
58
+ adapter: "http",
59
+ url,
60
+ authToken: typeof serverEntry["authToken"] === "string" ? serverEntry["authToken"] : undefined,
61
+ timeoutMs: 15_000,
62
+ }
63
+ });
64
+ continue;
65
+ }
66
+ // Local process server (has command field)
67
+ if (typeof command !== "string" || command.length === 0) {
68
+ continue;
69
+ }
70
+ const parsedArgs = [];
71
+ if (Array.isArray(args)) {
72
+ for (const arg of args) {
73
+ if (typeof arg === "string") {
74
+ parsedArgs.push(arg);
75
+ }
76
+ }
77
+ }
78
+ const env = typeof serverEntry["env"] === "object" && serverEntry["env"] !== null && !Array.isArray(serverEntry["env"])
79
+ ? serverEntry["env"]
80
+ : undefined;
81
+ results.push({
82
+ source,
83
+ config: {
84
+ targetId: name,
85
+ adapter: "local-process",
86
+ command,
87
+ args: parsedArgs,
88
+ env,
89
+ timeoutMs: 15_000,
90
+ }
91
+ });
92
+ }
93
+ return results;
94
+ }
95
+ export async function scanForTargets(configPath) {
96
+ const paths = configPath ? [configPath] : defaultConfigPaths();
97
+ const allTargets = [];
98
+ const seen = new Set();
99
+ for (const p of paths) {
100
+ const data = await tryReadJson(p);
101
+ if (data === undefined)
102
+ continue;
103
+ const targets = extractTargets(data, p);
104
+ for (const target of targets) {
105
+ const key = target.config.adapter === "http"
106
+ ? target.config.url
107
+ : `${target.config.command} ${target.config.args.join(" ")}`;
108
+ if (!seen.has(key)) {
109
+ seen.add(key);
110
+ allTargets.push(target);
111
+ }
112
+ }
113
+ }
114
+ return allTargets;
115
+ }
116
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export { diffArtifacts } from "./diff.js";
2
+ export { scanForTargets } from "./discovery.js";
3
+ export { renderHtml } from "./reporters/html.js";
4
+ export { renderMarkdown } from "./reporters/markdown.js";
5
+ export { renderTerminal } from "./reporters/terminal.js";
6
+ export { runTarget, type RunOptions } from "./runner.js";
7
+ export { defaultRunsDirectory, readArtifact, writeRunArtifact } from "./storage.js";
8
+ export * from "./types.js";
9
+ export { validateDiffArtifact, validateRunArtifact, validateTargetConfig } from "./validate.js";
@@ -0,0 +1,10 @@
1
+ export { diffArtifacts } from "./diff.js";
2
+ export { scanForTargets } from "./discovery.js";
3
+ export { renderHtml } from "./reporters/html.js";
4
+ export { renderMarkdown } from "./reporters/markdown.js";
5
+ export { renderTerminal } from "./reporters/terminal.js";
6
+ export { runTarget } from "./runner.js";
7
+ export { defaultRunsDirectory, readArtifact, writeRunArtifact } from "./storage.js";
8
+ export * from "./types.js";
9
+ export { validateDiffArtifact, validateRunArtifact, validateTargetConfig } from "./validate.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAmB,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,gBAAgB,EACjB,MAAM,cAAc,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { CheckResult, CheckStatus, RunArtifact } from "../types.js";
2
+ export declare function sortChecksByActionability(checks: CheckResult[]): CheckResult[];
3
+ export declare function findChecksByStatus(checks: CheckResult[], status: CheckStatus): CheckResult[];
4
+ export declare function describeCheckList(checks: CheckResult[]): string;
5
+ export declare function focusLabel(status: CheckStatus): string;
6
+ export declare function previewList(values: string[] | undefined, limit?: number): string;
7
+ export declare function recommendRunNextStep(artifact: RunArtifact): string;
@@ -0,0 +1,63 @@
1
+ import { STATUS_RANK } from "../types.js";
2
+ const STATUS_PRIORITY = Object.fromEntries(Object.entries(STATUS_RANK).map(([status, rank]) => [status, -rank]));
3
+ export function sortChecksByActionability(checks) {
4
+ return [...checks].sort((left, right) => {
5
+ const priorityDiff = STATUS_PRIORITY[left.status] - STATUS_PRIORITY[right.status];
6
+ if (priorityDiff !== 0) {
7
+ return priorityDiff;
8
+ }
9
+ return left.id.localeCompare(right.id);
10
+ });
11
+ }
12
+ export function findChecksByStatus(checks, status) {
13
+ return checks.filter((check) => check.status === status);
14
+ }
15
+ export function describeCheckList(checks) {
16
+ return checks.length > 0 ? checks.map((check) => check.id).join(", ") : "none";
17
+ }
18
+ export function focusLabel(status) {
19
+ switch (status) {
20
+ case "fail":
21
+ return "act now";
22
+ case "partial":
23
+ case "flaky":
24
+ return "review";
25
+ case "skipped":
26
+ return "inspect startup";
27
+ case "unsupported":
28
+ return "confirm intent";
29
+ case "pass":
30
+ return "healthy";
31
+ }
32
+ }
33
+ export function previewList(values, limit = 5) {
34
+ if (values === undefined || values.length === 0) {
35
+ return "none";
36
+ }
37
+ if (values.length <= limit) {
38
+ return values.join(", ");
39
+ }
40
+ return `${values.slice(0, limit).join(", ")} (+${values.length - limit} more)`;
41
+ }
42
+ export function recommendRunNextStep(artifact) {
43
+ if (artifact.fatalError !== undefined) {
44
+ return "Run the target command manually, compare stderr with the diagnosis below, and only raise timeoutMs if startup is genuinely slow.";
45
+ }
46
+ const failingChecks = findChecksByStatus(artifact.checks, "fail");
47
+ if (failingChecks.length > 0) {
48
+ return `Start with the failing checks: ${describeCheckList(failingChecks)}.`;
49
+ }
50
+ const partialChecks = [
51
+ ...findChecksByStatus(artifact.checks, "partial"),
52
+ ...findChecksByStatus(artifact.checks, "flaky")
53
+ ];
54
+ if (partialChecks.length > 0) {
55
+ return `Review the caveated checks next: ${describeCheckList(partialChecks)}.`;
56
+ }
57
+ const unsupportedChecks = findChecksByStatus(artifact.checks, "unsupported");
58
+ if (unsupportedChecks.length > 0) {
59
+ return `Confirm that unsupported capabilities are intentional for this target: ${describeCheckList(unsupportedChecks)}.`;
60
+ }
61
+ return "Save this run artifact and diff it against the next meaningful server or package change.";
62
+ }
63
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/reporters/common.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,eAAe,GAAgC,MAAM,CAAC,WAAW,CACrE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CACtC,CAAC;AAEjC,MAAM,UAAU,yBAAyB,CAAC,MAAqB;IAC7D,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACtC,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClF,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,YAAY,CAAC;QACtB,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,MAAqB,EACrB,MAAmB;IAEnB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAmB;IAC5C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,SAAS,CAAC;QACnB,KAAK,SAAS,CAAC;QACf,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC;QAClB,KAAK,SAAS;YACZ,OAAO,iBAAiB,CAAC;QAC3B,KAAK,aAAa;YAChB,OAAO,gBAAgB,CAAC;QAC1B,KAAK,MAAM;YACT,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAA4B,EAAE,KAAK,GAAG,CAAC;IACjE,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,MAAM,GAAG,KAAK,QAAQ,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAqB;IACxD,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,kIAAkI,CAAC;IAC5I,CAAC;IAED,MAAM,aAAa,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClE,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,kCAAkC,iBAAiB,CAAC,aAAa,CAAC,GAAG,CAAC;IAC/E,CAAC;IAED,MAAM,aAAa,GAAG;QACpB,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;QACjD,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KAChD,CAAC;IACF,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,oCAAoC,iBAAiB,CAAC,aAAa,CAAC,GAAG,CAAC;IACjF,CAAC;IAED,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC7E,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,OAAO,0EAA0E,iBAAiB,CAAC,iBAAiB,CAAC,GAAG,CAAC;IAC3H,CAAC;IAED,OAAO,0FAA0F,CAAC;AACpG,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { DiffArtifact, RunArtifact } from "../types.js";
2
+ export declare function renderHtml(artifact: RunArtifact | DiffArtifact): string;
@@ -0,0 +1,228 @@
1
+ function esc(text) {
2
+ return text
3
+ .replace(/&/g, "&amp;")
4
+ .replace(/</g, "&lt;")
5
+ .replace(/>/g, "&gt;")
6
+ .replace(/"/g, "&quot;");
7
+ }
8
+ function statusBadge(status) {
9
+ const colors = {
10
+ pass: "#22c55e",
11
+ fail: "#ef4444",
12
+ partial: "#eab308",
13
+ flaky: "#eab308",
14
+ unsupported: "#6b7280",
15
+ skipped: "#6b7280",
16
+ };
17
+ const bg = colors[status] ?? "#6b7280";
18
+ return `<span style="display:inline-block;padding:2px 8px;border-radius:4px;background:${bg};color:#fff;font-size:12px;font-weight:600">${esc(status)}</span>`;
19
+ }
20
+ function gateBadge(gate) {
21
+ const bg = gate === "pass" ? "#22c55e" : "#ef4444";
22
+ return `<span style="display:inline-block;padding:4px 12px;border-radius:6px;background:${bg};color:#fff;font-size:14px;font-weight:700">${esc(gate).toUpperCase()}</span>`;
23
+ }
24
+ function checksTable(checks) {
25
+ const rows = checks.map((c) => `
26
+ <tr>
27
+ <td style="padding:8px 12px;border-bottom:1px solid #e5e7eb">${esc(c.id)}</td>
28
+ <td style="padding:8px 12px;border-bottom:1px solid #e5e7eb">${statusBadge(c.status)}</td>
29
+ <td style="padding:8px 12px;border-bottom:1px solid #e5e7eb">${c.durationMs.toFixed(0)}ms</td>
30
+ <td style="padding:8px 12px;border-bottom:1px solid #e5e7eb;font-size:13px">${esc(c.message)}</td>
31
+ </tr>`).join("");
32
+ return `
33
+ <table style="width:100%;border-collapse:collapse;margin:16px 0">
34
+ <thead>
35
+ <tr style="background:#f9fafb;text-align:left">
36
+ <th style="padding:8px 12px;border-bottom:2px solid #e5e7eb">Check</th>
37
+ <th style="padding:8px 12px;border-bottom:2px solid #e5e7eb">Status</th>
38
+ <th style="padding:8px 12px;border-bottom:2px solid #e5e7eb">Duration</th>
39
+ <th style="padding:8px 12px;border-bottom:2px solid #e5e7eb">Message</th>
40
+ </tr>
41
+ </thead>
42
+ <tbody>${rows}</tbody>
43
+ </table>`;
44
+ }
45
+ function evidenceSection(checks) {
46
+ return checks.map((c) => {
47
+ const items = c.evidence.flatMap((ev) => {
48
+ const parts = [];
49
+ if (ev.identifiers && ev.identifiers.length > 0) {
50
+ parts.push(`<strong>Items:</strong> ${esc(ev.identifiers.join(", "))}`);
51
+ }
52
+ if (ev.diagnostics && ev.diagnostics.length > 0) {
53
+ parts.push(`<strong>Diagnostics:</strong> ${esc(ev.diagnostics.join("; "))}`);
54
+ }
55
+ if (ev.schemas) {
56
+ const count = Object.keys(ev.schemas).length;
57
+ parts.push(`<strong>Schemas captured:</strong> ${count}`);
58
+ }
59
+ return parts;
60
+ });
61
+ if (items.length === 0)
62
+ return "";
63
+ return `
64
+ <details style="margin:8px 0">
65
+ <summary style="cursor:pointer;font-weight:600">${esc(c.id)} — ${esc(c.status)}</summary>
66
+ <div style="padding:8px 16px;font-size:13px">${items.map((i) => `<p style="margin:4px 0">${i}</p>`).join("")}</div>
67
+ </details>`;
68
+ }).join("");
69
+ }
70
+ function diffTable(label, color, entries) {
71
+ if (entries.length === 0)
72
+ return "";
73
+ const rows = entries.map((e) => `
74
+ <tr>
75
+ <td style="padding:6px 12px;border-bottom:1px solid #e5e7eb">${esc(e.id)}</td>
76
+ <td style="padding:6px 12px;border-bottom:1px solid #e5e7eb">${esc(e.fromStatus ?? "n/a")}</td>
77
+ <td style="padding:6px 12px;border-bottom:1px solid #e5e7eb">${esc(e.toStatus ?? "n/a")}</td>
78
+ <td style="padding:6px 12px;border-bottom:1px solid #e5e7eb;font-size:13px">${esc(e.message)}</td>
79
+ </tr>`).join("");
80
+ return `
81
+ <h3 style="color:${color};margin-top:24px">${esc(label)} (${entries.length})</h3>
82
+ <table style="width:100%;border-collapse:collapse;margin:8px 0">
83
+ <thead>
84
+ <tr style="background:#f9fafb;text-align:left">
85
+ <th style="padding:6px 12px;border-bottom:2px solid #e5e7eb">Check</th>
86
+ <th style="padding:6px 12px;border-bottom:2px solid #e5e7eb">From</th>
87
+ <th style="padding:6px 12px;border-bottom:2px solid #e5e7eb">To</th>
88
+ <th style="padding:6px 12px;border-bottom:2px solid #e5e7eb">Message</th>
89
+ </tr>
90
+ </thead>
91
+ <tbody>${rows}</tbody>
92
+ </table>`;
93
+ }
94
+ function schemaDriftSection(drift) {
95
+ if (!drift || drift.length === 0)
96
+ return "";
97
+ const rows = drift.map((d) => `
98
+ <tr>
99
+ <td style="padding:6px 12px;border-bottom:1px solid #e5e7eb">${esc(d.name)}</td>
100
+ <td style="padding:6px 12px;border-bottom:1px solid #e5e7eb">${esc(d.capability)}</td>
101
+ <td style="padding:6px 12px;border-bottom:1px solid #e5e7eb;font-size:13px">${esc(d.changes.join(", "))}</td>
102
+ </tr>`).join("");
103
+ return `
104
+ <h3 style="color:#eab308;margin-top:24px">Schema Drift (${drift.length})</h3>
105
+ <table style="width:100%;border-collapse:collapse;margin:8px 0">
106
+ <thead>
107
+ <tr style="background:#f9fafb;text-align:left">
108
+ <th style="padding:6px 12px;border-bottom:2px solid #e5e7eb">Name</th>
109
+ <th style="padding:6px 12px;border-bottom:2px solid #e5e7eb">Capability</th>
110
+ <th style="padding:6px 12px;border-bottom:2px solid #e5e7eb">Changes</th>
111
+ </tr>
112
+ </thead>
113
+ <tbody>${rows}</tbody>
114
+ </table>`;
115
+ }
116
+ function renderRunHtml(artifact) {
117
+ const serverLabel = [artifact.target.serverName, artifact.target.serverVersion].filter(Boolean).join(" ") || "unknown";
118
+ return `<!DOCTYPE html>
119
+ <html lang="en">
120
+ <head>
121
+ <meta charset="utf-8">
122
+ <meta name="viewport" content="width=device-width,initial-scale=1">
123
+ <title>MCP Observatory — ${esc(artifact.target.targetId)}</title>
124
+ <style>
125
+ * { box-sizing: border-box; margin: 0; padding: 0; }
126
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.5; color: #1f2937; max-width: 900px; margin: 0 auto; padding: 32px 16px; background: #fafafa; }
127
+ h1 { font-size: 24px; margin-bottom: 4px; }
128
+ h2 { font-size: 18px; margin-top: 32px; margin-bottom: 12px; border-bottom: 1px solid #e5e7eb; padding-bottom: 6px; }
129
+ .meta { color: #6b7280; font-size: 13px; margin-bottom: 24px; }
130
+ .meta span { margin-right: 16px; }
131
+ .card { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px; margin: 16px 0; }
132
+ .fatal { background: #fef2f2; border-color: #fecaca; }
133
+ pre { background: #f3f4f6; padding: 12px; border-radius: 6px; overflow-x: auto; font-size: 13px; white-space: pre-wrap; }
134
+ </style>
135
+ </head>
136
+ <body>
137
+ <h1>MCP Observatory Run Report</h1>
138
+ <div class="meta">
139
+ <span>${esc(artifact.createdAt)}</span>
140
+ <span>Run: ${esc(artifact.runId)}</span>
141
+ </div>
142
+
143
+ <div class="card">
144
+ <div style="display:flex;align-items:center;gap:16px;margin-bottom:12px">
145
+ <div style="font-size:16px;font-weight:600">Gate: ${gateBadge(artifact.gate)}</div>
146
+ </div>
147
+ <div style="font-size:13px;color:#6b7280">
148
+ <div><strong>Target:</strong> ${esc(artifact.target.targetId)} (${esc(artifact.target.adapter)})</div>
149
+ <div><strong>Server:</strong> ${esc(serverLabel)}</div>
150
+ <div><strong>Command:</strong> <code>${esc(artifact.target.command)} ${esc(artifact.target.args.join(" "))}</code></div>
151
+ <div><strong>Platform:</strong> ${esc(artifact.environment.platform)} · Node ${esc(artifact.environment.nodeVersion)}</div>
152
+ </div>
153
+ </div>
154
+
155
+ ${artifact.fatalError ? `<div class="card fatal"><h3 style="color:#ef4444;margin-bottom:8px">Failure Diagnosis</h3><pre>${esc(artifact.fatalError)}</pre></div>` : ""}
156
+
157
+ <h2>Capability Checks</h2>
158
+ ${checksTable(artifact.checks)}
159
+
160
+ <h2>Evidence</h2>
161
+ ${evidenceSection(artifact.checks) || '<p style="color:#6b7280">No evidence captured.</p>'}
162
+
163
+ <div style="margin-top:32px;padding-top:16px;border-top:1px solid #e5e7eb;font-size:12px;color:#9ca3af">
164
+ Generated by MCP Observatory v${esc(artifact.toolVersion)} · Schema ${esc(artifact.schemaVersion)}
165
+ </div>
166
+ </body>
167
+ </html>`;
168
+ }
169
+ function renderDiffHtml(artifact) {
170
+ return `<!DOCTYPE html>
171
+ <html lang="en">
172
+ <head>
173
+ <meta charset="utf-8">
174
+ <meta name="viewport" content="width=device-width,initial-scale=1">
175
+ <title>MCP Observatory — Diff</title>
176
+ <style>
177
+ * { box-sizing: border-box; margin: 0; padding: 0; }
178
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.5; color: #1f2937; max-width: 900px; margin: 0 auto; padding: 32px 16px; background: #fafafa; }
179
+ h1 { font-size: 24px; margin-bottom: 4px; }
180
+ h2 { font-size: 18px; margin-top: 32px; margin-bottom: 12px; border-bottom: 1px solid #e5e7eb; padding-bottom: 6px; }
181
+ .meta { color: #6b7280; font-size: 13px; margin-bottom: 24px; }
182
+ .meta span { margin-right: 16px; }
183
+ .card { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px; margin: 16px 0; }
184
+ .stat { display: inline-block; text-align: center; margin-right: 24px; }
185
+ .stat-num { font-size: 28px; font-weight: 700; }
186
+ .stat-label { font-size: 12px; color: #6b7280; text-transform: uppercase; }
187
+ </style>
188
+ </head>
189
+ <body>
190
+ <h1>MCP Observatory Diff Report</h1>
191
+ <div class="meta">
192
+ <span>${esc(artifact.createdAt)}</span>
193
+ <span>Base: ${esc(artifact.baseRunId)}</span>
194
+ <span>Head: ${esc(artifact.headRunId)}</span>
195
+ </div>
196
+
197
+ <div class="card">
198
+ <div style="display:flex;align-items:center;gap:16px;margin-bottom:16px">
199
+ <div style="font-size:16px;font-weight:600">Gate: ${gateBadge(artifact.gate)}</div>
200
+ </div>
201
+ <div>
202
+ <div class="stat"><div class="stat-num" style="color:#ef4444">${artifact.summary.regressions}</div><div class="stat-label">Regressions</div></div>
203
+ <div class="stat"><div class="stat-num" style="color:#22c55e">${artifact.summary.recoveries}</div><div class="stat-label">Recoveries</div></div>
204
+ <div class="stat"><div class="stat-num" style="color:#6b7280">${artifact.summary.unchanged}</div><div class="stat-label">Unchanged</div></div>
205
+ <div class="stat"><div class="stat-num" style="color:#3b82f6">${artifact.summary.added}</div><div class="stat-label">Added</div></div>
206
+ <div class="stat"><div class="stat-num" style="color:#f97316">${artifact.summary.removed}</div><div class="stat-label">Removed</div></div>
207
+ </div>
208
+ </div>
209
+
210
+ ${diffTable("Regressions", "#ef4444", artifact.regressions)}
211
+ ${diffTable("Recoveries", "#22c55e", artifact.recoveries)}
212
+ ${diffTable("Unchanged", "#6b7280", artifact.unchanged)}
213
+ ${schemaDriftSection(artifact.schemaDrift)}
214
+
215
+ ${artifact.regressions.length === 0 && artifact.recoveries.length === 0 && (!artifact.schemaDrift || artifact.schemaDrift.length === 0) ? '<p style="color:#6b7280;margin-top:24px">No regressions, recoveries, or schema drift detected.</p>' : ""}
216
+
217
+ <div style="margin-top:32px;padding-top:16px;border-top:1px solid #e5e7eb;font-size:12px;color:#9ca3af">
218
+ Generated by MCP Observatory · Schema ${esc(artifact.schemaVersion)}
219
+ </div>
220
+ </body>
221
+ </html>`;
222
+ }
223
+ export function renderHtml(artifact) {
224
+ return artifact.artifactType === "run"
225
+ ? renderRunHtml(artifact)
226
+ : renderDiffHtml(artifact);
227
+ }
228
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../../src/reporters/html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { DiffArtifact, RunArtifact } from "../types.js";
2
+ export declare function renderMarkdown(artifact: RunArtifact | DiffArtifact): string;