mcp 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ module MCP
6
6
  def initialize(capabilities_hash = nil)
7
7
  @completions = nil
8
8
  @experimental = nil
9
+ @extensions = nil
9
10
  @logging = nil
10
11
  @prompts = nil
11
12
  @resources = nil
@@ -14,6 +15,7 @@ module MCP
14
15
  if capabilities_hash
15
16
  support_completions if capabilities_hash.key?(:completions)
16
17
  support_experimental(capabilities_hash[:experimental]) if capabilities_hash.key?(:experimental)
18
+ support_extensions(capabilities_hash[:extensions]) if capabilities_hash.key?(:extensions)
17
19
  support_logging if capabilities_hash.key?(:logging)
18
20
 
19
21
  if capabilities_hash.key?(:prompts)
@@ -45,6 +47,17 @@ module MCP
45
47
  @experimental = config || {}
46
48
  end
47
49
 
50
+ # Declares support for capability extensions per SEP-2133. Keys are
51
+ # extension identifiers using the reverse-DNS prefix convention
52
+ # (e.g. `"io.modelcontextprotocol/tasks"`, `"com.example/feature"`);
53
+ # values are arbitrary extension-defined configuration objects,
54
+ # with an empty hash meaning "supported with no settings".
55
+ # Repeated calls merge, so several extensions can be declared independently.
56
+ # https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133
57
+ def support_extensions(extensions = {})
58
+ @extensions = (@extensions || {}).merge(extensions || {})
59
+ end
60
+
48
61
  def support_logging
49
62
  @logging ||= {}
50
63
  end
