Skip to content

Commit 1272360

Browse files
authored
Merge branch 'modelcontextprotocol:main' into patch-1
2 parents 5cd3ed7 + 70384c8 commit 1272360

File tree

16 files changed

+649
-83
lines changed

16 files changed

+649
-83
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.3.0] - 2025-09-14
11+
12+
### Added
13+
14+
- Tool output schema support with comprehensive validation (#122)
15+
- HTTP client transport layer for MCP clients (#28)
16+
- Tool annotations validation for protocol compatibility (#122)
17+
- Server instructions support (#87)
18+
- Title support in server info (#119)
19+
- Default values for tool annotation hints (#118)
20+
- Notifications/initialized method implementation (#84)
21+
22+
### Changed
23+
24+
- Make default protocol version the latest specification version (#83)
25+
- Protocol version validation to ensure valid values (#80)
26+
- Improved tool handling for tools with no arguments (#85, #86)
27+
- Better error handling and response API (#109)
28+
29+
### Fixed
30+
31+
- JSON-RPC notification format in Streamable HTTP transport (#91)
32+
- Errors when title is not specified (#126)
33+
- Tools with missing arguments handling (#86)
34+
- Namespacing issues in README examples (#89)
35+
1036
## [0.2.0] - 2025-07-15
1137

1238
### Added

README.md

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ Notifications follow the JSON-RPC 2.0 specification and use these method names:
124124

125125
#### Transport Support
126126

127-
- **HTTP Transport**: Notifications are sent as Server-Sent Events (SSE) to all connected sessions
128-
- **Stdio Transport**: Notifications are sent as JSON-RPC 2.0 messages to stdout
127+
- **stdio**: Notifications are sent as JSON-RPC 2.0 messages to stdout
128+
- **Streamable HTTP**: Notifications are sent as JSON-RPC 2.0 messages over HTTP with streaming (chunked transfer or SSE)
129129

130130
#### Usage Example
131131

@@ -385,6 +385,14 @@ class MyTool < MCP::Tool
385385
},
386386
required: ["message"]
387387
)
388+
output_schema(
389+
properties: {
390+
result: { type: "string" },
391+
success: { type: "boolean" },
392+
timestamp: { type: "string", format: "date-time" }
393+
},
394+
required: ["result", "success", "timestamp"]
395+
)
388396
annotations(
389397
read_only_hint: true,
390398
destructive_hint: false,
@@ -448,6 +456,105 @@ Tools can include annotations that provide additional metadata about their behav
448456

449457
Annotations can be set either through the class definition using the `annotations` class method or when defining a tool using the `define` method.
450458

459+
> [!NOTE]
460+
> This **Tool Annotations** feature is supported starting from `protocol_version: '2025-03-26'`.
461+
462+
### Tool Output Schemas
463+
464+
Tools can optionally define an `output_schema` to specify the expected structure of their results. This works similarly to how `input_schema` is defined and can be used in three ways:
465+
466+
1. **Class definition with output_schema:**
467+
468+
```ruby
469+
class WeatherTool < MCP::Tool
470+
tool_name "get_weather"
471+
description "Get current weather for a location"
472+
473+
input_schema(
474+
properties: {
475+
location: { type: "string" },
476+
units: { type: "string", enum: ["celsius", "fahrenheit"] }
477+
},
478+
required: ["location"]
479+
)
480+
481+
output_schema(
482+
properties: {
483+
temperature: { type: "number" },
484+
condition: { type: "string" },
485+
humidity: { type: "integer" }
486+
},
487+
required: ["temperature", "condition", "humidity"]
488+
)
489+
490+
def self.call(location:, units: "celsius", server_context:)
491+
# Call weather API and structure the response
492+
api_response = WeatherAPI.fetch(location, units)
493+
weather_data = {
494+
temperature: api_response.temp,
495+
condition: api_response.description,
496+
humidity: api_response.humidity_percent
497+
}
498+
499+
output_schema.validate_result(weather_data)
500+
501+
MCP::Tool::Response.new([{
502+
type: "text",
503+
text: weather_data.to_json
504+
}])
505+
end
506+
end
507+
```
508+
509+
2. **Using Tool.define with output_schema:**
510+
511+
```ruby
512+
tool = MCP::Tool.define(
513+
name: "calculate_stats",
514+
description: "Calculate statistics for a dataset",
515+
input_schema: {
516+
properties: {
517+
numbers: { type: "array", items: { type: "number" } }
518+
},
519+
required: ["numbers"]
520+
},
521+
output_schema: {
522+
properties: {
523+
mean: { type: "number" },
524+
median: { type: "number" },
525+
count: { type: "integer" }
526+
},
527+
required: ["mean", "median", "count"]
528+
}
529+
) do |args, server_context|
530+
# Calculate statistics and validate against schema
531+
MCP::Tool::Response.new([{ type: "text", text: "Statistics calculated" }])
532+
end
533+
```
534+
535+
3. **Using OutputSchema objects:**
536+
537+
```ruby
538+
class DataTool < MCP::Tool
539+
output_schema MCP::Tool::OutputSchema.new(
540+
properties: {
541+
success: { type: "boolean" },
542+
data: { type: "object" }
543+
},
544+
required: ["success"]
545+
)
546+
end
547+
```
548+
549+
MCP spec for the [Output Schema](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema) specifies that:
550+
551+
- **Server Validation**: Servers MUST provide structured results that conform to the output schema
552+
- **Client Validation**: Clients SHOULD validate structured results against the output schema
553+
- **Better Integration**: Enables strict schema validation, type information, and improved developer experience
554+
- **Backward Compatibility**: Tools returning structured content SHOULD also include serialized JSON in a TextContent block
555+
556+
The output schema follows standard JSON Schema format and helps ensure consistent data exchange between MCP servers and clients.
557+
451558
### Prompts
452559

453560
MCP spec includes [Prompts](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts), which enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs.
@@ -755,7 +862,7 @@ The client provides a wrapper class for tools returned by the server:
755862

756863
- `MCP::Client::Tool` - Represents a single tool with its metadata
757864

758-
This class provide easy access to tool properties like name, description, and input schema.
865+
This class provides easy access to tool properties like name, description, input schema, and output schema.
759866

760867
## Releases
761868

examples/http_client.rb

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,23 @@ def initialize_session
5050
},
5151
})
5252
puts "Response: #{JSON.pretty_generate(result)}"
53-
puts
53+
5454
result
5555
end
5656

5757
def ping
5858
puts "=== Sending ping ==="
5959
result = send_request("ping")
6060
puts "Response: #{JSON.pretty_generate(result)}"
61-
puts
61+
6262
result
6363
end
6464

6565
def list_tools
6666
puts "=== Listing tools ==="
6767
result = send_request("tools/list")
6868
puts "Response: #{JSON.pretty_generate(result)}"
69-
puts
69+
7070
result
7171
end
7272

@@ -77,15 +77,15 @@ def call_tool(name, arguments)
7777
arguments: arguments,
7878
})
7979
puts "Response: #{JSON.pretty_generate(result)}"
80-
puts
80+
8181
result
8282
end
8383

8484
def list_prompts
8585
puts "=== Listing prompts ==="
8686
result = send_request("prompts/list")
8787
puts "Response: #{JSON.pretty_generate(result)}"
88-
puts
88+
8989
result
9090
end
9191

@@ -96,15 +96,15 @@ def get_prompt(name, arguments)
9696
arguments: arguments,
9797
})
9898
puts "Response: #{JSON.pretty_generate(result)}"
99-
puts
99+
100100
result
101101
end
102102

103103
def list_resources
104104
puts "=== Listing resources ==="
105105
result = send_request("resources/list")
106106
puts "Response: #{JSON.pretty_generate(result)}"
107-
puts
107+
108108
result
109109
end
110110

@@ -114,7 +114,7 @@ def read_resource(uri)
114114
uri: uri,
115115
})
116116
puts "Response: #{JSON.pretty_generate(result)}"
117-
puts
117+
118118
result
119119
end
120120

@@ -131,7 +131,6 @@ def close_session
131131
response = http.request(request)
132132
result = JSON.parse(response.body)
133133
puts "Response: #{JSON.pretty_generate(result)}"
134-
puts
135134

136135
@session_id = nil
137136
result
@@ -140,10 +139,11 @@ def close_session
140139

141140
# Main script
142141
if __FILE__ == $PROGRAM_NAME
143-
puts "MCP HTTP Client Example"
144-
puts "Make sure the HTTP server is running (ruby examples/http_server.rb)"
145-
puts "=" * 50
146-
puts
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
147147

148148
client = MCPHTTPClient.new
149149

examples/http_server.rb

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,17 @@ def template(args, server_context:)
154154
end
155155

156156
# Start the server
157-
puts "Starting MCP HTTP server on http://localhost:9292"
158-
puts "Use POST requests to initialize and send JSON-RPC commands"
159-
puts "Example initialization:"
160-
puts ' 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"}}}\''
161-
puts ""
162-
puts "The server will return a session ID in the Mcp-Session-Id header."
163-
puts "Use this session ID for subsequent requests."
164-
puts ""
165-
puts "Press Ctrl+C to stop the server"
157+
puts <<~MESSAGE
158+
Starting MCP HTTP server on http://localhost:9292
159+
Use POST requests to initialize and send JSON-RPC commands
160+
Example initialization:
161+
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"}}}'
162+
163+
The server will return a session ID in the Mcp-Session-Id header.
164+
Use this session ID for subsequent requests.
165+
166+
Press Ctrl+C to stop the server
167+
MESSAGE
166168

167169
# Run the server
168170
# Use Rackup to run the server

examples/streamable_http_client.rb

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ def main
9292
end
9393

9494
puts "=== MCP SSE Test Client ==="
95-
puts ""
9695

9796
# Step 1: Initialize session
9897
logger.info("Initializing session...")
@@ -134,13 +133,16 @@ def main
134133

135134
# Step 3: Interactive menu
136135
loop do
137-
puts "\n=== Available Actions ==="
138-
puts "1. Send custom notification"
139-
puts "2. Test echo"
140-
puts "3. List tools"
141-
puts "0. Exit"
142-
puts ""
143-
print("Choose an action: ")
136+
puts <<~MESSAGE.chomp
137+
138+
=== Available Actions ===
139+
1. Send custom notification
140+
2. Test echo
141+
3. List tools
142+
0. Exit
143+
144+
Choose an action:#{" "}
145+
MESSAGE
144146

145147
choice = gets.chomp
146148

@@ -167,19 +169,15 @@ def main
167169
else
168170
logger.error("Error: #{response[:body]["error"]}")
169171
end
170-
171172
when "2"
172173
print("Enter message to echo: ")
173174
message = gets.chomp
174175
make_request(session_id, "tools/call", { name: "echo", arguments: { message: message } })
175-
176176
when "3"
177177
make_request(session_id, "tools/list")
178-
179178
when "0"
180179
logger.info("Exiting...")
181180
break
182-
183181
else
184182
puts "Invalid choice"
185183
end

0 commit comments

Comments
 (0)