MCP Real-World Tasks Reference
A minimal, dependency-free MCP server demonstrating the 2026-07-28 protocol with a toy example of sending a person to check something in the physical world and returning structured evidence, ideal for learning the protocol layer.
README
MCP reference server for real-world tasks
A minimal, dependency-free MCP server written against protocol revision
2026-07-28 — the one that removed sessions and the initialize handshake.
It models a service that sends a person to check something in the physical
world and returns structured evidence.
The domain is a toy. The shape is not: it is extracted from a production server that answers agents today, with the business logic replaced by a readable example so the protocol layer is the thing you actually read.
npm start # http://localhost:8787
npm test # 20 tests, no dependencies
curl -s -X POST http://localhost:8787/ \
-H 'content-type: application/json' \
-H 'mcp-protocol-version: 2026-07-28' \
-H 'mcp-method: tools/list' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28"}}}'
What changed in 2026-07-28, and why it makes this simpler
If you last read the MCP spec before this revision, most of what you remember about transports is gone:
| Removed | Replaced by |
|---|---|
Protocol-level sessions, Mcp-Session-Id |
Nothing. Every request stands alone. |
The initialize / notifications/initialized handshake |
Version and capabilities in each request's _meta |
The standalone GET SSE stream |
subscriptions/listen, opt-in, not needed by most servers |
ping, logging/setLevel |
Removed outright |
Resumable streams (Last-Event-ID) |
Removed; a broken stream is re-issued as a new request |
The practical consequence is the whole point of this repo: a stateless
request handler is now the shape the spec expects. There is no session to
store, nothing to expire, and no reason to reach for a durable object, a Redis,
or a stateful process. src/protocol.js is a pure function from Request to
Response.
Two things were added that are easy to miss and that clients silently rely on:
- Every result carries
resultType: "complete". - List results (
tools/list,server/discover) carryttlMsandcacheScope. server/discoveris mandatory — clients may call it before anything else.
Layout
| File | What it is |
|---|---|
src/protocol.js |
The protocol layer. Knows nothing about the domain. This is the part worth copying. |
src/tools.js |
The example domain: a catalogue, a deterministic quote engine, and one write tool. |
src/server.js |
Wires the two together; exposes a fetch handler and a Node listener. |
test/protocol.test.mjs |
20 tests, each pinning one rule that is easy to get subtly wrong. |
src/server.js exports fetchHandler(request) — a plain Request →
Response function, so it drops unchanged into Cloudflare Workers, Deno
Deploy, Bun, or a Next.js route handler. The Node listener exists so npm start works without a platform account.
The five decisions worth stealing
Everything below is a choice that cost real debugging somewhere else.
1. The header/body mirror is a real requirement, not decoration.
Mcp-Method and Mcp-Name duplicate values from the body so intermediaries
can route without parsing. If they disagree, reject with -32020. Skipping
this creates a split-brain where a proxy routes on the header while your
handler executes the body — which is exactly the bug the rule exists to
prevent.
2. Unknown method is 404, not 200 with an error body.
The spec uses the HTTP status so a client can tell "this endpoint does not
implement that method" from "this URL is not an MCP endpoint at all". Return
200 and you break client fallback logic.
3. Read-only tools need no authentication. Quoting creates nothing, reserves nothing, costs nothing. There is nothing to gate. Gating it only teaches agents that exploring your service is expensive. Authenticate the tools that commit something.
4. Exactly one tool has a side effect, and its annotations say so.
readOnlyHint, destructiveHint, idempotentHint and openWorldHint are what
clients and directory reviewers branch on. An unannotated write tool will be
treated as dangerous or rejected.
5. One pricing function serves every surface.
In the production system this is extracted from, the same quote() answers the
web form, the REST endpoint and the MCP tool. A human and an agent therefore
cannot be shown different prices for identical work. If you take one idea from
this repository, take this one — it is a correctness property, not an
optimisation.
The safety shape
The example write tool (submit_request) is deliberately not a booking:
- It requires
consent: true, set only after a human has agreed. - It returns
status: "pending_human_review"anddispatch: "never_automatic". - Nothing in the response can be mistaken for a confirmation.
An agent should never be able to send a person into the physical world without someone reading the request first. In the system this comes from, that is a fixed product guardrail rather than a limitation to be lifted later — and the tool description says so, because the description is what the model reads when it decides whether to call it.
Compatibility with older clients
src/protocol.js answers initialize, notifications/initialized and ping
for the 2025-* revisions, because deployed clients lag published specs by
months. It is a shim, not a second protocol: both paths serve the same tools
from the same definitions. Header mirroring is enforced only on the modern
revisions, since requiring it on legacy clients would reject exactly the
clients the shim exists for.
Delete LEGACY_PROTOCOL_VERSIONS once nothing in the wild still opens with a
handshake.
Connecting it
Once deployed at a public HTTPS URL, add it as a custom connector in Claude (Settings → Connectors) or in ChatGPT (Settings → Apps → Advanced → Developer mode). Both accept a no-auth Streamable HTTP endpoint.
License
MIT. Built as a by-product of VorOrtAI, which runs the non-toy version of this.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.