Add logging support
A server can send structured logging messages to the client. https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging#logging Logging was specified in the 2024-11-05 specification, but since it was not supported in ruby-sdk, I implemented it. https://modelcontextprotocol.io/specification/2024-11-05/server/utilities/logging I also made it possible to output a simple notification message in the examples.
This commit is contained in:
parent
830913be5a
commit
afa2fb4077
3 changed files with 434 additions and 7 deletions
|
|
@ -105,6 +105,8 @@ module JsonRpcHandler
|
|||
result = method.call(params)
|
||||
|
||||
success_response(id: id, result: result)
|
||||
rescue MCP::Server::RequestHandlerError => e
|
||||
handle_request_error(e, id, id_validation_pattern)
|
||||
rescue StandardError => e
|
||||
error_response(id: id, id_validation_pattern: id_validation_pattern, error: {
|
||||
code: ErrorCode::INTERNAL_ERROR,
|
||||
|
|
@ -114,6 +116,24 @@ module JsonRpcHandler
|
|||
end
|
||||
end
|
||||
|
||||
def handle_request_error(error, id, id_validation_pattern)
|
||||
error_type = error.respond_to?(:error_type) ? error.error_type : nil
|
||||
|
||||
code, message = case error_type
|
||||
when :invalid_request then [ErrorCode::INVALID_REQUEST, "Invalid Request"]
|
||||
when :invalid_params then [ErrorCode::INVALID_PARAMS, "Invalid params"]
|
||||
when :parse_error then [ErrorCode::PARSE_ERROR, "Parse error"]
|
||||
when :internal_error then [ErrorCode::INTERNAL_ERROR, "Internal error"]
|
||||
else [ErrorCode::INTERNAL_ERROR, "Internal error"]
|
||||
end
|
||||
|
||||
error_response(id: id, id_validation_pattern: id_validation_pattern, error: {
|
||||
code: code,
|
||||
message: message,
|
||||
data: error.message,
|
||||
})
|
||||
end
|
||||
|
||||
def valid_version?(version)
|
||||
version == Version::V2_0
|
||||
end
|
||||
|
|
|
|||
32
lib/mcp/logging_message_notification.rb
Normal file
32
lib/mcp/logging_message_notification.rb
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "json_rpc_handler"
|
||||
|
||||
module MCP
|
||||
class LoggingMessageNotification
|
||||
LOG_LEVEL_SEVERITY = {
|
||||
"debug" => 0,
|
||||
"info" => 1,
|
||||
"notice" => 2,
|
||||
"warning" => 3,
|
||||
"error" => 4,
|
||||
"critical" => 5,
|
||||
"alert" => 6,
|
||||
"emergency" => 7,
|
||||
}.freeze
|
||||
|
||||
def initialize(level:)
|
||||
@level = level
|
||||
end
|
||||
|
||||
def valid_level?
|
||||
LOG_LEVEL_SEVERITY.keys.include?(@level)
|
||||
end
|
||||
|
||||
def should_notify?(log_level)
|
||||
return false unless LOG_LEVEL_SEVERITY.key?(log_level)
|
||||
|
||||
LOG_LEVEL_SEVERITY[log_level] >= LOG_LEVEL_SEVERITY[@level]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
require_relative "../json_rpc_handler"
|
||||
require_relative "instrumentation"
|
||||
require_relative "methods"
|
||||
require_relative "logging_message_notification"
|
||||
|
||||
module MCP
|
||||
class ToolNotUnique < StandardError
|
||||
|
|
@ -40,7 +41,7 @@ module MCP
|
|||
|
||||
include Instrumentation
|
||||
|
||||
attr_accessor :description, :icons, :name, :title, :version, :website_url, :instructions, :tools, :prompts, :resources, :server_context, :configuration, :capabilities, :transport
|
||||
attr_accessor :description, :icons, :name, :title, :version, :website_url, :instructions, :tools, :prompts, :resources, :server_context, :configuration, :capabilities, :transport, :logging_message_notification
|
||||
|
||||
def initialize(
|
||||
description: nil,
|
||||
|
|
@ -78,6 +79,7 @@ module MCP
|
|||
validate!
|
||||
|
||||
@capabilities = capabilities || default_capabilities
|
||||
@logging_message_notification = nil
|
||||
|
||||
@handlers = {
|
||||
Methods::RESOURCES_LIST => method(:list_resources),
|
||||
|
|
@ -90,12 +92,12 @@ module MCP
|
|||
Methods::INITIALIZE => method(:init),
|
||||
Methods::PING => ->(_) { {} },
|
||||
Methods::NOTIFICATIONS_INITIALIZED => ->(_) {},
|
||||
Methods::LOGGING_SET_LEVEL => method(:configure_logging_level),
|
||||
|
||||
# No op handlers for currently unsupported methods
|
||||
Methods::RESOURCES_SUBSCRIBE => ->(_) {},
|
||||
Methods::RESOURCES_UNSUBSCRIBE => ->(_) {},
|
||||
Methods::COMPLETION_COMPLETE => ->(_) {},
|
||||
Methods::LOGGING_SET_LEVEL => ->(_) {},
|
||||
Methods::ELICITATION_CREATE => ->(_) {},
|
||||
}
|
||||
@transport = transport
|
||||
|
|
@ -162,6 +164,18 @@ module MCP
|
|||
report_exception(e, { notification: "resources_list_changed" })
|
||||
end
|
||||
|
||||
def notify_log_message(data:, level:, logger: nil)
|
||||
return unless @transport
|
||||
return unless logging_message_notification&.should_notify?(level)
|
||||
|
||||
params = { "data" => data, "level" => level }
|
||||
params["logger"] = logger if logger
|
||||
|
||||
@transport.send_notification(Methods::NOTIFICATIONS_MESSAGE, params)
|
||||
rescue => e
|
||||
report_exception(e, { notification: "log_message" })
|
||||
end
|
||||
|
||||
def resources_list_handler(&block)
|
||||
@handlers[Methods::RESOURCES_LIST] = block
|
||||
end
|
||||
|
|
@ -284,6 +298,7 @@ module MCP
|
|||
tools: { listChanged: true },
|
||||
prompts: { listChanged: true },
|
||||
resources: { listChanged: true },
|
||||
logging: {},
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -307,6 +322,19 @@ module MCP
|
|||
}.compact
|
||||
end
|
||||
|
||||
def configure_logging_level(request)
|
||||
if capabilities[:logging].nil?
|
||||
raise RequestHandlerError.new("Server does not support logging", request, error_type: :internal_error)
|
||||
end
|
||||
|
||||
logging_message_notification = LoggingMessageNotification.new(level: request[:level])
|
||||
unless logging_message_notification.valid_level?
|
||||
raise RequestHandlerError.new("Invalid log level #{request[:level]}", request, error_type: :invalid_params)
|
||||
end
|
||||
|
||||
@logging_message_notification = logging_message_notification
|
||||
end
|
||||
|
||||
def list_tools(request)
|
||||
@tools.values.map(&:to_h)
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue