@tuteliq/mcp 2.2.0 → 3.0.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 +25 -4
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +29 -0
- package/dist/src/formatters.d.ts +8 -0
- package/dist/src/formatters.d.ts.map +1 -0
- package/dist/src/formatters.js +104 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +39 -0
- package/dist/src/tools/admin.d.ts +4 -0
- package/dist/src/tools/admin.d.ts.map +1 -0
- package/dist/src/tools/admin.js +206 -0
- package/dist/src/tools/analysis.d.ts +4 -0
- package/dist/src/tools/analysis.d.ts.map +1 -0
- package/dist/src/tools/analysis.js +179 -0
- package/dist/src/tools/detection.d.ts +4 -0
- package/dist/src/tools/detection.d.ts.map +1 -0
- package/dist/src/tools/detection.js +170 -0
- package/dist/src/tools/fraud.d.ts +4 -0
- package/dist/src/tools/fraud.d.ts.map +1 -0
- package/dist/src/tools/fraud.js +109 -0
- package/dist/src/tools/media.d.ts +4 -0
- package/dist/src/tools/media.d.ts.map +1 -0
- package/dist/src/tools/media.js +172 -0
- package/dist/src/transport.d.ts +7 -0
- package/dist/src/transport.d.ts.map +1 -0
- package/dist/src/transport.js +60 -0
- package/dist-ui/action-plan.html +159 -0
- package/dist-ui/detection-result.html +159 -0
- package/dist-ui/emotions-result.html +159 -0
- package/dist-ui/media-result.html +159 -0
- package/dist-ui/multi-result.html +159 -0
- package/dist-ui/report-result.html +159 -0
- package/package.json +31 -10
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -1036
package/README.md
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
17
|
-
<a href="https://
|
|
18
|
-
<a href="https://tuteliq.
|
|
17
|
+
<a href="https://docs.tuteliq.ai">API Docs</a> •
|
|
18
|
+
<a href="https://tuteliq.ai">Dashboard</a> •
|
|
19
|
+
<a href="https://trust.tuteliq.ai">Trust</a> •
|
|
19
20
|
<a href="https://discord.gg/7kbTeRYRXD">Discord</a>
|
|
20
21
|
</p>
|
|
21
22
|
|
|
@@ -202,7 +203,7 @@ The message contains direct exclusionary language...
|
|
|
202
203
|
|
|
203
204
|
## Get an API Key
|
|
204
205
|
|
|
205
|
-
1. Go to [tuteliq.
|
|
206
|
+
1. Go to [tuteliq.ai](https://tuteliq.ai)
|
|
206
207
|
2. Create an account
|
|
207
208
|
3. Generate an API key
|
|
208
209
|
4. Add it to your MCP config
|
|
@@ -232,7 +233,7 @@ Enable `PII_REDACTION_ENABLED=true` on your Tuteliq API to automatically strip e
|
|
|
232
233
|
|
|
233
234
|
## Support
|
|
234
235
|
|
|
235
|
-
- **API Docs**: [
|
|
236
|
+
- **API Docs**: [docs.tuteliq.ai](https://docs.tuteliq.ai)
|
|
236
237
|
- **Discord**: [discord.gg/7kbTeRYRXD](https://discord.gg/7kbTeRYRXD)
|
|
237
238
|
- **Email**: support@tuteliq.ai
|
|
238
239
|
|
|
@@ -244,6 +245,26 @@ MIT License - see [LICENSE](LICENSE) for details.
|
|
|
244
245
|
|
|
245
246
|
---
|
|
246
247
|
|
|
248
|
+
## Get Certified — Free
|
|
249
|
+
|
|
250
|
+
Tuteliq offers a **free certification program** for anyone who wants to deepen their understanding of online child safety. Complete a track, pass the quiz, and earn your official Tuteliq certificate — verified and shareable.
|
|
251
|
+
|
|
252
|
+
**Three tracks available:**
|
|
253
|
+
|
|
254
|
+
| Track | Who it's for | Duration |
|
|
255
|
+
|-------|-------------|----------|
|
|
256
|
+
| **Parents & Caregivers** | Parents, guardians, grandparents, teachers, coaches | ~90 min |
|
|
257
|
+
| **Young People (10–16)** | Young people who want to learn to spot manipulation | ~60 min |
|
|
258
|
+
| **Companies & Platforms** | Product managers, trust & safety teams, CTOs, compliance officers | ~120 min |
|
|
259
|
+
|
|
260
|
+
**Start here →** [tuteliq.ai/certify](https://tuteliq.ai/certify)
|
|
261
|
+
|
|
262
|
+
- 100% Free — no login required
|
|
263
|
+
- Verifiable certificate on completion
|
|
264
|
+
- Covers grooming recognition, sextortion, cyberbullying, regulatory obligations (KOSA, EU DSA), and more
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
247
268
|
## The Mission: Why This Matters
|
|
248
269
|
|
|
249
270
|
Before you decide to contribute or sponsor, read these numbers. They are not projections. They are not estimates from a pitch deck. They are verified statistics from the University of Edinburgh, UNICEF, NCMEC, and Interpol.
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../server.ts"],"names":[],"mappings":""}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const cors_1 = __importDefault(require("cors"));
|
|
8
|
+
const index_js_1 = require("./src/index.js");
|
|
9
|
+
const transport_js_1 = require("./src/transport.js");
|
|
10
|
+
const app = (0, express_1.default)();
|
|
11
|
+
const port = parseInt(process.env.PORT || '3001', 10);
|
|
12
|
+
app.use((0, cors_1.default)());
|
|
13
|
+
app.use(express_1.default.json());
|
|
14
|
+
const mcpServer = (0, index_js_1.createServer)();
|
|
15
|
+
const mcpHandler = (0, transport_js_1.createHttpHandler)(mcpServer);
|
|
16
|
+
app.all('/mcp', (req, res) => {
|
|
17
|
+
mcpHandler(req, res).catch((err) => {
|
|
18
|
+
console.error('MCP handler error:', err);
|
|
19
|
+
if (!res.headersSent) {
|
|
20
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
app.get('/health', (_req, res) => {
|
|
25
|
+
res.json({ status: 'ok', version: '3.0.0' });
|
|
26
|
+
});
|
|
27
|
+
app.listen(port, () => {
|
|
28
|
+
console.error(`Tuteliq MCP App server running on http://localhost:${port}/mcp`);
|
|
29
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DetectionResult, AnalyseMultiResult, VideoAnalysisResult } from '@tuteliq/sdk';
|
|
2
|
+
export declare const severityEmoji: Record<string, string>;
|
|
3
|
+
export declare const riskEmoji: Record<string, string>;
|
|
4
|
+
export declare const trendEmoji: Record<string, string>;
|
|
5
|
+
export declare function formatDetectionResult(result: DetectionResult): string;
|
|
6
|
+
export declare function formatMultiResult(result: AnalyseMultiResult): string;
|
|
7
|
+
export declare function formatVideoResult(result: VideoAnalysisResult): string;
|
|
8
|
+
//# sourceMappingURL=formatters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/formatters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE7F,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKhD,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO5C,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAI7C,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAuCrE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CA2BpE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAoBrE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.trendEmoji = exports.riskEmoji = exports.severityEmoji = void 0;
|
|
4
|
+
exports.formatDetectionResult = formatDetectionResult;
|
|
5
|
+
exports.formatMultiResult = formatMultiResult;
|
|
6
|
+
exports.formatVideoResult = formatVideoResult;
|
|
7
|
+
exports.severityEmoji = {
|
|
8
|
+
low: '\u{1F7E1}',
|
|
9
|
+
medium: '\u{1F7E0}',
|
|
10
|
+
high: '\u{1F534}',
|
|
11
|
+
critical: '\u26D4',
|
|
12
|
+
};
|
|
13
|
+
exports.riskEmoji = {
|
|
14
|
+
safe: '\u2705',
|
|
15
|
+
none: '\u2705',
|
|
16
|
+
low: '\u{1F7E1}',
|
|
17
|
+
medium: '\u{1F7E0}',
|
|
18
|
+
high: '\u{1F534}',
|
|
19
|
+
critical: '\u26D4',
|
|
20
|
+
};
|
|
21
|
+
exports.trendEmoji = {
|
|
22
|
+
improving: '\u{1F4C8}',
|
|
23
|
+
stable: '\u27A1\uFE0F',
|
|
24
|
+
worsening: '\u{1F4C9}',
|
|
25
|
+
};
|
|
26
|
+
function formatDetectionResult(result) {
|
|
27
|
+
const detected = result.detected;
|
|
28
|
+
const levelEmoji = exports.riskEmoji[result.level] || '\u26AA';
|
|
29
|
+
const label = result.endpoint
|
|
30
|
+
.split('_')
|
|
31
|
+
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
32
|
+
.join(' ');
|
|
33
|
+
const header = detected
|
|
34
|
+
? `## ${levelEmoji} ${label} Detected`
|
|
35
|
+
: `## \u2705 No ${label} Detected`;
|
|
36
|
+
const categories = result.categories.length > 0
|
|
37
|
+
? `**Categories:** ${result.categories.map(c => c.tag).join(', ')}`
|
|
38
|
+
: '';
|
|
39
|
+
const evidence = result.evidence && result.evidence.length > 0
|
|
40
|
+
? `### Evidence\n${result.evidence.map(e => `- _"${e.text}"_ \u2014 **${e.tactic}** (weight: ${e.weight.toFixed(2)})`).join('\n')}`
|
|
41
|
+
: '';
|
|
42
|
+
const calibration = result.age_calibration?.applied
|
|
43
|
+
? `**Age Calibration:** ${result.age_calibration.age_group} (${result.age_calibration.multiplier}x)`
|
|
44
|
+
: '';
|
|
45
|
+
return `${header}
|
|
46
|
+
|
|
47
|
+
**Risk Score:** ${(result.risk_score * 100).toFixed(0)}%
|
|
48
|
+
**Level:** ${result.level}
|
|
49
|
+
**Confidence:** ${(result.confidence * 100).toFixed(0)}%
|
|
50
|
+
${categories}
|
|
51
|
+
|
|
52
|
+
### Rationale
|
|
53
|
+
${result.rationale}
|
|
54
|
+
|
|
55
|
+
### Recommended Action
|
|
56
|
+
\`${result.recommended_action}\`
|
|
57
|
+
|
|
58
|
+
${evidence}
|
|
59
|
+
${calibration}`.trim();
|
|
60
|
+
}
|
|
61
|
+
function formatMultiResult(result) {
|
|
62
|
+
const s = result.summary;
|
|
63
|
+
const overallEmoji = exports.riskEmoji[s.overall_risk_level] || '\u26AA';
|
|
64
|
+
const summarySection = `## Multi-Endpoint Analysis
|
|
65
|
+
|
|
66
|
+
**Overall Risk:** ${overallEmoji} ${s.overall_risk_level}
|
|
67
|
+
**Endpoints Analyzed:** ${s.total_endpoints}
|
|
68
|
+
**Threats Detected:** ${s.detected_count}
|
|
69
|
+
**Highest Risk:** ${s.highest_risk.endpoint} (${(s.highest_risk.risk_score * 100).toFixed(0)}%)
|
|
70
|
+
${result.cross_endpoint_modifier ? `**Cross-Endpoint Modifier:** ${result.cross_endpoint_modifier.toFixed(2)}x` : ''}`;
|
|
71
|
+
const perEndpoint = result.results
|
|
72
|
+
.map(r => {
|
|
73
|
+
const emoji = r.detected ? (exports.riskEmoji[r.level] || '\u26AA') : '\u2705';
|
|
74
|
+
return `### ${emoji} ${r.endpoint}
|
|
75
|
+
**Detected:** ${r.detected ? 'Yes' : 'No'} | **Risk:** ${(r.risk_score * 100).toFixed(0)}% | **Level:** ${r.level}
|
|
76
|
+
${r.categories.length > 0 ? `**Categories:** ${r.categories.map(c => c.tag).join(', ')}` : ''}
|
|
77
|
+
${r.rationale}`;
|
|
78
|
+
})
|
|
79
|
+
.join('\n\n');
|
|
80
|
+
return `${summarySection}
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
${perEndpoint}`;
|
|
85
|
+
}
|
|
86
|
+
function formatVideoResult(result) {
|
|
87
|
+
const emoji = exports.severityEmoji[result.overall_severity] || '\u2705';
|
|
88
|
+
const findingsSection = result.safety_findings.length > 0
|
|
89
|
+
? result.safety_findings
|
|
90
|
+
.map(f => {
|
|
91
|
+
const fEmoji = exports.severityEmoji[f.severity <= 0.3 ? 'low' : f.severity <= 0.6 ? 'medium' : f.severity <= 0.85 ? 'high' : 'critical'] || '\u26AA';
|
|
92
|
+
return `- \`${f.timestamp.toFixed(1)}s\` (frame ${f.frame_index}) ${fEmoji} ${f.description}\n Categories: ${f.categories.join(', ')} | Severity: ${(f.severity * 100).toFixed(0)}%`;
|
|
93
|
+
})
|
|
94
|
+
.join('\n')
|
|
95
|
+
: '_No safety findings._';
|
|
96
|
+
return `## \u{1F3AC} Video Analysis
|
|
97
|
+
|
|
98
|
+
**Overall Severity:** ${emoji} ${result.overall_severity}
|
|
99
|
+
**Overall Risk Score:** ${(result.overall_risk_score * 100).toFixed(0)}%
|
|
100
|
+
**Frames Analyzed:** ${result.frames_analyzed}
|
|
101
|
+
|
|
102
|
+
### Safety Findings
|
|
103
|
+
${findingsSection}`;
|
|
104
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUpE,wBAAgB,YAAY,IAAI,SAAS,CAsBxC"}
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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"}
|