Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions lib/model_context_protocol/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ def initialize(message, request, error_type: :internal_error, original_error: ni

include Instrumentation

attr_accessor :name, :version, :tools, :prompts, :resources, :server_context, :configuration, :capabilities
attr_writer :capabilities
attr_accessor :name, :version, :tools, :prompts, :resources, :server_context, :configuration

def initialize(
name: "model_context_protocol",
Expand All @@ -33,7 +34,7 @@ def initialize(
resource_templates: [],
server_context: nil,
configuration: nil,
capabilities: { prompts: {}, resources: {}, tools: {} }
capabilities: nil
)
@name = name
@version = version
Expand All @@ -44,7 +45,6 @@ def initialize(
@resource_index = index_resources_by_uri(resources)
@server_context = server_context
@configuration = ModelContextProtocol.configuration.merge(configuration)
@capabilities = capabilities

@handlers = {
Methods::RESOURCES_LIST => method(:list_resources),
Expand All @@ -64,6 +64,10 @@ def initialize(
}
end

def capabilities
@capabilities ||= determine_capabilities
end

def handle(request)
JsonRpcHandler.handle(request) do |method|
handle_request(request, method)
Expand Down Expand Up @@ -154,6 +158,18 @@ def handle_request(request, method)
}
end

def determine_capabilities
defines_prompts = @prompts.any? || @handlers[Methods::PROMPTS_LIST] != method(:list_prompts)
defines_tools = @tools.any? || @handlers[Methods::TOOLS_LIST] != method(:list_tools)
defines_resources = @resources.any? || @handlers[Methods::RESOURCES_LIST] != method(:list_resources)
defines_resource_templates = @resource_templates.any? || @handlers[Methods::RESOURCES_TEMPLATES_LIST] != method(:list_resource_templates)
{
prompts: defines_prompts ? {} : nil,
resources: defines_resources || defines_resource_templates ? {} : nil,
tools: defines_tools ? {} : nil,
}.compact
end

def server_info
@server_info ||= {
name:,
Expand Down
62 changes: 62 additions & 0 deletions test/model_context_protocol/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -739,5 +739,67 @@ class ServerTest < ActiveSupport::TestCase
response = server.handle(request)
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
end
end