diff --git a/lib/mcp/server.rb b/lib/mcp/server.rb index 98c5ac1..63a2cd4 100644 --- a/lib/mcp/server.rb +++ b/lib/mcp/server.rb @@ -3,7 +3,6 @@ require "json_rpc_handler" require_relative "instrumentation" require_relative "methods" -require_relative "server/capabilities" module MCP class Server @@ -23,8 +22,7 @@ def initialize(message, request, error_type: :internal_error, original_error: ni include Instrumentation - attr_writer :capabilities - attr_accessor :name, :version, :tools, :prompts, :resources, :server_context, :configuration, :transport + attr_accessor :name, :version, :tools, :prompts, :resources, :server_context, :configuration, :capabilities, :transport def initialize( name: "model_context_protocol", @@ -47,10 +45,7 @@ def initialize( @resource_index = index_resources_by_uri(resources) @server_context = server_context @configuration = MCP.configuration.merge(configuration) - @capabilities = Capabilities.new(capabilities) - @capabilities.support_tools if tools.any? - @capabilities.support_prompts if prompts.any? - @capabilities.support_resources if resources.any? || resource_templates.any? + @capabilities = capabilities || default_capabilities @handlers = { Methods::RESOURCES_LIST => method(:list_resources), @@ -72,10 +67,6 @@ def initialize( @transport = transport end - def capabilities - @capabilities.to_h - end - def handle(request) JsonRpcHandler.handle(request) do |method| handle_request(request, method) @@ -123,7 +114,6 @@ def notify_resources_list_changed end def resources_list_handler(&block) - @capabilities.support_resources @handlers[Methods::RESOURCES_LIST] = block end @@ -132,12 +122,10 @@ def resources_read_handler(&block) end def resources_templates_list_handler(&block) - @capabilities.support_resources @handlers[Methods::RESOURCES_TEMPLATES_LIST] = block end def tools_list_handler(&block) - @capabilities.support_tools @handlers[Methods::TOOLS_LIST] = block end @@ -146,7 +134,6 @@ def tools_call_handler(&block) end def prompts_list_handler(&block) - @capabilities.support_prompts @handlers[Methods::PROMPTS_LIST] = block end @@ -194,6 +181,14 @@ def handle_request(request, method) } end + def default_capabilities + { + tools: { listChanged: true }, + prompts: { listChanged: true }, + resources: { listChanged: true }, + } + end + def server_info @server_info ||= { name:, diff --git a/test/mcp/server/capabilities_test.rb b/test/mcp/server/capabilities_test.rb deleted file mode 100644 index 031b0e3..0000000 --- a/test/mcp/server/capabilities_test.rb +++ /dev/null @@ -1,197 +0,0 @@ -# frozen_string_literal: true - -require "test_helper" - -module MCP - class Server - class CapabilitiesTest < ActiveSupport::TestCase - test "can be initialized with a capabilities hash" do - capabilities = MCP::Server::Capabilities.new(prompts: {}) - - assert_equal({ prompts: {} }, capabilities.to_h) - end - - test "ignores unknown values in capabilities hash" do - capabilities = MCP::Server::Capabilities.new(prompts: {}, invalid: :value) - - assert_equal({ prompts: {} }, capabilities.to_h) - end - - test "support prompts" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_prompts - - assert_operator capabilities.to_h, :key?, :prompts - end - - test "support resources" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_resources - - assert_operator capabilities.to_h, :key?, :resources - end - - test "support tools" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_tools - - assert_operator capabilities.to_h, :key?, :tools - end - - test "support completions" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_completions - - assert_operator capabilities.to_h, :key?, :completions - assert_empty(capabilities.to_h[:completions]) - end - - test "support logging" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_logging - - assert_operator capabilities.to_h, :key?, :logging - assert_empty(capabilities.to_h[:logging]) - end - - test "support experimental with custom config" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_experimental({ myFeature: { enabled: true } }) - - assert_operator capabilities.to_h, :key?, :experimental - assert_equal({ myFeature: { enabled: true } }, capabilities.to_h[:experimental]) - end - - test "support prompts list changed" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_prompts_list_changed - - assert_equal({ prompts: { listChanged: true } }, capabilities.to_h) - end - - test "support resources list changed" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_resources_list_changed - - assert_equal({ resources: { listChanged: true } }, capabilities.to_h) - end - - test "support resources subscribe" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_resources_subscribe - - assert_equal({ resources: { subscribe: true } }, capabilities.to_h) - end - - test "support resources with both list changed and subscribe" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_resources_list_changed - capabilities.support_resources_subscribe - - assert_equal({ resources: { listChanged: true, subscribe: true } }, capabilities.to_h) - end - - test "support tools list changed" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_tools_list_changed - - assert_equal({ tools: { listChanged: true } }, capabilities.to_h) - end - - test "initializes with prompts list changed from hash" do - capabilities = MCP::Server::Capabilities.new(prompts: { listChanged: true }) - - assert_equal({ prompts: { listChanged: true } }, capabilities.to_h) - end - - test "initializes with resources list changed and subscribe from hash" do - capabilities = MCP::Server::Capabilities.new(resources: { listChanged: true, subscribe: true }) - - assert_equal({ resources: { listChanged: true, subscribe: true } }, capabilities.to_h) - end - - test "initializes with tools list changed from hash" do - capabilities = MCP::Server::Capabilities.new(tools: { listChanged: true }) - - assert_equal({ tools: { listChanged: true } }, capabilities.to_h) - end - - test "initializes with completions from hash" do - capabilities = MCP::Server::Capabilities.new(completions: {}) - - assert_equal({ completions: {} }, capabilities.to_h) - end - - test "initializes with logging from hash" do - capabilities = MCP::Server::Capabilities.new(logging: {}) - - assert_equal({ logging: {} }, capabilities.to_h) - end - - test "initializes with experimental config from hash" do - capabilities = MCP::Server::Capabilities.new(experimental: { feature1: { enabled: true }, feature2: "config" }) - - assert_equal({ experimental: { feature1: { enabled: true }, feature2: "config" } }, capabilities.to_h) - end - - test "initializes with all capabilities from hash" do - capabilities = MCP::Server::Capabilities.new( - completions: {}, - experimental: { customFeature: true }, - logging: {}, - prompts: { listChanged: true }, - resources: { listChanged: true, subscribe: true }, - tools: { listChanged: true }, - ) - - expected = { - completions: {}, - experimental: { customFeature: true }, - logging: {}, - prompts: { listChanged: true }, - resources: { listChanged: true, subscribe: true }, - tools: { listChanged: true }, - } - - assert_equal(expected, capabilities.to_h) - end - - test "handles nil values in capabilities hash gracefully" do - capabilities = MCP::Server::Capabilities.new( - prompts: nil, - resources: nil, - tools: nil, - ) - - assert_equal({ prompts: {}, resources: {}, tools: {} }, capabilities.to_h) - end - - test "to_h returns empty hash when no capabilities are set" do - capabilities = MCP::Server::Capabilities.new - - assert_empty(capabilities.to_h) - end - - test "calling support methods multiple times does not duplicate entries" do - capabilities = MCP::Server::Capabilities.new - - capabilities.support_prompts - capabilities.support_prompts - capabilities.support_prompts_list_changed - - assert_equal({ prompts: { listChanged: true } }, capabilities.to_h) - end - end - end -end diff --git a/test/mcp/server_test.rb b/test/mcp/server_test.rb index 267f4ca..45c5e20 100644 --- a/test/mcp/server_test.rb +++ b/test/mcp/server_test.rb @@ -72,9 +72,9 @@ class ServerTest < ActiveSupport::TestCase response = @server.handle(request) assert_equal( { - "jsonrpc": "2.0", - "id": "123", - "result": {}, + jsonrpc: "2.0", + id: "123", + result: {}, }, response, ) @@ -91,9 +91,9 @@ class ServerTest < ActiveSupport::TestCase response = JSON.parse(@server.handle_json(request), symbolize_names: true) assert_equal( { - "jsonrpc": "2.0", - "id": "123", - "result": {}, + jsonrpc: "2.0", + id: "123", + result: {}, }, response, ) @@ -111,18 +111,18 @@ class ServerTest < ActiveSupport::TestCase refute_nil response expected_result = { - "jsonrpc": "2.0", - "id": 1, - "result": { - "protocolVersion": "2024-11-05", - "capabilities": { - "prompts": {}, - "resources": {}, - "tools": {}, + jsonrpc: "2.0", + id: 1, + result: { + protocolVersion: "2024-11-05", + capabilities: { + prompts: { listChanged: true }, + resources: { listChanged: true }, + tools: { listChanged: true }, }, - "serverInfo": { - "name": @server_name, - "version": "1.2.3", + serverInfo: { + name: @server_name, + version: "1.2.3", }, }, } @@ -778,68 +778,6 @@ def call(message:, server_context: nil) assert_equal custom_version, response[:result][:protocolVersion] end - test "has tool capability only if tools or a tools_list_handler is defined" do - server_with_tools = Server.new(name: "test_server", tools: [@tool]) - - assert_includes server_with_tools.capabilities, :tools - - server_with_handler = Server.new(name: "test_server") - server_with_handler.tools_list_handler do - [{ name: "test_tool", description: "Test tool" }] - end - - assert_includes server_with_handler.capabilities, :tools - - server_without_tools = Server.new(name: "test_server") - - refute_includes server_without_tools.capabilities, :tools - end - - test "has prompt capability only if prompts or a prompts_list_handler is defined" do - server_with_prompts = Server.new(name: "test_server", prompts: [@prompt]) - - assert_includes server_with_prompts.capabilities, :prompts - - server_with_handler = Server.new(name: "test_server") - server_with_handler.prompts_list_handler do - [{ name: "test_prompt", description: "Test prompt" }] - end - - assert_includes server_with_handler.capabilities, :prompts - - server_without_prompts = Server.new(name: "test_server") - - refute_includes server_without_prompts.capabilities, :prompts - end - - test "has resources capability only if resources, template or custom handler is defined" do - server_with_resources = Server.new(name: "test_server", resources: [@resource]) - - assert_includes server_with_resources.capabilities, :resources - - server_with_resource_template = Server.new(name: "test_server", resource_templates: [@resource_template]) - - assert_includes server_with_resource_template.capabilities, :resources - - server_with_resources_list_handler = Server.new(name: "test_server") - server_with_resources_list_handler.resources_list_handler do - [{ uri: "test_resource", name: "Test resource", description: "Test resource" }] - end - - assert_includes server_with_resources_list_handler.capabilities, :resources - - server_with_resources_templates_list_handler = Server.new(name: "test_server") - server_with_resources_templates_list_handler.resources_templates_list_handler do - [{ uri_template: "test_resource/{id}", name: "Test resource", description: "Test resource" }] - end - - assert_includes server_with_resources_templates_list_handler.capabilities, :resources - - server_without_resources = Server.new(name: "test_server") - - refute_includes server_without_resources.capabilities, :resources - end - test "tools/call validates arguments against input schema when validate_tool_call_arguments is true" do server = Server.new( tools: [TestTool],