ruby-mcp-client 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e0949102c5c22625bef68e2f5b4a0e2aecdfc636495ecba52d16f663c35f2fc
4
- data.tar.gz: fbeb7484c1458775b88aa074422913e9d09d8a37b23aac38c7edf82e2111b9cb
3
+ metadata.gz: a5e35c00292938e3c6cd17885d6578e465d12853ed1844f67dc4633096bb2fa3
4
+ data.tar.gz: e9419a3969689a0768ea8290f2a45f5214fcc4b74521ac38aac3bfd842b7ec49
5
5
  SHA512:
6
- metadata.gz: b2e75071c0fceb5061037b1537d9e71fd8f03dc5c94aa6c2bbc2a63ce6f24ae16c1c2c2cdb560f48e12ca4115224561f89043dde2145116d1e4e397c9cb7e409
7
- data.tar.gz: d3900ae79aba5d896a1496bc28af3e9ec8f0b7066f30eb0723cd1664845769dd09afd130faa2db0b4b1b67d948f15e3532cfb5abcd3b076d0e7547e7e89671fb
6
+ metadata.gz: 063bc6c91cf5d6e82a05b17dd90519e07d3168baf5efb36969e98d0893d40f4d630db3181dfd28929fc255f579780c041b407dfd8bfec17f3461022f2cb84471
7
+ data.tar.gz: e92a6c8d29474c08966008f9e2e5f530520258f546b40694fc0245eaf3da2ad6064ce7bb312596a07d5e4a94f4516603256c2bd7375cfc67065180f636f64eb1
data/README.md CHANGED
@@ -40,11 +40,16 @@ with popular AI services with built-in conversions:
40
40
  - `to_anthropic_tools()` - Formats tools for Anthropic Claude API
41
41
  - `to_google_tools()` - Formats tools for Google Vertex AI API (automatically removes "$schema" keys not accepted by Vertex AI)
42
42
 
43
- ## MCP 2025-03-26 Protocol Features
43
+ ## MCP Protocol Support
44
44
 
45
- This Ruby MCP Client implements key features from the latest MCP specification (Protocol Revision: 2025-03-26):
45
+ This Ruby MCP Client implements the **MCP 2025-06-18** specification with full backward compatibility.
46
46
 
47
- ### Implemented Features
47
+ ### Key Features
48
+
49
+ **MCP 2025-06-18 (Latest):**
50
+ - **Structured Tool Outputs** - Tools can declare output schemas and return type-safe, validated structured data
51
+ - **Elicitation (Server-initiated User Interactions)** - Servers can request user input during tool execution via bidirectional JSON-RPC (stdio, SSE, and Streamable HTTP transports)
52
+ - **Tool Annotations** - Support for tool behavior annotations (readOnly, destructive, requiresConfirmation) for safer tool execution
48
53
  - **OAuth 2.1 Authorization Framework** - Complete authentication with PKCE, dynamic client registration, server discovery, and runtime configuration
49
54
  - **Streamable HTTP Transport** - Enhanced transport with Server-Sent Event formatted responses and session management
50
55
  - **HTTP Redirect Support** - Automatic redirect handling for both SSE and HTTP transports with configurable limits
@@ -581,14 +586,22 @@ client = Anthropic::Client.new(access_token: ENV['ANTHROPIC_API_KEY'])
581
586
  ```
582
587
 
583
588
  Complete examples can be found in the `examples/` directory:
589
+
590
+ **AI Integration Examples:**
584
591
  - `ruby_openai_mcp.rb` - Integration with alexrudall/ruby-openai gem
585
592
  - `openai_ruby_mcp.rb` - Integration with official openai/openai-ruby gem
586
593
  - `ruby_anthropic_mcp.rb` - Integration with alexrudall/ruby-anthropic gem
587
594
  - `gemini_ai_mcp.rb` - Integration with Google Vertex AI and Gemini models
595
+
596
+ **Transport Examples:**
588
597
  - `streamable_http_example.rb` - Streamable HTTP transport with Playwright MCP
589
598
  - `echo_server.py` & `echo_server_client.rb` - FastMCP server example with full setup
590
599
  - `echo_server_streamable.py` & `echo_server_streamable_client.rb` - Enhanced streamable HTTP server example
591
600
 
601
+ **MCP 2025 Protocol Features:**
602
+ - `structured_output_server.py` & `test_structured_outputs.rb` - Structured tool outputs (MCP 2025-06-18)
603
+ - `echo_server_with_annotations.py` & `test_tool_annotations.rb` - Tool annotations (MCP 2025-03-26)
604
+
592
605
  ## MCP Server Compatibility
593
606
 
594
607
  This client works with any MCP-compatible server, including:
@@ -777,6 +790,83 @@ auth_url = oauth_provider.start_authorization_flow
777
790
  token = oauth_provider.complete_authorization_flow(code, state)
778
791
  ```
