tender-mcp 1.2.8 → 1.2.9
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 +5 -0
- package/README.md +43 -0
- package/package.json +1 -1
- package/src/server.js +7 -4
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -22,6 +22,49 @@ Or via Smithery:
|
|
|
22
22
|
npx -y @smithery/cli@latest mcp add OjasKord/tender-mcp
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Harness Integration
|
|
26
|
+
|
|
27
|
+
### Claude Code / Claude Desktop (.mcp.json)
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"tender": {
|
|
32
|
+
"type": "http",
|
|
33
|
+
"url": "https://tender-mcp-production.up.railway.app"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### LangChain (Python)
|
|
40
|
+
```python
|
|
41
|
+
from langchain_mcp_adapters.client import MultiServerMCPClient
|
|
42
|
+
client = MultiServerMCPClient({
|
|
43
|
+
"tender": {
|
|
44
|
+
"url": "https://tender-mcp-production.up.railway.app",
|
|
45
|
+
"transport": "http"
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
tools = await client.get_tools()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### OpenAI Agents SDK (Python)
|
|
52
|
+
```python
|
|
53
|
+
from agents import Agent, HostedMCPTool
|
|
54
|
+
agent = Agent(
|
|
55
|
+
name="Assistant",
|
|
56
|
+
tools=[HostedMCPTool(tool_config={
|
|
57
|
+
"type": "mcp",
|
|
58
|
+
"server_label": "tender",
|
|
59
|
+
"server_url": "https://tender-mcp-production.up.railway.app",
|
|
60
|
+
"require_approval": "never"
|
|
61
|
+
})]
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### LangGraph
|
|
66
|
+
Same as LangChain above — langchain-mcp-adapters works with LangGraph natively.
|
|
67
|
+
|
|
25
68
|
## Why Use This
|
|
26
69
|
|
|
27
70
|
Any business that sells to government needs to monitor tender opportunities. But searching three separate government portals daily, reading hundreds of notices, and manually judging relevance takes hours. Tender MCP does it in seconds — search UK, EU, and US simultaneously, then let AI score which opportunities actually match your capabilities.
|
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.9",
|
|
5
5
|
"description": "Government tender search and AI opportunity scoring for AI agents. UK Contracts Finder, EU TED, US SAM.gov.",
|
|
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.2.
|
|
6
|
+
const VERSION = '1.2.9';
|
|
7
7
|
const PRO_UPGRADE_URL = 'https://buy.stripe.com/9B600i5k1bPv2xC6Fqebu0n';
|
|
8
8
|
const ENTERPRISE_UPGRADE_URL = 'https://buy.stripe.com/7sY7sKaEldXDegk0h2ebu0o';
|
|
9
9
|
const PERSIST_FILE = '/tmp/tender_stats.json';
|
|
@@ -571,7 +571,8 @@ function checkAccess(req, toolName) {
|
|
|
571
571
|
}
|
|
572
572
|
|
|
573
573
|
// Free tier — allow all tools, but pass tier='free' so executeTool can gate paid features
|
|
574
|
-
const
|
|
574
|
+
const rawIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress || 'unknown';
|
|
575
|
+
const ip = rawIp.split(',')[0].trim();
|
|
575
576
|
const monthKey = getMonthKey(ip);
|
|
576
577
|
const calls = freeTierUsage.get(monthKey) || 0;
|
|
577
578
|
if (calls >= FREE_TIER_LIMIT) {
|
|
@@ -696,7 +697,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
696
697
|
if (!name || !email) { res.writeHead(400, { ...cors, 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'name and email are required', agent_action: 'PROVIDE_REQUIRED_FIELDS' })); return; }
|
|
697
698
|
const emailKey = 'trial:' + email.toLowerCase().trim();
|
|
698
699
|
if (trialExtensions.has(emailKey)) { res.writeHead(409, { ...cors, 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Trial extension already granted for this email.', upgrade_url: PRO_UPGRADE_URL, agent_action: 'INFORM_USER_TRIAL_ALREADY_USED' })); return; }
|
|
699
|
-
const
|
|
700
|
+
const rawIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress || 'unknown';
|
|
701
|
+
const ip = rawIp.split(',')[0].trim();
|
|
700
702
|
const monthKey = getMonthKey(ip);
|
|
701
703
|
const currentCalls = freeTierUsage.get(monthKey) || 0;
|
|
702
704
|
freeTierUsage.set(monthKey, Math.max(0, currentCalls - TRIAL_EXTENSION_CALLS));
|
|
@@ -753,7 +755,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
753
755
|
return;
|
|
754
756
|
}
|
|
755
757
|
|
|
756
|
-
const
|
|
758
|
+
const rawIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress || 'unknown';
|
|
759
|
+
const ip = rawIp.split(',')[0].trim();
|
|
757
760
|
usageLog.push({ tool: name, tier: access.tier, time: nowISO(), ip: ip.slice(0, 8) + '...' });
|
|
758
761
|
if (usageLog.length > 1000) usageLog.shift();
|
|
759
762
|
toolUsageCounts[name] = (toolUsageCounts[name] || 0) + 1;
|