diff --git a/solutions/mercadopago-agent-toolkit/.env.example b/solutions/mercadopago-agent-toolkit/.env.example new file mode 100644 index 0000000000..fe0e398785 --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/.env.example @@ -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 diff --git a/solutions/mercadopago-agent-toolkit/.gitignore b/solutions/mercadopago-agent-toolkit/.gitignore new file mode 100644 index 0000000000..c21489df93 --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/.gitignore @@ -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 diff --git a/solutions/mercadopago-agent-toolkit/README.md b/solutions/mercadopago-agent-toolkit/README.md new file mode 100644 index 0000000000..8fc3d6d58f --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/README.md @@ -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) diff --git a/solutions/mercadopago-agent-toolkit/app/api/agent/route.ts b/solutions/mercadopago-agent-toolkit/app/api/agent/route.ts new file mode 100644 index 0000000000..43a675ed7a --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/app/api/agent/route.ts @@ -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(); +} diff --git a/solutions/mercadopago-agent-toolkit/app/layout.tsx b/solutions/mercadopago-agent-toolkit/app/layout.tsx new file mode 100644 index 0000000000..cba740e294 --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/app/layout.tsx @@ -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 ( + + + {children} + + + ); +} diff --git a/solutions/mercadopago-agent-toolkit/app/page.tsx b/solutions/mercadopago-agent-toolkit/app/page.tsx new file mode 100644 index 0000000000..39e574bc9d --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/app/page.tsx @@ -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 ( +
+
+

+ Mercado Pago Agent Toolkit +

+

+ Agent that drives Mercado Pago billing flows via{" "} + + @ar-agents/mercadopago + {" "} + tools, Vercel AI SDK 6, and AI Gateway. +

+
+ +
+ {[ + "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) => ( + + ))} +
+ +
+ {messages.length === 0 ? ( +

+ Pick a prompt above or write your own. +

+ ) : ( + messages.map((m) => ( +
+
+ {m.role === "user" ? "You" : "Agent"} +
+
+ {m.parts + .filter((p) => p.type === "text") + .map((p, i) => ( + {p.text} + ))} +
+ {m.parts + .filter((p) => p.type.startsWith("tool-")) + .map((p, i) => ( +
+ {p.type.replace(/^tool-/, "")} +
+ ))} +
+ )) + )} +
+ +
{ + e.preventDefault(); + send(input); + }} + style={{ marginTop: 16, display: "flex", gap: 8 }} + > + 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, + }} + /> + +
+
+ ); +} diff --git a/solutions/mercadopago-agent-toolkit/next.config.ts b/solutions/mercadopago-agent-toolkit/next.config.ts new file mode 100644 index 0000000000..ef932d9918 --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/next.config.ts @@ -0,0 +1,11 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + experimental: { + serverActions: { + bodySizeLimit: "1mb", + }, + }, +}; + +export default nextConfig; diff --git a/solutions/mercadopago-agent-toolkit/package.json b/solutions/mercadopago-agent-toolkit/package.json new file mode 100644 index 0000000000..a9a245ea62 --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/package.json @@ -0,0 +1,29 @@ +{ + "name": "mercadopago-agent-toolkit", + "version": "0.1.0", + "private": true, + "license": "MIT", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@ai-sdk/react": "^3.0.0", + "@ar-agents/mercadopago": "^0.15.2", + "ai": "^6.0.175", + "next": "^16.2.4", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "zod": "^4.4.3" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "^16.2.4", + "typescript": "^5" + } +} diff --git a/solutions/mercadopago-agent-toolkit/tsconfig.json b/solutions/mercadopago-agent-toolkit/tsconfig.json new file mode 100644 index 0000000000..5ddf5a504c --- /dev/null +++ b/solutions/mercadopago-agent-toolkit/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +}