More and more product teams hear the same question from customers: "Can I use this from Copilot, Claude or ChatGPT?" The practical answer is a production MCP server — a Model Context Protocol endpoint that exposes a carefully chosen slice of your product to AI agents. The timing is good. The MCP specification revision dated 2026-07-28 made the protocol core stateless and tightened authorization, so a team starting now can build on the stable revision instead of migrating later. This guide walks through the steps, with the trade-offs at each one. The rule of thumb: start small, stateless and authenticated.

What the 2026-07-28 MCP spec changes

According to the MCP project's release announcement, the revision brings a stateless protocol core, Multi Round-Trip Requests, header-based routing, cacheable list results, authorization hardening, a formal extensions framework and updated Tier 1 SDKs. The final release post on July 28 adds that all four Tier 1 SDKs (TypeScript, Python, Go and C#) speak the new revision, and reports close to half a billion SDK downloads a month. Three changes shape your architecture:

  • No handshake, no session header. As Google's developer blog explains, the initialize/initialized exchange and the Mcp-Session-Id header are gone. Protocol version, client info and capabilities now travel in a _meta field on every request, so each request stands on its own.
  • Plain horizontal scaling. The release candidate post notes that any request can land on any instance. Sticky routing and shared session stores are no longer required at the protocol layer.
  • Extensions with a lifecycle. MCP Apps lets servers ship interactive HTML that hosts render in a sandboxed iframe, and Tasks moved from an experimental core feature into an extension. Every feature now follows an Active → Deprecated → Removed lifecycle, with at least twelve months between deprecation and the earliest removal. That gives you a predictable planning window.

Two more changes affect planning. Roots, Sampling and Logging are deprecated but keep working for at least twelve months, and the legacy HTTP+SSE transport gets a year-long offramp, so don't build anything new on them. On the authorization side, the spec deprecates Dynamic Client Registration: new implementations should use Client ID Metadata Documents instead.

Step 1: Pick 5–10 tools, not your whole API

The temptation is to auto-generate one tool per endpoint. Resist it. Every tool is a contract with a model you don't control and a new piece of attack surface, and a dozen well-described tools are easier for a model to use correctly than a hundred auto-generated ones. A good first catalog is:

  • Read-heavy. Search, look up, summarize. Agents become useful quickly and mistakes are cheap.
  • Shaped around outcomes. One get_order_status tool that combines two internal calls beats three low-level tools the model has to chain correctly.
  • Narrow in its inputs. Enums and IDs instead of free-text filters wherever possible.
  • No tenant or user IDs as parameters. Derive them from the access token on the server, so a confused or manipulated agent cannot ask for another customer's data.
  • Free of irreversible writes in v1. If you need a write, make it a draft the user confirms inside your app.

Write the catalog down before any code, with one scope per tool:

# v1 tool catalog: read-heavy, one scope per tool
- name: search_orders
  scope: orders.read
  returns: paginated order summaries
- name: get_order_status
  scope: orders.read
  combines: orders API + shipping API
- name: create_draft_invoice
  scope: invoices.draft
  note: draft only, user confirms in the app

Step 2: Choose how to build your production MCP server

You rarely need to write an MCP server from scratch. If your REST API already sits behind Azure API Management, its built-in AI gateway can expose selected API operations as MCP tools. Write a custom server only when you need something the gateway can't do.

Expose via API Management Custom server (TypeScript SDK v2)
Effort Configuration on top of an existing API New code and a new deployment
MCP features Tools only — no resources or prompts Tools, resources and prompts
Tool shape One API operation per tool Can merge several API calls into one tool
Hosting Your existing API Management instance For example Azure Functions on Flex Consumption

Trade-offs worth knowing:

  • API Management reuses the gateway you already run, but Microsoft's docs state it currently supports tools only. One gotcha: don't read context.Response.Body inside MCP server policies. It triggers response buffering and breaks the streaming that MCP relies on.
  • Azure Functions can host servers built with the official SDKs, currently in public preview. The preview supports stateless servers using the streamable-http transport, and the app must run on the Flex Consumption plan. If you genuinely need server-side state, Microsoft points to the Functions MCP extension instead.
  • Azure API Center can serve as a private registry for MCP servers, both those exposed through API Management and those hosted elsewhere, so other teams can discover what already exists.

