seer-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 (371) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/LICENSE +176 -0
  3. package/README.md +272 -0
  4. package/README_dev.md +199 -0
  5. package/dist/bundle/ci.d.ts +47 -0
  6. package/dist/bundle/ci.d.ts.map +1 -0
  7. package/dist/bundle/ci.js +113 -0
  8. package/dist/bundle/ci.js.map +1 -0
  9. package/dist/bundle/contract.d.ts +111 -0
  10. package/dist/bundle/contract.d.ts.map +1 -0
  11. package/dist/bundle/contract.js +352 -0
  12. package/dist/bundle/contract.js.map +1 -0
  13. package/dist/bundle/export.d.ts +36 -0
  14. package/dist/bundle/export.d.ts.map +1 -0
  15. package/dist/bundle/export.js +152 -0
  16. package/dist/bundle/export.js.map +1 -0
  17. package/dist/bundle/external.d.ts +66 -0
  18. package/dist/bundle/external.d.ts.map +1 -0
  19. package/dist/bundle/external.js +238 -0
  20. package/dist/bundle/external.js.map +1 -0
  21. package/dist/bundle/format.d.ts +94 -0
  22. package/dist/bundle/format.d.ts.map +1 -0
  23. package/dist/bundle/format.js +42 -0
  24. package/dist/bundle/format.js.map +1 -0
  25. package/dist/bundle/import.d.ts +49 -0
  26. package/dist/bundle/import.d.ts.map +1 -0
  27. package/dist/bundle/import.js +116 -0
  28. package/dist/bundle/import.js.map +1 -0
  29. package/dist/cli/index.d.ts +3 -0
  30. package/dist/cli/index.d.ts.map +1 -0
  31. package/dist/cli/index.js +1402 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/cli/init.d.ts +48 -0
  34. package/dist/cli/init.d.ts.map +1 -0
  35. package/dist/cli/init.js +284 -0
  36. package/dist/cli/init.js.map +1 -0
  37. package/dist/db/schema.d.ts +3 -0
  38. package/dist/db/schema.d.ts.map +1 -0
  39. package/dist/db/schema.js +616 -0
  40. package/dist/db/schema.js.map +1 -0
  41. package/dist/db/store.d.ts +1011 -0
  42. package/dist/db/store.d.ts.map +1 -0
  43. package/dist/db/store.js +3888 -0
  44. package/dist/db/store.js.map +1 -0
  45. package/dist/graph/pagerank.d.ts +9 -0
  46. package/dist/graph/pagerank.d.ts.map +1 -0
  47. package/dist/graph/pagerank.js +47 -0
  48. package/dist/graph/pagerank.js.map +1 -0
  49. package/dist/indexer/architecture.d.ts +72 -0
  50. package/dist/indexer/architecture.d.ts.map +1 -0
  51. package/dist/indexer/architecture.js +112 -0
  52. package/dist/indexer/architecture.js.map +1 -0
  53. package/dist/indexer/behavior.d.ts +75 -0
  54. package/dist/indexer/behavior.d.ts.map +1 -0
  55. package/dist/indexer/behavior.js +395 -0
  56. package/dist/indexer/behavior.js.map +1 -0
  57. package/dist/indexer/boundaries.d.ts +60 -0
  58. package/dist/indexer/boundaries.d.ts.map +1 -0
  59. package/dist/indexer/boundaries.js +366 -0
  60. package/dist/indexer/boundaries.js.map +1 -0
  61. package/dist/indexer/churn.d.ts +15 -0
  62. package/dist/indexer/churn.d.ts.map +1 -0
  63. package/dist/indexer/churn.js +49 -0
  64. package/dist/indexer/churn.js.map +1 -0
  65. package/dist/indexer/classify.d.ts +9 -0
  66. package/dist/indexer/classify.d.ts.map +1 -0
  67. package/dist/indexer/classify.js +90 -0
  68. package/dist/indexer/classify.js.map +1 -0
  69. package/dist/indexer/context.d.ts +176 -0
  70. package/dist/indexer/context.d.ts.map +1 -0
  71. package/dist/indexer/context.js +193 -0
  72. package/dist/indexer/context.js.map +1 -0
  73. package/dist/indexer/continuity.d.ts +67 -0
  74. package/dist/indexer/continuity.d.ts.map +1 -0
  75. package/dist/indexer/continuity.js +288 -0
  76. package/dist/indexer/continuity.js.map +1 -0
  77. package/dist/indexer/detectchanges.d.ts +32 -0
  78. package/dist/indexer/detectchanges.d.ts.map +1 -0
  79. package/dist/indexer/detectchanges.js +74 -0
  80. package/dist/indexer/detectchanges.js.map +1 -0
  81. package/dist/indexer/discovery.d.ts +37 -0
  82. package/dist/indexer/discovery.d.ts.map +1 -0
  83. package/dist/indexer/discovery.js +136 -0
  84. package/dist/indexer/discovery.js.map +1 -0
  85. package/dist/indexer/externaldeps.d.ts +18 -0
  86. package/dist/indexer/externaldeps.d.ts.map +1 -0
  87. package/dist/indexer/externaldeps.js +288 -0
  88. package/dist/indexer/externaldeps.js.map +1 -0
  89. package/dist/indexer/freshness.d.ts +48 -0
  90. package/dist/indexer/freshness.d.ts.map +1 -0
  91. package/dist/indexer/freshness.js +128 -0
  92. package/dist/indexer/freshness.js.map +1 -0
  93. package/dist/indexer/git.d.ts +144 -0
  94. package/dist/indexer/git.d.ts.map +1 -0
  95. package/dist/indexer/git.js +444 -0
  96. package/dist/indexer/git.js.map +1 -0
  97. package/dist/indexer/index.d.ts +145 -0
  98. package/dist/indexer/index.d.ts.map +1 -0
  99. package/dist/indexer/index.js +930 -0
  100. package/dist/indexer/index.js.map +1 -0
  101. package/dist/indexer/modules.d.ts +62 -0
  102. package/dist/indexer/modules.d.ts.map +1 -0
  103. package/dist/indexer/modules.js +293 -0
  104. package/dist/indexer/modules.js.map +1 -0
  105. package/dist/indexer/preflight.d.ts +154 -0
  106. package/dist/indexer/preflight.d.ts.map +1 -0
  107. package/dist/indexer/preflight.js +399 -0
  108. package/dist/indexer/preflight.js.map +1 -0
  109. package/dist/indexer/protoScanner.d.ts +34 -0
  110. package/dist/indexer/protoScanner.d.ts.map +1 -0
  111. package/dist/indexer/protoScanner.js +133 -0
  112. package/dist/indexer/protoScanner.js.map +1 -0
  113. package/dist/indexer/risk.d.ts +115 -0
  114. package/dist/indexer/risk.d.ts.map +1 -0
  115. package/dist/indexer/risk.js +194 -0
  116. package/dist/indexer/risk.js.map +1 -0
  117. package/dist/indexer/serviceHostScanner.d.ts +25 -0
  118. package/dist/indexer/serviceHostScanner.d.ts.map +1 -0
  119. package/dist/indexer/serviceHostScanner.js +95 -0
  120. package/dist/indexer/serviceHostScanner.js.map +1 -0
  121. package/dist/indexer/serviceLinks.d.ts +105 -0
  122. package/dist/indexer/serviceLinks.d.ts.map +1 -0
  123. package/dist/indexer/serviceLinks.js +509 -0
  124. package/dist/indexer/serviceLinks.js.map +1 -0
  125. package/dist/indexer/shapehash.d.ts +98 -0
  126. package/dist/indexer/shapehash.d.ts.map +1 -0
  127. package/dist/indexer/shapehash.js +354 -0
  128. package/dist/indexer/shapehash.js.map +1 -0
  129. package/dist/indexer/skeleton.d.ts +15 -0
  130. package/dist/indexer/skeleton.d.ts.map +1 -0
  131. package/dist/indexer/skeleton.js +136 -0
  132. package/dist/indexer/skeleton.js.map +1 -0
  133. package/dist/indexer/symbolhistory.d.ts +41 -0
  134. package/dist/indexer/symbolhistory.d.ts.map +1 -0
  135. package/dist/indexer/symbolhistory.js +124 -0
  136. package/dist/indexer/symbolhistory.js.map +1 -0
  137. package/dist/indexer/watcher.d.ts +68 -0
  138. package/dist/indexer/watcher.d.ts.map +1 -0
  139. package/dist/indexer/watcher.js +179 -0
  140. package/dist/indexer/watcher.js.map +1 -0
  141. package/dist/mcp/server.d.ts +80 -0
  142. package/dist/mcp/server.d.ts.map +1 -0
  143. package/dist/mcp/server.js +1610 -0
  144. package/dist/mcp/server.js.map +1 -0
  145. package/dist/parser/index.d.ts +8 -0
  146. package/dist/parser/index.d.ts.map +1 -0
  147. package/dist/parser/index.js +33 -0
  148. package/dist/parser/index.js.map +1 -0
  149. package/dist/parser/languages/cpp.d.ts +3 -0
  150. package/dist/parser/languages/cpp.d.ts.map +1 -0
  151. package/dist/parser/languages/cpp.js +350 -0
  152. package/dist/parser/languages/cpp.js.map +1 -0
  153. package/dist/parser/languages/csharp.d.ts +3 -0
  154. package/dist/parser/languages/csharp.d.ts.map +1 -0
  155. package/dist/parser/languages/csharp.js +239 -0
  156. package/dist/parser/languages/csharp.js.map +1 -0
  157. package/dist/parser/languages/go.d.ts +3 -0
  158. package/dist/parser/languages/go.d.ts.map +1 -0
  159. package/dist/parser/languages/go.js +259 -0
  160. package/dist/parser/languages/go.js.map +1 -0
  161. package/dist/parser/languages/java.d.ts +3 -0
  162. package/dist/parser/languages/java.d.ts.map +1 -0
  163. package/dist/parser/languages/java.js +391 -0
  164. package/dist/parser/languages/java.js.map +1 -0
  165. package/dist/parser/languages/python.d.ts +3 -0
  166. package/dist/parser/languages/python.d.ts.map +1 -0
  167. package/dist/parser/languages/python.js +396 -0
  168. package/dist/parser/languages/python.js.map +1 -0
  169. package/dist/parser/languages/rust.d.ts +3 -0
  170. package/dist/parser/languages/rust.d.ts.map +1 -0
  171. package/dist/parser/languages/rust.js +159 -0
  172. package/dist/parser/languages/rust.js.map +1 -0
  173. package/dist/parser/languages/typescript.d.ts +3 -0
  174. package/dist/parser/languages/typescript.d.ts.map +1 -0
  175. package/dist/parser/languages/typescript.js +1442 -0
  176. package/dist/parser/languages/typescript.js.map +1 -0
  177. package/dist/parser/parserContext.d.ts +77 -0
  178. package/dist/parser/parserContext.d.ts.map +1 -0
  179. package/dist/parser/parserContext.js +354 -0
  180. package/dist/parser/parserContext.js.map +1 -0
  181. package/dist/parser/walker.d.ts +81 -0
  182. package/dist/parser/walker.d.ts.map +1 -0
  183. package/dist/parser/walker.js +217 -0
  184. package/dist/parser/walker.js.map +1 -0
  185. package/dist/parser/worker.d.ts +66 -0
  186. package/dist/parser/worker.d.ts.map +1 -0
  187. package/dist/parser/worker.js +129 -0
  188. package/dist/parser/worker.js.map +1 -0
  189. package/dist/parser/workerpool.d.ts +107 -0
  190. package/dist/parser/workerpool.d.ts.map +1 -0
  191. package/dist/parser/workerpool.js +383 -0
  192. package/dist/parser/workerpool.js.map +1 -0
  193. package/dist/scip/format.d.ts +87 -0
  194. package/dist/scip/format.d.ts.map +1 -0
  195. package/dist/scip/format.js +31 -0
  196. package/dist/scip/format.js.map +1 -0
  197. package/dist/scip/import.d.ts +37 -0
  198. package/dist/scip/import.d.ts.map +1 -0
  199. package/dist/scip/import.js +180 -0
  200. package/dist/scip/import.js.map +1 -0
  201. package/dist/types.d.ts +392 -0
  202. package/dist/types.d.ts.map +1 -0
  203. package/dist/types.js +4 -0
  204. package/dist/types.js.map +1 -0
  205. package/docs/architecture.md +105 -0
  206. package/docs/benchmarks/methodology.md +134 -0
  207. package/docs/benchmarks/raw-results.md +71 -0
  208. package/docs/benchmarks.md +74 -0
  209. package/docs/cli.md +148 -0
  210. package/docs/examples/behavior-tests.md +70 -0
  211. package/docs/examples/change-history.md +85 -0
  212. package/docs/examples/pre-edit-context.md +81 -0
  213. package/docs/examples/service-links.md +88 -0
  214. package/docs/examples.md +80 -0
  215. package/docs/faq.md +70 -0
  216. package/docs/internals.md +104 -0
  217. package/docs/languages.md +70 -0
  218. package/docs/limits.md +52 -0
  219. package/docs/mcp.md +199 -0
  220. package/docs/quickstart.md +119 -0
  221. package/docs/testing.md +123 -0
  222. package/docs/tools.md +115 -0
  223. package/package.json +52 -0
  224. package/research-codebase.md +578 -0
  225. package/seer-cli-docs.md +326 -0
  226. package/seer-master-guide.md +246 -0
  227. package/src/bundle/ci.ts +141 -0
  228. package/src/bundle/contract.ts +387 -0
  229. package/src/bundle/export.ts +175 -0
  230. package/src/bundle/external.ts +285 -0
  231. package/src/bundle/format.ts +92 -0
  232. package/src/bundle/import.ts +157 -0
  233. package/src/cli/index.ts +1249 -0
  234. package/src/cli/init.ts +389 -0
  235. package/src/db/schema.ts +614 -0
  236. package/src/db/store.ts +4306 -0
  237. package/src/graph/pagerank.ts +53 -0
  238. package/src/indexer/architecture.ts +148 -0
  239. package/src/indexer/behavior.ts +466 -0
  240. package/src/indexer/boundaries.ts +374 -0
  241. package/src/indexer/churn.ts +58 -0
  242. package/src/indexer/classify.ts +96 -0
  243. package/src/indexer/context.ts +340 -0
  244. package/src/indexer/continuity.ts +322 -0
  245. package/src/indexer/detectchanges.ts +94 -0
  246. package/src/indexer/discovery.ts +176 -0
  247. package/src/indexer/externaldeps.ts +243 -0
  248. package/src/indexer/freshness.ts +166 -0
  249. package/src/indexer/git.ts +453 -0
  250. package/src/indexer/index.ts +1092 -0
  251. package/src/indexer/modules.ts +358 -0
  252. package/src/indexer/preflight.ts +548 -0
  253. package/src/indexer/protoScanner.ts +147 -0
  254. package/src/indexer/risk.ts +304 -0
  255. package/src/indexer/serviceHostScanner.ts +92 -0
  256. package/src/indexer/serviceLinks.ts +543 -0
  257. package/src/indexer/shapehash.ts +370 -0
  258. package/src/indexer/skeleton.ts +169 -0
  259. package/src/indexer/symbolhistory.ts +172 -0
  260. package/src/indexer/watcher.ts +206 -0
  261. package/src/mcp/server.ts +1659 -0
  262. package/src/parser/index.ts +37 -0
  263. package/src/parser/languages/cpp.ts +361 -0
  264. package/src/parser/languages/csharp.ts +235 -0
  265. package/src/parser/languages/go.ts +259 -0
  266. package/src/parser/languages/java.ts +382 -0
  267. package/src/parser/languages/python.ts +370 -0
  268. package/src/parser/languages/rust.ts +164 -0
  269. package/src/parser/languages/typescript.ts +1435 -0
  270. package/src/parser/parserContext.ts +392 -0
  271. package/src/parser/walker.ts +306 -0
  272. package/src/parser/worker.ts +181 -0
  273. package/src/parser/workerpool.ts +448 -0
  274. package/src/scip/format.ts +83 -0
  275. package/src/scip/import.ts +216 -0
  276. package/src/types.ts +457 -0
  277. package/tests/benchmark-service-links.ts +244 -0
  278. package/tests/bug-regressions.ts +626 -0
  279. package/tests/filters.ts +264 -0
  280. package/tests/fixtures/Counter.tsx +38 -0
  281. package/tests/fixtures/caller.ts +7 -0
  282. package/tests/fixtures/collisions.ts +23 -0
  283. package/tests/fixtures/local_helper.ts +5 -0
  284. package/tests/fixtures/overloads.java +17 -0
  285. package/tests/fixtures/remote_helper.ts +4 -0
  286. package/tests/fixtures/sample.c +15 -0
  287. package/tests/fixtures/sample.cpp +47 -0
  288. package/tests/fixtures/sample.cs +62 -0
  289. package/tests/fixtures/sample.go +68 -0
  290. package/tests/fixtures/sample.h +30 -0
  291. package/tests/fixtures/sample.java +85 -0
  292. package/tests/fixtures/sample.py +46 -0
  293. package/tests/fixtures/sample.rs +78 -0
  294. package/tests/fixtures/sample.ts +76 -0
  295. package/tests/fixtures-service/HttpClients.cs +30 -0
  296. package/tests/fixtures-service/HttpClients.java +24 -0
  297. package/tests/fixtures-service/billing.ts +15 -0
  298. package/tests/fixtures-service/docker-compose.yml +15 -0
  299. package/tests/fixtures-service/gateway.ts +10 -0
  300. package/tests/fixtures-service/get_user.ts +11 -0
  301. package/tests/fixtures-service/graphql_client.ts +63 -0
  302. package/tests/fixtures-service/graphql_server.ts +30 -0
  303. package/tests/fixtures-service/grpc_client.go +30 -0
  304. package/tests/fixtures-service/http_clients.go +23 -0
  305. package/tests/fixtures-service/http_clients.py +38 -0
  306. package/tests/fixtures-service/http_clients.ts +49 -0
  307. package/tests/fixtures-service/k8s/payment-service.yaml +22 -0
  308. package/tests/fixtures-service/k8s_calls.ts +20 -0
  309. package/tests/fixtures-service/messaging.ts +87 -0
  310. package/tests/fixtures-service/trpc_client.ts +39 -0
  311. package/tests/fixtures-service/trpc_server.ts +39 -0
  312. package/tests/fixtures-service/user_service.proto +33 -0
  313. package/tests/fixtures-trackcd/Cargo.toml +11 -0
  314. package/tests/fixtures-trackcd/SpringController.java +36 -0
  315. package/tests/fixtures-trackcd/auth_service.ts +19 -0
  316. package/tests/fixtures-trackcd/complex_module.py +50 -0
  317. package/tests/fixtures-trackcd/express_app.js +30 -0
  318. package/tests/fixtures-trackcd/fastapi_app.py +49 -0
  319. package/tests/fixtures-trackcd/fastify_object_routes.js +32 -0
  320. package/tests/fixtures-trackcd/go.mod +8 -0
  321. package/tests/fixtures-trackcd/package.json +15 -0
  322. package/tests/fixtures-trackcd/requirements.txt +4 -0
  323. package/tests/fixtures-trackcd/tests/auth_service.test.ts +13 -0
  324. package/tests/fixtures-tracke/auth/AuthService.ts +23 -0
  325. package/tests/fixtures-tracke/auth/crypto.ts +7 -0
  326. package/tests/fixtures-tracke/billing/Billing.ts +20 -0
  327. package/tests/fixtures-tracke/billing/Invoice.ts +10 -0
  328. package/tests/fixtures-tracke/billing/server.ts +17 -0
  329. package/tests/fixtures-tracke/package.json +7 -0
  330. package/tests/fixtures-tracke/tests/auth.test.ts +23 -0
  331. package/tests/fixtures-tracke/tests/billing.test.ts +14 -0
  332. package/tests/fixtures-trackf/package.json +5 -0
  333. package/tests/fixtures-trackf/src/auth.ts +26 -0
  334. package/tests/fixtures-trackf/src/handlers.ts +35 -0
  335. package/tests/fixtures-tracki/billing/routes.ts +12 -0
  336. package/tests/fixtures-tracki/gateway/client.ts +13 -0
  337. package/tests/git-features.ts +267 -0
  338. package/tests/init.ts +141 -0
  339. package/tests/mcp-jit.ts +130 -0
  340. package/tests/mcp-smoke.ts +191 -0
  341. package/tests/mcp-trackcd.ts +169 -0
  342. package/tests/mcp-tracke.ts +229 -0
  343. package/tests/mcp-trackf.ts +330 -0
  344. package/tests/mcp-trackg.ts +219 -0
  345. package/tests/mcp-tracki.ts +174 -0
  346. package/tests/mcp-watcher.ts +126 -0
  347. package/tests/optspec.ts +194 -0
  348. package/tests/parallel-index.ts +333 -0
  349. package/tests/parallel-read.ts +125 -0
  350. package/tests/parallel-recovery.ts +241 -0
  351. package/tests/perf-callers.ts +145 -0
  352. package/tests/query-parity.ts +184 -0
  353. package/tests/query-perf.ts +55 -0
  354. package/tests/scale-parallel-parity.ts +225 -0
  355. package/tests/scale-test.ts +523 -0
  356. package/tests/smoke.ts +396 -0
  357. package/tests/trackcd.ts +325 -0
  358. package/tests/tracke-collisions.ts +255 -0
  359. package/tests/tracke.ts +314 -0
  360. package/tests/trackf-bugs.ts +406 -0
  361. package/tests/trackf.ts +390 -0
  362. package/tests/trackg.ts +1372 -0
  363. package/tests/tracki-boundaries.ts +202 -0
  364. package/tests/tracki-continuity.ts +253 -0
  365. package/tests/tracki-contract-diff.ts +249 -0
  366. package/tests/tracki-external-bundles.ts +341 -0
  367. package/tests/tracki-preflight.ts +251 -0
  368. package/tests/verify-roles.ts +51 -0
  369. package/tests/worker-parity.ts +286 -0
  370. package/tests/worker-pool.ts +262 -0
  371. package/tsconfig.json +20 -0
