Stateless MCP server on Azure App Service — MCP 2026-07-28 edition (no handshake, no session, server/discover, explicit handles). Companion sample for the App Service blog Part 2.

Stateless MCP Server on Azure App Service — 2026-07-28 edition

A reference implementation of a stateless, horizontally scaled MCP serverbuilt on the MCP 2026-07-28 specification and deployed behind Azure AppService's built-in load balancer.

The 2026-07-28 revision makes MCP stateless at the protocol level: it removesthe initialize handshake and the Mcp-Session-Id header, so any instance canserve any request with no prior context. That is a perfect match for AppService's built-in load balancer — scale out and every instance isinterchangeable.

Part 2. This is the sequel toYou can scale MCP servers behind a load balancer on App Service — here's how,which scaled a 2025-11-25 server. The original sample lives atapp-service-mcp-stateless-scale-python.This repo is the standalone 2026-07-28 version.

  • Stateless Streamable HTTP (MCP 2026-07-28) — no handshake, no session
  • Three App Service instances by default, no sticky sessions
  • Explicit-handle tool (tally) — the stateless replacement for session state
  • Spec-compliant Python client that exercises the new headers + _meta
  • Staging deployment slot for zero-downtime updates
  • Application Insights auto-instrumentation with per-instance request tagging
  • k6 load test that visualizes load distribution

What changed from 2025-11-25

Area 2025-11-25 2026-07-28 (this sample)
Handshake initialize + notifications/initialized Removed — every request self-describes via _meta (SEP-2575)
Sessions Mcp-Session-Id header pins a client to state Removed — explicit server-minted handles as tool args (SEP-2567)
Discovery implied by initialize result server/discover RPC, required (SEP-2575)
Headers none required Mcp-Method + Mcp-Name required on POST (SEP-2243)
List results plain ttlMs + cacheScope cache hints (SEP-2549)
Results plain resultType: "complete" on every result (SEP-2322)
Tracing ad hoc W3C Trace Context in _meta (SEP-414)
Tool schema subset full JSON Schema 2020-12 (SEP-2106)
Resource-not-found -32002 -32602 (Invalid Params)

Full changelog: https://modelcontextprotocol.io/specification/draft/changelog

What's in the box

.
├── main.py                       # FastAPI app — MCP 2026-07-28 over stateless HTTP
├── requirements.txt
├── azure.yaml                    # azd service definition
├── client/
│   └── mcp_client.py             # spec-compliant 2026-07-28 client (headers + _meta + handles)
├── infra/
│   ├── main.bicep                # Resource group scope
│   ├── main.parameters.json
│   ├── abbreviations.json
│   ├── app/
│   │   └── web.bicep             # App Service + staging slot
│   └── shared/
│       ├── app-service-plan.bicep
│       └── monitoring.bicep      # Log Analytics + App Insights
├── loadtest/
│   ├── k6-mcp.js                 # k6 script — tags hits per instance
│   └── README.md
├── static/style.css
└── templates/index.html          # Status page showing serving instance

MCP tools

