{
  "serverInfo": {
    "name": "maacgo-email",
    "title": "MAAC Go Email",
    "version": "0.3.0",
    "description": "Send transactional email, run campaigns, manage contacts and read delivery reports. Free to start; sk_test_ keys record sends without delivering them.",
    "websiteUrl": "https://edm.cresclab.com/mcp"
  },
  "transport": {
    "type": "streamable-http",
    "url": "https://edm.cresclab.com/api/mcp",
    "protocolVersions": [
      "2025-06-18",
      "2025-03-26",
      "2024-11-05"
    ],
    "stateless": true
  },
  "authentication": {
    "required": true,
    "schemes": [
      "bearer",
      "apiKey"
    ],
    "description": "Send an API key from https://edm.cresclab.com/developers as `Authorization: Bearer <key>`, or as `x-maacgo-api-key: <key>`. sk_test_ keys record send_email messages without delivering them; sk_live_ keys deliver and need a verified sending domain, credit or a paid plan. initialize and tools/list work without a key; tools/call needs one. OAuth is not supported.",
    "headers": [
      {
        "name": "Authorization",
        "value": "Bearer {api_key}",
        "description": "Standard header."
      },
      {
        "name": "x-maacgo-api-key",
        "value": "{api_key}",
        "description": "Alternative header (Smithery-style)."
      }
    ],
    "configSchemaUrl": "https://edm.cresclab.com/smithery.config.schema.json"
  },
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "instructions": "MAAC Go Email: transactional email, campaigns and contacts for one account, authenticated by the API key in use. Keys come in two modes and the mode is in the prefix. sk_live_ keys deliver mail. sk_test_ keys only RECORD a message: send_email returns 202 with status \"sent\" and test_mode:true, but nothing reaches any inbox and nothing is charged. Before telling a user an email \"was sent\", check test_mode on the result (or call get_me and read key_mode). If it is a test key and they expect delivery, tell them to create an sk_live_ key at https://edm.cresclab.com/developers. For any email sent more than once, create_template once and then send_email with template_id + variables, rather than inlining HTML on every call. Test keys never deliver: send_email records the message without sending it, and send_campaign refuses a test key. For a campaign, run estimate_campaign before send_campaign and tell the user the recipient count. If a send is refused for credit, show the top-up link from the error.",
  "tools": [
    {
      "name": "send_email",
      "title": "Send email",
      "description": "Send one transactional email — a receipt, an OTP, an order update. Draws on the free monthly allowance first, then prepaid credit. Prefer template_id over inline html for anything sent more than once: the stored template is byte-identical every time and {{variables}} carry the per-message parts. IMPORTANT: a key beginning sk_test_ only RECORDS the message — nothing is delivered, even though the result says status \"sent\". Check test_mode in the result; get_me shows the key mode.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "to": {
            "type": "string",
            "format": "email",
            "description": "Recipient email address (one address)."
          },
          "subject": {
            "type": "string",
            "maxLength": 300,
            "description": "Subject line, up to 300 characters. Required unless template_id is given (then it overrides the template subject)."
          },
          "html": {
            "type": "string",
            "description": "HTML body. Either html, text or template_id is required."
          },
          "text": {
            "type": "string",
            "description": "Plain-text body. Derived from html when omitted."
          },
          "template_id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Id of a stored template (see list_templates / create_template). Supplies subject, html and text."
          },
          "variables": {
            "type": "object",
            "description": "Values for the template's {{variables}}, e.g. {\"first_name\":\"Ada\",\"order_id\":\"1042\"}. A {{name|fallback}} without a value uses the fallback.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "from": {
            "type": "string",
            "description": "Display name shown to the recipient. The address itself is the account's sending address."
          },
          "reply_to": {
            "type": "string",
            "format": "email",
            "description": "Where replies should go."
          }
        },
        "required": [
          "to"
        ]
      },
      "annotations": {
        "title": "Send email",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      }
    },
    {
      "name": "list_templates",
      "title": "List templates",
      "description": "Stored transactional templates on this account (up to 100), with the {{variables}} each one expects. Use a template_id with send_email instead of inlining HTML every time.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "title": "List templates",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_template",
      "title": "Get template",
      "description": "One stored template: name, subject, html, text and the variables it uses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Template id from list_templates."
          }
        },
        "required": [
          "id"
        ]
      },
      "annotations": {
        "title": "Get template",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "create_template",
      "title": "Create template",
      "description": "Save a reusable transactional template. Write {{variable}} or {{variable|fallback}} anywhere in subject, html or text; pass the values as `variables` on send_email. Returns the id to send with. Names are unique per account; an account holds up to 100 templates.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Short unique name, e.g. \"order-confirmation\"."
          },
          "subject": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300,
            "description": "Subject line; may use {{variables}}."
          },
          "html": {
            "type": "string",
            "description": "HTML body. Either html or text is required."
          },
          "text": {
            "type": "string",
            "description": "Plain-text body."
          }
        },
        "required": [
          "name",
          "subject"
        ]
      },
      "annotations": {
        "title": "Create template",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "update_template",
      "title": "Update template",
      "description": "Change the name, subject, html or text of a stored template. Only the fields you pass change.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Template id from list_templates."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "New unique name."
          },
          "subject": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300,
            "description": "New subject line."
          },
          "html": {
            "type": "string",
            "description": "New HTML body."
          },
          "text": {
            "type": "string",
            "description": "New plain-text body."
          }
        },
        "required": [
          "id"
        ]
      },
      "annotations": {
        "title": "Update template",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "delete_template",
      "title": "Delete template",
      "description": "Delete a stored template. This cannot be undone. Messages already sent with it are unaffected.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Template id from list_templates."
          }
        },
        "required": [
          "id"
        ]
      },
      "annotations": {
        "title": "Delete template",
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_message",
      "title": "Get message",
      "description": "Look up one transactional message by id and see whether it was delivered, bounced or is still in flight.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Message id returned by send_email or list_messages."
          }
        },
        "required": [
          "id"
        ]
      },
      "annotations": {
        "title": "Get message",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_messages",
      "title": "List messages",
      "description": "List the 100 most recent transactional messages with their delivery status.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "title": "List messages",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "list_campaigns",
      "title": "List campaigns",
      "description": "List the email campaigns on the account (newest 500) with their status; sent ones carry their delivery funnel, unsent ones the live audience count.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "title": "List campaigns",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_campaign",
      "title": "Get campaign",
      "description": "One campaign in full: name, subject, preview text, sender name, content blocks, audience, status, schedule, review state and web-version link. For delivery results use campaign_report.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Campaign id from list_campaigns."
          }
        },
        "required": [
          "id"
        ]
      },
      "annotations": {
        "title": "Get campaign",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "campaign_report",
      "title": "Campaign report",
      "description": "Delivery report for one campaign: attempted, skipped, sent, bounced, delivered, opened, clicked, plus up to 1,000 per-recipient rows.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Campaign id from list_campaigns."
          }
        },
        "required": [
          "id"
        ]
      },
      "annotations": {
        "title": "Campaign report",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "estimate_campaign",
      "title": "Estimate campaign audience",
      "description": "How many contacts an audience would actually reach, after unsubscribes, bounces, exclusions and the limits on accounts without a verified domain. Nothing is sent or charged. Run this before send_campaign and tell the user the count.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "include": {
            "type": "object",
            "description": "Who to send to. Defaults to {\"type\":\"all\"}.",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "all",
                  "tag",
                  "engaged",
                  "never_engaged"
                ],
                "description": "\"all\" = every contact; \"tag\" = contacts carrying `tag`; \"engaged\" = ever opened or clicked; \"never_engaged\" = never opened or clicked."
              },
              "tag": {
                "type": "string",
                "description": "The tag, when type is \"tag\"."
              }
            },
            "required": [
              "type"
            ]
          },
          "exclude": {
            "type": "object",
            "description": "Who to leave out. Defaults to {\"type\":\"none\"}.",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "none",
                  "all",
                  "tag",
                  "engaged",
                  "never_engaged"
                ],
                "description": "\"all\" = every contact; \"tag\" = contacts carrying `tag`; \"engaged\" = ever opened or clicked; \"never_engaged\" = never opened or clicked; \"none\" = leave nobody out."
              },
              "tag": {
                "type": "string",
                "description": "The tag, when type is \"tag\"."
              }
            },
            "required": [
              "type"
            ]
          }
        }
      },
      "annotations": {
        "title": "Estimate campaign audience",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "send_campaign",
      "title": "Send campaign",
      "description": "Send an existing draft campaign to its saved audience now, or schedule it. Campaigns are written by a person in the editor; API keys cannot create or edit them. Refuses with a priced breakdown if the wallet cannot cover it — nothing is queued or charged in that case. A campaign can be sent only once. Needs a live key (sk_live_…): a test key is refused, because test keys never deliver.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Campaign id from list_campaigns."
          },
          "scheduled_at": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional Unix epoch milliseconds to send at instead of now."
          }
        },
        "required": [
          "id"
        ]
      },
      "annotations": {
        "title": "Send campaign",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      }
    },
    {
      "name": "list_contacts",
      "title": "List contacts",
      "description": "The newest 2,000 contacts on the account with their ids, tags and sendability status, plus every tag in use. The ids are what tag_contacts and contact_activity take.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "title": "List contacts",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "add_contact",
      "title": "Add contact",
      "description": "Add one contact, or update the first name of one already on the list. Only add people who have agreed to hear from this sender. Refused with contact_limit when the plan's contact allowance is full.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "The contact's email address."
          },
          "first_name": {
            "type": "string",
            "description": "First name, used by {{first_name}} in campaigns."
          },
          "last_name": {
            "type": "string",
            "description": "Last name."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags for a new contact, e.g. [\"vip\"]."
          }
        },
        "required": [
          "email"
        ]
      },
      "annotations": {
        "title": "Add contact",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "import_contacts",
      "title": "Import contacts",
      "description": "Add up to 5,000 contacts in one call; rows past 5,000 are dropped and counted as `truncated`. Addresses already on the list and invalid addresses are skipped, not updated. If the new people would exceed the plan's contact allowance the whole call is refused with contact_limit and nothing is added. Only import people who agreed to hear from this sender. Until the account has a verified sending domain, credit or a paid plan, campaigns reach at most 50 of these contacts a day (200 in total); estimate_campaign shows the exact reach.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "rows": {
            "type": "array",
            "minItems": 1,
            "maxItems": 5000,
            "description": "The contacts to add.",
            "items": {
              "type": "object",
              "properties": {
                "email": {
                  "type": "string",
                  "format": "email",
                  "description": "Email address."
                },
                "first_name": {
                  "type": "string",
                  "maxLength": 120,
                  "description": "First name."
                },
                "last_name": {
                  "type": "string",
                  "maxLength": 120,
                  "description": "Last name."
                },
                "tags": {
                  "type": "array",
                  "maxItems": 20,
                  "items": {
                    "type": "string",
                    "maxLength": 40
                  },
                  "description": "Tags for this row (up to 20, 40 characters each, including `tag` below)."
                }
              },
              "required": [
                "email"
              ]
            }
          },
          "tag": {
            "type": "string",
            "maxLength": 40,
            "description": "One tag added to every row, e.g. \"webinar-2026-10\"."
          }
        },
        "required": [
          "rows"
        ]
      },
      "annotations": {
        "title": "Import contacts",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "tag_contacts",
      "title": "Tag or untag contacts",
      "description": "Add a tag to, or remove a tag from, many contacts at once, by the ids list_contacts returns. Contacts that already have (or already lack) the tag are left alone; `updated` counts the ones that changed. This tool never deletes contacts.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "op": {
            "type": "string",
            "enum": [
              "tag",
              "untag"
            ],
            "description": "\"tag\" adds the tag, \"untag\" removes it."
          },
          "ids": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "pattern": "^[0-9]+$"
            },
            "description": "Contact ids from list_contacts."
          },
          "tag": {
            "type": "string",
            "minLength": 1,
            "description": "The tag, e.g. \"vip\"."
          }
        },
        "required": [
          "op",
          "ids",
          "tag"
        ]
      },
      "annotations": {
        "title": "Tag or untag contacts",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "contact_activity",
      "title": "Contact activity",
      "description": "One contact's campaign history, newest first: up to 50 events (sent, delivered, opened, clicked, bounced, complained …) each with its campaign name, plus when and how the contact was added. Transactional messages are not included; see list_messages.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Contact id from list_contacts."
          }
        },
        "required": [
          "id"
        ]
      },
      "annotations": {
        "title": "Contact activity",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "wallet_balance",
      "title": "Wallet balance",
      "description": "Prepaid credit left, this month's usage, and how much of the free allowance remains.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "title": "Wallet balance",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "wallet_events",
      "title": "Wallet ledger",
      "description": "The prepaid-credit ledger, newest first: top-ups, send charges and refunds, each with amount_cents (in the account currency; wallet_balance names it), type, reason and time.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 200,
            "default": 50,
            "description": "How many entries, 1–200. Default 50."
          }
        }
      },
      "annotations": {
        "title": "Wallet ledger",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "quote_cost",
      "title": "Quote send cost",
      "description": "What sending a given number of emails would cost right now, before sending: how many fall inside this month's free allowance, how many are billable, the price in cents of the account currency, and the current balance. It does not look at an audience; for a campaign, get the count from estimate_campaign first.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "minimum": 1,
            "description": "Number of emails to price."
          }
        },
        "required": [
          "count"
        ]
      },
      "annotations": {
        "title": "Quote send cost",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "get_me",
      "title": "Get account",
      "description": "The account this key belongs to: brand, plan, limits, usage — and key_mode (\"test\" keys record sends without delivering; \"live\" keys deliver).",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "title": "Get account",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    }
  ],
  "resources": [],
  "prompts": []
}