779
792
 
793
+ ### Browser-Based OAuth Authentication
794
+
795
+ For the easiest authentication experience, use the browser-based OAuth flow that automatically handles the entire process:
796
+
797
+ ```ruby
798
+ require 'mcp_client/auth/browser_oauth'
799
+
800
+ # Create OAuth provider
801
+ oauth_provider = MCPClient::Auth::OAuthProvider.new(
802
+ server_url: 'https://api.example.com/mcp',
803
+ redirect_uri: 'http://localhost:8080/callback',
804
+ scope: 'read:tools write:tools' # Optional
805
+ )
806
+
807
+ # Create browser OAuth helper
808
+ browser_oauth = MCPClient::Auth::BrowserOAuth.new(
809
+ oauth_provider,
810
+ callback_port: 8080, # Optional: Port for local server (default: 8080)
811
+ callback_path: '/callback' # Optional: Callback path (default: '/callback')
812
+ )
813
+
814
+ # Authenticate (opens browser automatically and handles callback)
815
+ token = browser_oauth.authenticate(
816
+ timeout: 300, # Optional: 5 minutes (default: 300)
817
+ auto_open_browser: true # Optional: Auto-open browser (default: true)
818
+ )
819
+
820
+ # Create authenticated client (use streamable_http for modern MCP servers)
821
+ server_config = {
822
+ type: 'streamable_http', # or 'http' for simple HTTP-only servers
823
+ base_url: 'https://api.example.com/mcp',
824
+ oauth_provider: oauth_provider
825
+ }
826
+
827
+ client = MCPClient::Client.new(mcp_server_configs: [server_config])
828
+ ```
829
+
830
+ **How it works:**
831
+ 1. **Automatically starts a local HTTP server** (using pure Ruby `TCPServer`) to handle the OAuth callback
832
+ 2. **Opens your default browser** to the authorization page (macOS: `open`, Linux: `xdg-open`, Windows: `start`)
833
+ 3. **Captures the authorization code** automatically from the callback
834
+ 4. **Completes the OAuth flow** and stores the access token
835
+ 5. **Handles token refresh** automatically when tokens expire
836
+
837
+ **Features:**
838
+ - **Zero external dependencies** - Uses pure Ruby stdlib (`TCPServer`)
839
+ - **User-friendly HTML pages** - Shows success/error pages in the browser
840
+ - **Automatic token management** - Handles token refresh transparently
841
+ - **Configurable timeout** - Raises `Timeout::Error` if user doesn't authorize in time
842
+ - **Token storage** - Supports custom storage backends for persistent tokens
843
+ - **Port conflict detection** - Clear error messages if port is already in use
844
+ - **Security hardened** - PKCE, CSRF protection, request validation, header limits
845
+
846
+ **Error Handling:**
847
+ ```ruby
848
+ begin
849
+ token = browser_oauth.authenticate
850
+ rescue Timeout::Error
851
+ puts "User took too long to authorize"
852
+ rescue MCPClient::Errors::ConnectionError => e
853
+ puts "OAuth flow failed: #{e.message}"
854
+ # Port already in use, server doesn't support OAuth, etc.
855
+ rescue ArgumentError => e
856
+ puts "Invalid state parameter (CSRF protection)"
857
+ end
858
+ ```
859
+
860
+ **Server Requirements:**
861
+ - OAuth 2.1 Protocol with PKCE
862
+ - Authorization Server Discovery via:
863
+ - `/.well-known/oauth-authorization-server` (primary, for self-contained servers)
864
+ - `/.well-known/oauth-protected-resource` (fallback, for delegated auth)
865
+ - Dynamic Client Registration (recommended)
866
+ - Authorization Code Grant Flow
867
+
868
+ For a complete working example, see [`examples/oauth_browser_auth.rb`](examples/oauth_browser_auth.rb).
869
+
780
870
  ### OAuth Features
