sendhustle-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| MCP_PORT | No | Port for the HTTP transport when using --http (default: 3000). | |
| SENDHUSTLE_API_KEY | Yes | Your SendHustle API key (starts with sh_). Required for stdio mode; optional for HTTP mode (clients pass it via Bearer token). | |
| SENDHUSTLE_BASE_URL | No | API base URL for SendHustle (default: https://api.sendhustle.com). | |
| SENDER_EMAIL_ADDRESS | No | Default sender email address from a verified domain (optional). If not provided, the `send_email` and `create_campaign` tools require a `from` argument on each call. | |
| REPLY_TO_EMAIL_ADDRESSES | No | Comma-separated default reply-to email addresses (optional). |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| send_emailA | Send a single email through SendHustle (POST /emails). Supports HTML and/or plain text, CC/BCC, reply-to, scheduling, attachments, tags, and custom headers. The "from" address must be on a verified domain; if omitted, the server default sender is used. |
| retrieve_emailA | Retrieve a sent email and its events by id (GET /emails/:id). |
| list_emailsA | List sent emails with cursor pagination (GET /emails). |
| create_domainA | Create a sender domain (POST /domains). The response includes DNS records you must add at your DNS provider before verifying. |
| list_domainsB | List sender domains (GET /domains). |
| create_audienceA | Create an audience — a named list of contacts (POST /audiences). |
| list_audiencesC | List audiences (GET /audiences). |
| create_contactA | Create a contact in a sending domain's contact pool (POST /contacts, domain-first). The same address on two domains is two records with separate consent. |
| list_contactsC | List a sending domain's contacts (GET /contacts?domain=..., domain-first). Optional segment_id restricts to that segment's members. |
| create_campaignA | Create a campaign targeting a sending domain's contact pool (POST /campaigns, domain-first). Send it later with send_campaign. |
| send_campaignA | Send a campaign now, or schedule it with scheduled_at (POST /campaigns/:id/send). |
| send_batch_emailsA | Send up to 100 emails in one request (POST /emails/batch). Pass an array of email objects, each with from/to/subject and html and/or text. Per-email "from" defaults to the configured sender if omitted. |
| update_emailA | Reschedule a scheduled email by setting a new send time (PATCH /emails/:id). |
| cancel_emailA | Cancel a scheduled email before it sends (POST /emails/:id/cancel). |
| list_email_attachmentsA | List a sent email's attachments (GET /emails/:id/attachments). |
| get_email_attachmentA | Retrieve metadata for one attachment of a sent email (GET /emails/:id/attachments/:attachmentId). Returns a download_url for the file. |
| list_received_emailsA | List inbound (received) emails with cursor pagination (GET /emails/receiving). |
| get_received_emailA | Retrieve a single received email by id (GET /emails/receiving/:id). |
| get_received_email_rawA | Download the original RFC822/MIME message of a received email (GET /emails/receiving/:id/raw). Returns the raw message as text. |
| forward_received_emailB | Forward a received email to other recipients (POST /emails/receiving/:id/forward). "from" must be a verified sending address; defaults to the configured sender. |
| list_received_email_attachmentsA | List a received email's attachments as metadata with download URLs (GET /emails/receiving/:id/attachments). |
| get_received_email_attachmentA | Retrieve metadata for one attachment of a received email, including its download_url. (GET /emails/receiving/:id/attachments/:attachmentId serves the raw bytes; this tool returns the matching metadata entry from the attachment list instead.) |
| delete_received_emailA | Permanently delete a received email, including its stored raw message and attachments (DELETE /emails/receiving/:id). This cannot be undone. |
| get_domainA | Retrieve a domain, including its DNS records and verification status (GET /domains/:id). |
| check_domain_mxA | MX preflight for a hostname before enabling receiving (GET /domains/mx-check). Returns { has_mx, ours, records } — whether the hostname already has MX records and whether they all point at our inbound host. |
| update_domainB | Update a domain's tracking/TLS/capabilities settings (PATCH /domains/:id). |
| verify_domainA | Trigger verification of a domain after its DNS records are in place (POST /domains/:id/verify). |
| delete_domainB | Delete a domain (DELETE /domains/:id). |
| claim_domainA | Claim a domain already verified elsewhere (POST /domains/claim). Returns a TXT record to add. |
| detect_domain_dnsA | Detect a domain's DNS provider and the one-click apply methods available (GET /domains/:id/dns/detect). |
| apply_domain_dns_cloudflareA | Apply a domain's DNS records via the Cloudflare API, then auto-verify (POST /domains/:id/dns/cloudflare). Requires a scoped Zone.DNS:Edit token. |
| apply_domain_dns_godaddyB | Apply a domain's DNS records via the GoDaddy API, then auto-verify (POST /domains/:id/dns/godaddy). |
| apply_domain_dns_namecheapA | Apply a domain's DNS records via the Namecheap API, then auto-verify (POST /domains/:id/dns/namecheap). Namecheap requires the calling server's IP to be whitelisted on the account. |
| get_domain_claimA | Retrieve the current claim (and its TXT verification record) for a placeholder domain (GET /domains/:id/claim). |
| verify_domain_claimA | Trigger DNS verification for a pending domain claim (POST /domains/:id/claim/verify). |
| get_audienceA | Retrieve an audience by id (GET /audiences/:id). |
| update_audienceB | Rename an audience (PATCH /audiences/:id). |
| delete_audienceA | Delete an audience and its contacts (DELETE /audiences/:id). |
| get_contactA | Retrieve a contact (GET /contacts/:id). A contact id is exact; an EMAIL can exist in several domains' pools, so pass |
| update_contactA | Update a contact by id or email (PATCH /contacts/:id). Pass |
| delete_contactA | Delete a contact by id or email (DELETE /contacts/:id). Pass |
| add_contact_to_segmentA | Add a contact to a segment (POST /contacts/:id/segments/:segmentId). |
| list_contact_segmentsA | List the segments a contact belongs to (GET /contacts/:id/segments). |
| remove_contact_from_segmentA | Remove a contact from a segment (DELETE /contacts/:id/segments/:segmentId). |
| get_contact_topicsA | Get a contact's topic subscriptions (GET /contacts/:id/topics). |
| update_contact_topicsA | Update a contact's topic subscriptions (PATCH /contacts/:id/topics). Provide topics as an array of { id, subscription: 'opt_in' | 'opt_out' }. |
| create_contact_propertyA | Create a custom contact property / merge tag (POST /contact-properties). type is 'string' or 'number'. Idempotent on key: if a property with the same key already exists, returns that property (with already_existed: true) instead of an error. |
| reply_received_emailA | Reply to a received email's sender, threaded into the same conversation (POST /emails/receiving/:id/reply). |
| import_sheet_contactsA | Import contacts from a link-shared Google Sheet (POST /audiences/:id/contacts/import-sheet). Sheet headers become contact properties usable as {{merge_tags}}; rows land in a fresh segment. |
| import_contacts_batchA | Bulk-import contacts from a JSON array (POST /audiences/:id/contacts/batch). Upserts by email; max 10,000 per call. Each contact: { email, first_name?, last_name?, unsubscribed?, properties? }. |
| import_contacts_csvB | Bulk-import contacts from CSV text (POST /audiences/:id/contacts/import). Header row optional; the email column is required. Non-builtin columns auto-register as custom properties unless create_properties is false. Max 5 MB / 10,000 rows. |
| list_pollsC | List emails that have in-email poll responses, with per-email counts (GET /polls). |
| get_poll_resultsA | Get the aggregated answer breakdown for one email's poll (GET /polls/:emailId). |
| list_contact_propertiesC | List contact properties (GET /contact-properties). |
| get_contact_propertyA | Retrieve a contact property by id (GET /contact-properties/:id). |
| update_contact_propertyA | Update a contact property's fallback value (PATCH /contact-properties/:id). key/type are immutable. |
| delete_contact_propertyA | Delete a contact property (DELETE /contact-properties/:id). |
| create_segmentA | Create a segment on a sending domain (POST /segments, domain-first). Segment names are unique WITHIN a domain but reusable across domains. filter supports { status, email_contains, property_filters }. |
| list_segmentsB | List a sending domain's segments (GET /segments?domain=...). Every domain includes an auto-created "General" (all contacts) segment. |
| get_segmentA | Retrieve a segment by id (GET /segments/:id). |
| list_segment_contactsA | Preview the contacts a segment currently resolves to (GET /segments/:id/contacts). |
| update_segmentA | Update a segment's name or filter (PATCH /segments/:id). |
| delete_segmentA | Delete a segment (DELETE /segments/:id). |
| create_topicB | Create a subscription topic on a sending domain (POST /topics, domain-first). default_subscription is 'opt_in' or 'opt_out'; visibility is 'public' or 'private'. |
| list_topicsA | List a sending domain's subscription topics (GET /topics?domain=...). |
| get_topicA | Retrieve a topic by id (GET /topics/:id). |
| update_topicC | Update a topic (PATCH /topics/:id). |
| delete_topicA | Delete a topic (DELETE /topics/:id). |
| get_campaignA | Retrieve a campaign by id (GET /campaigns/:id). |
| get_campaign_statsB | Per-campaign analytics — counts, engagement rates, and top clicked links (GET /campaigns/:id/stats). |
| get_campaign_abA | A/B winner evaluation for an A/B-test campaign (GET /campaigns/:id/ab). |
| list_campaignsC | List campaigns (GET /campaigns). |
| update_campaignB | Update a draft campaign's content or targeting (PATCH /campaigns/:id). |
| cancel_campaignA | Cancel a scheduled campaign, returning it to draft (POST /campaigns/:id/cancel). |
| delete_campaignA | Delete a campaign (DELETE /campaigns/:id). |
| create_templateA | Create a reusable email template (POST /templates). Supports subject/html/text with {{ variables }} and optional variable definitions. |
| list_templatesC | List templates (GET /templates). |
| get_templateA | Retrieve a template by id (GET /templates/:id). |
| update_templateC | Update a template (PATCH /templates/:id). |
| duplicate_templateA | Duplicate a template (POST /templates/:id/duplicate). |
| publish_templateA | Publish a template, making its latest draft live (POST /templates/:id/publish). |
| delete_templateB | Delete a template (DELETE /templates/:id). |
| create_automationA | Create an automation triggered by an event (POST /automations). Every automation belongs to one sending domain — only send_event calls with the same domain trigger it. Optionally pass an inline step graph (steps + connections). |
| list_automationsC | List automations (GET /automations). |
| get_automationA | Retrieve an automation, including its step graph (GET /automations/:id). |
| add_automation_stepA | Append a step to an automation (POST /automations/:id/steps). |
| update_automation_stepA | Update a step in place (PATCH /automations/:id/steps/:stepId). The graph key stays stable, so connections keep pointing at it. Disable the automation first if it is enabled. |
| delete_automation_stepA | Delete a step from an automation (DELETE /automations/:id/steps/:stepId). Stop (disable) the automation first if it is enabled. |
| update_automationA | Update an automation's name, status, domain, or connection graph (PATCH /automations/:id). Enabling requires at least one step; editing connections or the domain requires the automation to be disabled. |
| list_automation_runsA | List an automation's runs (GET /automations/:id/runs). Filter with status (comma-separated: running,completed,failed,cancelled,skipped). |
| get_automation_runA | Retrieve a single automation run, including its step-by-step trace (GET /automations/:id/runs/:runId). |
| stop_automationA | Stop an automation — prevents new runs; in-progress runs finish (POST /automations/:id/stop). |
| delete_automationA | Delete an automation (DELETE /automations/:id). |
| create_webhookA | Create a webhook endpoint (POST /webhooks). The signing secret is returned ONCE on create — store it. |
| list_webhooksC | List webhooks (GET /webhooks). |
| get_webhookA | Retrieve a webhook by id (GET /webhooks/:id). |
| update_webhookB | Update a webhook's endpoint, events, or status (PATCH /webhooks/:id). |
| delete_webhookB | Delete a webhook (DELETE /webhooks/:id). |
| rotate_webhook_secretA | Rotate a webhook signing secret (POST /webhooks/:id/rotate). The new secret is returned ONCE; the old secret stops verifying immediately. |
| test_webhookA | Send a synchronous test delivery and return the endpoint's live result (POST /webhooks/:id/test). |
| verify_webhook_signatureA | Locally verify a webhook delivery's Svix-style signature (no API call). Provide the exact raw request body, the svix-id/svix-timestamp/svix-signature headers, and your endpoint signing secret. Returns { valid, reason? }. |
| list_logsC | List API request logs (GET /logs). |
| get_logA | Retrieve a single API log entry, including request/response bodies (GET /logs/:id). |
| send_eventA | Send a custom event that automations can trigger on (POST /events/send). |
| create_eventA | Create a custom-event definition (POST /events). Optional flat schema (key → 'string'|'number'|'boolean'|'date') validates /events/send payloads (422 on mismatch). |
| list_eventsB | List custom-event definitions (GET /events). |
| update_eventA | Update a custom-event definition's payload schema (PATCH /events/:id). The event NAME is immutable (renaming would detach automations that reference it) — create a new event to rename. Pass |
| delete_eventA | Delete a custom-event definition (DELETE /events/:id). Existing automations keep their trigger names; this only removes the definition + its payload validation. |
| create_api_keyA | Create an API key (POST /api-keys). The token is returned ONCE. permission is 'full_access' or 'sending_access'. |
| list_api_keysA | List API keys (GET /api-keys). |
| delete_api_keyA | Delete (revoke) an API key (DELETE /api-keys/:id). |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 111 tools
Almost every tool maps to a distinct resource+action, and the descriptions are specific. A few pairs could be confused—create_event (definition) vs send_event, and get_email_attachment vs get_received_email_attachment—but the names plus descriptions largely disambiguate them.
Tools overwhelmingly follow a snake_case verb_noun pattern (create_domain, list_contacts, update_campaign). Minor inconsistency: retrieve_email instead of get_email, and several synonyms for similar actions (stop/cancel/delete, verify/check) slightly soften the pattern.
111 tools is an extreme count; even if each maps to a distinct endpoint, the set is far beyond the well-scoped range and will burden an agent's selection. This aligns with the 50+ overgrown category.
The surface covers full lifecycles for domains, audiences, contacts, properties, segments, topics, templates, campaigns, sent/received emails, automations, events, webhooks, API keys, logs, and polls. There are no obvious dead ends or missing CRUD operations for the apparent email-platform domain.