paygate-mcp 7.3.0 → 7.5.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 CHANGED
@@ -100,6 +100,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
100
100
  - **Scheduled Actions** — `POST /keys/schedule` creates future-dated actions (revoke/suspend/topup) on API keys, `GET /keys/schedule` lists pending schedules with optional `?key=` filter, `DELETE /keys/schedule?id=...` cancels a schedule — max 20 per key, alias support, background execution timer, audit trail
101
101
  - **Key Activity Timeline** — `GET /keys/activity?key=...` returns a unified chronological feed of audit events and usage events for a specific key — newest first, optional `?since=` and `?limit=` filters, alias support
102
102
  - **Credit Reservations** — `POST /keys/reserve` holds credits, `POST /keys/reserve/commit` deducts held credits, `POST /keys/reserve/release` frees the hold, `GET /keys/reserve` lists active reservations — prevents overcommit, configurable TTL (10s–1h), max 50 per key, auto-expiry, audit trail
103
+ - **Request Log** — `GET /requests` queryable log of every tool call with timing, credits charged, status (allowed/denied), deny reason, key, and request ID — filter by key/tool/status/since, pagination, summary statistics (totals + avg duration), 5000-entry ring buffer
104
+ - **Tool Stats** — `GET /tools/stats` per-tool analytics: call counts, success rate, avg/p95 latency, credits consumed, deny reason breakdown, top 10 consumers — optional `?tool=` for detailed single-tool view, `?since=` filter
103
105
  - **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
104
106
  - **Webhook Events** — POST batched usage events to any URL for external billing/alerting
105
107
  - **Config File Mode** — Load all settings from a JSON file (`--config`)
@@ -2075,6 +2077,134 @@ curl "http://localhost:3402/keys/reserve" -H "X-Admin-Key: YOUR_ADMIN_KEY"
2075
2077
 
2076
2078
  TTL range: 10s to 1h (default 5 min). Max 50 reservations per key. Expired reservations auto-cleanup. Alias support. Rejects revoked/suspended keys. Audit trail (`credits.reserved` / `credits.committed` / `credits.released`).
2077
2079
 