781
871
 
782
872
  - **OAuth 2.1 compliance** with PKCE for security
@@ -785,6 +875,7 @@ token = oauth_provider.complete_authorization_flow(code, state)
785
875
  - **Token refresh** and automatic token management
786
876
  - **Pluggable storage** for tokens and client credentials
787
877
  - **Runtime configuration** via getter/setter methods
878
+ - **Browser-based authentication** with automatic callback handling
788
879
 
789
880
  For complete OAuth documentation, see [OAUTH.md](OAUTH.md).
790
881
 
@@ -908,6 +999,254 @@ elsif content.binary?
908
999
  end
909
1000
  ```
910
1001
 
1002
+ ## Tool Annotations
1003
+
1004
+ MCP 2025-06-18 supports tool annotations that describe tool behavior, enabling safer and more informed tool execution. The Ruby MCP Client provides full support for reading and interpreting these annotations.
1005
+
1006
+ ### Annotation Types
1007
+
1008
+ Tools can include the following annotations:
1009
+
1010
+ - **`readOnly`** - Indicates the tool only reads data and doesn't modify state
1011
+ - **`destructive`** - Indicates the tool performs potentially dangerous operations (e.g., deleting data)
1012
+ - **`requiresConfirmation`** - Indicates the tool should require explicit user confirmation before execution
1013
+
1014
+ ### Using Tool Annotations
1015
+
1016
+ ```ruby
1017
+ # List tools and check their annotations
1018
+ tools = client.list_tools
1019
+
1020
+ tools.each do |tool|
1021
+ puts "Tool: #{tool.name}"
1022
+
1023
+ # Check annotations using helper methods
1024
+ if tool.read_only?
1025
+ puts " → Safe to execute (read-only)"
1026
+ end
1027
+
1028
+ if tool.destructive?
1029
+ puts " ⚠️ Warning: This tool is destructive"
1030
+ end
1031
+
1032
+ if tool.requires_confirmation?
1033
+ puts " 🛡️ Requires user confirmation before execution"
1034
+ end
1035
+
1036
+ # Access raw annotations hash
1037
+ if tool.annotations
1038
+ puts " Annotations: #{tool.annotations.inspect}"
1039
+ end
1040
+ end
1041
+
1042
+ # Make informed decisions based on annotations
1043
+ tool = client.find_tool('delete_user')
1044
+ if tool.destructive? && tool.requires_confirmation?
1045
+ # Prompt user for confirmation before executing
1046
+ puts "This will delete user data. Are you sure? (y/n)"
1047
+ response = gets.chomp
1048
+ if response.downcase == 'y'
1049
+ result = client.call_tool('delete_user', { user_id: 123 })
1050
+ else
1051
+ puts "Operation cancelled"
1052
+ end
1053
+ else
1054
+ # Safe to execute without confirmation
1055
+ result = client.call_tool('get_user', { user_id: 123 })
1056
+ end
1057
+ ```
1058
+
1059
+ ### Tool Annotation Helper Methods
1060
+
1061
+ The `Tool` class provides convenient helper methods:
1062
+
1063
+ - `tool.read_only?` - Returns true if the tool is marked as read-only
1064
+ - `tool.destructive?` - Returns true if the tool is marked as destructive
1065
+ - `tool.requires_confirmation?` - Returns true if the tool requires confirmation
1066
+ - `tool.annotations` - Returns the raw annotations hash
1067
+
1068
+ ### Example
1069
+
1070
+ See `examples/test_tool_annotations.rb` for a complete working example demonstrating:
1071
+ - Listing tools with their annotations
1072
+ - Using annotation helper methods
1073
+ - Making annotation-aware decisions about tool execution
1074
+ - Handling destructive operations safely
1075
+
1076
+ ## Structured Tool Outputs
1077
+
1078
+ MCP 2025-06-18 introduces structured tool outputs, allowing tools to declare output schemas and return type-safe, validated data structures instead of just text.
1079
+
1080
+ ### Overview
1081
+
1082
+ Tools can now specify an `outputSchema` (JSON Schema) that defines the expected structure of their results. When called, they return data in a `structuredContent` field that validates against this schema, providing:
1083
+
1084
+ - **Type safety** - Predictable, validated data structures
1085
+ - **Better IDE support** - Code completion and type checking
1086
+ - **Easier parsing** - No need to parse text or guess formats
1087
+ - **Backward compatibility** - Text content is still provided alongside structured data
1088
+
1089
+ ### Using Structured Outputs
1090
+
1091
+ ```ruby
1092
+ # List tools and check for structured output support
1093
+ tools = client.list_tools
1094
+
1095
+ tools.each do |tool|
1096
+ if tool.structured_output?
1097
+ puts "#{tool.name} supports structured output"
1098
+ puts "Output schema: #{tool.output_schema}"
1099
+ end
1100
+ end
1101
+
1102
+ # Call a tool that returns structured output
1103
+ result = client.call_tool('get_weather', { location: 'San Francisco' })
1104
+
1105
+ # Access structured data (type-safe, validated)
1106
+ if result['structuredContent']
1107
+ data = result['structuredContent']
1108
+ puts "Temperature: #{data['temperature']}°C"
1109
+ puts "Conditions: #{data['conditions']}"
1110
+ puts "Humidity: #{data['humidity']}%"
1111
+ end
1112
+
1113
+ # Backward compatibility: text content is still available
1114
+ text_content = result['content']&.first&.dig('text')
1115
+ parsed = JSON.parse(text_content) if text_content
1116
+ ```
1117
+
1118
+ ### Helper Methods
1119
+
1120
+ The `Tool` class provides a convenient helper method:
1121
+
1122
+ - `tool.structured_output?` - Returns true if the tool has an output schema defined
1123
+ - `tool.output_schema` - Returns the JSON Schema for the tool's output
1124
+
1125
+ ### Example
1126
+
1127
+ See `examples/test_structured_outputs.rb` for a complete working example demonstrating:
1128
+ - Detecting tools with structured output support
1129
+ - Accessing output schemas
1130
+ - Calling tools and receiving structured data
1131
+ - Backward compatibility with text content
1132
+
1133
+ The example includes a Python MCP server (`examples/structured_output_server.py`) that provides three tools with structured outputs:
1134
+ - `get_weather` - Weather data with temperature, conditions, humidity
1135
+ - `analyze_text` - Text analysis with word count, character count, statistics
1136
+ - `calculate_stats` - Statistical calculations (mean, median, min, max, etc.)
1137
+
1138
+ ## Elicitation (Server-initiated User Interactions) - MCP 2025-06-18
1139
+
1140
+ Elicitation enables MCP servers to request user input during tool execution via bidirectional JSON-RPC.
1141
+ This allows servers to gather additional information, confirm sensitive operations, or interact with users
1142
+ dynamically during the execution of tools.
1143
+
1144
+ ### Transport Support
1145
+
1146
+ **Fully Supported:**
1147
+ - ✅ **stdio** - Full bidirectional JSON-RPC over stdin/stdout
1148
+ - ✅ **SSE** - Server sends requests via SSE stream, client responds via HTTP POST
1149
+ - ✅ **Streamable HTTP** - Server sends requests via SSE-formatted responses, client responds via HTTP POST
1150
+
1151
+ **Not Supported:**
1152
+ - ❌ **HTTP** - Pure request-response architecture prevents server-initiated requests
1153
+
1154
+ ### Using Elicitation
1155
+
1156
+ To use elicitation, register a handler when creating the client:
1157
+
1158
+ ```ruby
1159
+ # Define an elicitation handler
1160
+ elicitation_handler = lambda do |message, requested_schema|
1161
+ puts "Server requests: #{message}"
1162
+
1163
+ # Show expected input format from schema
1164
+ if requested_schema && requested_schema['properties']
1165
+ requested_schema['properties'].each do |field, schema|
1166
+ puts " #{field}: #{schema['type']}"
1167
+ end
1168
+ end
1169
+
1170
+ # Prompt user and return one of three response types:
1171
+
1172
+ # 1. Accept and provide data to the server
1173
+ {
1174
+ 'action' => 'accept',
1175
+ 'content' => {
1176
+ 'field_name' => 'user_input_value'
1177
+ }
1178
+ }
1179
+
1180
+ # 2. Decline to provide input
1181
+ # { 'action' => 'decline' }
1182
+
1183
+ # 3. Cancel the operation
1184
+ # { 'action' => 'cancel' }
1185
+ end
1186
+
1187
+ # Create client with elicitation handler
1188
+ # Works with stdio, SSE, and Streamable HTTP transports
1189
+ client = MCPClient::Client.new(
1190
+ mcp_server_configs: [
1191
+ # Stdio transport
1192
+ MCPClient.stdio_config(
1193
+ command: 'python my_server.py',
1194
+ name: 'my-server'
1195
+ ),
1196
+ # Or SSE transport
1197
+ MCPClient.sse_config(
1198
+ base_url: 'https://api.example.com/sse',
1199
+ name: 'remote-server'
1200
+ ),
1201
+ # Or Streamable HTTP transport
1202
+ MCPClient.streamable_http_config(
1203
+ base_url: 'https://api.example.com',
1204
+ endpoint: '/mcp',
1205
+ name: 'streamable-server'
1206
+ )
1207
+ ],
1208
+ elicitation_handler: elicitation_handler
1209
+ )
1210
+
1211
+ # Call tools - connection happens automatically
1212
+ # Server may send elicitation requests during tool execution
1213
+ result = client.call_tool('create_document', { format: 'markdown' }, server: 'my-server')
1214
+ ```
1215
+
1216
+ ### Response Actions
1217
+
1218
+ The elicitation handler should return a hash with an `action` field:
1219
+
1220
+ 1. **Accept** - Provide the requested input:
1221
+ ```ruby
1222
+ {
1223
+ 'action' => 'accept',
1224
+ 'content' => { 'field' => 'value' } # Must match requested schema
1225
+ }
1226
+ ```
1227
+
1228
+ 2. **Decline** - Refuse to provide input (server should handle gracefully):
1229
+ ```ruby
1230
+ { 'action' => 'decline' }
1231
+ ```
1232
+
1233
+ 3. **Cancel** - Cancel the entire operation:
1234
+ ```ruby
1235
+ { 'action' => 'cancel' }
1236
+ ```
1237
+
1238
+ ### Example
1239
+
1240
+ See `examples/elicitation/test_elicitation.rb` for a complete working example demonstrating:
1241
+ - Registering an elicitation handler
1242
+ - Handling different message types and schemas
1243
+ - Responding with accept/decline/cancel actions
1244
+ - Interactive user prompts during tool execution
1245
+
1246
+ The example includes a Python MCP server (`examples/elicitation/elicitation_server.py`) that provides tools using elicitation:
1247
+ - `create_document` - Requests title and content via elicitation
1248
+ - `send_notification` - Confirms before sending a notification
1249
+
911
1250
  ## Key Features
912
1251
 
913
1252
  ### Client Features
@@ -918,6 +1257,9 @@ end
918
1257
  - **Server lookup** - Find servers by name using `find_server`
919
1258
  - **Tool association** - Each tool knows which server it belongs to
920
1259
  - **Tool discovery** - Find tools by name or pattern
1260
+ - **Structured outputs** - Support for MCP 2025-06-18 structured tool outputs with output schemas and type-safe responses
1261
+ - **Tool annotations** - Support for readOnly, destructive, and requiresConfirmation annotations with helper methods
1262
+ - **Elicitation support** - Server-initiated user interactions during tool execution (stdio, SSE, and Streamable HTTP transports)
921
1263
  - **Server disambiguation** - Specify which server to use when tools with same name exist in multiple servers
922
1264
  - **Atomic tool calls** - Simple API for invoking tools with parameters
923
1265
  - **Batch support** - Call multiple tools in a single operation