learn-mcp-with-phoebe / Builder session 6 of 8
Learn MCP with Phoebe · Builder track · Session 6 of 8

Take Daybreak remote

stdio was perfect for your laptop. To let a teammate - or Claude's own API - reach Daybreak, it has to become a web service. Tonight you learn Streamable HTTP in depth: one endpoint, a JSON-RPC POST, an optional SSE upgrade per request, and sessions carried in a header today that the 2026-07-28 stateless core removes. Then you flip Daybreak's transport with one line, deploy it provider-neutral, and call it from the Messages API MCP connector with no client code at all.

🔶 Hands-on Practitioners: DE · DS · AI engineers · builders Python · spec 2025-11-25 45 min
0-3 · Recap 3-18 · Streamable HTTP in depth 18-40 · Deploy + the MCP connector 40-45 · Q&A
Part 0

Where we are

You have a server (b2-b3) and a client (b5), both talking over stdio - the host launches the server as a local subprocess. That does not cross machines. Tonight Daybreak becomes a real web service on Streamable HTTP, the only remote transport in the current spec. Then you meet three ways to consume a remote server: a host app, your own client from b5, or - new tonight - Claude's Messages API connector, which needs no client code whatsoever.

Live - presented in session Self-study - read after class ▶ Live MCP wire - step through real messages Official sources covered
★ What you walk out with today Daybreak flipped to Streamable HTTP with a one-line change, running as a web service you can curl. A clear mental model of how the single endpoint works - POST in, optional SSE stream out - and why sessions live in the Mcp-Session-Id header today but disappear on 2026-07-28. And the March answer coming back from the Anthropic Messages API with zero lines of client code, via the MCP connector.
Part 1 · covers Academy Advanced "StreamableHTTP" + "State + StreamableHTTP", spec Transports

Streamable HTTP in depth 8 min live

Streamable HTTP replaced the old HTTP+SSE transport in March 2025. It is deliberately simple: the server exposes one endpoint (say /mcp). The client POSTs a JSON-RPC message there. The server may reply with a single JSON response, or - if the work streams (progress, partial results) - it may upgrade that one request to an SSE stream and send a sequence of events before closing. One endpoint, one POST per message, optional stream per request. That is the whole transport.

CLIENT SERVER · one /mcp endpoint POST /mcp { jsonrpc, method: "tools/call" } 200 OK single JSON response (simple case) OR, when the work streams: POST /mcp (a long-running call) 200 OK Content-Type: text/event-stream → SSE events, then close The SSE upgrade is per request and server-chosen. HTTP+SSE (two endpoints) is deprecated - never teach it.
🔍 Click to zoom - one endpoint, a POST per message, an optional SSE upgrade
LiveThe request lifecycle - what a single call looks like4 min▶

Everything you stepped through in b1 still applies - it is the same JSON-RPC, now inside HTTP. The client opens by POSTing initialize; the server answers and (on the current spec) issues an Mcp-Session-Id. Every later POST carries that header so the server knows which session it belongs to.

the wire, as HTTP POST /mcp HTTP/1.1 Content-Type: application/json Accept: application/json, text/event-stream Mcp-Session-Id: 6f1c... # returned by initialize; sent on every later request {"jsonrpc":"2.0","id":3,"method":"tools/call", "params":{"name":"query_sales","arguments":{"month":"2026-03"}}} # Server may answer with a single JSON body, OR upgrade THIS response # to Content-Type: text/event-stream and push SSE events before closing.

The Accept header is how the client signals it can handle either shape. The server decides per request whether to stream.

LiveState, and the 2026-07-28 stateless core4 min▶

This is the course differentiator - nobody else teaches it yet. Today (spec 2025-11-25), a Streamable HTTP server is stateful: initialize hands out an Mcp-Session-Id, and the server remembers that session between requests. That makes horizontal scaling awkward - every request for a session must reach the node that owns it (sticky sessions).

The 2026-07-28 revision makes the core stateless. The initialize/initialized handshake and Mcp-Session-Id are removed. Instead:

  • Capabilities travel in _meta on each request - no negotiated session to remember.
  • Routing headers Mcp-Method and Mcp-Name let infrastructure route a request without parsing the JSON body.
  • Any node can serve any request - no stickiness, trivial horizontal scale.
Part 2 · covers official "Connect to remote MCP servers", DLAI "Creating and Deploying Remote Servers"

Take Daybreak remote 6 min live

