Skip to main content
Glama

create_coupon

Generate discount coupons for Stream by specifying name, value, and type (percentage or fixed amount with currency). Configure active status for promotional campaigns.

Instructions

Create a new discount coupon on Stream.

Set is_percentage to True for percentage discount, False for fixed amount. For fixed coupons, currency is required (e.g. SAR, USD).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYes
discount_valueYes
is_percentageNo
currencyNo
is_activeNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The create_coupon handler function decorated with @mcp.tool. It accepts parameters (name, discount_value, is_percentage, currency, is_active), creates a CreateCouponRequest body, gets an API client, and makes a POST request to create the coupon.
    async def create_coupon(
        name: str,
        discount_value: float,
        is_percentage: bool = False,
        currency: str | None = None,
        is_active: bool = True,
        ctx: Context = None,  # type: ignore[assignment]
    ) -> dict[str, Any]:
        """Create a new discount coupon on Stream.
    
        Set *is_percentage* to True for percentage discount, False for fixed amount.
        For fixed coupons, *currency* is required (e.g. SAR, USD).
        """
        body = CreateCouponRequest(
            name=name,
            discount_value=discount_value,
            is_percentage=is_percentage,
            currency=currency,
            is_active=is_active,
        )
        client = await get_client(ctx)
        try:
            return await client.post(_BASE, body.model_dump(exclude_none=True))
        except StreamAPIError as exc:
            return _err(exc)
  • Pydantic BaseModel for validating create_coupon request parameters. Defines fields for name, discount_value, currency, is_percentage, and is_active with proper constraints and descriptions.
    class CreateCouponRequest(BaseModel):
        """Request body for creating a new coupon."""
    
        name: str = Field(..., min_length=1, max_length=80, description="Coupon display name.")
        discount_value: float = Field(..., ge=0, description="Discount value (amount or percentage).")
        currency: str | None = Field(
            default=None,
            description="ISO-4217 currency code. Required when is_percentage is false. Must be null when is_percentage is true.",
        )
        is_percentage: bool = Field(default=False, description="True for percentage discount, false for fixed amount.")
        is_active: bool = Field(default=True, description="Whether the coupon is active.")
  • The register function that defines and registers the create_coupon tool using the @mcp.tool decorator. This function is called during MCP server initialization to register all coupon-related tools.
    def register(mcp: FastMCP) -> None:
        """Register all coupon tools on *mcp*."""
    
        @mcp.tool
        async def create_coupon(
            name: str,
            discount_value: float,
            is_percentage: bool = False,
            currency: str | None = None,
            is_active: bool = True,
            ctx: Context = None,  # type: ignore[assignment]
        ) -> dict[str, Any]:
            """Create a new discount coupon on Stream.
    
            Set *is_percentage* to True for percentage discount, False for fixed amount.
            For fixed coupons, *currency* is required (e.g. SAR, USD).
            """
            body = CreateCouponRequest(
                name=name,
                discount_value=discount_value,
                is_percentage=is_percentage,
                currency=currency,
                is_active=is_active,
            )
            client = await get_client(ctx)
            try:
                return await client.post(_BASE, body.model_dump(exclude_none=True))
            except StreamAPIError as exc:
                return _err(exc)
  • Main registration orchestrator that imports the coupons module and calls coupons.register(mcp) to register all coupon tools including create_coupon.
    def register_all_tools(mcp: FastMCP) -> None:
        """Import every tool / resource module and call its ``register(mcp)``."""
        from stream_mcp.tools import (
            coupons,
            customers,
            docs,
            invoices,
            payment_links,
            payments,
            products,
        )
    
        payment_links.register(mcp)
        customers.register(mcp)
        products.register(mcp)
        payments.register(mcp)
        coupons.register(mcp)
        invoices.register(mcp)
        docs.register(mcp)
  • The get_client helper function used by create_coupon to obtain a StreamClient instance. It handles both local (stdio) and remote (SSE/http) modes by retrieving the client from lifespan context or creating a per-user client from the Bearer token.
    async def get_client(ctx: "Context") -> StreamClient:
        """Return a :class:`StreamClient` for the current request.
    
        Resolution order:
    
        1. **Lifespan client** — used in local / stdio mode where a single
           ``STREAM_API_KEY`` is set as an environment variable.
        2. **Per-user client** — used in remote mode where each user passes
           their own API key as a Bearer token and (optionally) a custom
           base URL via the ``X-Stream-Base-URL`` header.
        """
        # ── 1. Local mode: shared client from server lifespan ─────────────
        shared_client = ctx.lifespan_context.get("client")
        if shared_client is not None:
            return shared_client
    
        # ── 2. Remote mode: per-user client from Bearer token ─────────────
        api_key = current_api_key.get()
        if not api_key:
            raise StreamError(
                "No Stream API key found. "
                "In local mode, set the STREAM_API_KEY env var. "
                "In remote mode, pass your key as a Bearer token in the Authorization header."
            )
    
        base_url = current_base_url.get() or settings.stream_base_url
        cache_key = f"{api_key}::{base_url}"
    
        if cache_key not in _client_cache:
            client = StreamClient(
                api_key=api_key,
                base_url=base_url,
                timeout=settings.stream_timeout,
                max_retries=settings.stream_max_retries,
            )
            await client.__aenter__()
            _client_cache[cache_key] = client
            logger.info(
                "Created cached StreamClient for remote user (key=…%s, base=%s)",
                api_key[-4:], base_url,
            )
    
        return _client_cache[cache_key]
Behavior2/5

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

No annotations are provided, so the description carries the full burden. It mentions that the tool creates a coupon but lacks critical behavioral details: it doesn't specify required permissions, rate limits, whether the operation is idempotent, what happens on failure, or the expected output format. The description only covers basic parameter usage without broader context.

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 highly concise and well-structured: a clear purpose statement followed by two focused sentences explaining key parameter usage. Every sentence adds value without redundancy, and the information is front-loaded for quick understanding.

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

Completeness3/5

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

Given the complexity (a write operation with 5 parameters), no annotations, and an output schema (which reduces the need to describe return values), the description is moderately complete. It covers the core action and some parameter logic but lacks behavioral context (e.g., permissions, side effects) and full parameter documentation, making it adequate but with clear gaps.

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

Parameters3/5

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

Schema description coverage is 0%, so the description must compensate. It explains the semantics of 'is_percentage' and 'currency' parameters, clarifying their roles in percentage vs. fixed discounts. However, it doesn't cover 'name', 'discount_value', or 'is_active', leaving three of five parameters undocumented. This partial coverage meets the baseline for low schema coverage.

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

Purpose4/5

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

The description clearly states the tool's purpose: 'Create a new discount coupon on Stream.' It specifies the verb ('Create') and resource ('discount coupon'), making the action unambiguous. However, it doesn't explicitly differentiate from sibling tools like 'deactivate_coupon' or 'get_coupon' beyond the 'create' action.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., authentication needs), compare it to sibling tools like 'list_coupons' or 'deactivate_coupon', or specify scenarios where coupon creation is appropriate versus other operations.

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/streampayments/stream'

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