Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe
Front Page / AI Tools / Deep Dive

Build a Marketo Engage MCP Server for Agentic Marketing Campaign Automation

Adobe's Marketo Engage MCP Server connects AI assistants to more than 100 operations across forms, programs, smart campaigns, leads, and emails. This guide builds a production FastMCP TypeScript server that wraps Marketo into typed agent tools — program health, campaign status, lead routing, and form management — with OAuth 2.0 and action-level RBAC.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Aug 15, 2026 Published
|
Aug 15, 2026 Updated
|
15 Minutes Reading Time
Core Takeaways for Founders & Builders
  • Adobe's Marketo Engage MCP Server connects AI assistants to more than 100 Marketo operations across forms, programs, smart campaigns, leads, and emails.
  • A read-first FastMCP TypeScript gateway exposes program health, campaign status, lead search, and form listings to agents while keeping destructive operations behind explicit gates.
  • OAuth 2.0 client-credentials authentication with action-level RBAC means agents get exactly the marketing operations they were approved to run — and nothing else.
  • Wiring the gateway into LangGraph lets a marketing-ops agent monitor programs, flag stalled campaigns, and propose lead-routing changes for human approval inside one workflow.

By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.

Adobe's Marketo Engage MCP Server connects AI assistants to more than 100 Marketo operations spanning forms, programs, smart campaigns, leads, and emails — the marketing platform finally speaking the agent ecosystem's language. For marketing-operations teams, the launch is the difference between an AI assistant that answers "how is the Q3 nurture doing?" with a guess and one that answers it with a live program-health query. This guide builds a production-grade TypeScript FastMCP server, marketo-mcp, that wraps Marketo into a curated, read-first set of agent tools — program health, campaign status, lead search, and form listings — plus explicitly gated write tools for campaign activation and lead routing, all behind OAuth 2.0 and action-level RBAC. The same gateway pattern applies to any enterprise platform with a REST API, so the architecture is worth studying even if you never touch Marketo. If you are building agent tool surfaces in marketing, the MCP directory is the reference map.

Why agentic marketing needs a gateway, not just the MCP server

Adobe's MCP server is the raw access path — exactly what you want for a power user and exactly what you do not want a general-purpose agent pointing at directly. A gateway between the agent and the platform is where governance lives:

  • A read-first, audited surface. The gateway exposes reads by default — program health, campaign status, lead search — and keeps writes behind explicit gates. A marketing agent that cannot activate a campaign without approval computes less risk than one that can.
  • Action-level RBAC. The gateway maps each tool to a permission scope, so a content agent can search leads but only a campaign manager agent can activate a smart campaign. Agents get exactly the operations they were approved to run.
  • Combined workflows in one call. A single get_program_health call that aggregates program status across a portfolio is painful to orchestrate against a raw API.
  • Full audit. Every command is logged with the agent identity that issued it, the exact payload, and the verdict, so a bad campaign decision is reconstructable after the fact.

The launch matters beyond Marketo because marketing operations are the highest-leverage agent use case in the enterprise: the platform already runs on rules, segments, and triggers — the exact shape of agentic work. The shift from dashboards to workflow helpers is what we track across the AI workflows library, and Adobe's MCP server is one of the biggest platforms yet to embrace it.

The tool surface and architecture

The gateway exposes eight tools against the Marketo REST API:

Tool Type What it does
list_programs R List programs with type, status, and dates
get_program_health R Program status, member counts, and engagement signals
list_smart_campaigns R Smart campaigns with status and approval state
search_leads R Search leads by email, name, or field filter
get_lead R Full lead record with activity history summary
list_forms R Forms with embed status and landing page association
activate_campaign W Approve and activate a smart campaign (gated)
update_lead_segment W Move a lead into a program segment (gated)

Writes are not disabled; they are policed. Every W tool passes through the RBAC gate first.

Step 1: Scaffold the TypeScript FastMCP server

mkdir marketo-mcp && cd marketo-mcp
npm init -y
npm install @modelcontextprotocol/sdk fastmcp zod
// server.ts
import { FastMCP } from "fastmcp";
import { z } from "zod";

