@tuteliq/mcp 3.2.4 → 3.2.6
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/dist/server.js +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/tools/analysis.d.ts.map +1 -1
- package/dist/src/tools/analysis.js +87 -40
- package/dist/src/tools/detection.d.ts.map +1 -1
- package/dist/src/tools/detection.js +84 -37
- package/dist/src/tools/fraud.d.ts.map +1 -1
- package/dist/src/tools/fraud.js +29 -12
- package/dist/src/tools/media.d.ts.map +1 -1
- package/dist/src/tools/media.js +105 -66
- package/dist-ui/action-plan.html +28 -23
- package/dist-ui/detection-result.html +48 -43
- package/dist-ui/emotions-result.html +28 -23
- package/dist-ui/media-result.html +29 -24
- package/dist-ui/multi-result.html +28 -23
- package/dist-ui/report-result.html +28 -23
- package/package.json +1 -1
package/dist/src/tools/media.js
CHANGED
|
@@ -13,6 +13,21 @@ function loadWidget(name) {
|
|
|
13
13
|
function filenameFromPath(filePath) {
|
|
14
14
|
return filePath.split('/').pop() || filePath;
|
|
15
15
|
}
|
|
16
|
+
function handleTierError(err, toolName, featureLabel) {
|
|
17
|
+
if (err?.status === 403 || err?.response?.status === 403) {
|
|
18
|
+
const upsellResult = {
|
|
19
|
+
error: 'tier_restricted',
|
|
20
|
+
tier_restricted: true,
|
|
21
|
+
upgrade: true,
|
|
22
|
+
message: `Your current plan does not include ${featureLabel.toLowerCase()}. Upgrade your plan or purchase additional credits to unlock this feature.`,
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
structuredContent: { toolName, result: upsellResult, branding: { appName: 'Tuteliq' } },
|
|
26
|
+
content: [{ type: 'text', text: `\u26A0\uFE0F ${upsellResult.message}\n\nUpgrade at: https://tuteliq.ai/dashboard` }],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
16
31
|
export function registerMediaTools(server, client) {
|
|
17
32
|
registerAppResource(server, MEDIA_WIDGET_URI, MEDIA_WIDGET_URI, { mimeType: RESOURCE_MIME_TYPE }, async () => ({
|
|
18
33
|
contents: [{
|
|
@@ -39,34 +54,35 @@ export function registerMediaTools(server, client) {
|
|
|
39
54
|
'openai/toolInvocation/invoked': 'Voice analysis complete.',
|
|
40
55
|
},
|
|
41
56
|
}, async ({ file_path, analysis_type, child_age, language }) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
try {
|
|
58
|
+
const buffer = readFileSync(file_path);
|
|
59
|
+
const filename = filenameFromPath(file_path);
|
|
60
|
+
const result = await client.analyzeVoice({
|
|
61
|
+
file: buffer,
|
|
62
|
+
filename,
|
|
63
|
+
analysisType: analysis_type || 'all',
|
|
64
|
+
language,
|
|
65
|
+
childAge: child_age,
|
|
66
|
+
});
|
|
67
|
+
const emoji = severityEmoji[result.overall_severity] || '\u2705';
|
|
68
|
+
const segmentLines = result.transcription.segments
|
|
69
|
+
.slice(0, 20)
|
|
70
|
+
.map(s => `\`${s.start.toFixed(1)}s\u2013${s.end.toFixed(1)}s\` ${s.text}`)
|
|
71
|
+
.join('\n');
|
|
72
|
+
const analysisLines = [];
|
|
73
|
+
if (result.analysis.bullying) {
|
|
74
|
+
analysisLines.push(`**Bullying:** ${result.analysis.bullying.is_bullying ? '\u26A0\uFE0F Detected' : '\u2705 Clear'} (${(result.analysis.bullying.risk_score * 100).toFixed(0)}%)`);
|
|
75
|
+
}
|
|
76
|
+
if (result.analysis.unsafe) {
|
|
77
|
+
analysisLines.push(`**Unsafe:** ${result.analysis.unsafe.unsafe ? '\u26A0\uFE0F Detected' : '\u2705 Clear'} (${(result.analysis.unsafe.risk_score * 100).toFixed(0)}%)`);
|
|
78
|
+
}
|
|
79
|
+
if (result.analysis.grooming) {
|
|
80
|
+
analysisLines.push(`**Grooming:** ${result.analysis.grooming.grooming_risk !== 'none' ? '\u26A0\uFE0F ' + result.analysis.grooming.grooming_risk : '\u2705 Clear'} (${(result.analysis.grooming.risk_score * 100).toFixed(0)}%)`);
|
|
81
|
+
}
|
|
82
|
+
if (result.analysis.emotions) {
|
|
83
|
+
analysisLines.push(`**Emotions:** ${result.analysis.emotions.dominant_emotions.join(', ')} (${trendEmoji[result.analysis.emotions.trend] || ''} ${result.analysis.emotions.trend})`);
|
|
84
|
+
}
|
|
85
|
+
const text = `## \u{1F399}\uFE0F Voice Analysis
|
|
70
86
|
|
|
71
87
|
**Overall Severity:** ${emoji} ${result.overall_severity}
|
|
72
88
|
**Overall Risk Score:** ${(result.overall_risk_score * 100).toFixed(0)}%
|
|
@@ -81,10 +97,17 @@ ${segmentLines}${result.transcription.segments.length > 20 ? `\n_...and ${result
|
|
|
81
97
|
|
|
82
98
|
### Analysis Results
|
|
83
99
|
${analysisLines.join('\n')}`;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
return {
|
|
101
|
+
structuredContent: { toolName: 'analyze_voice', result, branding: { appName: 'Tuteliq' } },
|
|
102
|
+
content: [{ type: 'text', text }],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const upsell = handleTierError(err, 'analyze_voice', 'Voice Analysis');
|
|
107
|
+
if (upsell)
|
|
108
|
+
return upsell;
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
88
111
|
});
|
|
89
112
|
// ── analyze_image ──────────────────────────────────────────────────────────
|
|
90
113
|
registerAppTool(server, 'analyze_image', {
|
|
@@ -102,25 +125,26 @@ ${analysisLines.join('\n')}`;
|
|
|
102
125
|
'openai/toolInvocation/invoked': 'Image analysis complete.',
|
|
103
126
|
},
|
|
104
127
|
}, async ({ file_path, analysis_type }) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
try {
|
|
129
|
+
const buffer = readFileSync(file_path);
|
|
130
|
+
const filename = filenameFromPath(file_path);
|
|
131
|
+
const result = await client.analyzeImage({
|
|
132
|
+
file: buffer,
|
|
133
|
+
filename,
|
|
134
|
+
analysisType: analysis_type || 'all',
|
|
135
|
+
});
|
|
136
|
+
const emoji = severityEmoji[result.overall_severity] || '\u2705';
|
|
137
|
+
const textAnalysisLines = [];
|
|
138
|
+
if (result.text_analysis?.bullying) {
|
|
139
|
+
textAnalysisLines.push(`**Bullying:** ${result.text_analysis.bullying.is_bullying ? '\u26A0\uFE0F Detected' : '\u2705 Clear'} (${(result.text_analysis.bullying.risk_score * 100).toFixed(0)}%)`);
|
|
140
|
+
}
|
|
141
|
+
if (result.text_analysis?.unsafe) {
|
|
142
|
+
textAnalysisLines.push(`**Unsafe:** ${result.text_analysis.unsafe.unsafe ? '\u26A0\uFE0F Detected' : '\u2705 Clear'} (${(result.text_analysis.unsafe.risk_score * 100).toFixed(0)}%)`);
|
|
143
|
+
}
|
|
144
|
+
if (result.text_analysis?.emotions) {
|
|
145
|
+
textAnalysisLines.push(`**Emotions:** ${result.text_analysis.emotions.dominant_emotions.join(', ')}`);
|
|
146
|
+
}
|
|
147
|
+
const text = `## \u{1F5BC}\uFE0F Image Analysis
|
|
124
148
|
|
|
125
149
|
**Overall Severity:** ${emoji} ${result.overall_severity}
|
|
126
150
|
**Overall Risk Score:** ${(result.overall_risk_score * 100).toFixed(0)}%
|
|
@@ -136,10 +160,17 @@ ${result.vision.visual_categories.length > 0 ? `**Visual Categories:** ${result.
|
|
|
136
160
|
${result.vision.extracted_text ? `### Extracted Text (OCR)\n${result.vision.extracted_text}` : ''}
|
|
137
161
|
|
|
138
162
|
${textAnalysisLines.length > 0 ? `### Text Analysis Results\n${textAnalysisLines.join('\n')}` : ''}`;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
163
|
+
return {
|
|
164
|
+
structuredContent: { toolName: 'analyze_image', result, branding: { appName: 'Tuteliq' } },
|
|
165
|
+
content: [{ type: 'text', text }],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
const upsell = handleTierError(err, 'analyze_image', 'Image Analysis');
|
|
170
|
+
if (upsell)
|
|
171
|
+
return upsell;
|
|
172
|
+
throw err;
|
|
173
|
+
}
|
|
143
174
|
});
|
|
144
175
|
// ── analyze_video ──────────────────────────────────────────────────────────
|
|
145
176
|
registerAppTool(server, 'analyze_video', {
|
|
@@ -157,16 +188,24 @@ ${textAnalysisLines.length > 0 ? `### Text Analysis Results\n${textAnalysisLines
|
|
|
157
188
|
'openai/toolInvocation/invoked': 'Video analysis complete.',
|
|
158
189
|
},
|
|
159
190
|
}, async ({ file_path, age_group }) => {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
191
|
+
try {
|
|
192
|
+
const buffer = readFileSync(file_path);
|
|
193
|
+
const filename = filenameFromPath(file_path);
|
|
194
|
+
const result = await client.analyzeVideo({
|
|
195
|
+
file: buffer,
|
|
196
|
+
filename,
|
|
197
|
+
ageGroup: age_group,
|
|
198
|
+
});
|
|
199
|
+
return {
|
|
200
|
+
structuredContent: { toolName: 'analyze_video', result, branding: { appName: 'Tuteliq' } },
|
|
201
|
+
content: [{ type: 'text', text: formatVideoResult(result) }],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
const upsell = handleTierError(err, 'analyze_video', 'Video Analysis');
|
|
206
|
+
if (upsell)
|
|
207
|
+
return upsell;
|
|
208
|
+
throw err;
|
|
209
|
+
}
|
|
171
210
|
});
|
|
172
211
|
}
|