How to Implement MCP (Model Context Protocol) in 2026

MCP is how an AI client calls your code: tools, resources and prompts over JSON-RPC 2.0. This page gives you three working servers built against the current 2026-07-28 revision, and shows what the stateless rewrite changed.

CategoryAgent & AI protocols StatusStable, widely adopted Maintained byAnthropic Glippy checkAgent Interactivity (category 10)

MCP (Model Context Protocol) is an open standard that lets an AI application call your tools, read your resources and load your prompts over JSON-RPC 2.0. You run an MCP server, the AI client connects to it over stdio or Streamable HTTP, and the model gets typed, permissioned access to whatever that server chooses to expose. The current revision is 2026-07-28, which removed the connection handshake and made every request self-contained.

Why MCP matters for AI visibility

Structured data and llms.txt decide what an assistant can read about you. MCP decides what it can do with you. Once a model has your MCP server connected, it stops paraphrasing your marketing pages and starts calling tools/call against live inventory, live pricing and live account state. That is a different surface from the crawl, and it is the one that turns a mention into a transaction. MCP is the server-side half of this story; the browser-side half is WebMCP, and the conceptual explanation lives here.

The 2026-07-28 revision is the one to build against, and it is a genuine break. Protocol-level sessions and the Mcp-Session-Id header are gone, the initialize handshake is gone, and the standalone HTTP GET stream is gone. Each request now carries its own protocol version and client capabilities in _meta, so an MCP endpoint is an ordinary stateless HTTP workload you can put behind any load balancer without sticky routing. Two new required headers, Mcp-Method and Mcp-Name, mirror the body so gateways and WAFs can route and rate limit without parsing JSON.

Where the spec lives

MCP is versioned by dated revisions. Read the changelog before anything else: a lot of what older tutorials show you was removed in July 2026.

  • Key Changes, revision 2026-07-28 - the authoritative list of what was removed, renamed and deprecated since 2025-11-25. Start here.
  • Streamable HTTP transport - the exact wire contract for remote servers: required headers, Origin validation, cancellation, and what to return to legacy clients.
  • Authorization - OAuth 2.1, RFC 9728 protected resource metadata, RFC 8707 resource indicators and audience validation. Read it before you expose anything private.
  • server/discover - the RPC every server must implement so clients can read supported versions, capabilities and identity in one call.
  • TypeScript SDK - v2 ships as the split packages @modelcontextprotocol/server and @modelcontextprotocol/client. The v1 @modelcontextprotocol/sdk package still gets fixes on the v1.x branch.
  • Python SDK - v2 renamed FastMCP to MCPServer and moved transport options from the constructor to run(). Note that pip install mcp now gives you 2.x.

Three ways to implement MCP

Start local with a stdio server if you only need the tools on your own machine or inside a coding agent. Move to a remote Streamable HTTP server when the tools need production data, real authentication or more than one user. Then publish the thing so clients can actually find and install it. The three examples below are one server taken through all three stages.

01

Local stdio server with the TypeScript SDK

The shortest path from an existing HTTP API to something an AI client can call. The client launches your process, talks newline-delimited JSON-RPC over stdin and stdout, and there is no network surface, no CORS and no OAuth to think about. This is the right shape for a coding agent, a CLI workflow or a first prototype.

typescriptsrc/server.ts
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const server = new McpServer({ name: 'catalog-server', version: '1.0.0' });