const CLIENT_ID = process.env.MARKETO_CLIENT_ID!;
const CLIENT_SECRET = process.env.MARKETO_CLIENT_SECRET!;
const BASE = process.env.MARKETO_BASE ?? "https://000-AAA-111.mktorest.com";
const AGENT_SCOPE = process.env.MARKETO_AGENT_SCOPE ?? "read"; // read | ops

const mcp = new FastMCP("marketo-mcp", {
  instructions: "Read-first Marketo tools. Campaign activation and lead routing require the ops scope and explicit approval.",
});

let token: { value: string; exp: number } = { value: "", exp: 0 };

async function getToken() {
  if (token.value && token.exp > Date.now() + 60_000) return token.value;
  const form = new URLSearchParams({
    grant_type: "client_credentials",
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
  });
  const r = await fetch(`${BASE}/identity/oauth/token`, {
    method: "POST",
    body: form,
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
  });
  const body = await r.json();
  token = { value: body.access_token, exp: Date.now() + body.expires_in * 1000 };
  return token.value;
}

async function call(path: string, params: { [key: string]: string } = {}) {
  const qs = new URLSearchParams(params).toString();
  const r = await fetch(`${BASE}/rest/v1${path}${qs ? `?${qs}` : ""}`, {
    headers: { Authorization: `Bearer ${await getToken()}` },
  });
  return r.json();
}

const requireScope = (scope: string) => {
  if (AGENT_SCOPE !== scope && AGENT_SCOPE !== "ops") {
    throw new Error(`blocked: agent scope ${AGENT_SCOPE} cannot run this operation`);
  }
};

mcp.addTool({
  name: "list_programs",
  description: "List programs with type, status, and dates.",
  inputSchema: z.object({ maxReturn: z.number().int().max(200).default(50) }),
  execute: async (args) => JSON.stringify(await call("/programs.json", { maxReturn: String(args.maxReturn) })),
});

mcp.addTool({
  name: "get_program_health",
  description: "Return program status, member counts, and engagement signals.",
  inputSchema: z.object({ programId: z.number() }),
  execute: async (args) => JSON.stringify(await call(`/programs/${args.programId}.json`)),
});

mcp.addTool({
  name: "list_smart_campaigns",
  description: "List smart campaigns with status and approval state.",
  inputSchema: z.object({ programId: z.number().optional() }),
  execute: async (args) => JSON.stringify(await call("/campaigns.json", args.programId ? { programId: String(args.programId) } : {})),
});

mcp.addTool({
  name: "search_leads",
  description: "Search leads by email, name, or field filter.",
  inputSchema: z.object({
    filterType: z.enum(["email", "id", "firstName", "lastName"]).default("email"),
    filterValues: z.array(z.string()),
  }),
  execute: async (args) => JSON.stringify(await call("/leads.json", {
    filterType: args.filterType,
    filterValues: args.filterValues.join(","),
  })),
});

mcp.addTool({
  name: "get_lead",
  description: "Return a full lead record with summary activity.",
  inputSchema: z.object({ id: z.number() }),
  execute: async (args) => JSON.stringify(await call(`/lead/${args.id}.json`)),
});

mcp.addTool({
  name: "list_forms",
  description: "List forms with embed status and landing page association.",
  inputSchema: z.object({ maxReturn: z.number().int().max(200).default(50) }),
  execute: async (args) => JSON.stringify(await call("/forms.json", { maxReturn: String(args.maxReturn) })),
});

mcp.addTool({
  name: "activate_campaign",
  description: "Approve and activate a smart campaign. Requires ops scope and explicit approval.",
  inputSchema: z.object({ campaignId: z.number(), approve: z.boolean().default(false) }),
  execute: async (args) => {
    requireScope("ops");
    if (!args.approve) return "blocked: activation requires explicit approval";
    return JSON.stringify(await call(`/campaigns/${args.campaignId}/activate.json`));
  },
});

