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.
- checksums.yaml +4 -4
- data/README.md +1384 -691
- data/lib/json_rpc_handler.rb +23 -10
- data/lib/mcp/cancellation.rb +72 -0
- data/lib/mcp/cancelled_error.rb +13 -0
- data/lib/mcp/client/http.rb +234 -15
- data/lib/mcp/client/paginated_result.rb +13 -0
- data/lib/mcp/client/stdio.rb +100 -49
- data/lib/mcp/client.rb +293 -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 +42 -0
- data/lib/mcp/server/transports/streamable_http_transport.rb +383 -88
- data/lib/mcp/server.rb +361 -50
- data/lib/mcp/server_context.rb +81 -2
- data/lib/mcp/server_session.rb +170 -12
- 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
- data/lib/mcp.rb +2 -0
- metadata +11 -6
- data/lib/mcp/transports/stdio.rb +0 -15
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,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
|