Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions solutions/mercadopago-agent-toolkit/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Mercado Pago access token from https://www.mercadopago.com.ar/developers/panel/app
# Use TEST- prefix for sandbox, APP_USR- for production.
MP_ACCESS_TOKEN=TEST-...

# Vercel AI Gateway API key from https://vercel.com/dashboard/ai-gateway
# The Gateway routes the LLM call without exposing your Anthropic key to the client.
AI_GATEWAY_API_KEY=

# HTTPS URL where MP redirects buyers after the first payment.
# In dev, use a stable tunnel (e.g. https://your-tunnel.ngrok-free.app/done).
NEXT_PUBLIC_BACK_URL=https://example.com/done
42 changes: 42 additions & 0 deletions solutions/mercadopago-agent-toolkit/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files
.env*.local
.env

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
87 changes: 87 additions & 0 deletions solutions/mercadopago-agent-toolkit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
name: Mercado Pago Agent Toolkit
slug: mercadopago-agent-toolkit
description: A Next.js app where an agent drives Mercado Pago billing flows (Subscriptions, Payments, Refunds, Cuotas) via @ar-agents/mercadopago tools, Vercel AI SDK 6, and AI Gateway.
framework: Next.js
useCase:
- AI
- Edge Functions
- Agents
css: CSS Modules
deployUrl: https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fexamples%2Ftree%2Fmain%2Fsolutions%2Fmercadopago-agent-toolkit&project-name=mercadopago-agent-toolkit&repository-name=mercadopago-agent-toolkit&env=MP_ACCESS_TOKEN,AI_GATEWAY_API_KEY,NEXT_PUBLIC_BACK_URL&envDescription=API%20keys%20needed%20to%20run%20the%20agent&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fexamples%2Ftree%2Fmain%2Fsolutions%2Fmercadopago-agent-toolkit%23environment-variables
demoUrl: https://ar-agents.vercel.app
---

# Mercado Pago Agent Toolkit

