@inso_web/els-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/README.md +482 -0
  2. package/dist/audit/prisma.d.ts +67 -0
  3. package/dist/audit/prisma.d.ts.map +1 -0
  4. package/dist/audit/prisma.js +65 -0
  5. package/dist/audit/prisma.js.map +1 -0
  6. package/dist/audit/service.d.ts +72 -0
  7. package/dist/audit/service.d.ts.map +1 -0
  8. package/dist/audit/service.js +137 -0
  9. package/dist/audit/service.js.map +1 -0
  10. package/dist/billing/limits.d.ts +34 -0
  11. package/dist/billing/limits.d.ts.map +1 -0
  12. package/dist/billing/limits.js +51 -0
  13. package/dist/billing/limits.js.map +1 -0
  14. package/dist/billing/tracker.d.ts +39 -0
  15. package/dist/billing/tracker.d.ts.map +1 -0
  16. package/dist/billing/tracker.js +92 -0
  17. package/dist/billing/tracker.js.map +1 -0
  18. package/dist/cache/cachedElsClient.d.ts +71 -0
  19. package/dist/cache/cachedElsClient.d.ts.map +1 -0
  20. package/dist/cache/cachedElsClient.js +167 -0
  21. package/dist/cache/cachedElsClient.js.map +1 -0
  22. package/dist/cache/index.d.ts +10 -0
  23. package/dist/cache/index.d.ts.map +1 -0
  24. package/dist/cache/index.js +6 -0
  25. package/dist/cache/index.js.map +1 -0
  26. package/dist/cache/policies.d.ts +60 -0
  27. package/dist/cache/policies.d.ts.map +1 -0
  28. package/dist/cache/policies.js +90 -0
  29. package/dist/cache/policies.js.map +1 -0
  30. package/dist/cache/redis.d.ts +52 -0
  31. package/dist/cache/redis.d.ts.map +1 -0
  32. package/dist/cache/redis.js +134 -0
  33. package/dist/cache/redis.js.map +1 -0
  34. package/dist/cache/types.d.ts +32 -0
  35. package/dist/cache/types.d.ts.map +1 -0
  36. package/dist/cache/types.js +32 -0
  37. package/dist/cache/types.js.map +1 -0
  38. package/dist/cache/wrapper.d.ts +38 -0
  39. package/dist/cache/wrapper.d.ts.map +1 -0
  40. package/dist/cache/wrapper.js +109 -0
  41. package/dist/cache/wrapper.js.map +1 -0
  42. package/dist/cli.d.ts +3 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +86 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/config.d.ts +105 -0
  47. package/dist/config.d.ts.map +1 -0
  48. package/dist/config.js +211 -0
  49. package/dist/config.js.map +1 -0
  50. package/dist/elsClient.d.ts +137 -0
  51. package/dist/elsClient.d.ts.map +1 -0
  52. package/dist/elsClient.js +285 -0
  53. package/dist/elsClient.js.map +1 -0
  54. package/dist/http/app.d.ts +40 -0
  55. package/dist/http/app.d.ts.map +1 -0
  56. package/dist/http/app.js +135 -0
  57. package/dist/http/app.js.map +1 -0
  58. package/dist/http/jwks.d.ts +8 -0
  59. package/dist/http/jwks.d.ts.map +1 -0
  60. package/dist/http/jwks.js +34 -0
  61. package/dist/http/jwks.js.map +1 -0
  62. package/dist/http/middleware/auth.d.ts +11 -0
  63. package/dist/http/middleware/auth.d.ts.map +1 -0
  64. package/dist/http/middleware/auth.js +225 -0
  65. package/dist/http/middleware/auth.js.map +1 -0
  66. package/dist/http/middleware/dcrRateLimit.d.ts +29 -0
  67. package/dist/http/middleware/dcrRateLimit.d.ts.map +1 -0
  68. package/dist/http/middleware/dcrRateLimit.js +59 -0
  69. package/dist/http/middleware/dcrRateLimit.js.map +1 -0
  70. package/dist/http/middleware/errorHandler.d.ts +12 -0
  71. package/dist/http/middleware/errorHandler.d.ts.map +1 -0
  72. package/dist/http/middleware/errorHandler.js +26 -0
  73. package/dist/http/middleware/errorHandler.js.map +1 -0
  74. package/dist/http/middleware/originGuard.d.ts +28 -0
  75. package/dist/http/middleware/originGuard.d.ts.map +1 -0
  76. package/dist/http/middleware/originGuard.js +55 -0
  77. package/dist/http/middleware/originGuard.js.map +1 -0
  78. package/dist/http/middleware/requestId.d.ts +19 -0
  79. package/dist/http/middleware/requestId.d.ts.map +1 -0
  80. package/dist/http/middleware/requestId.js +23 -0
  81. package/dist/http/middleware/requestId.js.map +1 -0
  82. package/dist/http/routes/health.d.ts +24 -0
  83. package/dist/http/routes/health.d.ts.map +1 -0
  84. package/dist/http/routes/health.js +73 -0
  85. package/dist/http/routes/health.js.map +1 -0
  86. package/dist/http/routes/metrics.d.ts +18 -0
  87. package/dist/http/routes/metrics.d.ts.map +1 -0
  88. package/dist/http/routes/metrics.js +42 -0
  89. package/dist/http/routes/metrics.js.map +1 -0
  90. package/dist/http/routes/wellKnown.d.ts +15 -0
  91. package/dist/http/routes/wellKnown.d.ts.map +1 -0
  92. package/dist/http/routes/wellKnown.js +43 -0
  93. package/dist/http/routes/wellKnown.js.map +1 -0
  94. package/dist/http/types.d.ts +40 -0
  95. package/dist/http/types.d.ts.map +1 -0
  96. package/dist/http/types.js +9 -0
  97. package/dist/http/types.js.map +1 -0
  98. package/dist/instrumentation.d.ts +22 -0
  99. package/dist/instrumentation.d.ts.map +1 -0
  100. package/dist/instrumentation.js +38 -0
  101. package/dist/instrumentation.js.map +1 -0
  102. package/dist/lib/cursor.d.ts +22 -0
  103. package/dist/lib/cursor.d.ts.map +1 -0
  104. package/dist/lib/cursor.js +95 -0
  105. package/dist/lib/cursor.js.map +1 -0
  106. package/dist/lib/errors.d.ts +49 -0
  107. package/dist/lib/errors.d.ts.map +1 -0
  108. package/dist/lib/errors.js +83 -0
  109. package/dist/lib/errors.js.map +1 -0
  110. package/dist/lib/responseFormat.d.ts +14 -0
  111. package/dist/lib/responseFormat.d.ts.map +1 -0
  112. package/dist/lib/responseFormat.js +74 -0
  113. package/dist/lib/responseFormat.js.map +1 -0
  114. package/dist/middleware/withMiddleware.d.ts +53 -0
  115. package/dist/middleware/withMiddleware.d.ts.map +1 -0
  116. package/dist/middleware/withMiddleware.js +190 -0
  117. package/dist/middleware/withMiddleware.js.map +1 -0
  118. package/dist/observability/health.d.ts +51 -0
  119. package/dist/observability/health.d.ts.map +1 -0
  120. package/dist/observability/health.js +77 -0
  121. package/dist/observability/health.js.map +1 -0
  122. package/dist/observability/index.d.ts +8 -0
  123. package/dist/observability/index.d.ts.map +1 -0
  124. package/dist/observability/index.js +5 -0
  125. package/dist/observability/index.js.map +1 -0
  126. package/dist/observability/logger.d.ts +45 -0
  127. package/dist/observability/logger.d.ts.map +1 -0
  128. package/dist/observability/logger.js +75 -0
  129. package/dist/observability/logger.js.map +1 -0
  130. package/dist/observability/metrics.d.ts +49 -0
  131. package/dist/observability/metrics.d.ts.map +1 -0
  132. package/dist/observability/metrics.js +184 -0
  133. package/dist/observability/metrics.js.map +1 -0
  134. package/dist/observability/tracing.d.ts +28 -0
  135. package/dist/observability/tracing.d.ts.map +1 -0
  136. package/dist/observability/tracing.js +56 -0
  137. package/dist/observability/tracing.js.map +1 -0
  138. package/dist/prompts/index.d.ts +20 -0
  139. package/dist/prompts/index.d.ts.map +1 -0
  140. package/dist/prompts/index.js +202 -0
  141. package/dist/prompts/index.js.map +1 -0
  142. package/dist/redaction/argsRedactor.d.ts +22 -0
  143. package/dist/redaction/argsRedactor.d.ts.map +1 -0
  144. package/dist/redaction/argsRedactor.js +97 -0
  145. package/dist/redaction/argsRedactor.js.map +1 -0
  146. package/dist/redaction/fields.d.ts +64 -0
  147. package/dist/redaction/fields.d.ts.map +1 -0
  148. package/dist/redaction/fields.js +155 -0
  149. package/dist/redaction/fields.js.map +1 -0
  150. package/dist/redaction/index.d.ts +52 -0
  151. package/dist/redaction/index.d.ts.map +1 -0
  152. package/dist/redaction/index.js +160 -0
  153. package/dist/redaction/index.js.map +1 -0
  154. package/dist/redaction/promptInjection.d.ts +32 -0
  155. package/dist/redaction/promptInjection.d.ts.map +1 -0
  156. package/dist/redaction/promptInjection.js +68 -0
  157. package/dist/redaction/promptInjection.js.map +1 -0
  158. package/dist/redaction/url.d.ts +8 -0
  159. package/dist/redaction/url.d.ts.map +1 -0
  160. package/dist/redaction/url.js +26 -0
  161. package/dist/redaction/url.js.map +1 -0
  162. package/dist/redaction/userAgent.d.ts +9 -0
  163. package/dist/redaction/userAgent.d.ts.map +1 -0
  164. package/dist/redaction/userAgent.js +39 -0
  165. package/dist/redaction/userAgent.js.map +1 -0
  166. package/dist/resources/index.d.ts +24 -0
  167. package/dist/resources/index.d.ts.map +1 -0
  168. package/dist/resources/index.js +150 -0
  169. package/dist/resources/index.js.map +1 -0
  170. package/dist/server.d.ts +37 -0
  171. package/dist/server.d.ts.map +1 -0
  172. package/dist/server.js +35 -0
  173. package/dist/server.js.map +1 -0
  174. package/dist/tools/baselineCompare.d.ts +36 -0
  175. package/dist/tools/baselineCompare.d.ts.map +1 -0
  176. package/dist/tools/baselineCompare.js +69 -0
  177. package/dist/tools/baselineCompare.js.map +1 -0
  178. package/dist/tools/errorHeatmap.d.ts +40 -0
  179. package/dist/tools/errorHeatmap.d.ts.map +1 -0
  180. package/dist/tools/errorHeatmap.js +69 -0
  181. package/dist/tools/errorHeatmap.js.map +1 -0
  182. package/dist/tools/errorHistogram.d.ts +39 -0
  183. package/dist/tools/errorHistogram.d.ts.map +1 -0
  184. package/dist/tools/errorHistogram.js +61 -0
  185. package/dist/tools/errorHistogram.js.map +1 -0
  186. package/dist/tools/errorStatsBreakdown.d.ts +43 -0
  187. package/dist/tools/errorStatsBreakdown.d.ts.map +1 -0
  188. package/dist/tools/errorStatsBreakdown.js +77 -0
  189. package/dist/tools/errorStatsBreakdown.js.map +1 -0
  190. package/dist/tools/errorsInSession.d.ts +44 -0
  191. package/dist/tools/errorsInSession.d.ts.map +1 -0
  192. package/dist/tools/errorsInSession.js +91 -0
  193. package/dist/tools/errorsInSession.js.map +1 -0
  194. package/dist/tools/explainError.d.ts +35 -0
  195. package/dist/tools/explainError.d.ts.map +1 -0
  196. package/dist/tools/explainError.js +98 -0
  197. package/dist/tools/explainError.js.map +1 -0
  198. package/dist/tools/findCorrelatedErrors.d.ts +43 -0
  199. package/dist/tools/findCorrelatedErrors.d.ts.map +1 -0
  200. package/dist/tools/findCorrelatedErrors.js +59 -0
  201. package/dist/tools/findCorrelatedErrors.js.map +1 -0
  202. package/dist/tools/findSimilarErrors.d.ts +44 -0
  203. package/dist/tools/findSimilarErrors.d.ts.map +1 -0
  204. package/dist/tools/findSimilarErrors.js +59 -0
  205. package/dist/tools/findSimilarErrors.js.map +1 -0
  206. package/dist/tools/getLogDetails.d.ts +30 -0
  207. package/dist/tools/getLogDetails.d.ts.map +1 -0
  208. package/dist/tools/getLogDetails.js +49 -0
  209. package/dist/tools/getLogDetails.js.map +1 -0
  210. package/dist/tools/groupedErrors.d.ts +46 -0
  211. package/dist/tools/groupedErrors.d.ts.map +1 -0
  212. package/dist/tools/groupedErrors.js +71 -0
  213. package/dist/tools/groupedErrors.js.map +1 -0
  214. package/dist/tools/impactAnalysis.d.ts +42 -0
  215. package/dist/tools/impactAnalysis.d.ts.map +1 -0
  216. package/dist/tools/impactAnalysis.js +61 -0
  217. package/dist/tools/impactAnalysis.js.map +1 -0
  218. package/dist/tools/index.d.ts +41 -0
  219. package/dist/tools/index.d.ts.map +1 -0
  220. package/dist/tools/index.js +83 -0
  221. package/dist/tools/index.js.map +1 -0
  222. package/dist/tools/listApps.d.ts +27 -0
  223. package/dist/tools/listApps.d.ts.map +1 -0
  224. package/dist/tools/listApps.js +78 -0
  225. package/dist/tools/listApps.js.map +1 -0
  226. package/dist/tools/queryLogsJql.d.ts +44 -0
  227. package/dist/tools/queryLogsJql.d.ts.map +1 -0
  228. package/dist/tools/queryLogsJql.js +107 -0
  229. package/dist/tools/queryLogsJql.js.map +1 -0
  230. package/dist/tools/searchLogs.d.ts +60 -0
  231. package/dist/tools/searchLogs.d.ts.map +1 -0
  232. package/dist/tools/searchLogs.js +127 -0
  233. package/dist/tools/searchLogs.js.map +1 -0
  234. package/dist/tools/topErrorMessages.d.ts +42 -0
  235. package/dist/tools/topErrorMessages.d.ts.map +1 -0
  236. package/dist/tools/topErrorMessages.js +63 -0
  237. package/dist/tools/topErrorMessages.js.map +1 -0
  238. package/dist/tools/trafficStats.d.ts +39 -0
  239. package/dist/tools/trafficStats.d.ts.map +1 -0
  240. package/dist/tools/trafficStats.js +57 -0
  241. package/dist/tools/trafficStats.js.map +1 -0
  242. package/dist/tools/triageRecentCritical.d.ts +26 -0
  243. package/dist/tools/triageRecentCritical.d.ts.map +1 -0
  244. package/dist/tools/triageRecentCritical.js +81 -0
  245. package/dist/tools/triageRecentCritical.js.map +1 -0
  246. package/dist/tools/versionRegression.d.ts +29 -0
  247. package/dist/tools/versionRegression.d.ts.map +1 -0
  248. package/dist/tools/versionRegression.js +80 -0
  249. package/dist/tools/versionRegression.js.map +1 -0
  250. package/dist/transports/http-server.d.ts +25 -0
  251. package/dist/transports/http-server.d.ts.map +1 -0
  252. package/dist/transports/http-server.js +84 -0
  253. package/dist/transports/http-server.js.map +1 -0
  254. package/dist/transports/http.d.ts +71 -0
  255. package/dist/transports/http.d.ts.map +1 -0
  256. package/dist/transports/http.js +315 -0
  257. package/dist/transports/http.js.map +1 -0
  258. package/dist/transports/stdio.d.ts +13 -0
  259. package/dist/transports/stdio.d.ts.map +1 -0
  260. package/dist/transports/stdio.js +16 -0
  261. package/dist/transports/stdio.js.map +1 -0
  262. package/dist/types.d.ts +150 -0
  263. package/dist/types.d.ts.map +1 -0
  264. package/dist/types.js +8 -0
  265. package/dist/types.js.map +1 -0
  266. package/package.json +95 -0
