mcp-sdk.rb 0.1.1 → 0.1.4

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: 144a7bc5d730857d1ea77754f36046acf3e12fcd043f3f84b709e19f8d2c6f99
4
- data.tar.gz: 492711d7e4b5796548f669d593eff579dc906ea3eeabdf29839c25187e2d0b32
3
+ metadata.gz: 4a9f49fa38172f0c33f2294a0854871f9dbd0ba3e8a7ee2b68d168fff64f2bbf
4
+ data.tar.gz: 25a48fe35af5cbdf85e36cddde8ece4d1628e1a72a728d4c78eb66eefb4a9284
5
5
  SHA512:
6
- metadata.gz: 60f59abaa36e97a5cae62385e4a9034885d29dd8d6447311f7b87e4805bd87fd198308a3d3d3da8c81487b6a1a8f254bf8f3e28bd5c11476712d52dea85be491
7
- data.tar.gz: cd9a155aa772476ffaa2c7cf3aa8ae509dd824292483e5133168df86bbdf5ef7522e88c8bd38e7df9b711d71c48e881d0cb031a1db55cd0eab10e7fa651d44d1
6
+ metadata.gz: 35a216611ff9232bac3b3791f45e36317c02067e3cdcf273fe40861a4faa0946f85c861631d29d08b4987724fd9a18ff229e5385b7c2a8501bf529ba3cc3880d
7
+ data.tar.gz: c0fda6f709b4ac25aefd98a45b17c7991b53a97f48790eeef1ce09f8c7efb2489e433deba8048862aa6c8176a88435aada59f56fed39b2a5d63653d9b21df656
data/README.md CHANGED
@@ -2,14 +2,16 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/mcp-sdk.rb.svg)](https://badge.fury.io/rb/mcp-sdk.rb)
4
4
 
5
- A Ruby implementation of the Model Context Protocol (MCP) for connecting to MCP servers.
5
+ A Ruby implementation of the Model Context Protocol (MCP) for both connecting to MCP servers and creating MCP servers.
6
6
 
7
7
  ## Features
8
8
 
9
- - Supports both SSE (Server-Sent Events) and Stdio-based MCP servers
10
- - Type-safe client interfaces
9
+ - **Client Support**: Connect to SSE (Server-Sent Events) and Stdio-based MCP servers
10
+ - **Server Support**: Create MCP servers with tool registration
11
+ - Type-safe client interfaces
11
12
  - Easy integration with Ruby applications
12
13
  - Comprehensive error handling
14
+ - JSON-RPC 2.0 compliant
13
15
 
14
16
  ## Installation
15
17
 
@@ -33,7 +35,9 @@ $ gem install mcp-sdk.rb
33
35
 
34
36
  ## Usage
35
37
 
36
- ### Connecting to an SSE-based MCP server
38
+ ### MCP Client
39
+
40
+ #### Connecting to an SSE-based MCP server
37
41
 
38
42
  ```ruby
39
43
  require 'mcp-sdk.rb'
@@ -43,7 +47,7 @@ mcp_server_json = client.list_tools
43
47
  puts JSON.pretty_generate(convertFormat(mcp_server_json))
44
48
  ```
45
49
 
46
- ### Connecting to a Stdio-based MCP server
50
+ #### Connecting to a Stdio-based MCP server
47
51
 
48
52
  ```ruby
49
53
  require 'mcp-sdk.rb'
@@ -54,6 +58,285 @@ mcp_server_json = client.list_tools
54
58
  puts JSON.pretty_generate(convertFormat(mcp_server_json))
55
59
  ```
56
60
 
61
+ ### MCP Server
62
+
63
+ #### Creating an MCP Server
64
+
65
+ **Stdio Server (Default)**
66
+ ```ruby
67
+ require 'mcp-sdk.rb'
68
+
69
+ # Create stdio server (processes JSON-RPC over stdin/stdout)
70
+ server = MCP::Server.new(
71
+ name: "Demo",
72
+ version: "1.0.0",
73
+ type: "stdio" # optional, this is the default
74
+ )
75
+
76
+ # Add an addition tool
77
+ server.add_tool("add") do |params|
78
+ result = params["a"] + params["b"]
79
+ {
80
+ content: [{ type: "text", text: result.to_s }]
81
+ }
82
+ end
83
+
84
+ # Start the server (listens on stdin, responds on stdout)
85
+ server.start
86
+ ```
87
+
88
+ **SSE Server (HTTP with Server-Sent Events)**
89
+ ```ruby
90
+ require 'mcp-sdk.rb'
91
+
92
+ # Create SSE server (HTTP server with SSE support)
93
+ server = MCP::Server.new(
94
+ name: "Demo",
95
+ version: "1.0.0",
96
+ type: "sse",
97
+ port: 8080
98
+ )
99
+
100
+ # Add tools as needed
101
+ server.add_tool("add") do |params|
102
+ result = params["a"] + params["b"]
103
+ {
104
+ content: [{ type: "text", text: result.to_s }]
105
+ }
106
+ end
107
+
108
+ server.add_tool("multiply") do |params|
109
+ result = params["x"] * params["y"]
110
+ {
111
+ content: [{ type: "text", text: result.to_s }]
112
+ }
113
+ end
114
+
115
+ server.add_tool("greet") do |params|
116
+ name = params["name"] || "World"
117
+ {
118
+ content: [{ type: "text", text: "Hello, #{name}!" }]
119
+ }
120
+ end
121
+
122
+ # Start the HTTP server
123
+ server.start
124
+ ```
125
+
126
+ **Enhanced SSE Server (Advanced Features)**
127
+ ```ruby
128
+ require 'mcp-sdk.rb'
129
+
130
+ # Create Enhanced SSE server with advanced features
131
+ server = MCP::Server.new(
132
+ name: "Enhanced Demo",
133
+ version: "1.0.0",
134
+ type: "enhanced_sse",
135
+ port: 8080
136
+ )
137
+
138
+ # Add tools as needed
139
+ server.add_tool("calculate") do |params|
140
+ operation = params["operation"] || "add"
141
+ a = params["a"] || 0
142
+ b = params["b"] || 0
143
+
144
+ result = case operation
145
+ when "add" then a + b
146
+ when "multiply" then a * b
147
+ when "subtract" then a - b
148
+ when "divide" then b != 0 ? a / b : "Error: Division by zero"
149
+ else "Unknown operation"
150
+ end
151
+
152
+ {
153
+ content: [{ type: "text", text: "#{a} #{operation} #{b} = #{result}" }]
154
+ }
155
+ end
156
+
157
+ # Start the Enhanced SSE server
158
+ server.start
159
+ ```
160
+
161
+ #### Server Types
162
+
163
+ **Stdio Server (`type: "stdio"`)**
164
+ - Default server type
165
+ - Communicates via stdin/stdout using JSON-RPC 2.0
166
+ - Perfect for command-line tools and process-based communication
167
+ - No additional configuration required
168
+
169
+ **SSE Server (`type: "sse"`)**
170
+ - HTTP server with Server-Sent Events support
171
+ - Requires `port` parameter
172
+ - Provides REST endpoints and real-time SSE communication
173
+ - Includes CORS support for web applications
174
+
175
+ **Enhanced SSE Server (`type: "enhanced_sse"`)**
176
+ - Advanced HTTP server with enhanced SSE capabilities
177
+ - Requires `port` parameter
178
+ - Provides all standard SSE features plus:
179
+ - Connection management and tracking
180
+ - Broadcasting to multiple clients
181
+ - WebSocket-like bidirectional communication simulation
182
+ - Enhanced health monitoring
183
+ - Connection status endpoints
184
+ - Includes CORS support for web applications
185
+ - Built with Sinatra for better performance and extensibility
186
+
187
+ #### SSE Server Protocol
188
+
189
+ The MCP SSE server follows a specific two-step protocol flow:
190
+
191
+ **Step 1: Get Message Endpoint**
192
+ ```bash
193
+ curl http://localhost:8080/sse
194
+ ```
195
+ This returns the endpoint information in SSE format:
196
+ ```
197
+ event: endpoint
198
+ data: /mcp/message
199
+ ```
200
+
201
+ **Step 2: Send JSON-RPC to Message Endpoint**
202
+ ```bash
203
+ curl -X POST http://localhost:8080/mcp/message \
204
+ -H 'Content-Type: application/json' \
205
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
206
+ ```
207
+ This returns the JSON-RPC response in SSE format:
208
+ ```
209
+ data: {"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}
210
+ ```
211
+
212
+ **Additional Endpoints:**
213
+ - `GET /health` - Server health check (convenience)
214
+
215
+ #### Enhanced SSE Server Protocol
216
+
217
+ The Enhanced SSE server provides all standard SSE endpoints plus advanced features:
218
+
219
+ **Standard Endpoints:**
220
+ - `GET /sse` - Get message endpoint (MCP protocol compliance)
221
+ - `POST /mcp/message` - Send JSON-RPC requests and receive SSE responses
222
+ - `GET /health` - Enhanced health check with detailed server information
223
+
224
+ **Advanced Endpoints:**
225
+ - `GET /sse/events` - Advanced SSE endpoint with connection management
226
+ - `POST /mcp/broadcast` - Broadcast messages to all connected SSE clients
227
+ - `GET /ws/connect` - WebSocket-like connection simulation (long polling)
228
+ - `POST /ws/send/:connection_id` - Send messages to specific connections
229
+ - `GET /connections` - View active connection status
230
+
231
+ **Enhanced SSE Events Example:**
232
+ ```bash
233
+ # Connect to advanced SSE endpoint
234
+ curl -N http://localhost:8080/sse/events
235
+ # Returns connection ID and keeps connection alive with heartbeats
236
+
237
+ # Broadcast to all connected clients
238
+ curl -X POST http://localhost:8080/mcp/broadcast \
239
+ -H 'Content-Type: application/json' \
240
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
241
+
242
+ # Check connection status
243
+ curl http://localhost:8080/connections
244
+ ```
245
+
246
+ #### Server API
247
+
248
+ - `MCP::Server.new(name:, version:, type:, port:)` - Create a new server instance
249
+ - `name`: Server name (required)
250
+ - `version`: Server version (required)
251
+ - `type`: Server type - `"stdio"` (default), `"sse"`, or `"enhanced_sse"` (optional)
252
+ - `port`: Port number (required for SSE servers, ignored for stdio)
253
+ - `server.add_tool(name, &block)` - Register a tool with a block that receives parameters
254
+ - `server.start` - Start the server and listen for requests
255
+ - `server.stop` - Stop the server
256
+ - `server.list_tools` - Get list of registered tools
257
+ - `server.call_tool(name, arguments)` - Call a tool directly
258
+
259
+ The server implements the MCP protocol over JSON-RPC 2.0, supporting:
260
+ - `tools/list` - List available tools
261
+ - `tools/call` - Execute a specific tool
262
+
263
+ #### Examples
264
+
265
+ **Test MCP SSE Protocol with curl:**
266
+ ```bash
267
+ # Step 1: Get the message endpoint
268
+ curl http://localhost:8080/sse
269
+ # Returns: event: endpoint\ndata: /mcp/message
270
+
271
+ # Step 2: Send JSON-RPC requests to the message endpoint
272
+ curl -X POST http://localhost:8080/mcp/message \
273
+ -H 'Content-Type: application/json' \
274
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
275
+
276
+ curl -X POST http://localhost:8080/mcp/message \
277
+ -H 'Content-Type: application/json' \
278
+ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"add","arguments":{"a":5,"b":3}}}'
279
+
280
+ # Health check (convenience)
281
+ curl http://localhost:8080/health
282
+ ```
283
+
284
+ **Test Stdio Server:**
285
+ ```bash
286
+ # Send JSON-RPC to stdin
287
+ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | ruby your_server.rb
288
+ ```
289
+
290
+ #### Tool Response Format
291
+
292
+ Tools should return responses in MCP format:
293
+
294
+ ```ruby
295
+ {
296
+ content: [
297
+ { type: "text", text: "your response text" }
298
+ ]
299
+ }
300
+ ```
301
+
302
+ For simple text responses, you can return any value and it will be automatically wrapped in the proper format.
303
+
304
+ ## Example Files
305
+
306
+ This repository includes several example files to help you get started:
307
+
308
+ - `example_enhanced_sse.rb` - Complete Enhanced SSE server example with multiple tools
309
+ - `enhanced_sse_client.html` - HTML client demo for testing SSE connections
310
+ - `test_enhanced_sse.rb` - Integration test suite for Enhanced SSE functionality
311
+ - `demo_both_servers.rb` - Demonstration of both stdio and SSE servers
312
+ - `example_usage.rb` - Basic usage examples
313
+
314
+ ### Running Examples
315
+
316
+ **Start Enhanced SSE Server:**
317
+ ```bash
318
+ ruby example_enhanced_sse.rb
319
+ ```
320
+ Then open `enhanced_sse_client.html` in your browser to test the connection.
321
+
322
+ **Run Integration Tests:**
323
+ ```bash
324
+ ruby test_enhanced_sse.rb
325
+ ```
326
+
327
+ **Test Basic SSE Protocol:**
328
+ ```bash
329
+ # Terminal 1: Start server
330
+ ruby example_enhanced_sse.rb
331
+
332
+ # Terminal 2: Test endpoints
333
+ curl http://localhost:8081/health
334
+ curl http://localhost:8081/sse
335
+ curl -X POST http://localhost:8081/mcp/message \
336
+ -H 'Content-Type: application/json' \
337
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
338
+ ```
339
+
57
340
  ## Contributing
58
341
 
59
342
  Bug reports and pull requests are welcome on GitHub at https://github.com/zhuangbiaowei/mcp-sdk.rb.
@@ -0,0 +1,200 @@
1
+ require "json"
2
+ require "angelo"
3
+
4
+ module MCP
5
+ # Angelo-based SSE server implementation for MCP
6
+ class AngeloSSEServer < Angelo::Base
7
+ attr_reader :mcp_server_instance
8
+
9
+ def initialize(mcp_server_instance)
10
+ @mcp_server_instance = mcp_server_instance
11
+ super()
12
+ end
13
+
14
+ # Configure server settings
15
+ def self.configure_for_mcp(port)
16
+ port port
17
+ addr "0.0.0.0"
18
+ end
19
+
20
+ # Enable CORS for all routes
21
+ before do
22
+ headers 'Access-Control-Allow-Origin' => '*',
23
+ 'Access-Control-Allow-Methods' => 'GET, POST, OPTIONS',
24
+ 'Access-Control-Allow-Headers' => 'Content-Type'
25
+ end
26
+
27
+ # Handle OPTIONS requests for CORS
28
+ options '*' do
29
+ halt 200
30
+ end
31
+
32
+ # SSE endpoint - returns the message endpoint for subsequent requests
33
+ # This follows the MCP SSE protocol specification
34
+ get '/sse' do
35
+ content_type 'text/event-stream'
36
+ headers 'Cache-Control' => 'no-cache',
37
+ 'Connection' => 'keep-alive'
38
+
39
+ # Send endpoint event as per MCP SSE protocol
40
+ response = "event: endpoint\n"
41
+ response += "data: /mcp/message\n\n"
42
+ response
43
+ end
44
+
45
+ # MCP message endpoint - handles POST requests and returns SSE responses
46
+ post '/mcp/message' do
47
+ content_type 'text/event-stream'
48
+ headers 'Cache-Control' => 'no-cache',
49
+ 'Connection' => 'keep-alive'
50
+
51
+ begin
52
+ request_data = JSON.parse(request.body.read)
53
+ response = @mcp_server_instance.send(:handle_request, request_data)
54
+
55
+ # Return JSON-RPC response in SSE format
56
+ sse_response = "data: #{response.to_json}\n\n"
57
+ sse_response
58
+ rescue JSON::ParserError => e
59
+ error_response = {
60
+ jsonrpc: "2.0",
61
+ id: nil,
62
+ error: {
63
+ code: -32700,
64
+ message: "Parse error: #{e.message}"
65
+ }
66
+ }
67
+ "data: #{error_response.to_json}\n\n"
68
+ rescue => e
69
+ error_response = {
70
+ jsonrpc: "2.0",
71
+ id: nil,
72
+ error: {
73
+ code: -32603,
74
+ message: "Internal error: #{e.message}"
75
+ }
76
+ }
77
+ "data: #{error_response.to_json}\n\n"
78
+ end
79
+ end
80
+
81
+ # Alternative SSE endpoint using Angelo's eventsource functionality
82
+ # This provides more advanced SSE features
83
+ eventsource '/sse/events' do |sse|
84
+ # Add the connection to the SSE stash for broadcasting
85
+ sses << sse
86
+
87
+ # Send initial endpoint event
88
+ sse.event :endpoint, '/mcp/message'
89
+
90
+ # Keep connection alive
91
+ sse.on_close do
92
+ puts "SSE client disconnected"
93
+ end
94
+ end
95
+
96
+ # POST endpoint for broadcasting events to all SSE connections
97
+ post '/mcp/broadcast' do
98
+ content_type 'application/json'
99
+
100
+ begin
101
+ request_data = JSON.parse(request.body.read)
102
+ response = @mcp_server_instance.send(:handle_request, request_data)
103
+
104
+ # Broadcast to all connected SSE clients
105
+ sses.each do |sse|
106
+ sse.message response.to_json
107
+ end
108
+
109
+ { status: 'broadcasted', clients: sses.count }.to_json
110
+ rescue JSON::ParserError => e
111
+ error_response = {
112
+ jsonrpc: "2.0",
113
+ id: nil,
114
+ error: {
115
+ code: -32700,
116
+ message: "Parse error: #{e.message}"
117
+ }
118
+ }
119
+
120
+ sses.each do |sse|
121
+ sse.message error_response.to_json
122
+ end
123
+
124
+ { status: 'error', message: e.message }.to_json
125
+ rescue => e
126
+ error_response = {
127
+ jsonrpc: "2.0",
128
+ id: nil,
129
+ error: {
130
+ code: -32603,
131
+ message: "Internal error: #{e.message}"
132
+ }
133
+ }
134
+
135
+ sses.each do |sse|
136
+ sse.message error_response.to_json
137
+ end
138
+
139
+ { status: 'error', message: e.message }.to_json
140
+ end
141
+ end
142
+
143
+ # Health check endpoint
144
+ get '/health' do
145
+ content_type 'application/json'
146
+ {
147
+ status: 'ok',
148
+ server: @mcp_server_instance.name,
149
+ version: @mcp_server_instance.version,
150
+ type: 'angelo_sse',
151
+ tools_count: @mcp_server_instance.tools.size,
152
+ protocol: 'MCP SSE (Angelo)',
153
+ endpoints: {
154
+ sse: '/sse',
155
+ sse_events: '/sse/events',
156
+ message: '/mcp/message',
157
+ broadcast: '/mcp/broadcast'
158
+ },
159
+ connected_clients: sses.count
160
+ }.to_json
161
+ end
162
+
163
+ # WebSocket endpoint for real-time bidirectional communication
164
+ websocket '/ws' do |ws|
165
+ websockets << ws
166
+
167
+ ws.on_message do |msg|
168
+ begin
169
+ request_data = JSON.parse(msg)
170
+ response = @mcp_server_instance.send(:handle_request, request_data)
171
+ ws.write response.to_json
172
+ rescue JSON::ParserError => e
173
+ error_response = {
174
+ jsonrpc: "2.0",
175
+ id: nil,
176
+ error: {
177
+ code: -32700,
178
+ message: "Parse error: #{e.message}"
179
+ }
180
+ }
181
+ ws.write error_response.to_json
182
+ rescue => e
183
+ error_response = {
184
+ jsonrpc: "2.0",
185
+ id: nil,
186
+ error: {
187
+ code: -32603,
188
+ message: "Internal error: #{e.message}"
189
+ }
190
+ }
191
+ ws.write error_response.to_json
192
+ end
193
+ end
194
+
195
+ ws.on_close do
196
+ puts "WebSocket client disconnected"
197
+ end
198
+ end
199
+ end
200
+ end