@opencodehub/mcp 0.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 (210) hide show
  1. package/README.md +62 -0
  2. package/dist/analysis-bridge.d.ts +23 -0
  3. package/dist/analysis-bridge.d.ts.map +1 -0
  4. package/dist/analysis-bridge.js +83 -0
  5. package/dist/analysis-bridge.js.map +1 -0
  6. package/dist/connection-pool.d.ts +76 -0
  7. package/dist/connection-pool.d.ts.map +1 -0
  8. package/dist/connection-pool.js +179 -0
  9. package/dist/connection-pool.js.map +1 -0
  10. package/dist/error-envelope.d.ts +97 -0
  11. package/dist/error-envelope.d.ts.map +1 -0
  12. package/dist/error-envelope.js +75 -0
  13. package/dist/error-envelope.js.map +1 -0
  14. package/dist/group-resolver.d.ts +29 -0
  15. package/dist/group-resolver.d.ts.map +1 -0
  16. package/dist/group-resolver.js +100 -0
  17. package/dist/group-resolver.js.map +1 -0
  18. package/dist/index.d.ts +43 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +54 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/next-step-hints.d.ts +24 -0
  23. package/dist/next-step-hints.d.ts.map +1 -0
  24. package/dist/next-step-hints.js +41 -0
  25. package/dist/next-step-hints.js.map +1 -0
  26. package/dist/repo-resolver.d.ts +88 -0
  27. package/dist/repo-resolver.d.ts.map +1 -0
  28. package/dist/repo-resolver.js +211 -0
  29. package/dist/repo-resolver.js.map +1 -0
  30. package/dist/repo-uri-for-entry.d.ts +25 -0
  31. package/dist/repo-uri-for-entry.d.ts.map +1 -0
  32. package/dist/repo-uri-for-entry.js +64 -0
  33. package/dist/repo-uri-for-entry.js.map +1 -0
  34. package/dist/resources/repo-cluster.d.ts +19 -0
  35. package/dist/resources/repo-cluster.d.ts.map +1 -0
  36. package/dist/resources/repo-cluster.js +203 -0
  37. package/dist/resources/repo-cluster.js.map +1 -0
  38. package/dist/resources/repo-clusters.d.ts +14 -0
  39. package/dist/resources/repo-clusters.d.ts.map +1 -0
  40. package/dist/resources/repo-clusters.js +97 -0
  41. package/dist/resources/repo-clusters.js.map +1 -0
  42. package/dist/resources/repo-context.d.ts +12 -0
  43. package/dist/resources/repo-context.d.ts.map +1 -0
  44. package/dist/resources/repo-context.js +84 -0
  45. package/dist/resources/repo-context.js.map +1 -0
  46. package/dist/resources/repo-process.d.ts +19 -0
  47. package/dist/resources/repo-process.d.ts.map +1 -0
  48. package/dist/resources/repo-process.js +220 -0
  49. package/dist/resources/repo-process.js.map +1 -0
  50. package/dist/resources/repo-processes.d.ts +13 -0
  51. package/dist/resources/repo-processes.d.ts.map +1 -0
  52. package/dist/resources/repo-processes.js +99 -0
  53. package/dist/resources/repo-processes.js.map +1 -0
  54. package/dist/resources/repo-schema.d.ts +13 -0
  55. package/dist/resources/repo-schema.d.ts.map +1 -0
  56. package/dist/resources/repo-schema.js +99 -0
  57. package/dist/resources/repo-schema.js.map +1 -0
  58. package/dist/resources/repos.d.ts +20 -0
  59. package/dist/resources/repos.d.ts.map +1 -0
  60. package/dist/resources/repos.js +58 -0
  61. package/dist/resources/repos.js.map +1 -0
  62. package/dist/resources/store-helper.d.ts +28 -0
  63. package/dist/resources/store-helper.d.ts.map +1 -0
  64. package/dist/resources/store-helper.js +58 -0
  65. package/dist/resources/store-helper.js.map +1 -0
  66. package/dist/resources/yaml.d.ts +10 -0
  67. package/dist/resources/yaml.d.ts.map +1 -0
  68. package/dist/resources/yaml.js +16 -0
  69. package/dist/resources/yaml.js.map +1 -0
  70. package/dist/server.d.ts +46 -0
  71. package/dist/server.d.ts.map +1 -0
  72. package/dist/server.js +194 -0
  73. package/dist/server.js.map +1 -0
  74. package/dist/staleness.d.ts +19 -0
  75. package/dist/staleness.d.ts.map +1 -0
  76. package/dist/staleness.js +40 -0
  77. package/dist/staleness.js.map +1 -0
  78. package/dist/test-utils.d.ts +170 -0
  79. package/dist/test-utils.d.ts.map +1 -0
  80. package/dist/test-utils.js +473 -0
  81. package/dist/test-utils.js.map +1 -0
  82. package/dist/tools/api-impact.d.ts +47 -0
  83. package/dist/tools/api-impact.d.ts.map +1 -0
  84. package/dist/tools/api-impact.js +199 -0
  85. package/dist/tools/api-impact.js.map +1 -0
  86. package/dist/tools/confidence.d.ts +39 -0
  87. package/dist/tools/confidence.d.ts.map +1 -0
  88. package/dist/tools/confidence.js +58 -0
  89. package/dist/tools/confidence.js.map +1 -0
  90. package/dist/tools/context.d.ts +47 -0
  91. package/dist/tools/context.d.ts.map +1 -0
  92. package/dist/tools/context.js +577 -0
  93. package/dist/tools/context.js.map +1 -0
  94. package/dist/tools/dependencies.d.ts +29 -0
  95. package/dist/tools/dependencies.d.ts.map +1 -0
  96. package/dist/tools/dependencies.js +110 -0
  97. package/dist/tools/dependencies.js.map +1 -0
  98. package/dist/tools/detect-changes.d.ts +15 -0
  99. package/dist/tools/detect-changes.d.ts.map +1 -0
  100. package/dist/tools/detect-changes.js +78 -0
  101. package/dist/tools/detect-changes.js.map +1 -0
  102. package/dist/tools/group-contracts.d.ts +26 -0
  103. package/dist/tools/group-contracts.d.ts.map +1 -0
  104. package/dist/tools/group-contracts.js +251 -0
  105. package/dist/tools/group-contracts.js.map +1 -0
  106. package/dist/tools/group-cross-repo-links.d.ts +28 -0
  107. package/dist/tools/group-cross-repo-links.d.ts.map +1 -0
  108. package/dist/tools/group-cross-repo-links.js +128 -0
  109. package/dist/tools/group-cross-repo-links.js.map +1 -0
  110. package/dist/tools/group-list.d.ts +10 -0
  111. package/dist/tools/group-list.d.ts.map +1 -0
  112. package/dist/tools/group-list.js +74 -0
  113. package/dist/tools/group-list.js.map +1 -0
  114. package/dist/tools/group-query.d.ts +40 -0
  115. package/dist/tools/group-query.d.ts.map +1 -0
  116. package/dist/tools/group-query.js +209 -0
  117. package/dist/tools/group-query.js.map +1 -0
  118. package/dist/tools/group-status.d.ts +21 -0
  119. package/dist/tools/group-status.d.ts.map +1 -0
  120. package/dist/tools/group-status.js +121 -0
  121. package/dist/tools/group-status.js.map +1 -0
  122. package/dist/tools/group-sync.d.ts +23 -0
  123. package/dist/tools/group-sync.d.ts.map +1 -0
  124. package/dist/tools/group-sync.js +112 -0
  125. package/dist/tools/group-sync.js.map +1 -0
  126. package/dist/tools/impact.d.ts +36 -0
  127. package/dist/tools/impact.d.ts.map +1 -0
  128. package/dist/tools/impact.js +232 -0
  129. package/dist/tools/impact.js.map +1 -0
  130. package/dist/tools/license-audit.d.ts +34 -0
  131. package/dist/tools/license-audit.d.ts.map +1 -0
  132. package/dist/tools/license-audit.js +108 -0
  133. package/dist/tools/license-audit.js.map +1 -0
  134. package/dist/tools/list-dead-code.d.ts +26 -0
  135. package/dist/tools/list-dead-code.d.ts.map +1 -0
  136. package/dist/tools/list-dead-code.js +110 -0
  137. package/dist/tools/list-dead-code.js.map +1 -0
  138. package/dist/tools/list-findings-delta.d.ts +36 -0
  139. package/dist/tools/list-findings-delta.d.ts.map +1 -0
  140. package/dist/tools/list-findings-delta.js +274 -0
  141. package/dist/tools/list-findings-delta.js.map +1 -0
  142. package/dist/tools/list-findings.d.ts +30 -0
  143. package/dist/tools/list-findings.d.ts.map +1 -0
  144. package/dist/tools/list-findings.js +129 -0
  145. package/dist/tools/list-findings.js.map +1 -0
  146. package/dist/tools/list-repos.d.ts +17 -0
  147. package/dist/tools/list-repos.d.ts.map +1 -0
  148. package/dist/tools/list-repos.js +63 -0
  149. package/dist/tools/list-repos.js.map +1 -0
  150. package/dist/tools/owners.d.ts +23 -0
  151. package/dist/tools/owners.d.ts.map +1 -0
  152. package/dist/tools/owners.js +103 -0
  153. package/dist/tools/owners.js.map +1 -0
  154. package/dist/tools/pack-codebase.d.ts +76 -0
  155. package/dist/tools/pack-codebase.d.ts.map +1 -0
  156. package/dist/tools/pack-codebase.js +289 -0
  157. package/dist/tools/pack-codebase.js.map +1 -0
  158. package/dist/tools/project-profile.d.ts +28 -0
  159. package/dist/tools/project-profile.d.ts.map +1 -0
  160. package/dist/tools/project-profile.js +109 -0
  161. package/dist/tools/project-profile.js.map +1 -0
  162. package/dist/tools/query.d.ts +63 -0
  163. package/dist/tools/query.d.ts.map +1 -0
  164. package/dist/tools/query.js +662 -0
  165. package/dist/tools/query.js.map +1 -0
  166. package/dist/tools/remove-dead-code.d.ts +47 -0
  167. package/dist/tools/remove-dead-code.d.ts.map +1 -0
  168. package/dist/tools/remove-dead-code.js +258 -0
  169. package/dist/tools/remove-dead-code.js.map +1 -0
  170. package/dist/tools/rename.d.ts +21 -0
  171. package/dist/tools/rename.d.ts.map +1 -0
  172. package/dist/tools/rename.js +116 -0
  173. package/dist/tools/rename.js.map +1 -0
  174. package/dist/tools/risk-trends.d.ts +19 -0
  175. package/dist/tools/risk-trends.d.ts.map +1 -0
  176. package/dist/tools/risk-trends.js +73 -0
  177. package/dist/tools/risk-trends.js.map +1 -0
  178. package/dist/tools/route-map.d.ts +27 -0
  179. package/dist/tools/route-map.d.ts.map +1 -0
  180. package/dist/tools/route-map.js +119 -0
  181. package/dist/tools/route-map.js.map +1 -0
  182. package/dist/tools/scan.d.ts +27 -0
  183. package/dist/tools/scan.d.ts.map +1 -0
  184. package/dist/tools/scan.js +136 -0
  185. package/dist/tools/scan.js.map +1 -0
  186. package/dist/tools/shape-check.d.ts +53 -0
  187. package/dist/tools/shape-check.d.ts.map +1 -0
  188. package/dist/tools/shape-check.js +161 -0
  189. package/dist/tools/shape-check.js.map +1 -0
  190. package/dist/tools/shared.d.ts +101 -0
  191. package/dist/tools/shared.d.ts.map +1 -0
  192. package/dist/tools/shared.js +114 -0
  193. package/dist/tools/shared.js.map +1 -0
  194. package/dist/tools/signature.d.ts +38 -0
  195. package/dist/tools/signature.d.ts.map +1 -0
  196. package/dist/tools/signature.js +332 -0
  197. package/dist/tools/signature.js.map +1 -0
  198. package/dist/tools/sql.d.ts +34 -0
  199. package/dist/tools/sql.d.ts.map +1 -0
  200. package/dist/tools/sql.js +222 -0
  201. package/dist/tools/sql.js.map +1 -0
  202. package/dist/tools/tool-map.d.ts +24 -0
  203. package/dist/tools/tool-map.d.ts.map +1 -0
  204. package/dist/tools/tool-map.js +97 -0
  205. package/dist/tools/tool-map.js.map +1 -0
  206. package/dist/tools/verdict.d.ts +33 -0
  207. package/dist/tools/verdict.d.ts.map +1 -0
  208. package/dist/tools/verdict.js +102 -0
  209. package/dist/tools/verdict.js.map +1 -0
  210. package/package.json +76 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `route_map` — enumerate HTTP `Route` nodes with handlers + consumers.
