tender-mcp 1.2.18 → 1.2.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,3 +1,9 @@
1
+ ## [1.2.20] - 2026-06-17
2
+ - fix: sendEmail now logs Resend HTTP errors in Railway logs
3
+
4
+ ## [1.2.19] - 2026-06-17
5
+ - fix: Stripe webhook now validates payment_link ID — ignores events not belonging to this server
6
+
1
7
  ## [1.2.18] - 2026-06-17
2
8
  - feat: SmitheryBot detection on search_tenders and get_tender_intelligence — returns mock empty results without consuming SAM.gov/UK/EU TED API credits
3
9
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tender-mcp",
3
3
  "mcpName": "io.github.OjasKord/tender-mcp",
4
- "version": "1.2.18",
4
+ "version": "1.2.20",
5
5
  "description": "Government tender search for AI agents. UK, EU, US contracts with AI bid scoring. BID/SKIP verdict with deadline and value in one call.",
6
6
  "main": "src/server.js",
7
7
  "scripts": {
package/src/server.js CHANGED
@@ -3,9 +3,10 @@ const https = require('https');
3
3
  const crypto = require('crypto');
4
4
  const fs = require('fs');
5
5
 
6
- const VERSION = '1.2.18';
6
+ const VERSION = '1.2.20';
7
7
  const PRO_UPGRADE_URL = 'https://buy.stripe.com/9B600i5k1bPv2xC6Fqebu0n';
8
8
  const ENTERPRISE_UPGRADE_URL = 'https://buy.stripe.com/7sY7sKaEldXDegk0h2ebu0o';
9
+ const ALLOWED_PAYMENT_LINK_IDS = ['plink_1TQz8hD6WvRe6sn3qXhoyAWT', 'plink_1TQzAhD6WvRe6sn3P0CAabOs'];
9
10
  const PERSIST_FILE = '/tmp/tender_stats.json';
10
11
  const API_KEYS_FILE = '/tmp/tender_apikeys.json';
11
12
  const RESEND_API_KEY = process.env.RESEND_API_KEY || '';
@@ -117,8 +118,11 @@ async function sendEmail(to, subject, html) {
117
118
  const req = https.request({
118
119
  hostname: 'api.resend.com', path: '/emails', method: 'POST',
119
120
  headers: { 'Authorization': 'Bearer ' + RESEND_API_KEY, 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }
120
- }, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => resolve({ status: res.statusCode, body: d })); });
121
- req.on('error', e => resolve({ error: e.message }));
121
+ }, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => {
122
+ if (res.statusCode < 200 || res.statusCode >= 300) console.error('[Resend] Email failed: HTTP ' + res.statusCode + ' ' + d);
123
+ resolve({ status: res.statusCode, body: d });
124
+ }); });
125
+ req.on('error', e => { console.error('[Resend] Email network error:', e.message); resolve({ error: e.message }); });
122
126
  req.write(body); req.end();
123
127
  });
124
128
  }
@@ -744,6 +748,11 @@ async function handleStripeWebhook(body, sig) {
744
748
  const event = JSON.parse(body);
745
749
  if (event.type === 'checkout.session.completed') {
746
750
  const session = event.data.object;
751
+ const paymentLinkId = session.payment_link;
752
+ if (paymentLinkId && !ALLOWED_PAYMENT_LINK_IDS.includes(paymentLinkId)) {
753
+ console.log('[tender] Webhook received but payment link ' + paymentLinkId + ' not for this server — ignoring.');
754
+ return { received: true, ignored: true };
755
+ }
747
756
  const email = session.customer_email || session.customer_details?.email;
748
757
  const plan = getPlanFromProduct(session.metadata?.product_name || '');
749
758
  if (email) {