troxy-cli 1.4.15 → 1.4.17
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/package.json +1 -1
- package/src/api.js +1 -0
- package/src/mcp-server.js +64 -2
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -55,6 +55,7 @@ export const api = {
|
|
|
55
55
|
// Evaluate + confirm (agent API key)
|
|
56
56
|
evaluate: (body, apiKey) => request('POST', '/evaluate', { apiKey, body }),
|
|
57
57
|
confirmPayment: (auditId, body, apiKey) => request('POST', `/payments/${auditId}/confirm`, { apiKey, body }),
|
|
58
|
+
waitApprovalStatus: (token) => request('GET', `/approvals/${encodeURIComponent(token)}/wait`),
|
|
58
59
|
|
|
59
60
|
// MCP heartbeat (agent API key)
|
|
60
61
|
mcpHeartbeat: (apiKey, agentName) => request('POST', '/mcp/heartbeat', { apiKey, body: agentName ? { agent_name: agentName } : undefined }),
|
package/src/mcp-server.js
CHANGED
|
@@ -35,7 +35,7 @@ export async function runMcp() {
|
|
|
35
35
|
'You MUST call the evaluate_payment tool before completing any purchase, payment, or financial transaction. ' +
|
|
36
36
|
'Never submit a payment form, confirm a checkout, or transfer funds without first receiving an ALLOW decision from evaluate_payment. ' +
|
|
37
37
|
'If the decision is BLOCK, abort the transaction and inform the user. ' +
|
|
38
|
-
'If the decision is ESCALATE, the response includes an approval_token.
|
|
38
|
+
'If the decision is ESCALATE, the response includes an approval_token. Immediately call wait_for_payment_approval with that token — it polls automatically until the payment is approved or declined (up to 10 minutes). Once it returns approved, call evaluate_payment again with the same payment details PLUS the approval_token field. The second call will return ALLOW without re-escalating. ' +
|
|
39
39
|
'If the decision is NOTIFY, proceed but the user will be notified. ' +
|
|
40
40
|
'When calling evaluate_payment, use the merchant\'s domain name as merchant_name when available (e.g. "amazon.com", "indigobloom.co.il") — this ensures consistent matching against policies. ' +
|
|
41
41
|
'After every ALLOW or NOTIFY decision, attempt the payment, then call confirm_payment with the audit_id and the outcome (success/failed/cancelled). Always include a reason when the charge fails.',
|
|
@@ -80,6 +80,23 @@ export async function runMcp() {
|
|
|
80
80
|
},
|
|
81
81
|
},
|
|
82
82
|
},
|
|
83
|
+
{
|
|
84
|
+
name: 'wait_for_payment_approval',
|
|
85
|
+
description:
|
|
86
|
+
'Poll Troxy every 10 seconds until a pending payment approval is resolved. ' +
|
|
87
|
+
'Call this immediately after receiving an ESCALATE decision. ' +
|
|
88
|
+
'It will block until the account owner approves or declines (up to 10 minutes), then return the result automatically.',
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
required: ['approval_token'],
|
|
92
|
+
properties: {
|
|
93
|
+
approval_token: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
description: 'The approval_token from the ESCALATE response',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
83
100
|
{
|
|
84
101
|
name: 'evaluate_payment',
|
|
85
102
|
description:
|
|
@@ -123,6 +140,51 @@ export async function runMcp() {
|
|
|
123
140
|
const toolName = request.params.name;
|
|
124
141
|
const args = request.params.arguments ?? {};
|
|
125
142
|
|
|
143
|
+
if (toolName === 'wait_for_payment_approval') {
|
|
144
|
+
const { approval_token } = args;
|
|
145
|
+
const MAX_MS = 60 * 60_000; // overall cap: 1 hour
|
|
146
|
+
const deadline = Date.now() + MAX_MS;
|
|
147
|
+
|
|
148
|
+
// Long-poll: each request blocks on the server for ~20s, returning the
|
|
149
|
+
// moment the approval is resolved. No sleep needed — instant detection.
|
|
150
|
+
while (Date.now() < deadline) {
|
|
151
|
+
let status;
|
|
152
|
+
try {
|
|
153
|
+
status = await api.waitApprovalStatus(approval_token);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
return {
|
|
156
|
+
content: [{ type: 'text', text: `Troxy error waiting for approval: ${err.message}` }],
|
|
157
|
+
isError: true,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (status.status === 'APPROVED') {
|
|
162
|
+
return {
|
|
163
|
+
content: [{ type: 'text', text: `✅ Payment approved! Call evaluate_payment again with the same payment details and approval_token: "${approval_token}" to proceed.` }],
|
|
164
|
+
isError: false,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (status.status === 'DECLINED') {
|
|
168
|
+
return {
|
|
169
|
+
content: [{ type: 'text', text: `❌ Payment declined by the account owner. Do not proceed with this payment.` }],
|
|
170
|
+
isError: true,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (status.status === 'EXPIRED') {
|
|
174
|
+
return {
|
|
175
|
+
content: [{ type: 'text', text: `⏱ Approval request has expired. The payment cannot be approved.` }],
|
|
176
|
+
isError: true,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
// status === 'pending': server timed out its 20s window, loop immediately
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
content: [{ type: 'text', text: `⏳ Still waiting for approval after 1 hour. The request has likely expired.` }],
|
|
184
|
+
isError: true,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
126
188
|
if (toolName === 'confirm_payment') {
|
|
127
189
|
const { audit_id, status, provider, provider_transaction_id, reason } = args;
|
|
128
190
|
try {
|
|
@@ -167,7 +229,7 @@ export async function runMcp() {
|
|
|
167
229
|
text = `✗ Payment blocked by policy "${policy}". Do not proceed with this payment. (audit: ${audit_id})`;
|
|
168
230
|
break;
|
|
169
231
|
case 'ESCALATE':
|
|
170
|
-
text = `⏳ Payment requires human approval — a request has been sent to the account owner
|
|
232
|
+
text = `⏳ Payment requires human approval — a request has been sent to the account owner.\n\nApproval token: ${approval_token}\n\nNow call wait_for_payment_approval(approval_token="${approval_token}") to automatically detect approval. Do not proceed until it returns approved. (audit: ${audit_id})`;
|
|
171
233
|
break;
|
|
172
234
|
case 'NOTIFY':
|
|
173
235
|
text = `✓ Payment approved with notification. Policy matched: "${policy}". (audit: ${audit_id})\n\nAfter the charge attempt completes, call confirm_payment with audit_id "${audit_id}" and status "success", "failed", or "cancelled".`;
|