paygate-mcp 8.7.0 → 8.9.0
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/README.md +40 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +247 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,6 +114,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
114
114
|
- **Rate Limit Analysis** — `GET /admin/rate-limits` rate limit utilization analysis with per-key and per-tool breakdown, denial trends, most throttled keys, and current window utilization
|
|
115
115
|
- **Quota Analysis** — `GET /admin/quotas` quota utilization analysis with per-key daily/monthly usage vs limits, per-tool denial breakdown, most constrained keys, and global/per-key quota source tracking
|
|
116
116
|
- **Denial Analysis** — `GET /admin/denials` comprehensive denial breakdown by reason type (insufficient_credits, rate_limited, quota_exceeded, key_suspended, etc.) with per-key and per-tool stats, hourly trends, and most denied keys
|
|
117
|
+
- **Traffic Analysis** — `GET /admin/traffic` request volume analysis with tool popularity, hourly volume, top consumers by call count, namespace breakdown, peak hour identification, and success rates
|
|
118
|
+
- **Security Audit** — `GET /admin/security` security posture analysis identifying keys without IP allowlists, quotas, ACL restrictions, spending limits, or expiry dates, flagging high-credit keys, and computing a composite security score
|
|
117
119
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
118
120
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
119
121
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2636,6 +2638,44 @@ curl http://localhost:3000/admin/denials -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
|
2636
2638
|
|
|
2637
2639
|
Returns denial summary with denial rate, breakdown by canonical reason type (insufficient_credits, rate_limited, tool_rate_limited, quota_exceeded, key_suspended, api_key_expired, invalid_api_key, missing_api_key, tool_not_allowed, ip_not_allowed, spending_limit_exceeded, etc.), per-key denial counts with top reason, per-tool denial counts, hourly denial trends (last 24 hours), and top 10 most denied keys. Read-only.
|
|
2638
2640
|
|
|
2641
|
+
### Traffic Analysis
|
|
2642
|
+
|
|
2643
|
+
```bash
|
|
2644
|
+
curl http://localhost:3000/admin/traffic -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2645
|
+
```
|
|
2646
|
+
|
|
2647
|
+
```json
|
|
2648
|
+
{
|
|
2649
|
+
"summary": { "totalCalls": 500, "totalAllowed": 470, "totalDenied": 30, "successRate": 0.94, "uniqueKeys": 8, "uniqueTools": 3, "peakHour": "2025-01-15T14", "peakHourCalls": 85 },
|
|
2650
|
+
"toolPopularity": [{ "tool": "summarize", "calls": 250, "successRate": 0.96, "credits": 2500 }],
|
|
2651
|
+
"hourlyVolume": [{ "hour": "2025-01-15T14", "calls": 85, "allowed": 80, "denied": 5, "credits": 400 }],
|
|
2652
|
+
"topConsumers": [{ "name": "heavy-user", "calls": 150, "successRate": 0.92, "credits": 1380 }],
|
|
2653
|
+
"byNamespace": [{ "namespace": "production", "calls": 400, "allowed": 380, "credits": 3800 }]
|
|
2654
|
+
}
|
|
2655
|
+
```
|
|
2656
|
+
|
|
2657
|
+
Returns traffic summary with success rate and peak hour, tool popularity ranked by call count with success rates and credit totals, hourly volume (last 24 hours) with allowed/denied/credit breakdowns, top 10 consumers by call count, and namespace breakdown with per-namespace stats. Read-only.
|
|
2658
|
+
|
|
2659
|
+
### Security Audit
|
|
2660
|
+
|
|
2661
|
+
```bash
|
|
2662
|
+
curl http://localhost:3000/admin/security -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2663
|
+
```
|
|
2664
|
+
|
|
2665
|
+
```json
|
|
2666
|
+
{
|
|
2667
|
+
"score": 72,
|
|
2668
|
+
"summary": { "totalKeys": 5, "totalFindings": 12 },
|
|
2669
|
+
"findings": [
|
|
2670
|
+
{ "type": "no_ip_allowlist", "severity": "warning", "keys": ["prod-key", "dev-key"], "description": "Keys without IP allowlists can be used from any IP address" },
|
|
2671
|
+
{ "type": "no_acl_restriction", "severity": "info", "keys": ["dev-key"], "description": "Keys without ACL restrictions can access all tools" },
|
|
2672
|
+
{ "type": "high_credit_balance", "severity": "warning", "keys": ["whale-key"], "description": "Keys with 10000+ credits are high-value targets if compromised" }
|
|
2673
|
+
]
|
|
2674
|
+
}
|
|
2675
|
+
```
|
|
2676
|
+
|
|
2677
|
+
Returns a composite security score (0-100) with per-finding breakdown. Scans all active keys for: missing IP allowlists (warning), missing quotas (info), unrestricted ACLs (info), no spending limits (info), no expiry dates (info), and high credit balances (warning). Well-configured keys with IP restrictions, tool ACLs, quotas, spending limits, and expiry dates will not appear in any findings. Read-only — does not modify system state.
|
|
2678
|
+
|
|
2639
2679
|
### IP Allowlisting
|
|
2640
2680
|
|
|
2641
2681
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -249,6 +249,8 @@ export declare class PayGateServer {
|
|
|
249
249
|
private handleRateLimitAnalysis;
|
|
250
250
|
private handleQuotaAnalysis;
|
|
251
251
|
private handleDenialAnalysis;
|
|
252
|
+
private handleTrafficAnalysis;
|
|
253
|
+
private handleSecurityAudit;
|
|
252
254
|
private handleGetNotes;
|
|
253
255
|
private handleAddNote;
|
|
254
256
|
private handleDeleteNote;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -840,6 +840,18 @@ class PayGateServer {
|
|
|
840
840
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
841
841
|
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
842
842
|
return;
|
|
843
|
+
case '/admin/traffic':
|
|
844
|
+
if (req.method === 'GET')
|
|
845
|
+
return this.handleTrafficAnalysis(req, res);
|
|
846
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
847
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
848
|
+
return;
|
|
849
|
+
case '/admin/security':
|
|
850
|
+
if (req.method === 'GET')
|
|
851
|
+
return this.handleSecurityAudit(req, res);
|
|
852
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
853
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
854
|
+
return;
|
|
843
855
|
// ─── Plugin endpoints ──────────────────────────────────────────────
|
|
844
856
|
case '/plugins':
|
|
845
857
|
return this.handleListPlugins(req, res);
|
|
@@ -1387,6 +1399,8 @@ class PayGateServer {
|
|
|
1387
1399
|
rateLimitAnalysis: 'GET /admin/rate-limits — Rate limit utilization analysis with per-key and per-tool breakdown, denial trends, and most throttled keys (requires X-Admin-Key)',
|
|
1388
1400
|
quotaAnalysis: 'GET /admin/quotas — Quota utilization analysis with per-key and per-tool breakdown, denial trends, most constrained keys, and configuration display (requires X-Admin-Key)',
|
|
1389
1401
|
denialAnalysis: 'GET /admin/denials — Comprehensive denial breakdown by reason type with per-key and per-tool stats, hourly trends, and most denied keys (requires X-Admin-Key)',
|
|
1402
|
+
trafficAnalysis: 'GET /admin/traffic — Traffic volume analysis with tool popularity, hourly volume, top consumers, namespace breakdown, and peak hour identification (requires X-Admin-Key)',
|
|
1403
|
+
securityAudit: 'GET /admin/security — Security posture analysis with findings for missing IP allowlists, quotas, ACLs, spending limits, expiry, high-credit keys, and composite score (requires X-Admin-Key)',
|
|
1390
1404
|
...(this.oauth ? {
|
|
1391
1405
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1392
1406
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -5150,6 +5164,239 @@ class PayGateServer {
|
|
|
5150
5164
|
mostDenied,
|
|
5151
5165
|
}));
|
|
5152
5166
|
}
|
|
5167
|
+
// ─── /admin/traffic — Traffic volume analysis ─────────────────────────────
|
|
5168
|
+
handleTrafficAnalysis(req, res) {
|
|
5169
|
+
if (!this.checkAdmin(req, res))
|
|
5170
|
+
return;
|
|
5171
|
+
const events = this.gate.meter.getEvents();
|
|
5172
|
+
// ── Summary ──
|
|
5173
|
+
let totalCalls = 0;
|
|
5174
|
+
let totalAllowed = 0;
|
|
5175
|
+
let totalDenied = 0;
|
|
5176
|
+
const uniqueKeysSet = new Set();
|
|
5177
|
+
const uniqueToolsSet = new Set();
|
|
5178
|
+
for (const e of events) {
|
|
5179
|
+
totalCalls++;
|
|
5180
|
+
if (e.allowed)
|
|
5181
|
+
totalAllowed++;
|
|
5182
|
+
else
|
|
5183
|
+
totalDenied++;
|
|
5184
|
+
uniqueKeysSet.add(e.keyName || e.apiKey.slice(0, 10));
|
|
5185
|
+
uniqueToolsSet.add(e.tool);
|
|
5186
|
+
}
|
|
5187
|
+
const successRate = totalCalls > 0
|
|
5188
|
+
? Math.round(totalAllowed / totalCalls * 10000) / 10000
|
|
5189
|
+
: 0;
|
|
5190
|
+
// ── Tool popularity ──
|
|
5191
|
+
const toolMap = new Map();
|
|
5192
|
+
for (const e of events) {
|
|
5193
|
+
if (!toolMap.has(e.tool))
|
|
5194
|
+
toolMap.set(e.tool, { calls: 0, allowed: 0, credits: 0 });
|
|
5195
|
+
const t = toolMap.get(e.tool);
|
|
5196
|
+
t.calls++;
|
|
5197
|
+
if (e.allowed) {
|
|
5198
|
+
t.allowed++;
|
|
5199
|
+
t.credits += e.creditsCharged;
|
|
5200
|
+
}
|
|
5201
|
+
}
|
|
5202
|
+
const toolPopularity = Array.from(toolMap.entries())
|
|
5203
|
+
.map(([tool, stats]) => ({
|
|
5204
|
+
tool,
|
|
5205
|
+
calls: stats.calls,
|
|
5206
|
+
successRate: stats.calls > 0 ? Math.round(stats.allowed / stats.calls * 10000) / 10000 : 0,
|
|
5207
|
+
credits: stats.credits,
|
|
5208
|
+
}))
|
|
5209
|
+
.sort((a, b) => b.calls - a.calls);
|
|
5210
|
+
// ── Hourly volume ──
|
|
5211
|
+
const hourBuckets = new Map();
|
|
5212
|
+
for (const e of events) {
|
|
5213
|
+
const hour = e.timestamp.slice(0, 13);
|
|
5214
|
+
if (!hourBuckets.has(hour))
|
|
5215
|
+
hourBuckets.set(hour, { calls: 0, allowed: 0, denied: 0, credits: 0 });
|
|
5216
|
+
const h = hourBuckets.get(hour);
|
|
5217
|
+
h.calls++;
|
|
5218
|
+
if (e.allowed) {
|
|
5219
|
+
h.allowed++;
|
|
5220
|
+
h.credits += e.creditsCharged;
|
|
5221
|
+
}
|
|
5222
|
+
else {
|
|
5223
|
+
h.denied++;
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
const hourlyVolume = Array.from(hourBuckets.entries())
|
|
5227
|
+
.map(([hour, stats]) => ({ hour, ...stats }))
|
|
5228
|
+
.sort((a, b) => a.hour.localeCompare(b.hour))
|
|
5229
|
+
.slice(-24);
|
|
5230
|
+
// ── Peak hour ──
|
|
5231
|
+
let peakHour = null;
|
|
5232
|
+
let peakHourCalls = 0;
|
|
5233
|
+
for (const h of hourlyVolume) {
|
|
5234
|
+
if (h.calls > peakHourCalls) {
|
|
5235
|
+
peakHour = h.hour;
|
|
5236
|
+
peakHourCalls = h.calls;
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
// ── Top consumers (by call count) ──
|
|
5240
|
+
const keyMap = new Map();
|
|
5241
|
+
for (const e of events) {
|
|
5242
|
+
const name = e.keyName || e.apiKey.slice(0, 10);
|
|
5243
|
+
if (!keyMap.has(name))
|
|
5244
|
+
keyMap.set(name, { name, calls: 0, allowed: 0, credits: 0 });
|
|
5245
|
+
const k = keyMap.get(name);
|
|
5246
|
+
k.calls++;
|
|
5247
|
+
if (e.allowed) {
|
|
5248
|
+
k.allowed++;
|
|
5249
|
+
k.credits += e.creditsCharged;
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5252
|
+
const topConsumers = Array.from(keyMap.values())
|
|
5253
|
+
.map(k => ({
|
|
5254
|
+
name: k.name,
|
|
5255
|
+
calls: k.calls,
|
|
5256
|
+
successRate: k.calls > 0 ? Math.round(k.allowed / k.calls * 10000) / 10000 : 0,
|
|
5257
|
+
credits: k.credits,
|
|
5258
|
+
}))
|
|
5259
|
+
.sort((a, b) => b.calls - a.calls)
|
|
5260
|
+
.slice(0, 10);
|
|
5261
|
+
// ── Namespace breakdown ──
|
|
5262
|
+
const nsMap = new Map();
|
|
5263
|
+
for (const e of events) {
|
|
5264
|
+
const ns = e.namespace || 'default';
|
|
5265
|
+
if (!nsMap.has(ns))
|
|
5266
|
+
nsMap.set(ns, { calls: 0, allowed: 0, credits: 0 });
|
|
5267
|
+
const n = nsMap.get(ns);
|
|
5268
|
+
n.calls++;
|
|
5269
|
+
if (e.allowed) {
|
|
5270
|
+
n.allowed++;
|
|
5271
|
+
n.credits += e.creditsCharged;
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
const byNamespace = Array.from(nsMap.entries())
|
|
5275
|
+
.map(([namespace, stats]) => ({ namespace, ...stats }))
|
|
5276
|
+
.sort((a, b) => b.calls - a.calls);
|
|
5277
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5278
|
+
res.end(JSON.stringify({
|
|
5279
|
+
summary: {
|
|
5280
|
+
totalCalls,
|
|
5281
|
+
totalAllowed,
|
|
5282
|
+
totalDenied,
|
|
5283
|
+
successRate,
|
|
5284
|
+
uniqueKeys: uniqueKeysSet.size,
|
|
5285
|
+
uniqueTools: uniqueToolsSet.size,
|
|
5286
|
+
peakHour,
|
|
5287
|
+
peakHourCalls,
|
|
5288
|
+
},
|
|
5289
|
+
toolPopularity,
|
|
5290
|
+
hourlyVolume,
|
|
5291
|
+
topConsumers,
|
|
5292
|
+
byNamespace,
|
|
5293
|
+
}));
|
|
5294
|
+
}
|
|
5295
|
+
// ─── /admin/security — Security posture audit ───────────────────────────
|
|
5296
|
+
handleSecurityAudit(req, res) {
|
|
5297
|
+
if (!this.checkAdmin(req, res))
|
|
5298
|
+
return;
|
|
5299
|
+
const allRecords = this.gate.store.getAllRecords();
|
|
5300
|
+
const HIGH_CREDIT_THRESHOLD = 10000;
|
|
5301
|
+
const findings = [];
|
|
5302
|
+
// ── no_ip_allowlist (warning) ──
|
|
5303
|
+
const noIp = allRecords.filter(r => r.active && (!r.ipAllowlist || r.ipAllowlist.length === 0));
|
|
5304
|
+
if (noIp.length > 0) {
|
|
5305
|
+
findings.push({
|
|
5306
|
+
type: 'no_ip_allowlist',
|
|
5307
|
+
severity: 'warning',
|
|
5308
|
+
keys: noIp.map(r => r.name),
|
|
5309
|
+
description: 'Keys without IP allowlists can be used from any IP address',
|
|
5310
|
+
});
|
|
5311
|
+
}
|
|
5312
|
+
// ── no_quota (info) ──
|
|
5313
|
+
const noQuota = allRecords.filter(r => {
|
|
5314
|
+
if (!r.active)
|
|
5315
|
+
return false;
|
|
5316
|
+
// Has per-key quota?
|
|
5317
|
+
if (r.quota) {
|
|
5318
|
+
const q = r.quota;
|
|
5319
|
+
if (q.dailyCallLimit > 0 || q.monthlyCallLimit > 0 || q.dailyCreditLimit > 0 || q.monthlyCreditLimit > 0)
|
|
5320
|
+
return false;
|
|
5321
|
+
}
|
|
5322
|
+
// Has global quota?
|
|
5323
|
+
if (this.config.globalQuota) {
|
|
5324
|
+
const gq = this.config.globalQuota;
|
|
5325
|
+
if (gq.dailyCallLimit > 0 || gq.monthlyCallLimit > 0 || gq.dailyCreditLimit > 0 || gq.monthlyCreditLimit > 0)
|
|
5326
|
+
return false;
|
|
5327
|
+
}
|
|
5328
|
+
return true;
|
|
5329
|
+
});
|
|
5330
|
+
if (noQuota.length > 0) {
|
|
5331
|
+
findings.push({
|
|
5332
|
+
type: 'no_quota',
|
|
5333
|
+
severity: 'info',
|
|
5334
|
+
keys: noQuota.map(r => r.name),
|
|
5335
|
+
description: 'Keys without quotas have no daily/monthly usage limits',
|
|
5336
|
+
});
|
|
5337
|
+
}
|
|
5338
|
+
// ── no_acl_restriction (info) ──
|
|
5339
|
+
const noAcl = allRecords.filter(r => r.active && (!r.allowedTools || r.allowedTools.length === 0));
|
|
5340
|
+
if (noAcl.length > 0) {
|
|
5341
|
+
findings.push({
|
|
5342
|
+
type: 'no_acl_restriction',
|
|
5343
|
+
severity: 'info',
|
|
5344
|
+
keys: noAcl.map(r => r.name),
|
|
5345
|
+
description: 'Keys without ACL restrictions can access all tools',
|
|
5346
|
+
});
|
|
5347
|
+
}
|
|
5348
|
+
// ── no_spending_limit (info) ──
|
|
5349
|
+
const noSpend = allRecords.filter(r => r.active && (!r.spendingLimit || r.spendingLimit === 0));
|
|
5350
|
+
if (noSpend.length > 0) {
|
|
5351
|
+
findings.push({
|
|
5352
|
+
type: 'no_spending_limit',
|
|
5353
|
+
severity: 'info',
|
|
5354
|
+
keys: noSpend.map(r => r.name),
|
|
5355
|
+
description: 'Keys without spending limits have no cap on total credit consumption',
|
|
5356
|
+
});
|
|
5357
|
+
}
|
|
5358
|
+
// ── no_expiry (info) ──
|
|
5359
|
+
const noExpiry = allRecords.filter(r => r.active && !r.expiresAt);
|
|
5360
|
+
if (noExpiry.length > 0) {
|
|
5361
|
+
findings.push({
|
|
5362
|
+
type: 'no_expiry',
|
|
5363
|
+
severity: 'info',
|
|
5364
|
+
keys: noExpiry.map(r => r.name),
|
|
5365
|
+
description: 'Keys without expiry dates remain valid indefinitely',
|
|
5366
|
+
});
|
|
5367
|
+
}
|
|
5368
|
+
// ── high_credit_balance (warning) ──
|
|
5369
|
+
const highCredit = allRecords.filter(r => r.active && r.credits >= HIGH_CREDIT_THRESHOLD);
|
|
5370
|
+
if (highCredit.length > 0) {
|
|
5371
|
+
findings.push({
|
|
5372
|
+
type: 'high_credit_balance',
|
|
5373
|
+
severity: 'warning',
|
|
5374
|
+
keys: highCredit.map(r => r.name),
|
|
5375
|
+
description: `Keys with ${HIGH_CREDIT_THRESHOLD}+ credits are high-value targets if compromised`,
|
|
5376
|
+
});
|
|
5377
|
+
}
|
|
5378
|
+
// ── Compute score ──
|
|
5379
|
+
const totalKeys = allRecords.filter(r => r.active).length;
|
|
5380
|
+
const totalFindings = findings.reduce((sum, f) => sum + f.keys.length, 0);
|
|
5381
|
+
let score = 100;
|
|
5382
|
+
if (totalKeys > 0) {
|
|
5383
|
+
// Each warning finding per key deducts more than info
|
|
5384
|
+
for (const f of findings) {
|
|
5385
|
+
const weight = f.severity === 'warning' ? 5 : f.severity === 'critical' ? 10 : 2;
|
|
5386
|
+
score -= f.keys.length * weight;
|
|
5387
|
+
}
|
|
5388
|
+
score = Math.max(0, Math.min(100, score));
|
|
5389
|
+
}
|
|
5390
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5391
|
+
res.end(JSON.stringify({
|
|
5392
|
+
score,
|
|
5393
|
+
summary: {
|
|
5394
|
+
totalKeys,
|
|
5395
|
+
totalFindings,
|
|
5396
|
+
},
|
|
5397
|
+
findings,
|
|
5398
|
+
}));
|
|
5399
|
+
}
|
|
5153
5400
|
// ─── /keys/notes — Timestamped notes on API keys ─────────────────────────
|
|
5154
5401
|
handleGetNotes(req, res) {
|
|
5155
5402
|
if (!this.checkAdmin(req, res))
|