@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.
@@ -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: JSON.stringify(
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: JSON.stringify(result, null, 2)
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
- function registerListPoliciesTool(server, client) {
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: JSON.stringify(result, null, 2)
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 import_zod3 = require("zod");
165
- var UsageStatsInputSchema = import_zod3.z.object({
166
- days: import_zod3.z.number().min(1).max(365).default(30).describe("Number of days to include in statistics")
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: JSON.stringify(result, null, 2)
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 import_zod4 = require("zod");
211
- var DecisionsInputSchema = import_zod4.z.object({
212
- limit: import_zod4.z.number().min(1).max(50).default(10).describe("Number of decisions to return"),
213
- flagged: import_zod4.z.boolean().optional().describe("Filter to only flagged content (true) or safe content (false)"),
214
- policyId: import_zod4.z.string().optional().describe("Filter by specific policy ID"),
215
- contentType: import_zod4.z.enum(["text", "image", "video"]).optional().describe("Filter by content type")
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: JSON.stringify({ decisions }, null, 2)
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
- function registerTools(server, client) {
273
- registerModerateTool(server, client);
274
- registerValidateTool(server, client);
275
- registerListPoliciesTool(server, client);
276
- registerUsageStatsTool(server, client);
277
- registerDecisionsTool(server, client);
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 import_zod5 = require("zod");
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: import_zod5.z.string().optional().describe('The type of platform (e.g. "kids app", "marketplace", "social media", "dating app")') },
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 import_zod6 = require("zod");
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: import_zod6.z.string().optional().describe("Filter flagged decisions to a specific policy ID") },
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: config.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("Error: VETTLY_API_KEY environment variable is required");
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,
@@ -1,29 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createVettlyMcpServer
4
- } from "../chunk-L5ZHIGXM.js";
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("Error: VETTLY_API_KEY environment variable is required");
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: JSON.stringify(
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: JSON.stringify(result, null, 2)
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
- function registerListPoliciesTool(server, client) {
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: JSON.stringify(result, null, 2)
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 z3 } from "zod";
159
- var UsageStatsInputSchema = z3.object({
160
- days: z3.number().min(1).max(365).default(30).describe("Number of days to include in statistics")
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: JSON.stringify(result, null, 2)
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 z4 } from "zod";
205
- var DecisionsInputSchema = z4.object({
206
- limit: z4.number().min(1).max(50).default(10).describe("Number of decisions to return"),
207
- flagged: z4.boolean().optional().describe("Filter to only flagged content (true) or safe content (false)"),
208
- policyId: z4.string().optional().describe("Filter by specific policy ID"),
209
- contentType: z4.enum(["text", "image", "video"]).optional().describe("Filter by content type")
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: JSON.stringify({ decisions }, null, 2)
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
- function registerTools(server, client) {
267
- registerModerateTool(server, client);
268
- registerValidateTool(server, client);
269
- registerListPoliciesTool(server, client);
270
- registerUsageStatsTool(server, client);
271
- registerDecisionsTool(server, client);
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 z5 } from "zod";
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: z5.string().optional().describe('The type of platform (e.g. "kids app", "marketplace", "social media", "dating app")') },
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 z6 } from "zod";
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: z6.string().optional().describe("Filter flagged decisions to a specific policy ID") },
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: config.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 import_zod7 = require("zod");
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: JSON.stringify(
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: JSON.stringify(result, null, 2)
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
- function registerListPoliciesTool(server, client) {
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: JSON.stringify(result, null, 2)
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 import_zod3 = require("zod");
187
- var UsageStatsInputSchema = import_zod3.z.object({
188
- days: import_zod3.z.number().min(1).max(365).default(30).describe("Number of days to include in statistics")
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: JSON.stringify(result, null, 2)
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 import_zod4 = require("zod");
233
- var DecisionsInputSchema = import_zod4.z.object({
234
- limit: import_zod4.z.number().min(1).max(50).default(10).describe("Number of decisions to return"),
235
- flagged: import_zod4.z.boolean().optional().describe("Filter to only flagged content (true) or safe content (false)"),
236
- policyId: import_zod4.z.string().optional().describe("Filter by specific policy ID"),
237
- contentType: import_zod4.z.enum(["text", "image", "video"]).optional().describe("Filter by content type")
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: JSON.stringify({ decisions }, null, 2)
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
- function registerTools(server, client) {
295
- registerModerateTool(server, client);
296
- registerValidateTool(server, client);
297
- registerListPoliciesTool(server, client);
298
- registerUsageStatsTool(server, client);
299
- registerDecisionsTool(server, client);
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 import_zod5 = require("zod");
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: import_zod5.z.string().optional().describe('The type of platform (e.g. "kids app", "marketplace", "social media", "dating app")') },
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 import_zod6 = require("zod");
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: import_zod6.z.string().optional().describe("Filter flagged decisions to a specific policy ID") },
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: config.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 = import_zod7.z.object({
570
- vettlyApiKey: import_zod7.z.string().describe(
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: import_zod7.z.string().url().default("https://api.vettly.dev").describe("Custom API base URL. Only change this for self-hosted Vettly instances.")
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-L5ZHIGXM.js";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vettly/mcp",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "mcpName": "io.github.code-with-brian/vettly",
5
5
  "description": "MCP server for content moderation with Claude and Cursor. Content moderation policy testing.",
6
6
  "type": "module",
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 } : {}) } })