@testrelic/mcp 2.1.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 (204) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +198 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/cache/blob.d.ts +22 -0
  5. package/dist/cache/blob.d.ts.map +1 -0
  6. package/dist/cache/blob.js +92 -0
  7. package/dist/cache/blob.js.map +1 -0
  8. package/dist/cache/diff-reader.d.ts +29 -0
  9. package/dist/cache/diff-reader.d.ts.map +1 -0
  10. package/dist/cache/diff-reader.js +34 -0
  11. package/dist/cache/diff-reader.js.map +1 -0
  12. package/dist/cache/index.d.ts +57 -0
  13. package/dist/cache/index.d.ts.map +1 -0
  14. package/dist/cache/index.js +99 -0
  15. package/dist/cache/index.js.map +1 -0
  16. package/dist/cache/key.d.ts +15 -0
  17. package/dist/cache/key.d.ts.map +1 -0
  18. package/dist/cache/key.js +32 -0
  19. package/dist/cache/key.js.map +1 -0
  20. package/dist/cache/lru.d.ts +21 -0
  21. package/dist/cache/lru.d.ts.map +1 -0
  22. package/dist/cache/lru.js +32 -0
  23. package/dist/cache/lru.js.map +1 -0
  24. package/dist/cache/sqlite.d.ts +22 -0
  25. package/dist/cache/sqlite.d.ts.map +1 -0
  26. package/dist/cache/sqlite.js +102 -0
  27. package/dist/cache/sqlite.js.map +1 -0
  28. package/dist/cache/vector.d.ts +42 -0
  29. package/dist/cache/vector.d.ts.map +1 -0
  30. package/dist/cache/vector.js +187 -0
  31. package/dist/cache/vector.js.map +1 -0
  32. package/dist/cli.d.ts +3 -0
  33. package/dist/cli.d.ts.map +1 -0
  34. package/dist/cli.js +205 -0
  35. package/dist/cli.js.map +1 -0
  36. package/dist/clients/amplitude.d.ts +24 -0
  37. package/dist/clients/amplitude.d.ts.map +1 -0
  38. package/dist/clients/amplitude.js +15 -0
  39. package/dist/clients/amplitude.js.map +1 -0
  40. package/dist/clients/clickhouse.d.ts +10 -0
  41. package/dist/clients/clickhouse.d.ts.map +1 -0
  42. package/dist/clients/clickhouse.js +8 -0
  43. package/dist/clients/clickhouse.js.map +1 -0
  44. package/dist/clients/cloud.d.ts +347 -0
  45. package/dist/clients/cloud.d.ts.map +1 -0
  46. package/dist/clients/cloud.js +402 -0
  47. package/dist/clients/cloud.js.map +1 -0
  48. package/dist/clients/http.d.ts +40 -0
  49. package/dist/clients/http.d.ts.map +1 -0
  50. package/dist/clients/http.js +67 -0
  51. package/dist/clients/http.js.map +1 -0
  52. package/dist/clients/index.d.ts +38 -0
  53. package/dist/clients/index.d.ts.map +1 -0
  54. package/dist/clients/index.js +24 -0
  55. package/dist/clients/index.js.map +1 -0
  56. package/dist/clients/jira.d.ts +16 -0
  57. package/dist/clients/jira.d.ts.map +1 -0
  58. package/dist/clients/jira.js +11 -0
  59. package/dist/clients/jira.js.map +1 -0
  60. package/dist/clients/loki.d.ts +7 -0
  61. package/dist/clients/loki.d.ts.map +1 -0
  62. package/dist/clients/loki.js +8 -0
  63. package/dist/clients/loki.js.map +1 -0
  64. package/dist/clients/retry.d.ts +28 -0
  65. package/dist/clients/retry.d.ts.map +1 -0
  66. package/dist/clients/retry.js +79 -0
  67. package/dist/clients/retry.js.map +1 -0
  68. package/dist/clients/testrelic.d.ts +90 -0
  69. package/dist/clients/testrelic.d.ts.map +1 -0
  70. package/dist/clients/testrelic.js +68 -0
  71. package/dist/clients/testrelic.js.map +1 -0
  72. package/dist/config.d.ts +216 -0
  73. package/dist/config.d.ts.map +1 -0
  74. package/dist/config.js +233 -0
  75. package/dist/config.js.map +1 -0
  76. package/dist/context/code-map.d.ts +35 -0
  77. package/dist/context/code-map.d.ts.map +1 -0
  78. package/dist/context/code-map.js +187 -0
  79. package/dist/context/code-map.js.map +1 -0
  80. package/dist/context/correlator.d.ts +32 -0
  81. package/dist/context/correlator.d.ts.map +1 -0
  82. package/dist/context/correlator.js +106 -0
  83. package/dist/context/correlator.js.map +1 -0
  84. package/dist/context/coverage-map.d.ts +25 -0
  85. package/dist/context/coverage-map.d.ts.map +1 -0
  86. package/dist/context/coverage-map.js +44 -0
  87. package/dist/context/coverage-map.js.map +1 -0
  88. package/dist/context/index.d.ts +20 -0
  89. package/dist/context/index.d.ts.map +1 -0
  90. package/dist/context/index.js +18 -0
  91. package/dist/context/index.js.map +1 -0
  92. package/dist/context/journey-graph.d.ts +22 -0
  93. package/dist/context/journey-graph.d.ts.map +1 -0
  94. package/dist/context/journey-graph.js +38 -0
  95. package/dist/context/journey-graph.js.map +1 -0
  96. package/dist/context/signal-map.d.ts +26 -0
  97. package/dist/context/signal-map.d.ts.map +1 -0
  98. package/dist/context/signal-map.js +30 -0
  99. package/dist/context/signal-map.js.map +1 -0
  100. package/dist/elicit/ask.d.ts +28 -0
  101. package/dist/elicit/ask.d.ts.map +1 -0
  102. package/dist/elicit/ask.js +31 -0
  103. package/dist/elicit/ask.js.map +1 -0
  104. package/dist/elicit/zod-to-json.d.ts +7 -0
  105. package/dist/elicit/zod-to-json.d.ts.map +1 -0
  106. package/dist/elicit/zod-to-json.js +37 -0
  107. package/dist/elicit/zod-to-json.js.map +1 -0
  108. package/dist/errors.d.ts +59 -0
  109. package/dist/errors.d.ts.map +1 -0
  110. package/dist/errors.js +122 -0
  111. package/dist/errors.js.map +1 -0
  112. package/dist/index.d.ts +43 -0
  113. package/dist/index.d.ts.map +1 -0
  114. package/dist/index.js +127 -0
  115. package/dist/index.js.map +1 -0
  116. package/dist/logger.d.ts +6 -0
  117. package/dist/logger.d.ts.map +1 -0
  118. package/dist/logger.js +29 -0
  119. package/dist/logger.js.map +1 -0
  120. package/dist/prompts/index.d.ts +8 -0
  121. package/dist/prompts/index.d.ts.map +1 -0
  122. package/dist/prompts/index.js +86 -0
  123. package/dist/prompts/index.js.map +1 -0
  124. package/dist/registry/index.d.ts +70 -0
  125. package/dist/registry/index.d.ts.map +1 -0
  126. package/dist/registry/index.js +88 -0
  127. package/dist/registry/index.js.map +1 -0
  128. package/dist/registry/project.d.ts +20 -0
  129. package/dist/registry/project.d.ts.map +1 -0
  130. package/dist/registry/project.js +44 -0
  131. package/dist/registry/project.js.map +1 -0
  132. package/dist/resources/index.d.ts +13 -0
  133. package/dist/resources/index.d.ts.map +1 -0
  134. package/dist/resources/index.js +76 -0
  135. package/dist/resources/index.js.map +1 -0
  136. package/dist/sampling/bridge.d.ts +35 -0
  137. package/dist/sampling/bridge.d.ts.map +1 -0
  138. package/dist/sampling/bridge.js +39 -0
  139. package/dist/sampling/bridge.js.map +1 -0
  140. package/dist/telemetry/metrics.d.ts +29 -0
  141. package/dist/telemetry/metrics.d.ts.map +1 -0
  142. package/dist/telemetry/metrics.js +46 -0
  143. package/dist/telemetry/metrics.js.map +1 -0
  144. package/dist/telemetry/tokens.d.ts +16 -0
  145. package/dist/telemetry/tokens.d.ts.map +1 -0
  146. package/dist/telemetry/tokens.js +40 -0
  147. package/dist/telemetry/tokens.js.map +1 -0
  148. package/dist/tools/core/index.d.ts +8 -0
  149. package/dist/tools/core/index.d.ts.map +1 -0
  150. package/dist/tools/core/index.js +219 -0
  151. package/dist/tools/core/index.js.map +1 -0
  152. package/dist/tools/coverage/index.d.ts +9 -0
  153. package/dist/tools/coverage/index.d.ts.map +1 -0
  154. package/dist/tools/coverage/index.js +247 -0
  155. package/dist/tools/coverage/index.js.map +1 -0
  156. package/dist/tools/creation/index.d.ts +9 -0
  157. package/dist/tools/creation/index.d.ts.map +1 -0
  158. package/dist/tools/creation/index.js +357 -0
  159. package/dist/tools/creation/index.js.map +1 -0
  160. package/dist/tools/creation/templates.d.ts +17 -0
  161. package/dist/tools/creation/templates.d.ts.map +1 -0
  162. package/dist/tools/creation/templates.js +63 -0
  163. package/dist/tools/creation/templates.js.map +1 -0
  164. package/dist/tools/devtools/index.d.ts +9 -0
  165. package/dist/tools/devtools/index.d.ts.map +1 -0
  166. package/dist/tools/devtools/index.js +106 -0
  167. package/dist/tools/devtools/index.js.map +1 -0
  168. package/dist/tools/healing/index.d.ts +10 -0
  169. package/dist/tools/healing/index.d.ts.map +1 -0
  170. package/dist/tools/healing/index.js +190 -0
  171. package/dist/tools/healing/index.js.map +1 -0
  172. package/dist/tools/impact/index.d.ts +8 -0
  173. package/dist/tools/impact/index.d.ts.map +1 -0
  174. package/dist/tools/impact/index.js +215 -0
  175. package/dist/tools/impact/index.js.map +1 -0
  176. package/dist/tools/index.d.ts +9 -0
  177. package/dist/tools/index.d.ts.map +1 -0
  178. package/dist/tools/index.js +29 -0
  179. package/dist/tools/index.js.map +1 -0
  180. package/dist/tools/signals/index.d.ts +9 -0
  181. package/dist/tools/signals/index.d.ts.map +1 -0
  182. package/dist/tools/signals/index.js +100 -0
  183. package/dist/tools/signals/index.js.map +1 -0
  184. package/dist/tools/triage/index.d.ts +9 -0
  185. package/dist/tools/triage/index.d.ts.map +1 -0
  186. package/dist/tools/triage/index.js +366 -0
  187. package/dist/tools/triage/index.js.map +1 -0
  188. package/dist/transport/http.d.ts +16 -0
  189. package/dist/transport/http.d.ts.map +1 -0
  190. package/dist/transport/http.js +110 -0
  191. package/dist/transport/http.js.map +1 -0
  192. package/dist/transport/stdio.d.ts +6 -0
  193. package/dist/transport/stdio.d.ts.map +1 -0
  194. package/dist/transport/stdio.js +19 -0
  195. package/dist/transport/stdio.js.map +1 -0
  196. package/dist/types/index.d.ts +268 -0
  197. package/dist/types/index.d.ts.map +1 -0
  198. package/dist/types/index.js +6 -0
  199. package/dist/types/index.js.map +1 -0
  200. package/dist/version.d.ts +7 -0
  201. package/dist/version.d.ts.map +1 -0
  202. package/dist/version.js +7 -0
  203. package/dist/version.js.map +1 -0
  204. package/package.json +84 -0
