paygate-mcp 6.6.0 → 6.7.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 +29 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +170 -0
- package/dist/server.js.map +1 -1
- package/package.json +35 -8
package/README.md
CHANGED
|
@@ -93,6 +93,7 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
93
93
|
- **Credit History** — `GET /keys/credit-history?key=...` returns per-key credit mutation log — tracks initial allocation, topups, transfers (in/out), auto-topups, with type/limit/since filters, balance-before/after on every entry, newest-first ordering, capped at 100 entries per key
|
|
94
94
|
- **Spending Velocity** — `GET /keys/spending-velocity?key=...` returns credit burn rate and depletion forecast — credits/calls per hour/day, estimated depletion date, top tools by spend, configurable analysis window (1h–30d)
|
|
95
95
|
- **Key Comparison** — `GET /keys/compare?keys=pg_a,pg_b` returns side-by-side comparison of 2–10 keys — credits, usage, velocity, rate limits, status, metadata (namespace/group/tags) — with not-found key reporting
|
|
96
|
+
- **Key Health Score** — `GET /keys/health?key=...` returns composite health score (0–100) with weighted component breakdown: balance health (30%), quota utilization (25%), rate limit pressure (20%), error rate (25%) — status levels (healthy/good/caution/warning/critical), key issue detection (revoked/suspended/expired/expiring/zero credits), alias support
|
|
96
97
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
97
98
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
98
99
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -1837,6 +1838,34 @@ curl "http://localhost:3402/keys/compare?keys=pg_abc,pg_xyz" -H "X-Admin-Key: YO
|
|
|
1837
1838
|
|
|
1838
1839
|
Keys not found are reported in a `notFound` array. Supports aliases. Maximum 10 keys per comparison.
|
|
1839
1840
|
|
|
1841
|
+
### Key Health Score
|
|
1842
|
+
|
|
1843
|
+
`GET /keys/health?key=...` returns a composite health score (0–100) with weighted component breakdown:
|
|
1844
|
+
|
|
1845
|
+
```bash
|
|
1846
|
+
curl "http://localhost:3402/keys/health?key=pg_abc" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
1847
|
+
```
|
|
1848
|
+
|
|
1849
|
+
**Response:**
|
|
1850
|
+
|
|
1851
|
+
```json
|
|
1852
|
+
{
|
|
1853
|
+
"key": "pg_abc123...",
|
|
1854
|
+
"name": "prod-agent",
|
|
1855
|
+
"score": 72,
|
|
1856
|
+
"status": "caution",
|
|
1857
|
+
"issues": ["Key expires within 7 days", "Credits depleting rapidly"],
|
|
1858
|
+
"components": {
|
|
1859
|
+
"balance": { "score": 40, "risk": "warning", "weight": 0.30 },
|
|
1860
|
+
"quota": { "score": 85, "risk": "good", "weight": 0.25 },
|
|
1861
|
+
"rateLimit": { "score": 100, "risk": "healthy", "weight": 0.20 },
|
|
1862
|
+
"errorRate": { "score": 75, "risk": "good", "weight": 0.25 }
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
```
|
|
1866
|
+
|
|
1867
|
+
Components: **balance** (30%, hours until credit depletion), **quota** (25%, max utilization across daily/monthly limits), **rateLimit** (20%, current window usage), **errorRate** (25%, denied/total ratio). Status: healthy (≥90), good (≥75), caution (≥50), warning (≥25), critical (<25). Issues detect: revoked, suspended, expired, expiring soon, zero credits, rapid depletion. Supports aliases.
|
|
1868
|
+
|
|
1840
1869
|
### IP Allowlisting
|
|
1841
1870
|
|
|
1842
1871
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
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
|
@@ -542,6 +542,8 @@ class PayGateServer {
|
|
|
542
542
|
return this.handleSpendingVelocity(req, res);
|
|
543
543
|
case '/keys/compare':
|
|
544
544
|
return this.handleKeyComparison(req, res);
|
|
545
|
+
case '/keys/health':
|
|
546
|
+
return this.handleKeyHealth(req, res);
|
|
545
547
|
case '/keys/templates':
|
|
546
548
|
if (req.method === 'GET')
|
|
547
549
|
return this.handleListTemplates(req, res);
|
|
@@ -1090,6 +1092,7 @@ class PayGateServer {
|
|
|
1090
1092
|
creditHistory: 'GET /keys/credit-history?key=... — Per-key credit mutation history (requires X-Admin-Key)',
|
|
1091
1093
|
spendingVelocity: 'GET /keys/spending-velocity?key=... — Spending rate and depletion forecast (requires X-Admin-Key)',
|
|
1092
1094
|
keyComparison: 'GET /keys/compare?keys=pg_a,pg_b — Side-by-side key comparison (requires X-Admin-Key)',
|
|
1095
|
+
keyHealth: 'GET /keys/health?key=... — Composite health score (0-100) with component breakdown (requires X-Admin-Key)',
|
|
1093
1096
|
keyTemplates: 'GET /keys/templates — List key templates + POST to create/update (requires X-Admin-Key)',
|
|
1094
1097
|
deleteTemplate: 'POST /keys/templates/delete — Delete a key template (requires X-Admin-Key)',
|
|
1095
1098
|
pricing: 'GET /pricing — Tool pricing breakdown (public)',
|
|
@@ -2922,6 +2925,173 @@ class PayGateServer {
|
|
|
2922
2925
|
keys: comparisons,
|
|
2923
2926
|
}));
|
|
2924
2927
|
}
|
|
2928
|
+
// ─── /keys/health — Composite health score ──────────────────────────────────
|
|
2929
|
+
handleKeyHealth(req, res) {
|
|
2930
|
+
if (req.method !== 'GET') {
|
|
2931
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
2932
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
2933
|
+
return;
|
|
2934
|
+
}
|
|
2935
|
+
if (!this.checkAdmin(req, res))
|
|
2936
|
+
return;
|
|
2937
|
+
const urlParts = req.url?.split('?') || [];
|
|
2938
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
2939
|
+
const key = params.get('key');
|
|
2940
|
+
if (!key) {
|
|
2941
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
2942
|
+
res.end(JSON.stringify({ error: 'Missing required query parameter: key' }));
|
|
2943
|
+
return;
|
|
2944
|
+
}
|
|
2945
|
+
// Use resolveKeyRaw to include expired/suspended/revoked keys in health check
|
|
2946
|
+
const record = this.gate.store.resolveKeyRaw(key);
|
|
2947
|
+
if (!record) {
|
|
2948
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
2949
|
+
res.end(JSON.stringify({ error: 'Key not found' }));
|
|
2950
|
+
return;
|
|
2951
|
+
}
|
|
2952
|
+
const actualKey = record.key;
|
|
2953
|
+
// ── Component 1: Balance health (30% weight) ──
|
|
2954
|
+
// Based on spending velocity → how many hours of usage remaining
|
|
2955
|
+
const velocity = this.creditLedger.getSpendingVelocity(actualKey, record.credits, 24);
|
|
2956
|
+
let balanceScore = 100;
|
|
2957
|
+
let balanceRisk = 'healthy';
|
|
2958
|
+
if (velocity.creditsPerHour > 0) {
|
|
2959
|
+
const hoursLeft = velocity.estimatedHoursRemaining ?? Infinity;
|
|
2960
|
+
if (hoursLeft <= 1) {
|
|
2961
|
+
balanceScore = 0;
|
|
2962
|
+
balanceRisk = 'critical';
|
|
2963
|
+
}
|
|
2964
|
+
else if (hoursLeft <= 6) {
|
|
2965
|
+
balanceScore = 20;
|
|
2966
|
+
balanceRisk = 'critical';
|
|
2967
|
+
}
|
|
2968
|
+
else if (hoursLeft <= 24) {
|
|
2969
|
+
balanceScore = 40;
|
|
2970
|
+
balanceRisk = 'warning';
|
|
2971
|
+
}
|
|
2972
|
+
else if (hoursLeft <= 72) {
|
|
2973
|
+
balanceScore = 60;
|
|
2974
|
+
balanceRisk = 'caution';
|
|
2975
|
+
}
|
|
2976
|
+
else if (hoursLeft <= 168) {
|
|
2977
|
+
balanceScore = 80;
|
|
2978
|
+
balanceRisk = 'good';
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
else if (record.credits <= 0) {
|
|
2982
|
+
balanceScore = 0;
|
|
2983
|
+
balanceRisk = 'critical';
|
|
2984
|
+
}
|
|
2985
|
+
// ── Component 2: Quota utilization (25% weight) ──
|
|
2986
|
+
let quotaScore = 100;
|
|
2987
|
+
let quotaRisk = 'healthy';
|
|
2988
|
+
const quotaConfig = record.quota || this.config.globalQuota;
|
|
2989
|
+
if (quotaConfig) {
|
|
2990
|
+
// Reset counters if day/month rolled over before reading
|
|
2991
|
+
this.gate.quotaTracker.resetIfNeeded(record);
|
|
2992
|
+
let maxUtilization = 0;
|
|
2993
|
+
if (quotaConfig.dailyCallLimit && quotaConfig.dailyCallLimit > 0) {
|
|
2994
|
+
maxUtilization = Math.max(maxUtilization, record.quotaDailyCalls / quotaConfig.dailyCallLimit);
|
|
2995
|
+
}
|
|
2996
|
+
if (quotaConfig.monthlyCallLimit && quotaConfig.monthlyCallLimit > 0) {
|
|
2997
|
+
maxUtilization = Math.max(maxUtilization, record.quotaMonthlyCalls / quotaConfig.monthlyCallLimit);
|
|
2998
|
+
}
|
|
2999
|
+
if (quotaConfig.dailyCreditLimit && quotaConfig.dailyCreditLimit > 0) {
|
|
3000
|
+
maxUtilization = Math.max(maxUtilization, record.quotaDailyCredits / quotaConfig.dailyCreditLimit);
|
|
3001
|
+
}
|
|
3002
|
+
if (quotaConfig.monthlyCreditLimit && quotaConfig.monthlyCreditLimit > 0) {
|
|
3003
|
+
maxUtilization = Math.max(maxUtilization, record.quotaMonthlyCredits / quotaConfig.monthlyCreditLimit);
|
|
3004
|
+
}
|
|
3005
|
+
quotaScore = Math.max(0, Math.round((1 - maxUtilization) * 100));
|
|
3006
|
+
if (maxUtilization >= 1)
|
|
3007
|
+
quotaRisk = 'critical';
|
|
3008
|
+
else if (maxUtilization >= 0.9)
|
|
3009
|
+
quotaRisk = 'warning';
|
|
3010
|
+
else if (maxUtilization >= 0.75)
|
|
3011
|
+
quotaRisk = 'caution';
|
|
3012
|
+
else if (maxUtilization >= 0.5)
|
|
3013
|
+
quotaRisk = 'good';
|
|
3014
|
+
}
|
|
3015
|
+
// ── Component 3: Rate limit pressure (20% weight) ──
|
|
3016
|
+
const rateStatus = this.gate.rateLimiter.getStatus(actualKey);
|
|
3017
|
+
let rateLimitScore = 100;
|
|
3018
|
+
let rateLimitRisk = 'healthy';
|
|
3019
|
+
if (rateStatus.limit > 0) {
|
|
3020
|
+
const utilization = rateStatus.used / rateStatus.limit;
|
|
3021
|
+
rateLimitScore = Math.max(0, Math.round((1 - utilization) * 100));
|
|
3022
|
+
if (utilization >= 1)
|
|
3023
|
+
rateLimitRisk = 'critical';
|
|
3024
|
+
else if (utilization >= 0.9)
|
|
3025
|
+
rateLimitRisk = 'warning';
|
|
3026
|
+
else if (utilization >= 0.75)
|
|
3027
|
+
rateLimitRisk = 'caution';
|
|
3028
|
+
else if (utilization >= 0.5)
|
|
3029
|
+
rateLimitRisk = 'good';
|
|
3030
|
+
}
|
|
3031
|
+
// ── Component 4: Error rate (25% weight) ──
|
|
3032
|
+
const keyUsage = this.gate.meter.getKeyUsage(actualKey);
|
|
3033
|
+
let errorScore = 100;
|
|
3034
|
+
let errorRisk = 'healthy';
|
|
3035
|
+
if (keyUsage.totalCalls > 0) {
|
|
3036
|
+
const errorRate = keyUsage.totalDenied / keyUsage.totalCalls;
|
|
3037
|
+
errorScore = Math.max(0, Math.round((1 - errorRate) * 100));
|
|
3038
|
+
if (errorRate >= 0.5)
|
|
3039
|
+
errorRisk = 'critical';
|
|
3040
|
+
else if (errorRate >= 0.25)
|
|
3041
|
+
errorRisk = 'warning';
|
|
3042
|
+
else if (errorRate >= 0.1)
|
|
3043
|
+
errorRisk = 'caution';
|
|
3044
|
+
else if (errorRate > 0)
|
|
3045
|
+
errorRisk = 'good';
|
|
3046
|
+
}
|
|
3047
|
+
// ── Composite score (weighted) ──
|
|
3048
|
+
const overallScore = Math.round(balanceScore * 0.30 +
|
|
3049
|
+
quotaScore * 0.25 +
|
|
3050
|
+
rateLimitScore * 0.20 +
|
|
3051
|
+
errorScore * 0.25);
|
|
3052
|
+
let overallStatus = 'healthy';
|
|
3053
|
+
if (overallScore < 25)
|
|
3054
|
+
overallStatus = 'critical';
|
|
3055
|
+
else if (overallScore < 50)
|
|
3056
|
+
overallStatus = 'warning';
|
|
3057
|
+
else if (overallScore < 75)
|
|
3058
|
+
overallStatus = 'caution';
|
|
3059
|
+
else if (overallScore < 90)
|
|
3060
|
+
overallStatus = 'good';
|
|
3061
|
+
// Check key-level issues
|
|
3062
|
+
const issues = [];
|
|
3063
|
+
if (!record.active)
|
|
3064
|
+
issues.push('Key is revoked');
|
|
3065
|
+
if (record.suspended)
|
|
3066
|
+
issues.push('Key is suspended');
|
|
3067
|
+
if (record.expiresAt && new Date(record.expiresAt).getTime() < Date.now())
|
|
3068
|
+
issues.push('Key has expired');
|
|
3069
|
+
if (record.expiresAt) {
|
|
3070
|
+
const hoursToExpiry = (new Date(record.expiresAt).getTime() - Date.now()) / 3_600_000;
|
|
3071
|
+
if (hoursToExpiry > 0 && hoursToExpiry <= 24)
|
|
3072
|
+
issues.push('Key expires within 24 hours');
|
|
3073
|
+
else if (hoursToExpiry > 0 && hoursToExpiry <= 168)
|
|
3074
|
+
issues.push('Key expires within 7 days');
|
|
3075
|
+
}
|
|
3076
|
+
if (record.credits <= 0)
|
|
3077
|
+
issues.push('Zero credits remaining');
|
|
3078
|
+
if (balanceRisk === 'critical')
|
|
3079
|
+
issues.push('Credits depleting rapidly');
|
|
3080
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3081
|
+
res.end(JSON.stringify({
|
|
3082
|
+
key: actualKey.slice(0, 10) + '...',
|
|
3083
|
+
name: record.name,
|
|
3084
|
+
score: overallScore,
|
|
3085
|
+
status: overallStatus,
|
|
3086
|
+
issues: issues.length > 0 ? issues : undefined,
|
|
3087
|
+
components: {
|
|
3088
|
+
balance: { score: balanceScore, risk: balanceRisk, weight: 0.30 },
|
|
3089
|
+
quota: { score: quotaScore, risk: quotaRisk, weight: 0.25 },
|
|
3090
|
+
rateLimit: { score: rateLimitScore, risk: rateLimitRisk, weight: 0.20 },
|
|
3091
|
+
errorRate: { score: errorScore, risk: errorRisk, weight: 0.25 },
|
|
3092
|
+
},
|
|
3093
|
+
}));
|
|
3094
|
+
}
|
|
2925
3095
|
// ─── /keys/auto-topup — Configure auto-topup ────────────────────────────────
|
|
2926
3096
|
async handleSetAutoTopup(req, res) {
|
|
2927
3097
|
if (req.method !== 'POST') {
|