An MCP (Model Context Protocol) server is a JSON-RPC service that exposes tools, resources, and prompts to an MCP client like Claude Desktop, Cursor, or Cline. The right pattern is stdio transport for local servers (the client spawns the server as a subprocess) and Streamable HTTP for remote servers (the client connects over HTTPS). The right auth pattern is OAuth 2.1 with PKCE for the remote case. The right tool schema is the JSON Schema that describes each tool’s input and output.
This post walks through the protocol, the two transports, the tool surface design, the auth mistake every team makes, and the deploy pattern for both local and remote servers.
Table of contents
- The protocol — what an MCP server actually does
- The transport — stdio for local, Streamable HTTP for remote
- The tool surface — JSON Schema, descriptions, and the model that reads them
- The auth mistake — shipping a remote server with no auth
- The deploy pattern — stdio for local, managed platform for remote
- How this fits the rest of the stack
- FAQ
The protocol — what an MCP server actually does
MCP is a JSON-RPC 2.0 protocol with three primitives the server exposes: tools (functions the model can call), resources (files, database rows, or other addressable data the model can read), and prompts (templated message starters the model can use). The client connects to the server, negotiates capabilities (initialize), and then the model can call any tool or read any resource that the server exposes.
The standard flow: the model receives a user prompt, the model decides it needs a tool, the client sends a tools/call request to the server, the server runs the tool, returns the result, the model incorporates the result into the response. The server is stateless between calls — the model holds the conversation state, the server is just a function dispatcher.
The right answer for a team that has a function the model should be able to call is to wrap the function as an MCP tool. The function takes a typed input, returns a typed output, and the tool’s JSON Schema describes both. The model sees the schema, the model calls the tool with the right arguments, the server returns the right output.
The transport — stdio for local, Streamable HTTP for remote
The first transport is stdio. The client spawns the server as a subprocess, the server reads JSON-RPC messages from stdin and writes them to stdout. The transport is local (the server is on the same machine as the client), it has no auth (the OS process isolation is the security boundary), and it is the right answer for a developer tool, an IDE plugin, a local CLI.
The second transport is Streamable HTTP. The server runs as a long-lived HTTP endpoint, the client connects with a POST request, the server can return a stream of events (SSE) or a single JSON-RPC response. The transport is remote (the server is on a different machine than the client), it needs auth (the network is the security boundary), and it is the right answer for a SaaS tool, a shared team tool, a server-side integration.
The gotcha: the older SSE (Server-Sent Events) transport was deprecated in MCP 2025-03-26. The right answer for a new server is Streamable HTTP, which supports both single responses and streaming. The right answer for a team that has an SSE-based server is to migrate to Streamable HTTP, because the new clients will not speak SSE.
The tool surface — JSON Schema, descriptions, and the model that reads them
The tool is the unit the model interacts with. The tool has a name, a description, and an input schema. The description is what the model sees when it decides whether to call the tool — the right answer is a clear, specific description that explains when the tool is the right one to call. The input schema is the JSON Schema that describes the arguments the tool takes.
The right pattern for a tool description:
- Start with what the tool returns, not what it does. The model reads ‘Get the current weather for a city’ and knows what to expect.
- Mention edge cases the model should know about. ‘Returns temperature in Celsius. For historical data, use get_weather_history instead.’
- Use specific names.
get_weatheris fine;do_thingis not.
The right pattern for the input schema is a strict JSON Schema with required for every mandatory argument, type for every property, and description for every property the model might be uncertain about. The model uses the schema to validate the arguments before calling — a clear schema means fewer malformed calls.
The auth mistake — shipping a remote server with no auth
The mistake is the same one every team makes: the team writes a remote MCP server, runs it on a public URL, and forgets the auth. The server is callable by anyone on the internet. The tools are the team’s database tools, the file system tools, the API integration tools. The attacker calls the tools, the server runs them, the data is gone.
The right answer for a remote MCP server is OAuth 2.1 with PKCE. The client (Claude, Cursor) handles the OAuth flow, the server validates the access token on every request, the token has a scope and an expiry. The right answer for a server that is meant to be used by a specific team is a per-team registration and a per-team access token.
The MCP spec recommends OAuth 2.1 with dynamic client registration for the auth. The right answer for a team that does not want to build OAuth from scratch is to use the mcp-remote or @modelcontextprotocol/sdk auth helpers, which implement the spec’s recommended pattern.
The deploy pattern — stdio for local, managed platform for remote
The local stdio server is deployed as a config entry in the client’s mcp.json (or claude_desktop_config.json for Claude Desktop). The client spawns the server when the user enables it, the client kills the server when the user closes the app. The right answer for a local developer tool is to ship the server as a binary and let the client spawn it.
The remote Streamable HTTP server is deployed as a long-running service on a platform. The server has a URL, the client POSTs JSON-RPC messages to the URL. The right answer for a remote server is a managed platform with logging, secrets, auth, and a public URL. The right answer for a team that is also shipping the tools the server wraps is to deploy the server on the same platform that hosts the tools.
The right answer for the team’s MCP server is to start with stdio (local dev, no auth, fast iteration) and graduate to Streamable HTTP (production, OAuth, multi-user). The transition is the same code, different transport. The right answer is to design the tool surface and the auth first, and let the transport be a config switch.
How this fits the rest of the stack
The infrastructure question is a small piece of a larger pattern: the team’s runtime, storage, database, secret store, logs, and deployment platform are all parts of the same platform. The right answer is to model the full stack before the project ships, not after. The RunxBuild hosting calculator is the right place to do that exercise — pick the runtime, the memory tier, the storage, the secret store, and the egress, and the calculator shows what the deploy actually costs at the team’s actual usage.
Useful related references:
FAQ
What is an MCP server?
An MCP (Model Context Protocol) server is a JSON-RPC 2.0 service that exposes tools, resources, and prompts to MCP clients like Claude Desktop, Cursor, or Cline.
What is the difference between stdio and Streamable HTTP transports in MCP?
stdio is for local servers — the client spawns the server as a subprocess. Streamable HTTP is for remote servers — the client connects over HTTPS.
How do I add OAuth to a remote MCP server?
Implement OAuth 2.1 with PKCE. The MCP spec recommends the mcp-remote auth helpers or the @modelcontextprotocol/sdk auth middleware.
What is a tool in MCP?
A tool is a function the model can call. The tool has a name, a description, and a JSON Schema for its input.
What is a resource in MCP?
A resource is addressable data the model can read. The server exposes a list, the model reads with resources/read.
What is a prompt in MCP?
A prompt is a templated message starter the model can use. The right pattern is for recurring workflows.
How do I deploy an MCP server?
The local stdio server is deployed as a config entry in the client’s mcp.json. The remote Streamable HTTP server is deployed as a long-running service on a platform.
Can I run an MCP server in a serverless function?
Not for stdio. For Streamable HTTP, yes — a serverless function can serve the JSON-RPC requests, though streaming may need a long-lived connection.