Tool Purpose
whoami Returns the App Service instance ID handling the request
echo Echoes a message, tagged with the instance ID
lookup_fact Static read-only fact lookup (stateless)
compute_primes CPU-bound prime counter (useful for load testing each instance's CPU)
tally Running total via an explicit signed handle — stateless cross-call state

Why tally matters

In 2025-11-25, a tool that needed to remember something across calls leaned onthe session. The 2026-07-28 spec removes sessions, so this server mints anexplicit handle instead: tally returns a signed token that contains therunning total. Pass it back on the next call and the total accumulates — eventhough the load balancer may route each call to a different instance. Statetravels with the request, not the connection. (For real workloads you'd backhandles with a shared store like Azure Storage, Cosmos DB, or Redis; here thehandle is self-contained so the sample needs zero extra infrastructure.)

Local development

python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python main.py

Open http://localhost:8000/. The MCP endpoint is athttp://localhost:8000/mcp.

Try the bundled client

python client/mcp_client.py                 # against localhost
python client/mcp_client.py https://<your-app>.azurewebsites.net

It runs server/discover, lists tools (showing the cache hints), callswhoami a few times so you can watch the instance ID move, then drives thetally handle across calls — all with the required 2026-07-28 headers and_meta.

Strict header mode

By default the server is lenient about the new Mcp-Method / Mcp-Name headersso that not-yet-2026-07-28 clients still work. To enforce them (return-32020 HeaderMismatch when they're missing or wrong), set:

MCP_STRICT_HEADERS=1 python main.py

The bundled client and load test always send them.

Deploy to Azure

azd auth login
azd up

azd up provisions:

  • A Premium v3 (P0v3) Linux App Service Plan with capacity: 3 — three liveinstances behind App Service's built-in load balancer.
  • The Web App, with clientAffinityEnabled: false — no ARR Affinity cookie,so the load balancer is free to round-robin every request.
  • A staging deployment slot wired to the same plan for zero-downtime swaps.
  • A Log Analytics workspace + Application Insights resource, connected viaAPPLICATIONINSIGHTS_CONNECTION_STRING so the OpenTelemetry distro emitstraces tagged with cloud_RoleInstance = WEBSITE_INSTANCE_ID.

Tune the scale-out level

azd env set INSTANCE_COUNT 5
azd provision

(The instanceCount bicep parameter accepts 1–10, wired throughinfra/main.parameters.json.)

Connect VS Code to the deployed server

Update .vscode/mcp.json:

{
  "servers": {
    "stateless-mcp-app-service-2026": {
      "url": "https://<your-app>.azurewebsites.net/mcp",
      "type": "http"
    }
  }
}

Verify load distribution

  1. Hit the home page a few times — the Instance ID value should change.

  2. Run the bundled client or the k6 load test:

    BASE_URL=https://<your-app>.azurewebsites.net k6 run loadtest/k6-mcp.js
    
  3. Inspect Application Insights:

    requests
    | where timestamp > ago(15m)
    | where name contains "/mcp"
    | summarize count() by cloud_RoleInstance
    

Architecture

                       ┌─────────────────────────────────────────┐
                       │       Azure App Service (P0v3 × 3)      │
                       │  ┌────────────┐ ┌────────────┐ ┌──────┐ │
   MCP client ── HTTP ─┤ ▶  instance0  │ │  instance1 │ │  …   │ │
   (stateless,         │  └────────────┘ └────────────┘ └──────┘ │
    no session,        │     ▲ built-in load balancer ▲          │
    no cookies)        │     │   clientAffinityEnabled=false     │
                       │  ┌──┴────────────────────────────────┐  │
                       │  │       Staging slot (same plan)    │  │
                       │  └───────────────────────────────────┘  │
                       └────────────────────┬────────────────────┘
                                            ▼
                                   Application Insights
                                  (cloud_RoleInstance =
                                   WEBSITE_INSTANCE_ID)

License

MIT.

MCP Server · Populars

MCP Server · New

    healthchainai

    healthchain

    Python SDK for healthcare AI — typed, validated FHIR tools for agents, real-time EHR connectivity, production deployment ✨ 🏥

    Community healthchainai
    deverman

    FocusRelay — Fast Swift OmniFocus MCP Server and CLI for macOS

    Fast native Swift OmniFocus MCP server and CLI for macOS. Let AI assistants safely read, update, complete, and organize tasks and projects through documented Omni Automation APIs.

    Community deverman
    Glade-tool

    GladeKit MCP

    Connect any MCP-compatible AI client (Claude Code, Cursor, Windsurf) to Unity or Godot. 235+ granular tools, an editor aware system prompt, game design document project context, script semantic search, and skill calibration.

    Community Glade-tool
    Consiliency

    Code-Index-MCP

    Code indexing MCP server to provide context to coding agents.

    Community Consiliency
    semihbugrasezer

    seerxo

    AI-powered Etsy product listing generator for Claude Desktop Generate perfect SEO titles, descriptions, and tags in seconds

    Community semihbugrasezer