teachyou.ai academy
← All posts
MCP

MCP Client Comparison: How Different AI Tools Support MCP

Pramod Dutta · May 28, 2026 · 18 min read

Why "MCP support" means different things depending on the client

If you have spent any time building with the Model Context Protocol, you have probably noticed that the phrase "this tool supports MCP" hides a lot of variation. One client might connect to a local stdio server in two lines of config. Another might only accept remote HTTP servers behind OAuth. A third might read your tool descriptions and show them to a human for approval before every call, while a fourth executes tool calls silently in a loop with no human in sight at all.

This is not a flaw in the protocol. MCP was deliberately designed as a thin, transport-agnostic contract between hosts, clients, and servers — it specifies how capabilities are discovered and invoked, not how a particular application should present those capabilities to its users. The result is that the same MCP server, unmodified, can behave like a cautious assistant in one client and a fully autonomous agent in another. Understanding those differences is not a trivia exercise. It changes how you design tool schemas, how you write descriptions, how much error-handling you put in your server, and how you think about security boundaries.

This article compares how four categories of MCP clients — Claude Desktop, Claude Code, Cursor, and custom-built clients using the MCP SDK — actually implement the protocol in practice. We will look at connection models, permission handling, tool-call flow, and the practical implications for anyone building or integrating an MCP server. If you are building servers rather than just consuming them, this comparison should sit alongside our companion piece, Building & Integrating MCP Servers, which walks through the server side of this same relationship.

A quick refresher on the client-host-server split

Before comparing tools, it helps to be precise about vocabulary, because "MCP client" gets used loosely.

In the protocol's own architecture, there are three roles:

  • Host — the application the user actually opens, like Claude Desktop, Cursor, or a terminal-based coding agent. The host owns the UI, the permission model, and the overall product experience.
  • Client — the piece inside the host that speaks MCP on behalf of one connection. A host typically maintains one client instance per connected server, each with its own isolated session.
  • Server — the process (local or remote) that exposes tools, resources, and prompts over the protocol.

When people say "Claude Desktop is an MCP client," they usually mean the host-plus-client combination, since from a user's perspective those are fused. That is the convention this article follows: each section below covers a host application, and inside it we describe how its embedded client behaves.

This distinction matters because two hosts can embed clients that are protocol-compliant in an identical technical sense, yet feel completely different to use, because the host layer on top makes different choices about autonomy, approval flows, and which primitives (tools, resources, prompts, sampling) it actually surfaces.

Claude Desktop: the reference consumer experience

Claude Desktop was one of the first mainstream applications to ship MCP support, and it remains a useful baseline because it implements the protocol close to the letter of the original specification, aimed at a general knowledge-worker audience rather than developers.

Connection model. Claude Desktop primarily connects to local MCP servers launched as subprocesses over stdio, configured through a JSON file (commonly at a path like ~/Library/Application Support/Claude/claude_desktop_config.json on macOS). A typical entry looks like this:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
    }
  }
}

Newer versions have added support for remote servers over HTTP with streaming responses, plus a connector/directory experience where users can browse and install servers without hand-editing JSON. But the mental model is still: a small number of long-lived server connections, configured once, available across every conversation.

Tool-call flow. This is where Claude Desktop's identity as a consumer product shows. Every tool call that a server exposes is, by default, gated behind a visible approval prompt. When the model decides to call a tool, the user sees a card describing which server, which tool, and what arguments are about to be sent, with explicit Allow once / Allow for this chat style controls. Only after the human clicks through does the call execute and the result flow back into the conversation.

This matters enormously for how you should write tool descriptions if you expect Claude Desktop users to be among your audience. Because a human reads the tool name and arguments before approving, ambiguous or jargon-heavy tool definitions cause real friction — users decline calls they do not understand, or approve calls blindly, which is arguably worse. A tool called execute with a single input: string parameter is a bad experience in Claude Desktop; a tool called search_local_invoices with a documented date_range and vendor parameter reads clearly in the approval dialog and builds trust.

Resources and prompts. Claude Desktop also supports MCP resources (data a server can expose for the user to attach into context, such as a file or a database record) and prompts (server-defined slash-command-like templates). These show up in the paperclip/attachment UI and in a prompts picker respectively. Many third-party servers only implement tools and skip resources and prompts entirely, which is a missed opportunity in this particular host, since resources give users a much cheaper way to pull in read-only context than wrapping everything in a tool call.

