@tuteliq/mcp 2.2.0 → 3.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.
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.createServer = createServer;
5
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
6
+ const sdk_1 = require("@tuteliq/sdk");
7
+ const detection_js_1 = require("./tools/detection.js");
8
+ const fraud_js_1 = require("./tools/fraud.js");
9
+ const media_js_1 = require("./tools/media.js");
10
+ const analysis_js_1 = require("./tools/analysis.js");
11
+ const admin_js_1 = require("./tools/admin.js");
12
+ const transport_js_1 = require("./transport.js");
13
+ function createServer() {
14
+ const apiKey = process.env.TUTELIQ_API_KEY;
15
+ if (!apiKey) {
16
+ console.error('Error: TUTELIQ_API_KEY environment variable is required');
17
+ process.exit(1);
18
+ }
19
+ const client = new sdk_1.Tuteliq(apiKey);
20
+ const server = new mcp_js_1.McpServer({
21
+ name: 'tuteliq-mcp',
22
+ version: '3.0.0',
23
+ });
24
+ // Register all tool groups
25
+ (0, detection_js_1.registerDetectionTools)(server, client);
26
+ (0, fraud_js_1.registerFraudTools)(server, client);
27
+ (0, media_js_1.registerMediaTools)(server, client);
28
+ (0, analysis_js_1.registerAnalysisTools)(server, client);
29
+ (0, admin_js_1.registerAdminTools)(server, client);
30
+ return server;
31
+ }
32
+ // Direct execution: stdio mode
33
+ if ((0, transport_js_1.getTransportMode)() === 'stdio') {
34
+ const server = createServer();
35
+ (0, transport_js_1.startStdio)(server).catch((error) => {
36
+ console.error('Fatal error:', error);
37
+ process.exit(1);
38
+ });
39
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Tuteliq } from '@tuteliq/sdk';
3
+ export declare function registerAdminTools(server: McpServer, client: Tuteliq): void;
4
+ //# sourceMappingURL=admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../../src/tools/admin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,OAAO,EAAsG,MAAM,cAAc,CAAC;AAGhJ,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAmU3E"}
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAdminTools = registerAdminTools;
4
+ const zod_1 = require("zod");
5
+ const formatters_js_1 = require("../formatters.js");
6
+ function registerAdminTools(server, client) {
7
+ // =========================================================================
8
+ // Webhook Management
9
+ // =========================================================================
10
+ server.tool('list_webhooks', 'List all webhooks configured for your account.', {}, async () => {
11
+ const result = await client.listWebhooks();
12
+ if (result.webhooks.length === 0) {
13
+ return { content: [{ type: 'text', text: 'No webhooks configured.' }] };
14
+ }
15
+ const lines = result.webhooks.map(w => `- ${w.is_active ? '\u{1F7E2}' : '\u26AA'} **${w.name}** \u2014 \`${w.url}\`\n Events: ${w.events.join(', ')} _(${w.id})_`).join('\n');
16
+ return { content: [{ type: 'text', text: `## Webhooks\n\n${lines}` }] };
17
+ });
18
+ server.tool('create_webhook', 'Create a new webhook endpoint.', {
19
+ name: zod_1.z.string().describe('Display name for the webhook'),
20
+ url: zod_1.z.string().describe('HTTPS URL to receive webhook payloads'),
21
+ events: zod_1.z.array(zod_1.z.string()).describe('Event types to subscribe to'),
22
+ }, async ({ name, url, events }) => {
23
+ const result = await client.createWebhook({
24
+ name,
25
+ url,
26
+ events: events,
27
+ });
28
+ return { content: [{ type: 'text', text: `## \u2705 Webhook Created\n\n**ID:** ${result.id}\n**Name:** ${result.name}\n**URL:** ${result.url}\n**Events:** ${result.events.join(', ')}\n\n\u26A0\uFE0F **Secret (save this \u2014 shown only once):**\n\`${result.secret}\`` }] };
29
+ });
30
+ server.tool('update_webhook', 'Update an existing webhook configuration.', {
31
+ id: zod_1.z.string().describe('Webhook ID'),
32
+ name: zod_1.z.string().optional().describe('New display name'),
33
+ url: zod_1.z.string().optional().describe('New HTTPS URL'),
34
+ events: zod_1.z.array(zod_1.z.string()).optional().describe('New event subscriptions'),
35
+ is_active: zod_1.z.boolean().optional().describe('Enable or disable the webhook'),
36
+ }, async ({ id, name, url, events, is_active }) => {
37
+ const result = await client.updateWebhook(id, {
38
+ name,
39
+ url,
40
+ events: events,
41
+ isActive: is_active,
42
+ });
43
+ return { content: [{ type: 'text', text: `## \u2705 Webhook Updated\n\n**ID:** ${result.id}\n**Name:** ${result.name}\n**Active:** ${result.is_active ? '\u{1F7E2} Yes' : '\u26AA No'}` }] };
44
+ });
45
+ server.tool('delete_webhook', 'Permanently delete a webhook.', { id: zod_1.z.string().describe('Webhook ID to delete') }, async ({ id }) => {
46
+ await client.deleteWebhook(id);
47
+ return { content: [{ type: 'text', text: `## \u2705 Webhook Deleted\n\nWebhook \`${id}\` has been permanently deleted.` }] };
48
+ });
49
+ server.tool('test_webhook', 'Send a test payload to a webhook to verify it is working correctly.', { id: zod_1.z.string().describe('Webhook ID to test') }, async ({ id }) => {
50
+ const result = await client.testWebhook(id);
51
+ return { content: [{ type: 'text', text: `## ${result.success ? '\u2705' : '\u274C'} 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}` : ''}` }] };
52
+ });
53
+ server.tool('regenerate_webhook_secret', 'Regenerate a webhook signing secret.', { id: zod_1.z.string().describe('Webhook ID') }, async ({ id }) => {
54
+ const result = await client.regenerateWebhookSecret(id);
55
+ return { content: [{ type: 'text', text: `## \u2705 Secret Regenerated\n\nThe old secret has been invalidated.\n\n\u26A0\uFE0F **New Secret (save this \u2014 shown only once):**\n\`${result.secret}\`` }] };
56
+ });
57
+ // =========================================================================
58
+ // Pricing
59
+ // =========================================================================
60
+ server.tool('get_pricing', 'Get available pricing plans for Tuteliq.', {}, async () => {
61
+ const result = await client.getPricing();
62
+ const lines = result.plans.map(p => `### ${p.name}\n**Price:** ${p.price}\n${p.features.map(f => `- ${f}`).join('\n')}`).join('\n\n');
63
+ return { content: [{ type: 'text', text: `## Tuteliq Pricing\n\n${lines}` }] };
64
+ });
65
+ server.tool('get_pricing_details', 'Get detailed pricing plans.', {}, async () => {
66
+ const result = await client.getPricingDetails();
67
+ 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');
68
+ return { content: [{ type: 'text', text: `## Tuteliq Pricing Details\n\n${lines}` }] };
69
+ });
70
+ // =========================================================================
71
+ // Usage & Billing
72
+ // =========================================================================
73
+ server.tool('get_usage_history', 'Get daily usage history for the past N days.', { days: zod_1.z.number().optional().describe('Number of days to retrieve (1-30, default: 7)') }, async ({ days }) => {
74
+ const result = await client.getUsageHistory(days);
75
+ if (result.days.length === 0) {
76
+ return { content: [{ type: 'text', text: 'No usage data available.' }] };
77
+ }
78
+ const lines = result.days.map(d => `| ${d.date} | ${d.total_requests} | ${d.success_requests} | ${d.error_requests} |`).join('\n');
79
+ return { content: [{ type: 'text', text: `## Usage History\n\n| Date | Total | Success | Errors |\n|------|-------|---------|--------|\n${lines}` }] };
80
+ });
81
+ server.tool('get_usage_by_tool', 'Get usage broken down by tool/endpoint for a specific date.', { date: zod_1.z.string().optional().describe('Date in YYYY-MM-DD format (default: today)') }, async ({ date }) => {
82
+ const result = await client.getUsageByTool(date);
83
+ const toolLines = Object.entries(result.tools).map(([tool, count]) => `- **${tool}:** ${count}`).join('\n');
84
+ const endpointLines = Object.entries(result.endpoints).map(([ep, count]) => `- **${ep}:** ${count}`).join('\n');
85
+ return { content: [{ type: 'text', text: `## Usage by Tool \u2014 ${result.date}\n\n### By Tool\n${toolLines || '_No data_'}\n\n### By Endpoint\n${endpointLines || '_No data_'}` }] };
86
+ });
87
+ server.tool('get_usage_monthly', 'Get monthly usage summary.', {}, async () => {
88
+ const result = await client.getUsageMonthly();
89
+ const text = `## Monthly Usage
90
+
91
+ **Tier:** ${result.tier_display_name}
92
+ **Billing Period:** ${result.billing.current_period_start} \u2192 ${result.billing.current_period_end} (${result.billing.days_remaining} days left)
93
+
94
+ ### Usage
95
+ **Used:** ${result.usage.used} / ${result.usage.limit} (${result.usage.percent_used.toFixed(1)}%)
96
+ **Remaining:** ${result.usage.remaining}
97
+ **Rate Limit:** ${result.rate_limit.requests_per_minute}/min
98
+
99
+ ${result.recommendations ? `### Recommendation\n${result.recommendations.reason}\n**Suggested Tier:** ${result.recommendations.suggested_tier}\n[Upgrade](${result.recommendations.upgrade_url})` : ''}`;
100
+ return { content: [{ type: 'text', text }] };
101
+ });
102
+ // =========================================================================
103
+ // GDPR Account
104
+ // =========================================================================
105
+ server.tool('delete_account_data', 'Delete all account data (GDPR Right to Erasure).', {}, async () => {
106
+ const result = await client.deleteAccountData();
107
+ return { content: [{ type: 'text', text: `## \u2705 Account Data Deleted\n\n**Message:** ${result.message}\n**Records Deleted:** ${result.deleted_count}` }] };
108
+ });
109
+ server.tool('export_account_data', 'Export all account data as JSON (GDPR Data Portability).', {}, async () => {
110
+ const result = await client.exportAccountData();
111
+ const collections = Object.keys(result.data).join(', ');
112
+ return { content: [{ type: 'text', text: `## \u{1F4E6} 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\`\`\`` }] };
113
+ });
114
+ const consentTypeEnum = zod_1.z.enum(['data_processing', 'analytics', 'marketing', 'third_party_sharing', 'child_safety_monitoring']);
115
+ server.tool('record_consent', 'Record user consent for data processing (GDPR Article 7).', {
116
+ consent_type: consentTypeEnum.describe('Type of consent to record'),
117
+ version: zod_1.z.string().describe('Policy version the user is consenting to'),
118
+ }, async ({ consent_type, version }) => {
119
+ const result = await client.recordConsent({ consent_type: consent_type, version });
120
+ return { content: [{ type: 'text', text: `## \u2705 Consent Recorded\n\n**Type:** ${result.consent.consent_type}\n**Status:** ${result.consent.status}\n**Version:** ${result.consent.version}` }] };
121
+ });
122
+ server.tool('get_consent_status', 'Get current consent status.', { type: consentTypeEnum.optional().describe('Optional: filter by consent type') }, async ({ type }) => {
123
+ const result = await client.getConsentStatus(type);
124
+ if (result.consents.length === 0) {
125
+ return { content: [{ type: 'text', text: 'No consent records found.' }] };
126
+ }
127
+ const lines = result.consents.map(c => `- **${c.consent_type}**: ${c.status === 'granted' ? '\u2705' : '\u274C'} ${c.status} (v${c.version})`).join('\n');
128
+ return { content: [{ type: 'text', text: `## Consent Status\n\n${lines}` }] };
129
+ });
130
+ server.tool('withdraw_consent', 'Withdraw a previously granted consent.', { type: consentTypeEnum.describe('Type of consent to withdraw') }, async ({ type }) => {
131
+ const result = await client.withdrawConsent(type);
132
+ return { content: [{ type: 'text', text: `## \u26A0\uFE0F Consent Withdrawn\n\n**Type:** ${result.consent.consent_type}\n**Status:** ${result.consent.status}` }] };
133
+ });
134
+ server.tool('rectify_data', 'Rectify (correct) user data (GDPR Right to Rectification).', {
135
+ collection: zod_1.z.string().describe('Firestore collection name'),
136
+ document_id: zod_1.z.string().describe('Document ID to rectify'),
137
+ fields: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).describe('Fields to update'),
138
+ }, async ({ collection, document_id, fields }) => {
139
+ const result = await client.rectifyData({ collection, document_id, fields: fields });
140
+ return { content: [{ type: 'text', text: `## \u2705 Data Rectified\n\n**Message:** ${result.message}\n**Updated Fields:** ${result.updated_fields.join(', ')}` }] };
141
+ });
142
+ server.tool('get_audit_logs', 'Get audit trail of data operations.', {
143
+ action: zod_1.z.enum(['data_access', 'data_export', 'data_deletion', 'data_rectification', 'consent_granted', 'consent_withdrawn', 'breach_notification']).optional().describe('Filter by action type'),
144
+ limit: zod_1.z.number().optional().describe('Maximum number of results'),
145
+ }, async ({ action, limit }) => {
146
+ const result = await client.getAuditLogs({ action: action, limit });
147
+ if (result.audit_logs.length === 0) {
148
+ return { content: [{ type: 'text', text: 'No audit logs found.' }] };
149
+ }
150
+ const logLines = result.audit_logs.map(l => `- \`${l.created_at}\` **${l.action}** _(${l.id})_`).join('\n');
151
+ return { content: [{ type: 'text', text: `## \u{1F4CB} Audit Logs\n\n${logLines}` }] };
152
+ });
153
+ // =========================================================================
154
+ // Breach Management
155
+ // =========================================================================
156
+ server.tool('log_breach', 'Log a new data breach (GDPR Article 33/34).', {
157
+ title: zod_1.z.string().describe('Brief title of the breach'),
158
+ description: zod_1.z.string().describe('Detailed description'),
159
+ severity: zod_1.z.enum(['low', 'medium', 'high', 'critical']).describe('Breach severity'),
160
+ affected_user_ids: zod_1.z.array(zod_1.z.string()).describe('List of affected user IDs'),
161
+ data_categories: zod_1.z.array(zod_1.z.string()).describe('Categories of data affected'),
162
+ reported_by: zod_1.z.string().describe('Who reported the breach'),
163
+ }, async ({ title, description, severity, affected_user_ids, data_categories, reported_by }) => {
164
+ const result = await client.logBreach({
165
+ title,
166
+ description,
167
+ severity: severity,
168
+ affected_user_ids,
169
+ data_categories,
170
+ reported_by,
171
+ });
172
+ const b = result.breach;
173
+ return { content: [{ type: 'text', text: `## \u26A0\uFE0F Breach Logged\n\n**ID:** ${b.id}\n**Title:** ${b.title}\n**Severity:** ${formatters_js_1.severityEmoji[b.severity] || '\u26AA'} ${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(', ')}` }] };
174
+ });
175
+ const breachStatusEnum = zod_1.z.enum(['detected', 'investigating', 'contained', 'reported', 'resolved']);
176
+ server.tool('list_breaches', 'List all data breaches.', {
177
+ status: breachStatusEnum.optional().describe('Filter by status'),
178
+ limit: zod_1.z.number().optional().describe('Maximum number of results'),
179
+ }, async ({ status, limit }) => {
180
+ const result = await client.listBreaches({ status: status, limit });
181
+ if (result.breaches.length === 0) {
182
+ return { content: [{ type: 'text', text: 'No breaches found.' }] };
183
+ }
184
+ const breachLines = result.breaches.map(b => `- ${formatters_js_1.severityEmoji[b.severity] || '\u26AA'} **${b.title}** \u2014 ${b.status} _(${b.id})_`).join('\n');
185
+ return { content: [{ type: 'text', text: `## Data Breaches\n\n${breachLines}` }] };
186
+ });
187
+ server.tool('get_breach', 'Get details of a specific data breach.', { id: zod_1.z.string().describe('Breach ID') }, async ({ id }) => {
188
+ const result = await client.getBreach(id);
189
+ const b = result.breach;
190
+ return { content: [{ type: 'text', text: `## Breach Details\n\n**ID:** ${b.id}\n**Title:** ${b.title}\n**Severity:** ${formatters_js_1.severityEmoji[b.severity] || '\u26AA'} ${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(', ')}` }] };
191
+ });
192
+ server.tool('update_breach_status', 'Update a breach status and notification progress.', {
193
+ id: zod_1.z.string().describe('Breach ID'),
194
+ status: breachStatusEnum.describe('New breach status'),
195
+ notification_status: zod_1.z.enum(['pending', 'users_notified', 'dpa_notified', 'completed']).optional().describe('Notification progress'),
196
+ notes: zod_1.z.string().optional().describe('Additional notes'),
197
+ }, async ({ id, status, notification_status, notes }) => {
198
+ const result = await client.updateBreachStatus(id, {
199
+ status: status,
200
+ notification_status: notification_status,
201
+ notes,
202
+ });
203
+ const b = result.breach;
204
+ return { content: [{ type: 'text', text: `## \u2705 Breach Updated\n\n**ID:** ${b.id}\n**Status:** ${b.status}\n**Notification:** ${b.notification_status}` }] };
205
+ });
206
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Tuteliq } from '@tuteliq/sdk';
3
+ export declare function registerAnalysisTools(server: McpServer, client: Tuteliq): void;
4
+ //# sourceMappingURL=analysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../../../src/tools/analysis.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,cAAc,CAAC;AAc1D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CA0M9E"}
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAnalysisTools = registerAnalysisTools;
4
+ const zod_1 = require("zod");
5
+ const server_1 = require("@modelcontextprotocol/ext-apps/server");
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const formatters_js_1 = require("../formatters.js");
9
+ const EMOTIONS_WIDGET_URI = 'ui://tuteliq/emotions-result.html';
10
+ const ACTION_PLAN_WIDGET_URI = 'ui://tuteliq/action-plan.html';
11
+ const REPORT_WIDGET_URI = 'ui://tuteliq/report-result.html';
12
+ const MULTI_WIDGET_URI = 'ui://tuteliq/multi-result.html';
13
+ function loadWidget(name) {
14
+ return (0, fs_1.readFileSync)((0, path_1.resolve)(__dirname, '../../dist-ui', name), 'utf-8');
15
+ }
16
+ function registerAnalysisTools(server, client) {
17
+ const resources = [
18
+ { uri: EMOTIONS_WIDGET_URI, file: 'emotions-result.html' },
19
+ { uri: ACTION_PLAN_WIDGET_URI, file: 'action-plan.html' },
20
+ { uri: REPORT_WIDGET_URI, file: 'report-result.html' },
21
+ { uri: MULTI_WIDGET_URI, file: 'multi-result.html' },
22
+ ];
23
+ for (const r of resources) {
24
+ (0, server_1.registerAppResource)(server, r.uri, r.uri, { mimeType: server_1.RESOURCE_MIME_TYPE }, async () => ({
25
+ contents: [{
26
+ uri: r.uri,
27
+ mimeType: server_1.RESOURCE_MIME_TYPE,
28
+ text: loadWidget(r.file),
29
+ }],
30
+ }));
31
+ }
32
+ // ── analyze_emotions ───────────────────────────────────────────────────────
33
+ (0, server_1.registerAppTool)(server, 'analyze_emotions', {
34
+ title: 'Analyze Emotions',
35
+ description: 'Analyze emotional content and mental state indicators. Identifies dominant emotions, trends, and provides follow-up recommendations.',
36
+ annotations: { readOnlyHint: true, openWorldHint: true, destructiveHint: false },
37
+ inputSchema: {
38
+ content: zod_1.z.string().describe('The text content to analyze for emotions'),
39
+ },
40
+ _meta: {
41
+ ui: { resourceUri: EMOTIONS_WIDGET_URI },
42
+ 'openai/widgetDescription': 'Shows emotion analysis results with charts and trends',
43
+ 'openai/toolInvocation/invoking': 'Analyzing emotional content...',
44
+ 'openai/toolInvocation/invoked': 'Emotion analysis complete.',
45
+ },
46
+ }, async ({ content }) => {
47
+ const result = await client.analyzeEmotions({ content });
48
+ const emoji = formatters_js_1.trendEmoji[result.trend] || '\u27A1\uFE0F';
49
+ const emotionScoresList = Object.entries(result.emotion_scores)
50
+ .sort((a, b) => b[1] - a[1])
51
+ .map(([emotion, score]) => `- ${emotion}: ${(score * 100).toFixed(0)}%`)
52
+ .join('\n');
53
+ const text = `## Emotion Analysis
54
+
55
+ **Dominant Emotions:** ${result.dominant_emotions.join(', ')}
56
+ **Trend:** ${emoji} ${result.trend.charAt(0).toUpperCase() + result.trend.slice(1)}
57
+
58
+ ### Emotion Scores
59
+ ${emotionScoresList}
60
+
61
+ ### Summary
62
+ ${result.summary}
63
+
64
+ ### Recommended Follow-up
65
+ ${result.recommended_followup}`;
66
+ return {
67
+ structuredContent: { toolName: 'analyze_emotions', result, branding: { appName: 'Tuteliq' } },
68
+ content: [{ type: 'text', text }],
69
+ };
70
+ });
71
+ // ── get_action_plan ────────────────────────────────────────────────────────
72
+ (0, server_1.registerAppTool)(server, 'get_action_plan', {
73
+ title: 'Get Action Plan',
74
+ description: 'Generate age-appropriate guidance and action steps for handling a safety situation.',
75
+ annotations: { readOnlyHint: true, openWorldHint: true, destructiveHint: false },
76
+ inputSchema: {
77
+ situation: zod_1.z.string().describe('Description of the situation needing guidance'),
78
+ childAge: zod_1.z.number().optional().describe('Age of the child involved'),
79
+ audience: zod_1.z.enum(['child', 'parent', 'educator', 'platform']).optional().describe('Who the guidance is for (default: parent)'),
80
+ severity: zod_1.z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Severity of the situation'),
81
+ },
82
+ _meta: {
83
+ ui: { resourceUri: ACTION_PLAN_WIDGET_URI },
84
+ 'openai/widgetDescription': 'Shows age-appropriate action plan with step-by-step guidance',
85
+ 'openai/toolInvocation/invoking': 'Generating action plan...',
86
+ 'openai/toolInvocation/invoked': 'Action plan ready.',
87
+ },
88
+ }, async ({ situation, childAge, audience, severity }) => {
89
+ const result = await client.getActionPlan({ situation, childAge, audience, severity });
90
+ const text = `## Action Plan
91
+
92
+ **Audience:** ${result.audience}
93
+ **Tone:** ${result.tone}
94
+ ${result.reading_level ? `**Reading Level:** ${result.reading_level}` : ''}
95
+
96
+ ### Steps
97
+ ${result.steps.map((step, i) => `${i + 1}. ${step}`).join('\n')}`;
98
+ return {
99
+ structuredContent: { toolName: 'get_action_plan', result, branding: { appName: 'Tuteliq' } },
100
+ content: [{ type: 'text', text }],
101
+ };
102
+ });
103
+ // ── generate_report ────────────────────────────────────────────────────────
104
+ (0, server_1.registerAppTool)(server, 'generate_report', {
105
+ title: 'Generate Report',
106
+ description: 'Generate a comprehensive incident report from a conversation.',
107
+ annotations: { readOnlyHint: true, openWorldHint: true, destructiveHint: false },
108
+ inputSchema: {
109
+ messages: zod_1.z.array(zod_1.z.object({
110
+ sender: zod_1.z.string().describe('Name/ID of sender'),
111
+ content: zod_1.z.string().describe('Message content'),
112
+ })).describe('Array of messages in the incident'),
113
+ childAge: zod_1.z.number().optional().describe('Age of the child involved'),
114
+ incidentType: zod_1.z.string().optional().describe('Type of incident (e.g., bullying, grooming)'),
115
+ },
116
+ _meta: {
117
+ ui: { resourceUri: REPORT_WIDGET_URI },
118
+ 'openai/widgetDescription': 'Shows comprehensive incident report with risk assessment',
119
+ 'openai/toolInvocation/invoking': 'Generating incident report...',
120
+ 'openai/toolInvocation/invoked': 'Incident report ready.',
121
+ },
122
+ }, async ({ messages, childAge, incidentType }) => {
123
+ const result = await client.generateReport({
124
+ messages,
125
+ childAge,
126
+ incident: incidentType ? { type: incidentType } : undefined,
127
+ });
128
+ const emoji = formatters_js_1.riskEmoji[result.risk_level] || '\u26AA';
129
+ const text = `## \u{1F4CB} Incident Report
130
+
131
+ **Risk Level:** ${emoji} ${result.risk_level.charAt(0).toUpperCase() + result.risk_level.slice(1)}
132
+
133
+ ### Summary
134
+ ${result.summary}
135
+
136
+ ### Categories
137
+ ${result.categories.map(c => `- ${c}`).join('\n')}
138
+
139
+ ### Recommended Next Steps
140
+ ${result.recommended_next_steps.map((step, i) => `${i + 1}. ${step}`).join('\n')}`;
141
+ return {
142
+ structuredContent: { toolName: 'generate_report', result, branding: { appName: 'Tuteliq' } },
143
+ content: [{ type: 'text', text }],
144
+ };
145
+ });
146
+ // ── analyse_multi ──────────────────────────────────────────────────────────
147
+ (0, server_1.registerAppTool)(server, 'analyse_multi', {
148
+ title: 'Multi-Endpoint Analysis',
149
+ description: 'Run multiple detection endpoints on a single piece of text.',
150
+ annotations: { readOnlyHint: true, openWorldHint: true, destructiveHint: false },
151
+ inputSchema: {
152
+ content: zod_1.z.string().describe('Text content to analyze'),
153
+ endpoints: zod_1.z.array(zod_1.z.string()).describe('Detection endpoints to run'),
154
+ context: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional().describe('Optional analysis context'),
155
+ include_evidence: zod_1.z.boolean().optional().describe('Include supporting evidence'),
156
+ external_id: zod_1.z.string().optional().describe('External tracking ID'),
157
+ customer_id: zod_1.z.string().optional().describe('Customer identifier'),
158
+ },
159
+ _meta: {
160
+ ui: { resourceUri: MULTI_WIDGET_URI },
161
+ 'openai/widgetDescription': 'Shows multi-endpoint analysis with aggregated risk assessment',
162
+ 'openai/toolInvocation/invoking': 'Running multi-endpoint analysis...',
163
+ 'openai/toolInvocation/invoked': 'Multi-endpoint analysis complete.',
164
+ },
165
+ }, async ({ content, endpoints, context, include_evidence, external_id, customer_id }) => {
166
+ const result = await client.analyseMulti({
167
+ content,
168
+ detections: endpoints,
169
+ context: context,
170
+ includeEvidence: include_evidence,
171
+ external_id,
172
+ customer_id,
173
+ });
174
+ return {
175
+ structuredContent: { toolName: 'analyse_multi', result, branding: { appName: 'Tuteliq' } },
176
+ content: [{ type: 'text', text: (0, formatters_js_1.formatMultiResult)(result) }],
177
+ };
178
+ });
179
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Tuteliq } from '@tuteliq/sdk';
3
+ export declare function registerDetectionTools(server: McpServer, client: Tuteliq): void;
4
+ //# sourceMappingURL=detection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../../src/tools/detection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAyB5C,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAuL/E"}
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerDetectionTools = registerDetectionTools;
4
+ const zod_1 = require("zod");
5
+ const server_1 = require("@modelcontextprotocol/ext-apps/server");
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const formatters_js_1 = require("../formatters.js");
9
+ const DETECTION_WIDGET_URI = 'ui://tuteliq/detection-result.html';
10
+ function loadWidget(name) {
11
+ return (0, fs_1.readFileSync)((0, path_1.resolve)(__dirname, '../../dist-ui', name), 'utf-8');
12
+ }
13
+ const contextSchema = zod_1.z.object({
14
+ language: zod_1.z.string().optional(),
15
+ ageGroup: zod_1.z.string().optional(),
16
+ relationship: zod_1.z.string().optional(),
17
+ platform: zod_1.z.string().optional(),
18
+ }).optional();
19
+ const uiMeta = (desc, invoking, invoked) => ({
20
+ ui: { resourceUri: DETECTION_WIDGET_URI },
21
+ 'openai/widgetDescription': desc,
22
+ 'openai/toolInvocation/invoking': invoking,
23
+ 'openai/toolInvocation/invoked': invoked,
24
+ });
25
+ function registerDetectionTools(server, client) {
26
+ // Cast needed due to CJS/ESM dual-module type mismatch between MCP SDK and ext-apps
27
+ (0, server_1.registerAppResource)(server, DETECTION_WIDGET_URI, DETECTION_WIDGET_URI, { mimeType: server_1.RESOURCE_MIME_TYPE }, async () => ({
28
+ contents: [{
29
+ uri: DETECTION_WIDGET_URI,
30
+ mimeType: server_1.RESOURCE_MIME_TYPE,
31
+ text: loadWidget('detection-result.html'),
32
+ }],
33
+ }));
34
+ // ── detect_bullying ────────────────────────────────────────────────────────
35
+ (0, server_1.registerAppTool)(server, 'detect_bullying', {
36
+ title: 'Detect Bullying',
37
+ description: 'Analyze text content to detect bullying, harassment, or harmful language.',
38
+ annotations: { readOnlyHint: true, openWorldHint: true, destructiveHint: false },
39
+ inputSchema: {
40
+ content: zod_1.z.string().describe('The text content to analyze for bullying'),
41
+ context: contextSchema,
42
+ },
43
+ _meta: uiMeta('Shows bullying detection results with risk indicators', 'Analyzing content for bullying...', 'Bullying analysis complete.'),
44
+ }, async ({ content, context }) => {
45
+ const result = await client.detectBullying({
46
+ content,
47
+ context: context,
48
+ });
49
+ const emoji = formatters_js_1.severityEmoji[result.severity] || '\u26AA';
50
+ const text = `## ${result.is_bullying ? '\u26A0\uFE0F Bullying Detected' : '\u2705 No Bullying Detected'}
51
+
52
+ **Severity:** ${emoji} ${result.severity.charAt(0).toUpperCase() + result.severity.slice(1)}
53
+ **Confidence:** ${(result.confidence * 100).toFixed(0)}%
54
+ **Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
55
+
56
+ ${result.is_bullying ? `**Types:** ${result.bullying_type.join(', ')}` : ''}
57
+
58
+ ### Rationale
59
+ ${result.rationale}
60
+
61
+ ### Recommended Action
62
+ \`${result.recommended_action}\``;
63
+ return {
64
+ structuredContent: { toolName: 'detect_bullying', result, branding: { appName: 'Tuteliq' } },
65
+ content: [{ type: 'text', text }],
66
+ };
67
+ });
68
+ // ── detect_grooming ────────────────────────────────────────────────────────
69
+ (0, server_1.registerAppTool)(server, 'detect_grooming', {
70
+ title: 'Detect Grooming',
71
+ description: 'Analyze a conversation for grooming patterns and predatory behavior.',
72
+ annotations: { readOnlyHint: true, openWorldHint: true, destructiveHint: false },
73
+ inputSchema: {
74
+ messages: zod_1.z.array(zod_1.z.object({
75
+ role: zod_1.z.enum(['adult', 'child', 'unknown']),
76
+ content: zod_1.z.string(),
77
+ })).describe('Array of messages in the conversation'),
78
+ childAge: zod_1.z.number().optional().describe('Age of the child in the conversation'),
79
+ },
80
+ _meta: uiMeta('Shows grooming detection results with risk indicators', 'Analyzing conversation for grooming patterns...', 'Grooming analysis complete.'),
81
+ }, async ({ messages, childAge }) => {
82
+ const result = await client.detectGrooming({
83
+ messages,
84
+ childAge,
85
+ });
86
+ const emoji = formatters_js_1.riskEmoji[result.grooming_risk] || '\u26AA';
87
+ const text = `## ${result.grooming_risk === 'none' ? '\u2705 No Grooming Detected' : '\u26A0\uFE0F Grooming Risk Detected'}
88
+
89
+ **Risk Level:** ${emoji} ${result.grooming_risk.charAt(0).toUpperCase() + result.grooming_risk.slice(1)}
90
+ **Confidence:** ${(result.confidence * 100).toFixed(0)}%
91
+ **Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
92
+
93
+ ${result.flags.length > 0 ? `**Warning Flags:**\n${result.flags.map(f => `- \u{1F6A9} ${f}`).join('\n')}` : ''}
94
+
95
+ ### Rationale
96
+ ${result.rationale}
97
+
98
+ ### Recommended Action
99
+ \`${result.recommended_action}\``;
100
+ return {
101
+ structuredContent: { toolName: 'detect_grooming', result, branding: { appName: 'Tuteliq' } },
102
+ content: [{ type: 'text', text }],
103
+ };
104
+ });
105
+ // ── detect_unsafe ──────────────────────────────────────────────────────────
106
+ (0, server_1.registerAppTool)(server, 'detect_unsafe', {
107
+ title: 'Detect Unsafe Content',
108
+ description: 'Detect unsafe content including self-harm, violence, drugs, explicit material.',
109
+ annotations: { readOnlyHint: true, openWorldHint: true, destructiveHint: false },
110
+ inputSchema: {
111
+ content: zod_1.z.string().describe('The text content to analyze for unsafe content'),
112
+ context: contextSchema,
113
+ },
114
+ _meta: uiMeta('Shows unsafe content detection results', 'Analyzing content for safety concerns...', 'Safety analysis complete.'),
115
+ }, async ({ content, context }) => {
116
+ const result = await client.detectUnsafe({
117
+ content,
118
+ context: context,
119
+ });
120
+ const emoji = formatters_js_1.severityEmoji[result.severity] || '\u26AA';
121
+ const text = `## ${result.unsafe ? '\u26A0\uFE0F Unsafe Content Detected' : '\u2705 Content is Safe'}
122
+
123
+ **Severity:** ${emoji} ${result.severity.charAt(0).toUpperCase() + result.severity.slice(1)}
124
+ **Confidence:** ${(result.confidence * 100).toFixed(0)}%
125
+ **Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
126
+
127
+ ${result.unsafe ? `**Categories:**\n${result.categories.map(c => `- \u26A0\uFE0F ${c}`).join('\n')}` : ''}
128
+
129
+ ### Rationale
130
+ ${result.rationale}
131
+
132
+ ### Recommended Action
133
+ \`${result.recommended_action}\``;
134
+ return {
135
+ structuredContent: { toolName: 'detect_unsafe', result, branding: { appName: 'Tuteliq' } },
136
+ content: [{ type: 'text', text }],
137
+ };
138
+ });
139
+ // ── analyze (quick combined) ───────────────────────────────────────────────
140
+ (0, server_1.registerAppTool)(server, 'analyze', {
141
+ title: 'Quick Safety Analysis',
142
+ description: 'Quick comprehensive safety analysis that checks for both bullying and unsafe content.',
143
+ annotations: { readOnlyHint: true, openWorldHint: true, destructiveHint: false },
144
+ inputSchema: {
145
+ content: zod_1.z.string().describe('The text content to analyze'),
146
+ include: zod_1.z.array(zod_1.z.enum(['bullying', 'unsafe'])).optional().describe('Which checks to run (default: both)'),
147
+ },
148
+ _meta: uiMeta('Shows combined safety analysis results', 'Running safety analysis...', 'Safety analysis complete.'),
149
+ }, async ({ content, include }) => {
150
+ const result = await client.analyze({ content, include });
151
+ const emoji = formatters_js_1.riskEmoji[result.risk_level] || '\u26AA';
152
+ const text = `## Safety Analysis Results
153
+
154
+ **Overall Risk:** ${emoji} ${result.risk_level.charAt(0).toUpperCase() + result.risk_level.slice(1)}
155
+ **Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
156
+
157
+ ### Summary
158
+ ${result.summary}
159
+
160
+ ### Recommended Action
161
+ \`${result.recommended_action}\`
162
+
163
+ ---
164
+ ${result.bullying ? `\n**Bullying Check:** ${result.bullying.is_bullying ? '\u26A0\uFE0F Detected' : '\u2705 Clear'}\n` : ''}${result.unsafe ? `\n**Unsafe Content:** ${result.unsafe.unsafe ? '\u26A0\uFE0F Detected' : '\u2705 Clear'}\n` : ''}`;
165
+ return {
166
+ structuredContent: { toolName: 'analyze', result, branding: { appName: 'Tuteliq' } },
167
+ content: [{ type: 'text', text }],
168
+ };
169
+ });
170
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Tuteliq } from '@tuteliq/sdk';
3
+ export declare function registerFraudTools(server: McpServer, client: Tuteliq): void;
4
+ //# sourceMappingURL=fraud.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fraud.d.ts","sourceRoot":"","sources":["../../../src/tools/fraud.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,cAAc,CAAC;AAyF1D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAkC3E"}