AI

Migrate an MCP Server to the Stateless 2026-07-28 Spec

Bump an MCP server to the new SDK and the first run dies on an import. Fix that and it dies on the constructor. Fix that and every tool that asks the user a question breaks on the new protocol while still working for old clients. That is the MCP 2026-07-28 migration in three errors, and all three are cheap to fix once you know which line to change.

Original content from computingforgeeks.com - post 172049

This guide migrates a real Streamable HTTP server in Python (FastMCP to MCPServer) and in TypeScript (the official codemod plus createMcpHandler), then proves the stateless wire with curl: server/discover, the new required headers, the multi round-trip (MRTR) replacement for elicitation, and the error codes you get when a request is wrong. If you only consume servers, the Claude Code MCP setup guide covers the client side, and MCP vs RAG vs agent skills explains where MCP fits at all. Everything below was run on Ubuntu 26.04 in September 2026 with the MCP Python SDK 2.2.0, the TypeScript SDK 2.1.0, Claude Code 2.1.283 and MCP Inspector 2.8.0.

What the MCP 2026-07-28 migration breaks

The revision makes MCP stateless. There is no initialize handshake and no Mcp-Session-Id, and the server can no longer send requests to the client. Every request carries its own protocol version and client capabilities in _meta, so any worker behind a load balancer can answer any request. The official specification changelog lists nine major changes; these are the ones that break running code:

Removed in 2026-07-28Replaced by
initialize / notifications/initialized handshakePer-request _meta keys plus a mandatory server/discover RPC
Mcp-Session-Id header and protocol sessionsNothing. Cross-call state goes in explicit handles you pass as tool arguments
Server-initiated elicitation/create, sampling/createMessage, roots/listMRTR: the server returns resultType: "input_required" and the client retries with answers
HTTP GET stream, resources/subscribeOne long-lived POST: subscriptions/listen
ping, logging/setLevelLog level per request via io.modelcontextprotocol/logLevel in _meta
SSE resumability (Last-Event-ID)Re-issue the request with a new ID

Two new obligations come with it. Every result carries a resultType field, and every Streamable HTTP POST must send MCP-Protocol-Version, Mcp-Method and, for tools/call, resources/read and prompts/get, an Mcp-Name header that matches the body.

Check which clients already speak the new protocol

You do not have to migrate everything on one day. The Python SDK v2 serves 2025-era and 2026-era clients from the same endpoint with no configuration, the TypeScript SDK v2 does the same once you switch to createMcpHandler, and a v2 client falls back to the old handshake when a server has not been upgraded. We ran every pairing we could build on the lab VM and captured what each side negotiated:

ClientServerNegotiated
Python SDK v1 ClientSessionPython SDK v2 MCPServer2025-11-25, session ID issued, elicitation works
Python SDK v2 Client, mode="auto"Python SDK v2 MCPServer2026-07-28
Python SDK v2 Client, mode="auto"Python SDK v1 FastMCP2025-11-25 (server/discover gets HTTP 400, client falls back)
Python SDK v2 Client, mode="auto"TypeScript v2 createMcpHandler2026-07-28
Claude CodePython SDK v2 MCPServer2026-07-28 (server/discover, then subscriptions/listen)
Claude CodePython SDK v1 FastMCP2025-11-25 after a failed server/discover probe, session ID issued
MCP Inspector, default settingsPython SDK v2 MCPServer2025-11-25, because Inspector defaults to Legacy
2026-07-28 tools/callTypeScript server after the codemod onlyRejected: Unsupported protocol version: 2026-07-28

The Claude Code result comes from a packet capture on the server, not from a changelog. It opens with a server/discover probe, mcp-protocol-version: 2026-07-28 in the headers and "name":"claude-code" in clientInfo, so a migrated server gets modern traffic the moment you point the Claude Code mcp add command at it. The last row is the one that catches people out, and the TypeScript section explains it.

Upgrade a Python MCP server to SDK v2

The test server is a small FastMCP app with three DevOps tools: disk_usage, check_port, and restart_service, which asks the user to confirm before it does anything. It runs Streamable HTTP on port 8000 and was built against mcp==1.30.0, the last v1 release. Commit your work first, then move the dependency to the v2 line inside the project:

uv add "mcp[cli]>=2,<3"

