purecontext-mcp 1.0.1

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 (542) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +207 -0
  3. package/dist/adapters/actix-web.d.ts +28 -0
  4. package/dist/adapters/actix-web.d.ts.map +1 -0
  5. package/dist/adapters/actix-web.js +362 -0
  6. package/dist/adapters/actix-web.js.map +1 -0
  7. package/dist/adapters/adapter-registry.d.ts +34 -0
  8. package/dist/adapters/adapter-registry.d.ts.map +1 -0
  9. package/dist/adapters/adapter-registry.js +92 -0
  10. package/dist/adapters/adapter-registry.js.map +1 -0
  11. package/dist/adapters/angular.d.ts +13 -0
  12. package/dist/adapters/angular.d.ts.map +1 -0
  13. package/dist/adapters/angular.js +348 -0
  14. package/dist/adapters/angular.js.map +1 -0
  15. package/dist/adapters/axum.d.ts +16 -0
  16. package/dist/adapters/axum.d.ts.map +1 -0
  17. package/dist/adapters/axum.js +299 -0
  18. package/dist/adapters/axum.js.map +1 -0
  19. package/dist/adapters/django-orm.d.ts +28 -0
  20. package/dist/adapters/django-orm.d.ts.map +1 -0
  21. package/dist/adapters/django-orm.js +464 -0
  22. package/dist/adapters/django-orm.js.map +1 -0
  23. package/dist/adapters/django.d.ts +16 -0
  24. package/dist/adapters/django.d.ts.map +1 -0
  25. package/dist/adapters/django.js +257 -0
  26. package/dist/adapters/django.js.map +1 -0
  27. package/dist/adapters/echo.d.ts +15 -0
  28. package/dist/adapters/echo.d.ts.map +1 -0
  29. package/dist/adapters/echo.js +142 -0
  30. package/dist/adapters/echo.js.map +1 -0
  31. package/dist/adapters/express.d.ts +15 -0
  32. package/dist/adapters/express.d.ts.map +1 -0
  33. package/dist/adapters/express.js +126 -0
  34. package/dist/adapters/express.js.map +1 -0
  35. package/dist/adapters/fastapi.d.ts +16 -0
  36. package/dist/adapters/fastapi.d.ts.map +1 -0
  37. package/dist/adapters/fastapi.js +113 -0
  38. package/dist/adapters/fastapi.js.map +1 -0
  39. package/dist/adapters/fastify.d.ts +14 -0
  40. package/dist/adapters/fastify.d.ts.map +1 -0
  41. package/dist/adapters/fastify.js +124 -0
  42. package/dist/adapters/fastify.js.map +1 -0
  43. package/dist/adapters/fiber.d.ts +15 -0
  44. package/dist/adapters/fiber.d.ts.map +1 -0
  45. package/dist/adapters/fiber.js +147 -0
  46. package/dist/adapters/fiber.js.map +1 -0
  47. package/dist/adapters/flask.d.ts +18 -0
  48. package/dist/adapters/flask.d.ts.map +1 -0
  49. package/dist/adapters/flask.js +175 -0
  50. package/dist/adapters/flask.js.map +1 -0
  51. package/dist/adapters/flutter.d.ts +19 -0
  52. package/dist/adapters/flutter.d.ts.map +1 -0
  53. package/dist/adapters/flutter.js +251 -0
  54. package/dist/adapters/flutter.js.map +1 -0
  55. package/dist/adapters/gin.d.ts +16 -0
  56. package/dist/adapters/gin.d.ts.map +1 -0
  57. package/dist/adapters/gin.js +159 -0
  58. package/dist/adapters/gin.js.map +1 -0
  59. package/dist/adapters/hibernate.d.ts +24 -0
  60. package/dist/adapters/hibernate.d.ts.map +1 -0
  61. package/dist/adapters/hibernate.js +448 -0
  62. package/dist/adapters/hibernate.js.map +1 -0
  63. package/dist/adapters/ktor.d.ts +18 -0
  64. package/dist/adapters/ktor.d.ts.map +1 -0
  65. package/dist/adapters/ktor.js +219 -0
  66. package/dist/adapters/ktor.js.map +1 -0
  67. package/dist/adapters/laravel.d.ts +19 -0
  68. package/dist/adapters/laravel.d.ts.map +1 -0
  69. package/dist/adapters/laravel.js +370 -0
  70. package/dist/adapters/laravel.js.map +1 -0
  71. package/dist/adapters/micronaut.d.ts +21 -0
  72. package/dist/adapters/micronaut.d.ts.map +1 -0
  73. package/dist/adapters/micronaut.js +435 -0
  74. package/dist/adapters/micronaut.js.map +1 -0
  75. package/dist/adapters/nestjs.d.ts +12 -0
  76. package/dist/adapters/nestjs.d.ts.map +1 -0
  77. package/dist/adapters/nestjs.js +401 -0
  78. package/dist/adapters/nestjs.js.map +1 -0
  79. package/dist/adapters/nextjs.d.ts +49 -0
  80. package/dist/adapters/nextjs.d.ts.map +1 -0
  81. package/dist/adapters/nextjs.js +412 -0
  82. package/dist/adapters/nextjs.js.map +1 -0
  83. package/dist/adapters/nuxt.d.ts +44 -0
  84. package/dist/adapters/nuxt.d.ts.map +1 -0
  85. package/dist/adapters/nuxt.js +243 -0
  86. package/dist/adapters/nuxt.js.map +1 -0
  87. package/dist/adapters/quarkus.d.ts +21 -0
  88. package/dist/adapters/quarkus.d.ts.map +1 -0
  89. package/dist/adapters/quarkus.js +395 -0
  90. package/dist/adapters/quarkus.js.map +1 -0
  91. package/dist/adapters/rails.d.ts +18 -0
  92. package/dist/adapters/rails.d.ts.map +1 -0
  93. package/dist/adapters/rails.js +363 -0
  94. package/dist/adapters/rails.js.map +1 -0
  95. package/dist/adapters/react.d.ts +18 -0
  96. package/dist/adapters/react.d.ts.map +1 -0
  97. package/dist/adapters/react.js +95 -0
  98. package/dist/adapters/react.js.map +1 -0
  99. package/dist/adapters/rocket.d.ts +19 -0
  100. package/dist/adapters/rocket.d.ts.map +1 -0
  101. package/dist/adapters/rocket.js +271 -0
  102. package/dist/adapters/rocket.js.map +1 -0
  103. package/dist/adapters/sinatra.d.ts +16 -0
  104. package/dist/adapters/sinatra.d.ts.map +1 -0
  105. package/dist/adapters/sinatra.js +114 -0
  106. package/dist/adapters/sinatra.js.map +1 -0
  107. package/dist/adapters/spring-boot.d.ts +24 -0
  108. package/dist/adapters/spring-boot.d.ts.map +1 -0
  109. package/dist/adapters/spring-boot.js +399 -0
  110. package/dist/adapters/spring-boot.js.map +1 -0
  111. package/dist/adapters/spring-kotlin.d.ts +19 -0
  112. package/dist/adapters/spring-kotlin.d.ts.map +1 -0
  113. package/dist/adapters/spring-kotlin.js +297 -0
  114. package/dist/adapters/spring-kotlin.js.map +1 -0
  115. package/dist/adapters/sqlalchemy.d.ts +28 -0
  116. package/dist/adapters/sqlalchemy.d.ts.map +1 -0
  117. package/dist/adapters/sqlalchemy.js +435 -0
  118. package/dist/adapters/sqlalchemy.js.map +1 -0
  119. package/dist/adapters/symfony.d.ts +16 -0
  120. package/dist/adapters/symfony.d.ts.map +1 -0
  121. package/dist/adapters/symfony.js +242 -0
  122. package/dist/adapters/symfony.js.map +1 -0
  123. package/dist/adapters/vapor.d.ts +21 -0
  124. package/dist/adapters/vapor.d.ts.map +1 -0
  125. package/dist/adapters/vapor.js +269 -0
  126. package/dist/adapters/vapor.js.map +1 -0
  127. package/dist/adapters/vue-preprocessor.d.ts +26 -0
  128. package/dist/adapters/vue-preprocessor.d.ts.map +1 -0
  129. package/dist/adapters/vue-preprocessor.js +82 -0
  130. package/dist/adapters/vue-preprocessor.js.map +1 -0
  131. package/dist/adapters/vue.d.ts +13 -0
  132. package/dist/adapters/vue.d.ts.map +1 -0
  133. package/dist/adapters/vue.js +134 -0
  134. package/dist/adapters/vue.js.map +1 -0
  135. package/dist/config/cli.d.ts +26 -0
  136. package/dist/config/cli.d.ts.map +1 -0
  137. package/dist/config/cli.js +291 -0
  138. package/dist/config/cli.js.map +1 -0
  139. package/dist/config/config-loader.d.ts +26 -0
  140. package/dist/config/config-loader.d.ts.map +1 -0
  141. package/dist/config/config-loader.js +168 -0
  142. package/dist/config/config-loader.js.map +1 -0
  143. package/dist/config/config-schema.d.ts +228 -0
  144. package/dist/config/config-schema.d.ts.map +1 -0
  145. package/dist/config/config-schema.js +400 -0
  146. package/dist/config/config-schema.js.map +1 -0
  147. package/dist/config/keys-cli.d.ts +37 -0
  148. package/dist/config/keys-cli.d.ts.map +1 -0
  149. package/dist/config/keys-cli.js +179 -0
  150. package/dist/config/keys-cli.js.map +1 -0
  151. package/dist/config/workspaces-cli.d.ts +2 -0
  152. package/dist/config/workspaces-cli.d.ts.map +1 -0
  153. package/dist/config/workspaces-cli.js +84 -0
  154. package/dist/config/workspaces-cli.js.map +1 -0
  155. package/dist/core/db/api-keys.d.ts +95 -0
  156. package/dist/core/db/api-keys.d.ts.map +1 -0
  157. package/dist/core/db/api-keys.js +293 -0
  158. package/dist/core/db/api-keys.js.map +1 -0
  159. package/dist/core/db/dep-store.d.ts +18 -0
  160. package/dist/core/db/dep-store.d.ts.map +1 -0
  161. package/dist/core/db/dep-store.js +155 -0
  162. package/dist/core/db/dep-store.js.map +1 -0
  163. package/dist/core/db/embedding-store.d.ts +40 -0
  164. package/dist/core/db/embedding-store.d.ts.map +1 -0
  165. package/dist/core/db/embedding-store.js +120 -0
  166. package/dist/core/db/embedding-store.js.map +1 -0
  167. package/dist/core/db/file-store.d.ts +27 -0
  168. package/dist/core/db/file-store.d.ts.map +1 -0
  169. package/dist/core/db/file-store.js +101 -0
  170. package/dist/core/db/file-store.js.map +1 -0
  171. package/dist/core/db/request-log.d.ts +40 -0
  172. package/dist/core/db/request-log.d.ts.map +1 -0
  173. package/dist/core/db/request-log.js +90 -0
  174. package/dist/core/db/request-log.js.map +1 -0
  175. package/dist/core/db/savings-store.d.ts +9 -0
  176. package/dist/core/db/savings-store.d.ts.map +1 -0
  177. package/dist/core/db/savings-store.js +64 -0
  178. package/dist/core/db/savings-store.js.map +1 -0
  179. package/dist/core/db/schema.d.ts +14 -0
  180. package/dist/core/db/schema.d.ts.map +1 -0
  181. package/dist/core/db/schema.js +235 -0
  182. package/dist/core/db/schema.js.map +1 -0
  183. package/dist/core/db/symbol-store.d.ts +64 -0
  184. package/dist/core/db/symbol-store.d.ts.map +1 -0
  185. package/dist/core/db/symbol-store.js +243 -0
  186. package/dist/core/db/symbol-store.js.map +1 -0
  187. package/dist/core/db/tenants.d.ts +91 -0
  188. package/dist/core/db/tenants.d.ts.map +1 -0
  189. package/dist/core/db/tenants.js +219 -0
  190. package/dist/core/db/tenants.js.map +1 -0
  191. package/dist/core/errors.d.ts +72 -0
  192. package/dist/core/errors.d.ts.map +1 -0
  193. package/dist/core/errors.js +140 -0
  194. package/dist/core/errors.js.map +1 -0
  195. package/dist/core/file-discovery.d.ts +17 -0
  196. package/dist/core/file-discovery.d.ts.map +1 -0
  197. package/dist/core/file-discovery.js +136 -0
  198. package/dist/core/file-discovery.js.map +1 -0
  199. package/dist/core/file-processor.d.ts +21 -0
  200. package/dist/core/file-processor.d.ts.map +1 -0
  201. package/dist/core/file-processor.js +110 -0
  202. package/dist/core/file-processor.js.map +1 -0
  203. package/dist/core/hash-cache.d.ts +18 -0
  204. package/dist/core/hash-cache.d.ts.map +1 -0
  205. package/dist/core/hash-cache.js +35 -0
  206. package/dist/core/hash-cache.js.map +1 -0
  207. package/dist/core/index-manager.d.ts +18 -0
  208. package/dist/core/index-manager.d.ts.map +1 -0
  209. package/dist/core/index-manager.js +461 -0
  210. package/dist/core/index-manager.js.map +1 -0
  211. package/dist/core/indexing-worker.d.ts +60 -0
  212. package/dist/core/indexing-worker.d.ts.map +1 -0
  213. package/dist/core/indexing-worker.js +128 -0
  214. package/dist/core/indexing-worker.js.map +1 -0
  215. package/dist/core/logger.d.ts +10 -0
  216. package/dist/core/logger.d.ts.map +1 -0
  217. package/dist/core/logger.js +40 -0
  218. package/dist/core/logger.js.map +1 -0
  219. package/dist/core/parse-dispatcher.d.ts +18 -0
  220. package/dist/core/parse-dispatcher.d.ts.map +1 -0
  221. package/dist/core/parse-dispatcher.js +74 -0
  222. package/dist/core/parse-dispatcher.js.map +1 -0
  223. package/dist/core/search/query-preprocessor.d.ts +22 -0
  224. package/dist/core/search/query-preprocessor.d.ts.map +1 -0
  225. package/dist/core/search/query-preprocessor.js +77 -0
  226. package/dist/core/search/query-preprocessor.js.map +1 -0
  227. package/dist/core/search/relevance-ranker.d.ts +34 -0
  228. package/dist/core/search/relevance-ranker.d.ts.map +1 -0
  229. package/dist/core/search/relevance-ranker.js +132 -0
  230. package/dist/core/search/relevance-ranker.js.map +1 -0
  231. package/dist/core/security.d.ts +40 -0
  232. package/dist/core/security.d.ts.map +1 -0
  233. package/dist/core/security.js +133 -0
  234. package/dist/core/security.js.map +1 -0
  235. package/dist/core/telemetry.d.ts +19 -0
  236. package/dist/core/telemetry.d.ts.map +1 -0
  237. package/dist/core/telemetry.js +101 -0
  238. package/dist/core/telemetry.js.map +1 -0
  239. package/dist/core/tenant-context.d.ts +22 -0
  240. package/dist/core/tenant-context.d.ts.map +1 -0
  241. package/dist/core/tenant-context.js +16 -0
  242. package/dist/core/tenant-context.js.map +1 -0
  243. package/dist/core/token-tracker.d.ts +39 -0
  244. package/dist/core/token-tracker.d.ts.map +1 -0
  245. package/dist/core/token-tracker.js +153 -0
  246. package/dist/core/token-tracker.js.map +1 -0
  247. package/dist/core/types.d.ts +145 -0
  248. package/dist/core/types.d.ts.map +1 -0
  249. package/dist/core/types.js +2 -0
  250. package/dist/core/types.js.map +1 -0
  251. package/dist/core/watcher/file-watcher.d.ts +18 -0
  252. package/dist/core/watcher/file-watcher.d.ts.map +1 -0
  253. package/dist/core/watcher/file-watcher.js +123 -0
  254. package/dist/core/watcher/file-watcher.js.map +1 -0
  255. package/dist/core/worker-pool.d.ts +24 -0
  256. package/dist/core/worker-pool.d.ts.map +1 -0
  257. package/dist/core/worker-pool.js +92 -0
  258. package/dist/core/worker-pool.js.map +1 -0
  259. package/dist/graph/graph-builder.d.ts +16 -0
  260. package/dist/graph/graph-builder.d.ts.map +1 -0
  261. package/dist/graph/graph-builder.js +51 -0
  262. package/dist/graph/graph-builder.js.map +1 -0
  263. package/dist/graph/graph-traversal.d.ts +57 -0
  264. package/dist/graph/graph-traversal.d.ts.map +1 -0
  265. package/dist/graph/graph-traversal.js +144 -0
  266. package/dist/graph/graph-traversal.js.map +1 -0
  267. package/dist/graph/path-resolver.d.ts +13 -0
  268. package/dist/graph/path-resolver.d.ts.map +1 -0
  269. package/dist/graph/path-resolver.js +161 -0
  270. package/dist/graph/path-resolver.js.map +1 -0
  271. package/dist/handlers/c.d.ts +3 -0
  272. package/dist/handlers/c.d.ts.map +1 -0
  273. package/dist/handlers/c.js +473 -0
  274. package/dist/handlers/c.js.map +1 -0
  275. package/dist/handlers/cpp.d.ts +3 -0
  276. package/dist/handlers/cpp.d.ts.map +1 -0
  277. package/dist/handlers/cpp.js +682 -0
  278. package/dist/handlers/cpp.js.map +1 -0
  279. package/dist/handlers/csharp.d.ts +3 -0
  280. package/dist/handlers/csharp.d.ts.map +1 -0
  281. package/dist/handlers/csharp.js +369 -0
  282. package/dist/handlers/csharp.js.map +1 -0
  283. package/dist/handlers/dart.d.ts +3 -0
  284. package/dist/handlers/dart.d.ts.map +1 -0
  285. package/dist/handlers/dart.js +596 -0
  286. package/dist/handlers/dart.js.map +1 -0
  287. package/dist/handlers/elixir.d.ts +8 -0
  288. package/dist/handlers/elixir.d.ts.map +1 -0
  289. package/dist/handlers/elixir.js +412 -0
  290. package/dist/handlers/elixir.js.map +1 -0
  291. package/dist/handlers/go.d.ts +3 -0
  292. package/dist/handlers/go.d.ts.map +1 -0
  293. package/dist/handlers/go.js +262 -0
  294. package/dist/handlers/go.js.map +1 -0
  295. package/dist/handlers/handler-registry.d.ts +20 -0
  296. package/dist/handlers/handler-registry.d.ts.map +1 -0
  297. package/dist/handlers/handler-registry.js +69 -0
  298. package/dist/handlers/handler-registry.js.map +1 -0
  299. package/dist/handlers/haskell.d.ts +17 -0
  300. package/dist/handlers/haskell.d.ts.map +1 -0
  301. package/dist/handlers/haskell.js +356 -0
  302. package/dist/handlers/haskell.js.map +1 -0
  303. package/dist/handlers/java.d.ts +3 -0
  304. package/dist/handlers/java.d.ts.map +1 -0
  305. package/dist/handlers/java.js +350 -0
  306. package/dist/handlers/java.js.map +1 -0
  307. package/dist/handlers/javascript.d.ts +3 -0
  308. package/dist/handlers/javascript.d.ts.map +1 -0
  309. package/dist/handlers/javascript.js +232 -0
  310. package/dist/handlers/javascript.js.map +1 -0
  311. package/dist/handlers/kotlin.d.ts +3 -0
  312. package/dist/handlers/kotlin.d.ts.map +1 -0
  313. package/dist/handlers/kotlin.js +317 -0
  314. package/dist/handlers/kotlin.js.map +1 -0
  315. package/dist/handlers/lua.d.ts +3 -0
  316. package/dist/handlers/lua.d.ts.map +1 -0
  317. package/dist/handlers/lua.js +328 -0
  318. package/dist/handlers/lua.js.map +1 -0
  319. package/dist/handlers/php.d.ts +3 -0
  320. package/dist/handlers/php.d.ts.map +1 -0
  321. package/dist/handlers/php.js +343 -0
  322. package/dist/handlers/php.js.map +1 -0
  323. package/dist/handlers/python.d.ts +3 -0
  324. package/dist/handlers/python.d.ts.map +1 -0
  325. package/dist/handlers/python.js +343 -0
  326. package/dist/handlers/python.js.map +1 -0
  327. package/dist/handlers/r.d.ts +9 -0
  328. package/dist/handlers/r.d.ts.map +1 -0
  329. package/dist/handlers/r.js +452 -0
  330. package/dist/handlers/r.js.map +1 -0
  331. package/dist/handlers/ruby.d.ts +8 -0
  332. package/dist/handlers/ruby.d.ts.map +1 -0
  333. package/dist/handlers/ruby.js +271 -0
  334. package/dist/handlers/ruby.js.map +1 -0
  335. package/dist/handlers/rust.d.ts +3 -0
  336. package/dist/handlers/rust.d.ts.map +1 -0
  337. package/dist/handlers/rust.js +420 -0
  338. package/dist/handlers/rust.js.map +1 -0
  339. package/dist/handlers/scala.d.ts +9 -0
  340. package/dist/handlers/scala.d.ts.map +1 -0
  341. package/dist/handlers/scala.js +480 -0
  342. package/dist/handlers/scala.js.map +1 -0
  343. package/dist/handlers/swift.d.ts +3 -0
  344. package/dist/handlers/swift.d.ts.map +1 -0
  345. package/dist/handlers/swift.js +553 -0
  346. package/dist/handlers/swift.js.map +1 -0
  347. package/dist/handlers/typescript.d.ts +4 -0
  348. package/dist/handlers/typescript.d.ts.map +1 -0
  349. package/dist/handlers/typescript.js +325 -0
  350. package/dist/handlers/typescript.js.map +1 -0
  351. package/dist/index.d.ts +44 -0
  352. package/dist/index.d.ts.map +1 -0
  353. package/dist/index.js +266 -0
  354. package/dist/index.js.map +1 -0
  355. package/dist/semantic/embedding-provider.d.ts +66 -0
  356. package/dist/semantic/embedding-provider.d.ts.map +1 -0
  357. package/dist/semantic/embedding-provider.js +176 -0
  358. package/dist/semantic/embedding-provider.js.map +1 -0
  359. package/dist/semantic/hnsw-index.d.ts +96 -0
  360. package/dist/semantic/hnsw-index.d.ts.map +1 -0
  361. package/dist/semantic/hnsw-index.js +364 -0
  362. package/dist/semantic/hnsw-index.js.map +1 -0
  363. package/dist/semantic/hybrid-search.d.ts +46 -0
  364. package/dist/semantic/hybrid-search.d.ts.map +1 -0
  365. package/dist/semantic/hybrid-search.js +164 -0
  366. package/dist/semantic/hybrid-search.js.map +1 -0
  367. package/dist/semantic/query-expansion.d.ts +25 -0
  368. package/dist/semantic/query-expansion.d.ts.map +1 -0
  369. package/dist/semantic/query-expansion.js +150 -0
  370. package/dist/semantic/query-expansion.js.map +1 -0
  371. package/dist/semantic/semantic-indexer.d.ts +64 -0
  372. package/dist/semantic/semantic-indexer.d.ts.map +1 -0
  373. package/dist/semantic/semantic-indexer.js +162 -0
  374. package/dist/semantic/semantic-indexer.js.map +1 -0
  375. package/dist/semantic/text-preparation.d.ts +23 -0
  376. package/dist/semantic/text-preparation.d.ts.map +1 -0
  377. package/dist/semantic/text-preparation.js +56 -0
  378. package/dist/semantic/text-preparation.js.map +1 -0
  379. package/dist/semantic/vector-store.d.ts +81 -0
  380. package/dist/semantic/vector-store.d.ts.map +1 -0
  381. package/dist/semantic/vector-store.js +157 -0
  382. package/dist/semantic/vector-store.js.map +1 -0
  383. package/dist/server/admin-api.d.ts +88 -0
  384. package/dist/server/admin-api.d.ts.map +1 -0
  385. package/dist/server/admin-api.js +661 -0
  386. package/dist/server/admin-api.js.map +1 -0
  387. package/dist/server/auth/api-key.d.ts +105 -0
  388. package/dist/server/auth/api-key.d.ts.map +1 -0
  389. package/dist/server/auth/api-key.js +276 -0
  390. package/dist/server/auth/api-key.js.map +1 -0
  391. package/dist/server/http-server.d.ts +100 -0
  392. package/dist/server/http-server.d.ts.map +1 -0
  393. package/dist/server/http-server.js +823 -0
  394. package/dist/server/http-server.js.map +1 -0
  395. package/dist/server/mcp-server.d.ts +23 -0
  396. package/dist/server/mcp-server.d.ts.map +1 -0
  397. package/dist/server/mcp-server.js +214 -0
  398. package/dist/server/mcp-server.js.map +1 -0
  399. package/dist/server/rate-limit-store.d.ts +25 -0
  400. package/dist/server/rate-limit-store.d.ts.map +1 -0
  401. package/dist/server/rate-limit-store.js +46 -0
  402. package/dist/server/rate-limit-store.js.map +1 -0
  403. package/dist/server/rate-limiter.d.ts +59 -0
  404. package/dist/server/rate-limiter.d.ts.map +1 -0
  405. package/dist/server/rate-limiter.js +85 -0
  406. package/dist/server/rate-limiter.js.map +1 -0
  407. package/dist/server/tools/_meta.d.ts +24 -0
  408. package/dist/server/tools/_meta.d.ts.map +1 -0
  409. package/dist/server/tools/_meta.js +35 -0
  410. package/dist/server/tools/_meta.js.map +1 -0
  411. package/dist/server/tools/find-dead-code.d.ts +11 -0
  412. package/dist/server/tools/find-dead-code.d.ts.map +1 -0
  413. package/dist/server/tools/find-dead-code.js +45 -0
  414. package/dist/server/tools/find-dead-code.js.map +1 -0
  415. package/dist/server/tools/find-importers.d.ts +13 -0
  416. package/dist/server/tools/find-importers.d.ts.map +1 -0
  417. package/dist/server/tools/find-importers.js +46 -0
  418. package/dist/server/tools/find-importers.js.map +1 -0
  419. package/dist/server/tools/get-blast-radius.d.ts +15 -0
  420. package/dist/server/tools/get-blast-radius.d.ts.map +1 -0
  421. package/dist/server/tools/get-blast-radius.js +51 -0
  422. package/dist/server/tools/get-blast-radius.js.map +1 -0
  423. package/dist/server/tools/get-context-bundle.d.ts +15 -0
  424. package/dist/server/tools/get-context-bundle.d.ts.map +1 -0
  425. package/dist/server/tools/get-context-bundle.js +53 -0
  426. package/dist/server/tools/get-context-bundle.js.map +1 -0
  427. package/dist/server/tools/get-file-outline.d.ts +13 -0
  428. package/dist/server/tools/get-file-outline.d.ts.map +1 -0
  429. package/dist/server/tools/get-file-outline.js +42 -0
  430. package/dist/server/tools/get-file-outline.js.map +1 -0
  431. package/dist/server/tools/get-file-tree.d.ts +11 -0
  432. package/dist/server/tools/get-file-tree.d.ts.map +1 -0
  433. package/dist/server/tools/get-file-tree.js +63 -0
  434. package/dist/server/tools/get-file-tree.js.map +1 -0
  435. package/dist/server/tools/get-graph.d.ts +46 -0
  436. package/dist/server/tools/get-graph.d.ts.map +1 -0
  437. package/dist/server/tools/get-graph.js +168 -0
  438. package/dist/server/tools/get-graph.js.map +1 -0
  439. package/dist/server/tools/get-layer-violations.d.ts +37 -0
  440. package/dist/server/tools/get-layer-violations.d.ts.map +1 -0
  441. package/dist/server/tools/get-layer-violations.js +138 -0
  442. package/dist/server/tools/get-layer-violations.js.map +1 -0
  443. package/dist/server/tools/get-repo-outline.d.ts +13 -0
  444. package/dist/server/tools/get-repo-outline.d.ts.map +1 -0
  445. package/dist/server/tools/get-repo-outline.js +72 -0
  446. package/dist/server/tools/get-repo-outline.js.map +1 -0
  447. package/dist/server/tools/get-savings-stats.d.ts +11 -0
  448. package/dist/server/tools/get-savings-stats.d.ts.map +1 -0
  449. package/dist/server/tools/get-savings-stats.js +56 -0
  450. package/dist/server/tools/get-savings-stats.js.map +1 -0
  451. package/dist/server/tools/get-symbol-source.d.ts +13 -0
  452. package/dist/server/tools/get-symbol-source.d.ts.map +1 -0
  453. package/dist/server/tools/get-symbol-source.js +93 -0
  454. package/dist/server/tools/get-symbol-source.js.map +1 -0
  455. package/dist/server/tools/index-folder.d.ts +15 -0
  456. package/dist/server/tools/index-folder.d.ts.map +1 -0
  457. package/dist/server/tools/index-folder.js +45 -0
  458. package/dist/server/tools/index-folder.js.map +1 -0
  459. package/dist/server/tools/index-repo.d.ts +26 -0
  460. package/dist/server/tools/index-repo.d.ts.map +1 -0
  461. package/dist/server/tools/index-repo.js +164 -0
  462. package/dist/server/tools/index-repo.js.map +1 -0
  463. package/dist/server/tools/list-repos.d.ts +11 -0
  464. package/dist/server/tools/list-repos.d.ts.map +1 -0
  465. package/dist/server/tools/list-repos.js +60 -0
  466. package/dist/server/tools/list-repos.js.map +1 -0
  467. package/dist/server/tools/resolve-repo.d.ts +11 -0
  468. package/dist/server/tools/resolve-repo.d.ts.map +1 -0
  469. package/dist/server/tools/resolve-repo.js +44 -0
  470. package/dist/server/tools/resolve-repo.js.map +1 -0
  471. package/dist/server/tools/search-semantic.d.ts +56 -0
  472. package/dist/server/tools/search-semantic.d.ts.map +1 -0
  473. package/dist/server/tools/search-semantic.js +187 -0
  474. package/dist/server/tools/search-semantic.js.map +1 -0
  475. package/dist/server/tools/search-symbols.d.ts +43 -0
  476. package/dist/server/tools/search-symbols.d.ts.map +1 -0
  477. package/dist/server/tools/search-symbols.js +197 -0
  478. package/dist/server/tools/search-symbols.js.map +1 -0
  479. package/dist/server/tools/search-text.d.ts +21 -0
  480. package/dist/server/tools/search-text.d.ts.map +1 -0
  481. package/dist/server/tools/search-text.js +131 -0
  482. package/dist/server/tools/search-text.js.map +1 -0
  483. package/dist/server/transport.d.ts +59 -0
  484. package/dist/server/transport.d.ts.map +1 -0
  485. package/dist/server/transport.js +92 -0
  486. package/dist/server/transport.js.map +1 -0
  487. package/dist/summarizer/ai-summarizer.d.ts +71 -0
  488. package/dist/summarizer/ai-summarizer.d.ts.map +1 -0
  489. package/dist/summarizer/ai-summarizer.js +228 -0
  490. package/dist/summarizer/ai-summarizer.js.map +1 -0
  491. package/dist/summarizer/docstring-extractor.d.ts +9 -0
  492. package/dist/summarizer/docstring-extractor.d.ts.map +1 -0
  493. package/dist/summarizer/docstring-extractor.js +89 -0
  494. package/dist/summarizer/docstring-extractor.js.map +1 -0
  495. package/dist/summarizer/embeddings.d.ts +29 -0
  496. package/dist/summarizer/embeddings.d.ts.map +1 -0
  497. package/dist/summarizer/embeddings.js +133 -0
  498. package/dist/summarizer/embeddings.js.map +1 -0
  499. package/dist/summarizer/summarizer.d.ts +22 -0
  500. package/dist/summarizer/summarizer.d.ts.map +1 -0
  501. package/dist/summarizer/summarizer.js +86 -0
  502. package/dist/summarizer/summarizer.js.map +1 -0
  503. package/dist/ui/assets/BlastRadius-ccQ3ktbv.js +1 -0
  504. package/dist/ui/assets/DependencyGraph-BZV40eAE.css +1 -0
  505. package/dist/ui/assets/DependencyGraph-Ca84q89b.js +27 -0
  506. package/dist/ui/assets/NotFound-C1wVKP-6.js +1 -0
  507. package/dist/ui/assets/RepoDetail-B_8qid2Y.js +1 -0
  508. package/dist/ui/assets/RepoList-BuPu-ODM.js +1 -0
  509. package/dist/ui/assets/Search-BzZ9Og8E.js +1 -0
  510. package/dist/ui/assets/SymbolView-FdpKlzql.js +14 -0
  511. package/dist/ui/assets/client-BANW_tIp.js +1 -0
  512. package/dist/ui/assets/index-CnwwQi_S.js +61 -0
  513. package/dist/ui/assets/index-SS2IXr8I.css +1 -0
  514. package/dist/ui/assets/repoStore-SbGR6YI6.js +1 -0
  515. package/dist/ui/assets/useSearch-DHyve22h.js +1 -0
  516. package/dist/ui/index.html +13 -0
  517. package/dist/version.d.ts +2 -0
  518. package/dist/version.d.ts.map +1 -0
  519. package/dist/version.js +2 -0
  520. package/dist/version.js.map +1 -0
  521. package/grammars/tree-sitter-c.wasm +0 -0
  522. package/grammars/tree-sitter-cpp.wasm +0 -0
  523. package/grammars/tree-sitter-csharp.wasm +0 -0
  524. package/grammars/tree-sitter-dart.wasm +0 -0
  525. package/grammars/tree-sitter-elixir.wasm +0 -0
  526. package/grammars/tree-sitter-go.wasm +0 -0
  527. package/grammars/tree-sitter-haskell.wasm +0 -0
  528. package/grammars/tree-sitter-java.wasm +0 -0
  529. package/grammars/tree-sitter-javascript.wasm +0 -0
  530. package/grammars/tree-sitter-kotlin.wasm +0 -0
  531. package/grammars/tree-sitter-lua.wasm +0 -0
  532. package/grammars/tree-sitter-php.wasm +0 -0
  533. package/grammars/tree-sitter-python.wasm +0 -0
  534. package/grammars/tree-sitter-r.wasm +0 -0
  535. package/grammars/tree-sitter-ruby.wasm +0 -0
  536. package/grammars/tree-sitter-rust.wasm +0 -0
  537. package/grammars/tree-sitter-scala.wasm +0 -0
  538. package/grammars/tree-sitter-swift.wasm +0 -0
  539. package/grammars/tree-sitter-tsx.wasm +0 -0
  540. package/grammars/tree-sitter-typescript.wasm +0 -0
  541. package/package.json +104 -0
  542. package/scripts/check-sqlite.js +41 -0