@@ -0,0 +1,330 @@
1
+ /**
2
+ * MCP smoke test for Track-F tools (portability + precision). Spawns
3
+ * `seer mcp` against the fixtures-trackf workspace and exercises every new
4
+ * tool over stdio.
5
+ *
6
+ * Run: npx tsx tests/mcp-trackf.ts
7
+ */
8
+
9
+ import { spawn } from 'child_process';
10
+ import path from 'path';
11
+ import fs from 'fs';
12
+ import os from 'os';
13
+
14
+ const ROOT = path.resolve(__dirname, '..');
15
+ const FIX = path.join(ROOT, 'tests/fixtures-trackf');
16
+ const TMP_WS = path.join(os.tmpdir(), `seer-mcp-f-${Date.now()}`);
17
+ const CLI = path.join(ROOT, 'dist/cli/index.js');
18
+
19
+ let passed = 0;
20
+ let failed = 0;
21
+ const ok = (m: string): void => { passed++; console.log(` ✓ ${m}`); };
22
+ const bad = (m: string, x?: unknown): void => { failed++; console.error(` ✗ ${m}` + (x !== undefined ? ` :: ${JSON.stringify(x).slice(0, 300)}` : '')); };
23
+
24
+ function copyRecursive(src: string, dst: string): void {
25
+ fs.mkdirSync(dst, { recursive: true });
26
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
27
+ const s = path.join(src, entry.name);
28
+ const d = path.join(dst, entry.name);
29
+ if (entry.isDirectory()) copyRecursive(s, d);
30
+ else fs.copyFileSync(s, d);
31
+ }
32
+ }
33
+
34
+ async function main(): Promise<void> {
35
+ console.log('\nSeer MCP Track-F Smoke\n==========================\n');
36
+ copyRecursive(FIX, TMP_WS);
37
+ console.log(` Workspace: ${TMP_WS}`);
38
+
39
+ const proc = spawn(process.execPath, [CLI, 'mcp', '--workspace', TMP_WS, '--no-watch', '--no-jit'],
40
+ { stdio: ['pipe', 'pipe', 'pipe'] });
41
+ proc.stderr.on('data', (d) => process.stderr.write(`[mcp-stderr] ${d}`));
42
+
43
+ let buf = '';
44
+ const pending = new Map<number, (msg: any) => void>();
45
+ proc.stdout.on('data', (chunk: Buffer) => {
46
+ buf += chunk.toString('utf8');
47
+ let nl: number;
48
+ while ((nl = buf.indexOf('\n')) >= 0) {
49
+ const line = buf.slice(0, nl).trim();
50
+ buf = buf.slice(nl + 1);
51
+ if (!line) continue;
52
+ let msg: any;
53
+ try { msg = JSON.parse(line); } catch { continue; }
54
+ if (msg.id != null && pending.has(msg.id)) {
55
+ pending.get(msg.id)!(msg);
56
+ pending.delete(msg.id);
57
+ }
58
+ }
59
+ });
60
+
61
+ let nextId = 1;
62
+ const call = (method: string, params: any): Promise<any> => {
63
+ const id = nextId++;
64
+ return new Promise((resolve, reject) => {
65
+ pending.set(id, resolve);
66
+ proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
67
+ setTimeout(() => {
68
+ if (pending.has(id)) { pending.delete(id); reject(new Error(`timeout ${method}`)); }
69
+ }, 30_000);
70
+ });
71
+ };
72
+
73
+ // Wait for ready
74
+ let initOk = false;
75
+ for (let i = 0; i < 30; i++) {
76
+ try {
77
+ const r = await call('initialize', {
78
+ protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 't', version: '0' },
79
+ });
80
+ if (r.result) { initOk = true; break; }
81
+ } catch { /* */ }
82
+ await new Promise(r => setTimeout(r, 500));
83
+ }
84
+ if (initOk) ok('initialize'); else { bad('initialize'); process.exit(1); }
85
+
86
+ // tools/list — verify Track-F tools are advertised
87
+ const list = await call('tools/list', {});
88
+ const names: string[] = (list.result?.tools ?? []).map((t: any) => t.name);
89
+ const trackfTools = [
90
+ 'seer_bundle_export', 'seer_bundle_info', 'seer_bundle_import',
91
+ 'seer_scip_import', 'seer_scip_imports', 'seer_provenance',
92
+ 'seer_duplicates', 'seer_shape_hash_build',
93
+ ];
94
+ for (const n of trackfTools) {
95
+ if (names.includes(n)) ok(`tools/list advertises ${n}`);
96
+ else bad(`tools/list missing ${n}`, names);
97
+ }
98
+
99
+ const callTool = async (name: string, args: any = {}): Promise<any> => {
100
+ const r = await call('tools/call', { name, arguments: args });
101
+ return JSON.parse(r.result?.content?.[0]?.text ?? '{}');
102
+ };
103
+
104
+ // seer_health surfaces v7 fields
105
+ const health = await callTool('seer_health', {});
106
+ if (health.schemaVersion === 10) ok(`seer_health.schemaVersion = 10`);
107
+ else bad(`seer_health.schemaVersion not 9`, health);
108
+ if (health.provenance && health.provenance.symbols)
109
+ ok(`seer_health.provenance present`);
110
+ else bad('seer_health.provenance missing', health);
111
+ if (typeof health.shapeHashed === 'number' && health.shapeHashed >= 3)
112
+ ok(`seer_health.shapeHashed = ${health.shapeHashed}`);
113
+ else bad('seer_health.shapeHashed low', health);
114
+ if (typeof health.scipImports === 'number' && health.scipImports === 0)
115
+ ok('seer_health.scipImports = 0 pre-SCIP');
116
+ else bad('seer_health.scipImports unexpected', health);
117
+
118
+ // seer_provenance
119
+ const prov = await callTool('seer_provenance', {});
120
+ if (prov.provenance?.symbols?.['tree-sitter'] >= 3)
121
+ ok(`seer_provenance has tree-sitter symbols`);
122
+ else bad('seer_provenance missing tree-sitter symbols', prov);
123
+ if (Array.isArray(prov.scipImports)) ok('seer_provenance.scipImports is an array');
124
+ else bad('seer_provenance.scipImports not array', prov);
125
+
126
+ // seer_duplicates — fetchUserById ≡ fetchOrderById
127
+ const dupes = await callTool('seer_duplicates', { maxDistance: 4, minLoc: 3 });
128
+ if (dupes.clusters >= 1) ok(`seer_duplicates found ${dupes.clusters} cluster(s)`);
129
+ else bad('seer_duplicates empty', dupes);
130
+ const fetchPair = (dupes.items ?? []).find((c: any) =>
131
+ c.symbols?.some((s: any) => s.name === 'fetchUserById') &&
132
+ c.symbols?.some((s: any) => s.name === 'fetchOrderById'));
133
+ if (fetchPair) ok('seer_duplicates clusters fetchUserById + fetchOrderById together');
134
+ else bad('seer_duplicates missing fetch* cluster', dupes.items);
135
+
136
+ // seer_shape_hash_build is idempotent
137
+ const sh = await callTool('seer_shape_hash_build', {});
138
+ if (typeof sh.symbolsHashed === 'number' && sh.symbolsHashed === 0)
139
+ ok('seer_shape_hash_build idempotent (re-run hashed 0)');
140
+ else bad('seer_shape_hash_build not idempotent', sh);
141
+
142
+ // seer_bundle_export
143
+ const bundleOut = path.join(TMP_WS, 'mcp-export.seerbundle');
144
+ const exp = await callTool('seer_bundle_export', { out: bundleOut, compressionLevel: 9 });
145
+ if (exp.bundlePath === bundleOut) ok('seer_bundle_export writes to requested path');
146
+ else bad('seer_bundle_export wrong path', exp);
147
+ if (fs.existsSync(bundleOut)) ok('seer_bundle_export bundle exists on disk');
148
+ else bad('bundle not on disk', exp);
149
+ if (exp.manifest?.schemaVersion === 10) ok('exported manifest.schemaVersion=10');
150
+ else bad('exported manifest wrong', exp.manifest);
151
+
152
+ // seer_bundle_info — manifest peek
153
+ const info = await callTool('seer_bundle_info', { bundle: bundleOut });
154
+ if (info.schemaVersion === 10) ok('seer_bundle_info reads manifest');
155
+ else bad('seer_bundle_info wrong', info);
156
+ if (info.index?.symbols >= 3) ok('seer_bundle_info reports symbol count');
157
+ else bad('seer_bundle_info missing symbols', info.index);
158
+
159
+ // Bad bundle path → graceful error envelope
160
+ const badInfo = await callTool('seer_bundle_info', { bundle: path.join(TMP_WS, 'no-such.bundle') });
161
+ if (badInfo.ok === false) ok('seer_bundle_info(missing path) → ok=false');
162
+ else bad('seer_bundle_info did not surface missing', badInfo);
163
+
164
+ // seer_scip_import — author + import a tiny SCIP doc
165
+ const scipJson = {
166
+ tool: 'scip-mcp-test/0.0.1',
167
+ documents: [
168
+ {
169
+ relativePath: 'src/auth.ts',
170
+ symbols: [
171
+ {
172
+ symbolId: 'auth#login',
173
+ displayName: 'login',
174
+ qualifiedName: 'AuthService.login',
175
+ kind: 'method',
176
+ relativePath: 'src/auth.ts',
177
+ range: { startLine: 3, startCharacter: 0, endLine: 6, endCharacter: 1 },
178
+ },
179
+ ],
180
+ occurrences: [],
181
+ },
182
+ ],
183
+ };
184
+ const scipPath = path.join(TMP_WS, 'auth.scip.json');
185
+ fs.writeFileSync(scipPath, JSON.stringify(scipJson));
186
+ const scipImp = await callTool('seer_scip_import', { scipPath });
187
+ if (scipImp.documentsProcessed === 1) ok('seer_scip_import processed 1 doc');
188
+ else bad('seer_scip_import wrong doc count', scipImp);
189
+ if ((scipImp.symbolsMerged ?? 0) >= 1) ok('seer_scip_import merged ≥1 existing symbol');
190
+ else bad('seer_scip_import did not merge', scipImp);
191
+
192
+ // seer_scip_imports — listing
193
+ const sciList = await callTool('seer_scip_imports', {});
194
+ if (Array.isArray(sciList.items) && sciList.items.length === 1)
195
+ ok('seer_scip_imports lists 1 entry post-import');
196
+ else bad('seer_scip_imports listing wrong', sciList);
197
+ if (sciList.provenance?.symbols['scip-merge'] >= 1)
198
+ ok('seer_scip_imports provenance includes scip-merge bucket');
199
+ else bad('scip-merge bucket missing', sciList.provenance);
200
+
201
+ // Re-import is idempotent
202
+ const scipImp2 = await callTool('seer_scip_import', { scipPath });
203
+ if (scipImp2.symbolsInserted === 0 && scipImp2.symbolsMerged === 0)
204
+ ok('seer_scip_import is idempotent on same path+sha');
205
+ else bad('seer_scip_import not idempotent', scipImp2);
206
+
207
+ // seer_bundle_import — round-trip the just-exported bundle into a fresh
208
+ // location (overwrite=true since the workspace already has a DB).
209
+ // Importing back over the live DB is supported via overwrite=true; the
210
+ // server re-opens transparently.
211
+ const reImport = await callTool('seer_bundle_import', {
212
+ bundle: bundleOut, overwrite: true,
213
+ });
214
+ if (reImport.ok === true) ok('seer_bundle_import overwrite=true succeeds');
215
+ else bad('seer_bundle_import failed', reImport);
216
+
217
+ // Health after re-import still healthy.
218
+ const health2 = await callTool('seer_health', {});
219
+ if (health2.schemaVersion === 10) ok('post-import seer_health.schemaVersion = 10');
220
+ else bad('post-import schema wrong', health2);
221
+
222
+ // Bundle import refuses missing file
223
+ const importMissing = await callTool('seer_bundle_import', {
224
+ bundle: path.join(TMP_WS, 'no-such.bundle'), overwrite: true,
225
+ });
226
+ if (importMissing.ok === false) ok('seer_bundle_import(missing) → ok=false');
227
+ else bad('seer_bundle_import did not refuse missing', importMissing);
228
+
229
+ proc.stdin.end(); proc.kill();
230
+ await new Promise(r => setTimeout(r, 200));
231
+
232
+ // ── Bug 4 regression at the MCP layer ────────────────────────────────
233
+ // Spin up a second MCP server with --db <customPath>, import the bundle,
234
+ // then confirm the imported DB lives at customPath rather than the
235
+ // workspace default. Pre-fix, the bundle landed at <workspace>/.seer/
236
+ // graph.db while the server kept reading customPath.
237
+ console.log('\n── Bug 4: MCP --db honoured by bundle import ──');
238
+ const customWs = TMP_WS + '-custom';
239
+ copyRecursive(FIX, customWs);
240
+ const customDb = path.join(customWs, 'sub', 'elsewhere.db');
241
+ fs.mkdirSync(path.dirname(customDb), { recursive: true });
242
+
243
+ const bundlePath = path.join(TMP_WS, 'mcp-export.seerbundle');
244
+ // Sanity: the bundle was already exported by the first server above; reuse it.
245
+ if (!fs.existsSync(bundlePath)) {
246
+ bad('bundle from first phase missing — cannot test --db');
247
+ } else {
248
+ const proc2 = spawn(process.execPath,
249
+ [CLI, 'mcp', '--workspace', customWs, '--db', customDb, '--no-watch', '--no-jit'],
250
+ { stdio: ['pipe', 'pipe', 'pipe'] });
251
+ proc2.stderr.on('data', (d) => process.stderr.write(`[mcp2-stderr] ${d}`));
252
+
253
+ let buf2 = '';
254
+ const pending2 = new Map<number, (msg: any) => void>();
255
+ proc2.stdout.on('data', (chunk: Buffer) => {
256
+ buf2 += chunk.toString('utf8');
257
+ let nl: number;
258
+ while ((nl = buf2.indexOf('\n')) >= 0) {
259
+ const line = buf2.slice(0, nl).trim();
260
+ buf2 = buf2.slice(nl + 1);
261
+ if (!line) continue;
262
+ let msg: any;
263
+ try { msg = JSON.parse(line); } catch { continue; }
264
+ if (msg.id != null && pending2.has(msg.id)) {
265
+ pending2.get(msg.id)!(msg);
266
+ pending2.delete(msg.id);
267
+ }
268
+ }
269
+ });
270
+ let id2 = 1;
271
+ const call2 = (method: string, params: any): Promise<any> => {
272
+ const id = id2++;
273
+ return new Promise((resolve, reject) => {
274
+ pending2.set(id, resolve);
275
+ proc2.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
276
+ setTimeout(() => { if (pending2.has(id)) { pending2.delete(id); reject(new Error(`timeout ${method}`)); } }, 30_000);
277
+ });
278
+ };
279
+ let init2Ok = false;
280
+ for (let i = 0; i < 30; i++) {
281
+ try {
282
+ const r = await call2('initialize', {
283
+ protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 't', version: '0' },
284
+ });
285
+ if (r.result) { init2Ok = true; break; }
286
+ } catch { /* */ }
287
+ await new Promise(r => setTimeout(r, 500));
288
+ }
289
+ if (!init2Ok) { bad('initialize on --db server'); }
290
+ else {
291
+ ok('--db server initialized');
292
+ // Health should report a non-empty index because MCP's first-touch
293
+ // logic runs `indexDirectory` against customDb if it's empty.
294
+ const r = await call2('tools/call', {
295
+ name: 'seer_bundle_import',
296
+ arguments: { bundle: bundlePath, overwrite: true },
297
+ });
298
+ const out = JSON.parse(r.result?.content?.[0]?.text ?? '{}');
299
+ if (out.ok === true && out.dbPath === customDb)
300
+ ok(`bundle_import landed at --db path (${customDb})`);
301
+ else bad('bundle_import did not land at --db path', out);
302
+
303
+ // Default workspace path must NOT have been created.
304
+ const defaultPath = path.join(customWs, '.seer', 'graph.db');
305
+ if (!fs.existsSync(defaultPath))
306
+ ok('default <workspace>/.seer/graph.db was NOT created by import');
307
+ else bad('default DB path was clobbered despite --db override');
308
+
309
+ // Health after import still reports the schema.
310
+ const h2 = await call2('tools/call', {
311
+ name: 'seer_health', arguments: {},
312
+ });
313
+ const health2 = JSON.parse(h2.result?.content?.[0]?.text ?? '{}');
314
+ if (health2.schemaVersion === 10 && health2.dbPath === customDb)
315
+ ok('post-import health.dbPath still equals --db override');
316
+ else bad('post-import health did not honour --db', health2);
317
+ }
318
+ proc2.stdin.end(); proc2.kill();
319
+ await new Promise(r => setTimeout(r, 200));
320
+ }
321
+
322
+ try { fs.rmSync(TMP_WS, { recursive: true, force: true }); } catch { /* */ }
323
+ try { fs.rmSync(customWs, { recursive: true, force: true }); } catch { /* */ }
324
+
325
+ console.log(`\n══════════════════════════════════════════════════════════════`);
326
+ console.log(` MCP Track-F: ${passed} passed, ${failed} failed`);
327
+ if (failed > 0) process.exit(1);
328
+ }
329
+
330
+ main().catch(err => { console.error('mcp-trackf crashed:', err); process.exit(1); });
@@ -0,0 +1,219 @@
1
+ /**
2
+ * MCP smoke test for Track G — service-link tools.
3
+ * Spawns `seer mcp` against the fixtures-service workspace and exercises
4
+ * seer_service_calls, seer_service_links, seer_trace_service_path.
5
+ *
6
+ * Run: npx tsx tests/mcp-trackg.ts
7
+ */
8
+
9
+ import { spawn } from 'child_process';
10
+ import path from 'path';
11
+ import fs from 'fs';
12
+ import os from 'os';
13
+
14
+ const ROOT = path.resolve(__dirname, '..');
15
+ const FIX = path.join(ROOT, 'tests/fixtures-service');
16
+ const TMP_WS = path.join(os.tmpdir(), `seer-mcp-g-${Date.now()}`);
17
+ const CLI = path.join(ROOT, 'dist/cli/index.js');
18
+
19
+ let passed = 0;
20
+ let failed = 0;
21
+ const ok = (m: string): void => { passed++; console.log(` ✓ ${m}`); };
22
+ const bad = (m: string, x?: unknown): void => {
23
+ failed++;
24
+ console.error(` ✗ ${m}` + (x !== undefined ? ` :: ${JSON.stringify(x).slice(0, 400)}` : ''));
25
+ };
26
+
27
+ function copyRecursive(src: string, dst: string): void {
28
+ fs.mkdirSync(dst, { recursive: true });
29
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
30
+ const s = path.join(src, entry.name);
31
+ const d = path.join(dst, entry.name);
32
+ if (entry.isDirectory()) copyRecursive(s, d);
33
+ else fs.copyFileSync(s, d);
34
+ }
35
+ }
36
+
37
+ async function main(): Promise<void> {
38
+ console.log('\nSeer MCP Track-G Smoke');
39
+ console.log('======================\n');
40
+ copyRecursive(FIX, TMP_WS);
41
+ console.log(` Workspace: ${TMP_WS}`);
42
+
43
+ const proc = spawn(process.execPath,
44
+ [CLI, 'mcp', '--workspace', TMP_WS, '--no-watch', '--no-jit'],
45
+ { stdio: ['pipe', 'pipe', 'pipe'] });
46
+ proc.stderr.on('data', d => process.stderr.write(`[mcp-stderr] ${d}`));
47
+
48
+ let buf = '';
49
+ const pending = new Map<number, (msg: any) => void>();
50
+ proc.stdout.on('data', (chunk: Buffer) => {
51
+ buf += chunk.toString('utf8');
52
+ let nl: number;
53
+ while ((nl = buf.indexOf('\n')) >= 0) {
54
+ const line = buf.slice(0, nl).trim();
55
+ buf = buf.slice(nl + 1);
56
+ if (!line) continue;
57
+ let msg: any;
58
+ try { msg = JSON.parse(line); } catch { continue; }
59
+ if (msg.id != null && pending.has(msg.id)) {
60
+ pending.get(msg.id)!(msg);
61
+ pending.delete(msg.id);
62
+ }
63
+ }
64
+ });
65
+
66
+ let nextId = 1;
67
+ const call = (method: string, params: any): Promise<any> => {
68
+ const id = nextId++;
69
+ return new Promise((resolve, reject) => {
70
+ pending.set(id, resolve);
71
+ proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
72
+ setTimeout(() => {
73
+ if (pending.has(id)) { pending.delete(id); reject(new Error(`timeout ${method}`)); }
74
+ }, 30_000);
75
+ });
76
+ };
77
+
78
+ let initOk = false;
79
+ for (let i = 0; i < 30; i++) {
80
+ try {
81
+ const r = await call('initialize', {
82
+ protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 't', version: '0' },
83
+ });
84
+ if (r.result) { initOk = true; break; }
85
+ } catch { /* */ }
86
+ await new Promise(r => setTimeout(r, 500));
87
+ }
88
+ if (initOk) ok('initialize'); else { bad('initialize'); process.exit(1); }
89
+
90
+ const list = await call('tools/list', {});
91
+ const names: string[] = (list.result?.tools ?? []).map((t: any) => t.name);
92
+ for (const n of ['seer_service_calls', 'seer_service_links', 'seer_trace_service_path']) {
93
+ if (names.includes(n)) ok(`tools/list advertises ${n}`);
94
+ else bad(`tools/list missing ${n}`, names);
95
+ }
96
+
97
+ const callTool = async (name: string, args: any = {}): Promise<any> => {
98
+ const r = await call('tools/call', { name, arguments: args });
99
+ return JSON.parse(r.result?.content?.[0]?.text ?? '{}');
100
+ };
101
+
102
+ // seer_service_calls
103
+ const sc = await callTool('seer_service_calls', { limit: 100 });
104
+ if (sc.total > 0) ok(`seer_service_calls.total = ${sc.total}`);
105
+ else bad('seer_service_calls.total is 0', sc);
106
+ if (Array.isArray(sc.items) && sc.items.length > 0) ok('seer_service_calls.items non-empty');
107
+ else bad('seer_service_calls.items empty', sc);
108
+ const charge = sc.items?.find((r: any) => r.rawTarget === '/api/charge');
109
+ if (charge) ok('seer_service_calls includes /api/charge');
110
+ else bad('seer_service_calls missing /api/charge', sc.items);
111
+
112
+ // pagination
113
+ const page1 = await callTool('seer_service_calls', { limit: 2, offset: 0 });
114
+ const page2 = await callTool('seer_service_calls', { limit: 2, offset: 2 });
115
+ if ((page1.items?.[0]?.id ?? -1) !== (page2.items?.[0]?.id ?? -2)) ok('pagination paginates');
116
+ else bad('pagination did not move offset', { page1, page2 });
117
+
118
+ // filter by framework
119
+ const fetched = await callTool('seer_service_calls', { framework: 'fetch' });
120
+ if ((fetched.items ?? []).every((r: any) => r.framework === 'fetch'))
121
+ ok('framework filter applied');
122
+ else bad('framework filter not applied', fetched.items);
123
+
124
+ // summaryOnly
125
+ const summary = await callTool('seer_service_calls', { summaryOnly: true });
126
+ if (typeof summary.total === 'number' && !summary.items) ok('summaryOnly returns counts only');
127
+ else bad('summaryOnly returned items', summary);
128
+
129
+ // seer_service_links
130
+ const sl = await callTool('seer_service_links', { limit: 50 });
131
+ if (sl.total >= 2) ok(`seer_service_links.total ≥ 2 (got ${sl.total})`);
132
+ else bad('seer_service_links.total < 2', sl);
133
+ const chargeLink = sl.items?.find((r: any) => r.routePath === '/api/charge');
134
+ if (chargeLink) {
135
+ ok('seer_service_links includes /api/charge link');
136
+ if (chargeLink.callerQualifiedName === 'processPayment') ok('link caller = processPayment');
137
+ else bad('link caller wrong', chargeLink);
138
+ if (chargeLink.handlerQualifiedName === 'chargeHandler') ok('link handler = chargeHandler');
139
+ else bad('link handler wrong', chargeLink);
140
+ if (chargeLink.matchKind === 'literal_path') ok('link match_kind = literal_path');
141
+ else bad('link match_kind wrong', chargeLink);
142
+ } else bad('seer_service_links missing /api/charge', sl.items);
143
+
144
+ // filter by matchKind
145
+ const patterns = await callTool('seer_service_links', { matchKind: 'route_pattern' });
146
+ if ((patterns.items ?? []).every((r: any) => r.matchKind === 'route_pattern'))
147
+ ok('matchKind filter applied');
148
+ else bad('matchKind filter not applied', patterns.items);
149
+
150
+ // seer_trace_service_path
151
+ const trace = await callTool('seer_trace_service_path', {
152
+ from: 'processPayment', to: 'chargeHandler', maxDepth: 4,
153
+ });
154
+ if (trace.ok && trace.found) ok('trace_service_path found');
155
+ else bad('trace_service_path did not find path', trace);
156
+ if (trace.path?.length === 2) ok('trace path is 2 hops');
157
+ else bad('trace path length unexpected', trace);
158
+
159
+ const noTrace = await callTool('seer_trace_service_path', {
160
+ from: 'processPayment', to: 'nonexistentSymbolXYZ', maxDepth: 4,
161
+ });
162
+ if (noTrace.ok === false) ok('trace_service_path returns ok=false on missing target');
163
+ else bad('trace_service_path did not refuse missing target', noTrace);
164
+
165
+ // v9 Track-H Step 8: seer_trace_service_dependencies
166
+ const tools = await call('tools/list', {});
167
+ const toolNames = (tools.result?.tools ?? []).map((t: any) => t.name);
168
+ if (toolNames.includes('seer_trace_service_dependencies'))
169
+ ok('tools/list advertises seer_trace_service_dependencies');
170
+ else bad('seer_trace_service_dependencies missing from tools/list', toolNames);
171
+ if (toolNames.includes('seer_trace_module_service_dependencies'))
172
+ ok('tools/list advertises seer_trace_module_service_dependencies');
173
+ else bad('seer_trace_module_service_dependencies missing from tools/list', toolNames);
174
+
175
+ const deps = await callTool('seer_trace_service_dependencies', {
176
+ from: 'processPayment', maxDepth: 4, maxNodes: 50,
177
+ });
178
+ if (deps.ok && Array.isArray(deps.items))
179
+ ok(`seer_trace_service_dependencies returned ${deps.items.length} items`);
180
+ else bad('seer_trace_service_dependencies failed', deps);
181
+ if ((deps.items ?? []).some((i: any) => i.qualifiedName === 'chargeHandler'))
182
+ ok('processPayment reaches chargeHandler');
183
+ else bad('chargeHandler not in reached set', deps.items);
184
+
185
+ const bounded = await callTool('seer_trace_service_dependencies', {
186
+ from: 'processPayment', maxDepth: 4, maxNodes: 1,
187
+ });
188
+ if (bounded.ok && bounded.items.length <= 1)
189
+ ok('trace_service_dependencies honours maxNodes cap (limit 1)');
190
+ else bad('trace_service_dependencies did not cut off', bounded);
191
+
192
+ const noFrom = await callTool('seer_trace_service_dependencies', {
193
+ from: 'nonexistentSymbolXYZ',
194
+ });
195
+ if (noFrom.ok === false) ok('trace_service_dependencies returns ok=false on missing source');
196
+ else bad('trace_service_dependencies did not refuse missing source', noFrom);
197
+
198
+ const modDeps = await callTool('seer_trace_module_service_dependencies', {
199
+ moduleId: 1, maxDepth: 2, maxNodes: 10,
200
+ });
201
+ if (modDeps.ok && Array.isArray(modDeps.items))
202
+ ok(`trace_module_service_dependencies returned ${modDeps.items.length} items`);
203
+ else bad('trace_module_service_dependencies failed', modDeps);
204
+
205
+ // seer_health surfaces v9 fields
206
+ const health = await callTool('seer_health', {});
207
+ if (health.schemaVersion === 10) ok('seer_health.schemaVersion = 10');
208
+ else bad('seer_health.schemaVersion not 9', health);
209
+
210
+ proc.stdin.end(); proc.kill();
211
+ await new Promise(r => setTimeout(r, 200));
212
+ try { fs.rmSync(TMP_WS, { recursive: true, force: true }); } catch { /* */ }
213
+
214
+ console.log(`\n══════════════════════════════════════════════════════════════`);
215
+ console.log(` MCP Track-G: ${passed} passed, ${failed} failed`);
216
+ if (failed > 0) process.exit(1);
217
+ }
218
+
219
+ main().catch(err => { console.error('mcp-trackg crashed:', err); process.exit(1); });