Connect your AI Agents to Salesforce Marketing Cloud in minutes

Merge lets you securely connect your agents to Salesforce Marketing Cloud and thousands of tools instantly
Salesforce Marketing Cloud
Learn more about Merge Agent Handler

Available tools

validate_email_address

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_assets

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_assets

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_asset

Get one Content Builder asset by ID, including its full content, views, data, and meta. Use listassets or searchassets to find asset IDs.

create_asset

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_asset

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_asset

Delete a Content Builder asset by ID. Use listassets or searchassets to find the asset ID.

list_asset_categories

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_asset_category

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.

update_asset_category

Rename or move a Content Builder folder (full PUT: name and parentid are both required). Use listassetcategories to find categoryid and parent_id.

delete_asset_category

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_automations

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_automation

Get one automation by ID, including its schedule, status, and steps with their activities. Use list_automations to find automation IDs.

run_automation

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_folders

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_campaigns

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_campaign

Get one campaign by ID. Use list_campaigns to find campaign IDs.

create_campaign

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_campaign

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_campaign

Delete a campaign by ID. Use list_campaigns to find the campaign ID.

list_campaign_assets

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.

add_campaign_asset

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_campaign_asset

Remove an asset association from a campaign. campaignassetid is the association id from listcampaignassets (or addcampaignasset), NOT the email or item ID.

search_contacts_by_email

Find contacts by email address (1-100 addresses). Returns the contact keys that use each address, for updatecontact, deletecontacts, or sendtransactionalemail recipients.

create_contact

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_contact

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

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_contact_delete_status

Get the status of a contact delete operation started by deletecontacts. Pass the operationid that delete_contacts returned.

list_attribute_set_definitions

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.

get_data_extension_rows

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.

upsert_data_extension_rows

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.

upsert_data_extension_rows_async

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_async_request_status

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_async_request_results

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

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_data_extension

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_data_extension_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_data_extension

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_data_extension

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.

delete_data_extension

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.

create_data_extension_field

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.

update_data_extension_field

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.

clear_data_extension

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_event_definitions

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_event_definition

Get one event definition by eventdefinitionid or key (exactly one). Use listeventdefinitions to find them.

create_event_definition

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.

update_event_definition

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_event_definition

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_journeys

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_journey

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_journey

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.

update_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_journey

Delete a journey, or one version with versionnumber. Running journeys must be stopped first with stopjourney. Use listjourneys to find the journeyid.

publish_journey

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.

get_journey_publish_status

Check the progress of a publishjourney request by its statusid. Returns status plus any validation errors and warnings.

stop_journey

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_journey

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_journey

Resume a paused journey version (inverse of pausejourney). Set allversions to resume every version. Use listjourneys for journeyid and version.

get_journey_audit_log

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.

fire_entry_event

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_contacts_from_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.

get_journey_exit_status

Check whether contacts removed with removecontactsfromjourney have exited. Pass the same contactkey/definitionkey entries (up to 50). Use listjourneys for journey keys.

list_contact_journeys

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_email_send_definitions

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_email_send_definition

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_email_send_definition

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_email_send_definition

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_email_send_definition

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_transactional_email

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_email_message_status

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_credential

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.

View all tools by creating a free accountSee more tools

How to set up Merge Agent Handler

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}
11
Copy Code

Open 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}
Copy Code
Copied!

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  }
Copy Code

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}
Copy Code

FAQs on using Merge's Salesforce Marketing Cloud  MCP server

FAQs on using Merge's Salesforce Marketing Cloud  MCP server

Explore other MCP servers built and managed by Merge

dealops
Dealops
deel
Deel
discord
Discord
docrio
Docrio
docusign
DocuSign
doordash
DoorDash
dropbox
Dropbox
duffel
Duffel
dynamics365
Dynamics 365 Sales
exa
Exa
excel_gcc
Excel GCC High
expensify
Expensify
figma
Figma
firecrawl
Firecrawl
fireflies
Fireflies
firehydrant
FireHydrant
foursquare
Foursquare
frameio
Frame.io
freshbooks
FreshBooks
freshdesk
Freshdesk
freshservice
Freshservice
front
Front
gamma
Gamma
github
GitHub

Ready to try it out?

Whether you're an engineer experimenting with agents or a product manager looking to add tools, you can get started for free now