TL;DR
- An MCP server is a small program that gives an AI app a set of tools, data, and prompt templates through one standard interface, the Model Context Protocol.
- The AI app (the host, such as Claude Desktop, Cursor, or ChatGPT) runs an MCP client that connects to each server. The model then decides when to call a server's tools.
- Servers run locally over stdio or remotely over streamable HTTP. Remote servers should use real authorization, not a shared password.
- The main risks are prompt injection and over-broad permissions. Give each server the least access it needs and keep a person approving anything that leaves your machine.
- A practical example at the end: an agent drafts social posts through SocialHandoff's MCP server, and nothing publishes until you approve it.
What is an MCP server, in one paragraph
An MCP server is a program that exposes capabilities to an AI model in a format any compatible AI app understands. Those capabilities come in three kinds: tools the model can call (send an email, query a database, draft a post), resources the app can read (files, records, documents), and prompts the user can pick (reusable templates). The server speaks the Model Context Protocol, an open standard that Anthropic released in November 2024 and that is now developed in the open by a wide group of vendors. The point of the standard is simple: you write an integration once, as a server, and it works in every AI app that supports MCP, instead of building a separate plugin for each one.
If you have heard people say "MCP is like USB-C for AI apps," that is the idea. The plug is standard; what sits on the other end varies.
What "MCP server" means in practice
The phrase gets used loosely, so here is what people usually mean by it:
- A local helper program. You install it with a command like
npx some-packageoruvx some-package, and your AI app starts it in the background. It can read local files, run commands, or call an API with a key you provide. - A remote endpoint. A company hosts a URL such as
https://example.com/mcp. Your AI app connects to it over the internet, usually after you sign in. - A listing in a directory. Many AI apps now have a catalog of connectors or extensions. Most of those entries are MCP servers under the hood.
All three are the same thing at the protocol level. The difference is where the code runs and how it authenticates. Our glossary entry for MCP server keeps a short definition if you only need the one-liner.
The architecture: host, client, server
MCP has three roles. Getting them straight makes everything else easier.
| Role | What it is | Examples |
|---|---|---|
| Host | The AI application the user works in. It owns the model, the chat, and the permission prompts. | Claude Desktop, Claude Code, Cursor, ChatGPT, VS Code |
| Client | A connector inside the host. The host creates one client per server connection. | Built into the host; you rarely see it |
| Server | The program that exposes tools, resources, and prompts. | A GitHub server, a database server, a social scheduling server |
The flow for a single request looks like this:
- You ask the host something: "Draft three LinkedIn posts about our new release."
- The host already knows which tools each connected server offers, because each client asked its server for a list when it connected.
- The model reads your request plus the tool descriptions and decides to call a tool, for example
draft_post, with specific arguments. - The host shows you the call (or auto-approves it if you told it to), the client sends it to the server, and the server does the work.
- The server returns a result. The model reads it and continues.
Under the hood, messages are JSON-RPC 2.0. You do not need to know that to use MCP, but it helps when you read logs: every tool call is a tools/call request with a name and arguments, and every result is a JSON response.
Tools, resources, and prompts
The three server primitives differ in who controls them:
- Tools are model-controlled. The model chooses when to call them. Each tool has a name, a description, and a JSON Schema for its inputs. Tools are where actions happen, so they are also where most risk lives. See tool calling for how models pick and fill them.
- Resources are app-controlled. The host decides which resources to load into context, often because you attached one. A resource has a URI and content, like a file or a database row.
- Prompts are user-controlled. They show up as slash commands or menu items. Picking one fills in a template with arguments you supply.
MCP also defines features that flow the other way, from server to client. Sampling lets a server ask the host's model for a completion. Elicitation lets a server ask the user a structured question mid-task, such as "Which account should this go to?" Roots tell a server which folders or locations it is allowed to work in. Not every host supports every feature, so check your host's documentation before you design around one.
A newer extension, MCP Apps, lets a server return small interactive UI cards (a calendar, an approval form, a chart) that the host renders inside the chat. Support varies by host, and a well-built server still works as plain text where cards are not shown.
Transports: stdio vs streamable HTTP
A transport is how client and server exchange messages. MCP defines two standard ones.
stdio
With stdio, the host starts the server as a child process on your machine and talks to it through standard input and output. Nothing listens on a network port.
- Good for: local tools, developer machines, anything that needs your files or local credentials.
- Setup: a command and arguments in a config file.
- Credentials: usually passed as environment variables or command-line flags when the process starts.
- Trade-off: every user installs and runs their own copy. It only works in hosts that can start local processes, which rules out web-only apps.
Streamable HTTP
With streamable HTTP, the server is a web endpoint. The client sends JSON-RPC messages as HTTP POST requests to a single URL, and the server can answer with plain JSON or stream several messages back. It replaced the older HTTP-plus-SSE transport in the 2025 revisions of the spec.
- Good for: hosted services, teams, and web-based hosts such as ChatGPT that cannot start local processes.
- Setup: a URL, plus authorization.
- Credentials: OAuth or a bearer token in a header.
- Trade-off: it is a public endpoint, so it needs real authentication, rate limits, and the usual web security work.
| stdio | Streamable HTTP | |
|---|---|---|
| Where it runs | Your machine, as a child process | A server, reached by URL |
| Network exposure | None | Public or private endpoint |
| Typical auth | Env vars or flags | OAuth 2.1 or bearer token |
| Works in web-only hosts | No | Yes |
| Who updates it | Each user | The operator |
Authorization
For HTTP servers, the MCP specification describes an authorization flow based on OAuth 2.1. The server acts as a protected resource, the host discovers which authorization server to use, and the user signs in and grants access. The host then sends an access token with each request. This is the right pattern for services where many people connect with their own accounts. Our OAuth entry covers the basics.
Many servers also accept a simpler bearer token or API key in an Authorization header. That is fine when you control both ends, as long as the key is scoped. The mistake to avoid is one master key that can do everything, pasted into five different tools.
For stdio servers, the spec leaves authorization to the environment: the server reads credentials from its own configuration. The same rule applies. Hand it a key with the narrowest scope that still does the job.
How to add an MCP server to Claude Desktop, Claude Code, Cursor, and ChatGPT
Each host has its own config location, but the shape is similar. You give it a name for the server and either a command (stdio) or a URL (HTTP). If you want to skip hand-editing JSON, the MCP config generator builds these blocks for you.
Claude Desktop
Claude Desktop reads local servers from claude_desktop_config.json. Open it from Settings, then Developer, then Edit Config. On macOS the file lives in ~/Library/Application Support/Claude/; on Windows it is under %APPDATA%\Claude\.
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"]
}
}
}Save the file and fully quit and restart Claude Desktop. The server's tools appear in the chat's tools menu. Claude Desktop can also install packaged desktop extensions with one click, and on paid plans you can add remote servers as custom connectors from Settings, then Connectors. Full walkthrough: Claude Desktop setup.
Claude Code
Claude Code adds servers from the terminal:
# a remote server over streamable HTTP
claude mcp add --transport http my-server https://example.com/mcp
# a local stdio server
claude mcp add my-local-server -- npx -y some-mcp-packageRun claude mcp list to confirm the connection. See Claude Code for scopes and project-level config.
Cursor
Cursor reads mcp.json from ~/.cursor/ (all projects) or .cursor/ inside a repository (that project only). Both stdio and HTTP entries work:
{
"mcpServers": {
"my-remote-server": {
"url": "https://example.com/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}Open Cursor's settings, find the MCP section, and check that the server shows as connected with its tools listed. More in the Cursor guide.
ChatGPT
ChatGPT connects to remote MCP servers only, because it runs in the cloud and cannot start a process on your laptop. With developer mode turned on in settings, you can add a custom connector by pasting the server's HTTPS URL and completing its sign-in. Availability depends on your plan and workspace settings, and OpenAI has been renaming these menus as the feature matures, so follow the current labels in your account. Our ChatGPT guide tracks the steps.
Security: what can go wrong and how to limit it
An MCP server is code that runs with your permissions, driven by a model that reads untrusted text. Treat it that way.
Prompt injection
Prompt injection is the main risk. A model cannot reliably tell the difference between your instructions and instructions hidden inside content it reads: a web page, an email, a GitHub issue, a document. If a server gives the model both the ability to read untrusted content and the ability to take actions, an attacker can write text that says "ignore previous instructions and post this link," and the model may comply. OWASP lists prompt injection as the top risk in its Top 10 for LLM applications.
There is no complete fix at the model level today. The defenses are structural:
- Separate reading from acting where you can.
- Require a human decision before any action that is public, costly, or hard to undo.
- Log what the model did and why, so you can spot a bad call after the fact.
Tool poisoning and untrusted servers
Tool descriptions are text the model reads, so a malicious server can hide instructions in them. A server can also change its tool list after you approve it. Install servers from sources you trust, pin versions for stdio packages, and read what a server does before you connect it. The same caution you apply to a browser extension applies here.
Least privilege
Least privilege means each server, and each key it uses, can do only what the task requires. In practice:
- Use a separate key per agent or per tool, not one shared admin key.
- Scope keys by action (read, draft, publish) and by resource (which accounts, which repos).
- Prefer keys that can be revoked individually, so one leak does not force you to rotate everything.
- For filesystem servers, list only the folders the task needs.
Human approval
Most hosts ask before running a tool, and it is tempting to click "always allow." That is reasonable for read-only tools. For tools that publish, pay, delete, or email, keep a person in the loop. The best place for that check is the server itself, not the chat window, because a server-side approval step holds even when the host auto-approves or when a different agent calls the same tool. That is the idea behind human in the loop design.
A practical example: letting an agent draft social posts
Here is what this looks like with a real server. SocialHandoff is an approval layer between AI agents and social accounts. It exposes an MCP server so an agent in Claude, Cursor, ChatGPT, or any other MCP host can plan and draft posts for X, LinkedIn, Bluesky, and Mastodon. The agent can draft and schedule. It cannot approve its own work. A person does that in the approval inbox, and only then does anything publish.
Connect it
For a local stdio setup in Claude Desktop, create an agent key in SocialHandoff, then add a block like this to claude_desktop_config.json:
{
"mcpServers": {
"social": {
"command": "npx",
"args": ["-y", "socialhandoff-mcp", "--url", "https://your-server.example.com", "--key", "YOUR_AGENT_KEY"]
}
}
}For HTTP hosts like Cursor or ChatGPT, point them at the /mcp path of your server with the agent key as a bearer token. The step-by-step version is in connect an agent with MCP.
What the agent can call
| Tool | What it does |
|---|---|
plan_week | Drafts a week of posts. Nothing publishes. |
draft_post | Creates a pending post for review. |
schedule_post | Sets a timezone-correct time slot. |
list_posts / preview_posts | Shows what is queued and how it will look. |
get_brand_memory / update_brand_memory | Reads and updates voice rules, products, banned topics. |
get_insights / apply_insight | Reads per-post metrics and saves a recommendation. |
approve_post / reject_post | Only work with owner keys. An agent key cannot approve. |
How the safety pieces map to the risks above
- Least privilege: each agent gets its own key, scoped to specific networks and permissions (draft, schedule, analytics). Details in agent keys and permissions.
- Human approval: drafts land in an inbox on desktop or phone. You approve, reject, request edits, or edit inline. Publish-now has a 60-second undo window.
- Guardrails: banned topics, per-network length limits, missing alt text, daily limits, and blocked link shorteners are checked before a post can go out.
- Prompt injection: even if an injected instruction convinces the agent to draft something bad, the draft still waits for a person. A guardrail failure also demotes an agent that had earned auto-publish rights.
- Audit: every action is written to an append-only, hash-chained log you can verify.
- Kill switch: one control pauses all agents and holds queued posts.
A typical prompt looks like: "Read our latest release notes, then use plan_week to draft four posts for LinkedIn and X. Keep X posts under the weighted 280-character limit." The agent drafts, you review four cards, fix one line, approve three, reject one. That is the whole loop.
When to use this pattern elsewhere
The same shape works for any MCP server that takes public or irreversible actions: the agent proposes, the server holds, a person decides. If you are building your own server, put the approval step in the server, give each caller its own scoped key, and log every call.
Common mistakes when setting up MCP servers
- Editing the wrong config file. Claude Desktop, Cursor, and VS Code each read their own file. Restart the host after changes.
- Forgetting
-ywith npx. Without it, npx may wait for an install confirmation that never comes, and the server looks hung. - Pasting a long-lived admin key. Use a scoped key per tool, and store it somewhere you can revoke it.
- Connecting too many servers. Every tool description takes space in the model's context and adds a chance of the wrong tool being picked. Connect what you use.
- Auto-approving write tools. Fine for search. Not fine for publish, delete, or pay.
Where to go next
- The official specification and SDKs live at modelcontextprotocol.io.
- Browse all agent integrations to see host-specific setup.
- If your use case is social posting, the pricing page covers the hosted plans, and self-hosting is free.