troxy-cli 1.4.15 → 1.4.16

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "troxy-cli",
3
- "version": "1.4.15",
3
+ "version": "1.4.16",
4
4
  "description": "AI payment control — protect your agent's payments with policies",
5
5
  "type": "module",
6
6
  "bin": {
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
+ checkApprovalStatus: (token) => request('GET', `/approvals/${encodeURIComponent(token)}`),
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. Wait for the user to approve, then call evaluate_payment again with the same payment details PLUS the approval_token field. The second call will return ALLOW without re-escalating. ' +
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,53 @@ 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 POLL_MS = 10_000; // check every 10 seconds
146
+ const MAX_MS = 10 * 60_000; // give up after 10 minutes
147
+ const deadline = Date.now() + MAX_MS;
148
+
149
+ while (Date.now() < deadline) {
150
+ let status;
151
+ try {
152
+ status = await api.checkApprovalStatus(approval_token);
153
+ } catch (err) {
154
+ return {
155
+ content: [{ type: 'text', text: `Troxy error checking approval: ${err.message}` }],
156
+ isError: true,
157
+ };
158
+ }
159
+
160
+ if (status.resolution === 'APPROVED') {
161
+ return {
162
+ content: [{ type: 'text', text: `✅ Payment approved! Call evaluate_payment again with the same payment details and approval_token: "${approval_token}" to proceed.` }],
163
+ isError: false,
164
+ };
165
+ }
166
+ if (status.resolution === 'DECLINED') {
167
+ return {
168
+ content: [{ type: 'text', text: `❌ Payment declined by the account owner. Do not proceed with this payment.` }],
169
+ isError: true,
170
+ };
171
+ }
172
+
173
+ // Check for expiry
174
+ if (status.expires_at && new Date(status.expires_at) < new Date()) {
175
+ return {
176
+ content: [{ type: 'text', text: `⏱ Approval request has expired. The payment cannot be approved.` }],
177
+ isError: true,
178
+ };
179
+ }
180
+
181
+ await new Promise(resolve => setTimeout(resolve, POLL_MS));
182
+ }
183
+
184
+ return {
185
+ content: [{ type: 'text', text: `⏳ Still waiting for approval after 10 minutes. Call wait_for_payment_approval again to keep checking, or let the user know it is still pending.` }],
186
+ isError: false,
187
+ };
188
+ }
189
+
126
190
  if (toolName === 'confirm_payment') {
127
191
  const { audit_id, status, provider, provider_transaction_id, reason } = args;
128
192
  try {
@@ -167,7 +231,7 @@ export async function runMcp() {
167
231
  text = `✗ Payment blocked by policy "${policy}". Do not proceed with this payment. (audit: ${audit_id})`;
168
232
  break;
169
233
  case 'ESCALATE':
170
- text = `⏳ Payment requires human approval — a request has been sent to the account owner. Do not proceed until approved.\n\nApproval token: ${approval_token}\n\nWait for the owner to approve in the Troxy dashboard, then tell me "I approved it" or "continue" so I can proceed. Once you do, I will call evaluate_payment again with the same payment details and approval_token: "${approval_token}". (audit: ${audit_id})`;
234
+ 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
235
  break;
172
236
  case 'NOTIFY':
173
237
  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".`;