package/README.md ADDED
@@ -0,0 +1,482 @@
1
+ # @inso/els-mcp
2
+
3
+ MCP-сервер поверх INSO Error Logs Service (ELS). Даёт LLM-агентам (Claude
4
+ Desktop, mcp-inspector, любой MCP-клиент) read-only доступ к логам ошибок и
5
+ аналитическим эндпоинтам — для триажа, поиска паттернов и анализа трендов.
6
+
7
+ > **Phase 1+3 готовы.** stdio + Streamable HTTP transport, 8 базовых read-only
8
+ > tools, OIDC через INSO Auth, RFC 9728 resource metadata.
9
+ > Полный roadmap (resources, prompts, cache, AI) —
10
+ > в `/todo/error-logs-service/mcp/` (см. `12-migration-rollout.md`).
11
+
12
+ ## TL;DR
13
+
14
+ ```bash
15
+ npm install
16
+ npm test
17
+ ELS_API_KEY=els_live_... npm run dev
18
+ ```
19
+
20
+ ## Quickstart
21
+
22
+ ### 1. Установить зависимости
23
+
24
+ ```bash
25
+ npm install
26
+ ```
27
+
28
+ ### 2. Сконфигурировать ELS endpoint
29
+
30
+ Скопируйте `.env.example` → `.env` и задайте `ELS_API_KEY`:
31
+
32
+ ```bash
33
+ ELS_API_KEY=els_live_xxxxxxxxxxxxxxxxxxxx
34
+ ELS_BASE_URL=http://localhost:4010 # или https://api.insoweb.ru/els
35
+ MCP_LOG_LEVEL=info
36
+ ```
37
+
38
+ По умолчанию `ELS_BASE_URL` берётся из `NODE_ENV`:
39
+ - `development` → `http://localhost:4010`
40
+ - `production` → `https://api.insoweb.ru/els`
41
+
42
+ ### 3. Запустить локально
43
+
44
+ ```bash
45
+ npm run dev # tsx src/cli.ts — без сборки
46
+ ```
47
+
48
+ или после `npm run build`:
49
+
50
+ ```bash
51
+ node dist/cli.js
52
+ ```
53
+
54
+ stdout зарезервирован под JSON-RPC; все логи идут в stderr.
55
+
56
+ ### 4. Запустить тесты и typecheck
57
+
58
+ ```bash
59
+ npm test # vitest run — 36 unit-тестов
60
+ npm run typecheck
61
+ ```
62
+
63
+ ## Claude Desktop integration
64
+
65
+ Добавьте в `~/Library/Application Support/Claude/claude_desktop_config.json`
66
+ (macOS) или `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "els": {
72
+ "command": "node",
73
+ "args": ["/absolute/path/to/event-log-mcp-service/dist/cli.js"],
74
+ "env": {
75
+ "ELS_API_KEY": "els_live_xxxxxxxxxxxxxxxxxxxx",
76
+ "ELS_BASE_URL": "https://api.insoweb.ru/els",
77
+ "MCP_LOG_LEVEL": "info"
78
+ }
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ Перезапустите Claude Desktop — в списке инструментов появятся 8 ELS-tools.
85
+
86
+ ## Доступные tools (Phase 1)
87
+
88
+ | Tool | ELS endpoint | Описание |
89
+ |------|---|---|
90
+ | `search_logs` | `GET /errors` | Search логов с фасет-фильтрами (level, serviceName, appVersion, url, fingerprint, sessionId) + facets + histogram. |
91
+ | `get_log_details` | `GET /errors/:traceId` | Полные детали одного лога: stack, headers, context. |
92
+ | `find_similar_errors` | `GET /errors/:traceId/similar` | Агрегаты по похожим ошибкам: totalOccurrences, lastHour/24h/7d, topUrls/IPs, sourceSplit. |
93
+ | `find_correlated_errors` | `GET /errors/:traceId/correlated` | Ошибки в ±N минут вокруг указанного traceId — каскадные сбои. |
94
+ | `top_error_messages` | `GET /analytics/top-messages` | Топ-N сообщений по частоте (deduped). |
95
+ | `error_histogram` | `GET /analytics/histogram` | Time-series histogram (auto bucket: 5m/30m/1h/1d). |
96
+ | `traffic_stats` | `GET /analytics/traffic` | RPM + latency p50/p95/p99 per service. |
97
+ | `list_apps` | `GET /apps` | Список tenant-ов (master key — все, обычный — fallback на current-app). |
98
+
99
+ Все tools — `errors:read` scope. `list_apps` требует master key; на 403
100
+ graceful fallback (`apps: [{ slug: '(current-app)' }]`).
101
+
102
+ ## Response format
103
+
104
+ Все listing-tools поддерживают `response_format`:
105
+
106
+ - **`compact`** (default) — без `stack`/`componentStack`/`userAgent`, `message` ≤ 200ch. Дешёво по LLM-контексту.
107
+ - **`full`** — все поля.
108
+ - **`summary`** — только `traceId`/`message`/`level`/`lastSeen`. Для ranked-list view.
109
+
110
+ Если хотя бы один `message` обрезан, в `_meta.truncated = true`.
111
+
112
+ ## Pagination (seek-cursor)
113
+
114
+ Listing-tools используют opaque `cursor` (НЕ `page`/`offset`). Внутри
115
+ зашит `filtersHash` — если клиент сменил фильтры между страницами,
116
+ сервер вернёт `INVALID_ARGS` с предложением начать с первой страницы.
117
+
118
+ > Phase 1 — cursor transitional: внутри хранятся `page`/`limit` (ELS пока
119
+ > offset-based). Phase 4 переключится на настоящий seek `(receivedAt, id)` —
120
+ > формат cursor останется тот же.
121
+
122
+ ## Error shape
123
+
124
+ Все ошибки приходят как MCP-стандартный tool error:
125
+
126
+ ```json
127
+ {
128
+ "isError": true,
129
+ "content": [{ "type": "text", "text": "[RATE_LIMITED] ..." }],
130
+ "_meta": { "code": "RATE_LIMITED", "retryAfter": 60 }
131
+ }
132
+ ```
133
+
134
+ Коды: `RATE_LIMITED`, `UPSTREAM_UNAVAILABLE`, `INVALID_ARGS`, `NOT_FOUND`,
135
+ `INSUFFICIENT_SCOPE`, `QUOTA_EXCEEDED`, `INTERNAL`.
136
+
137
+ ## Конфигурация
138
+
139
+ | ENV | Default | Описание |
140
+ |---|---|---|
141
+ | `ELS_API_KEY` | — (required) | Bearer key (`els_live_*` или `els_test_*`). |
142
+ | `ELS_BASE_URL` | dev:`localhost:4010` / prod:`api.insoweb.ru/els` | Upstream ELS. |
143
+ | `MCP_LOG_LEVEL` | `info` | pino level. |
144
+ | `MCP_DISABLE_TOOLS` | — | CSV с именами tools для отключения. |
145
+ | `MCP_UPSTREAM_TIMEOUT_MS` | `30000` | Таймаут одного ELS-запроса. |
146
+
147
+ ## HTTP transport (Phase 3)
148
+
149
+ В дополнение к stdio сервер поддерживает Streamable HTTP transport через
150
+ `MCP_TRANSPORT=http`. Используется для hosted endpoint'а
151
+ `https://mcp.insoweb.ru/els/` (ChatGPT Custom Connector, remote Claude).
152
+
153
+ ### Запуск локально
154
+
155
+ ```bash
156
+ MCP_TRANSPORT=http \
157
+ MCP_HTTP_PORT=3030 \
158
+ MCP_OIDC_DEMO_APP_SLUG=acme \
159
+ ELS_API_KEY=els_live_xxx \
160
+ npm run dev
161
+ ```
162
+
163
+ Локально OIDC можно направить на dev-инстанс INSO Auth:
164
+
165
+ ```bash
166
+ MCP_OIDC_ISSUER=http://localhost:4002 \
167
+ MCP_OIDC_JWKS_URL=http://localhost:4002/oidc/.well-known/jwks.json \
168
+ MCP_TRANSPORT=http npm run dev
169
+ ```
170
+
171
+ ### Маршруты
172
+
173
+ | Method | URL | Назначение |
174
+ |---|---|---|
175
+ | `POST /els/mcp` | MCP JSON-RPC (Streamable HTTP) | Требует Bearer (ELS-key или OIDC JWT) |
176
+ | `GET /els/mcp` | Long-lived SSE (server→client notifications) | Требует Bearer |
177
+ | `DELETE /els/mcp` | Terminate session | Требует Bearer |
178
+ | `GET /els/healthz` | Liveness probe (всегда 200) | Public |
179
+ | `GET /els/readyz` | Readiness probe (ELS upstream check) | Public |
180
+ | `GET /els/.well-known/oauth-protected-resource` | RFC 9728 resource metadata | Public |
181
+ | `GET /els/.well-known/mcp` | MCP discovery (tools list, transports) | Public |
182
+
183
+ ### Quick curl
184
+
185
+ ```bash
186
+ # liveness
187
+ curl http://localhost:3030/els/healthz
188
+ # {"status":"ok"}
189
+
190
+ # resource metadata
191
+ curl http://localhost:3030/els/.well-known/oauth-protected-resource
192
+ # { "resource": "https://mcp.insoweb.ru/els/",
193
+ # "authorization_servers": ["https://auth.insoweb.ru"],
194
+ # "scopes_supported": ["errors:mcp-read"],
195
+ # "bearer_methods_supported": ["header"] }
196
+
197
+ # MCP discovery
198
+ curl http://localhost:3030/els/.well-known/mcp
199
+
200
+ # Bearer ELS-key — без OIDC
201
+ curl -X POST http://localhost:3030/els/mcp \
202
+ -H "Authorization: Bearer els_live_xxx" \
203
+ -H "Content-Type: application/json" \
204
+ -H "Accept: application/json, text/event-stream" \
205
+ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"curl","version":"1"},"capabilities":{}}}'
206
+ # Response includes Mcp-Session-Id header; используй его для последующих запросов.
207
+ ```
208
+
209
+ ### Аутентификация
210
+
211
+ Поддерживаются два path'а (детектится по shape Bearer):
212
+
213
+ 1. **ELS-key** — `Authorization: Bearer els_(live|test)_<key>` — passthrough в ELS.
214
+ Используется в CI/CD, server-to-server и debug-сценариях.
215
+ 2. **OIDC JWT** — `Authorization: Bearer <jwt>` — локально валидируется через
216
+ JWKS INSO Auth (`https://auth.insoweb.ru/oidc/.well-known/jwks.json`,
217
+ RS256, audience `els-mcp`, scope `errors:mcp-read`).
218
+
219
+ Если оба Bearer-ы отсутствуют — 401 + `WWW-Authenticate: Bearer realm="els-mcp",
220
+ resource_metadata="https://mcp.insoweb.ru/els/.well-known/oauth-protected-resource"`.
221
+
222
+ ### Sessions
223
+
224
+ `Mcp-Session-Id` header возвращается на первый `initialize`-запрос и должен
225
+ передаваться во все последующие. TTL — 30 мин idle. В Phase 3 хранение
226
+ in-memory (Map); Phase 4 переедет в Redis.
227
+
228
+ > **Phase 3 workaround:** OIDC `sub → appSlug` resolver пока не подключён к
229
+ > LK API — все OIDC-юзеры мапятся на один app через `MCP_OIDC_DEMO_APP_SLUG`.
230
+ > TODO: реализовать настоящий resolver в Phase 4.
231
+
232
+ ### ENV-переменные транспорта
233
+
234
+ | ENV | Default | Описание |
235
+ |---|---|---|
236
+ | `MCP_TRANSPORT` | `stdio` | `stdio` или `http` |
237
+ | `MCP_HTTP_PORT` | `3030` | Порт listen для HTTP-режима |
238
+ | `MCP_PUBLIC_URL` | `https://mcp.insoweb.ru/els` | Используется в WWW-Authenticate и discovery |
239
+ | `MCP_OIDC_ISSUER` | `https://auth.insoweb.ru` | OIDC issuer |
240
+ | `MCP_OIDC_JWKS_URL` | derived | JWKS endpoint |
241
+ | `MCP_OIDC_AUDIENCE` | `els-mcp` | Expected `aud` claim |
242
+ | `MCP_OIDC_DEMO_APP_SLUG` | — | Phase 3 workaround для всех OIDC-юзеров |
243
+ | `MCP_CORS_ORIGINS` | `https://claude.ai,https://chat.openai.com` | CSV allowed origins (в dev добавляется localhost) |
244
+
245
+ ## Security: PII redaction (Phase 5)
246
+
247
+ По умолчанию все ответы tools проходят через redaction pipeline до отдачи
248
+ LLM. Применяемые правила:
249
+
250
+ | Поле | Действие |
251
+ |---|---|
252
+ | `ip` (IPv4) | Last octet → `0` (`192.168.1.42` → `192.168.1.0`) |
253
+ | `ip` (IPv6) | `/64` prefix |
254
+ | `email` | → `[EMAIL_REDACTED]` |
255
+ | `jwt` / `Bearer …` | → `[JWT_REDACTED]` / `[BEARER_REDACTED]` |
256
+ | `phone` | → `[PHONE_REDACTED]` |
257
+ | credit card (Luhn-valid) | → `[CARD_REDACTED]` |
258
+ | `userAgent` | family-only (Chrome / Firefox / Safari / …) |
259
+ | `url`, `referrer` | strip query string |
260
+ | `message`, `stack` | wrap в `<untrusted>…</untrusted>` + PII regex |
261
+ | sensitive args ключи (`password`, `token`, `secret`, `email`, `ip` и т.п.) | drop value → `[REDACTED]` |
262
+
263
+ ### Prompt-injection mitigation
264
+
265
+ Все строковые поля из логов оборачиваются в `<untrusted>…</untrusted>` теги.
266
+ В description каждого tool — system note, что LLM **не** должен следовать
267
+ инструкциям из такого контента. Параллельно работает regex deny-list (см.
268
+ `src/redaction/promptInjection.ts`): при совпадении (`ignore previous
269
+ instructions`, `system:`, `jailbreak`, …) в `_meta.suspiciousContentBlocked
270
+ = true` + `_meta.suspiciousRule = <name>`.
271
+
272
+ ### Конфигурация
273
+
274
+ | ENV | Default | Описание |
275
+ |---|---|---|
276
+ | `MCP_REDACTION_ENABLED` | `true` | Включена ли редакция целиком |
277
+ | `MCP_REDACTION_FIELDS` | — | CSV whitelist полей (пусто → редактим все) |
278
+
279
+ ## Billing / quotas (Phase 5)
280
+
281
+ Tier-matrix (см. также
282
+ `todo/error-logs-service/mcp/08-billing-integration.md` §1):
283
+
284
+ | Tier | req/день | concurrent SSE | AI-explain/день |
285
+ |---|---|---|---|
286
+ | `FREE` | 1 000 | 2 | 20 |
287
+ | `STANDARD` | 50 000 | 20 | 500 |
288
+ | `PREMIUM` | 500 000 | 100 | 5 000 |
289
+ | `UNLIMITED` | ∞ | 500 | ∞ |
290
+
291
+ Перед каждым tool-call middleware (`src/middleware/withMiddleware.ts`)
292
+ проверяет дневной лимит через usage tracker. При превышении возвращается
293
+ `TIER_QUOTA_EXCEEDED` с `retryAfter` до полуночи UTC. До 110 % лимита
294
+ работает grace-zone (`_meta.overage = true`). После запроса
295
+ fire-and-forget пишутся `mcp_audit.audit_log` и `mcp_billing.usage_daily`.
296
+
297
+ ### Audit log
298
+
299
+ - Append-only, schema `mcp_audit` (отдельная БД от ELS).
300
+ - Hash-chain: `prevHash` + `rowHash` (sha256) per `appId`-partition.
301
+ - Партиционирование по месяцу (RANGE `createdAt`). См.
302
+ `prisma/migrations/init/migration.sql`.
303
+ - Запись non-blocking: если БД недоступна, tool-call продолжает работать
304
+ (silent fail с warn-логом).
305
+
306
+ ### Что НЕ пишется в audit
307
+
308
+ - Полный API-ключ (только prefix 8 символов).
309
+ - Контент логов (только метаданные tool-call).
310
+ - Полный IP (anonymized).
311
+ - Cookies, Authorization headers.
312
+
313
+ ### Конфигурация
314
+
315
+ | ENV | Default | Описание |
316
+ |---|---|---|
317
+ | `MCP_DATABASE_URL` | — | Postgres URL для audit/billing. Если пусто → no-op |
318
+ | `MCP_DEFAULT_APP_ID` | `default` | Используется в stdio-режиме |
319
+ | `MCP_DEFAULT_TIER` | `STANDARD` | Tier по умолчанию для quota-check |
320
+
321
+ ### Prisma setup
322
+
323
+ ```bash
324
+ # Сгенерировать клиент (output → node_modules/.prisma/mcp)
325
+ npm run prisma:generate
326
+
327
+ # Применить миграцию (создаёт schemas + partitioned audit table)
328
+ psql $MCP_DATABASE_URL -f prisma/migrations/init/migration.sql
329
+ ```
330
+
331
+ ## Cache & Observability (Phase 4)
332
+
333
+ ### Redis cache
334
+
335
+ Lookup-aside кэш для read-heavy эндпоинтов. TTL — по классам (см.
336
+ `src/cache/policies.ts`):
337
+
338
+ | Class | TTL | Tool(s) |
339
+ |---|---|---|
340
+ | `log_details` | 1h | `get_log_details` |
341
+ | `top_messages` | 2m | `top_error_messages` |
342
+ | `histogram` | 1m | `error_histogram` |
343
+ | `heatmap` | 5m | `error_heatmap` |
344
+ | `traffic_long` | 5m | `traffic_stats` |
345
+ | `search_recent` | 15s | `search_logs` |
346
+ | `list_apps` | 30s | `list_apps` |
347
+ | `stats_breakdown` | 2m | `error_stats_breakdown` |
348
+ | `baseline` | 5m | `baseline_compare` |
349
+ | `version_timeline` | 5m | `version_regression` |
350
+ | `grouped_errors` | 2m | `grouped_errors` |
351
+
352
+ Все cache keys обязательно tenant-prefixed:
353
+ `mcp:cache:{class}:{appSlug | k:keyPrefix}:{...}`. Это защищает от
354
+ cross-tenant data leak (см. `05-high-load.md` § 2.1).
355
+
356
+ **Graceful degradation.** Если Redis недоступен или
357
+ `MCP_CACHE_ENABLED=false` — все запросы прозрачно идут в ELS без ошибок
358
+ (cache miss во всех случаях). Sub-25ms задержка коннекта/PING не блокирует
359
+ старт процесса (`lazyConnect: true`).
360
+
361
+ **Compression.** Значения > 10 KB автоматически gzip-сжимаются (префикс
362
+ `gz:`) и расшифровываются при чтении.
363
+
364
+ ### Prometheus метрики
365
+
366
+ Endpoint: `GET /els/metrics` (Prometheus text format).
367
+
368
+ Ключевые метрики:
369
+
370
+ - `mcp_requests_total{tool,status,cached}` — counter всех tool-call'ов
371
+ - `mcp_request_duration_seconds{tool}` — histogram (12 buckets:
372
+ 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 20, 30)
373
+ - `mcp_errors_total{tool,code}`
374
+ - `mcp_cache_hits_total{tool_class}`, `mcp_cache_misses_total{tool_class}`,
375
+ `mcp_cache_hit_ratio{tool_class}`
376
+ - `mcp_els_upstream_errors_total{endpoint,status}`
377
+ - `mcp_sse_connections_active`
378
+ - `mcp_redaction_applied_total{field}`
379
+ - `mcp_billing_events_total{appSlug,tier}`
380
+
381
+ **Prometheus scrape config:**
382
+
383
+ ```yaml
384
+ scrape_configs:
385
+ - job_name: els-mcp
386
+ scrape_interval: 15s
387
+ metrics_path: /els/metrics
388
+ static_configs:
389
+ - targets: ['mcp-1.internal:3030', 'mcp-2.internal:3030']
390
+ ```
391
+
392
+ **Grafana dashboard (минимальный JSON):**
393
+
394
+ ```json
395
+ {
396
+ "title": "MCP — Overview",
397
+ "panels": [
398
+ { "title": "RPS by tool",
399
+ "targets": [{ "expr": "sum by (tool) (rate(mcp_requests_total[1m]))" }] },
400
+ { "title": "p95 latency",
401
+ "targets": [{ "expr": "histogram_quantile(0.95, sum by (tool, le) (rate(mcp_request_duration_seconds_bucket[5m])))" }] },
402
+ { "title": "Cache hit ratio",
403
+ "targets": [{ "expr": "mcp_cache_hit_ratio" }] },
404
+ { "title": "Upstream errors",
405
+ "targets": [{ "expr": "sum by (status) (rate(mcp_els_upstream_errors_total[5m]))" }] }
406
+ ]
407
+ }
408
+ ```
409
+
410
+ Полный SRE-dashboard + per-tool + per-tenant — `07-observability.md` § 4.
411
+
412
+ ### Логи (Loki shipper)
413
+
414
+ Логи pino → stderr (stdio-mode) или stdout (HTTP-mode) → Promtail → Loki.
415
+
416
+ **Promtail config (минимальный):**
417
+
418
+ ```yaml
419
+ scrape_configs:
420
+ - job_name: els-mcp
421
+ static_configs:
422
+ - targets: [localhost]
423
+ labels:
424
+ job: els-mcp
425
+ service: els-mcp
426
+ __path__: /var/log/els-mcp/*.log
427
+ pipeline_stages:
428
+ - json:
429
+ expressions:
430
+ level: level
431
+ tool: tool
432
+ appSlug: appSlug
433
+ requestId: requestId
434
+ - labels:
435
+ level:
436
+ tool:
437
+ ```
438
+
439
+ Чувствительные поля (`*.token`, `*.apiKey`, `Authorization` headers и т.д.)
440
+ автоматически замещаются на `<REDACTED>` в pino-логах (см.
441
+ `src/observability/logger.ts`).
442
+
443
+ ### Tracing (OpenTelemetry)
444
+
445
+ Опциональный — включается через `OTEL_EXPORTER_OTLP_ENDPOINT`. Если не
446
+ задан, SDK вообще не загружается (zero overhead).
447
+
448
+ Auto-instrumentation: HTTP, undici (ELS calls), ioredis, Express.
449
+
450
+ ### Health endpoints
451
+
452
+ - `GET /els/healthz` — liveness (всегда 200 если процесс жив).
453
+ - `GET /els/readyz` — readiness: проверяет Redis ping + ELS upstream
454
+ reachability. Возвращает 503 если хотя бы одна зависимость не отвечает.
455
+ Готовые handler'ы — `src/http/routes/metrics.ts` (подключаются
456
+ Phase 3 transport-роутером).
457
+
458
+ ### ENV-переменные (Phase 4)
459
+
460
+ | ENV | Default | Описание |
461
+ |---|---|---|
462
+ | `MCP_REDIS_URL` | `redis://localhost:6379` | Redis URL |
463
+ | `MCP_CACHE_ENABLED` | `true` | Включить cache layer |
464
+ | `MCP_METRICS_ENABLED` | `true` | Включить `/els/metrics` |
465
+ | `MCP_CACHE_TTL_OVERRIDE_*` | — | Override TTL per class (секунды) |
466
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | — | OTLP traces; если не задан → no-op |
467
+ | `MCP_LOG_PRETTY` | `true` в dev | Pretty-print pino |
468
+
469
+ ## Что дальше (roadmap)
470
+
471
+ Полная дорожная карта — `/todo/error-logs-service/mcp/12-migration-rollout.md`:
472
+
473
+ - **Phase 2** — оставшиеся 10 tools + Resources (5 URI) + Prompts (4 templates) ✓
474
+ - **Phase 3** — Streamable HTTP transport + OIDC через INSO Auth ✓
475
+ - **Phase 4** — Redis cache + observability (Prometheus, OTel) + LK resolver для OIDC sub ✓
476
+ - **Phase 5** — PII redaction + audit log + billing tracking ✓
477
+ - **Phase 6** — Security hardening (deny-list, STRIDE), Mistral `explain_error`
478
+ - **Phase 7** — Packaging (npm, Docker, Helm)
479
+
480
+ ## License
481
+
482
+ Internal INSO project. Not for public distribution.
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Тонкая обёртка над Prisma client, который **опционально** доступен.
3
+ *
4
+ * Подход:
5
+ * - Импорт `@prisma/client` (path: ../../node_modules/.prisma/mcp) делается
6
+ * лениво и в try/catch — если пакет не сгенерирован, audit/billing
7
+ * модули переходят в no-op режим.
8
+ * - В Phase 5 мы НЕ требуем live Postgres для тестов / dev — все
9
+ * `prisma.*`-операции в тестах подменяются через `vi.mock`.
10
+ *
11
+ * Контракт:
12
+ * - `getPrisma()` возвращает либо живой client, либо null (если MCP_DATABASE_URL
13
+ * не задан или пакет не сгенерирован).
14
+ * - Идемпотентен: повторные вызовы возвращают тот же instance.
15
+ */
16
+ import type { Logger } from 'pino';
17
+ /**
18
+ * Минимальный интерфейс клиента, который мы используем.
19
+ * Совпадает по форме с реальным Prisma client.
20
+ */
21
+ export interface McpPrismaClient {
22
+ $disconnect(): Promise<void>;
23
+ $transaction<T>(fn: (tx: McpPrismaClient) => Promise<T>): Promise<T>;
24
+ mcpAuditLog: {
25
+ findFirst(args: {
26
+ where?: Record<string, unknown>;
27
+ orderBy?: Record<string, 'asc' | 'desc'>;
28
+ select?: Record<string, boolean>;
29
+ }): Promise<{
30
+ rowHash: string;
31
+ } | null>;
32
+ create(args: {
33
+ data: Record<string, unknown>;
34
+ }): Promise<unknown>;
35
+ };
36
+ mcpUsageDaily: {
37
+ upsert(args: {
38
+ where: Record<string, unknown>;
39
+ update: Record<string, unknown>;
40
+ create: Record<string, unknown>;
41
+ }): Promise<unknown>;
42
+ aggregate(args: {
43
+ where: Record<string, unknown>;
44
+ _sum: {
45
+ count: true;
46
+ };
47
+ }): Promise<{
48
+ _sum: {
49
+ count: bigint | null;
50
+ };
51
+ }>;
52
+ };
53
+ }
54
+ export interface PrismaInitOptions {
55
+ databaseUrl?: string;
56
+ log?: Logger;
57
+ }
58
+ /**
59
+ * Lazy init. Если пакет недоступен — возвращает null, логирует warn.
60
+ */
61
+ export declare function getPrisma(opts?: PrismaInitOptions): Promise<McpPrismaClient | null>;
62
+ /**
63
+ * Для тестов — позволяет инжектировать mock-client.
64
+ */
65
+ export declare function setPrismaForTests(client: McpPrismaClient | null): void;
66
+ export declare function resetPrismaForTests(): void;
67
+ //# sourceMappingURL=prisma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/audit/prisma.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,eAAe,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACrE,WAAW,EAAE;QACX,SAAS,CAAC,IAAI,EAAE;YACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;YACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAClC,GAAG,OAAO,CAAC;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACnE,CAAC;IACF,aAAa,EAAE;QACb,MAAM,CAAC,IAAI,EAAE;YACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SACjC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACrB,SAAS,CAAC,IAAI,EAAE;YACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC/B,IAAI,EAAE;gBAAE,KAAK,EAAE,IAAI,CAAA;aAAE,CAAC;SACvB,GAAG,OAAO,CAAC;YAAE,IAAI,EAAE;gBAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;aAAE,CAAA;SAAE,CAAC,CAAC;KACjD,CAAC;CACH;AAKD,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAoC7F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI,CAGtE;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Тонкая обёртка над Prisma client, который **опционально** доступен.
3
+ *
4
+ * Подход:
5
+ * - Импорт `@prisma/client` (path: ../../node_modules/.prisma/mcp) делается
6
+ * лениво и в try/catch — если пакет не сгенерирован, audit/billing
7
+ * модули переходят в no-op режим.
8
+ * - В Phase 5 мы НЕ требуем live Postgres для тестов / dev — все
9
+ * `prisma.*`-операции в тестах подменяются через `vi.mock`.
10
+ *
11
+ * Контракт:
12
+ * - `getPrisma()` возвращает либо живой client, либо null (если MCP_DATABASE_URL
13
+ * не задан или пакет не сгенерирован).
14
+ * - Идемпотентен: повторные вызовы возвращают тот же instance.
15
+ */
16
+ let cachedClient = null;
17
+ let attempted = false;
18
+ /**
19
+ * Lazy init. Если пакет недоступен — возвращает null, логирует warn.
20
+ */
21
+ export async function getPrisma(opts = {}) {
22
+ if (cachedClient)
23
+ return cachedClient;
24
+ if (attempted)
25
+ return null;
26
+ attempted = true;
27
+ if (!opts.databaseUrl || opts.databaseUrl.length === 0) {
28
+ opts.log?.debug?.('MCP_DATABASE_URL not set; audit/billing in no-op mode');
29
+ return null;
30
+ }
31
+ try {
32
+ // Динамический импорт — не падаем, если @prisma/client не сгенерирован.
33
+ // Path указывает на custom-output из schema.prisma. Спецификатор —
34
+ // переменная (не литерал), чтобы TS не пытался резолвить путь во время
35
+ // компиляции (модуль может физически отсутствовать).
36
+ const segments = ['..', '..', 'node_modules', '.prisma', 'mcp', 'index.js'];
37
+ const modPath = segments.join('/');
38
+ const mod = (await import(modPath /* @vite-ignore */).catch(() => null));
39
+ if (!mod || typeof mod.PrismaClient !== 'function') {
40
+ opts.log?.warn?.('MCP Prisma client not generated (node_modules/.prisma/mcp). Run `npm run prisma:generate`. Audit/billing disabled.');
41
+ return null;
42
+ }
43
+ cachedClient = new mod.PrismaClient({
44
+ datasources: { db: { url: opts.databaseUrl } },
45
+ });
46
+ opts.log?.info?.('MCP Prisma client initialised');
47
+ return cachedClient;
48
+ }
49
+ catch (err) {
50
+ opts.log?.warn?.({ err }, 'Failed to init MCP Prisma client; audit/billing disabled');
51
+ return null;
52
+ }
53
+ }
54
+ /**
55
+ * Для тестов — позволяет инжектировать mock-client.
56
+ */
57
+ export function setPrismaForTests(client) {
58
+ cachedClient = client;
59
+ attempted = true;
60
+ }
61
+ export function resetPrismaForTests() {
62
+ cachedClient = null;
63
+ attempted = false;
64
+ }
65
+ //# sourceMappingURL=prisma.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma.js","sourceRoot":"","sources":["../../src/audit/prisma.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgCH,IAAI,YAAY,GAA2B,IAAI,CAAC;AAChD,IAAI,SAAS,GAAG,KAAK,CAAC;AAOtB;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAA0B,EAAE;IAC1D,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,IAAI,SAAS;QAAE,OAAO,IAAI,CAAC;IAC3B,SAAS,GAAG,IAAI,CAAC;IAEjB,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,uDAAuD,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,wEAAwE;QACxE,mEAAmE;QACnE,uEAAuE;QACvE,qDAAqD;QACrD,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAC5E,MAAM,OAAO,GAAW,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAE/D,CAAC;QAET,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;YACnD,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,CACd,oHAAoH,CACrH,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,YAAY,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC;YAClC,WAAW,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE;SAC/C,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,+BAA+B,CAAC,CAAC;QAClD,OAAO,YAAY,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,0DAA0D,CAAC,CAAC;QACtF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAA8B;IAC9D,YAAY,GAAG,MAAM,CAAC;IACtB,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,YAAY,GAAG,IAAI,CAAC;IACpB,SAAS,GAAG,KAAK,CAAC;AACpB,CAAC"}