@pagosrd/mcp 0.0.0-stage → 0.1.1
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 +10 -0
- package/LICENSE +21 -0
- package/README.md +34 -2
- package/dist/index.js +193 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -4
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cristian Encarnación
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @pagosrd/mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Servidor MCP de PagosRD. Le permite a Claude (u otro cliente MCP) crear links de cobro, consultar pagos y reembolsar con la llave de API de tu plataforma.
|
|
4
|
+
|
|
5
|
+
## Configuración
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"pagosrd": {
|
|
11
|
+
"command": "npx",
|
|
12
|
+
"args": ["-y", "@pagosrd/mcp"],
|
|
13
|
+
"env": { "PAGOSRD_API_KEY": "sk_test_…" }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`PAGOSRD_API_URL` es opcional (por defecto `https://api.pagosrd.com`).
|
|
20
|
+
|
|
21
|
+
## Herramientas
|
|
22
|
+
|
|
23
|
+
| Herramienta | Qué hace |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `crear_link_de_cobro` | Crea un link y devuelve la URL para el cliente. El monto se da en pesos ("4,500.00"). |
|
|
26
|
+
| `consultar_link`, `listar_links` | Estado de los links. |
|
|
27
|
+
| `cancelar_link` | Cancela un link sin pagar (destructiva). |
|
|
28
|
+
| `consultar_pago`, `listar_pagos` | Estado de los pagos. |
|
|
29
|
+
| `reembolsar_pago` | Reembolso total o parcial (destructiva: mueve dinero en live). |
|
|
30
|
+
| `listar_comercios` | Comercios de la plataforma. |
|
|
31
|
+
| `resumen_de_cobros` | Totales de los últimos días. |
|
|
32
|
+
|
|
33
|
+
Las herramientas destructivas están marcadas con `destructiveHint` para que el cliente pida confirmación.
|
|
34
|
+
|
|
35
|
+
Licencia MIT.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { PagosRD as PagosRD2 } from "@pagosrd/node";
|
|
6
|
+
|
|
7
|
+
// src/server.ts
|
|
8
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import { formatMoney, PagosRDError, toCents } from "@pagosrd/node";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
var pesos = z.string().describe('Monto en pesos dominicanos, por ejemplo "4,500.00" o "1500".');
|
|
12
|
+
function linkText(l) {
|
|
13
|
+
return [
|
|
14
|
+
`Link ${l.id} (${l.status})`,
|
|
15
|
+
`Monto: ${formatMoney(l.amount, l.currency)}${l.itbis ? ` (ITBIS ${formatMoney(l.itbis, l.currency)})` : ""}`,
|
|
16
|
+
`Concepto: ${l.description}`,
|
|
17
|
+
l.reference ? `Referencia: ${l.reference}` : null,
|
|
18
|
+
l.customer.name ? `Cliente: ${l.customer.name}` : null,
|
|
19
|
+
`URL para pagar: ${l.url}`,
|
|
20
|
+
l.expires_at ? `Vence: ${l.expires_at}` : null,
|
|
21
|
+
l.livemode ? null : "Modo de prueba: no se cobra dinero real."
|
|
22
|
+
].filter(Boolean).join("\n");
|
|
23
|
+
}
|
|
24
|
+
function paymentText(p) {
|
|
25
|
+
return [
|
|
26
|
+
`Pago ${p.id}: ${p.status}${p.requires_review ? " (requiere revisi\xF3n)" : ""}`,
|
|
27
|
+
`Monto: ${formatMoney(p.amount, p.currency)}${p.amount_refunded ? `, reembolsado ${formatMoney(p.amount_refunded, p.currency)}` : ""}`,
|
|
28
|
+
p.card ? `Tarjeta: ${p.card.brand ?? ""} terminada en ${p.card.last4}` : null,
|
|
29
|
+
p.failure_message ? `Detalle: ${p.failure_message}` : null,
|
|
30
|
+
p.paid_at ? `Pagado: ${p.paid_at}` : null,
|
|
31
|
+
p.payment_link_id ? `Link: ${p.payment_link_id}` : null
|
|
32
|
+
].filter(Boolean).join("\n");
|
|
33
|
+
}
|
|
34
|
+
async function run(fn) {
|
|
35
|
+
try {
|
|
36
|
+
return { content: [{ type: "text", text: await fn() }] };
|
|
37
|
+
} catch (err) {
|
|
38
|
+
const message = err instanceof PagosRDError ? `${err.message}${err.field ? ` (campo: ${err.field})` : ""} [${err.code}]` : err.message;
|
|
39
|
+
return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function createServer(client2) {
|
|
43
|
+
const server2 = new McpServer({ name: "pagosrd", version: "0.1.1" });
|
|
44
|
+
server2.registerTool(
|
|
45
|
+
"crear_link_de_cobro",
|
|
46
|
+
{
|
|
47
|
+
title: "Crear link de cobro",
|
|
48
|
+
description: "Crea un link de cobro con tarjeta para un comercio y devuelve la URL para envi\xE1rsela al cliente (por WhatsApp o correo). Si no sabes el comercio, usa primero listar_comercios.",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
comercio_id: z.string().describe("Id del comercio (mer_\u2026)."),
|
|
51
|
+
monto: pesos.describe('Total a cobrar en pesos, ITBIS incluido. Ej: "4,500.00".'),
|
|
52
|
+
itbis: pesos.optional().describe("Parte del monto que es ITBIS, en pesos. Om\xEDtelo si est\xE1 exento."),
|
|
53
|
+
concepto: z.string().min(1).max(140).describe('Qu\xE9 se cobra. Ej: "Cuota de mantenimiento octubre, Apto 4B".'),
|
|
54
|
+
referencia: z.string().max(100).optional().describe("Tu referencia interna (factura, cuota). Vuelve en los webhooks."),
|
|
55
|
+
cliente_nombre: z.string().optional(),
|
|
56
|
+
cliente_correo: z.string().optional(),
|
|
57
|
+
cliente_telefono: z.string().optional(),
|
|
58
|
+
idioma: z.enum(["es", "en"]).optional().describe("Idioma de la p\xE1gina de pago. Por defecto es."),
|
|
59
|
+
vence_en_dias: z.number().int().min(1).max(90).optional().describe("D\xEDas hasta que vence el link. Por defecto 30.")
|
|
60
|
+
},
|
|
61
|
+
annotations: { destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
62
|
+
},
|
|
63
|
+
async (a) => run(async () => {
|
|
64
|
+
const link = await client2.paymentLinks.create({
|
|
65
|
+
merchant_id: a.comercio_id,
|
|
66
|
+
amount: toCents(a.monto),
|
|
67
|
+
itbis: a.itbis ? toCents(a.itbis) : void 0,
|
|
68
|
+
description: a.concepto,
|
|
69
|
+
reference: a.referencia,
|
|
70
|
+
customer: { name: a.cliente_nombre, email: a.cliente_correo, phone: a.cliente_telefono },
|
|
71
|
+
locale: a.idioma,
|
|
72
|
+
expires_at: a.vence_en_dias ? new Date(Date.now() + a.vence_en_dias * 864e5).toISOString() : void 0
|
|
73
|
+
});
|
|
74
|
+
return `Link de cobro creado.
|
|
75
|
+
${linkText(link)}`;
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
server2.registerTool(
|
|
79
|
+
"consultar_link",
|
|
80
|
+
{ title: "Consultar link de cobro", description: "Muestra el estado de un link de cobro (abierto, pagado, vencido o cancelado).", inputSchema: { link_id: z.string() }, annotations: { readOnlyHint: true } },
|
|
81
|
+
async (a) => run(async () => linkText(await client2.paymentLinks.retrieve(a.link_id)))
|
|
82
|
+
);
|
|
83
|
+
server2.registerTool(
|
|
84
|
+
"listar_links",
|
|
85
|
+
{
|
|
86
|
+
title: "Listar links de cobro",
|
|
87
|
+
description: "Lista links de cobro recientes. Puede filtrar por estado o por referencia.",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
estado: z.enum(["open", "paid", "expired", "canceled"]).optional().describe("open = pendiente de pago."),
|
|
90
|
+
referencia: z.string().optional(),
|
|
91
|
+
cantidad: z.number().int().min(1).max(50).optional()
|
|
92
|
+
},
|
|
93
|
+
annotations: { readOnlyHint: true }
|
|
94
|
+
},
|
|
95
|
+
async (a) => run(async () => {
|
|
96
|
+
const list = await client2.paymentLinks.list({ status: a.estado, reference: a.referencia, limit: a.cantidad ?? 10 });
|
|
97
|
+
if (list.data.length === 0) return "No hay links que coincidan.";
|
|
98
|
+
return list.data.map(linkText).join("\n\n") + (list.has_more ? "\n\nHay m\xE1s resultados." : "");
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
server2.registerTool(
|
|
102
|
+
"cancelar_link",
|
|
103
|
+
{
|
|
104
|
+
title: "Cancelar link de cobro",
|
|
105
|
+
description: "Cancela un link que a\xFAn no se ha pagado, para que ya no se pueda pagar. Confirma con el usuario antes de usarla.",
|
|
106
|
+
inputSchema: { link_id: z.string() },
|
|
107
|
+
annotations: { destructiveHint: true, idempotentHint: true }
|
|
108
|
+
},
|
|
109
|
+
async (a) => run(async () => `Link cancelado.
|
|
110
|
+
${linkText(await client2.paymentLinks.cancel(a.link_id))}`)
|
|
111
|
+
);
|
|
112
|
+
server2.registerTool(
|
|
113
|
+
"consultar_pago",
|
|
114
|
+
{ title: "Consultar pago", description: "Muestra el detalle de un pago: estado, monto, tarjeta y reembolsos.", inputSchema: { pago_id: z.string() }, annotations: { readOnlyHint: true } },
|
|
115
|
+
async (a) => run(async () => paymentText(await client2.payments.retrieve(a.pago_id)))
|
|
116
|
+
);
|
|
117
|
+
server2.registerTool(
|
|
118
|
+
"listar_pagos",
|
|
119
|
+
{
|
|
120
|
+
title: "Listar pagos",
|
|
121
|
+
description: "Lista pagos recientes, con filtro opcional por estado.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
estado: z.enum(["pending", "processing", "succeeded", "failed", "canceled"]).optional().describe("succeeded = cobrado."),
|
|
124
|
+
cantidad: z.number().int().min(1).max(50).optional()
|
|
125
|
+
},
|
|
126
|
+
annotations: { readOnlyHint: true }
|
|
127
|
+
},
|
|
128
|
+
async (a) => run(async () => {
|
|
129
|
+
const list = await client2.payments.list({ status: a.estado, limit: a.cantidad ?? 10 });
|
|
130
|
+
if (list.data.length === 0) return "No hay pagos que coincidan.";
|
|
131
|
+
return list.data.map(paymentText).join("\n\n");
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
server2.registerTool(
|
|
135
|
+
"reembolsar_pago",
|
|
136
|
+
{
|
|
137
|
+
title: "Reembolsar pago",
|
|
138
|
+
description: "Devuelve dinero de un pago cobrado al cliente, total o parcial. Mueve dinero real en modo live: confirma SIEMPRE con el usuario el pago y el monto antes de llamarla.",
|
|
139
|
+
inputSchema: {
|
|
140
|
+
pago_id: z.string(),
|
|
141
|
+
monto: pesos.optional().describe("Monto a reembolsar en pesos. Om\xEDtelo para reembolsar todo lo disponible."),
|
|
142
|
+
motivo: z.string().max(300).optional()
|
|
143
|
+
},
|
|
144
|
+
annotations: { destructiveHint: true, idempotentHint: false, openWorldHint: true }
|
|
145
|
+
},
|
|
146
|
+
async (a) => run(async () => {
|
|
147
|
+
const r = await client2.refunds.create(a.pago_id, { amount: a.monto ? toCents(a.monto) : void 0, reason: a.motivo });
|
|
148
|
+
return `Reembolso ${r.id}: ${r.status}, ${formatMoney(r.amount)}${r.failure_message ? `. Detalle: ${r.failure_message}` : ""}`;
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
server2.registerTool(
|
|
152
|
+
"listar_comercios",
|
|
153
|
+
{ title: "Listar comercios", description: "Lista los comercios de la plataforma, con su procesador y estado.", inputSchema: {}, annotations: { readOnlyHint: true } },
|
|
154
|
+
async () => run(async () => {
|
|
155
|
+
const list = await client2.merchants.list({ limit: 100 });
|
|
156
|
+
if (list.data.length === 0) return "No hay comercios.";
|
|
157
|
+
return list.data.map((m) => `${m.id}: ${m.name} (${m.processor}, ${m.status}${m.processor_configured ? "" : ", sin credenciales"})`).join("\n");
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
server2.registerTool(
|
|
161
|
+
"resumen_de_cobros",
|
|
162
|
+
{
|
|
163
|
+
title: "Resumen de cobros",
|
|
164
|
+
description: "Totales de los \xFAltimos d\xEDas: cobrado, cantidad de pagos, reembolsado, fallidos y pagos en revisi\xF3n.",
|
|
165
|
+
inputSchema: { dias: z.number().int().min(1).max(365).optional().describe("Por defecto 30.") },
|
|
166
|
+
annotations: { readOnlyHint: true }
|
|
167
|
+
},
|
|
168
|
+
async (a) => run(async () => {
|
|
169
|
+
const dias = a.dias ?? 30;
|
|
170
|
+
const s = await client2.summary({ from: new Date(Date.now() - dias * 864e5).toISOString() });
|
|
171
|
+
return [
|
|
172
|
+
`\xDAltimos ${dias} d\xEDas:`,
|
|
173
|
+
`Cobrado: ${formatMoney(s.succeeded_amount)} en ${s.succeeded_count} ${s.succeeded_count === 1 ? "pago" : "pagos"}`,
|
|
174
|
+
`Reembolsado: ${formatMoney(s.refunded_amount)}`,
|
|
175
|
+
`Pagos fallidos: ${s.failed_count}`,
|
|
176
|
+
s.review_count ? `Pagos que requieren revisi\xF3n: ${s.review_count}` : null
|
|
177
|
+
].filter(Boolean).join("\n");
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
return server2;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/index.ts
|
|
184
|
+
var apiKey = process.env.PAGOSRD_API_KEY;
|
|
185
|
+
if (!apiKey) {
|
|
186
|
+
console.error("Falta PAGOSRD_API_KEY (tu llave sk_test_\u2026 o sk_live_\u2026).");
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
var client = new PagosRD2(apiKey, { baseUrl: process.env.PAGOSRD_API_URL });
|
|
190
|
+
var server = createServer(client);
|
|
191
|
+
await server.connect(new StdioServerTransport());
|
|
192
|
+
console.error(`PagosRD MCP listo (${client.testMode ? "modo de prueba" : "modo live"}).`);
|
|
193
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { PagosRD } from \"@pagosrd/node\";\nimport { createServer } from \"./server.js\";\n\nconst apiKey = process.env.PAGOSRD_API_KEY;\nif (!apiKey) {\n console.error(\"Falta PAGOSRD_API_KEY (tu llave sk_test_… o sk_live_…).\");\n process.exit(1);\n}\nconst client = new PagosRD(apiKey, { baseUrl: process.env.PAGOSRD_API_URL });\nconst server = createServer(client);\nawait server.connect(new StdioServerTransport());\n// stdout es del protocolo MCP: los mensajes para humanos van a stderr.\nconsole.error(`PagosRD MCP listo (${client.testMode ? \"modo de prueba\" : \"modo live\"}).`);\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { formatMoney, PagosRD, PagosRDError, toCents, type Payment, type PaymentLink } from \"@pagosrd/node\";\nimport { z } from \"zod\";\n\n/**\n * Servidor MCP de PagosRD. Permite a Claude (u otro cliente MCP) crear links de\n * cobro, consultar pagos y reembolsar usando la llave de API de la plataforma.\n *\n * Los montos se piden en PESOS (\"4,500.00\") porque es como habla la gente; el\n * servidor los convierte a centavos sin errores de redondeo.\n */\n\nconst pesos = z.string().describe('Monto en pesos dominicanos, por ejemplo \"4,500.00\" o \"1500\".');\n\nfunction linkText(l: PaymentLink): string {\n return [\n `Link ${l.id} (${l.status})`,\n `Monto: ${formatMoney(l.amount, l.currency)}${l.itbis ? ` (ITBIS ${formatMoney(l.itbis, l.currency)})` : \"\"}`,\n `Concepto: ${l.description}`,\n l.reference ? `Referencia: ${l.reference}` : null,\n l.customer.name ? `Cliente: ${l.customer.name}` : null,\n `URL para pagar: ${l.url}`,\n l.expires_at ? `Vence: ${l.expires_at}` : null,\n l.livemode ? null : \"Modo de prueba: no se cobra dinero real.\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nfunction paymentText(p: Payment): string {\n return [\n `Pago ${p.id}: ${p.status}${p.requires_review ? \" (requiere revisión)\" : \"\"}`,\n `Monto: ${formatMoney(p.amount, p.currency)}${p.amount_refunded ? `, reembolsado ${formatMoney(p.amount_refunded, p.currency)}` : \"\"}`,\n p.card ? `Tarjeta: ${p.card.brand ?? \"\"} terminada en ${p.card.last4}` : null,\n p.failure_message ? `Detalle: ${p.failure_message}` : null,\n p.paid_at ? `Pagado: ${p.paid_at}` : null,\n p.payment_link_id ? `Link: ${p.payment_link_id}` : null,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\ntype ToolResult = { content: { type: \"text\"; text: string }[]; isError?: boolean };\n\nasync function run(fn: () => Promise<string>): Promise<ToolResult> {\n try {\n return { content: [{ type: \"text\", text: await fn() }] };\n } catch (err) {\n const message = err instanceof PagosRDError ? `${err.message}${err.field ? ` (campo: ${err.field})` : \"\"} [${err.code}]` : (err as Error).message;\n return { content: [{ type: \"text\", text: `Error: ${message}` }], isError: true };\n }\n}\n\nexport function createServer(client: PagosRD): McpServer {\n const server = new McpServer({ name: \"pagosrd\", version: \"0.1.1\" });\n\n server.registerTool(\n \"crear_link_de_cobro\",\n {\n title: \"Crear link de cobro\",\n description:\n \"Crea un link de cobro con tarjeta para un comercio y devuelve la URL para enviársela al cliente (por WhatsApp o correo). Si no sabes el comercio, usa primero listar_comercios.\",\n inputSchema: {\n comercio_id: z.string().describe(\"Id del comercio (mer_…).\"),\n monto: pesos.describe('Total a cobrar en pesos, ITBIS incluido. Ej: \"4,500.00\".'),\n itbis: pesos.optional().describe(\"Parte del monto que es ITBIS, en pesos. Omítelo si está exento.\"),\n concepto: z.string().min(1).max(140).describe('Qué se cobra. Ej: \"Cuota de mantenimiento octubre, Apto 4B\".'),\n referencia: z.string().max(100).optional().describe(\"Tu referencia interna (factura, cuota). Vuelve en los webhooks.\"),\n cliente_nombre: z.string().optional(),\n cliente_correo: z.string().optional(),\n cliente_telefono: z.string().optional(),\n idioma: z.enum([\"es\", \"en\"]).optional().describe(\"Idioma de la página de pago. Por defecto es.\"),\n vence_en_dias: z.number().int().min(1).max(90).optional().describe(\"Días hasta que vence el link. Por defecto 30.\"),\n },\n annotations: { destructiveHint: false, idempotentHint: false, openWorldHint: true },\n },\n async (a) =>\n run(async () => {\n const link = await client.paymentLinks.create({\n merchant_id: a.comercio_id,\n amount: toCents(a.monto),\n itbis: a.itbis ? toCents(a.itbis) : undefined,\n description: a.concepto,\n reference: a.referencia,\n customer: { name: a.cliente_nombre, email: a.cliente_correo, phone: a.cliente_telefono },\n locale: a.idioma,\n expires_at: a.vence_en_dias ? new Date(Date.now() + a.vence_en_dias * 86_400_000).toISOString() : undefined,\n });\n return `Link de cobro creado.\\n${linkText(link)}`;\n }),\n );\n\n server.registerTool(\n \"consultar_link\",\n { title: \"Consultar link de cobro\", description: \"Muestra el estado de un link de cobro (abierto, pagado, vencido o cancelado).\", inputSchema: { link_id: z.string() }, annotations: { readOnlyHint: true } },\n async (a) => run(async () => linkText(await client.paymentLinks.retrieve(a.link_id))),\n );\n\n server.registerTool(\n \"listar_links\",\n {\n title: \"Listar links de cobro\",\n description: \"Lista links de cobro recientes. Puede filtrar por estado o por referencia.\",\n inputSchema: {\n estado: z.enum([\"open\", \"paid\", \"expired\", \"canceled\"]).optional().describe(\"open = pendiente de pago.\"),\n referencia: z.string().optional(),\n cantidad: z.number().int().min(1).max(50).optional(),\n },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const list = await client.paymentLinks.list({ status: a.estado, reference: a.referencia, limit: a.cantidad ?? 10 });\n if (list.data.length === 0) return \"No hay links que coincidan.\";\n return list.data.map(linkText).join(\"\\n\\n\") + (list.has_more ? \"\\n\\nHay más resultados.\" : \"\");\n }),\n );\n\n server.registerTool(\n \"cancelar_link\",\n {\n title: \"Cancelar link de cobro\",\n description: \"Cancela un link que aún no se ha pagado, para que ya no se pueda pagar. Confirma con el usuario antes de usarla.\",\n inputSchema: { link_id: z.string() },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async (a) => run(async () => `Link cancelado.\\n${linkText(await client.paymentLinks.cancel(a.link_id))}`),\n );\n\n server.registerTool(\n \"consultar_pago\",\n { title: \"Consultar pago\", description: \"Muestra el detalle de un pago: estado, monto, tarjeta y reembolsos.\", inputSchema: { pago_id: z.string() }, annotations: { readOnlyHint: true } },\n async (a) => run(async () => paymentText(await client.payments.retrieve(a.pago_id))),\n );\n\n server.registerTool(\n \"listar_pagos\",\n {\n title: \"Listar pagos\",\n description: \"Lista pagos recientes, con filtro opcional por estado.\",\n inputSchema: {\n estado: z.enum([\"pending\", \"processing\", \"succeeded\", \"failed\", \"canceled\"]).optional().describe(\"succeeded = cobrado.\"),\n cantidad: z.number().int().min(1).max(50).optional(),\n },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const list = await client.payments.list({ status: a.estado, limit: a.cantidad ?? 10 });\n if (list.data.length === 0) return \"No hay pagos que coincidan.\";\n return list.data.map(paymentText).join(\"\\n\\n\");\n }),\n );\n\n server.registerTool(\n \"reembolsar_pago\",\n {\n title: \"Reembolsar pago\",\n description:\n \"Devuelve dinero de un pago cobrado al cliente, total o parcial. Mueve dinero real en modo live: confirma SIEMPRE con el usuario el pago y el monto antes de llamarla.\",\n inputSchema: {\n pago_id: z.string(),\n monto: pesos.optional().describe(\"Monto a reembolsar en pesos. Omítelo para reembolsar todo lo disponible.\"),\n motivo: z.string().max(300).optional(),\n },\n annotations: { destructiveHint: true, idempotentHint: false, openWorldHint: true },\n },\n async (a) =>\n run(async () => {\n const r = await client.refunds.create(a.pago_id, { amount: a.monto ? toCents(a.monto) : undefined, reason: a.motivo });\n return `Reembolso ${r.id}: ${r.status}, ${formatMoney(r.amount)}${r.failure_message ? `. Detalle: ${r.failure_message}` : \"\"}`;\n }),\n );\n\n server.registerTool(\n \"listar_comercios\",\n { title: \"Listar comercios\", description: \"Lista los comercios de la plataforma, con su procesador y estado.\", inputSchema: {}, annotations: { readOnlyHint: true } },\n async () =>\n run(async () => {\n const list = await client.merchants.list({ limit: 100 });\n if (list.data.length === 0) return \"No hay comercios.\";\n return list.data.map((m) => `${m.id}: ${m.name} (${m.processor}, ${m.status}${m.processor_configured ? \"\" : \", sin credenciales\"})`).join(\"\\n\");\n }),\n );\n\n server.registerTool(\n \"resumen_de_cobros\",\n {\n title: \"Resumen de cobros\",\n description: \"Totales de los últimos días: cobrado, cantidad de pagos, reembolsado, fallidos y pagos en revisión.\",\n inputSchema: { dias: z.number().int().min(1).max(365).optional().describe(\"Por defecto 30.\") },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const dias = a.dias ?? 30;\n const s = await client.summary({ from: new Date(Date.now() - dias * 86_400_000).toISOString() });\n return [\n `Últimos ${dias} días:`,\n `Cobrado: ${formatMoney(s.succeeded_amount)} en ${s.succeeded_count} ${s.succeeded_count === 1 ? \"pago\" : \"pagos\"}`,\n `Reembolsado: ${formatMoney(s.refunded_amount)}`,\n `Pagos fallidos: ${s.failed_count}`,\n s.review_count ? `Pagos que requieren revisión: ${s.review_count}` : null,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n }),\n );\n\n return server;\n}\n"],"mappings":"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","names":["PagosRD","client","server","PagosRD"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagosrd/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Servidor MCP de PagosRD: crea links de cobro, consulta pagos y reembolsa desde Claude u otros clientes MCP.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Cristian Encarnación",
|
|
7
|
+
"homepage": "https://github.com/cristianEncarnacion/pagosrd-mcp#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/cristianEncarnacion/pagosrd-mcp.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/cristianEncarnacion/pagosrd-mcp/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"pagosrd",
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"claude",
|
|
20
|
+
"pagos",
|
|
21
|
+
"republica-dominicana"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"bin": {
|
|
25
|
+
"pagosrd-mcp": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"prepublishOnly": "npm run build && npm test"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
41
|
+
"@pagosrd/node": "^0.1.0",
|
|
42
|
+
"zod": "^4.6.7"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^22.20.5",
|
|
46
|
+
"tsup": "^8.5.1",
|
|
47
|
+
"typescript": "^5.9.3",
|
|
48
|
+
"vitest": "^5.0.5"
|
|
49
|
+
}
|
|
50
|
+
}
|