data-compliance-mcp 1.0.3 → 1.0.4
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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/server.js +67 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.4] - 2026-04-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `token_count` field on all tool responses — lets orchestrator budget ledgers track token cost per call
|
|
7
|
+
- `/ready` endpoint — returns 200 when `ANTHROPIC_API_KEY` is present, 503 otherwise
|
|
8
|
+
- Phase 4 enhanced error objects: `category`, `retryable`, `retry_after_ms`, `fallback_tool`, `trace_id` on all 7 error paths
|
|
9
|
+
- `validate_data_safety_lite` tool — pattern-detection only with no AI call, for budget-constrained orchestrators
|
|
10
|
+
|
|
3
11
|
## [1.0.3] - 2026-04-26
|
|
4
12
|
|
|
5
13
|
### Improved
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-compliance-mcp",
|
|
3
3
|
"mcpName": "io.github.OjasKord/data-compliance-mcp",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"description": "Classify data safety before your agent stores or shares it. GDPR, HIPAA, PCI-DSS, CCPA. AI-powered.",
|
|
6
6
|
"main": "src/server.js",
|
|
7
7
|
"scripts": {
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.OjasKord/data-compliance-mcp",
|
|
4
4
|
"title": "Data Compliance Classifier MCP",
|
|
5
5
|
"description": "Classify data safety before storing or sharing. GDPR, HIPAA, PCI-DSS, CCPA. AI-powered.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.4",
|
|
7
7
|
"websiteUrl": "https://kordagencies.com",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/OjasKord/data-compliance-mcp",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"identifier": "data-compliance-mcp",
|
|
16
|
-
"version": "1.0.
|
|
16
|
+
"version": "1.0.4",
|
|
17
17
|
"transport": { "type": "stdio" },
|
|
18
18
|
"environmentVariables": [
|
|
19
19
|
{ "name": "ANTHROPIC_API_KEY", "description": "Anthropic API key for AI classification", "isRequired": true, "isSecret": true },
|
package/src/server.js
CHANGED
|
@@ -3,7 +3,7 @@ const https = require('https');
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
|
|
6
|
-
const VERSION = '1.0.
|
|
6
|
+
const VERSION = '1.0.4';
|
|
7
7
|
const PERSIST_FILE = '/tmp/datacompliance_stats.json';
|
|
8
8
|
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || '';
|
|
9
9
|
const ABUSEIPDB_API_KEY = process.env.ABUSEIPDB_API_KEY || '';
|
|
@@ -280,6 +280,18 @@ const tools = [
|
|
|
280
280
|
},
|
|
281
281
|
required: ['mode']
|
|
282
282
|
}
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'validate_data_safety_lite',
|
|
286
|
+
description: 'CALL THIS TOOL when your orchestrator is budget-constrained and cannot afford the full AI classification. validate_data_safety_lite runs pattern detection only -- no Claude API call, no IP check, no credential lookup. Returns verdict and detected_categories in under 100ms at roughly 70% lower token cost than validate_data_safety. Use when: (1) your budget ledger has less than 300 tokens remaining for this call, (2) you need a fast pre-screen before committing to a full AI classification, or (3) you are processing high-volume data where AI classification is applied selectively. Returns SAFE_TO_PROCESS if no sensitive patterns found, REVIEW_REQUIRED if patterns detected. If REVIEW_REQUIRED, follow up with validate_data_safety for full AI verdict with regulatory framework mapping. LEGAL NOTICE: Pattern detection only -- not a substitute for AI-powered classification in regulated environments. Full terms: kordagencies.com/terms.html. Free tier: 20 calls/month.',
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
payload: { type: 'string', description: 'The data payload to screen for sensitive patterns.' },
|
|
291
|
+
context: { type: 'string', description: 'Optional: what your agent plans to do with this data.' }
|
|
292
|
+
},
|
|
293
|
+
required: ['payload']
|
|
294
|
+
}
|
|
283
295
|
}
|
|
284
296
|
];
|
|
285
297
|
|
|
@@ -291,7 +303,7 @@ async function executeTool(name, args, tier) {
|
|
|
291
303
|
// ── validate_data_safety ──────────────────────────────────────────────────
|
|
292
304
|
if (name === 'validate_data_safety') {
|
|
293
305
|
const { payload, context, data_origin_ip, jurisdiction } = args;
|
|
294
|
-
if (!payload) return { error: 'payload is required', agent_action: 'PROVIDE_REQUIRED_FIELD', _disclaimer: LEGAL_DISCLAIMER };
|
|
306
|
+
if (!payload) return { error: 'payload is required', agent_action: 'PROVIDE_REQUIRED_FIELD', category: 'invalid_input', retryable: false, retry_after_ms: null, fallback_tool: 'validate_data_safety_lite', trace_id: Math.random().toString(36).slice(2, 10), _disclaimer: LEGAL_DISCLAIMER };
|
|
295
307
|
|
|
296
308
|
// Step 1: Pattern detection (fast, no API call)
|
|
297
309
|
const patterns = detectPatterns(payload);
|
|
@@ -414,20 +426,21 @@ async function executeTool(name, args, tier) {
|
|
|
414
426
|
result.redaction_targets = classification.redaction_targets;
|
|
415
427
|
}
|
|
416
428
|
|
|
429
|
+
result.token_count = Math.ceil(JSON.stringify(result).length / 4);
|
|
417
430
|
return result;
|
|
418
431
|
}
|
|
419
432
|
|
|
420
433
|
// ── get_safety_report ─────────────────────────────────────────────────────
|
|
421
434
|
if (name === 'get_safety_report') {
|
|
422
435
|
const { mode, payloads, dataset_description, context } = args;
|
|
423
|
-
if (!mode) return { error: 'mode is required: BATCH or AUDIT', agent_action: 'PROVIDE_REQUIRED_FIELD', _disclaimer: LEGAL_DISCLAIMER };
|
|
436
|
+
if (!mode) return { error: 'mode is required: BATCH or AUDIT', agent_action: 'PROVIDE_REQUIRED_FIELD', category: 'invalid_input', retryable: false, retry_after_ms: null, fallback_tool: 'get_safety_report', trace_id: Math.random().toString(36).slice(2, 10), _disclaimer: LEGAL_DISCLAIMER };
|
|
424
437
|
|
|
425
438
|
// Free tier preview — run count analysis without full classification
|
|
426
439
|
if (tier === 'free') {
|
|
427
440
|
if (mode === 'BATCH' && payloads && Array.isArray(payloads)) {
|
|
428
441
|
const previewPatterns = payloads.slice(0, 5).map(p => detectPatterns(p));
|
|
429
442
|
const flaggedCount = previewPatterns.filter(p => p.length > 0).length;
|
|
430
|
-
|
|
443
|
+
const _rBatchPreview = {
|
|
431
444
|
mode: 'BATCH',
|
|
432
445
|
status: 'PREVIEW — paid plan required for full classification',
|
|
433
446
|
payloads_submitted: payloads.length,
|
|
@@ -447,8 +460,10 @@ async function executeTool(name, args, tier) {
|
|
|
447
460
|
checked_at: checkedAt,
|
|
448
461
|
_disclaimer: LEGAL_DISCLAIMER
|
|
449
462
|
};
|
|
463
|
+
_rBatchPreview.token_count = Math.ceil(JSON.stringify(_rBatchPreview).length / 4);
|
|
464
|
+
return _rBatchPreview;
|
|
450
465
|
}
|
|
451
|
-
|
|
466
|
+
const _rPreview = {
|
|
452
467
|
mode: mode,
|
|
453
468
|
status: 'PREVIEW — paid plan required',
|
|
454
469
|
message: 'Pro plan required for ' + mode + ' reports. Upgrade at kordagencies.com.',
|
|
@@ -456,12 +471,14 @@ async function executeTool(name, args, tier) {
|
|
|
456
471
|
checked_at: checkedAt,
|
|
457
472
|
_disclaimer: LEGAL_DISCLAIMER
|
|
458
473
|
};
|
|
474
|
+
_rPreview.token_count = Math.ceil(JSON.stringify(_rPreview).length / 4);
|
|
475
|
+
return _rPreview;
|
|
459
476
|
}
|
|
460
477
|
|
|
461
478
|
// ── PAID: BATCH mode ──
|
|
462
479
|
if (mode === 'BATCH') {
|
|
463
480
|
if (!payloads || !Array.isArray(payloads) || payloads.length === 0) {
|
|
464
|
-
return { error: 'payloads array is required for BATCH mode', agent_action: 'PROVIDE_REQUIRED_FIELD', _disclaimer: LEGAL_DISCLAIMER };
|
|
481
|
+
return { error: 'payloads array is required for BATCH mode', agent_action: 'PROVIDE_REQUIRED_FIELD', category: 'invalid_input', retryable: false, retry_after_ms: null, fallback_tool: 'get_safety_report', trace_id: Math.random().toString(36).slice(2, 10), _disclaimer: LEGAL_DISCLAIMER };
|
|
465
482
|
}
|
|
466
483
|
const batch = payloads.slice(0, 50);
|
|
467
484
|
const results = [];
|
|
@@ -518,7 +535,7 @@ async function executeTool(name, args, tier) {
|
|
|
518
535
|
results.forEach(r => { verdictCounts[r.verdict] = (verdictCounts[r.verdict] || 0) + 1; });
|
|
519
536
|
const highestRisk = results.filter(r => r.verdict === 'ESCALATE' || r.verdict === 'DO_NOT_STORE');
|
|
520
537
|
|
|
521
|
-
|
|
538
|
+
const _rBatch = {
|
|
522
539
|
mode: 'BATCH',
|
|
523
540
|
total_payloads: batch.length,
|
|
524
541
|
classified: results.length,
|
|
@@ -533,12 +550,14 @@ async function executeTool(name, args, tier) {
|
|
|
533
550
|
checked_at: checkedAt,
|
|
534
551
|
_disclaimer: LEGAL_DISCLAIMER
|
|
535
552
|
};
|
|
553
|
+
_rBatch.token_count = Math.ceil(JSON.stringify(_rBatch).length / 4);
|
|
554
|
+
return _rBatch;
|
|
536
555
|
}
|
|
537
556
|
|
|
538
557
|
// ── PAID: AUDIT mode ──
|
|
539
558
|
if (mode === 'AUDIT') {
|
|
540
559
|
if (!dataset_description) {
|
|
541
|
-
return { error: 'dataset_description is required for AUDIT mode', agent_action: 'PROVIDE_REQUIRED_FIELD', _disclaimer: LEGAL_DISCLAIMER };
|
|
560
|
+
return { error: 'dataset_description is required for AUDIT mode', agent_action: 'PROVIDE_REQUIRED_FIELD', category: 'invalid_input', retryable: false, retry_after_ms: null, fallback_tool: 'get_safety_report', trace_id: Math.random().toString(36).slice(2, 10), _disclaimer: LEGAL_DISCLAIMER };
|
|
542
561
|
}
|
|
543
562
|
|
|
544
563
|
const prompt = 'You are a data compliance auditor. Generate a structured compliance audit report for the following dataset.\n\n' +
|
|
@@ -551,7 +570,7 @@ async function executeTool(name, args, tier) {
|
|
|
551
570
|
const response = await callClaude(prompt);
|
|
552
571
|
const clean = response.replace(/```json|```/g, '').trim();
|
|
553
572
|
const report = JSON.parse(clean);
|
|
554
|
-
|
|
573
|
+
const _rAudit = {
|
|
555
574
|
mode: 'AUDIT',
|
|
556
575
|
dataset_description,
|
|
557
576
|
report,
|
|
@@ -559,15 +578,42 @@ async function executeTool(name, args, tier) {
|
|
|
559
578
|
checked_at: checkedAt,
|
|
560
579
|
_disclaimer: LEGAL_DISCLAIMER
|
|
561
580
|
};
|
|
581
|
+
_rAudit.token_count = Math.ceil(JSON.stringify(_rAudit).length / 4);
|
|
582
|
+
return _rAudit;
|
|
562
583
|
} catch(e) {
|
|
563
|
-
return { error: 'Audit report generation failed. Please retry.', agent_action: 'RETRY_IN_2_MIN', checked_at: checkedAt, _disclaimer: LEGAL_DISCLAIMER };
|
|
584
|
+
return { error: 'Audit report generation failed. Please retry.', agent_action: 'RETRY_IN_2_MIN', category: 'upstream_unavailable', retryable: true, retry_after_ms: 120000, fallback_tool: 'get_safety_report', trace_id: Math.random().toString(36).slice(2, 10), checked_at: checkedAt, _disclaimer: LEGAL_DISCLAIMER };
|
|
564
585
|
}
|
|
565
586
|
}
|
|
566
587
|
|
|
567
|
-
return { error: 'Invalid mode. Use BATCH or AUDIT.', agent_action: 'PROVIDE_REQUIRED_FIELD', _disclaimer: LEGAL_DISCLAIMER };
|
|
588
|
+
return { error: 'Invalid mode. Use BATCH or AUDIT.', agent_action: 'PROVIDE_REQUIRED_FIELD', category: 'invalid_input', retryable: false, retry_after_ms: null, fallback_tool: 'get_safety_report', trace_id: Math.random().toString(36).slice(2, 10), _disclaimer: LEGAL_DISCLAIMER };
|
|
568
589
|
}
|
|
569
590
|
|
|
570
|
-
|
|
591
|
+
// ── validate_data_safety_lite ─────────────────────────────────────────────
|
|
592
|
+
// Pattern detection only. No AI call, no IP check, no credential check.
|
|
593
|
+
if (name === 'validate_data_safety_lite') {
|
|
594
|
+
const { payload, context } = args;
|
|
595
|
+
if (!payload) return { error: 'payload is required', agent_action: 'PROVIDE_REQUIRED_FIELD', category: 'invalid_input', retryable: false, retry_after_ms: null, fallback_tool: 'validate_data_safety_lite', trace_id: Math.random().toString(36).slice(2, 10), _disclaimer: LEGAL_DISCLAIMER };
|
|
596
|
+
const patterns = detectPatterns(payload);
|
|
597
|
+
const hasSensitive = patterns.length > 0;
|
|
598
|
+
const sensitivityLevel = patterns.some(p => ['SPECIAL_CATEGORY', 'CREDENTIAL', 'FINANCIAL'].includes(p))
|
|
599
|
+
? 'CONFIDENTIAL'
|
|
600
|
+
: hasSensitive ? 'INTERNAL' : 'PUBLIC';
|
|
601
|
+
const _rLite = {
|
|
602
|
+
verdict: hasSensitive ? 'REVIEW_REQUIRED' : 'SAFE_TO_PROCESS',
|
|
603
|
+
agent_action: hasSensitive
|
|
604
|
+
? 'Run validate_data_safety for full AI classification before storing or transmitting this payload.'
|
|
605
|
+
: 'No sensitive patterns detected. Proceed with caution -- pattern detection does not replace AI classification.',
|
|
606
|
+
patterns_detected: patterns,
|
|
607
|
+
sensitivity_level: sensitivityLevel,
|
|
608
|
+
analysis_type: 'Pattern detection only -- no AI analysis. Use validate_data_safety for full AI verdict.',
|
|
609
|
+
checked_at: checkedAt,
|
|
610
|
+
_disclaimer: LEGAL_DISCLAIMER
|
|
611
|
+
};
|
|
612
|
+
_rLite.token_count = Math.ceil(JSON.stringify(_rLite).length / 4);
|
|
613
|
+
return _rLite;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
return { error: 'Unknown tool: ' + name, agent_action: 'RETRY_IN_2_MIN', category: 'unknown_tool', retryable: false, retry_after_ms: null, fallback_tool: null, trace_id: Math.random().toString(36).slice(2, 10) };
|
|
571
617
|
}
|
|
572
618
|
|
|
573
619
|
// ─── ACCESS CONTROL ───────────────────────────────────────────────────────────
|
|
@@ -672,9 +718,17 @@ const server = http.createServer(async (req, res) => {
|
|
|
672
718
|
return;
|
|
673
719
|
}
|
|
674
720
|
|
|
721
|
+
if (req.url === '/ready' && (req.method === 'GET' || req.method === 'HEAD')) {
|
|
722
|
+
const checks = { anthropic: !!ANTHROPIC_API_KEY };
|
|
723
|
+
const ready = checks.anthropic;
|
|
724
|
+
res.writeHead(ready ? 200 : 503, { ...cors, 'Content-Type': 'application/json' });
|
|
725
|
+
res.end(JSON.stringify({ status: ready ? 'ready' : 'not_ready', version: VERSION, checks }));
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
675
729
|
if (req.url === '/.well-known/mcp/server-card.json') {
|
|
676
730
|
res.writeHead(200, { ...cors, 'Content-Type': 'application/json' });
|
|
677
|
-
res.end(JSON.stringify({ name: 'data-compliance-mcp', version: VERSION, description: 'Classify data safety before your agent stores or shares it. GDPR, HIPAA, PCI-DSS. Free tier: 20/month.', tools: tools.map(t => ({ name: t.name, description: t.description.slice(0, 100) })), transport: '
|
|
731
|
+
res.end(JSON.stringify({ name: 'data-compliance-mcp', version: VERSION, description: 'Classify data safety before your agent stores or shares it. GDPR, HIPAA, PCI-DSS. Free tier: 20/month.', tools: tools.map(t => ({ name: t.name, description: t.description.slice(0, 100) })), transport: 'streamable-http', homepage: 'https://kordagencies.com', author: 'ojas1', token_footprint_min: 238, token_footprint_max: 2000, token_footprint_avg: 709, idempotent_tools: ['validate_data_safety', 'get_safety_report', 'validate_data_safety_lite'], circuit_breaker: false, health_endpoint: '/health', ready_endpoint: '/ready' }));
|
|
678
732
|
return;
|
|
679
733
|
}
|
|
680
734
|
|