mcp 0.10.0 → 0.15.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.
@@ -3,15 +3,16 @@
3
3
  module MCP
4
4
  class Prompt
5
5
  class Result
6
- attr_reader :description, :messages
6
+ attr_reader :description, :messages, :meta
7
7
 
8
- def initialize(description: nil, messages: [])
8
+ def initialize(description: nil, messages: [], meta: nil)
9
9
  @description = description
10
10
  @messages = messages
11
+ @meta = meta
11
12
  end
12
13
 
13
14
  def to_h
14
- { description: description, messages: messages.map(&:to_h) }.compact
15
+ { description: description, messages: messages.map(&:to_h), _meta: meta }.compact
15
16
  end
16
17
  end
17
18
  end
@@ -3,23 +3,24 @@
3
3
  module MCP
4
4
  class Resource
5
5
  class Contents
6
- attr_reader :uri, :mime_type
6
+ attr_reader :uri, :mime_type, :meta
7
7
 
8
- def initialize(uri:, mime_type: nil)
8
+ def initialize(uri:, mime_type: nil, meta: nil)
9
9
  @uri = uri
10
10
  @mime_type = mime_type
11
+ @meta = meta
11
12
  end
12
13
 
13
14
  def to_h
14
- { uri: uri, mimeType: mime_type }.compact
15
+ { uri: uri, mimeType: mime_type, _meta: meta }.compact
15
16
  end
16
17
  end
17
18
 
18
19
  class TextContents < Contents
19
20
  attr_reader :text
20
21
 
21
- def initialize(text:, uri:, mime_type:)
22
- super(uri: uri, mime_type: mime_type)
22
+ def initialize(text:, uri:, mime_type:, meta: nil)
23
+ super(uri: uri, mime_type: mime_type, meta: meta)
23
24
  @text = text
24
25
  end
25
26
 
@@ -31,8 +32,8 @@ module MCP
31
32
  class BlobContents < Contents
32
33
  attr_reader :data
33
34
 
34
- def initialize(data:, uri:, mime_type:)
35
- super(uri: uri, mime_type: mime_type)
35
+ def initialize(data:, uri:, mime_type:, meta: nil)
36
+ super(uri: uri, mime_type: mime_type, meta: meta)
36
37
  @data = data
37
38
  end
38
39
 
data/lib/mcp/resource.rb CHANGED
@@ -5,15 +5,16 @@ require_relative "resource/embedded"
5
5
 
6
6
  module MCP
7
7
  class Resource
8
- attr_reader :uri, :name, :title, :description, :icons, :mime_type
8
+ attr_reader :uri, :name, :title, :description, :icons, :mime_type, :meta
9
9
 
10
- def initialize(uri:, name:, title: nil, description: nil, icons: [], mime_type: nil)
10
+ def initialize(uri:, name:, title: nil, description: nil, icons: [], mime_type: nil, meta: nil)
11
11
  @uri = uri
12
12
  @name = name
13
13
  @title = title
14
14
  @description = description
15
15
  @icons = icons
16
16
  @mime_type = mime_type
17
+ @meta = meta
17
18
  end
18
19
 
19
20
  def to_h
@@ -24,6 +25,7 @@ module MCP
24
25
  description: description,
25
26
  icons: icons&.then { |icons| icons.empty? ? nil : icons.map(&:to_h) },
26
27
  mimeType: mime_type,
28
+ _meta: meta,
27
29
  }.compact
28
30
  end
29
31
  end
@@ -2,15 +2,16 @@
2
2
 
3
3
  module MCP
4
4
  class ResourceTemplate
5
- attr_reader :uri_template, :name, :title, :description, :icons, :mime_type
5
+ attr_reader :uri_template, :name, :title, :description, :icons, :mime_type, :meta
6
6
 
7
- def initialize(uri_template:, name:, title: nil, description: nil, icons: [], mime_type: nil)
7
+ def initialize(uri_template:, name:, title: nil, description: nil, icons: [], mime_type: nil, meta: nil)
8
8
  @uri_template = uri_template
