palaryn 0.1.0 → 0.3.2

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 (344) hide show
  1. package/README.md +243 -588
  2. package/dist/sdk/typescript/src/client.js +2 -2
  3. package/dist/sdk/typescript/src/client.js.map +1 -1
  4. package/dist/src/anomaly/detector.d.ts +7 -4
  5. package/dist/src/anomaly/detector.d.ts.map +1 -1
  6. package/dist/src/anomaly/detector.js +22 -12
  7. package/dist/src/anomaly/detector.js.map +1 -1
  8. package/dist/src/audit/logger.d.ts +10 -0
  9. package/dist/src/audit/logger.d.ts.map +1 -1
  10. package/dist/src/audit/logger.js +52 -38
  11. package/dist/src/audit/logger.js.map +1 -1
  12. package/dist/src/auth/routes.d.ts.map +1 -1
  13. package/dist/src/auth/routes.js +35 -0
  14. package/dist/src/auth/routes.js.map +1 -1
  15. package/dist/src/budget/manager.d.ts +5 -0
  16. package/dist/src/budget/manager.d.ts.map +1 -1
  17. package/dist/src/budget/manager.js +32 -0
  18. package/dist/src/budget/manager.js.map +1 -1
  19. package/dist/src/budget/model-pricing.d.ts +20 -0
  20. package/dist/src/budget/model-pricing.d.ts.map +1 -0
  21. package/dist/src/budget/model-pricing.js +107 -0
  22. package/dist/src/budget/model-pricing.js.map +1 -0
  23. package/dist/src/budget/usage-extractor.d.ts +3 -1
  24. package/dist/src/budget/usage-extractor.d.ts.map +1 -1
  25. package/dist/src/budget/usage-extractor.js +47 -3
  26. package/dist/src/budget/usage-extractor.js.map +1 -1
  27. package/dist/src/config/defaults.d.ts.map +1 -1
  28. package/dist/src/config/defaults.js +65 -13
  29. package/dist/src/config/defaults.js.map +1 -1
  30. package/dist/src/dlp/tool-patterns.d.ts +7 -0
  31. package/dist/src/dlp/tool-patterns.d.ts.map +1 -0
  32. package/dist/src/dlp/tool-patterns.js +34 -0
  33. package/dist/src/dlp/tool-patterns.js.map +1 -0
  34. package/dist/src/executor/filesystem-executor.d.ts +28 -0
  35. package/dist/src/executor/filesystem-executor.d.ts.map +1 -0
  36. package/dist/src/executor/filesystem-executor.js +192 -0
  37. package/dist/src/executor/filesystem-executor.js.map +1 -0
  38. package/dist/src/executor/http-executor.d.ts.map +1 -1
  39. package/dist/src/executor/http-executor.js +22 -2
  40. package/dist/src/executor/http-executor.js.map +1 -1
  41. package/dist/src/executor/index.d.ts +4 -0
  42. package/dist/src/executor/index.d.ts.map +1 -1
  43. package/dist/src/executor/index.js +9 -1
  44. package/dist/src/executor/index.js.map +1 -1
  45. package/dist/src/executor/shell-executor.d.ts +22 -0
  46. package/dist/src/executor/shell-executor.d.ts.map +1 -0
  47. package/dist/src/executor/shell-executor.js +119 -0
  48. package/dist/src/executor/shell-executor.js.map +1 -0
  49. package/dist/src/executor/sql-executor.d.ts +29 -0
  50. package/dist/src/executor/sql-executor.d.ts.map +1 -0
  51. package/dist/src/executor/sql-executor.js +114 -0
  52. package/dist/src/executor/sql-executor.js.map +1 -0
  53. package/dist/src/executor/websocket-executor.d.ts +26 -0
  54. package/dist/src/executor/websocket-executor.d.ts.map +1 -0
  55. package/dist/src/executor/websocket-executor.js +205 -0
  56. package/dist/src/executor/websocket-executor.js.map +1 -0
  57. package/dist/src/interceptor/index.d.ts +2 -0
  58. package/dist/src/interceptor/index.d.ts.map +1 -0
  59. package/dist/src/interceptor/index.js +6 -0
  60. package/dist/src/interceptor/index.js.map +1 -0
  61. package/dist/src/interceptor/provider-interceptor.d.ts +36 -0
  62. package/dist/src/interceptor/provider-interceptor.d.ts.map +1 -0
  63. package/dist/src/interceptor/provider-interceptor.js +302 -0
  64. package/dist/src/interceptor/provider-interceptor.js.map +1 -0
  65. package/dist/src/mcp/auth-verifier.d.ts.map +1 -1
  66. package/dist/src/mcp/auth-verifier.js +3 -2
  67. package/dist/src/mcp/auth-verifier.js.map +1 -1
  68. package/dist/src/mcp/bridge.d.ts +14 -10
  69. package/dist/src/mcp/bridge.d.ts.map +1 -1
  70. package/dist/src/mcp/bridge.js +51 -227
  71. package/dist/src/mcp/bridge.js.map +1 -1
  72. package/dist/src/mcp/http-transport.d.ts +2 -0
  73. package/dist/src/mcp/http-transport.d.ts.map +1 -1
  74. package/dist/src/mcp/http-transport.js +117 -66
  75. package/dist/src/mcp/http-transport.js.map +1 -1
  76. package/dist/src/mcp/internal-auth.d.ts +13 -0
  77. package/dist/src/mcp/internal-auth.d.ts.map +1 -0
  78. package/dist/src/mcp/internal-auth.js +12 -0
  79. package/dist/src/mcp/internal-auth.js.map +1 -0
  80. package/dist/src/mcp/tool-definitions.d.ts +41 -0
  81. package/dist/src/mcp/tool-definitions.d.ts.map +1 -0
  82. package/dist/src/mcp/tool-definitions.js +491 -0
  83. package/dist/src/mcp/tool-definitions.js.map +1 -0
  84. package/dist/src/middleware/auth.js.map +1 -1
  85. package/dist/src/middleware/session.js.map +1 -1
  86. package/dist/src/middleware/validate.d.ts +8 -0
  87. package/dist/src/middleware/validate.d.ts.map +1 -1
  88. package/dist/src/middleware/validate.js +45 -0
  89. package/dist/src/middleware/validate.js.map +1 -1
  90. package/dist/src/policy/engine.d.ts +4 -0
  91. package/dist/src/policy/engine.d.ts.map +1 -1
  92. package/dist/src/policy/engine.js +117 -0
  93. package/dist/src/policy/engine.js.map +1 -1
  94. package/dist/src/saas/routes.d.ts.map +1 -1
  95. package/dist/src/saas/routes.js +355 -22
  96. package/dist/src/saas/routes.js.map +1 -1
  97. package/dist/src/server/app.d.ts.map +1 -1
  98. package/dist/src/server/app.js +24 -3
  99. package/dist/src/server/app.js.map +1 -1
  100. package/dist/src/server/gateway.d.ts.map +1 -1
  101. package/dist/src/server/gateway.js +17 -0
  102. package/dist/src/server/gateway.js.map +1 -1
  103. package/dist/src/server/index.d.ts.map +1 -1
  104. package/dist/src/server/index.js +18 -0
  105. package/dist/src/server/index.js.map +1 -1
  106. package/dist/src/storage/interfaces.d.ts +14 -3
  107. package/dist/src/storage/interfaces.d.ts.map +1 -1
  108. package/dist/src/storage/memory.d.ts +2 -0
  109. package/dist/src/storage/memory.d.ts.map +1 -1
  110. package/dist/src/storage/memory.js +6 -0
  111. package/dist/src/storage/memory.js.map +1 -1
  112. package/dist/src/storage/postgres.d.ts +5 -0
  113. package/dist/src/storage/postgres.d.ts.map +1 -1
  114. package/dist/src/storage/postgres.js +16 -0
  115. package/dist/src/storage/postgres.js.map +1 -1
  116. package/dist/src/storage/redis.d.ts +10 -0
  117. package/dist/src/storage/redis.d.ts.map +1 -1
  118. package/dist/src/storage/redis.js +65 -0
  119. package/dist/src/storage/redis.js.map +1 -1
  120. package/dist/src/types/budget.d.ts +4 -0
  121. package/dist/src/types/budget.d.ts.map +1 -1
  122. package/dist/src/types/config.d.ts +58 -0
  123. package/dist/src/types/config.d.ts.map +1 -1
  124. package/dist/src/types/events.d.ts +1 -0
  125. package/dist/src/types/events.d.ts.map +1 -1
  126. package/dist/src/types/policy.d.ts +11 -1
  127. package/dist/src/types/policy.d.ts.map +1 -1
  128. package/dist/src/types/tool-result.d.ts +11 -0
  129. package/dist/src/types/tool-result.d.ts.map +1 -1
  130. package/dist/tests/unit/app-routes.test.d.ts +2 -0
  131. package/dist/tests/unit/app-routes.test.d.ts.map +1 -0
  132. package/dist/tests/unit/app-routes.test.js +715 -0
  133. package/dist/tests/unit/app-routes.test.js.map +1 -0
  134. package/dist/tests/unit/audit-logger.test.js +105 -0
  135. package/dist/tests/unit/audit-logger.test.js.map +1 -1
  136. package/dist/tests/unit/auth-providers.test.d.ts +2 -0
  137. package/dist/tests/unit/auth-providers.test.d.ts.map +1 -0
  138. package/dist/tests/unit/auth-providers.test.js +279 -0
  139. package/dist/tests/unit/auth-providers.test.js.map +1 -0
  140. package/dist/tests/unit/auth-routes-extended.test.d.ts +2 -0
  141. package/dist/tests/unit/auth-routes-extended.test.d.ts.map +1 -0
  142. package/dist/tests/unit/auth-routes-extended.test.js +993 -0
  143. package/dist/tests/unit/auth-routes-extended.test.js.map +1 -0
  144. package/dist/tests/unit/auth-verifier.test.d.ts +2 -0
  145. package/dist/tests/unit/auth-verifier.test.d.ts.map +1 -0
  146. package/dist/tests/unit/auth-verifier.test.js +505 -0
  147. package/dist/tests/unit/auth-verifier.test.js.map +1 -0
  148. package/dist/tests/unit/billing-routes.test.d.ts +2 -0
  149. package/dist/tests/unit/billing-routes.test.d.ts.map +1 -0
  150. package/dist/tests/unit/billing-routes.test.js +432 -0
  151. package/dist/tests/unit/billing-routes.test.js.map +1 -0
  152. package/dist/tests/unit/config-defaults.test.d.ts +2 -0
  153. package/dist/tests/unit/config-defaults.test.d.ts.map +1 -0
  154. package/dist/tests/unit/config-defaults.test.js +119 -0
  155. package/dist/tests/unit/config-defaults.test.js.map +1 -0
  156. package/dist/tests/unit/defaults.test.js +0 -10
  157. package/dist/tests/unit/defaults.test.js.map +1 -1
  158. package/dist/tests/unit/filesystem-executor.test.d.ts +2 -0
  159. package/dist/tests/unit/filesystem-executor.test.d.ts.map +1 -0
  160. package/dist/tests/unit/filesystem-executor.test.js +280 -0
  161. package/dist/tests/unit/filesystem-executor.test.js.map +1 -0
  162. package/dist/tests/unit/gateway-branches.test.d.ts +2 -0
  163. package/dist/tests/unit/gateway-branches.test.d.ts.map +1 -0
  164. package/dist/tests/unit/gateway-branches.test.js +1039 -0
  165. package/dist/tests/unit/gateway-branches.test.js.map +1 -0
  166. package/dist/tests/unit/http-executor-branches.test.d.ts +2 -0
  167. package/dist/tests/unit/http-executor-branches.test.d.ts.map +1 -0
  168. package/dist/tests/unit/http-executor-branches.test.js +495 -0
  169. package/dist/tests/unit/http-executor-branches.test.js.map +1 -0
  170. package/dist/tests/unit/logger.test.d.ts +2 -0
  171. package/dist/tests/unit/logger.test.d.ts.map +1 -0
  172. package/dist/tests/unit/logger.test.js +97 -0
  173. package/dist/tests/unit/logger.test.js.map +1 -0
  174. package/dist/tests/unit/mcp-internal-auth.test.d.ts +2 -0
  175. package/dist/tests/unit/mcp-internal-auth.test.d.ts.map +1 -0
  176. package/dist/tests/unit/mcp-internal-auth.test.js +445 -0
  177. package/dist/tests/unit/mcp-internal-auth.test.js.map +1 -0
  178. package/dist/tests/unit/metrics.test.js +102 -0
  179. package/dist/tests/unit/metrics.test.js.map +1 -1
  180. package/dist/tests/unit/model-pricing.test.d.ts +2 -0
  181. package/dist/tests/unit/model-pricing.test.d.ts.map +1 -0
  182. package/dist/tests/unit/model-pricing.test.js +87 -0
  183. package/dist/tests/unit/model-pricing.test.js.map +1 -0
  184. package/dist/tests/unit/oauth-stores.test.d.ts +2 -0
  185. package/dist/tests/unit/oauth-stores.test.d.ts.map +1 -0
  186. package/dist/tests/unit/oauth-stores.test.js +260 -0
  187. package/dist/tests/unit/oauth-stores.test.js.map +1 -0
  188. package/dist/tests/unit/policy-engine.test.js +466 -0
  189. package/dist/tests/unit/policy-engine.test.js.map +1 -1
  190. package/dist/tests/unit/provider-interceptor.test.d.ts +2 -0
  191. package/dist/tests/unit/provider-interceptor.test.d.ts.map +1 -0
  192. package/dist/tests/unit/provider-interceptor.test.js +472 -0
  193. package/dist/tests/unit/provider-interceptor.test.js.map +1 -0
  194. package/dist/tests/unit/saas-routes-branches.test.d.ts +2 -0
  195. package/dist/tests/unit/saas-routes-branches.test.d.ts.map +1 -0
  196. package/dist/tests/unit/saas-routes-branches.test.js +2165 -0
  197. package/dist/tests/unit/saas-routes-branches.test.js.map +1 -0
  198. package/dist/tests/unit/saas-routes-crud.test.d.ts +2 -0
  199. package/dist/tests/unit/saas-routes-crud.test.d.ts.map +1 -0
  200. package/dist/tests/unit/saas-routes-crud.test.js +332 -0
  201. package/dist/tests/unit/saas-routes-crud.test.js.map +1 -0
  202. package/dist/tests/unit/saas-routes-data.test.d.ts +2 -0
  203. package/dist/tests/unit/saas-routes-data.test.d.ts.map +1 -0
  204. package/dist/tests/unit/saas-routes-data.test.js +405 -0
  205. package/dist/tests/unit/saas-routes-data.test.js.map +1 -0
  206. package/dist/tests/unit/saas-routes.test.js +3 -3
  207. package/dist/tests/unit/saas-routes.test.js.map +1 -1
  208. package/dist/tests/unit/shell-executor.test.d.ts +2 -0
  209. package/dist/tests/unit/shell-executor.test.d.ts.map +1 -0
  210. package/dist/tests/unit/shell-executor.test.js +145 -0
  211. package/dist/tests/unit/shell-executor.test.js.map +1 -0
  212. package/dist/tests/unit/sql-executor.test.d.ts +2 -0
  213. package/dist/tests/unit/sql-executor.test.d.ts.map +1 -0
  214. package/dist/tests/unit/sql-executor.test.js +177 -0
  215. package/dist/tests/unit/sql-executor.test.js.map +1 -0
  216. package/dist/tests/unit/stream-proxy.test.d.ts +2 -0
  217. package/dist/tests/unit/stream-proxy.test.d.ts.map +1 -0
  218. package/dist/tests/unit/stream-proxy.test.js +147 -0
  219. package/dist/tests/unit/stream-proxy.test.js.map +1 -0
  220. package/dist/tests/unit/tool-definitions.test.d.ts +2 -0
  221. package/dist/tests/unit/tool-definitions.test.d.ts.map +1 -0
  222. package/dist/tests/unit/tool-definitions.test.js +184 -0
  223. package/dist/tests/unit/tool-definitions.test.js.map +1 -0
  224. package/dist/tests/unit/usage-extractor.test.js +140 -0
  225. package/dist/tests/unit/usage-extractor.test.js.map +1 -1
  226. package/dist/tests/unit/webhook-handler.test.d.ts +2 -0
  227. package/dist/tests/unit/webhook-handler.test.d.ts.map +1 -0
  228. package/dist/tests/unit/webhook-handler.test.js +453 -0
  229. package/dist/tests/unit/webhook-handler.test.js.map +1 -0
  230. package/dist/tests/unit/webhook-routes.test.d.ts +2 -0
  231. package/dist/tests/unit/webhook-routes.test.d.ts.map +1 -0
  232. package/dist/tests/unit/webhook-routes.test.js +69 -0
  233. package/dist/tests/unit/webhook-routes.test.js.map +1 -0
  234. package/dist/tests/unit/websocket-executor.test.d.ts +2 -0
  235. package/dist/tests/unit/websocket-executor.test.d.ts.map +1 -0
  236. package/dist/tests/unit/websocket-executor.test.js +121 -0
  237. package/dist/tests/unit/websocket-executor.test.js.map +1 -0
  238. package/package.json +8 -2
  239. package/policy-packs/demo_fail.yaml +41 -0
  240. package/policy-packs/full_tools.yaml +136 -0
  241. package/src/admin/index.ts +1 -0
  242. package/src/admin/routes.ts +509 -0
  243. package/src/admin/templates.ts +572 -0
  244. package/src/anomaly/detector.ts +730 -0
  245. package/src/anomaly/index.ts +1 -0
  246. package/src/approval/manager.ts +569 -0
  247. package/src/approval/webhook.ts +133 -0
  248. package/src/audit/logger.ts +490 -0
  249. package/src/auth/index.ts +5 -0
  250. package/src/auth/password.ts +21 -0
  251. package/src/auth/pkce.ts +22 -0
  252. package/src/auth/providers.ts +208 -0
  253. package/src/auth/routes.ts +561 -0
  254. package/src/auth/session.ts +84 -0
  255. package/src/billing/index.ts +6 -0
  256. package/src/billing/plan-enforcer.ts +135 -0
  257. package/src/billing/routes.ts +229 -0
  258. package/src/billing/stripe-client.ts +58 -0
  259. package/src/billing/webhook-handler.ts +182 -0
  260. package/src/billing/webhook-routes.ts +28 -0
  261. package/src/budget/manager.ts +679 -0
  262. package/src/budget/model-pricing.ts +119 -0
  263. package/src/budget/usage-extractor.ts +214 -0
  264. package/src/cli.ts +91 -0
  265. package/src/config/defaults.ts +261 -0
  266. package/src/config/validate.ts +88 -0
  267. package/src/dlp/composite-scanner.ts +213 -0
  268. package/src/dlp/index.ts +9 -0
  269. package/src/dlp/interfaces.ts +34 -0
  270. package/src/dlp/patterns.ts +30 -0
  271. package/src/dlp/prompt-injection-backend.ts +181 -0
  272. package/src/dlp/prompt-injection-patterns.ts +302 -0
  273. package/src/dlp/regex-backend.ts +181 -0
  274. package/src/dlp/scanner.ts +502 -0
  275. package/src/dlp/text-normalizer.ts +225 -0
  276. package/src/dlp/tool-patterns.ts +35 -0
  277. package/src/dlp/trufflehog-backend.ts +190 -0
  278. package/src/executor/filesystem-executor.ts +196 -0
  279. package/src/executor/http-executor.ts +349 -0
  280. package/src/executor/index.ts +9 -0
  281. package/src/executor/interfaces.ts +11 -0
  282. package/src/executor/noop-executor.ts +23 -0
  283. package/src/executor/registry.ts +64 -0
  284. package/src/executor/shell-executor.ts +148 -0
  285. package/src/executor/slack-executor.ts +176 -0
  286. package/src/executor/sql-executor.ts +146 -0
  287. package/src/executor/websocket-executor.ts +211 -0
  288. package/src/index.ts +24 -0
  289. package/src/interceptor/index.ts +1 -0
  290. package/src/interceptor/provider-interceptor.ts +315 -0
  291. package/src/mcp/auth-verifier.ts +152 -0
  292. package/src/mcp/bridge.ts +703 -0
  293. package/src/mcp/http-transport.ts +698 -0
  294. package/src/mcp/index.ts +9 -0
  295. package/src/mcp/internal-auth.ts +14 -0
  296. package/src/mcp/oauth-pages.ts +139 -0
  297. package/src/mcp/oauth-postgres-stores.ts +278 -0
  298. package/src/mcp/oauth-provider.ts +536 -0
  299. package/src/mcp/oauth-stores.ts +202 -0
  300. package/src/mcp/server.ts +55 -0
  301. package/src/mcp/tool-definitions.ts +562 -0
  302. package/src/metrics/collector.ts +357 -0
  303. package/src/metrics/index.ts +1 -0
  304. package/src/middleware/auth.ts +814 -0
  305. package/src/middleware/session.ts +85 -0
  306. package/src/middleware/validate.ts +130 -0
  307. package/src/policy/engine.ts +815 -0
  308. package/src/policy/index.ts +2 -0
  309. package/src/policy/opa-engine.ts +829 -0
  310. package/src/proxy/forward-proxy.ts +649 -0
  311. package/src/proxy/index.ts +1 -0
  312. package/src/ratelimit/limiter.ts +196 -0
  313. package/src/replay/engine.ts +142 -0
  314. package/src/replay/index.ts +1 -0
  315. package/src/saas/index.ts +1 -0
  316. package/src/saas/routes.ts +2178 -0
  317. package/src/server/app.ts +985 -0
  318. package/src/server/errors.ts +49 -0
  319. package/src/server/gateway.ts +1130 -0
  320. package/src/server/index.ts +307 -0
  321. package/src/server/logger.ts +255 -0
  322. package/src/server/stream-proxy.ts +202 -0
  323. package/src/storage/file-persistence.ts +315 -0
  324. package/src/storage/index.ts +4 -0
  325. package/src/storage/interfaces.ts +287 -0
  326. package/src/storage/memory.ts +686 -0
  327. package/src/storage/postgres.ts +1831 -0
  328. package/src/storage/redis.ts +835 -0
  329. package/src/tracing/index.ts +1 -0
  330. package/src/tracing/provider.ts +100 -0
  331. package/src/trust/calculator.ts +141 -0
  332. package/src/trust/index.ts +7 -0
  333. package/src/types/budget.ts +36 -0
  334. package/src/types/config.ts +278 -0
  335. package/src/types/events.ts +41 -0
  336. package/src/types/express.d.ts +14 -0
  337. package/src/types/index.ts +7 -0
  338. package/src/types/policy.ts +83 -0
  339. package/src/types/stripe-config.ts +11 -0
  340. package/src/types/subscription.ts +59 -0
  341. package/src/types/tool-call.ts +47 -0
  342. package/src/types/tool-result.ts +82 -0
  343. package/src/types/user.ts +125 -0
  344. package/tsconfig.json +24 -0