@@ -0,0 +1,823 @@
1
+ import { createServer } from 'node:http';
2
+ import { timingSafeEqual } from 'node:crypto';
3
+ import { createReadStream, existsSync, statSync, readdirSync } from 'node:fs';
4
+ import { join, extname, resolve as resolvePath } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
7
+ import { logger } from '../core/logger.js';
8
+ import { QuotaExceededError } from '../core/errors.js';
9
+ import { VERSION } from '../version.js';
10
+ import { getIndexDir } from '../core/db/schema.js';
11
+ import { TokenBucketLimiter } from './rate-limiter.js';
12
+ import { extractApiKeyFromHeaders, validateFormat, getRequiredPermission, hasPermission, } from './auth/api-key.js';
13
+ // ─── Constants ────────────────────────────────────────────────────────────────
14
+ const MAX_BODY_BYTES = 1_048_576; // 1 MB — guard against malformed / oversized requests
15
+ const SSE_KEEPALIVE_MS = 30_000; // 30 s — prevents proxy/load-balancer timeouts on idle SSE streams
16
+ // ─── CORS ──────────────────────────────────────────────────────────────────────
17
+ /**
18
+ * Match an origin against a list of patterns.
19
+ * Patterns may contain `*` as a wildcard within a path segment (e.g. `http://localhost:*`).
20
+ */
21
+ export function originAllowed(origin, patterns) {
22
+ for (const pattern of patterns) {
23
+ // Escape regex special chars except *, then replace * with a segment wildcard
24
+ const re = new RegExp('^' +
25
+ pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '[^/]*') +
26
+ '$');
27
+ if (re.test(origin))
28
+ return true;
29
+ }
30
+ return false;
31
+ }
32
+ function setCorsHeaders(req, res, corsOrigins) {
33
+ const origin = req.headers['origin'] ?? '';
34
+ if (origin && originAllowed(origin, corsOrigins)) {
35
+ res.setHeader('Access-Control-Allow-Origin', origin);
36
+ res.setHeader('Vary', 'Origin');
37
+ }
38
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
39
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, mcp-session-id, last-event-id');
40
+ }
41
+ // ─── Auth ─────────────────────────────────────────────────────────────────────
42
+ /**
43
+ * Check the Authorization header against the configured bearer token.
44
+ * Uses timing-safe comparison to prevent timing attacks.
45
+ * Returns true if auth is disabled OR the token matches.
46
+ */
47
+ export function checkAuth(req, auth) {
48
+ if (!auth.enabled)
49
+ return true;
50
+ const header = req.headers['authorization'] ?? '';
51
+ const prefix = 'Bearer ';
52
+ if (!header.startsWith(prefix))
53
+ return false;
54
+ const provided = header.slice(prefix.length);
55
+ if (!provided || !auth.token)
56
+ return false;
57
+ // Pad to same length before comparison to avoid length-based oracle
58
+ try {
59
+ const a = Buffer.from(provided.padEnd(auth.token.length, '\0'));
60
+ const b = Buffer.from(auth.token.padEnd(provided.length, '\0'));
61
+ // timingSafeEqual requires same-length buffers
62
+ const maxLen = Math.max(a.length, b.length);
63
+ const ba = Buffer.alloc(maxLen);
64
+ const bb = Buffer.alloc(maxLen);
65
+ a.copy(ba);
66
+ b.copy(bb);
67
+ return timingSafeEqual(ba, bb) && provided.length === auth.token.length;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ // ─── Body reader ──────────────────────────────────────────────────────────────
74
+ export function readBody(req, maxBytes) {
75
+ return new Promise((resolve, reject) => {
76
+ const chunks = [];
77
+ let total = 0;
78
+ req.on('data', (chunk) => {
79
+ total += chunk.length;
80
+ if (total > maxBytes) {
81
+ req.destroy(new Error('Request body too large'));
82
+ return;
83
+ }
84
+ chunks.push(chunk);
85
+ });
86
+ req.on('end', () => resolve(Buffer.concat(chunks)));
87
+ req.on('error', reject);
88
+ });
89
+ }
90
+ // ─── Rate limit helpers ────────────────────────────────────────────────────────
91
+ /**
92
+ * Extract the MCP tool name from a parsed JSON-RPC request body.
93
+ * Returns undefined for non-tool-call methods (e.g. initialize, list_tools).
94
+ */
95
+ export function extractToolName(body) {
96
+ if (typeof body !== 'object' || body === null)
97
+ return undefined;
98
+ const b = body;
99
+ if (b['method'] !== 'tools/call')
100
+ return undefined;
101
+ const params = b['params'];
102
+ if (typeof params !== 'object' || params === null)
103
+ return undefined;
104
+ const name = params['name'];
105
+ return typeof name === 'string' ? name : undefined;
106
+ }
107
+ /**
108
+ * Return the token cost for a tool call.
109
+ * Falls back to 1 for tools not in the perToolLimits map.
110
+ */
111
+ export function getToolCost(toolName, perToolLimits) {
112
+ if (toolName === undefined)
113
+ return 1;
114
+ return perToolLimits[toolName] ?? 1;
115
+ }
116
+ // ─── Health helpers ───────────────────────────────────────────────────────────
117
+ function getRepoCount() {
118
+ const dir = getIndexDir();
119
+ if (!existsSync(dir))
120
+ return 0;
121
+ try {
122
+ return readdirSync(dir).filter((f) => f.endsWith('.db')).length;
123
+ }
124
+ catch {
125
+ return 0;
126
+ }
127
+ }
128
+ // ─── REST API ─────────────────────────────────────────────────────────────────
129
+ /**
130
+ * Parse URL search params from a URL string that may include a query string.
131
+ */
132
+ export function parseQuery(url) {
133
+ const idx = url.indexOf('?');
134
+ return new URLSearchParams(idx >= 0 ? url.slice(idx + 1) : '');
135
+ }
136
+ /**
137
+ * Match a URL pattern with named segments. Returns captured groups or null.
138
+ * Pattern example: '/api/repos/:id/tree'
139
+ */
140
+ export function matchRoute(url, pattern) {
141
+ const urlPath = url.split('?')[0];
142
+ const patternParts = pattern.split('/');
143
+ const urlParts = urlPath.split('/');
144
+ if (patternParts.length !== urlParts.length)
145
+ return null;
146
+ const params = {};
147
+ for (let i = 0; i < patternParts.length; i++) {
148
+ const p = patternParts[i];
149
+ const u = urlParts[i];
150
+ if (p.startsWith(':')) {
151
+ params[p.slice(1)] = decodeURIComponent(u ?? '');
152
+ }
153
+ else if (p !== u) {
154
+ return null;
155
+ }
156
+ }
157
+ return params;
158
+ }
159
+ function sendJson(res, status, body) {
160
+ const payload = JSON.stringify(body);
161
+ res.writeHead(status, { 'Content-Type': 'application/json' });
162
+ res.end(payload);
163
+ }
164
+ async function handleRestApi(_req, res, url, method) {
165
+ if (method !== 'GET') {
166
+ sendJson(res, 405, { error: 'Method not allowed' });
167
+ return;
168
+ }
169
+ const query = parseQuery(url);
170
+ // GET /api/repos
171
+ if (matchRoute(url, '/api/repos') !== null) {
172
+ const { handler } = await import('./tools/list-repos.js');
173
+ const result = handler();
174
+ const text = result.content[0];
175
+ if (text && text.type === 'text') {
176
+ res.writeHead(200, { 'Content-Type': 'application/json' });
177
+ res.end(text.text);
178
+ }
179
+ else {
180
+ sendJson(res, 500, { error: 'Unexpected tool response' });
181
+ }
182
+ return;
183
+ }
184
+ // GET /api/repos/:id/tree
185
+ const treeParams = matchRoute(url, '/api/repos/:id/tree');
186
+ if (treeParams !== null) {
187
+ const { handler } = await import('./tools/get-file-tree.js');
188
+ const result = handler({ repoId: treeParams['id'] });
189
+ const text = result.content[0];
190
+ if (text && text.type === 'text') {
191
+ if (result.isError) {
192
+ res.writeHead(404, { 'Content-Type': 'application/json' });
193
+ }
194
+ else {
195
+ res.writeHead(200, { 'Content-Type': 'application/json' });
196
+ }
197
+ res.end(text.text);
198
+ }
199
+ else {
200
+ sendJson(res, 500, { error: 'Unexpected tool response' });
201
+ }
202
+ return;
203
+ }
204
+ // GET /api/repos/:id/outline
205
+ const outlineParams = matchRoute(url, '/api/repos/:id/outline');
206
+ if (outlineParams !== null) {
207
+ const { handler } = await import('./tools/get-repo-outline.js');
208
+ const limitStr = query.get('limit');
209
+ const limit = limitStr ? parseInt(limitStr, 10) : undefined;
210
+ const result = handler({ repoId: outlineParams['id'], limit });
211
+ const text = result.content[0];
212
+ if (text && text.type === 'text') {
213
+ if (result.isError) {
214
+ res.writeHead(404, { 'Content-Type': 'application/json' });
215
+ }
216
+ else {
217
+ res.writeHead(200, { 'Content-Type': 'application/json' });
218
+ }
219
+ res.end(text.text);
220
+ }
221
+ else {
222
+ sendJson(res, 500, { error: 'Unexpected tool response' });
223
+ }
224
+ return;
225
+ }
226
+ // GET /api/repos/:id/search
227
+ const searchParams = matchRoute(url, '/api/repos/:id/search');
228
+ if (searchParams !== null) {
229
+ const q = query.get('query');
230
+ if (!q) {
231
+ sendJson(res, 400, { error: 'Missing required query parameter: query' });
232
+ return;
233
+ }
234
+ const { handler } = await import('./tools/search-symbols.js');
235
+ const limitStr = query.get('limit');
236
+ const result = await handler({
237
+ repoId: searchParams['id'],
238
+ query: q,
239
+ kind: query.get('kind') ?? undefined,
240
+ filePath: query.get('filePath') ?? undefined,
241
+ limit: limitStr ? parseInt(limitStr, 10) : undefined,
242
+ mode: query.get('mode') ?? undefined,
243
+ });
244
+ const text = result.content[0];
245
+ if (text && text.type === 'text') {
246
+ if (result.isError) {
247
+ res.writeHead(400, { 'Content-Type': 'application/json' });
248
+ }
249
+ else {
250
+ res.writeHead(200, { 'Content-Type': 'application/json' });
251
+ }
252
+ res.end(text.text);
253
+ }
254
+ else {
255
+ sendJson(res, 500, { error: 'Unexpected tool response' });
256
+ }
257
+ return;
258
+ }
259
+ // GET /api/symbols/:id
260
+ const symbolParams = matchRoute(url, '/api/symbols/:id');
261
+ if (symbolParams !== null) {
262
+ const repoId = query.get('repoId');
263
+ if (!repoId) {
264
+ sendJson(res, 400, { error: 'Missing required query parameter: repoId' });
265
+ return;
266
+ }
267
+ const { handler } = await import('./tools/get-symbol-source.js');
268
+ const result = handler({ repoId, symbolId: symbolParams['id'] });
269
+ const text = result.content[0];
270
+ if (text && text.type === 'text') {
271
+ if (result.isError) {
272
+ res.writeHead(404, { 'Content-Type': 'application/json' });
273
+ }
274
+ else {
275
+ res.writeHead(200, { 'Content-Type': 'application/json' });
276
+ }
277
+ res.end(text.text);
278
+ }
279
+ else {
280
+ sendJson(res, 500, { error: 'Unexpected tool response' });
281
+ }
282
+ return;
283
+ }
284
+ // GET /api/repos/:id/file-outline
285
+ const fileOutlineParams = matchRoute(url, '/api/repos/:id/file-outline');
286
+ if (fileOutlineParams !== null) {
287
+ const filePath = query.get('filePath');
288
+ if (!filePath) {
289
+ sendJson(res, 400, { error: 'Missing required query parameter: filePath' });
290
+ return;
291
+ }
292
+ const { handler } = await import('./tools/get-file-outline.js');
293
+ const result = handler({ repoId: fileOutlineParams['id'], filePath });
294
+ const text = result.content[0];
295
+ if (text && text.type === 'text') {
296
+ if (result.isError) {
297
+ res.writeHead(404, { 'Content-Type': 'application/json' });
298
+ }
299
+ else {
300
+ res.writeHead(200, { 'Content-Type': 'application/json' });
301
+ }
302
+ res.end(text.text);
303
+ }
304
+ else {
305
+ sendJson(res, 500, { error: 'Unexpected tool response' });
306
+ }
307
+ return;
308
+ }
309
+ // GET /api/repos/:id/graph
310
+ const graphParams = matchRoute(url, '/api/repos/:id/graph');
311
+ if (graphParams !== null) {
312
+ const { handler } = await import('./tools/get-graph.js');
313
+ const depthStr = query.get('depth');
314
+ const limitStr2 = query.get('limit');
315
+ const result = handler({
316
+ repoId: graphParams['id'],
317
+ filePath: query.get('filePath') ?? undefined,
318
+ depth: depthStr ? parseInt(depthStr, 10) : undefined,
319
+ limit: limitStr2 ? parseInt(limitStr2, 10) : undefined,
320
+ });
321
+ const text = result.content[0];
322
+ if (text && text.type === 'text') {
323
+ if (result.isError) {
324
+ res.writeHead(404, { 'Content-Type': 'application/json' });
325
+ }
326
+ else {
327
+ res.writeHead(200, { 'Content-Type': 'application/json' });
328
+ }
329
+ res.end(text.text);
330
+ }
331
+ else {
332
+ sendJson(res, 500, { error: 'Unexpected tool response' });
333
+ }
334
+ return;
335
+ }
336
+ // GET /api/repos/:id/blast-radius?symbolId=...&depth=...
337
+ const blastRadiusParams = matchRoute(url, '/api/repos/:id/blast-radius');
338
+ if (blastRadiusParams !== null) {
339
+ const symbolId = query.get('symbolId');
340
+ if (!symbolId) {
341
+ sendJson(res, 400, { error: 'Missing required query parameter: symbolId' });
342
+ return;
343
+ }
344
+ const depthStr = query.get('depth');
345
+ const depth = depthStr ? parseInt(depthStr, 10) : undefined;
346
+ const t0 = Date.now();
347
+ try {
348
+ const { openDatabase } = await import('../core/db/schema.js');
349
+ const { getBlastRadiusWithDepths } = await import('../graph/graph-traversal.js');
350
+ const db = openDatabase(blastRadiusParams['id']);
351
+ const result = getBlastRadiusWithDepths(symbolId, blastRadiusParams['id'], db, depth);
352
+ db.close();
353
+ if (!result) {
354
+ sendJson(res, 404, { error: `Symbol not found: ${symbolId}` });
355
+ return;
356
+ }
357
+ const payload = JSON.stringify({ ...result, _meta: { timingMs: Date.now() - t0 } });
358
+ res.writeHead(200, { 'Content-Type': 'application/json' });
359
+ res.end(payload);
360
+ }
361
+ catch (err) {
362
+ sendJson(res, 500, { error: String(err) });
363
+ }
364
+ return;
365
+ }
366
+ // GET /api/repos/:id/importers
367
+ const importersParams = matchRoute(url, '/api/repos/:id/importers');
368
+ if (importersParams !== null) {
369
+ const filePath = query.get('filePath');
370
+ if (!filePath) {
371
+ sendJson(res, 400, { error: 'Missing required query parameter: filePath' });
372
+ return;
373
+ }
374
+ const { handler } = await import('./tools/find-importers.js');
375
+ const result = handler({ repoId: importersParams['id'], filePath });
376
+ const text = result.content[0];
377
+ if (text && text.type === 'text') {
378
+ if (result.isError) {
379
+ res.writeHead(404, { 'Content-Type': 'application/json' });
380
+ }
381
+ else {
382
+ res.writeHead(200, { 'Content-Type': 'application/json' });
383
+ }
384
+ res.end(text.text);
385
+ }
386
+ else {
387
+ sendJson(res, 500, { error: 'Unexpected tool response' });
388
+ }
389
+ return;
390
+ }
391
+ sendJson(res, 404, { error: 'API route not found' });
392
+ }
393
+ // ─── Static file serving ──────────────────────────────────────────────────────
394
+ const MIME_TYPES = {
395
+ '.html': 'text/html; charset=utf-8',
396
+ '.js': 'application/javascript',
397
+ '.mjs': 'application/javascript',
398
+ '.css': 'text/css',
399
+ '.json': 'application/json',
400
+ '.svg': 'image/svg+xml',
401
+ '.png': 'image/png',
402
+ '.ico': 'image/x-icon',
403
+ '.woff': 'font/woff',
404
+ '.woff2': 'font/woff2',
405
+ };
406
+ /** Resolve path to the built UI directory. */
407
+ function getUiDir() {
408
+ // In production: dist/server/http-server.js → dist/server/ → dist/ → project root
409
+ // In source/test: src/server/http-server.ts → src/server/ → src/ → project root
410
+ // Either way, the compiled UI lives at <project root>/dist/ui/
411
+ const __filename = fileURLToPath(import.meta.url);
412
+ const projectRoot = resolvePath(__filename, '..', '..', '..');
413
+ return join(projectRoot, 'dist', 'ui');
414
+ }
415
+ /**
416
+ * Serve a static file from dist/ui/. Returns true if handled.
417
+ * For SPA: unknown paths serve index.html.
418
+ * Returns false immediately when serveUi is false.
419
+ */
420
+ function serveStatic(_req, res, url, serveUi) {
421
+ if (!serveUi)
422
+ return false;
423
+ const uiDir = getUiDir();
424
+ if (!existsSync(uiDir))
425
+ return false; // UI not built — skip
426
+ // Strip query string for file lookup
427
+ const pathname = url.split('?')[0];
428
+ const safePath = resolvePath(uiDir, '.' + pathname);
429
+ // Path traversal guard
430
+ if (!safePath.startsWith(uiDir)) {
431
+ sendJson(res, 403, { error: 'Forbidden' });
432
+ return true;
433
+ }
434
+ let filePath = safePath;
435
+ let stat;
436
+ try {
437
+ stat = statSync(filePath);
438
+ if (stat.isDirectory()) {
439
+ filePath = join(filePath, 'index.html');
440
+ stat = statSync(filePath);
441
+ }
442
+ }
443
+ catch {
444
+ // File not found — SPA fallback to index.html
445
+ filePath = join(uiDir, 'index.html');
446
+ try {
447
+ stat = statSync(filePath);
448
+ }
449
+ catch {
450
+ return false; // No index.html either
451
+ }
452
+ }
453
+ const ext = extname(filePath);
454
+ const contentType = MIME_TYPES[ext] ?? 'application/octet-stream';
455
+ res.writeHead(200, {
456
+ 'Content-Type': contentType,
457
+ 'Content-Length': stat.size,
458
+ });
459
+ createReadStream(filePath).pipe(res);
460
+ return true;
461
+ }
462
+ // ─── HTTP server ──────────────────────────────────────────────────────────────
463
+ export function startHttpServer(options) {
464
+ const startTime = Date.now();
465
+ // Create a single limiter instance shared across all requests
466
+ const rl = options.rateLimit;
467
+ const limiter = (rl?.enabled ?? false)
468
+ ? new TokenBucketLimiter({
469
+ maxTokens: rl.maxTokens,
470
+ refillRate: rl.refillRate,
471
+ })
472
+ : null;
473
+ return new Promise((resolve, reject) => {
474
+ const server = createServer(async (req, res) => {
475
+ const url = req.url ?? '/';
476
+ const method = req.method ?? 'GET';
477
+ const clientIp = req.socket.remoteAddress ?? 'unknown';
478
+ logger.debug(`${method} ${url} from ${clientIp}`);
479
+ setCorsHeaders(req, res, options.corsOrigins);
480
+ // ── CORS preflight ──────────────────────────────────────────────────────
481
+ if (method === 'OPTIONS') {
482
+ res.writeHead(204);
483
+ res.end();
484
+ return;
485
+ }
486
+ // ── Health check ────────────────────────────────────────────────────────
487
+ if (url === '/health' && method === 'GET') {
488
+ const body = JSON.stringify({
489
+ status: 'ok',
490
+ version: VERSION,
491
+ repoCount: getRepoCount(),
492
+ uptime: (Date.now() - startTime) / 1000,
493
+ });
494
+ res.writeHead(200, { 'Content-Type': 'application/json' });
495
+ res.end(body);
496
+ return;
497
+ }
498
+ // ── MCP SSE endpoint (stateful sessions, for agent connections) ─────────
499
+ if ((url === '/mcp/sse' || url.startsWith('/mcp/sse?')) && options.sseTransport) {
500
+ // ── Authentication ─────────────────────────────────────────────────
501
+ let authResult = null;
502
+ if (options.apiKeyAuth?.enabled) {
503
+ const apiKey = extractApiKeyFromHeaders(req.headers);
504
+ if (apiKey === null) {
505
+ res.writeHead(401, { 'Content-Type': 'application/json' });
506
+ res.end(JSON.stringify({ error: 'Unauthorized: API key required (Authorization: Bearer or X-API-Key)' }));
507
+ return;
508
+ }
509
+ if (!validateFormat(apiKey)) {
510
+ res.writeHead(401, { 'Content-Type': 'application/json' });
511
+ res.end(JSON.stringify({ error: 'Unauthorized: invalid API key format' }));
512
+ return;
513
+ }
514
+ authResult = await options.apiKeyAuth.validator.validate(apiKey);
515
+ if (!authResult.valid) {
516
+ res.writeHead(401, { 'Content-Type': 'application/json' });
517
+ res.end(JSON.stringify({ error: 'Unauthorized: API key not found or revoked' }));
518
+ return;
519
+ }
520
+ }
521
+ else if (!checkAuth(req, options.auth)) {
522
+ res.writeHead(401, { 'Content-Type': 'application/json' });
523
+ res.end(JSON.stringify({ error: 'Unauthorized' }));
524
+ return;
525
+ }
526
+ // ── POST-specific: body reading, permissions, rate limiting ────────
527
+ let sseParsedBody;
528
+ if (method === 'POST') {
529
+ const contentLength = parseInt(req.headers['content-length'] ?? '0', 10);
530
+ if (!isNaN(contentLength) && contentLength > MAX_BODY_BYTES) {
531
+ res.writeHead(413, { 'Content-Type': 'application/json' });
532
+ res.end(JSON.stringify({ error: 'Request body too large' }));
533
+ return;
534
+ }
535
+ try {
536
+ const raw = await readBody(req, MAX_BODY_BYTES);
537
+ sseParsedBody = JSON.parse(raw.toString('utf8'));
538
+ }
539
+ catch (err) {
540
+ if (!res.headersSent) {
541
+ res.writeHead(400, { 'Content-Type': 'application/json' });
542
+ res.end(JSON.stringify({ error: `Bad request: ${err}` }));
543
+ }
544
+ return;
545
+ }
546
+ const toolName = extractToolName(sseParsedBody);
547
+ if (authResult !== null && toolName !== undefined) {
548
+ const required = getRequiredPermission(toolName);
549
+ if (!hasPermission(authResult, required)) {
550
+ res.writeHead(403, { 'Content-Type': 'application/json' });
551
+ res.end(JSON.stringify({
552
+ error: `Forbidden: '${required}' permission required for ${toolName}`,
553
+ }));
554
+ return;
555
+ }
556
+ }
557
+ if (limiter !== null) {
558
+ const rateLimitKey = authResult?.tenantId ? `tenant:${authResult.tenantId}` : clientIp;
559
+ const cost = getToolCost(toolName, rl.perToolLimits);
560
+ const rlResult = limiter.tryConsume(rateLimitKey, cost);
561
+ if (!rlResult.allowed) {
562
+ const retryAfterSec = Math.ceil(rlResult.retryAfterMs / 1000);
563
+ res.writeHead(429, {
564
+ 'Content-Type': 'application/json',
565
+ 'Retry-After': String(retryAfterSec),
566
+ 'X-RateLimit-Limit': String(rl.maxTokens),
567
+ 'X-RateLimit-Remaining': String(rlResult.remainingTokens),
568
+ });
569
+ res.end(JSON.stringify({ error: 'Too Many Requests', retryAfterMs: rlResult.retryAfterMs }));
570
+ return;
571
+ }
572
+ }
573
+ }
574
+ // ── Keepalive pings for GET (SSE) connections ──────────────────────
575
+ // Writes a comment line every 30 s to prevent proxy/LB connection timeouts.
576
+ if (method === 'GET') {
577
+ const keepaliveTimer = setInterval(() => {
578
+ if (!res.writableEnded) {
579
+ res.write(': ping\n\n');
580
+ }
581
+ else {
582
+ clearInterval(keepaliveTimer);
583
+ }
584
+ }, SSE_KEEPALIVE_MS);
585
+ res.on('close', () => clearInterval(keepaliveTimer));
586
+ }
587
+ try {
588
+ await options.sseTransport.handleRequest(req, res, sseParsedBody);
589
+ }
590
+ catch (err) {
591
+ logger.warn(`MCP SSE request error: ${err}`);
592
+ if (!res.headersSent) {
593
+ res.writeHead(500, { 'Content-Type': 'application/json' });
594
+ res.end(JSON.stringify({
595
+ jsonrpc: '2.0',
596
+ error: { code: -32603, message: 'Internal server error' },
597
+ id: null,
598
+ }));
599
+ }
600
+ }
601
+ return;
602
+ }
603
+ // ── MCP endpoint (Streamable HTTP, stateless per-request) ───────────────
604
+ if (url === '/mcp') {
605
+ if (method !== 'POST') {
606
+ res.writeHead(405, { 'Content-Type': 'application/json' });
607
+ res.end(JSON.stringify({ error: 'Method not allowed. Use POST for MCP requests.' }));
608
+ return;
609
+ }
610
+ // ── Authentication ───────────────────────────────────────────────────
611
+ let authResult = null;
612
+ if (options.apiKeyAuth?.enabled) {
613
+ // API key authentication (Task 97) — supersedes simple bearer token
614
+ const apiKey = extractApiKeyFromHeaders(req.headers);
615
+ if (apiKey === null) {
616
+ res.writeHead(401, { 'Content-Type': 'application/json' });
617
+ res.end(JSON.stringify({ error: 'Unauthorized: API key required (Authorization: Bearer or X-API-Key)' }));
618
+ return;
619
+ }
620
+ // Fast format check before any DB access
621
+ if (!validateFormat(apiKey)) {
622
+ res.writeHead(401, { 'Content-Type': 'application/json' });
623
+ res.end(JSON.stringify({ error: 'Unauthorized: invalid API key format' }));
624
+ return;
625
+ }
626
+ // Full DB validation
627
+ authResult = await options.apiKeyAuth.validator.validate(apiKey);
628
+ if (!authResult.valid) {
629
+ res.writeHead(401, { 'Content-Type': 'application/json' });
630
+ res.end(JSON.stringify({ error: 'Unauthorized: API key not found or revoked' }));
631
+ return;
632
+ }
633
+ }
634
+ else {
635
+ // Legacy bearer token authentication
636
+ if (!checkAuth(req, options.auth)) {
637
+ res.writeHead(401, { 'Content-Type': 'application/json' });
638
+ res.end(JSON.stringify({ error: 'Unauthorized' }));
639
+ return;
640
+ }
641
+ }
642
+ // Reject oversized requests before reading body
643
+ const contentLength = parseInt(req.headers['content-length'] ?? '0', 10);
644
+ if (!isNaN(contentLength) && contentLength > MAX_BODY_BYTES) {
645
+ res.writeHead(413, { 'Content-Type': 'application/json' });
646
+ res.end(JSON.stringify({ error: 'Request body too large' }));
647
+ return;
648
+ }
649
+ let body;
650
+ try {
651
+ const raw = await readBody(req, MAX_BODY_BYTES);
652
+ body = JSON.parse(raw.toString('utf8'));
653
+ }
654
+ catch (err) {
655
+ if (!res.headersSent) {
656
+ res.writeHead(400, { 'Content-Type': 'application/json' });
657
+ res.end(JSON.stringify({ error: `Bad request: ${err}` }));
658
+ }
659
+ return;
660
+ }
661
+ // Extract tool name once — used by both permission check and rate limiting
662
+ const toolName = extractToolName(body);
663
+ // ── Permission check (API key auth only) ─────────────────────────────
664
+ if (authResult !== null && toolName !== undefined) {
665
+ const required = getRequiredPermission(toolName);
666
+ if (!hasPermission(authResult, required)) {
667
+ res.writeHead(403, { 'Content-Type': 'application/json' });
668
+ res.end(JSON.stringify({
669
+ error: `Forbidden: '${required}' permission required for ${toolName}`,
670
+ }));
671
+ return;
672
+ }
673
+ }
674
+ // ── Rate limiting ────────────────────────────────────────────────────
675
+ if (limiter !== null) {
676
+ // Prefer tenant ID as rate-limit key when authenticated; fall back to IP
677
+ const rateLimitKey = authResult?.tenantId ? `tenant:${authResult.tenantId}` : clientIp;
678
+ const cost = getToolCost(toolName, rl.perToolLimits);
679
+ const result = limiter.tryConsume(rateLimitKey, cost);
680
+ if (!result.allowed) {
681
+ const retryAfterSec = Math.ceil(result.retryAfterMs / 1000);
682
+ res.writeHead(429, {
683
+ 'Content-Type': 'application/json',
684
+ 'Retry-After': String(retryAfterSec),
685
+ 'X-RateLimit-Limit': String(rl.maxTokens),
686
+ 'X-RateLimit-Remaining': String(result.remainingTokens),
687
+ });
688
+ res.end(JSON.stringify({
689
+ error: 'Too Many Requests',
690
+ retryAfterMs: result.retryAfterMs,
691
+ }));
692
+ logger.debug(`Rate limit exceeded for ${rateLimitKey} (tool: ${toolName ?? 'unknown'})`);
693
+ options.adminApi?.api.logRequest({
694
+ timestamp: new Date().toISOString(),
695
+ tenantId: authResult?.tenantId ?? null,
696
+ tool: toolName ?? '__unknown__',
697
+ durationMs: 0,
698
+ status: 'rate_limited',
699
+ errorMessage: null,
700
+ });
701
+ return;
702
+ }
703
+ }
704
+ // Each request gets a fresh McpServer + transport (stateless mode)
705
+ const mcpServer = options.serverFactory();
706
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
707
+ const mcpStart = Date.now();
708
+ try {
709
+ await mcpServer.connect(transport);
710
+ await transport.handleRequest(req, res, body);
711
+ options.adminApi?.api.logRequest({
712
+ timestamp: new Date().toISOString(),
713
+ tenantId: authResult?.tenantId ?? null,
714
+ tool: toolName ?? '__unknown__',
715
+ durationMs: Date.now() - mcpStart,
716
+ status: 'success',
717
+ errorMessage: null,
718
+ });
719
+ res.on('close', () => {
720
+ transport.close().catch(() => { });
721
+ mcpServer.close().catch(() => { });
722
+ });
723
+ }
724
+ catch (err) {
725
+ logger.warn(`MCP request error: ${err}`);
726
+ options.adminApi?.api.logRequest({
727
+ timestamp: new Date().toISOString(),
728
+ tenantId: authResult?.tenantId ?? null,
729
+ tool: toolName ?? '__unknown__',
730
+ durationMs: Date.now() - mcpStart,
731
+ status: 'error',
732
+ errorMessage: String(err),
733
+ });
734
+ if (!res.headersSent) {
735
+ if (err instanceof QuotaExceededError) {
736
+ res.writeHead(507, { 'Content-Type': 'application/json' });
737
+ res.end(JSON.stringify({
738
+ error: 'Insufficient Storage',
739
+ tenantId: err.tenantId,
740
+ quotaBytes: err.quotaBytes,
741
+ usedBytes: err.usedBytes,
742
+ requestedBytes: err.requestedBytes,
743
+ }));
744
+ }
745
+ else {
746
+ res.writeHead(500, { 'Content-Type': 'application/json' });
747
+ res.end(JSON.stringify({
748
+ jsonrpc: '2.0',
749
+ error: { code: -32603, message: 'Internal server error' },
750
+ id: null,
751
+ }));
752
+ }
753
+ }
754
+ }
755
+ return;
756
+ }
757
+ // ── Admin API ────────────────────────────────────────────────────────────
758
+ if (url.startsWith('/admin') && options.adminApi) {
759
+ // Admin routes require API key authentication
760
+ if (!options.apiKeyAuth?.enabled) {
761
+ res.writeHead(403, { 'Content-Type': 'application/json' });
762
+ res.end(JSON.stringify({ error: 'Forbidden: admin API requires API key authentication' }));
763
+ return;
764
+ }
765
+ const adminApiKey = extractApiKeyFromHeaders(req.headers);
766
+ if (adminApiKey === null || !validateFormat(adminApiKey)) {
767
+ res.writeHead(401, { 'Content-Type': 'application/json' });
768
+ res.end(JSON.stringify({ error: 'Unauthorized: valid API key required' }));
769
+ return;
770
+ }
771
+ const adminAuth = await options.apiKeyAuth.validator.validate(adminApiKey);
772
+ if (!adminAuth.valid) {
773
+ res.writeHead(401, { 'Content-Type': 'application/json' });
774
+ res.end(JSON.stringify({ error: 'Unauthorized: API key not found or revoked' }));
775
+ return;
776
+ }
777
+ if (!hasPermission(adminAuth, 'admin')) {
778
+ res.writeHead(403, { 'Content-Type': 'application/json' });
779
+ res.end(JSON.stringify({ error: 'Forbidden: admin permission required' }));
780
+ return;
781
+ }
782
+ await options.adminApi.api.handle(req, res, adminAuth);
783
+ return;
784
+ }
785
+ // ── REST API for web UI ──────────────────────────────────────────────────
786
+ if (url.startsWith('/api/')) {
787
+ // Apply the same API key auth as the MCP endpoint when apiKeyAuth is enabled
788
+ if (options.apiKeyAuth?.enabled) {
789
+ const apiKey = extractApiKeyFromHeaders(req.headers);
790
+ if (apiKey === null || !validateFormat(apiKey)) {
791
+ res.writeHead(401, { 'Content-Type': 'application/json' });
792
+ res.end(JSON.stringify({ error: 'Unauthorized: API key required (Authorization: Bearer pctx_...)' }));
793
+ return;
794
+ }
795
+ const auth = await options.apiKeyAuth.validator.validate(apiKey);
796
+ if (!auth.valid) {
797
+ res.writeHead(403, { 'Content-Type': 'application/json' });
798
+ res.end(JSON.stringify({ error: 'Forbidden: invalid or revoked API key' }));
799
+ return;
800
+ }
801
+ }
802
+ await handleRestApi(req, res, url, method);
803
+ return;
804
+ }
805
+ // ── Static UI assets ─────────────────────────────────────────────────────
806
+ {
807
+ const served = serveStatic(req, res, url, options.serveUi ?? true);
808
+ if (served)
809
+ return;
810
+ }
811
+ // ── 404 ─────────────────────────────────────────────────────────────────
812
+ res.writeHead(404, { 'Content-Type': 'application/json' });
813
+ res.end(JSON.stringify({ error: 'Not found' }));
814
+ });
815
+ server.listen(options.port, options.host, () => {
816
+ const addr = server.address();
817
+ logger.info(`PureContext MCP listening on http://${options.host}:${addr.port}`);
818
+ resolve(server);
819
+ });
820
+ server.on('error', reject);
821
+ }); // end Promise
822
+ }
823
+ //# sourceMappingURL=http-server.js.map