One caution: at the time of writing, Microsoft's documentation doesn't say explicitly whether either Azure option serves or proxies the 2026-07-28 wire revision. For existing MCP servers it puts behind the gateway, API Management only states that they must conform to version 2025-06-18 or later. Check current support before you commit — another reason to plan for both protocol generations (Step 4).

Step 3: Design stateless tools with explicit handles

With sessions gone from the protocol, multi-step workflows need another home for their state. The release candidate post gives the answer HTTP APIs have always used: a tool mints an explicit identifier — a basket_id, a browser_id — and the model passes it back as an ordinary argument on later calls.

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "add_line_item",
    "arguments": {
      "basket_id": "bsk_8f2c",
      "sku": "PLAN-PRO",
      "quantity": 1
    }
  }
}

Practical rules:

  • Keep the state behind the handle in your normal datastore, never in server memory.
  • On every call, check that the authenticated user actually owns that handle. A handle is a pointer, not a credential.
  • Give handles an expiry and make write tools idempotent where possible, because agents retry.

This design is exactly what lets the server run on stateless, scale-out hosting such as Flex Consumption.

Step 4: Target 2026-07-28 without stranding older clients

The TypeScript SDK's main branch is v2 (@modelcontextprotocol/server and @modelcontextprotocol/client), the stable line implementing the 2026-07-28 spec. The v1.x package (@modelcontextprotocol/sdk) keeps receiving bug fixes and security updates for at least six months after v2's release on 2026-07-27.

npm install @modelcontextprotocol/server

Migrating an existing v1 server requires Node.js 20 or newer, and the SDK ships a codemod. Run it at the package root, then work through what it can't rewrite:

node --version   # must be 20 or newer
npx @modelcontextprotocol/codemod@latest v1-to-v2 .

Two details from the SDK's migration guide matter:

  1. The new wire format is opt-in. A server built with v2 keeps speaking the 2025-era protocol unless you explicitly enable 2026-07-28. Bumping the package alone changes nothing on the wire; turn the new revision on deliberately and test it.
  2. Caching defaults are conservative. When serving 2026-07-28, the SDK always emits the list-caching fields, defaulting to ttlMs: 0 and cacheScope: 'private'. Keep those defaults until you know a list is identical for every user and rarely changes.

The reassuring part is interoperability. The other SDKs differ: according to the SDK beta announcement, serving 2026-07-28 is also an explicit choice in the Go SDK, while a Python v2 server answers both revisions from one endpoint and the C# preview's HTTP transport defaults to the new stateless mode. In TypeScript, the SDK's migration guide says createMcpHandler serves 2026-07-28 and, by default, 2025-era requests from the same endpoint, and a v2 client set to versionNegotiation: { mode: 'auto' } falls back to the 2025 initialize handshake when it reaches a 2025-only server. Our recommendation: build on v2, opt in to the new revision, keep accepting older clients during a transition period while agent hosts catch up, and schedule your move off v1.x well inside its support window.

Step 5: Secure the server from day one

Microsoft's Azure Functions team has said openly that auth remains the biggest barrier to getting an MCP server into production. That is a reason to start with it, not to postpone it.

Authentication. Use OAuth with Microsoft Entra ID. The samples in Microsoft's aka.ms/remote-mcp repo enable built-in MCP auth with Entra ID by default, which is a sensible baseline. The new spec also hardens the flow: clients must validate the iss parameter on authorization responses (RFC 9207), and with RFC 8707 resource indicators clients state exactly which MCP server a token is meant for, which addresses the confused-deputy problem. On your side, reject tokens that weren't issued for your server, and publish OAuth 2.0 Protected Resource Metadata (RFC 9728) so clients can discover your authorization server. If you use an identity provider other than Entra ID, check whether it supports Client ID Metadata Documents rather than assuming it does.