v2 depends on mcp-types, httpx2 and opentelemetry-api, which the v1 line did not. Start the unchanged server to see the first break:

uv run server.py

Error: “No module named ‘mcp.server.fastmcp'”

The traceback ends with a message that tells you exactly what happened:

    from mcp.server.fastmcp import Context, FastMCP
  File "/home/jmutai/ops-mcp/.venv/lib/python3.14/site-packages/mcp/server/fastmcp.py", line 16, in <module>
    raise ModuleNotFoundError(_MESSAGE, name=__name__)
ModuleNotFoundError: No module named 'mcp.server.fastmcp'. This is mcp 2.x, where FastMCP was renamed to MCPServer (from mcp.server.mcpserver import MCPServer) and other APIs changed; see the migration guide at https://py.sdk.modelcontextprotocol.io/v2/migration/#fastmcp-renamed-to-mcpserver or pin 'mcp<2' to keep running v1 code.

FastMCP is now MCPServer and lives under mcp.server.mcpserver. The decorators (@mcp.tool(), @mcp.resource(), @mcp.prompt()) did not change. One sed handles both the import and the class name:

sed -i 's/from mcp.server.fastmcp import Context, FastMCP/from mcp.server.mcpserver import Context, MCPServer/; s/^mcp = FastMCP(/mcp = MCPServer(/' server.py

Error: “MCPServer.__init__() got an unexpected keyword argument ‘host'”

The second run gets past the import and fails on the constructor:

  File "/home/jmutai/ops-mcp/server.py", line 12, in <module>
    mcp = MCPServer("ops-tools", host="0.0.0.0", port=8000)
TypeError: MCPServer.__init__() got an unexpected keyword argument 'host'

Every transport option moved off the constructor and onto run() or streamable_http_app(): host, port, streamable_http_path, json_response, stateless_http and transport_security. The v1 line mcp = FastMCP("ops-tools", host="0.0.0.0", port=8000) becomes a bare constructor plus mcp.run(transport="streamable-http", host="0.0.0.0", port=8000). Pass version= while you are there, because v2 reports an empty server version instead of the SDK’s own. Also watch positional arguments: v2 inserted title and description before instructions, so FastMCP("Demo", "You answer questions...") silently turns your instructions into a title.

This sed makes both edits on the test server:

sed -i 's/^mcp = MCPServer("ops-tools", host="0.0.0.0", port=8000)/mcp = MCPServer("ops-tools", version="2.0.0")/; s/mcp.run(transport="streamable-http")/mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)/' server.py

With those changes the server starts and serves both protocol eras on /mcp:

INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

The pinned v1 client still connects, gets a session ID, and calls tools exactly as before. That is the dual-era promise working. It does not mean the migration is finished, because one tool is still written for a protocol that no longer exists.

Replace ctx.elicit() with a Resolve parameter

This is the v1 version of the confirmation tool. It pushes an elicitation/create request to the client in the middle of the call and waits for the answer:

@mcp.tool()
async def restart_service(name: str, ctx: Context) -> str:
    """Ask the user to confirm before pretending to restart a systemd unit."""
    result = await ctx.elicit(
        message=f"Restart {name}? This drops active connections.",
        schema=Confirm,
    )
    if result.action == "accept" and result.data.confirm:
        return f"restart of {name} queued"
    return f"restart of {name} cancelled"

Error: “Cannot send ‘elicitation/create’: this transport context has no back-channel for server-initiated requests”

Legacy clients still get the prompt. A 2026-07-28 client calling the same tool gets a top-level JSON-RPC error instead of a result, whether or not it declared the elicitation capability:

{"jsonrpc":"2.0","id":8,"error":{"code":-32600,"message":"Cannot send 'elicitation/create': this transport context has no back-channel for server-initiated requests."}}

The server cannot call the client any more, so the tool has to return the question instead. The v2 SDK wraps that in a resolver: a function that returns an Elicit marker, attached to a tool parameter with Annotated[..., Resolve(fn)]. On a 2025 session the SDK sends a live elicitation request. On a 2026-07-28 request it returns input_required and runs your tool body only after the client retries with the answer. One tool body serves both eras, and the resolved parameter stays out of the tool schema: tools/list shows restart_service taking only name.

Open the server file:

