data-compliance-mcp 1.0.25 → 1.0.27
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 +3 -0
- package/package.json +1 -1
- package/src/server.js +14 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.26] - 2026-06-26
|
|
4
|
+
- fix: trial extension requests now written to Redis (dcc:trial:{email}) on grant -- permanent audit trail that survives redeploys; previously in-memory only
|
|
5
|
+
|
|
3
6
|
## [1.0.25] - 2026-06-25
|
|
4
7
|
- feat: calls_remaining field added to every successful tool response -- "unlimited" for paid keys, numeric free-tier headroom otherwise
|
|
5
8
|
- feat: verdict_ttl field added to validate_data_safety, validate_data_safety_lite, get_safety_report responses (86400s/24h each)
|
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.27",
|
|
5
5
|
"description": "Data safety classifier for AI agents. GDPR, HIPAA, PCI-DSS compliance before your agent stores or shares any payload. SAFE/ESCALATE verdict in one call.",
|
|
6
6
|
"main": "src/server.js",
|
|
7
7
|
"scripts": {
|
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.27';
|
|
7
7
|
const FIRST_DEPLOYED = '2026-04-21T09:53:12Z';
|
|
8
8
|
const LIFETIME_CALLS_REDIS_KEY = 'dcc:lifetime_calls';
|
|
9
9
|
const UPTIME_HEARTBEAT_KEY = 'dcc:uptime:heartbeat_count';
|
|
@@ -16,6 +16,7 @@ const API_KEYS_FILE = '/tmp/datacompliance_apikeys.json';
|
|
|
16
16
|
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || '';
|
|
17
17
|
const ABUSEIPDB_API_KEY = process.env.ABUSEIPDB_API_KEY || '';
|
|
18
18
|
const RESEND_API_KEY = process.env.RESEND_API_KEY || '';
|
|
19
|
+
const OWNER_KEY = process.env.OWNER_KEY || '';
|
|
19
20
|
const STATS_KEY = process.env.STATS_KEY || 'ojas2026';
|
|
20
21
|
const PORT = process.env.PORT || 3000;
|
|
21
22
|
|
|
@@ -986,6 +987,15 @@ async function executeTool(name, args, tier) {
|
|
|
986
987
|
|
|
987
988
|
// ─── ACCESS CONTROL ───────────────────────────────────────────────────────────
|
|
988
989
|
|
|
990
|
+
async function checkOwnerKey(req, requestBody) {
|
|
991
|
+
if (!OWNER_KEY) return false;
|
|
992
|
+
const provided = req.headers['x-owner-key'] || (requestBody && requestBody.owner_key) || '';
|
|
993
|
+
if (provided !== OWNER_KEY) return false;
|
|
994
|
+
redisIncr(REDIS_PREFIX + ':owner_calls:' + new Date().toISOString().slice(0, 7)).catch(() => {});
|
|
995
|
+
console.log('[owner] owner key used');
|
|
996
|
+
return true;
|
|
997
|
+
}
|
|
998
|
+
|
|
989
999
|
async function checkAccess(req, toolName) {
|
|
990
1000
|
const apiKey = req.headers['x-api-key'];
|
|
991
1001
|
const rawIpAll = req.headers['x-forwarded-for'] || req.socket.remoteAddress || 'unknown';
|
|
@@ -1267,6 +1277,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1267
1277
|
freeTierUsage.set(monthKey, Math.max(0, currentCalls - TRIAL_EXTENSION_CALLS));
|
|
1268
1278
|
trialExtensions.set(emailKey, { name, email, use_case: use_case || '', ip, granted_at: nowISO() });
|
|
1269
1279
|
saveStats();
|
|
1280
|
+
await redisSet(REDIS_PREFIX + ':trial:' + email.toLowerCase().trim(), { name, email, use_case: use_case || '', ip, timestamp: nowISO(), server: 'data-compliance-mcp' });
|
|
1270
1281
|
// 24h follow-up record -- processed by /process-trial-followups (fleet cron)
|
|
1271
1282
|
await redisSet(REDIS_PREFIX + ':followup:' + email.toLowerCase().trim(), { email, name, server: 'data-compliance-mcp', granted_at: nowISO(), sent: false });
|
|
1272
1283
|
await sendEmail('ojas@kordagencies.com', 'Data Compliance MCP -- Trial Extension: ' + name,
|
|
@@ -1409,7 +1420,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
1409
1420
|
res.end(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { content: [{ type: 'text', text: JSON.stringify({ error: 'Rate limit exceeded — maximum 5 calls per minute per IP on AI-powered tools. Your workflow is calling this tool too rapidly.', agent_action: 'RETRY_IN_60_SEC', retryable: true, retry_after_ms: 60000, limit: 5, window: '1 minute' }) }] } }));
|
|
1410
1421
|
return;
|
|
1411
1422
|
}
|
|
1412
|
-
const
|
|
1423
|
+
const isOwner = await checkOwnerKey(req, request);
|
|
1424
|
+
const access = isOwner ? { allowed: true, tier: 'owner', paid: true } : await checkAccess(req, name);
|
|
1413
1425
|
|
|
1414
1426
|
if (!access.allowed) {
|
|
1415
1427
|
const likelyCause = access.tier === 'invalid' ? 'invalid or expired API key' : 'free tier monthly limit reached';
|