@tuteliq/mcp 2.2.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/LICENSE +21 -0
- package/README.md +274 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1036 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1036 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
5
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
7
|
+
const sdk_1 = require("@tuteliq/sdk");
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
// Initialize Tuteliq client
|
|
10
|
+
const apiKey = process.env.TUTELIQ_API_KEY;
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
console.error('Error: TUTELIQ_API_KEY environment variable is required');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const client = new sdk_1.Tuteliq(apiKey);
|
|
16
|
+
// Severity emoji mapping
|
|
17
|
+
const severityEmoji = {
|
|
18
|
+
low: '🟡',
|
|
19
|
+
medium: '🟠',
|
|
20
|
+
high: '🔴',
|
|
21
|
+
critical: '⛔',
|
|
22
|
+
};
|
|
23
|
+
const riskEmoji = {
|
|
24
|
+
safe: '✅',
|
|
25
|
+
none: '✅',
|
|
26
|
+
low: '🟡',
|
|
27
|
+
medium: '🟠',
|
|
28
|
+
high: '🔴',
|
|
29
|
+
critical: '⛔',
|
|
30
|
+
};
|
|
31
|
+
const trendEmoji = {
|
|
32
|
+
improving: '📈',
|
|
33
|
+
stable: '➡️',
|
|
34
|
+
worsening: '📉',
|
|
35
|
+
};
|
|
36
|
+
// Tool definitions
|
|
37
|
+
const tools = [
|
|
38
|
+
// =========================================================================
|
|
39
|
+
// Safety Detection Tools
|
|
40
|
+
// =========================================================================
|
|
41
|
+
{
|
|
42
|
+
name: 'detect_bullying',
|
|
43
|
+
description: 'Analyze text content to detect bullying, harassment, or harmful language. Returns severity, type of bullying, confidence score, and recommended actions.',
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
content: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'The text content to analyze for bullying',
|
|
50
|
+
},
|
|
51
|
+
context: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
description: 'Optional context for better analysis',
|
|
54
|
+
properties: {
|
|
55
|
+
language: { type: 'string' },
|
|
56
|
+
ageGroup: { type: 'string' },
|
|
57
|
+
relationship: { type: 'string' },
|
|
58
|
+
platform: { type: 'string' },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
required: ['content'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'detect_grooming',
|
|
67
|
+
description: 'Analyze a conversation for grooming patterns and predatory behavior. Identifies manipulation tactics, boundary violations, and isolation attempts.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
messages: {
|
|
72
|
+
type: 'array',
|
|
73
|
+
description: 'Array of messages in the conversation',
|
|
74
|
+
items: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
role: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
enum: ['adult', 'child', 'unknown'],
|
|
80
|
+
description: 'Role of the message sender',
|
|
81
|
+
},
|
|
82
|
+
content: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
description: 'Message content',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ['role', 'content'],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
childAge: {
|
|
91
|
+
type: 'number',
|
|
92
|
+
description: 'Age of the child in the conversation',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
required: ['messages'],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'detect_unsafe',
|
|
100
|
+
description: 'Detect unsafe content including self-harm, violence, drugs, explicit material, or other harmful content categories.',
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {
|
|
104
|
+
content: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: 'The text content to analyze for unsafe content',
|
|
107
|
+
},
|
|
108
|
+
context: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
description: 'Optional context for better analysis',
|
|
111
|
+
properties: {
|
|
112
|
+
language: { type: 'string' },
|
|
113
|
+
ageGroup: { type: 'string' },
|
|
114
|
+
platform: { type: 'string' },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
required: ['content'],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'analyze',
|
|
123
|
+
description: 'Quick comprehensive safety analysis that checks for both bullying and unsafe content. Best for general content screening.',
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: 'object',
|
|
126
|
+
properties: {
|
|
127
|
+
content: {
|
|
128
|
+
type: 'string',
|
|
129
|
+
description: 'The text content to analyze',
|
|
130
|
+
},
|
|
131
|
+
include: {
|
|
132
|
+
type: 'array',
|
|
133
|
+
items: { type: 'string', enum: ['bullying', 'unsafe'] },
|
|
134
|
+
description: 'Which checks to run (default: both)',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
required: ['content'],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'analyze_emotions',
|
|
142
|
+
description: 'Analyze emotional content and mental state indicators. Identifies dominant emotions, trends, and provides follow-up recommendations.',
|
|
143
|
+
inputSchema: {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
content: {
|
|
147
|
+
type: 'string',
|
|
148
|
+
description: 'The text content to analyze for emotions',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
required: ['content'],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: 'get_action_plan',
|
|
156
|
+
description: 'Generate age-appropriate guidance and action steps for handling a safety situation. Tailored for children, parents, or educators.',
|
|
157
|
+
inputSchema: {
|
|
158
|
+
type: 'object',
|
|
159
|
+
properties: {
|
|
160
|
+
situation: {
|
|
161
|
+
type: 'string',
|
|
162
|
+
description: 'Description of the situation needing guidance',
|
|
163
|
+
},
|
|
164
|
+
childAge: {
|
|
165
|
+
type: 'number',
|
|
166
|
+
description: 'Age of the child involved',
|
|
167
|
+
},
|
|
168
|
+
audience: {
|
|
169
|
+
type: 'string',
|
|
170
|
+
enum: ['child', 'parent', 'educator', 'platform'],
|
|
171
|
+
description: 'Who the guidance is for (default: parent)',
|
|
172
|
+
},
|
|
173
|
+
severity: {
|
|
174
|
+
type: 'string',
|
|
175
|
+
enum: ['low', 'medium', 'high', 'critical'],
|
|
176
|
+
description: 'Severity of the situation',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
required: ['situation'],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'generate_report',
|
|
184
|
+
description: 'Generate a comprehensive incident report from a conversation. Includes summary, risk level, and recommended next steps.',
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: 'object',
|
|
187
|
+
properties: {
|
|
188
|
+
messages: {
|
|
189
|
+
type: 'array',
|
|
190
|
+
description: 'Array of messages in the incident',
|
|
191
|
+
items: {
|
|
192
|
+
type: 'object',
|
|
193
|
+
properties: {
|
|
194
|
+
sender: { type: 'string', description: 'Name/ID of sender' },
|
|
195
|
+
content: { type: 'string', description: 'Message content' },
|
|
196
|
+
},
|
|
197
|
+
required: ['sender', 'content'],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
childAge: {
|
|
201
|
+
type: 'number',
|
|
202
|
+
description: 'Age of the child involved',
|
|
203
|
+
},
|
|
204
|
+
incidentType: {
|
|
205
|
+
type: 'string',
|
|
206
|
+
description: 'Type of incident (e.g., bullying, grooming)',
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
required: ['messages'],
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
// =========================================================================
|
|
213
|
+
// Voice & Image Analysis Tools
|
|
214
|
+
// =========================================================================
|
|
215
|
+
{
|
|
216
|
+
name: 'analyze_voice',
|
|
217
|
+
description: 'Analyze an audio file for safety concerns. Transcribes the audio via Whisper, then runs safety analysis on the transcript. Returns timestamped segments for incident reports. Supports mp3, wav, m4a, ogg, flac, webm, mp4.',
|
|
218
|
+
inputSchema: {
|
|
219
|
+
type: 'object',
|
|
220
|
+
properties: {
|
|
221
|
+
file_path: {
|
|
222
|
+
type: 'string',
|
|
223
|
+
description: 'Absolute path to the audio file on disk',
|
|
224
|
+
},
|
|
225
|
+
analysis_type: {
|
|
226
|
+
type: 'string',
|
|
227
|
+
enum: ['bullying', 'unsafe', 'grooming', 'emotions', 'all'],
|
|
228
|
+
description: 'Type of analysis to run on the transcript (default: all)',
|
|
229
|
+
},
|
|
230
|
+
child_age: {
|
|
231
|
+
type: 'number',
|
|
232
|
+
description: 'Child age (used for grooming analysis)',
|
|
233
|
+
},
|
|
234
|
+
language: {
|
|
235
|
+
type: 'string',
|
|
236
|
+
description: 'Language hint for transcription (e.g., "en", "es")',
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
required: ['file_path'],
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'analyze_image',
|
|
244
|
+
description: 'Analyze an image for visual safety concerns and OCR text extraction. Uses vision AI for content classification, then runs safety analysis on any extracted text. Supports png, jpg, jpeg, gif, webp.',
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
properties: {
|
|
248
|
+
file_path: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
description: 'Absolute path to the image file on disk',
|
|
251
|
+
},
|
|
252
|
+
analysis_type: {
|
|
253
|
+
type: 'string',
|
|
254
|
+
enum: ['bullying', 'unsafe', 'emotions', 'all'],
|
|
255
|
+
description: 'Type of analysis to run on extracted text (default: all)',
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
required: ['file_path'],
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
// =========================================================================
|
|
262
|
+
// Webhook Management Tools
|
|
263
|
+
// =========================================================================
|
|
264
|
+
{
|
|
265
|
+
name: 'list_webhooks',
|
|
266
|
+
description: 'List all webhooks configured for your account.',
|
|
267
|
+
inputSchema: {
|
|
268
|
+
type: 'object',
|
|
269
|
+
properties: {},
|
|
270
|
+
required: [],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: 'create_webhook',
|
|
275
|
+
description: 'Create a new webhook endpoint. The returned secret is only shown once — store it securely for signature verification.',
|
|
276
|
+
inputSchema: {
|
|
277
|
+
type: 'object',
|
|
278
|
+
properties: {
|
|
279
|
+
name: { type: 'string', description: 'Display name for the webhook' },
|
|
280
|
+
url: { type: 'string', description: 'HTTPS URL to receive webhook payloads' },
|
|
281
|
+
events: {
|
|
282
|
+
type: 'array',
|
|
283
|
+
items: { type: 'string' },
|
|
284
|
+
description: 'Event types to subscribe to (e.g., incident.critical, grooming.detected, unsafe.detected, bullying.detected)',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
required: ['name', 'url', 'events'],
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: 'update_webhook',
|
|
292
|
+
description: 'Update an existing webhook configuration.',
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: 'object',
|
|
295
|
+
properties: {
|
|
296
|
+
id: { type: 'string', description: 'Webhook ID' },
|
|
297
|
+
name: { type: 'string', description: 'New display name' },
|
|
298
|
+
url: { type: 'string', description: 'New HTTPS URL' },
|
|
299
|
+
events: { type: 'array', items: { type: 'string' }, description: 'New event subscriptions' },
|
|
300
|
+
is_active: { type: 'boolean', description: 'Enable or disable the webhook' },
|
|
301
|
+
},
|
|
302
|
+
required: ['id'],
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: 'delete_webhook',
|
|
307
|
+
description: 'Permanently delete a webhook.',
|
|
308
|
+
inputSchema: {
|
|
309
|
+
type: 'object',
|
|
310
|
+
properties: {
|
|
311
|
+
id: { type: 'string', description: 'Webhook ID to delete' },
|
|
312
|
+
},
|
|
313
|
+
required: ['id'],
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'test_webhook',
|
|
318
|
+
description: 'Send a test payload to a webhook to verify it is working correctly.',
|
|
319
|
+
inputSchema: {
|
|
320
|
+
type: 'object',
|
|
321
|
+
properties: {
|
|
322
|
+
id: { type: 'string', description: 'Webhook ID to test' },
|
|
323
|
+
},
|
|
324
|
+
required: ['id'],
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: 'regenerate_webhook_secret',
|
|
329
|
+
description: 'Regenerate a webhook signing secret. The old secret is immediately invalidated.',
|
|
330
|
+
inputSchema: {
|
|
331
|
+
type: 'object',
|
|
332
|
+
properties: {
|
|
333
|
+
id: { type: 'string', description: 'Webhook ID' },
|
|
334
|
+
},
|
|
335
|
+
required: ['id'],
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
// =========================================================================
|
|
339
|
+
// Pricing Tools
|
|
340
|
+
// =========================================================================
|
|
341
|
+
{
|
|
342
|
+
name: 'get_pricing',
|
|
343
|
+
description: 'Get available pricing plans for Tuteliq.',
|
|
344
|
+
inputSchema: {
|
|
345
|
+
type: 'object',
|
|
346
|
+
properties: {},
|
|
347
|
+
required: [],
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: 'get_pricing_details',
|
|
352
|
+
description: 'Get detailed pricing plans with monthly/yearly prices, API call limits, and feature lists.',
|
|
353
|
+
inputSchema: {
|
|
354
|
+
type: 'object',
|
|
355
|
+
properties: {},
|
|
356
|
+
required: [],
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
// =========================================================================
|
|
360
|
+
// Usage & Billing Tools
|
|
361
|
+
// =========================================================================
|
|
362
|
+
{
|
|
363
|
+
name: 'get_usage_history',
|
|
364
|
+
description: 'Get daily usage history for the past N days, showing request counts per day.',
|
|
365
|
+
inputSchema: {
|
|
366
|
+
type: 'object',
|
|
367
|
+
properties: {
|
|
368
|
+
days: {
|
|
369
|
+
type: 'number',
|
|
370
|
+
description: 'Number of days to retrieve (1-30, default: 7)',
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
required: [],
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: 'get_usage_by_tool',
|
|
378
|
+
description: 'Get usage broken down by tool/endpoint for a specific date.',
|
|
379
|
+
inputSchema: {
|
|
380
|
+
type: 'object',
|
|
381
|
+
properties: {
|
|
382
|
+
date: {
|
|
383
|
+
type: 'string',
|
|
384
|
+
description: 'Date in YYYY-MM-DD format (default: today)',
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
required: [],
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: 'get_usage_monthly',
|
|
392
|
+
description: 'Get monthly usage summary including billing period, limits, rate limits, and upgrade recommendations.',
|
|
393
|
+
inputSchema: {
|
|
394
|
+
type: 'object',
|
|
395
|
+
properties: {},
|
|
396
|
+
required: [],
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
// =========================================================================
|
|
400
|
+
// GDPR Account Tools
|
|
401
|
+
// =========================================================================
|
|
402
|
+
{
|
|
403
|
+
name: 'delete_account_data',
|
|
404
|
+
description: 'Delete all account data (GDPR Article 17 — Right to Erasure). Permanently removes all stored user data.',
|
|
405
|
+
inputSchema: {
|
|
406
|
+
type: 'object',
|
|
407
|
+
properties: {},
|
|
408
|
+
required: [],
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: 'export_account_data',
|
|
413
|
+
description: 'Export all account data as JSON (GDPR Article 20 — Right to Data Portability).',
|
|
414
|
+
inputSchema: {
|
|
415
|
+
type: 'object',
|
|
416
|
+
properties: {},
|
|
417
|
+
required: [],
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: 'record_consent',
|
|
422
|
+
description: 'Record user consent for a specific data processing purpose (GDPR Article 7).',
|
|
423
|
+
inputSchema: {
|
|
424
|
+
type: 'object',
|
|
425
|
+
properties: {
|
|
426
|
+
consent_type: {
|
|
427
|
+
type: 'string',
|
|
428
|
+
enum: ['data_processing', 'analytics', 'marketing', 'third_party_sharing', 'child_safety_monitoring'],
|
|
429
|
+
description: 'Type of consent to record',
|
|
430
|
+
},
|
|
431
|
+
version: {
|
|
432
|
+
type: 'string',
|
|
433
|
+
description: 'Policy version the user is consenting to',
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
required: ['consent_type', 'version'],
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'get_consent_status',
|
|
441
|
+
description: 'Get current consent status for all or a specific consent type (GDPR Article 7).',
|
|
442
|
+
inputSchema: {
|
|
443
|
+
type: 'object',
|
|
444
|
+
properties: {
|
|
445
|
+
type: {
|
|
446
|
+
type: 'string',
|
|
447
|
+
enum: ['data_processing', 'analytics', 'marketing', 'third_party_sharing', 'child_safety_monitoring'],
|
|
448
|
+
description: 'Optional: filter by consent type',
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
required: [],
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
name: 'withdraw_consent',
|
|
456
|
+
description: 'Withdraw a previously granted consent (GDPR Article 7.3).',
|
|
457
|
+
inputSchema: {
|
|
458
|
+
type: 'object',
|
|
459
|
+
properties: {
|
|
460
|
+
type: {
|
|
461
|
+
type: 'string',
|
|
462
|
+
enum: ['data_processing', 'analytics', 'marketing', 'third_party_sharing', 'child_safety_monitoring'],
|
|
463
|
+
description: 'Type of consent to withdraw',
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
required: ['type'],
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
name: 'rectify_data',
|
|
471
|
+
description: 'Rectify (correct) user data in a specific collection (GDPR Article 16 — Right to Rectification).',
|
|
472
|
+
inputSchema: {
|
|
473
|
+
type: 'object',
|
|
474
|
+
properties: {
|
|
475
|
+
collection: {
|
|
476
|
+
type: 'string',
|
|
477
|
+
description: 'Firestore collection name',
|
|
478
|
+
},
|
|
479
|
+
document_id: {
|
|
480
|
+
type: 'string',
|
|
481
|
+
description: 'Document ID to rectify',
|
|
482
|
+
},
|
|
483
|
+
fields: {
|
|
484
|
+
type: 'object',
|
|
485
|
+
description: 'Fields to update (only allowlisted fields accepted)',
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
required: ['collection', 'document_id', 'fields'],
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: 'get_audit_logs',
|
|
493
|
+
description: 'Get audit trail of all data operations (GDPR Article 15 — Right of Access).',
|
|
494
|
+
inputSchema: {
|
|
495
|
+
type: 'object',
|
|
496
|
+
properties: {
|
|
497
|
+
action: {
|
|
498
|
+
type: 'string',
|
|
499
|
+
enum: ['data_access', 'data_export', 'data_deletion', 'data_rectification', 'consent_granted', 'consent_withdrawn', 'breach_notification'],
|
|
500
|
+
description: 'Optional: filter by action type',
|
|
501
|
+
},
|
|
502
|
+
limit: {
|
|
503
|
+
type: 'number',
|
|
504
|
+
description: 'Maximum number of results',
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
required: [],
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
// =========================================================================
|
|
511
|
+
// Breach Management Tools
|
|
512
|
+
// =========================================================================
|
|
513
|
+
{
|
|
514
|
+
name: 'log_breach',
|
|
515
|
+
description: 'Log a new data breach (GDPR Article 33/34). Records breach details and starts the 72-hour notification clock.',
|
|
516
|
+
inputSchema: {
|
|
517
|
+
type: 'object',
|
|
518
|
+
properties: {
|
|
519
|
+
title: { type: 'string', description: 'Brief title of the breach' },
|
|
520
|
+
description: { type: 'string', description: 'Detailed description of what happened' },
|
|
521
|
+
severity: { type: 'string', enum: ['low', 'medium', 'high', 'critical'], description: 'Breach severity' },
|
|
522
|
+
affected_user_ids: { type: 'array', items: { type: 'string' }, description: 'List of affected user IDs' },
|
|
523
|
+
data_categories: { type: 'array', items: { type: 'string' }, description: 'Categories of data affected (e.g. email, name, address)' },
|
|
524
|
+
reported_by: { type: 'string', description: 'Who reported the breach' },
|
|
525
|
+
},
|
|
526
|
+
required: ['title', 'description', 'severity', 'affected_user_ids', 'data_categories', 'reported_by'],
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
name: 'list_breaches',
|
|
531
|
+
description: 'List all data breaches, optionally filtered by status.',
|
|
532
|
+
inputSchema: {
|
|
533
|
+
type: 'object',
|
|
534
|
+
properties: {
|
|
535
|
+
status: { type: 'string', enum: ['detected', 'investigating', 'contained', 'reported', 'resolved'], description: 'Filter by breach status' },
|
|
536
|
+
limit: { type: 'number', description: 'Maximum number of results' },
|
|
537
|
+
},
|
|
538
|
+
required: [],
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
name: 'get_breach',
|
|
543
|
+
description: 'Get details of a specific data breach by ID.',
|
|
544
|
+
inputSchema: {
|
|
545
|
+
type: 'object',
|
|
546
|
+
properties: {
|
|
547
|
+
id: { type: 'string', description: 'Breach ID' },
|
|
548
|
+
},
|
|
549
|
+
required: ['id'],
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
name: 'update_breach_status',
|
|
554
|
+
description: 'Update a breach status and notification progress.',
|
|
555
|
+
inputSchema: {
|
|
556
|
+
type: 'object',
|
|
557
|
+
properties: {
|
|
558
|
+
id: { type: 'string', description: 'Breach ID' },
|
|
559
|
+
status: { type: 'string', enum: ['detected', 'investigating', 'contained', 'reported', 'resolved'], description: 'New breach status' },
|
|
560
|
+
notification_status: { type: 'string', enum: ['pending', 'users_notified', 'dpa_notified', 'completed'], description: 'Notification progress status' },
|
|
561
|
+
notes: { type: 'string', description: 'Additional notes about the update' },
|
|
562
|
+
},
|
|
563
|
+
required: ['id', 'status'],
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
];
|
|
567
|
+
// Create MCP server
|
|
568
|
+
const server = new index_js_1.Server({
|
|
569
|
+
name: 'tuteliq-mcp',
|
|
570
|
+
version: '2.1.0',
|
|
571
|
+
}, {
|
|
572
|
+
capabilities: {
|
|
573
|
+
tools: {},
|
|
574
|
+
},
|
|
575
|
+
});
|
|
576
|
+
// List tools handler
|
|
577
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
578
|
+
return { tools };
|
|
579
|
+
});
|
|
580
|
+
// Helper to extract filename from path
|
|
581
|
+
function filenameFromPath(filePath) {
|
|
582
|
+
return filePath.split('/').pop() || filePath;
|
|
583
|
+
}
|
|
584
|
+
// Call tool handler
|
|
585
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
586
|
+
const { name, arguments: args = {} } = request.params;
|
|
587
|
+
try {
|
|
588
|
+
switch (name) {
|
|
589
|
+
// =====================================================================
|
|
590
|
+
// Safety Detection
|
|
591
|
+
// =====================================================================
|
|
592
|
+
case 'detect_bullying': {
|
|
593
|
+
const result = await client.detectBullying({
|
|
594
|
+
content: args.content,
|
|
595
|
+
context: args.context,
|
|
596
|
+
});
|
|
597
|
+
const emoji = severityEmoji[result.severity] || '⚪';
|
|
598
|
+
const response = `## ${result.is_bullying ? '⚠️ Bullying Detected' : '✅ No Bullying Detected'}
|
|
599
|
+
|
|
600
|
+
**Severity:** ${emoji} ${result.severity.charAt(0).toUpperCase() + result.severity.slice(1)}
|
|
601
|
+
**Confidence:** ${(result.confidence * 100).toFixed(0)}%
|
|
602
|
+
**Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
|
|
603
|
+
|
|
604
|
+
${result.is_bullying ? `**Types:** ${result.bullying_type.join(', ')}` : ''}
|
|
605
|
+
|
|
606
|
+
### Rationale
|
|
607
|
+
${result.rationale}
|
|
608
|
+
|
|
609
|
+
### Recommended Action
|
|
610
|
+
\`${result.recommended_action}\``;
|
|
611
|
+
return { content: [{ type: 'text', text: response }] };
|
|
612
|
+
}
|
|
613
|
+
case 'detect_grooming': {
|
|
614
|
+
const messages = args.messages.map((m) => ({
|
|
615
|
+
role: m.role,
|
|
616
|
+
content: m.content,
|
|
617
|
+
}));
|
|
618
|
+
const result = await client.detectGrooming({
|
|
619
|
+
messages,
|
|
620
|
+
childAge: args.childAge,
|
|
621
|
+
});
|
|
622
|
+
const emoji = riskEmoji[result.grooming_risk] || '⚪';
|
|
623
|
+
const response = `## ${result.grooming_risk === 'none' ? '✅ No Grooming Detected' : '⚠️ Grooming Risk Detected'}
|
|
624
|
+
|
|
625
|
+
**Risk Level:** ${emoji} ${result.grooming_risk.charAt(0).toUpperCase() + result.grooming_risk.slice(1)}
|
|
626
|
+
**Confidence:** ${(result.confidence * 100).toFixed(0)}%
|
|
627
|
+
**Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
|
|
628
|
+
|
|
629
|
+
${result.flags.length > 0 ? `**Warning Flags:**\n${result.flags.map(f => `- 🚩 ${f}`).join('\n')}` : ''}
|
|
630
|
+
|
|
631
|
+
### Rationale
|
|
632
|
+
${result.rationale}
|
|
633
|
+
|
|
634
|
+
### Recommended Action
|
|
635
|
+
\`${result.recommended_action}\``;
|
|
636
|
+
return { content: [{ type: 'text', text: response }] };
|
|
637
|
+
}
|
|
638
|
+
case 'detect_unsafe': {
|
|
639
|
+
const result = await client.detectUnsafe({
|
|
640
|
+
content: args.content,
|
|
641
|
+
context: args.context,
|
|
642
|
+
});
|
|
643
|
+
const emoji = severityEmoji[result.severity] || '⚪';
|
|
644
|
+
const response = `## ${result.unsafe ? '⚠️ Unsafe Content Detected' : '✅ Content is Safe'}
|
|
645
|
+
|
|
646
|
+
**Severity:** ${emoji} ${result.severity.charAt(0).toUpperCase() + result.severity.slice(1)}
|
|
647
|
+
**Confidence:** ${(result.confidence * 100).toFixed(0)}%
|
|
648
|
+
**Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
|
|
649
|
+
|
|
650
|
+
${result.unsafe ? `**Categories:**\n${result.categories.map(c => `- ⚠️ ${c}`).join('\n')}` : ''}
|
|
651
|
+
|
|
652
|
+
### Rationale
|
|
653
|
+
${result.rationale}
|
|
654
|
+
|
|
655
|
+
### Recommended Action
|
|
656
|
+
\`${result.recommended_action}\``;
|
|
657
|
+
return { content: [{ type: 'text', text: response }] };
|
|
658
|
+
}
|
|
659
|
+
case 'analyze': {
|
|
660
|
+
const result = await client.analyze({
|
|
661
|
+
content: args.content,
|
|
662
|
+
include: args.include,
|
|
663
|
+
});
|
|
664
|
+
const emoji = riskEmoji[result.risk_level] || '⚪';
|
|
665
|
+
const response = `## Safety Analysis Results
|
|
666
|
+
|
|
667
|
+
**Overall Risk:** ${emoji} ${result.risk_level.charAt(0).toUpperCase() + result.risk_level.slice(1)}
|
|
668
|
+
**Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
|
|
669
|
+
|
|
670
|
+
### Summary
|
|
671
|
+
${result.summary}
|
|
672
|
+
|
|
673
|
+
### Recommended Action
|
|
674
|
+
\`${result.recommended_action}\`
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
${result.bullying ? `
|
|
678
|
+
**Bullying Check:** ${result.bullying.is_bullying ? '⚠️ Detected' : '✅ Clear'}
|
|
679
|
+
` : ''}${result.unsafe ? `
|
|
680
|
+
**Unsafe Content:** ${result.unsafe.unsafe ? '⚠️ Detected' : '✅ Clear'}
|
|
681
|
+
` : ''}`;
|
|
682
|
+
return { content: [{ type: 'text', text: response }] };
|
|
683
|
+
}
|
|
684
|
+
case 'analyze_emotions': {
|
|
685
|
+
const result = await client.analyzeEmotions({
|
|
686
|
+
content: args.content,
|
|
687
|
+
});
|
|
688
|
+
const emoji = trendEmoji[result.trend] || '➡️';
|
|
689
|
+
const emotionScoresList = Object.entries(result.emotion_scores)
|
|
690
|
+
.sort((a, b) => b[1] - a[1])
|
|
691
|
+
.map(([emotion, score]) => `- ${emotion}: ${(score * 100).toFixed(0)}%`)
|
|
692
|
+
.join('\n');
|
|
693
|
+
const response = `## Emotion Analysis
|
|
694
|
+
|
|
695
|
+
**Dominant Emotions:** ${result.dominant_emotions.join(', ')}
|
|
696
|
+
**Trend:** ${emoji} ${result.trend.charAt(0).toUpperCase() + result.trend.slice(1)}
|
|
697
|
+
|
|
698
|
+
### Emotion Scores
|
|
699
|
+
${emotionScoresList}
|
|
700
|
+
|
|
701
|
+
### Summary
|
|
702
|
+
${result.summary}
|
|
703
|
+
|
|
704
|
+
### Recommended Follow-up
|
|
705
|
+
${result.recommended_followup}`;
|
|
706
|
+
return { content: [{ type: 'text', text: response }] };
|
|
707
|
+
}
|
|
708
|
+
case 'get_action_plan': {
|
|
709
|
+
const result = await client.getActionPlan({
|
|
710
|
+
situation: args.situation,
|
|
711
|
+
childAge: args.childAge,
|
|
712
|
+
audience: args.audience,
|
|
713
|
+
severity: args.severity,
|
|
714
|
+
});
|
|
715
|
+
const response = `## Action Plan
|
|
716
|
+
|
|
717
|
+
**Audience:** ${result.audience}
|
|
718
|
+
**Tone:** ${result.tone}
|
|
719
|
+
${result.reading_level ? `**Reading Level:** ${result.reading_level}` : ''}
|
|
720
|
+
|
|
721
|
+
### Steps
|
|
722
|
+
${result.steps.map((step, i) => `${i + 1}. ${step}`).join('\n')}`;
|
|
723
|
+
return { content: [{ type: 'text', text: response }] };
|
|
724
|
+
}
|
|
725
|
+
case 'generate_report': {
|
|
726
|
+
const messages = args.messages.map((m) => ({
|
|
727
|
+
sender: m.sender,
|
|
728
|
+
content: m.content,
|
|
729
|
+
}));
|
|
730
|
+
const result = await client.generateReport({
|
|
731
|
+
messages,
|
|
732
|
+
childAge: args.childAge,
|
|
733
|
+
incident: args.incidentType ? { type: args.incidentType } : undefined,
|
|
734
|
+
});
|
|
735
|
+
const emoji = riskEmoji[result.risk_level] || '⚪';
|
|
736
|
+
const response = `## 📋 Incident Report
|
|
737
|
+
|
|
738
|
+
**Risk Level:** ${emoji} ${result.risk_level.charAt(0).toUpperCase() + result.risk_level.slice(1)}
|
|
739
|
+
|
|
740
|
+
### Summary
|
|
741
|
+
${result.summary}
|
|
742
|
+
|
|
743
|
+
### Categories
|
|
744
|
+
${result.categories.map(c => `- ${c}`).join('\n')}
|
|
745
|
+
|
|
746
|
+
### Recommended Next Steps
|
|
747
|
+
${result.recommended_next_steps.map((step, i) => `${i + 1}. ${step}`).join('\n')}`;
|
|
748
|
+
return { content: [{ type: 'text', text: response }] };
|
|
749
|
+
}
|
|
750
|
+
// =====================================================================
|
|
751
|
+
// Voice & Image Analysis
|
|
752
|
+
// =====================================================================
|
|
753
|
+
case 'analyze_voice': {
|
|
754
|
+
const filePath = args.file_path;
|
|
755
|
+
const buffer = (0, fs_1.readFileSync)(filePath);
|
|
756
|
+
const filename = filenameFromPath(filePath);
|
|
757
|
+
const result = await client.analyzeVoice({
|
|
758
|
+
file: buffer,
|
|
759
|
+
filename,
|
|
760
|
+
analysisType: args.analysis_type || 'all',
|
|
761
|
+
language: args.language,
|
|
762
|
+
childAge: args.child_age,
|
|
763
|
+
});
|
|
764
|
+
const emoji = severityEmoji[result.overall_severity] || '✅';
|
|
765
|
+
const segmentLines = result.transcription.segments
|
|
766
|
+
.slice(0, 20)
|
|
767
|
+
.map(s => `\`${s.start.toFixed(1)}s–${s.end.toFixed(1)}s\` ${s.text}`)
|
|
768
|
+
.join('\n');
|
|
769
|
+
const analysisLines = [];
|
|
770
|
+
if (result.analysis.bullying) {
|
|
771
|
+
analysisLines.push(`**Bullying:** ${result.analysis.bullying.is_bullying ? '⚠️ Detected' : '✅ Clear'} (${(result.analysis.bullying.risk_score * 100).toFixed(0)}%)`);
|
|
772
|
+
}
|
|
773
|
+
if (result.analysis.unsafe) {
|
|
774
|
+
analysisLines.push(`**Unsafe:** ${result.analysis.unsafe.unsafe ? '⚠️ Detected' : '✅ Clear'} (${(result.analysis.unsafe.risk_score * 100).toFixed(0)}%)`);
|
|
775
|
+
}
|
|
776
|
+
if (result.analysis.grooming) {
|
|
777
|
+
analysisLines.push(`**Grooming:** ${result.analysis.grooming.grooming_risk !== 'none' ? '⚠️ ' + result.analysis.grooming.grooming_risk : '✅ Clear'} (${(result.analysis.grooming.risk_score * 100).toFixed(0)}%)`);
|
|
778
|
+
}
|
|
779
|
+
if (result.analysis.emotions) {
|
|
780
|
+
analysisLines.push(`**Emotions:** ${result.analysis.emotions.dominant_emotions.join(', ')} (${trendEmoji[result.analysis.emotions.trend] || ''} ${result.analysis.emotions.trend})`);
|
|
781
|
+
}
|
|
782
|
+
const response = `## 🎙️ Voice Analysis
|
|
783
|
+
|
|
784
|
+
**Overall Severity:** ${emoji} ${result.overall_severity}
|
|
785
|
+
**Overall Risk Score:** ${(result.overall_risk_score * 100).toFixed(0)}%
|
|
786
|
+
**Language:** ${result.transcription.language}
|
|
787
|
+
**Duration:** ${result.transcription.duration.toFixed(1)}s
|
|
788
|
+
|
|
789
|
+
### Transcript
|
|
790
|
+
${result.transcription.text}
|
|
791
|
+
|
|
792
|
+
### Timestamped Segments
|
|
793
|
+
${segmentLines}${result.transcription.segments.length > 20 ? `\n_...and ${result.transcription.segments.length - 20} more segments_` : ''}
|
|
794
|
+
|
|
795
|
+
### Analysis Results
|
|
796
|
+
${analysisLines.join('\n')}`;
|
|
797
|
+
return { content: [{ type: 'text', text: response }] };
|
|
798
|
+
}
|
|
799
|
+
case 'analyze_image': {
|
|
800
|
+
const filePath = args.file_path;
|
|
801
|
+
const buffer = (0, fs_1.readFileSync)(filePath);
|
|
802
|
+
const filename = filenameFromPath(filePath);
|
|
803
|
+
const result = await client.analyzeImage({
|
|
804
|
+
file: buffer,
|
|
805
|
+
filename,
|
|
806
|
+
analysisType: args.analysis_type || 'all',
|
|
807
|
+
});
|
|
808
|
+
const emoji = severityEmoji[result.overall_severity] || '✅';
|
|
809
|
+
const textAnalysisLines = [];
|
|
810
|
+
if (result.text_analysis?.bullying) {
|
|
811
|
+
textAnalysisLines.push(`**Bullying:** ${result.text_analysis.bullying.is_bullying ? '⚠️ Detected' : '✅ Clear'} (${(result.text_analysis.bullying.risk_score * 100).toFixed(0)}%)`);
|
|
812
|
+
}
|
|
813
|
+
if (result.text_analysis?.unsafe) {
|
|
814
|
+
textAnalysisLines.push(`**Unsafe:** ${result.text_analysis.unsafe.unsafe ? '⚠️ Detected' : '✅ Clear'} (${(result.text_analysis.unsafe.risk_score * 100).toFixed(0)}%)`);
|
|
815
|
+
}
|
|
816
|
+
if (result.text_analysis?.emotions) {
|
|
817
|
+
textAnalysisLines.push(`**Emotions:** ${result.text_analysis.emotions.dominant_emotions.join(', ')}`);
|
|
818
|
+
}
|
|
819
|
+
const response = `## 🖼️ Image Analysis
|
|
820
|
+
|
|
821
|
+
**Overall Severity:** ${emoji} ${result.overall_severity}
|
|
822
|
+
**Overall Risk Score:** ${(result.overall_risk_score * 100).toFixed(0)}%
|
|
823
|
+
|
|
824
|
+
### Vision Results
|
|
825
|
+
**Description:** ${result.vision.visual_description}
|
|
826
|
+
**Visual Severity:** ${severityEmoji[result.vision.visual_severity] || '✅'} ${result.vision.visual_severity}
|
|
827
|
+
**Visual Confidence:** ${(result.vision.visual_confidence * 100).toFixed(0)}%
|
|
828
|
+
**Contains Text:** ${result.vision.contains_text ? 'Yes' : 'No'}
|
|
829
|
+
**Contains Faces:** ${result.vision.contains_faces ? 'Yes' : 'No'}
|
|
830
|
+
${result.vision.visual_categories.length > 0 ? `**Visual Categories:** ${result.vision.visual_categories.join(', ')}` : ''}
|
|
831
|
+
|
|
832
|
+
${result.vision.extracted_text ? `### Extracted Text (OCR)\n${result.vision.extracted_text}` : ''}
|
|
833
|
+
|
|
834
|
+
${textAnalysisLines.length > 0 ? `### Text Analysis Results\n${textAnalysisLines.join('\n')}` : ''}`;
|
|
835
|
+
return { content: [{ type: 'text', text: response }] };
|
|
836
|
+
}
|
|
837
|
+
// =====================================================================
|
|
838
|
+
// Webhook Management
|
|
839
|
+
// =====================================================================
|
|
840
|
+
case 'list_webhooks': {
|
|
841
|
+
const result = await client.listWebhooks();
|
|
842
|
+
if (result.webhooks.length === 0) {
|
|
843
|
+
return { content: [{ type: 'text', text: 'No webhooks configured.' }] };
|
|
844
|
+
}
|
|
845
|
+
const lines = result.webhooks.map(w => `- ${w.is_active ? '🟢' : '⚪'} **${w.name}** — \`${w.url}\`\n Events: ${w.events.join(', ')} _(${w.id})_`).join('\n');
|
|
846
|
+
return { content: [{ type: 'text', text: `## Webhooks\n\n${lines}` }] };
|
|
847
|
+
}
|
|
848
|
+
case 'create_webhook': {
|
|
849
|
+
const result = await client.createWebhook({
|
|
850
|
+
name: args.name,
|
|
851
|
+
url: args.url,
|
|
852
|
+
events: args.events,
|
|
853
|
+
});
|
|
854
|
+
return { content: [{ type: 'text', text: `## ✅ Webhook Created\n\n**ID:** ${result.id}\n**Name:** ${result.name}\n**URL:** ${result.url}\n**Events:** ${result.events.join(', ')}\n\n⚠️ **Secret (save this — shown only once):**\n\`${result.secret}\`` }] };
|
|
855
|
+
}
|
|
856
|
+
case 'update_webhook': {
|
|
857
|
+
const result = await client.updateWebhook(args.id, {
|
|
858
|
+
name: args.name,
|
|
859
|
+
url: args.url,
|
|
860
|
+
events: args.events,
|
|
861
|
+
isActive: args.is_active,
|
|
862
|
+
});
|
|
863
|
+
return { content: [{ type: 'text', text: `## ✅ Webhook Updated\n\n**ID:** ${result.id}\n**Name:** ${result.name}\n**Active:** ${result.is_active ? '🟢 Yes' : '⚪ No'}` }] };
|
|
864
|
+
}
|
|
865
|
+
case 'delete_webhook': {
|
|
866
|
+
await client.deleteWebhook(args.id);
|
|
867
|
+
return { content: [{ type: 'text', text: `## ✅ Webhook Deleted\n\nWebhook \`${args.id}\` has been permanently deleted.` }] };
|
|
868
|
+
}
|
|
869
|
+
case 'test_webhook': {
|
|
870
|
+
const result = await client.testWebhook(args.id);
|
|
871
|
+
return { content: [{ type: 'text', text: `## ${result.success ? '✅' : '❌'} Webhook Test\n\n**Success:** ${result.success}\n**Status Code:** ${result.status_code}\n**Latency:** ${result.latency_ms}ms${result.error ? `\n**Error:** ${result.error}` : ''}` }] };
|
|
872
|
+
}
|
|
873
|
+
case 'regenerate_webhook_secret': {
|
|
874
|
+
const result = await client.regenerateWebhookSecret(args.id);
|
|
875
|
+
return { content: [{ type: 'text', text: `## ✅ Secret Regenerated\n\nThe old secret has been invalidated.\n\n⚠️ **New Secret (save this — shown only once):**\n\`${result.secret}\`` }] };
|
|
876
|
+
}
|
|
877
|
+
// =====================================================================
|
|
878
|
+
// Pricing
|
|
879
|
+
// =====================================================================
|
|
880
|
+
case 'get_pricing': {
|
|
881
|
+
const result = await client.getPricing();
|
|
882
|
+
const lines = result.plans.map(p => `### ${p.name}\n**Price:** ${p.price}\n${p.features.map(f => `- ${f}`).join('\n')}`).join('\n\n');
|
|
883
|
+
return { content: [{ type: 'text', text: `## Tuteliq Pricing\n\n${lines}` }] };
|
|
884
|
+
}
|
|
885
|
+
case 'get_pricing_details': {
|
|
886
|
+
const result = await client.getPricingDetails();
|
|
887
|
+
const lines = result.plans.map(p => `### ${p.name}\n**Monthly:** ${p.price_monthly}/mo | **Yearly:** ${p.price_yearly}/mo\n**API Calls:** ${p.api_calls_per_month}/mo | **Rate Limit:** ${p.rate_limit}/min\n${p.features.map(f => `- ${f}`).join('\n')}`).join('\n\n');
|
|
888
|
+
return { content: [{ type: 'text', text: `## Tuteliq Pricing Details\n\n${lines}` }] };
|
|
889
|
+
}
|
|
890
|
+
// =====================================================================
|
|
891
|
+
// Usage & Billing
|
|
892
|
+
// =====================================================================
|
|
893
|
+
case 'get_usage_history': {
|
|
894
|
+
const result = await client.getUsageHistory(args.days);
|
|
895
|
+
if (result.days.length === 0) {
|
|
896
|
+
return { content: [{ type: 'text', text: 'No usage data available.' }] };
|
|
897
|
+
}
|
|
898
|
+
const lines = result.days.map(d => `| ${d.date} | ${d.total_requests} | ${d.success_requests} | ${d.error_requests} |`).join('\n');
|
|
899
|
+
return { content: [{ type: 'text', text: `## Usage History\n\n| Date | Total | Success | Errors |\n|------|-------|---------|--------|\n${lines}` }] };
|
|
900
|
+
}
|
|
901
|
+
case 'get_usage_by_tool': {
|
|
902
|
+
const result = await client.getUsageByTool(args.date);
|
|
903
|
+
const toolLines = Object.entries(result.tools).map(([tool, count]) => `- **${tool}:** ${count}`).join('\n');
|
|
904
|
+
const endpointLines = Object.entries(result.endpoints).map(([ep, count]) => `- **${ep}:** ${count}`).join('\n');
|
|
905
|
+
return { content: [{ type: 'text', text: `## Usage by Tool — ${result.date}\n\n### By Tool\n${toolLines || '_No data_'}\n\n### By Endpoint\n${endpointLines || '_No data_'}` }] };
|
|
906
|
+
}
|
|
907
|
+
case 'get_usage_monthly': {
|
|
908
|
+
const result = await client.getUsageMonthly();
|
|
909
|
+
const response = `## Monthly Usage
|
|
910
|
+
|
|
911
|
+
**Tier:** ${result.tier_display_name}
|
|
912
|
+
**Billing Period:** ${result.billing.current_period_start} → ${result.billing.current_period_end} (${result.billing.days_remaining} days left)
|
|
913
|
+
|
|
914
|
+
### Usage
|
|
915
|
+
**Used:** ${result.usage.used} / ${result.usage.limit} (${result.usage.percent_used.toFixed(1)}%)
|
|
916
|
+
**Remaining:** ${result.usage.remaining}
|
|
917
|
+
**Rate Limit:** ${result.rate_limit.requests_per_minute}/min
|
|
918
|
+
|
|
919
|
+
${result.recommendations ? `### Recommendation\n${result.recommendations.reason}\n**Suggested Tier:** ${result.recommendations.suggested_tier}\n[Upgrade](${result.recommendations.upgrade_url})` : ''}`;
|
|
920
|
+
return { content: [{ type: 'text', text: response }] };
|
|
921
|
+
}
|
|
922
|
+
// =====================================================================
|
|
923
|
+
// GDPR Account
|
|
924
|
+
// =====================================================================
|
|
925
|
+
case 'delete_account_data': {
|
|
926
|
+
const result = await client.deleteAccountData();
|
|
927
|
+
return { content: [{ type: 'text', text: `## ✅ Account Data Deleted\n\n**Message:** ${result.message}\n**Records Deleted:** ${result.deleted_count}` }] };
|
|
928
|
+
}
|
|
929
|
+
case 'export_account_data': {
|
|
930
|
+
const result = await client.exportAccountData();
|
|
931
|
+
const collections = Object.keys(result.data).join(', ');
|
|
932
|
+
return { content: [{ type: 'text', text: `## 📦 Account Data Export\n\n**User ID:** ${result.userId}\n**Exported At:** ${result.exportedAt}\n**Collections:** ${collections}\n\n\`\`\`json\n${JSON.stringify(result.data, null, 2).slice(0, 5000)}\n\`\`\`` }] };
|
|
933
|
+
}
|
|
934
|
+
case 'record_consent': {
|
|
935
|
+
const result = await client.recordConsent({
|
|
936
|
+
consent_type: args.consent_type,
|
|
937
|
+
version: args.version,
|
|
938
|
+
});
|
|
939
|
+
return { content: [{ type: 'text', text: `## ✅ Consent Recorded\n\n**Type:** ${result.consent.consent_type}\n**Status:** ${result.consent.status}\n**Version:** ${result.consent.version}` }] };
|
|
940
|
+
}
|
|
941
|
+
case 'get_consent_status': {
|
|
942
|
+
const result = await client.getConsentStatus(args.type);
|
|
943
|
+
if (result.consents.length === 0) {
|
|
944
|
+
return { content: [{ type: 'text', text: 'No consent records found.' }] };
|
|
945
|
+
}
|
|
946
|
+
const lines = result.consents.map(c => `- **${c.consent_type}**: ${c.status === 'granted' ? '✅' : '❌'} ${c.status} (v${c.version})`).join('\n');
|
|
947
|
+
return { content: [{ type: 'text', text: `## Consent Status\n\n${lines}` }] };
|
|
948
|
+
}
|
|
949
|
+
case 'withdraw_consent': {
|
|
950
|
+
const result = await client.withdrawConsent(args.type);
|
|
951
|
+
return { content: [{ type: 'text', text: `## ⚠️ Consent Withdrawn\n\n**Type:** ${result.consent.consent_type}\n**Status:** ${result.consent.status}` }] };
|
|
952
|
+
}
|
|
953
|
+
case 'rectify_data': {
|
|
954
|
+
const result = await client.rectifyData({
|
|
955
|
+
collection: args.collection,
|
|
956
|
+
document_id: args.document_id,
|
|
957
|
+
fields: args.fields,
|
|
958
|
+
});
|
|
959
|
+
return { content: [{ type: 'text', text: `## ✅ Data Rectified\n\n**Message:** ${result.message}\n**Updated Fields:** ${result.updated_fields.join(', ')}` }] };
|
|
960
|
+
}
|
|
961
|
+
case 'get_audit_logs': {
|
|
962
|
+
const result = await client.getAuditLogs({
|
|
963
|
+
action: args.action,
|
|
964
|
+
limit: args.limit,
|
|
965
|
+
});
|
|
966
|
+
if (result.audit_logs.length === 0) {
|
|
967
|
+
return { content: [{ type: 'text', text: 'No audit logs found.' }] };
|
|
968
|
+
}
|
|
969
|
+
const logLines = result.audit_logs.map(l => `- \`${l.created_at}\` **${l.action}** _(${l.id})_`).join('\n');
|
|
970
|
+
return { content: [{ type: 'text', text: `## 📋 Audit Logs\n\n${logLines}` }] };
|
|
971
|
+
}
|
|
972
|
+
// =====================================================================
|
|
973
|
+
// Breach Management
|
|
974
|
+
// =====================================================================
|
|
975
|
+
case 'log_breach': {
|
|
976
|
+
const result = await client.logBreach({
|
|
977
|
+
title: args.title,
|
|
978
|
+
description: args.description,
|
|
979
|
+
severity: args.severity,
|
|
980
|
+
affected_user_ids: args.affected_user_ids,
|
|
981
|
+
data_categories: args.data_categories,
|
|
982
|
+
reported_by: args.reported_by,
|
|
983
|
+
});
|
|
984
|
+
const b = result.breach;
|
|
985
|
+
return { content: [{ type: 'text', text: `## ⚠️ Breach Logged\n\n**ID:** ${b.id}\n**Title:** ${b.title}\n**Severity:** ${severityEmoji[b.severity] || '⚪'} ${b.severity}\n**Status:** ${b.status}\n**Notification Deadline:** ${b.notification_deadline}\n**Affected Users:** ${b.affected_user_ids.length}\n**Data Categories:** ${b.data_categories.join(', ')}` }] };
|
|
986
|
+
}
|
|
987
|
+
case 'list_breaches': {
|
|
988
|
+
const result = await client.listBreaches({
|
|
989
|
+
status: args.status,
|
|
990
|
+
limit: args.limit,
|
|
991
|
+
});
|
|
992
|
+
if (result.breaches.length === 0) {
|
|
993
|
+
return { content: [{ type: 'text', text: 'No breaches found.' }] };
|
|
994
|
+
}
|
|
995
|
+
const breachLines = result.breaches.map(b => `- ${severityEmoji[b.severity] || '⚪'} **${b.title}** — ${b.status} _(${b.id})_`).join('\n');
|
|
996
|
+
return { content: [{ type: 'text', text: `## Data Breaches\n\n${breachLines}` }] };
|
|
997
|
+
}
|
|
998
|
+
case 'get_breach': {
|
|
999
|
+
const result = await client.getBreach(args.id);
|
|
1000
|
+
const b = result.breach;
|
|
1001
|
+
return { content: [{ type: 'text', text: `## Breach Details\n\n**ID:** ${b.id}\n**Title:** ${b.title}\n**Severity:** ${severityEmoji[b.severity] || '⚪'} ${b.severity}\n**Status:** ${b.status}\n**Notification:** ${b.notification_status}\n**Reported By:** ${b.reported_by}\n**Deadline:** ${b.notification_deadline}\n**Created:** ${b.created_at}\n**Updated:** ${b.updated_at}\n\n### Description\n${b.description}\n\n**Affected Users:** ${b.affected_user_ids.join(', ')}\n**Data Categories:** ${b.data_categories.join(', ')}` }] };
|
|
1002
|
+
}
|
|
1003
|
+
case 'update_breach_status': {
|
|
1004
|
+
const result = await client.updateBreachStatus(args.id, {
|
|
1005
|
+
status: args.status,
|
|
1006
|
+
notification_status: args.notification_status,
|
|
1007
|
+
notes: args.notes,
|
|
1008
|
+
});
|
|
1009
|
+
const b = result.breach;
|
|
1010
|
+
return { content: [{ type: 'text', text: `## ✅ Breach Updated\n\n**ID:** ${b.id}\n**Status:** ${b.status}\n**Notification:** ${b.notification_status}` }] };
|
|
1011
|
+
}
|
|
1012
|
+
default:
|
|
1013
|
+
return {
|
|
1014
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
1015
|
+
isError: true,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
catch (error) {
|
|
1020
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
1021
|
+
return {
|
|
1022
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
1023
|
+
isError: true,
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
// Start server
|
|
1028
|
+
async function main() {
|
|
1029
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
1030
|
+
await server.connect(transport);
|
|
1031
|
+
console.error('Tuteliq MCP server running on stdio');
|
|
1032
|
+
}
|
|
1033
|
+
main().catch((error) => {
|
|
1034
|
+
console.error('Fatal error:', error);
|
|
1035
|
+
process.exit(1);
|
|
1036
|
+
});
|