diff --git a/README.md b/README.md index b1b0ba0..81c2003 100644 --- a/README.md +++ b/README.md @@ -124,8 +124,8 @@ Notifications follow the JSON-RPC 2.0 specification and use these method names: #### Transport Support -- **HTTP Transport**: Notifications are sent as Server-Sent Events (SSE) to all connected sessions -- **Stdio Transport**: Notifications are sent as JSON-RPC 2.0 messages to stdout +- **stdio**: Notifications are sent as JSON-RPC 2.0 messages to stdout +- **Streamable HTTP**: Notifications are sent as JSON-RPC 2.0 messages over HTTP with streaming (chunked transfer or SSE) #### Usage Example @@ -456,6 +456,9 @@ Tools can include annotations that provide additional metadata about their behav Annotations can be set either through the class definition using the `annotations` class method or when defining a tool using the `define` method. +> [!NOTE] +> This **Tool Annotations** feature is supported starting from `protocol_version: '2025-03-26'`. + ### Tool Output Schemas 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: diff --git a/lib/mcp/server.rb b/lib/mcp/server.rb index b259620..2e8625a 100644 --- a/lib/mcp/server.rb +++ b/lib/mcp/server.rb @@ -59,10 +59,7 @@ module MCP @server_context = server_context @configuration = MCP.configuration.merge(configuration) - if @configuration.protocol_version == "2024-11-05" && @instructions - message = "`instructions` supported by protocol version 2025-03-26 or higher" - raise ArgumentError, message - end + validate! @capabilities = capabilities || default_capabilities @@ -102,6 +99,8 @@ module MCP def define_tool(name: nil, title: nil, description: nil, input_schema: nil, annotations: nil, &block) tool = Tool.define(name:, title:, description:, input_schema:, annotations:, &block) @tools[tool.name_value] = tool + + validate! end def define_prompt(name: nil, title: nil, description: nil, arguments: [], &block) @@ -171,6 +170,25 @@ module MCP private + def validate! + if @configuration.protocol_version == "2024-11-05" + if @instructions + message = "`instructions` supported by protocol version 2025-03-26 or higher" + raise ArgumentError, message + end + + error_tool_names = @tools.each_with_object([]) do |(tool_name, tool), error_tool_names| + if tool.annotations + error_tool_names << tool_name + end + end + unless error_tool_names.empty? + message = "Error occurred in #{error_tool_names.join(", ")}. `annotations` are supported by protocol version 2025-03-26 or higher" + raise ArgumentError, message + end + end + end + def handle_request(request, method) handler = @handlers[method] unless handler diff --git a/test/mcp/server_test.rb b/test/mcp/server_test.rb index c723472..48b9c14 100644 --- a/test/mcp/server_test.rb +++ b/test/mcp/server_test.rb @@ -822,6 +822,28 @@ module MCP assert_equal("`instructions` supported by protocol version 2025-03-26 or higher", exception.message) end + test "server uses annotations when configured with protocol version 2025-03-26" do + configuration = Configuration.new(protocol_version: "2025-03-26") + server = Server.new(name: "test_server", configuration: configuration) + server.define_tool( + name: "defined_tool", + annotations: { title: "test server" }, + ) + assert_equal({ destructiveHint: true, idempotentHint: false, openWorldHint: true, readOnlyHint: false, title: "test server" }, server.tools.first[1].annotations.to_h) + end + + test "raises error if annotations are used with protocol version 2024-11-05" do + configuration = Configuration.new(protocol_version: "2024-11-05") + exception = assert_raises(ArgumentError) do + server = Server.new(name: "test_server", configuration: configuration) + server.define_tool( + name: "defined_tool", + annotations: { title: "test server" }, + ) + end + assert_equal("Error occurred in defined_tool. `annotations` are supported by protocol version 2025-03-26 or higher", exception.message) + end + test "#define_tool adds a tool to the server" do @server.define_tool( name: "defined_tool",