vim server.py

This is the complete migrated file, exactly as it ran in the lab:

import os
import shutil
import socket
from typing import Annotated

from mcp.server.mcpserver import Elicit, MCPServer, RequestStateSecurity, Resolve
from pydantic import BaseModel


class Confirm(BaseModel):
    confirm: bool


mcp = MCPServer(
    "ops-tools",
    version="2.0.0",
    request_state_security=RequestStateSecurity(keys=[os.environ["MCP_STATE_KEY"]]),
)


@mcp.tool()
def disk_usage(path: str = "/") -> dict:
    """Report total, used and free space for a mount point, in GiB."""
    usage = shutil.disk_usage(path)
    gib = 1024**3
    return {
        "path": path,
        "total_gib": round(usage.total / gib, 1),
        "used_gib": round(usage.used / gib, 1),
        "free_gib": round(usage.free / gib, 1),
    }


@mcp.tool()
def check_port(host: str, port: int, timeout: float = 2.0) -> str:
    """Check whether a TCP port is accepting connections."""
    try:
        with socket.create_connection((host, port), timeout=timeout):
            return f"{host}:{port} is open"
    except OSError as exc:
        return f"{host}:{port} is closed ({exc.strerror or exc})"


async def confirm_restart(name: str) -> Elicit[Confirm]:
    return Elicit(f"Restart {name}? This drops active connections.", Confirm)


@mcp.tool()
async def restart_service(name: str, answer: Annotated[Confirm, Resolve(confirm_restart)]) -> str:
    """Ask the user to confirm before pretending to restart a systemd unit."""
    if answer.confirm:
        return f"restart of {name} queued"
    return f"restart of {name} cancelled"


if __name__ == "__main__":
    mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)

Binding 0.0.0.0 on run() switches off the SDK’s localhost Host-header check, which is why Claude Code on another machine could connect. That also leaves the endpoint with no Host or Origin check and no authentication, and check_port becomes a port scanner for anyone who can reach port 8000, so treat this binding as lab only or firewall it. Behind a reverse proxy, bind to localhost and pass TransportSecuritySettings(allowed_hosts=...) instead, and put auth in front. The request_state_security line is there because of the next error.

Error: “Invalid or expired requestState” after a restart or on another worker

The first MRTR round returns an opaque, signed requestState that the client must echo back on the retry. Without RequestStateSecurity, the SDK seals it with a random per-process key. We started a round, restarted the server process, then sent the retry:

{"jsonrpc":"2.0","id":21,"error":{"code":-32602,"message":"Invalid or expired requestState","data":{"reason":"invalid_request_state"}}}

A restart is the easy way to see it. In production the same thing happens whenever the retry lands on a different replica, which is exactly the scale-out case statelessness was meant to fix. Give every instance the same key, and keep the server name identical because it is the audience claim inside the token:

export MCP_STATE_KEY=$(openssl rand -hex 32)
uv run server.py

In Kubernetes that value belongs in a Secret mounted into every pod. With the shared key, the same restart test completed the round trip and returned restart of nginx queued. The token is signed, so a tampered requestState is rejected with the same -32602.

The v2 Python Client retries MRTR rounds for you. With an elicitation_callback that approves the prompt, the same tool works in both eras, and the old v1 client works too:

mode=auto negotiated=2026-07-28
  server asked: Restart nginx? This drops active connections.
  result: {'result': 'restart of nginx queued'}
mode=legacy negotiated=2025-11-25
  server asked: Restart nginx? This drops active connections.
  result: {'result': 'restart of nginx queued'}
v1 client negotiated: 2025-11-25
  server asked: Restart nginx? This drops active connections.
  result: restart of nginx queued

Registering elicitation_callback= is how a v2 client declares the capability. Leave it out and the server refuses before your tool runs, with error -32021: Client did not declare the form elicitation capability required by resolver '__main__:confirm_restart'.

Test the stateless MCP protocol with curl

SDK clients hide the wire, and the wire is where a hand-rolled client or proxy breaks. Set the endpoint once:

export MCP_URL=http://localhost:8000/mcp

Every server must implement server/discover. No session, no handshake, one POST:

curl -s "$MCP_URL" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'Mcp-Method: server/discover' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'

