MCP server for the Lettr email API — send transactional emails, manage templates, domains, and webhooks from any AI assistant
This server demonstrates good overall definition quality with consistent naming patterns, comprehensive descriptions, and well-structured schemas across 28 tools. All tools follow verb_noun naming convention (list-, create-, get-, delete-, update-, attach-, detach-, subscribe-, unsubscribe-, bulk-). Descriptions are detailed and include important context about API behavior, constraints, and prerequisites. Parameter schemas are properly typed with constraints (enums, minLength, maxLength, minimum, maximum, regex patterns). However, output schemas are not documented in the tool definitions themselves, the source code shows input schemas clearly but output structure is inferred from API responses rather than explicitly declared. Error handling includes some guidance (e.g., 'HTTP 409' mentioned in create-audience-contact) but is not consistently comprehensive across all tools. Security practices are solid: no credentials exposed as parameters (LETTR_API_KEY injected via environment), and permissions are implicit by API design. Tool composition is excellent, tools are single-responsibility, chainable (IDs returned enable downstream calls), and batch variants exist for bulk operations.
Add a single contact to a single list. Both the contact and the list must belong to your team.
Attach multiple contacts to multiple lists at once. Every contact is attached to every list (a cartesian product of contact_ids × list_ids). All IDs must belong to your team.
Create many contacts in one request (max 1000). Provide exactly one of: - `emails` — a flat list of addresses, when every contact gets the same treatment. - `contacts` — one row per contact, when they differ. Each row takes its own `properties`, `list_ids` and `topics`, applied on top of the batch-wide `list_ids`, `topics` and `properties`. A row-level topic `opt_out` beats a batch-level `opt_in`. That is how you keep specific people off a topic that auto-subscribes new contacts, without a second cleanup call. `update_existing` (default false) controls only whether properties are merged into contacts that already exist — submitted keys overwrite, absent keys are preserved. Existing contacts are attached to the requested lists and topics either way. IMPORTANT — this call can partially succeed. Rows that fail validation are skipped and the rest of the batch still commits, so a successful response does NOT mean every row landed. Always read the reported error count back to the user rather than claiming the whole batch was imported. With `contacts`, pass rows through as the user gave them — a malformed address is reported back as a skipped row, so do not drop or "fix" entries yourself first. With `emails`, a single invalid address rejects the whole request, so check them before sending.
Output schemas not explicitly documented in tool definitions. The source shows input schemas clearly (with types, constraints, descriptions) but output structures are inferred from API responses rather than formally declared in the tool registration. This forces LLMs to reason about response structure without explicit guidance.
Some single-word descriptions lacking sufficient context. Tools like detach-contact-from-list ('Remove a single contact from a single list.') and unsubscribe-contact-from-topic ('Unsubscribe a single contact from a single topic.') have minimal descriptions (under 60 chars) that fail to explain when/why to use them vs similar tools or what happens on the backend.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | B | 70 | 2026-07-28+ | v2 |
Delete between 1 and 50 audience lists in a single call. Before using this tool, you MUST double-check with the user. Warn them that this action is irreversible — every listed list is removed and its contacts detached. All IDs must belong to your team.
Detach multiple contacts from multiple lists at once (a cartesian product of contact_ids × list_ids). Before using this tool, you MUST double-check with the user, as it removes many memberships at once.
Subscribe multiple contacts to multiple topics at once. Every contact is subscribed to every topic (a cartesian product of contact_ids × topic_ids). All IDs must belong to your team.
Unsubscribe multiple contacts from multiple topics at once (a cartesian product of contact_ids × topic_ids). Before using this tool, you MUST double-check with the user, as it drops many subscriptions at once.
Create a single audience contact. - `properties` keys must match properties already defined for the team (use list-audience-properties). - When `double_opt_in` is provided, the contact is created in `unverified` status and receives a confirmation email; all four of its fields (from, subject, template_slug, redirect_url) are required. - If the email already exists for the team this fails with HTTP 409 (`resource_already_exists`). That is a client-correctable condition, not an outage — do NOT retry it. Update the existing contact with update-audience-contact, or use bulk-create-audience-contacts with `update_existing` set.
Create a new audience list. The name must be unique within the team.
Define a new custom contact property. The name must start with a lowercase letter and contain only lowercase letters, numbers, and underscores. The type fixes how values are stored and cannot be changed later.
Register a new sending domain with Lettr. The domain starts in pending status until its DKIM record is set up and it is verified. After creation you MUST display the returned DKIM selector/public key to the user so they can configure their DNS.
Delete an audience list. Before using this tool, you MUST double-check with the user that they want to delete this list. Warn them that this action is irreversible — the list is removed and its contacts are detached from it.
Delete a custom contact property. Before using this tool, you MUST double-check with the user that they want to delete this property. Warn them that this action is irreversible and removes the property value from every contact.
Delete a sending domain from Lettr. Before using this tool, you MUST double-check with the user that they want to delete this domain. Warn them that this action is irreversible and will stop all email sending for that domain.
Remove a single contact from a single list.
Retrieve a single contact by ID, including status, custom properties, and the lists and topics it belongs to.
Retrieve a single audience list by its ID, including its current contact count.
Retrieve a single custom contact property by its ID.
Retrieve full details of a sending domain including CNAME, DKIM, DMARC and SPF status, tracking domain configuration, and any detected DNS provider.
List audience contacts with pagination and optional filters. Filter by free-text search (email or name), status, a specific list, or a specific segment.
List the audience (contact) lists for your team, with pagination. Use this to discover list IDs to pass to contact and segment tools.
List the custom contact properties defined for your team, with pagination. Use this to discover which property keys are valid when creating or updating contacts.
List all sending domains registered with your Lettr account. Returns domain names, statuses, and CNAME/DKIM verification state.
Subscribe a single contact to a single topic. Both must belong to your team.
Unsubscribe a single contact from a single topic.
Rename an audience list. The new name must remain unique within the team.
Update a property's fallback value. The name and type are fixed at creation and cannot be changed. Set fallback_value to null to clear it.
Trigger DNS verification for a domain. Checks DKIM, CNAME (when applicable), DMARC and SPF records and returns the full validation report.
Destructive tools lack confirmation mechanisms. Tools like delete-domain, delete-audience-list, bulk-delete-audience-lists, and delete-audience-property include warnings in descriptions ('MUST double-check with the user', 'irreversible') but no built-in confirmation step (dry-run mode or confirmation requirement). Agent behavior depends entirely on LLM memory and instruction following.
Partial-success handling in bulk operations not explicitly documented in tool descriptions. bulk-create-audience-contacts states 'can partially succeed' and 'Always read the reported error count back to the user' but the input/output contract around error arrays and skip counts is not formally specified in the schema provided.
Tool annotations (readOnlyHint, destructiveHint, idempotentHint) not present in source code. Tools are marked with risk categories in the source data (READ_ONLY, WRITE, DESTRUCTIVE, REVERSIBLE) but these are not visible in the MCP tool registration, the server does not emit tool annotations via the MCP protocol.