
Check whether an email address is valid before sending: syntax, whether the domain accepts mail (MX), and known-bad address lists. Returns valid and, when invalid, the failed validator. Sends no email.
List Content Builder assets (emails, content blocks, images, templates) with an optional simple $filter (e.g. "assetType.name eq 'htmlemail'", 'category.id eq 123', "name like 'Welcome'") and $orderBy. Returns a lean field set by default; pass fields to widen. Use searchassets for compound AND/OR queries. Paginated: pass pageinfo.next_page as page.
Search Content Builder assets with an advanced query: nested AND/OR operand trees, multiple sort clauses, and a field projection. Use for compound conditions (e.g. created after a date AND of type htmlemail); use list_assets for a single simple filter. Lean fields by default. Paginated via page.
Get one Content Builder asset by ID, including its full content, views, data, and meta. Use listassets or searchassets to find asset IDs.
Create a Content Builder asset. Requires name and assettype {name, id} (e.g. htmlemail=208, htmlblock=197, png=28). Emails take views (html, subjectline); blocks take content; images take base64 file. Call listassetcategories first to pick a folder categoryid. Returns the new asset id and customerKey.
Update a Content Builder asset (partial PATCH): name, description, folder, content, views, data, meta, channels, or asset type. Provide at least one field. Use listassets or searchassets to find assetid and listassetcategories for categoryid.
Delete a Content Builder asset by ID. Use listassets or searchassets to find the asset ID.
List Content Builder categories (folders) with optional $filter (e.g. 'parentId eq 516016'), $orderBy, and scope ('Shared'). Use to find category IDs for createasset, updateasset, and folder tools; the root 'Content Builder' folder has parentId 0. Paginated via page.
Create a Content Builder folder under a parent folder. Requires name and parentid; call listasset_categories first (the root 'Content Builder' folder has parentId 0). Returns the new category id.
Rename or move a Content Builder folder (full PUT: name and parentid are both required). Use listassetcategories to find categoryid and parent_id.
Delete a Content Builder folder by ID. The folder must be empty: move or delete its assets and subfolders first. Use listassetcategories to find the ID and list_assets with filter 'category.id eq <id>' to check contents.
List Automation Studio automations with page-number pagination (pagesize up to 500), optionally sorted (orderby) and filtered (filter). Returns each automation's id, status, and schedule; pass the id to getautomation or runautomation.
Get one automation by ID, including its schedule, status, and steps with their activities. Use list_automations to find automation IDs.
Run an automation once now, performing all of its activities (queries, imports, sends). Use list_automations to find the automation ID. The API defines no stop endpoint, so a started run cannot be cancelled through this connector.
List Marketing Cloud folders of one type, e.g. categorytype 'dataextension'. Call this to get the categoryid that createdataextension requires and that listdataextensions can filter by. For Content Builder folders with paging and filters, use listassetcategories instead.
List Marketing Cloud campaigns with optional $orderBy (e.g. 'createdDate DESC'). Returns campaign IDs for getcampaign, updatecampaign, deletecampaign, and campaign asset tools. Paginated: pass pageinfo.next_page as page.
Get one campaign by ID. Use list_campaigns to find campaign IDs.
Create a campaign. Requires name; optional description, campaigncode, color (hex without '#'), and favorite. Returns the new campaign id for addcampaign_asset and the other campaign tools.
Update a campaign's name, description, campaigncode, color, or favorite flag. Provide at least one field; fields you leave null keep their current values. Use listcampaigns to find the campaign ID.
Delete a campaign by ID. Use list_campaigns to find the campaign ID.
List the assets (emails, journeys, ...) associated with a campaign. Each item's id is the association ID removecampaignasset takes; itemID is the underlying item. Use list_campaigns to find the campaign ID.
Associate one or more items with a campaign, e.g. assettype 'EMAIL' with itemids from listassets. Use listcampaigns for the campaign ID. Returns the association IDs that removecampaignasset takes.
Remove an asset association from a campaign. campaignassetid is the association id from listcampaignassets (or addcampaignasset), NOT the email or item ID.
Find contacts by email address (1-100 addresses). Returns the contact keys that use each address, for updatecontact, deletecontacts, or sendtransactionalemail recipients.
Create a contact with attribute values grouped by attribute set (most accounts require the 'Email Addresses' set). Call listattributesetdefinitions first for valid attribute set and attribute names. Returns contactkey and contact_id.
Update an existing contact's attribute values, grouped by attribute set. Use searchcontactsbyemail to find the contactkey and listattributeset_definitions for valid attribute set and attribute names.
Delete contacts by contact key. Asynchronous: SFMC deletes them after the account's retention period. Returns operationid; poll getcontactdeletestatus with it. Use searchcontactsby_email to find contact keys.
Get the status of a contact delete operation started by deletecontacts. Pass the operationid that delete_contacts returned.
List Contact Builder attribute set definitions and their attributes, with page-number pagination. Call this before createcontact or updatecontact to get valid attribute set names and attribute names.
Read rows from a data extension by external key or ID (exactly one), with page-number pagination (pagesize up to 2500), a filter like "Email eq 'a@b.com'", orderby, and a fields column mask. Use listdataextensions for the key/ID and listdataextension_fields for column names.
Insert or update up to 500 rows synchronously, matched on primary key columns. Use listdataextensions for the external key and listdataextensionfields for column names and primary keys. For larger batches use upsertdataextensionrowsasync. No row-delete API exists; use cleardata_extension.
Queue a large batch of rows for a data extension asynchronously. mode 'upsert' (default) inserts or updates by primary key; 'insert' only adds new rows. Returns requestid; poll getasyncrequeststatus, then getasyncrequestresults. Use listdataextensions for the key and listdataextensionfields for columns.
Get the processing status of an asynchronous data request (requestStatus, resultStatus, hasErrors). The requestid comes from upsertdataextensionrowsasync. When complete, use getasyncrequestresults for per-row outcomes.
Get per-row results (status, errorCode, message) of a completed asynchronous data request, with page-number pagination. The requestid comes from upsertdataextensionrowsasync; check getasyncrequeststatus first.
List data extensions with page-number pagination (page, pagesize; use pageinfo.nextpage). The API requires categoryid (a folder, from listfolders with categorytype 'dataextension') or search (a name fragment); order_by sorts. Returns each data extension's id (for get/update/delete/field tools) and key (for row tools).
Get one data extension's properties (name, key, folder, sendable settings, row count) by ID. Use listdataextensions to find the ID. For its columns use listdataextension_fields.
List a data extension's fields (column names, types, lengths, primary keys, nullability). Call this before upserting or filtering rows to learn valid column names and which are primary keys. Use listdataextensions to find the data extension ID.
Create a data extension with at least one field. Set issendable with sendablecustomobjectfield and sendablesubscriberfield to use it as a send audience. Returns the new id and key. categoryid is required: call listfolders with category_type 'dataextension' first to get a folder ID.
Update a data extension's name, description, sendable/testable flags, or retention settings. Pass at least one change. Use listdataextensions to find the ID. To change columns use createdataextensionfield or updatedataextensionfield.
Permanently delete a data extension and all its rows. Use listdataextensions to find the ID. To remove only the rows and keep the structure use cleardataextension.
Add one or more fields (columns) to an existing data extension. The change is applied asynchronously, so re-check with listdataextensionfields. There is no API to delete a field. Use listdata_extensions to find the ID.
Change existing data extension fields (length, scale, nullability, default value), matched by name. Applied asynchronously; confirm with listdataextensionfields, which also lists field names. Use listdata_extensions to find the data extension ID.
Delete ALL rows from a data extension, keeping its structure. This removes every row and cannot be undone. Use this to remove rows, since the REST API has no single-row delete. Use listdataextensions to find the ID.
List Journey Builder event definitions (entry sources) with their id and eventDefinitionKey, optionally filtered by name. Use the key with fireentryevent or a journey trigger. Paginated: pass pageinfo.nextpage as page.
Get one event definition by eventdefinitionid or key (exactly one). Use listeventdefinitions to find them.
Create a journey entry event (e.g. type 'APIEvent') bound to a data extension. Use listdataextensions for dataextensionid. Returns the new id and eventDefinitionKey for createjourney triggers and fireentry_event.
Replace an event definition (full body: type, name, key, data extension). Call geteventdefinition first and resend fields to keep. Use listeventdefinitions for the ID and listdataextensions for dataextensionid.
Delete an event definition. Journeys that use it as an entry source must be stopped or deleted first. Use listeventdefinitions to find the ID.
List Journey Builder journeys (lean summaries: id, key, version, status). Filter by status, type, tag, version, or name/description text; sort with orderby. Use getjourney for the full definition. Paginated: pass pageinfo.nextpage as page.
Get a journey's full definition (triggers, activities, goals, exits) by journeyid or key (exactly one). Use listjourneys to find IDs and keys. Optionally pick a version_number.
Create a journey from a Journey Spec (key, name, triggers, activities). An API entry trigger references an event definition key: call listeventdefinitions or createeventdefinition first. Returns the new journey id, key, and version; then publish_journey.
Replace a draft journey version's full Journey Spec. Requires journeyid, key, version, and name; call getjourney first and resend every section you want to keep (omitted sections are cleared). Pass modifieddate from getjourney, or null to have the tool read it. Use list_journeys to find IDs.
Delete a journey, or one version with versionnumber. Running journeys must be stopped first with stopjourney. Use listjourneys to find the journeyid.
Start publishing (activating) a journey version asynchronously. Returns a statusid; poll getjourneypublishstatus until it completes. Use listjourneys for journeyid and version. Undo with stop_journey.
Check the progress of a publishjourney request by its statusid. Returns status plus any validation errors and warnings.
Stop a running journey version; contacts exit and it cannot be restarted (inverse of publishjourney). Use listjourneys for journeyid and version. For a temporary halt use pausejourney.
Pause a running journey version (contacts hold in place). Set allversions to pause every version. Undo with resumejourney. Use listjourneys for journeyid and version.
Resume a paused journey version (inverse of pausejourney). Set allversions to resume every version. Use listjourneys for journeyid and version.
Get a journey's audit log (who created, modified, published, unpublished, or deleted it), optionally filtered by action. Use listjourneys for journeyid. Paginated: pass pageinfo.nextpage as page.
Inject a contact into a published journey by firing its API entry event. Use listeventdefinitions for eventdefinitionkey; data must match the event's data extension fields. Returns eventinstanceid. Undo with removecontactsfrom_journey.
Remove up to 50 contacts from journeys (inverse of fireentryevent). Each entry needs a contactkey and the journey key (definitionkey, from listjourneys); omit versions for all versions. Check progress with getjourneyexitstatus.
Check whether contacts removed with removecontactsfromjourney have exited. Pass the same contactkey/definitionkey entries (up to 50). Use listjourneys for journey keys.
List which journeys (key and version) the given contacts are currently in, plus contact keys that were not found. Use before removecontactsfrom_journey to get definition keys.
List transactional email send definitions with page-number pagination (pagesize up to 100, the API maximum). Supports sorting (orderby) and filtering (filter, e.g. 'status eq Active'). Use this to find the definitionkey that get/update/deleteemailsenddefinition and sendtransactionalemail take.
Get one transactional email send definition by its definition key, including its email asset, subscription settings, and options. Use listemailsend_definitions to find definition keys.
Create a transactional email send definition that links a Content Builder email to a subscriber list. Call listassets or getasset first to get the email asset's customerKey. Returns the new definitionkey for sendtransactional_email.
Update a transactional email send definition: name, description, status (Active/Inactive), classification, email asset, subscriptions, or options. Pass at least one field. Use listemailsenddefinitions for the definitionkey and list_assets for a new email customerKey.
Delete a transactional email send definition by definition key. Messages can no longer be sent with it. To stop sending temporarily, use updateemailsenddefinition with status 'Inactive' instead. Use listemailsenddefinitions to find definition keys.
Send a real transactional email to 1-500 recipients using an active send definition. Call listemailsenddefinitions first for the definitionkey. A message key is generated per recipient when omitted; pass the returned messagekeys to getemailmessagestatus.
Get the send status of one transactional email message by message key (e.g. 'TransactionalSendEvents.EmailSent'). Use the messagekeys returned by sendtransactional_email. This endpoint is rate-limited; use it for occasional checks, not tight polling.
Validate Salesforce Marketing Cloud credentials by fetching the token context (enterprise, business unit, and user) for the connected account. Returns success and a message. Use this to confirm the subdomain, client ID, and client secret are correct.

