mcp-interaction-studio
v1.8.1
Published
MCP server for Salesforce Marketing Cloud Personalization — datasets, campaigns, executive audits (markdown + DOCX), governance, consumption, and gated write tools
Maintainers
Readme
mcp-interaction-studio
MCP (Model Context Protocol) server for Salesforce Interaction Studio / Marketing Cloud Personalization admin APIs.
Connect Cursor, Claude Desktop, or any MCP client to your Interaction Studio instance to list datasets, campaigns, segments, templates, recipes, customer profiles, event streams, sitemaps, and campaign performance stats through natural language.
Current version: 1.8.1 · 31 tools · Session hot-reload via config.env + optional bookmarklet sidecar
What's new since 1.3.0
- Site & customer tools —
get_sitemap,find_customers,get_event_stream - Web templates —
list_web_templates(distinct from item templates inlist_templates) - Campaign & segment writes —
clone_campaign,create_web_campaign,clone_segment,create_segment - QA write sandbox — all write tools restricted to dataset
meublesrdqa(Meubles RD QA) - Dataset picker —
get_sitemap,find_customers, andget_event_streamlist datasets whendatasetis omitted
How it works
Interaction Studio’s admin UI uses undocumented internal APIs under /internal/.... This server authenticates the same way your browser does: with a session cookie (JSESSIONID) from an active UI login.
| Auth method | Used for |
|-------------|----------|
| JSESSIONID session cookie | All tools in this package (/internal/*) |
| API token (Basic auth) | Public /api/* endpoints only — not used by this MCP yet |
Prerequisites
- Node.js 18+
- Access to Interaction Studio admin UI (Gears) for your org
- An MCP host (e.g. Cursor, Claude Desktop)
Step 1 — Find your base URL
Log into Interaction Studio. The URL in your browser looks like:
https://{accountName}.{instance}.evergage.com/ui/...Your base URL is everything before /ui:
https://{accountName}.{instance}.evergage.comExample: if you seehttps://demo.us-1.evergage.com/ui/
then put this in your config.env (see Step 2):
base_url=https://demo.us-1.evergage.comDo not include a trailing slash.
You can also find this from any Network request to *.evergage.com — use the scheme + host only.
Step 2 — Session (JSESSIONID) without constant MCP restarts
The admin API uses a browser session cookie (JSESSIONID). It expires after idle timeout or logout.
Recommended (v1.3+): store connection details in a single config.env file. Optionally run a session sidecar so you can refresh the cookie from the browser with one click — no MCP restart.
Option A — config.env (recommended)
Point MCP at one dotenv-style file with both base_url and session_id:
"env": {
"IS_CONFIG_FILE": "/Users/you/.config/mcp-interaction-studio/config.env",
"IS_SESSION_SYNC_URL": "http://127.0.0.1:4043/session",
"IS_TLS_INSECURE": "true"
}Remove IS_BASE_URL, IS_SESSION_ID, IS_SESSION_FILE, and IS_BASE_URL_FILE from mcp.json when using config.env.
One-time setup:
mkdir -p ~/.config/mcp-interaction-studio
cat > ~/.config/mcp-interaction-studio/config.env <<'EOF'
base_url=https://demo.us-1.evergage.com
session_id=YOUR-JSESSIONID-HERE
EOF
chmod 600 ~/.config/mcp-interaction-studio/config.envMigrate from legacy two-file setup (base-url + session):
cat > ~/.config/mcp-interaction-studio/config.env <<EOF
base_url=$(cat ~/.config/mcp-interaction-studio/base-url | tr -d '[:space:]')
session_id=$(cat ~/.config/mcp-interaction-studio/session | tr -d '[:space:]')
EOF
chmod 600 ~/.config/mcp-interaction-studio/config.envThen switch mcp.json to IS_CONFIG_FILE only.
Getting JSESSIONID manually (DevTools)
- Log into Interaction Studio at
{base_url}/ui/ - DevTools → Network → any
*.evergage.comrequest → Cookie header - Copy the value after
JSESSIONID=up to the next; - Paste into
config.envassession_id=..., or pass to theupdate_sessionMCP tool
Security: Treat
JSESSIONIDlike a password. Do not commitconfig.envto git.
Option B — Session sidecar + bookmarklet (easiest refresh)
What the sidecar does
The sidecar is a tiny local HTTP server (default port 4043) that sits between your browser and MCP:
- You log into Interaction Studio normally in Chrome (SSO/MFA as usual).
- You click a bookmarklet while on any
*.evergage.comtab. - The bookmarklet reads the
JSESSIONIDcookie from that page and POSTs it (plusbase_urlfromlocation.origin) to the sidecar onlocalhost. - The sidecar writes both values into your
config.env. - MCP picks up the new session on the next tool call (or after a 401 auto-retry) — no MCP restart.
This is not auto-login or OAuth. MCP cannot open Chrome and sign you in; the bookmarklet only copies a cookie from a tab where you are already logged in.
Start the sidecar
From npm (npx or global install):
npx mcp-interaction-studio-sidecarOr after npm install -g mcp-interaction-studio:
mcp-interaction-studio-sidecarFrom a cloned repo:
npm run session-sidecarThen open http://127.0.0.1:4043/ → drag Sync IS Session → MCP to your bookmarks bar → log into Interaction Studio → click the bookmarklet on any *.evergage.com tab.
In Cursor, run update_session with sync_from_sidecar=true, or retry your tool (MCP auto-reloads config.env).
Sidecar flow
You (manual) Browser Sidecar MCP
| | | |
|-- start sidecar -->| listens :4043 | |
|-- log into IS ---->| cookie set on evergage | |
|-- click bookmarklet| POST /session ---------->| writes config.env |
|-- update_session or retry tool --------------|------------------->| reads config.envOption C — Legacy static env vars
Still supported for older setups:
"IS_BASE_URL": "https://demo.us-1.evergage.com",
"IS_SESSION_ID": "paste-your-JSESSIONID-here"When the session expires, use MCP tool update_session with a new session_id instead of restarting MCP.
Connection resolution order
When MCP needs base_url or session_id, it checks in this order:
- In-memory (
update_sessiontool) IS_CONFIG_FILE(config.env)- Legacy env:
IS_BASE_URL,IS_SESSION_ID - Legacy files:
~/.config/mcp-interaction-studio/base-urlandsession(fallback whenconfig.envis empty)
Auto-retry on 401
When a tool gets 401 Unauthorized, MCP automatically:
- Re-reads
IS_CONFIG_FILE(e.g. after bookmarklet sync) - Fetches
IS_SESSION_SYNC_URLonce (sidecar) - Retries the request
If still unauthorized, refresh via DevTools, edit config.env, or sync again with the bookmarklet.
Three ways to refresh (no MCP restart)
| Method | When to use |
|--------|-------------|
| update_session MCP tool | Paste new session_id / base_url in chat |
| Edit config.env | Update session_id= in IS_CONFIG_FILE; MCP reloads on next request |
| Sidecar + bookmarklet | One-click sync while logged into Gears |
Step 3 — Install the MCP server
Option A — Run with npx (recommended)
No global install needed:
npx mcp-interaction-studioOption B — Global install
npm install -g mcp-interaction-studio
mcp-interaction-studio # MCP server (stdio)
mcp-interaction-studio-sidecar # optional: session bookmarklet helperOption C — Clone and run locally
git clone <your-repo-url>
cd mcp-interaction-studio
npm install
npm startStep 4 — Configure your MCP host
Cursor
Open Cursor Settings → MCP (or edit ~/.cursor/mcp.json):
{
"mcpServers": {
"interaction-studio": {
"command": "npx",
"args": ["-y", "mcp-interaction-studio"],
"env": {
"IS_CONFIG_FILE": "/Users/you/.config/mcp-interaction-studio/config.env",
"IS_SESSION_SYNC_URL": "http://127.0.0.1:4043/session",
"IS_DEFAULT_DATASET": "my-dataset",
"IS_TLS_INSECURE": "true"
}
}
}
}If installed globally:
{
"mcpServers": {
"interaction-studio": {
"command": "mcp-interaction-studio",
"env": {
"IS_CONFIG_FILE": "/Users/you/.config/mcp-interaction-studio/config.env",
"IS_SESSION_SYNC_URL": "http://127.0.0.1:4043/session",
"IS_DEFAULT_DATASET": "my-dataset",
"IS_TLS_INSECURE": "true"
}
}
}
}Restart MCP in Cursor Settings after changing config.
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"interaction-studio": {
"command": "npx",
"args": ["-y", "mcp-interaction-studio"],
"env": {
"IS_CONFIG_FILE": "/Users/you/.config/mcp-interaction-studio/config.env",
"IS_SESSION_SYNC_URL": "http://127.0.0.1:4043/session",
"IS_DEFAULT_DATASET": "my-dataset",
"IS_TLS_INSECURE": "true"
}
}
}
}Environment variables
| Variable | Required | Description |
|----------|----------|-------------|
| IS_CONFIG_FILE | Recommended | Path to dotenv-style connection file (base_url= + session_id=); hot-reloads without MCP restart (default: ~/.config/mcp-interaction-studio/config.env) |
| IS_SESSION_SYNC_URL | No | Local sidecar URL for bookmarklet sync (default: http://127.0.0.1:4043/session) |
| IS_BASE_URL | Legacy | Static host URL; prefer IS_CONFIG_FILE |
| IS_SESSION_ID | Legacy | Static JSESSIONID; use update_session tool when it expires |
| IS_SESSION_FILE | Deprecated | Legacy single-line session file; use IS_CONFIG_FILE |
| IS_BASE_URL_FILE | Deprecated | Legacy single-line base URL file; use IS_CONFIG_FILE |
| IS_DEFAULT_DATASET | No | Default dataset name when a tool omits the dataset parameter |
| IS_TLS_INSECURE | No | Set to true if curl/Node fails with SSL certificate errors (common behind Zscaler/corporate proxies). Equivalent to curl -k. |
| IS_WRITES_ENABLED | No | Set to true to enable write tools (clone_campaign, create_web_campaign, clone_segment, create_segment, set_campaign_state, set_segment_enabled). Off by default. All writes restricted to QA dataset meublesrdqa. |
| NODE_EXTRA_CA_CERTS | No | Path to your corporate root CA .pem file — preferred over IS_TLS_INSECURE for production use |
Copy .env.example to .env when running locally:
cp .env.example .env
# edit .env, then:
npm startCorporate SSL (Zscaler / proxy)
If you see:
SSL certificate problem: unable to get local issuer certificateQuick fix (dev): set IS_TLS_INSECURE=true
Proper fix: export your org’s Zscaler/corporate root CA and set:
export NODE_EXTRA_CA_CERTS=/path/to/corporate-root.pemThen remove IS_TLS_INSECURE.
Session expiration
When JSESSIONID expires, tools return 401 with refresh instructions.
Fix without restarting MCP:
update_session— paste newsession_idin chat, orsync_from_sidecar=trueafter bookmarklet- Edit
config.env— updatesession_id=inIS_CONFIG_FILE(auto-reload on next request) - Sidecar + bookmarklet —
npx mcp-interaction-studio-sidecar→ click bookmarklet while logged into Gears - DevTools — copy fresh
JSESSIONIDfrom Network tab → paste intoconfig.envorupdate_session
Available tools (31)
Set IS_DEFAULT_DATASET to skip passing dataset on most calls. Tools marked picker list available datasets when dataset is omitted.
Session & connection
| Tool | Description |
|------|-------------|
| check_session | Validate session and list datasets (use when you get 401 errors) |
| update_session | Set JSESSIONID / base URL at runtime or sync from local sidecar — no MCP restart |
Datasets
| Tool | Description |
|------|-------------|
| list_datasets | All datasets in the account |
| get_dataset | Dataset metadata and content zones |
Site config & customers
| Tool | Description |
|------|-------------|
| get_sitemap | Web SDK sitemap (siteConfig): page types, content zones, tracking flags (picker) |
| find_customers | Search customers by email or id prefix; returns profile, segments, activity (picker) |
| get_event_stream | Recent Event Stream events; optional filter by customer (picker) |
Campaigns (read)
| Tool | Description |
|------|-------------|
| list_campaigns | Campaigns in a dataset |
| get_campaign | Campaign details: state, targeting, experiences |
| get_campaign_stats | Impressions, clicks, goals, orders, revenue |
| get_campaign_context | Campaign experiences, messages, and runtime context |
Segments (read)
| Tool | Description |
|------|-------------|
| list_segments | Segments in a dataset |
| get_segment | Segment details and targeting rules |
| list_all_segments | Segments across all datasets (or filter one) |
| get_segment_details | Extended segment info including membership counts |
| list_segment_stats | Batch segment membership counts and calc status |
Templates, recipes & surveys
| Tool | Description |
|------|-------------|
| list_templates | Item/message templates (/itemTemplates/) |
| list_web_templates | Global Web Templates — HTML/CSS/JS rendering engines (distinct from item templates) |
| list_recipes | Recommendation recipes |
| get_recipe_usage | Map recipes to campaigns (resolves inUse vs actual linkage) |
| list_surveys | Surveys in a dataset |
| list_content_zone_logs | Content zone activity logs |
Audit & compare
| Tool | Description |
|------|-------------|
| audit_dataset | Dataset audit report (markdown or JSON); optional file output |
| audit_mcp_dataset | Executive-ready orchestrated audit: narrative, health score, governance, consumption, settings; markdown + DOCX |
| compare_datasets | Side-by-side comparison of two datasets |
Write tools (gated — QA dataset only)
All write tools require IS_WRITES_ENABLED=true, default to dry_run=true, and are restricted to dataset meublesrdqa (Meubles RD QA). Execute with dry_run=false and confirm=true.
| Tool | Description |
|------|-------------|
| set_campaign_state | Publish or disable a campaign (Published / Disabled) |
| set_segment_enabled | Enable or disable a segment |
| clone_campaign | Clone a campaign with regenerated rule/experience ids (new campaign starts Disabled) |
| create_web_campaign | Create a NextGen Web campaign from template EM0PD by default |
| clone_segment | Clone a segment within or across datasets |
| create_segment | Create a new User segment with a default "visited in last day" rule |
Always preview first with dry_run=true (default):
- "Dry run: clone campaign Q07yv in meublesrdqa"
- "Create web campaign 'Holiday Banner Test' in meublesrdqa — dry run only"
- "Disable segment uvoSx in meublesrdqa — dry run only"
Execute only when intentional:
- "Clone campaign Q07yv in meublesrdqa with dry_run false and confirm true"
- "Set campaign qDArI to Published in meublesrdqa with dry_run false and confirm true"
| Endpoint | Method | Operation |
|----------|--------|-----------|
| /internal/dataset/{ds}/campaign/ | POST | Clone / create web campaign |
| /internal/dataset/{ds}/campaign/{id}/ | PUT | Publish / disable campaign |
| /internal/dataset/{ds}/segments/ | PUT | Create / clone segment |
| /internal/dataset/{ds}/segments/disableOrEnable/ | POST | Enable / disable segment |
Executive audit (audit_mcp_dataset)
Board-ready assessment with health rating, strengths/gaps narrative, domain assessments, and prioritized recommendations. Writes markdown and DOCX when output_path is set.
| Parameter | Description |
|-----------|-------------|
| report_style | executive (default) or technical |
| output_format | markdown, json, or docx |
| export_docx | When saving .md, also write .docx (default: true) |
| sections | governance, consumption, settings (default: all) |
Example:
- "Full MCP audit of eharper — save to audit/eharper-audit.md"
- "audit_mcp_dataset dataset=eharper report_style=executive output_path=audit/report.md"
Example prompts
- "Check my Interaction Studio session"
- "Update session from sidecar" / "update_session sync_from_sidecar true"
- "List my Interaction Studio datasets"
- "Get sitemap for eharper"
- "Find customer by email [email protected] in eharper"
- "Get event stream for user abc123 in eharper"
- "List web templates in eharper"
- "Audit the eharper dataset and save to audit/eharper-audit.md"
- "Full executive MCP audit of eharper with markdown and docx"
- "Compare mathes and eharper datasets"
- "Which campaigns use recipe 4RhQk in eharper?"
- "List segment stats for all segments in eharper"
- "Dry run: create web campaign 'Test Banner' in meublesrdqa"
- "Clone segment XYZ in meublesrdqa — dry run only"
- "Get stats for campaign ABC123 in my-dataset"
- "List all segments across every dataset"
Campaign stats time ranges
For get_campaign_stats and list_campaigns, use time_range:
pastDay,pastWeek(default),pastMonth,pastQuarter,Today- Custom:
YYYY-MM-DD..YYYY-MM-DD(max range limits apply per IS API)
CLI audit script
After building locally, run a full audit from the command line:
npm run build
node dist/scripts/audit-dataset.js eharper --output ../audit/eharper-audit.mdOr use the npm script:
npm run audit -- eharper --output ../audit/eharper-audit.mdTroubleshooting
| Problem | Solution |
|---------|----------|
| 401 / Bad Credentials | Session expired. Refresh via DevTools → update session_id in config.env, use update_session, or sync with bookmarklet + sidecar |
| Missing Interaction Studio base URL | Add base_url=https://account.region.evergage.com to IS_CONFIG_FILE, or set legacy IS_BASE_URL |
| SSL certificate error | IS_TLS_INSECURE=true or NODE_EXTRA_CA_CERTS |
| Tool not found in Cursor | Restart MCP after upgrading the package |
| Sidecar not reachable | Run npx mcp-interaction-studio-sidecar and open http://127.0.0.1:4043/ |
| Zero campaign stats | Campaign may have no traffic in the selected time window; try a wider custom range |
| Query param [timeRange] is required | Use a valid time_range value (see above) |
Documentation & agent skill
This package includes a Cursor skill for Marketing Cloud Personalization domain knowledge, audit workflows, and tool selection:
.cursor/skills/marketing-cloud-personalization/
├── SKILL.md # Main skill — read when working on MCP or IS audits
├── reference-documentation.md # Official + community doc links
└── reference-mcp-server.md # Tool list, env vars, extension patternsAdditional docs:
| Doc | Purpose |
|-----|---------|
| docs/ROADMAP.md | Planned tools (consumption stats, governance, catalog browse, …) |
| docs/Endpoint-discovery.md | Internal /internal/* endpoints from Network tab |
| docs/STAGE3-WRITES.md | Write tools, safety gates, capture guide |
External references: Salesforce MCP Developer docs · Help · Mateusz Dąbrowski guides · Campaign templates repo
License
MIT
