## Motivation and Context This commit implements Phase 1 of 3 for #334. The MCP specification requires an `initialize` request followed by a `notifications/initialized` notification before any other interaction. PR #327 added `MCP::Client#connect` and `MCP::Client::HTTP#connect` to expose this handshake explicitly for the HTTP transport, but stdio remained on its private `initialize_session` lazy-init path, inconsistent with the Python SDK (`ClientSession.initialize()`) and TypeScript SDK (`Client.connect(transport)`), both of which require an explicit handshake call regardless of transport. The implicit init path on the first `send_request` is preserved as a non-breaking compatibility shim. Public API added on `Stdio`: - `connect(client_info:, protocol_version:, capabilities:)` performs the handshake, validates the negotiated `protocolVersion` against `MCP::Configuration::SUPPORTED_STABLE_PROTOCOL_VERSIONS`, caches the `InitializeResult`, and is idempotent. - `connected?` reports handshake completion. - `server_info` exposes the cached `InitializeResult`. Documentation and examples are updated so new users learn the explicit pattern from the start. ## How Has This Been Tested? Fifteen regression tests cover: explicit handshake, `server_info` caching, idempotence, custom keyword arguments, JSON-RPC error response, missing or non-Hash `result`, unsupported protocol version with state rollback, state rollback when the `notifications/initialized` write fails, retry after a failed handshake, and the `connected?` / `server_info` lifecycle. The pre-existing stdio tests pass without modification, verifying that the implicit-init compatibility path still works. ## Breaking Changes None for the typical "implicit init via `client.tools` / `client.call_tool`" usage. Existing stdio clients keep working unchanged. Three observable behavior changes for code that inspects transport state directly: - `Client#connected?` for a never-initialized stdio client returns `false` (previously fell through to a `true` default via the `respond_to?` fallback). - `Client#server_info` for stdio returns the cached `InitializeResult` after the handshake (previously always `nil` via the fallback). - The implicit handshake path now validates the server's negotiated protocol version, raising `RequestHandlerError` for unsupported versions. Production servers that return one of the supported versions are unaffected; this closes a pre-existing spec-conformance gap on stdio.
241 lines
6.5 KiB
Markdown
241 lines
6.5 KiB
Markdown
# MCP Ruby Examples
|
|
|
|
This directory contains examples of how to use the Model Context Protocol (MCP) Ruby library.
|
|
|
|
## Available Examples
|
|
|
|
### 1. stdio Server (`stdio_server.rb`)
|
|
|
|
A simple server that communicates over standard input/output. This is useful for desktop applications and command-line tools.
|
|
|
|
**Usage:**
|
|
|
|
```console
|
|
$ ruby examples/stdio_server.rb
|
|
{"jsonrpc":"2.0","id":0,"method":"tools/list"}
|
|
```
|
|
|
|
### 2. stdio Client (`stdio_client.rb`)
|
|
|
|
A client that connects to the stdio server using the `MCP::Client::Stdio` transport.
|
|
This demonstrates how to use the SDK's built-in client classes to interact with a server subprocess.
|
|
|
|
**Usage:**
|
|
|
|
```console
|
|
$ ruby examples/stdio_client.rb
|
|
```
|
|
|
|
The client will automatically launch `stdio_server.rb` as a subprocess and demonstrate:
|
|
|
|
- Performing the MCP initialization handshake via `client.connect`
|
|
- Listing and calling tools
|
|
- Listing prompts
|
|
- Listing and reading resources
|
|
- Transport cleanup on exit
|
|
|
|
### 3. HTTP Server (`http_server.rb`)
|
|
|
|
A standalone HTTP server built with Rack that implements the MCP Streamable HTTP transport protocol. This demonstrates how to create a web-based MCP server with session management and Server-Sent Events (SSE) support.
|
|
|
|
**Features:**
|
|
|
|
- HTTP transport with Server-Sent Events (SSE) for streaming
|
|
- Session management with unique session IDs
|
|
- Example tools, prompts, and resources
|
|
- JSON-RPC 2.0 protocol implementation
|
|
- Full MCP protocol compliance
|
|
|
|
**Usage:**
|
|
|
|
```console
|
|
$ ruby examples/http_server.rb
|
|
```
|
|
|
|
The server will start on `http://localhost:9292` and provide:
|
|
|
|
- **Tools**:
|
|
- `ExampleTool` - adds two numbers
|
|
- `echo` - echoes back messages
|
|
- **Prompts**: `ExamplePrompt` - echoes back arguments as a prompt
|
|
- **Resources**: `test_resource` - returns example content
|
|
|
|
### 4. HTTP Client Example (`http_client.rb`)
|
|
|
|
A client that demonstrates how to interact with the HTTP server using all MCP protocol methods.
|
|
|
|
**Usage:**
|
|
|
|
1. Start the HTTP server in one terminal:
|
|
|
|
```console
|
|
$ ruby examples/http_server.rb
|
|
```
|
|
|
|
2. Run the client example in another terminal:
|
|
```console
|
|
$ ruby examples/http_client.rb
|
|
```
|
|
|
|
The client will demonstrate:
|
|
|
|
- Session initialization
|
|
- Ping requests
|
|
- Listing and calling tools
|
|
- Listing and getting prompts
|
|
- Listing and reading resources
|
|
- Session cleanup
|
|
|
|
### 5. Streamable HTTP Server (`streamable_http_server.rb`)
|
|
|
|
A specialized HTTP server designed to test and demonstrate Server-Sent Events (SSE) functionality in the MCP protocol.
|
|
|
|
**Features:**
|
|
|
|
- Tools specifically designed to trigger SSE notifications
|
|
- Real-time progress updates and notifications
|
|
- Detailed SSE-specific logging
|
|
|
|
**Available Tools:**
|
|
|
|
- `NotificationTool` - Send custom SSE notifications with optional delays
|
|
- `echo` - Simple echo tool for basic testing
|
|
|
|
**Usage:**
|
|
|
|
```console
|
|
$ ruby examples/streamable_http_server.rb
|
|
```
|
|
|
|
The server will start on `http://localhost:9393` and provide detailed instructions for testing SSE functionality.
|
|
|
|
### 6. Streamable HTTP Client (`streamable_http_client.rb`)
|
|
|
|
An interactive client that connects to the SSE stream and provides a menu-driven interface for testing SSE functionality.
|
|
|
|
**Features:**
|
|
|
|
- Automatic SSE stream connection
|
|
- Interactive menu for triggering various SSE events
|
|
- Real-time display of received SSE notifications
|
|
- Session management
|
|
|
|
**Usage:**
|
|
|
|
1. Start the SSE test server in one terminal:
|
|
|
|
```console
|
|
$ ruby examples/streamable_http_server.rb
|
|
```
|
|
|
|
2. Run the SSE test client in another terminal:
|
|
```console
|
|
$ ruby examples/streamable_http_client.rb
|
|
```
|
|
|
|
The client will:
|
|
|
|
- Initialize a session automatically
|
|
- Connect to the SSE stream
|
|
- Provide an interactive menu to trigger notifications
|
|
- Display all received SSE events in real-time
|
|
|
|
### Testing with MCP Inspector
|
|
|
|
[MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) is a browser-based tool for testing and debugging MCP servers.
|
|
|
|
1. Start the server:
|
|
|
|
```console
|
|
$ ruby examples/streamable_http_server.rb
|
|
```
|
|
|
|
2. Start Inspector in another terminal:
|
|
|
|
```console
|
|
$ npx @modelcontextprotocol/inspector
|
|
```
|
|
|
|
3. Open `http://localhost:6274` in a browser:
|
|
|
|
- Set Transport Type to "Streamable HTTP"
|
|
- Set URL to `http://localhost:9393`
|
|
- Disable the Authorization header toggle (the example server does not require authentication)
|
|
- Click "Connect"
|
|
|
|
Once connected, you can list tools, call them, and see SSE notifications in the Inspector UI.
|
|
|
|
### Testing SSE with cURL
|
|
|
|
You can also test SSE functionality manually using cURL:
|
|
|
|
1. Initialize a session:
|
|
|
|
```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')
|
|
```
|
|
|
|
2. Connect to SSE stream (in one terminal):
|
|
|
|
```console
|
|
curl -i -N -H "Mcp-Session-Id: $SESSION_ID" http://localhost:9393
|
|
```
|
|
|
|
3. Trigger notifications (in another terminal):
|
|
|
|
```console
|
|
# Send immediate notification
|
|
curl -i http://localhost:9393 \
|
|
-H "Mcp-Session-Id: $SESSION_ID" \
|
|
--json '{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"notification_tool","arguments":{"message":"Hello from cURL!"}}}'
|
|
```
|
|
|
|
## Streamable HTTP Transport Details
|
|
|
|
### Protocol Flow
|
|
|
|
The HTTP server implements the MCP Streamable HTTP transport protocol:
|
|
|
|
1. **Initialize Session**:
|
|
|
|
- Client sends POST request with `initialize` method
|
|
- Server responds with session ID in `Mcp-Session-Id` header
|
|
|
|
2. **Establish SSE Connection** (optional):
|
|
|
|
- Client sends GET request with `Mcp-Session-Id` header
|
|
- Server establishes Server-Sent Events stream for notifications
|
|
|
|
3. **Send Requests**:
|
|
|
|
- Client sends POST requests with JSON-RPC 2.0 format
|
|
- Server processes and responds with results
|
|
|
|
4. **Close Session**:
|
|
- Client sends DELETE request with `Mcp-Session-Id` header
|
|
|
|
### Example cURL Commands
|
|
|
|
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"}}}'
|
|
```
|
|
|
|
List tools (using the session ID from initialization):
|
|
|
|
```console
|
|
curl -i http://localhost:9292 \
|
|
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
|
|
--json '{"jsonrpc":"2.0","method":"tools/list","id":2}'
|
|
```
|
|
|
|
Call a tool:
|
|
|
|
```console
|
|
curl -i http://localhost:9292 \
|
|
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
|
|
--json '{"jsonrpc":"2.0","method":"tools/call","id":3,"params":{"name":"ExampleTool","arguments":{"a":5,"b":3}}}'
|
|
```
|