mcp.addTool({
  name: "update_lead_segment",
  description: "Move a lead into a program segment. Requires ops scope and explicit approval.",
  inputSchema: z.object({ leadId: z.number(), programId: z.number(), segment: z.string(), approve: z.boolean().default(false) }),
  execute: async (args) => {
    requireScope("ops");
    if (!args.approve) return "blocked: segment change requires explicit approval";
    return JSON.stringify(await call(`/leads/${args.leadId}/changeProgram.json`, {
      programId: String(args.programId), statusName: args.segment,
    }));
  },
});

mcp.start({ transport: "stdio" });

The critical details: reads need no special scope, writes call requireScope("ops") and demand an explicit approve: true from the agent, and the default AGENT_SCOPE is read. A content agent that tries to activate a campaign gets a blocked verdict, not a campaign.

Step 2: inputSchema definitions published to agents

{
  "search_leads": {
    "type": "object",
    "properties": {
      "filterType": { "type": "string", "enum": ["email", "id", "firstName", "lastName"], "default": "email" },
      "filterValues": { "type": "array", "items": { "type": "string" }, "description": "One or more values to match" }
    },
    "required": ["filterValues"]
  },
  "activate_campaign": {
    "type": "object",
    "properties": {
      "campaignId": { "type": "integer" },
      "approve": { "type": "boolean", "default": false, "description": "Explicit opt-in; required for activation" }
    },
    "required": ["campaignId"]
  },
  "update_lead_segment": {
    "type": "object",
    "properties": {
      "leadId": { "type": "integer" },
      "programId": { "type": "integer" },
      "segment": { "type": "string" },
      "approve": { "type": "boolean", "default": false }
    },
    "required": ["leadId", "programId", "segment"]
  }
}

Descriptions in agent-facing schemas decide which tool the model picks and how it fills arguments — say what each tool returns and what the gates are, so a model does not discover the RBAC limit by hitting it.

Step 3: Wire into Claude Desktop and Cursor

{
  "mcpServers": {
    "marketo-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/marketo-mcp/dist/server.js"],
      "env": {
        "MARKETO_CLIENT_ID": "your-client-id",
        "MARKETO_CLIENT_SECRET": "your-client-secret",
        "MARKETO_BASE": "https://000-AAA-111.mktorest.com",
        "MARKETO_AGENT_SCOPE": "read"
      }
    }
  }
}

Note MARKETO_AGENT_SCOPE: "read" — the default deployment cannot activate campaigns at all. Flip it to ops only for agents you have explicitly authorized to make changes, and keep the secret in a secret manager, rotated on a schedule.

OAuth 2.0, RBAC, and the human approval gate

  • Client-credentials flow. The gateway authenticates as a registered Marketo application, caching tokens until near expiry. Rotate the client secret centrally and audit every application that has one.
  • Action-level RBAC. The AGENT_SCOPE env var is the coarse gate; the requireScope check inside each write tool is the fine gate. Read tools need no scope; write tools need ops plus explicit approval. Defense in depth for the two most dangerous operations in marketing: activation and list mutation.
  • Human approval for writes. In production, route write tools through a LangGraph human-in-the-loop node — the agent prepares the activation or segment change, a human approves or rejects it, and the gateway executes only the approved payload. The same pattern we document across the AI workflows library.
  • Short-lived tokens. The gateway refreshes its OAuth token automatically; never paste a long-lived token into a committed config. An agent with a leaked credential is a liability, an agent with an expiring one is an incident to investigate.
  • Full audit. Every command, agent identity, payload, and verdict goes to the audit log. When a campaign goes out wrong, you can reconstruct exactly which agent proposed it and who approved it — the discipline the latest AI news coverage of agent governance keeps returning to.

Step 4: Wire into a LangGraph marketing-ops agent

from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import ToolNode

async def marketing_tools():
    # stdio client for marketo-mcp
    return ToolNode(await load_mcp_tools(read, write))

# Graph: monitor -> flag -> propose -> human_approve -> execute

