paygate-mcp 8.9.0 → 8.11.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 +38 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +174 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -116,6 +116,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
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
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
|
+
- **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
|
|
119
121
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
120
122
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
121
123
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2676,6 +2678,42 @@ curl http://localhost:3000/admin/security -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
|
2676
2678
|
|
|
2677
2679
|
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
2680
|
|
|
2681
|
+
### Revenue Analysis
|
|
2682
|
+
|
|
2683
|
+
```bash
|
|
2684
|
+
curl http://localhost:3000/admin/revenue -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2685
|
+
```
|
|
2686
|
+
|
|
2687
|
+
```json
|
|
2688
|
+
{
|
|
2689
|
+
"summary": { "totalRevenue": 5000, "totalCalls": 250, "averageRevenuePerCall": 20 },
|
|
2690
|
+
"byTool": [{ "tool": "summarize", "revenue": 3000, "calls": 150, "averagePerCall": 20 }],
|
|
2691
|
+
"byKey": [{ "name": "heavy-user", "revenue": 2000, "calls": 80 }],
|
|
2692
|
+
"hourlyRevenue": [{ "hour": "2025-01-15T14", "revenue": 500, "calls": 25 }],
|
|
2693
|
+
"creditFlow": { "totalAllocated": 50000, "totalSpent": 5000, "totalRemaining": 45000 }
|
|
2694
|
+
}
|
|
2695
|
+
```
|
|
2696
|
+
|
|
2697
|
+
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.
|
|
2698
|
+
|
|
2699
|
+
### Key Portfolio Health
|
|
2700
|
+
|
|
2701
|
+
```bash
|
|
2702
|
+
curl http://localhost:3000/admin/key-portfolio -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2703
|
+
```
|
|
2704
|
+
|
|
2705
|
+
```json
|
|
2706
|
+
{
|
|
2707
|
+
"summary": { "totalKeys": 10, "activeKeys": 7, "inactiveKeys": 2, "suspendedKeys": 1, "averageCreditUtilization": 0.35 },
|
|
2708
|
+
"staleKeys": [{ "name": "unused-key", "createdAt": "2025-01-01T00:00:00Z", "credits": 500, "ageDays": 30 }],
|
|
2709
|
+
"expiringSoon": [{ "name": "temp-key", "expiresAt": "2025-01-20T00:00:00Z", "hoursRemaining": 48, "credits": 100 }],
|
|
2710
|
+
"ageDistribution": { "averageAgeDays": 15, "oldestAgeDays": 60, "newestAgeDays": 0 },
|
|
2711
|
+
"byNamespace": [{ "namespace": "production", "total": 5, "active": 4, "suspended": 1 }]
|
|
2712
|
+
}
|
|
2713
|
+
```
|
|
2714
|
+
|
|
2715
|
+
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.
|
|
2716
|
+
|
|
2679
2717
|
### IP Allowlisting
|
|
2680
2718
|
|
|
2681
2719
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -251,6 +251,8 @@ export declare class PayGateServer {
|
|
|
251
251
|
private handleDenialAnalysis;
|
|
252
252
|
private handleTrafficAnalysis;
|
|
253
253
|
private handleSecurityAudit;
|
|
254
|
+
private handleRevenueAnalysis;
|
|
255
|
+
private handleLifecycleAnalysis;
|
|
254
256
|
private handleGetNotes;
|
|
255
257
|
private handleAddNote;
|
|
256
258
|
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
|
@@ -852,6 +852,18 @@ class PayGateServer {
|
|
|
852
852
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
853
853
|
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
854
854
|
return;
|
|
855
|
+
case '/admin/revenue':
|
|
856
|
+
if (req.method === 'GET')
|
|
857
|
+
return this.handleRevenueAnalysis(req, res);
|
|
858
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
859
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
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;
|
|
855
867
|
// ─── Plugin endpoints ──────────────────────────────────────────────
|
|
856
868
|
case '/plugins':
|
|
857
869
|
return this.handleListPlugins(req, res);
|
|
@@ -1401,6 +1413,8 @@ class PayGateServer {
|
|
|
1401
1413
|
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
1414
|
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
1415
|
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)',
|
|
1416
|
+
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)',
|
|
1417
|
+
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)',
|
|
1404
1418
|
...(this.oauth ? {
|
|
1405
1419
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1406
1420
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -5397,6 +5411,166 @@ class PayGateServer {
|
|
|
5397
5411
|
findings,
|
|
5398
5412
|
}));
|
|
5399
5413
|
}
|
|
5414
|
+
// ─── /admin/revenue — Revenue analysis ──────────────────────────────────
|
|
5415
|
+
handleRevenueAnalysis(req, res) {
|
|
5416
|
+
if (!this.checkAdmin(req, res))
|
|
5417
|
+
return;
|
|
5418
|
+
const events = this.gate.meter.getEvents();
|
|
5419
|
+
const allRecords = this.gate.store.getAllRecords();
|
|
5420
|
+
// ── Summary ──
|
|
5421
|
+
const allowedEvents = events.filter(e => e.allowed);
|
|
5422
|
+
const totalRevenue = allowedEvents.reduce((sum, e) => sum + e.creditsCharged, 0);
|
|
5423
|
+
const totalCalls = allowedEvents.length;
|
|
5424
|
+
const averageRevenuePerCall = totalCalls > 0 ? Math.round(totalRevenue / totalCalls * 100) / 100 : 0;
|
|
5425
|
+
// ── By tool ──
|
|
5426
|
+
const toolMap = new Map();
|
|
5427
|
+
for (const e of allowedEvents) {
|
|
5428
|
+
if (!toolMap.has(e.tool))
|
|
5429
|
+
toolMap.set(e.tool, { revenue: 0, calls: 0 });
|
|
5430
|
+
const t = toolMap.get(e.tool);
|
|
5431
|
+
t.revenue += e.creditsCharged;
|
|
5432
|
+
t.calls++;
|
|
5433
|
+
}
|
|
5434
|
+
const byTool = Array.from(toolMap.entries())
|
|
5435
|
+
.map(([tool, stats]) => ({ tool, ...stats, averagePerCall: stats.calls > 0 ? Math.round(stats.revenue / stats.calls * 100) / 100 : 0 }))
|
|
5436
|
+
.sort((a, b) => b.revenue - a.revenue);
|
|
5437
|
+
// ── By key ──
|
|
5438
|
+
const keyMap = new Map();
|
|
5439
|
+
for (const e of allowedEvents) {
|
|
5440
|
+
const name = e.keyName || e.apiKey;
|
|
5441
|
+
if (!keyMap.has(name))
|
|
5442
|
+
keyMap.set(name, { name, revenue: 0, calls: 0 });
|
|
5443
|
+
const k = keyMap.get(name);
|
|
5444
|
+
k.revenue += e.creditsCharged;
|
|
5445
|
+
k.calls++;
|
|
5446
|
+
}
|
|
5447
|
+
const byKey = Array.from(keyMap.values())
|
|
5448
|
+
.sort((a, b) => b.revenue - a.revenue)
|
|
5449
|
+
.slice(0, 10);
|
|
5450
|
+
// ── Hourly revenue ──
|
|
5451
|
+
const hourMap = new Map();
|
|
5452
|
+
for (const e of allowedEvents) {
|
|
5453
|
+
const hour = new Date(e.timestamp).toISOString().slice(0, 13);
|
|
5454
|
+
if (!hourMap.has(hour))
|
|
5455
|
+
hourMap.set(hour, { revenue: 0, calls: 0 });
|
|
5456
|
+
const h = hourMap.get(hour);
|
|
5457
|
+
h.revenue += e.creditsCharged;
|
|
5458
|
+
h.calls++;
|
|
5459
|
+
}
|
|
5460
|
+
const hourlyRevenue = Array.from(hourMap.entries())
|
|
5461
|
+
.map(([hour, stats]) => ({ hour, ...stats }))
|
|
5462
|
+
.sort((a, b) => a.hour.localeCompare(b.hour));
|
|
5463
|
+
// ── Credit flow ──
|
|
5464
|
+
let totalAllocated = 0;
|
|
5465
|
+
let totalSpent = 0;
|
|
5466
|
+
let totalRemaining = 0;
|
|
5467
|
+
for (const record of allRecords) {
|
|
5468
|
+
if (!record.active)
|
|
5469
|
+
continue;
|
|
5470
|
+
totalAllocated += record.credits + (record.totalSpent || 0);
|
|
5471
|
+
totalSpent += record.totalSpent || 0;
|
|
5472
|
+
totalRemaining += record.credits;
|
|
5473
|
+
}
|
|
5474
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5475
|
+
res.end(JSON.stringify({
|
|
5476
|
+
summary: {
|
|
5477
|
+
totalRevenue,
|
|
5478
|
+
totalCalls,
|
|
5479
|
+
averageRevenuePerCall,
|
|
5480
|
+
},
|
|
5481
|
+
byTool,
|
|
5482
|
+
byKey,
|
|
5483
|
+
hourlyRevenue,
|
|
5484
|
+
creditFlow: {
|
|
5485
|
+
totalAllocated,
|
|
5486
|
+
totalSpent,
|
|
5487
|
+
totalRemaining,
|
|
5488
|
+
},
|
|
5489
|
+
}));
|
|
5490
|
+
}
|
|
5491
|
+
// ─── /admin/lifecycle — Key lifecycle analysis ─────────────────────────
|
|
5492
|
+
handleLifecycleAnalysis(req, res) {
|
|
5493
|
+
if (!this.checkAdmin(req, res))
|
|
5494
|
+
return;
|
|
5495
|
+
const allRecords = this.gate.store.getAllRecords();
|
|
5496
|
+
const now = Date.now();
|
|
5497
|
+
const EXPIRY_SOON_MS = 7 * 24 * 3600 * 1000; // 7 days
|
|
5498
|
+
// ── Summary counts ──
|
|
5499
|
+
const activeKeys = allRecords.filter(r => r.active && !r.suspended);
|
|
5500
|
+
const inactiveKeys = allRecords.filter(r => !r.active);
|
|
5501
|
+
const suspendedKeys = allRecords.filter(r => r.active && r.suspended);
|
|
5502
|
+
// ── Stale keys (active, never used) ──
|
|
5503
|
+
const staleKeys = allRecords
|
|
5504
|
+
.filter(r => r.active && !r.suspended && !r.lastUsedAt)
|
|
5505
|
+
.map(r => ({
|
|
5506
|
+
name: r.name,
|
|
5507
|
+
createdAt: r.createdAt,
|
|
5508
|
+
credits: r.credits,
|
|
5509
|
+
ageDays: Math.round((now - new Date(r.createdAt).getTime()) / 86400000 * 10) / 10,
|
|
5510
|
+
}));
|
|
5511
|
+
// ── Expiring soon (active, expiry within threshold) ──
|
|
5512
|
+
const expiringSoon = allRecords
|
|
5513
|
+
.filter(r => {
|
|
5514
|
+
if (!r.active || !r.expiresAt)
|
|
5515
|
+
return false;
|
|
5516
|
+
const expiresMs = new Date(r.expiresAt).getTime();
|
|
5517
|
+
return expiresMs > now && expiresMs - now <= EXPIRY_SOON_MS;
|
|
5518
|
+
})
|
|
5519
|
+
.map(r => ({
|
|
5520
|
+
name: r.name,
|
|
5521
|
+
expiresAt: r.expiresAt,
|
|
5522
|
+
hoursRemaining: Math.round((new Date(r.expiresAt).getTime() - now) / 3600000 * 10) / 10,
|
|
5523
|
+
credits: r.credits,
|
|
5524
|
+
}))
|
|
5525
|
+
.sort((a, b) => a.hoursRemaining - b.hoursRemaining);
|
|
5526
|
+
// ── Age distribution ──
|
|
5527
|
+
const ages = allRecords.map(r => (now - new Date(r.createdAt).getTime()) / 86400000);
|
|
5528
|
+
const averageAgeDays = ages.length > 0 ? Math.round(ages.reduce((s, a) => s + a, 0) / ages.length * 10) / 10 : 0;
|
|
5529
|
+
const oldestAgeDays = ages.length > 0 ? Math.round(Math.max(...ages) * 10) / 10 : 0;
|
|
5530
|
+
const newestAgeDays = ages.length > 0 ? Math.round(Math.min(...ages) * 10) / 10 : 0;
|
|
5531
|
+
// ── Credit utilization ──
|
|
5532
|
+
let totalAllocated = 0;
|
|
5533
|
+
let totalSpent = 0;
|
|
5534
|
+
for (const r of allRecords) {
|
|
5535
|
+
totalAllocated += r.credits + (r.totalSpent || 0);
|
|
5536
|
+
totalSpent += r.totalSpent || 0;
|
|
5537
|
+
}
|
|
5538
|
+
const averageCreditUtilization = totalAllocated > 0 ? Math.round(totalSpent / totalAllocated * 10000) / 10000 : 0;
|
|
5539
|
+
// ── Namespace breakdown ──
|
|
5540
|
+
const nsMap = new Map();
|
|
5541
|
+
for (const r of allRecords) {
|
|
5542
|
+
const ns = r.namespace || 'default';
|
|
5543
|
+
if (!nsMap.has(ns))
|
|
5544
|
+
nsMap.set(ns, { total: 0, active: 0, suspended: 0 });
|
|
5545
|
+
const n = nsMap.get(ns);
|
|
5546
|
+
n.total++;
|
|
5547
|
+
if (r.active && !r.suspended)
|
|
5548
|
+
n.active++;
|
|
5549
|
+
if (r.suspended)
|
|
5550
|
+
n.suspended++;
|
|
5551
|
+
}
|
|
5552
|
+
const byNamespace = Array.from(nsMap.entries())
|
|
5553
|
+
.map(([namespace, stats]) => ({ namespace, ...stats }))
|
|
5554
|
+
.sort((a, b) => b.total - a.total);
|
|
5555
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5556
|
+
res.end(JSON.stringify({
|
|
5557
|
+
summary: {
|
|
5558
|
+
totalKeys: allRecords.length,
|
|
5559
|
+
activeKeys: activeKeys.length,
|
|
5560
|
+
inactiveKeys: inactiveKeys.length,
|
|
5561
|
+
suspendedKeys: suspendedKeys.length,
|
|
5562
|
+
averageCreditUtilization,
|
|
5563
|
+
},
|
|
5564
|
+
staleKeys,
|
|
5565
|
+
expiringSoon,
|
|
5566
|
+
ageDistribution: {
|
|
5567
|
+
averageAgeDays,
|
|
5568
|
+
oldestAgeDays,
|
|
5569
|
+
newestAgeDays,
|
|
5570
|
+
},
|
|
5571
|
+
byNamespace,
|
|
5572
|
+
}));
|
|
5573
|
+
}
|
|
5400
5574
|
// ─── /keys/notes — Timestamped notes on API keys ─────────────────────────
|
|
5401
5575
|
handleGetNotes(req, res) {
|
|
5402
5576
|
if (!this.checkAdmin(req, res))
|