3
+ *
4
+ * One row per Route, filtered by optional repo / route URL substring /
5
+ * method. For every row we also pull:
6
+ * - handlers = ids of nodes pointing at the route via HANDLES_ROUTE
7
+ * (typically Files for framework routes, Operations for
8
+ * OpenAPI specs).
9
+ * - consumers = ids of nodes pointing at the route via FETCHES (the
10
+ * `from_id` side — the symbol doing the outbound call).
11
+ * - responseKeys = the TEXT[] response-shape keys populated by the
12
+ * `routes` phase when static detection identified the
13
+ * response body.
14
+ */
15
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
+ import { type ToolContext, type ToolResult } from "./shared.js";
17
+ interface RouteMapArgs {
18
+ readonly repo?: string | undefined;
19
+ readonly repo_uri?: string | undefined;
20
+ readonly route?: string | undefined;
21
+ readonly method?: string | undefined;
22
+ readonly framework?: string | undefined;
23
+ }
24
+ export declare function runRouteMap(ctx: ToolContext, args: RouteMapArgs): Promise<ToolResult>;
25
+ export declare function registerRouteMapTool(server: McpServer, ctx: ToolContext): void;
26
+ export {};
27
+ //# sourceMappingURL=route-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-map.d.ts","sourceRoot":"","sources":["../../src/tools/route-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,UAAU,EAGhB,MAAM,aAAa,CAAC;AAsBrB,UAAU,YAAY;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAkF3F;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAiB9E"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * `route_map` — enumerate HTTP `Route` nodes with handlers + consumers.
3
+ *
4
+ * One row per Route, filtered by optional repo / route URL substring /
5
+ * method. For every row we also pull:
6
+ * - handlers = ids of nodes pointing at the route via HANDLES_ROUTE
7
+ * (typically Files for framework routes, Operations for
8
+ * OpenAPI specs).
9
+ * - consumers = ids of nodes pointing at the route via FETCHES (the
10
+ * `from_id` side — the symbol doing the outbound call).
11
+ * - responseKeys = the TEXT[] response-shape keys populated by the
12
+ * `routes` phase when static detection identified the
13
+ * response body.
14
+ */
15
+ // biome-ignore-all lint/complexity/useLiteralKeys: dot-access disallowed on Record index signatures
16
+ import { z } from "zod";
17
+ import { toolErrorFromUnknown } from "../error-envelope.js";
18
+ import { withNextSteps } from "../next-step-hints.js";
19
+ import { stalenessFromMeta } from "../staleness.js";
20
+ import { fromToolResult, repoArgShape, toToolResult, withStore, } from "./shared.js";
21
+ const RouteMapInput = {
22
+ ...repoArgShape,
23
+ route: z.string().optional().describe("Substring match against Route.url (e.g. '/api/users')."),
24
+ method: z.string().optional().describe("Exact match against Route.method (e.g. 'GET')."),
25
+ framework: z
26
+ .string()
27
+ .optional()
28
+ .describe("Reserved for a future framework filter; currently ignored."),
29
+ };
30
+ export async function runRouteMap(ctx, args) {
31
+ const call = await withStore(ctx, args, async (store, resolved) => {
32
+ try {
33
+ const graph = store.graph;
34
+ const opts = { limit: 500 };
35
+ if (args.route !== undefined && args.route.length > 0)
36
+ opts.pathLike = args.route;
37
+ if (args.method !== undefined &&
38
+ ["GET", "POST", "PUT", "DELETE", "PATCH"].includes(args.method)) {
39
+ opts.methods = [args.method];
40
+ }
41
+ let listed = await graph.listRoutes(opts);
42
+ if (args.method !== undefined &&
43
+ !["GET", "POST", "PUT", "DELETE", "PATCH"].includes(args.method)) {
44
+ listed = listed.filter((r) => r.method === args.method);
45
+ }
46
+ const sortedRoutes = [...listed].sort((a, b) => {
47
+ if (a.url !== b.url)
48
+ return a.url < b.url ? -1 : 1;
49
+ const am = a.method ?? "";
50
+ const bm = b.method ?? "";
51
+ return am < bm ? -1 : am > bm ? 1 : 0;
52
+ });
53
+ const routes = [];
54
+ for (const r of sortedRoutes) {
55
+ const [handlers, consumers] = await Promise.all([
56
+ fetchRelationFromIds(graph, r.id, "HANDLES_ROUTE"),
57
+ fetchRelationFromIds(graph, r.id, "FETCHES"),
58
+ ]);
59
+ routes.push({
60
+ id: r.id,
61
+ url: stringOr(r.url, ""),
62
+ method: stringOr(r.method, ""),
63
+ filePath: stringOr(r.filePath, ""),
64
+ responseKeys: r.responseKeys ?? [],
65
+ handlers,
66
+ consumers,
67
+ });
68
+ }
69
+ const header = `Routes (${routes.length}) for ${resolved.name}${args.route ? ` · url~${args.route}` : ""}${args.method ? ` · method=${args.method}` : ""}:`;
70
+ const body = routes.length === 0
71
+ ? "(no routes matched — verify the `routes` phase ran on a supported framework)"
72
+ : routes
73
+ .map((r) => `- ${r.method} ${r.url} handlers=${r.handlers.length} consumers=${r.consumers.length} keys=${r.responseKeys.length}`)
74
+ .join("\n");
75
+ const next = routes.length === 0
76
+ ? [
77
+ "call `list_repos` to confirm the repo is indexed",
78
+ "re-index with `codehub analyze` to emit Route nodes",
79
+ ]
80
+ : [
81
+ `call \`api_impact\` with route="${routes[0]?.url ?? ""}" to score blast radius`,
82
+ `call \`shape_check\` with route="${routes[0]?.url ?? ""}" to compare responseKeys vs consumer ACCESSES`,
83
+ ];
84
+ return withNextSteps(`${header}\n${body}`, { routes, total: routes.length }, next, stalenessFromMeta(resolved.meta));
85
+ }
86
+ catch (err) {
87
+ return toolErrorFromUnknown(err);
88
+ }
89
+ });
90
+ return toToolResult(call);
91
+ }
92
+ export function registerRouteMapTool(server, ctx) {
93
+ server.registerTool("route_map", {
94
+ title: "Map HTTP routes to handlers and consumers",
95
+ description: "Enumerate Route nodes filtered by url substring and/or method. For each route returns the static responseKeys (when detected), the HANDLES_ROUTE handlers (Files or Operations pointing at the route), and the FETCHES consumers (caller symbols). Read-only.",
96
+ inputSchema: RouteMapInput,
97
+ annotations: {
98
+ readOnlyHint: true,
99
+ destructiveHint: false,
100
+ idempotentHint: true,
101
+ openWorldHint: false,
102
+ },
103
+ }, async (args) => fromToolResult(await runRouteMap(ctx, args)));
104
+ }
105
+ async function fetchRelationFromIds(graph, routeId, type) {
106
+ const edges = await graph.listEdgesByType(type, { toIds: [routeId] });
107
+ return edges
108
+ .map((e) => e.from)
109
+ .filter((s) => s.length > 0)
110
+ .sort();
111
+ }
112
+ function stringOr(v, fallback) {
113
+ if (typeof v === "string")
114
+ return v;
115
+ if (typeof v === "number" || typeof v === "boolean")
116
+ return String(v);
117
+ return fallback;
118
+ }
119
+ //# sourceMappingURL=route-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-map.js","sourceRoot":"","sources":["../../src/tools/route-map.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `scan` — run Priority-1 scanners against a repo and return merged SARIF.
3
+ *
4
+ * Unlike every other OpenCodeHub tool, `scan` has filesystem effects:
5
+ * it spawns external scanner binaries and writes `.codehub/scan.sarif`
6
+ * on disk. Annotations reflect that:
7
+ *
8
+ * readOnlyHint = false — we write SARIF to disk
9
+ * destructiveHint = false — we never delete anything
10
+ * openWorldHint = true — we spawn untrusted subprocesses
11
+ * idempotentHint = false — two scans can differ (tool output drift)
12
+ *
13
+ * The tool returns the merged SARIF plus a summary by tool + severity so
14
+ * agents can reason about results without fetching the whole log.
15
+ */
16
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
+ import { type ToolContext, type ToolResult } from "./shared.js";
18
+ interface ScanArgs {
19
+ readonly repo?: string | undefined;
20
+ readonly repo_uri?: string | undefined;
21
+ readonly scanners?: readonly string[] | undefined;
22
+ readonly timeoutMs?: number | undefined;
23
+ }
24
+ export declare function runScan(ctx: ToolContext, args: ScanArgs): Promise<ToolResult>;
25
+ export declare function registerScanTool(server: McpServer, ctx: ToolContext): void;
26
+ export {};
27
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/tools/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAezE,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,UAAU,EAGhB,MAAM,aAAa,CAAC;AAyBrB,UAAU,QAAQ;IAChB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAsDnF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAiB1E"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * `scan` — run Priority-1 scanners against a repo and return merged SARIF.
3
+ *
4
+ * Unlike every other OpenCodeHub tool, `scan` has filesystem effects:
5
+ * it spawns external scanner binaries and writes `.codehub/scan.sarif`
6
+ * on disk. Annotations reflect that:
7
+ *
8
+ * readOnlyHint = false — we write SARIF to disk
9
+ * destructiveHint = false — we never delete anything
10
+ * openWorldHint = true — we spawn untrusted subprocesses
11
+ * idempotentHint = false — two scans can differ (tool output drift)
12
+ *
13
+ * The tool returns the merged SARIF plus a summary by tool + severity so
14
+ * agents can reason about results without fetching the whole log.
15
+ */
16
+ // biome-ignore-all lint/complexity/useLiteralKeys: dot-access disallowed on Record index signatures
17
+ import { mkdir, writeFile } from "node:fs/promises";
18
+ import { ALL_SPECS, createDefaultWrappers, filterSpecsByProfile, runScanners, } from "@opencodehub/scanners";
19
+ import { resolveRepoMetaDir } from "@opencodehub/storage";
20
+ import { z } from "zod";
21
+ import { toolErrorFromUnknown } from "../error-envelope.js";
22
+ import { withNextSteps } from "../next-step-hints.js";
23
+ import { stalenessFromMeta } from "../staleness.js";
24
+ import { fromToolResult, repoArgShape, toToolResult, withStore, } from "./shared.js";
25
+ const ScanInput = {
26
+ ...repoArgShape,
27
+ scanners: z
28
+ .array(z.string())
29
+ .optional()
30
+ .describe("Explicit scanner ids. When omitted, scanners are gated by the ProjectProfile languages."),
31
+ timeoutMs: z
32
+ .number()
33
+ .int()
34
+ .positive()
35
+ .max(600_000)
36
+ .optional()
37
+ .describe("Per-scanner wall-clock timeout in ms (default 300000)."),
38
+ };
39
+ export async function runScan(ctx, args) {
40
+ const call = await withStore(ctx, args, async (store, resolved) => {
41
+ try {
42
+ const specs = await selectScanners(store.graph, args.scanners);
43
+ if (specs.length === 0) {
44
+ return withNextSteps(`No scanners selected for ${resolved.name}.`, { sarif: { version: "2.1.0", runs: [] }, summary: emptySummary() }, [
45
+ "pass explicit scanners=['semgrep'] to override profile gating",
46
+ "run `codehub analyze --force` to refresh the ProjectProfile",
47
+ ], stalenessFromMeta(resolved.meta));
48
+ }
49
+ const wrappers = createDefaultWrappers(specs);
50
+ const runnerOpts = args.timeoutMs !== undefined ? { timeoutMs: args.timeoutMs } : {};
51
+ const result = await runScanners(resolved.repoPath, wrappers, runnerOpts);
52
+ // Persist merged SARIF to `.codehub/scan.sarif` for later ingestion.
53
+ await mkdir(resolveRepoMetaDir(resolved.repoPath), { recursive: true });
54
+ const sarifPath = `${resolveRepoMetaDir(resolved.repoPath)}/scan.sarif`;
55
+ await writeFile(sarifPath, `${JSON.stringify(result.sarif, null, 2)}\n`, "utf8");
56
+ const summary = summarize(result.sarif);
57
+ const errored = result.errored.map((e) => `${e.spec.id}: ${e.error}`);
58
+ const header = `scan — ${summary.total} findings across ${Object.keys(summary.byTool).length} scanner(s); wrote ${sarifPath}`;
59
+ const lines = [header];
60
+ for (const [tool, count] of Object.entries(summary.byTool).sort()) {
61
+ lines.push(` ${tool}: ${count}`);
62
+ }
63
+ if (errored.length > 0) {
64
+ lines.push("Errored scanners:");
65
+ for (const e of errored)
66
+ lines.push(` - ${e}`);
67
+ }
68
+ const next = [
69
+ "call `list_findings` to browse the ingested findings",
70
+ "call `codehub ingest-sarif` to re-ingest if you edit the SARIF",
71
+ ];
72
+ return withNextSteps(lines.join("\n"), { sarif: result.sarif, summary, errored, outputPath: sarifPath }, next, stalenessFromMeta(resolved.meta));
73
+ }
74
+ catch (err) {
75
+ return toolErrorFromUnknown(err);
76
+ }
77
+ });
78
+ return toToolResult(call);
79
+ }
80
+ export function registerScanTool(server, ctx) {
81
+ server.registerTool("scan", {
82
+ title: "Run Priority-1 scanners",
83
+ description: "Spawn Semgrep + Betterleaks + OSV-Scanner (+ Bandit/Biome when the project profile supports them), merge their SARIF outputs, write `.codehub/scan.sarif`, and return a summary. Selected scanners default to the polyglot set filtered by ProjectProfile.languages. IMPORTANT: this tool has filesystem effects and spawns external processes.",
84
+ inputSchema: ScanInput,
85
+ annotations: {
86
+ readOnlyHint: false,
87
+ destructiveHint: false,
88
+ openWorldHint: true,
89
+ idempotentHint: false,
90
+ },
91
+ }, async (args) => fromToolResult(await runScan(ctx, args)));
92
+ }
93
+ async function selectScanners(graph, explicit) {
94
+ if (explicit !== undefined && explicit.length > 0) {
95
+ const wanted = new Set(explicit);
96
+ return ALL_SPECS.filter((s) => wanted.has(s.id));
97
+ }
98
+ const profile = await readProfile(graph);
99
+ return filterSpecsByProfile(ALL_SPECS, profile);
100
+ }
101
+ async function readProfile(graph) {
102
+ try {
103
+ const nodes = await graph.listNodesByKind("ProjectProfile", { limit: 1 });
104
+ const first = nodes[0];
105
+ if (!first)
106
+ return {};
107
+ return {
108
+ languages: first.languages ?? [],
109
+ iacTypes: first.iacTypes ?? [],
110
+ apiContracts: first.apiContracts ?? [],
111
+ };
112
+ }
113
+ catch {
114
+ return {};
115
+ }
116
+ }
117
+ function summarize(sarif) {
118
+ const byTool = {};
119
+ const bySeverity = {};
120
+ let total = 0;
121
+ for (const run of sarif.runs) {
122
+ const tool = run.tool.driver.name;
123
+ const count = run.results?.length ?? 0;
124
+ byTool[tool] = (byTool[tool] ?? 0) + count;
125
+ total += count;
126
+ for (const result of run.results ?? []) {
127
+ const level = result.level ?? "note";
128
+ bySeverity[level] = (bySeverity[level] ?? 0) + 1;
129
+ }
130
+ }
131
+ return { total, byTool, bySeverity };
132
+ }
133
+ function emptySummary() {
134
+ return { total: 0, byTool: {}, bySeverity: {} };
135
+ }
136
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/tools/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `shape_check` — compare a Route's static responseKeys against the
3
+ * property names each consumer file actually reads off the response.
4
+ *
5
+ * For every Route matching `route` (URL substring) we:
6
+ * 1. Find FETCHES edges pointing AT the route. Each `from_id` is a
7
+ * symbol (Function / Method / Constructor) that issued the call.
8
+ * 2. Group those consumer symbols by file.
9
+ * 3. Walk outgoing ACCESSES from every symbol in each consumer file to
10
+ * its Property target; collect Property.name as the accessed key.
11
+ * 4. Compare that set against Route.responseKeys (populated by the
12
+ * `routes` phase when the response literal was statically known).
13
+ *
14
+ * Per-consumer status:
15
+ * - MATCH — every accessed key is in responseKeys.
16
+ * - MISMATCH — at least one accessed key is NOT in responseKeys.
17
+ * - PARTIAL — no accessed keys found (can't check).
18
+ *
19
+ * `classifyShape` is exported so `api_impact` can reuse it for its
20
+ * `mismatches` count without re-walking the graph.
21
+ */
22
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23
+ import type { IGraphStore } from "@opencodehub/storage";
24
+ import { type ToolContext, type ToolResult } from "./shared.js";
25
+ export type ShapeStatus = "MATCH" | "MISMATCH" | "PARTIAL";
26
+ export interface ConsumerShape {
27
+ readonly file: string;
28
+ readonly accessedKeys: readonly string[];
29
+ readonly status: ShapeStatus;
30
+ readonly missing: readonly string[];
31
+ }
32
+ export interface RouteShape {
33
+ readonly url: string;
34
+ readonly method: string;
35
+ readonly responseKeys: readonly string[];
36
+ readonly consumers: readonly ConsumerShape[];
37
+ }
38
+ interface ShapeCheckArgs {
39
+ readonly repo?: string | undefined;
40
+ readonly repo_uri?: string | undefined;
41
+ readonly route?: string | undefined;
42
+ }
43
+ export declare function runShapeCheck(ctx: ToolContext, args: ShapeCheckArgs): Promise<ToolResult>;
44
+ export declare function registerShapeCheckTool(server: McpServer, ctx: ToolContext): void;
45
+ /** Load every Route matching the filter and classify each consumer file. */
46
+ export declare function loadRouteShapes(graph: IGraphStore, routeFilter: string | undefined): Promise<readonly RouteShape[]>;
47
+ /** Classify a set of accessed keys against responseKeys. */
48
+ export declare function classifyShape(accessedKeys: readonly string[], responseKeys: readonly string[]): {
49
+ status: ShapeStatus;
50
+ missing: readonly string[];
51
+ };
52
+ export {};
53
+ //# sourceMappingURL=shape-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-check.d.ts","sourceRoot":"","sources":["../../src/tools/shape-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKxD,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,UAAU,EAGhB,MAAM,aAAa,CAAC;AAOrB,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;CAC9C;AAED,UAAU,cAAc;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAsC/F;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAiBhF;AAED,4EAA4E;AAC5E,wBAAsB,eAAe,CACnC,KAAK,EAAE,WAAW,EAClB,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAmBhC;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAC3B,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAMrD"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * `shape_check` — compare a Route's static responseKeys against the
3
+ * property names each consumer file actually reads off the response.
4
+ *
5
+ * For every Route matching `route` (URL substring) we:
6
+ * 1. Find FETCHES edges pointing AT the route. Each `from_id` is a
7
+ * symbol (Function / Method / Constructor) that issued the call.
8
+ * 2. Group those consumer symbols by file.
9
+ * 3. Walk outgoing ACCESSES from every symbol in each consumer file to
10
+ * its Property target; collect Property.name as the accessed key.
11
+ * 4. Compare that set against Route.responseKeys (populated by the
12
+ * `routes` phase when the response literal was statically known).
13
+ *
14
+ * Per-consumer status:
15
+ * - MATCH — every accessed key is in responseKeys.
16
+ * - MISMATCH — at least one accessed key is NOT in responseKeys.
17
+ * - PARTIAL — no accessed keys found (can't check).
18
+ *
19
+ * `classifyShape` is exported so `api_impact` can reuse it for its
20
+ * `mismatches` count without re-walking the graph.
21
+ */
22
+ // biome-ignore-all lint/complexity/useLiteralKeys: dot-access disallowed on Record index signatures
23
+ import { z } from "zod";
24
+ import { toolErrorFromUnknown } from "../error-envelope.js";
25
+ import { withNextSteps } from "../next-step-hints.js";
26
+ import { stalenessFromMeta } from "../staleness.js";
27
+ import { fromToolResult, repoArgShape, toToolResult, withStore, } from "./shared.js";
28
+ const ShapeCheckInput = {
29
+ ...repoArgShape,
30
+ route: z.string().optional().describe("Substring match against Route.url."),
31
+ };
32
+ export async function runShapeCheck(ctx, args) {
33
+ const call = await withStore(ctx, args, async (store, resolved) => {
34
+ try {
35
+ const routes = await loadRouteShapes(store.graph, args.route);
36
+ const header = `shape_check — ${routes.length} route(s) for ${resolved.name}${args.route ? ` · url~${args.route}` : ""}:`;
37
+ const lines = [header];
38
+ let mismatchTotal = 0;
39
+ for (const r of routes) {
40
+ lines.push(`${r.method} ${r.url} keys=${r.responseKeys.length}`);
41
+ for (const c of r.consumers) {
42
+ if (c.status === "MISMATCH")
43
+ mismatchTotal += 1;
44
+ const miss = c.missing.length > 0 ? ` missing=[${c.missing.join(",")}]` : "";
45
+ lines.push(` [${c.status}] ${c.file} accessed=${c.accessedKeys.length}${miss}`);
46
+ }
47
+ }
48
+ if (routes.length === 0) {
49
+ lines.push("(no routes matched — check the url filter)");
50
+ }
51
+ const next = routes.length === 0
52
+ ? ["call `route_map` with the same filter to list available routes"]
53
+ : mismatchTotal > 0
54
+ ? [
55
+ "investigate each MISMATCH — consumer reads a key not in responseKeys",
56
+ "call `context` on the consumer file for upstream callers",
57
+ ]
58
+ : ["no mismatches — consumer shape matches Route.responseKeys"];
59
+ return withNextSteps(lines.join("\n"), { routes }, next, stalenessFromMeta(resolved.meta));
60
+ }
61
+ catch (err) {
62
+ return toolErrorFromUnknown(err);
63
+ }
64
+ });
65
+ return toToolResult(call);
66
+ }
67
+ export function registerShapeCheckTool(server, ctx) {
68
+ server.registerTool("shape_check", {
69
+ title: "Route response-shape mismatch check",
70
+ description: "For each Route matching the filter, walk ACCESSES edges from the consumer files that FETCH this route and compare accessed property names against Route.responseKeys. Returns MATCH / MISMATCH / PARTIAL per consumer. Read-only.",
71
+ inputSchema: ShapeCheckInput,
72
+ annotations: {
73
+ readOnlyHint: true,
74
+ destructiveHint: false,
75
+ idempotentHint: true,
76
+ openWorldHint: false,
77
+ },
78
+ }, async (args) => fromToolResult(await runShapeCheck(ctx, args)));
79
+ }
80
+ /** Load every Route matching the filter and classify each consumer file. */
81
+ export async function loadRouteShapes(graph, routeFilter) {
82
+ const opts = { limit: 500 };
83
+ if (routeFilter !== undefined && routeFilter.length > 0)
84
+ opts.pathLike = routeFilter;
85
+ const listed = await graph.listRoutes(opts);
86
+ const sorted = [...listed].sort((a, b) => {
87
+ if (a.url !== b.url)
88
+ return a.url < b.url ? -1 : 1;
89
+ const am = a.method ?? "";
90
+ const bm = b.method ?? "";
91
+ return am < bm ? -1 : am > bm ? 1 : 0;
92
+ });
93
+ const accessesEdges = await graph.listEdgesByType("ACCESSES");
94
+ const routes = [];
95
+ for (const r of sorted) {
96
+ const responseKeys = r.responseKeys ?? [];
97
+ const consumers = await collectConsumerShapes(graph, accessesEdges, r.id, responseKeys);
98
+ routes.push({ url: r.url, method: r.method ?? "", responseKeys, consumers });
99
+ }
100
+ return routes;
101
+ }
102
+ /** Classify a set of accessed keys against responseKeys. */
103
+ export function classifyShape(accessedKeys, responseKeys) {
104
+ if (accessedKeys.length === 0)
105
+ return { status: "PARTIAL", missing: [] };
106
+ const known = new Set(responseKeys);
107
+ const missing = accessedKeys.filter((k) => !known.has(k));
108
+ if (missing.length === 0)
109
+ return { status: "MATCH", missing: [] };
110
+ return { status: "MISMATCH", missing };
111
+ }
112
+ async function collectConsumerShapes(graph, accessesEdges, routeId, responseKeys) {
113
+ const fetches = await graph.listEdgesByType("FETCHES", { toIds: [routeId] });
114
+ const consumerSymbolIds = fetches
115
+ .map((e) => e.from)
116
+ .filter((s) => s.length > 0)
117
+ .sort();
118
+ if (consumerSymbolIds.length === 0)
119
+ return [];
120
+ const consumerSymbols = await graph.listNodes({ ids: consumerSymbolIds });
121
+ const consumerById = new Map();
122
+ for (const n of consumerSymbols)
123
+ consumerById.set(n.id, n);
124
+ const consumerFiles = new Set();
125
+ for (const sid of consumerSymbolIds) {
126
+ const n = consumerById.get(sid);
127
+ if (n && n.filePath.length > 0)
128
+ consumerFiles.add(n.filePath);
129
+ }
130
+ // Snapshot all nodes referenced by ACCESSES edges so per-file walks
131
+ // don't fan out per-iteration.
132
+ const accessedIds = new Set();
133
+ for (const e of accessesEdges) {
134
+ accessedIds.add(e.from);
135
+ accessedIds.add(e.to);
136
+ }
137
+ const accessedNodes = accessedIds.size > 0 ? await graph.listNodes({ ids: [...accessedIds] }) : [];
138
+ const accByID = new Map();
139
+ for (const n of accessedNodes)
140
+ accByID.set(n.id, n);
141
+ const out = [];
142
+ const sortedFiles = [...consumerFiles].sort();
143
+ for (const file of sortedFiles) {
144
+ const accessedSet = new Set();
145
+ for (const e of accessesEdges) {
146
+ const src = accByID.get(e.from);
147
+ if (!src || src.filePath !== file)
148
+ continue;
149
+ const target = accByID.get(e.to);
150
+ if (!target || target.kind !== "Property")
151
+ continue;
152
+ if (target.name && target.name.length > 0)
153
+ accessedSet.add(target.name);
154
+ }
155
+ const accessedKeys = Array.from(accessedSet).sort();
156
+ const { status, missing } = classifyShape(accessedKeys, responseKeys);
157
+ out.push({ file, accessedKeys, status, missing });
158
+ }
159
+ return out;
160
+ }
161
+ //# sourceMappingURL=shape-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-check.js","sourceRoot":"","sources":["../../src/tools/shape-check.ts"],"names":[],"mappings":"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"}