claude_swarm 1.0.1 → 1.0.4
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/.claude/commands/release.md +1 -1
- data/.claude/hooks/lint-code-files.rb +65 -0
- data/.rubocop.yml +22 -2
- data/CHANGELOG.md +14 -1
- data/CLAUDE.md +1 -1
- data/CONTRIBUTING.md +69 -0
- data/README.md +27 -2
- data/Rakefile +71 -3
- data/analyze_coverage.rb +94 -0
- data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
- data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
- data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
- data/docs/v2/README.md +308 -0
- data/docs/v2/guides/claude-code-agents.md +262 -0
- data/docs/v2/guides/complete-tutorial.md +3088 -0
- data/docs/v2/guides/getting-started.md +1456 -0
- data/docs/v2/guides/memory-adapters.md +998 -0
- data/docs/v2/guides/plugins.md +816 -0
- data/docs/v2/guides/quick-start-cli.md +1745 -0
- data/docs/v2/guides/rails-integration.md +1902 -0
- data/docs/v2/guides/swarm-memory.md +599 -0
- data/docs/v2/reference/cli.md +729 -0
- data/docs/v2/reference/ruby-dsl.md +2154 -0
- data/docs/v2/reference/yaml.md +1835 -0
- data/docs-team-swarm.yml +2222 -0
- data/examples/learning-assistant/assistant.md +7 -0
- data/examples/learning-assistant/example-memories/concept-example.md +90 -0
- data/examples/learning-assistant/example-memories/experience-example.md +66 -0
- data/examples/learning-assistant/example-memories/fact-example.md +76 -0
- data/examples/learning-assistant/example-memories/memory-index.md +78 -0
- data/examples/learning-assistant/example-memories/skill-example.md +168 -0
- data/examples/learning-assistant/learning_assistant.rb +34 -0
- data/examples/learning-assistant/learning_assistant.yml +20 -0
- data/examples/v2/dsl/01_basic.rb +44 -0
- data/examples/v2/dsl/02_core_parameters.rb +59 -0
- data/examples/v2/dsl/03_capabilities.rb +71 -0
- data/examples/v2/dsl/04_llm_parameters.rb +56 -0
- data/examples/v2/dsl/05_advanced_flags.rb +73 -0
- data/examples/v2/dsl/06_permissions.rb +80 -0
- data/examples/v2/dsl/07_mcp_server.rb +62 -0
- data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
- data/examples/v2/dsl/09_agent_hooks.rb +67 -0
- data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
- data/examples/v2/dsl/11_delegation.rb +60 -0
- data/examples/v2/dsl/12_complete_integration.rb +137 -0
- data/examples/v2/file_tools_swarm.yml +102 -0
- data/examples/v2/hooks/01_basic_hooks.rb +133 -0
- data/examples/v2/hooks/02_usage_tracking.rb +201 -0
- data/examples/v2/hooks/03_production_monitoring.rb +429 -0
- data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
- data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
- data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
- data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
- data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
- data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
- data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
- data/examples/v2/hooks/swarm_summary.sh +44 -0
- data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
- data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
- data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
- data/examples/v2/hooks/validate_bash.rb +59 -0
- data/examples/v2/multi_directory_permissions.yml +221 -0
- data/examples/v2/node_context_demo.rb +127 -0
- data/examples/v2/node_workflow.rb +173 -0
- data/examples/v2/path_resolution_demo.rb +216 -0
- data/examples/v2/simple-swarm-v2.rb +90 -0
- data/examples/v2/simple-swarm-v2.yml +62 -0
- data/examples/v2/swarm.yml +71 -0
- data/examples/v2/swarm_with_hooks.yml +61 -0
- data/examples/v2/swarm_with_hooks_simple.yml +25 -0
- data/examples/v2/think_tool_demo.rb +62 -0
- data/exe/swarm +6 -0
- data/lib/claude_swarm/claude_mcp_server.rb +0 -6
- data/lib/claude_swarm/cli.rb +10 -3
- data/lib/claude_swarm/commands/ps.rb +19 -20
- data/lib/claude_swarm/commands/show.rb +1 -1
- data/lib/claude_swarm/configuration.rb +10 -12
- data/lib/claude_swarm/mcp_generator.rb +10 -1
- data/lib/claude_swarm/orchestrator.rb +73 -49
- data/lib/claude_swarm/system_utils.rb +37 -11
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm/worktree_manager.rb +1 -0
- data/lib/claude_swarm/yaml_loader.rb +22 -0
- data/lib/claude_swarm.rb +7 -2
- data/lib/swarm_cli/cli.rb +201 -0
- data/lib/swarm_cli/command_registry.rb +61 -0
- data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
- data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
- data/lib/swarm_cli/commands/migrate.rb +55 -0
- data/lib/swarm_cli/commands/run.rb +173 -0
- data/lib/swarm_cli/config_loader.rb +97 -0
- data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
- data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
- data/lib/swarm_cli/interactive_repl.rb +918 -0
- data/lib/swarm_cli/mcp_serve_options.rb +44 -0
- data/lib/swarm_cli/mcp_tools_options.rb +59 -0
- data/lib/swarm_cli/migrate_options.rb +54 -0
- data/lib/swarm_cli/migrator.rb +132 -0
- data/lib/swarm_cli/options.rb +151 -0
- data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
- data/lib/swarm_cli/ui/components/content_block.rb +120 -0
- data/lib/swarm_cli/ui/components/divider.rb +57 -0
- data/lib/swarm_cli/ui/components/panel.rb +62 -0
- data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
- data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
- data/lib/swarm_cli/ui/formatters/number.rb +58 -0
- data/lib/swarm_cli/ui/formatters/text.rb +77 -0
- data/lib/swarm_cli/ui/formatters/time.rb +73 -0
- data/lib/swarm_cli/ui/icons.rb +59 -0
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
- data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
- data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
- data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
- data/lib/swarm_cli/version.rb +5 -0
- data/lib/swarm_cli.rb +44 -0
- data/lib/swarm_memory/adapters/base.rb +141 -0
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
- data/lib/swarm_memory/chat_extension.rb +34 -0
- data/lib/swarm_memory/cli/commands.rb +306 -0
- data/lib/swarm_memory/core/entry.rb +37 -0
- data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
- data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
- data/lib/swarm_memory/core/path_normalizer.rb +75 -0
- data/lib/swarm_memory/core/semantic_index.rb +244 -0
- data/lib/swarm_memory/core/storage.rb +288 -0
- data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
- data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
- data/lib/swarm_memory/dsl/memory_config.rb +113 -0
- data/lib/swarm_memory/embeddings/embedder.rb +36 -0
- data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
- data/lib/swarm_memory/errors.rb +21 -0
- data/lib/swarm_memory/integration/cli_registration.rb +30 -0
- data/lib/swarm_memory/integration/configuration.rb +43 -0
- data/lib/swarm_memory/integration/registration.rb +31 -0
- data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
- data/lib/swarm_memory/optimization/analyzer.rb +244 -0
- data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
- data/lib/swarm_memory/prompts/memory.md.erb +109 -0
- data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
- data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
- data/lib/swarm_memory/search/semantic_search.rb +112 -0
- data/lib/swarm_memory/search/text_search.rb +42 -0
- data/lib/swarm_memory/search/text_similarity.rb +80 -0
- data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
- data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
- data/lib/swarm_memory/tools/load_skill.rb +313 -0
- data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
- data/lib/swarm_memory/tools/memory_delete.rb +99 -0
- data/lib/swarm_memory/tools/memory_edit.rb +185 -0
- data/lib/swarm_memory/tools/memory_glob.rb +160 -0
- data/lib/swarm_memory/tools/memory_grep.rb +247 -0
- data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
- data/lib/swarm_memory/tools/memory_read.rb +123 -0
- data/lib/swarm_memory/tools/memory_write.rb +231 -0
- data/lib/swarm_memory/utils.rb +50 -0
- data/lib/swarm_memory/version.rb +5 -0
- data/lib/swarm_memory.rb +166 -0
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
- data/lib/swarm_sdk/agent/builder.rb +461 -0
- data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
- data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
- data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
- data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
- data/lib/swarm_sdk/agent/chat.rb +1159 -0
- data/lib/swarm_sdk/agent/context.rb +112 -0
- data/lib/swarm_sdk/agent/context_manager.rb +309 -0
- data/lib/swarm_sdk/agent/definition.rb +556 -0
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
- data/lib/swarm_sdk/configuration.rb +296 -0
- data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
- data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
- data/lib/swarm_sdk/context_compactor.rb +340 -0
- data/lib/swarm_sdk/hooks/adapter.rb +359 -0
- data/lib/swarm_sdk/hooks/context.rb +197 -0
- data/lib/swarm_sdk/hooks/definition.rb +80 -0
- data/lib/swarm_sdk/hooks/error.rb +29 -0
- data/lib/swarm_sdk/hooks/executor.rb +146 -0
- data/lib/swarm_sdk/hooks/registry.rb +147 -0
- data/lib/swarm_sdk/hooks/result.rb +150 -0
- data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
- data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
- data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
- data/lib/swarm_sdk/log_collector.rb +51 -0
- data/lib/swarm_sdk/log_stream.rb +69 -0
- data/lib/swarm_sdk/markdown_parser.rb +75 -0
- data/lib/swarm_sdk/model_aliases.json +5 -0
- data/lib/swarm_sdk/models.json +1 -0
- data/lib/swarm_sdk/models.rb +120 -0
- data/lib/swarm_sdk/node/agent_config.rb +49 -0
- data/lib/swarm_sdk/node/builder.rb +439 -0
- data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
- data/lib/swarm_sdk/node_context.rb +170 -0
- data/lib/swarm_sdk/node_orchestrator.rb +384 -0
- data/lib/swarm_sdk/permissions/config.rb +239 -0
- data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
- data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
- data/lib/swarm_sdk/permissions/validator.rb +173 -0
- data/lib/swarm_sdk/permissions_builder.rb +122 -0
- data/lib/swarm_sdk/plugin.rb +147 -0
- data/lib/swarm_sdk/plugin_registry.rb +101 -0
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
- data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
- data/lib/swarm_sdk/result.rb +97 -0
- data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
- data/lib/swarm_sdk/swarm/builder.rb +586 -0
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
- data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
- data/lib/swarm_sdk/swarm.rb +982 -0
- data/lib/swarm_sdk/tools/bash.rb +274 -0
- data/lib/swarm_sdk/tools/clock.rb +44 -0
- data/lib/swarm_sdk/tools/delegate.rb +164 -0
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
- data/lib/swarm_sdk/tools/edit.rb +150 -0
- data/lib/swarm_sdk/tools/glob.rb +158 -0
- data/lib/swarm_sdk/tools/grep.rb +228 -0
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
- data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
- data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
- data/lib/swarm_sdk/tools/read.rb +251 -0
- data/lib/swarm_sdk/tools/registry.rb +93 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
- data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
- data/lib/swarm_sdk/tools/think.rb +95 -0
- data/lib/swarm_sdk/tools/todo_write.rb +216 -0
- data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
- data/lib/swarm_sdk/tools/write.rb +117 -0
- data/lib/swarm_sdk/utils.rb +50 -0
- data/lib/swarm_sdk/version.rb +5 -0
- data/lib/swarm_sdk.rb +157 -0
- data/llm.v2.txt +13407 -0
- data/rubocop/cop/security/no_reflection_methods.rb +47 -0
- data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
- data/swarm_cli.gemspec +57 -0
- data/swarm_memory.gemspec +28 -0
- data/swarm_sdk.gemspec +41 -0
- data/team.yml +1 -1
- data/team_full.yml +1875 -0
- data/{team_v2.yml → team_sdk.yml} +121 -52
- metadata +249 -6
- data/EXAMPLES.md +0 -164
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
class Swarm
|
|
5
|
+
# Handles MCP (Model Context Protocol) server configuration and client management
|
|
6
|
+
#
|
|
7
|
+
# Responsibilities:
|
|
8
|
+
# - Register MCP servers for agents
|
|
9
|
+
# - Initialize MCP clients (stdio, SSE, streamable transports)
|
|
10
|
+
# - Build transport-specific configurations
|
|
11
|
+
# - Track clients for cleanup
|
|
12
|
+
#
|
|
13
|
+
# This encapsulates all MCP-related logic that was previously in Swarm.
|
|
14
|
+
class McpConfigurator
|
|
15
|
+
def initialize(swarm)
|
|
16
|
+
@swarm = swarm
|
|
17
|
+
@mcp_clients = swarm.mcp_clients
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Register MCP servers for an agent
|
|
21
|
+
#
|
|
22
|
+
# Connects to MCP servers and registers their tools with the agent's chat instance.
|
|
23
|
+
# Supports stdio, SSE, and HTTP (streamable) transports.
|
|
24
|
+
#
|
|
25
|
+
# @param chat [AgentChat] The agent's chat instance
|
|
26
|
+
# @param mcp_server_configs [Array<Hash>] MCP server configurations
|
|
27
|
+
# @param agent_name [Symbol] Agent name for tracking clients
|
|
28
|
+
def register_mcp_servers(chat, mcp_server_configs, agent_name:)
|
|
29
|
+
return if mcp_server_configs.nil? || mcp_server_configs.empty?
|
|
30
|
+
|
|
31
|
+
# Ensure MCP logging is configured before creating clients
|
|
32
|
+
Swarm.apply_mcp_logging_configuration
|
|
33
|
+
|
|
34
|
+
mcp_server_configs.each do |server_config|
|
|
35
|
+
client = initialize_mcp_client(server_config)
|
|
36
|
+
|
|
37
|
+
# Store client for cleanup
|
|
38
|
+
@mcp_clients[agent_name] << client
|
|
39
|
+
|
|
40
|
+
# Fetch tools from MCP server and register with chat
|
|
41
|
+
# Tools are already in RubyLLM::Tool format
|
|
42
|
+
tools = client.tools
|
|
43
|
+
tools.each { |tool| chat.with_tool(tool) }
|
|
44
|
+
|
|
45
|
+
RubyLLM.logger.debug("SwarmSDK: Registered #{tools.size} tools from MCP server '#{server_config[:name]}' for agent #{agent_name}")
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
RubyLLM.logger.error("SwarmSDK: Failed to initialize MCP server '#{server_config[:name]}' for agent #{agent_name}: #{e.message}")
|
|
48
|
+
raise ConfigurationError, "Failed to initialize MCP server '#{server_config[:name]}': #{e.message}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Build transport-specific configuration for MCP client
|
|
53
|
+
#
|
|
54
|
+
# This method is public for testing delegation from Swarm.
|
|
55
|
+
#
|
|
56
|
+
# @param transport_type [Symbol] Transport type (:stdio, :sse, :streamable)
|
|
57
|
+
# @param config [Hash] MCP server configuration
|
|
58
|
+
# @return [Hash] Transport-specific configuration
|
|
59
|
+
def build_transport_config(transport_type, config)
|
|
60
|
+
case transport_type
|
|
61
|
+
when :stdio
|
|
62
|
+
build_stdio_config(config)
|
|
63
|
+
when :sse
|
|
64
|
+
build_sse_config(config)
|
|
65
|
+
when :streamable
|
|
66
|
+
build_streamable_config(config)
|
|
67
|
+
else
|
|
68
|
+
raise ArgumentError, "Unsupported transport type: #{transport_type}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Initialize an MCP client from configuration
|
|
75
|
+
#
|
|
76
|
+
# @param config [Hash] MCP server configuration
|
|
77
|
+
# @return [RubyLLM::MCP::Client] Initialized MCP client
|
|
78
|
+
def initialize_mcp_client(config)
|
|
79
|
+
# Convert timeout from seconds to milliseconds
|
|
80
|
+
timeout_seconds = config[:timeout] || 30
|
|
81
|
+
timeout_ms = timeout_seconds * 1000
|
|
82
|
+
|
|
83
|
+
# Determine transport type
|
|
84
|
+
transport_type = determine_transport_type(config[:type])
|
|
85
|
+
|
|
86
|
+
# Build transport-specific configuration
|
|
87
|
+
client_config = build_transport_config(transport_type, config)
|
|
88
|
+
|
|
89
|
+
# Create and start MCP client
|
|
90
|
+
RubyLLM::MCP.client(
|
|
91
|
+
name: config[:name],
|
|
92
|
+
transport_type: transport_type,
|
|
93
|
+
request_timeout: timeout_ms,
|
|
94
|
+
config: client_config,
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Determine transport type from configuration
|
|
99
|
+
#
|
|
100
|
+
# @param type [Symbol, String, nil] Transport type from config
|
|
101
|
+
# @return [Symbol] Normalized transport type
|
|
102
|
+
def determine_transport_type(type)
|
|
103
|
+
case type&.to_sym
|
|
104
|
+
when :stdio then :stdio
|
|
105
|
+
when :sse then :sse
|
|
106
|
+
when :http, :streamable then :streamable
|
|
107
|
+
else
|
|
108
|
+
raise ArgumentError, "Unknown MCP transport type: #{type}"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Build stdio transport configuration
|
|
113
|
+
#
|
|
114
|
+
# @param config [Hash] MCP server configuration
|
|
115
|
+
# @return [Hash] Stdio configuration
|
|
116
|
+
def build_stdio_config(config)
|
|
117
|
+
{
|
|
118
|
+
command: config[:command],
|
|
119
|
+
args: config[:args] || [],
|
|
120
|
+
env: Utils.stringify_keys(config[:env] || {}),
|
|
121
|
+
}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Build SSE transport configuration
|
|
125
|
+
#
|
|
126
|
+
# @param config [Hash] MCP server configuration
|
|
127
|
+
# @return [Hash] SSE configuration
|
|
128
|
+
def build_sse_config(config)
|
|
129
|
+
{
|
|
130
|
+
url: config[:url],
|
|
131
|
+
headers: config[:headers] || {},
|
|
132
|
+
version: config[:version]&.to_sym || :http2,
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Build streamable (HTTP) transport configuration
|
|
137
|
+
#
|
|
138
|
+
# @param config [Hash] MCP server configuration
|
|
139
|
+
# @return [Hash] Streamable configuration
|
|
140
|
+
def build_streamable_config(config)
|
|
141
|
+
{
|
|
142
|
+
url: config[:url],
|
|
143
|
+
headers: config[:headers] || {},
|
|
144
|
+
version: config[:version]&.to_sym || :http2,
|
|
145
|
+
oauth: config[:oauth],
|
|
146
|
+
rate_limit: config[:rate_limit],
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
class Swarm
|
|
5
|
+
# Handles tool creation, registration, and permissions wrapping
|
|
6
|
+
#
|
|
7
|
+
# Responsibilities:
|
|
8
|
+
# - Register explicit tools for agents
|
|
9
|
+
# - Register default tools (Read, Grep, Glob, etc.)
|
|
10
|
+
# - Create tool instances (with agent context)
|
|
11
|
+
# - Wrap tools with permissions validators
|
|
12
|
+
#
|
|
13
|
+
# This encapsulates all tool-related logic that was previously in Swarm.
|
|
14
|
+
class ToolConfigurator
|
|
15
|
+
# Default tools available to all agents (unless disable_default_tools is set)
|
|
16
|
+
DEFAULT_TOOLS = [
|
|
17
|
+
:Read,
|
|
18
|
+
:Grep,
|
|
19
|
+
:Glob,
|
|
20
|
+
:TodoWrite,
|
|
21
|
+
:Think,
|
|
22
|
+
:WebFetch,
|
|
23
|
+
:Clock,
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
# Scratchpad tools (added if scratchpad is enabled)
|
|
27
|
+
SCRATCHPAD_TOOLS = [
|
|
28
|
+
:ScratchpadWrite,
|
|
29
|
+
:ScratchpadRead,
|
|
30
|
+
:ScratchpadList,
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
def initialize(swarm, scratchpad_storage, plugin_storages = {})
|
|
34
|
+
@swarm = swarm
|
|
35
|
+
@scratchpad_storage = scratchpad_storage
|
|
36
|
+
# Plugin storages: { plugin_name => { agent_name => storage } }
|
|
37
|
+
# e.g., { memory: { agent1: storage1, agent2: storage2 } }
|
|
38
|
+
@plugin_storages = plugin_storages
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Register all tools for an agent (both explicit and default)
|
|
42
|
+
#
|
|
43
|
+
# @param chat [AgentChat] The chat instance to register tools with
|
|
44
|
+
# @param agent_name [Symbol] Name of the agent
|
|
45
|
+
# @param agent_definition [AgentDefinition] Agent definition object
|
|
46
|
+
def register_all_tools(chat:, agent_name:, agent_definition:)
|
|
47
|
+
register_explicit_tools(chat, agent_definition.tools, agent_name: agent_name, agent_definition: agent_definition)
|
|
48
|
+
register_default_tools(chat, agent_name: agent_name, agent_definition: agent_definition)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Create a tool instance by name
|
|
52
|
+
#
|
|
53
|
+
# File tools and TodoWrite require agent context for tracking state.
|
|
54
|
+
# Scratchpad tools require shared scratchpad instance.
|
|
55
|
+
# Plugin tools are delegated to their respective plugins.
|
|
56
|
+
#
|
|
57
|
+
# This method is public for testing delegation from Swarm.
|
|
58
|
+
#
|
|
59
|
+
# @param tool_name [Symbol, String] Tool name
|
|
60
|
+
# @param agent_name [Symbol] Agent name for context
|
|
61
|
+
# @param directory [String] Agent's working directory
|
|
62
|
+
# @param chat [Agent::Chat, nil] Optional chat instance for tools that need it
|
|
63
|
+
# @param agent_definition [Agent::Definition, nil] Optional agent definition
|
|
64
|
+
# @return [RubyLLM::Tool] Tool instance
|
|
65
|
+
def create_tool_instance(tool_name, agent_name, directory, chat: nil, agent_definition: nil)
|
|
66
|
+
tool_name_sym = tool_name.to_sym
|
|
67
|
+
|
|
68
|
+
# Check if tool is provided by a plugin
|
|
69
|
+
if PluginRegistry.plugin_tool?(tool_name_sym)
|
|
70
|
+
return create_plugin_tool(tool_name_sym, agent_name, directory, chat, agent_definition)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
case tool_name_sym
|
|
74
|
+
when :Read
|
|
75
|
+
Tools::Read.new(agent_name: agent_name, directory: directory)
|
|
76
|
+
when :Write
|
|
77
|
+
Tools::Write.new(agent_name: agent_name, directory: directory)
|
|
78
|
+
when :Edit
|
|
79
|
+
Tools::Edit.new(agent_name: agent_name, directory: directory)
|
|
80
|
+
when :MultiEdit
|
|
81
|
+
Tools::MultiEdit.new(agent_name: agent_name, directory: directory)
|
|
82
|
+
when :Bash
|
|
83
|
+
Tools::Bash.new(directory: directory)
|
|
84
|
+
when :Glob
|
|
85
|
+
Tools::Glob.new(directory: directory)
|
|
86
|
+
when :Grep
|
|
87
|
+
Tools::Grep.new(directory: directory)
|
|
88
|
+
when :TodoWrite
|
|
89
|
+
Tools::TodoWrite.new(agent_name: agent_name) # TodoWrite doesn't need directory
|
|
90
|
+
when :ScratchpadWrite
|
|
91
|
+
Tools::Scratchpad::ScratchpadWrite.create_for_scratchpad(@scratchpad_storage)
|
|
92
|
+
when :ScratchpadRead
|
|
93
|
+
Tools::Scratchpad::ScratchpadRead.create_for_scratchpad(@scratchpad_storage)
|
|
94
|
+
when :ScratchpadList
|
|
95
|
+
Tools::Scratchpad::ScratchpadList.create_for_scratchpad(@scratchpad_storage)
|
|
96
|
+
when :Think
|
|
97
|
+
Tools::Think.new
|
|
98
|
+
when :Clock
|
|
99
|
+
Tools::Clock.new
|
|
100
|
+
else
|
|
101
|
+
# Regular tools - get class from registry and instantiate
|
|
102
|
+
tool_class = Tools::Registry.get(tool_name_sym)
|
|
103
|
+
raise ConfigurationError, "Unknown tool: #{tool_name}" unless tool_class
|
|
104
|
+
|
|
105
|
+
# Check if tool is marked as :special but not handled in case statement
|
|
106
|
+
if tool_class == :special
|
|
107
|
+
raise ConfigurationError,
|
|
108
|
+
"Tool '#{tool_name}' requires special initialization but is not handled in create_tool_instance. " \
|
|
109
|
+
"This is a bug - #{tool_name} should be added to the case statement above."
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
tool_class.new
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Wrap a tool instance with permissions validator if configured
|
|
117
|
+
#
|
|
118
|
+
# This method is public for testing delegation from Swarm.
|
|
119
|
+
#
|
|
120
|
+
# @param tool_instance [RubyLLM::Tool] Tool instance to wrap
|
|
121
|
+
# @param permissions_config [Hash, nil] Permission configuration
|
|
122
|
+
# @param agent_definition [AgentDefinition] Agent definition
|
|
123
|
+
# @return [RubyLLM::Tool] Either the wrapped tool or original tool
|
|
124
|
+
def wrap_tool_with_permissions(tool_instance, permissions_config, agent_definition)
|
|
125
|
+
# Skip wrapping if no permissions or agent bypasses permissions
|
|
126
|
+
return tool_instance unless permissions_config
|
|
127
|
+
return tool_instance if agent_definition.bypass_permissions
|
|
128
|
+
|
|
129
|
+
# Create permissions config and wrap tool with validator
|
|
130
|
+
permissions = Permissions::Config.new(
|
|
131
|
+
permissions_config,
|
|
132
|
+
base_directory: agent_definition.directory,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
Permissions::Validator.new(tool_instance, permissions)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
# Register explicitly configured tools
|
|
141
|
+
#
|
|
142
|
+
# @param chat [AgentChat] The chat instance
|
|
143
|
+
# @param tool_configs [Array<Hash>] Tool configurations with optional permissions
|
|
144
|
+
# @param agent_name [Symbol] Agent name
|
|
145
|
+
# @param agent_definition [AgentDefinition] Agent definition
|
|
146
|
+
def register_explicit_tools(chat, tool_configs, agent_name:, agent_definition:)
|
|
147
|
+
tool_configs.each do |tool_config|
|
|
148
|
+
tool_name = tool_config[:name]
|
|
149
|
+
permissions_config = tool_config[:permissions]
|
|
150
|
+
|
|
151
|
+
# Create tool instance
|
|
152
|
+
tool_instance = create_tool_instance(tool_name, agent_name, agent_definition.directory)
|
|
153
|
+
|
|
154
|
+
# Wrap with permissions validator if configured
|
|
155
|
+
tool_instance = wrap_tool_with_permissions(
|
|
156
|
+
tool_instance,
|
|
157
|
+
permissions_config,
|
|
158
|
+
agent_definition,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
chat.with_tool(tool_instance)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Register default tools for agents (unless disabled)
|
|
166
|
+
#
|
|
167
|
+
# Note: Memory tools are registered separately and are NOT affected by
|
|
168
|
+
# disable_default_tools, since they're configured via memory {} block.
|
|
169
|
+
#
|
|
170
|
+
# @param chat [AgentChat] The chat instance
|
|
171
|
+
# @param agent_name [Symbol] Agent name
|
|
172
|
+
# @param agent_definition [AgentDefinition] Agent definition
|
|
173
|
+
def register_default_tools(chat, agent_name:, agent_definition:)
|
|
174
|
+
# Get explicit tool names to avoid duplicates
|
|
175
|
+
explicit_tool_names = agent_definition.tools.map { |t| t[:name] }.to_set
|
|
176
|
+
|
|
177
|
+
# Register core default tools (unless disabled)
|
|
178
|
+
if agent_definition.disable_default_tools != true
|
|
179
|
+
DEFAULT_TOOLS.each do |tool_name|
|
|
180
|
+
register_tool_if_not_disabled(chat, tool_name, explicit_tool_names, agent_name, agent_definition)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Register scratchpad tools if enabled
|
|
184
|
+
if @swarm.scratchpad_enabled?
|
|
185
|
+
SCRATCHPAD_TOOLS.each do |tool_name|
|
|
186
|
+
register_tool_if_not_disabled(chat, tool_name, explicit_tool_names, agent_name, agent_definition)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Register plugin tools if plugin storage is enabled for this agent
|
|
192
|
+
# Plugin tools ARE affected by disable_default_tools (allows fine-grained control)
|
|
193
|
+
register_plugin_tools(chat, agent_name, agent_definition, explicit_tool_names)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Register a tool if not already explicit or disabled
|
|
197
|
+
def register_tool_if_not_disabled(chat, tool_name, explicit_tool_names, agent_name, agent_definition)
|
|
198
|
+
# Skip if already registered explicitly
|
|
199
|
+
return if explicit_tool_names.include?(tool_name)
|
|
200
|
+
|
|
201
|
+
# Skip if tool is in the disable list
|
|
202
|
+
return if tool_disabled?(tool_name, agent_definition.disable_default_tools)
|
|
203
|
+
|
|
204
|
+
tool_instance = create_tool_instance(tool_name, agent_name, agent_definition.directory)
|
|
205
|
+
|
|
206
|
+
# Resolve permissions for default tool
|
|
207
|
+
permissions_config = agent_definition.agent_permissions[tool_name] ||
|
|
208
|
+
agent_definition.default_permissions[tool_name]
|
|
209
|
+
|
|
210
|
+
# Wrap with permissions validator if configured
|
|
211
|
+
tool_instance = wrap_tool_with_permissions(
|
|
212
|
+
tool_instance,
|
|
213
|
+
permissions_config,
|
|
214
|
+
agent_definition,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
chat.with_tool(tool_instance)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Create a tool instance via plugin
|
|
221
|
+
#
|
|
222
|
+
# @param tool_name [Symbol] Tool name
|
|
223
|
+
# @param agent_name [Symbol] Agent name
|
|
224
|
+
# @param directory [String] Working directory
|
|
225
|
+
# @param chat [Agent::Chat, nil] Chat instance
|
|
226
|
+
# @param agent_definition [Agent::Definition, nil] Agent definition
|
|
227
|
+
# @return [RubyLLM::Tool] Tool instance
|
|
228
|
+
def create_plugin_tool(tool_name, agent_name, directory, chat, agent_definition)
|
|
229
|
+
plugin = PluginRegistry.plugin_for_tool(tool_name)
|
|
230
|
+
raise ConfigurationError, "Tool #{tool_name} is not provided by any plugin" unless plugin
|
|
231
|
+
|
|
232
|
+
# Get plugin storage for this agent
|
|
233
|
+
plugin_storages = @plugin_storages[plugin.name] || {}
|
|
234
|
+
storage = plugin_storages[agent_name]
|
|
235
|
+
|
|
236
|
+
# Build context for tool creation
|
|
237
|
+
context = {
|
|
238
|
+
agent_name: agent_name,
|
|
239
|
+
directory: directory,
|
|
240
|
+
storage: storage,
|
|
241
|
+
agent_definition: agent_definition,
|
|
242
|
+
chat: chat,
|
|
243
|
+
tool_configurator: self,
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
plugin.create_tool(tool_name, context)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Register plugin-provided tools for an agent
|
|
250
|
+
#
|
|
251
|
+
# Asks all plugins if they have tools to register for this agent.
|
|
252
|
+
#
|
|
253
|
+
# @param chat [Agent::Chat] Chat instance
|
|
254
|
+
# @param agent_name [Symbol] Agent name
|
|
255
|
+
# @param agent_definition [Agent::Definition] Agent definition
|
|
256
|
+
# @param explicit_tool_names [Set<Symbol>] Already-registered tool names
|
|
257
|
+
def register_plugin_tools(chat, agent_name, agent_definition, explicit_tool_names)
|
|
258
|
+
PluginRegistry.all.each do |plugin|
|
|
259
|
+
# Check if plugin has storage enabled for this agent
|
|
260
|
+
next unless plugin.storage_enabled?(agent_definition)
|
|
261
|
+
|
|
262
|
+
# Get plugin storage for this agent
|
|
263
|
+
plugin_storages = @plugin_storages[plugin.name] || {}
|
|
264
|
+
plugin_storages[agent_name]
|
|
265
|
+
|
|
266
|
+
# Register each tool provided by the plugin
|
|
267
|
+
plugin.tools.each do |tool_name|
|
|
268
|
+
# Skip if already registered explicitly
|
|
269
|
+
next if explicit_tool_names.include?(tool_name)
|
|
270
|
+
|
|
271
|
+
# Skip if tool is disabled via disable_default_tools
|
|
272
|
+
next if tool_disabled?(tool_name, agent_definition.disable_default_tools)
|
|
273
|
+
|
|
274
|
+
tool_instance = create_tool_instance(
|
|
275
|
+
tool_name,
|
|
276
|
+
agent_name,
|
|
277
|
+
agent_definition.directory,
|
|
278
|
+
chat: chat,
|
|
279
|
+
agent_definition: agent_definition,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# Resolve permissions for plugin tool
|
|
283
|
+
permissions_config = agent_definition.agent_permissions[tool_name] ||
|
|
284
|
+
agent_definition.default_permissions[tool_name]
|
|
285
|
+
|
|
286
|
+
# Wrap with permissions validator if configured
|
|
287
|
+
tool_instance = wrap_tool_with_permissions(
|
|
288
|
+
tool_instance,
|
|
289
|
+
permissions_config,
|
|
290
|
+
agent_definition,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
chat.with_tool(tool_instance)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Check if a tool should be disabled based on disable_default_tools config
|
|
299
|
+
#
|
|
300
|
+
# @param tool_name [Symbol] Tool name to check
|
|
301
|
+
# @param disable_config [nil, Boolean, Symbol, Array<Symbol>] Disable configuration
|
|
302
|
+
# @return [Boolean] True if tool should be disabled
|
|
303
|
+
def tool_disabled?(tool_name, disable_config)
|
|
304
|
+
return false if disable_config.nil?
|
|
305
|
+
|
|
306
|
+
if disable_config == true
|
|
307
|
+
# Disable all default tools
|
|
308
|
+
true
|
|
309
|
+
elsif disable_config.is_a?(Symbol)
|
|
310
|
+
# Single tool name
|
|
311
|
+
disable_config == tool_name
|
|
312
|
+
elsif disable_config.is_a?(Array)
|
|
313
|
+
# Disable only tools in the array
|
|
314
|
+
disable_config.include?(tool_name)
|
|
315
|
+
else
|
|
316
|
+
false
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Register agent delegation tools
|
|
321
|
+
#
|
|
322
|
+
# Creates delegation tools that allow one agent to call another.
|
|
323
|
+
#
|
|
324
|
+
# @param chat [AgentChat] The chat instance
|
|
325
|
+
# @param delegate_names [Array<Symbol>] Names of agents to delegate to
|
|
326
|
+
# @param agent_name [Symbol] Name of the agent doing the delegating
|
|
327
|
+
def register_delegation_tools(chat, delegate_names, agent_name:)
|
|
328
|
+
return if delegate_names.empty?
|
|
329
|
+
|
|
330
|
+
delegate_names.each do |delegate_name|
|
|
331
|
+
delegate_name = delegate_name.to_sym
|
|
332
|
+
|
|
333
|
+
unless @agents.key?(delegate_name)
|
|
334
|
+
raise ConfigurationError, "Agent delegates to unknown agent '#{delegate_name}'"
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Create a tool that delegates to the specified agent
|
|
338
|
+
delegate_agent = @agents[delegate_name]
|
|
339
|
+
delegate_definition = @agent_definitions[delegate_name]
|
|
340
|
+
|
|
341
|
+
tool = Tools::Delegate.new(
|
|
342
|
+
delegate_name: delegate_name.to_s,
|
|
343
|
+
delegate_description: delegate_definition.description,
|
|
344
|
+
delegate_chat: delegate_agent,
|
|
345
|
+
agent_name: agent_name,
|
|
346
|
+
swarm: @swarm,
|
|
347
|
+
hook_registry: @hook_registry,
|
|
348
|
+
delegating_chat: chat,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
chat.with_tool(tool)
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Pass 4: Configure hook system
|
|
356
|
+
#
|
|
357
|
+
# Setup the callback system for each agent.
|
|
358
|
+
def pass_4_configure_hooks
|
|
359
|
+
@agents.each do |agent_name, chat|
|
|
360
|
+
agent_definition = @agent_definitions[agent_name]
|
|
361
|
+
|
|
362
|
+
chat.setup_hooks(
|
|
363
|
+
registry: @hook_registry,
|
|
364
|
+
agent_definition: agent_definition,
|
|
365
|
+
swarm: @swarm,
|
|
366
|
+
) if chat.respond_to?(:setup_hooks)
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Pass 5: Apply YAML hooks if present
|
|
371
|
+
#
|
|
372
|
+
# If loaded from YAML, apply agent-specific hooks.
|
|
373
|
+
def pass_5_apply_yaml_hooks
|
|
374
|
+
return unless @config_for_hooks
|
|
375
|
+
|
|
376
|
+
@agents.each do |agent_name, chat|
|
|
377
|
+
agent_def = @config_for_hooks.agents[agent_name]
|
|
378
|
+
next unless agent_def&.hooks
|
|
379
|
+
|
|
380
|
+
HooksAdapter.apply_agent_hooks(chat, agent_name, agent_def.hooks, @swarm.name)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Create an AgentChat instance
|
|
385
|
+
#
|
|
386
|
+
# NOTE: This is dead code, left over from refactoring. AgentInitializer
|
|
387
|
+
# now handles agent creation. This should be removed in a cleanup pass.
|
|
388
|
+
#
|
|
389
|
+
# @param agent_name [Symbol] Agent name
|
|
390
|
+
# @param agent_definition [AgentDefinition] Agent definition
|
|
391
|
+
# @param tool_configurator [ToolConfigurator] Tool configurator
|
|
392
|
+
# @return [AgentChat] Configured chat instance
|
|
393
|
+
def create_agent_chat(agent_name, agent_definition, tool_configurator)
|
|
394
|
+
chat = AgentChat.new(
|
|
395
|
+
definition: agent_definition.to_h,
|
|
396
|
+
global_semaphore: @global_semaphore,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Set agent name on provider for logging (if provider supports it)
|
|
400
|
+
chat.provider.agent_name = agent_name if chat.provider.respond_to?(:agent_name=)
|
|
401
|
+
|
|
402
|
+
# Register tools
|
|
403
|
+
tool_configurator.register_all_tools(
|
|
404
|
+
chat: chat,
|
|
405
|
+
agent_name: agent_name,
|
|
406
|
+
agent_definition: agent_definition,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
# Register MCP servers if any
|
|
410
|
+
if agent_definition.mcp_servers.any?
|
|
411
|
+
mcp_configurator = McpConfigurator.new(@swarm)
|
|
412
|
+
mcp_configurator.register_mcp_servers(chat, agent_definition.mcp_servers, agent_name: agent_name)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
chat
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
end
|