local-model-suitability-mcp 1.1.19 → 1.1.20

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 CHANGED
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.20] - 2026-06-20
4
+ - feat: email notification on free tier gate hit
5
+
3
6
  ## [1.1.19] - 2026-06-18
4
7
  - feat: revoke API key on Stripe refund
5
8
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "local-model-suitability-mcp",
3
3
  "mcpName": "io.github.OjasKord/local-model-suitability-mcp",
4
- "version": "1.1.19",
4
+ "version": "1.1.20",
5
5
  "description": "AI model router for agents. Checks whether a local model can handle the task before calling cloud inference. LOCAL/CLOUD verdict saves cost on every call.",
6
6
  "main": "src/server.js",
7
7
  "type": "module",
package/src/server.js CHANGED
@@ -3,7 +3,7 @@ import { createHmac, timingSafeEqual } from 'crypto';
3
3
  import { readFileSync, writeFileSync } from 'fs';
4
4
  import Anthropic from '@anthropic-ai/sdk';
5
5
 
6
- const VERSION = '1.1.19';
6
+ const VERSION = '1.1.20';
7
7
  const PRO_UPGRADE_URL = 'https://buy.stripe.com/cNibJ08wd7zf6NS0h2ebu0p';
8
8
  const ENTERPRISE_UPGRADE_URL = 'https://buy.stripe.com/28E9AS27PbPvfkoe7Sebu0q';
9
9
  const ALLOWED_PAYMENT_LINK_IDS = ['plink_1TQzCBD6WvRe6sn3H1q5t2LF', 'plink_1TQzDSD6WvRe6sn3UM2G1EgX'];
@@ -105,6 +105,23 @@ function checkAccess(ip, apiKey) {
105
105
  return { allowed: true, tier: 'free', remaining, count };
106
106
  }
107
107
 
108
+ function truncateIp(ip) {
109
+ const parts = (ip || '').split('.');
110
+ return parts.length === 4 ? parts.slice(0, 3).join('.') + '.0' : ip;
111
+ }
112
+
113
+ function notifyGateHit(serverName, ip, toolName, totalCalls, stripeUrl) {
114
+ if (!process.env.RESEND_API_KEY) return;
115
+ const maskedIp = truncateIp(ip);
116
+ const html = '<p>Server: ' + serverName + '</p><p>IP: ' + maskedIp + '</p><p>Tool: ' + (toolName || 'unknown') + '</p><p>Calls this month: ' + totalCalls + '</p><p>Time: ' + new Date().toISOString() + '</p><p>Upgrade: ' + stripeUrl + '</p>';
117
+ fetch('https://api.resend.com/emails', {
118
+ method: 'POST',
119
+ headers: { 'Authorization': `Bearer ${process.env.RESEND_API_KEY}`, 'Content-Type': 'application/json' },
120
+ body: JSON.stringify({ from: 'Kord Agencies <ojas@kordagencies.com>', to: 'ojas@kordagencies.com', subject: '[Gate Hit] ' + serverName + ' — ' + maskedIp + ' hit free tier limit', html })
121
+ }).then(r => { if (!r.ok) r.text().then(t => console.error('[GateNotify] failed: HTTP ' + r.status + ' ' + t)); })
122
+ .catch(e => console.error('[GateNotify] network error:', e.message));
123
+ }
124
+
108
125
  function logCall(tool, tier, ip) {
109
126
  stats.tool_usage[tool] = (stats.tool_usage[tool] || 0) + 1;
110
127
  stats.recent_calls.push({ tool, tier, time: nowISO(), ip });
@@ -713,6 +730,7 @@ const server = createServer(async (req, res) => {
713
730
  const access = checkAccess(clientIp, apiKey);
714
731
 
715
732
  if (!access.allowed) {
733
+ notifyGateHit('Local Model Suitability', clientIp, 'check_local_viability', getFreeTierCount(clientIp), PRO_UPGRADE_URL);
716
734
  response = {
717
735
  jsonrpc: '2.0', id: request.id,
718
736
  result: { content: [{ type: 'text', text: JSON.stringify({ error: `Free tier limit reached. Get 500 calls for $20 at ${PRO_UPGRADE_URL} -- calls never expire.`, likely_cause: 'free tier monthly limit reached', retryable: false, retry_after_ms: null, fallback_tool: null, agent_action: `Inform user free tier quota is exhausted. Get 500 calls for $20 at ${PRO_UPGRADE_URL} -- calls never expire.`, category: 'rate_limit', trace_id: nowISO(), upgrade_url: PRO_UPGRADE_URL }) }] }