server.registerTool(
  'lookup_product',
  {
    description: 'Look up a product by SKU and return its price and stock level',
    inputSchema: z.object({ sku: z.string() })
  },
  async ({ sku }) => {
    const res = await fetch(`https://api.example.com/products/${sku}`);
    if (!res.ok) {
      return {
        content: [{ type: 'text', text: `No product found for SKU ${sku}` }],
        isError: true
      };
    }
    const product = await res.json();
    return { content: [{ type: 'text', text: JSON.stringify(product) }] };
  }
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main();

What this does: the model now sees a tool called lookup_product with a one-line description and a typed sku argument, and it picks the tool on the strength of that description alone. Wire it into a client with {"mcpServers": {"catalog": {"command": "node", "args": ["dist/server.js"]}}}, which is the same config shape the Glippy MCP server uses.

02

Remote Streamable HTTP server with the Python SDK

Use this when the tools need your production database, per-user authorisation or shared infrastructure. Under 2026-07-28 there is nothing stateful to configure: one POST endpoint, one self-contained request, any worker can serve it. The one thing that will stop you is the Host allowlist, which defaults to localhost and rejects every request behind a real hostname with a bare 421 Misdirected Request.

pythonserver.py
from mcp.server import MCPServer
from mcp.server.transport_security import TransportSecuritySettings

mcp = MCPServer("catalog")


@mcp.tool()
def lookup_product(sku: str) -> dict:
    """Look up a product by SKU and return its price and stock level."""
    return {"sku": sku, "price": 24.99, "in_stock": True}


# Without this, every request behind a real hostname gets 421 Misdirected
# Request. Entries are exact strings, so list the bare host and the :* form.
security = TransportSecuritySettings(
    allowed_hosts=["mcp.example.com", "mcp.example.com:*"],
    allowed_origins=["https://app.example.com"],
)

# Starlette ASGI app, MCP endpoint mounted at /mcp
app = mcp.streamable_http_app(transport_security=security)

# Serve with any ASGI server. No sticky sessions needed on 2026-07-28:
#   uvicorn server:app --workers 4

What this does: it exposes the same tool at https://mcp.example.com/mcp as a plain HTTP POST endpoint, and the DNS-rebinding guard rejects any browser origin you did not name. Add OAuth on top of this and the model can call the tool as a specific signed-in user rather than as your service account.

03

Publish it to the MCP registry so clients can find it

A server nobody can discover is invisible, and this is the step most teams skip. The official MCP registry hosts metadata only, not artifacts: you publish the package to npm or PyPI first, then publish a server.json that points at it. The registry verifies ownership by checking that the package carries a matching mcpName field.

jsonserver.json
{
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
  "name": "io.github.my-org/catalog",
  "description": "Look up catalog products, pricing and stock levels",
  "repository": {
    "url": "https://github.com/my-org/catalog-mcp",
    "source": "github"
  },
  "version": "1.0.1",
  "packages": [
    {
      "registryType": "npm",
      "identifier": "@my-org/catalog-mcp",
      "version": "1.0.1",
      "transport": {
        "type": "stdio"
      },
      "environmentVariables": [
        {
          "name": "CATALOG_API_KEY",
          "description": "API key for the catalog service",
          "isRequired": true,
          "isSecret": true,
          "format": "string"
        }
      ]
    }
  ]
}

What this does: after mcp-publisher login github and mcp-publisher publish, any client browsing the registry can install your server and see up front which secrets it will ask for. The name here must match the mcpName in package.json exactly, and with GitHub auth it must start with io.github.your-username/. The Glippy MCP server is published this way, as io.github.jbobbink/glippy-mcp.

Implementation guidelines

These are the things that break in production rather than in the tutorial.

  1. Send the protocol version twice, and keep it consistent. Every request carries io.modelcontextprotocol/protocolVersion inside params._meta, and every Streamable HTTP POST must also carry a matching MCP-Protocol-Version header. If the header and the body disagree the server must reject the request with 400 and error code -32020 (HeaderMismatch).
  2. Do not build anything on sessions. There is no Mcp-Session-Id and no initialize handshake in 2026-07-28. If a tool genuinely needs state across calls, mint an explicit opaque handle server-side and pass it back as an ordinary tool argument, exactly as you would with a REST API.
  3. Return cache hints on every list result. tools/list, prompts/list, resources/list, resources/read and resources/templates/list now require ttlMs and cacheScope ("public" or "private"). Return tools in a deterministic order too: it lets clients cache and it improves LLM prompt cache hit rates.
  4. Treat the MCP endpoint as a hostile HTTP surface. Validate the Origin header and answer 403 when it is present and invalid, bind to 127.0.0.1 for local servers, and answer GET or DELETE on the MCP endpoint with 405 Method Not Allowed. Without Origin validation a web page can drive a local MCP server through DNS rebinding.
  5. Validate the token audience, not just the signature. A protected server must implement RFC 9728 protected resource metadata, must confirm the access token was issued for its own canonical URI per RFC 8707, and must never accept or forward a token minted for something else. Include a scope parameter in the WWW-Authenticate challenge so clients ask for the right permissions first time.
  6. Assume the stream can die. Resumability via Last-Event-ID was removed. A broken SSE response stream loses the in-flight request, and the client must re-issue it as a new request with a new id, so make long-running tools idempotent or move them behind the Tasks extension.
  7. Plan for two protocol eras. Plenty of deployed clients still send initialize and expect a session. Decide explicitly whether you support them, and test both paths: a modern server returns a recognisable JSON-RPC error body on 400 so a dual-era client knows to stop probing rather than fall back.

Do this, not that

Do

  • Return ttlMs and cacheScope on every list result so clients stop re-polling tools/list on a loop.
  • Mirror Mcp-Method and Mcp-Name onto every POST, and reject any request whose body disagrees with its headers.
  • Publish an mcpName in package.json that matches the name in server.json before running mcp-publisher publish.
  • Serve protected resource metadata at /.well-known/oauth-protected-resource and point the WWW-Authenticate header at it on a 401.

Do not

  • Do not mint, echo or read Mcp-Session-Id. The current transport has no protocol-level session, and older clients that send one should simply have it ignored.
  • Do not expose a standalone GET stream on the MCP endpoint. Long-lived change notifications now ride the response stream of a subscriptions/listen POST.
  • Do not start new work on Roots, Sampling or Logging. All three are formally Deprecated with a twelve month minimum removal window; log to stderr or OpenTelemetry instead.
  • Do not adopt the 2024-11-05 HTTP+SSE transport or Dynamic Client Registration for anything new. Use Streamable HTTP and Client ID Metadata Documents.

How Glippy checks this

Glippy scores MCP-adjacent signals under Agent Interactivity (category 10), which is the same category that covers WebMCP tool declarations and agent-facing affordances. The extension cannot connect to your private MCP endpoint, so what it checks is the public evidence that one exists and is reachable: documented endpoints, agent-readable descriptions of what your tools do, and whether an agent hitting your site is blocked before it gets that far. Run it alongside the AI agent accessibility checker to see how the two categories score together.

Check your MCP setup

Glippy runs 240+ checks across 16 categories on any page, including Agent Interactivity (category 10). No sign-up required.

Frequently asked questions

Use stdio when the client launches your server as a local subprocess: it is the simplest option, it needs no authentication because credentials come from the environment, and it suits developer tooling. Use Streamable HTTP when the server is a shared service that multiple users or machines connect to over the network. Those are the only two standard transports in the 2026-07-28 revision; the older HTTP+SSE transport from 2024-11-05 is deprecated and new implementations should not adopt it.

MCP became stateless. The initialize handshake, protocol-level sessions and the Mcp-Session-Id header were all removed, and every request now carries its protocol version and client capabilities in _meta instead. Server-initiated requests such as sampling and elicitation were replaced by the Multi Round-Trip Requests pattern, where the server returns resultType: "input_required" and the client retries with the answers attached. Roots, Sampling and Logging were deprecated, and Tasks moved out of the core protocol into an official extension.

No, and it is worth being blunt about that. Citation in an AI answer is driven by what crawlers can read from your pages: structured data, clean semantic HTML, crawler access in robots.txt and content that is worth quoting. MCP is a different surface entirely. It matters once a user has already connected your server, because from that point the assistant can call your live systems instead of guessing from a cached page. Treat MCP as the conversion layer, not the discovery layer.

Today it is mostly manual configuration plus the official MCP registry, where you publish a server.json describing your packages and transports. Once a client is connected, server/discover is the RPC that returns supported protocol versions, capabilities and server identity in a single call, and every server must implement it. A well-known URL convention for remote servers is still being worked out: MCP Server Cards live in an experimental extension repository, not in the released specification, so do not build a product on that path yet.

Reviewed against the primary sources on . These standards move quickly, so check the linked specs before you ship.

Check your site for AI search readiness

Start free with the Glippy Chrome extension for instant page checks. Scaling up? Automate audits across many URLs and your whole sitemap with the Glippy MCP server.

Add Glippy to Chrome – free Automate with the MCP server →