atabey 0.0.13 → 0.0.14

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 (271) hide show
  1. package/ATABEY.md +1 -1
  2. package/README.md +252 -52
  3. package/dist/framework-mcp/src/index.js +914 -62
  4. package/dist/framework-mcp/src/index.js.map +1 -1
  5. package/dist/framework-mcp/src/tools/compliance/mask_pii.d.ts +17 -0
  6. package/dist/framework-mcp/src/tools/compliance/mask_pii.js +56 -0
  7. package/dist/framework-mcp/src/tools/compliance/mask_pii.js.map +1 -0
  8. package/dist/framework-mcp/src/tools/definitions.js +10 -9
  9. package/dist/framework-mcp/src/tools/definitions.js.map +1 -1
  10. package/dist/framework-mcp/src/tools/framework/run_tests.js +25 -0
  11. package/dist/framework-mcp/src/tools/framework/run_tests.js.map +1 -1
  12. package/dist/framework-mcp/src/tools/index.js +7 -8
  13. package/dist/framework-mcp/src/tools/index.js.map +1 -1
  14. package/dist/framework-mcp/src/tools/messaging/approve_operation.d.ts +22 -0
  15. package/dist/framework-mcp/src/tools/messaging/approve_operation.js +81 -0
  16. package/dist/framework-mcp/src/tools/messaging/approve_operation.js.map +1 -0
  17. package/dist/framework-mcp/src/tools/messaging/ask_human.d.ts +15 -0
  18. package/dist/framework-mcp/src/tools/messaging/ask_human.js +89 -20
  19. package/dist/framework-mcp/src/tools/messaging/ask_human.js.map +1 -1
  20. package/dist/framework-mcp/src/tools/observability/check_ports.js +3 -1
  21. package/dist/framework-mcp/src/tools/observability/check_ports.js.map +1 -1
  22. package/dist/framework-mcp/src/tools/quality/analyze_code_quality.js +13 -64
  23. package/dist/framework-mcp/src/tools/quality/analyze_code_quality.js.map +1 -1
  24. package/dist/framework-mcp/src/tools/schemas.d.ts +48 -53
  25. package/dist/framework-mcp/src/tools/schemas.js +18 -22
  26. package/dist/framework-mcp/src/tools/schemas.js.map +1 -1
  27. package/dist/framework-mcp/src/tools/shell/run_command.js +10 -0
  28. package/dist/framework-mcp/src/tools/shell/run_command.js.map +1 -1
  29. package/dist/framework-mcp/src/tools/types.d.ts +11 -105
  30. package/dist/framework-mcp/src/tools/types.js +1 -0
  31. package/dist/framework-mcp/src/tools/types.js.map +1 -1
  32. package/dist/framework-mcp/src/utils/auth.d.ts +36 -0
  33. package/dist/framework-mcp/src/utils/auth.js +79 -0
  34. package/dist/framework-mcp/src/utils/auth.js.map +1 -0
  35. package/dist/framework-mcp/src/utils/auto-rollback.d.ts +71 -0
  36. package/dist/framework-mcp/src/utils/auto-rollback.js +266 -0
  37. package/dist/framework-mcp/src/utils/auto-rollback.js.map +1 -0
  38. package/dist/framework-mcp/src/utils/compliance.js +12 -4
  39. package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
  40. package/dist/framework-mcp/src/utils/context-optimizer.d.ts +69 -0
  41. package/dist/framework-mcp/src/utils/context-optimizer.js +157 -0
  42. package/dist/framework-mcp/src/utils/context-optimizer.js.map +1 -0
  43. package/dist/framework-mcp/src/utils/discipline.d.ts +68 -0
  44. package/dist/framework-mcp/src/utils/discipline.js +218 -0
  45. package/dist/framework-mcp/src/utils/discipline.js.map +1 -0
  46. package/dist/framework-mcp/src/utils/finops.d.ts +143 -0
  47. package/dist/framework-mcp/src/utils/finops.js +326 -0
  48. package/dist/framework-mcp/src/utils/finops.js.map +1 -0
  49. package/dist/framework-mcp/src/utils/human-in-loop.d.ts +95 -0
  50. package/dist/framework-mcp/src/utils/human-in-loop.js +216 -0
  51. package/dist/framework-mcp/src/utils/human-in-loop.js.map +1 -0
  52. package/dist/framework-mcp/src/utils/license-scanner.d.ts +71 -0
  53. package/dist/framework-mcp/src/utils/license-scanner.js +284 -0
  54. package/dist/framework-mcp/src/utils/license-scanner.js.map +1 -0
  55. package/dist/framework-mcp/src/utils/loop-detector.d.ts +105 -0
  56. package/dist/framework-mcp/src/utils/loop-detector.js +362 -0
  57. package/dist/framework-mcp/src/utils/loop-detector.js.map +1 -0
  58. package/dist/framework-mcp/src/utils/quality.d.ts +19 -0
  59. package/dist/framework-mcp/src/utils/quality.js +124 -0
  60. package/dist/framework-mcp/src/utils/quality.js.map +1 -0
  61. package/dist/framework-mcp/src/utils/rules-engine.d.ts +78 -0
  62. package/dist/framework-mcp/src/utils/rules-engine.js +232 -0
  63. package/dist/framework-mcp/src/utils/rules-engine.js.map +1 -0
  64. package/dist/framework-mcp/src/utils/silent-router.d.ts +47 -0
  65. package/dist/framework-mcp/src/utils/silent-router.js +129 -0
  66. package/dist/framework-mcp/src/utils/silent-router.js.map +1 -0
  67. package/dist/framework-mcp/src/utils/storage.d.ts +1 -0
  68. package/dist/framework-mcp/src/utils/storage.js +2 -2
  69. package/dist/framework-mcp/src/utils/storage.js.map +1 -1
  70. package/dist/framework-mcp/src/utils/telemetry-streamer.d.ts +133 -0
  71. package/dist/framework-mcp/src/utils/telemetry-streamer.js +397 -0
  72. package/dist/framework-mcp/src/utils/telemetry-streamer.js.map +1 -0
  73. package/dist/framework-mcp/tests/tools/file_system/file_system_tools.test.js +2 -2
  74. package/dist/framework-mcp/tests/tools/file_system/file_system_tools.test.js.map +1 -1
  75. package/dist/framework-mcp/tests/tools/framework/run_tests.test.d.ts +1 -0
  76. package/dist/framework-mcp/tests/tools/framework/run_tests.test.js +49 -0
  77. package/dist/framework-mcp/tests/tools/framework/run_tests.test.js.map +1 -0
  78. package/dist/framework-mcp/tests/tools/messaging/send_message.test.js +14 -5
  79. package/dist/framework-mcp/tests/tools/messaging/send_message.test.js.map +1 -1
  80. package/dist/framework-mcp/tests/tools/observability/check_ports.test.d.ts +1 -0
  81. package/dist/framework-mcp/tests/tools/observability/check_ports.test.js +30 -0
  82. package/dist/framework-mcp/tests/tools/observability/check_ports.test.js.map +1 -0
  83. package/dist/framework-mcp/tests/tools/shell/run_command.test.js +7 -0
  84. package/dist/framework-mcp/tests/tools/shell/run_command.test.js.map +1 -1
  85. package/dist/framework-mcp/tests/utils/auto-rollback.test.d.ts +7 -0
  86. package/dist/framework-mcp/tests/utils/auto-rollback.test.js +178 -0
  87. package/dist/framework-mcp/tests/utils/auto-rollback.test.js.map +1 -0
  88. package/dist/framework-mcp/tests/utils/context-optimizer.test.d.ts +1 -0
  89. package/dist/framework-mcp/tests/utils/context-optimizer.test.js +62 -0
  90. package/dist/framework-mcp/tests/utils/context-optimizer.test.js.map +1 -0
  91. package/dist/framework-mcp/tests/utils/finops.test.d.ts +7 -0
  92. package/dist/framework-mcp/tests/utils/finops.test.js +157 -0
  93. package/dist/framework-mcp/tests/utils/finops.test.js.map +1 -0
  94. package/dist/framework-mcp/tests/utils/human-in-loop.test.d.ts +1 -0
  95. package/dist/framework-mcp/tests/utils/human-in-loop.test.js +59 -0
  96. package/dist/framework-mcp/tests/utils/human-in-loop.test.js.map +1 -0
  97. package/dist/framework-mcp/tests/utils/license-scanner.test.d.ts +7 -0
  98. package/dist/framework-mcp/tests/utils/license-scanner.test.js +131 -0
  99. package/dist/framework-mcp/tests/utils/license-scanner.test.js.map +1 -0
  100. package/dist/framework-mcp/tests/utils/loop-detector.test.d.ts +7 -0
  101. package/dist/framework-mcp/tests/utils/loop-detector.test.js +204 -0
  102. package/dist/framework-mcp/tests/utils/loop-detector.test.js.map +1 -0
  103. package/dist/framework-mcp/tests/utils/rules-engine.test.d.ts +1 -0
  104. package/dist/framework-mcp/tests/utils/rules-engine.test.js +106 -0
  105. package/dist/framework-mcp/tests/utils/rules-engine.test.js.map +1 -0
  106. package/dist/framework-mcp/tests/utils/telemetry-streamer.test.d.ts +7 -0
  107. package/dist/framework-mcp/tests/utils/telemetry-streamer.test.js +201 -0
  108. package/dist/framework-mcp/tests/utils/telemetry-streamer.test.js.map +1 -0
  109. package/dist/src/cli/commands/check.js +16 -1
  110. package/dist/src/cli/commands/check.js.map +1 -1
  111. package/dist/src/cli/commands/dashboard.js +188 -164
  112. package/dist/src/cli/commands/dashboard.js.map +1 -1
  113. package/dist/src/cli/commands/gateway.js +2 -2
  114. package/dist/src/cli/commands/gateway.js.map +1 -1
  115. package/dist/src/cli/commands/init/scaffold-core.d.ts +2 -0
  116. package/dist/src/cli/commands/init/scaffold-core.js +16 -1
  117. package/dist/src/cli/commands/init/scaffold-core.js.map +1 -1
  118. package/dist/src/cli/commands/init/scaffold-ops.js +2 -2
  119. package/dist/src/cli/commands/init/scaffold-ops.js.map +1 -1
  120. package/dist/src/cli/commands/init/scaffold-standards.js +13 -1
  121. package/dist/src/cli/commands/init/scaffold-standards.js.map +1 -1
  122. package/dist/src/cli/commands/init.d.ts +4 -0
  123. package/dist/src/cli/commands/init.js +80 -24
  124. package/dist/src/cli/commands/init.js.map +1 -1
  125. package/dist/src/cli/commands/quickstart.d.ts +1 -1
  126. package/dist/src/cli/commands/quickstart.js +12 -24
  127. package/dist/src/cli/commands/quickstart.js.map +1 -1
  128. package/dist/src/cli/index.js +24 -23
  129. package/dist/src/cli/index.js.map +1 -1
  130. package/dist/src/cli/shims.js +7 -7
  131. package/dist/src/cli/utils/pkg.js +2 -3
  132. package/dist/src/cli/utils/pkg.js.map +1 -1
  133. package/dist/src/cli/utils/quality.d.ts +10 -0
  134. package/dist/src/cli/utils/quality.js +10 -0
  135. package/dist/src/cli/utils/quality.js.map +1 -0
  136. package/dist/src/modules/agents/definitions.js +45 -40
  137. package/dist/src/modules/agents/definitions.js.map +1 -1
  138. package/dist/src/modules/agents/registry/manager.js +6 -1
  139. package/dist/src/modules/agents/registry/manager.js.map +1 -1
  140. package/dist/src/modules/engines/agent-executor.d.ts +6 -10
  141. package/dist/src/modules/engines/agent-executor.js +53 -21
  142. package/dist/src/modules/engines/agent-executor.js.map +1 -1
  143. package/dist/src/modules/engines/agent-loop.d.ts +34 -0
  144. package/dist/src/modules/engines/agent-loop.js +102 -0
  145. package/dist/src/modules/engines/agent-loop.js.map +1 -0
  146. package/dist/src/modules/engines/evaluation-engine.js +2 -2
  147. package/dist/src/modules/engines/evaluation-engine.js.map +1 -1
  148. package/dist/src/modules/engines/quality-gate.d.ts +13 -5
  149. package/dist/src/modules/engines/quality-gate.js +39 -31
  150. package/dist/src/modules/engines/quality-gate.js.map +1 -1
  151. package/dist/src/modules/engines/routing-engine.d.ts +5 -3
  152. package/dist/src/modules/engines/routing-engine.js +16 -9
  153. package/dist/src/modules/engines/routing-engine.js.map +1 -1
  154. package/dist/src/modules/gateway/cost-tracker.d.ts +5 -0
  155. package/dist/src/modules/gateway/cost-tracker.js +71 -0
  156. package/dist/src/modules/gateway/cost-tracker.js.map +1 -1
  157. package/dist/src/modules/gateway/gateway.d.ts +4 -0
  158. package/dist/src/modules/gateway/gateway.js +8 -0
  159. package/dist/src/modules/gateway/gateway.js.map +1 -1
  160. package/dist/src/shared/audit.js +3 -2
  161. package/dist/src/shared/audit.js.map +1 -1
  162. package/dist/src/shared/pii.d.ts +41 -9
  163. package/dist/src/shared/pii.js +122 -31
  164. package/dist/src/shared/pii.js.map +1 -1
  165. package/dist/src/shared/storage.d.ts +19 -0
  166. package/dist/src/shared/storage.js +46 -6
  167. package/dist/src/shared/storage.js.map +1 -1
  168. package/dist/tests/embedding.test.d.ts +1 -0
  169. package/dist/tests/embedding.test.js +82 -0
  170. package/dist/tests/embedding.test.js.map +1 -0
  171. package/dist/tests/engines/evaluation-engine.test.d.ts +1 -0
  172. package/dist/tests/engines/evaluation-engine.test.js +71 -0
  173. package/dist/tests/engines/evaluation-engine.test.js.map +1 -0
  174. package/dist/tests/engines/planning-engine.test.d.ts +1 -0
  175. package/dist/tests/engines/planning-engine.test.js +100 -0
  176. package/dist/tests/engines/planning-engine.test.js.map +1 -0
  177. package/dist/tests/engines/quality-gate.test.d.ts +1 -0
  178. package/dist/tests/engines/quality-gate.test.js +59 -0
  179. package/dist/tests/engines/quality-gate.test.js.map +1 -0
  180. package/dist/tests/engines/risk-engine.test.d.ts +1 -0
  181. package/dist/tests/engines/risk-engine.test.js +80 -0
  182. package/dist/tests/engines/risk-engine.test.js.map +1 -0
  183. package/dist/tests/engines/routing-engine.test.d.ts +1 -0
  184. package/dist/tests/engines/routing-engine.test.js +78 -0
  185. package/dist/tests/engines/routing-engine.test.js.map +1 -0
  186. package/dist/tests/integration/hermes_locking.test.js +1 -0
  187. package/dist/tests/integration/hermes_locking.test.js.map +1 -1
  188. package/dist/tests/modules/gateway/cost-tracker.test.js +20 -1
  189. package/dist/tests/modules/gateway/cost-tracker.test.js.map +1 -1
  190. package/dist/tests/shared/lock.test.d.ts +1 -0
  191. package/dist/tests/shared/lock.test.js +95 -0
  192. package/dist/tests/shared/lock.test.js.map +1 -0
  193. package/dist/tests/shared/pii.test.d.ts +1 -0
  194. package/dist/tests/shared/pii.test.js +183 -0
  195. package/dist/tests/shared/pii.test.js.map +1 -0
  196. package/framework-mcp/README.md +307 -35
  197. package/framework-mcp/dist/dashboard/assets/index-B2mYld0c.js +1221 -0
  198. package/framework-mcp/dist/dashboard/index.html +1 -1
  199. package/framework-mcp/dist/framework-mcp/src/index.js +914 -62
  200. package/framework-mcp/dist/framework-mcp/src/tools/compliance/mask_pii.js +55 -0
  201. package/framework-mcp/dist/framework-mcp/src/tools/definitions.js +10 -9
  202. package/framework-mcp/dist/framework-mcp/src/tools/framework/run_tests.js +25 -0
  203. package/framework-mcp/dist/framework-mcp/src/tools/index.js +7 -8
  204. package/framework-mcp/dist/framework-mcp/src/tools/messaging/approve_operation.js +80 -0
  205. package/framework-mcp/dist/framework-mcp/src/tools/messaging/ask_human.js +89 -20
  206. package/framework-mcp/dist/framework-mcp/src/tools/observability/check_ports.js +3 -1
  207. package/framework-mcp/dist/framework-mcp/src/tools/quality/analyze_code_quality.js +13 -64
  208. package/framework-mcp/dist/framework-mcp/src/tools/schemas.js +18 -22
  209. package/framework-mcp/dist/framework-mcp/src/tools/shell/run_command.js +10 -0
  210. package/framework-mcp/dist/framework-mcp/src/tools/types.js +1 -0
  211. package/framework-mcp/dist/framework-mcp/src/utils/auth.js +78 -0
  212. package/framework-mcp/dist/framework-mcp/src/utils/auto-rollback.js +265 -0
  213. package/framework-mcp/dist/framework-mcp/src/utils/compliance.js +12 -4
  214. package/framework-mcp/dist/framework-mcp/src/utils/context-optimizer.js +156 -0
  215. package/framework-mcp/dist/framework-mcp/src/utils/discipline.js +217 -0
  216. package/framework-mcp/dist/framework-mcp/src/utils/finops.js +325 -0
  217. package/framework-mcp/dist/framework-mcp/src/utils/human-in-loop.js +215 -0
  218. package/framework-mcp/dist/framework-mcp/src/utils/license-scanner.js +283 -0
  219. package/framework-mcp/dist/framework-mcp/src/utils/loop-detector.js +361 -0
  220. package/framework-mcp/dist/framework-mcp/src/utils/quality.js +123 -0
  221. package/framework-mcp/dist/framework-mcp/src/utils/rules-engine.js +231 -0
  222. package/framework-mcp/dist/framework-mcp/src/utils/silent-router.js +128 -0
  223. package/framework-mcp/dist/framework-mcp/src/utils/storage.js +2 -2
  224. package/framework-mcp/dist/framework-mcp/src/utils/telemetry-streamer.js +396 -0
  225. package/framework-mcp/dist/src/cli/platforms/core.js +72 -0
  226. package/framework-mcp/dist/src/cli/platforms/index.js +5 -0
  227. package/framework-mcp/dist/src/cli/platforms/paths.js +101 -0
  228. package/framework-mcp/dist/src/cli/platforms/scaffold.js +72 -0
  229. package/framework-mcp/dist/src/cli/platforms/utils.js +75 -0
  230. package/framework-mcp/dist/src/cli/utils/claude.js +57 -0
  231. package/framework-mcp/dist/src/cli/utils/compliance.js +12 -0
  232. package/framework-mcp/dist/src/cli/utils/fs.js +138 -0
  233. package/framework-mcp/dist/src/cli/utils/pkg.js +282 -0
  234. package/framework-mcp/dist/src/cli/utils/quality.js +9 -0
  235. package/framework-mcp/dist/src/modules/agents/definitions.js +563 -0
  236. package/framework-mcp/dist/src/modules/agents/registry/analyst.js +39 -0
  237. package/framework-mcp/dist/src/modules/agents/registry/architect.js +42 -0
  238. package/framework-mcp/dist/src/modules/agents/registry/backend.js +50 -0
  239. package/framework-mcp/dist/src/modules/agents/registry/database.js +45 -0
  240. package/framework-mcp/dist/src/modules/agents/registry/devops.js +45 -0
  241. package/framework-mcp/dist/src/modules/agents/registry/explorer.js +37 -0
  242. package/framework-mcp/dist/src/modules/agents/registry/frontend.js +52 -0
  243. package/framework-mcp/dist/src/modules/agents/registry/git.js +36 -0
  244. package/framework-mcp/dist/src/modules/agents/registry/manager.js +64 -0
  245. package/framework-mcp/dist/src/modules/agents/registry/mobile.js +41 -0
  246. package/framework-mcp/dist/src/modules/agents/registry/native.js +41 -0
  247. package/framework-mcp/dist/src/modules/agents/registry/quality.js +43 -0
  248. package/framework-mcp/dist/src/modules/agents/registry/security.js +42 -0
  249. package/framework-mcp/dist/src/modules/agents/types.js +1 -0
  250. package/framework-mcp/dist/src/modules/engines/quality-gate.js +92 -0
  251. package/framework-mcp/dist/src/modules/engines/risk-engine.js +105 -0
  252. package/framework-mcp/dist/src/modules/engines/routing-engine.js +408 -0
  253. package/framework-mcp/dist/src/modules/engines/types.js +1 -0
  254. package/framework-mcp/dist/src/modules/providers/definitions.js +140 -0
  255. package/framework-mcp/dist/src/modules/providers/registry.js +18 -0
  256. package/framework-mcp/dist/src/modules/providers/shared.js +104 -0
  257. package/framework-mcp/dist/src/modules/providers/types.js +1 -0
  258. package/framework-mcp/dist/src/modules/skills/adapter-skills.js +449 -0
  259. package/framework-mcp/dist/src/modules/skills/definitions.js +70 -0
  260. package/framework-mcp/dist/src/shared/audit.js +220 -0
  261. package/framework-mcp/dist/src/shared/errors.js +68 -0
  262. package/framework-mcp/dist/src/shared/lock.js +159 -0
  263. package/framework-mcp/dist/src/shared/pii.js +122 -31
  264. package/framework-mcp/dist/src/shared/retention.js +170 -0
  265. package/framework-mcp/dist/src/shared/storage.js +46 -6
  266. package/framework-mcp/dist/src/shared/string.js +29 -0
  267. package/framework-mcp/package.json +8 -2
  268. package/mcp.json +11 -6
  269. package/package.json +13 -6
  270. package/templates/full/ATABEY_FULL.md +1 -1
  271. package/framework-mcp/dist/dashboard/assets/index-BAPGLiWE.js +0 -267
