mcp 0.7.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.
data/examples/README.md DELETED
@@ -1,197 +0,0 @@
1
- # MCP Ruby Examples
2
-
3
- This directory contains examples of how to use the Model Context Protocol (MCP) Ruby library.
4
-
5
- ## Available Examples
6
-
7
- ### 1. STDIO Server (`stdio_server.rb`)
8
-
9
- A simple server that communicates over standard input/output. This is useful for desktop applications and command-line tools.
10
-
11
- **Usage:**
12
-
13
- ```console
14
- $ ruby examples/stdio_server.rb
15
- {"jsonrpc":"2.0","id":0,"method":"tools/list"}
16
- ```
17
-
18
- ### 2. HTTP Server (`http_server.rb`)
19
-
20
- 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.
21
-
22
- **Features:**
23
-
24
- - HTTP transport with Server-Sent Events (SSE) for streaming
25
- - Session management with unique session IDs
26
- - Example tools, prompts, and resources
27
- - JSON-RPC 2.0 protocol implementation
28
- - Full MCP protocol compliance
29
-
30
- **Usage:**
31
-
32
- ```console
33
- $ ruby examples/http_server.rb
34
- ```
35
-
36
- The server will start on `http://localhost:9292` and provide:
37
-
38
- - **Tools**:
39
- - `ExampleTool` - adds two numbers
40
- - `echo` - echoes back messages
41
- - **Prompts**: `ExamplePrompt` - echoes back arguments as a prompt
42
- - **Resources**: `test_resource` - returns example content
43
-
44
- ### 3. HTTP Client Example (`http_client.rb`)
45
-
46
- A client that demonstrates how to interact with the HTTP server using all MCP protocol methods.
47
-
48
- **Usage:**
49
-
50
- 1. Start the HTTP server in one terminal:
51
-
52
- ```console
53
- $ ruby examples/http_server.rb
54
- ```
55
-
56
- 2. Run the client example in another terminal:
57
- ```console
58
- $ ruby examples/http_client.rb
59
- ```
60
-
61
- The client will demonstrate:
62
-
63
- - Session initialization
64
- - Ping requests
65
- - Listing and calling tools
66
- - Listing and getting prompts
67
- - Listing and reading resources
68
- - Session cleanup
69
-
70
- ### 4. Streamable HTTP Server (`streamable_http_server.rb`)
71
-
72
- A specialized HTTP server designed to test and demonstrate Server-Sent Events (SSE) functionality in the MCP protocol.
73
-
74
- **Features:**
75
-
76
- - Tools specifically designed to trigger SSE notifications
77
- - Real-time progress updates and notifications
78
- - Detailed SSE-specific logging
79
-
80
- **Available Tools:**
81
-
82
- - `NotificationTool` - Send custom SSE notifications with optional delays
83
- - `echo` - Simple echo tool for basic testing
84
-
85
- **Usage:**
86
-
87
- ```console
88
- $ ruby examples/streamable_http_server.rb
89
- ```
90
-
91
- The server will start on `http://localhost:9393` and provide detailed instructions for testing SSE functionality.
92
-
93
- ### 5. Streamable HTTP Client (`streamable_http_client.rb`)
94
-
95
- An interactive client that connects to the SSE stream and provides a menu-driven interface for testing SSE functionality.
96
-
97
- **Features:**
98
-
99
- - Automatic SSE stream connection
100
- - Interactive menu for triggering various SSE events
101
- - Real-time display of received SSE notifications
102
- - Session management
103
-
104
- **Usage:**
105
-
106
- 1. Start the SSE test server in one terminal:
107
-
108
- ```console
109
- $ ruby examples/streamable_http_server.rb
110
- ```
111
-
112
- 2. Run the SSE test client in another terminal:
113
- ```console
114
- $ ruby examples/streamable_http_client.rb
115
- ```
116
-
117
- The client will:
118
-
119
- - Initialize a session automatically
120
- - Connect to the SSE stream
121
- - Provide an interactive menu to trigger notifications
122
- - Display all received SSE events in real-time
123
-
124
- ### Testing SSE with cURL
125
-
126
- You can also test SSE functionality manually using cURL:
127
-
128
- 1. Initialize a session:
129
-
130
- ```console
131
- SESSION_ID=$(curl -D - -s -o /dev/null http://localhost:9393 \
132
- --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')
133
- ```
134
-
135
- 2. Connect to SSE stream (in one terminal):
136
-
137
- ```console
138
- curl -i -N -H "Mcp-Session-Id: $SESSION_ID" http://localhost:9393
139
- ```
140
-
141
- 3. Trigger notifications (in another terminal):
142
-
143
- ```console
144
- # Send immediate notification
145
- curl -i http://localhost:9393 \
146
- -H "Mcp-Session-Id: $SESSION_ID" \
147
- --json '{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"notification_tool","arguments":{"message":"Hello from cURL!"}}}'
148
- ```
149
-
150
- ## Streamable HTTP Transport Details
151
-
152
- ### Protocol Flow
153
-
154
- The HTTP server implements the MCP Streamable HTTP transport protocol:
155
-
156
- 1. **Initialize Session**:
157
-
158
- - Client sends POST request with `initialize` method
159
- - Server responds with session ID in `Mcp-Session-Id` header
160
-
161
- 2. **Establish SSE Connection** (optional):
162
-
163
- - Client sends GET request with `Mcp-Session-Id` header
164
- - Server establishes Server-Sent Events stream for notifications
165
-
166
- 3. **Send Requests**:
167
-
168
- - Client sends POST requests with JSON-RPC 2.0 format
169
- - Server processes and responds with results
170
-
171
- 4. **Close Session**:
172
- - Client sends DELETE request with `Mcp-Session-Id` header
173
-
174
- ### Example cURL Commands
175
-
176
- Initialize a session:
177
-
178
- ```console
179
- curl -i http://localhost:9292 \
180
- --json '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
181
- ```
182
-
183
- List tools (using the session ID from initialization):
184
-
185
- ```console
186
- curl -i http://localhost:9292 \
187
- -H "Mcp-Session-Id: YOUR_SESSION_ID" \
188
- --json '{"jsonrpc":"2.0","method":"tools/list","id":2}'
189
- ```
190
-
191
- Call a tool:
192
-
193
- ```console
194
- curl -i http://localhost:9292 \
195
- -H "Mcp-Session-Id: YOUR_SESSION_ID" \
196
- --json '{"jsonrpc":"2.0","method":"tools/call","id":3,"params":{"name":"ExampleTool","arguments":{"a":5,"b":3}}}'
197
- ```
@@ -1,184 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "net/http"
4
- require "json"
5
- require "uri"
6
-
7
- # Simple HTTP client example for interacting with the MCP HTTP server
8
- class MCPHTTPClient
9
- def initialize(base_url = "http://localhost:9292")
10
- @base_url = base_url
11
- @session_id = nil
12
- end
13
-
14
- def send_request(method, params = nil, id = nil)
15
- uri = URI(@base_url)
16
- http = Net::HTTP.new(uri.host, uri.port)
17
-
18
- request = Net::HTTP::Post.new(uri.path.empty? ? "/" : uri.path)
19
- request["Content-Type"] = "application/json"
20
- request["Mcp-Session-Id"] = @session_id if @session_id
21
-
22
- body = {
23
- jsonrpc: "2.0",
24
- method: method,
25
- params: params,
26
- id: id || rand(10000),
27
- }.compact
28
-
29
- request.body = body.to_json
30
-
31
- response = http.request(request)
32
-
33
- # Store session ID if provided
34
- if response["Mcp-Session-Id"]
35
- @session_id = response["Mcp-Session-Id"]
36
- puts "Session ID: #{@session_id}"
37
- end
38
-
39
- JSON.parse(response.body)
40
- end
41
-
42
- def initialize_session
43
- puts "=== Initializing session ==="
44
- result = send_request("initialize", {
45
- protocolVersion: "2024-11-05",
46
- capabilities: {},
47
- clientInfo: {
48
- name: "example_client",
49
- version: "1.0",
50
- },
51
- })
52
- puts "Response: #{JSON.pretty_generate(result)}"
53
-
54
- result
55
- end
56
-
57
- def ping
58
- puts "=== Sending ping ==="
59
- result = send_request("ping")
60
- puts "Response: #{JSON.pretty_generate(result)}"
61
-
62
- result
63
- end
64
-
65
- def list_tools
66
- puts "=== Listing tools ==="
67
- result = send_request("tools/list")
68
- puts "Response: #{JSON.pretty_generate(result)}"
69
-
70
- result
71
- end
72
-
73
- def call_tool(name, arguments)
74
- puts "=== Calling tool: #{name} ==="
75
- result = send_request("tools/call", {
76
- name: name,
77
- arguments: arguments,
78
- })
79
- puts "Response: #{JSON.pretty_generate(result)}"
80
-
81
- result
82
- end
83
-
84
- def list_prompts
85
- puts "=== Listing prompts ==="
86
- result = send_request("prompts/list")
87
- puts "Response: #{JSON.pretty_generate(result)}"
88
-
89
- result
90
- end
91
-
92
- def get_prompt(name, arguments)
93
- puts "=== Getting prompt: #{name} ==="
94
- result = send_request("prompts/get", {
95
- name: name,
96
- arguments: arguments,
97
- })
98
- puts "Response: #{JSON.pretty_generate(result)}"
99
-
100
- result
101
- end
102
-
103
- def list_resources
104
- puts "=== Listing resources ==="
105
- result = send_request("resources/list")
106
- puts "Response: #{JSON.pretty_generate(result)}"
107
-
108
- result
109
- end
110
-
111
- def read_resource(uri)
112
- puts "=== Reading resource: #{uri} ==="
113
- result = send_request("resources/read", {
114
- uri: uri,
115
- })
116
- puts "Response: #{JSON.pretty_generate(result)}"
117
-
118
- result
119
- end
120
-
121
- def close_session
122
- return unless @session_id
123
-
124
- puts "=== Closing session ==="
125
- uri = URI(@base_url)
126
- http = Net::HTTP.new(uri.host, uri.port)
127
-
128
- request = Net::HTTP::Delete.new(uri.path.empty? ? "/" : uri.path)
129
- request["Mcp-Session-Id"] = @session_id
130
-
131
- response = http.request(request)
132
- result = JSON.parse(response.body)
133
- puts "Response: #{JSON.pretty_generate(result)}"
134
-
135
- @session_id = nil
136
- result
137
- end
138
- end
139
-
140
- # Main script
141
- if __FILE__ == $PROGRAM_NAME
142
- puts <<~MESSAGE
143
- MCP HTTP Client Example
144
- Make sure the HTTP server is running (ruby examples/http_server.rb)
145
- #{"=" * 50}
146
- MESSAGE
147
-
148
- client = MCPHTTPClient.new
149
-
150
- begin
151
- # Initialize session
152
- client.initialize_session
153
-
154
- # Test ping
155
- client.ping
156
-
157
- # List available tools
158
- client.list_tools
159
-
160
- # Call the example_tool (note: snake_case name)
161
- client.call_tool("example_tool", { a: 5, b: 3 })
162
-
163
- # Call the echo tool
164
- client.call_tool("echo", { message: "Hello from client!" })
165
-
166
- # List prompts
167
- client.list_prompts
168
-
169
- # Get a prompt (note: snake_case name)
170
- client.get_prompt("example_prompt", { message: "This is a test message" })
171
-
172
- # List resources
173
- client.list_resources
174
-
175
- # Read a resource
176
- client.read_resource("test_resource")
177
- rescue => e
178
- puts "Error: #{e.message}"
179
- puts e.backtrace
180
- ensure
181
- # Clean up session
182
- client.close_session
183
- end
184
- end
@@ -1,169 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
4
- require "mcp"
5
- require "rackup"
6
- require "json"
7
- require "logger"
8
-
9
- # Create a simple tool
10
- class ExampleTool < MCP::Tool
11
- description "A simple example tool that adds two numbers"
12
- input_schema(
13
- properties: {
14
- a: { type: "number" },
15
- b: { type: "number" },
16
- },
17
- required: ["a", "b"],
18
- )
19
-
20
- class << self
21
- def call(a:, b:)
22
- MCP::Tool::Response.new([{
23
- type: "text",
24
- text: "The sum of #{a} and #{b} is #{a + b}",
25
- }])
26
- end
27
- end
28
- end
29
-
30
- # Create a simple prompt
31
- class ExamplePrompt < MCP::Prompt
32
- description "A simple example prompt that echoes back its arguments"
33
- arguments [
34
- MCP::Prompt::Argument.new(
35
- name: "message",
36
- description: "The message to echo back",
37
- required: true,
38
- ),
39
- ]
40
-
41
- class << self
42
- def template(args, server_context:)
43
- MCP::Prompt::Result.new(
44
- messages: [
45
- MCP::Prompt::Message.new(
46
- role: "user",
47
- content: MCP::Content::Text.new(args[:message]),
48
- ),
49
- ],
50
- )
51
- end
52
- end
53
- end
54
-
55
- # Set up the server
56
- server = MCP::Server.new(
57
- name: "example_http_server",
58
- tools: [ExampleTool],
59
- prompts: [ExamplePrompt],
60
- resources: [
61
- MCP::Resource.new(
62
- uri: "https://test_resource.invalid",
63
- name: "test-resource",
64
- title: "Test Resource",
65
- description: "Test resource that echoes back the uri as its content",
66
- mime_type: "text/plain",
67
- ),
68
- ],
69
- )
70
-
71
- server.define_tool(
72
- name: "echo",
73
- description: "A simple example tool that echoes back its arguments",
74
- input_schema: { properties: { message: { type: "string" } }, required: ["message"] },
75
- ) do |message:|
76
- MCP::Tool::Response.new(
77
- [
78
- {
79
- type: "text",
80
- text: "Hello from echo tool! Message: #{message}",
81
- },
82
- ],
83
- )
84
- end
85
-
86
- server.resources_read_handler do |params|
87
- [{
88
- uri: params[:uri],
89
- mimeType: "text/plain",
90
- text: "Hello from HTTP server resource!",
91
- }]
92
- end
93
-
94
- # Create the Streamable HTTP transport
95
- transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
96
- server.transport = transport
97
-
98
- # Create a logger for MCP-specific logging
99
- mcp_logger = Logger.new($stdout)
100
- mcp_logger.formatter = proc do |_severity, _datetime, _progname, msg|
101
- "[MCP] #{msg}\n"
102
- end
103
-
104
- # Create a Rack application with logging
105
- app = proc do |env|
106
- request = Rack::Request.new(env)
107
-
108
- # Log MCP-specific details for POST requests
109
- if request.post?
110
- body = request.body.read
111
- request.body.rewind
112
- begin
113
- parsed_body = JSON.parse(body)
114
- mcp_logger.info("Request: #{parsed_body["method"]} (id: #{parsed_body["id"]})")
115
- mcp_logger.debug("Request body: #{JSON.pretty_generate(parsed_body)}")
116
- rescue JSON::ParserError
117
- mcp_logger.warn("Request body (raw): #{body}")
118
- end
119
- end
120
-
121
- # Handle the request
122
- response = transport.handle_request(request)
123
-
124
- # Log the MCP response details
125
- _, _, body = response
126
- if body.is_a?(Array) && !body.empty? && body.first
127
- begin
128
- parsed_response = JSON.parse(body.first)
129
- if parsed_response["error"]
130
- mcp_logger.error("Response error: #{parsed_response["error"]["message"]}")
131
- else
132
- mcp_logger.info("Response: #{parsed_response["result"] ? "success" : "empty"} (id: #{parsed_response["id"]})")
133
- end
134
- mcp_logger.debug("Response body: #{JSON.pretty_generate(parsed_response)}")
135
- rescue JSON::ParserError
136
- mcp_logger.warn("Response body (raw): #{body}")
137
- end
138
- end
139
-
140
- response
141
- end
142
-
143
- # Wrap the app with Rack middleware
144
- rack_app = Rack::Builder.new do
145
- # Use CommonLogger for standard HTTP request logging
146
- use(Rack::CommonLogger, Logger.new($stdout))
147
-
148
- # Add other useful middleware
149
- use(Rack::ShowExceptions)
150
-
151
- run(app)
152
- end
153
-
154
- # Start the server
155
- puts <<~MESSAGE
156
- Starting MCP HTTP server on http://localhost:9292
157
- Use POST requests to initialize and send JSON-RPC commands
158
- Example initialization:
159
- 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"}}}'
160
-
161
- The server will return a session ID in the Mcp-Session-Id header.
162
- Use this session ID for subsequent requests.
163
-
164
- Press Ctrl+C to stop the server
165
- MESSAGE
166
-
167
- # Run the server
168
- # Use Rackup to run the server
169
- Rackup::Handler.get("puma").run(rack_app, Port: 9292, Host: "localhost")
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
4
- require "mcp"
5
-
6
- # Create a simple tool
7
- class ExampleTool < MCP::Tool
8
- description "A simple example tool that adds two numbers"
9
- input_schema(
10
- properties: {
11
- a: { type: "number" },
12
- b: { type: "number" },
13
- },
14
- required: ["a", "b"],
15
- )
16
-
17
- class << self
18
- def call(a:, b:)
19
- MCP::Tool::Response.new([{
20
- type: "text",
21
- text: "The sum of #{a} and #{b} is #{a + b}",
22
- }])
23
- end
24
- end
25
- end
26
-
27
- # Create a simple prompt
28
- class ExamplePrompt < MCP::Prompt
29
- description "A simple example prompt that echoes back its arguments"
30
- arguments [
31
- MCP::Prompt::Argument.new(
32
- name: "message",
33
- description: "The message to echo back",
34
- required: true,
35
- ),
36
- ]
37
-
38
- class << self
39
- def template(args, server_context:)
40
- MCP::Prompt::Result.new(
41
- messages: [
42
- MCP::Prompt::Message.new(
43
- role: "user",
44
- content: MCP::Content::Text.new(args[:message]),
45
- ),
46
- ],
47
- )
48
- end
49
- end
50
- end
51
-
52
- # Set up the server
53
- server = MCP::Server.new(
54
- name: "example_server",
55
- version: "1.0.0",
56
- tools: [ExampleTool],
57
- prompts: [ExamplePrompt],
58
- resources: [
59
- MCP::Resource.new(
60
- uri: "https://test_resource.invalid",
61
- name: "test-resource",
62
- title: "Test Resource",
63
- description: "Test resource that echoes back the uri as its content",
64
- mime_type: "text/plain",
65
- ),
66
- ],
67
- )
68
-
69
- server.define_tool(
70
- name: "echo",
71
- description: "A simple example tool that echoes back its arguments",
72
- input_schema: { properties: { message: { type: "string" } }, required: ["message"] },
73
- ) do |message:|
74
- MCP::Tool::Response.new(
75
- [
76
- {
77
- type: "text",
78
- text: "Hello from echo tool! Message: #{message}",
79
- },
80
- ],
81
- )
82
- end
83
-
84
- server.resources_read_handler do |params|
85
- [{
86
- uri: params[:uri],
87
- mimeType: "text/plain",
88
- text: "Hello, world! URI: #{params[:uri]}",
89
- }]
90
- end
91
-
92
- # Create and start the transport
93
- transport = MCP::Server::Transports::StdioTransport.new(server)
94
- transport.open