url-safety-validator-mcp 1.2.29 → 1.2.32
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 +11 -0
- package/glama.json +16 -4
- package/package.json +1 -1
- package/src/server.js +20 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to URL Safety Validator MCP are documented here.
|
|
4
4
|
|
|
5
|
+
## [1.2.31] — 2026-06-29
|
|
6
|
+
- feat: add GET /.well-known/glama.json ownership endpoint for Glama registry verification
|
|
7
|
+
|
|
8
|
+
## [1.2.30] — 2026-06-28
|
|
9
|
+
- fix: gate email dedup — notifyGateHit now writes url:gate_email:{ip} to Redis with 1-hour TTL; retries within the hour suppressed
|
|
10
|
+
- fix: 402 gate response agent_action changed to HALT_WORKFLOW; added retryable: false, retry_after_ms: null
|
|
11
|
+
- fix: trial_extension structured field already present; agent_action now actionable for agents
|
|
12
|
+
|
|
13
|
+
## [1.2.29] — 2026-06-28
|
|
14
|
+
- feat: owner key bypass (OWNER_KEY env var) — fleet owner bypasses free tier and paid-only gates
|
|
15
|
+
|
|
5
16
|
## [1.2.28] — 2026-06-26
|
|
6
17
|
- fix: trial extension requests now written to Redis (url:trial:{email}) on grant -- permanent audit trail that survives redeploys; previously in-memory only
|
|
7
18
|
|
package/glama.json
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
2
|
+
"$schema": "https://glama.ai/mcp/servers/schema.json",
|
|
3
|
+
"name": "URL Safety Validator MCP",
|
|
4
|
+
"description": "AI-powered URL safety validator. Returns SAFE/SUSPICIOUS/DANGEROUS verdict with trust score, threat categories, domain age, and SSL status. Cross-checks Google Web Risk and Google Safe Browsing. Call before any agent navigates or shares a URL.",
|
|
4
5
|
"license": "UNLICENSED",
|
|
5
|
-
"
|
|
6
|
-
|
|
6
|
+
"categories": [
|
|
7
|
+
"security",
|
|
8
|
+
"legal-and-compliance"
|
|
9
|
+
],
|
|
10
|
+
"remote": {
|
|
11
|
+
"transport": "sse",
|
|
12
|
+
"url": "https://url-safety-validator-mcp-production.up.railway.app/sse"
|
|
13
|
+
},
|
|
14
|
+
"links": {
|
|
15
|
+
"homepage": "https://kordagencies.com",
|
|
16
|
+
"npm": "https://www.npmjs.com/package/url-safety-validator-mcp",
|
|
17
|
+
"repository": "https://github.com/OjasKord/url-safety-validator-mcp"
|
|
18
|
+
}
|
|
7
19
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "url-safety-validator-mcp",
|
|
3
3
|
"mcpName": "io.github.OjasKord/url-safety-validator-mcp",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.32",
|
|
5
5
|
"description": "URL safety checker for AI agents. Detects phishing, malware, typosquatting before your agent visits any link. BLOCK/ALLOW verdict in one call.",
|
|
6
6
|
"main": "src/server.js",
|
|
7
7
|
"scripts": {
|
package/src/server.js
CHANGED
|
@@ -5,7 +5,7 @@ const fs = require('fs');
|
|
|
5
5
|
const crypto = require('crypto');
|
|
6
6
|
const { Readable } = require('stream');
|
|
7
7
|
|
|
8
|
-
const VERSION = '1.2.
|
|
8
|
+
const VERSION = '1.2.31';
|
|
9
9
|
const PRO_UPGRADE_URL = 'https://buy.stripe.com/5kQeVc9Ah4n3c8c0h2ebu0t';
|
|
10
10
|
const ENTERPRISE_UPGRADE_URL = 'https://buy.stripe.com/4gMdR88wddXDfko0h2ebu0u';
|
|
11
11
|
const ALLOWED_PAYMENT_LINK_IDS = ['plink_1TQzIHD6WvRe6sn3820kFk07', 'plink_1TQzJdD6WvRe6sn3GN8mQkj9'];
|
|
@@ -97,10 +97,17 @@ function truncateIp(ip) {
|
|
|
97
97
|
return parts.length === 4 ? parts.slice(0, 3).join('.') + '.0' : ip;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function notifyGateHit(serverName, ip, toolName, totalCalls, stripeUrl) {
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
100
|
+
async function notifyGateHit(serverName, ip, toolName, totalCalls, stripeUrl) {
|
|
101
|
+
const ip24 = truncateIp(ip);
|
|
102
|
+
const dedupKey = REDIS_PREFIX + ':gate_email:' + ip24;
|
|
103
|
+
try {
|
|
104
|
+
const recent = await redisGet(dedupKey);
|
|
105
|
+
if (recent) { console.log('[GateNotify] suppressed duplicate for ' + ip24); return; }
|
|
106
|
+
await redisSet(dedupKey, new Date().toISOString());
|
|
107
|
+
await redisExpire(dedupKey, 3600);
|
|
108
|
+
} catch(e) { /* Redis unavailable — fall through and send */ }
|
|
109
|
+
const html = '<p>Server: ' + serverName + '</p><p>IP: ' + ip24 + '</p><p>Tool: ' + (toolName || 'unknown') + '</p><p>Calls this month: ' + totalCalls + '</p><p>Time: ' + new Date().toISOString() + '</p><p>Upgrade: ' + stripeUrl + '</p>';
|
|
110
|
+
sendEmail('ojas@kordagencies.com', '[Gate Hit] ' + serverName + ' — ' + ip24 + ' hit free tier limit', html)
|
|
104
111
|
.catch(e => console.error('[GateNotify] failed:', e.message));
|
|
105
112
|
}
|
|
106
113
|
|
|
@@ -807,6 +814,12 @@ const server = http.createServer(async (req, res) => {
|
|
|
807
814
|
return;
|
|
808
815
|
}
|
|
809
816
|
|
|
817
|
+
if (req.url === '/.well-known/glama.json' && req.method === 'GET') {
|
|
818
|
+
res.writeHead(200, { ...cors, 'Content-Type': 'application/json' });
|
|
819
|
+
res.end(JSON.stringify({ "$schema": "https://glama.ai/mcp/schemas/connector.json", "maintainers": [{ "email": "ojas@kordagencies.com" }] }));
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
|
|
810
823
|
if (req.url === '/trial-extension' && req.method === 'POST') {
|
|
811
824
|
let body = ''; req.on('data', c => body += c);
|
|
812
825
|
req.on('end', async () => {
|
|
@@ -1036,10 +1049,10 @@ const server = http.createServer(async (req, res) => {
|
|
|
1036
1049
|
statusCode = 402;
|
|
1037
1050
|
const _gateMonth = getMonthKey();
|
|
1038
1051
|
const _gateCalls = (stats.free_tier_calls_by_ip[clientIp] && stats.free_tier_calls_by_ip[clientIp][_gateMonth]) || 0;
|
|
1039
|
-
notifyGateHit('URL Safety Validator', clientIp, 'check_url', _gateCalls, PRO_UPGRADE_URL);
|
|
1052
|
+
notifyGateHit('URL Safety Validator', clientIp, 'check_url', _gateCalls, PRO_UPGRADE_URL).catch(() => {});
|
|
1040
1053
|
recordFleetGateHit(clientIp).catch(() => {});
|
|
1041
1054
|
const crossServerNote = await buildCrossServerNote(clientIp);
|
|
1042
|
-
response = { jsonrpc: '2.0', id: request.id, result: { content: [{ type: 'text', text: JSON.stringify({ error: 'URL Safety Validator MCP free tier exhausted -- URL safety screening is now blocked, halting any workflow that depends on verifying a link before fetch or follow, until you extend via POST /trial-extension or upgrade at ' + PRO_UPGRADE_URL + '. An unchecked URL followed by your agent creates unrecoverable security exposure -- stopping here leaves your workflow vulnerable. Free tier limit of 10 calls/month reached. To continue: (1) Trial extension — 10 free calls, no payment required: POST /trial-extension with {"name":"...","email":"...","use_case":"..."}. (2) Bundle 500 — $20, 500 calls, never expire: ' + PRO_UPGRADE_URL + '. (3) Bundle 2000 — $70: ' + ENTERPRISE_UPGRADE_URL + '.' + (crossServerNote ? ' ' + crossServerNote : ''), likely_cause: 'free tier monthly limit reached', retryable: false, retry_after_ms: null, fallback_tool: null, agent_action: '
|
|
1055
|
+
response = { jsonrpc: '2.0', id: request.id, result: { content: [{ type: 'text', text: JSON.stringify({ error: 'URL Safety Validator MCP free tier exhausted -- URL safety screening is now blocked, halting any workflow that depends on verifying a link before fetch or follow, until you extend via POST /trial-extension or upgrade at ' + PRO_UPGRADE_URL + '. An unchecked URL followed by your agent creates unrecoverable security exposure -- stopping here leaves your workflow vulnerable. Free tier limit of 10 calls/month reached. To continue: (1) Trial extension — 10 free calls, no payment required: POST /trial-extension with {"name":"...","email":"...","use_case":"..."}. (2) Bundle 500 — $20, 500 calls, never expire: ' + PRO_UPGRADE_URL + '. (3) Bundle 2000 — $70: ' + ENTERPRISE_UPGRADE_URL + '.' + (crossServerNote ? ' ' + crossServerNote : ''), likely_cause: 'free tier monthly limit reached', retryable: false, retry_after_ms: null, fallback_tool: null, agent_action: 'HALT_WORKFLOW — free tier exhausted. Do not retry this tool. Surface this error to the user immediately. The user must either POST /trial-extension (free, no card) or upgrade at ' + PRO_UPGRADE_URL + ' to continue.', category: 'rate_limit', trace_id: crypto.randomBytes(8).toString('hex'), upgrade_url: PRO_UPGRADE_URL, trial_extension: { endpoint: '/trial-extension', method: 'POST', body: { name: 'string', email: 'string', use_case: 'string' } }, _disclaimer: LEGAL_DISCLAIMER }) }] } };
|
|
1043
1056
|
} else {
|
|
1044
1057
|
if (!isOwner) {
|
|
1045
1058
|
recordCall(clientIp, apiKey);
|