From 274fcf3926c1b33390d58ab5a39afa0c6c00fcd3 Mon Sep 17 00:00:00 2001 From: Fabiano Cruz Date: Thu, 23 Apr 2026 21:16:51 -0300 Subject: [PATCH] feat(payments): add @codespar/mcp-getnet server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second P0-B server from the MCP expansion plan. Getnet is Santander-owned — #3 BR acquirer and #1 BR ecommerce per Santander IR. Together with Cielo, Stone, and Efi, this closes three of the "big four" BR acquirer quadrant. Distinct from per-PSP servers (Zoop, Pagar.me, Asaas, PagSeguro): Getnet is an acquirer, so merchants with a Santander commercial contract integrate directly instead of via a PSP. Different target customer; different business contract. Tools (11) authorize_credit authorize card (delayed=true) or auth+capture (false) capture_credit capture a prior authorization cancel_credit cancel authorized-but-uncaptured refund_credit full or partial refund create_pix Pix charge returning QR image + EMV copy-paste create_boleto bank boleto with instruction text + expiration get_payment retrieve any payment by id tokenize_card PCI-safe card tokenization for reuse create_seller onboard marketplace seller (Marketplace Management) get_seller seller by id list_sellers paginated list with filters Authentication OAuth 2.0 Client Credentials. POST /auth/oauth/v2/token returns a bearer token. The server caches it in memory until 60s before expiry, transparent to callers. Every API call also includes seller_id header for tenant scoping. Why Getnet first (after dLocal) - No blocking product decision (Braspag waits on Cielo-boundary naming; Chargebee has fork-vs-native open; Matera needs mTLS sandbox; Adyen is a bigger multi-API scope) - Closes a concrete quadrant gap: three of four big BR acquirers in the catalog - Marketplace seller onboarding is a first-class API, unusual for acquirers at this tier Build verified (tsc 0 errors, dist/index.js emitted). Co-Authored-By: Claude Opus 4.7 (1M context) --- package-lock.json | 19 ++ packages/payments/getnet/README.md | 55 ++++ packages/payments/getnet/package.json | 33 ++ packages/payments/getnet/server.json | 51 +++ packages/payments/getnet/src/index.ts | 430 +++++++++++++++++++++++++ packages/payments/getnet/tsconfig.json | 14 + 6 files changed, 602 insertions(+) create mode 100644 packages/payments/getnet/README.md create mode 100644 packages/payments/getnet/package.json create mode 100644 packages/payments/getnet/server.json create mode 100644 packages/payments/getnet/src/index.ts create mode 100644 packages/payments/getnet/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 3f113670..4275d592 100644 --- a/package-lock.json +++ b/package-lock.json @@ -122,6 +122,10 @@ "resolved": "packages/fiscal/focus-nfe", "link": true }, + "node_modules/@codespar/mcp-getnet": { + "resolved": "packages/payments/getnet", + "link": true + }, "node_modules/@codespar/mcp-inter-bank": { "resolved": "packages/payments/inter-bank", "link": true @@ -4204,6 +4208,21 @@ "typescript": "^5.8.0" } }, + "packages/payments/getnet": { + "name": "@codespar/mcp-getnet", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.0" + }, + "bin": { + "mcp-getnet": "dist/index.js" + }, + "devDependencies": { + "@types/node": "^25.5.0", + "typescript": "^5.8.0" + } + }, "packages/payments/inter-bank": { "name": "@codespar/mcp-inter-bank", "version": "0.1.0", diff --git a/packages/payments/getnet/README.md b/packages/payments/getnet/README.md new file mode 100644 index 00000000..c78f7571 --- /dev/null +++ b/packages/payments/getnet/README.md @@ -0,0 +1,55 @@ +# @codespar/mcp-getnet + +MCP server for [Getnet](https://developers.getnet.com.br) — Santander-owned Brazilian card acquirer. + +Together with Cielo, Stone, and Efi, Getnet closes three of the "big four" BR acquirer quadrant. Distinct from per-PSP servers (Zoop, Pagar.me, Asaas): Getnet is an acquirer, so merchants with a Santander commercial contract integrate directly instead of going through a PSP. + +## Tools + +| Tool | Purpose | +|---|---| +| `authorize_credit` | Authorize card payment (optional auto-capture) | +| `capture_credit` | Capture previously authorized payment | +| `cancel_credit` | Cancel authorized-but-uncaptured payment | +| `refund_credit` | Full or partial refund | +| `create_pix` | Create Pix charge, returns QR + copy-paste | +| `create_boleto` | Create boleto charge | +| `get_payment` | Retrieve any payment by id | +| `tokenize_card` | PCI-safe card tokenization | +| `create_seller` | Onboard a marketplace seller | +| `get_seller` | Retrieve seller by id | +| `list_sellers` | List marketplace sellers | + +## Install + +```bash +npm install @codespar/mcp-getnet +``` + +## Environment + +```bash +GETNET_CLIENT_ID="..." # OAuth client_id +GETNET_CLIENT_SECRET="..." # OAuth client_secret +GETNET_SELLER_ID="..." # seller_id from your merchant contract +GETNET_BASE_URL="..." # Optional. Default: https://api.getnet.com.br + # Sandbox: https://api-homologacao.getnet.com.br +``` + +## Authentication + +OAuth 2.0 Client Credentials. The server calls `POST /auth/oauth/v2/token` with Basic auth and caches the bearer token in memory until 60s before expiry. Transparent to callers. + +## Run + +```bash +# stdio (default) +npx @codespar/mcp-getnet + +# HTTP +MCP_HTTP=true MCP_PORT=3000 npx @codespar/mcp-getnet +``` + +## License + +MIT diff --git a/packages/payments/getnet/package.json b/packages/payments/getnet/package.json new file mode 100644 index 00000000..e001729f --- /dev/null +++ b/packages/payments/getnet/package.json @@ -0,0 +1,33 @@ +{ + "name": "@codespar/mcp-getnet", + "version": "0.1.0", + "description": "MCP server for Getnet — Santander-owned Brazilian acquirer. Credit, debit, Pix, boleto + marketplace seller onboarding and split.", + "type": "module", + "main": "./dist/index.js", + "bin": { + "mcp-getnet": "./dist/index.js" + }, + "scripts": { + "build": "tsc", + "start": "node dist/index.js" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.0" + }, + "devDependencies": { + "@types/node": "^25.5.0", + "typescript": "^5.8.0" + }, + "license": "MIT", + "keywords": [ + "mcp", + "getnet", + "santander", + "payments", + "pix", + "boleto", + "marketplace", + "brazil" + ], + "mcpName": "io.github.codespar/mcp-getnet" +} diff --git a/packages/payments/getnet/server.json b/packages/payments/getnet/server.json new file mode 100644 index 00000000..53d1fa30 --- /dev/null +++ b/packages/payments/getnet/server.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.codespar/mcp-getnet", + "description": "MCP server for Getnet — Santander-owned Brazilian acquirer. Credit, debit, Pix, boleto + marketplace seller onboarding and split.", + "repository": { + "url": "https://github.com/codespar/mcp-dev-brasil", + "source": "github", + "subfolder": "packages/payments/getnet" + }, + "version": "0.1.0", + "packages": [ + { + "registryType": "npm", + "identifier": "@codespar/mcp-getnet", + "version": "0.1.0", + "transport": { + "type": "stdio" + }, + "environmentVariables": [ + { + "name": "GETNET_CLIENT_ID", + "description": "Getnet OAuth client_id", + "isRequired": true, + "format": "string", + "isSecret": false + }, + { + "name": "GETNET_CLIENT_SECRET", + "description": "Getnet OAuth client_secret", + "isRequired": true, + "format": "string", + "isSecret": true + }, + { + "name": "GETNET_SELLER_ID", + "description": "Getnet seller_id issued with your merchant contract", + "isRequired": true, + "format": "string", + "isSecret": false + }, + { + "name": "GETNET_BASE_URL", + "description": "Getnet API base URL. Defaults to production (https://api.getnet.com.br). Use https://api-homologacao.getnet.com.br for sandbox.", + "isRequired": false, + "format": "string", + "isSecret": false + } + ] + } + ] +} diff --git a/packages/payments/getnet/src/index.ts b/packages/payments/getnet/src/index.ts new file mode 100644 index 00000000..92ce8e94 --- /dev/null +++ b/packages/payments/getnet/src/index.ts @@ -0,0 +1,430 @@ +#!/usr/bin/env node + +/** + * MCP Server for Getnet — Santander-owned Brazilian card acquirer. + * + * Getnet is the #3 BR acquirer and the #1 in BR ecommerce (per Santander IR, + * 2021 data). Together with Cielo and Stone, adding Getnet closes three of + * the "big four" BR acquirer quadrant. Distinct from per-PSP servers like + * Zoop or Pagar.me: Getnet is an acquirer, so merchants with a Santander + * commercial contract integrate directly instead of going through a PSP. + * + * Tools (11): + * authorize_credit — authorize a credit-card payment (optional auto-capture) + * capture_credit — capture a previously authorized credit payment + * cancel_credit — cancel an authorized-but-uncaptured credit payment + * refund_credit — refund a captured credit payment (full or partial) + * create_pix — create a Pix charge, returns QR code + copy-paste payload + * create_boleto — create a boleto charge + * get_payment — retrieve any payment by id + * tokenize_card — PCI-safe card tokenization for reuse + * create_seller — onboard a marketplace seller (Marketplace Management) + * get_seller — retrieve a seller by id + * list_sellers — list marketplace sellers with filters + * + * Authentication + * OAuth 2.0 Client Credentials. The server calls POST /auth/oauth/v2/token + * with Basic auth (client_id:client_secret) and caches the bearer token + * in memory until a minute before expiry. + * + * Environment + * GETNET_CLIENT_ID OAuth client_id + * GETNET_CLIENT_SECRET OAuth client_secret + * GETNET_SELLER_ID seller_id issued with your merchant contract + * GETNET_BASE_URL optional; defaults to https://api.getnet.com.br + * (use https://api-homologacao.getnet.com.br for sandbox) + * + * Docs: https://developers.getnet.com.br + */ + +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; + +const CLIENT_ID = process.env.GETNET_CLIENT_ID || ""; +const CLIENT_SECRET = process.env.GETNET_CLIENT_SECRET || ""; +const SELLER_ID = process.env.GETNET_SELLER_ID || ""; +const BASE_URL = process.env.GETNET_BASE_URL || "https://api.getnet.com.br"; + +interface TokenCache { + accessToken: string; + expiresAt: number; +} + +let tokenCache: TokenCache | null = null; + +async function getAccessToken(): Promise { + const now = Date.now(); + if (tokenCache && tokenCache.expiresAt > now + 60_000) { + return tokenCache.accessToken; + } + const basic = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64"); + const res = await fetch(`${BASE_URL}/auth/oauth/v2/token`, { + method: "POST", + headers: { + "Authorization": `Basic ${basic}`, + "Content-Type": "application/x-www-form-urlencoded", + }, + body: "scope=oob&grant_type=client_credentials", + }); + if (!res.ok) { + throw new Error(`Getnet OAuth ${res.status}: ${await res.text()}`); + } + const data = (await res.json()) as { access_token: string; expires_in: number }; + tokenCache = { + accessToken: data.access_token, + expiresAt: now + data.expires_in * 1000, + }; + return data.access_token; +} + +async function getnetRequest(method: string, path: string, body?: unknown): Promise { + const token = await getAccessToken(); + const res = await fetch(`${BASE_URL}${path}`, { + method, + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}`, + "seller_id": SELLER_ID, + }, + body: body ? JSON.stringify(body) : undefined, + }); + if (!res.ok) { + throw new Error(`Getnet API ${res.status}: ${await res.text()}`); + } + return res.json(); +} + +const server = new Server( + { name: "mcp-getnet", version: "0.1.0" }, + { capabilities: { tools: {} } } +); + +server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: "authorize_credit", + description: "Authorize a credit-card payment on Getnet. Set delayed=false to authorize+capture atomically; delayed=true to authorize only (use capture_credit later).", + inputSchema: { + type: "object", + properties: { + amount: { type: "number", description: "Amount in cents" }, + currency: { type: "string", description: "ISO-4217 currency code (default BRL)" }, + order_id: { type: "string", description: "Merchant-side order identifier" }, + delayed: { type: "boolean", description: "true = authorize only; false = authorize + capture" }, + customer: { + type: "object", + description: "Customer identity", + properties: { + customer_id: { type: "string", description: "Merchant-side customer id" }, + first_name: { type: "string" }, + last_name: { type: "string" }, + name: { type: "string" }, + email: { type: "string" }, + document_type: { type: "string", enum: ["CPF", "CNPJ"] }, + document_number: { type: "string", description: "CPF or CNPJ digits only" }, + phone_number: { type: "string" }, + }, + required: ["customer_id", "first_name", "last_name", "email", "document_type", "document_number"], + }, + credit: { + type: "object", + description: "Card data (pre-tokenized preferred via number_token)", + properties: { + number_token: { type: "string", description: "Token from tokenize_card" }, + cardholder_name: { type: "string" }, + security_code: { type: "string" }, + brand: { type: "string", description: "Visa, Mastercard, Elo, Amex, Hipercard" }, + expiration_month: { type: "string" }, + expiration_year: { type: "string" }, + save_card_data: { type: "boolean" }, + transaction_type: { type: "string", enum: ["FULL", "INSTALL_NO_INTEREST", "INSTALL_WITH_INTEREST"] }, + number_installments: { type: "number" }, + soft_descriptor: { type: "string" }, + }, + required: ["number_token", "cardholder_name", "security_code", "brand", "expiration_month", "expiration_year"], + }, + }, + required: ["amount", "order_id", "delayed", "customer", "credit"], + }, + }, + { + name: "capture_credit", + description: "Capture a previously authorized credit payment (when delayed=true was used).", + inputSchema: { + type: "object", + properties: { + payment_id: { type: "string", description: "Getnet payment_id from authorize_credit" }, + amount: { type: "number", description: "Amount to capture in cents. Omit to capture the full authorized amount." }, + }, + required: ["payment_id"], + }, + }, + { + name: "cancel_credit", + description: "Cancel an authorized-but-uncaptured credit payment.", + inputSchema: { + type: "object", + properties: { + payment_id: { type: "string", description: "Getnet payment_id" }, + }, + required: ["payment_id"], + }, + }, + { + name: "refund_credit", + description: "Refund a captured credit payment. Pass amount for a partial refund; omit for full.", + inputSchema: { + type: "object", + properties: { + payment_id: { type: "string", description: "Getnet payment_id" }, + amount: { type: "number", description: "Refund amount in cents. Omit for a full refund." }, + }, + required: ["payment_id"], + }, + }, + { + name: "create_pix", + description: "Create a Pix charge. Returns qr_code (image base64), qr_code_text (EMV copy-paste payload) and payment_id. Expires per Getnet defaults unless expires_in is set.", + inputSchema: { + type: "object", + properties: { + amount: { type: "number", description: "Amount in cents" }, + order_id: { type: "string", description: "Merchant-side order identifier" }, + customer: { + type: "object", + description: "Payer identity (CPF/CNPJ required by BCB)", + properties: { + customer_id: { type: "string" }, + first_name: { type: "string" }, + last_name: { type: "string" }, + email: { type: "string" }, + document_type: { type: "string", enum: ["CPF", "CNPJ"] }, + document_number: { type: "string" }, + }, + required: ["customer_id", "document_type", "document_number"], + }, + expires_in: { type: "number", description: "QR code lifetime in seconds (Getnet default applies if omitted)" }, + }, + required: ["amount", "order_id", "customer"], + }, + }, + { + name: "create_boleto", + description: "Create a boleto charge. Returns boleto PDF URL, barcode, and expiration date.", + inputSchema: { + type: "object", + properties: { + amount: { type: "number", description: "Amount in cents" }, + order_id: { type: "string", description: "Merchant-side order identifier" }, + customer_id: { type: "string", description: "Merchant-side customer id" }, + boleto: { + type: "object", + description: "Boleto instructions + payer data", + properties: { + document_number: { type: "string", description: "Boleto document number" }, + expiration_date: { type: "string", description: "DD/MM/YYYY" }, + instructions: { type: "string", description: "Free-text instructions shown on boleto" }, + provider: { type: "string", description: "Bank provider identifier (e.g. santander)" }, + }, + required: ["document_number", "expiration_date"], + }, + customer: { + type: "object", + description: "Payer identity", + properties: { + first_name: { type: "string" }, + last_name: { type: "string" }, + document_type: { type: "string", enum: ["CPF", "CNPJ"] }, + document_number: { type: "string" }, + }, + required: ["first_name", "last_name", "document_type", "document_number"], + }, + }, + required: ["amount", "order_id", "customer_id", "boleto", "customer"], + }, + }, + { + name: "get_payment", + description: "Retrieve a payment by Getnet payment_id. Works for credit, debit, Pix, and boleto.", + inputSchema: { + type: "object", + properties: { + payment_id: { type: "string", description: "Getnet payment_id" }, + }, + required: ["payment_id"], + }, + }, + { + name: "tokenize_card", + description: "Tokenize a card for PCI-safe reuse. Returns a number_token to pass into authorize_credit.credit.number_token.", + inputSchema: { + type: "object", + properties: { + card_number: { type: "string", description: "PAN; never log this value" }, + customer_id: { type: "string", description: "Customer id to associate the token with" }, + }, + required: ["card_number", "customer_id"], + }, + }, + { + name: "create_seller", + description: "Onboard a marketplace seller via Getnet Marketplace Management. Required before routing split payments to a seller.", + inputSchema: { + type: "object", + properties: { + merchant_id: { type: "string", description: "Marketplace merchant id" }, + legal_document_type: { type: "string", enum: ["CPF", "CNPJ"] }, + legal_document_number: { type: "string" }, + legal_name: { type: "string", description: "Razão social (CNPJ) or full name (CPF)" }, + trade_name: { type: "string", description: "Nome fantasia" }, + mcc: { type: "string", description: "Merchant category code (ISO 18245)" }, + business_address: { + type: "object", + description: "Seller commercial address", + properties: { + mailing_address_equals: { type: "string", enum: ["S", "N"] }, + street: { type: "string" }, + number: { type: "string" }, + district: { type: "string" }, + city: { type: "string" }, + state: { type: "string" }, + postal_code: { type: "string" }, + country_code: { type: "string", description: "ISO-3166 alpha-3 (e.g. BRA)" }, + }, + }, + bank_accounts: { + type: "array", + description: "Seller payout bank accounts", + }, + }, + required: ["merchant_id", "legal_document_type", "legal_document_number", "legal_name"], + }, + }, + { + name: "get_seller", + description: "Retrieve a seller by Getnet seller_id.", + inputSchema: { + type: "object", + properties: { + seller_id: { type: "string", description: "Getnet seller_id" }, + }, + required: ["seller_id"], + }, + }, + { + name: "list_sellers", + description: "List marketplace sellers with optional filters.", + inputSchema: { + type: "object", + properties: { + page: { type: "number", description: "Page number (starts at 1)" }, + limit: { type: "number", description: "Page size" }, + legal_document_number: { type: "string", description: "Filter by CPF/CNPJ" }, + status: { type: "string", description: "Filter by status" }, + }, + }, + }, + ], +})); + +server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + try { + switch (name) { + case "authorize_credit": { + const body = { ...(args as Record), seller_id: SELLER_ID }; + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("POST", "/v1/payments/credit", body), null, 2) }] }; + } + case "capture_credit": { + const paymentId = (args as { payment_id: string }).payment_id; + const body: Record = {}; + if ((args as { amount?: number }).amount !== undefined) body.amount = (args as { amount: number }).amount; + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("POST", `/v1/payments/credit/${paymentId}/confirm`, body), null, 2) }] }; + } + case "cancel_credit": { + const paymentId = (args as { payment_id: string }).payment_id; + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("POST", `/v1/payments/credit/${paymentId}/cancel`), null, 2) }] }; + } + case "refund_credit": { + const paymentId = (args as { payment_id: string }).payment_id; + const body: Record = {}; + if ((args as { amount?: number }).amount !== undefined) body.amount = (args as { amount: number }).amount; + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("POST", `/v1/payments/credit/${paymentId}/refund`, body), null, 2) }] }; + } + case "create_pix": { + const body = { ...(args as Record), seller_id: SELLER_ID }; + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("POST", "/v1/payments/qrcode/pix", body), null, 2) }] }; + } + case "create_boleto": { + const body = { ...(args as Record), seller_id: SELLER_ID }; + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("POST", "/v1/payments/boleto", body), null, 2) }] }; + } + case "get_payment": { + const paymentId = (args as { payment_id: string }).payment_id; + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("GET", `/v1/payments/${paymentId}`), null, 2) }] }; + } + case "tokenize_card": + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("POST", "/v1/tokens/card", args), null, 2) }] }; + case "create_seller": + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("POST", "/v1/mgm/sellers", args), null, 2) }] }; + case "get_seller": { + const sellerId = (args as { seller_id: string }).seller_id; + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("GET", `/v1/mgm/sellers/${sellerId}`), null, 2) }] }; + } + case "list_sellers": { + const params = new URLSearchParams(); + const a = args as Record; + if (a?.page) params.set("page", String(a.page)); + if (a?.limit) params.set("limit", String(a.limit)); + if (a?.legal_document_number) params.set("legal_document_number", String(a.legal_document_number)); + if (a?.status) params.set("status", String(a.status)); + return { content: [{ type: "text", text: JSON.stringify(await getnetRequest("GET", `/v1/mgm/sellers?${params}`), null, 2) }] }; + } + default: + return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true }; + } + } catch (err) { + return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true }; + } +}); + +async function main() { + if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") { + const { default: express } = await import("express"); + const { randomUUID } = await import("node:crypto"); + const app = express(); + app.use(express.json()); + const transports = new Map(); + app.get("/health", (_req: unknown, res: { json: (body: unknown) => unknown }) => res.json({ status: "ok", sessions: transports.size })); + app.post("/mcp", async (req: { headers: Record; body: unknown }, res: { status: (code: number) => { json: (body: unknown) => unknown } }) => { + const sid = req.headers["mcp-session-id"] as string | undefined; + if (sid && transports.has(sid)) { await transports.get(sid)!.handleRequest(req as never, res as never, req.body); return; } + if (!sid && isInitializeRequest(req.body)) { + const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } }); + t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); }; + const s = new Server({ name: "mcp-getnet", version: "0.1.0" }, { capabilities: { tools: {} } }); + (server as unknown as { _requestHandlers: Map })._requestHandlers.forEach((v, k) => (s as unknown as { _requestHandlers: Map })._requestHandlers.set(k, v)); + (server as unknown as { _notificationHandlers?: Map })._notificationHandlers?.forEach((v, k) => (s as unknown as { _notificationHandlers: Map })._notificationHandlers.set(k, v)); + await s.connect(t); + await t.handleRequest(req as never, res as never, req.body); return; + } + res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null }); + }); + app.get("/mcp", async (req: { headers: Record }, res: { status: (code: number) => { send: (body: string) => unknown } }) => { const sid = req.headers["mcp-session-id"] as string; if (sid && transports.has(sid)) await transports.get(sid)!.handleRequest(req as never, res as never); else res.status(400).send("Invalid session"); }); + app.delete("/mcp", async (req: { headers: Record }, res: { status: (code: number) => { send: (body: string) => unknown } }) => { const sid = req.headers["mcp-session-id"] as string; if (sid && transports.has(sid)) await transports.get(sid)!.handleRequest(req as never, res as never); else res.status(400).send("Invalid session"); }); + const port = Number(process.env.MCP_PORT) || 3000; + app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); }); + } else { + const transport = new StdioServerTransport(); + await server.connect(transport); + } +} + +main().catch(console.error); diff --git a/packages/payments/getnet/tsconfig.json b/packages/payments/getnet/tsconfig.json new file mode 100644 index 00000000..40c56c6e --- /dev/null +++ b/packages/payments/getnet/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "declaration": true + }, + "include": ["src"] +}