package/dist/index.js ADDED
@@ -0,0 +1,127 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { mergeConfig, configFromEnv, resolveConfig } from "./config.js";
3
+ import { createLogger, getLogger } from "./logger.js";
4
+ import { metrics } from "./telemetry/metrics.js";
5
+ import { CacheManager } from "./cache/index.js";
6
+ import { buildClients } from "./clients/index.js";
7
+ import { buildContextEngine } from "./context/index.js";
8
+ import { SamplingBridge } from "./sampling/bridge.js";
9
+ import { Elicitor } from "./elicit/ask.js";
10
+ import { ToolRegistry } from "./registry/index.js";
11
+ import { registerAllTools } from "./tools/index.js";
12
+ import { registerResources } from "./resources/index.js";
13
+ import { registerPrompts } from "./prompts/index.js";
14
+ import { version, name } from "./version.js";
15
+ import { startStdio } from "./transport/stdio.js";
16
+ import { startHttp } from "./transport/http.js";
17
+ export async function createServer(inputConfig = {}) {
18
+ const config = resolveConfig(mergeConfig(configFromEnv(), inputConfig));
19
+ createLogger(config.logLevel);
20
+ const server = new McpServer({ name, version }, {
21
+ capabilities: {
22
+ tools: {},
23
+ resources: {},
24
+ prompts: {},
25
+ logging: {},
26
+ },
27
+ instructions: "TestRelic MCP — intelligent testing context for creation, healing, coverage, and impact. Start with `tr_list_repos` or `tr_coverage_report` to orient; capabilities are gated by the --caps flag to keep the tool schema small.",
28
+ });
29
+ metrics.init(config.outputDir);
30
+ const cache = new CacheManager(config);
31
+ await cache.init();
32
+ const clients = buildClients(config);
33
+ const context = buildContextEngine(clients, cache);
34
+ const sampling = new SamplingBridge(server);
35
+ const elicit = new Elicitor(server);
36
+ // Best-effort bootstrap: one shot to /api/v1/mcp/bootstrap to pull the
37
+ // authenticated user/org/repo/integration summary. We never block startup
38
+ // on this — missing bootstrap just means repo_id must be given explicitly.
39
+ let bootstrap;
40
+ try {
41
+ bootstrap = await clients.cloud.bootstrap();
42
+ getLogger().info({
43
+ org: bootstrap.organization.id,
44
+ repos: bootstrap.repos.length,
45
+ integrations: bootstrap.integrations.filter((i) => i.connected).map((i) => i.type),
46
+ }, "mcp bootstrap ok");
47
+ }
48
+ catch (err) {
49
+ getLogger().warn({ err: err.message, cloudUrl: config.cloud.baseUrl }, "mcp bootstrap failed — continuing without repo/integration discovery");
50
+ }
51
+ const registry = new ToolRegistry();
52
+ const toolCtx = { server, config, clients, context, cache, sampling, elicit, bootstrap };
53
+ registerAllTools(toolCtx, registry);
54
+ registerResources(server, toolCtx);
55
+ registerPrompts(server);
56
+ // Factory used by the HTTP transport. `McpServer` is a single-connection
57
+ // object (throws on the second `connect`), so Streamable HTTP needs a
58
+ // fresh server + registrations per session. The singleton above is only
59
+ // for stdio and as the ctx anchor for non-server-bound helpers.
60
+ function buildSessionServer() {
61
+ const sessionServer = new McpServer({ name, version }, {
62
+ capabilities: { tools: {}, resources: {}, prompts: {}, logging: {} },
63
+ instructions: "TestRelic MCP — intelligent testing context for creation, healing, coverage, and impact. Start with `tr_list_repos` or `tr_coverage_report` to orient; capabilities are gated by the --caps flag to keep the tool schema small.",
64
+ });
65
+ const sessionRegistry = new ToolRegistry();
66
+ const sessionCtx = {
67
+ server: sessionServer,
68
+ config,
69
+ clients,
70
+ context,
71
+ cache,
72
+ sampling: new SamplingBridge(sessionServer),
73
+ elicit: new Elicitor(sessionServer),
74
+ bootstrap,
75
+ };
76
+ registerAllTools(sessionCtx, sessionRegistry);
77
+ registerResources(sessionServer, sessionCtx);
78
+ registerPrompts(sessionServer);
79
+ return sessionServer;
80
+ }
81
+ let stopTransport = null;
82
+ let stopped = false;
83
+ async function stop() {
84
+ if (stopped)
85
+ return;
86
+ stopped = true;
87
+ getLogger().info("shutting down");
88
+ if (stopTransport) {
89
+ await stopTransport().catch((err) => getLogger().warn({ err }, "transport close failed"));
90
+ }
91
+ await cache.close().catch((err) => getLogger().warn({ err }, "cache close failed"));
92
+ await metrics.close();
93
+ try {
94
+ await server.close();
95
+ }
96
+ catch {
97
+ // best effort
98
+ }
99
+ }
100
+ async function start() {
101
+ if (stopTransport)
102
+ return;
103
+ if (config.server.transport === "http") {
104
+ stopTransport = await startHttp(buildSessionServer, config);
105
+ }
106
+ else {
107
+ stopTransport = await startStdio(server);
108
+ }
109
+ const onSignal = (signal) => {
110
+ getLogger().info({ signal }, "signal received, stopping");
111
+ void stop().then(() => process.exit(0));
112
+ };
113
+ process.once("SIGINT", onSignal);
114
+ process.once("SIGTERM", onSignal);
115
+ }
116
+ return {
117
+ server,
118
+ config,
119
+ registeredTools: registry.list(),
120
+ start,
121
+ stop,
122
+ __ctx: toolCtx,
123
+ };
124
+ }
125
+ export { TestRelicMcpError, AuthError, UpstreamError, NotFoundError, RateLimitedError, InvalidInputError, TimeoutError, CircuitOpenError } from "./errors.js";
126
+ export { version, name } from "./version.js";
127
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import pino from "pino";
2
+ import type { LogLevel } from "./config.js";
3
+ export declare function createLogger(level?: LogLevel): pino.Logger;
4
+ export declare function getLogger(): pino.Logger;
5
+ export declare function setLogLevel(level: LogLevel): void;
6
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAS5C,wBAAgB,YAAY,CAAC,KAAK,GAAE,QAAiB,GAAG,IAAI,CAAC,MAAM,CAelE;AAED,wBAAgB,SAAS,IAAI,IAAI,CAAC,MAAM,CAKvC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD"}
package/dist/logger.js ADDED
@@ -0,0 +1,29 @@
1
+ import pino from "pino";
2
+ /**
3
+ * Structured logger that writes to STDERR only. MCP stdio transport uses
4
+ * STDOUT exclusively for JSON-RPC frames — logging to stdout breaks clients.
5
+ */
6
+ let logger = null;
7
+ export function createLogger(level = "info") {
8
+ logger = pino({
9
+ level,
10
+ base: { svc: "testrelic-mcp" },
11
+ timestamp: pino.stdTimeFunctions.isoTime,
12
+ formatters: {
13
+ level(label) {
14
+ return { level: label };
15
+ },
16
+ },
17
+ }, pino.destination({ dest: 2, sync: false }));
18
+ return logger;
19
+ }
20
+ export function getLogger() {
21
+ if (!logger) {
22
+ logger = createLogger("info");
23
+ }
24
+ return logger;
25
+ }
26
+ export function setLogLevel(level) {
27
+ getLogger().level = level;
28
+ }
29
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB;;;GAGG;AAEH,IAAI,MAAM,GAAuB,IAAI,CAAC;AAEtC,MAAM,UAAU,YAAY,CAAC,QAAkB,MAAM;IACnD,MAAM,GAAG,IAAI,CACX;QACE,KAAK;QACL,IAAI,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE;QAC9B,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,OAAO;QACxC,UAAU,EAAE;YACV,KAAK,CAAC,KAAK;gBACT,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC1B,CAAC;SACF;KACF,EACD,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAC3C,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,SAAS,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /**
3
+ * Ready-made prompts the client UI can surface as slash commands.
4
+ * Each one guides the agent through a canonical workflow — tools it should
5
+ * call, in what order, and what to do with the results.
6
+ */
7
+ export declare function registerPrompts(server: McpServer): void;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE;;;;GAIG;AAEH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6FvD"}
@@ -0,0 +1,86 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Ready-made prompts the client UI can surface as slash commands.
4
+ * Each one guides the agent through a canonical workflow — tools it should
5
+ * call, in what order, and what to do with the results.
6
+ */
7
+ export function registerPrompts(server) {
8
+ server.registerPrompt("create_test_from_gap", {
9
+ title: "Create a test for the highest-impact coverage gap",
10
+ description: "Finds the top uncovered journey, plans a test, generates code, and runs a dry-run type check. End-to-end flow.",
11
+ argsSchema: {
12
+ project_id: z.string().describe("TestRelic project_id"),
13
+ framework: z.enum(["playwright", "cypress", "jest", "vitest"]).optional(),
14
+ },
15
+ }, ({ project_id, framework }) => ({
16
+ messages: [
17
+ {
18
+ role: "user",
19
+ content: {
20
+ type: "text",
21
+ text: [
22
+ `Create a high-impact test for ${project_id}.`,
23
+ `1. Call \`tr_coverage_gaps\` with project_id="${project_id}" limit=3.`,
24
+ `2. Call \`tr_plan_test\` with the highest-gain journey_id from step 1${framework ? ` and framework="${framework}"` : ""}.`,
25
+ `3. Call \`tr_generate_test\` with the plan_cache_key from step 2.`,
26
+ `4. Call \`tr_dry_run_test\` with the generated file_path.`,
27
+ `5. Summarise: what user-coverage gain the new test brings and any dry-run issues.`,
28
+ ].join("\n"),
29
+ },
30
+ },
31
+ ],
32
+ }));
33
+ server.registerPrompt("triage_and_heal", {
34
+ title: "Triage a failing run, correlate impact, propose a heal",
35
+ description: "Diagnose → correlate user impact → propose a healing patch → optionally file a Jira ticket.",
36
+ argsSchema: {
37
+ run_id: z.string(),
38
+ },
39
+ }, ({ run_id }) => ({
40
+ messages: [
41
+ {
42
+ role: "user",
43
+ content: {
44
+ type: "text",
45
+ text: [
46
+ `Triage run ${run_id}.`,
47
+ `1. Call \`tr_diagnose_run\` with run_id="${run_id}".`,
48
+ `2. Call \`tr_user_impact\` with run_id="${run_id}".`,
49
+ `3. Call \`tr_ai_rca\` with run_id="${run_id}".`,
50
+ `4. Call \`tr_heal_run\` with run_id="${run_id}" — propose a patch.`,
51
+ `5. If user impact is high, call \`tr_create_jira\` with dry_run=true and surface the summary for confirmation.`,
52
+ ].join("\n"),
53
+ },
54
+ },
55
+ ],
56
+ }));
57
+ server.registerPrompt("pr_impact_gate", {
58
+ title: "Risk-rank tests for a PR diff",
59
+ description: "Given a diff, rank tests into MUST/SHOULD/OPTIONAL buckets with a user-impact risk score.",
60
+ argsSchema: {
61
+ project_id: z.string(),
62
+ unified_diff: z.string(),
63
+ },
64
+ }, ({ project_id, unified_diff }) => ({
65
+ messages: [
66
+ {
67
+ role: "user",
68
+ content: {
69
+ type: "text",
70
+ text: [
71
+ `Gate the following diff for ${project_id}.`,
72
+ `1. Call \`tr_analyze_diff\` with the attached unified_diff.`,
73
+ `2. Call \`tr_select_tests\` with the same inputs.`,
74
+ `3. Summarise: risk level, MUST-run tests, SHOULD-run tests, and which journeys are at risk.`,
75
+ "",
76
+ "Diff:",
77
+ "```diff",
78
+ unified_diff,
79
+ "```",
80
+ ].join("\n"),
81
+ },
82
+ },
83
+ ],
84
+ }));
85
+ }
86
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AAEH,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,MAAM,CAAC,cAAc,CACnB,sBAAsB,EACtB;QACE,KAAK,EAAE,mDAAmD;QAC1D,WAAW,EACT,gHAAgH;QAClH,UAAU,EAAE;YACV,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YACvD,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;SAC1E;KACF,EACD,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9B,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;wBACJ,iCAAiC,UAAU,GAAG;wBAC9C,iDAAiD,UAAU,YAAY;wBACvE,wEAAwE,SAAS,CAAC,CAAC,CAAC,mBAAmB,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG;wBAC3H,mEAAmE;wBACnE,2DAA2D;wBAC3D,mFAAmF;qBACpF,CAAC,IAAI,CAAC,IAAI,CAAC;iBACb;aACF;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,cAAc,CACnB,iBAAiB,EACjB;QACE,KAAK,EAAE,wDAAwD;QAC/D,WAAW,EAAE,6FAA6F;QAC1G,UAAU,EAAE;YACV,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;SACnB;KACF,EACD,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QACf,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;wBACJ,cAAc,MAAM,GAAG;wBACvB,4CAA4C,MAAM,IAAI;wBACtD,2CAA2C,MAAM,IAAI;wBACrD,sCAAsC,MAAM,IAAI;wBAChD,wCAAwC,MAAM,sBAAsB;wBACpE,gHAAgH;qBACjH,CAAC,IAAI,CAAC,IAAI,CAAC;iBACb;aACF;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,cAAc,CACnB,gBAAgB,EAChB;QACE,KAAK,EAAE,+BAA+B;QACtC,WAAW,EAAE,2FAA2F;QACxG,UAAU,EAAE;YACV,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;YACtB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;SACzB;KACF,EACD,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QACjC,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;wBACJ,+BAA+B,UAAU,GAAG;wBAC5C,6DAA6D;wBAC7D,mDAAmD;wBACnD,6FAA6F;wBAC7F,EAAE;wBACF,OAAO;wBACP,SAAS;wBACT,YAAY;wBACZ,KAAK;qBACN,CAAC,IAAI,CAAC,IAAI,CAAC;iBACb;aACF;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,70 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { z } from "zod";
3
+ import type { ResolvedConfig, Capability } from "../config.js";
4
+ import type { ClientBundle } from "../clients/index.js";
5
+ import type { BootstrapResponse } from "../clients/cloud.js";
6
+ import type { ContextEngine } from "../context/index.js";
7
+ import type { CacheManager } from "../cache/index.js";
8
+ import type { SamplingBridge } from "../sampling/bridge.js";
9
+ import type { Elicitor } from "../elicit/ask.js";
10
+ /**
11
+ * Capability-gated tool registry.
12
+ *
13
+ * Every tool declares its capability. `registerTools` filters by the
14
+ * enabled capability set (with "core" always on) — this is the primary
15
+ * token-reduction lever. A client with `--caps=creation` sees ~6 tools
16
+ * instead of the full ~35, meaning the tool-schema prelude sent with
17
+ * every request shrinks dramatically.
18
+ */
19
+ export interface ToolContext {
20
+ server: McpServer;
21
+ config: ResolvedConfig;
22
+ clients: ClientBundle;
23
+ context: ContextEngine;
24
+ cache: CacheManager;
25
+ sampling: SamplingBridge;
26
+ elicit: Elicitor;
27
+ /**
28
+ * One-time fetched at startup from `GET /api/v1/mcp/bootstrap`. Populated
29
+ * best-effort — undefined if the call failed (e.g. no token, offline).
30
+ * Tools that depend on repo discovery should guard on this.
31
+ */
32
+ bootstrap?: BootstrapResponse;
33
+ }
34
+ export interface ToolResponse {
35
+ /** Human-readable summary (capped by tokenBudgetPerTool). */
36
+ text: string;
37
+ /** Machine-readable payload for MCP structured output. Typed loosely so handler authors can return concrete interfaces without indexable casts. */
38
+ structured?: Record<string, unknown> | object;
39
+ /** If this result is large, cache_key points to the full blob in L4. */
40
+ cacheKey?: string;
41
+ }
42
+ export interface ToolDefinition<I extends z.ZodRawShape = z.ZodRawShape, O extends z.ZodRawShape = z.ZodRawShape> {
43
+ name: string;
44
+ capability: Capability;
45
+ title: string;
46
+ description: string;
47
+ inputSchema: I;
48
+ outputSchema?: O;
49
+ /** Alias names registered at the same time (used to deprecate v1 flat names). */
50
+ aliases?: Array<{
51
+ name: string;
52
+ description: string;
53
+ }>;
54
+ /** Whether to treat this as deprecated (just metadata for listing). */
55
+ deprecated?: boolean;
56
+ handler: (input: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResponse>;
57
+ }
58
+ export interface RegisteredTool {
59
+ name: string;
60
+ capability: Capability;
61
+ title: string;
62
+ description: string;
63
+ deprecated: boolean;
64
+ }
65
+ export declare class ToolRegistry {
66
+ private readonly registered;
67
+ register<I extends z.ZodRawShape, O extends z.ZodRawShape>(ctx: ToolContext, def: ToolDefinition<I, O>): void;
68
+ list(): RegisteredTool[];
69
+ }
70
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAMjD;;;;;;;;GAQG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,EAAE,QAAQ,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,mJAAmJ;IACnJ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IAC9C,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW;IAC9G,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,iFAAiF;IACjF,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;IAE5C,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,CAAC,WAAW,EAC9D,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GACxB,IAAI;IAsFA,IAAI,IAAI,cAAc,EAAE;CAGhC"}
@@ -0,0 +1,88 @@
1
+ import { getLogger } from "../logger.js";
2
+ import { countObjectTokens, truncateToTokens } from "../telemetry/tokens.js";
3
+ import { metrics } from "../telemetry/metrics.js";
4
+ import { TestRelicMcpError } from "../errors.js";
5
+ export class ToolRegistry {
6
+ registered = [];
7
+ register(ctx, def) {
8
+ const isEnabled = ctx.config.capabilities.includes(def.capability);
9
+ if (!isEnabled)
10
+ return;
11
+ const wrapped = async (rawInput) => {
12
+ const start = Date.now();
13
+ const inputTokens = countObjectTokens(rawInput);
14
+ let outputTokens = 0;
15
+ let errCode;
16
+ try {
17
+ const result = await def.handler(rawInput, ctx);
18
+ const budget = ctx.config.tokenBudgetPerTool;
19
+ const text = result.text.length > 0 ? truncateToTokens(result.text, budget) : "";
20
+ const structured = (result.structured ?? {});
21
+ outputTokens = countObjectTokens(text) + countObjectTokens(structured);
22
+ return {
23
+ content: [{ type: "text", text }],
24
+ structuredContent: structured,
25
+ };
26
+ }
27
+ catch (err) {
28
+ if (err instanceof TestRelicMcpError) {
29
+ errCode = err.code;
30
+ return err.toToolError();
31
+ }
32
+ errCode = "INTERNAL";
33
+ const message = err instanceof Error ? err.message : String(err);
34
+ getLogger().error({ tool: def.name, err }, "tool handler threw");
35
+ return {
36
+ isError: true,
37
+ content: [{ type: "text", text: `Internal error in ${def.name}: ${message}` }],
38
+ structuredContent: { error: { code: "INTERNAL", message } },
39
+ };
40
+ }
41
+ finally {
42
+ const duration = Date.now() - start;
43
+ metrics.record({
44
+ ts: new Date().toISOString(),
45
+ tool: def.name,
46
+ capability: def.capability,
47
+ input_tokens: inputTokens,
48
+ output_tokens: outputTokens,
49
+ duration_ms: duration,
50
+ cache_hit: false,
51
+ error_code: errCode,
52
+ });
53
+ }
54
+ };
55
+ ctx.server.registerTool(def.name, {
56
+ title: def.title,
57
+ description: def.description,
58
+ inputSchema: def.inputSchema,
59
+ outputSchema: def.outputSchema,
60
+ }, wrapped);
61
+ this.registered.push({
62
+ name: def.name,
63
+ capability: def.capability,
64
+ title: def.title,
65
+ description: def.description,
66
+ deprecated: def.deprecated ?? false,
67
+ });
68
+ for (const alias of def.aliases ?? []) {
69
+ ctx.server.registerTool(alias.name, {
70
+ title: def.title,
71
+ description: `[DEPRECATED — use ${def.name}] ${alias.description}`,
72
+ inputSchema: def.inputSchema,
73
+ outputSchema: def.outputSchema,
74
+ }, wrapped);
75
+ this.registered.push({
76
+ name: alias.name,
77
+ capability: def.capability,
78
+ title: def.title,
79
+ description: `[DEPRECATED] ${alias.description}`,
80
+ deprecated: true,
81
+ });
82
+ }
83
+ }
84
+ list() {
85
+ return [...this.registered];
86
+ }
87
+ }
88
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ToolContext } from "./index.js";
2
+ /**
3
+ * Resolve the project (cloud-platform-app repoId) a tool should operate on.
4
+ *
5
+ * Precedence:
6
+ * 1. Explicit `project_id` argument from the caller — matched against
7
+ * bootstrap.repos[].id then .gitId (so users can pass either).
8
+ * 2. `config.cloud.defaultRepoId` if set.
9
+ * 3. If there's exactly one repo in bootstrap, use it silently.
10
+ * 4. Throw InvalidInputError and include the available repo list in the
11
+ * structured error so the LLM can re-prompt the user.
12
+ */
13
+ export declare function resolveProjectId(ctx: ToolContext, argProjectId?: string): string;
14
+ /**
15
+ * Require that a given integration type is connected in the current org.
16
+ * Emits a clean, user-facing error pointing at the settings URL so the LLM
17
+ * can tell the user exactly where to go.
18
+ */
19
+ export declare function requireIntegration(ctx: ToolContext, type: string): void;
20
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/registry/project.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAuBhF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAQvE"}
@@ -0,0 +1,44 @@
1
+ import { InvalidInputError } from "../errors.js";
2
+ /**
3
+ * Resolve the project (cloud-platform-app repoId) a tool should operate on.
4
+ *
5
+ * Precedence:
6
+ * 1. Explicit `project_id` argument from the caller — matched against
7
+ * bootstrap.repos[].id then .gitId (so users can pass either).
8
+ * 2. `config.cloud.defaultRepoId` if set.
9
+ * 3. If there's exactly one repo in bootstrap, use it silently.
10
+ * 4. Throw InvalidInputError and include the available repo list in the
11
+ * structured error so the LLM can re-prompt the user.
12
+ */
13
+ export function resolveProjectId(ctx, argProjectId) {
14
+ const repos = ctx.bootstrap?.repos ?? [];
15
+ if (argProjectId) {
16
+ const match = repos.find((r) => r.id === argProjectId || r.gitId === argProjectId);
17
+ if (match)
18
+ return match.id;
19
+ if (repos.length === 0)
20
+ return argProjectId; // no bootstrap — trust caller
21
+ throw new InvalidInputError(`Unknown project_id "${argProjectId}". Available repos: ${repos.map((r) => r.gitId).join(", ")}`, "UNKNOWN_PROJECT");
22
+ }
23
+ if (ctx.config.cloud.defaultRepoId)
24
+ return ctx.config.cloud.defaultRepoId;
25
+ if (repos.length === 1)
26
+ return repos[0].id;
27
+ if (repos.length === 0) {
28
+ throw new InvalidInputError("No project_id provided and no repos found in bootstrap. Provide project_id or configure default_repo_id.", "PROJECT_REQUIRED");
29
+ }
30
+ throw new InvalidInputError(`project_id is required when multiple repos exist. Available: ${repos.map((r) => r.gitId).join(", ")}`, "PROJECT_REQUIRED");
31
+ }
32
+ /**
33
+ * Require that a given integration type is connected in the current org.
34
+ * Emits a clean, user-facing error pointing at the settings URL so the LLM
35
+ * can tell the user exactly where to go.
36
+ */
37
+ export function requireIntegration(ctx, type) {
38
+ const integration = ctx.bootstrap?.integrations.find((i) => i.type === type);
39
+ if (integration?.connected)
40
+ return;
41
+ const base = ctx.config.cloud.baseUrl.replace(/\/api\/v1\/?$/, "");
42
+ throw new InvalidInputError(`Integration "${type}" is not connected. Configure it at ${base}/settings/integrations`, "INTEGRATION_NOT_CONNECTED");
43
+ }
44
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/registry/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGjD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAgB,EAAE,YAAqB;IACtE,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC;IACzC,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,YAAY,IAAI,CAAC,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC;QACnF,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,YAAY,CAAC,CAAC,8BAA8B;QAC3E,MAAM,IAAI,iBAAiB,CACzB,uBAAuB,YAAY,uBAAuB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAChG,iBAAiB,CAClB,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC;IAC1E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC;IAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,iBAAiB,CACzB,0GAA0G,EAC1G,kBAAkB,CACnB,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,iBAAiB,CACzB,gEAAgE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACtG,kBAAkB,CACnB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAgB,EAAE,IAAY;IAC/D,MAAM,WAAW,GAAG,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC7E,IAAI,WAAW,EAAE,SAAS;QAAE,OAAO;IACnC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IACnE,MAAM,IAAI,iBAAiB,CACzB,gBAAgB,IAAI,uCAAuC,IAAI,wBAAwB,EACvF,2BAA2B,CAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ToolContext } from "../registry/index.js";
3
+ /**
4
+ * MCP resources — read-only URIs the client can fetch on demand.
5
+ *
6
+ * URI schemes:
7
+ * testrelic://repos/{repo_id}/journeys
8
+ * testrelic://repos/{repo_id}/coverage-report
9
+ * testrelic://repos/{repo_id}/gaps
10
+ * testrelic://cache/{key} (resolves cached blobs)
11
+ */
12
+ export declare function registerResources(server: McpServer, ctx: ToolContext): void;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;;;;;;GAQG;AAEH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAmF3E"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * MCP resources — read-only URIs the client can fetch on demand.
3
+ *
4
+ * URI schemes:
5
+ * testrelic://repos/{repo_id}/journeys
6
+ * testrelic://repos/{repo_id}/coverage-report
7
+ * testrelic://repos/{repo_id}/gaps
8
+ * testrelic://cache/{key} (resolves cached blobs)
9
+ */
10
+ export function registerResources(server, ctx) {
11
+ server.registerResource("journeys", "testrelic://repos/{repo_id}/journeys", {
12
+ title: "Top user journeys for a repo",
13
+ description: "JSON list of top-N user journeys for the given repo, Amplitude-derived.",
14
+ mimeType: "application/json",
15
+ }, async (uri) => {
16
+ const match = uri.pathname.match(/^\/repos\/([^/]+)\/journeys$/);
17
+ if (!match)
18
+ throw new Error(`Invalid journeys URI: ${uri.href}`);
19
+ const repo_id = decodeURIComponent(match[1] ?? "");
20
+ const journeys = await ctx.context.journeys.top(repo_id, 200);
21
+ return {
22
+ contents: [
23
+ {
24
+ uri: uri.href,
25
+ mimeType: "application/json",
26
+ text: JSON.stringify({ repo_id, journeys }, null, 2),
27
+ },
28
+ ],
29
+ };
30
+ });
31
+ server.registerResource("coverage-report", "testrelic://repos/{repo_id}/coverage-report", {
32
+ title: "Coverage report (95% readout)",
33
+ description: "User/test coverage, gaps summary, and targets.",
34
+ mimeType: "application/json",
35
+ }, async (uri) => {
36
+ const match = uri.pathname.match(/^\/repos\/([^/]+)\/coverage-report$/);
37
+ if (!match)
38
+ throw new Error(`Invalid coverage-report URI: ${uri.href}`);
39
+ const repo_id = decodeURIComponent(match[1] ?? "");
40
+ const report = await ctx.context.correlator.coverageReport(repo_id);
41
+ return {
42
+ contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(report, null, 2) }],
43
+ };
44
+ });
45
+ server.registerResource("coverage-gaps", "testrelic://repos/{repo_id}/gaps", {
46
+ title: "Ranked coverage gaps",
47
+ description: "Top-N uncovered journeys ranked by user count.",
48
+ mimeType: "application/json",
49
+ }, async (uri) => {
50
+ const match = uri.pathname.match(/^\/repos\/([^/]+)\/gaps$/);
51
+ if (!match)
52
+ throw new Error(`Invalid gaps URI: ${uri.href}`);
53
+ const repo_id = decodeURIComponent(match[1] ?? "");
54
+ const gaps = await ctx.context.correlator.rankedGaps(repo_id, 50);
55
+ return { contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify({ repo_id, gaps }, null, 2) }] };
56
+ });
57
+ server.registerResource("cache-blob", "testrelic://cache/{key}", {
58
+ title: "Cached payload blob",
59
+ description: "Resolves a cache_key returned by other tools to the full payload.",
60
+ mimeType: "application/octet-stream",
61
+ }, async (uri) => {
62
+ const match = uri.pathname.match(/^\/cache\/([^/]+)$/);
63
+ if (!match)
64
+ throw new Error(`Invalid cache URI: ${uri.href}`);
65
+ const key = decodeURIComponent(match[1] ?? "");
66
+ const value = ctx.cache.get(key);
67
+ if (!value)
68
+ throw new Error(`No cached value for key ${key}`);
69
+ if (value.value && typeof value.value === "object" && "blob" in value.value && typeof value.value.blob === "string") {
70
+ const text = ctx.cache.blob.readText(value.value.blob);
71
+ return { contents: [{ uri: uri.href, mimeType: "application/octet-stream", text: text ?? "" }] };
72
+ }
73
+ return { contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(value.value, null, 2) }] };
74
+ });
75
+ }
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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"}