Skip to main content
Streamable HTTP is the recommended transport for remote MCP servers (MCP Specification 2025-11-25). It enables real-time progress notifications via a structured JSON-RPC notification system, making it ideal for cloud deployments, multi-client scenarios, and long-running AI tasks.

Learning objectives

By the end of this lesson you will be able to:
  • Explain the difference between classic HTTP streaming and MCP streaming notifications
  • Implement a server that sends progress notifications using Streamable HTTP
  • Write a client that handles incoming notifications in real time
  • Understand the security considerations for HTTP-based MCP servers

Transport comparison

TransportReal-time updatesStreamingScalabilityRecommended for
stdioNoNoLowLocal CLI tools
SSE (deprecated)YesYesMedium
Streamable HTTPYesYesHighCloud, multi-client

Classic streaming vs. MCP streaming

MCP streaming is not about chunked HTTP responses. Instead it uses a structured notification system:
FeatureClassic HTTP streamingMCP streaming (notifications)
Main responseChunkedSingle, at end
Progress updatesSent as data chunksSent as separate notifications
Client requirementsProcess streamImplement message handler
Message formatPlain text chunksStructured JSON-RPC objects

What is an MCP notification?

A notification is a JSON-RPC message the server sends to the client to report progress during a long-running operation:
{
  "jsonrpc": "2.0",
  "method": "notifications/message",
  "params": {
    "level": "info",
    "data": "Processing document 3/10"
  }
}

Notification levels

LevelDescriptionExample use case
debugDetailed debugging infoFunction entry/exit
infoGeneral progressOperation progress updates
noticeNormal but significant eventsConfiguration changes
warningWarning conditionsDeprecated feature usage
errorError conditionsOperation failures
criticalCritical conditionsSystem component failures

Implementing notifications

Server side: sending notifications

from mcp.server.fastmcp import FastMCP, Context
from mcp.types import TextContent

mcp = FastMCP("streaming-server")

@mcp.tool(description="A tool that sends progress notifications")
async def process_files(message: str, ctx: Context) -> TextContent:
    for i in range(1, 11):
        await ctx.info(f"Processing document {i}/10")
    await ctx.info("Processing complete!")
    return TextContent(type="text", text=f"Done: {message}")

# Use streamable-http transport
mcp.run(transport="streamable-http")

Client side: receiving notifications

async def message_handler(message):
    if isinstance(message, types.ServerNotification):
        print("NOTIFICATION:", message)
    else:
        print("SERVER MESSAGE:", message)

async with ClientSession(
    read_stream,
    write_stream,
    logging_callback=logging_collector,
    message_handler=message_handler,
) as session:
    await session.initialize()
    result = await session.call_tool("process_files", {"message": "batch-1"})

A simple HTTP streaming example

To understand the mechanics before using MCP’s notification layer, here is a plain HTTP streaming server and client:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import time

app = FastAPI()

async def event_stream():
    for i in range(1, 6):
        yield f"data: Message {i}\n\n"
        time.sleep(1)

@app.get("/stream")
def stream():
    return StreamingResponse(event_stream(), media_type="text/event-stream")

Security considerations

When you expose an MCP server over HTTP, you must address the following:
Always validate the Origin header to prevent DNS rebinding attacks. Reject requests from unexpected origins.
During local development, bind servers to localhost (127.0.0.1) only. Never expose a development server on 0.0.0.0.
Implement authentication for any publicly accessible server — API keys, OAuth 2.0, or JWT tokens. Never deploy an unauthenticated HTTP MCP server to the public internet.
Configure Cross-Origin Resource Sharing (CORS) policies to restrict which origins can connect to your server.
Use HTTPS to encrypt traffic. Consider a reverse proxy (nginx, Caddy) to handle TLS termination.

Migrating from SSE to Streamable HTTP

If you have an existing SSE-based server:
  1. Update server code to use transport="streamable-http" in mcp.run().
  2. Update client code to use streamablehttp_client instead of the SSE client.
  3. Implement a message_handler in the client to process notifications.
  4. Test with existing tools and workflows.
  5. Optionally run both SSE and Streamable HTTP in parallel on different endpoints during the transition.

Assignment

Build an MCP server and client where the server processes a list of items (e.g., files or documents) and sends a progress notification for each item. The client should display each notification as it arrives. Steps:
  1. Implement a server tool that processes a list and calls ctx.info() for each item.
  2. Run the server with transport="streamable-http".
  3. Write a client with a message_handler that prints notifications.
  4. Observe the real-time output.

Key takeaways

  • Streamable HTTP is the recommended transport for remote MCP servers (MCP spec 2025-11-25).
  • MCP streaming uses a notification system — the final result is still a single response, but progress is sent as separate messages.
  • Always validate the Origin header and use HTTPS in production deployments.
  • Migrating from the deprecated SSE transport to Streamable HTTP requires only minor changes to both server and client.

Build docs developers (and LLMs) love