The Python SDK makes this almost anticlimactic. The whole server you built in b2-b3 stays identical; you change how mcp.run is called. FastMCP serves Streamable HTTP on a mount path, and you host the process anywhere that runs Python - no vendor lock-in.

LiveThe one-line transport flip3 min▶

In project/server.py, the last line is mcp.run() (stdio). Switch the transport and Daybreak becomes a web service - same tools, resources, and prompt.

project/server.py · the only change if __name__ == "__main__": # b2-b5 used stdio: # mcp.run() # b6 serves it over HTTP on a single endpoint: mcp.run(transport="streamable-http") # default mount path: /mcp

Run it and FastMCP starts an HTTP server (default http://127.0.0.1:8000/mcp). Your b5 client can now dial the URL instead of launching a subprocess - and so can anything else that speaks the protocol.

LiveLocal-to-remote checklist - deploy anywhere Python runs3 min▶

Deployment is provider-neutral - these are generic steps, not one host's buttons. The pattern is the same on any Python-friendly platform:

Bind to the platform's port: read PORT from the environment and pass host 0.0.0.0 so the process is reachable.

Pin dependencies: mcp[cli] and your DB driver in requirements.txt (or pyproject.toml). Ship daybreak.db or point at a hosted DB.

Set the start command to run the server module; expose the /mcp path publicly over HTTPS.

Add auth before you expose real data - b7 is entirely about this. For tonight, deploy the read-only demo.

Self-studyStateless-friendly design now = painless migration later3 min read▶

You cannot adopt the stateless core before 2026-07-28, but you can build so the switch is a non-event. Keep tool handlers pure: derive everything from the request arguments, not from server memory built up across earlier calls in the same session. Do not stash per-user state on the server between requests; if you need it, pass it in or store it in a shared backing store keyed by request data. Servers written this way barely notice when Mcp-Session-Id goes away.

Part 3 · covers platform.claude.com MCP connector docs

The MCP connector: serverless clients 6 min live

Once Daybreak is remote, the Anthropic Messages API can call it directly - no client code, no b5 loop, no session management. You add an mcp_servers entry to a normal Messages request, list a toolset in tools, and Claude discovers and calls your tools for you. Three consumption modes now exist: a host app, your own client, or this connector.

Three ways to consume the same remote Daybreak 1 · Host appClaude Desktop / Code 2 · Your own clientthe b5 chatbot loop 3 · MCP connectorMessages API, no client code Daybreak (remote)Streamable HTTP · /mcp The connector is remote-only and tool-only: no stdio, no resources or prompts - just tool calls.
🔍 Click to zoom - host app, your client, or the connector all reach one remote server
LiveConnector walkthrough - a real request body4 min▶

Add the beta header, point mcp_servers at your deployed URL, and reference it with an mcp_toolset. Claude does the discovery and the tool call; you get the answer back in one response. This is the current connector shape (beta mcp-client-2025-11-20).

curl · Messages API with the MCP connector curl https://api.anthropic.com/v1/messages \ -H "Content-Type: application/json" \ -H "X-API-Key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: mcp-client-2025-11-20" \ -d '{ "model": "claude-sonnet-5", "max_tokens": 1000, "messages": [{"role": "user", "content": "How did March look for Daybreak?"}], "mcp_servers": [ { "type": "url", "url": "https://your-daybreak.example.com/mcp", "name": "daybreak", "authorization_token": "YOUR_OAUTH_TOKEN" } ], "tools": [ { "type": "mcp_toolset", "mcp_server_name": "daybreak", "default_config": { "enabled": false }, "configs": { "query_sales": { "enabled": true } } } ] }'

The default_config.enabled:false + per-tool configs pattern is an allowlist: only query_sales is exposed to the model. Flip the logic for a denylist. Response comes back with mcp_tool_use and mcp_tool_result blocks, then Claude's written answer.

LiveConnector vs your own client - when each3 min▶
Use the connector when...Write your own client (b5) when...
The server is remote and reachable by HTTPS URLYou need local stdio servers
You only need tool callsYou need resources, prompts, or elicitation
You want zero client code + built-in allowlistsYou want full control of the loop and model choice (incl. Ollama)
Claude is your modelYou are orchestrating multiple servers your own way
Remote-only, tool-only The connector cannot reach a local stdio server, and today it supports only tool calls - not resources or prompts. Need those, or a non-Claude model? That is exactly what your b5 client is for.
Demo 1 of 2

Flip the transport ★ 8 min · one line, then curl it

Turn the local Daybreak server into a web service and prove it with a raw HTTP request - no client, no model, just the protocol over HTTP.

Change the last line of project/server.py to mcp.run(transport="streamable-http") and run it. FastMCP serves http://127.0.0.1:8000/mcp.

POST an initialize to the endpoint (below). You get a JSON-RPC result back plus an Mcp-Session-Id header - the session id you will send on every later request.

Reuse that session id to POST tools/call for query_sales with {"month":"2026-03"} and read the $150.00 result straight off the wire.

Point your b5 client at the URL instead of a script path - same chatbot, now hitting a remote server.

curl · initialize the remote server curl -i http://127.0.0.1:8000/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize", "params":{"protocolVersion":"2025-11-25", "capabilities":{}, "clientInfo":{"name":"curl","version":"1.0.0"}}}' # Look at the response headers for: Mcp-Session-Id: ... # Send that header on every subsequent POST (tools/list, tools/call).
Demo 2 of 2

Call it from the Messages API ★ 8 min · zero client code

Deploy Daybreak to any public HTTPS host, then let Claude's API call it directly. No b5 loop, no session management - the connector handles discovery and the tool call for you.

Deploy the Streamable HTTP server to a public URL (the provider-neutral checklist in Part 2). Confirm curl https://.../mcp answers.

Send the connector request from Demo 1's Part-3 body, pointing url at your deployment.

Read the response: an mcp_tool_use block (Claude chose query_sales), an mcp_tool_result block (the $150.00 text), then Claude's written summary of the March dip - all with no client code you wrote.

Real world

Serverless integration is why the connector matters. A team that just wants Claude to reach their internal metrics server does not want to build and host a chatbot. They deploy one MCP server, hand the URL and a scoped OAuth token to whoever calls the Messages API, and allowlist exactly the read-only tools. No client fleet to maintain - the model app is Claude itself.

Homework

Try it yourself - this week ◐ 40-60 min total

Source material

Official sources covered

Remote transport is where the courses thin out and the connector is Anthropic-only; this page teaches Streamable HTTP against the current spec, decodes the stateless core ahead of finalization, and wires the connector to your own server. This page covers:

✓Anthropic Academy Advanced - "Transports" (StreamableHTTP + State)Single endpoint, SSE upgrade, session state, in depth
✓modelcontextprotocol.io - "Connect to remote MCP servers"Transport flip, mount path, remote consumption modes
✓platform.claude.com - MCP connector docsmcp_servers + mcp_toolset, allowlists, OAuth Bearer, remote-only
✓DeepLearning.AI - "Creating and Deploying Remote Servers"Provider-neutral deploy path, going from local to hosted
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · In Streamable HTTP, how does the server stream a long-running result?

One endpoint, one POST per message. The server may upgrade that single request's response to text/event-stream when the work streams. The old two-endpoint HTTP+SSE transport is deprecated.

2 · How is a session tracked today, and what changes on 2026-07-28?

On spec 2025-11-25, initialize hands out an Mcp-Session-Id carried on every later request. The 2026-07-28 stateless core removes the handshake and that header; capabilities travel in _meta, with Mcp-Method/Mcp-Name routing headers.

3 · You want Claude's API to call a local stdio server with no client code. Can the MCP connector do it?

The connector reaches remote servers by HTTPS URL and supports tool calls only - no stdio, no resources/prompts. A local server or those primitives need the client you built in b5.

Builder session 6 cheat sheet · pin this

Streamable HTTPOne endpoint. Client POSTs JSON-RPC; server returns JSON or upgrades that request to an SSE stream.
Sessions todayinitialize issues Mcp-Session-Id; sent on every later POST. Accept header signals JSON-or-stream.
2026-07-28 stateless coreHandshake + Mcp-Session-Id gone; capabilities in _meta; Mcp-Method/Mcp-Name routing headers; any node serves any request.
The one-line flipmcp.run(transport="streamable-http"). Same tools/resources/prompt; now a web service at /mcp.
Deploy anywhereBind 0.0.0.0 + $PORT, pin deps, expose /mcp over HTTPS, add auth (b7). Provider-neutral.
Three consumption modesHost app · your own client (b5) · MCP connector (Messages API, no client code).
Connector shapeBeta mcp-client-2025-11-20 + mcp_servers[url] + tools[mcp_toolset]. Allowlist via default_config/configs.
Connector limitsRemote-only (HTTPS URL), tool-only. No stdio, no resources/prompts. OAuth Bearer for auth.