Skip to main content
Glama
hectortemich

@deonpay/mcp-server

by hectortemich

Create a subscription plan

deonpay_create_subscription

Set up a recurring charge by creating a subscription plan. Specify name, amount in centavos, and billing interval; optionally add trial days and customer controls.

Instructions

Create a new subscription PLAN (template). Use this when the user wants to set up a recurring charge: 'create a $299 monthly plan called Premium'. Required: name, amount (centavos, min 100 = $1 MXN), interval_type. Important: in production the merchant must have an On-Demand NetPay key configured for the active environment, otherwise this returns ondemand_key_missing. trial_days > 0 enables a free trial period (charges a $10 MXN card-validation tx on the first day to verify the card). Optional flags allow_customer_cancel/pause/advance_payments control the customer self-service portal.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYesPlan name shown to subscribers.
amountYesAmount per cycle in CENTAVOS (min 100 = $1 MXN).
interval_typeYesBilling cadence.
descriptionNo
currencyNoISO 3-letter code, default MXN.
interval_countNoMultiplier of interval_type, e.g. interval_type='monthly' + interval_count=3 -> every 3 months.
max_chargesNoHard cap on total charges per subscriber. Null/omitted = unlimited.
trial_daysNoFree trial length in days. 0 = no trial.
product_idNoOptional product to associate (must be active and not stock-tracked).
contract_termsNoTerms shown at checkout.
allow_customer_cancelNoDefault true.
allow_customer_pauseNoDefault false.
allow_advance_paymentsNoDefault false.
max_advance_paymentsNo
portal_enabledNoDefault true.
metadataNo

