What Is MCP? The Model Context Protocol, Explained From the Wire Up
MCP is a specification for exactly two exchanges: an AI application asking a service what it can do, and then asking it to do one of those things. The messages are JSON-RPC 2.0. They travel either down a pipe to a subprocess or over a single HTTPS endpoint. There is no bespoke binary framing, no manifest format to learn, no runtime to install. If you can read JSON and follow an HTTP request, you can read a full MCP session end to end — which is why this page starts with the traffic and only afterwards explains the ideas behind it.
Anthropic published the Model Context Protocol in November 2024 along with the JSON schema and SDKs for several languages under an open license. Client implementations now ship from multiple vendors, and a server written today is not tied to whoever made the model calling it.
Say plainly what MCP is not, because most confusion starts there. It is not a model. It is not an agent framework. It is not a hosting product, a plugin store, or a replacement for HTTP APIs — most remote MCP servers are a thin front for an ordinary REST service that already existed. What the protocol adds is a shared way to describe that service to a model, negotiate which optional features both sides support, stream progress while slow work runs, and carry an identity. The model then picks what to call, instead of a developer wiring each call by hand.
Ten Seconds of Actual Traffic
A session opens with a handshake. The client announces the protocol revision it wants, the optional features it can support, and who it is:
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "my-agent", "version": "0.1.0" } }
}The server replies with the revision it will actually speak — which may be older than the one requested — its own capabilities, and optionally a block of free-text instructions written for the model rather than for the developer:
{ "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2025-06-18", "capabilities": { "tools": { "listChanged": true } }, "serverInfo": { "name": "clipspeed-creator-agent", "version": "1.0.0" }, "instructions": "Submit a video, then poll for finished clips." }
}The client then fires notifications/initialized — no id, no reply expected — and normal traffic begins. Every message after that is one of three shapes. A request carries id, method and params. A response carries the same id plus either result or error. A notification omits id entirely and nobody answers it. That grammar is JSON-RPC 2.0 verbatim; MCP contributes the method names and the payload schemas, nothing more.
Two consequences fall out immediately. First, a session is stateful — the handshake establishes what both ends agreed to, so you cannot fire a tool call in isolation and expect a strict server to honour it. Second, everything is inspectable: no step in an MCP conversation is hidden from you, which makes debugging a matter of reading, not guessing.
The Method Names Worth Memorising
The vocabulary is small and has stayed stable across revisions. In rough order of how often you will meet them:
initialize,notifications/initialized,ping— session lifecycle and liveness.tools/list,tools/call— the pair that carries almost every real integration.resources/list,resources/read,resources/subscribe— addressable context.prompts/list,prompts/get— named templates the user invokes.logging/setLevel,completion/complete— diagnostics and argument autocompletion.
If you have written a JSON-RPC client before, most of this is already familiar; the learning curve is in the semantics of the primitives, not the framing.
Protocol revisions are date-stamped rather than semver-numbered — 2024-11-05, 2025-03-26, 2025-06-18 — and the version in play is settled during the handshake. That design choice matters in practice: a newer client can hold a conversation with an older server as long as they can agree on a shared revision, so upgrading one side of a deployment does not automatically strand the other. It also means "which MCP version?" is a real question with a real answer you can read off the wire, rather than a vague statement about how recently something was built.
Capability negotiation runs on the same principle. A server that does not advertise resources in its initialize result does not have resources, and a well-behaved client will not ask. Never assume a feature is present because the specification describes it.
Why Anyone Bothered: Integration Math
Picture one small team with three places an AI does work: a desktop assistant, a coding CLI, and an unattended agent in CI. Between them they need six services — the issue tracker, the git host, the analytics warehouse, the internal admin API, a support inbox, and a video pipeline. Without a shared protocol that is eighteen separate integrations, each owned by somebody, each drifting out of date on its own schedule, each rewritten when a fourth host appears.
With MCP it is nine pieces: six servers and three clients. The multiplication becomes addition. Each service is integrated once, by the people who understand it, and tested once. Each host implements the protocol once and inherits every server that exists — including ones written after the host shipped.
Wire standards have made this trade repeatedly. Mail clients stopped needing per-provider code once SMTP and IMAP settled; every new mail service became usable in every existing client for free. MCP is the same bargain applied to tool calls, with the extra wrinkle that the consumer on the other end is a language model, so the description of each operation has to be good enough for a model to choose correctly from it.
That is the whole value proposition. Everything else in the specification — capability negotiation, session ids, structured errors, progress notifications — exists to make that one exchange unambiguous and safe.
Three Words the Spec Uses Precisely
The documentation is consistent about three terms, and conversations go badly when people blur them.
- Host — the application a person actually uses, which holds the model and the conversation. A desktop assistant, a coding CLI, an IDE, or your own agent process.
- Client — a connector inside the host that maintains exactly one session with exactly one server. A host wired to five servers is running five clients. This one-to-one rule is the isolation mechanism: a server sees its own session and nothing else, not the host's other connections and not the full conversation.
- Server — whatever exposes capabilities. A local binary reading your filesystem, or a multi-tenant HTTPS service in front of a SaaS product.
+---------------------------------------------------+ | HOST (desktop app, coding CLI, IDE, your agent) | | holds the model + the conversation | | +------------+ +------------+ | | | MCP client | | MCP client | ... | 1 client : 1 server | +-----+------+ +-----+------+ | +---------|----------------|-------------------------+ | JSON-RPC | JSON-RPC | over stdio | over HTTP(S) +-----v------+ +-----v-----------+ | LOCAL | | REMOTE | | subprocess | | https://.../mcp | +-----+------+ +--------+--------+ | | local files, git third-party API, DB
Local versus remote is the first architectural fork you hit, and it decides how credentials are stored, how updates reach users, and how large the blast radius is when something misbehaves. Remote MCP vs Local MCP Servers is the page devoted to that choice; the internals of one live session are covered in How MCP Servers Work: Architecture and Request Flow.
Tools, Resources, Prompts: Who Pulls the Trigger
A server can expose three kinds of capability. The useful way to tell them apart is not what they contain but who decides to use them.
Tools are model-controlled. Each has a name, a description, and a JSON Schema for its arguments. The client retrieves them with tools/list and hands them to the model, which picks one and fills in the arguments. Side effects live here — sending a message, writing a row, kicking off a job. When someone says "an MCP server", they almost always mean tools.
Resources are application-controlled. They are readable, addressable context identified by URI: a file, a record, a document. The host decides whether and when to place one into the model's context. A resource read is meant to be free of side effects — closer to a GET than a POST.
Prompts are user-controlled. Named, parameterised templates the host surfaces deliberately as a slash command or a menu entry. The person chooses; the model does not reach for prompts on its own.
who decides primitive example side effects? ----------- --------- --------------------- ------------- model tool send_email, run_query yes application resource file:///repo/README.md no user prompt /summarize-this-pr n/a (template)
Most hosts today implement tools thoroughly and the other two patchily. Build for tools first; treat resources and prompts as enhancements you check for rather than depend on.
The Reverse Channel: Sampling, Roots and Elicitation
Traffic does not only flow client-to-server. Three negotiated capabilities run the other way, and they are the part of the specification people most often forget exists.
Sampling lets a server ask the host to run a model completion on its behalf. The point is credential economics: server logic can use a language model without shipping and paying for its own API key, and the user keeps a single approval surface for model usage.
Roots let the client tell the server which directories or URIs are in scope for this session. A filesystem server does not have to guess which project you meant, and does not have to be handed your whole home directory to be useful.
Elicitation lets a server pause mid-call and ask the user for something it is missing — a confirmation, a value it cannot infer — instead of failing and hoping the model retries with better arguments.
All three are announced during initialize, and support is genuinely uneven across hosts. A server that hard-depends on sampling will simply not work in a client that never advertised it, and the failure looks like a mysterious dead tool rather than an explicit error. Feature-detect from the handshake result, and always ship a path that works with tools alone.
Two Transports, One Message Format
The specification defines two standard transports. The JSON is byte-for-byte the same in both; only the plumbing changes.
stdio. The host launches the server as a child process and exchanges newline-delimited JSON over stdin and stdout. No network, no port, no authentication layer — the trust boundary is process ownership. Anything the server writes to stderr is captured as logs, which makes stderr the only sane place to print diagnostics, since stdout is the wire. This is the correct transport for anything touching local state.
Streamable HTTP. The server exposes one endpoint. The client POSTs JSON-RPC to it and gets back either a single application/json response or a text/event-stream when the server wants to push progress notifications before the final result — so clients must advertise both in the Accept header. The server may issue an Mcp-Session-Id on initialize, which the client echoes on every later request; an HTTP DELETE tears the session down. From the 2025-06-18 revision onward, clients also send an MCP-Protocol-Version header on post-handshake requests.
Streamable HTTP superseded an earlier HTTP+SSE design that used two endpoints. A tutorial that tells you to open a long-lived /sse connection and POST back to a separate URL is describing the deprecated shape; some servers still accept it for compatibility.
| stdio | Streamable HTTP | |
|---|---|---|
| Where it runs | Subprocess on the user's machine | Anywhere reachable over HTTPS |
| Auth | None — inherits OS process trust | OAuth 2.1 or a Bearer token |
| Credentials live | Local config file or environment | On the server, or as a scoped key |
| Multi-user | No — one process per person | Yes, sessions are isolated |
| Shipping a fix | Every user must upgrade | Deploy once, server-side |
| Local file access | Yes | No |
| Natural fit | Filesystem, git, local DBs, dev tooling | Hosted products, team services |
Identity: What a Bearer Key Actually Is
A stdio server has no authentication story at all — it runs as you, with your permissions. Remote servers need one, and MCP's authorization specification builds on OAuth 2.1. The server plays the role of an OAuth resource server: it publishes protected-resource metadata (RFC 9728) pointing at an authorization server, and access tokens are bound to a specific resource through Resource Indicators (RFC 8707), so a token minted for one server cannot be replayed against a different one. An unauthenticated request should come back as HTTP 401 with a WWW-Authenticate header naming that metadata document — that is how a client discovers where the flow starts without being told out of band.
In daily use you meet two flavours. GUI clients do the browser OAuth dance: you click connect, approve, and the client stores and refreshes tokens without a key ever touching a config file. CLI and headless clients send a long-lived key as Authorization: Bearer <key>.
It is worth seeing what a real key looks like rather than treating it as an abstraction. ClipSpeedAI's keys are the string csai_live_ followed by 48 hexadecimal characters, generated from 24 random bytes. You create one with POST /auth/api-keys (in the product UI: Account → API & Integrations → Generate API Key). Only the key's prefix is stored for display — the first 18 characters plus an ellipsis — so the full value is shown once at creation and cannot be retrieved afterwards. GET /auth/api-keys lists what exists, with each key's name, prefix, plan, rate limit, requests today, total requests, last request time, active flag and creation date. DELETE /auth/api-keys/:id revokes one: the key is marked inactive and stamped with a revocation time.
Three habits follow from that design. Issue a separate key per integration, because a per-key request counter only tells you something if the keys are not shared. Revoke rather than rotate everything when one leaks. And never paste a key into a file that a repository tracks — the Bearer pattern is the easiest credential in this entire ecosystem to commit by accident. MCP Authentication: OAuth and Bearer Keys walks both flows step by step; the scoping question is handled in MCP Security: Scopes, Keys and Safe Tool Design.
Connecting a Client, and What "Supported" Means Here
Adding a server to a client comes down to two facts — an endpoint and a credential. For a remote server that is a URL plus an Authorization header; for a local one it is the command that launches the process, plus any arguments scoping it to a directory.
For Claude Code the exact, verified command is:
claude mcp add --transport http clipspeed https://api.clipspeed.ai/mcp \ --header "Authorization: Bearer <API_KEY>"
Other clients store the same two facts in their own file format, under their own key names. Rather than reprint config that may be stale or wrong for your version, the honest instruction is: add ClipSpeedAI as an HTTP MCP server pointing at https://api.clipspeed.ai/mcp, with your key in the Authorization header, following that client's own MCP documentation. An npm package, clipspeed-mcp, also exists.
Support is not uniform, and it is worth being precise about tiers instead of listing every client as equivalent:
- Verified end to end. Claude on claude.ai, Claude Code, Claude Desktop and Windsurf have been set up and driven through a full clipping run. Start from ClipSpeedAI MCP for Claude Code: Complete Setup Guide if you are on the CLI, or from the Claude Desktop equivalent for the desktop app. Browser-based Claude differs from all the file-based clients — it uses a one-click OAuth connector, covered in ClipSpeedAI MCP for Claude (claude.ai): Complete Setup Guide.
- Protocol-compatible, verification in progress. Cursor, Codex, OpenClaw and Hermes speak the same transport and the same Bearer scheme, so the setup is expected to work, but ClipSpeedAI has not certified them end to end. Their per-client pages — such as ClipSpeedAI MCP for Cursor: Complete Setup Guide — say so plainly rather than implying parity.
- Vendor-gated. ChatGPT connector support depends on OpenAI's own rollout, which no server author controls. That status is tracked in ClipSpeedAI MCP for ChatGPT: Complete Setup Guide.
Notice what does not change across those three groups: the endpoint, the key, and the ten tool definitions. Only the place you type them moves.
Debugging From a Shell With curl
Because the transport is ordinary HTTP and the payload is ordinary JSON, you can drive a remote MCP server from a terminal. This is the single fastest way to decide whether a problem is in the server or in your client's configuration — and the answer is usually the configuration.
curl -isS https://api.clipspeed.ai/mcp \ -H "Authorization: Bearer $CLIPSPEED_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "MCP-Protocol-Version: 2025-06-18" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'The -i matters: a strict server expects initialize first and hands back an Mcp-Session-Id response header you must echo on everything after it. If you skip the handshake and get an error, that is the protocol working, not a broken server. Invoking a tool is the same envelope with a different method:
{ "jsonrpc": "2.0", "id": 7, "method": "tools/call", "params": { "name": "list_templates", "arguments": {} }
}A successful call returns a content array rather than a bare value:
{ "jsonrpc": "2.0", "id": 7, "result": { "content": [ { "type": "text", "text": "..." } ], "isError": false }
}For that particular tool the text enumerates the caption-style templates the engine can burn in — the real ids are karaoke, hormozi, beasty, fire, youshaei and cinematic, and you pass the one you want back as captionStyle on a later call. Reading a live tool list beats reading documentation about it, which is the general lesson here.
One distinction is easy to miss and important to get right: a protocol error is a JSON-RPC error object (malformed request, unknown method), while a tool error is a normal result with isError: true. The split is deliberate. A tool that fails should report its failure as content the model can read and react to, not as a transport fault the model never sees.
A Node Script With No Model In It
The official TypeScript SDK handles the handshake, the session id and the streaming for you. This connects to a remote server, lists tools, and calls one:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; const transport = new StreamableHTTPClientTransport( new URL("https://api.clipspeed.ai/mcp"), { requestInit: { headers: { Authorization: `Bearer ${process.env.CLIPSPEED_API_KEY}` } } }
); const client = new Client({ name: "demo", version: "1.0.0" });
await client.connect(transport); const { tools } = await client.listTools();
console.log(tools.map(t => t.name)); const result = await client.callTool({ name: "list_templates", arguments: {}
});
console.log(result.content); await client.close();There is no model anywhere in that program, and that is the point worth internalising. An MCP server is a perfectly ordinary service you can script against deterministically, in CI, from a cron job, or from a test. Handing its tool list to a model is one way to consume it, not a requirement. Teams frequently discover this backwards — they build a server for an assistant and then find their own automation using it directly because the schemas were already written and already validated.
Twenty Lines That Are a Working Server
On the Python side, the FastMCP helper derives the JSON Schema from your type hints and uses the docstring as the description the model reads. The docstring is not a comment here — it is production copy.
from mcp.server.fastmcp import FastMCP mcp = FastMCP("text-utils") @mcp.tool()
def word_count(text: str) -> int: """Count the whitespace-separated words in a string. Use this when the user asks how long a passage is. Returns an integer count. """ return len(text.split()) @mcp.resource("config://limits")
def limits() -> str: """Current service limits, as JSON.""" return '{"max_chars": 100000}' if __name__ == "__main__": mcp.run() # stdio by defaultThat is complete. Point a client at python server.py and the tool shows up in its list. Moving this to a remote deployment mostly means swapping the transport and adding an authorization layer in front — the function bodies do not change, and neither do the schemas. Packaging, structured errors, logging to stderr and deployment are handled properly in How to Build an MCP Server (Practical Guide).
Write the docstring before the implementation. It is the text a model uses to decide whether this tool is the right one, and a vague first line is the most common reason a technically correct server never gets called.
Case Study: Ten Tools Built Around Work That Takes Minutes
MCP tool calls are request/response, which suits a database query and fights anything that takes minutes. Video is the clearest illustration, so it is worth walking one real server rather than a hypothetical: ClipSpeedAI, an AI clipping engine exposed at https://api.clipspeed.ai/mcp over Streamable HTTP.
Ten tools, and the interesting thing is their shape rather than their subject:
submit_to_clipspeed— drops a video URL or file into ClipSpeed. The clip button.check_clips— returns the finished, scored, captioned 9:16 vertical clips for a projectId, each with a title, a viral score and a download URL.discover_trending— finds the fastest-growing recent video in a niche to turn into shorts, searching only videos published in roughly the last three weeks.creator_pack— per-clip suggested titles, hooks and best posting times for a projectId.list_templates— the caption-style templates, passed back ascaptionStyle.publish_to_youtube— publishes a finished clip, defaulting to private, taking a projectId plus optional clipId, title and privacyStatus.clip_livestream,check_livestream,extend_livestream,stop_livestream— live mode, keyed on a subscriptionId.
recorded video live stream -------------- ----------- submit_to_clipspeed (returns clip_livestream (returns a | a projectId) | subscriptionId) v v check_clips -> "still working" check_livestream -> "monitoring" | | = stream still live v v check_clips -> clips + scores extend_livestream / stop_livestream
The submit call returns fast with a handle. The check call is cheap and idempotent, so it can be polled without consequence. That keeps every individual call short, which matters for two independent reasons: clients time calls out, and a model parked inside a four-minute tool call can neither report progress nor be interrupted. The live tools add an explicit lifecycle — start, extend, stop — because a stream has no ending the server can infer on its own, and stop_livestream keeps the clips already produced, so stopping is not destructive. A check_livestream status of monitoring means the stream is still running and still being clipped.
Trying it is not free: there is no free plan. One free demo covers a single video under 30 minutes; past that, $1 starts a 3-day trial that converts unless cancelled, with Starter at $15/mo, Pro at $29/mo and Ultra at $49/mo, and annual billing saving 50%. The domain-side treatment lives in MCP for Video Editing and Clipping Workflows and in Livestream Clipping API: Clip While You Stream; chaining these tools with others is the subject of AI Agent Video Automation: End-to-End Workflows.
MCP, REST and Function Calling Do Different Jobs
These three get conflated constantly because their diagrams look similar. The clean separation: function calling is a model capability, REST is a convention for shaping HTTP, and MCP is a protocol that sits between a host and a service.
| REST API | Function calling | MCP | |
|---|---|---|---|
| What it is | HTTP resource convention | Model emits a structured call | Protocol for tool exchange |
| Discovery | Out-of-band docs or OpenAPI | You supply schemas per request | tools/list at runtime |
| Who defines tools | N/A | The application developer | The server author |
| Portability | Universal, glue per client | Tied to one vendor's API shape | Any MCP client, unmodified |
| State | Typically stateless | Stateless per request | Session-based, negotiated |
| Progress streaming | Ad hoc (SSE, websockets) | Not part of the concept | Built in via notifications |
| Who executes | Your code | Your code, always | The server |
Function calling and MCP are complements. A host takes MCP tool definitions and converts them into whatever function-calling format its model expects; the contribution MCP makes is that those definitions arrive from a server at runtime instead of being compiled into the application. The trade-offs are argued out in MCP vs REST API: When to Use Each and in MCP vs Function Calling: What Actually Differs, but the short version is easy to state — if you control both ends and the caller is code rather than a model, a plain REST client is simpler and you should use one.
When Not To Reach For MCP
Pretending a protocol fits everywhere wastes people's afternoons. Cases where it is the wrong choice:
- Machine-to-machine calls at volume. With no model in the loop, MCP adds a handshake, a session and a schema layer that buy you nothing. Call the underlying API.
- Latency-sensitive paths. Discovery, negotiation and JSON-RPC framing sit on top of whatever the service already costs.
- Large binary payloads. The content model is oriented toward text, images and embedded resources. Move gigabytes with a signed URL that a tool hands back instead.
- Sprawling tool surfaces. Every listed tool occupies context on every turn, and large tool surfaces are widely reported by practitioners to make model selection worse — the opposite of the intent. Treat curation as a design requirement.
- Workflows that must be identical every run. A model choosing tools introduces variance by construction. Script it.
- Hard multi-tenant isolation. Remote servers can do this well, but it is work you do; the protocol does not enforce tenancy on your behalf.
A quick test: if a competent person reading the tool list and deciding what to call would add value, MCP fits. If the call sequence is known before the program runs, it probably does not.
The Threat Model Is Prompt Injection
MCP does not invent new vulnerability classes so much as relocate the decision-maker. A model that reads untrusted content — a web page, an inbound email, a pull request diff — and then chooses tools is exposed to prompt injection, and the protocol has no way to tell a legitimate instruction from an injected one. Text is text on the wire. The mitigations therefore live in the host and in how tools are designed:
- Scope keys narrowly and issue them per integration. A key that can submit jobs and read results cannot delete an account. Per-integration keys are also the only ones you can revoke without breaking everything else, and revocation is a real operation — ClipSpeedAI's
DELETE /auth/api-keys/:iddeactivates a single key and records when. - Keep destructive operations behind confirmation and say so in the tool description, so a host can raise an approval prompt at the right moment.
- Treat tool output as untrusted input. Whatever a server returns flows into the model's context; a hostile or compromised server can attempt to steer the next decision.
- Audit what you install locally. A stdio server runs as a child process with your full user permissions. "It's only an MCP server" is the same trust decision as "it's only an npm postinstall script."
- Bind tokens to a resource. Resource Indicators exist precisely to stop a token issued for one server being replayed against another.
Note where the approval gate sits: in the host, not in the protocol. MCP does not decide whether a call is safe. It supplies enough structure for the host to ask a person. Confused-deputy risks in servers that proxy other APIs on a user's behalf are covered in MCP Security: Scopes, Keys and Safe Tool Design.
Naming and Describing Tools a Model Can Choose Correctly
A technically flawless server can still be dead weight, because a model selects tools by reading names, descriptions and schemas and nothing else. Those strings are the interface.
- Name the intent, not the implementation.
check_clipstells a model when to reach for it.get_job_status_v2does not. - Write descriptions as instructions. State when to use it, when not to, and what the return value means. A sentence or two of direction beats a paragraph of background.
- Constrain the schema. Enums, formats and required fields remove whole categories of invalid call before they happen. An unconstrained
stringis an invitation to guess. - Return something a model can act on. Dumping a 400 KB JSON blob into context has a real cost. Summarise, paginate, and offer a second tool for detail.
- Make errors instructive. "Invalid input" produces a retry loop; "start_time must be less than end_time" produces a fix.
- Keep the surface small. A short list of clearly distinct tools is easier for a model to choose from than a long list of overlapping ones, and costs fewer tokens per turn.
Each of these is expanded with before-and-after rewrites in MCP Tool Design: Writing Tools an Agent Can Actually Use.
Claims About MCP That Don't Survive Contact
"It's an Anthropic product." Anthropic created and published it, but the specification and SDKs are open and clients and servers ship from multiple vendors. Writing a server does not bind you to one model provider.
"It replaces REST." Most remote MCP servers call a REST API internally. MCP standardises how capabilities are described and negotiated for a model; the API underneath is untouched.
"You need an LLM to use one." No. The Node example above never touches a model. A server is a normal service you can script.
"More tools means more capability." Past a point it means worse selection and higher token cost every turn.
"Installing a server is safe because it's sandboxed." The protocol sandboxes nothing. Local stdio servers are child processes carrying your permissions.
"Every client supports every feature." Sampling, roots, elicitation and resource subscriptions are negotiated. Many hosts implement tools and little else.
"Each client still needs its own integration." This is the belief the protocol exists to kill. One endpoint plus one auth scheme serves every compliant client; the per-client guides differ mainly in where you type the two values.
"A key can be recovered from the dashboard later." Not on a well-built server. ClipSpeedAI stores only the first 18 characters of a key for display, so the full value exists exactly once, at creation. Lose it and you issue a new one.
A Path That Actually Works
Four steps, in this order, because each one makes the next easier to debug:
- Install one existing server in a client you already use — a filesystem or git server is a good first target — and watch its tools appear. You are looking for the shape of the experience, not building anything yet.
- Run
tools/listwith curl against a remote server so you have seen raw JSON-RPC once with your own eyes. Every later problem is easier to diagnose once you know what correct traffic looks like. - Write a two-tool server with the Python or TypeScript SDK. Keep it on stdio and ignore authentication entirely at this stage. Spend your effort on the tool descriptions.
- Only then go remote, which is where OAuth, sessions, session ids and multi-tenancy start to matter and where a mistake has an audience.
From there the cluster splits by what you are trying to do. For mechanism, read How MCP Servers Work: Architecture and Request Flow. For implementation, How to Build an MCP Server (Practical Guide). If you are picking a working environment, Claude Code vs Cursor for MCP Workflows compares two common ones. And if your interest is the product rather than the protocol, MCP for Creators: Automating Video Without Code, AI Clipping API: Programmatic Short-Form Video and Video Clipping API for Developers approach the same server from the outside in, while Best MCP Servers for Video and Content Workflows surveys what else exists.
Frequently asked questions
- What does MCP stand for, and who maintains it?
- Model Context Protocol. Anthropic introduced it in November 2024 and published the specification, JSON schema and SDKs under an open license. Clients and servers now ship from multiple vendors, so writing a server does not tie you to one model provider.
- Is MCP just a wrapper around a REST API?
- Internally, often yes — plenty of remote MCP servers call an existing REST service. The difference is what MCP standardises around it: runtime discovery via tools/list, schema descriptions a model can read, session negotiation, progress streaming and an authorization model. If no model is involved and you own both ends, call the REST API directly instead.
- What is the difference between an MCP client and an MCP server?
- The server exposes capabilities — tools, resources, prompts. The client is a connector inside the host application that maintains exactly one session with exactly one server. A host wired to five servers runs five clients, and that one-to-one rule is how sessions stay isolated from each other.
- Which transport should I choose, stdio or Streamable HTTP?
- Use stdio when the server needs local files, local databases or developer tooling on the user's machine — it runs as a subprocess with your permissions and has no auth layer. Use Streamable HTTP when the server fronts a hosted service, must serve multiple users, or needs to be fixable without anyone reinstalling anything.
- What does an MCP API key look like, and can I get it back later?
- It depends on the server. ClipSpeedAI's keys are csai_live_ followed by 48 hexadecimal characters, generated from 24 random bytes. Only the first 18 characters are stored for display, so the full key is shown once at creation and cannot be retrieved afterwards. You can list your keys with their prefixes, plans, rate limits and request counters, and revoking one deactivates that key and records when it happened.
- How does authentication work for a remote MCP server?
- The authorization specification builds on OAuth 2.1, with the MCP server acting as a resource server: it publishes protected-resource metadata (RFC 9728) and accepts tokens bound to it through Resource Indicators (RFC 8707). GUI clients generally use the browser OAuth flow; CLI and headless clients generally send a long-lived key as an Authorization: Bearer header.
- What happens when a tool fails?
- There are two distinct failure modes. A protocol failure — malformed request, unknown method — returns a JSON-RPC error object. A tool failure returns a normal result with isError set to true and a readable explanation in the content array, so the model can see what went wrong and adjust rather than hitting an opaque transport fault.
- Can an MCP server run a job that takes several minutes?
- Not comfortably inside one tool call. The standard pattern splits the work: one tool submits and returns a handle immediately, and a second cheap, idempotent tool is polled for status and results. ClipSpeedAI's submit_to_clipspeed and check_clips pair works exactly this way, and its live tools add explicit extend and stop calls because a stream has no ending the server can infer.
- Do I need a separate integration for every AI client I use?
- No — removing that work is the reason the protocol exists. One endpoint plus one auth scheme serves every compliant client. What differs between clients is where you enter those two values and whether they use OAuth or a Bearer key.