9
9
  @name = name
10
10
  @title = title
11
11
  @description = description
12
12
  @icons = icons
13
13
  @mime_type = mime_type
14
+ @meta = meta
14
15
  end
15
16
 
16
17
  def to_h
@@ -21,6 +22,7 @@ module MCP
21
22
  description: description,
22
23
  icons: icons&.then { |icons| icons.empty? ? nil : icons.map(&:to_h) },
23
24
  mimeType: mime_type,
25
+ _meta: meta,
24
26
  }.compact
25
27
  end
26
28
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MCP
4
+ class Server
5
+ module Pagination
6
+ private
7
+
8
+ def cursor_from(request)
9
+ return if request.nil?
10
+
11
+ unless request.is_a?(Hash)
12
+ raise RequestHandlerError.new("Invalid params", request, error_type: :invalid_params)
13
+ end
14
+
15
+ request[:cursor]
16
+ end
17
+
18
+ def paginate(items, cursor:, page_size:, request:, &block)
19
+ start_index = 0
20
+
21
+ if cursor
22
+ unless cursor.is_a?(String)
23
+ raise RequestHandlerError.new("Invalid cursor", request, error_type: :invalid_params)
24
+ end
25
+
26
+ start_index = Integer(cursor, exception: false)
27
+ if start_index.nil? || start_index < 0 || start_index >= items.size
28
+ raise RequestHandlerError.new("Invalid cursor", request, error_type: :invalid_params)
29
+ end
30
+ end
31
+
32
+ end_index = page_size ? start_index + page_size : items.size
33
+ page = items[start_index...end_index]
34
+ page = page.map(&block) if block
35
+
36
+ result = { items: page }
37
+ result[:next_cursor] = end_index.to_s if end_index < items.size
38
+ result
39
+ end
40
+ end
41
+ end
42
+ end
@@ -53,6 +53,48 @@ module MCP
53
53
  MCP.configuration.exception_reporter.call(e, { error: "Failed to send notification" })
54
54
  false
55
55
  end
56
+
57
+ # NOTE: This signature deliberately matches the abstract `Transport#send_request` contract
58
+ # (`method, params = nil`) without the cancellation kwargs that `StreamableHTTPTransport#send_request` accepts.
59
+ # On Ruby 2.7 the project's supported minimum a method that mixes a positional `params` Hash with
60
+ # explicit keyword arguments cannot be called as `send_request(method, { ... })` - the trailing Hash would be
61
+ # auto-promoted to keyword arguments. Stdio is single-threaded and blocks on `$stdin.gets`, so nested-request
62
+ # cancellation has very limited value here regardless; servers that need cancellation propagation for nested
63
+ # server-to-client requests should use `StreamableHTTPTransport`.
64
+ def send_request(method, params = nil)
65
+ request_id = generate_request_id
66
+ request = { jsonrpc: "2.0", id: request_id, method: method }
67
+ request[:params] = params if params
68
+
69
+ begin
70
+ send_response(request)
71
+ rescue => e
72
+ MCP.configuration.exception_reporter.call(e, { error: "Failed to send request" })
73
+ raise
74
+ end
75
+
76
+ while @open && (line = $stdin.gets)
77
+ begin
78
+ parsed = JSON.parse(line.strip, symbolize_names: true)
79
+ rescue JSON::ParserError => e
80
+ MCP.configuration.exception_reporter.call(e, { error: "Failed to parse response" })
81
+ raise
82
+ end
83
+
84
+ if parsed[:id] == request_id && !parsed.key?(:method)
85
+ if parsed[:error]
86
+ raise StandardError, "Client returned an error for #{method} request (code: #{parsed[:error][:code]}): #{parsed[:error][:message]}"
87
+ end
88
+
89
+ return parsed[:result]
90
+ else
91
+ response = @session ? @session.handle(parsed) : @server.handle(parsed)
92
+ send_response(response) if response
93
+ end
94
+ end
95
+
96
+ raise "Transport closed while waiting for response to #{method} request."
97
+ end
56
98
  end
57
99
  end
58
100
  end