Map the token to the same user, tenant and role checks your web app uses: the agent should never have broader rights than the person it acts for.

No token passthrough. Never forward the token a client sent you to an upstream API. Call internal APIs with the server's own credentials, scoped to exactly what each tool needs, so orders.read can never unlock invoices.

Human confirmation. The spec requires hosts to obtain explicit user consent before invoking any tool. Add your own layer for destructive actions such as delete, refund or send: return a preview first and require a separate confirming call before anything irreversible happens.

Input validation. The NSA's MCP security guidance, published in May 2026, points to weak input validation and thin logging as common gaps in MCP deployments. Give each tool a strict JSON Schema and repeat the checks server-side; never assume the model sent sensible values.

Untrusted descriptions. The spec says tool descriptions should be treated as untrusted unless they come from a trusted server. If you aggregate third-party tools, don't pass their descriptions through blindly.

Logging. Log every tool invocation with its parameters and the identities involved, and feed that telemetry into your existing SIEM. Agree on the log schema before launch, not after the first incident. An illustrative record:

{
  "ts": "2026-10-14T09:12:03Z",
  "tool": "get_order_status",
  "client": "partner-agent",
  "subject": "user-7f3a",
  "scopes": ["orders.read"],
  "arguments": { "order_id": "ORD-1042" },
  "result_sha256": "9c1e...",
  "latency_ms": 184
}

Step 6: Run it like any other public API

An MCP server is an internet-facing API with a new client type. Give it the same discipline: a patch schedule, dependency monitoring and versioned changes. After launch:

  • Staged rollout. Internal users first, then a few design-partner customers, then general availability.
  • Per-tool metrics. Call volume, error rate and latency for each tool. Tools no agent calls are candidates for removal; frequent errors usually point to a confusing description or schema.
  • Rate limits and quotas at the gateway, so one runaway agent can't degrade service for other customers.
  • Your own deprecation policy. Mirror the spec's model: announce, keep the old tool working for a defined window, then remove.
  • A spec calendar. Track the twelve-month windows for deprecated features and the end of v1 SDK support so migrations never become emergencies.

The framework you run on deserves a watch too. In its September 2026 security release, Next.js disclosed that its development server (next dev) exposed an MCP endpoint that didn't verify which website a request came from. Production deployments don't serve that endpoint, and fixes shipped in v16.3.8 (Active LTS) and v15.5.27 (Maintenance LTS). The lesson: MCP surfaces can appear in your stack through tooling, not only through code you wrote, so keep developer machines patched as well.

Know when not to use an agent at all. Microsoft's Agent Framework documentation puts it plainly: if you can write a function to handle the task, do that instead. A nightly reconciliation job doesn't need an LLM; MCP earns its place when users want to ask open-ended questions or combine your product with other tools.

Finally, think about discovery. Registering the server in Azure API Center helps internal teams find it, and on the agent side Microsoft's Azure Functions team plans to surface Functions-hosted MCP servers in Foundry Toolbox, a new feature that lets Foundry agents discover and consume tools from a single endpoint — worth watching if your own agents will consume the server.

Key takeaways

  • Start with 5–10 read-heavy, outcome-shaped tools, each with a single least-privilege scope.
  • Reuse your existing API through API Management where tools are enough; write a custom SDK v2 server only for resources, prompts or composite tools.
  • Design for statelessness: return explicit handles and verify ownership on every call.
  • Build on SDK v2 and opt in to 2026-07-28 deliberately, while still serving older clients during the transition.
  • Confirm Azure's support for the new wire revision before committing to a hosting path.
  • Ship with Entra-based OAuth, Protected Resource Metadata, audience checks, no token passthrough, strict schemas, human confirmation for destructive actions and full SIEM logging.
  • Patch the server and its framework on a schedule, like any public API.
  • If deterministic code can do the job, skip the agent.