Practical takeaway. If your server targets Claude Desktop users, assume a human is in the loop for every call, keep tool descriptions plain-English, and use resources for anything that is fundamentally "give me this data" rather than "do this action."

Claude Code: an agentic, permissioned client for developers

Claude Code is a different animal even though it embeds a conceptually similar MCP client. It is a CLI-first coding agent that runs in a terminal (or through an SDK/IDE integration), and its whole design center is autonomous, multi-step execution over a codebase — so its MCP implementation reflects that.

Connection model. Claude Code supports the same three server transports the ecosystem has converged on: local stdio servers, and remote servers over HTTP/SSE. Servers can be registered per-project (checked into a repo-level config so a whole team shares the same MCP setup) or per-user (available across every project on the machine). Configuration commonly happens through a CLI command rather than hand-editing a file, for example:

claude mcp add my-server -- node ./mcp-server/index.js

or by pointing at a remote endpoint with credentials handled through an OAuth flow when the server requires it. This project-scoped configuration is one of the more practically important differences from Claude Desktop: a team can commit an MCP server configuration to version control so that everyone working on the repository gets the same tools (say, a server that talks to internal ticketing or deployment systems) without each engineer configuring it by hand.

Tool-call flow and permissions. Claude Code's defining trait is a layered permission system that sits above raw MCP tool calls. Rather than a single global "allow/deny," it supports project-level and user-level permission rules, allow-lists for specific tools or command patterns, and a "plan mode" where the agent proposes a sequence of actions before executing any of them. For MCP tools specifically, the first call to a new server's tool typically prompts for approval, and the user can choose to remember that decision for the rest of the session, the project, or indefinitely.

Because Claude Code is built to chain many tool calls in sequence — read a file, call an MCP tool, edit a file, run a test, call the tool again — server responses need to be usable by a model operating with much less per-step human oversight than in Claude Desktop. This has a direct implication for server authors: error messages and partial-failure states matter more here than in a single-shot chat client, because the agent will often try to recover and continue autonomously. A server that throws an opaque 500 with no message forces the agent to guess; a server that returns a structured error explaining exactly what was invalid (a missing field, an out-of-range value, an auth token that expired) lets the agent self-correct and keep the task moving.

Extensibility. Claude Code also treats MCP as a first-class extension point for its own internal capabilities — for example, MCP servers can supply additional tools that show up alongside its built-in file and shell tools, and slash commands can be backed by MCP prompts. This blurs the historical line between "built-in agent tool" and "external MCP tool" in a way that Claude Desktop, being a more contained chat product, does not really attempt.

Practical takeaway. If your server targets Claude Code users, invest in structured, actionable error responses, support project-scoped configuration cleanly (idempotent startup, no hardcoded per-user paths), and expect your tools to be called many times in a row without a human reviewing each one — so make destructive operations opt-in and clearly labeled.

Cursor: MCP as an IDE co-pilot integration

Cursor is a fork of VS Code with a deeply integrated AI agent, and its MCP support is aimed squarely at the "AI pair programmer inside my editor" use case rather than either the general consumer chat experience or the CLI automation experience.

