paygate-mcp 10.4.0 → 10.6.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/dist/billable-metrics.d.ts +130 -0
- package/dist/billable-metrics.d.ts.map +1 -0
- package/dist/billable-metrics.js +387 -0
- package/dist/billable-metrics.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +117 -0
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/openapi-backend.d.ts +68 -0
- package/dist/openapi-backend.d.ts.map +1 -0
- package/dist/openapi-backend.js +179 -0
- package/dist/openapi-backend.js.map +1 -0
- package/dist/openapi-to-mcp.d.ts +113 -0
- package/dist/openapi-to-mcp.d.ts.map +1 -0
- package/dist/openapi-to-mcp.js +318 -0
- package/dist/openapi-to-mcp.js.map +1 -0
- package/dist/otel-emitter.d.ts +159 -0
- package/dist/otel-emitter.d.ts.map +1 -0
- package/dist/otel-emitter.js +349 -0
- package/dist/otel-emitter.js.map +1 -0
- package/dist/pii-masking.d.ts +150 -0
- package/dist/pii-masking.d.ts.map +1 -0
- package/dist/pii-masking.js +303 -0
- package/dist/pii-masking.js.map +1 -0
- package/dist/server.d.ts +6 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +111 -1
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/virtual-server.d.ts +140 -0
- package/dist/virtual-server.d.ts.map +1 -0
- package/dist/virtual-server.js +339 -0
- package/dist/virtual-server.js.map +1 -0
- package/dist/x402.d.ts +100 -0
- package/dist/x402.d.ts.map +1 -0
- package/dist/x402.js +248 -0
- package/dist/x402.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Billable Metric Expressions — Config-driven pricing formulas.
|
|
3
|
+
*
|
|
4
|
+
* Instead of flat per-call pricing, define expressions that compute
|
|
5
|
+
* cost based on request/response attributes:
|
|
6
|
+
*
|
|
7
|
+
* cost = input_tokens * 0.001 + output_tokens * 0.003
|
|
8
|
+
* cost = max(1, file_size_kb * 0.5)
|
|
9
|
+
* cost = base_cost + (duration_ms / 1000) * 2
|
|
10
|
+
*
|
|
11
|
+
* SECURITY: Expressions are parsed with a safe recursive descent parser.
|
|
12
|
+
* NO JavaScript eval(), NO Function constructor. The parser only
|
|
13
|
+
* supports arithmetic (+, -, *, /, %), built-in math functions
|
|
14
|
+
* (min, max, ceil, floor, round, abs, sqrt, log, pow),
|
|
15
|
+
* numeric literals, and named variables.
|
|
16
|
+
*
|
|
17
|
+
* Features:
|
|
18
|
+
* - Safe expression parsing (AST-based, no code execution)
|
|
19
|
+
* - Built-in math functions: min, max, ceil, floor, round, abs
|
|
20
|
+
* - Variable binding from tool args and response fields
|
|
21
|
+
* - Per-tool metric definitions
|
|
22
|
+
* - Fallback to flat pricing if expression errors
|
|
23
|
+
* - Statistics and audit trail
|
|
24
|
+
*
|
|
25
|
+
* Zero external dependencies.
|
|
26
|
+
*/
|
|
27
|
+
export interface BillableMetric {
|
|
28
|
+
/** Unique metric ID. */
|
|
29
|
+
id: string;
|
|
30
|
+
/** Human-readable name. */
|
|
31
|
+
name: string;
|
|
32
|
+
/** Expression string (e.g., 'input_tokens * 0.001 + output_tokens * 0.003'). */
|
|
33
|
+
expression: string;
|
|
34
|
+
/** Tools this metric applies to. Empty = all. */
|
|
35
|
+
tools: string[];
|
|
36
|
+
/** Minimum cost (floor). Default: 0. */
|
|
37
|
+
minCost?: number;
|
|
38
|
+
/** Maximum cost (ceiling). Default: Infinity. */
|
|
39
|
+
maxCost?: number;
|
|
40
|
+
/** Whether this metric is active. Default: true. */
|
|
41
|
+
active: boolean;
|
|
42
|
+
/** Description. */
|
|
43
|
+
description?: string;
|
|
44
|
+
/** Fallback flat cost if expression fails. Default: 1. */
|
|
45
|
+
fallbackCost?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface MetricContext {
|
|
48
|
+
/** Tool name. */
|
|
49
|
+
tool: string;
|
|
50
|
+
/** Input arguments (flattened key-value). */
|
|
51
|
+
inputArgs: Record<string, unknown>;
|
|
52
|
+
/** Response content (if post-call). */
|
|
53
|
+
responseContent?: string;
|
|
54
|
+
/** Response size in bytes. */
|
|
55
|
+
responseSizeBytes?: number;
|
|
56
|
+
/** Call duration in ms. */
|
|
57
|
+
durationMs?: number;
|
|
58
|
+
/** Input size in bytes. */
|
|
59
|
+
inputSizeBytes?: number;
|
|
60
|
+
/** Custom variables injected by caller. */
|
|
61
|
+
customVars?: Record<string, number>;
|
|
62
|
+
}
|
|
63
|
+
export interface MetricResult {
|
|
64
|
+
/** Computed cost in credits. */
|
|
65
|
+
cost: number;
|
|
66
|
+
/** Metric ID used. */
|
|
67
|
+
metricId: string;
|
|
68
|
+
/** Whether fallback was used. */
|
|
69
|
+
usedFallback: boolean;
|
|
70
|
+
/** Error if expression failed. */
|
|
71
|
+
error?: string;
|
|
72
|
+
/** Variables resolved for the expression. */
|
|
73
|
+
resolvedVars: Record<string, number>;
|
|
74
|
+
}
|
|
75
|
+
export interface BillableMetricStats {
|
|
76
|
+
/** Total evaluations. */
|
|
77
|
+
totalEvaluations: number;
|
|
78
|
+
/** Successful expression evaluations. */
|
|
79
|
+
successfulEvals: number;
|
|
80
|
+
/** Fallback evaluations (expression error). */
|
|
81
|
+
fallbackEvals: number;
|
|
82
|
+
/** Total credits computed. */
|
|
83
|
+
totalCreditsComputed: number;
|
|
84
|
+
/** Evaluations by metric ID. */
|
|
85
|
+
byMetric: Record<string, number>;
|
|
86
|
+
/** Evaluations by tool. */
|
|
87
|
+
byTool: Record<string, number>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Safely compute a math expression with named variables.
|
|
91
|
+
*
|
|
92
|
+
* Uses a recursive descent parser. No JavaScript code execution.
|
|
93
|
+
* Only supports: numbers, variables, +, -, *, /, %, parentheses,
|
|
94
|
+
* and built-in math functions (min, max, ceil, floor, round, abs, sqrt, log, pow).
|
|
95
|
+
*/
|
|
96
|
+
export declare function computeExpression(expression: string, vars: Record<string, number>): number;
|
|
97
|
+
export declare class BillableMetricEngine {
|
|
98
|
+
private metrics;
|
|
99
|
+
private stats;
|
|
100
|
+
constructor(metrics?: BillableMetric[]);
|
|
101
|
+
/** Get all metrics. */
|
|
102
|
+
getMetrics(): BillableMetric[];
|
|
103
|
+
/** Add or update a metric. */
|
|
104
|
+
upsertMetric(metric: BillableMetric): BillableMetric;
|
|
105
|
+
/** Remove a metric by ID. */
|
|
106
|
+
removeMetric(id: string): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Find the matching metric for a tool.
|
|
109
|
+
* Returns the first active metric whose tool list matches.
|
|
110
|
+
*/
|
|
111
|
+
findMetric(tool: string): BillableMetric | null;
|
|
112
|
+
/**
|
|
113
|
+
* Compute cost for a tool call using billable metric expressions.
|
|
114
|
+
*
|
|
115
|
+
* @param context - Context with args, response, timing data
|
|
116
|
+
* @returns Result with computed cost, or null if no metric matches
|
|
117
|
+
*/
|
|
118
|
+
computeCost(context: MetricContext): MetricResult | null;
|
|
119
|
+
/** Get statistics. */
|
|
120
|
+
getStats(): BillableMetricStats;
|
|
121
|
+
/** Reset stats. */
|
|
122
|
+
resetStats(): void;
|
|
123
|
+
/** Export metrics for backup/serialization. */
|
|
124
|
+
exportMetrics(): BillableMetric[];
|
|
125
|
+
/** Import metrics. */
|
|
126
|
+
importMetrics(metrics: BillableMetric[], mode?: 'merge' | 'replace'): number;
|
|
127
|
+
/** Destroy. */
|
|
128
|
+
destroy(): void;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=billable-metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billable-metrics.d.ts","sourceRoot":"","sources":["../src/billable-metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Billable Metric Expressions — Config-driven pricing formulas.
|
|
4
|
+
*
|
|
5
|
+
* Instead of flat per-call pricing, define expressions that compute
|
|
6
|
+
* cost based on request/response attributes:
|
|
7
|
+
*
|
|
8
|
+
* cost = input_tokens * 0.001 + output_tokens * 0.003
|
|
9
|
+
* cost = max(1, file_size_kb * 0.5)
|
|
10
|
+
* cost = base_cost + (duration_ms / 1000) * 2
|
|
11
|
+
*
|
|
12
|
+
* SECURITY: Expressions are parsed with a safe recursive descent parser.
|
|
13
|
+
* NO JavaScript eval(), NO Function constructor. The parser only
|
|
14
|
+
* supports arithmetic (+, -, *, /, %), built-in math functions
|
|
15
|
+
* (min, max, ceil, floor, round, abs, sqrt, log, pow),
|
|
16
|
+
* numeric literals, and named variables.
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Safe expression parsing (AST-based, no code execution)
|
|
20
|
+
* - Built-in math functions: min, max, ceil, floor, round, abs
|
|
21
|
+
* - Variable binding from tool args and response fields
|
|
22
|
+
* - Per-tool metric definitions
|
|
23
|
+
* - Fallback to flat pricing if expression errors
|
|
24
|
+
* - Statistics and audit trail
|
|
25
|
+
*
|
|
26
|
+
* Zero external dependencies.
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.BillableMetricEngine = void 0;
|
|
30
|
+
exports.computeExpression = computeExpression;
|
|
31
|
+
/** Tokenize an expression string into a list of tokens. */
|
|
32
|
+
function tokenize(expr) {
|
|
33
|
+
const tokens = [];
|
|
34
|
+
let i = 0;
|
|
35
|
+
while (i < expr.length) {
|
|
36
|
+
const ch = expr[i];
|
|
37
|
+
// Skip whitespace
|
|
38
|
+
if (/\s/.test(ch)) {
|
|
39
|
+
i++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// Number literal
|
|
43
|
+
if (/[0-9.]/.test(ch)) {
|
|
44
|
+
let num = '';
|
|
45
|
+
while (i < expr.length && /[0-9.]/.test(expr[i])) {
|
|
46
|
+
num += expr[i++];
|
|
47
|
+
}
|
|
48
|
+
tokens.push({ type: 'number', value: num, numValue: parseFloat(num) });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// Identifier (variable name or function name)
|
|
52
|
+
if (/[a-zA-Z_]/.test(ch)) {
|
|
53
|
+
let ident = '';
|
|
54
|
+
while (i < expr.length && /[a-zA-Z0-9_]/.test(expr[i])) {
|
|
55
|
+
ident += expr[i++];
|
|
56
|
+
}
|
|
57
|
+
tokens.push({ type: 'ident', value: ident });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// Arithmetic operators
|
|
61
|
+
if ('+-*/%'.includes(ch)) {
|
|
62
|
+
tokens.push({ type: 'op', value: ch });
|
|
63
|
+
i++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// Parentheses and comma
|
|
67
|
+
if (ch === '(') {
|
|
68
|
+
tokens.push({ type: 'lparen', value: '(' });
|
|
69
|
+
i++;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch === ')') {
|
|
73
|
+
tokens.push({ type: 'rparen', value: ')' });
|
|
74
|
+
i++;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (ch === ',') {
|
|
78
|
+
tokens.push({ type: 'comma', value: ',' });
|
|
79
|
+
i++;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Unexpected character: ${ch} at position ${i}`);
|
|
83
|
+
}
|
|
84
|
+
tokens.push({ type: 'eof', value: '' });
|
|
85
|
+
return tokens;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Recursive descent parser for arithmetic expressions.
|
|
89
|
+
* Grammar:
|
|
90
|
+
* expr = addSub
|
|
91
|
+
* addSub = mulDiv (('+' | '-') mulDiv)*
|
|
92
|
+
* mulDiv = unary (('*' | '/' | '%') unary)*
|
|
93
|
+
* unary = ('-' | '+')? primary
|
|
94
|
+
* primary = NUMBER | IDENT | IDENT '(' args ')' | '(' expr ')'
|
|
95
|
+
* args = expr (',' expr)*
|
|
96
|
+
*/
|
|
97
|
+
class ExprParser {
|
|
98
|
+
tokens;
|
|
99
|
+
pos = 0;
|
|
100
|
+
vars;
|
|
101
|
+
funcs;
|
|
102
|
+
constructor(tokens, vars) {
|
|
103
|
+
this.tokens = tokens;
|
|
104
|
+
this.vars = vars;
|
|
105
|
+
this.funcs = {
|
|
106
|
+
min: Math.min,
|
|
107
|
+
max: Math.max,
|
|
108
|
+
ceil: Math.ceil,
|
|
109
|
+
floor: Math.floor,
|
|
110
|
+
round: Math.round,
|
|
111
|
+
abs: Math.abs,
|
|
112
|
+
sqrt: Math.sqrt,
|
|
113
|
+
log: Math.log,
|
|
114
|
+
log10: Math.log10,
|
|
115
|
+
pow: Math.pow,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
parse() {
|
|
119
|
+
const result = this.parseAddSub();
|
|
120
|
+
if (this.tokens[this.pos].type !== 'eof') {
|
|
121
|
+
throw new Error(`Unexpected token: ${this.tokens[this.pos].value}`);
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
parseAddSub() {
|
|
126
|
+
let left = this.parseMulDiv();
|
|
127
|
+
while (this.pos < this.tokens.length && this.tokens[this.pos].type === 'op' && '+-'.includes(this.tokens[this.pos].value)) {
|
|
128
|
+
const op = this.tokens[this.pos++].value;
|
|
129
|
+
const right = this.parseMulDiv();
|
|
130
|
+
left = op === '+' ? left + right : left - right;
|
|
131
|
+
}
|
|
132
|
+
return left;
|
|
133
|
+
}
|
|
134
|
+
parseMulDiv() {
|
|
135
|
+
let left = this.parseUnary();
|
|
136
|
+
while (this.pos < this.tokens.length && this.tokens[this.pos].type === 'op' && '*/%'.includes(this.tokens[this.pos].value)) {
|
|
137
|
+
const op = this.tokens[this.pos++].value;
|
|
138
|
+
const right = this.parseUnary();
|
|
139
|
+
if (op === '*')
|
|
140
|
+
left *= right;
|
|
141
|
+
else if (op === '/')
|
|
142
|
+
left = right !== 0 ? left / right : 0;
|
|
143
|
+
else
|
|
144
|
+
left = right !== 0 ? left % right : 0;
|
|
145
|
+
}
|
|
146
|
+
return left;
|
|
147
|
+
}
|
|
148
|
+
parseUnary() {
|
|
149
|
+
if (this.tokens[this.pos].type === 'op' && this.tokens[this.pos].value === '-') {
|
|
150
|
+
this.pos++;
|
|
151
|
+
return -this.parsePrimary();
|
|
152
|
+
}
|
|
153
|
+
if (this.tokens[this.pos].type === 'op' && this.tokens[this.pos].value === '+') {
|
|
154
|
+
this.pos++;
|
|
155
|
+
}
|
|
156
|
+
return this.parsePrimary();
|
|
157
|
+
}
|
|
158
|
+
parsePrimary() {
|
|
159
|
+
const token = this.tokens[this.pos];
|
|
160
|
+
// Number literal
|
|
161
|
+
if (token.type === 'number') {
|
|
162
|
+
this.pos++;
|
|
163
|
+
return token.numValue;
|
|
164
|
+
}
|
|
165
|
+
// Identifier (variable or function call)
|
|
166
|
+
if (token.type === 'ident') {
|
|
167
|
+
this.pos++;
|
|
168
|
+
// Check if it's a function call: IDENT '(' args ')'
|
|
169
|
+
if (this.pos < this.tokens.length && this.tokens[this.pos].type === 'lparen') {
|
|
170
|
+
const func = this.funcs[token.value];
|
|
171
|
+
if (!func)
|
|
172
|
+
throw new Error(`Unknown function: ${token.value}`);
|
|
173
|
+
this.pos++; // skip (
|
|
174
|
+
const args = [];
|
|
175
|
+
if (this.tokens[this.pos].type !== 'rparen') {
|
|
176
|
+
args.push(this.parseAddSub());
|
|
177
|
+
while (this.tokens[this.pos].type === 'comma') {
|
|
178
|
+
this.pos++; // skip ,
|
|
179
|
+
args.push(this.parseAddSub());
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (this.tokens[this.pos].type !== 'rparen') {
|
|
183
|
+
throw new Error('Expected closing parenthesis');
|
|
184
|
+
}
|
|
185
|
+
this.pos++; // skip )
|
|
186
|
+
return func(...args);
|
|
187
|
+
}
|
|
188
|
+
// Variable lookup
|
|
189
|
+
const val = this.vars[token.value];
|
|
190
|
+
if (val === undefined) {
|
|
191
|
+
throw new Error(`Unknown variable: ${token.value}`);
|
|
192
|
+
}
|
|
193
|
+
return val;
|
|
194
|
+
}
|
|
195
|
+
// Parenthesized expression
|
|
196
|
+
if (token.type === 'lparen') {
|
|
197
|
+
this.pos++; // skip (
|
|
198
|
+
const result = this.parseAddSub();
|
|
199
|
+
if (this.tokens[this.pos].type !== 'rparen') {
|
|
200
|
+
throw new Error('Expected closing parenthesis');
|
|
201
|
+
}
|
|
202
|
+
this.pos++; // skip )
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
throw new Error(`Unexpected token: ${token.value}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Safely compute a math expression with named variables.
|
|
210
|
+
*
|
|
211
|
+
* Uses a recursive descent parser. No JavaScript code execution.
|
|
212
|
+
* Only supports: numbers, variables, +, -, *, /, %, parentheses,
|
|
213
|
+
* and built-in math functions (min, max, ceil, floor, round, abs, sqrt, log, pow).
|
|
214
|
+
*/
|
|
215
|
+
function computeExpression(expression, vars) {
|
|
216
|
+
const tokens = tokenize(expression);
|
|
217
|
+
const parser = new ExprParser(tokens, vars);
|
|
218
|
+
return parser.parse();
|
|
219
|
+
}
|
|
220
|
+
// ─── Billable Metrics Engine ─────────────────────────────────────────────────
|
|
221
|
+
class BillableMetricEngine {
|
|
222
|
+
metrics = [];
|
|
223
|
+
stats = {
|
|
224
|
+
totalEvaluations: 0,
|
|
225
|
+
successfulEvals: 0,
|
|
226
|
+
fallbackEvals: 0,
|
|
227
|
+
totalCreditsComputed: 0,
|
|
228
|
+
byMetric: {},
|
|
229
|
+
byTool: {},
|
|
230
|
+
};
|
|
231
|
+
constructor(metrics) {
|
|
232
|
+
if (metrics)
|
|
233
|
+
this.metrics = [...metrics];
|
|
234
|
+
}
|
|
235
|
+
/** Get all metrics. */
|
|
236
|
+
getMetrics() { return [...this.metrics]; }
|
|
237
|
+
/** Add or update a metric. */
|
|
238
|
+
upsertMetric(metric) {
|
|
239
|
+
// Validate expression syntax by tokenizing
|
|
240
|
+
try {
|
|
241
|
+
tokenize(metric.expression);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
throw new Error(`Invalid expression: ${err}`);
|
|
245
|
+
}
|
|
246
|
+
const idx = this.metrics.findIndex(m => m.id === metric.id);
|
|
247
|
+
if (idx >= 0) {
|
|
248
|
+
this.metrics[idx] = metric;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
this.metrics.push(metric);
|
|
252
|
+
}
|
|
253
|
+
return metric;
|
|
254
|
+
}
|
|
255
|
+
/** Remove a metric by ID. */
|
|
256
|
+
removeMetric(id) {
|
|
257
|
+
const idx = this.metrics.findIndex(m => m.id === id);
|
|
258
|
+
if (idx < 0)
|
|
259
|
+
return false;
|
|
260
|
+
this.metrics.splice(idx, 1);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Find the matching metric for a tool.
|
|
265
|
+
* Returns the first active metric whose tool list matches.
|
|
266
|
+
*/
|
|
267
|
+
findMetric(tool) {
|
|
268
|
+
for (const metric of this.metrics) {
|
|
269
|
+
if (!metric.active)
|
|
270
|
+
continue;
|
|
271
|
+
if (metric.tools.length === 0 || metric.tools.includes(tool)) {
|
|
272
|
+
return metric;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Compute cost for a tool call using billable metric expressions.
|
|
279
|
+
*
|
|
280
|
+
* @param context - Context with args, response, timing data
|
|
281
|
+
* @returns Result with computed cost, or null if no metric matches
|
|
282
|
+
*/
|
|
283
|
+
computeCost(context) {
|
|
284
|
+
const metric = this.findMetric(context.tool);
|
|
285
|
+
if (!metric)
|
|
286
|
+
return null;
|
|
287
|
+
this.stats.totalEvaluations++;
|
|
288
|
+
this.stats.byMetric[metric.id] = (this.stats.byMetric[metric.id] ?? 0) + 1;
|
|
289
|
+
this.stats.byTool[context.tool] = (this.stats.byTool[context.tool] ?? 0) + 1;
|
|
290
|
+
// Build variable map from context
|
|
291
|
+
const vars = {
|
|
292
|
+
// Built-in variables
|
|
293
|
+
input_size_bytes: context.inputSizeBytes ?? 0,
|
|
294
|
+
input_size_kb: (context.inputSizeBytes ?? 0) / 1024,
|
|
295
|
+
response_size_bytes: context.responseSizeBytes ?? 0,
|
|
296
|
+
response_size_kb: (context.responseSizeBytes ?? 0) / 1024,
|
|
297
|
+
duration_ms: context.durationMs ?? 0,
|
|
298
|
+
duration_s: (context.durationMs ?? 0) / 1000,
|
|
299
|
+
// Inject custom variables
|
|
300
|
+
...(context.customVars ?? {}),
|
|
301
|
+
};
|
|
302
|
+
// Extract numeric values from input args (flatten one level)
|
|
303
|
+
if (context.inputArgs) {
|
|
304
|
+
for (const [key, value] of Object.entries(context.inputArgs)) {
|
|
305
|
+
if (typeof value === 'number') {
|
|
306
|
+
vars[key] = value;
|
|
307
|
+
}
|
|
308
|
+
else if (typeof value === 'string') {
|
|
309
|
+
vars[`${key}_length`] = value.length;
|
|
310
|
+
}
|
|
311
|
+
else if (typeof value === 'boolean') {
|
|
312
|
+
vars[key] = value ? 1 : 0;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
let cost = computeExpression(metric.expression, vars);
|
|
318
|
+
// Apply floor/ceiling
|
|
319
|
+
if (metric.minCost !== undefined)
|
|
320
|
+
cost = Math.max(metric.minCost, cost);
|
|
321
|
+
if (metric.maxCost !== undefined)
|
|
322
|
+
cost = Math.min(metric.maxCost, cost);
|
|
323
|
+
// Round to nearest integer (credits are whole numbers)
|
|
324
|
+
cost = Math.round(cost);
|
|
325
|
+
if (cost < 0)
|
|
326
|
+
cost = 0;
|
|
327
|
+
this.stats.successfulEvals++;
|
|
328
|
+
this.stats.totalCreditsComputed += cost;
|
|
329
|
+
return {
|
|
330
|
+
cost,
|
|
331
|
+
metricId: metric.id,
|
|
332
|
+
usedFallback: false,
|
|
333
|
+
resolvedVars: vars,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
catch (err) {
|
|
337
|
+
// Expression failed — use fallback
|
|
338
|
+
const fallback = metric.fallbackCost ?? 1;
|
|
339
|
+
this.stats.fallbackEvals++;
|
|
340
|
+
this.stats.totalCreditsComputed += fallback;
|
|
341
|
+
return {
|
|
342
|
+
cost: fallback,
|
|
343
|
+
metricId: metric.id,
|
|
344
|
+
usedFallback: true,
|
|
345
|
+
error: String(err),
|
|
346
|
+
resolvedVars: vars,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/** Get statistics. */
|
|
351
|
+
getStats() {
|
|
352
|
+
return { ...this.stats };
|
|
353
|
+
}
|
|
354
|
+
/** Reset stats. */
|
|
355
|
+
resetStats() {
|
|
356
|
+
this.stats = {
|
|
357
|
+
totalEvaluations: 0,
|
|
358
|
+
successfulEvals: 0,
|
|
359
|
+
fallbackEvals: 0,
|
|
360
|
+
totalCreditsComputed: 0,
|
|
361
|
+
byMetric: {},
|
|
362
|
+
byTool: {},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
/** Export metrics for backup/serialization. */
|
|
366
|
+
exportMetrics() {
|
|
367
|
+
return JSON.parse(JSON.stringify(this.metrics));
|
|
368
|
+
}
|
|
369
|
+
/** Import metrics. */
|
|
370
|
+
importMetrics(metrics, mode = 'merge') {
|
|
371
|
+
if (mode === 'replace') {
|
|
372
|
+
this.metrics = [];
|
|
373
|
+
}
|
|
374
|
+
let imported = 0;
|
|
375
|
+
for (const m of metrics) {
|
|
376
|
+
this.upsertMetric(m);
|
|
377
|
+
imported++;
|
|
378
|
+
}
|
|
379
|
+
return imported;
|
|
380
|
+
}
|
|
381
|
+
/** Destroy. */
|
|
382
|
+
destroy() {
|
|
383
|
+
this.metrics = [];
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
exports.BillableMetricEngine = BillableMetricEngine;
|
|
387
|
+
//# sourceMappingURL=billable-metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billable-metrics.js","sourceRoot":"","sources":["../src/billable-metrics.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AA4OH;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA6B9C,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -53,6 +53,7 @@ function printUsage() {
|
|
|
53
53
|
paygate-mcp wrap --server <command> [options] # stdio transport
|
|
54
54
|
paygate-mcp wrap --remote-url <url> [options] # Streamable HTTP transport
|
|
55
55
|
paygate-mcp wrap --config <path> [options] # load from config file
|
|
56
|
+
paygate-mcp wrap-api --openapi <spec.json> [opts] # wrap REST API as MCP tools
|
|
56
57
|
paygate-mcp init [--output <path>] [--force] # interactive setup wizard
|
|
57
58
|
paygate-mcp validate --config <path> # validate config without starting
|
|
58
59
|
paygate-mcp completions <bash|zsh|fish> # generate shell completions
|
|
@@ -569,6 +570,122 @@ async function main() {
|
|
|
569
570
|
}
|
|
570
571
|
break;
|
|
571
572
|
}
|
|
573
|
+
case 'wrap-api': {
|
|
574
|
+
// ─── wrap-api: Convert OpenAPI spec → gated MCP tools ───────────────
|
|
575
|
+
const specPath = flags['openapi'] || flags['spec'];
|
|
576
|
+
if (!specPath) {
|
|
577
|
+
console.error('Error: --openapi <path-or-url> is required for wrap-api.\n');
|
|
578
|
+
console.log(' Usage: paygate-mcp wrap-api --openapi <spec.json> [--base-url <url>] [options]');
|
|
579
|
+
console.log('');
|
|
580
|
+
console.log(' Wraps any REST API described by an OpenAPI 3.x spec as gated MCP tools.');
|
|
581
|
+
console.log(' All standard PayGate options (--port, --price, --rate-limit, etc.) apply.');
|
|
582
|
+
console.log('');
|
|
583
|
+
console.log(' Options:');
|
|
584
|
+
console.log(' --openapi <path> Path to OpenAPI 3.x spec (JSON)');
|
|
585
|
+
console.log(' --base-url <url> Override base URL for API calls');
|
|
586
|
+
console.log(' --prefix <s> Prefix for tool names (e.g. "api_")');
|
|
587
|
+
console.log(' --tag-filter <s> Only include operations with this tag');
|
|
588
|
+
console.log('');
|
|
589
|
+
process.exit(1);
|
|
590
|
+
}
|
|
591
|
+
let specJson;
|
|
592
|
+
try {
|
|
593
|
+
specJson = (0, fs_1.readFileSync)(specPath, 'utf-8');
|
|
594
|
+
// Validate it's parseable JSON
|
|
595
|
+
JSON.parse(specJson);
|
|
596
|
+
}
|
|
597
|
+
catch (err) {
|
|
598
|
+
console.error(`Error reading OpenAPI spec: ${err.message}`);
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
const apiBaseUrl = flags['base-url'] || undefined;
|
|
602
|
+
const apiPrefix = flags['prefix'] || undefined;
|
|
603
|
+
const apiTagFilter = flags['tag-filter'] || undefined;
|
|
604
|
+
const apiPortFlag = flags['port'] || env('PAYGATE_PORT');
|
|
605
|
+
const apiPriceFlag = flags['price'] || env('PAYGATE_PRICE');
|
|
606
|
+
const apiRateLimitFlag = flags['rate-limit'] || env('PAYGATE_RATE_LIMIT');
|
|
607
|
+
const apiShadowFlag = ('shadow' in flags) || env('PAYGATE_SHADOW') === 'true';
|
|
608
|
+
const apiAdminKeyFlag = flags['admin-key'] || env('PAYGATE_ADMIN_KEY');
|
|
609
|
+
const apiStateFileFlag = flags['state-file'] || env('PAYGATE_STATE_FILE');
|
|
610
|
+
const apiLogLevelFlag = flags['log-level'] || env('PAYGATE_LOG_LEVEL');
|
|
611
|
+
const apiLogFormatFlag = flags['log-format'] || env('PAYGATE_LOG_FORMAT');
|
|
612
|
+
const apiToolPriceFlag = flags['tool-price'] || env('PAYGATE_TOOL_PRICE');
|
|
613
|
+
const apiRedisUrlFlag = flags['redis-url'] || env('PAYGATE_REDIS_URL');
|
|
614
|
+
const apiPort = parseInt(apiPortFlag || '3402', 10);
|
|
615
|
+
const apiPrice = parseInt(apiPriceFlag || '1', 10);
|
|
616
|
+
const apiRateLimit = parseInt(apiRateLimitFlag || '60', 10);
|
|
617
|
+
// Build a dummy serverCommand since PayGateServer requires it
|
|
618
|
+
const config = {
|
|
619
|
+
serverCommand: '__openapi__',
|
|
620
|
+
serverArgs: [],
|
|
621
|
+
port: apiPort,
|
|
622
|
+
defaultCreditsPerCall: apiPrice,
|
|
623
|
+
globalRateLimitPerMin: apiRateLimit,
|
|
624
|
+
shadowMode: !!apiShadowFlag,
|
|
625
|
+
openApiSpec: specJson,
|
|
626
|
+
openApiBaseUrl: apiBaseUrl,
|
|
627
|
+
logLevel: apiLogLevelFlag || 'info',
|
|
628
|
+
logFormat: apiLogFormatFlag || 'text',
|
|
629
|
+
name: 'PayGate MCP (OpenAPI)',
|
|
630
|
+
toolPricing: apiToolPriceFlag ? parseToolPricing(apiToolPriceFlag) : {},
|
|
631
|
+
};
|
|
632
|
+
const apiServer = new server_1.PayGateServer(config, apiAdminKeyFlag, apiStateFileFlag, undefined, // remoteUrl
|
|
633
|
+
undefined, // stripeSecret
|
|
634
|
+
undefined, // servers
|
|
635
|
+
apiRedisUrlFlag);
|
|
636
|
+
// Handle graceful shutdown
|
|
637
|
+
let apiShuttingDown = false;
|
|
638
|
+
const apiShutdown = async (reason) => {
|
|
639
|
+
if (apiShuttingDown)
|
|
640
|
+
return;
|
|
641
|
+
apiShuttingDown = true;
|
|
642
|
+
console.log(`\nGraceful shutdown initiated${reason ? ` (${reason})` : ''}…`);
|
|
643
|
+
await apiServer.gracefulStop(30_000);
|
|
644
|
+
process.exit(reason ? 1 : 0);
|
|
645
|
+
};
|
|
646
|
+
process.on('SIGINT', () => apiShutdown());
|
|
647
|
+
process.on('SIGTERM', () => apiShutdown());
|
|
648
|
+
try {
|
|
649
|
+
const result = await apiServer.start();
|
|
650
|
+
// Get tool count from the OpenAPI backend
|
|
651
|
+
const handler = apiServer.handler;
|
|
652
|
+
let toolCount = 0;
|
|
653
|
+
try {
|
|
654
|
+
const toolsResp = await handler.handleRequest({ jsonrpc: '2.0', id: 'init', method: 'tools/list', params: {} }, null);
|
|
655
|
+
toolCount = (toolsResp?.result?.tools || []).length;
|
|
656
|
+
}
|
|
657
|
+
catch { /* ignore */ }
|
|
658
|
+
console.log(`
|
|
659
|
+
╔══════════════════════════════════════════════════╗
|
|
660
|
+
║ PayGate MCP — OpenAPI Wrapped ║
|
|
661
|
+
╠══════════════════════════════════════════════════╣
|
|
662
|
+
║ ║
|
|
663
|
+
║ Endpoint: http://localhost:${String(result.port).padEnd(5)} ║
|
|
664
|
+
║ Admin Key: ${result.adminKey.slice(0, 20)}... ║
|
|
665
|
+
║ Backend: OpenAPI (${String(toolCount).padEnd(3)} tools) ║
|
|
666
|
+
║ Source: ${specPath.slice(0, 35).padEnd(35)}║
|
|
667
|
+
║ ║
|
|
668
|
+
║ Pricing: ${String(apiPrice).padEnd(3)} credit(s) per tool call ║
|
|
669
|
+
║ Rate Limit: ${String(apiRateLimit).padEnd(3)} calls/min per key ║
|
|
670
|
+
║ Shadow: ${String(!!apiShadowFlag).padEnd(5)} ║
|
|
671
|
+
║ ║
|
|
672
|
+
╚══════════════════════════════════════════════════╝
|
|
673
|
+
`);
|
|
674
|
+
console.log(` Admin key (save this): ${result.adminKey}\n`);
|
|
675
|
+
// Dry run
|
|
676
|
+
const isDryRun = flags['dry-run'] === 'true' || 'dry-run' in flags;
|
|
677
|
+
if (isDryRun) {
|
|
678
|
+
console.log(` Discovered ${toolCount} tool(s) from OpenAPI spec. Dry run complete.\n`);
|
|
679
|
+
await apiServer.stop();
|
|
680
|
+
process.exit(0);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
catch (error) {
|
|
684
|
+
console.error('Failed to start server:', error);
|
|
685
|
+
process.exit(1);
|
|
686
|
+
}
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
572
689
|
case 'help':
|
|
573
690
|
case '--help':
|
|
574
691
|
case '-h':
|