The realistic pattern is monitor-first: the agent runs get_program_health across the portfolio, flags stalled or over-performing programs, searches leads to answer segmentation questions, and proposes activation or routing changes. The write tools sit at the end of the graph behind a human approval node, so the agent's output is a reviewed marketing decision, not an autonomous one. That is the difference between agentic marketing and reckless automation — and it is exactly why Adobe's MCP server is exciting but the gateway is what makes it deployable.

Testing the server end to end

npx fastmcp inspect "$(pwd)/dist/server.js"
> get_program_health(1042)
  → status: running, members: 12480, engaged: 3402, conversion: 27.3%

> search_leads({ filterType: "email", filterValues: ["ada@contoso.com"] })
  → 1 lead: id 88213, status: nurture, last_activity: form_submit

> activate_campaign({ campaignId: 77, approve: false })
  → blocked: activation requires explicit approval

The last call is the most important test: the gateway blocked activation because the agent did not opt in. Try it with approve: true under a read-scoped agent and confirm the scope gate also fires — those two tests prove the RBAC and approval chain are actually in the execution path, not painted on the README.

Frequently Asked Questions

What is the Marketo Engage MCP Server?

It is Adobe's Model Context Protocol server for Marketo Engage, connecting AI assistants to more than 100 operations across forms, programs, smart campaigns, leads, and emails so agents can work with the marketing platform directly.

Which tools should a production Marketo MCP server expose?

A read-first production surface: list_programs, get_program_health, list_smart_campaigns, search_leads, get_lead, and list_forms — with campaign activation, lead routing, and any destructive operation behind explicit approval gates or RBAC.

How do you keep an agentic marketing server safe?

Default to read-first, enforce action-level RBAC so each agent only reaches approved operations, require approval for campaign activation and list changes, use short-lived OAuth tokens, and log every call with the agent identity that issued it.

Does the Marketo MCP server replace the Marketo UI?

No — it is a new access path. It gives AI assistants and AI-powered products a protocol-level connection to the same programs, campaigns, leads, and forms, complementing the platform's native tools.

What is the realistic use case for an agentic Marketo server?

A marketing-operations agent that monitors program health across a portfolio, flags stalled or over-performing campaigns, searches leads for segmentation questions, and proposes routing or activation changes for a human to approve.

Closing thoughts

Adobe's Marketo Engage MCP Server is the most concrete sign yet that enterprise marketing platforms are building for agents, not just humans. The gateway pattern — read-first surface, action-level RBAC, human approval on writes, and full audit — is what turns that access path into something a marketing-operations team can sign off on. Build the monitor-and-flag loop first, keep writes behind the gate, and the same platform that runs your nurture programs today becomes your most productive marketing-ops assistant tomorrow. Track more marketing agent builds in the MCP directory and watch AI news for the next platform to follow Adobe's lead.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

Frequently Asked Questions
It is Adobe's Model Context Protocol server for Marketo Engage, connecting AI assistants to more than 100 operations across forms, programs, smart campaigns, leads, and emails so agents can work with the marketing platform directly.
A read-first production surface: list_programs, get_program_health, list_smart_campaigns, search_leads, get_lead, and list_forms — with campaign activation, lead routing, and any destructive operation behind explicit approval gates or RBAC.
Default to read-first, enforce action-level RBAC so each agent only reaches approved operations, require approval for campaign activation and list changes, use short-lived OAuth tokens, and log every call with the agent identity that issued it.
No — it is a new access path. It gives AI assistants and AI-powered products a protocol-level connection to the same programs, campaigns, leads, and forms, complementing the platform's native tools.
A marketing-operations agent that monitors program health across a portfolio, flags stalled or over-performing campaigns, searches leads for segmentation questions, and proposes routing or activation changes for a human to approve.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Briefing AI Tools

Vercel AI SDK Tool Calling React: 5 Steps (2026)

Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...

Deepak Bagada Deepak Bagada
12m read
Breaking AI Tools

Fact-Density vs. Word Count: The New SEO for 2026

Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...

Deepak Bagada Deepak Bagada
4m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc