@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.
- package/README.md +482 -0
- package/dist/audit/prisma.d.ts +67 -0
- package/dist/audit/prisma.d.ts.map +1 -0
- package/dist/audit/prisma.js +65 -0
- package/dist/audit/prisma.js.map +1 -0
- package/dist/audit/service.d.ts +72 -0
- package/dist/audit/service.d.ts.map +1 -0
- package/dist/audit/service.js +137 -0
- package/dist/audit/service.js.map +1 -0
- package/dist/billing/limits.d.ts +34 -0
- package/dist/billing/limits.d.ts.map +1 -0
- package/dist/billing/limits.js +51 -0
- package/dist/billing/limits.js.map +1 -0
- package/dist/billing/tracker.d.ts +39 -0
- package/dist/billing/tracker.d.ts.map +1 -0
- package/dist/billing/tracker.js +92 -0
- package/dist/billing/tracker.js.map +1 -0
- package/dist/cache/cachedElsClient.d.ts +71 -0
- package/dist/cache/cachedElsClient.d.ts.map +1 -0
- package/dist/cache/cachedElsClient.js +167 -0
- package/dist/cache/cachedElsClient.js.map +1 -0
- package/dist/cache/index.d.ts +10 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/policies.d.ts +60 -0
- package/dist/cache/policies.d.ts.map +1 -0
- package/dist/cache/policies.js +90 -0
- package/dist/cache/policies.js.map +1 -0
- package/dist/cache/redis.d.ts +52 -0
- package/dist/cache/redis.d.ts.map +1 -0
- package/dist/cache/redis.js +134 -0
- package/dist/cache/redis.js.map +1 -0
- package/dist/cache/types.d.ts +32 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +32 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/cache/wrapper.d.ts +38 -0
- package/dist/cache/wrapper.d.ts.map +1 -0
- package/dist/cache/wrapper.js +109 -0
- package/dist/cache/wrapper.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +86 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +105 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +211 -0
- package/dist/config.js.map +1 -0
- package/dist/elsClient.d.ts +137 -0
- package/dist/elsClient.d.ts.map +1 -0
- package/dist/elsClient.js +285 -0
- package/dist/elsClient.js.map +1 -0
- package/dist/http/app.d.ts +40 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +135 -0
- package/dist/http/app.js.map +1 -0
- package/dist/http/jwks.d.ts +8 -0
- package/dist/http/jwks.d.ts.map +1 -0
- package/dist/http/jwks.js +34 -0
- package/dist/http/jwks.js.map +1 -0
- package/dist/http/middleware/auth.d.ts +11 -0
- package/dist/http/middleware/auth.d.ts.map +1 -0
- package/dist/http/middleware/auth.js +225 -0
- package/dist/http/middleware/auth.js.map +1 -0
- package/dist/http/middleware/dcrRateLimit.d.ts +29 -0
- package/dist/http/middleware/dcrRateLimit.d.ts.map +1 -0
- package/dist/http/middleware/dcrRateLimit.js +59 -0
- package/dist/http/middleware/dcrRateLimit.js.map +1 -0
- package/dist/http/middleware/errorHandler.d.ts +12 -0
- package/dist/http/middleware/errorHandler.d.ts.map +1 -0
- package/dist/http/middleware/errorHandler.js +26 -0
- package/dist/http/middleware/errorHandler.js.map +1 -0
- package/dist/http/middleware/originGuard.d.ts +28 -0
- package/dist/http/middleware/originGuard.d.ts.map +1 -0
- package/dist/http/middleware/originGuard.js +55 -0
- package/dist/http/middleware/originGuard.js.map +1 -0
- package/dist/http/middleware/requestId.d.ts +19 -0
- package/dist/http/middleware/requestId.d.ts.map +1 -0
- package/dist/http/middleware/requestId.js +23 -0
- package/dist/http/middleware/requestId.js.map +1 -0
- package/dist/http/routes/health.d.ts +24 -0
- package/dist/http/routes/health.d.ts.map +1 -0
- package/dist/http/routes/health.js +73 -0
- package/dist/http/routes/health.js.map +1 -0
- package/dist/http/routes/metrics.d.ts +18 -0
- package/dist/http/routes/metrics.d.ts.map +1 -0
- package/dist/http/routes/metrics.js +42 -0
- package/dist/http/routes/metrics.js.map +1 -0
- package/dist/http/routes/wellKnown.d.ts +15 -0
- package/dist/http/routes/wellKnown.d.ts.map +1 -0
- package/dist/http/routes/wellKnown.js +43 -0
- package/dist/http/routes/wellKnown.js.map +1 -0
- package/dist/http/types.d.ts +40 -0
- package/dist/http/types.d.ts.map +1 -0
- package/dist/http/types.js +9 -0
- package/dist/http/types.js.map +1 -0
- package/dist/instrumentation.d.ts +22 -0
- package/dist/instrumentation.d.ts.map +1 -0
- package/dist/instrumentation.js +38 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/lib/cursor.d.ts +22 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +95 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/errors.d.ts +49 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +83 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/responseFormat.d.ts +14 -0
- package/dist/lib/responseFormat.d.ts.map +1 -0
- package/dist/lib/responseFormat.js +74 -0
- package/dist/lib/responseFormat.js.map +1 -0
- package/dist/middleware/withMiddleware.d.ts +53 -0
- package/dist/middleware/withMiddleware.d.ts.map +1 -0
- package/dist/middleware/withMiddleware.js +190 -0
- package/dist/middleware/withMiddleware.js.map +1 -0
- package/dist/observability/health.d.ts +51 -0
- package/dist/observability/health.d.ts.map +1 -0
- package/dist/observability/health.js +77 -0
- package/dist/observability/health.js.map +1 -0
- package/dist/observability/index.d.ts +8 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +5 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/logger.d.ts +45 -0
- package/dist/observability/logger.d.ts.map +1 -0
- package/dist/observability/logger.js +75 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/observability/metrics.d.ts +49 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +184 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/tracing.d.ts +28 -0
- package/dist/observability/tracing.d.ts.map +1 -0
- package/dist/observability/tracing.js +56 -0
- package/dist/observability/tracing.js.map +1 -0
- package/dist/prompts/index.d.ts +20 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +202 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/redaction/argsRedactor.d.ts +22 -0
- package/dist/redaction/argsRedactor.d.ts.map +1 -0
- package/dist/redaction/argsRedactor.js +97 -0
- package/dist/redaction/argsRedactor.js.map +1 -0
- package/dist/redaction/fields.d.ts +64 -0
- package/dist/redaction/fields.d.ts.map +1 -0
- package/dist/redaction/fields.js +155 -0
- package/dist/redaction/fields.js.map +1 -0
- package/dist/redaction/index.d.ts +52 -0
- package/dist/redaction/index.d.ts.map +1 -0
- package/dist/redaction/index.js +160 -0
- package/dist/redaction/index.js.map +1 -0
- package/dist/redaction/promptInjection.d.ts +32 -0
- package/dist/redaction/promptInjection.d.ts.map +1 -0
- package/dist/redaction/promptInjection.js +68 -0
- package/dist/redaction/promptInjection.js.map +1 -0
- package/dist/redaction/url.d.ts +8 -0
- package/dist/redaction/url.d.ts.map +1 -0
- package/dist/redaction/url.js +26 -0
- package/dist/redaction/url.js.map +1 -0
- package/dist/redaction/userAgent.d.ts +9 -0
- package/dist/redaction/userAgent.d.ts.map +1 -0
- package/dist/redaction/userAgent.js +39 -0
- package/dist/redaction/userAgent.js.map +1 -0
- package/dist/resources/index.d.ts +24 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +150 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/server.d.ts +37 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +35 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/baselineCompare.d.ts +36 -0
- package/dist/tools/baselineCompare.d.ts.map +1 -0
- package/dist/tools/baselineCompare.js +69 -0
- package/dist/tools/baselineCompare.js.map +1 -0
- package/dist/tools/errorHeatmap.d.ts +40 -0
- package/dist/tools/errorHeatmap.d.ts.map +1 -0
- package/dist/tools/errorHeatmap.js +69 -0
- package/dist/tools/errorHeatmap.js.map +1 -0
- package/dist/tools/errorHistogram.d.ts +39 -0
- package/dist/tools/errorHistogram.d.ts.map +1 -0
- package/dist/tools/errorHistogram.js +61 -0
- package/dist/tools/errorHistogram.js.map +1 -0
- package/dist/tools/errorStatsBreakdown.d.ts +43 -0
- package/dist/tools/errorStatsBreakdown.d.ts.map +1 -0
- package/dist/tools/errorStatsBreakdown.js +77 -0
- package/dist/tools/errorStatsBreakdown.js.map +1 -0
- package/dist/tools/errorsInSession.d.ts +44 -0
- package/dist/tools/errorsInSession.d.ts.map +1 -0
- package/dist/tools/errorsInSession.js +91 -0
- package/dist/tools/errorsInSession.js.map +1 -0
- package/dist/tools/explainError.d.ts +35 -0
- package/dist/tools/explainError.d.ts.map +1 -0
- package/dist/tools/explainError.js +98 -0
- package/dist/tools/explainError.js.map +1 -0
- package/dist/tools/findCorrelatedErrors.d.ts +43 -0
- package/dist/tools/findCorrelatedErrors.d.ts.map +1 -0
- package/dist/tools/findCorrelatedErrors.js +59 -0
- package/dist/tools/findCorrelatedErrors.js.map +1 -0
- package/dist/tools/findSimilarErrors.d.ts +44 -0
- package/dist/tools/findSimilarErrors.d.ts.map +1 -0
- package/dist/tools/findSimilarErrors.js +59 -0
- package/dist/tools/findSimilarErrors.js.map +1 -0
- package/dist/tools/getLogDetails.d.ts +30 -0
- package/dist/tools/getLogDetails.d.ts.map +1 -0
- package/dist/tools/getLogDetails.js +49 -0
- package/dist/tools/getLogDetails.js.map +1 -0
- package/dist/tools/groupedErrors.d.ts +46 -0
- package/dist/tools/groupedErrors.d.ts.map +1 -0
- package/dist/tools/groupedErrors.js +71 -0
- package/dist/tools/groupedErrors.js.map +1 -0
- package/dist/tools/impactAnalysis.d.ts +42 -0
- package/dist/tools/impactAnalysis.d.ts.map +1 -0
- package/dist/tools/impactAnalysis.js +61 -0
- package/dist/tools/impactAnalysis.js.map +1 -0
- package/dist/tools/index.d.ts +41 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +83 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/listApps.d.ts +27 -0
- package/dist/tools/listApps.d.ts.map +1 -0
- package/dist/tools/listApps.js +78 -0
- package/dist/tools/listApps.js.map +1 -0
- package/dist/tools/queryLogsJql.d.ts +44 -0
- package/dist/tools/queryLogsJql.d.ts.map +1 -0
- package/dist/tools/queryLogsJql.js +107 -0
- package/dist/tools/queryLogsJql.js.map +1 -0
- package/dist/tools/searchLogs.d.ts +60 -0
- package/dist/tools/searchLogs.d.ts.map +1 -0
- package/dist/tools/searchLogs.js +127 -0
- package/dist/tools/searchLogs.js.map +1 -0
- package/dist/tools/topErrorMessages.d.ts +42 -0
- package/dist/tools/topErrorMessages.d.ts.map +1 -0
- package/dist/tools/topErrorMessages.js +63 -0
- package/dist/tools/topErrorMessages.js.map +1 -0
- package/dist/tools/trafficStats.d.ts +39 -0
- package/dist/tools/trafficStats.d.ts.map +1 -0
- package/dist/tools/trafficStats.js +57 -0
- package/dist/tools/trafficStats.js.map +1 -0
- package/dist/tools/triageRecentCritical.d.ts +26 -0
- package/dist/tools/triageRecentCritical.d.ts.map +1 -0
- package/dist/tools/triageRecentCritical.js +81 -0
- package/dist/tools/triageRecentCritical.js.map +1 -0
- package/dist/tools/versionRegression.d.ts +29 -0
- package/dist/tools/versionRegression.d.ts.map +1 -0
- package/dist/tools/versionRegression.js +80 -0
- package/dist/tools/versionRegression.js.map +1 -0
- package/dist/transports/http-server.d.ts +25 -0
- package/dist/transports/http-server.d.ts.map +1 -0
- package/dist/transports/http-server.js +84 -0
- package/dist/transports/http-server.js.map +1 -0
- package/dist/transports/http.d.ts +71 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +315 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/stdio.d.ts +13 -0
- package/dist/transports/stdio.d.ts.map +1 -0
- package/dist/transports/stdio.js +16 -0
- package/dist/transports/stdio.js.map +1 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- 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"}
|