swarm_sdk 2.7.5 → 2.7.7
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b83fd113813e0667b02d12c5ef6b9f4d47c134afa0665e2b6e482a6c9d3dacd7
|
|
4
|
+
data.tar.gz: 8a758e06817af7690c21fc446597b0e1a91b876643d20a4a111a62bc953d3d5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5bfcfaf82ca105e6b1e1f04e009c703b641c25af6797ccd72c945a0c8dbbfbe60a0d783fc9e8d43322bbefac95b16123c4e7232c4ac9e1850a770d1120568344
|
|
7
|
+
data.tar.gz: 0aa16f8d6ebe0fd2acb4cb52baba570000c82c4607d015d3a4569d671f9743cb1199edd80953002b8dc0fb673fb464ebfc2d5d0c645f3afb50258c7451281304
|
|
@@ -45,17 +45,23 @@ module SwarmSDK
|
|
|
45
45
|
Swarm.apply_mcp_logging_configuration
|
|
46
46
|
|
|
47
47
|
mcp_server_configs.each do |server_config|
|
|
48
|
+
tools_config = server_config[:tools]
|
|
49
|
+
mode = tools_config.nil? ? :discovery : :optimized
|
|
50
|
+
|
|
51
|
+
# Emit event before initialization
|
|
52
|
+
emit_mcp_init_start(agent_name, server_config, mode)
|
|
53
|
+
|
|
48
54
|
client = initialize_mcp_client(server_config)
|
|
49
55
|
|
|
50
56
|
# Store client for cleanup
|
|
51
57
|
@mcp_clients[agent_name] << client
|
|
52
58
|
|
|
53
|
-
tools_config = server_config[:tools]
|
|
54
|
-
|
|
55
59
|
if tools_config.nil?
|
|
56
60
|
# Discovery mode: Fetch all tools from server (calls tools/list)
|
|
57
61
|
# client.tools returns RubyLLM::Tool instances (already wrapped by internal Coordinator)
|
|
58
62
|
all_tools = client.tools
|
|
63
|
+
tool_names = all_tools.map { |t| t.respond_to?(:name) ? t.name : t.to_s }
|
|
64
|
+
|
|
59
65
|
all_tools.each do |tool|
|
|
60
66
|
chat.tool_registry.register(
|
|
61
67
|
tool,
|
|
@@ -63,14 +69,20 @@ module SwarmSDK
|
|
|
63
69
|
metadata: { server_name: server_config[:name] },
|
|
64
70
|
)
|
|
65
71
|
end
|
|
72
|
+
|
|
73
|
+
# Emit completion event for discovery mode
|
|
74
|
+
emit_mcp_init_complete(agent_name, server_config, mode, all_tools.size, tool_names)
|
|
66
75
|
RubyLLM.logger.debug("SwarmSDK: Discovered and registered #{all_tools.size} tools from MCP server '#{server_config[:name]}'")
|
|
67
76
|
else
|
|
68
77
|
# Optimized mode: Create tool stubs without tools/list RPC (Plan 025)
|
|
69
78
|
# Use client directly (it has internal coordinator)
|
|
79
|
+
tool_names = tools_config.map(&:to_s)
|
|
80
|
+
|
|
70
81
|
tools_config.each do |tool_name|
|
|
71
82
|
stub = Tools::McpToolStub.new(
|
|
72
83
|
client: client,
|
|
73
84
|
name: tool_name.to_s,
|
|
85
|
+
server_name: server_config[:name],
|
|
74
86
|
)
|
|
75
87
|
chat.tool_registry.register(
|
|
76
88
|
stub,
|
|
@@ -78,6 +90,9 @@ module SwarmSDK
|
|
|
78
90
|
metadata: { server_name: server_config[:name] },
|
|
79
91
|
)
|
|
80
92
|
end
|
|
93
|
+
|
|
94
|
+
# Emit completion event for optimized mode
|
|
95
|
+
emit_mcp_init_complete(agent_name, server_config, mode, tools_config.size, tool_names)
|
|
81
96
|
RubyLLM.logger.debug("SwarmSDK: Registered #{tools_config.size} tool stubs from MCP server '#{server_config[:name]}' (lazy schema)")
|
|
82
97
|
end
|
|
83
98
|
rescue StandardError => e
|
|
@@ -187,6 +202,42 @@ module SwarmSDK
|
|
|
187
202
|
|
|
188
203
|
streamable_config
|
|
189
204
|
end
|
|
205
|
+
|
|
206
|
+
# Emit MCP server initialization start event
|
|
207
|
+
#
|
|
208
|
+
# @param agent_name [Symbol] Agent name
|
|
209
|
+
# @param server_config [Hash] MCP server configuration
|
|
210
|
+
# @param mode [Symbol] Initialization mode (:discovery or :optimized)
|
|
211
|
+
# @return [void]
|
|
212
|
+
def emit_mcp_init_start(agent_name, server_config, mode)
|
|
213
|
+
LogStream.emit(
|
|
214
|
+
type: "mcp_server_init_start",
|
|
215
|
+
agent: agent_name,
|
|
216
|
+
server_name: server_config[:name],
|
|
217
|
+
transport_type: server_config[:type],
|
|
218
|
+
mode: mode,
|
|
219
|
+
)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Emit MCP server initialization complete event
|
|
223
|
+
#
|
|
224
|
+
# @param agent_name [Symbol] Agent name
|
|
225
|
+
# @param server_config [Hash] MCP server configuration
|
|
226
|
+
# @param mode [Symbol] Initialization mode (:discovery or :optimized)
|
|
227
|
+
# @param tool_count [Integer] Number of tools registered
|
|
228
|
+
# @param tool_names [Array<String>] Names of registered tools
|
|
229
|
+
# @return [void]
|
|
230
|
+
def emit_mcp_init_complete(agent_name, server_config, mode, tool_count, tool_names)
|
|
231
|
+
LogStream.emit(
|
|
232
|
+
type: "mcp_server_init_complete",
|
|
233
|
+
agent: agent_name,
|
|
234
|
+
server_name: server_config[:name],
|
|
235
|
+
transport_type: server_config[:type],
|
|
236
|
+
mode: mode,
|
|
237
|
+
tool_count: tool_count,
|
|
238
|
+
tools: tool_names,
|
|
239
|
+
)
|
|
240
|
+
end
|
|
190
241
|
end
|
|
191
242
|
end
|
|
192
243
|
end
|
|
@@ -4,8 +4,10 @@ module SwarmSDK
|
|
|
4
4
|
module Tools
|
|
5
5
|
module ImageExtractors
|
|
6
6
|
# Extracts images from PDF documents
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# Only extracts JPEG images (DCTDecode format) which are LLM API compatible
|
|
8
|
+
# Non-JPEG images (FlateDecode, LZWDecode) are skipped because they would
|
|
9
|
+
# require TIFF format which is not supported by LLM APIs
|
|
10
|
+
# Supported LLM image formats: ['png', 'jpeg', 'gif', 'webp']
|
|
9
11
|
class PdfImageExtractor
|
|
10
12
|
class << self
|
|
11
13
|
# Extract all images from a PDF document
|
|
@@ -65,11 +67,13 @@ module SwarmSDK
|
|
|
65
67
|
|
|
66
68
|
case filter
|
|
67
69
|
when :DCTDecode
|
|
68
|
-
# JPEG images can be saved directly
|
|
70
|
+
# JPEG images can be saved directly - LLM API compatible
|
|
69
71
|
save_jpeg(stream, page_number, name, temp_dir)
|
|
70
72
|
when :FlateDecode, :LZWDecode, nil
|
|
71
|
-
#
|
|
72
|
-
|
|
73
|
+
# Skip non-JPEG images to avoid TIFF format (not supported by LLM APIs)
|
|
74
|
+
# LLM APIs only support: ['png', 'jpeg', 'gif', 'webp']
|
|
75
|
+
# These images would require TIFF conversion which causes API errors
|
|
76
|
+
nil
|
|
73
77
|
end
|
|
74
78
|
# Unsupported formats return nil
|
|
75
79
|
rescue StandardError
|
|
@@ -33,22 +33,24 @@ module SwarmSDK
|
|
|
33
33
|
class McpToolStub < Base
|
|
34
34
|
removable true # MCP tools can be controlled by skills
|
|
35
35
|
|
|
36
|
-
attr_reader :name, :client
|
|
36
|
+
attr_reader :name, :client, :server_name
|
|
37
37
|
|
|
38
38
|
# Create a new MCP tool stub
|
|
39
39
|
#
|
|
40
40
|
# @param client [RubyLLM::MCP::Client] MCP client instance
|
|
41
41
|
# @param name [String] Tool name
|
|
42
|
+
# @param server_name [String, nil] MCP server name for error context
|
|
42
43
|
# @param description [String, nil] Tool description (optional, fetched if nil)
|
|
43
44
|
# @param schema [Hash, nil] Tool input schema (optional, fetched if nil)
|
|
44
45
|
#
|
|
45
46
|
# @example Minimal stub (lazy description + schema)
|
|
46
|
-
# McpToolStub.new(client: client, name: "search")
|
|
47
|
+
# McpToolStub.new(client: client, name: "search", server_name: "codebase")
|
|
47
48
|
#
|
|
48
49
|
# @example With description (lazy schema only)
|
|
49
50
|
# McpToolStub.new(
|
|
50
51
|
# client: client,
|
|
51
52
|
# name: "search",
|
|
53
|
+
# server_name: "codebase",
|
|
52
54
|
# description: "Search the codebase"
|
|
53
55
|
# )
|
|
54
56
|
#
|
|
@@ -56,14 +58,16 @@ module SwarmSDK
|
|
|
56
58
|
# McpToolStub.new(
|
|
57
59
|
# client: client,
|
|
58
60
|
# name: "search",
|
|
61
|
+
# server_name: "codebase",
|
|
59
62
|
# description: "Search the codebase",
|
|
60
63
|
# schema: { type: "object", properties: {...} }
|
|
61
64
|
# )
|
|
62
|
-
def initialize(client:, name:, description: nil, schema: nil)
|
|
65
|
+
def initialize(client:, name:, server_name: nil, description: nil, schema: nil)
|
|
63
66
|
super()
|
|
64
67
|
@client = client
|
|
65
68
|
@name = name
|
|
66
69
|
@mcp_name = name
|
|
70
|
+
@server_name = server_name || "unknown"
|
|
67
71
|
@description = description || "MCP tool: #{name}"
|
|
68
72
|
@input_schema = schema
|
|
69
73
|
@schema_loaded = !schema.nil?
|
|
@@ -93,6 +97,9 @@ module SwarmSDK
|
|
|
93
97
|
#
|
|
94
98
|
# @param params [Hash] Tool parameters
|
|
95
99
|
# @return [String, Hash] Tool result content or error hash
|
|
100
|
+
# @raise [MCPTimeoutError] When the MCP server times out
|
|
101
|
+
# @raise [MCPTransportError] When there's a transport-level error
|
|
102
|
+
# @raise [MCPError] When any other MCP error occurs
|
|
96
103
|
def execute(**params)
|
|
97
104
|
# Use client.call_tool (client has internal coordinator)
|
|
98
105
|
result = @client.call_tool(
|
|
@@ -102,6 +109,23 @@ module SwarmSDK
|
|
|
102
109
|
|
|
103
110
|
# client.call_tool returns the result content directly
|
|
104
111
|
result
|
|
112
|
+
rescue RubyLLM::MCP::Errors::TimeoutError => e
|
|
113
|
+
raise MCPTimeoutError, format_mcp_error(
|
|
114
|
+
"MCP request timed out",
|
|
115
|
+
original_message: e.message,
|
|
116
|
+
request_id: e.request_id,
|
|
117
|
+
)
|
|
118
|
+
rescue RubyLLM::MCP::Errors::TransportError => e
|
|
119
|
+
raise MCPTransportError, format_mcp_error(
|
|
120
|
+
"MCP transport error",
|
|
121
|
+
original_message: e.message,
|
|
122
|
+
code: e.code,
|
|
123
|
+
)
|
|
124
|
+
rescue RubyLLM::MCP::Errors::BaseError => e
|
|
125
|
+
raise MCPError, format_mcp_error(
|
|
126
|
+
"MCP error",
|
|
127
|
+
original_message: e.message,
|
|
128
|
+
)
|
|
105
129
|
end
|
|
106
130
|
|
|
107
131
|
private
|
|
@@ -112,6 +136,9 @@ module SwarmSDK
|
|
|
112
136
|
# Multiple concurrent fibers will only trigger one fetch.
|
|
113
137
|
#
|
|
114
138
|
# @return [void]
|
|
139
|
+
# @raise [MCPTimeoutError] When the MCP server times out during schema fetch
|
|
140
|
+
# @raise [MCPTransportError] When there's a transport-level error
|
|
141
|
+
# @raise [MCPError] When any other MCP error occurs
|
|
115
142
|
def ensure_schema_loaded!
|
|
116
143
|
return if @schema_loaded
|
|
117
144
|
|
|
@@ -131,6 +158,40 @@ module SwarmSDK
|
|
|
131
158
|
|
|
132
159
|
@schema_loaded = true
|
|
133
160
|
end
|
|
161
|
+
rescue RubyLLM::MCP::Errors::TimeoutError => e
|
|
162
|
+
raise MCPTimeoutError, format_mcp_error(
|
|
163
|
+
"MCP schema fetch timed out",
|
|
164
|
+
original_message: e.message,
|
|
165
|
+
request_id: e.request_id,
|
|
166
|
+
)
|
|
167
|
+
rescue RubyLLM::MCP::Errors::TransportError => e
|
|
168
|
+
raise MCPTransportError, format_mcp_error(
|
|
169
|
+
"MCP transport error during schema fetch",
|
|
170
|
+
original_message: e.message,
|
|
171
|
+
code: e.code,
|
|
172
|
+
)
|
|
173
|
+
rescue RubyLLM::MCP::Errors::BaseError => e
|
|
174
|
+
raise MCPError, format_mcp_error(
|
|
175
|
+
"MCP error during schema fetch",
|
|
176
|
+
original_message: e.message,
|
|
177
|
+
)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Format MCP error message with contextual information
|
|
181
|
+
#
|
|
182
|
+
# @param prefix [String] Error message prefix
|
|
183
|
+
# @param original_message [String] Original error message from RubyLLM::MCP
|
|
184
|
+
# @param request_id [String, nil] MCP request ID (for timeout errors)
|
|
185
|
+
# @param code [Integer, nil] HTTP status code (for transport errors)
|
|
186
|
+
# @return [String] Formatted error message with full context
|
|
187
|
+
def format_mcp_error(prefix, original_message:, request_id: nil, code: nil)
|
|
188
|
+
parts = [prefix]
|
|
189
|
+
parts << "[server: #{@server_name}]"
|
|
190
|
+
parts << "[tool: #{@mcp_name}]"
|
|
191
|
+
parts << "[request_id: #{request_id}]" if request_id
|
|
192
|
+
parts << "[code: #{code}]" if code
|
|
193
|
+
parts << "- #{original_message}"
|
|
194
|
+
parts.join(" ")
|
|
134
195
|
end
|
|
135
196
|
end
|
|
136
197
|
end
|
data/lib/swarm_sdk/version.rb
CHANGED
data/lib/swarm_sdk.rb
CHANGED
|
@@ -71,6 +71,15 @@ module SwarmSDK
|
|
|
71
71
|
# Raised when agent turn exceeds turn_timeout
|
|
72
72
|
class TurnTimeoutError < TimeoutError; end
|
|
73
73
|
|
|
74
|
+
# Base class for MCP-related errors (provides context about server/tool)
|
|
75
|
+
class MCPError < Error; end
|
|
76
|
+
|
|
77
|
+
# Raised when MCP request times out
|
|
78
|
+
class MCPTimeoutError < MCPError; end
|
|
79
|
+
|
|
80
|
+
# Raised when MCP transport fails (connection, HTTP errors)
|
|
81
|
+
class MCPTransportError < MCPError; end
|
|
82
|
+
|
|
74
83
|
class << self
|
|
75
84
|
# Get the global configuration instance
|
|
76
85
|
#
|