Implementation Reference

  • The actual execution logic: posts the validated args as JSON body to POST /api/v1/subscriptions via the DeonPay HTTP client, wrapped in safeHandler to catch errors.
    safeHandler(async (args) => {
      return client.post("/subscriptions", compact(args));
    }),
  • Zod-based input validation schema defining all fields for creating a subscription plan: name (required), amount (required, centavos), interval_type (required), plus optionals like description, currency, interval_count, max_charges, trial_days, product_id, contract_terms, customer portal flags, and metadata.
    inputSchema: {
      name: z.string().min(1).max(255).describe("Plan name shown to subscribers."),
      amount: z.number().int().min(100).describe("Amount per cycle in CENTAVOS (min 100 = $1 MXN)."),
      interval_type: IntervalSchema.describe("Billing cadence."),
      description: z.string().max(1000).optional(),
      currency: z.string().length(3).optional().describe("ISO 3-letter code, default MXN."),
      interval_count: z
        .number()
        .int()
        .min(1)
        .max(365)
        .optional()
        .describe("Multiplier of interval_type, e.g. interval_type='monthly' + interval_count=3 -> every 3 months."),
      max_charges: z
        .number()
        .int()
        .min(1)
        .optional()
        .describe("Hard cap on total charges per subscriber. Null/omitted = unlimited."),
      trial_days: z
        .number()
        .int()
        .min(0)
        .max(365)
        .optional()
        .describe("Free trial length in days. 0 = no trial."),
      product_id: z
        .string()
        .uuid()
        .optional()
        .describe("Optional product to associate (must be active and not stock-tracked)."),
      contract_terms: z.string().max(20000).optional().describe("Terms shown at checkout."),
      allow_customer_cancel: z.boolean().optional().describe("Default true."),
      allow_customer_pause: z.boolean().optional().describe("Default false."),
      allow_advance_payments: z.boolean().optional().describe("Default false."),
      max_advance_payments: z.number().int().min(1).optional(),
      portal_enabled: z.boolean().optional().describe("Default true."),
      metadata: z.record(z.unknown()).optional(),
    },
  • Registration of the tool with the MCP server via server.registerTool, called within the registerSubscriptionTools function.
    server.registerTool(
      "deonpay_create_subscription",
      {
        title: "Create a subscription plan",
        description:
          "Create a new subscription PLAN (template). Use this when the user wants to set up a recurring charge: 'create a $299 monthly plan called Premium'. Required: name, amount (centavos, min 100 = $1 MXN), interval_type. Important: in production the merchant must have an On-Demand NetPay key configured for the active environment, otherwise this returns ondemand_key_missing. trial_days > 0 enables a free trial period (charges a $10 MXN card-validation tx on the first day to verify the card). Optional flags allow_customer_cancel/pause/advance_payments control the customer self-service portal.",
        inputSchema: {
          name: z.string().min(1).max(255).describe("Plan name shown to subscribers."),
          amount: z.number().int().min(100).describe("Amount per cycle in CENTAVOS (min 100 = $1 MXN)."),
          interval_type: IntervalSchema.describe("Billing cadence."),
          description: z.string().max(1000).optional(),
          currency: z.string().length(3).optional().describe("ISO 3-letter code, default MXN."),
          interval_count: z
            .number()
            .int()
            .min(1)
            .max(365)
            .optional()
            .describe("Multiplier of interval_type, e.g. interval_type='monthly' + interval_count=3 -> every 3 months."),
          max_charges: z
            .number()
            .int()
            .min(1)
            .optional()
            .describe("Hard cap on total charges per subscriber. Null/omitted = unlimited."),
          trial_days: z
            .number()
            .int()
            .min(0)
            .max(365)
            .optional()
            .describe("Free trial length in days. 0 = no trial."),
          product_id: z
            .string()
            .uuid()
            .optional()
            .describe("Optional product to associate (must be active and not stock-tracked)."),
          contract_terms: z.string().max(20000).optional().describe("Terms shown at checkout."),
          allow_customer_cancel: z.boolean().optional().describe("Default true."),
          allow_customer_pause: z.boolean().optional().describe("Default false."),
          allow_advance_payments: z.boolean().optional().describe("Default false."),
          max_advance_payments: z.number().int().min(1).optional(),
          portal_enabled: z.boolean().optional().describe("Default true."),
          metadata: z.record(z.unknown()).optional(),
        },
      },
      safeHandler(async (args) => {
        return client.post("/subscriptions", compact(args));
      }),
    );
  • safeHandler wraps the handler fn with try/catch, converting thrown errors into structured MCP error results so the LLM sees a clear failure message.
    export function safeHandler<TArgs>(
      fn: (args: TArgs) => Promise<unknown>,
    ): (args: TArgs) => Promise<CallToolResult> {
      return async (args: TArgs) => {
        try {
          const value = await fn(args);
          return jsonResult(value);
        } catch (err) {
          return errorResult(err);
        }
      };
    }
  • compact() strips undefined/null/empty-string entries from the args object before POSTing, ensuring server-side defaults aren't overridden.
    export function compact<T extends Record<string, unknown>>(obj: T): Partial<T> {
      const out: Record<string, unknown> = {};
      for (const [key, value] of Object.entries(obj)) {
        if (value === undefined || value === null) continue;
        if (typeof value === "string" && value.trim() === "") continue;
        out[key] = value;
      }
      return out as Partial<T>;
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses the required fields, amount format (centavos, min 100), trial behavior (charges $10 MXN on first day), error condition (ondemand_key_missing), and the effect of optional flags on customer self-service. While it doesn't mention idempotency or rate limits, the coverage is solid for a create tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured paragraph. It front-loads the purpose and example, then lists required fields, followed by a critical production note, trial behavior, and optional flags. Every sentence earns its place; there is no fluff or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the lack of an output schema, the description could hint at the return value (likely the created plan object), but it doesn't. However, it covers prerequisites, an error condition, and key parameter behaviors. For a tool with 16 parameters, the description provides sufficient context to use the tool correctly, though a note on what is returned would improve completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is high (81%), so the baseline is 3. The description adds value by explaining the centavos unit for amount (not just 'integer'), the min amount in MXN, the special trial_days behavior (card-validation charge), and the collective effect of optional flags. This goes beyond what the schema alone provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('Create') and resource ('new subscription PLAN (template)'), and includes an example use case ('create a $299 monthly plan called Premium'). It distinguishes itself from sibling tools like deonpay_create_checkout_session by focusing on recurring charge template creation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides an explicit usage context ('when the user wants to set up a recurring charge') and important prerequisites (merchant must have On-Demand NetPay key). It also explains the behavior of trial_days and optional flags. However, it doesn't specify when not to use the tool or suggest alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hectortemich/deonpay-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server