mcp 0.10.0 → 0.14.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.
- checksums.yaml +4 -4
- data/README.md +1267 -709
- data/lib/json_rpc_handler.rb +17 -10
- data/lib/mcp/client/http.rb +137 -15
- data/lib/mcp/client/paginated_result.rb +13 -0
- data/lib/mcp/client.rb +253 -66
- data/lib/mcp/configuration.rb +38 -2
- data/lib/mcp/content.rb +16 -12
- data/lib/mcp/instrumentation.rb +23 -2
- data/lib/mcp/methods.rb +4 -5
- data/lib/mcp/progress.rb +3 -1
- data/lib/mcp/prompt/result.rb +4 -3
- data/lib/mcp/resource/contents.rb +8 -7
- data/lib/mcp/resource.rb +4 -2
- data/lib/mcp/resource_template.rb +4 -2
- data/lib/mcp/server/pagination.rb +42 -0
- data/lib/mcp/server/transports/stdio_transport.rb +35 -0
- data/lib/mcp/server/transports/streamable_http_transport.rb +321 -88
- data/lib/mcp/server.rb +226 -46
- data/lib/mcp/server_context.rb +70 -2
- data/lib/mcp/server_session.rb +80 -7
- data/lib/mcp/tool/response.rb +4 -3
- data/lib/mcp/tool/schema.rb +1 -14
- data/lib/mcp/transport.rb +14 -0
- data/lib/mcp/version.rb +1 -1
- metadata +9 -5
data/lib/mcp/configuration.rb
CHANGED
|
@@ -7,11 +7,18 @@ module MCP
|
|
|
7
7
|
LATEST_STABLE_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05",
|
|
8
8
|
]
|
|
9
9
|
|
|
10
|
-
attr_writer :exception_reporter, :
|
|
10
|
+
attr_writer :exception_reporter, :around_request
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# @deprecated Use {#around_request=} instead. `instrumentation_callback`
|
|
13
|
+
# fires only after a request completes and cannot wrap execution in a
|
|
14
|
+
# surrounding block (e.g. for Application Performance Monitoring (APM) spans).
|
|
15
|
+
# @see #around_request=
|
|
16
|
+
attr_writer :instrumentation_callback
|
|
17
|
+
|
|
18
|
+
def initialize(exception_reporter: nil, around_request: nil, instrumentation_callback: nil, protocol_version: nil,
|
|
13
19
|
validate_tool_call_arguments: true)
|
|
14
20
|
@exception_reporter = exception_reporter
|
|
21
|
+
@around_request = around_request
|
|
15
22
|
@instrumentation_callback = instrumentation_callback
|
|
16
23
|
@protocol_version = protocol_version
|
|
17
24
|
if protocol_version
|
|
@@ -50,10 +57,24 @@ module MCP
|
|
|
50
57
|
!@exception_reporter.nil?
|
|
51
58
|
end
|
|
52
59
|
|
|
60
|
+
def around_request
|
|
61
|
+
@around_request || default_around_request
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def around_request?
|
|
65
|
+
!@around_request.nil?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @deprecated Use {#around_request} instead. `instrumentation_callback`
|
|
69
|
+
# fires only after a request completes and cannot wrap execution in a
|
|
70
|
+
# surrounding block (e.g. for Application Performance Monitoring (APM) spans).
|
|
71
|
+
# @see #around_request
|
|
53
72
|
def instrumentation_callback
|
|
54
73
|
@instrumentation_callback || default_instrumentation_callback
|
|
55
74
|
end
|
|
56
75
|
|
|
76
|
+
# @deprecated Use {#around_request?} instead.
|
|
77
|
+
# @see #around_request?
|
|
57
78
|
def instrumentation_callback?
|
|
58
79
|
!@instrumentation_callback.nil?
|
|
59
80
|
end
|
|
@@ -72,20 +93,30 @@ module MCP
|
|
|
72
93
|
else
|
|
73
94
|
@exception_reporter
|
|
74
95
|
end
|
|
96
|
+
|
|
97
|
+
around_request = if other.around_request?
|
|
98
|
+
other.around_request
|
|
99
|
+
else
|
|
100
|
+
@around_request
|
|
101
|
+
end
|
|
102
|
+
|
|
75
103
|
instrumentation_callback = if other.instrumentation_callback?
|
|
76
104
|
other.instrumentation_callback
|
|
77
105
|
else
|
|
78
106
|
@instrumentation_callback
|
|
79
107
|
end
|
|
108
|
+
|
|
80
109
|
protocol_version = if other.protocol_version?
|
|
81
110
|
other.protocol_version
|
|
82
111
|
else
|
|
83
112
|
@protocol_version
|
|
84
113
|
end
|
|
114
|
+
|
|
85
115
|
validate_tool_call_arguments = other.validate_tool_call_arguments
|
|
86
116
|
|
|
87
117
|
Configuration.new(
|
|
88
118
|
exception_reporter: exception_reporter,
|
|
119
|
+
around_request: around_request,
|
|
89
120
|
instrumentation_callback: instrumentation_callback,
|
|
90
121
|
protocol_version: protocol_version,
|
|
91
122
|
validate_tool_call_arguments: validate_tool_call_arguments,
|
|
@@ -111,6 +142,11 @@ module MCP
|
|
|
111
142
|
@default_exception_reporter ||= ->(exception, server_context) {}
|
|
112
143
|
end
|
|
113
144
|
|
|
145
|
+
def default_around_request
|
|
146
|
+
@default_around_request ||= ->(_data, &request_handler) { request_handler.call }
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @deprecated Use {#default_around_request} instead.
|
|
114
150
|
def default_instrumentation_callback
|
|
115
151
|
@default_instrumentation_callback ||= ->(data) {}
|
|
116
152
|
end
|
data/lib/mcp/content.rb
CHANGED
|
@@ -3,56 +3,60 @@
|
|
|
3
3
|
module MCP
|
|
4
4
|
module Content
|
|
5
5
|
class Text
|
|
6
|
-
attr_reader :text, :annotations
|
|
6
|
+
attr_reader :text, :annotations, :meta
|
|
7
7
|
|
|
8
|
-
def initialize(text, annotations: nil)
|
|
8
|
+
def initialize(text, annotations: nil, meta: nil)
|
|
9
9
|
@text = text
|
|
10
10
|
@annotations = annotations
|
|
11
|
+
@meta = meta
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def to_h
|
|
14
|
-
{ text: text, annotations: annotations, type: "text" }.compact
|
|
15
|
+
{ text: text, annotations: annotations, _meta: meta, type: "text" }.compact
|
|
15
16
|
end
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
class Image
|
|
19
|
-
attr_reader :data, :mime_type, :annotations
|
|
20
|
+
attr_reader :data, :mime_type, :annotations, :meta
|
|
20
21
|
|
|
21
|
-
def initialize(data, mime_type, annotations: nil)
|
|
22
|
+
def initialize(data, mime_type, annotations: nil, meta: nil)
|
|
22
23
|
@data = data
|
|
23
24
|
@mime_type = mime_type
|
|
24
25
|
@annotations = annotations
|
|
26
|
+
@meta = meta
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def to_h
|
|
28
|
-
{ data: data, mimeType: mime_type, annotations: annotations, type: "image" }.compact
|
|
30
|
+
{ data: data, mimeType: mime_type, annotations: annotations, _meta: meta, type: "image" }.compact
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
class Audio
|
|
33
|
-
attr_reader :data, :mime_type, :annotations
|
|
35
|
+
attr_reader :data, :mime_type, :annotations, :meta
|
|
34
36
|
|
|
35
|
-
def initialize(data, mime_type, annotations: nil)
|
|
37
|
+
def initialize(data, mime_type, annotations: nil, meta: nil)
|
|
36
38
|
@data = data
|
|
37
39
|
@mime_type = mime_type
|
|
38
40
|
@annotations = annotations
|
|
41
|
+
@meta = meta
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def to_h
|
|
42
|
-
{ data: data, mimeType: mime_type, annotations: annotations, type: "audio" }.compact
|
|
45
|
+
{ data: data, mimeType: mime_type, annotations: annotations, _meta: meta, type: "audio" }.compact
|
|
43
46
|
end
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
class EmbeddedResource
|
|
47
|
-
attr_reader :resource, :annotations
|
|
50
|
+
attr_reader :resource, :annotations, :meta
|
|
48
51
|
|
|
49
|
-
def initialize(resource, annotations: nil)
|
|
52
|
+
def initialize(resource, annotations: nil, meta: nil)
|
|
50
53
|
@resource = resource
|
|
51
54
|
@annotations = annotations
|
|
55
|
+
@meta = meta
|
|
52
56
|
end
|
|
53
57
|
|
|
54
58
|
def to_h
|
|
55
|
-
{ resource: resource.to_h, annotations: annotations, type: "resource" }.compact
|
|
59
|
+
{ resource: resource.to_h, annotations: annotations, _meta: meta, type: "resource" }.compact
|
|
56
60
|
end
|
|
57
61
|
end
|
|
58
62
|
end
|
data/lib/mcp/instrumentation.rb
CHANGED
|
@@ -2,19 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
module MCP
|
|
4
4
|
module Instrumentation
|
|
5
|
-
def instrument_call(method, &block)
|
|
5
|
+
def instrument_call(method, server_context: {}, exception_already_reported: nil, &block)
|
|
6
6
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
7
7
|
begin
|
|
8
8
|
@instrumentation_data = {}
|
|
9
9
|
add_instrumentation_data(method: method)
|
|
10
10
|
|
|
11
|
-
result =
|
|
11
|
+
result = configuration.around_request.call(@instrumentation_data, &block)
|
|
12
12
|
|
|
13
13
|
result
|
|
14
|
+
rescue => e
|
|
15
|
+
already_reported = begin
|
|
16
|
+
!!exception_already_reported&.call(e)
|
|
17
|
+
# rubocop:disable Lint/RescueException
|
|
18
|
+
rescue Exception
|
|
19
|
+
# rubocop:enable Lint/RescueException
|
|
20
|
+
# The predicate is expected to be side-effect-free and return a boolean.
|
|
21
|
+
# Any exception raised from it (including non-StandardError such as SystemExit)
|
|
22
|
+
# must not shadow the original exception.
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
unless already_reported
|
|
27
|
+
add_instrumentation_data(error: :internal_error) unless @instrumentation_data.key?(:error)
|
|
28
|
+
configuration.exception_reporter.call(e, server_context)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
raise
|
|
14
32
|
ensure
|
|
15
33
|
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
16
34
|
add_instrumentation_data(duration: end_time - start_time)
|
|
17
35
|
|
|
36
|
+
# Backward compatibility: `instrumentation_callback` is soft-deprecated
|
|
37
|
+
# in favor of `around_request`, but existing callers still expect it
|
|
38
|
+
# to fire after every request until it is removed in a future version.
|
|
18
39
|
configuration.instrumentation_callback.call(@instrumentation_data)
|
|
19
40
|
end
|
|
20
41
|
end
|
data/lib/mcp/methods.rb
CHANGED
|
@@ -33,6 +33,7 @@ module MCP
|
|
|
33
33
|
NOTIFICATIONS_MESSAGE = "notifications/message"
|
|
34
34
|
NOTIFICATIONS_PROGRESS = "notifications/progress"
|
|
35
35
|
NOTIFICATIONS_CANCELLED = "notifications/cancelled"
|
|
36
|
+
NOTIFICATIONS_ELICITATION_COMPLETE = "notifications/elicitation/complete"
|
|
36
37
|
|
|
37
38
|
class MissingRequiredCapabilityError < StandardError
|
|
38
39
|
attr_reader :method
|
|
@@ -72,15 +73,13 @@ module MCP
|
|
|
72
73
|
require_capability!(method, capabilities, :completions)
|
|
73
74
|
when ROOTS_LIST
|
|
74
75
|
require_capability!(method, capabilities, :roots)
|
|
75
|
-
when NOTIFICATIONS_ROOTS_LIST_CHANGED
|
|
76
|
-
require_capability!(method, capabilities, :roots)
|
|
77
|
-
require_capability!(method, capabilities, :roots, :listChanged)
|
|
78
76
|
when SAMPLING_CREATE_MESSAGE
|
|
79
77
|
require_capability!(method, capabilities, :sampling)
|
|
80
78
|
when ELICITATION_CREATE
|
|
81
79
|
require_capability!(method, capabilities, :elicitation)
|
|
82
|
-
when INITIALIZE, PING, NOTIFICATIONS_INITIALIZED,
|
|
83
|
-
|
|
80
|
+
when INITIALIZE, PING, NOTIFICATIONS_INITIALIZED, NOTIFICATIONS_ROOTS_LIST_CHANGED,
|
|
81
|
+
NOTIFICATIONS_PROGRESS, NOTIFICATIONS_CANCELLED, NOTIFICATIONS_ELICITATION_COMPLETE
|
|
82
|
+
# No specific capability required.
|
|
84
83
|
end
|
|
85
84
|
end
|
|
86
85
|
|
data/lib/mcp/progress.rb
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module MCP
|
|
4
4
|
class Progress
|
|
5
|
-
def initialize(notification_target:, progress_token:)
|
|
5
|
+
def initialize(notification_target:, progress_token:, related_request_id: nil)
|
|
6
6
|
@notification_target = notification_target
|
|
7
7
|
@progress_token = progress_token
|
|
8
|
+
@related_request_id = related_request_id
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def report(progress, total: nil, message: nil)
|
|
@@ -16,6 +17,7 @@ module MCP
|
|
|
16
17
|
progress: progress,
|
|
17
18
|
total: total,
|
|
18
19
|
message: message,
|
|
20
|
+
related_request_id: @related_request_id,
|
|
19
21
|
)
|
|
20
22
|
end
|
|
21
23
|
end
|
data/lib/mcp/prompt/result.rb
CHANGED
|
@@ -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,41 @@ module MCP
|
|
|
53
53
|
MCP.configuration.exception_reporter.call(e, { error: "Failed to send notification" })
|
|
54
54
|
false
|
|
55
55
|
end
|
|
56
|
+
|
|
57
|
+
def send_request(method, params = nil)
|
|
58
|
+
request_id = generate_request_id
|
|
59
|
+
request = { jsonrpc: "2.0", id: request_id, method: method }
|
|
60
|
+
request[:params] = params if params
|
|
61
|
+
|
|
62
|
+
begin
|
|
63
|
+
send_response(request)
|
|
64
|
+
rescue => e
|
|
65
|
+
MCP.configuration.exception_reporter.call(e, { error: "Failed to send request" })
|
|
66
|
+
raise
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
while @open && (line = $stdin.gets)
|
|
70
|
+
begin
|
|
71
|
+
parsed = JSON.parse(line.strip, symbolize_names: true)
|
|
72
|
+
rescue JSON::ParserError => e
|
|
73
|
+
MCP.configuration.exception_reporter.call(e, { error: "Failed to parse response" })
|
|
74
|
+
raise
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if parsed[:id] == request_id && !parsed.key?(:method)
|
|
78
|
+
if parsed[:error]
|
|
79
|
+
raise StandardError, "Client returned an error for #{method} request (code: #{parsed[:error][:code]}): #{parsed[:error][:message]}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
return parsed[:result]
|
|
83
|
+
else
|
|
84
|
+
response = @session ? @session.handle(parsed) : @server.handle(parsed)
|
|
85
|
+
send_response(response) if response
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
raise "Transport closed while waiting for response to #{method} request."
|
|
90
|
+
end
|
|
56
91
|
end
|
|
57
92
|
end
|
|
58
93
|
end
|