@@ -1,15 +1,21 @@
1
1
  #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
2
6
  import fs from "fs";
7
+ import http from "http";
3
8
  import path from "path";
4
9
  import { fileURLToPath } from "url";
5
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
- import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
8
- import { TOOLS, toolHandlers, toolSchemas } from "./tools/index.js";
10
+ import { WebSocket, WebSocketServer } from "ws";
11
+ import { generateULID } from "../../src/cli/utils/time.js";
12
+ import { Audit } from "../../src/shared/audit.js";
13
+ import { maskText, maskToolArgs, maskToolResult } from "../../src/shared/pii.js";
14
+ import { Storage } from "../../src/shared/storage.js";
9
15
  import { RESOURCES, handleReadResource } from "./resources/index.js";
10
- // ─── Server Setup ─────────────────────────────────────────────────
16
+ import { TOOLS, toolHandlers, toolSchemas } from "./tools/index.js";
17
+ // ─── Paths ────────────────────────────────────────────────────────
11
18
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
- // Robustly find package.json by walking up from __dirname
13
19
  function findPackageJson(startDir) {
14
20
  let currentDir = startDir;
15
21
  while (currentDir !== path.parse(currentDir).root) {
@@ -23,19 +29,35 @@ function findPackageJson(startDir) {
23
29
  const pkgPath = findPackageJson(__dirname);
24
30
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
25
31
  const serverVersion = pkg.version;
26
- const server = new Server({
27
- name: "atabey-mcp",
28
- version: serverVersion,
29
- }, {
30
- capabilities: {
31
- tools: {},
32
- resources: {},
33
- },
34
- });
35
- /**
36
- * Validates tool arguments using Zod schemas for "Zero Type Hole" compliance.
37
- * Falls back to basic presence check if no Zod schema is defined for the tool.
38
- */
32
+ const PROJECT_ROOT = process.env.ATABEY_PROJECT_ROOT || process.cwd();
33
+ const FRAMEWORK_DIR = process.env.ATABEY_FRAMEWORK_DIR || path.join(PROJECT_ROOT, ".atabey");
34
+ const UI_DIST_PATH = path.join(__dirname, "../../dist/dashboard");
35
+ // ─── Ports ────────────────────────────────────────────────────────
36
+ const PORT = parseInt(process.env.MCP_PORT || "5858", 10);
37
+ const HOST = process.env.MCP_HOST || "0.0.0.0";
38
+ // ─── MCP Server ───────────────────────────────────────────────────
39
+ const server = new Server({ name: "atabey-mcp", version: serverVersion }, { capabilities: { tools: {}, resources: {} } });
40
+ // Track multiple SSE sessions
41
+ const sessions = new Map();
42
+ // ─── WebSocket Clients ────────────────────────────────────────────
43
+ const wsClients = new Set();
44
+ function broadcastWS(type, payload) {
45
+ const msg = JSON.stringify({ type, payload });
46
+ wsClients.forEach((ws) => {
47
+ if (ws.readyState === WebSocket.OPEN) {
48
+ try {
49
+ ws.send(msg);
50
+ }
51
+ catch {
52
+ wsClients.delete(ws);
53
+ }
54
+ }
55
+ else {
56
+ wsClients.delete(ws);
57
+ }
58
+ });
59
+ }
60
+ // ─── Validation ───────────────────────────────────────────────────
39
61
  function validateArgs(toolName, args) {
40
62
  const schema = toolSchemas[toolName];
41
63
  if (schema) {
@@ -45,7 +67,6 @@ function validateArgs(toolName, args) {
45
67
  }
46
68
  return null;
47
69
  }
48
- // Fallback for tools without Zod schemas yet
49
70
  const definition = TOOLS.find(t => t.name === toolName);
50
71
  if (!definition)
51
72
  return `Unknown tool: ${toolName}`;
@@ -57,11 +78,11 @@ function validateArgs(toolName, args) {
57
78
  }
58
79
  return null;
59
80
  }
81
+ // ─── MCP Handlers ─────────────────────────────────────────────────
60
82
  server.setRequestHandler(ListToolsRequestSchema, async (request) => {
61
- // 2026 Stateless Spec: Log client info from metadata if available
62
83
  const meta = request._meta;
63
84
  if (meta) {
64
- process.stderr.write(`[MCP] Stateless ListTools from ${meta.client?.name || "unknown"} v${meta.client?.version || "?.?"}\n`);
85
+ process.stderr.write(`[MCP] ListTools from ${meta.client?.name || "unknown"} v${meta.client?.version || "?.?"}\n`);
65
86
  }
66
87
  return { tools: TOOLS };
67
88
  });
@@ -72,15 +93,7 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
72
93
  const uri = request.params.uri;
73
94
  try {
74
95
  const content = await handleReadResource(uri);
75
- return {
76
- contents: [
77
- {
78
- uri,
79
- mimeType: "text/markdown",
80
- text: content,
81
- },
82
- ],
83
- };
96
+ return { contents: [{ uri, mimeType: "text/markdown", text: content }] };
84
97
  }
85
98
  catch (error) {
86
99
  const message = error instanceof Error ? error.message : String(error);
@@ -91,66 +104,905 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
91
104
  const req = request;
92
105
  const { name, arguments: args } = req.params;
93
106
  const meta = request._meta;
94
- // 2026 Stateless Spec: Prioritize metadata-driven context
95
107
  if (meta) {
96
- process.stderr.write(`[MCP] Stateless CallTool: ${name} (Client: ${meta.client?.name || "unknown"})\n`);
108
+ process.stderr.write(`[MCP] CallTool: ${name} (Client: ${meta.client?.name || "unknown"})\n`);
97
109
  }
98
- const projectRoot = process.env.ATABEY_PROJECT_ROOT || process.cwd();
99
110
  try {
100
111
  const handler = toolHandlers[name];
101
112
  if (!handler) {
102
- return {
103
- isError: true,
104
- content: [{ type: "text", text: `[ERROR] Unknown tool: ${name}` }],
105
- };
113
+ return { isError: true, content: [{ type: "text", text: `[ERROR] Unknown tool: ${name}` }] };
106
114
  }
107
- // [SECURITY] Runtime Validation
108
115
  const validationError = validateArgs(name, args || {});
109
116
  if (validationError) {
110
- return {
111
- isError: true,
112
- content: [{ type: "text", text: `[ERROR] Validation Error: ${validationError}` }],
113
- };
117
+ return { isError: true, content: [{ type: "text", text: `[ERROR] Validation Error: ${validationError}` }] };
118
+ }
119
+ // [PII/GDPR/KVKK] Mask args before handler
120
+ const maskedArgs = maskToolArgs(args || {});
121
+ // [TOKEN ECONOMY] Log token usage
122
+ const argsJson = JSON.stringify(maskedArgs);
123
+ const estimatedTokens = Math.ceil(argsJson.length / 4);
124
+ const clientAgent = meta?.client?.name || "mcp-client";
125
+ try {
126
+ const { Metrics } = await import("./utils/metrics.js");
127
+ Metrics.logUsage(PROJECT_ROOT, clientAgent, name, estimatedTokens);
128
+ }
129
+ catch { /* metrics should not block */ }
130
+ // [GOVERNANCE] Validate arguments against governance rules (pre-execution)
131
+ try {
132
+ const { validateArgsAgainstRules } = await import("./utils/rules-engine.js");
133
+ const governanceError = validateArgsAgainstRules(name, maskedArgs);
134
+ if (governanceError) {
135
+ process.stderr.write(`[GOVERNANCE] Blocked: ${governanceError}\n`);
136
+ broadcastWS("governance_violation", { agent: clientAgent, tool: name, error: governanceError, timestamp: new Date().toISOString() });
137
+ return { isError: true, content: [{ type: "text", text: governanceError }] };
138
+ }
139
+ }
140
+ catch { /* governance should not block */ }
141
+ // [DISCIPLINE] Enforce AI discipline at tool level BEFORE execution
142
+ try {
143
+ const { enforceDiscipline } = await import("./utils/discipline.js");
144
+ const disciplineError = await enforceDiscipline(clientAgent, name, maskedArgs);
145
+ if (disciplineError) {
146
+ process.stderr.write(`[DISCIPLINE] Blocked: ${disciplineError}\n`);
147
+ broadcastWS("discipline_violation", { agent: clientAgent, tool: name, error: disciplineError, timestamp: new Date().toISOString() });
148
+ return { isError: true, content: [{ type: "text", text: `[DISCIPLINE] ${disciplineError}` }] };
149
+ }
150
+ }
151
+ catch { /* discipline should not block execution if module fails */ }
152
+ // [SILENT ROUTER] Detect agent from context and inject rules
153
+ const traceId = generateULID();
154
+ let detectedAgent = clientAgent;
155
+ try {
156
+ const { detectAgent, stealthNotify } = await import("./utils/silent-router.js");
157
+ detectedAgent = detectAgent(name, maskedArgs);
158
+ if (detectedAgent !== clientAgent) {
159
+ stealthNotify(detectedAgent, name, `Silently routed from ${clientAgent}`);
160
+ process.stderr.write(`[SILENT ROUTER] ${name} → ${detectedAgent} (from ${clientAgent})\n`);
161
+ }
162
+ }
163
+ catch { /* silent router should not block */ }
164
+ // [LOOP DETECTION] Check for infinite loop patterns
165
+ try {
166
+ const { recordAndCheck } = await import("./utils/loop-detector.js");
167
+ const loopAlert = recordAndCheck(detectedAgent, name, maskedArgs);
168
+ if (loopAlert) {
169
+ process.stderr.write(`[LOOP DETECT] ${loopAlert.type}: ${loopAlert.detail}\n`);
170
+ broadcastWS("loop_detected", { agent: detectedAgent, tool: name, alert: loopAlert, timestamp: new Date().toISOString() });
171
+ if (loopAlert.severity === "critical" && loopAlert.cooldownUntil) {
172
+ const remaining = Math.ceil((loopAlert.cooldownUntil - Date.now()) / 1000);
173
+ return {
174
+ isError: true,
175
+ content: [{ type: "text", text: `[LOOP DETECTED] ${loopAlert.detail} Cooldown: ${remaining}s. Please change your approach.` }]
176
+ };
177
+ }
178
+ }
179
+ }
180
+ catch { /* loop detection should not block */ }
181
+ // [FINOPS] Check budget BEFORE execution (record usage)
182
+ try {
183
+ const { budgetManager } = await import("./utils/finops.js");
184
+ const budgetError = budgetManager.recordUsage(detectedAgent, estimatedTokens);
185
+ if (budgetError) {
186
+ process.stderr.write(`[FINOPS] Budget blocked: ${budgetError}\n`);
187
+ broadcastWS("budget_blocked", { agent: detectedAgent, tool: name, error: budgetError, timestamp: new Date().toISOString() });
188
+ return { isError: true, content: [{ type: "text", text: budgetError }] };
189
+ }
190
+ }
191
+ catch { /* finops should not block */ }
192
+ // [LICENSE] Validate write content for license compliance (pre-execution)
193
+ if (name === "write_file" || name === "replace_text" || name === "patch_file") {
194
+ try {
195
+ const { validateLicenseCompliance } = await import("./utils/license-scanner.js");
196
+ const filePath = maskedArgs.path || "";
197
+ const content = maskedArgs.content || "";
198
+ if (filePath && content) {
199
+ const licenseError = validateLicenseCompliance(filePath, content);
200
+ if (licenseError) {
201
+ process.stderr.write(`[LICENSE] Blocked: ${licenseError}\n`);
202
+ broadcastWS("license_violation", { agent: detectedAgent, tool: name, error: licenseError, timestamp: new Date().toISOString() });
203
+ return { isError: true, content: [{ type: "text", text: licenseError }] };
204
+ }
205
+ }
206
+ }
207
+ catch { /* license scan should not block */ }
208
+ }
209
+ // [AUTO-ROLLBACK] Prepare snapshot for write operations
210
+ if (name === "write_file" || name === "replace_text" || name === "patch_file") {
211
+ try {
212
+ const { AutoRollbackEngine } = await import("./utils/auto-rollback.js");
213
+ const filePath = maskedArgs.path || "";
214
+ if (filePath) {
215
+ AutoRollbackEngine.prepareWrite(filePath, traceId);
216
+ }
217
+ }
218
+ catch { /* auto-rollback should not block */ }
219
+ }
220
+ // [HUMAN-IN-LOOP] Check risk gate before execution
221
+ try {
222
+ const { RiskEngine } = await import("../../src/modules/engines/risk-engine.js");
223
+ const riskContext = `${name} ${JSON.stringify(maskedArgs)}`;
224
+ const riskResult = RiskEngine.assessTaskRisk(riskContext);
225
+ if (riskResult.totalScore > 0) {
226
+ const { checkRiskGate } = await import("./utils/human-in-loop.js");
227
+ const riskReason = riskResult.factors.map(f => f.description).join("; ") || "High-risk operation detected";
228
+ const gateResult = checkRiskGate(traceId, name, detectedAgent, riskResult.totalScore, riskReason, maskedArgs);
229
+ if (gateResult?.blocked) {
230
+ process.stderr.write(`[RISK GATE] Blocked: ${riskReason} (score: ${riskResult.totalScore})\n`);
231
+ broadcastWS("risk_blocked", { agent: detectedAgent, tool: name, riskScore: riskResult.totalScore, traceId, timestamp: new Date().toISOString() });
232
+ return { isError: true, content: [{ type: "text", text: gateResult.message }] };
233
+ }
234
+ if (gateResult?.warning) {
235
+ process.stderr.write(`[RISK WARNING] ${gateResult.warning}\n`);
236
+ }
237
+ }
238
+ }
239
+ catch { /* risk gate should not block */ }
240
+ // Execute
241
+ const result = await handler(PROJECT_ROOT, maskedArgs);
242
+ // [AUTO-ROLLBACK] Check write results for governance violations
243
+ if (name === "write_file" || name === "replace_text" || name === "patch_file") {
244
+ try {
245
+ const responseText = result.content?.filter(b => b.type === "text").map(b => b.text).join(" ") || "";
246
+ const filePath = maskedArgs.path || "";
247
+ const content = maskedArgs.content || "";
248
+ if (filePath && content) {
249
+ const { AutoRollbackEngine } = await import("./utils/auto-rollback.js");
250
+ const { scanFileForViolations } = await import("./utils/rules-engine.js");
251
+ const violations = scanFileForViolations(filePath, content);
252
+ if (violations.length > 0) {
253
+ const violationRecords = violations.map(v => ({
254
+ rule: v.rule.name,
255
+ severity: v.rule.priority,
256
+ filePath,
257
+ line: v.line,
258
+ detail: `Line ${v.line}: ${v.match}`,
259
+ regenerateInstruction: v.rule.errorMessage,
260
+ }));
261
+ const rolledBack = AutoRollbackEngine.validateAndRollback(filePath, content, violationRecords);
262
+ if (rolledBack) {
263
+ const instruction = AutoRollbackEngine.buildRegenerateInstruction(rolledBack, name);
264
+ broadcastWS("rollback_violation", { agent: detectedAgent, tool: name, violations: rolledBack, timestamp: new Date().toISOString() });
265
+ return { isError: true, content: [{ type: "text", text: instruction }] };
266
+ }
267
+ }
268
+ }
269
+ }
270
+ catch { /* auto-rollback should not block */ }
271
+ }
272
+ // [DISCIPLINE] Validate response content BEFORE returning to AI
273
+ try {
274
+ const { validateResponse } = await import("./utils/discipline.js");
275
+ const responseError = validateResponse(name, result);
276
+ if (responseError) {
277
+ process.stderr.write(`[DISCIPLINE] Response blocked: ${responseError}\n`);
278
+ broadcastWS("discipline_violation", { agent: detectedAgent, tool: name, error: responseError, timestamp: new Date().toISOString() });
279
+ return { isError: true, content: [{ type: "text", text: `[DISCIPLINE] ${responseError}` }] };
280
+ }
281
+ }
282
+ catch { /* response validation should not block */ }
283
+ // [GOVERNANCE] Validate response against governance rules (post-execution)
284
+ try {
285
+ const { validateResponseAgainstRules } = await import("./utils/rules-engine.js");
286
+ const responseText = result.content?.filter(b => b.type === "text").map(b => b.text).join(" ") || "";
287
+ const govResponseError = validateResponseAgainstRules(name, responseText);
288
+ if (govResponseError) {
289
+ process.stderr.write(`[GOVERNANCE] Response violation: ${govResponseError}\n`);
290
+ broadcastWS("governance_violation", { agent: detectedAgent, tool: name, error: govResponseError, timestamp: new Date().toISOString() });
291
+ // Don't block the response, just warn
292
+ }
293
+ }
294
+ catch { /* governance response validation should not block */ }
295
+ // [CONTEXT OPTIMIZER] Check token budget after execution
296
+ try {
297
+ const { checkTokenBudget } = await import("./utils/context-optimizer.js");
298
+ const responseText = result.content?.filter(b => b.type === "text").map(b => b.text).join(" ") || "";
299
+ const budgetError = checkTokenBudget(detectedAgent, name, responseText);
300
+ if (budgetError) {
301
+ process.stderr.write(`[TOKEN BUDGET] Warning: ${budgetError}\n`);
302
+ broadcastWS("token_budget_warning", { agent: detectedAgent, tool: name, error: budgetError, timestamp: new Date().toISOString() });
303
+ }
114
304
  }
115
- return await handler(projectRoot, args || {});
305
+ catch { /* token budget should not block */ }
306
+ // [SILENT ROUTER] Build silent context injection
307
+ try {
308
+ const { buildSilentContext } = await import("./utils/silent-router.js");
309
+ if (result.content) {
310
+ for (let i = 0; i < result.content.length; i++) {
311
+ const block = result.content[i];
312
+ if (block.type === "text") {
313
+ result.content[i] = {
314
+ ...block,
315
+ text: await buildSilentContext(detectedAgent, name, block.text),
316
+ };
317
+ }
318
+ }
319
+ }
320
+ }
321
+ catch { /* silent context injection should not block */ }
322
+ // Broadcast to dashboard WS
323
+ broadcastWS("tool_call", { agent: detectedAgent, action: name, tokens: estimatedTokens, traceId, timestamp: new Date().toISOString() });
324
+ // [PII/GDPR/KVKK] Mask result before returning to AI
325
+ return maskToolResult(result);
116
326
  }
117
327
  catch (error) {
118
328
  const message = error instanceof Error ? error.message : "Unknown error occurred";
119
- return {
120
- isError: true,
121
- content: [{ type: "text", text: `[ERROR] Execution failed: ${message}` }],
122
- };
329
+ return { isError: true, content: [{ type: "text", text: `[ERROR] Execution failed: ${message}` }] };
123
330
  }
124
331
  });
125
- // ─── Graceful Startup & Shutdown ──────────────────────────────────
332
+ // ─── API Helpers ──────────────────────────────────────────────────
333
+ function serveJson(res, statusCode, data) {
334
+ res.writeHead(statusCode, { "Content-Type": "application/json" });
335
+ res.end(JSON.stringify(data));
336
+ }
337
+ function serveFile(res, filePath, contentType) {
338
+ if (fs.existsSync(filePath)) {
339
+ try {
340
+ const content = fs.readFileSync(filePath);
341
+ res.writeHead(200, { "Content-Type": contentType });
342
+ res.end(content);
343
+ }
344
+ catch (e) {
345
+ res.writeHead(500);
346
+ res.end(e.message);
347
+ }
348
+ }
349
+ else {
350
+ res.writeHead(404);
351
+ res.end("Not Found");
352
+ }
353
+ }
354
+ function parseUrl(url) {
355
+ const [pathname, queryString] = url.split("?");
356
+ const params = {};
357
+ if (queryString) {
358
+ queryString.split("&").forEach((pair) => {
359
+ const [key, value] = pair.split("=");
360
+ params[decodeURIComponent(key)] = decodeURIComponent(value || "");
361
+ });
362
+ }
363
+ return { pathname, params };
364
+ }
365
+ // ─── Unified HTTP Server ──────────────────────────────────────────
366
+ function createUnifiedServer() {
367
+ const httpServer = http.createServer(async (req, res) => {
368
+ const url = req.url || "/";
369
+ const { pathname, params } = parseUrl(url);
370
+ const method = req.method || "GET";
371
+ // CORS
372
+ res.setHeader("Access-Control-Allow-Origin", "*");
373
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
374
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
375
+ if (req.method === "OPTIONS") {
376
+ res.writeHead(204);
377
+ res.end();
378
+ return;
379
+ }
380
+ // [SECURITY] Authentication check for API/MCP routes
381
+ // Skip auth for static UI and health endpoints
382
+ const needsAuth = pathname.startsWith("/api/") || pathname.startsWith("/mcp/");
383
+ const isPublicPath = pathname === "/api/health" || pathname === "/mcp/health" || !pathname.startsWith("/api/") && !pathname.startsWith("/mcp/");
384
+ if (needsAuth && !isPublicPath) {
385
+ const { authenticate } = await import("./utils/auth.js");
386
+ const auth = authenticate(req);
387
+ if (!auth.authenticated) {
388
+ process.stderr.write(`[AUTH] Unauthorized request: ${pathname}\n`);
389
+ serveJson(res, 401, { error: "Unauthorized. Provide Authorization: Bearer <token> header. Set MCP_AUTH_TOKEN env var to configure." });
390
+ return;
391
+ }
392
+ }
393
+ // ───────────── MCP ROUTES (prefix: /mcp) ─────────────
394
+ if (pathname === "/mcp/health") {
395
+ serveJson(res, 200, { status: "ok", version: serverVersion, sessions: sessions.size });
396
+ return;
397
+ }
398
+ if (pathname === "/mcp/sse") {
399
+ const transport = new SSEServerTransport("/mcp/messages", res);
400
+ const sessionId = transport.sessionId;
401
+ sessions.set(sessionId, transport);
402
+ process.stderr.write(`[MCP] Client connected: ${sessionId} (total: ${sessions.size})\n`);
403
+ broadcastWS("mcp_session", { sessionId, action: "connected", total: sessions.size });
404
+ res.on("close", () => {
405
+ sessions.delete(sessionId);
406
+ process.stderr.write(`[MCP] Client disconnected: ${sessionId} (remaining: ${sessions.size})\n`);
407
+ broadcastWS("mcp_session", { sessionId, action: "disconnected", total: sessions.size });
408
+ });
409
+ await server.connect(transport);
410
+ return;
411
+ }
412
+ if (pathname === "/mcp/messages") {
413
+ const sessionId = params.sessionId;
414
+ if (!sessionId || !sessions.has(sessionId)) {
415
+ serveJson(res, 400, { error: "Invalid or missing sessionId" });
416
+ return;
417
+ }
418
+ const transport = sessions.get(sessionId);
419
+ await transport.handlePostMessage(req, res);
420
+ return;
421
+ }
422
+ // ───────────── DASHBOARD API ROUTES (/api) ───────────
423
+ // Health
424
+ if (pathname === "/api/health") {
425
+ serveJson(res, 200, { status: "healthy", version: serverVersion, frameworkDir: FRAMEWORK_DIR });
426
+ return;
427
+ }
428
+ // Status
429
+ if (pathname === "/api/status") {
430
+ const statusPath = path.join(FRAMEWORK_DIR, "memory", "status.json");
431
+ if (fs.existsSync(statusPath)) {
432
+ serveFile(res, statusPath, "application/json");
433
+ return;
434
+ }
435
+ serveJson(res, 200, { success: true, data: { status: "initializing" } });
436
+ return;
437
+ }
438
+ // Memory (PROJECT_MEMORY.md)
439
+ if (pathname === "/api/memory") {
440
+ const memoryPath = path.join(FRAMEWORK_DIR, "memory", "PROJECT_MEMORY.md");
441
+ if (fs.existsSync(memoryPath)) {
442
+ serveFile(res, memoryPath, "text/markdown");
443
+ return;
444
+ }
445
+ serveJson(res, 200, { success: true, data: "No memory file yet." });
446
+ return;
447
+ }
448
+ // Memory Search
449
+ if (pathname === "/api/memory/search") {
450
+ try {
451
+ const query = params.q || "";
452
+ const limit = parseInt(params.limit || "10", 10);
453
+ if (!query) {
454
+ const db = Storage.getDB();
455
+ const rows = db.prepare("SELECT * FROM vector_memory ORDER BY created_at DESC LIMIT ?").all(limit);
456
+ // [KVKK/GDPR] Mask PII in memory content before returning
457
+ const results = rows.map(row => {
458
+ const meta = JSON.parse(row.metadata || "{}");
459
+ return { id: row.id, content: maskText(row.content), type: row.category || meta.category || "memory", timestamp: meta.createdAt || row.created_at, relevance: 1.0 };
460
+ });
461
+ serveJson(res, 200, { success: true, data: results });
462
+ return;
463
+ }
464
+ const { CoreMemory } = await import("../../src/modules/memory/core.js");
465
+ const results = await CoreMemory.recall(query, { limit });
466
+ // [KVKK/GDPR] Mask PII in memory content before returning
467
+ const flattened = results.map(r => ({
468
+ id: r.entry.id, content: maskText(r.entry.content), type: r.entry.metadata.category || "memory",
469
+ timestamp: r.entry.metadata.createdAt || new Date().toISOString(), relevance: r.score
470
+ }));
471
+ serveJson(res, 200, { success: true, data: flattened });
472
+ }
473
+ catch (e) {
474
+ serveJson(res, 500, { success: false, error: e.message });
475
+ }
476
+ return;
477
+ }
478
+ // Agents
479
+ if (pathname === "/api/agents") {
480
+ try {
481
+ const agents = Storage.getAllAgents();
482
+ serveJson(res, 200, { success: true, data: agents });
483
+ }
484
+ catch (e) {
485
+ serveJson(res, 500, { success: false, error: e.message });
486
+ }
487
+ return;
488
+ }
489
+ // Messages (Hermes queue)
490
+ if (pathname === "/api/messages") {
491
+ try {
492
+ const messages = Storage.getPendingMessages();
493
+ serveJson(res, 200, { success: true, data: messages });
494
+ }
495
+ catch (e) {
496
+ serveJson(res, 500, { success: false, error: e.message });
497
+ }
498
+ return;
499
+ }
500
+ // Hermes Stats
501
+ if (pathname === "/api/hermes/stats") {
502
+ try {
503
+ const messages = Storage.getPendingMessages();
504
+ const totalMessages = Storage.getDB().prepare("SELECT COUNT(*) as count FROM messages").get();
505
+ const byCategory = {};
506
+ const byStatus = {};
507
+ messages.forEach(m => {
508
+ byCategory[m.category] = (byCategory[m.category] || 0) + 1;
509
+ byStatus[m.status] = (byStatus[m.status] || 0) + 1;
510
+ });
511
+ serveJson(res, 200, {
512
+ success: true, data: {
513
+ total: totalMessages.count, pending: messages.length, byCategory, byStatus,
514
+ lastMessage: messages.length > 0 ? messages[messages.length - 1] : null,
515
+ }
516
+ });
517
+ }
518
+ catch (e) {
519
+ serveJson(res, 500, { success: false, error: e.message });
520
+ }
521
+ return;
522
+ }
523
+ // Tasks
524
+ if (pathname === "/api/tasks") {
525
+ try {
526
+ const { asTraceID } = await import("../../src/shared/types.js");
527
+ const traceId = params.traceId ? asTraceID(params.traceId) : undefined;
528
+ const tasks = Storage.getTasks(traceId);
529
+ // [KVKK/GDPR] Mask PII in task descriptions before returning
530
+ const maskedTasks = tasks.map(t => ({
531
+ ...t,
532
+ description: maskText(t.description),
533
+ }));
534
+ serveJson(res, 200, { success: true, data: maskedTasks });
535
+ }
536
+ catch (e) {
537
+ serveJson(res, 500, { success: false, error: e.message });
538
+ }
539
+ return;
540
+ }
541
+ // Logs
542
+ if (pathname.startsWith("/api/logs")) {
543
+ try {
544
+ const logs = Storage.getLogs();
545
+ const agent = pathname.split("/api/logs/")[1];
546
+ const filteredLogs = agent ? logs.filter(l => l.agent === agent) : logs;
547
+ serveJson(res, 200, { success: true, data: filteredLogs });
548
+ }
549
+ catch (e) {
550
+ serveJson(res, 500, { success: false, error: e.message });
551
+ }
552
+ return;
553
+ }
554
+ // Approvals
555
+ if (pathname === "/api/approvals") {
556
+ try {
557
+ const allMessages = Storage.getPendingMessages();
558
+ // [KVKK/GDPR] Mask PII in approval descriptions
559
+ const approvals = allMessages.filter(m => m.category === "ALERT" || m.category === "ACTION")
560
+ .map(m => ({
561
+ id: String(m.id || m.traceId), traceId: m.traceId,
562
+ description: m.content ? (typeof m.content === "string" ? maskText(m.content) : maskText(JSON.stringify(m.content))) : "Approval required",
563
+ status: m.status || "PENDING", timestamp: m.timestamp || new Date().toISOString()
564
+ }));
565
+ serveJson(res, 200, { success: true, data: approvals });
566
+ }
567
+ catch (e) {
568
+ serveJson(res, 500, { success: false, error: e.message });
569
+ }
570
+ return;
571
+ }
572
+ // Approve
573
+ if (pathname.startsWith("/api/approve/") && method === "POST") {
574
+ const traceId = pathname.replace("/api/approve/", "");
575
+ if (!traceId) {
576
+ serveJson(res, 400, { success: false, error: "Trace ID required" });
577
+ return;
578
+ }
579
+ try {
580
+ const pendingMessages = Storage.getPendingMessages().filter(m => m.traceId === traceId && (m.category === "ALERT" || m.category === "ACTION"));
581
+ if (pendingMessages.length === 0) {
582
+ serveJson(res, 404, { success: false, error: "No pending approval found" });
583
+ return;
584
+ }
585
+ pendingMessages.forEach(msg => { if (msg.id !== undefined)
586
+ Storage.updateMessageStatus(msg.id, "APPROVED"); });
587
+ broadcastWS("approval", { traceId, status: "APPROVED" });
588
+ serveJson(res, 200, { success: true, message: `Trace ${traceId} approved`, approved: pendingMessages.length });
589
+ }
590
+ catch (e) {
591
+ serveJson(res, 500, { success: false, error: e.message });
592
+ }
593
+ return;
594
+ }
595
+ // Reject
596
+ if (pathname.startsWith("/api/reject/") && method === "POST") {
597
+ const traceId = pathname.replace("/api/reject/", "");
598
+ if (!traceId) {
599
+ serveJson(res, 400, { success: false, error: "Trace ID required" });
600
+ return;
601
+ }
602
+ try {
603
+ const pendingMessages = Storage.getPendingMessages().filter(m => m.traceId === traceId && (m.category === "ALERT" || m.category === "ACTION"));
604
+ if (pendingMessages.length === 0) {
605
+ serveJson(res, 404, { success: false, error: "No pending approval found" });
606
+ return;
607
+ }
608
+ pendingMessages.forEach(msg => { if (msg.id !== undefined)
609
+ Storage.updateMessageStatus(msg.id, "REJECTED"); });
610
+ broadcastWS("approval", { traceId, status: "REJECTED" });
611
+ serveJson(res, 200, { success: true, message: `Trace ${traceId} rejected`, rejected: pendingMessages.length });
612
+ }
613
+ catch (e) {
614
+ serveJson(res, 500, { success: false, error: e.message });
615
+ }
616
+ return;
617
+ }
618
+ // Audit (GDPR/KVKK)
619
+ if (pathname === "/api/audit") {
620
+ try {
621
+ Audit.initialize();
622
+ const stats = Audit.getStats();
623
+ const entries = Audit.query({ limit: 100 });
624
+ serveJson(res, 200, { success: true, data: { stats, entries } });
625
+ }
626
+ catch (e) {
627
+ serveJson(res, 500, { success: false, error: e.message });
628
+ }
629
+ return;
630
+ }
631
+ // Audit Erase (GDPR/KVKK Right to Erasure)
632
+ if (pathname === "/api/audit/erase" && method === "POST") {
633
+ try {
634
+ Audit.initialize();
635
+ let body = "";
636
+ req.on("data", chunk => { body += chunk.toString(); });
637
+ req.on("end", () => {
638
+ try {
639
+ const parsed = JSON.parse(body);
640
+ const confirmation = parsed.confirmationCode || "";
641
+ const changes = Audit.clearAll(confirmation);
642
+ serveJson(res, 200, { success: true, message: `${changes} records cleared under KVKK/GDPR right to erasure.` });
643
+ }
644
+ catch (err) {
645
+ serveJson(res, 400, { success: false, error: err.message });
646
+ }
647
+ });
648
+ }
649
+ catch (e) {
650
+ serveJson(res, 500, { success: false, error: e.message });
651
+ }
652
+ return;
653
+ }
654
+ // Compliance
655
+ if (pathname === "/api/compliance") {
656
+ try {
657
+ const { scanProjectCompliance } = await import("../../src/cli/utils/compliance.js");
658
+ const targetPath = params.path || "src";
659
+ const scanPath = path.join(PROJECT_ROOT, targetPath);
660
+ const rawIssues = scanProjectCompliance(scanPath);
661
+ const violations = rawIssues.map(issue => {
662
+ const ruleLower = issue.rule.toLowerCase();
663
+ const type = ruleLower.includes("any") ? "any-type" : ruleLower.includes("console") ? "console-log" : "other";
664
+ return { file: path.relative(PROJECT_ROOT, issue.file), line: issue.line, type, message: issue.rule.replace(/^\[ERROR\]\s*Corporate\s*Compliance\s*Breach:\s*/i, "") };
665
+ });
666
+ const violatingFiles = new Set(violations.map(v => v.file));
667
+ serveJson(res, 200, {
668
+ success: true, data: {
669
+ summary: { totalFiles: violatingFiles.size, totalViolations: violations.length, byType: { "any-type": violations.filter(v => v.type === "any-type").length, "console-log": violations.filter(v => v.type === "console-log").length } },
670
+ violations
671
+ }
672
+ });
673
+ }
674
+ catch (e) {
675
+ serveJson(res, 500, { success: false, error: e.message });
676
+ }
677
+ return;
678
+ }
679
+ // Quality
680
+ if (pathname === "/api/quality") {
681
+ try {
682
+ const { analyzePathQuality } = await import("../../src/cli/utils/quality.js");
683
+ const targetPath = params.path || "src";
684
+ const result = analyzePathQuality(PROJECT_ROOT, targetPath);
685
+ serveJson(res, 200, {
686
+ success: true, data: {
687
+ totalFiles: result.totalFiles, totalIssues: result.totalIssues,
688
+ longFunctions: result.longFunctions, deepNesting: result.deepNesting,
689
+ anyTypes: result.anyTypes, issues: result.issues.slice(0, 50),
690
+ }
691
+ });
692
+ }
693
+ catch (e) {
694
+ serveJson(res, 500, { success: false, error: e.message });
695
+ }
696
+ return;
697
+ }
698
+ // Adapter Skills
699
+ if (pathname === "/api/adapters/skills") {
700
+ try {
701
+ const { ADAPTER_SKILLS } = await import("../../src/modules/skills/adapter-skills.js");
702
+ const summary = Object.entries(ADAPTER_SKILLS).map(([id, config]) => ({
703
+ id, skillCount: Object.keys(config.enabledSkills).length,
704
+ toolsCount: config.toolMapping ? Object.keys(config.toolMapping).length : 0,
705
+ skills: Object.keys(config.enabledSkills),
706
+ }));
707
+ serveJson(res, 200, { success: true, data: summary });
708
+ }
709
+ catch (e) {
710
+ serveJson(res, 500, { success: false, error: e.message });
711
+ }
712
+ return;
713
+ }
714
+ // MCP Sessions
715
+ if (pathname === "/api/mcp/sessions") {
716
+ const sessionList = Array.from(sessions.keys()).map(id => ({ id, connectedAt: new Date().toISOString() }));
717
+ serveJson(res, 200, { success: true, data: { total: sessions.size, sessions: sessionList } });
718
+ return;
719
+ }
720
+ // Discipline Stats
721
+ if (pathname === "/api/discipline") {
722
+ try {
723
+ const { getAllDisciplineStats } = await import("./utils/discipline.js");
724
+ const stats = getAllDisciplineStats();
725
+ serveJson(res, 200, { success: true, data: stats });
726
+ }
727
+ catch (e) {
728
+ serveJson(res, 500, { success: false, error: e.message });
729
+ }
730
+ return;
731
+ }
732
+ // Token Economy / Metrics
733
+ if (pathname === "/api/metrics") {
734
+ try {
735
+ const metricsPath = path.join(FRAMEWORK_DIR, "observability/metrics.json");
736
+ let entries = [];
737
+ if (fs.existsSync(metricsPath)) {
738
+ entries = JSON.parse(fs.readFileSync(metricsPath, "utf8"));
739
+ }
740
+ const byAgent = {};
741
+ const byAction = {};
742
+ let totalTokens = 0;
743
+ for (const entry of entries) {
744
+ const agent = entry.agent || "unknown";
745
+ const action = entry.action || "unknown";
746
+ const tokens = entry.estimatedTokens || 0;
747
+ if (!byAgent[agent])
748
+ byAgent[agent] = { calls: 0, tokens: 0, cost: 0 };
749
+ byAgent[agent].calls++;
750
+ byAgent[agent].tokens += tokens;
751
+ byAgent[agent].cost = +(byAgent[agent].tokens / 1000 * 0.003).toFixed(4);
752
+ if (!byAction[action])
753
+ byAction[action] = { calls: 0, tokens: 0 };
754
+ byAction[action].calls++;
755
+ byAction[action].tokens += tokens;
756
+ totalTokens += tokens;
757
+ }
758
+ serveJson(res, 200, {
759
+ success: true, data: {
760
+ totalToolCalls: entries.length, totalEstimatedTokens: totalTokens,
761
+ totalEstimatedCost: +(totalTokens / 1000 * 0.003).toFixed(4),
762
+ mcpSessions: sessions.size, byAgent, byAction, recentEntries: entries.slice(-100),
763
+ }
764
+ });
765
+ }
766
+ catch (e) {
767
+ serveJson(res, 500, { success: false, error: e.message });
768
+ }
769
+ return;
770
+ }
771
+ // Telemetry Status
772
+ if (pathname === "/api/telemetry") {
773
+ try {
774
+ const { telemetryStreamer, TelemetryConfig } = await import("./utils/telemetry-streamer.js");
775
+ const status = telemetryStreamer.getStatus();
776
+ serveJson(res, 200, { success: true, data: { ...status, config: { ...TelemetryConfig, AUTH_TOKEN: TelemetryConfig.AUTH_TOKEN ? "***" : "" } } });
777
+ }
778
+ catch (e) {
779
+ serveJson(res, 500, { success: false, error: e.message });
780
+ }
781
+ return;
782
+ }
783
+ // Loop Detection Stats
784
+ if (pathname === "/api/loop-detector") {
785
+ try {
786
+ const { getAllLoopStats } = await import("./utils/loop-detector.js");
787
+ const agent = params.agent;
788
+ const stats = agent
789
+ ? (await import("./utils/loop-detector.js")).getLoopStats(agent)
790
+ : getAllLoopStats();
791
+ serveJson(res, 200, { success: true, data: stats });
792
+ }
793
+ catch (e) {
794
+ serveJson(res, 500, { success: false, error: e.message });
795
+ }
796
+ return;
797
+ }
798
+ // Clear Loop Cooldown
799
+ if (pathname.startsWith("/api/loop-detector/clear/") && method === "POST") {
800
+ const agent = decodeURIComponent(pathname.replace("/api/loop-detector/clear/", ""));
801
+ try {
802
+ const { clearCooldown } = await import("./utils/loop-detector.js");
803
+ const cleared = clearCooldown(agent);
804
+ serveJson(res, 200, { success: true, data: { agent, cleared } });
805
+ }
806
+ catch (e) {
807
+ serveJson(res, 500, { success: false, error: e.message });
808
+ }
809
+ return;
810
+ }
811
+ // Budget / FinOps Status
812
+ if (pathname === "/api/finops") {
813
+ try {
814
+ const { budgetManager } = await import("./utils/finops.js");
815
+ const state = budgetManager.getState();
816
+ serveJson(res, 200, { success: true, data: state });
817
+ }
818
+ catch (e) {
819
+ serveJson(res, 500, { success: false, error: e.message });
820
+ }
821
+ return;
822
+ }
823
+ // Budget Check for an agent
824
+ if (pathname === "/api/finops/check") {
825
+ try {
826
+ const agent = params.agent || "default";
827
+ const { budgetManager } = await import("./utils/finops.js");
828
+ const result = budgetManager.checkBudget(agent);
829
+ serveJson(res, 200, { success: true, data: result });
830
+ }
831
+ catch (e) {
832
+ serveJson(res, 500, { success: false, error: e.message });
833
+ }
834
+ return;
835
+ }
836
+ // Budget Reset
837
+ if (pathname === "/api/finops/reset" && method === "POST") {
838
+ try {
839
+ const { budgetManager } = await import("./utils/finops.js");
840
+ budgetManager.resetPeriod();
841
+ serveJson(res, 200, { success: true, message: "Budget period reset." });
842
+ }
843
+ catch (e) {
844
+ serveJson(res, 500, { success: false, error: e.message });
845
+ }
846
+ return;
847
+ }
848
+ // License Scanner
849
+ if (pathname === "/api/license") {
850
+ try {
851
+ const { scanForLicenses, getLicenseSeveritySummary, LicenseScannerConfig } = await import("./utils/license-scanner.js");
852
+ const filePath = params.path || "";
853
+ const content = params.content || "";
854
+ const matches = filePath && content ? scanForLicenses(filePath, content) : [];
855
+ const summary = matches.length > 0 ? getLicenseSeveritySummary(matches) : null;
856
+ serveJson(res, 200, {
857
+ success: true, data: {
858
+ matches,
859
+ summary,
860
+ config: LicenseScannerConfig,
861
+ }
862
+ });
863
+ }
864
+ catch (e) {
865
+ serveJson(res, 500, { success: false, error: e.message });
866
+ }
867
+ return;
868
+ }
869
+ // Auto-Rollback Stats
870
+ if (pathname === "/api/rollback") {
871
+ try {
872
+ const { AutoRollbackEngine } = await import("./utils/auto-rollback.js");
873
+ const stats = AutoRollbackEngine.getSnapshotStats();
874
+ serveJson(res, 200, { success: true, data: stats });
875
+ }
876
+ catch (e) {
877
+ serveJson(res, 500, { success: false, error: e.message });
878
+ }
879
+ return;
880
+ }
881
+ // All Governance Stats (combined endpoint)
882
+ if (pathname === "/api/governance") {
883
+ try {
884
+ const { getAllDisciplineStats } = await import("./utils/discipline.js");
885
+ const { getAllTokenBudgetStats } = await import("./utils/context-optimizer.js");
886
+ const { getAllLoopStats } = await import("./utils/loop-detector.js");
887
+ const { getAllRules } = await import("./utils/rules-engine.js");
888
+ const { AutoRollbackEngine } = await import("./utils/auto-rollback.js");
889
+ const { budgetManager } = await import("./utils/finops.js");
890
+ serveJson(res, 200, {
891
+ success: true, data: {
892
+ discipline: getAllDisciplineStats(),
893
+ tokenBudget: getAllTokenBudgetStats(),
894
+ loopDetection: getAllLoopStats(),
895
+ rules: getAllRules().map(r => ({ id: r.id, name: r.name, priority: r.priority, bypassable: r.bypassable })),
896
+ rollback: AutoRollbackEngine.getSnapshotStats(),
897
+ budget: budgetManager.getState(),
898
+ telemetry: (await import("./utils/telemetry-streamer.js")).telemetryStreamer.getStatus(),
899
+ }
900
+ });
901
+ }
902
+ catch (e) {
903
+ serveJson(res, 500, { success: false, error: e.message });
904
+ }
905
+ return;
906
+ }
907
+ // SSE (legacy support)
908
+ if (pathname === "/api/events") {
909
+ res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive" });
910
+ res.write(": connected\n\n");
911
+ const keepAlive = setInterval(() => res.write(": keepalive\n\n"), 30000);
912
+ req.on("close", () => clearInterval(keepAlive));
913
+ return;
914
+ }
915
+ // ───────────── STATIC UI ─────────────────────────────
916
+ const uiPath = path.join(UI_DIST_PATH, pathname === "/" ? "index.html" : pathname);
917
+ const finalPath = (fs.existsSync(uiPath) && !fs.statSync(uiPath).isDirectory())
918
+ ? uiPath
919
+ : path.join(UI_DIST_PATH, "index.html");
920
+ if (fs.existsSync(finalPath) && !fs.statSync(finalPath).isDirectory()) {
921
+ const ext = path.extname(finalPath).toLowerCase();
922
+ const mimeTypes = {
923
+ ".html": "text/html", ".js": "application/javascript", ".css": "text/css",
924
+ ".json": "application/json", ".png": "image/png", ".jpg": "image/jpg",
925
+ ".gif": "image/gif", ".svg": "image/svg+xml", ".ico": "image/x-icon",
926
+ };
927
+ serveFile(res, finalPath, mimeTypes[ext] || "application/octet-stream");
928
+ }
929
+ else {
930
+ serveJson(res, 404, { error: "Not Found" });
931
+ }
932
+ });
933
+ // ─── WebSocket Server ────────────────────────────────────
934
+ const wss = new WebSocketServer({ server: httpServer, path: "/ws" });
935
+ wss.on("connection", (ws) => {
936
+ wsClients.add(ws);
937
+ ws.send(JSON.stringify({ type: "mcp_session", payload: { total: sessions.size, sessions: Array.from(sessions.keys()) } }));
938
+ ws.on("close", () => wsClients.delete(ws));
939
+ ws.on("message", (data) => {
940
+ try {
941
+ const msg = JSON.parse(data.toString());
942
+ if (msg.type === "ping")
943
+ ws.send(JSON.stringify({ type: "pong" }));
944
+ }
945
+ catch { /* ignore */ }
946
+ });
947
+ });
948
+ // ─── Periodic Broadcast ──────────────────────────────────
949
+ const broadcastInterval = setInterval(() => {
950
+ try {
951
+ broadcastWS("mcp_heartbeat", { sessions: sessions.size, timestamp: new Date().toISOString() });
952
+ }
953
+ catch { /* silent */ }
954
+ }, 10000);
955
+ httpServer.on("close", () => {
956
+ clearInterval(broadcastInterval);
957
+ wsClients.clear();
958
+ });
959
+ // ─── Start ───────────────────────────────────────────────
960
+ httpServer.listen(PORT, HOST, () => {
961
+ process.stderr.write("\n");
962
+ process.stderr.write("╔══════════════════════════════════════════════════════╗\n");
963
+ process.stderr.write("║ ATABEY UNIFIED SERVER ║\n");
964
+ process.stderr.write("╠══════════════════════════════════════════════════════╣\n");
965
+ process.stderr.write(`║ Dashboard: http://localhost:${PORT} \n`);
966
+ process.stderr.write(`║ MCP SSE: http://localhost:${PORT}/mcp/sse \n`);
967
+ process.stderr.write(`║ WS Live: ws://localhost:${PORT}/ws \n`);
968
+ process.stderr.write(`║ API: http://localhost:${PORT}/api/health \n`);
969
+ process.stderr.write(`║ Sessions: ${sessions.size} active \n`);
970
+ process.stderr.write("╚══════════════════════════════════════════════════════╝\n");
971
+ process.stderr.write("\n");
972
+ process.stderr.write(`[atabey-mcp] Unified server ready on port ${PORT}\n`);
973
+ process.stderr.write(`[atabey-mcp] mcp.json: { "url": "http://localhost:${PORT}/mcp/sse" }\n`);
974
+ process.stderr.write("\n");
975
+ });
976
+ }
977
+ // ─── Startup ──────────────────────────────────────────────────────
126
978
  async function run() {
127
- const transport = new StdioServerTransport();
128
- // Prevent unhandled errors from crashing the MCP stream
979
+ const transportMode = process.env.MCP_TRANSPORT || "unified";
129
980
  process.on("uncaughtException", (error) => {
130
- process.stderr.write(`[atabey-mcp] Uncaught exception: ${error.message}
131
- `);
981
+ process.stderr.write(`[atabey-mcp] Uncaught exception: ${error.message}\n`);
132
982
  });
133
983
  process.on("unhandledRejection", (reason) => {
134
984
  const message = reason instanceof Error ? reason.message : String(reason);
135
- process.stderr.write(`[atabey-mcp] Unhandled rejection: ${message}
136
- `);
985
+ process.stderr.write(`[atabey-mcp] Unhandled rejection: ${message}\n`);
137
986
  });
138
- // Graceful shutdown on SIGINT/SIGTERM
139
987
  const shutdown = async () => {
140
988
  try {
141
989
  await server.close();
142
990
  }
143
- catch {
144
- // Already closed or failed — safe to ignore
145
- }
991
+ catch { /* ignore */ }
146
992
  process.exit(0);
147
993
  };
148
994
  process.on("SIGINT", shutdown);
149
995
  process.on("SIGTERM", shutdown);
150
- await server.connect(transport);
996
+ if (transportMode === "stdio") {
997
+ const transport = new StdioServerTransport();
998
+ process.stderr.write("[atabey-mcp] Stdio mode (single client).\n");
999
+ await server.connect(transport);
1000
+ }
1001
+ else {
1002
+ createUnifiedServer();
1003
+ }
151
1004
  }
152
1005
  run().catch((error) => {
153
- process.stderr.write(`[atabey-mcp] Fatal startup error: ${error.message}
154
- `);
1006
+ process.stderr.write(`[atabey-mcp] Fatal startup error: ${error.message}\n`);
155
1007
  process.exit(1);
156
1008
  });