From 072c3cf97daafd7e37fe6ddc5ab5206b249c2e56 Mon Sep 17 00:00:00 2001 From: ConnectMeGuru Date: Thu, 17 Sep 2026 16:03:41 +0530 Subject: [PATCH 1/2] feat(agentkit): add ConnectMeGuru eSIM action provider --- typescript/agentkit/README.md | 17 ++ .../action-providers/connectmeguru/README.md | 20 ++ .../connectmeguruActionProvider.test.ts | 168 +++++++++++++++++ .../connectmeguruActionProvider.ts | 171 ++++++++++++++++++ .../connectmeguru/constants.ts | 7 + .../action-providers/connectmeguru/index.ts | 7 + .../action-providers/connectmeguru/schemas.ts | 52 ++++++ .../agentkit/src/action-providers/index.ts | 1 + 8 files changed, 443 insertions(+) create mode 100644 typescript/agentkit/src/action-providers/connectmeguru/README.md create mode 100644 typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.test.ts create mode 100644 typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.ts create mode 100644 typescript/agentkit/src/action-providers/connectmeguru/constants.ts create mode 100644 typescript/agentkit/src/action-providers/connectmeguru/index.ts create mode 100644 typescript/agentkit/src/action-providers/connectmeguru/schemas.ts diff --git a/typescript/agentkit/README.md b/typescript/agentkit/README.md index 37b14207f..dc42ef349 100644 --- a/typescript/agentkit/README.md +++ b/typescript/agentkit/README.md @@ -238,6 +238,23 @@ const agent = createAgent({
+ConnectMeGuru + + + + + + + + + + + + + +
search_esim_plansSearches over 3,000+ international travel eSIM data plans across 190+ countries with retail prices and validity periods.
purchase_esimInitiates an eSIM purchase and generates a non-custodial USDT payment invoice on Polygon, Arbitrum One, or TRON.
check_order_statusChecks on-chain payment settlement and retrieves the fulfilled eSIM profile (ICCID, SM-DP+ LPA string, and QR code URL).
+
+
CDP API diff --git a/typescript/agentkit/src/action-providers/connectmeguru/README.md b/typescript/agentkit/src/action-providers/connectmeguru/README.md new file mode 100644 index 000000000..e78710f67 --- /dev/null +++ b/typescript/agentkit/src/action-providers/connectmeguru/README.md @@ -0,0 +1,20 @@ +# ConnectMeGuru Action Provider + +The ConnectMeGuru Action Provider enables AI agents to search and purchase international travel eSIM data plans across 190+ countries with non-custodial USDT payments on Polygon, Arbitrum One, and TRON. + +## Actions + +- `search_esim_plans`: Search 3,000+ local and regional eSIM packages by destination country. +- `purchase_esim`: Create a non-custodial crypto checkout invoice with collision-free spot discount pricing. +- `check_order_status`: Poll on-chain payment confirmation and retrieve the fulfilled eSIM profile (ICCID, SM-DP+ LPA string, and QR code URL). + +## Configuration + +```typescript +import { connectmeguruActionProvider } from "@coinbase/agentkit"; + +const actionProvider = connectmeguruActionProvider({ + patToken: process.env.CMG_PAT_TOKEN, // Optional: Personal Access Token for verified machine identity + baseUrl: "https://www.connectmeguru.com/api", // Optional: Custom API URL +}); +``` diff --git a/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.test.ts b/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.test.ts new file mode 100644 index 000000000..989d3000d --- /dev/null +++ b/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.test.ts @@ -0,0 +1,168 @@ +import { + ConnectMeGuruActionProvider, + connectmeguruActionProvider, +} from "./connectmeguruActionProvider"; + +describe("ConnectMeGuruActionProvider", () => { + const fetchMock = jest.fn(); + global.fetch = fetchMock; + + const provider = connectmeguruActionProvider({ + baseUrl: "https://www.connectmeguru.com/api", + patToken: "test_pat_token", + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe("searchEsimPlans", () => { + it("should return plans when API call is successful", async () => { + const mockResponse = { + success: true, + totalCount: 1, + plans: [ + { + packageCode: "P4XU0X3CX", + name: "Japan 1GB 7Days", + dataAmount: 1, + duration: 7, + retailPrice: 3.85, + }, + ], + }; + fetchMock.mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue(mockResponse), + }); + + const result = await provider.searchEsimPlans({ country: "Japan" }); + const parsed = JSON.parse(result); + expect(parsed.success).toBe(true); + expect(parsed.plans[0].packageCode).toBe("P4XU0X3CX"); + }); + + it("should handle API errors gracefully", async () => { + fetchMock.mockResolvedValue({ + ok: false, + status: 500, + statusText: "Internal Server Error", + }); + + const result = await provider.searchEsimPlans({ country: "Japan" }); + expect(result).toContain("Error searching eSIM plans"); + expect(result).toContain("500"); + }); + + it("should handle network failure", async () => { + fetchMock.mockRejectedValue(new Error("Network connection dropped")); + + const result = await provider.searchEsimPlans({ country: "Japan" }); + expect(result).toContain("Error searching eSIM plans"); + expect(result).toContain("Network connection dropped"); + }); + }); + + describe("purchaseEsim", () => { + it("should return invoice when checkout is successful", async () => { + const mockInvoice = { + status: "PENDING_PAYMENT", + invoiceId: "CMG-INV-TEST-001", + plan: { + packageCode: "P4XU0X3CX", + name: "Japan 1GB 7Days", + }, + payment: { + currency: "USDT", + network: "polygon", + expectedAmount: 3.850012, + receivingAddress: "0x1234567890123456789012345678901234567890", + }, + }; + + fetchMock.mockResolvedValue({ + status: 402, + ok: false, + json: jest.fn().mockResolvedValue(mockInvoice), + }); + + const result = await provider.purchaseEsim({ + packageCode: "P4XU0X3CX", + customerEmail: "agent@example.com", + network: "polygon", + currency: "USDT", + }); + + const parsed = JSON.parse(result); + expect(parsed.invoiceId).toBe("CMG-INV-TEST-001"); + expect(parsed.payment.expectedAmount).toBe(3.850012); + }); + + it("should handle purchase failure", async () => { + fetchMock.mockResolvedValue({ + status: 400, + ok: false, + json: jest.fn().mockResolvedValue({ error: "Invalid package code" }), + }); + + const result = await provider.purchaseEsim({ + packageCode: "INVALID", + customerEmail: "agent@example.com", + network: "polygon", + currency: "USDT", + }); + + expect(result).toContain("Error purchasing eSIM"); + expect(result).toContain("Invalid package code"); + }); + }); + + describe("checkOrderStatus", () => { + it("should return fulfilled eSIM details when completed", async () => { + const mockOrder = { + status: "COMPLETED", + invoiceId: "CMG-INV-TEST-001", + esim: { + iccid: "8985200000000000001", + lpaString: "LPA:1$smdp.io$MATCHING-ID", + qrCodeUrl: "https://qr.connectmeguru.com/esim.png", + }, + }; + + fetchMock.mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue(mockOrder), + }); + + const result = await provider.checkOrderStatus({ + invoiceId: "CMG-INV-TEST-001", + }); + + const parsed = JSON.parse(result); + expect(parsed.status).toBe("COMPLETED"); + expect(parsed.esim.iccid).toBe("8985200000000000001"); + }); + + it("should handle order not found error", async () => { + fetchMock.mockResolvedValue({ + ok: false, + status: 404, + statusText: "Not Found", + json: jest.fn().mockResolvedValue({ error: "Invoice not found" }), + }); + + const result = await provider.checkOrderStatus({ + invoiceId: "NON_EXISTENT", + }); + + expect(result).toContain("Error checking order status"); + expect(result).toContain("Invoice not found"); + }); + }); + + describe("supportsNetwork", () => { + it("should return true for any network", () => { + expect(provider.supportsNetwork()).toBe(true); + }); + }); +}); diff --git a/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.ts b/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.ts new file mode 100644 index 000000000..8ba3fe9dc --- /dev/null +++ b/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.ts @@ -0,0 +1,171 @@ +import { z } from "zod"; +import { ActionProvider } from "../actionProvider"; +import { CreateAction } from "../actionDecorator"; +import { SearchEsimSchema, PurchaseEsimSchema, CheckOrderStatusSchema } from "./schemas"; +import { CONNECTMEGURU_BASE_URL } from "./constants"; + +export interface ConnectMeGuruConfig { + patToken?: string; + baseUrl?: string; +} + +/** + * Extract human-readable message from an unknown error. + */ +function getErrorMessage(error: unknown): string { + if (error instanceof Error) { + return error.message || error.toString(); + } + const str = String(error); + if (str === "[object Object]") { + try { + return JSON.stringify(error); + } catch { + return "Unknown error"; + } + } + return str; +} + +/** + * ConnectMeGuruActionProvider provides actions for autonomous global travel eSIM search and purchase. + * + * Supports searching 3,000+ data plans across 190+ countries, non-custodial crypto checkout + * using USDT on Polygon, Arbitrum One, and TRON, and instant eSIM profile delivery (QR code and LPA string). + */ +export class ConnectMeGuruActionProvider extends ActionProvider { + private readonly patToken: string; + private readonly baseUrl: string; + + /** + * Creates a new ConnectMeGuruActionProvider instance. + * + * @param config - Optional configuration containing Personal Access Token or custom base URL. + */ + constructor(config?: ConnectMeGuruConfig) { + super("connectmeguru", []); + this.patToken = config?.patToken || process.env.CMG_PAT_TOKEN || ""; + this.baseUrl = (config?.baseUrl || process.env.CMG_BASE_URL || CONNECTMEGURU_BASE_URL).replace(/\/$/, ""); + } + + /** + * Search for available travel eSIM data packages for a destination country. + * + * @param args - Search arguments containing the destination country. + * @returns JSON string containing list of available eSIM plans with prices and durations. + */ + @CreateAction({ + name: "search_esim_plans", + description: `Search for available international travel eSIM data plans and pricing across 190+ countries. +Takes the destination country name or ISO code and returns a list of package codes, data caps (GB), validity duration (days), and retail prices in USD/USDT.`, + schema: SearchEsimSchema, + }) + async searchEsimPlans(args: z.infer): Promise { + try { + const url = `${this.baseUrl}/products/search?country=${encodeURIComponent(args.country)}`; + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`HTTP error ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + return JSON.stringify(data, null, 2); + } catch (error) { + return `Error searching eSIM plans: ${getErrorMessage(error)}`; + } + } + + /** + * Purchase an eSIM plan and generate a non-custodial USDT payment invoice. + * + * @param args - Purchase arguments including packageCode, customerEmail, network, and currency. + * @returns JSON string containing payment invoice, exact USDT amount with spot discount offset, and receiving wallet address. + */ + @CreateAction({ + name: "purchase_esim", + description: `Initiates an eSIM purchase and generates a non-custodial USDT payment invoice. +Requires packageCode (from search_esim_plans), customerEmail, and network ('polygon', 'arbitrum', or 'tron'). +Returns payment instructions including receiving address, exact USDT amount to transfer, and expiry timestamp.`, + schema: PurchaseEsimSchema, + }) + async purchaseEsim(args: z.infer): Promise { + try { + const headers: Record = { + "Content-Type": "application/json", + }; + if (this.patToken) { + headers["Authorization"] = `Bearer ${this.patToken}`; + } + + const response = await fetch(`${this.baseUrl}/agentic/checkout`, { + method: "POST", + headers, + body: JSON.stringify({ + packageCode: args.packageCode, + customerEmail: args.customerEmail, + currency: args.currency || "USDT", + network: args.network || "polygon", + }), + }); + + if (response.status !== 200 && response.status !== 402) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + return JSON.stringify(data, null, 2); + } catch (error) { + return `Error purchasing eSIM: ${getErrorMessage(error)}`; + } + } + + /** + * Check order status and download eSIM profile. + * + * @param args - Order status arguments containing invoiceId. + * @returns JSON string containing fulfillment status, ICCID, LPA activation string, and QR code URL. + */ + @CreateAction({ + name: "check_order_status", + description: `Check the on-chain payment settlement and provisioning status for a ConnectMeGuru invoice ID. +When status is COMPLETED, returns the eSIM ICCID, LPA activation code string, and QR code image URL for installation.`, + schema: CheckOrderStatusSchema, + }) + async checkOrderStatus(args: z.infer): Promise { + try { + const url = `${this.baseUrl}/agentic/order/${encodeURIComponent(args.invoiceId)}`; + const response = await fetch(url); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + return JSON.stringify(data, null, 2); + } catch (error) { + return `Error checking order status: ${getErrorMessage(error)}`; + } + } + + /** + * Checks if the action provider supports the given network. + * ConnectMeGuru supports non-custodial USDT payments across EVM (Polygon, Arbitrum) and TRON. + * + * @returns True, as ConnectMeGuru actions are supported across networks. + */ + supportsNetwork(): boolean { + return true; + } +} + +/** + * Creates a new instance of the ConnectMeGuru action provider. + * + * @param config - Optional configuration for ConnectMeGuru Action Provider. + * @returns A new ConnectMeGuruActionProvider instance. + */ +export const connectmeguruActionProvider = (config?: ConnectMeGuruConfig) => + new ConnectMeGuruActionProvider(config); diff --git a/typescript/agentkit/src/action-providers/connectmeguru/constants.ts b/typescript/agentkit/src/action-providers/connectmeguru/constants.ts new file mode 100644 index 000000000..fe640f54c --- /dev/null +++ b/typescript/agentkit/src/action-providers/connectmeguru/constants.ts @@ -0,0 +1,7 @@ +/** + * ConnectMeGuru API Constants + */ +export const CONNECTMEGURU_BASE_URL = "https://www.connectmeguru.com/api"; +export const CONNECTMEGURU_PORTAL_URL = "https://www.connectmeguru.com/developers/agentic"; +export const SUPPORTED_NETWORKS = ["polygon", "arbitrum", "tron"] as const; +export const SUPPORTED_CURRENCY = "USDT" as const; diff --git a/typescript/agentkit/src/action-providers/connectmeguru/index.ts b/typescript/agentkit/src/action-providers/connectmeguru/index.ts new file mode 100644 index 000000000..3bba21f29 --- /dev/null +++ b/typescript/agentkit/src/action-providers/connectmeguru/index.ts @@ -0,0 +1,7 @@ +export { + ConnectMeGuruActionProvider, + connectmeguruActionProvider, + ConnectMeGuruConfig, +} from "./connectmeguruActionProvider"; +export * from "./schemas"; +export * from "./constants"; diff --git a/typescript/agentkit/src/action-providers/connectmeguru/schemas.ts b/typescript/agentkit/src/action-providers/connectmeguru/schemas.ts new file mode 100644 index 000000000..64834ce53 --- /dev/null +++ b/typescript/agentkit/src/action-providers/connectmeguru/schemas.ts @@ -0,0 +1,52 @@ +import { z } from "zod"; + +/** + * Schema for searching available travel eSIM data plans. + */ +export const SearchEsimSchema = z + .object({ + country: z + .string() + .min(2, "Country name or ISO code must be at least 2 characters") + .describe("The destination country name (e.g. 'Japan', 'France', 'United States') or ISO code"), + }) + .strip() + .describe("Parameters for searching available travel eSIM data packages"); + +/** + * Schema for purchasing an eSIM via non-custodial USDT settlement invoice. + */ +export const PurchaseEsimSchema = z + .object({ + packageCode: z + .string() + .min(1, "Package code is required") + .describe("The unique package code identifier of the selected eSIM plan (e.g. 'P4XU0X3CX')"), + customerEmail: z + .string() + .email("A valid email address is required for eSIM profile delivery") + .describe("The delivery email address where eSIM installation credentials and receipt will be sent"), + network: z + .enum(["polygon", "arbitrum", "tron"]) + .default("polygon") + .describe("The blockchain network to settle the USDT payment on ('polygon', 'arbitrum', or 'tron')"), + currency: z + .literal("USDT") + .default("USDT") + .describe("Payment currency, must be USDT"), + }) + .strip() + .describe("Parameters for initiating an eSIM purchase and generating a payment invoice"); + +/** + * Schema for checking the status and retrieving eSIM QR code/LPA string. + */ +export const CheckOrderStatusSchema = z + .object({ + invoiceId: z + .string() + .min(1, "Invoice ID is required") + .describe("The ConnectMeGuru invoice ID returned from purchase_esim (e.g. 'CMG-INV-1718000000-001')"), + }) + .strip() + .describe("Parameters for polling settlement status and downloading the eSIM profile"); diff --git a/typescript/agentkit/src/action-providers/index.ts b/typescript/agentkit/src/action-providers/index.ts index 9f7164086..576bf27b7 100644 --- a/typescript/agentkit/src/action-providers/index.ts +++ b/typescript/agentkit/src/action-providers/index.ts @@ -10,6 +10,7 @@ export * from "./basename"; export * from "./cdp"; export * from "./clanker"; export * from "./compound"; +export * from "./connectmeguru"; export * from "./defillama"; export * from "./dtelecom"; export * from "./enso"; From d4081af98a1a864aac297b96f49e391f2cdfadd7 Mon Sep 17 00:00:00 2001 From: ConnectMeGuru Date: Fri, 18 Sep 2026 07:39:25 +0530 Subject: [PATCH 2/2] feat(connectmeguru): add Base L2 and USDC currency support as default --- .../connectmeguru/connectmeguruActionProvider.ts | 11 ++++++----- .../src/action-providers/connectmeguru/schemas.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.ts b/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.ts index 8ba3fe9dc..d68b18162 100644 --- a/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.ts +++ b/typescript/agentkit/src/action-providers/connectmeguru/connectmeguruActionProvider.ts @@ -84,9 +84,10 @@ Takes the destination country name or ISO code and returns a list of package cod */ @CreateAction({ name: "purchase_esim", - description: `Initiates an eSIM purchase and generates a non-custodial USDT payment invoice. -Requires packageCode (from search_esim_plans), customerEmail, and network ('polygon', 'arbitrum', or 'tron'). -Returns payment instructions including receiving address, exact USDT amount to transfer, and expiry timestamp.`, + description: `Initiates an eSIM purchase and generates a non-custodial USDC or USDT payment invoice. +Requires packageCode (from search_esim_plans), customerEmail, network ('base', 'polygon', 'arbitrum', or 'tron'), and currency ('USDC' or 'USDT'). +Defaults to USDC on Base (Coinbase L2) for sub-cent transaction fees. +Returns payment instructions including receiving address, exact token amount to transfer, and expiry timestamp.`, schema: PurchaseEsimSchema, }) async purchaseEsim(args: z.infer): Promise { @@ -104,8 +105,8 @@ Returns payment instructions including receiving address, exact USDT amount to t body: JSON.stringify({ packageCode: args.packageCode, customerEmail: args.customerEmail, - currency: args.currency || "USDT", - network: args.network || "polygon", + preferredCurrency: args.currency ? args.currency.toUpperCase() : "USDC", + preferredNetwork: args.network ? args.network.toUpperCase() : "BASE", }), }); diff --git a/typescript/agentkit/src/action-providers/connectmeguru/schemas.ts b/typescript/agentkit/src/action-providers/connectmeguru/schemas.ts index 64834ce53..a674e0786 100644 --- a/typescript/agentkit/src/action-providers/connectmeguru/schemas.ts +++ b/typescript/agentkit/src/action-providers/connectmeguru/schemas.ts @@ -27,13 +27,13 @@ export const PurchaseEsimSchema = z .email("A valid email address is required for eSIM profile delivery") .describe("The delivery email address where eSIM installation credentials and receipt will be sent"), network: z - .enum(["polygon", "arbitrum", "tron"]) - .default("polygon") - .describe("The blockchain network to settle the USDT payment on ('polygon', 'arbitrum', or 'tron')"), + .enum(["base", "polygon", "arbitrum", "tron"]) + .default("base") + .describe("The blockchain network to settle the payment on ('base', 'polygon', 'arbitrum', or 'tron'). Defaults to 'base'"), currency: z - .literal("USDT") - .default("USDT") - .describe("Payment currency, must be USDT"), + .enum(["USDC", "USDT"]) + .default("USDC") + .describe("Payment stablecoin currency, 'USDC' or 'USDT'. Defaults to 'USDC'"), }) .strip() .describe("Parameters for initiating an eSIM purchase and generating a payment invoice");