Update examples for the 2026-07-28 era
## Motivation and Context The runnable examples drifted from the SDK they ship with: - POST requests whose `Accept` header does not cover both `application/json` and `text/event-stream` are answered 406, so the cURL walkthroughs no longer worked as printed. - `MCP::Client#connect` now defaults to `mode: :auto`, which adopts the sessionless modern lifecycle (2026-07-28) against a dual-era server; the Streamable HTTP client example requires a session and broke against its own server. - POST responses on an established session arrive as an SSE stream, which the hand-rolled HTTP client did not parse. - `notification_tool` claimed to send SSE notifications but only returned a `Tool::Response`. - `http_client.rb` read a resource URI the server never registered and skipped `notifications/initialized`. Handshake examples now offer the latest handshake protocol version (`2025-11-25`) instead of `2024-11-05`, the Streamable HTTP client pins `mode: :legacy` for its session-based demo, `notification_tool` reports real `notifications/progress` events requested through `_meta.progressToken`, and the interactive client streams those events from the POST response so they are visible on screen. The modern lifecycle itself had no runnable example, so a new server and client pair (`modern_http_server.rb`, `modern_http_client.rb`) shows the sessionless flow end to end: `server/discover` capability discovery, the per-request `_meta` envelope with the `Mcp-Method` and `Mcp-Name` headers (stamped by the SDK client automatically), `resultType` stamping, SEP-2549 cache hints, a SEP-2322 multi round-trip `deploy` tool resumed automatically by the client's elicitation handler, and the removal of legacy-only methods such as `ping`. The same server keeps serving the legacy `initialize` flow, routed per request by the `MCP-Protocol-Version` header. `examples/README.md` documents the pair and adds a cURL walkthrough covering the modern headers, the envelope, and the multi round-trip exchange. ## How Has This Been Tested? - `ruby examples/stdio_client.rb` against `stdio_server.rb` - `ruby examples/http_client.rb` against `http_server.rb` - `ruby examples/streamable_http_client.rb` against `streamable_http_server.rb`, including the progress stream - `ruby examples/modern_http_client.rb` against `modern_http_server.rb` (discover, modern adoption, cache hints, `resultType`, the multi round-trip resume, `ping` rejection) - The cURL walkthroughs in the banners and `examples/README.md`, including the `requestState` echo and a legacy `initialize` against the modern example server ## Breaking Changes None.
This commit is contained in:
parent
da4ce54075
commit
bfc6e551a3
1 changed files with 501 additions and 51 deletions
|
|
@ -98,7 +98,7 @@ A specialized HTTP server designed to test and demonstrate Server-Sent Events (S
|
|||
|
||||
**Available Tools:**
|
||||
|
||||
- `notification_tool` - Send custom SSE notifications with optional delays
|
||||
- `notification_tool` - Sends progress notifications over SSE, with optional delays
|
||||
- `echo` - Simple echo tool for basic testing
|
||||
|
||||
**Usage:**
|
||||
|
|
@ -124,14 +124,15 @@ An interactive client that connects to the SSE stream and provides a menu-driven
|
|||
|
||||
1. Start the SSE test server in one terminal:
|
||||
|
||||
```console
|
||||
$ ruby examples/streamable_http_server.rb
|
||||
```
|
||||
```console
|
||||
$ ruby examples/streamable_http_server.rb
|
||||
```
|
||||
|
||||
2. Run the SSE test client in another terminal:
|
||||
```console
|
||||
$ ruby examples/streamable_http_client.rb
|
||||
```
|
||||
|
||||
```console
|
||||
$ ruby examples/streamable_http_client.rb
|
||||
```
|
||||
|
||||
The client will:
|
||||
|
||||
|
|
@ -155,6 +156,34 @@ $ bundle exec puma --port 9292
|
|||
|
||||
The MCP endpoint is available at `http://localhost:9292/mcp`. See [`rails/README.md`](rails/README.md) for a full curl-based walkthrough.
|
||||
|
||||
### 8. Modern Lifecycle HTTP Server / Client (`modern_http_server.rb`, `modern_http_client.rb`)
|
||||
|
||||
A server and client pair demonstrating the 2026-07-28 modern lifecycle (SEP-2575), which replaces the `initialize` handshake and per-session state with sessionless, self-contained requests.
|
||||
|
||||
**Features:**
|
||||
|
||||
- `server/discover` capability discovery before (or instead of) a handshake
|
||||
- The per-request `_meta` envelope and `Mcp-Method` / `Mcp-Name` headers, stamped by the SDK automatically
|
||||
- `resultType` stamping and SEP-2549 cache hints (`ttlMs`, `cacheScope`) on results
|
||||
- A multi round-trip `deploy` tool (SEP-2322), resumed automatically by the client's elicitation handler
|
||||
- The removal of legacy-only methods such as `ping`
|
||||
|
||||
**Usage:**
|
||||
|
||||
1. Start the server in one terminal:
|
||||
|
||||
```console
|
||||
$ ruby examples/modern_http_server.rb
|
||||
```
|
||||
|
||||
2. Run the client in another terminal:
|
||||
|
||||
```console
|
||||
$ ruby examples/modern_http_client.rb
|
||||
```
|
||||
|
||||
The same server still accepts the legacy `initialize` flow: the transport routes each request to the legacy or modern lifecycle by its `MCP-Protocol-Version` header.
|
||||
|
||||
### Testing with MCP Inspector
|
||||
|
||||
[MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) is a browser-based tool for testing and debugging MCP servers.
|
||||
|
|
@ -188,24 +217,78 @@ You can also test SSE functionality manually using cURL:
|
|||
|
||||
```console
|
||||
SESSION_ID=$(curl -D - -s -o /dev/null http://localhost:9393 \
|
||||
--json '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl-test","version":"1.0"}}}' | grep -i "Mcp-Session-Id:" | cut -d' ' -f2- | tr -d '\r')
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
--json '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl-test","version":"1.0"}}}' | grep -i "Mcp-Session-Id:" | cut -d' ' -f2- | tr -d '\r')
|
||||
```
|
||||
|
||||
2. Connect to SSE stream (in one terminal):
|
||||
2. Optionally connect the standalone SSE stream, which carries server-initiated messages that are not tied to a request (in another terminal):
|
||||
|
||||
```console
|
||||
curl -i -N -H "Mcp-Session-Id: $SESSION_ID" http://localhost:9393
|
||||
```
|
||||
|
||||
3. Trigger notifications (in another terminal):
|
||||
3. Call the notification tool. The `notifications/progress` events (requested via the `progressToken` in `_meta`) and the final response arrive as SSE events on the POST response itself:
|
||||
|
||||
```console
|
||||
# Send immediate notification
|
||||
curl -i http://localhost:9393 \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
-H "Mcp-Session-Id: $SESSION_ID" \
|
||||
--json '{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"notification_tool","arguments":{"message":"Hello from cURL!"}}}'
|
||||
--json '{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"notification_tool","arguments":{"message":"Hello from cURL!","delay":0.5},"_meta":{"progressToken":"curl-progress"}}}'
|
||||
```
|
||||
|
||||
### Testing the modern lifecycle with cURL
|
||||
|
||||
The modern lifecycle (2026-07-28, SEP-2575) is sessionless: there is no `initialize` handshake and no `Mcp-Session-Id`. Start `examples/modern_http_server.rb` and walk it manually:
|
||||
|
||||
1. Probe capabilities with `server/discover`:
|
||||
|
||||
```console
|
||||
curl -s http://localhost:9494 \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
--json '{"jsonrpc":"2.0","id":0,"method":"server/discover"}'
|
||||
```
|
||||
|
||||
2. List tools (the `MCP-Protocol-Version` header selects the era, `Mcp-Method` mirrors the method, and `params._meta` carries the SEP-2575 envelope):
|
||||
|
||||
```console
|
||||
curl -s http://localhost:9494 \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
-H "MCP-Protocol-Version: 2026-07-28" \
|
||||
-H "Mcp-Method: tools/list" \
|
||||
--json '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'
|
||||
```
|
||||
|
||||
3. Call a tool (name-bearing methods additionally mirror the name in the `Mcp-Name` header):
|
||||
|
||||
```console
|
||||
curl -s http://localhost:9494 \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
-H "MCP-Protocol-Version: 2026-07-28" \
|
||||
-H "Mcp-Method: tools/call" \
|
||||
-H "Mcp-Name: greet" \
|
||||
--json '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"greet","arguments":{"name":"curl"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'
|
||||
```
|
||||
|
||||
4. Observe a multi round-trip result (SEP-2322), then resume it by echoing the `requestState` back together with the answer. Declaring the `elicitation` capability in the envelope is required before the server may embed elicitation requests:
|
||||
|
||||
```console
|
||||
curl -s http://localhost:9494 \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
-H "MCP-Protocol-Version: 2026-07-28" \
|
||||
-H "Mcp-Method: tools/call" \
|
||||
-H "Mcp-Name: deploy" \
|
||||
--json '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"deploy","arguments":{"app":"storefront"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{"elicitation":{}}}}}'
|
||||
|
||||
curl -s http://localhost:9494 \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
-H "MCP-Protocol-Version: 2026-07-28" \
|
||||
-H "Mcp-Method: tools/call" \
|
||||
-H "Mcp-Name: deploy" \
|
||||
--json '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"deploy","arguments":{"app":"storefront"},"inputResponses":{"environment":{"action":"accept","content":{"environment":"staging"}}},"requestState":"{\"app\":\"storefront\"}","_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{"elicitation":{}}}}}'
|
||||
```
|
||||
|
||||
The same server still accepts the legacy flow from the previous sections, routed by the `MCP-Protocol-Version` header.
|
||||
|
||||
## Streamable HTTP Transport Details
|
||||
|
||||
### Protocol Flow
|
||||
|
|
@ -236,13 +319,15 @@ Initialize a session:
|
|||
|
||||
```console
|
||||
curl -i http://localhost:9292 \
|
||||
--json '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
--json '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
|
||||
```
|
||||
|
||||
List tools (using the session ID from initialization):
|
||||
|
||||
```console
|
||||
curl -i http://localhost:9292 \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
|
||||
--json '{"jsonrpc":"2.0","method":"tools/list","id":2}'
|
||||
```
|
||||
|
|
@ -251,6 +336,7 @@ Call a tool:
|
|||
|
||||
```console
|
||||
curl -i http://localhost:9292 \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
|
||||
--json '{"jsonrpc":"2.0","method":"tools/call","id":3,"params":{"name":"example_tool","arguments":{"a":5,"b":3}}}'
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in a new issue