In an mcp.json file, add the configuration below, and restart Cursor.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "url": "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
5 "headers": {
6 "Authorization": "Bearer yMt*****"
7 }
8 }
9 }
10}
11Open your Claude Desktop configuration file and add the server configuration below. You'll also need to restart the application for the changes to take effect.
Make sure Claude is using the Node v20+.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "yMt*****"
14 }
15 }
16 }
17}Open your Windsurf MCP configuration file and add the server configuration below.
Click on the refresh button in the top right of the Manage MCP server page or in the top right of the chat box in the box icon.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api.merge.dev/api/v1/tool-packs/<tool-pack-id>/registered-users/<registered-user-id>/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "<ah-production-access-key>"
14 }
15 }
16 }
17 }In Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows), run "MCP: Open User Configuration".
You can then add the configuration below and press "start" right under servers. Enter the auth token when prompted.
Learn more in the official documentation ↗
1{
2 "inputs": [
3 {
4 "type": "promptString",
5 "id": "agent-handler-auth",
6 "description": "Agent Handler AUTH_TOKEN", // "yMt*****" when prompt
7 "password": true
8 }
9 ],
10 "servers": {
11 "agent-handler": {
12 "type": "stdio",
13 "command": "npx",
14 "args": [
15 "-y",
16 "mcp-remote@latest",
17 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
18 "--header",
19 "Authorization: Bearer ${input:agent-handler-auth}"
20 ]
21 }
22 }
23}Whether you're an engineer experimenting with agents or a product manager looking to add tools, you can get started for free now