Every time someone asks "how should my agent talk to my product," they're actually asking which of three integration shapes to pick. The shapes are MCP, REST plus webhooks, and per-vendor plugins. They look interchangeable on a whiteboard. They are not. Each one has a tradeoff zone where it is the obvious right answer, and a zone where picking it is going to hurt for years.
This piece is a comparative architectural read on the three, with code, and an opinion at the end about why MCP is the convergence layer for agent-tool integration even though REST and webhooks aren't going anywhere.
The three shapes, in one paragraph each
MCP is a bidirectional, stateful protocol between an LLM client (Claude Desktop, Cursor, ChatGPT, your in-house agent) and a tool server. The client connects, the server announces a catalog of tools, prompts, and resources, and the two sides negotiate capabilities. Auth uses OAuth 2.1 with Dynamic Client Registration so a brand-new agent can enroll itself without a human pasting a client secret. The same server speaks to every compliant client. Spec at modelcontextprotocol.io.
REST + webhooks is the request-response shape every backend engineer has internalized. The agent (or any client) hits an HTTP endpoint, the server responds. For server-initiated events, the server fires a webhook at a URL the consumer registered. It is stateless, well-tooled, and unambiguously defined by RFC 7231 (and now RFC 9110). Webhook discipline (HMAC signing, retries, idempotency) is documented exhaustively across the industry, e.g. Stripe's webhook docs.
Plugins are extensions written against a single LLM vendor's surface: ChatGPT plugins, Claude plugins, Cursor extensions. The vendor owns the marketplace, the install flow, the trust UI, and the runtime. They give you first-party UX inside that vendor's product. They do not work anywhere else.
These are different tradeoff zones, not competitors. The interesting question is which one wins which problem.
When MCP wins
MCP wins when an agent needs to discover tools dynamically, when you want to ship to multiple LLM clients with one implementation, when you need consent gates or authorization flows mid-session, and when you want the server to push prompts or resources at the client (not just respond to calls).
A tool registration on the Dock MCP server looks like this:
server.tool(
"create_workspace",
{
name: z.string().min(1).max(120),
mode: z.enum(["doc", "table"]).optional(),
initial_markdown: z.string().optional(),
},
async ({ name, mode, initial_markdown }, ctx) => {
const principal = await requirePrincipal(ctx);
const ws = await createWorkspace({ principal, name, mode, initial_markdown });
return { content: [{ type: "text", text: `Created ${ws.slug}` }], structuredContent: ws };
},
);
That one registration is callable from Claude Desktop, Cursor, ChatGPT, and our own in-house agents the same day, with the same OAuth handshake (draft-ietf-oauth-v2-1, RFC 7591 for DCR), the same capability negotiation, the same audit trail. We did not write four integrations. We wrote one server.
The other piece MCP gives you that REST does not: a mid-session consent handshake. When upgrade_plan runs, the first call returns a confirm_token, the agent shows it to the human, the human says yes, the agent re-calls. See Inside the magic-link gate for the auth substrate, and the smallest MCP tool for the surface discipline. Full Dock MCP reference at /docs/mcp/overview.
When REST + webhooks wins
REST plus webhooks wins for stateless event flows, third-party integrations into existing automation hubs (Slack, Zapier, n8n), high-volume transactional API access, and any consumer that is not an LLM. A Zap that watches Dock for new rows does not need a stateful MCP session. It needs a webhook.
// Subscribe a Slack-bound listener to row events on one workspace.
POST /api/webhooks
{
"url": "https://hooks.example.com/dock-to-slack",
"events": ["row.created", "row.updated", "comment.added"],
"workspace_slug": "launch-plan"
}
// Same workspace's transactional read path stays plain HTTP:
GET /api/workspaces/launch-plan/rows?limit=50
This is the model every backend already knows how to operate. The semantics are anchored in RFC 7231. The webhook side has decades of operational wisdom (Stripe's guide is the canonical reference: HMAC, retries, idempotency keys). When the consumer is server-to-server, not agent-to-tool, REST + webhooks is the correct shape, and adding MCP to that path is a category error. Full Dock webhook docs at /docs/api/webhooks.
When plugins win
Plugins win when you specifically want deep, first-party UX inside a particular LLM vendor's surface: a custom panel in ChatGPT, a Cursor extension that hooks the editor's command palette, a Claude plugin that registers a slash command. The vendor's marketplace gives you distribution. The vendor's UI shell gives you a level of polish a generic MCP server can't reach inside that specific product.
The cost is lock-in. A ChatGPT plugin runs in ChatGPT. A Cursor extension runs in Cursor. The same logical capability written as a plugin three times is three codebases, three review queues, three lifecycle policies. If your roadmap touches more than one client, plugins do not amortize.
The architectural insight
MCP is the convergence layer. REST and webhooks aren't going away (and shouldn't), but for agent-tool integration MCP is the only shape that scales across LLM clients without the integration matrix exploding.
That's the architectural read: pick the integration shape by who the consumer is, not by what's trendy. Server-to-server event flow: REST + webhooks. Deep single-vendor UX surface: plugin. Agent-tool integration that needs to work across LLM clients with auth, consent, and dynamic discovery: MCP. The first two were correctly shaped before LLMs existed. The third only became possible once a protocol specified how a client and a tool catalog negotiate in-band, which is exactly what MCP does.
The same capability, three surfaces
create_workspace is the worked example. The same logical capability is exposed via:
- MCP tool (above). Catalog-discoverable, capability-negotiated, OAuth-gated, callable from any compliant LLM client.
- REST endpoint:
POST /api/workspaceswith bearer auth. Used by our own web app, by curl scripts, by Zapier-like consumers, by anything that is not running inside an LLM. - Hypothetical plugin (e.g. a ChatGPT plugin): a single button inside one vendor's UI that calls the REST endpoint under the hood.
All three coexist. The MCP tool is the primary surface for agents because it composes with discovery, consent, and auth. The REST endpoint is the primary surface for everything else because it is stateless and universal. A plugin would be a thin convenience layer over the REST endpoint for one specific UI shell. None of these replace the others. They each occupy the zone where their tradeoffs are right.
See MCP-first workspace for why we built the MCP surface as the agent-primary one from day one, the smallest MCP tool for tool-shape discipline, and agents render HTML for the related rendering-vs-doing distinction (a different axis, same underlying point about picking the right shape for the consumer).
The trap is treating these three as alternatives competing for the same slot. They are not. Pick by consumer. If you only remember one rule: server consumers get REST + webhooks, vendor-locked UI surfaces get plugins, and any LLM client that needs to discover and call your tools gets MCP.