The server lists the versions it speaks and its capabilities, and moves serverInfo into the result’s _meta:

{"jsonrpc":"2.0","id":1,"result":{"cacheScope":"private","capabilities":{"prompts":{"listChanged":true},"resources":{"listChanged":true,"subscribe":true},"tools":{"listChanged":true}},"resultType":"complete","supportedVersions":["2026-07-28"],"ttlMs":0,"_meta":{"io.modelcontextprotocol/serverInfo":{"name":"ops-tools","version":"2.0.0"}}}}

Drop the MCP-Protocol-Version header and the Python SDK treats the request as 2025-era traffic, answering Bad Request: Missing session ID. The TypeScript SDK is stricter and returns -32020 with the required MCP-Protocol-Version header is absent. Either way, the header is not optional.

A tool call is the same shape, plus Mcp-Name:

curl -s "$MCP_URL" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'Mcp-Method: tools/call' -H 'Mcp-Name: disk_usage' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"disk_usage","arguments":{"path":"/"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{},"io.modelcontextprotocol/clientInfo":{"name":"curl","version":"8"}}}}'

The result comes back as plain application/json, with the new resultType field:

{"jsonrpc":"2.0","id":2,"result":{"content":[{"text":"{\n  \"path\": \"/\",\n  \"total_gib\": 18.2,\n  \"used_gib\": 3.9,\n  \"free_gib\": 14.3\n}","type":"text"}],"isError":false,"resultType":"complete","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"ops-tools","version":"2.0.0"}}}}

Calling restart_service the same way, with {"elicitation":{"form":{}}} in clientCapabilities, shows the first MRTR round. The question sits under inputRequests, keyed by the resolver’s name:

{"jsonrpc":"2.0","id":8,"result":{"inputRequests":{"__main__:confirm_restart":{"method":"elicitation/create","params":{"message":"Restart nginx? This drops active connections.","mode":"form","requestedSchema":{"properties":{"confirm":{"title":"Confirm","type":"boolean"}},"required":["confirm"],"type":"object"}}}},"requestState":"v1.gEkOIhMNvs3Ru5g6-AangyzjKaprxfJ2VA87mefZQ-fWV17dICJ5-i5V14QgvHqIy3B3ukYYpKFanpalWmgXV9ZOoSh4DqXNZX1xM7qax7-PeCs4oZQ3CsnRIWTXLVAJxpw8tngecMivgBNMvdFgKIQ65t9VQlZRd4TGlTX6Y5xItQEBXqBI9uedw00elwRpgMkKL53TUgQtiKkaVL5i_COsT9J5KR5JVp9iD9vNY3L94gJI7VZtQsD8yu5qr4ArstUxkBIPGftqu7j3AO9T2389Tp7m-xVbFZrZpblM8HJsV0y7xEIEHVU0KGfasrVNnDVHDu4vVWUp1r7lykOXZE2pFg6kO75ShBOyyi7pO3CF38w","resultType":"input_required","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"ops-tools","version":"2.0.0"}}}}

The retry is the original request with a new JSON-RPC id, plus "inputResponses":{"__main__:confirm_restart":{"action":"accept","content":{"confirm":true}}} and the requestState string copied byte for byte into params. The second round returned resultType: "complete" with restart of nginx queued. These are the errors a malformed request produced against the same server:

What we sentHTTPJSON-RPC error
tools/call without Mcp-Method400-32020 mcp-method header does not match the request body's method
Mcp-Name: check_port, body calls disk_usage400-32020 mcp-name header does not match the request body's 'name' parameter
Protocol version 2027-01-01400-32022 Unsupported protocol version, data.supported: ["2026-07-28"]
restart_service without the elicitation capability400-32021 Client did not declare the form elicitation capability...
ping404-32601 Method not found
Tampered or foreign requestState400-32602 Invalid or expired requestState

If you run MCP behind an API gateway, route on Mcp-Method and Mcp-Name instead of parsing JSON bodies. That is what the headers exist for, and the server rejects any request where they disagree with the body.

Migrate a TypeScript MCP server with the codemod

The TypeScript SDK split @modelcontextprotocol/sdk into @modelcontextprotocol/server, /client, /node and /core, and v2 needs Node.js 20 or newer. Ubuntu 26.04 ships Node 22 in its own repository, so sudo apt install nodejs npm is enough; the Node.js install guide covers newer lines. Our v1 server was an Express app with one server.tool() call and a stateless StreamableHTTPServerTransport. Commit it, then run the codemod from the package root, not from src/, because it also edits package.json:

npx @modelcontextprotocol/codemod@latest v1-to-v2 .

It rewrote three things and warned about a fourth it would not touch:

Changes: 3 across 1 file(s)

Info (1):
  /home/jmutai/ops-mcp-ts/src/server.ts:10 - [INFO] Raw object literal wrapped with z.object().

package.json updated:
  package.json
    Removed: @modelcontextprotocol/sdk
    Added:   @modelcontextprotocol/node, @modelcontextprotocol/server
    Warning: zod range '^3.25.76' cannot satisfy v2's floor: zod >=4.2.0 is required. An older range installs cleanly and then, depending on the zod entry point your code imports, fails type-checking (zod/v4 subpath) or only fails at runtime (main-entry imports: the server starts normally and the first tools/list reports the failure).

In the source, server.tool(name, description, shape, cb) became server.registerTool(name, { description, inputSchema: z.object(...) }, cb), and StreamableHTTPServerTransport became NodeStreamableHTTPServerTransport from @modelcontextprotocol/node. The codemod does not reformat, so run Prettier afterwards. Then install and type-check:

npm install
npx tsc --noEmit

Error: “Schema appears to be from zod 3, which the SDK cannot convert to JSON Schema”

The warning was accurate. tsc rejects the zod 3 object with error TS2769: No overload matches this call, but a runner that skips type-checking, such as tsx, starts the server fine. It answers initialize, then fails the first tools/list:

data: {"jsonrpc":"2.0","id":2,"error":{"code":-32603,"message":"Schema appears to be from zod 3, which the SDK cannot convert to JSON Schema. Upgrade to zod >=4.2.0, or wrap your JSON Schema with fromJsonSchema()."}}

A health check that only tests the port passes, and every client sees zero tools. Upgrade zod:

npm install zod@^4.2.0

Serve 2026-07-28 with createMcpHandler

Now the server compiles, lists tools and serves legacy clients. It still does not speak the new protocol. The TypeScript SDK never puts a 2026-07-28 byte on the wire unless you opt in, and the codemod deliberately leaves that architectural change to you. A 2026-07-28 request against the codemodded server:

{"jsonrpc":"2.0","error":{"code":-32000,"message":"Bad Request: Unsupported protocol version: 2026-07-28 (supported versions: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07)"},"id":null}

The fix is to replace the per-request transport with createMcpHandler, which takes a factory, answers 2026-07-28 requests natively and serves 2025 clients statelessly from the same factory. On Node it is wrapped in toNodeHandler(). Open the entry point:

vim src/server.ts

The finished file, verbatim from the lab. The migration guides only show this in fragments, so here it is whole:

import express from "express";
import { statfsSync } from "node:fs";
import { toNodeHandler } from "@modelcontextprotocol/node";
import { createMcpHandler, McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";

function buildServer(): McpServer {
  const server = new McpServer({ name: "ops-tools-ts", version: "2.0.0" });

  server.registerTool(
    "disk_usage",
    {
      description: "Report total and free space for a mount point, in GiB",
      inputSchema: z.object({ path: z.string().default("/") }),
    },
    async ({ path }) => {
      const s = statfsSync(path);
      const gib = 1024 ** 3;
      const total = ((s.blocks * s.bsize) / gib).toFixed(1);
      const free = ((s.bavail * s.bsize) / gib).toFixed(1);
      return { content: [{ type: "text", text: `${path}: ${free} GiB free of ${total} GiB` }] };
    },
  );

  return server;
}

const handler = createMcpHandler(buildServer);

const app = express();
app.all("/mcp", toNodeHandler(handler));

app.listen(3000, "0.0.0.0", () => console.log("ops-tools-ts listening on :3000/mcp"));

There is no express.json() in front of the handler; toNodeHandler() takes the raw Node request. The tools/call curl from earlier, pointed at port 3000 with empty arguments, now returns a 2026-07-28 result:

{"result":{"content":[{"type":"text","text":"/: 14.2 GiB free of 18.2 GiB"}],"resultType":"complete","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"ops-tools-ts","version":"2.0.0"}}},"jsonrpc":"2.0","id":2}

The legacy handshake still answers with "protocolVersion":"2025-11-25", and the Python v2 client negotiated 2026-07-28 against this TypeScript server without any glue. If you need sessionful 2025 behavior kept for old clients, pass { legacy: 'reject' } to createMcpHandler and route legacy requests to your existing transport. The TypeScript migration docs show that split.

Check the migrated server in MCP Inspector

MCP Inspector is the fastest way to see which era a server actually negotiates, but out of the box it will not show you. Start it with npx @modelcontextprotocol/inspector, add the server, and it connects with the 2025-11-25 handshake. Its own help text says it “Defaults to Legacy” because debugging tools should not auto-probe. Open the server’s Settings before connecting and change Protocol Era from Legacy to Modern:

MCP Inspector server settings with Protocol Era set to Modern 2026-07-28

After connecting, the server card reads MCP 2026-07-28, and the message log (sorted oldest first) opens with SERVER/DISCOVER and a pending SUBSCRIPTIONS/LISTEN in place of INITIALIZE:

MCP Inspector connected over 2026-07-28 with server/discover and subscriptions/listen

Run restart_service from the Tools tab to test the Resolve port. Inspector labels the call MRTR, holds it at one round awaiting input, and renders the form the server returned in inputRequests:

MCP Inspector elicitation request from an input_required MRTR result

Tick Confirm and submit. The retry completes the call in two rounds, and the structured output matches what the curl retry returned:

MCP Inspector tool result after a two round MRTR tools/call

Run the same check with Protocol Era on Legacy before you ship. Both eras should reach the same result, because that is the whole contract of a dual-era server.

MCP SDK v1 to v2 migration checklist

Every row below broke or changed something on the lab servers. The Python migration guide has the long tail (snake_case fields, McpError to MCPError, httpx to httpx2) if your server touches protocol types directly.

v1v2Symptom if missed
from mcp.server.fastmcp import FastMCPfrom mcp.server.mcpserver import MCPServerModuleNotFoundError at import
FastMCP(name, host=, port=)MCPServer(name, version=) + run(host=, port=)TypeError: unexpected keyword argument
await ctx.elicit(...)Annotated[Model, Resolve(fn)] returning Elicit(...)-32600 no back-channel on 2026-07-28 clients
Per-process MRTR key (default)RequestStateSecurity(keys=[shared_key])-32602 Invalid or expired requestState across replicas
@modelcontextprotocol/sdk@modelcontextprotocol/server + /node (codemod)Import errors
server.tool(name, desc, shape, cb)server.registerTool(name, { description, inputSchema }, cb)TypeError: server.tool is not a function (the variadic form was removed)
zod 3zod 4.2 or newer-32603 on tools/list, zero tools
StreamableHTTPServerTransportcreateMcpHandler(factory) + toNodeHandler()2026-07-28 requests rejected as unsupported
Relying on ping for healthserver/discover-32601 Method not found

The Roots, Sampling and Logging features are deprecated rather than removed, with at least a twelve-month window under the new lifecycle policy, so they are not on the critical path. The two rows that bite in production are the shared requestState key and the TypeScript opt-in: both leave a server that looks healthy and passes its own smoke test while every 2026-era client fails. If your servers feed an agent, context engineering for AI agents covers what to do with the deterministic, cacheable tool lists the new ttlMs and cacheScope fields enable.

Keep reading

Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) AI Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) Open Source LLM Comparison Table (2026) AI Open Source LLM Comparison Table (2026) Claude Code Cheat Sheet: Commands, Shortcuts and Flags (PDF) AI Claude Code Cheat Sheet: Commands, Shortcuts and Flags (PDF) Install HyperFrames on Ubuntu 26.04 / 24.04: Headless MP4 Render AI Install HyperFrames on Ubuntu 26.04 / 24.04: Headless MP4 Render Install NVIDIA OpenShell on Linux and Sandbox AI Coding Agents AI Install NVIDIA OpenShell on Linux and Sandbox AI Coding Agents Claw Code Explained: Real Claude Code Alternatives (2026) AI Claw Code Explained: Real Claude Code Alternatives (2026)

Leave a Comment

Press ESC to close