@ar-agents/mercadopago 0.13.0 → 0.15.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/CHANGELOG.md +92 -0
- package/README.md +11 -0
- package/README.skeleton.md +200 -0
- package/dist/index.cjs +167 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +167 -50
- package/dist/index.js.map +1 -1
- package/dist/vercel-kv.cjs +15 -3
- package/dist/vercel-kv.cjs.map +1 -1
- package/dist/vercel-kv.d.cts +39 -8
- package/dist/vercel-kv.d.ts +39 -8
- package/dist/vercel-kv.js +15 -3
- package/dist/vercel-kv.js.map +1 -1
- package/package.json +1 -1
- package/tools.manifest.json +545 -297
package/dist/index.d.cts
CHANGED
|
@@ -365,6 +365,12 @@ interface CreatePreapprovalParams {
|
|
|
365
365
|
backUrl: string;
|
|
366
366
|
/** Optional client-side identifier for the subscription. */
|
|
367
367
|
externalReference?: string;
|
|
368
|
+
/**
|
|
369
|
+
* Optional explicit idempotency key. If omitted, the client auto-generates
|
|
370
|
+
* a UUID v4 per call. Pass a deterministic key (e.g., hash of inputs) when
|
|
371
|
+
* you need cross-retry idempotency from the agent layer.
|
|
372
|
+
*/
|
|
373
|
+
idempotencyKey?: string;
|
|
368
374
|
}
|
|
369
375
|
/**
|
|
370
376
|
* The shape of an MP webhook notification body for `topic=preapproval`. MP's
|
|
@@ -734,6 +740,13 @@ interface CreatePreferenceParams {
|
|
|
734
740
|
marketplaceFee?: number;
|
|
735
741
|
/** Seller's MP user_id. Funds route here when set. */
|
|
736
742
|
collectorId?: string | number;
|
|
743
|
+
/**
|
|
744
|
+
* Optional explicit idempotency key. If omitted, the client auto-generates
|
|
745
|
+
* a UUID v4 per call. Pass a deterministic key (e.g., hash of items +
|
|
746
|
+
* external_reference) when you need cross-retry idempotency from the
|
|
747
|
+
* agent layer.
|
|
748
|
+
*/
|
|
749
|
+
idempotencyKey?: string;
|
|
737
750
|
}
|
|
738
751
|
declare const CustomerSchema: z.ZodObject<{
|
|
739
752
|
id: z.ZodString;
|
|
@@ -1332,6 +1345,14 @@ interface CreatePointPaymentIntentParams {
|
|
|
1332
1345
|
interface MercadoPagoClientOptions {
|
|
1333
1346
|
/** Access token. TEST- prefix for sandbox, APP_USR- for production. */
|
|
1334
1347
|
accessToken: string;
|
|
1348
|
+
/**
|
|
1349
|
+
* Escape hatch for browser-context tests (e.g., jsdom). MUST NOT be set
|
|
1350
|
+
* in production code — the constructor's browser-context check exists
|
|
1351
|
+
* specifically to prevent the access token from being bundled into a
|
|
1352
|
+
* client-side JavaScript bundle. The `__` prefix and explicit boolean
|
|
1353
|
+
* are deliberate friction so this never gets typed by accident.
|
|
1354
|
+
*/
|
|
1355
|
+
__allowBrowser?: boolean;
|
|
1335
1356
|
/**
|
|
1336
1357
|
* Override the API base URL. Mostly useful for tests against MSW or for
|
|
1337
1358
|
* pointing at a regional MP host. Defaults to https://api.mercadopago.com.
|
|
@@ -2146,7 +2167,49 @@ interface MercadoPagoToolsOptions {
|
|
|
2146
2167
|
* retries (which fire on 5xx and can deliver the same event 5+ times).
|
|
2147
2168
|
*/
|
|
2148
2169
|
webhookDedup?: WebhookDedup;
|
|
2170
|
+
/**
|
|
2171
|
+
* v0.15 — Programmatic Human-In-The-Loop gate for irreversible /
|
|
2172
|
+
* money-moving operations. When set, every call to one of the gated
|
|
2173
|
+
* tools (cancel_payment, capture_payment, refund_payment,
|
|
2174
|
+
* delete_customer_card, cancel_qr_payment, cancel_order,
|
|
2175
|
+
* cancel_point_payment_intent, delete_webhook) invokes this callback
|
|
2176
|
+
* BEFORE executing. Return `true` to proceed, `false` to reject the
|
|
2177
|
+
* call (the tool returns `{ ok: false, reason: "Confirmation declined" }`).
|
|
2178
|
+
*
|
|
2179
|
+
* The description-based HITL warnings still apply (they nudge the LLM
|
|
2180
|
+
* to confirm in-conversation), but those depend on the LLM's heuristic
|
|
2181
|
+
* and can be bypassed via prompt injection. This callback is the actual
|
|
2182
|
+
* out-of-band enforcement: wire it to your UI / Slack / email / SMS
|
|
2183
|
+
* confirmation flow so a human approves money-movement explicitly.
|
|
2184
|
+
*
|
|
2185
|
+
* @example
|
|
2186
|
+
* ```ts
|
|
2187
|
+
* mercadoPagoTools(client, {
|
|
2188
|
+
* state, backUrl,
|
|
2189
|
+
* requireConfirmation: async (op, args) => {
|
|
2190
|
+
* // Send a Slack DM to the operator with the operation summary
|
|
2191
|
+
* // and wait for their button click. Throw or return false to reject.
|
|
2192
|
+
* return await slack.confirm({
|
|
2193
|
+
* channel: "#mp-approvals",
|
|
2194
|
+
* text: `Refund $${args.amount ?? "FULL"} on payment ${args.payment_id}?`,
|
|
2195
|
+
* timeoutMs: 60_000,
|
|
2196
|
+
* });
|
|
2197
|
+
* },
|
|
2198
|
+
* });
|
|
2199
|
+
* ```
|
|
2200
|
+
*
|
|
2201
|
+
* If omitted (default), the description-based HITL is the only line of
|
|
2202
|
+
* defense — fine for trusted/internal agents, NOT recommended for
|
|
2203
|
+
* untrusted-input agents (anything reading from a public webhook).
|
|
2204
|
+
*/
|
|
2205
|
+
requireConfirmation?: (operation: GatedOperation, args: Record<string, unknown>) => Promise<boolean>;
|
|
2149
2206
|
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Tool names that go through `requireConfirmation` when configured.
|
|
2209
|
+
* Adding a new irreversible operation? Add it here AND in the
|
|
2210
|
+
* `applyConfirmationGate` wrapper at the bottom of this file.
|
|
2211
|
+
*/
|
|
2212
|
+
type GatedOperation = "cancel_payment" | "capture_payment" | "refund_payment" | "delete_customer_card" | "cancel_qr_payment" | "cancel_order" | "cancel_point_payment_intent" | "delete_webhook";
|
|
2150
2213
|
type ToolName = "create_subscription" | "get_subscription_status" | "cancel_subscription" | "pause_subscription" | "resume_subscription" | "create_payment" | "get_payment" | "search_payments" | "cancel_payment" | "capture_payment" | "refund_payment" | "list_refunds" | "create_payment_preference" | "get_payment_preference" | "create_customer" | "find_customer_by_email" | "list_customer_cards" | "delete_customer_card" | "list_payment_methods" | "calculate_installments" | "get_account_info" | "charge_saved_card" | "create_qr_payment" | "cancel_qr_payment" | "create_subscription_plan" | "list_subscription_plans" | "update_subscription_plan" | "subscribe_to_plan" | "list_subscription_payments" | "create_store" | "list_stores" | "create_pos" | "list_pos" | "list_payment_disputes" | "get_dispute" | "list_identification_types" | "list_issuers" | "list_webhooks" | "create_webhook" | "update_webhook" | "delete_webhook" | "handle_webhook" | "oauth_authorize_url" | "oauth_exchange_code" | "oauth_refresh_token" | "create_order" | "get_order" | "update_order" | "capture_order" | "cancel_order" | "get_account_balance" | "list_account_movements" | "list_settlements" | "get_settlement" | "analyze_payment_3ds" | "get_test_cards" | "get_customer" | "update_customer" | "create_customer_card" | "get_customer_card" | "get_subscription_plan" | "update_subscription" | "search_subscriptions" | "get_refund" | "update_payment_preference" | "get_merchant_order" | "search_merchant_orders" | "update_merchant_order" | "get_store" | "update_store" | "delete_store" | "get_pos" | "update_pos" | "delete_pos" | "list_bank_accounts" | "register_bank_account" | "list_point_devices" | "update_point_device_mode" | "create_point_payment_intent" | "get_point_payment_intent" | "cancel_point_payment_intent" | "compute_marketplace_fee" | "explain_payment_status" | "mp_health_check" | "find_applicable_promos" | "confirm_3ds_challenge" | "search_payments_all" | "list_settlements_all" | "validate_tax_id";
|
|
2151
2214
|
/**
|
|
2152
2215
|
* Build a tool set for the Vercel AI SDK that exposes Mercado Pago to an
|
package/dist/index.d.ts
CHANGED
|
@@ -365,6 +365,12 @@ interface CreatePreapprovalParams {
|
|
|
365
365
|
backUrl: string;
|
|
366
366
|
/** Optional client-side identifier for the subscription. */
|
|
367
367
|
externalReference?: string;
|
|
368
|
+
/**
|
|
369
|
+
* Optional explicit idempotency key. If omitted, the client auto-generates
|
|
370
|
+
* a UUID v4 per call. Pass a deterministic key (e.g., hash of inputs) when
|
|
371
|
+
* you need cross-retry idempotency from the agent layer.
|
|
372
|
+
*/
|
|
373
|
+
idempotencyKey?: string;
|
|
368
374
|
}
|
|
369
375
|
/**
|
|
370
376
|
* The shape of an MP webhook notification body for `topic=preapproval`. MP's
|
|
@@ -734,6 +740,13 @@ interface CreatePreferenceParams {
|
|
|
734
740
|
marketplaceFee?: number;
|
|
735
741
|
/** Seller's MP user_id. Funds route here when set. */
|
|
736
742
|
collectorId?: string | number;
|
|
743
|
+
/**
|
|
744
|
+
* Optional explicit idempotency key. If omitted, the client auto-generates
|
|
745
|
+
* a UUID v4 per call. Pass a deterministic key (e.g., hash of items +
|
|
746
|
+
* external_reference) when you need cross-retry idempotency from the
|
|
747
|
+
* agent layer.
|
|
748
|
+
*/
|
|
749
|
+
idempotencyKey?: string;
|
|
737
750
|
}
|
|
738
751
|
declare const CustomerSchema: z.ZodObject<{
|
|
739
752
|
id: z.ZodString;
|
|
@@ -1332,6 +1345,14 @@ interface CreatePointPaymentIntentParams {
|
|
|
1332
1345
|
interface MercadoPagoClientOptions {
|
|
1333
1346
|
/** Access token. TEST- prefix for sandbox, APP_USR- for production. */
|
|
1334
1347
|
accessToken: string;
|
|
1348
|
+
/**
|
|
1349
|
+
* Escape hatch for browser-context tests (e.g., jsdom). MUST NOT be set
|
|
1350
|
+
* in production code — the constructor's browser-context check exists
|
|
1351
|
+
* specifically to prevent the access token from being bundled into a
|
|
1352
|
+
* client-side JavaScript bundle. The `__` prefix and explicit boolean
|
|
1353
|
+
* are deliberate friction so this never gets typed by accident.
|
|
1354
|
+
*/
|
|
1355
|
+
__allowBrowser?: boolean;
|
|
1335
1356
|
/**
|
|
1336
1357
|
* Override the API base URL. Mostly useful for tests against MSW or for
|
|
1337
1358
|
* pointing at a regional MP host. Defaults to https://api.mercadopago.com.
|
|
@@ -2146,7 +2167,49 @@ interface MercadoPagoToolsOptions {
|
|
|
2146
2167
|
* retries (which fire on 5xx and can deliver the same event 5+ times).
|
|
2147
2168
|
*/
|
|
2148
2169
|
webhookDedup?: WebhookDedup;
|
|
2170
|
+
/**
|
|
2171
|
+
* v0.15 — Programmatic Human-In-The-Loop gate for irreversible /
|
|
2172
|
+
* money-moving operations. When set, every call to one of the gated
|
|
2173
|
+
* tools (cancel_payment, capture_payment, refund_payment,
|
|
2174
|
+
* delete_customer_card, cancel_qr_payment, cancel_order,
|
|
2175
|
+
* cancel_point_payment_intent, delete_webhook) invokes this callback
|
|
2176
|
+
* BEFORE executing. Return `true` to proceed, `false` to reject the
|
|
2177
|
+
* call (the tool returns `{ ok: false, reason: "Confirmation declined" }`).
|
|
2178
|
+
*
|
|
2179
|
+
* The description-based HITL warnings still apply (they nudge the LLM
|
|
2180
|
+
* to confirm in-conversation), but those depend on the LLM's heuristic
|
|
2181
|
+
* and can be bypassed via prompt injection. This callback is the actual
|
|
2182
|
+
* out-of-band enforcement: wire it to your UI / Slack / email / SMS
|
|
2183
|
+
* confirmation flow so a human approves money-movement explicitly.
|
|
2184
|
+
*
|
|
2185
|
+
* @example
|
|
2186
|
+
* ```ts
|
|
2187
|
+
* mercadoPagoTools(client, {
|
|
2188
|
+
* state, backUrl,
|
|
2189
|
+
* requireConfirmation: async (op, args) => {
|
|
2190
|
+
* // Send a Slack DM to the operator with the operation summary
|
|
2191
|
+
* // and wait for their button click. Throw or return false to reject.
|
|
2192
|
+
* return await slack.confirm({
|
|
2193
|
+
* channel: "#mp-approvals",
|
|
2194
|
+
* text: `Refund $${args.amount ?? "FULL"} on payment ${args.payment_id}?`,
|
|
2195
|
+
* timeoutMs: 60_000,
|
|
2196
|
+
* });
|
|
2197
|
+
* },
|
|
2198
|
+
* });
|
|
2199
|
+
* ```
|
|
2200
|
+
*
|
|
2201
|
+
* If omitted (default), the description-based HITL is the only line of
|
|
2202
|
+
* defense — fine for trusted/internal agents, NOT recommended for
|
|
2203
|
+
* untrusted-input agents (anything reading from a public webhook).
|
|
2204
|
+
*/
|
|
2205
|
+
requireConfirmation?: (operation: GatedOperation, args: Record<string, unknown>) => Promise<boolean>;
|
|
2149
2206
|
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Tool names that go through `requireConfirmation` when configured.
|
|
2209
|
+
* Adding a new irreversible operation? Add it here AND in the
|
|
2210
|
+
* `applyConfirmationGate` wrapper at the bottom of this file.
|
|
2211
|
+
*/
|
|
2212
|
+
type GatedOperation = "cancel_payment" | "capture_payment" | "refund_payment" | "delete_customer_card" | "cancel_qr_payment" | "cancel_order" | "cancel_point_payment_intent" | "delete_webhook";
|
|
2150
2213
|
type ToolName = "create_subscription" | "get_subscription_status" | "cancel_subscription" | "pause_subscription" | "resume_subscription" | "create_payment" | "get_payment" | "search_payments" | "cancel_payment" | "capture_payment" | "refund_payment" | "list_refunds" | "create_payment_preference" | "get_payment_preference" | "create_customer" | "find_customer_by_email" | "list_customer_cards" | "delete_customer_card" | "list_payment_methods" | "calculate_installments" | "get_account_info" | "charge_saved_card" | "create_qr_payment" | "cancel_qr_payment" | "create_subscription_plan" | "list_subscription_plans" | "update_subscription_plan" | "subscribe_to_plan" | "list_subscription_payments" | "create_store" | "list_stores" | "create_pos" | "list_pos" | "list_payment_disputes" | "get_dispute" | "list_identification_types" | "list_issuers" | "list_webhooks" | "create_webhook" | "update_webhook" | "delete_webhook" | "handle_webhook" | "oauth_authorize_url" | "oauth_exchange_code" | "oauth_refresh_token" | "create_order" | "get_order" | "update_order" | "capture_order" | "cancel_order" | "get_account_balance" | "list_account_movements" | "list_settlements" | "get_settlement" | "analyze_payment_3ds" | "get_test_cards" | "get_customer" | "update_customer" | "create_customer_card" | "get_customer_card" | "get_subscription_plan" | "update_subscription" | "search_subscriptions" | "get_refund" | "update_payment_preference" | "get_merchant_order" | "search_merchant_orders" | "update_merchant_order" | "get_store" | "update_store" | "delete_store" | "get_pos" | "update_pos" | "delete_pos" | "list_bank_accounts" | "register_bank_account" | "list_point_devices" | "update_point_device_mode" | "create_point_payment_intent" | "get_point_payment_intent" | "cancel_point_payment_intent" | "compute_marketplace_fee" | "explain_payment_status" | "mp_health_check" | "find_applicable_promos" | "confirm_3ds_challenge" | "search_payments_all" | "list_settlements_all" | "validate_tax_id";
|
|
2151
2214
|
/**
|
|
2152
2215
|
* Build a tool set for the Vercel AI SDK that exposes Mercado Pago to an
|
package/dist/index.js
CHANGED
|
@@ -253,6 +253,12 @@ var MercadoPagoClient = class {
|
|
|
253
253
|
circuitBreaker;
|
|
254
254
|
traceContext;
|
|
255
255
|
constructor(options) {
|
|
256
|
+
const w = globalThis.window;
|
|
257
|
+
if (typeof w !== "undefined" && !options.__allowBrowser) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
"MercadoPagoClient must NEVER be instantiated in a browser context \u2014 the access token would be exposed in the JavaScript bundle and visible to anyone in DevTools. Use a Server Component, Route Handler, Server Action, or any server-only execution context. If you're running in jsdom for tests and KNOW this is safe, pass `__allowBrowser: true`."
|
|
260
|
+
);
|
|
261
|
+
}
|
|
256
262
|
if (!options.accessToken) {
|
|
257
263
|
throw new Error(
|
|
258
264
|
"MercadoPagoClient requires an accessToken. Get one from https://www.mercadopago.com.ar/developers/panel/credentials"
|
|
@@ -451,7 +457,10 @@ var MercadoPagoClient = class {
|
|
|
451
457
|
currency_id: params.currency
|
|
452
458
|
}
|
|
453
459
|
},
|
|
454
|
-
{
|
|
460
|
+
{
|
|
461
|
+
...params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : {},
|
|
462
|
+
classifyContext: { payerEmail: params.payerEmail }
|
|
463
|
+
}
|
|
455
464
|
);
|
|
456
465
|
}
|
|
457
466
|
async getPreapproval(id) {
|
|
@@ -647,7 +656,12 @@ var MercadoPagoClient = class {
|
|
|
647
656
|
if (params.marketplace) body.marketplace = params.marketplace;
|
|
648
657
|
if (params.marketplaceFee !== void 0) body.marketplace_fee = params.marketplaceFee;
|
|
649
658
|
if (params.collectorId !== void 0) body.collector_id = params.collectorId;
|
|
650
|
-
return this.request(
|
|
659
|
+
return this.request(
|
|
660
|
+
"POST",
|
|
661
|
+
"/checkout/preferences",
|
|
662
|
+
body,
|
|
663
|
+
params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : void 0
|
|
664
|
+
);
|
|
651
665
|
}
|
|
652
666
|
async getPreference(id) {
|
|
653
667
|
return this.request("GET", `/checkout/preferences/${id}`);
|
|
@@ -3014,7 +3028,8 @@ async function verifyWebhookSignature(params) {
|
|
|
3014
3028
|
|
|
3015
3029
|
// src/tools.ts
|
|
3016
3030
|
async function deterministicIdempotencyKey(...parts) {
|
|
3017
|
-
const
|
|
3031
|
+
const filtered = parts.filter((p) => p !== void 0 && p !== null).map(String);
|
|
3032
|
+
const payload = filtered.map((p) => `${p.length}:${p}`).join("|");
|
|
3018
3033
|
return (await sha256Hex(payload)).slice(0, 32);
|
|
3019
3034
|
}
|
|
3020
3035
|
var DEFAULT_DESCRIPTIONS = {
|
|
@@ -3028,10 +3043,10 @@ var DEFAULT_DESCRIPTIONS = {
|
|
|
3028
3043
|
create_payment: "Create a one-time payment. Two flows: (a) with a card token from MP frontend Cardform \u2014 for transparent checkout; (b) without token, for non-card methods like 'account_money', 'rapipago', 'pagofacil'. For most agent flows where you only have a payer email and want to send them a payment link, use create_payment_preference instead (Checkout Pro hosted form). Returns the Payment object with status \u2014 typically 'approved' for account_money and 'pending' for tickets.",
|
|
3029
3044
|
get_payment: "Fetch a single payment by ID. Use to confirm status after webhook arrives, or to inspect details (status_detail explains rejections).",
|
|
3030
3045
|
search_payments: "Search payments with filters. Most common: by external_reference (your-system identifier) to find all payments for an order, or by status='approved' to list successful charges in a date range. Returns paginated results.",
|
|
3031
|
-
cancel_payment: "Cancel a pending or in_process payment (only works before approval). Once approved, use refund_payment instead. Common use: cancel an unpaid ticket payment that's still pending.",
|
|
3032
|
-
capture_payment: "Capture an authorized credit-card payment that was created with capture=false. Use for hold-then-capture flows (e.g., authorize on order, capture on shipment). Optional partial amount.",
|
|
3046
|
+
cancel_payment: "Cancel a pending or in_process payment (only works before approval). Once approved, use refund_payment instead. Common use: cancel an unpaid ticket payment that's still pending. **IRREVERSIBLE \u2014 confirm with the user before calling. Surface the payment_id, amount, payer_email, and current status, ask 's\xED, cancel\xE1' (or equivalent), then proceed.**",
|
|
3047
|
+
capture_payment: "Capture an authorized credit-card payment that was created with capture=false. Use for hold-then-capture flows (e.g., authorize on order, capture on shipment). Optional partial amount. **MOVES MONEY \u2014 confirm the amount with the user before calling.**",
|
|
3033
3048
|
// ── Refunds ──────────────────────────────────────────────────────────────
|
|
3034
|
-
refund_payment: "Refund an approved payment. Pass amount for partial refund; omit for full refund. Idempotency key is auto-generated based on paymentId+amount to prevent double-refunds on retries.",
|
|
3049
|
+
refund_payment: "Refund an approved payment. Pass amount for partial refund; omit for full refund. Idempotency key is auto-generated based on paymentId+amount to prevent double-refunds on retries. **IRREVERSIBLE AND MOVES MONEY \u2014 confirm with the user before calling. Restate the payment_id, the refund amount (full vs partial), and ask explicit confirmation. Mercado Pago does not support 'undo refund' \u2014 once issued, the buyer's bank releases the funds.**",
|
|
3035
3050
|
list_refunds: "List all refunds for a given payment. Returns array of Refund objects. Useful to confirm a refund was processed or to inspect partial-refund history.",
|
|
3036
3051
|
// ── Checkout Pro ─────────────────────────────────────────────────────────
|
|
3037
3052
|
create_payment_preference: "Create a Mercado Pago Checkout Pro preference and get back a payment URL (init_point) to send to the customer. THIS is the recommended way for an agent to take a payment when you only have a payer email \u2014 the buyer enters card data on MP's hosted form (no PCI scope needed). Supports cuotas configuration, payment method exclusions, back URLs after success/failure/pending. In sandbox, use sandbox_init_point from the response.",
|
|
@@ -3040,7 +3055,7 @@ var DEFAULT_DESCRIPTIONS = {
|
|
|
3040
3055
|
create_customer: "Create a Mercado Pago customer record so the buyer can save cards for future charges. Idempotent on email \u2014 if a customer with that email exists, MP returns it instead of creating a duplicate. Use find_customer_by_email first if you're unsure.",
|
|
3041
3056
|
find_customer_by_email: "Find an existing customer by email address. Returns the customer object if found, or null. Use before create_customer to avoid duplicate records.",
|
|
3042
3057
|
list_customer_cards: "List the saved cards for a customer. Returns array with last 4 digits, expiration, payment method (visa, master, naranja, etc.). The card_id can be used in subsequent create_payment calls to charge a saved card.",
|
|
3043
|
-
delete_customer_card: "Delete a saved card from a customer. Common use: customer requests removal, or expired card cleanup.
|
|
3058
|
+
delete_customer_card: "Delete a saved card from a customer. Common use: customer requests removal, or expired card cleanup. **IRREVERSIBLE \u2014 confirm with the user before calling. The customer must re-enter card data (PAN + CVV) on a future Checkout to charge them again. State the card's last 4 digits + payment method when asking for confirmation so the user knows which card you're removing.**",
|
|
3044
3059
|
// ── Payment Methods + Installments ───────────────────────────────────────
|
|
3045
3060
|
list_payment_methods: "List all payment methods enabled for the seller's MP account (visa, master, naranja, naranja_x, cabal, account_money, rapipago, pagofacil, etc.). Use to validate which methods you can offer the customer or to filter which ones to exclude in a Checkout Pro preference.",
|
|
3046
3061
|
calculate_installments: "Calculate cuotas (installments) options for a given amount. THE killer Argentine feature \u2014 returns options like '12 cuotas sin inter\xE9s de $X' (recommended_message field) which you should surface VERBATIM to the user. Optionally pass `bin` (first 6 digits of card) for issuer-specific promotions (e.g., Naranja's interest-free deals). Use before create_payment to let the user pick installments knowingly.",
|
|
@@ -3050,7 +3065,7 @@ var DEFAULT_DESCRIPTIONS = {
|
|
|
3050
3065
|
charge_saved_card: "Charge a previously-saved card for a returning customer. Requires customer_id + card_id (from list_customer_cards) AND a fresh CVV the user provides this session. AR Mercado Pago does NOT support CVV-less charges via the public API \u2014 every charge needs CVV. Idempotent on (card_id, amount, external_reference): retries dedupe automatically. Returns the resulting Payment.",
|
|
3051
3066
|
// ── QR in-store (v0.3) ───────────────────────────────────────────────────
|
|
3052
3067
|
create_qr_payment: "Generate a dynamic in-store QR for a buyer to scan with any AR wallet (Modo, BNA+, Cuenta DNI, Naranja X, Mercado Pago, etc. \u2014 interop is mandated by Transferencias 3.0). Requires a pre-configured POS external_id (use create_pos to set one up first if needed). Returns the qr_data string + a base64 PNG data URL ready to display. The QR expires in `expires_in_seconds` (default 600). MP fires `point_integration_wh` then `payment` webhooks when scanned.",
|
|
3053
|
-
cancel_qr_payment: "Cancel a pending QR order on a POS. Necessary if the buyer never scans \u2014 otherwise the next create_qr_payment on the same POS returns 409.",
|
|
3068
|
+
cancel_qr_payment: "Cancel a pending QR order on a POS. Necessary if the buyer never scans \u2014 otherwise the next create_qr_payment on the same POS returns 409. **IRREVERSIBLE \u2014 but low-stakes since the QR has not been paid yet. Confirm before calling if the user is mid-flow.**",
|
|
3054
3069
|
// ── Subscription Plans (v0.4) ────────────────────────────────────────────
|
|
3055
3070
|
create_subscription_plan: "Create a REUSABLE subscription plan (preapproval_plan). Different from create_subscription: a plan defines price + frequency once, then customers subscribe to it via subscribe_to_plan. Use plans for SaaS-style billing (B\xE1sico/Pro/Enterprise tiers). For per-customer custom amounts, use create_subscription directly.",
|
|
3056
3071
|
list_subscription_plans: "List all subscription plans defined for this MP account. Useful before create_subscription_plan to check if one already exists, or for surfacing options to a customer.",
|
|
@@ -3072,7 +3087,7 @@ var DEFAULT_DESCRIPTIONS = {
|
|
|
3072
3087
|
list_webhooks: "List all webhook subscriptions configured for this MP application. Use to see what topics + URLs are wired before adding new ones.",
|
|
3073
3088
|
create_webhook: "Subscribe a webhook URL to a MP topic (payment, subscription_authorized_payment, subscription_preapproval, merchant_order, point_integration_wh). MP will POST to this URL when events of that topic fire.",
|
|
3074
3089
|
update_webhook: "Update a webhook's URL or topic. Useful when you change deployment URLs without resubscribing from scratch.",
|
|
3075
|
-
delete_webhook: "Delete a webhook subscription. MP stops POSTing to it immediately.",
|
|
3090
|
+
delete_webhook: "Delete a webhook subscription. MP stops POSTing to it immediately. **IRREVERSIBLE \u2014 confirm before calling. State the webhook URL + topic so the user knows which subscription is being removed. Re-subscribing requires a new create_webhook call.**",
|
|
3076
3091
|
// ── Webhook handler combo (v0.5) ─────────────────────────────────────────
|
|
3077
3092
|
handle_webhook: "Process an incoming MP webhook in ONE call: verify the HMAC-SHA256 signature, parse the event, and (optionally) auto-fetch the underlying resource (Payment, Subscription, Order). Returns the structured event PLUS the full resource. USE THIS in your webhook endpoint INSTEAD of chaining verify_webhook_signature + parse_webhook_event + get_payment manually. Pass the raw request body, x-signature header, x-request-id header, and your MP webhook secret. SAFE: returns { verified: false } when signature mismatches \u2014 caller should respond 401 and stop processing. WHEN auto_fetch is true (default), the resource is fetched as the SAME MP user the client is configured for (so for marketplace integrations, instantiate a per-seller client).",
|
|
3078
3093
|
// ── OAuth Marketplace (v0.5) ─────────────────────────────────────────────
|
|
@@ -3084,7 +3099,7 @@ var DEFAULT_DESCRIPTIONS = {
|
|
|
3084
3099
|
get_order: "Fetch an Order by ID. Returns the Order with its lifecycle status and any attached payments/refunds.",
|
|
3085
3100
|
update_order: "Patch an existing Order before it's captured/canceled. Common use: update items or external_reference.",
|
|
3086
3101
|
capture_order: "Capture a previously-authorized Order (only for orders created with capture_mode='manual'). Captures up to the originally-authorized amount; pass amount for partial capture. Common use: ride-share marks ride complete \u2192 capture; hotel checks-out guest \u2192 capture.",
|
|
3087
|
-
cancel_order: "Cancel an Order. Releases any auth-holds and marks the Order as canceled. For orders that have already been CAPTURED, use refund_payment instead \u2014 cancel only works pre-capture.",
|
|
3102
|
+
cancel_order: "Cancel an Order. Releases any auth-holds and marks the Order as canceled. For orders that have already been CAPTURED, use refund_payment instead \u2014 cancel only works pre-capture. **IRREVERSIBLE \u2014 confirm with the user. State the order_id, total_amount, and current status before asking 's\xED, cancel\xE1'. The buyer's hold is released to their bank within 24-72h depending on issuer.**",
|
|
3088
3103
|
// ── Account / Balance / Movements / Settlements (v0.6) ───────────────────
|
|
3089
3104
|
get_account_balance: "Get the seller's current MP wallet balance. Returns { available_balance, unavailable_balance, total_amount, currency_id }. The available balance is what the seller can withdraw or pay with right now; unavailable is in retention (typically 14-21 days for new sellers or risk-flagged transactions). For per-seller marketplace setups, instantiate the client AS THE SELLER first.",
|
|
3090
3105
|
list_account_movements: "List wallet movements (incoming payments, transfers, refunds, holdings) for the active MP account. Filter by date range with `from`/`to` (ISO 8601). Useful for monthly conciliation or 'show me what came in this month' workflows.",
|
|
@@ -3124,7 +3139,7 @@ var DEFAULT_DESCRIPTIONS = {
|
|
|
3124
3139
|
update_point_device_mode: "Switch a Point device's operating_mode between 'PDV' (bound to a logical POS, takes payments triggered through that POS) and 'STANDALONE' (works independently, accepts any payment). PDV is for cash-register integrations; STANDALONE is for free-form retail. Affects how payments hit the device.",
|
|
3125
3140
|
create_point_payment_intent: "Create a payment intent on a physical Point device \u2014 the device prompts the buyer to tap/insert/swipe their card. Returns immediately with intent_id; query state via get_point_payment_intent or wait for point_integration_wh webhook. **AMOUNT IS IN CENTAVOS**, NOT pesos (Point API differs from Payments API): 100 = $1, 1000 = $10, 10000 = $100.",
|
|
3126
3141
|
get_point_payment_intent: "Get the current state of a Point payment intent (OPEN, PROCESSING, FINISHED, CANCELED, ERROR). USE in polling loops if you can't wait for the webhook. When state=FINISHED, the intent.payment.id is the resulting Payment id usable with get_payment.",
|
|
3127
|
-
cancel_point_payment_intent: "Cancel an OPEN point payment intent before the buyer interacts with the device. ONLY WORKS while state='OPEN' \u2014 once the buyer taps, you can't cancel; refund_payment after the fact instead.",
|
|
3142
|
+
cancel_point_payment_intent: "Cancel an OPEN point payment intent before the buyer interacts with the device. ONLY WORKS while state='OPEN' \u2014 once the buyer taps, you can't cancel; refund_payment after the fact instead. **IRREVERSIBLE \u2014 confirm with the cashier/operator before calling. State the device_id and amount.**",
|
|
3128
3143
|
// ── Pure helpers (v0.7) ──────────────────────────────────────────────────
|
|
3129
3144
|
compute_marketplace_fee: "PURE HELPER (no network) \u2014 given a transaction amount + fee rule (% or flat ARS, with optional min/max floors), returns the exact `marketplace_fee` value in ARS to pass to create_order or create_payment_preference. USE WHEN your platform takes a commission and you need to compute the exact fee per transaction. Examples: { percent: 5, minArs: 50, maxArs: 5000 } for percentage with floor + cap; { flatArs: 200, percent: 2 } for fixed + percentage.",
|
|
3130
3145
|
explain_payment_status: "PURE HELPER (no network) \u2014 given a Payment object (from get_payment / create_payment / handle_webhook), returns { summary, recommendedAction, final, paid, retryable } in Spanish. Translates MP's cryptic status_detail codes to plain Spanish + actionable guidance ('reintentar con otra tarjeta' vs 'esperar webhook' vs 'estado final'). USE THIS instead of having to memorize 30+ status_detail codes \u2014 surface summary + recommendedAction directly to the user.",
|
|
@@ -3142,6 +3157,10 @@ var DEFAULT_DESCRIPTIONS = {
|
|
|
3142
3157
|
};
|
|
3143
3158
|
function mercadoPagoTools(client, options) {
|
|
3144
3159
|
const desc = (name) => options.descriptions?.[name] ?? DEFAULT_DESCRIPTIONS[name];
|
|
3160
|
+
const built = buildAllTools(client, options, desc);
|
|
3161
|
+
return options.requireConfirmation ? applyConfirmationGate(built, options.requireConfirmation) : built;
|
|
3162
|
+
}
|
|
3163
|
+
function buildAllTools(client, options, desc) {
|
|
3145
3164
|
return {
|
|
3146
3165
|
// ─────────────────────────────────────────────────────────────────────────
|
|
3147
3166
|
// Subscriptions (v0.1 — kept identical for backward compatibility)
|
|
@@ -3156,16 +3175,28 @@ function mercadoPagoTools(client, options) {
|
|
|
3156
3175
|
external_reference: z.string().optional().describe("Optional id from your system to track this subscription")
|
|
3157
3176
|
}),
|
|
3158
3177
|
execute: async ({ customer_email, amount_ars, frequency_months, reason, external_reference }) => {
|
|
3159
|
-
const created = await client.createPreapproval(
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3178
|
+
const created = await client.createPreapproval(
|
|
3179
|
+
{
|
|
3180
|
+
reason,
|
|
3181
|
+
payerEmail: customer_email,
|
|
3182
|
+
amount: amount_ars,
|
|
3183
|
+
currency: "ARS",
|
|
3184
|
+
frequency: frequency_months,
|
|
3185
|
+
frequencyType: "months",
|
|
3186
|
+
backUrl: options.backUrl,
|
|
3187
|
+
...external_reference !== void 0 ? { externalReference: external_reference } : {},
|
|
3188
|
+
// Deterministic idempotency — if the LLM retries this tool call
|
|
3189
|
+
// with the same inputs (e.g., timeout + retry), MP returns the
|
|
3190
|
+
// EXISTING subscription instead of creating a duplicate.
|
|
3191
|
+
idempotencyKey: await deterministicIdempotencyKey(
|
|
3192
|
+
"create_subscription",
|
|
3193
|
+
customer_email,
|
|
3194
|
+
amount_ars,
|
|
3195
|
+
frequency_months,
|
|
3196
|
+
external_reference
|
|
3197
|
+
)
|
|
3198
|
+
}
|
|
3199
|
+
);
|
|
3169
3200
|
await options.state.set(created.id, {
|
|
3170
3201
|
status: created.status,
|
|
3171
3202
|
payerEmail: customer_email,
|
|
@@ -3372,9 +3403,9 @@ function mercadoPagoTools(client, options) {
|
|
|
3372
3403
|
inputSchema: z.object({
|
|
3373
3404
|
external_reference: z.string().optional(),
|
|
3374
3405
|
status: z.string().optional().describe("'approved' | 'pending' | 'rejected' | 'cancelled' | 'refunded' etc."),
|
|
3375
|
-
payer_email: z.string().optional(),
|
|
3376
|
-
begin_date: z.string().optional().describe("ISO 8601, e.g. 2026-01-01T00:00:00Z"),
|
|
3377
|
-
end_date: z.string().optional().describe("ISO 8601"),
|
|
3406
|
+
payer_email: z.string().email().optional().describe("Filter by payer email (exact match)."),
|
|
3407
|
+
begin_date: z.string().datetime().optional().describe("ISO 8601, e.g. 2026-01-01T00:00:00Z"),
|
|
3408
|
+
end_date: z.string().datetime().optional().describe("ISO 8601"),
|
|
3378
3409
|
limit: z.number().int().min(1).max(100).optional().describe("Default 30, max 100"),
|
|
3379
3410
|
offset: z.number().int().min(0).optional().describe("Pagination offset (default 0)")
|
|
3380
3411
|
}),
|
|
@@ -3492,28 +3523,36 @@ function mercadoPagoTools(client, options) {
|
|
|
3492
3523
|
excluded_payment_types: z.array(z.enum(["credit_card", "debit_card", "ticket", "atm", "bank_transfer"])).optional().describe("Block payment types \u2014 e.g., ['ticket'] to disable Rapipago/Pago F\xE1cil")
|
|
3493
3524
|
}),
|
|
3494
3525
|
execute: async (input) => {
|
|
3495
|
-
const
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3526
|
+
const idemKey = await deterministicIdempotencyKey(
|
|
3527
|
+
"create_payment_preference",
|
|
3528
|
+
input.external_reference ?? input.payer_email ?? "",
|
|
3529
|
+
input.items.map((it) => `${it.title}:${it.quantity}:${it.unit_price}`).join("|")
|
|
3530
|
+
);
|
|
3531
|
+
const pref = await client.createPreference(
|
|
3532
|
+
{
|
|
3533
|
+
items: input.items.map((it) => ({
|
|
3534
|
+
title: it.title,
|
|
3535
|
+
quantity: it.quantity,
|
|
3536
|
+
unit_price: it.unit_price,
|
|
3537
|
+
currency_id: "ARS",
|
|
3538
|
+
...it.description !== void 0 ? { description: it.description } : {},
|
|
3539
|
+
...it.picture_url !== void 0 ? { picture_url: it.picture_url } : {}
|
|
3540
|
+
})),
|
|
3541
|
+
...input.payer_email !== void 0 ? { payer: { email: input.payer_email } } : {},
|
|
3542
|
+
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
3543
|
+
...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
|
|
3544
|
+
backUrls: { success: options.backUrl, failure: options.backUrl, pending: options.backUrl },
|
|
3545
|
+
autoReturn: "approved",
|
|
3546
|
+
...options.notificationUrl !== void 0 ? { notificationUrl: options.notificationUrl } : {},
|
|
3547
|
+
...input.max_installments !== void 0 || input.excluded_payment_types !== void 0 ? {
|
|
3548
|
+
paymentMethods: {
|
|
3549
|
+
...input.max_installments !== void 0 ? { installments: input.max_installments } : {},
|
|
3550
|
+
...input.excluded_payment_types !== void 0 ? { excluded_payment_types: input.excluded_payment_types.map((id) => ({ id })) } : {}
|
|
3551
|
+
}
|
|
3552
|
+
} : {},
|
|
3553
|
+
idempotencyKey: idemKey
|
|
3554
|
+
}
|
|
3555
|
+
);
|
|
3517
3556
|
return {
|
|
3518
3557
|
preference_id: pref.id,
|
|
3519
3558
|
init_point_url: pref.init_point ?? null,
|
|
@@ -4235,6 +4274,22 @@ function mercadoPagoTools(client, options) {
|
|
|
4235
4274
|
resource: null
|
|
4236
4275
|
};
|
|
4237
4276
|
}
|
|
4277
|
+
if (options.webhookDedup && request_id_header) {
|
|
4278
|
+
const { shouldProcess } = await options.webhookDedup.check({
|
|
4279
|
+
topic: event.topic,
|
|
4280
|
+
dataId: event.dataId,
|
|
4281
|
+
requestId: request_id_header
|
|
4282
|
+
});
|
|
4283
|
+
if (!shouldProcess) {
|
|
4284
|
+
return {
|
|
4285
|
+
verified: true,
|
|
4286
|
+
deduplicated: true,
|
|
4287
|
+
event,
|
|
4288
|
+
resource: null,
|
|
4289
|
+
resource_error: "Webhook is a duplicate (same topic+dataId+requestId seen recently). Side effects skipped."
|
|
4290
|
+
};
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4238
4293
|
let resource = null;
|
|
4239
4294
|
let resourceError = null;
|
|
4240
4295
|
if (auto_fetch) {
|
|
@@ -4733,8 +4788,17 @@ function mercadoPagoTools(client, options) {
|
|
|
4733
4788
|
update_merchant_order: tool({
|
|
4734
4789
|
description: desc("update_merchant_order"),
|
|
4735
4790
|
inputSchema: z.object({
|
|
4736
|
-
merchant_order_id: z.string(),
|
|
4737
|
-
|
|
4791
|
+
merchant_order_id: z.string().min(1),
|
|
4792
|
+
// Narrow to MP's documented merchant_order PATCH fields. Previously
|
|
4793
|
+
// this was z.record(z.string(), z.unknown()) which let the LLM pass
|
|
4794
|
+
// arbitrary JSON; MP would silently ignore unknown keys, masking
|
|
4795
|
+
// typos. Strict schema = LLM gets a clear validation error instead.
|
|
4796
|
+
patch: z.object({
|
|
4797
|
+
external_reference: z.string().max(256).optional().describe("Your system's id for this order. Updateable while order is open."),
|
|
4798
|
+
notification_url: z.string().url().optional().describe("Where MP sends webhook notifications for this order."),
|
|
4799
|
+
additional_info: z.string().max(600).optional().describe("Free-form metadata stored alongside the order."),
|
|
4800
|
+
status: z.enum(["opened", "closed", "expired"]).optional().describe("Open orders can be closed (final) or expired (cleanup).")
|
|
4801
|
+
}).strict().describe("Subset of merchant_order fields to update. Unknown keys rejected.")
|
|
4738
4802
|
}),
|
|
4739
4803
|
execute: async ({ merchant_order_id, patch }) => {
|
|
4740
4804
|
return client.updateMerchantOrder(merchant_order_id, patch);
|
|
@@ -4953,8 +5017,24 @@ function mercadoPagoTools(client, options) {
|
|
|
4953
5017
|
explain_payment_status: tool({
|
|
4954
5018
|
description: desc("explain_payment_status"),
|
|
4955
5019
|
inputSchema: z.object({
|
|
4956
|
-
payment_id: z.string().optional().describe(
|
|
4957
|
-
|
|
5020
|
+
payment_id: z.string().min(1).optional().describe(
|
|
5021
|
+
"If provided, fetches the Payment first. RECOMMENDED PATH for agents \u2014 pass the id and let the lib fetch."
|
|
5022
|
+
),
|
|
5023
|
+
// Loose object kept for advanced manual callers that have already
|
|
5024
|
+
// fetched a Payment from another path. LLMs should prefer payment_id.
|
|
5025
|
+
// We don't strictly type the Payment shape here because MP's actual
|
|
5026
|
+
// response includes 100+ optional fields; the helper consumes only
|
|
5027
|
+
// status / status_detail / payment_method_id / etc. Exposed loosely
|
|
5028
|
+
// for ergonomic interop, NOT for LLM use.
|
|
5029
|
+
payment: z.object({
|
|
5030
|
+
id: z.union([z.string(), z.number()]).optional(),
|
|
5031
|
+
status: z.string().optional(),
|
|
5032
|
+
status_detail: z.string().optional(),
|
|
5033
|
+
payment_method_id: z.string().optional(),
|
|
5034
|
+
transaction_amount: z.number().optional()
|
|
5035
|
+
}).passthrough().optional().describe(
|
|
5036
|
+
"ADVANCED \u2014 pass a pre-fetched Payment object to skip the network call. LLMs: use payment_id instead."
|
|
5037
|
+
)
|
|
4958
5038
|
}),
|
|
4959
5039
|
execute: async ({ payment_id, payment }) => {
|
|
4960
5040
|
let p;
|
|
@@ -5102,6 +5182,43 @@ function mercadoPagoTools(client, options) {
|
|
|
5102
5182
|
})
|
|
5103
5183
|
};
|
|
5104
5184
|
}
|
|
5185
|
+
var GATED_TOOL_NAMES = [
|
|
5186
|
+
"cancel_payment",
|
|
5187
|
+
"capture_payment",
|
|
5188
|
+
"refund_payment",
|
|
5189
|
+
"delete_customer_card",
|
|
5190
|
+
"cancel_qr_payment",
|
|
5191
|
+
"cancel_order",
|
|
5192
|
+
"cancel_point_payment_intent",
|
|
5193
|
+
"delete_webhook"
|
|
5194
|
+
];
|
|
5195
|
+
function applyConfirmationGate(tools, requireConfirmation) {
|
|
5196
|
+
const wrapped = { ...tools };
|
|
5197
|
+
for (const name of GATED_TOOL_NAMES) {
|
|
5198
|
+
const original = tools[name];
|
|
5199
|
+
if (!original) continue;
|
|
5200
|
+
wrapped[name] = {
|
|
5201
|
+
...original,
|
|
5202
|
+
execute: async (input, ctx) => {
|
|
5203
|
+
const args = input ?? {};
|
|
5204
|
+
const approved = await requireConfirmation(name, args);
|
|
5205
|
+
if (!approved) {
|
|
5206
|
+
return {
|
|
5207
|
+
ok: false,
|
|
5208
|
+
reason: "Confirmation declined by requireConfirmation gate.",
|
|
5209
|
+
operation: name,
|
|
5210
|
+
args
|
|
5211
|
+
};
|
|
5212
|
+
}
|
|
5213
|
+
return await original.execute(
|
|
5214
|
+
input,
|
|
5215
|
+
ctx
|
|
5216
|
+
);
|
|
5217
|
+
}
|
|
5218
|
+
};
|
|
5219
|
+
}
|
|
5220
|
+
return wrapped;
|
|
5221
|
+
}
|
|
5105
5222
|
|
|
5106
5223
|
// src/state.ts
|
|
5107
5224
|
var InMemoryStateAdapter = class {
|