@vettly/mcp 0.1.9 → 0.1.10
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/bin/vettly-mcp.cjs +64 -62
- package/dist/bin/vettly-mcp.js +3 -17
- package/dist/{chunk-L5ZHIGXM.js → chunk-PMBQ22Z2.js} +62 -46
- package/dist/index.cjs +67 -51
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/smithery.yaml +2 -4
package/dist/bin/vettly-mcp.cjs
CHANGED
|
@@ -15,7 +15,7 @@ var ModerateInputSchema = import_zod.z.object({
|
|
|
15
15
|
policyId: import_zod.z.string().min(1).describe("The policy ID to use for moderation"),
|
|
16
16
|
contentType: import_zod.z.enum(["text", "image", "video"]).default("text").describe("Type of content being moderated")
|
|
17
17
|
});
|
|
18
|
-
function registerModerateTool(server, client) {
|
|
18
|
+
function registerModerateTool(server, client, demoMode = false) {
|
|
19
19
|
server.tool(
|
|
20
20
|
"moderate_content",
|
|
21
21
|
"Check text, image, or video content against a Vettly moderation policy. Returns safety assessment with category scores.",
|
|
@@ -39,24 +39,25 @@ function registerModerateTool(server, client) {
|
|
|
39
39
|
policyId: input.policyId,
|
|
40
40
|
contentType: input.contentType
|
|
41
41
|
});
|
|
42
|
+
const text = JSON.stringify(
|
|
43
|
+
{
|
|
44
|
+
decisionId: result.decisionId,
|
|
45
|
+
safe: result.safe,
|
|
46
|
+
flagged: result.flagged,
|
|
47
|
+
action: result.action,
|
|
48
|
+
categories: result.categories,
|
|
49
|
+
provider: result.provider,
|
|
50
|
+
latency: result.latency,
|
|
51
|
+
cost: result.cost
|
|
52
|
+
},
|
|
53
|
+
null,
|
|
54
|
+
2
|
|
55
|
+
);
|
|
42
56
|
return {
|
|
43
57
|
content: [
|
|
44
58
|
{
|
|
45
59
|
type: "text",
|
|
46
|
-
text:
|
|
47
|
-
{
|
|
48
|
-
decisionId: result.decisionId,
|
|
49
|
-
safe: result.safe,
|
|
50
|
-
flagged: result.flagged,
|
|
51
|
-
action: result.action,
|
|
52
|
-
categories: result.categories,
|
|
53
|
-
provider: result.provider,
|
|
54
|
-
latency: result.latency,
|
|
55
|
-
cost: result.cost
|
|
56
|
-
},
|
|
57
|
-
null,
|
|
58
|
-
2
|
|
59
|
-
)
|
|
60
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
60
61
|
}
|
|
61
62
|
]
|
|
62
63
|
};
|
|
@@ -80,7 +81,7 @@ var import_zod2 = require("zod");
|
|
|
80
81
|
var ValidateInputSchema = import_zod2.z.object({
|
|
81
82
|
yamlContent: import_zod2.z.string().min(1).describe("The YAML policy content to validate")
|
|
82
83
|
});
|
|
83
|
-
function registerValidateTool(server, client) {
|
|
84
|
+
function registerValidateTool(server, client, demoMode = false) {
|
|
84
85
|
server.tool(
|
|
85
86
|
"validate_policy",
|
|
86
87
|
"Validate a Vettly policy YAML without saving it. Returns validation results with any syntax or configuration errors.",
|
|
@@ -98,11 +99,12 @@ function registerValidateTool(server, client) {
|
|
|
98
99
|
const input = ValidateInputSchema.parse(args);
|
|
99
100
|
try {
|
|
100
101
|
const result = await client.validatePolicy(input.yamlContent);
|
|
102
|
+
const text = JSON.stringify(result, null, 2);
|
|
101
103
|
return {
|
|
102
104
|
content: [
|
|
103
105
|
{
|
|
104
106
|
type: "text",
|
|
105
|
-
text:
|
|
107
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
106
108
|
}
|
|
107
109
|
]
|
|
108
110
|
};
|
|
@@ -122,11 +124,14 @@ function registerValidateTool(server, client) {
|
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
// src/tools/list-policies.ts
|
|
125
|
-
|
|
127
|
+
var import_zod3 = require("zod");
|
|
128
|
+
function registerListPoliciesTool(server, client, demoMode = false) {
|
|
126
129
|
server.tool(
|
|
127
130
|
"list_policies",
|
|
128
131
|
"List all moderation policies available in your Vettly account.",
|
|
129
|
-
{
|
|
132
|
+
{
|
|
133
|
+
status: import_zod3.z.enum(["active", "all"]).default("active").describe("Filter by policy status: active only or all policies")
|
|
134
|
+
},
|
|
130
135
|
{
|
|
131
136
|
title: "List Policies",
|
|
132
137
|
readOnlyHint: true,
|
|
@@ -134,14 +139,19 @@ function registerListPoliciesTool(server, client) {
|
|
|
134
139
|
idempotentHint: true,
|
|
135
140
|
openWorldHint: true
|
|
136
141
|
},
|
|
137
|
-
async () => {
|
|
142
|
+
async (args) => {
|
|
138
143
|
try {
|
|
139
144
|
const result = await client.listPolicies();
|
|
145
|
+
let policies = result;
|
|
146
|
+
if (args.status === "active" && Array.isArray(policies)) {
|
|
147
|
+
policies = policies.filter((p) => p.status !== "archived");
|
|
148
|
+
}
|
|
149
|
+
const text = JSON.stringify(policies, null, 2);
|
|
140
150
|
return {
|
|
141
151
|
content: [
|
|
142
152
|
{
|
|
143
153
|
type: "text",
|
|
144
|
-
text:
|
|
154
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
145
155
|
}
|
|
146
156
|
]
|
|
147
157
|
};
|
|
@@ -161,11 +171,11 @@ function registerListPoliciesTool(server, client) {
|
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
// src/tools/usage-stats.ts
|
|
164
|
-
var
|
|
165
|
-
var UsageStatsInputSchema =
|
|
166
|
-
days:
|
|
174
|
+
var import_zod4 = require("zod");
|
|
175
|
+
var UsageStatsInputSchema = import_zod4.z.object({
|
|
176
|
+
days: import_zod4.z.number().min(1).max(365).default(30).describe("Number of days to include in statistics")
|
|
167
177
|
});
|
|
168
|
-
function registerUsageStatsTool(server, client) {
|
|
178
|
+
function registerUsageStatsTool(server, client, demoMode = false) {
|
|
169
179
|
server.tool(
|
|
170
180
|
"get_usage_stats",
|
|
171
181
|
"Get usage statistics for your Vettly account including request counts, costs, and moderation outcomes.",
|
|
@@ -183,11 +193,12 @@ function registerUsageStatsTool(server, client) {
|
|
|
183
193
|
const input = UsageStatsInputSchema.parse(args);
|
|
184
194
|
try {
|
|
185
195
|
const result = await client.getUsageStats(input.days);
|
|
196
|
+
const text = JSON.stringify(result, null, 2);
|
|
186
197
|
return {
|
|
187
198
|
content: [
|
|
188
199
|
{
|
|
189
200
|
type: "text",
|
|
190
|
-
text:
|
|
201
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
191
202
|
}
|
|
192
203
|
]
|
|
193
204
|
};
|
|
@@ -207,14 +218,14 @@ function registerUsageStatsTool(server, client) {
|
|
|
207
218
|
}
|
|
208
219
|
|
|
209
220
|
// src/tools/decisions.ts
|
|
210
|
-
var
|
|
211
|
-
var DecisionsInputSchema =
|
|
212
|
-
limit:
|
|
213
|
-
flagged:
|
|
214
|
-
policyId:
|
|
215
|
-
contentType:
|
|
221
|
+
var import_zod5 = require("zod");
|
|
222
|
+
var DecisionsInputSchema = import_zod5.z.object({
|
|
223
|
+
limit: import_zod5.z.number().min(1).max(50).default(10).describe("Number of decisions to return"),
|
|
224
|
+
flagged: import_zod5.z.boolean().optional().describe("Filter to only flagged content (true) or safe content (false)"),
|
|
225
|
+
policyId: import_zod5.z.string().optional().describe("Filter by specific policy ID"),
|
|
226
|
+
contentType: import_zod5.z.enum(["text", "image", "video"]).optional().describe("Filter by content type")
|
|
216
227
|
});
|
|
217
|
-
function registerDecisionsTool(server, client) {
|
|
228
|
+
function registerDecisionsTool(server, client, demoMode = false) {
|
|
218
229
|
server.tool(
|
|
219
230
|
"get_recent_decisions",
|
|
220
231
|
"Get recent moderation decisions with optional filtering by outcome, content type, or policy.",
|
|
@@ -245,11 +256,12 @@ function registerDecisionsTool(server, client) {
|
|
|
245
256
|
if (input.contentType) {
|
|
246
257
|
decisions = decisions.filter((d) => d.contentType === input.contentType);
|
|
247
258
|
}
|
|
259
|
+
const text = JSON.stringify({ decisions }, null, 2);
|
|
248
260
|
return {
|
|
249
261
|
content: [
|
|
250
262
|
{
|
|
251
263
|
type: "text",
|
|
252
|
-
text:
|
|
264
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
253
265
|
}
|
|
254
266
|
]
|
|
255
267
|
};
|
|
@@ -269,12 +281,13 @@ function registerDecisionsTool(server, client) {
|
|
|
269
281
|
}
|
|
270
282
|
|
|
271
283
|
// src/tools/index.ts
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
284
|
+
var DEMO_FOOTER = "\n\n---\n\u{1F511} Demo mode (10 requests/day). Get your free API key at https://vettly.dev";
|
|
285
|
+
function registerTools(server, client, demoMode = false) {
|
|
286
|
+
registerModerateTool(server, client, demoMode);
|
|
287
|
+
registerValidateTool(server, client, demoMode);
|
|
288
|
+
registerListPoliciesTool(server, client, demoMode);
|
|
289
|
+
registerUsageStatsTool(server, client, demoMode);
|
|
290
|
+
registerDecisionsTool(server, client, demoMode);
|
|
278
291
|
}
|
|
279
292
|
|
|
280
293
|
// src/resources/policies.ts
|
|
@@ -394,12 +407,12 @@ The response includes:
|
|
|
394
407
|
}
|
|
395
408
|
|
|
396
409
|
// src/prompts/write-policy.ts
|
|
397
|
-
var
|
|
410
|
+
var import_zod6 = require("zod");
|
|
398
411
|
function registerWritePolicy(server) {
|
|
399
412
|
server.prompt(
|
|
400
413
|
"write-policy",
|
|
401
414
|
"Guide for writing a Vettly moderation policy in YAML. Optionally tailored to a specific platform type.",
|
|
402
|
-
{ platform:
|
|
415
|
+
{ platform: import_zod6.z.string().optional().describe('The type of platform (e.g. "kids app", "marketplace", "social media", "dating app")') },
|
|
403
416
|
async (args) => {
|
|
404
417
|
const platformContext = args.platform ? `
|
|
405
418
|
|
|
@@ -469,12 +482,12 @@ Common categories: \`hate_speech\`, \`violence\`, \`sexual_content\`, \`self_har
|
|
|
469
482
|
}
|
|
470
483
|
|
|
471
484
|
// src/prompts/review-flagged-content.ts
|
|
472
|
-
var
|
|
485
|
+
var import_zod7 = require("zod");
|
|
473
486
|
function registerReviewFlaggedContent(server) {
|
|
474
487
|
server.prompt(
|
|
475
488
|
"review-flagged-content",
|
|
476
489
|
"Template for reviewing flagged moderation decisions. Optionally filtered to a specific policy.",
|
|
477
|
-
{ policyId:
|
|
490
|
+
{ policyId: import_zod7.z.string().optional().describe("Filter flagged decisions to a specific policy ID") },
|
|
478
491
|
async (args) => {
|
|
479
492
|
const policyFilter = args.policyId ? `
|
|
480
493
|
|
|
@@ -528,16 +541,19 @@ function registerPrompts(server) {
|
|
|
528
541
|
}
|
|
529
542
|
|
|
530
543
|
// src/server.ts
|
|
544
|
+
var DEMO_API_KEY = "vettly_test_demo_mcp_readonly";
|
|
531
545
|
function createVettlyMcpServer(config) {
|
|
546
|
+
const demoMode = !config.apiKey;
|
|
547
|
+
const apiKey = config.apiKey || DEMO_API_KEY;
|
|
532
548
|
const client = new import_sdk.ModerationClient({
|
|
533
|
-
apiKey
|
|
549
|
+
apiKey,
|
|
534
550
|
apiUrl: config.apiUrl
|
|
535
551
|
});
|
|
536
552
|
const server = new import_mcp2.McpServer({
|
|
537
553
|
name: "vettly",
|
|
538
554
|
version: "0.1.0"
|
|
539
555
|
});
|
|
540
|
-
registerTools(server, client);
|
|
556
|
+
registerTools(server, client, demoMode);
|
|
541
557
|
registerResources(server, client);
|
|
542
558
|
registerPrompts(server);
|
|
543
559
|
return server;
|
|
@@ -547,22 +563,8 @@ function createVettlyMcpServer(config) {
|
|
|
547
563
|
async function main() {
|
|
548
564
|
const apiKey = process.env.VETTLY_API_KEY;
|
|
549
565
|
if (!apiKey) {
|
|
550
|
-
console.error("
|
|
551
|
-
console.error("");
|
|
552
|
-
console.error("Usage:");
|
|
553
|
-
console.error(" VETTLY_API_KEY=vettly_live_xxx npx @vettly/mcp");
|
|
554
|
-
console.error("");
|
|
555
|
-
console.error("Or configure in Claude Desktop:");
|
|
556
|
-
console.error(" {");
|
|
557
|
-
console.error(' "mcpServers": {');
|
|
558
|
-
console.error(' "vettly": {');
|
|
559
|
-
console.error(' "command": "npx",');
|
|
560
|
-
console.error(' "args": ["-y", "@vettly/mcp"],');
|
|
561
|
-
console.error(' "env": { "VETTLY_API_KEY": "vettly_live_xxx" }');
|
|
562
|
-
console.error(" }");
|
|
563
|
-
console.error(" }");
|
|
564
|
-
console.error(" }");
|
|
565
|
-
process.exit(1);
|
|
566
|
+
console.error("No VETTLY_API_KEY set \u2014 starting in demo mode (limited to 10 requests/day).");
|
|
567
|
+
console.error("Get your free API key at https://vettly.dev");
|
|
566
568
|
}
|
|
567
569
|
const server = createVettlyMcpServer({
|
|
568
570
|
apiKey,
|
package/dist/bin/vettly-mcp.js
CHANGED
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createVettlyMcpServer
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-PMBQ22Z2.js";
|
|
5
5
|
|
|
6
6
|
// src/bin/vettly-mcp.ts
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
8
|
async function main() {
|
|
9
9
|
const apiKey = process.env.VETTLY_API_KEY;
|
|
10
10
|
if (!apiKey) {
|
|
11
|
-
console.error("
|
|
12
|
-
console.error("");
|
|
13
|
-
console.error("Usage:");
|
|
14
|
-
console.error(" VETTLY_API_KEY=vettly_live_xxx npx @vettly/mcp");
|
|
15
|
-
console.error("");
|
|
16
|
-
console.error("Or configure in Claude Desktop:");
|
|
17
|
-
console.error(" {");
|
|
18
|
-
console.error(' "mcpServers": {');
|
|
19
|
-
console.error(' "vettly": {');
|
|
20
|
-
console.error(' "command": "npx",');
|
|
21
|
-
console.error(' "args": ["-y", "@vettly/mcp"],');
|
|
22
|
-
console.error(' "env": { "VETTLY_API_KEY": "vettly_live_xxx" }');
|
|
23
|
-
console.error(" }");
|
|
24
|
-
console.error(" }");
|
|
25
|
-
console.error(" }");
|
|
26
|
-
process.exit(1);
|
|
11
|
+
console.error("No VETTLY_API_KEY set \u2014 starting in demo mode (limited to 10 requests/day).");
|
|
12
|
+
console.error("Get your free API key at https://vettly.dev");
|
|
27
13
|
}
|
|
28
14
|
const server = createVettlyMcpServer({
|
|
29
15
|
apiKey,
|
|
@@ -9,7 +9,7 @@ var ModerateInputSchema = z.object({
|
|
|
9
9
|
policyId: z.string().min(1).describe("The policy ID to use for moderation"),
|
|
10
10
|
contentType: z.enum(["text", "image", "video"]).default("text").describe("Type of content being moderated")
|
|
11
11
|
});
|
|
12
|
-
function registerModerateTool(server, client) {
|
|
12
|
+
function registerModerateTool(server, client, demoMode = false) {
|
|
13
13
|
server.tool(
|
|
14
14
|
"moderate_content",
|
|
15
15
|
"Check text, image, or video content against a Vettly moderation policy. Returns safety assessment with category scores.",
|
|
@@ -33,24 +33,25 @@ function registerModerateTool(server, client) {
|
|
|
33
33
|
policyId: input.policyId,
|
|
34
34
|
contentType: input.contentType
|
|
35
35
|
});
|
|
36
|
+
const text = JSON.stringify(
|
|
37
|
+
{
|
|
38
|
+
decisionId: result.decisionId,
|
|
39
|
+
safe: result.safe,
|
|
40
|
+
flagged: result.flagged,
|
|
41
|
+
action: result.action,
|
|
42
|
+
categories: result.categories,
|
|
43
|
+
provider: result.provider,
|
|
44
|
+
latency: result.latency,
|
|
45
|
+
cost: result.cost
|
|
46
|
+
},
|
|
47
|
+
null,
|
|
48
|
+
2
|
|
49
|
+
);
|
|
36
50
|
return {
|
|
37
51
|
content: [
|
|
38
52
|
{
|
|
39
53
|
type: "text",
|
|
40
|
-
text:
|
|
41
|
-
{
|
|
42
|
-
decisionId: result.decisionId,
|
|
43
|
-
safe: result.safe,
|
|
44
|
-
flagged: result.flagged,
|
|
45
|
-
action: result.action,
|
|
46
|
-
categories: result.categories,
|
|
47
|
-
provider: result.provider,
|
|
48
|
-
latency: result.latency,
|
|
49
|
-
cost: result.cost
|
|
50
|
-
},
|
|
51
|
-
null,
|
|
52
|
-
2
|
|
53
|
-
)
|
|
54
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
54
55
|
}
|
|
55
56
|
]
|
|
56
57
|
};
|
|
@@ -74,7 +75,7 @@ import { z as z2 } from "zod";
|
|
|
74
75
|
var ValidateInputSchema = z2.object({
|
|
75
76
|
yamlContent: z2.string().min(1).describe("The YAML policy content to validate")
|
|
76
77
|
});
|
|
77
|
-
function registerValidateTool(server, client) {
|
|
78
|
+
function registerValidateTool(server, client, demoMode = false) {
|
|
78
79
|
server.tool(
|
|
79
80
|
"validate_policy",
|
|
80
81
|
"Validate a Vettly policy YAML without saving it. Returns validation results with any syntax or configuration errors.",
|
|
@@ -92,11 +93,12 @@ function registerValidateTool(server, client) {
|
|
|
92
93
|
const input = ValidateInputSchema.parse(args);
|
|
93
94
|
try {
|
|
94
95
|
const result = await client.validatePolicy(input.yamlContent);
|
|
96
|
+
const text = JSON.stringify(result, null, 2);
|
|
95
97
|
return {
|
|
96
98
|
content: [
|
|
97
99
|
{
|
|
98
100
|
type: "text",
|
|
99
|
-
text:
|
|
101
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
100
102
|
}
|
|
101
103
|
]
|
|
102
104
|
};
|
|
@@ -116,11 +118,14 @@ function registerValidateTool(server, client) {
|
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
// src/tools/list-policies.ts
|
|
119
|
-
|
|
121
|
+
import { z as z3 } from "zod";
|
|
122
|
+
function registerListPoliciesTool(server, client, demoMode = false) {
|
|
120
123
|
server.tool(
|
|
121
124
|
"list_policies",
|
|
122
125
|
"List all moderation policies available in your Vettly account.",
|
|
123
|
-
{
|
|
126
|
+
{
|
|
127
|
+
status: z3.enum(["active", "all"]).default("active").describe("Filter by policy status: active only or all policies")
|
|
128
|
+
},
|
|
124
129
|
{
|
|
125
130
|
title: "List Policies",
|
|
126
131
|
readOnlyHint: true,
|
|
@@ -128,14 +133,19 @@ function registerListPoliciesTool(server, client) {
|
|
|
128
133
|
idempotentHint: true,
|
|
129
134
|
openWorldHint: true
|
|
130
135
|
},
|
|
131
|
-
async () => {
|
|
136
|
+
async (args) => {
|
|
132
137
|
try {
|
|
133
138
|
const result = await client.listPolicies();
|
|
139
|
+
let policies = result;
|
|
140
|
+
if (args.status === "active" && Array.isArray(policies)) {
|
|
141
|
+
policies = policies.filter((p) => p.status !== "archived");
|
|
142
|
+
}
|
|
143
|
+
const text = JSON.stringify(policies, null, 2);
|
|
134
144
|
return {
|
|
135
145
|
content: [
|
|
136
146
|
{
|
|
137
147
|
type: "text",
|
|
138
|
-
text:
|
|
148
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
139
149
|
}
|
|
140
150
|
]
|
|
141
151
|
};
|
|
@@ -155,11 +165,11 @@ function registerListPoliciesTool(server, client) {
|
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
// src/tools/usage-stats.ts
|
|
158
|
-
import { z as
|
|
159
|
-
var UsageStatsInputSchema =
|
|
160
|
-
days:
|
|
168
|
+
import { z as z4 } from "zod";
|
|
169
|
+
var UsageStatsInputSchema = z4.object({
|
|
170
|
+
days: z4.number().min(1).max(365).default(30).describe("Number of days to include in statistics")
|
|
161
171
|
});
|
|
162
|
-
function registerUsageStatsTool(server, client) {
|
|
172
|
+
function registerUsageStatsTool(server, client, demoMode = false) {
|
|
163
173
|
server.tool(
|
|
164
174
|
"get_usage_stats",
|
|
165
175
|
"Get usage statistics for your Vettly account including request counts, costs, and moderation outcomes.",
|
|
@@ -177,11 +187,12 @@ function registerUsageStatsTool(server, client) {
|
|
|
177
187
|
const input = UsageStatsInputSchema.parse(args);
|
|
178
188
|
try {
|
|
179
189
|
const result = await client.getUsageStats(input.days);
|
|
190
|
+
const text = JSON.stringify(result, null, 2);
|
|
180
191
|
return {
|
|
181
192
|
content: [
|
|
182
193
|
{
|
|
183
194
|
type: "text",
|
|
184
|
-
text:
|
|
195
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
185
196
|
}
|
|
186
197
|
]
|
|
187
198
|
};
|
|
@@ -201,14 +212,14 @@ function registerUsageStatsTool(server, client) {
|
|
|
201
212
|
}
|
|
202
213
|
|
|
203
214
|
// src/tools/decisions.ts
|
|
204
|
-
import { z as
|
|
205
|
-
var DecisionsInputSchema =
|
|
206
|
-
limit:
|
|
207
|
-
flagged:
|
|
208
|
-
policyId:
|
|
209
|
-
contentType:
|
|
215
|
+
import { z as z5 } from "zod";
|
|
216
|
+
var DecisionsInputSchema = z5.object({
|
|
217
|
+
limit: z5.number().min(1).max(50).default(10).describe("Number of decisions to return"),
|
|
218
|
+
flagged: z5.boolean().optional().describe("Filter to only flagged content (true) or safe content (false)"),
|
|
219
|
+
policyId: z5.string().optional().describe("Filter by specific policy ID"),
|
|
220
|
+
contentType: z5.enum(["text", "image", "video"]).optional().describe("Filter by content type")
|
|
210
221
|
});
|
|
211
|
-
function registerDecisionsTool(server, client) {
|
|
222
|
+
function registerDecisionsTool(server, client, demoMode = false) {
|
|
212
223
|
server.tool(
|
|
213
224
|
"get_recent_decisions",
|
|
214
225
|
"Get recent moderation decisions with optional filtering by outcome, content type, or policy.",
|
|
@@ -239,11 +250,12 @@ function registerDecisionsTool(server, client) {
|
|
|
239
250
|
if (input.contentType) {
|
|
240
251
|
decisions = decisions.filter((d) => d.contentType === input.contentType);
|
|
241
252
|
}
|
|
253
|
+
const text = JSON.stringify({ decisions }, null, 2);
|
|
242
254
|
return {
|
|
243
255
|
content: [
|
|
244
256
|
{
|
|
245
257
|
type: "text",
|
|
246
|
-
text:
|
|
258
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
247
259
|
}
|
|
248
260
|
]
|
|
249
261
|
};
|
|
@@ -263,12 +275,13 @@ function registerDecisionsTool(server, client) {
|
|
|
263
275
|
}
|
|
264
276
|
|
|
265
277
|
// src/tools/index.ts
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
278
|
+
var DEMO_FOOTER = "\n\n---\n\u{1F511} Demo mode (10 requests/day). Get your free API key at https://vettly.dev";
|
|
279
|
+
function registerTools(server, client, demoMode = false) {
|
|
280
|
+
registerModerateTool(server, client, demoMode);
|
|
281
|
+
registerValidateTool(server, client, demoMode);
|
|
282
|
+
registerListPoliciesTool(server, client, demoMode);
|
|
283
|
+
registerUsageStatsTool(server, client, demoMode);
|
|
284
|
+
registerDecisionsTool(server, client, demoMode);
|
|
272
285
|
}
|
|
273
286
|
|
|
274
287
|
// src/resources/policies.ts
|
|
@@ -388,12 +401,12 @@ The response includes:
|
|
|
388
401
|
}
|
|
389
402
|
|
|
390
403
|
// src/prompts/write-policy.ts
|
|
391
|
-
import { z as
|
|
404
|
+
import { z as z6 } from "zod";
|
|
392
405
|
function registerWritePolicy(server) {
|
|
393
406
|
server.prompt(
|
|
394
407
|
"write-policy",
|
|
395
408
|
"Guide for writing a Vettly moderation policy in YAML. Optionally tailored to a specific platform type.",
|
|
396
|
-
{ platform:
|
|
409
|
+
{ platform: z6.string().optional().describe('The type of platform (e.g. "kids app", "marketplace", "social media", "dating app")') },
|
|
397
410
|
async (args) => {
|
|
398
411
|
const platformContext = args.platform ? `
|
|
399
412
|
|
|
@@ -463,12 +476,12 @@ Common categories: \`hate_speech\`, \`violence\`, \`sexual_content\`, \`self_har
|
|
|
463
476
|
}
|
|
464
477
|
|
|
465
478
|
// src/prompts/review-flagged-content.ts
|
|
466
|
-
import { z as
|
|
479
|
+
import { z as z7 } from "zod";
|
|
467
480
|
function registerReviewFlaggedContent(server) {
|
|
468
481
|
server.prompt(
|
|
469
482
|
"review-flagged-content",
|
|
470
483
|
"Template for reviewing flagged moderation decisions. Optionally filtered to a specific policy.",
|
|
471
|
-
{ policyId:
|
|
484
|
+
{ policyId: z7.string().optional().describe("Filter flagged decisions to a specific policy ID") },
|
|
472
485
|
async (args) => {
|
|
473
486
|
const policyFilter = args.policyId ? `
|
|
474
487
|
|
|
@@ -522,16 +535,19 @@ function registerPrompts(server) {
|
|
|
522
535
|
}
|
|
523
536
|
|
|
524
537
|
// src/server.ts
|
|
538
|
+
var DEMO_API_KEY = "vettly_test_demo_mcp_readonly";
|
|
525
539
|
function createVettlyMcpServer(config) {
|
|
540
|
+
const demoMode = !config.apiKey;
|
|
541
|
+
const apiKey = config.apiKey || DEMO_API_KEY;
|
|
526
542
|
const client = new ModerationClient({
|
|
527
|
-
apiKey
|
|
543
|
+
apiKey,
|
|
528
544
|
apiUrl: config.apiUrl
|
|
529
545
|
});
|
|
530
546
|
const server = new McpServer({
|
|
531
547
|
name: "vettly",
|
|
532
548
|
version: "0.1.0"
|
|
533
549
|
});
|
|
534
|
-
registerTools(server, client);
|
|
550
|
+
registerTools(server, client, demoMode);
|
|
535
551
|
registerResources(server, client);
|
|
536
552
|
registerPrompts(server);
|
|
537
553
|
return server;
|
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
createVettlyMcpServer: () => createVettlyMcpServer
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
var
|
|
27
|
+
var import_zod8 = require("zod");
|
|
28
28
|
|
|
29
29
|
// src/server.ts
|
|
30
30
|
var import_mcp2 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
@@ -37,7 +37,7 @@ var ModerateInputSchema = import_zod.z.object({
|
|
|
37
37
|
policyId: import_zod.z.string().min(1).describe("The policy ID to use for moderation"),
|
|
38
38
|
contentType: import_zod.z.enum(["text", "image", "video"]).default("text").describe("Type of content being moderated")
|
|
39
39
|
});
|
|
40
|
-
function registerModerateTool(server, client) {
|
|
40
|
+
function registerModerateTool(server, client, demoMode = false) {
|
|
41
41
|
server.tool(
|
|
42
42
|
"moderate_content",
|
|
43
43
|
"Check text, image, or video content against a Vettly moderation policy. Returns safety assessment with category scores.",
|
|
@@ -61,24 +61,25 @@ function registerModerateTool(server, client) {
|
|
|
61
61
|
policyId: input.policyId,
|
|
62
62
|
contentType: input.contentType
|
|
63
63
|
});
|
|
64
|
+
const text = JSON.stringify(
|
|
65
|
+
{
|
|
66
|
+
decisionId: result.decisionId,
|
|
67
|
+
safe: result.safe,
|
|
68
|
+
flagged: result.flagged,
|
|
69
|
+
action: result.action,
|
|
70
|
+
categories: result.categories,
|
|
71
|
+
provider: result.provider,
|
|
72
|
+
latency: result.latency,
|
|
73
|
+
cost: result.cost
|
|
74
|
+
},
|
|
75
|
+
null,
|
|
76
|
+
2
|
|
77
|
+
);
|
|
64
78
|
return {
|
|
65
79
|
content: [
|
|
66
80
|
{
|
|
67
81
|
type: "text",
|
|
68
|
-
text:
|
|
69
|
-
{
|
|
70
|
-
decisionId: result.decisionId,
|
|
71
|
-
safe: result.safe,
|
|
72
|
-
flagged: result.flagged,
|
|
73
|
-
action: result.action,
|
|
74
|
-
categories: result.categories,
|
|
75
|
-
provider: result.provider,
|
|
76
|
-
latency: result.latency,
|
|
77
|
-
cost: result.cost
|
|
78
|
-
},
|
|
79
|
-
null,
|
|
80
|
-
2
|
|
81
|
-
)
|
|
82
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
82
83
|
}
|
|
83
84
|
]
|
|
84
85
|
};
|
|
@@ -102,7 +103,7 @@ var import_zod2 = require("zod");
|
|
|
102
103
|
var ValidateInputSchema = import_zod2.z.object({
|
|
103
104
|
yamlContent: import_zod2.z.string().min(1).describe("The YAML policy content to validate")
|
|
104
105
|
});
|
|
105
|
-
function registerValidateTool(server, client) {
|
|
106
|
+
function registerValidateTool(server, client, demoMode = false) {
|
|
106
107
|
server.tool(
|
|
107
108
|
"validate_policy",
|
|
108
109
|
"Validate a Vettly policy YAML without saving it. Returns validation results with any syntax or configuration errors.",
|
|
@@ -120,11 +121,12 @@ function registerValidateTool(server, client) {
|
|
|
120
121
|
const input = ValidateInputSchema.parse(args);
|
|
121
122
|
try {
|
|
122
123
|
const result = await client.validatePolicy(input.yamlContent);
|
|
124
|
+
const text = JSON.stringify(result, null, 2);
|
|
123
125
|
return {
|
|
124
126
|
content: [
|
|
125
127
|
{
|
|
126
128
|
type: "text",
|
|
127
|
-
text:
|
|
129
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
128
130
|
}
|
|
129
131
|
]
|
|
130
132
|
};
|
|
@@ -144,11 +146,14 @@ function registerValidateTool(server, client) {
|
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
// src/tools/list-policies.ts
|
|
147
|
-
|
|
149
|
+
var import_zod3 = require("zod");
|
|
150
|
+
function registerListPoliciesTool(server, client, demoMode = false) {
|
|
148
151
|
server.tool(
|
|
149
152
|
"list_policies",
|
|
150
153
|
"List all moderation policies available in your Vettly account.",
|
|
151
|
-
{
|
|
154
|
+
{
|
|
155
|
+
status: import_zod3.z.enum(["active", "all"]).default("active").describe("Filter by policy status: active only or all policies")
|
|
156
|
+
},
|
|
152
157
|
{
|
|
153
158
|
title: "List Policies",
|
|
154
159
|
readOnlyHint: true,
|
|
@@ -156,14 +161,19 @@ function registerListPoliciesTool(server, client) {
|
|
|
156
161
|
idempotentHint: true,
|
|
157
162
|
openWorldHint: true
|
|
158
163
|
},
|
|
159
|
-
async () => {
|
|
164
|
+
async (args) => {
|
|
160
165
|
try {
|
|
161
166
|
const result = await client.listPolicies();
|
|
167
|
+
let policies = result;
|
|
168
|
+
if (args.status === "active" && Array.isArray(policies)) {
|
|
169
|
+
policies = policies.filter((p) => p.status !== "archived");
|
|
170
|
+
}
|
|
171
|
+
const text = JSON.stringify(policies, null, 2);
|
|
162
172
|
return {
|
|
163
173
|
content: [
|
|
164
174
|
{
|
|
165
175
|
type: "text",
|
|
166
|
-
text:
|
|
176
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
167
177
|
}
|
|
168
178
|
]
|
|
169
179
|
};
|
|
@@ -183,11 +193,11 @@ function registerListPoliciesTool(server, client) {
|
|
|
183
193
|
}
|
|
184
194
|
|
|
185
195
|
// src/tools/usage-stats.ts
|
|
186
|
-
var
|
|
187
|
-
var UsageStatsInputSchema =
|
|
188
|
-
days:
|
|
196
|
+
var import_zod4 = require("zod");
|
|
197
|
+
var UsageStatsInputSchema = import_zod4.z.object({
|
|
198
|
+
days: import_zod4.z.number().min(1).max(365).default(30).describe("Number of days to include in statistics")
|
|
189
199
|
});
|
|
190
|
-
function registerUsageStatsTool(server, client) {
|
|
200
|
+
function registerUsageStatsTool(server, client, demoMode = false) {
|
|
191
201
|
server.tool(
|
|
192
202
|
"get_usage_stats",
|
|
193
203
|
"Get usage statistics for your Vettly account including request counts, costs, and moderation outcomes.",
|
|
@@ -205,11 +215,12 @@ function registerUsageStatsTool(server, client) {
|
|
|
205
215
|
const input = UsageStatsInputSchema.parse(args);
|
|
206
216
|
try {
|
|
207
217
|
const result = await client.getUsageStats(input.days);
|
|
218
|
+
const text = JSON.stringify(result, null, 2);
|
|
208
219
|
return {
|
|
209
220
|
content: [
|
|
210
221
|
{
|
|
211
222
|
type: "text",
|
|
212
|
-
text:
|
|
223
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
213
224
|
}
|
|
214
225
|
]
|
|
215
226
|
};
|
|
@@ -229,14 +240,14 @@ function registerUsageStatsTool(server, client) {
|
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
// src/tools/decisions.ts
|
|
232
|
-
var
|
|
233
|
-
var DecisionsInputSchema =
|
|
234
|
-
limit:
|
|
235
|
-
flagged:
|
|
236
|
-
policyId:
|
|
237
|
-
contentType:
|
|
243
|
+
var import_zod5 = require("zod");
|
|
244
|
+
var DecisionsInputSchema = import_zod5.z.object({
|
|
245
|
+
limit: import_zod5.z.number().min(1).max(50).default(10).describe("Number of decisions to return"),
|
|
246
|
+
flagged: import_zod5.z.boolean().optional().describe("Filter to only flagged content (true) or safe content (false)"),
|
|
247
|
+
policyId: import_zod5.z.string().optional().describe("Filter by specific policy ID"),
|
|
248
|
+
contentType: import_zod5.z.enum(["text", "image", "video"]).optional().describe("Filter by content type")
|
|
238
249
|
});
|
|
239
|
-
function registerDecisionsTool(server, client) {
|
|
250
|
+
function registerDecisionsTool(server, client, demoMode = false) {
|
|
240
251
|
server.tool(
|
|
241
252
|
"get_recent_decisions",
|
|
242
253
|
"Get recent moderation decisions with optional filtering by outcome, content type, or policy.",
|
|
@@ -267,11 +278,12 @@ function registerDecisionsTool(server, client) {
|
|
|
267
278
|
if (input.contentType) {
|
|
268
279
|
decisions = decisions.filter((d) => d.contentType === input.contentType);
|
|
269
280
|
}
|
|
281
|
+
const text = JSON.stringify({ decisions }, null, 2);
|
|
270
282
|
return {
|
|
271
283
|
content: [
|
|
272
284
|
{
|
|
273
285
|
type: "text",
|
|
274
|
-
text:
|
|
286
|
+
text: text + (demoMode ? DEMO_FOOTER : "")
|
|
275
287
|
}
|
|
276
288
|
]
|
|
277
289
|
};
|
|
@@ -291,12 +303,13 @@ function registerDecisionsTool(server, client) {
|
|
|
291
303
|
}
|
|
292
304
|
|
|
293
305
|
// src/tools/index.ts
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
306
|
+
var DEMO_FOOTER = "\n\n---\n\u{1F511} Demo mode (10 requests/day). Get your free API key at https://vettly.dev";
|
|
307
|
+
function registerTools(server, client, demoMode = false) {
|
|
308
|
+
registerModerateTool(server, client, demoMode);
|
|
309
|
+
registerValidateTool(server, client, demoMode);
|
|
310
|
+
registerListPoliciesTool(server, client, demoMode);
|
|
311
|
+
registerUsageStatsTool(server, client, demoMode);
|
|
312
|
+
registerDecisionsTool(server, client, demoMode);
|
|
300
313
|
}
|
|
301
314
|
|
|
302
315
|
// src/resources/policies.ts
|
|
@@ -416,12 +429,12 @@ The response includes:
|
|
|
416
429
|
}
|
|
417
430
|
|
|
418
431
|
// src/prompts/write-policy.ts
|
|
419
|
-
var
|
|
432
|
+
var import_zod6 = require("zod");
|
|
420
433
|
function registerWritePolicy(server) {
|
|
421
434
|
server.prompt(
|
|
422
435
|
"write-policy",
|
|
423
436
|
"Guide for writing a Vettly moderation policy in YAML. Optionally tailored to a specific platform type.",
|
|
424
|
-
{ platform:
|
|
437
|
+
{ platform: import_zod6.z.string().optional().describe('The type of platform (e.g. "kids app", "marketplace", "social media", "dating app")') },
|
|
425
438
|
async (args) => {
|
|
426
439
|
const platformContext = args.platform ? `
|
|
427
440
|
|
|
@@ -491,12 +504,12 @@ Common categories: \`hate_speech\`, \`violence\`, \`sexual_content\`, \`self_har
|
|
|
491
504
|
}
|
|
492
505
|
|
|
493
506
|
// src/prompts/review-flagged-content.ts
|
|
494
|
-
var
|
|
507
|
+
var import_zod7 = require("zod");
|
|
495
508
|
function registerReviewFlaggedContent(server) {
|
|
496
509
|
server.prompt(
|
|
497
510
|
"review-flagged-content",
|
|
498
511
|
"Template for reviewing flagged moderation decisions. Optionally filtered to a specific policy.",
|
|
499
|
-
{ policyId:
|
|
512
|
+
{ policyId: import_zod7.z.string().optional().describe("Filter flagged decisions to a specific policy ID") },
|
|
500
513
|
async (args) => {
|
|
501
514
|
const policyFilter = args.policyId ? `
|
|
502
515
|
|
|
@@ -550,27 +563,30 @@ function registerPrompts(server) {
|
|
|
550
563
|
}
|
|
551
564
|
|
|
552
565
|
// src/server.ts
|
|
566
|
+
var DEMO_API_KEY = "vettly_test_demo_mcp_readonly";
|
|
553
567
|
function createVettlyMcpServer(config) {
|
|
568
|
+
const demoMode = !config.apiKey;
|
|
569
|
+
const apiKey = config.apiKey || DEMO_API_KEY;
|
|
554
570
|
const client = new import_sdk.ModerationClient({
|
|
555
|
-
apiKey
|
|
571
|
+
apiKey,
|
|
556
572
|
apiUrl: config.apiUrl
|
|
557
573
|
});
|
|
558
574
|
const server = new import_mcp2.McpServer({
|
|
559
575
|
name: "vettly",
|
|
560
576
|
version: "0.1.0"
|
|
561
577
|
});
|
|
562
|
-
registerTools(server, client);
|
|
578
|
+
registerTools(server, client, demoMode);
|
|
563
579
|
registerResources(server, client);
|
|
564
580
|
registerPrompts(server);
|
|
565
581
|
return server;
|
|
566
582
|
}
|
|
567
583
|
|
|
568
584
|
// src/index.ts
|
|
569
|
-
var configSchema =
|
|
570
|
-
vettlyApiKey:
|
|
571
|
-
"Your Vettly API key (starts with vettly_live_ or vettly_test_). Get one free at https://vettly.dev"
|
|
585
|
+
var configSchema = import_zod8.z.object({
|
|
586
|
+
vettlyApiKey: import_zod8.z.string().optional().describe(
|
|
587
|
+
"Your Vettly API key (starts with vettly_live_ or vettly_test_). Leave empty for demo mode (10 requests/day). Get one free at https://vettly.dev"
|
|
572
588
|
),
|
|
573
|
-
apiUrl:
|
|
589
|
+
apiUrl: import_zod8.z.string().url().default("https://api.vettly.dev").describe("Custom API base URL. Only change this for self-hosted Vettly instances.")
|
|
574
590
|
});
|
|
575
591
|
// Annotate the CommonJS export names for ESM import in node:
|
|
576
592
|
0 && (module.exports = {
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createVettlyMcpServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PMBQ22Z2.js";
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
var configSchema = z.object({
|
|
8
|
-
vettlyApiKey: z.string().describe(
|
|
9
|
-
"Your Vettly API key (starts with vettly_live_ or vettly_test_). Get one free at https://vettly.dev"
|
|
8
|
+
vettlyApiKey: z.string().optional().describe(
|
|
9
|
+
"Your Vettly API key (starts with vettly_live_ or vettly_test_). Leave empty for demo mode (10 requests/day). Get one free at https://vettly.dev"
|
|
10
10
|
),
|
|
11
11
|
apiUrl: z.string().url().default("https://api.vettly.dev").describe("Custom API base URL. Only change this for self-hosted Vettly instances.")
|
|
12
12
|
});
|
package/package.json
CHANGED
package/smithery.yaml
CHANGED
|
@@ -5,12 +5,10 @@ startCommand:
|
|
|
5
5
|
|
|
6
6
|
configSchema:
|
|
7
7
|
type: object
|
|
8
|
-
required:
|
|
9
|
-
- vettlyApiKey
|
|
10
8
|
properties:
|
|
11
9
|
vettlyApiKey:
|
|
12
10
|
type: string
|
|
13
|
-
description: Your Vettly API key (starts with vettly_live_ or vettly_test_). Get one free at https://vettly.dev
|
|
11
|
+
description: Your Vettly API key (starts with vettly_live_ or vettly_test_). Leave empty for demo mode (10 requests/day). Get one free at https://vettly.dev
|
|
14
12
|
apiUrl:
|
|
15
13
|
type: string
|
|
16
14
|
default: https://api.vettly.dev
|
|
@@ -18,4 +16,4 @@ configSchema:
|
|
|
18
16
|
|
|
19
17
|
commandFunction:
|
|
20
18
|
|-
|
|
21
|
-
(config) => ({ command: 'npx', args: ['-y', '@vettly/mcp'], env: { VETTLY_API_KEY: config.vettlyApiKey, ...(config.apiUrl && config.apiUrl !== 'https://api.vettly.dev' ? { VETTLY_API_URL: config.apiUrl } : {}) } })
|
|
19
|
+
(config) => ({ command: 'npx', args: ['-y', '@vettly/mcp'], env: { ...(config.vettlyApiKey ? { VETTLY_API_KEY: config.vettlyApiKey } : {}), ...(config.apiUrl && config.apiUrl !== 'https://api.vettly.dev' ? { VETTLY_API_URL: config.apiUrl } : {}) } })
|