A Next.js app where an agent drives [Mercado Pago](https://www.mercadopago.com.ar/developers) billing flows via [`@ar-agents/mercadopago`](https://www.npmjs.com/package/@ar-agents/mercadopago) tools, [Vercel AI SDK 6](https://ai-sdk.dev/), and [AI Gateway](https://vercel.com/docs/ai-gateway).

The agent picks the right tool from a natural-language prompt:

- "Cobrale $25.000 mensual a juan@example.com con razón Plan Pro." → `create_subscription` returns an `init_point_url` you send to the customer.
- "Buscá pagos de los últimos 30 días por más de $5.000." → `search_payments` with date + amount filters.
- "Reembolsame el último pago de juan@example.com." → confirms then calls `refund_payment`.

## Demo

[ar-agents.vercel.app](https://ar-agents.vercel.app)

## How it works

`@ar-agents/mercadopago` ships 89 typed tools across the agent-relevant Mercado Pago API surface (Subscriptions, Payments, Checkout Pro, Marketplace OAuth, Order Management, Customers, Cards, Cuotas, QR, 3DS, Point devices, Stores+POS, Account/Balance/Settlements, Webhooks, Disputes, Lookups, Bank Accounts).

The example wires those tools into `streamText` from `ai`, picks `anthropic/claude-sonnet-4-6` via the AI Gateway (so the Anthropic key never reaches the client), and renders the result with `useChat` from `@ai-sdk/react`.

```ts
// app/api/agent/route.ts
const client = new MercadoPagoClient({
accessToken: process.env.MP_ACCESS_TOKEN!,
});

const tools = mercadoPagoTools(client, {
state: new InMemoryStateAdapter(),
backUrl: process.env.NEXT_PUBLIC_BACK_URL!,
});

const result = streamText({
model: "anthropic/claude-sonnet-4-6",
messages: await convertToModelMessages(messages),
tools,
stopWhen: stepCountIs(8),
});
```

Every `POST` against the Mercado Pago API gets an auto-generated idempotency key, so retries do not double-charge. Webhook signature verification (HMAC plus 5-minute replay window), circuit breaker, deadline propagation, and OpenTelemetry instrumentation are available in the same package via subpath imports.

## Environment variables

Copy `.env.example` to `.env.local` and fill in:

| Variable | Required | Where to get it |
| --- | --- | --- |
| `MP_ACCESS_TOKEN` | yes | [Mercado Pago dev panel](https://www.mercadopago.com.ar/developers/panel/app). Use a `TEST-` token for sandbox. |
| `AI_GATEWAY_API_KEY` | yes | [Vercel AI Gateway](https://vercel.com/dashboard/ai-gateway). Required so the LLM call routes through the Gateway and the Anthropic key is never bundled. |
| `NEXT_PUBLIC_BACK_URL` | yes | An HTTPS URL where MP redirects buyers after the first payment. In dev, use a stable tunnel (ngrok, cloudflared, etc.). |

## Run locally

```bash
pnpm install
cp .env.example .env.local
# fill in the env vars above
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000), pick a suggested prompt, and watch the agent drive the flow.

## Deploy

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fexamples%2Ftree%2Fmain%2Fsolutions%2Fmercadopago-agent-toolkit&project-name=mercadopago-agent-toolkit&repository-name=mercadopago-agent-toolkit&env=MP_ACCESS_TOKEN,AI_GATEWAY_API_KEY,NEXT_PUBLIC_BACK_URL)

## Learn more

- [`@ar-agents/mercadopago` README](https://github.com/ar-agents/ar-agents/blob/main/packages/mercadopago/README.md)
- [`@ar-agents/mercadopago` AGENTS.md](https://github.com/ar-agents/ar-agents/blob/main/packages/mercadopago/AGENTS.md): tool decision tree, result schemas, error patterns
- [Cookbook](https://github.com/ar-agents/ar-agents/tree/main/packages/mercadopago/cookbook): 9 production recipes (Checkout Pro, SaaS subscription, webhook handler, marketplace OAuth, QR in-store, 3DS challenge, manual capture, recovery patterns, OpenTelemetry)
- [Vercel AI SDK](https://ai-sdk.dev)
- [Vercel AI Gateway](https://vercel.com/docs/ai-gateway)
41 changes: 41 additions & 0 deletions solutions/mercadopago-agent-toolkit/app/api/agent/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
MercadoPagoClient,
mercadoPagoTools,
InMemoryStateAdapter,
} from "@ar-agents/mercadopago";
import {
convertToModelMessages,
stepCountIs,
streamText,
type UIMessage,
} from "ai";

export const runtime = "edge";

const client = new MercadoPagoClient({
accessToken: process.env.MP_ACCESS_TOKEN!,
});

const tools = mercadoPagoTools(client, {
state: new InMemoryStateAdapter(),
backUrl: process.env.NEXT_PUBLIC_BACK_URL ?? "https://example.com/done",
});

export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();

const result = streamText({
// Vercel AI Gateway routes by string model name; no provider package needed.
model: "anthropic/claude-sonnet-4-6",
system:
"Sos un asistente de billing para una SaaS argentina. " +
"Usás los tools de @ar-agents/mercadopago para crear suscripciones, " +
"cobrar pagos, y consultar estado. Respondé en español rioplatense, " +
"sin emojis, breve.",
messages: await convertToModelMessages(messages),
tools,
stopWhen: stepCountIs(8),
});

return result.toUIMessageStreamResponse();
}
26 changes: 26 additions & 0 deletions solutions/mercadopago-agent-toolkit/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Metadata } from "next";
import type { ReactNode } from "react";

export const metadata: Metadata = {
title: "Mercado Pago Agent Toolkit",
description:
"Agent that drives Mercado Pago billing flows via @ar-agents/mercadopago tools and Vercel AI SDK 6.",
};

export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body
style={{
margin: 0,
fontFamily:
"ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
background: "#0a0a0a",
color: "#ededed",
}}
>
{children}
</body>
</html>
);
}
163 changes: 163 additions & 0 deletions solutions/mercadopago-agent-toolkit/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
"use client";

import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { useState } from "react";

export default function Home() {
const [transport] = useState(
() => new DefaultChatTransport({ api: "/api/agent" }),
);
const { messages, sendMessage, status } = useChat({ transport });
const [input, setInput] = useState("");

const send = (text: string) => {
if (!text.trim() || status === "submitted" || status === "streaming") return;
sendMessage({ text });
setInput("");
};

return (
<main
style={{
maxWidth: 720,
margin: "0 auto",
padding: "32px 20px 120px",
minHeight: "100vh",
}}
>
<header style={{ marginBottom: 24 }}>
<h1 style={{ fontSize: 28, margin: 0, letterSpacing: "-0.02em" }}>
Mercado Pago Agent Toolkit
</h1>
<p style={{ color: "#888", marginTop: 8 }}>
Agent that drives Mercado Pago billing flows via{" "}
<a
href="https://www.npmjs.com/package/@ar-agents/mercadopago"
target="_blank"
rel="noreferrer"
style={{ color: "#00bcff" }}
>
@ar-agents/mercadopago
</a>{" "}
tools, Vercel AI SDK 6, and AI Gateway.
</p>
</header>

<section style={{ marginBottom: 16, display: "flex", gap: 8, flexWrap: "wrap" }}>
{[
"Cobrale $25.000 mensual a juan@example.com con razón Plan Pro.",
"Buscá pagos de los últimos 30 días por más de $5.000.",
"Reembolsame el último pago de juan@example.com.",
].map((s) => (
<button
key={s}
onClick={() => send(s)}
disabled={status === "submitted" || status === "streaming"}
style={{
fontSize: 13,
padding: "6px 12px",
borderRadius: 999,
border: "1px solid #333",
background: "#171717",
color: "#ddd",
cursor: "pointer",
}}
>
{s}
</button>
))}
</section>

<div
style={{
border: "1px solid #222",
borderRadius: 12,
background: "#0f0f0f",
padding: 20,
minHeight: 360,
}}
>
{messages.length === 0 ? (
<p style={{ color: "#666", margin: 0 }}>
Pick a prompt above or write your own.
</p>
) : (
messages.map((m) => (
<div key={m.id} style={{ marginBottom: 18 }}>
<div style={{ fontSize: 12, color: "#888", marginBottom: 4 }}>
{m.role === "user" ? "You" : "Agent"}
</div>
<div style={{ whiteSpace: "pre-wrap", lineHeight: 1.5 }}>
{m.parts
.filter((p) => p.type === "text")
.map((p, i) => (
<span key={i}>{p.text}</span>
))}
</div>
{m.parts
.filter((p) => p.type.startsWith("tool-"))
.map((p, i) => (
<div
key={`t-${i}`}
style={{
marginTop: 8,
padding: "8px 12px",
borderRadius: 8,
background: "#171717",
fontSize: 12,
color: "#aaa",
fontFamily: "ui-monospace, SFMono-Regular, monospace",
}}
>
{p.type.replace(/^tool-/, "")}
</div>
))}
</div>
))
)}
</div>

<form
onSubmit={(e) => {
e.preventDefault();
send(input);
}}
style={{ marginTop: 16, display: "flex", gap: 8 }}
>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Cobrale $X a Y@example.com..."
style={{
flex: 1,
padding: "10px 14px",
borderRadius: 8,
border: "1px solid #333",
background: "#0f0f0f",
color: "#ededed",
fontSize: 14,
}}
/>
<button
type="submit"
disabled={
!input.trim() || status === "submitted" || status === "streaming"
}
style={{
padding: "10px 18px",
borderRadius: 8,
border: "1px solid #00bcff",
background: "#00bcff",
color: "#000",
fontSize: 14,
cursor: "pointer",
fontWeight: 600,
}}
>
Send
</button>
</form>
</main>
);
}
11 changes: 11 additions & 0 deletions solutions/mercadopago-agent-toolkit/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
experimental: {
serverActions: {
bodySizeLimit: "1mb",
},
},
};

export default nextConfig;
Loading