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
|
|
@ -13,14 +13,16 @@ sse_logger.formatter = proc do |severity, datetime, _progname, msg|
|
|||
"[SSE] #{severity} #{datetime.strftime("%H:%M:%S.%L")} - #{msg}\n"
|
||||
end
|
||||
|
||||
# Tool that returns a response that will be sent via SSE if a stream is active
|
||||
# Tool that emits `notifications/progress` events, delivered over the SSE stream.
|
||||
# The client must request them by sending a `progressToken` in the request `_meta`.
|
||||
class NotificationTool < MCP::Tool
|
||||
tool_name "notification_tool"
|
||||
description "Returns a notification message that will be sent via SSE if stream is active"
|
||||
description "Sends the message back as progress notifications via SSE, then returns a summary"
|
||||
input_schema(
|
||||
properties: {
|
||||
message: { type: "string", description: "Message to send via SSE" },
|
||||
delay: { type: "number", description: "Delay in seconds before returning (optional)" },
|
||||
steps: { type: "number", description: "Number of progress notifications to send (default: 3)" },
|
||||
delay: { type: "number", description: "Delay in seconds between notifications (optional)" },
|
||||
},
|
||||
required: ["message"],
|
||||
)
|
||||
|
|
@ -28,14 +30,19 @@ class NotificationTool < MCP::Tool
|
|||
class << self
|
||||
attr_accessor :logger
|
||||
|
||||
def call(message:, delay: 0)
|
||||
sleep(delay) if delay > 0
|
||||
def call(message:, server_context:, steps: 3, delay: 0)
|
||||
steps = steps.to_i.clamp(1, 10)
|
||||
|
||||
logger&.info("Returning notification message: #{message}")
|
||||
steps.times do |i|
|
||||
sleep(delay) if delay > 0
|
||||
|
||||
logger&.info("Reporting progress #{i + 1}/#{steps}: #{message}")
|
||||
server_context.report_progress(i + 1, total: steps, message: message)
|
||||
end
|
||||
|
||||
MCP::Tool::Response.new([{
|
||||
type: "text",
|
||||
text: "Notification: #{message} (timestamp: #{Time.now.iso8601})",
|
||||
text: "Sent #{steps} progress notifications for: #{message}",
|
||||
}])
|
||||
end
|
||||
end
|
||||
|
|
@ -44,6 +51,7 @@ end
|
|||
# Create the server
|
||||
server = MCP::Server.new(
|
||||
name: "sse_test_server",
|
||||
version: "1.0.0",
|
||||
tools: [NotificationTool],
|
||||
prompts: [],
|
||||
resources: [],
|
||||
|
|
@ -149,29 +157,34 @@ puts <<~MESSAGE
|
|||
Starting server on http://localhost:9393
|
||||
|
||||
Available Tools:
|
||||
1. NotificationTool - Returns messages that are sent via SSE when stream is active"
|
||||
1. notification_tool - Sends the message back as progress notifications via SSE
|
||||
2. echo - Simple echo tool
|
||||
|
||||
Testing SSE:
|
||||
|
||||
1. Initialize session:
|
||||
curl -i http://localhost:9393 \\
|
||||
--json '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"sse-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":"sse-test","version":"1.0"}}}'
|
||||
|
||||
2. Connect SSE stream (use the session ID from step 1):"
|
||||
2. Connect SSE stream (use the session ID from step 1):
|
||||
curl -i -N -H "Mcp-Session-Id: YOUR_SESSION_ID" http://localhost:9393
|
||||
|
||||
3. In another terminal, test tools (responses will be sent via SSE if stream is active):
|
||||
|
||||
Echo tool:
|
||||
curl -i http://localhost:9393 -H "Mcp-Session-Id: YOUR_SESSION_ID" \\
|
||||
-H "Accept: application/json, text/event-stream" \\
|
||||
--json '{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"echo","arguments":{"message":"Hello SSE!"}}}'
|
||||
|
||||
Notification tool (with 2 second delay):
|
||||
Notification tool (progress notifications require a progressToken in _meta):
|
||||
curl -i http://localhost:9393 -H "Mcp-Session-Id: YOUR_SESSION_ID" \\
|
||||
--json '{"jsonrpc":"2.0","method":"tools/call","id":3,"params":{"name":"notification_tool","arguments":{"message":"Hello SSE!", "delay": 2}}}'
|
||||
-H "Accept: application/json, text/event-stream" \\
|
||||
--json '{"jsonrpc":"2.0","method":"tools/call","id":3,"params":{"name":"notification_tool","arguments":{"message":"Hello SSE!","delay":1},"_meta":{"progressToken":"curl-progress"}}}'
|
||||
|
||||
Note: When an SSE stream is active, tool responses will appear in the SSE stream and the POST request will return 202 Accepted with no body.
|
||||
Note: Each POST is answered with an SSE stream of its own; request-scoped notifications
|
||||
(such as the progress events above) appear on it before the final response.
|
||||
The standalone GET stream carries server-initiated messages that are not tied to a request.
|
||||
|
||||
Press Ctrl+C to stop the server
|
||||
MESSAGE
|
||||
|
|
|
|||
Loading…
Reference in a new issue