@@ -85,6 +98,7 @@ module MCP
85
98
  {
86
99
  completions: @completions,
87
100
  experimental: @experimental,
101
+ extensions: @extensions,
88
102
  logging: @logging,
89
103
  prompts: @prompts,
90
104
  resources: @resources,
@@ -85,8 +85,10 @@ module MCP
85
85
  end
86
86
 
87
87
  def send_notification(method, params = nil, session_id: nil, related_request_id: nil)
88
- # Stateless mode doesn't support notifications
89
- raise "Stateless mode does not support notifications" if @stateless
88
+ # Stateless mode has no streams to deliver notifications on. Report non-delivery instead of raising
89
+ # so the ephemeral per-request session's notify_* helpers (e.g. progress or log notifications from
90
+ # a tool handler) degrade gracefully rather than spamming the exception reporter on every call.
91
+ return false if @stateless
90
92
 
91
93
  notification = {
92
94
  jsonrpc: "2.0",
@@ -389,7 +391,11 @@ module MCP
389
391
  end
390
392
  rescue StandardError => e
391
393
  MCP.configuration.exception_reporter.call(e, { request: body_string })
392
- [500, { "Content-Type" => "application/json" }, [{ error: "Internal server error" }.to_json]]
394
+ json_rpc_error_response(
395
+ status: 500,
396
+ code: JsonRpcHandler::ErrorCode::INTERNAL_ERROR,
397
+ message: "Internal server error",
398
+ )
393
399
  end
394
400
 
395
401
  def handle_get(request)
@@ -513,19 +519,19 @@ module MCP
513
519
  media_type = content_type&.split(";")&.first&.strip&.downcase
514
520
  return if media_type == "application/json"
515
521
 
516
- [
517
- 415,
518
- { "Content-Type" => "application/json" },
519
- [{ error: "Unsupported Media Type: Content-Type must be application/json" }.to_json],
520
- ]
522
+ json_rpc_error_response(
523
+ status: 415,
524
+ code: JsonRpcHandler::ErrorCode::INVALID_REQUEST,
525
+ message: "Unsupported Media Type: Content-Type must be application/json",
526
+ )
521
527
  end
522
528
 
523
529
  def not_acceptable_response(required_types)
524
- [
525
- 406,
526
- { "Content-Type" => "application/json" },
527
- [{ error: "Not Acceptable: Accept header must include #{required_types.join(" and ")}" }.to_json],
528
- ]
530
+ json_rpc_error_response(
531
+ status: 406,
532
+ code: JsonRpcHandler::ErrorCode::INVALID_REQUEST,
533
+ message: "Not Acceptable: Accept header must include #{required_types.join(" and ")}",
534
+ )
529
535
  end
530
536
 
531
537
  def parse_request_body(body_string)
@@ -535,7 +541,11 @@ module MCP
535
541
  end
536
542
 
537
543
  def invalid_json_response
538
- [400, { "Content-Type" => "application/json" }, [{ error: "Invalid JSON" }.to_json]]
544
+ json_rpc_error_response(
545
+ status: 400,
546
+ code: JsonRpcHandler::ErrorCode::PARSE_ERROR,
547
+ message: "Parse error: Invalid JSON",
548
+ )
539
549
  end
540
550
 
541
551
  def initialize_request?(body)
@@ -543,20 +553,20 @@ module MCP
543
553
  end
544
554
 
545
555
  def validate_protocol_version_header(request)
546
- header_value = request.env["HTTP_MCP_PROTOCOL_VERSION"]
547
- return if header_value.nil?
556
+ header_value = request.env["HTTP_MCP_PROTOCOL_VERSION"] || MCP::Configuration::DEFAULT_NEGOTIATED_PROTOCOL_VERSION
548
557
  return if MCP::Configuration::SUPPORTED_STABLE_PROTOCOL_VERSIONS.include?(header_value)
549
558
 
550
559
  supported = MCP::Configuration::SUPPORTED_STABLE_PROTOCOL_VERSIONS.join(", ")
551
- body = {
552
- jsonrpc: "2.0",
553
- id: nil,
554
- error: {
555
- code: JsonRpcHandler::ErrorCode::INVALID_REQUEST,
556
- message: "Bad Request: Unsupported protocol version: #{header_value}. Supported versions: #{supported}",
557
- },
558
- }
559
- [400, { "Content-Type" => "application/json" }, [body.to_json]]
560
+ json_rpc_error_response(
561
+ status: 400,
562
+ code: JsonRpcHandler::ErrorCode::INVALID_REQUEST,
563
+ message: "Bad Request: Unsupported protocol version: #{header_value}. Supported versions: #{supported}",
564
+ )
565
+ end
566
+
567
+ def json_rpc_error_response(status:, code:, message:)
568
+ body = { jsonrpc: "2.0", id: nil, error: { code: code, message: message } }
569
+ [status, { "Content-Type" => "application/json" }, [body.to_json]]
560
570
  end
561
571
 
562
572
  def notification?(body)
@@ -567,7 +577,9 @@ module MCP
567
577
  # `notifications/initialized`) through the server so it can update session state.
568
578
  def dispatch_notification(body_string, session_id)
569
579
  server_session = nil
570
- if session_id && !@stateless
580
+ if @stateless
581
+ server_session = ephemeral_session
582
+ elsif session_id
571
583
  @mutex.synchronize do
572
584
  session = @sessions[session_id]
573
585
  server_session = session[:server_session] if session
@@ -603,9 +615,10 @@ module MCP
603
615
 
604
616
  def handle_initialization(body_string, body)
605
617
  session_id = nil
606
- server_session = nil
607
618
 
608
- unless @stateless
619
+ if @stateless
620
+ server_session = ephemeral_session
621
+ else
609
622
  session_id = SecureRandom.uuid
610
623
  server_session = ServerSession.new(server: @server, transport: self, session_id: session_id)
611
624
 
@@ -618,17 +631,13 @@ module MCP
618
631
  end
619
632
  end
620
633
 
621
- response = if server_session
622
- server_session.handle_json(body_string)
623
- else
624
- @server.handle_json(body_string)
625
- end
634
+ response = server_session.handle_json(body_string)
626
635
 
627
636
  # If `Server#init` produced an error response (e.g., malformed JSON-RPC envelope),
628
637
  # `mark_initialized!` was never called. Discard the orphaned session and omit
629
638
  # the `Mcp-Session-Id` header so the client retries from a clean state instead of
630
639
  # reusing a never-initialized ID that would later look like a duplicate `initialize`.
631
- if server_session && !server_session.initialized?
640
+ if session_id && !server_session.initialized?
632
641
  cleanup_session(session_id)
633
642
  session_id = nil
634
643
  end
@@ -649,15 +658,15 @@ module MCP
649
658
  def handle_regular_request(body_string, session_id, related_request_id: nil)
650
659
  server_session = nil
651
660
 
652
- unless @stateless
653
- if session_id
654
- error_response = validate_and_touch_session(session_id)
655
- return error_response if error_response
661
+ if @stateless
662
+ server_session = ephemeral_session
663
+ elsif session_id
664
+ error_response = validate_and_touch_session(session_id)
665
+ return error_response if error_response
656
666
 
657
- @mutex.synchronize do
658
- session = @sessions[session_id]
659
- server_session = session[:server_session] if session
660
- end
667
+ @mutex.synchronize do
668
+ session = @sessions[session_id]
669
+ server_session = session[:server_session] if session
661
670
  end
662
671
  end
663
672
 
@@ -767,6 +776,13 @@ module MCP
767
776
  @mutex.synchronize { @sessions.key?(session_id) }
768
777
  end
769
778
 
779
+ # Each stateless POST is self-contained (SEP-2567): handlers run against an ephemeral per-request `ServerSession`
780
+ # so client info, logging level, and initialized state never leak onto the shared `Server` instance or across concurrent requests.
781
+ # https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2567
782
+ def ephemeral_session
783
+ ServerSession.new(server: @server, transport: self, session_id: nil)
784
+ end
785
+
770
786
  # Returns true iff a session exists and is not past its idle timeout. Expired sessions
771
787
  # are evicted as a side effect so a live request never observes a zombie session that
772
788
  # the reaper hasn't yet pruned. Does NOT update `last_active_at`; callers that are
@@ -793,15 +809,27 @@ module MCP
793
809
  end
794
810
 
795
811
  def method_not_allowed_response
796
- [405, { "Content-Type" => "application/json" }, [{ error: "Method not allowed" }.to_json]]
812
+ json_rpc_error_response(
813
+ status: 405,
814
+ code: JsonRpcHandler::ErrorCode::INVALID_REQUEST,
815
+ message: "Method not allowed",
816
+ )
797
817
  end
798
818
 
799
819
  def missing_session_id_response
800
- [400, { "Content-Type" => "application/json" }, [{ error: "Missing session ID" }.to_json]]
820
+ json_rpc_error_response(
821
+ status: 400,
822
+ code: JsonRpcHandler::ErrorCode::INVALID_REQUEST,
823
+ message: "Missing session ID",
824
+ )
801
825
  end
802
826
 
803
827
  def session_not_found_response
804
- [404, { "Content-Type" => "application/json" }, [{ error: "Session not found" }.to_json]]
828
+ json_rpc_error_response(
829
+ status: 404,
830
+ code: JsonRpcHandler::ErrorCode::INVALID_REQUEST,
831
+ message: "Session not found",
832
+ )
805
833
  end
806
834
 
807
835
  def already_initialized_response(request_id)
@@ -821,11 +849,11 @@ module MCP
821
849
  end
822
850
 
823
851
  def session_already_connected_response
824
- [
825
- 409,
826
- { "Content-Type" => "application/json" },
827
- [{ error: "Conflict: Only one SSE stream is allowed per session" }.to_json],
828
- ]
852
+ json_rpc_error_response(
853
+ status: 409,
854
+ code: JsonRpcHandler::ErrorCode::INVALID_REQUEST,
855
+ message: "Conflict: Only one SSE stream is allowed per session",
856
+ )
829
857
  end
830
858
 
831
859
  def setup_sse_stream(session_id)
data/lib/mcp/server.rb CHANGED
@@ -8,6 +8,7 @@ require_relative "methods"
8
8
  require_relative "logging_message_notification"
9
9
  require_relative "progress"
10
10
  require_relative "server_context"
11
+ require_relative "server/capabilities"
11
12
  require_relative "server/pagination"
12
13
  require_relative "server/transports"
13
14
 
@@ -58,6 +59,32 @@ module MCP
58
59
  end
59
60
  end
60
61
 
62
+ # Raised when a requested resource URI does not exist. Per SEP-2164,
63
+ # resource-not-found errors use the standard JSON-RPC Invalid Params code (-32602)
64
+ # with the requested URI in the error `data` member. Raise this from
65
+ # a `resources_read_handler` block for unknown URIs:
66
+ #
67
+ # server.resources_read_handler do |params|
68
+ # raise MCP::Server::ResourceNotFoundError.new(params[:uri], params) unless known?(params[:uri])
69
+ # do_something(params[:uri])
70
+ # end
71
+ #
72
+ # https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2164
73
+ class ResourceNotFoundError < RequestHandlerError
74
+ def initialize(uri, request = nil)
75
+ # The explicit `error_code` keeps the descriptive message in the JSON-RPC
76
+ # error response; `error_type: :invalid_params` alone would replace it
77
+ # with the generic "Invalid params" string.
78
+ super(
79
+ "Resource not found: #{uri}",
80
+ request,
81
+ error_type: :invalid_params,
82
+ error_code: JsonRpcHandler::ErrorCode::INVALID_PARAMS,
83
+ error_data: { uri: uri },
84
+ )
85
+ end
86
+ end
87
+
61
88
  class MethodAlreadyDefinedError < StandardError
62
89
  attr_reader :method_name
63
90
 
@@ -116,7 +143,12 @@ module MCP
116
143
 
117
144
  validate!
118
145
 
119
- @capabilities = capabilities || default_capabilities
146
+ # Accept either a plain Hash or an `MCP::Server::Capabilities` builder.
147
+ @capabilities = if capabilities.is_a?(Capabilities)
148
+ capabilities.to_h
149
+ else
150
+ capabilities || default_capabilities
151
+ end
120
152
  @client_capabilities = nil
121
153
  @logging_message_notification = nil
122
154
 
@@ -784,6 +816,10 @@ module MCP
784
816
  end
785
817
 
786
818
  def call_tool_with_args(tool, arguments, context, progress_token: nil, session: nil, related_request_id: nil, cancellation: nil)
819
+ # Transports parse incoming JSON with `symbolize_names: true`, so `arguments` already arrives symbolized
820
+ # at every nesting level. This top-level transform only guards callers that hand in string-keyed top-level arguments;
821
+ # it does not recurse, and nested object keys remain symbols. Tools therefore receive symbol keys all the way down.
822
+ # See docs/building-servers.md ("Tool argument keys").
787
823
  args = arguments&.transform_keys(&:to_sym) || {}
788
824
 
789
825
  if accepts_server_context?(tool.method(:call))
@@ -846,7 +882,7 @@ module MCP
846
882
  uri = ref[:uri]
847
883
  found = @resource_index.key?(uri) || @resource_templates.any? { |t| t.uri_template == uri }
848
884
  unless found
849
- raise RequestHandlerError.new("Resource not found: #{uri}", params, error_type: :invalid_params)
885
+ raise ResourceNotFoundError.new(uri, params)
850
886
  end
851
887
  else
852
888
  raise RequestHandlerError.new("Invalid ref type: #{ref[:type]}", params, error_type: :invalid_params)
@@ -12,9 +12,9 @@ module MCP
12
12
  end
13
13
 
14
14
  def missing_required_arguments(arguments)
15
- return [] unless schema[:required].is_a?(Array)
15
+ return [] unless @schema[:required].is_a?(Array)
16
16
 
17
- (schema[:required] - arguments.keys.map(&:to_s))
17
+ (@schema[:required] - arguments.keys.map(&:to_s))
18
18
  end
19
19
 
20
20
  def validate_arguments(arguments)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "digest"
4
- require "json-schema"
4
+ require "json_schemer"
5
5
 
6
6
  module MCP
7
7
  class Tool
@@ -38,11 +38,10 @@ module MCP
38
38
 
39
39
  # JSON Schema 2020-12 is the default dialect for MCP schema definitions
40
40
  # per MCP 2025-11-25 (SEP-1613). Note: emission only — runtime validation
41
- # is still performed against the JSON Schema draft-04 metaschema because
42
- # the `json-schema` gem does not yet support 2020-12.
41
+ # is still performed against the JSON Schema draft-04 metaschema.
43
42
  JSON_SCHEMA_2020_12_URI = "https://json-schema.org/draft/2020-12/schema"
44
43
 
45
- attr_reader :schema
44
+ DRAFT4_META_SCHEMA_URI = "http://json-schema.org/draft-04/schema#"
46
45
 
47
46
  def initialize(schema = {})
48
47
  @schema = JSON.parse(JSON.dump(schema), symbolize_names: true)
@@ -51,7 +50,7 @@ module MCP
51
50
  end
52
51
 
53
52
  def ==(other)
54
- other.is_a?(self.class) && schema == other.schema
53
+ other.is_a?(self.class) && @schema == other.instance_variable_get(:@schema)
55
54
  end
56
55
 
57
56
  def to_h
@@ -62,8 +61,38 @@ module MCP
62
61
 
63
62
  private
64
63
 
64
+ def stringify(obj)
65
+ case obj
66
+ when Hash
67
+ obj.each_with_object({}) { |(k, v), h| h[k.to_s] = stringify(v) }
68
+ when Array
69
+ obj.map { |v| stringify(v) }
70
+ when Symbol
71
+ obj.to_s
72
+ else
73
+ obj
74
+ end
75
+ end
76
+
77
+ # Lazily built so a cache hit in `validate_schema!` avoids the schemer construction cost.
78
+ # Memoized per Schema instance because schema content is fixed at construction,
79
+ # so the compiled schemer is reusable across many `fully_validate` calls.
80
+ #
81
+ # `format: false` preserves the legacy behavior of the previous `json-schema` based implementation,
82
+ # which did not enforce `format` keywords. `RegexpError` from a malformed `pattern` is re-raised as
83
+ # `ArgumentError` so callers see the same exception class they used to.
84
+ def schemer
85
+ @schemer ||= JSONSchemer.schema(
86
+ stringify(schema_for_validation),
87
+ meta_schema: DRAFT4_META_SCHEMA_URI,
88
+ format: false,
89
+ )
90
+ rescue RegexpError => e
91
+ raise ArgumentError, "Invalid JSON Schema: #{e.message}"
92
+ end
93
+
65
94
  def fully_validate(data)
66
- JSON::Validator.fully_validate(schema_for_validation, data)
95
+ schemer.validate(stringify(data)).map { |validation_error| validation_error.fetch("error") }
67
96
  end
68
97
 
69
98
  def validate_schema!
@@ -75,16 +104,7 @@ module MCP
75
104
  key = Digest::SHA256.hexdigest(JSON.generate(target, max_nesting: false))
76
105
  return if VALIDATION_CACHE.validated?(key)
77
106
 
78
- gem_path = File.realpath(Gem.loaded_specs["json-schema"].full_gem_path)
79
- schema_reader = JSON::Schema::Reader.new(
80
- accept_uri: false,
81
- accept_file: ->(path) { File.realpath(path.to_s).start_with?(gem_path) },
82
- )
83
- metaschema_path = Pathname.new(JSON::Validator.validator_for_name("draft4").metaschema)
84
- # Converts metaschema to a file URI for cross-platform compatibility
85
- metaschema_uri = JSON::Util::URI.file_uri(metaschema_path.expand_path.cleanpath.to_s.tr("\\", "/"))
86
- metaschema = metaschema_uri.to_s
87
- errors = JSON::Validator.fully_validate(metaschema, target, schema_reader: schema_reader)
107
+ errors = schemer.validate_schema.map { |validation_error| validation_error.fetch("error") }
88
108
  if errors.any?
89
109
  raise ArgumentError, "Invalid JSON Schema: #{errors.join(", ")}"
90
110
  end
@@ -92,9 +112,8 @@ module MCP
92
112
  VALIDATION_CACHE.store(key)
93
113
  end
94
114
 
95
- # The `json-schema` gem's draft-04 validator cannot resolve newer or unknown `$schema`
96
- # dialect URIs. Strip the top-level `$schema` before validation so a dialect URI
97
- # (whether SDK-injected by `to_h` or user-supplied) does not break the validator.
115
+ # `json_schemer` is pinned to the draft-04 metaschema, so strip top-level `$schema` before validation:
116
+ # this preserves the legacy behavior of ignoring the advertised dialect URI when the SDK validates schemas.
98
117
  def schema_for_validation
99
118
  return @schema unless @schema.key?(:"$schema")
100
119
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MCP
4
+ # Reserved `_meta` keys for W3C Trace Context propagation, per SEP-414.
5
+ #
6
+ # The MCP spec reserves the un-prefixed `_meta` keys `traceparent`, `tracestate`, and `baggage`
7
+ # (an explicit exception to the reverse-DNS prefix rule for `_meta` keys) so that clients and
8
+ # servers can propagate distributed-tracing context across MCP requests.
9
+ # The SDK guarantees these keys pass through incoming request `_meta` untouched; tool, prompt,
10
+ # and resource handlers can read them from `server_context[:_meta]` and bridge them to a tracing
11
+ # system such as the `opentelemetry-ruby` gems. The SDK itself does not depend on OpenTelemetry.
12
+ #
13
+ # - https://github.com/modelcontextprotocol/modelcontextprotocol/pull/414
14
+ # - https://www.w3.org/TR/trace-context/
15
+ # - https://www.w3.org/TR/baggage/
16
+ module TraceContext
17
+ TRACEPARENT_META_KEY = "traceparent"
18
+ TRACESTATE_META_KEY = "tracestate"
19
+ BAGGAGE_META_KEY = "baggage"
20
+
21
+ META_KEYS = [TRACEPARENT_META_KEY, TRACESTATE_META_KEY, BAGGAGE_META_KEY].freeze
22
+ end
23
+ end
data/lib/mcp/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MCP
4
- VERSION = "0.19.0"
4
+ VERSION = "0.21.0"
5
5
  end
data/lib/mcp.rb CHANGED
@@ -19,6 +19,7 @@ module MCP
19
19
  autoload :Server, "mcp/server"
20
20
  autoload :ServerSession, "mcp/server_session"
21
21
  autoload :Tool, "mcp/tool"
22
+ autoload :TraceContext, "mcp/trace_context"
22
23
 
23
24
  class << self
24
25
  def configure
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Model Context Protocol
@@ -10,19 +10,19 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: json-schema
13
+ name: json_schemer
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '4.1'
18
+ version: '2.4'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '4.1'
25
+ version: '2.4'
26
26
  description: The official Ruby SDK for Model Context Protocol servers and clients
27
27
  email:
28
28
  - mcp-support@anthropic.com
@@ -42,11 +42,13 @@ files:
42
42
  - lib/mcp/client.rb
43
43
  - lib/mcp/client/http.rb
44
44
  - lib/mcp/client/oauth.rb
45
+ - lib/mcp/client/oauth/client_credentials_provider.rb
45
46
  - lib/mcp/client/oauth/discovery.rb
46
47
  - lib/mcp/client/oauth/flow.rb
47
48
  - lib/mcp/client/oauth/in_memory_storage.rb
48
49
  - lib/mcp/client/oauth/pkce.rb
49
50
  - lib/mcp/client/oauth/provider.rb
51
+ - lib/mcp/client/oauth/storage_backed_provider.rb
50
52
  - lib/mcp/client/paginated_result.rb
51
53
  - lib/mcp/client/stdio.rb
52
54
  - lib/mcp/client/tool.rb
@@ -80,6 +82,7 @@ files:
80
82
  - lib/mcp/tool/output_schema.rb
81
83
  - lib/mcp/tool/response.rb
82
84
  - lib/mcp/tool/schema.rb
85
+ - lib/mcp/trace_context.rb
83
86
  - lib/mcp/transport.rb
84
87
  - lib/mcp/version.rb
85
88
  homepage: https://ruby.sdk.modelcontextprotocol.io
@@ -87,7 +90,7 @@ licenses:
87
90
  - Apache-2.0
88
91
  metadata:
89
92
  allowed_push_host: https://rubygems.org
90
- changelog_uri: https://github.com/modelcontextprotocol/ruby-sdk/releases/tag/v0.19.0
93
+ changelog_uri: https://github.com/modelcontextprotocol/ruby-sdk/releases/tag/v0.21.0
91
94
  homepage_uri: https://ruby.sdk.modelcontextprotocol.io
92
95
  source_code_uri: https://github.com/modelcontextprotocol/ruby-sdk
93
96
  bug_tracker_uri: https://github.com/modelcontextprotocol/ruby-sdk/issues