@@ -0,0 +1,703 @@
1
+ import { Readable, Writable } from 'stream';
2
+ import { randomUUID } from 'crypto';
3
+ import { Gateway } from '../server/gateway';
4
+ import { ToolCall, ToolCallArgs, Actor, Source, ToolInfo } from '../types/tool-call';
5
+ import { ToolResult } from '../types/tool-result';
6
+ import { GatewayConfig } from '../types/config';
7
+ import { DEFAULT_CONFIG } from '../config/defaults';
8
+ import {
9
+ MCPToolHandler,
10
+ MCPToolDefinition,
11
+ HTTP_TOOLS,
12
+ FILE_TOOLS,
13
+ SQL_TOOLS,
14
+ SHELL_TOOLS,
15
+ validateUrlArg,
16
+ } from './tool-definitions';
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // JSON-RPC 2.0 types for the MCP protocol
20
+ // ---------------------------------------------------------------------------
21
+
22
+ interface JSONRPCRequest {
23
+ jsonrpc: '2.0';
24
+ id: string | number;
25
+ method: string;
26
+ params?: Record<string, unknown>;
27
+ }
28
+
29
+ interface JSONRPCNotification {
30
+ jsonrpc: '2.0';
31
+ method: string;
32
+ params?: Record<string, unknown>;
33
+ }
34
+
35
+ interface JSONRPCResponse {
36
+ jsonrpc: '2.0';
37
+ id: string | number | null;
38
+ result?: unknown;
39
+ error?: {
40
+ code: number;
41
+ message: string;
42
+ data?: unknown;
43
+ };
44
+ }
45
+
46
+ type JSONRPCMessage = JSONRPCRequest | JSONRPCNotification;
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // MCP protocol types (subset needed for tools)
50
+ // ---------------------------------------------------------------------------
51
+
52
+ /** MCP text content block */
53
+ interface MCPTextContent {
54
+ type: 'text';
55
+ text: string;
56
+ }
57
+
58
+ /** MCP tool call result */
59
+ interface MCPCallToolResult {
60
+ content: MCPTextContent[];
61
+ isError?: boolean;
62
+ }
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // MCP Bridge configuration
66
+ // ---------------------------------------------------------------------------
67
+
68
+ /**
69
+ * Configuration for the MCP bridge defaults.
70
+ * These values are used when MCP tool calls do not supply them explicitly.
71
+ */
72
+ export interface MCPBridgeConfig {
73
+ /** Default workspace ID for tool calls */
74
+ workspace_id?: string;
75
+ /** Default actor for tool calls */
76
+ actor?: Actor;
77
+ /** Default source platform identifier */
78
+ source?: Source;
79
+ /** Default task ID (if not provided, a new UUID is generated per call) */
80
+ task_id?: string;
81
+ }
82
+
83
+ const DEFAULT_BRIDGE_CONFIG: Required<MCPBridgeConfig> = {
84
+ workspace_id: 'ws_mcp_default',
85
+ actor: { type: 'agent', id: 'mcp-agent', display: 'MCP Agent' },
86
+ source: { platform: 'mcp' },
87
+ task_id: '',
88
+ };
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // MCP protocol constants
92
+ // ---------------------------------------------------------------------------
93
+
94
+ const LATEST_PROTOCOL_VERSION = '2025-03-26';
95
+ const JSONRPC_VERSION = '2.0';
96
+
97
+ const SERVER_INFO = {
98
+ name: 'palaryn-mcp-bridge',
99
+ version: '1.0.0',
100
+ };
101
+
102
+ // JSON-RPC error codes
103
+ const PARSE_ERROR = -32700;
104
+ const INVALID_REQUEST = -32600;
105
+ const METHOD_NOT_FOUND = -32601;
106
+ const INVALID_PARAMS = -32602;
107
+ const INTERNAL_ERROR = -32603;
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // StdioTransport - line-delimited JSON-RPC over stdin/stdout
111
+ // ---------------------------------------------------------------------------
112
+
113
+ /**
114
+ * Reads line-delimited JSON-RPC messages from a readable stream
115
+ * and writes JSON-RPC responses to a writable stream.
116
+ * Follows the MCP stdio transport specification.
117
+ */
118
+ class StdioTransport {
119
+ static readonly MAX_BUFFER_SIZE = 10 * 1024 * 1024; // 10MB
120
+ static readonly BUFFER_INACTIVITY_TIMEOUT_MS = 30_000; // 30s
121
+
122
+ private input: Readable;
123
+ private output: Writable;
124
+ private buffer: string = '';
125
+ private onMessage: ((msg: JSONRPCMessage) => void) | null = null;
126
+ private bufferTimeoutHandle: ReturnType<typeof setTimeout> | null = null;
127
+
128
+ constructor(input?: Readable, output?: Writable) {
129
+ this.input = input || process.stdin;
130
+ this.output = output || process.stdout;
131
+ }
132
+
133
+ /** Start listening for messages on the input stream. */
134
+ start(handler: (msg: JSONRPCMessage) => void): void {
135
+ this.onMessage = handler;
136
+ this.input.setEncoding('utf8');
137
+ this.input.on('data', (chunk: string) => this.onData(chunk));
138
+ }
139
+
140
+ /** Send a JSON-RPC response to the output stream. */
141
+ send(response: JSONRPCResponse): void {
142
+ const json = JSON.stringify(response);
143
+ this.output.write(json + '\n');
144
+ }
145
+
146
+ /** Stop listening and clean up. */
147
+ close(): void {
148
+ this.input.removeAllListeners('data');
149
+ this.onMessage = null;
150
+ this.clearBufferTimeout();
151
+ }
152
+
153
+ private clearBufferTimeout(): void {
154
+ if (this.bufferTimeoutHandle) {
155
+ clearTimeout(this.bufferTimeoutHandle);
156
+ this.bufferTimeoutHandle = null;
157
+ }
158
+ }
159
+
160
+ /** Reset the inactivity timer for partial buffer data. */
161
+ private resetBufferTimeout(): void {
162
+ this.clearBufferTimeout();
163
+ if (this.buffer.length > 0) {
164
+ this.bufferTimeoutHandle = setTimeout(() => {
165
+ if (this.buffer.length > 0) {
166
+ console.error(`[mcp-bridge] Buffer inactivity timeout (${StdioTransport.BUFFER_INACTIVITY_TIMEOUT_MS}ms) with ${this.buffer.length} bytes pending, clearing`);
167
+ this.buffer = '';
168
+ const errorResponse: JSONRPCResponse = {
169
+ jsonrpc: JSONRPC_VERSION,
170
+ id: null,
171
+ error: { code: PARSE_ERROR, message: 'Buffer inactivity timeout: incomplete message cleared' },
172
+ };
173
+ this.send(errorResponse);
174
+ }
175
+ }, StdioTransport.BUFFER_INACTIVITY_TIMEOUT_MS);
176
+ }
177
+ }
178
+
179
+ private onData(chunk: string): void {
180
+ this.buffer += chunk;
181
+
182
+ // Prevent unbounded buffer growth
183
+ if (this.buffer.length > StdioTransport.MAX_BUFFER_SIZE) {
184
+ console.error('[mcp-bridge] Buffer exceeded maximum size (10MB), clearing');
185
+ this.buffer = '';
186
+ this.clearBufferTimeout();
187
+ const errorResponse: JSONRPCResponse = {
188
+ jsonrpc: JSONRPC_VERSION,
189
+ id: null,
190
+ error: { code: PARSE_ERROR, message: 'Message too large: exceeded 10MB buffer limit' },
191
+ };
192
+ this.send(errorResponse);
193
+ return;
194
+ }
195
+
196
+ // Process complete lines
197
+ const lines = this.buffer.split('\n');
198
+ // Keep the last (possibly incomplete) line in the buffer
199
+ this.buffer = lines.pop() || '';
200
+
201
+ for (const line of lines) {
202
+ const trimmed = line.trim();
203
+ if (!trimmed) continue;
204
+
205
+ try {
206
+ const parsed = JSON.parse(trimmed);
207
+ if (this.onMessage) {
208
+ this.onMessage(parsed as JSONRPCMessage);
209
+ }
210
+ } catch {
211
+ // Send parse error for invalid JSON
212
+ const errorResponse: JSONRPCResponse = {
213
+ jsonrpc: JSONRPC_VERSION,
214
+ id: null,
215
+ error: { code: PARSE_ERROR, message: 'Parse error: invalid JSON' },
216
+ };
217
+ this.send(errorResponse);
218
+ }
219
+ }
220
+
221
+ // Reset inactivity timer if there's still partial data in the buffer
222
+ this.resetBufferTimeout();
223
+ }
224
+ }
225
+
226
+ // ---------------------------------------------------------------------------
227
+ // MCPBridge - the main bridge class
228
+ // ---------------------------------------------------------------------------
229
+
230
+ /**
231
+ * MCPBridge wraps a Palaryn Gateway instance as an MCP server, exposing
232
+ * the gateway's tool execution capabilities through the Model Context Protocol.
233
+ *
234
+ * Communication uses JSON-RPC 2.0 over stdio (line-delimited).
235
+ *
236
+ * Tools are managed via a registry pattern. By default, HTTP tools are
237
+ * registered (http_request, http_get, http_post). Additional tools
238
+ * (file, sql, shell) can be registered dynamically.
239
+ *
240
+ * Each tool constructs a proper ToolCall, runs it through the full gateway
241
+ * pipeline (policy, DLP, budget, rate limiting, execution), and returns
242
+ * the ToolResult as the MCP response.
243
+ *
244
+ * Supported MCP methods:
245
+ * - `initialize` - Protocol handshake (returns server info and capabilities)
246
+ * - `notifications/initialized` - Client acknowledgment (no-op notification)
247
+ * - `tools/list` - List available tools with their JSON schemas
248
+ * - `tools/call` - Execute a tool through the gateway
249
+ * - `ping` - Health check
250
+ */
251
+ export class MCPBridge {
252
+ private gateway: Gateway;
253
+ private bridgeConfig: Required<MCPBridgeConfig>;
254
+ private transport: StdioTransport | null = null;
255
+ private initialized: boolean = false;
256
+ private toolRegistry: Map<string, MCPToolHandler> = new Map();
257
+
258
+ constructor(gateway: Gateway, bridgeConfig?: MCPBridgeConfig) {
259
+ this.gateway = gateway;
260
+ this.bridgeConfig = {
261
+ ...DEFAULT_BRIDGE_CONFIG,
262
+ ...bridgeConfig,
263
+ };
264
+
265
+ // Register HTTP tools by default
266
+ this.registerTools(HTTP_TOOLS);
267
+ }
268
+
269
+ /**
270
+ * Register a single tool handler in the registry.
271
+ */
272
+ registerTool(handler: MCPToolHandler): void {
273
+ this.toolRegistry.set(handler.definition.name, handler);
274
+ }
275
+
276
+ /**
277
+ * Register multiple tool handlers in the registry.
278
+ */
279
+ registerTools(handlers: MCPToolHandler[]): void {
280
+ for (const handler of handlers) {
281
+ this.registerTool(handler);
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Connect via stdio transport (reads from stdin, writes to stdout).
287
+ * This is the standard way to run an MCP server for CLI-based clients.
288
+ * Optionally accepts custom input/output streams for testing.
289
+ */
290
+ async connectStdio(input?: Readable, output?: Writable): Promise<void> {
291
+ this.transport = new StdioTransport(input, output);
292
+ this.transport.start((msg) => this.handleMessage(msg));
293
+ }
294
+
295
+ /**
296
+ * Close the transport and shut down the gateway.
297
+ */
298
+ async close(): Promise<void> {
299
+ if (this.transport) {
300
+ this.transport.close();
301
+ this.transport = null;
302
+ }
303
+ this.gateway.shutdown();
304
+ }
305
+
306
+ /**
307
+ * Returns the underlying Gateway instance.
308
+ */
309
+ getGateway(): Gateway {
310
+ return this.gateway;
311
+ }
312
+
313
+ /**
314
+ * Whether the MCP handshake has been completed.
315
+ */
316
+ isInitialized(): boolean {
317
+ return this.initialized;
318
+ }
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // Message dispatch
322
+ // ---------------------------------------------------------------------------
323
+
324
+ /** Handle an incoming JSON-RPC message (request or notification). */
325
+ private handleMessage(msg: JSONRPCMessage): void {
326
+ // Notifications have no 'id' field
327
+ if (!('id' in msg) || msg.id === undefined) {
328
+ this.handleNotification(msg as JSONRPCNotification);
329
+ return;
330
+ }
331
+
332
+ const request = msg as JSONRPCRequest;
333
+ this.handleRequest(request).catch((err) => {
334
+ this.sendError(request.id, INTERNAL_ERROR, `Internal error: ${err instanceof Error ? err.message : String(err)}`);
335
+ });
336
+ }
337
+
338
+ /** Handle a JSON-RPC notification (no response expected). */
339
+ private handleNotification(msg: JSONRPCNotification): void {
340
+ // The only notification we handle is `notifications/initialized`
341
+ if (msg.method === 'notifications/initialized') {
342
+ // Client acknowledges initialization is complete; no action needed.
343
+ return;
344
+ }
345
+ // Unknown notifications are silently ignored per MCP spec.
346
+ }
347
+
348
+ /** Handle a JSON-RPC request and send a response. */
349
+ private async handleRequest(request: JSONRPCRequest): Promise<void> {
350
+ switch (request.method) {
351
+ case 'initialize':
352
+ this.handleInitialize(request);
353
+ break;
354
+ case 'tools/list':
355
+ this.handleToolsList(request);
356
+ break;
357
+ case 'tools/call':
358
+ await this.handleToolsCall(request);
359
+ break;
360
+ case 'ping':
361
+ this.sendResult(request.id, {});
362
+ break;
363
+ default:
364
+ this.sendError(request.id, METHOD_NOT_FOUND, `Method not found: ${request.method}`);
365
+ break;
366
+ }
367
+ }
368
+
369
+ // ---------------------------------------------------------------------------
370
+ // MCP method handlers
371
+ // ---------------------------------------------------------------------------
372
+
373
+ /** Handle `initialize` - protocol handshake. */
374
+ private handleInitialize(request: JSONRPCRequest): void {
375
+ this.initialized = true;
376
+
377
+ this.sendResult(request.id, {
378
+ protocolVersion: LATEST_PROTOCOL_VERSION,
379
+ capabilities: {
380
+ tools: {
381
+ listChanged: false,
382
+ },
383
+ },
384
+ serverInfo: SERVER_INFO,
385
+ });
386
+ }
387
+
388
+ /** Handle `tools/list` - return all registered tool definitions. */
389
+ private handleToolsList(request: JSONRPCRequest): void {
390
+ const tools = Array.from(this.toolRegistry.values()).map(h => h.definition);
391
+ this.sendResult(request.id, { tools });
392
+ }
393
+
394
+ /** Handle `tools/call` - execute a tool through the gateway. */
395
+ private async handleToolsCall(request: JSONRPCRequest): Promise<void> {
396
+ const params = request.params;
397
+ if (!params || typeof params.name !== 'string') {
398
+ this.sendError(request.id, INVALID_PARAMS, 'Missing required parameter: name');
399
+ return;
400
+ }
401
+
402
+ const toolName = params.name as string;
403
+ const toolArgs = (params.arguments || {}) as Record<string, unknown>;
404
+
405
+ const handler = this.toolRegistry.get(toolName);
406
+ if (!handler) {
407
+ this.sendError(request.id, INVALID_PARAMS, `Unknown tool: ${toolName}`);
408
+ return;
409
+ }
410
+
411
+ let result: MCPCallToolResult;
412
+ try {
413
+ // Validate required args for HTTP tools (url is required)
414
+ const urlError = validateUrlArg(toolArgs);
415
+ if (urlError && (toolName === 'http_request' || toolName === 'http_get' || toolName === 'http_post')) {
416
+ result = this.toolError(urlError);
417
+ } else {
418
+ const toolCall = handler.buildToolCall(toolArgs, this.bridgeConfig);
419
+ result = await this.executeAndFormat(toolCall);
420
+ }
421
+ } catch (err) {
422
+ result = {
423
+ content: [
424
+ {
425
+ type: 'text',
426
+ text: JSON.stringify({
427
+ error: err instanceof Error ? err.message : String(err),
428
+ status: 'error',
429
+ }, null, 2),
430
+ },
431
+ ],
432
+ isError: true,
433
+ };
434
+ }
435
+
436
+ this.sendResult(request.id, result);
437
+ }
438
+
439
+ // ---------------------------------------------------------------------------
440
+ // Internal helpers
441
+ // ---------------------------------------------------------------------------
442
+
443
+ /**
444
+ * Build a ToolCall from MCP tool arguments, applying bridge defaults.
445
+ * Kept for backward compatibility with tests that access it via reflection.
446
+ */
447
+ private buildToolCall(params: {
448
+ toolName: string;
449
+ capability: ToolInfo['capability'];
450
+ args: ToolCallArgs;
451
+ constraints?: { timeout_ms?: number; max_cost_usd?: number };
452
+ context?: { purpose?: string; labels?: string[] };
453
+ }): ToolCall {
454
+ const toolCall: ToolCall = {
455
+ tool_call_id: randomUUID(),
456
+ task_id: this.bridgeConfig.task_id || randomUUID(),
457
+ workspace_id: this.bridgeConfig.workspace_id,
458
+ actor: { ...this.bridgeConfig.actor },
459
+ source: { ...this.bridgeConfig.source },
460
+ tool: {
461
+ name: params.toolName,
462
+ version: '1.0.0',
463
+ capability: params.capability,
464
+ },
465
+ args: params.args,
466
+ timestamp: new Date().toISOString(),
467
+ };
468
+
469
+ // Add constraints if any are specified
470
+ if (params.constraints?.timeout_ms != null || params.constraints?.max_cost_usd != null) {
471
+ toolCall.constraints = {};
472
+ if (params.constraints.timeout_ms != null) {
473
+ toolCall.constraints.timeout_ms = params.constraints.timeout_ms;
474
+ }
475
+ if (params.constraints.max_cost_usd != null) {
476
+ toolCall.constraints.max_cost_usd = params.constraints.max_cost_usd;
477
+ }
478
+ }
479
+
480
+ // Add context if any is specified
481
+ if (params.context?.purpose || params.context?.labels) {
482
+ toolCall.context = {};
483
+ if (params.context.purpose) {
484
+ toolCall.context.purpose = params.context.purpose;
485
+ }
486
+ if (params.context.labels) {
487
+ toolCall.context.labels = params.context.labels;
488
+ }
489
+ }
490
+
491
+ return toolCall;
492
+ }
493
+
494
+ /**
495
+ * Execute a ToolCall through the gateway and format as MCP result.
496
+ */
497
+ private async executeAndFormat(toolCall: ToolCall): Promise<MCPCallToolResult> {
498
+ try {
499
+ const result: ToolResult = await this.gateway.execute(toolCall);
500
+ return this.formatResult(result);
501
+ } catch (err) {
502
+ const errorMessage = err instanceof Error ? err.message : String(err);
503
+ return {
504
+ content: [
505
+ {
506
+ type: 'text',
507
+ text: JSON.stringify(
508
+ {
509
+ error: errorMessage,
510
+ tool_call_id: toolCall.tool_call_id,
511
+ status: 'error',
512
+ },
513
+ null,
514
+ 2,
515
+ ),
516
+ },
517
+ ],
518
+ isError: true,
519
+ };
520
+ }
521
+ }
522
+
523
+ /**
524
+ * Convert a gateway ToolResult into an MCP CallToolResult.
525
+ *
526
+ * The result includes:
527
+ * - The tool output body (or error message) as the primary text content
528
+ * - Gateway metadata (status, policy decision, DLP report, budget, timing)
529
+ * as a second text content block for transparency
530
+ */
531
+ private formatResult(result: ToolResult): MCPCallToolResult {
532
+ const isError = result.status === 'error' || result.status === 'blocked';
533
+
534
+ // Primary content: the actual output or error
535
+ let primaryText: string;
536
+ if (result.error) {
537
+ primaryText = result.error;
538
+ } else if (result.output?.body !== undefined) {
539
+ primaryText =
540
+ typeof result.output.body === 'string'
541
+ ? result.output.body
542
+ : JSON.stringify(result.output.body, null, 2);
543
+ } else {
544
+ primaryText = `Request completed with status: ${result.status}`;
545
+ }
546
+
547
+ // Metadata block for gateway transparency
548
+ const metadata = {
549
+ tool_call_id: result.tool_call_id,
550
+ task_id: result.task_id,
551
+ status: result.status,
552
+ policy: result.policy,
553
+ dlp: {
554
+ detected: result.dlp.detected,
555
+ severity: result.dlp.severity,
556
+ redaction_count: result.dlp.redactions.length,
557
+ },
558
+ budget: result.budget,
559
+ timing: result.timing,
560
+ http_status: result.output?.http_status,
561
+ exit_code: result.output?.exit_code,
562
+ rows_affected: result.output?.rows_affected,
563
+ paths: result.output?.paths,
564
+ stderr: result.output?.stderr,
565
+ };
566
+
567
+ return {
568
+ content: [
569
+ {
570
+ type: 'text',
571
+ text: primaryText,
572
+ },
573
+ {
574
+ type: 'text',
575
+ text: `--- Gateway Metadata ---\n${JSON.stringify(metadata, null, 2)}`,
576
+ },
577
+ ],
578
+ isError,
579
+ };
580
+ }
581
+
582
+ /**
583
+ * Map an HTTP method to a ToolInfo capability level.
584
+ */
585
+ private methodToCapability(method: string): ToolInfo['capability'] {
586
+ switch (method.toUpperCase()) {
587
+ case 'GET':
588
+ case 'HEAD':
589
+ case 'OPTIONS':
590
+ return 'read';
591
+ case 'POST':
592
+ case 'PUT':
593
+ case 'PATCH':
594
+ return 'write';
595
+ case 'DELETE':
596
+ return 'delete';
597
+ default:
598
+ return 'write';
599
+ }
600
+ }
601
+
602
+ /**
603
+ * Attempt to parse a body string as JSON, falling back to the raw string.
604
+ */
605
+ private parseBody(body: string): unknown {
606
+ try {
607
+ return JSON.parse(body);
608
+ } catch {
609
+ return body;
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Create an MCP tool error result.
615
+ */
616
+ private toolError(message: string): MCPCallToolResult {
617
+ return {
618
+ content: [{ type: 'text', text: message }],
619
+ isError: true,
620
+ };
621
+ }
622
+
623
+ /**
624
+ * Send a successful JSON-RPC response.
625
+ */
626
+ private sendResult(id: string | number, result: unknown): void {
627
+ if (this.transport) {
628
+ this.transport.send({
629
+ jsonrpc: JSONRPC_VERSION,
630
+ id,
631
+ result,
632
+ });
633
+ }
634
+ }
635
+
636
+ /**
637
+ * Send a JSON-RPC error response.
638
+ */
639
+ private sendError(id: string | number, code: number, message: string, data?: unknown): void {
640
+ if (this.transport) {
641
+ this.transport.send({
642
+ jsonrpc: JSONRPC_VERSION,
643
+ id,
644
+ error: { code, message, data },
645
+ });
646
+ }
647
+ }
648
+ }
649
+
650
+ // ---------------------------------------------------------------------------
651
+ // Convenience entry point
652
+ // ---------------------------------------------------------------------------
653
+
654
+ /**
655
+ * Create a Gateway instance with the given config (or defaults),
656
+ * wrap it in an MCPBridge, and connect via stdio transport.
657
+ *
658
+ * This is the main entry point for running Palaryn as an MCP server.
659
+ *
660
+ * @param gatewayConfig - Full gateway configuration (defaults to DEFAULT_CONFIG with auth disabled)
661
+ * @param bridgeConfig - MCP bridge defaults for workspace, actor, source
662
+ * @returns The connected MCPBridge instance
663
+ */
664
+ export async function startMCPBridge(
665
+ gatewayConfig?: Partial<GatewayConfig>,
666
+ bridgeConfig?: MCPBridgeConfig,
667
+ ): Promise<MCPBridge> {
668
+ // Merge with defaults, disabling auth for MCP (auth is handled by the MCP client/transport)
669
+ const config: GatewayConfig = {
670
+ ...DEFAULT_CONFIG,
671
+ ...gatewayConfig,
672
+ auth: {
673
+ ...DEFAULT_CONFIG.auth,
674
+ enabled: false,
675
+ ...gatewayConfig?.auth,
676
+ },
677
+ // Disable console audit output when running as MCP server
678
+ // to avoid polluting stdout (which is the MCP transport channel)
679
+ audit: {
680
+ ...DEFAULT_CONFIG.audit,
681
+ console_output: false,
682
+ ...gatewayConfig?.audit,
683
+ },
684
+ };
685
+
686
+ const gateway = new Gateway(config);
687
+ const bridge = new MCPBridge(gateway, bridgeConfig);
688
+
689
+ // Register non-HTTP tools based on executor config
690
+ if (config.executor.filesystem?.enabled) {
691
+ bridge.registerTools(FILE_TOOLS);
692
+ }
693
+ if (config.executor.sql?.enabled) {
694
+ bridge.registerTools(SQL_TOOLS);
695
+ }
696
+ if (config.executor.shell?.enabled) {
697
+ bridge.registerTools(SHELL_TOOLS);
698
+ }
699
+
700
+ await bridge.connectStdio();
701
+
702
+ return bridge;
703
+ }