tender-mcp 1.2.17 → 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 +9 -0
- package/package.json +1 -1
- package/server.json +36 -24
- package/src/server.js +18 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
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
|
+
|
|
7
|
+
## [1.2.18] - 2026-06-17
|
|
8
|
+
- feat: SmitheryBot detection on search_tenders and get_tender_intelligence — returns mock empty results without consuming SAM.gov/UK/EU TED API credits
|
|
9
|
+
|
|
1
10
|
## [1.2.17] - 2026-06-16
|
|
2
11
|
- feat: ATO optimisation — purpose verb, usage context, required fields, ToolRank badge
|
|
3
12
|
|
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.
|
|
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/server.json
CHANGED
|
@@ -1,24 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
-
"name": "io.github.OjasKord/tender-mcp",
|
|
4
|
-
"title": "Tender MCP",
|
|
5
|
-
"description": "Government tender search for AI agents. UK, EU and US procurement opportunities.",
|
|
6
|
-
"version": "1.2.
|
|
7
|
-
"websiteUrl": "https://kordagencies.com",
|
|
8
|
-
"repository": {
|
|
9
|
-
"url": "https://github.com/OjasKord/tender-mcp",
|
|
10
|
-
"source": "github"
|
|
11
|
-
},
|
|
12
|
-
"packages": [
|
|
13
|
-
{
|
|
14
|
-
"registryType": "npm",
|
|
15
|
-
"identifier": "tender-mcp",
|
|
16
|
-
"version": "1.2.
|
|
17
|
-
"transport": {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.OjasKord/tender-mcp",
|
|
4
|
+
"title": "Tender MCP",
|
|
5
|
+
"description": "Government tender search for AI agents. UK, EU and US procurement opportunities.",
|
|
6
|
+
"version": "1.2.18",
|
|
7
|
+
"websiteUrl": "https://kordagencies.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/OjasKord/tender-mcp",
|
|
10
|
+
"source": "github"
|
|
11
|
+
},
|
|
12
|
+
"packages": [
|
|
13
|
+
{
|
|
14
|
+
"registryType": "npm",
|
|
15
|
+
"identifier": "tender-mcp",
|
|
16
|
+
"version": "1.2.17",
|
|
17
|
+
"transport": {
|
|
18
|
+
"type": "stdio"
|
|
19
|
+
},
|
|
20
|
+
"environmentVariables": [
|
|
21
|
+
{
|
|
22
|
+
"name": "ANTHROPIC_API_KEY",
|
|
23
|
+
"description": "Anthropic API key for AI-powered tender scoring",
|
|
24
|
+
"isRequired": true,
|
|
25
|
+
"isSecret": true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"remotes": [
|
|
31
|
+
{
|
|
32
|
+
"type": "streamable-http",
|
|
33
|
+
"url": "https://tender-mcp-production.up.railway.app"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
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.
|
|
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', () =>
|
|
121
|
-
|
|
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) {
|
|
@@ -967,6 +976,12 @@ const server = http.createServer(async (req, res) => {
|
|
|
967
976
|
}
|
|
968
977
|
const _rawIpKs = req.headers['x-forwarded-for'] || req.socket.remoteAddress || 'unknown';
|
|
969
978
|
const _clientIpKs = _rawIpKs.split(',')[0].trim();
|
|
979
|
+
if (['search_tenders', 'get_tender_intelligence'].includes(name) && (req.headers['user-agent'] || '').toLowerCase().includes('smithery')) {
|
|
980
|
+
// Detect Smithery scanner and return mock response to avoid consuming SAM.gov/UK/EU TED API credits
|
|
981
|
+
res.writeHead(200, { ...cors, 'Content-Type': 'application/json' });
|
|
982
|
+
res.end(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { content: [{ type: 'text', text: JSON.stringify({ tenders: [], total_found: 0, sources_searched: [], _note: 'Mock response — scanner detected' }) }] } }));
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
970
985
|
if (['search_tenders', 'get_tender_intelligence'].includes(name) && !checkPerMinuteLimit(_clientIpKs, name, 10)) {
|
|
971
986
|
res.writeHead(200, { ...cors, 'Content-Type': 'application/json' });
|
|
972
987
|
res.end(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { content: [{ type: 'text', text: JSON.stringify({ error: 'Rate limit exceeded — maximum 10 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: 10, window: '1 minute' }) }] } }));
|