Connection model. Cursor reads MCP server definitions from a configuration file, either project-scoped (so a repository can ship its own mcp.json alongside the code, similar in spirit to Claude Code's project config) or global to the user's Cursor installation. The format looks similar to other stdio-based configs:

{
  "mcpServers": {
    "postgres": {
      "command": "node",
      "args": ["./scripts/pg-mcp-server.js"],
      "env": {
        "DATABASE_URL": "postgres://localhost:5432/dev"
      }
    }
  }
}

Cursor also supports remote/HTTP servers, which matters for teams that want to expose a single hosted MCP server (say, one wrapping internal APIs) to every developer without each person running a local process.

Tool-call flow. Cursor's agent mode operates tool calls inline in the editor's chat/composer panel, and it shows the tool call and its arguments as an expandable step in that panel before or as it executes, depending on settings. Cursor gives users configuration to mark specific tools as auto-run versus requiring confirmation, which sits somewhere between Claude Desktop's always-confirm default and a fully autonomous background agent. In practice, teams using Cursor for day-to-day coding tend to accept the friction of confirming file edits and shell commands but often allow-list read-only MCP tools (like a documentation-lookup or ticket-lookup server) to run without confirmation, since those carry little risk.

Editor-specific context. Because Cursor is an IDE, its MCP client sits next to a huge amount of implicit editor context — open files, selections, the codebase index, terminal output — that a chat client like Claude Desktop simply does not have. This changes what MCP servers are actually useful for inside Cursor. A generic "read a file" MCP tool is largely redundant, since Cursor's built-in agent already has fast file access; the MCP servers that earn their keep in this environment are the ones that reach outside the local filesystem — calling a ticketing system, querying a production database read replica, hitting an internal design-system API, or triggering a deploy pipeline. If you are designing a server with Cursor users specifically in mind, resist the temptation to duplicate what the IDE already does well, and focus on capabilities the editor has no native access to.

Practical takeaway. Assume your Cursor-facing tools are competing for attention with a rich, already-context-aware coding agent. The tools that get used are the ones that add genuinely new reach — external systems, not local files — and it is worth explicitly documenting in your tool descriptions why a call is worth making versus what the IDE can already do on its own.

Custom clients: full control, full responsibility

Not every MCP client is a shipped product. Plenty of teams build their own thin client — often directly against the official SDKs (available for TypeScript, Python, and other languages) — to embed MCP-powered tool use into an internal application, a Slack bot, a support-ticket triage system, or a batch pipeline with no human anywhere near the loop.

What you get for free. The SDK handles the protocol mechanics: capability negotiation on connect, JSON-RPC message framing, the tools/list and tools/call request/response cycle, resource subscriptions, and transport implementations for stdio and HTTP. A minimal custom client in TypeScript is genuinely short:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const transport = new StdioClientTransport({
  command: "node",
  args: ["./my-mcp-server.js"],
});

const client = new Client({ name: "internal-triage-bot", version: "1.0.0" });
await client.connect(transport);

const { tools } = await client.listTools();
const result = await client.callTool({
  name: "lookup_customer",
  arguments: { email: "user@example.com" },
});

What you have to build yourself. Everything above the wire protocol is your problem. There is no built-in approval UI, no permission scoping, no audit log, no rate limiting, no retry policy, and no default behavior for what happens when a tool call fails midway through a multi-step task. If you want a human-in-the-loop confirmation step like Claude Desktop's, you build it. If you want project-scoped versus global server configuration like Claude Code's, you build that too. If you want to cap how many tool calls a single user request can trigger to control cost or blast radius, that is entirely on you.

This is precisely why custom clients are simultaneously the most powerful and the most dangerous category to build. A custom client embedded in an automated pipeline — say, one that reads incoming support emails, calls an MCP server to look up account data, and calls another MCP server to issue a refund — has no human reviewing each tool call by default. That is a deliberate design choice you are making, and it needs matching guardrails: strict input validation on what the LLM is allowed to pass to sensitive tools, hard caps on tool-call loops, and server-side authorization checks that do not simply trust whatever arguments arrive, because in this configuration the model's output is the only thing standing between a user request and a real-world side effect.

Where custom clients shine. The upside is that you are not constrained by someone else's product decisions. You can enforce domain-specific policy (e.g., "never call the refund tool for orders over $500 without a second approval"), log every call to your own observability stack in whatever shape you want, and integrate MCP tool use into a workflow engine, cron job, or event-driven system that has nothing to do with a chat UI at all. If your use case is "run this every night with no human present," a custom client is often the only real option, since every consumer host discussed above assumes a person is at least nominally present in the session.

Practical takeaway. If you are building a custom client, treat the permission and safety layer as a first-class engineering task, not an afterthought bolted on after the happy path works. Decide up front what "confirmation" means in a context with no human, and enforce your riskiest boundaries on the server side too, since a client-side check alone can always be bypassed by a bug in your own prompt or orchestration logic.

Comparing the primitives each client actually uses

MCP defines more than just tools — resources, prompts, and sampling are part of the same specification — but adoption of the non-tool primitives is uneven across clients, and that unevenness is worth planning around.

  • Tools are supported everywhere. Every client discussed above implements tools/list and tools/call, because tool calling is the primitive that maps most directly onto "let the model do something," which is the feature every one of these products is selling in some form.
  • Resources — a server's way of exposing readable, addressable data (files, records, query results) without wrapping it in a tool call — are well supported in Claude Desktop's attachment picker, partially surfaced in Claude Code, and more variably supported across IDE integrations depending on version. Custom clients get resources for free from the SDK but have to build their own UI or logic for using them, so many simply skip resources and re-implement the same functionality as a "list" or "get" tool instead, which works but forfeits some of the protocol's intended efficiency.
  • Prompts — reusable, server-defined prompt templates a user can invoke — show up as slash-command-like pickers in Claude Desktop and Claude Code. They are a nice ergonomic layer but far from universally implemented, and plenty of production servers ship with an empty prompts list simply because tools alone cover their use case.
  • Sampling — the ability for a server to ask the connected client's model to generate a completion on the server's behalf — is the least consistently implemented primitive across the ecosystem. It is a powerful idea (a server can request LLM reasoning without holding its own API key or model access) but it depends entirely on host support, and not every client you build against will honor a sampling request today.

The practical lesson: build your server so that tools carry the full functionality on their own, and treat resources, prompts, and sampling as enhancements that make the experience better in clients that support them, rather than as required for baseline functionality.

Transport and authentication differences that actually bite

Beyond the user-facing behavior, there are lower-level differences that determine whether a server you build will even connect cleanly to a given client.

  • stdio versus HTTP. Local stdio servers are simplest to develop and debug, and every client covered here supports them, but they only work when the client and server run on the same machine — which rules out mobile clients, hosted agents, or any scenario where you want one server shared by many users. HTTP-based servers (using streamable HTTP transport) solve that but bring in real infrastructure concerns: your server needs to be reachable, needs to handle concurrent sessions, and typically needs authentication.
  • Authentication. Remote MCP servers commonly implement OAuth 2.1-style authorization flows, and client support for walking a user through that flow varies — some clients handle the redirect and token storage smoothly, others are rougher around the edges, particularly for newer or less mainstream clients still catching up to the spec's authorization additions. If you are building a remote server intended for broad client compatibility, test your auth flow against more than one client before assuming it works everywhere.
  • Session state. Some clients reconnect and expect a server to be stateless-friendly (i.e., safe to restart the process on the client side without losing meaningful conversation state), while long-running agent clients may hold a single session open for an entire multi-hour task. Servers that keep meaningful state in memory between calls need to think about what happens on a mid-session client restart.

None of this is exotic, but it explains why a server that "works fine in my testing" can misbehave the moment a different client, or a different network topology, gets involved.

What this means for your MCP server design

Pulling the comparison together into concrete guidance:

  • Write tool descriptions assuming a human might read them before approving a call — clear names, documented parameters, no unexplained jargon — because Claude Desktop and Cursor both put a person in that path by default.
  • Return structured, specific error messages, not opaque failures — because agentic clients like Claude Code will often try to recover and continue without a human reading the raw exception.
  • Keep destructive or high-stakes tools opt-in and clearly labeled as such, since some clients (and definitely some custom clients) will run them without per-call confirmation.
  • Do not assume resources, prompts, or sampling will be used even if you implement them — make tools carry full functionality on their own.
  • If you expect to be embedded in a custom, human-absent pipeline, add your own authorization checks server-side rather than trusting that the calling client enforced anything.
  • Test against more than one client, and against both stdio and remote transports, before calling your server "done."

None of these are exotic ideas once you see the client landscape laid out side by side — they are exactly what you would predict once you internalize that MCP fixes the wire protocol and leaves the entire experience layer, from confirmation dialogs to autonomy level, up to each host application.

Where to go from here

The pattern that emerges from comparing Claude Desktop, Claude Code, Cursor, and custom SDK-based clients is that MCP succeeds precisely because it does not try to standardize the experience layer. A protocol that dictated exactly how every host must ask for permission, or exactly how autonomous an agent is allowed to be, would have made far less sense across use cases as different as a consumer chat app and an unattended nightly pipeline. Instead, MCP standardizes the boring, load-bearing part — capability discovery and invocation — and lets each client compete on the part that actually matters to its users: trust, autonomy, and integration depth.

If you are building on the server side of this relationship and want the practical mechanics — designing tool schemas that read well across clients, handling authentication for remote deployments, structuring resources and prompts, and testing against multiple hosts before shipping — that is exactly the ground covered in our companion guide, Building & Integrating MCP Servers. Once you understand how differently Claude Desktop, Claude Code, Cursor, and custom clients actually behave, building a server that works well across all of them stops being guesswork and starts being a design decision you can make deliberately from day one.

MCP Client Comparison: How Different AI Tools Support MCP · TeachYou Academy