Two years ago, connecting an AI model to your CRM meant writing a bespoke integration. Today there are two open protocols doing that job, and almost every serious marketing platform has picked at least one of them.
MCP (Model Context Protocol) connects an agent downward to tools and data. A2A (Agent2Agent) connects agents sideways to each other. They are not competitors — a working stack usually runs both.
This post does two things: it gives you the dated adoption timeline for marketing software, and then it gets into the wire protocol — the JSON-RPC calls, the transports, the auth handshake and the discovery documents you actually need to implement against.
Part 1 — The timeline: who shipped, and when
The protocols themselves
| Date | Event |
|---|---|
| 25 Nov 2024 | Anthropic announces MCP as an open standard |
| 9 Apr 2025 | Google announces A2A at Cloud Next |
| 23 Jun 2025 | Google donates A2A to the Linux Foundation |
| 30 Jul 2025 | A2A v0.3.0 — the version most tutorials still describe |
| 9 Dec 2025 | Anthropic donates MCP to the Agentic AI Foundation (Linux Foundation), co-founded with Block and OpenAI |
| 25 Nov 2025 | MCP spec revision 2025-11-25 — the biggest change set since launch: async tasks, server-side agent loops, richer elicitation, Client ID Metadata Documents |
| 12 Mar 2026 | A2A v1.0 — first stable major version. Breaking changes throughout |
| 28 Jul 2026 | Next MCP spec revision due — the release candidate is locked, but 2025-11-25 is still the current stable spec |
Neither protocol is a vendor project anymore. Both sit under the Linux Foundation, which is the single biggest reason procurement teams stopped blocking them.
Marketing software: the MCP adoption wave
The pattern is consistent. Local server first (an npm or pip package you run on your own machine), then a remote server (an HTTPS endpoint with OAuth), then write access. Most vendors are somewhere in the middle of that path.
| Tool | First shipped | Where it is now (Jul 2026) |
|---|---|---|
| Shopify (Dev MCP) | 31 Mar 2025 | GA. Storefront MCP followed 21 May 2025; Catalog MCP opened to all developers in the Winter '26 Edition. Both migrated to the Universal Commerce Protocol (UCP) in Apr 2026 — the old Storefront cart tools were deprecated, legacy support ended 15 Jun 2026 |
| Zapier MCP | 4 Apr 2025 | Remote, de-facto GA — the fastest way to reach 8,000+ apps |
| Ahrefs | 28 Apr 2025 (@ahrefs/mcp) |
Remote server at api.ahrefs.com/mcp; the local repo was archived Feb 2026 |
| HubSpot | 6 May 2025 (local beta) | Remote MCP GA 13 Apr 2026 at mcp.hubspot.com. Read and write on CRM objects and engagements — but marketing content (campaigns, landing pages, blog posts) stays read-only |
| Salesforce | 23 Jun 2025 (Agentforce 3) | Salesforce-hosted MCP servers GA 29 Apr 2026; Marketing Cloud Engagement MCP GA Jun 2026. Agentforce also acts as an MCP client |
| Google Analytics (GA4) | 22 Jul 2025 | Official, local only, read-only, Apache-2.0 |
| Klaviyo | 2025 | GA of its enhanced MCP server 20 Aug 2025 — among the first martech MCP servers to reach GA. mcp.klaviyo.com/mcp |
| Braze | 12 Aug 2025 | Still beta, still read-only |
| Mixpanel | 29 Aug 2025 | Remote, beta |
| Semrush | 10 Sep 2025 | GA at mcp.semrush.com, requires an API subscription |
| Slack | 1 Oct 2025 | GA 17 Feb 2026 at mcp.slack.com |
| Google Ads | 7 Oct 2025 | Official, open-source, self-hosted. Read-only in the initial release — Google's wording, and it still holds |
| Amplitude | 21 Oct 2025 | Remote, public |
| Asana | ~May 2025 | V2 GA 4 Feb 2026 |
| Adobe (Express, AEM, Analytics/CJA) | Sep 2025 → Mar 2026 | Express Dev MCP stable 16 Mar 2026; AEM + Analytics remote servers live |
| Meta Ads | 29 Apr 2026 | "Ads AI Connectors" open beta at mcp.facebook.com/ads — genuine read and write (campaign creation and editing, catalog management). The first major ad platform to ship write access at launch |
| Mailchimp | 28 May 2026 | Official Intuit connector for Claude and ChatGPT, following the Feb 2026 Intuit–Anthropic partnership. US, CA, UK, AU |
| SharksAPI.AI (that's us) | 2026 | Not another tool with an MCP server bolted on — an AI marketing platform built agent-first. One MCP endpoint (/mcp/{botType}) and one A2A v1.0 endpoint expose 520+ tools across 131+ integrations: GA4, Search Console, Google Ads, Meta Ads, WordPress, LinkedIn, Pipedrive and the rest of the stack above, behind a single OAuth token. Ships a signed Agent Card |
And the notable gaps, because they tell you as much as the launches:
- Google Search Console has no official MCP server. Everything you see —
mcp-gsc,mcp-server-gscand friends — is community-built. For the single most important free data source in SEO, that is a striking hole. - Shopify has no first-party Admin MCP. The admin story is Sidekick extensions.
- Zendesk built an MCP client, not a server — it wants to consume your tools, not expose its own.
- Most ad-platform MCPs are read-only by design. Google Ads, GA4 and Braze all shipped read-only first. Meta is the outlier, and that is a deliberate bet.
A2A adoption: a different crowd entirely
Here is the thing most posts get wrong. A2A adoption in marketing is not the same list.
- Salesforce — A2A shipped with Agentforce 3 (23 Jun 2025). AgentExchange supports MCP and A2A natively.
- ServiceNow — A2A endpoints out of the box via AI Agent Fabric.
- SAP — Joule's A2A toolkit went bidirectional in Joule Studio 2.0.
- Adobe — Agent Orchestrator (GA Sep 2025) is explicitly built on MCP + A2A.
- SharksAPI.AI (disclosure: this is our platform) — A2A v1.0 at
/a2a, MCP at/mcp/{botType}, and a signed Agent Card (JWS ES256, verifiable against/.well-known/jwks.json). The rare mid-market martech platform that ships both. - Not present: HubSpot, Klaviyo, Braze, Semrush, Ahrefs, Shopify.
The split is clean: A2A is an enterprise-suite story. MCP is the everyone-else story. If you are a mid-market marketing team, you will consume A2A far more often than you will publish it — and you will publish MCP servers long before you publish an Agent Card.
As of April 2026 the Linux Foundation counted 150+ organisations supporting A2A, with SDKs in five languages.
Part 2 — MCP, technically
The shape of it
MCP is JSON-RPC 2.0 with a defined vocabulary. A host (Claude, ChatGPT, Cursor, your own agent runtime) spawns one client per server, and each client holds a 1:1 stateful session.
Servers expose three primitives:
- Tools — model-invoked functions (
get_campaign_metrics,update_bid) - Resources — application-controlled context, addressed by URI (
ga4://property/123/report/weekly) - Prompts — user-selected templates
Clients can expose three back to the server:
- Sampling — the server asks the host to run an LLM completion
- Elicitation — the server asks the user a question mid-tool-call
- Roots — filesystem or URI boundaries the server is allowed to touch
Most marketing MCP servers only implement tools. That is fine, and it is the reason "MCP is just a wrapper around your REST API" is a half-truth — it is a self-describing, capability-negotiated, auth-aware wrapper, which is the part that matters.
Transports
There are exactly two you need to care about:
- stdio — the server is a subprocess. Newline-delimited JSON-RPC over stdin/stdout. This is how the GA4 and Google Ads servers run.
- Streamable HTTP — a single HTTP endpoint that accepts
POSTfor requests and can upgrade to Server-Sent Events for server→client streaming. This replaced the old HTTP+SSE dual-endpoint transport. Every remote server (HubSpot, Semrush, Klaviyo, Slack) uses this.
The handshake
// → client to server
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": { "sampling": {}, "elicitation": {} },
"clientInfo": { "name": "sharksapi-agent", "version": "2.4.0" }
}
}
// ← server to client
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-11-25",
"capabilities": { "tools": { "listChanged": true }, "resources": {} },
"serverInfo": { "name": "semrush", "version": "2.1.0" }
}
}
Then notifications/initialized, then discovery:
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
The server returns each tool with a JSON Schema for its inputs. That schema is what the model reads. If your description field is vague, the model will call the tool wrongly — tool descriptions are prompt engineering, not documentation.
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "domain_organic_search_keywords",
"arguments": {
"domain": "example.ee",
"database": "ee",
"limit": 100,
"sort_by": "traffic_desc"
}
}
}
The result comes back as typed content blocks (text, image, resource) plus an optional structuredContent object validated against the tool's outputSchema. Always read structuredContent if it is present — parsing the human-readable text block is how integrations break on the next release.
Authorization — the part everyone gets wrong
For remote servers, MCP defines the server as an OAuth 2.1 Resource Server. It does not issue tokens; it validates them. The flow:
- Client calls the MCP endpoint with no token → server returns
401with aWWW-Authenticateheader pointing at its Protected Resource Metadata (RFC 9728):
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
- Client fetches that document, learns which authorization server to talk to.
- Client discovers the auth server's endpoints (OAuth 2.0 AS metadata or OIDC — the 2025-11-25 spec requires at least one).
- Client runs Authorization Code + PKCE, and — critically — includes an RFC 8707
resourceindicator so the issued token is audience-bound to this MCP server only. - Client retries with
Authorization: Bearer ….
That resource indicator is not optional pedantry. Without audience binding, a token minted for one MCP server can be replayed against another — the exact class of confused-deputy bug that produced several one-click account-takeover write-ups in 2025. If you are building a server, reject tokens whose audience is not you.
Where vendors deviate: Semrush accepts OAuth or a static key via Authorization: Apikey YOUR_KEY for clients that cannot do OAuth. Convenient; strictly worse. Prefer OAuth where you have the choice.
Connecting a marketing stack — a real config
{
"mcpServers": {
"hubspot": {
"type": "http",
"url": "https://mcp.hubspot.com"
},
"semrush": {
"type": "http",
"url": "https://mcp.semrush.com/v2/mcp"
},
"klaviyo": {
"type": "http",
"url": "https://mcp.klaviyo.com/mcp?company=acme-retail"
},
"ahrefs": {
"type": "http",
"url": "https://api.ahrefs.com/mcp"
},
"ga4": {
"command": "pipx",
"args": ["run", "--spec", "google-analytics-mcp", "ga4-mcp-server"],
"env": { "GOOGLE_APPLICATION_CREDENTIALS": "/secrets/ga4-sa.json" }
}
}
}
Or, from the CLI:
claude mcp add --transport http hubspot https://mcp.hubspot.com
claude mcp add --transport http semrush https://mcp.semrush.com/v2/mcp
The remote ones will open a browser for OAuth on first use. The local one uses a service-account key file, because Google's GA4 server predates the remote-MCP pattern.
One hard-won warning: every tool from every connected server lands in the model's context window. Five marketing MCP servers can easily mean 150+ tool definitions and tens of thousands of tokens before the user has typed anything. Scope aggressively — enable the servers a given agent actually needs, not your whole stack. This is the single most common cause of "the agent got dumber after we added integrations".
Writing your own server
Three tools, cleanly described, beats forty auto-generated ones:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("acme-ads")
@mcp.tool()
def campaign_performance(campaign_id: str, days: int = 30) -> dict:
"""Return spend, impressions, clicks, conversions and ROAS for one campaign
over the last N days. Use this before recommending any budget change."""
return acme_api.get(f"/campaigns/{campaign_id}/stats", days=days)
if __name__ == "__main__":
mcp.run(transport="stdio")
The docstring is the tool description the model sees. Write it as an instruction to the model, including when not to call it.
Part 3 — A2A, technically (and what changed in v1.0)
MCP gives an agent hands. A2A gives it colleagues. Where MCP calls a function, A2A delegates a task to another opaque agent — one you do not control, whose internal tools and prompts you cannot see, and which may take minutes or hours to answer.
Discovery: the Agent Card
Every A2A agent publishes a JSON document at:
https://agent.example.com/.well-known/agent-card.json
{
"name": "SharksAPI SEO Agent",
"description": "Audits technical SEO, tracks rankings and drafts optimisation tasks.",
"provider": { "organization": "SharksAPI.AI", "url": "https://sharksapi.ai" },
"version": "2.1.0",
"supportedInterfaces": [
{
"url": "https://api.sharksapi.ai/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
},
{
"url": "https://api.sharksapi.ai/a2a/grpc",
"protocolBinding": "GRPC",
"protocolVersion": "1.0"
}
],
"capabilities": {
"streaming": true,
"pushNotifications": true,
"extendedAgentCard": true
},
"securitySchemes": {
"oauth2": {
"type": "oauth2",
"flows": {
"clientCredentials": {
"tokenUrl": "https://sharksapi.ai/oauth/token",
"scopes": { "seo:read": "Read audits", "seo:write": "Create tasks" }
}
}
}
},
"security": [{ "oauth2": ["seo:read"] }],
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["text/plain", "application/json"],
"skills": [
{
"id": "technical-audit",
"name": "Technical SEO audit",
"description": "Crawls a domain and returns prioritised technical issues.",
"tags": ["seo", "audit", "crawl"],
"examples": ["Audit example.ee and list the top 10 crawl blockers"]
}
]
}
Cards can be signed — v1.0 formalises this as JWS (RFC 7515) over an RFC 8785 canonicalisation of the card. If you are going to let an agent delegate money-spending work to another agent, verify the signature.
The v0.3 → v1.0 breaking changes
If you built against A2A in 2025, this list is your migration plan:
| v0.3.0 | v1.0 |
|---|---|
message/send |
SendMessage |
message/stream |
SendStreamingMessage |
tasks/get |
GetTask |
tasks/cancel |
CancelTask |
tasks/resubscribe |
SubscribeToTask |
agent/getAuthenticatedExtendedCard |
GetExtendedAgentCard |
| — | ListTasks (new, cursor-paginated) |
preferredTransport + additionalInterfaces |
supportedInterfaces[] |
protocolVersion on the card |
per-interface protocolVersion |
kind discriminator fields |
removed — discriminate on JSON member names |
kebab-case enums (input-required) |
SCREAMING_SNAKE_CASE (TASK_STATE_INPUT_REQUIRED) |
compound IDs (tasks/{id}/…) |
flat UUIDs |
POST /v1/message:send |
POST /message:send (no /v1 prefix) |
| implicit + password OAuth flows | removed; Device Code (RFC 8628) added; pkce_required added |
Also new in v1.0: a tenant field on every request (multi-tenancy without URL gymnastics), and a mandatory version header:
POST /a2a HTTP/1.1
Host: api.sharksapi.ai
A2A-Version: 1.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
An empty A2A-Version header is interpreted as 0.3. If you forget it, you silently get old semantics. Send it.
Delegating a task
{
"jsonrpc": "2.0",
"id": "req-01",
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"messageId": "c1f9a2e0-6b34-4f1a-9a7e-2f0d3b8c4a11",
"parts": [
{ "text": "Audit example.ee and return the top 10 crawl blockers." }
]
},
"configuration": {
"returnImmediately": true,
"acceptedOutputModes": ["application/json"]
}
}
}
With returnImmediately: true you get a Task back straight away, in TASK_STATE_SUBMITTED or TASK_STATE_WORKING, and you then poll GetTask, open a SubscribeToTask SSE stream, or register a webhook via CreateTaskPushNotificationConfig. With it unset (the default), the call blocks until the task reaches a terminal or interrupted state.
Task lifecycle
TASK_STATE_SUBMITTED
↓
TASK_STATE_WORKING ⇄ TASK_STATE_INPUT_REQUIRED (interrupted — needs a human or more data)
│ ⇄ TASK_STATE_AUTH_REQUIRED (interrupted — needs credentials)
↓
TASK_STATE_COMPLETED | TASK_STATE_FAILED | TASK_STATE_CANCELED | TASK_STATE_REJECTED
(all terminal)
Terminal tasks reject further messages with UnsupportedOperationError. Streams close when a terminal state is reached. TASK_STATE_REJECTED is the agent saying "I won't do this" — which is a legitimate, and underused, answer.
Outputs come back as Artifacts (the deliverable) rather than Messages (the conversation). That distinction is deliberate: the artifact is what you store; the messages are how you got there.
MCP vs A2A: which one, when
| MCP | A2A | |
|---|---|---|
| Connects | agent → tools & data | agent → agent |
| The other side is | a function you call | a peer you delegate to |
| Unit of work | tool call (fast, synchronous) | task (long-running, stateful) |
| Discovery | tools/list after initialize |
/.well-known/agent-card.json |
| Transport | stdio, Streamable HTTP | JSON-RPC, gRPC, HTTP+JSON/REST |
| Auth | OAuth 2.1 RS + RFC 9728 + RFC 8707 | OAuth 2.x / mTLS declared in the card |
| Governance | Agentic AI Foundation (LF) | Linux Foundation |
| Martech adoption | broad — HubSpot, Semrush, Klaviyo, Ahrefs, Shopify, Meta | narrow — Salesforce, SAP, ServiceNow, Adobe |
The rule of thumb: if the other side is a capability you fully control the semantics of, use MCP. If it is an actor with its own judgement, its own model and its own runtime, use A2A. A marketing agent that reads Semrush over MCP, decides on a content brief, and hands that brief to a separate copywriting agent over A2A is the canonical shape.
We go deeper on the trade-offs in MCP vs REST vs GraphQL and the A2A Protocol Deep Dive.
Security: five things to fix before you go live
- Audience-bind every token. RFC 8707
resourceindicators, and servers that reject tokens issued for anyone else. This is the whole ballgame. - Treat tool output as untrusted input. A campaign name, a support ticket, a product description — any of them can carry a prompt injection. Data returned by a tool must never be executed as an instruction.
- Read-only until proven otherwise. There is a reason Google Ads, GA4 and Braze all shipped read-only first. Write scopes (HubSpot, Meta Ads) need a human approval gate on anything that spends money.
- Verify Agent Card signatures before delegating over A2A, and pin the provider.
- Scope tool exposure per agent. Not per organisation. See the context-window warning above — this is a quality issue as much as a security one.
What to do this quarter
- Audit what you already have. If you use HubSpot, Semrush, Klaviyo, Shopify or Slack, you have official MCP servers sitting unused. Cost of connecting: minutes.
- Fill the gaps with your own servers. Search Console still has no first-party option, and neither does most of your internal data — a 100-line FastMCP wrapper around an API you already call is a weekend's work.
- Do not write an Agent Card yet unless you sell an agent. Consume A2A; publish MCP.
- Pin your spec versions. MCP's next revision lands 28 July 2026. A2A v1.0 broke almost everything from v0.3. Version negotiation is not decoration.
SharksAPI.AI exposes 520+ MCP tools across 131+ integrations, with OAuth 2.0 client-credentials agent self-registration, an A2A v1.0 endpoint, and a signed Agent Card you can verify before you trust it. If you would rather not maintain twelve MCP servers yourself, see how agent access works.
FAQ
What is the difference between MCP and an API?
An API is a contract for developers; MCP is a contract for models. The endpoints may be identical, but MCP adds runtime capability discovery (tools/list), machine-readable JSON Schema for every input and output, a standard OAuth 2.1 authorization flow, and a session lifecycle. You are not replacing your REST API — you are putting a self-describing, model-legible surface in front of it.
Which marketing tools have an official MCP server?
As of July 2026: HubSpot, Salesforce (including Marketing Cloud Engagement), Semrush, Ahrefs, Klaviyo, Braze, Shopify, Google Analytics 4, Google Ads, Meta Ads, Mailchimp, Slack, Asana, Amplitude, Mixpanel, Canva, Figma, Adobe (Express/AEM/Analytics), Stripe, Zapier, Make, n8n and Notion. Google Search Console notably does not — every GSC server you will find is community-built.
Is A2A a replacement for MCP?
No, and they were never competing. A2A explicitly positions MCP as complementary: MCP is how an agent reaches its tools, A2A is how agents reach each other. Google's own AP2 payments protocol is built as an extension of both.
What version of A2A should I build against?
v1.0 (released 12 March 2026, patch v1.0.1 in May 2026). Anything describing message/send, preferredTransport or kebab-case task states is documenting v0.3 and is now out of date. Always send the A2A-Version: 1.0 header — an empty header is interpreted as 0.3.
Can an AI agent change my ad campaigns?
Only if you let it. Most ad-platform MCP servers — Google Ads, GA4, Braze — shipped deliberately read-only. Meta's Ads AI Connectors (open beta since 29 April 2026) are the notable exception, with real write access to campaigns and catalogs, and HubSpot's remote server added CRM write scopes at GA in April 2026. Anything that spends money should sit behind a human approval step regardless of what the protocol permits.
Where does the .well-known Agent Card live?
At https://your-domain/.well-known/agent-card.json. In A2A v1.0 the card lists supportedInterfaces[], each with its own url, protocolBinding (JSONRPC, GRPC or HTTP+JSON) and protocolVersion — the old top-level preferredTransport and protocolVersion fields are gone.
Sources: Anthropic — Introducing MCP · MCP joins the Agentic AI Foundation · MCP Specification 2025-11-25 — Authorization · MCP 2026-07-28 Release Candidate · A2A Specification v1.0 · A2A — What's New in v1.0 · A2A GitHub releases · Linux Foundation — A2A project launch · HubSpot — Remote MCP server is now GA · Salesforce — Hosted MCP servers now GA · Semrush MCP docs · Klaviyo MCP server · Ahrefs MCP docs · Google Analytics MCP · Google Ads MCP server · Meta Ads AI Connectors · Shopify — UCP Cart MCP changelog · Intuit Mailchimp — AI and data integrations