@voidly/mcp-server 1.0.1 → 1.1.1

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.
Files changed (2) hide show
  1. package/dist/index.js +105 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16,8 +16,8 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
16
16
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
17
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
18
18
  // Voidly API endpoints
19
- const VOIDLY_API = 'https://censorship.voidly.ai';
20
- const VOIDLY_DATA_API = 'https://voidly.ai/api/data';
19
+ const VOIDLY_API = 'https://api.voidly.ai';
20
+ const VOIDLY_DATA_API = 'https://api.voidly.ai/data';
21
21
  // Country metadata for enriching responses
22
22
  const COUNTRY_NAMES = {
23
23
  CN: 'China', IR: 'Iran', RU: 'Russia', VE: 'Venezuela', CU: 'Cuba',
@@ -161,7 +161,7 @@ async function getMostCensored(limit = 10) {
161
161
  return result;
162
162
  }
163
163
  async function getActiveIncidents() {
164
- const data = await fetchJson(`${VOIDLY_API}/v1/censorship-index/incidents`);
164
+ const data = await fetchJson(`${VOIDLY_DATA_API}/incidents?status=active&limit=50`);
165
165
  let result = `# Active Censorship Incidents\n\n`;
166
166
  result += `Total: ${data.count} incidents\n\n`;
167
167
  if (data.incidents.length === 0) {
@@ -187,6 +187,84 @@ async function getActiveIncidents() {
187
187
  result += `URL: https://voidly.ai/censorship-index\n`;
188
188
  return result;
189
189
  }
190
+ async function verifyClaim(claim, requireEvidence = false) {
191
+ // Use POST for verify-claim
192
+ const response = await fetch(`${VOIDLY_API}/verify-claim`, {
193
+ method: 'POST',
194
+ headers: {
195
+ 'Content-Type': 'application/json',
196
+ 'User-Agent': 'Voidly-MCP-Server/1.0',
197
+ },
198
+ body: JSON.stringify({ claim, require_evidence: requireEvidence }),
199
+ });
200
+ if (!response.ok) {
201
+ throw new Error(`API request failed: ${response.status} ${response.statusText}`);
202
+ }
203
+ const data = await response.json();
204
+ let result = `# Claim Verification\n\n`;
205
+ result += `**Claim:** "${data.claim}"\n\n`;
206
+ // Verdict with emoji
207
+ const verdictEmoji = {
208
+ confirmed: '✅',
209
+ likely: '🟡',
210
+ unconfirmed: '❓',
211
+ no_data: '⚪',
212
+ insufficient_data: '⚠️',
213
+ };
214
+ result += `## Verdict: ${verdictEmoji[data.verdict] || ''} ${data.verdict.toUpperCase()}\n\n`;
215
+ result += `**Confidence:** ${(data.confidence * 100).toFixed(0)}%\n`;
216
+ result += `**Reason:** ${data.reason}\n\n`;
217
+ // Parsed components
218
+ result += `## Parsed Claim\n`;
219
+ if (data.parsed.country) {
220
+ result += `- Country: ${data.parsed.country} (${data.parsed.country_code})\n`;
221
+ }
222
+ if (data.parsed.service) {
223
+ result += `- Service: ${data.parsed.service}\n`;
224
+ }
225
+ if (data.parsed.date) {
226
+ result += `- Date: ${data.parsed.date}\n`;
227
+ }
228
+ if (data.parsed.date_range) {
229
+ result += `- Date Range: ${data.parsed.date_range.start} to ${data.parsed.date_range.end}\n`;
230
+ }
231
+ result += '\n';
232
+ // Matching incidents
233
+ if (data.incidents && data.incidents.length > 0) {
234
+ result += `## Supporting Incidents\n\n`;
235
+ data.incidents.forEach((inc, i) => {
236
+ result += `### ${i + 1}. ${inc.title}\n`;
237
+ result += `- ID: ${inc.id}\n`;
238
+ result += `- Status: ${inc.status}\n`;
239
+ result += `- Severity: ${inc.severity}\n`;
240
+ result += `- Confidence: ${(inc.confidence * 100).toFixed(0)}%\n`;
241
+ result += `- Started: ${inc.startTime.slice(0, 10)}\n`;
242
+ result += `- Permalink: ${inc.permalink}\n\n`;
243
+ });
244
+ }
245
+ // Evidence if requested
246
+ if (data.evidence && data.evidence.length > 0) {
247
+ result += `## Evidence Chain\n\n`;
248
+ data.evidence.forEach((ev, i) => {
249
+ result += `${i + 1}. **${ev.source.toUpperCase()}** (${ev.kind})\n`;
250
+ result += ` - Observed: ${ev.observedAt.slice(0, 10)}\n`;
251
+ result += ` - Confidence: ${(ev.confidence * 100).toFixed(0)}%\n`;
252
+ if (ev.permalink) {
253
+ result += ` - Verify: ${ev.permalink}\n`;
254
+ }
255
+ result += '\n';
256
+ });
257
+ }
258
+ // Citation
259
+ if (data.citation) {
260
+ result += `## Citation\n\n`;
261
+ result += `${data.citation}\n\n`;
262
+ }
263
+ result += `## Source\n`;
264
+ result += `Data: Voidly Research Claim Verification API\n`;
265
+ result += `License: CC BY 4.0\n`;
266
+ return result;
267
+ }
190
268
  // Create MCP server
191
269
  const server = new Server({
192
270
  name: 'voidly-censorship-index',
@@ -264,6 +342,24 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
264
342
  required: [],
265
343
  },
266
344
  },
345
+ {
346
+ name: 'verify_claim',
347
+ description: 'Verify a censorship claim with evidence. Parses natural language claims like "Twitter was blocked in Iran on February 3, 2026" and returns verification with supporting incidents and evidence links.',
348
+ inputSchema: {
349
+ type: 'object',
350
+ properties: {
351
+ claim: {
352
+ type: 'string',
353
+ description: 'Natural language censorship claim to verify (e.g., "Is YouTube blocked in China?", "Twitter was blocked in Iran on February 3, 2026")',
354
+ },
355
+ require_evidence: {
356
+ type: 'boolean',
357
+ description: 'Whether to include detailed evidence chain with source links (default: false)',
358
+ },
359
+ },
360
+ required: ['claim'],
361
+ },
362
+ },
267
363
  ],
268
364
  }));
269
365
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -293,6 +389,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
293
389
  case 'get_active_incidents':
294
390
  result = await getActiveIncidents();
295
391
  break;
392
+ case 'verify_claim':
393
+ if (!args?.claim) {
394
+ throw new Error('claim is required');
395
+ }
396
+ result = await verifyClaim(args.claim, args?.require_evidence || false);
397
+ break;
296
398
  default:
297
399
  throw new Error(`Unknown tool: ${name}`);
298
400
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidly/mcp-server",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "MCP server for Voidly Global Censorship Index - enables AI systems to query real-time censorship data",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",