paygate-mcp 8.10.0 → 8.12.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 +189 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,6 +117,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
117
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
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
|
|
119
119
|
- **Revenue Analysis** — `GET /admin/revenue` revenue metrics with per-tool revenue breakdown, per-key spending, hourly revenue trends, credit flow summary (allocated/spent/remaining), and average revenue per call
|
|
120
|
+
- **Key Portfolio Health** — `GET /admin/key-portfolio` portfolio-wide key health with active/inactive/suspended counts, stale keys, expiring-soon keys, age distribution, credit utilization, and namespace breakdown
|
|
121
|
+
- **Anomaly Detection** — `GET /admin/anomalies` identifies unusual patterns: keys with high denial rates, rapid credit depletion, low remaining credits, with severity ratings and detailed descriptions
|
|
120
122
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
121
123
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
122
124
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2695,6 +2697,44 @@ curl http://localhost:3000/admin/revenue -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
|
2695
2697
|
|
|
2696
2698
|
Returns revenue summary with total credits earned, per-tool revenue ranked by earnings with average per-call, top 10 per-key spending, hourly revenue trends (last 24 hours), and credit flow showing total allocated vs spent vs remaining across all active keys. Only counts successful (allowed) calls. Read-only.
|
|
2697
2699
|
|
|
2700
|
+
### Key Portfolio Health
|
|
2701
|
+
|
|
2702
|
+
```bash
|
|
2703
|
+
curl http://localhost:3000/admin/key-portfolio -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2704
|
+
```
|
|
2705
|
+
|
|
2706
|
+
```json
|
|
2707
|
+
{
|
|
2708
|
+
"summary": { "totalKeys": 10, "activeKeys": 7, "inactiveKeys": 2, "suspendedKeys": 1, "averageCreditUtilization": 0.35 },
|
|
2709
|
+
"staleKeys": [{ "name": "unused-key", "createdAt": "2025-01-01T00:00:00Z", "credits": 500, "ageDays": 30 }],
|
|
2710
|
+
"expiringSoon": [{ "name": "temp-key", "expiresAt": "2025-01-20T00:00:00Z", "hoursRemaining": 48, "credits": 100 }],
|
|
2711
|
+
"ageDistribution": { "averageAgeDays": 15, "oldestAgeDays": 60, "newestAgeDays": 0 },
|
|
2712
|
+
"byNamespace": [{ "namespace": "production", "total": 5, "active": 4, "suspended": 1 }]
|
|
2713
|
+
}
|
|
2714
|
+
```
|
|
2715
|
+
|
|
2716
|
+
Returns portfolio-wide key health: active/inactive/suspended counts, average credit utilization, stale keys (created but never used), keys expiring within 7 days sorted by urgency, age distribution statistics, and namespace breakdown. Read-only.
|
|
2717
|
+
|
|
2718
|
+
### Anomaly Detection
|
|
2719
|
+
|
|
2720
|
+
```bash
|
|
2721
|
+
curl http://localhost:3000/admin/anomalies -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2722
|
+
```
|
|
2723
|
+
|
|
2724
|
+
```json
|
|
2725
|
+
{
|
|
2726
|
+
"summary": { "totalAnomalies": 3, "byType": { "high_denial_rate": 1, "rapid_credit_depletion": 1, "low_credits": 1 } },
|
|
2727
|
+
"anomalies": [
|
|
2728
|
+
{ "type": "high_denial_rate", "severity": "warning", "keyName": "test-key", "description": "Key \"test-key\" has 80% denial rate (8/10 calls denied)" },
|
|
2729
|
+
{ "type": "rapid_credit_depletion", "severity": "warning", "keyName": "fast-spender", "description": "Key \"fast-spender\" has used 95% of allocated credits (950/1000)" },
|
|
2730
|
+
{ "type": "low_credits", "severity": "info", "keyName": "nearly-empty", "description": "Key \"nearly-empty\" has only 5 credits remaining (5% of allocated)" }
|
|
2731
|
+
],
|
|
2732
|
+
"analyzedAt": "2025-01-15T14:30:00Z"
|
|
2733
|
+
}
|
|
2734
|
+
```
|
|
2735
|
+
|
|
2736
|
+
Scans all active keys for anomalous patterns: keys with >50% denial rates (3+ calls minimum), rapid credit depletion (>=75% spent), and low remaining credits (<=10 credits or <=10% remaining). Each anomaly includes type, severity, affected key name, and human-readable description. Read-only.
|
|
2737
|
+
|
|
2698
2738
|
### IP Allowlisting
|
|
2699
2739
|
|
|
2700
2740
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -252,6 +252,8 @@ export declare class PayGateServer {
|
|
|
252
252
|
private handleTrafficAnalysis;
|
|
253
253
|
private handleSecurityAudit;
|
|
254
254
|
private handleRevenueAnalysis;
|
|
255
|
+
private handleLifecycleAnalysis;
|
|
256
|
+
private handleAnomalyDetection;
|
|
255
257
|
private handleGetNotes;
|
|
256
258
|
private handleAddNote;
|
|
257
259
|
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
|
@@ -858,6 +858,18 @@ class PayGateServer {
|
|
|
858
858
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
859
859
|
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
860
860
|
return;
|
|
861
|
+
case '/admin/key-portfolio':
|
|
862
|
+
if (req.method === 'GET')
|
|
863
|
+
return this.handleLifecycleAnalysis(req, res);
|
|
864
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
865
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
866
|
+
return;
|
|
867
|
+
case '/admin/anomalies':
|
|
868
|
+
if (req.method === 'GET')
|
|
869
|
+
return this.handleAnomalyDetection(req, res);
|
|
870
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
871
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
872
|
+
return;
|
|
861
873
|
// ─── Plugin endpoints ──────────────────────────────────────────────
|
|
862
874
|
case '/plugins':
|
|
863
875
|
return this.handleListPlugins(req, res);
|
|
@@ -1408,6 +1420,8 @@ class PayGateServer {
|
|
|
1408
1420
|
trafficAnalysis: 'GET /admin/traffic — Traffic volume analysis with tool popularity, hourly volume, top consumers, namespace breakdown, and peak hour identification (requires X-Admin-Key)',
|
|
1409
1421
|
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)',
|
|
1410
1422
|
revenueAnalysis: 'GET /admin/revenue — Revenue metrics with per-tool revenue, per-key spending, hourly revenue trends, credit flow summary, and average revenue per call (requires X-Admin-Key)',
|
|
1423
|
+
keyPortfolio: 'GET /admin/key-portfolio — Key portfolio health with active/inactive/suspended counts, stale keys, expiring-soon keys, age distribution, credit utilization, and namespace breakdown (requires X-Admin-Key)',
|
|
1424
|
+
anomalyDetection: 'GET /admin/anomalies — Anomaly detection identifying high denial rates, rapid credit depletion, low credit balances, and other unusual patterns (requires X-Admin-Key)',
|
|
1411
1425
|
...(this.oauth ? {
|
|
1412
1426
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1413
1427
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -5481,6 +5495,181 @@ class PayGateServer {
|
|
|
5481
5495
|
},
|
|
5482
5496
|
}));
|
|
5483
5497
|
}
|
|
5498
|
+
// ─── /admin/lifecycle — Key lifecycle analysis ─────────────────────────
|
|
5499
|
+
handleLifecycleAnalysis(req, res) {
|
|
5500
|
+
if (!this.checkAdmin(req, res))
|
|
5501
|
+
return;
|
|
5502
|
+
const allRecords = this.gate.store.getAllRecords();
|
|
5503
|
+
const now = Date.now();
|
|
5504
|
+
const EXPIRY_SOON_MS = 7 * 24 * 3600 * 1000; // 7 days
|
|
5505
|
+
// ── Summary counts ──
|
|
5506
|
+
const activeKeys = allRecords.filter(r => r.active && !r.suspended);
|
|
5507
|
+
const inactiveKeys = allRecords.filter(r => !r.active);
|
|
5508
|
+
const suspendedKeys = allRecords.filter(r => r.active && r.suspended);
|
|
5509
|
+
// ── Stale keys (active, never used) ──
|
|
5510
|
+
const staleKeys = allRecords
|
|
5511
|
+
.filter(r => r.active && !r.suspended && !r.lastUsedAt)
|
|
5512
|
+
.map(r => ({
|
|
5513
|
+
name: r.name,
|
|
5514
|
+
createdAt: r.createdAt,
|
|
5515
|
+
credits: r.credits,
|
|
5516
|
+
ageDays: Math.round((now - new Date(r.createdAt).getTime()) / 86400000 * 10) / 10,
|
|
5517
|
+
}));
|
|
5518
|
+
// ── Expiring soon (active, expiry within threshold) ──
|
|
5519
|
+
const expiringSoon = allRecords
|
|
5520
|
+
.filter(r => {
|
|
5521
|
+
if (!r.active || !r.expiresAt)
|
|
5522
|
+
return false;
|
|
5523
|
+
const expiresMs = new Date(r.expiresAt).getTime();
|
|
5524
|
+
return expiresMs > now && expiresMs - now <= EXPIRY_SOON_MS;
|
|
5525
|
+
})
|
|
5526
|
+
.map(r => ({
|
|
5527
|
+
name: r.name,
|
|
5528
|
+
expiresAt: r.expiresAt,
|
|
5529
|
+
hoursRemaining: Math.round((new Date(r.expiresAt).getTime() - now) / 3600000 * 10) / 10,
|
|
5530
|
+
credits: r.credits,
|
|
5531
|
+
}))
|
|
5532
|
+
.sort((a, b) => a.hoursRemaining - b.hoursRemaining);
|
|
5533
|
+
// ── Age distribution ──
|
|
5534
|
+
const ages = allRecords.map(r => (now - new Date(r.createdAt).getTime()) / 86400000);
|
|
5535
|
+
const averageAgeDays = ages.length > 0 ? Math.round(ages.reduce((s, a) => s + a, 0) / ages.length * 10) / 10 : 0;
|
|
5536
|
+
const oldestAgeDays = ages.length > 0 ? Math.round(Math.max(...ages) * 10) / 10 : 0;
|
|
5537
|
+
const newestAgeDays = ages.length > 0 ? Math.round(Math.min(...ages) * 10) / 10 : 0;
|
|
5538
|
+
// ── Credit utilization ──
|
|
5539
|
+
let totalAllocated = 0;
|
|
5540
|
+
let totalSpent = 0;
|
|
5541
|
+
for (const r of allRecords) {
|
|
5542
|
+
totalAllocated += r.credits + (r.totalSpent || 0);
|
|
5543
|
+
totalSpent += r.totalSpent || 0;
|
|
5544
|
+
}
|
|
5545
|
+
const averageCreditUtilization = totalAllocated > 0 ? Math.round(totalSpent / totalAllocated * 10000) / 10000 : 0;
|
|
5546
|
+
// ── Namespace breakdown ──
|
|
5547
|
+
const nsMap = new Map();
|
|
5548
|
+
for (const r of allRecords) {
|
|
5549
|
+
const ns = r.namespace || 'default';
|
|
5550
|
+
if (!nsMap.has(ns))
|
|
5551
|
+
nsMap.set(ns, { total: 0, active: 0, suspended: 0 });
|
|
5552
|
+
const n = nsMap.get(ns);
|
|
5553
|
+
n.total++;
|
|
5554
|
+
if (r.active && !r.suspended)
|
|
5555
|
+
n.active++;
|
|
5556
|
+
if (r.suspended)
|
|
5557
|
+
n.suspended++;
|
|
5558
|
+
}
|
|
5559
|
+
const byNamespace = Array.from(nsMap.entries())
|
|
5560
|
+
.map(([namespace, stats]) => ({ namespace, ...stats }))
|
|
5561
|
+
.sort((a, b) => b.total - a.total);
|
|
5562
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5563
|
+
res.end(JSON.stringify({
|
|
5564
|
+
summary: {
|
|
5565
|
+
totalKeys: allRecords.length,
|
|
5566
|
+
activeKeys: activeKeys.length,
|
|
5567
|
+
inactiveKeys: inactiveKeys.length,
|
|
5568
|
+
suspendedKeys: suspendedKeys.length,
|
|
5569
|
+
averageCreditUtilization,
|
|
5570
|
+
},
|
|
5571
|
+
staleKeys,
|
|
5572
|
+
expiringSoon,
|
|
5573
|
+
ageDistribution: {
|
|
5574
|
+
averageAgeDays,
|
|
5575
|
+
oldestAgeDays,
|
|
5576
|
+
newestAgeDays,
|
|
5577
|
+
},
|
|
5578
|
+
byNamespace,
|
|
5579
|
+
}));
|
|
5580
|
+
}
|
|
5581
|
+
// ─── /admin/anomalies — Anomaly detection ──────────────────────────────
|
|
5582
|
+
handleAnomalyDetection(req, res) {
|
|
5583
|
+
if (!this.checkAdmin(req, res))
|
|
5584
|
+
return;
|
|
5585
|
+
const events = this.gate.meter.getEvents();
|
|
5586
|
+
const allRecords = this.gate.store.getAllRecords();
|
|
5587
|
+
const anomalies = [];
|
|
5588
|
+
// ── High denial rate per key (>50% denied with at least 3 calls) ──
|
|
5589
|
+
const keyEvents = new Map();
|
|
5590
|
+
for (const e of events) {
|
|
5591
|
+
const name = e.keyName || e.apiKey;
|
|
5592
|
+
if (!keyEvents.has(name))
|
|
5593
|
+
keyEvents.set(name, { allowed: 0, denied: 0, name });
|
|
5594
|
+
const k = keyEvents.get(name);
|
|
5595
|
+
if (e.allowed)
|
|
5596
|
+
k.allowed++;
|
|
5597
|
+
else
|
|
5598
|
+
k.denied++;
|
|
5599
|
+
}
|
|
5600
|
+
for (const [, stats] of keyEvents) {
|
|
5601
|
+
const total = stats.allowed + stats.denied;
|
|
5602
|
+
if (total >= 3 && stats.denied / total > 0.5) {
|
|
5603
|
+
anomalies.push({
|
|
5604
|
+
type: 'high_denial_rate',
|
|
5605
|
+
severity: 'warning',
|
|
5606
|
+
keyName: stats.name,
|
|
5607
|
+
description: `Key "${stats.name}" has ${Math.round(stats.denied / total * 100)}% denial rate (${stats.denied}/${total} calls denied)`,
|
|
5608
|
+
details: { allowed: stats.allowed, denied: stats.denied, total, denialRate: Math.round(stats.denied / total * 10000) / 10000 },
|
|
5609
|
+
});
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5612
|
+
// ── Rapid credit depletion (>75% credits spent with at least 1 call) ──
|
|
5613
|
+
for (const record of allRecords) {
|
|
5614
|
+
if (!record.active)
|
|
5615
|
+
continue;
|
|
5616
|
+
const totalAllocated = record.credits + (record.totalSpent || 0);
|
|
5617
|
+
if (totalAllocated > 0 && record.totalSpent > 0) {
|
|
5618
|
+
const utilization = record.totalSpent / totalAllocated;
|
|
5619
|
+
if (utilization >= 0.75) {
|
|
5620
|
+
anomalies.push({
|
|
5621
|
+
type: 'rapid_credit_depletion',
|
|
5622
|
+
severity: 'warning',
|
|
5623
|
+
keyName: record.name,
|
|
5624
|
+
description: `Key "${record.name}" has used ${Math.round(utilization * 100)}% of allocated credits (${record.totalSpent}/${totalAllocated})`,
|
|
5625
|
+
details: { totalAllocated, totalSpent: record.totalSpent, remaining: record.credits, utilization: Math.round(utilization * 10000) / 10000 },
|
|
5626
|
+
});
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
// ── Low remaining credits (active key with <10% remaining or <10 credits) ──
|
|
5631
|
+
for (const record of allRecords) {
|
|
5632
|
+
if (!record.active)
|
|
5633
|
+
continue;
|
|
5634
|
+
const totalAllocated = record.credits + (record.totalSpent || 0);
|
|
5635
|
+
if (totalAllocated > 0 && record.totalSpent > 0) {
|
|
5636
|
+
const remaining = record.credits;
|
|
5637
|
+
const remainingPct = remaining / totalAllocated;
|
|
5638
|
+
if (remaining <= 10 || remainingPct <= 0.1) {
|
|
5639
|
+
// Don't duplicate if already flagged as rapid depletion
|
|
5640
|
+
const alreadyFlagged = anomalies.some(a => a.type === 'rapid_credit_depletion' && a.keyName === record.name);
|
|
5641
|
+
if (!alreadyFlagged) {
|
|
5642
|
+
anomalies.push({
|
|
5643
|
+
type: 'low_credits',
|
|
5644
|
+
severity: 'info',
|
|
5645
|
+
keyName: record.name,
|
|
5646
|
+
description: `Key "${record.name}" has only ${remaining} credits remaining (${Math.round(remainingPct * 100)}% of allocated)`,
|
|
5647
|
+
details: { remaining, totalAllocated, remainingPct: Math.round(remainingPct * 10000) / 10000 },
|
|
5648
|
+
});
|
|
5649
|
+
}
|
|
5650
|
+
else {
|
|
5651
|
+
// Still add as separate anomaly type for detection
|
|
5652
|
+
anomalies.push({
|
|
5653
|
+
type: 'low_credits',
|
|
5654
|
+
severity: 'info',
|
|
5655
|
+
keyName: record.name,
|
|
5656
|
+
description: `Key "${record.name}" has only ${remaining} credits remaining (${Math.round(remainingPct * 100)}% of allocated)`,
|
|
5657
|
+
details: { remaining, totalAllocated, remainingPct: Math.round(remainingPct * 10000) / 10000 },
|
|
5658
|
+
});
|
|
5659
|
+
}
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5663
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5664
|
+
res.end(JSON.stringify({
|
|
5665
|
+
summary: {
|
|
5666
|
+
totalAnomalies: anomalies.length,
|
|
5667
|
+
byType: anomalies.reduce((acc, a) => { acc[a.type] = (acc[a.type] || 0) + 1; return acc; }, {}),
|
|
5668
|
+
},
|
|
5669
|
+
anomalies,
|
|
5670
|
+
analyzedAt: new Date().toISOString(),
|
|
5671
|
+
}));
|
|
5672
|
+
}
|
|
5484
5673
|
// ─── /keys/notes — Timestamped notes on API keys ─────────────────────────
|
|
5485
5674
|
handleGetNotes(req, res) {
|
|
5486
5675
|
if (!this.checkAdmin(req, res))
|