2080
+ ### Request Log
2081
+
2082
+ Queryable log of every MCP tool call with timing, credits, status, and deny reason:
2083
+
2084
+ ```bash
2085
+ # Get all requests (newest first)
2086
+ curl "http://localhost:3402/requests" -H "X-Admin-Key: YOUR_ADMIN_KEY"
2087
+
2088
+ # Filter by tool name
2089
+ curl "http://localhost:3402/requests?tool=my_tool" -H "X-Admin-Key: YOUR_ADMIN_KEY"
2090
+
2091
+ # Filter by status (allowed or denied)
2092
+ curl "http://localhost:3402/requests?status=denied" -H "X-Admin-Key: YOUR_ADMIN_KEY"
2093
+
2094
+ # Filter by key (partial match on masked key)
2095
+ curl "http://localhost:3402/requests?key=pg_abc1" -H "X-Admin-Key: YOUR_ADMIN_KEY"
2096
+
2097
+ # Filter by time + pagination
2098
+ curl "http://localhost:3402/requests?since=2025-03-01T00:00:00Z&limit=50&offset=0" \
2099
+ -H "X-Admin-Key: YOUR_ADMIN_KEY"
2100
+
2101
+ # Combine filters
2102
+ curl "http://localhost:3402/requests?tool=my_tool&status=allowed&limit=10" \
2103
+ -H "X-Admin-Key: YOUR_ADMIN_KEY"
2104
+ ```
2105
+
2106
+ **Response:**
2107
+
2108
+ ```json
2109
+ {
2110
+ "total": 42,
2111
+ "offset": 0,
2112
+ "limit": 100,
2113
+ "summary": {
2114
+ "totalAllowed": 38,
2115
+ "totalDenied": 4,
2116
+ "totalCredits": 190,
2117
+ "avgDurationMs": 45
2118
+ },
2119
+ "requests": [
2120
+ {
2121
+ "id": 42,
2122
+ "timestamp": "2025-03-16T10:30:00Z",
2123
+ "tool": "my_tool",
2124
+ "key": "pg_abc1...2345",
2125
+ "status": "allowed",
2126
+ "credits": 5,
2127
+ "durationMs": 32,
2128
+ "requestId": "req_a1b2c3d4e5f6g7h8"
2129
+ },
2130
+ {
2131
+ "id": 41,
2132
+ "timestamp": "2025-03-16T10:29:55Z",
2133
+ "tool": "my_tool",
2134
+ "key": "pg_xyz9...8765",
2135
+ "status": "denied",
2136
+ "credits": 0,
2137
+ "durationMs": 1,
2138
+ "denyReason": "insufficient_credits",
2139
+ "requestId": "req_i9j0k1l2m3n4o5p6"
2140
+ }
2141
+ ]
2142
+ }
2143
+ ```
2144
+
2145
+ 5000-entry ring buffer. Summary statistics are computed on filtered results. Deny reasons: `insufficient_credits`, `rate_limited`, `invalid_api_key`, `key_suspended`, `api_key_expired`, `tool_not_allowed`, `quota_exceeded`.
2146
+
2147
+ ### Tool Stats
2148
+
2149
+ Per-tool analytics derived from the request log:
2150
+
2151
+ ```bash
2152
+ # Overview of all tools
2153
+ curl "http://localhost:3402/tools/stats" -H "X-Admin-Key: YOUR_ADMIN_KEY"
2154
+
2155
+ # Detailed stats for a specific tool
2156
+ curl "http://localhost:3402/tools/stats?tool=my_tool" -H "X-Admin-Key: YOUR_ADMIN_KEY"
2157
+
2158
+ # Filter by time range
2159
+ curl "http://localhost:3402/tools/stats?since=2025-03-01T00:00:00Z" \
2160
+ -H "X-Admin-Key: YOUR_ADMIN_KEY"
2161
+ ```
2162
+
2163
+ **Response (overview):**
2164
+
2165
+ ```json
2166
+ {
2167
+ "totalTools": 3,
2168
+ "totalCalls": 150,
2169
+ "tools": [
2170
+ {
2171
+ "tool": "my_tool",
2172
+ "totalCalls": 100,
2173
+ "allowed": 95,
2174
+ "denied": 5,
2175
+ "successRate": 95,
2176
+ "totalCredits": 475,
2177
+ "avgDurationMs": 42
2178
+ }
2179
+ ]
2180
+ }
2181
+ ```
2182
+
2183
+ **Response (detailed `?tool=my_tool`):**
2184
+
2185
+ ```json
2186
+ {
2187
+ "tool": "my_tool",
2188
+ "totalCalls": 100,
2189
+ "allowed": 95,
2190
+ "denied": 5,
2191
+ "successRate": 95,
2192
+ "totalCredits": 475,
2193
+ "avgDurationMs": 42,
2194
+ "p95DurationMs": 120,
2195
+ "denyReasons": {
2196
+ "insufficient_credits": 3,
2197
+ "rate_limited": 2
2198
+ },
2199
+ "topConsumers": [
2200
+ { "key": "pg_abc1...2345", "calls": 50, "credits": 250 },
2201
+ { "key": "pg_xyz9...8765", "calls": 30, "credits": 150 }
2202
+ ]
2203
+ }
2204
+ ```
2205
+
2206
+ Top consumers limited to 10. Tools sorted by call count in overview. Data sourced from request log (5000-entry ring buffer).
2207
+
2078
2208
  ### IP Allowlisting
2079
2209
 
2080
2210
  Restrict API keys to specific IP addresses or CIDR ranges:
package/dist/server.d.ts CHANGED
@@ -117,6 +117,12 @@ export declare class PayGateServer {
117
117
  private creditReservations;
118
118
  /** Auto-incrementing reservation ID counter */
119
119
  private nextReservationId;
120
+ /** Request log — ring buffer of tool call entries */
121
+ private requestLog;
122
+ /** Next request log entry ID */
123
+ private nextRequestLogId;
124
+ /** Max request log entries (ring buffer) */
125
+ private readonly maxRequestLogEntries;
120
126
  /** Number of in-flight /mcp requests */
121
127
  private inflight;
122
128
  /** Config file path for hot reload (null if not using config file) */
@@ -308,6 +314,10 @@ export declare class PayGateServer {
308
314
  * @returns Resolves when fully stopped.
309
315
  */
310
316
  gracefulStop(timeoutMs?: number): Promise<void>;
317
+ private handleRequestLog;
318
+ private handleToolStats;
319
+ /** Calculate percentile from an array of numbers. */
320
+ private percentile;
311
321
  }
312
322
  export {};
313
323
  //# sourceMappingURL=server.d.ts.map
@@ -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
@@ -235,6 +235,12 @@ class PayGateServer {
235
235
  creditReservations = new Map();
236
236
  /** Auto-incrementing reservation ID counter */
237
237
  nextReservationId = 1;
238
+ /** Request log — ring buffer of tool call entries */
239
+ requestLog = [];
240
+ /** Next request log entry ID */
241
+ nextRequestLogId = 1;
242
+ /** Max request log entries (ring buffer) */
243
+ maxRequestLogEntries = 5000;
238
244
  /** Number of in-flight /mcp requests */
239
245
  inflight = 0;
240
246
  /** Config file path for hot reload (null if not using config file) */
@@ -666,6 +672,12 @@ class PayGateServer {
666
672
  return this.handleAuditExport(req, res);
667
673
  case '/audit/stats':
668
674
  return this.handleAuditStats(req, res);
675
+ case '/requests':
676
+ if (req.method === 'GET')
677
+ return this.handleRequestLog(req, res);
678
+ res.writeHead(405, { 'Content-Type': 'application/json' });
679
+ res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
680
+ return;
669
681
  // ─── Registry / Discovery endpoints ──────────────────────────────
670
682
  case '/.well-known/mcp-payment':
671
683
  return this.handlePaymentMetadata(req, res);
@@ -675,6 +687,12 @@ class PayGateServer {
675
687
  return this.handleMetrics(req, res);
676
688
  case '/analytics':
677
689
  return this.handleAnalytics(req, res);
690
+ case '/tools/stats':
691
+ if (req.method === 'GET')
692
+ return this.handleToolStats(req, res);
693
+ res.writeHead(405, { 'Content-Type': 'application/json' });
694
+ res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
695
+ return;
678
696
  case '/alerts':
679
697
  if (req.method === 'GET')
680
698
  return this.handleGetAlerts(req, res);
@@ -915,6 +933,7 @@ class PayGateServer {
915
933
  const pluginCtx = { apiKey, toolName, toolArgs, request };
916
934
  pluginRequest = await this.plugins.executeBeforeToolCall(pluginCtx);
917
935
  }
936
+ const toolCallStartTime = Date.now();
918
937
  let response = await this.handler.handleRequest(pluginRequest, apiKey, clientIp, scopedTokenTools);
919
938
  // Plugin: afterToolCall — let plugins modify the response
920
939
  if (this.plugins.count > 0 && request.method === 'tools/call') {
@@ -982,6 +1001,49 @@ class PayGateServer {
982
1001
  }
983
1002
  }
984
1003
  }
1004
+ // Record request log entry for tools/call
1005
+ if (request.method === 'tools/call') {
1006
+ const toolName = request.params?.name || 'unknown';
1007
+ const durationMs = Date.now() - toolCallStartTime;
1008
+ const isError = !!response.error;
1009
+ let denyReason;
1010
+ if (isError) {
1011
+ const msg = response.error.message || '';
1012
+ if (msg.includes('rate_limited'))
1013
+ denyReason = 'rate_limited';
1014
+ else if (msg.includes('insufficient_credits'))
1015
+ denyReason = 'insufficient_credits';
1016
+ else if (msg.includes('invalid_api_key'))
1017
+ denyReason = 'invalid_api_key';
1018
+ else if (msg.includes('key_suspended'))
1019
+ denyReason = 'key_suspended';
1020
+ else if (msg.includes('api_key_expired'))
1021
+ denyReason = 'api_key_expired';
1022
+ else if (msg.includes('tool_not_allowed'))
1023
+ denyReason = 'tool_not_allowed';
1024
+ else if (msg.includes('quota_exceeded'))
1025
+ denyReason = 'quota_exceeded';
1026
+ else
1027
+ denyReason = msg || 'denied';
1028
+ }
1029
+ const creditsCharged = isError ? 0 : this.gate.getToolPrice(toolName, request.params?.arguments);
1030
+ const logEntry = {
1031
+ id: this.nextRequestLogId++,
1032
+ timestamp: new Date().toISOString(),
1033
+ tool: toolName,
1034
+ key: (0, audit_1.maskKeyForAudit)(apiKey || 'anonymous'),
1035
+ status: (isError ? 'denied' : 'allowed'),
1036
+ credits: creditsCharged,
1037
+ durationMs,
1038
+ ...(denyReason ? { denyReason } : {}),
1039
+ requestId,
1040
+ };
1041
+ this.requestLog.push(logEntry);
1042
+ // Enforce ring buffer size
1043
+ if (this.requestLog.length > this.maxRequestLogEntries) {
1044
+ this.requestLog = this.requestLog.slice(-this.maxRequestLogEntries);
1045
+ }
1046
+ }
985
1047
  // Build rate limit + credits headers for tools/call responses
986
1048
  const rateLimitHeaders = this.buildRateLimitHeaders(apiKey, request);
987
1049
  // Check if client accepts SSE
@@ -1239,6 +1301,8 @@ class PayGateServer {
1239
1301
  keySchedule: 'GET /keys/schedule?key=... — List schedules + POST to create + DELETE to cancel (requires X-Admin-Key)',
1240
1302
  keyActivity: 'GET /keys/activity?key=... — Unified activity timeline for a key (requires X-Admin-Key)',
1241
1303
  creditReservations: 'POST /keys/reserve to hold credits, POST /keys/reserve/commit to deduct, POST /keys/reserve/release to release, GET /keys/reserve to list (requires X-Admin-Key)',
1304
+ requestLog: 'GET /requests — Queryable log of tool call requests with timing, credits, status (requires X-Admin-Key)',
1305
+ toolStats: 'GET /tools/stats — Per-tool call counts, success rates, latency, credits, and top consumers (requires X-Admin-Key)',
1242
1306
  ...(this.oauth ? {
1243
1307
  oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
1244
1308
  oauthRegister: 'POST /oauth/register — Register OAuth client',
@@ -6224,6 +6288,168 @@ class PayGateServer {
6224
6288
  await this.redisSync.destroy();
6225
6289
  }
6226
6290
  }
6291
+ // ─── /requests — Request Log (queryable tool call log) ──────────────────────
6292
+ handleRequestLog(req, res) {
6293
+ if (!this.checkAdmin(req, res))
6294
+ return;
6295
+ const urlParts = req.url?.split('?') || [];
6296
+ const params = new URLSearchParams(urlParts[1] || '');
6297
+ // Filters
6298
+ const keyFilter = params.get('key');
6299
+ const toolFilter = params.get('tool');
6300
+ const statusFilter = params.get('status'); // 'allowed' | 'denied'
6301
+ const sinceFilter = params.get('since');
6302
+ const limit = Math.min(1000, Math.max(1, parseInt(params.get('limit') || '100', 10) || 100));
6303
+ const offset = Math.max(0, parseInt(params.get('offset') || '0', 10) || 0);
6304
+ let filtered = this.requestLog;
6305
+ // Filter by key (partial match on masked key)
6306
+ if (keyFilter) {
6307
+ const kf = keyFilter.toLowerCase();
6308
+ filtered = filtered.filter(e => e.key.toLowerCase().includes(kf));
6309
+ }
6310
+ // Filter by tool name (exact match)
6311
+ if (toolFilter) {
6312
+ filtered = filtered.filter(e => e.tool === toolFilter);
6313
+ }
6314
+ // Filter by status
6315
+ if (statusFilter === 'allowed' || statusFilter === 'denied') {
6316
+ filtered = filtered.filter(e => e.status === statusFilter);
6317
+ }
6318
+ // Filter by since timestamp
6319
+ if (sinceFilter) {
6320
+ const sinceTime = new Date(sinceFilter).getTime();
6321
+ if (!isNaN(sinceTime)) {
6322
+ filtered = filtered.filter(e => new Date(e.timestamp).getTime() >= sinceTime);
6323
+ }
6324
+ }
6325
+ const total = filtered.length;
6326
+ // Return newest first
6327
+ const reversed = [...filtered].reverse();
6328
+ const page = reversed.slice(offset, offset + limit);
6329
+ // Compute summary stats
6330
+ const totalAllowed = filtered.filter(e => e.status === 'allowed').length;
6331
+ const totalDenied = filtered.filter(e => e.status === 'denied').length;
6332
+ const totalCredits = filtered.reduce((sum, e) => sum + e.credits, 0);
6333
+ const avgDurationMs = filtered.length > 0
6334
+ ? Math.round(filtered.reduce((sum, e) => sum + e.durationMs, 0) / filtered.length)
6335
+ : 0;
6336
+ res.writeHead(200, { 'Content-Type': 'application/json' });
6337
+ res.end(JSON.stringify({
6338
+ total,
6339
+ offset,
6340
+ limit,
6341
+ summary: {
6342
+ totalAllowed,
6343
+ totalDenied,
6344
+ totalCredits,
6345
+ avgDurationMs,
6346
+ },
6347
+ requests: page,
6348
+ }));
6349
+ }
6350
+ // ─── /tools/stats — Per-tool analytics from request log ────────────────────
6351
+ handleToolStats(req, res) {
6352
+ if (!this.checkAdmin(req, res))
6353
+ return;
6354
+ const urlParts = req.url?.split('?') || [];
6355
+ const params = new URLSearchParams(urlParts[1] || '');
6356
+ const sinceFilter = params.get('since');
6357
+ const toolFilter = params.get('tool');
6358
+ let entries = this.requestLog;
6359
+ // Filter by since
6360
+ if (sinceFilter) {
6361
+ const sinceTime = new Date(sinceFilter).getTime();
6362
+ if (!isNaN(sinceTime)) {
6363
+ entries = entries.filter(e => new Date(e.timestamp).getTime() >= sinceTime);
6364
+ }
6365
+ }
6366
+ // If specific tool requested, return detailed stats for just that tool
6367
+ if (toolFilter) {
6368
+ const toolEntries = entries.filter(e => e.tool === toolFilter);
6369
+ const allowed = toolEntries.filter(e => e.status === 'allowed');
6370
+ const denied = toolEntries.filter(e => e.status === 'denied');
6371
+ const totalCredits = toolEntries.reduce((sum, e) => sum + e.credits, 0);
6372
+ const avgDurationMs = toolEntries.length > 0
6373
+ ? Math.round(toolEntries.reduce((sum, e) => sum + e.durationMs, 0) / toolEntries.length)
6374
+ : 0;
6375
+ const p95 = this.percentile(toolEntries.map(e => e.durationMs), 95);
6376
+ // Deny reason breakdown
6377
+ const denyReasons = {};
6378
+ for (const e of denied) {
6379
+ const reason = e.denyReason || 'unknown';
6380
+ denyReasons[reason] = (denyReasons[reason] || 0) + 1;
6381
+ }
6382
+ // Top consumers by call count
6383
+ const consumerCalls = {};
6384
+ const consumerCredits = {};
6385
+ for (const e of toolEntries) {
6386
+ consumerCalls[e.key] = (consumerCalls[e.key] || 0) + 1;
6387
+ consumerCredits[e.key] = (consumerCredits[e.key] || 0) + e.credits;
6388
+ }
6389
+ const topConsumers = Object.entries(consumerCalls)
6390
+ .sort((a, b) => b[1] - a[1])
6391
+ .slice(0, 10)
6392
+ .map(([key, calls]) => ({ key, calls, credits: consumerCredits[key] || 0 }));
6393
+ res.writeHead(200, { 'Content-Type': 'application/json' });
6394
+ res.end(JSON.stringify({
6395
+ tool: toolFilter,
6396
+ totalCalls: toolEntries.length,
6397
+ allowed: allowed.length,
6398
+ denied: denied.length,
6399
+ successRate: toolEntries.length > 0
6400
+ ? Math.round((allowed.length / toolEntries.length) * 10000) / 100
6401
+ : 0,
6402
+ totalCredits,
6403
+ avgDurationMs,
6404
+ p95DurationMs: p95,
6405
+ denyReasons,
6406
+ topConsumers,
6407
+ }));
6408
+ return;
6409
+ }
6410
+ // Aggregate stats per tool
6411
+ const toolMap = {};
6412
+ for (const e of entries) {
6413
+ if (!toolMap[e.tool]) {
6414
+ toolMap[e.tool] = { calls: 0, allowed: 0, denied: 0, credits: 0, totalDurationMs: 0 };
6415
+ }
6416
+ const t = toolMap[e.tool];
6417
+ t.calls++;
6418
+ if (e.status === 'allowed')
6419
+ t.allowed++;
6420
+ else
6421
+ t.denied++;
6422
+ t.credits += e.credits;
6423
+ t.totalDurationMs += e.durationMs;
6424
+ }
6425
+ const tools = Object.entries(toolMap)
6426
+ .map(([tool, stats]) => ({
6427
+ tool,
6428
+ totalCalls: stats.calls,
6429
+ allowed: stats.allowed,
6430
+ denied: stats.denied,
6431
+ successRate: stats.calls > 0
6432
+ ? Math.round((stats.allowed / stats.calls) * 10000) / 100
6433
+ : 0,
6434
+ totalCredits: stats.credits,
6435
+ avgDurationMs: stats.calls > 0 ? Math.round(stats.totalDurationMs / stats.calls) : 0,
6436
+ }))
6437
+ .sort((a, b) => b.totalCalls - a.totalCalls);
6438
+ res.writeHead(200, { 'Content-Type': 'application/json' });
6439
+ res.end(JSON.stringify({
6440
+ totalTools: tools.length,
6441
+ totalCalls: entries.length,
6442
+ tools,
6443
+ }));
6444
+ }
6445
+ /** Calculate percentile from an array of numbers. */
6446
+ percentile(values, p) {
6447
+ if (values.length === 0)
6448
+ return 0;
6449
+ const sorted = [...values].sort((a, b) => a - b);
6450
+ const idx = Math.ceil((p / 100) * sorted.length) - 1;
6451
+ return sorted[Math.max(0, idx)];
6452
+ }
6227
6453
  }
6228
6454
  exports.PayGateServer = PayGateServer;
6229
6455
  //# sourceMappingURL=server.js.map