autobyteus 1.1.2__py3-none-any.whl → 1.1.4__py3-none-any.whl
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.
- autobyteus/agent/agent.py +1 -1
- autobyteus/agent/bootstrap_steps/__init__.py +2 -0
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +2 -0
- autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +71 -0
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
- autobyteus/agent/context/agent_config.py +36 -5
- autobyteus/agent/events/worker_event_dispatcher.py +1 -2
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +1 -1
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +2 -2
- autobyteus/agent/handlers/tool_result_event_handler.py +48 -20
- autobyteus/agent/handlers/user_input_message_event_handler.py +1 -1
- autobyteus/agent/input_processor/__init__.py +1 -7
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +41 -12
- autobyteus/agent/message/context_file_type.py +6 -0
- autobyteus/agent/message/send_message_to.py +68 -99
- autobyteus/agent/phases/discover.py +2 -1
- autobyteus/agent/runtime/agent_worker.py +25 -34
- autobyteus/agent/shutdown_steps/__init__.py +17 -0
- autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +63 -0
- autobyteus/agent/shutdown_steps/base_shutdown_step.py +33 -0
- autobyteus/agent/shutdown_steps/llm_instance_cleanup_step.py +45 -0
- autobyteus/agent/shutdown_steps/mcp_server_cleanup_step.py +32 -0
- autobyteus/agent/tool_execution_result_processor/__init__.py +9 -0
- autobyteus/agent/tool_execution_result_processor/base_processor.py +46 -0
- autobyteus/agent/tool_execution_result_processor/processor_definition.py +36 -0
- autobyteus/agent/tool_execution_result_processor/processor_meta.py +36 -0
- autobyteus/agent/tool_execution_result_processor/processor_registry.py +70 -0
- autobyteus/agent/workspace/base_workspace.py +17 -2
- autobyteus/cli/__init__.py +1 -1
- autobyteus/cli/cli_display.py +1 -1
- autobyteus/cli/workflow_tui/__init__.py +4 -0
- autobyteus/cli/workflow_tui/app.py +210 -0
- autobyteus/cli/workflow_tui/state.py +189 -0
- autobyteus/cli/workflow_tui/widgets/__init__.py +6 -0
- autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/workflow_tui/widgets/focus_pane.py +335 -0
- autobyteus/cli/workflow_tui/widgets/logo.py +27 -0
- autobyteus/cli/workflow_tui/widgets/renderables.py +70 -0
- autobyteus/cli/workflow_tui/widgets/shared.py +51 -0
- autobyteus/cli/workflow_tui/widgets/status_bar.py +14 -0
- autobyteus/events/event_types.py +3 -0
- autobyteus/llm/api/lmstudio_llm.py +37 -0
- autobyteus/llm/api/openai_compatible_llm.py +20 -3
- autobyteus/llm/llm_factory.py +2 -0
- autobyteus/llm/lmstudio_provider.py +89 -0
- autobyteus/llm/providers.py +1 -0
- autobyteus/llm/token_counter/token_counter_factory.py +2 -0
- autobyteus/tools/__init__.py +2 -0
- autobyteus/tools/ask_user_input.py +2 -1
- autobyteus/tools/base_tool.py +2 -0
- autobyteus/tools/bash/bash_executor.py +2 -1
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +2 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +3 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -0
- autobyteus/tools/browser/standalone/google_search_ui.py +2 -0
- autobyteus/tools/browser/standalone/navigate_to.py +2 -0
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +3 -0
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +3 -0
- autobyteus/tools/browser/standalone/webpage_reader.py +2 -0
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +3 -0
- autobyteus/tools/file/file_reader.py +36 -9
- autobyteus/tools/file/file_writer.py +37 -9
- autobyteus/tools/functional_tool.py +5 -4
- autobyteus/tools/image_downloader.py +2 -0
- autobyteus/tools/mcp/__init__.py +10 -7
- autobyteus/tools/mcp/call_handlers/__init__.py +0 -2
- autobyteus/tools/mcp/config_service.py +1 -6
- autobyteus/tools/mcp/factory.py +12 -26
- autobyteus/tools/mcp/server/__init__.py +16 -0
- autobyteus/tools/mcp/server/base_managed_mcp_server.py +139 -0
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +29 -0
- autobyteus/tools/mcp/server/proxy.py +36 -0
- autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +33 -0
- autobyteus/tools/mcp/server_instance_manager.py +93 -0
- autobyteus/tools/mcp/tool.py +28 -46
- autobyteus/tools/mcp/tool_registrar.py +179 -0
- autobyteus/tools/mcp/types.py +10 -21
- autobyteus/tools/pdf_downloader.py +2 -1
- autobyteus/tools/registry/tool_definition.py +20 -7
- autobyteus/tools/registry/tool_registry.py +75 -28
- autobyteus/tools/timer.py +2 -0
- autobyteus/tools/tool_category.py +14 -4
- autobyteus/tools/tool_meta.py +6 -1
- autobyteus/tools/tool_origin.py +10 -0
- autobyteus/workflow/agentic_workflow.py +93 -0
- autobyteus/{agent/workflow → workflow}/base_agentic_workflow.py +19 -27
- autobyteus/workflow/bootstrap_steps/__init__.py +20 -0
- autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +34 -0
- autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +23 -0
- autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +108 -0
- autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +50 -0
- autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +25 -0
- autobyteus/workflow/context/__init__.py +17 -0
- autobyteus/workflow/context/team_manager.py +147 -0
- autobyteus/workflow/context/workflow_config.py +30 -0
- autobyteus/workflow/context/workflow_context.py +61 -0
- autobyteus/workflow/context/workflow_node_config.py +76 -0
- autobyteus/workflow/context/workflow_runtime_state.py +53 -0
- autobyteus/workflow/events/__init__.py +29 -0
- autobyteus/workflow/events/workflow_event_dispatcher.py +39 -0
- autobyteus/workflow/events/workflow_events.py +53 -0
- autobyteus/workflow/events/workflow_input_event_queue_manager.py +21 -0
- autobyteus/workflow/exceptions.py +8 -0
- autobyteus/workflow/factory/__init__.py +9 -0
- autobyteus/workflow/factory/workflow_factory.py +99 -0
- autobyteus/workflow/handlers/__init__.py +19 -0
- autobyteus/workflow/handlers/base_workflow_event_handler.py +16 -0
- autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +27 -0
- autobyteus/workflow/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +39 -0
- autobyteus/workflow/handlers/workflow_event_handler_registry.py +23 -0
- autobyteus/workflow/phases/__init__.py +11 -0
- autobyteus/workflow/phases/workflow_operational_phase.py +19 -0
- autobyteus/workflow/phases/workflow_phase_manager.py +48 -0
- autobyteus/workflow/runtime/__init__.py +13 -0
- autobyteus/workflow/runtime/workflow_runtime.py +82 -0
- autobyteus/workflow/runtime/workflow_worker.py +117 -0
- autobyteus/workflow/shutdown_steps/__init__.py +17 -0
- autobyteus/workflow/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/workflow/shutdown_steps/base_workflow_shutdown_step.py +16 -0
- autobyteus/workflow/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/workflow/shutdown_steps/sub_workflow_shutdown_step.py +41 -0
- autobyteus/workflow/shutdown_steps/workflow_shutdown_orchestrator.py +35 -0
- autobyteus/workflow/streaming/__init__.py +26 -0
- autobyteus/workflow/streaming/agent_event_bridge.py +48 -0
- autobyteus/workflow/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/workflow/streaming/workflow_event_bridge.py +50 -0
- autobyteus/workflow/streaming/workflow_event_notifier.py +83 -0
- autobyteus/workflow/streaming/workflow_event_stream.py +33 -0
- autobyteus/workflow/streaming/workflow_stream_event_payloads.py +28 -0
- autobyteus/workflow/streaming/workflow_stream_events.py +45 -0
- autobyteus/workflow/utils/__init__.py +9 -0
- autobyteus/workflow/utils/wait_for_idle.py +46 -0
- autobyteus/workflow/workflow_builder.py +151 -0
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/METADATA +16 -13
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/RECORD +156 -75
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/top_level.txt +1 -0
- examples/__init__.py +1 -0
- examples/discover_phase_transitions.py +104 -0
- examples/run_browser_agent.py +260 -0
- examples/run_google_slides_agent.py +286 -0
- examples/run_mcp_browser_client.py +174 -0
- examples/run_mcp_google_slides_client.py +270 -0
- examples/run_mcp_list_tools.py +189 -0
- examples/run_poem_writer.py +274 -0
- examples/run_sqlite_agent.py +293 -0
- examples/workflow/__init__.py +1 -0
- examples/workflow/run_basic_research_workflow.py +189 -0
- examples/workflow/run_code_review_workflow.py +269 -0
- examples/workflow/run_debate_workflow.py +212 -0
- examples/workflow/run_workflow_with_tui.py +153 -0
- autobyteus/agent/context/agent_phase_manager.py +0 -264
- autobyteus/agent/context/phases.py +0 -49
- autobyteus/agent/group/__init__.py +0 -0
- autobyteus/agent/group/agent_group.py +0 -164
- autobyteus/agent/group/agent_group_context.py +0 -81
- autobyteus/agent/input_processor/content_prefixing_input_processor.py +0 -41
- autobyteus/agent/input_processor/metadata_appending_input_processor.py +0 -34
- autobyteus/agent/input_processor/passthrough_input_processor.py +0 -33
- autobyteus/agent/workflow/__init__.py +0 -11
- autobyteus/agent/workflow/agentic_workflow.py +0 -89
- autobyteus/tools/mcp/call_handlers/sse_handler.py +0 -22
- autobyteus/tools/mcp/registrar.py +0 -323
- autobyteus/workflow/simple_task.py +0 -98
- autobyteus/workflow/task.py +0 -147
- autobyteus/workflow/workflow.py +0 -49
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/agent/group/agent_group_context.py
|
|
2
|
-
import logging
|
|
3
|
-
from typing import List, Dict, Optional, TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
if TYPE_CHECKING:
|
|
6
|
-
from autobyteus.agent.agent import Agent
|
|
7
|
-
|
|
8
|
-
logger = logging.getLogger(__name__)
|
|
9
|
-
|
|
10
|
-
class AgentGroupContext:
|
|
11
|
-
"""
|
|
12
|
-
Stores contextual information about an agent group, including its ID,
|
|
13
|
-
member agents, and the designated coordinator. Provides methods to
|
|
14
|
-
discover agents within the group.
|
|
15
|
-
"""
|
|
16
|
-
def __init__(self,
|
|
17
|
-
group_id: str,
|
|
18
|
-
agents: List['Agent'],
|
|
19
|
-
coordinator_agent_id: str):
|
|
20
|
-
"""
|
|
21
|
-
Initializes the AgentGroupContext.
|
|
22
|
-
"""
|
|
23
|
-
if not group_id or not isinstance(group_id, str):
|
|
24
|
-
raise ValueError("AgentGroupContext requires a non-empty string 'group_id'.")
|
|
25
|
-
if not coordinator_agent_id or not isinstance(coordinator_agent_id, str):
|
|
26
|
-
raise ValueError("AgentGroupContext requires a non-empty string 'coordinator_agent_id'.")
|
|
27
|
-
if not agents:
|
|
28
|
-
raise ValueError("AgentGroupContext requires a non-empty list of 'agents'.")
|
|
29
|
-
|
|
30
|
-
from autobyteus.agent.agent import Agent as AgentClassRef
|
|
31
|
-
if not all(isinstance(agent, AgentClassRef) for agent in agents):
|
|
32
|
-
raise TypeError("All items in 'agents' list must be instances of the 'Agent' class.")
|
|
33
|
-
|
|
34
|
-
self.group_id: str = group_id
|
|
35
|
-
self._agents_by_id: Dict[str, 'Agent'] = {agent.agent_id: agent for agent in agents}
|
|
36
|
-
self._coordinator_agent_id: str = coordinator_agent_id
|
|
37
|
-
|
|
38
|
-
if self._coordinator_agent_id not in self._agents_by_id:
|
|
39
|
-
logger.error(f"Coordinator agent with ID '{self._coordinator_agent_id}' not found in the provided list of agents for group '{self.group_id}'.")
|
|
40
|
-
|
|
41
|
-
logger.info(f"AgentGroupContext initialized for group_id '{self.group_id}'.")
|
|
42
|
-
|
|
43
|
-
def get_agent(self, agent_id: str) -> Optional['Agent']:
|
|
44
|
-
"""
|
|
45
|
-
Retrieves an agent from the group by its unique agent_id.
|
|
46
|
-
"""
|
|
47
|
-
return self._agents_by_id.get(agent_id)
|
|
48
|
-
|
|
49
|
-
def get_agents_by_role(self, role_name: str) -> List['Agent']:
|
|
50
|
-
"""
|
|
51
|
-
Retrieves all agents within the group that match the specified role name.
|
|
52
|
-
"""
|
|
53
|
-
if not isinstance(role_name, str):
|
|
54
|
-
logger.warning(f"Attempted to get_agents_by_role with non-string role_name: {role_name} in group '{self.group_id}'.")
|
|
55
|
-
return []
|
|
56
|
-
|
|
57
|
-
matching_agents: List['Agent'] = [
|
|
58
|
-
agent for agent in self._agents_by_id.values()
|
|
59
|
-
if agent.context and agent.context.config and agent.context.config.role == role_name
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
if not matching_agents:
|
|
63
|
-
logger.debug(f"No agents found with role '{role_name}' in group '{self.group_id}'.")
|
|
64
|
-
return matching_agents
|
|
65
|
-
|
|
66
|
-
def get_coordinator_agent(self) -> Optional['Agent']:
|
|
67
|
-
"""
|
|
68
|
-
Retrieves the designated coordinator agent for this group.
|
|
69
|
-
"""
|
|
70
|
-
return self.get_agent(self._coordinator_agent_id)
|
|
71
|
-
|
|
72
|
-
def get_all_agents(self) -> List['Agent']:
|
|
73
|
-
"""
|
|
74
|
-
Retrieves all agents currently part of this group.
|
|
75
|
-
"""
|
|
76
|
-
return list(self._agents_by_id.values())
|
|
77
|
-
|
|
78
|
-
def __repr__(self) -> str:
|
|
79
|
-
return (f"<AgentGroupContext group_id='{self.group_id}', "
|
|
80
|
-
f"num_agents={len(self._agents_by_id)}, "
|
|
81
|
-
f"coordinator_id='{self._coordinator_agent_id}'>")
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/agent/input_processor/content_prefixing_input_processor.py
|
|
2
|
-
import logging
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from .base_user_input_processor import BaseAgentUserInputMessageProcessor
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
-
from autobyteus.agent.context import AgentContext # Composite AgentContext
|
|
10
|
-
from autobyteus.agent.events import UserMessageReceivedEvent
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
class ContentPrefixingInputProcessor(BaseAgentUserInputMessageProcessor):
|
|
15
|
-
"""
|
|
16
|
-
A processor that adds a predefined prefix to the message content.
|
|
17
|
-
The prefix is defined by the agent's custom_data (in AgentRuntimeState) or a default.
|
|
18
|
-
Example prefix key in custom_data: "content_prefix"
|
|
19
|
-
"""
|
|
20
|
-
DEFAULT_PREFIX = "[Processed Message] "
|
|
21
|
-
|
|
22
|
-
async def process(self,
|
|
23
|
-
message: 'AgentInputUserMessage',
|
|
24
|
-
context: 'AgentContext',
|
|
25
|
-
triggering_event: 'UserMessageReceivedEvent') -> 'AgentInputUserMessage':
|
|
26
|
-
"""
|
|
27
|
-
Handles the message by prefixing its content.
|
|
28
|
-
The 'triggering_event' parameter is ignored by this processor.
|
|
29
|
-
"""
|
|
30
|
-
agent_id = context.agent_id # Convenience property
|
|
31
|
-
logger.debug(f"Agent '{agent_id}': ContentPrefixingInputProcessor processing message.")
|
|
32
|
-
|
|
33
|
-
# Access custom_data via convenience property (or context.state.custom_data)
|
|
34
|
-
prefix = context.custom_data.get("content_prefix", self.DEFAULT_PREFIX)
|
|
35
|
-
if not isinstance(prefix, str):
|
|
36
|
-
logger.warning(f"Agent '{agent_id}': 'content_prefix' in custom_data is not a string. Using default prefix. Found: {type(prefix)}")
|
|
37
|
-
prefix = self.DEFAULT_PREFIX
|
|
38
|
-
|
|
39
|
-
message.content = prefix + message.content
|
|
40
|
-
logger.info(f"Agent '{agent_id}': Prefixed message content with '{prefix}'.")
|
|
41
|
-
return message
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/agent/input_processor/metadata_appending_input_processor.py
|
|
2
|
-
import logging
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from .base_user_input_processor import BaseAgentUserInputMessageProcessor
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
-
from autobyteus.agent.context import AgentContext # Composite AgentContext
|
|
10
|
-
from autobyteus.agent.events import UserMessageReceivedEvent
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
class MetadataAppendingInputProcessor(BaseAgentUserInputMessageProcessor):
|
|
15
|
-
"""
|
|
16
|
-
A processor that appends fixed metadata to the message.
|
|
17
|
-
Example: Appends agent_id and config_name to metadata.
|
|
18
|
-
"""
|
|
19
|
-
async def process(self,
|
|
20
|
-
message: 'AgentInputUserMessage',
|
|
21
|
-
context: 'AgentContext',
|
|
22
|
-
triggering_event: 'UserMessageReceivedEvent') -> 'AgentInputUserMessage':
|
|
23
|
-
"""
|
|
24
|
-
Handles the message by appending metadata.
|
|
25
|
-
The 'triggering_event' parameter is ignored by this processor.
|
|
26
|
-
"""
|
|
27
|
-
agent_id = context.agent_id
|
|
28
|
-
config_name = context.config.name
|
|
29
|
-
|
|
30
|
-
logger.debug(f"Agent '{agent_id}': MetadataAppendingInputProcessor processing message.")
|
|
31
|
-
message.metadata["processed_by_agent_id"] = agent_id
|
|
32
|
-
message.metadata["processed_with_config_name"] = config_name
|
|
33
|
-
logger.info(f"Agent '{agent_id}': Appended 'processed_by_agent_id' and 'processed_with_config_name' to message metadata.")
|
|
34
|
-
return message
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/agent/input_processor/passthrough_input_processor.py
|
|
2
|
-
import logging
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from .base_user_input_processor import BaseAgentUserInputMessageProcessor
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
-
from autobyteus.agent.context import AgentContext # Composite AgentContext
|
|
10
|
-
from autobyteus.agent.events import UserMessageReceivedEvent
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
class PassthroughInputProcessor(BaseAgentUserInputMessageProcessor):
|
|
15
|
-
"""
|
|
16
|
-
A processor that returns the message unchanged.
|
|
17
|
-
Can be used as a default or for testing.
|
|
18
|
-
"""
|
|
19
|
-
@classmethod
|
|
20
|
-
def get_name(cls) -> str:
|
|
21
|
-
return "PassthroughInputProcessor"
|
|
22
|
-
|
|
23
|
-
async def process(self,
|
|
24
|
-
message: 'AgentInputUserMessage',
|
|
25
|
-
context: 'AgentContext',
|
|
26
|
-
triggering_event: 'UserMessageReceivedEvent') -> 'AgentInputUserMessage':
|
|
27
|
-
"""
|
|
28
|
-
Handles the message by returning it without modification.
|
|
29
|
-
The 'triggering_event' parameter is ignored by this processor.
|
|
30
|
-
"""
|
|
31
|
-
agent_id = context.agent_id # Convenience property
|
|
32
|
-
logger.debug(f"Agent '{agent_id}': PassthroughInputProcessor received message, returning as is.")
|
|
33
|
-
return message
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/agent/workflow/__init__.py
|
|
2
|
-
"""
|
|
3
|
-
Components for defining and running agentic workflows.
|
|
4
|
-
"""
|
|
5
|
-
from .agentic_workflow import AgenticWorkflow
|
|
6
|
-
from .base_agentic_workflow import BaseAgenticWorkflow
|
|
7
|
-
|
|
8
|
-
__all__ = [
|
|
9
|
-
"AgenticWorkflow",
|
|
10
|
-
"BaseAgenticWorkflow",
|
|
11
|
-
]
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/agent/workflow/agentic_workflow.py
|
|
2
|
-
import logging
|
|
3
|
-
import uuid
|
|
4
|
-
from typing import List, Dict, Optional, Any, cast
|
|
5
|
-
|
|
6
|
-
from autobyteus.agent.context.agent_config import AgentConfig
|
|
7
|
-
from autobyteus.agent.group.agent_group import AgentGroup
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
class AgenticWorkflow:
|
|
12
|
-
"""
|
|
13
|
-
A concrete class for defining and running multi-agent workflows declaratively.
|
|
14
|
-
It internally manages an AgentGroup and provides a user-friendly interface
|
|
15
|
-
to process tasks.
|
|
16
|
-
"""
|
|
17
|
-
def __init__(self,
|
|
18
|
-
agent_configs: List[AgentConfig],
|
|
19
|
-
coordinator_config_name: str,
|
|
20
|
-
workflow_id: Optional[str] = None,
|
|
21
|
-
input_param_name: str = "input",
|
|
22
|
-
):
|
|
23
|
-
"""
|
|
24
|
-
Initializes the AgenticWorkflow.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
agent_configs: List of pre-made AgentConfig instances for the agents in this workflow.
|
|
28
|
-
coordinator_config_name: Name of the agent config to be used as coordinator.
|
|
29
|
-
workflow_id: Optional. A unique ID for this workflow instance. Auto-generated if None.
|
|
30
|
-
input_param_name: The key to use in `process(**kwargs)` to find the initial
|
|
31
|
-
input string for the coordinator. Defaults to "input".
|
|
32
|
-
"""
|
|
33
|
-
self.workflow_id: str = workflow_id or f"workflow_{uuid.uuid4()}"
|
|
34
|
-
self._input_param_name: str = input_param_name
|
|
35
|
-
|
|
36
|
-
logger.info(f"Initializing AgenticWorkflow '{self.workflow_id}'. "
|
|
37
|
-
f"Input parameter name for process(): '{self._input_param_name}'.")
|
|
38
|
-
|
|
39
|
-
# The AgentGroup is now initialized directly with the user-provided configs.
|
|
40
|
-
self.agent_group: AgentGroup = AgentGroup(
|
|
41
|
-
agent_configs=agent_configs,
|
|
42
|
-
coordinator_config_name=coordinator_config_name,
|
|
43
|
-
group_id=f"group_for_{self.workflow_id}",
|
|
44
|
-
)
|
|
45
|
-
logger.info(f"AgenticWorkflow '{self.workflow_id}' successfully instantiated internal AgentGroup '{self.agent_group.group_id}'.")
|
|
46
|
-
|
|
47
|
-
async def process(self, **kwargs: Any) -> Any:
|
|
48
|
-
logger.info(f"AgenticWorkflow '{self.workflow_id}' received process request with kwargs: {list(kwargs.keys())}")
|
|
49
|
-
|
|
50
|
-
initial_input_content = kwargs.get(self._input_param_name)
|
|
51
|
-
if initial_input_content is None:
|
|
52
|
-
raise ValueError(f"Required input parameter '{self._input_param_name}' not found in process() arguments.")
|
|
53
|
-
if not isinstance(initial_input_content, str):
|
|
54
|
-
raise ValueError(f"Input parameter '{self._input_param_name}' must be a string, "
|
|
55
|
-
f"got {type(initial_input_content).__name__}.")
|
|
56
|
-
|
|
57
|
-
user_id: Optional[str] = cast(Optional[str], kwargs.get("user_id")) if isinstance(kwargs.get("user_id"), str) else None
|
|
58
|
-
|
|
59
|
-
logger.debug(f"AgenticWorkflow '{self.workflow_id}': Extracted initial input for coordinator: '{initial_input_content[:100]}...'")
|
|
60
|
-
|
|
61
|
-
result = await self.agent_group.process_task_for_coordinator(
|
|
62
|
-
initial_input_content=initial_input_content,
|
|
63
|
-
user_id=user_id
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
return result
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
async def start(self) -> None:
|
|
70
|
-
logger.info(f"AgenticWorkflow '{self.workflow_id}' received start() request. Delegating to AgentGroup.")
|
|
71
|
-
await self.agent_group.start()
|
|
72
|
-
|
|
73
|
-
async def stop(self, timeout: float = 10.0) -> None:
|
|
74
|
-
logger.info(f"AgenticWorkflow '{self.workflow_id}' received stop() request. Delegating to AgentGroup.")
|
|
75
|
-
await self.agent_group.stop(timeout)
|
|
76
|
-
|
|
77
|
-
@property
|
|
78
|
-
def is_running(self) -> bool:
|
|
79
|
-
return self.agent_group.is_running
|
|
80
|
-
|
|
81
|
-
@property
|
|
82
|
-
def group_id(self) -> str:
|
|
83
|
-
return self.agent_group.group_id
|
|
84
|
-
|
|
85
|
-
def __repr__(self) -> str:
|
|
86
|
-
return (f"<AgenticWorkflow workflow_id='{self.workflow_id}', "
|
|
87
|
-
f"group_id='{self.agent_group.group_id}', "
|
|
88
|
-
f"coordinator='{self.agent_group.coordinator_config_name}', "
|
|
89
|
-
f"is_running={self.is_running}>")
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/tools/mcp/call_handlers/sse_handler.py
|
|
2
|
-
import logging
|
|
3
|
-
from typing import Dict, Any, TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from .base_handler import McpCallHandler
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from ..types import BaseMcpConfig
|
|
9
|
-
|
|
10
|
-
logger = logging.getLogger(__name__)
|
|
11
|
-
|
|
12
|
-
class SseMcpCallHandler(McpCallHandler):
|
|
13
|
-
"""Placeholder handler for MCP tool calls over SSE."""
|
|
14
|
-
|
|
15
|
-
async def handle_call(
|
|
16
|
-
self,
|
|
17
|
-
config: 'BaseMcpConfig',
|
|
18
|
-
remote_tool_name: str,
|
|
19
|
-
arguments: Dict[str, Any]
|
|
20
|
-
) -> Any:
|
|
21
|
-
logger.warning(f"SseMcpCallHandler for server '{config.server_id}' is a placeholder and not fully implemented.")
|
|
22
|
-
raise NotImplementedError(f"SSE transport is not fully implemented for tool call to '{remote_tool_name}'.")
|
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/tools/mcp/registrar.py
|
|
2
|
-
import logging
|
|
3
|
-
from typing import Any, Dict, List, Optional, Union
|
|
4
|
-
|
|
5
|
-
# Import the new handler architecture components
|
|
6
|
-
from .call_handlers import (
|
|
7
|
-
McpCallHandler,
|
|
8
|
-
StdioMcpCallHandler,
|
|
9
|
-
StreamableHttpMcpCallHandler,
|
|
10
|
-
SseMcpCallHandler
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
# Consolidated imports from the autobyteus.autobyteus.mcp package public API
|
|
14
|
-
from autobyteus.tools.mcp import (
|
|
15
|
-
McpConfigService,
|
|
16
|
-
McpSchemaMapper,
|
|
17
|
-
McpToolFactory,
|
|
18
|
-
McpTransportType,
|
|
19
|
-
BaseMcpConfig
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
from autobyteus.tools.registry import ToolRegistry, ToolDefinition
|
|
23
|
-
from autobyteus.tools.tool_category import ToolCategory
|
|
24
|
-
from autobyteus.utils.singleton import SingletonMeta
|
|
25
|
-
from mcp import types as mcp_types
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
logger = logging.getLogger(__name__)
|
|
29
|
-
|
|
30
|
-
class McpToolRegistrar(metaclass=SingletonMeta):
|
|
31
|
-
"""
|
|
32
|
-
Orchestrates the discovery of remote MCP tools and their registration
|
|
33
|
-
with the AutoByteUs ToolRegistry using a handler-based architecture.
|
|
34
|
-
This class is a singleton.
|
|
35
|
-
"""
|
|
36
|
-
def __init__(self):
|
|
37
|
-
"""
|
|
38
|
-
Initializes the McpToolRegistrar singleton.
|
|
39
|
-
It retrieves singleton instances of its service dependencies and initializes
|
|
40
|
-
its internal state for tracking registered tools.
|
|
41
|
-
"""
|
|
42
|
-
self._config_service: McpConfigService = McpConfigService()
|
|
43
|
-
self._tool_registry: ToolRegistry = ToolRegistry()
|
|
44
|
-
|
|
45
|
-
# The handler registry maps a transport type to a reusable handler instance.
|
|
46
|
-
self._handler_registry: Dict[McpTransportType, McpCallHandler] = {
|
|
47
|
-
McpTransportType.STDIO: StdioMcpCallHandler(),
|
|
48
|
-
McpTransportType.STREAMABLE_HTTP: StreamableHttpMcpCallHandler(),
|
|
49
|
-
McpTransportType.SSE: SseMcpCallHandler(),
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
# Internal state to track which ToolDefinitions were registered from which server.
|
|
53
|
-
self._registered_tools_by_server: Dict[str, List[ToolDefinition]] = {}
|
|
54
|
-
|
|
55
|
-
logger.info(f"McpToolRegistrar initialized with {len(self._handler_registry)} call handlers.")
|
|
56
|
-
|
|
57
|
-
def _create_tool_definition_from_remote(
|
|
58
|
-
self,
|
|
59
|
-
remote_tool: mcp_types.Tool,
|
|
60
|
-
server_config: BaseMcpConfig,
|
|
61
|
-
handler: McpCallHandler,
|
|
62
|
-
schema_mapper: McpSchemaMapper
|
|
63
|
-
) -> ToolDefinition:
|
|
64
|
-
"""
|
|
65
|
-
Maps a single remote tool from an MCP server to an AutoByteUs ToolDefinition.
|
|
66
|
-
This is a helper method to centralize the mapping logic.
|
|
67
|
-
"""
|
|
68
|
-
if hasattr(remote_tool, 'model_dump_json'):
|
|
69
|
-
logger.debug(f"Processing remote tool from server '{server_config.server_id}':\n{remote_tool.model_dump_json(indent=2)}")
|
|
70
|
-
|
|
71
|
-
actual_arg_schema = schema_mapper.map_to_autobyteus_schema(remote_tool.inputSchema)
|
|
72
|
-
actual_desc = remote_tool.description
|
|
73
|
-
|
|
74
|
-
registered_name = remote_tool.name
|
|
75
|
-
if server_config.tool_name_prefix:
|
|
76
|
-
registered_name = f"{server_config.tool_name_prefix.rstrip('_')}_{remote_tool.name}"
|
|
77
|
-
|
|
78
|
-
tool_factory = McpToolFactory(
|
|
79
|
-
mcp_server_config=server_config,
|
|
80
|
-
mcp_remote_tool_name=remote_tool.name,
|
|
81
|
-
mcp_call_handler=handler,
|
|
82
|
-
registered_tool_name=registered_name,
|
|
83
|
-
tool_description=actual_desc,
|
|
84
|
-
tool_argument_schema=actual_arg_schema
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
tool_def = ToolDefinition(
|
|
88
|
-
name=registered_name,
|
|
89
|
-
description=actual_desc,
|
|
90
|
-
argument_schema=actual_arg_schema,
|
|
91
|
-
category=ToolCategory.MCP,
|
|
92
|
-
custom_factory=tool_factory.create_tool,
|
|
93
|
-
config_schema=None,
|
|
94
|
-
tool_class=None
|
|
95
|
-
)
|
|
96
|
-
return tool_def
|
|
97
|
-
|
|
98
|
-
async def discover_and_register_tools(self, mcp_config: Optional[Union[BaseMcpConfig, Dict[str, Any]]] = None) -> List[ToolDefinition]:
|
|
99
|
-
"""
|
|
100
|
-
Discovers tools from MCP servers and registers them.
|
|
101
|
-
|
|
102
|
-
If `mcp_config` is provided (as a validated object or a raw dictionary),
|
|
103
|
-
it discovers tools only from that specific server, unregistering any
|
|
104
|
-
of its old tools first.
|
|
105
|
-
|
|
106
|
-
If `mcp_config` is None, it unregisters all existing MCP tools and then
|
|
107
|
-
discovers tools from all enabled servers found in the McpConfigService.
|
|
108
|
-
|
|
109
|
-
Returns:
|
|
110
|
-
A list of ToolDefinition objects for all tools that were successfully
|
|
111
|
-
discovered and registered during this call.
|
|
112
|
-
"""
|
|
113
|
-
configs_to_process: List[BaseMcpConfig]
|
|
114
|
-
|
|
115
|
-
if mcp_config:
|
|
116
|
-
validated_config: BaseMcpConfig
|
|
117
|
-
# If the user provided a raw dictionary, parse and add it via the service.
|
|
118
|
-
if isinstance(mcp_config, dict):
|
|
119
|
-
try:
|
|
120
|
-
validated_config = self._config_service.load_config(mcp_config)
|
|
121
|
-
except ValueError as e:
|
|
122
|
-
logger.error(f"Failed to parse provided MCP config dictionary: {e}")
|
|
123
|
-
raise
|
|
124
|
-
# If a validated object was passed, add it to the service to ensure it's known.
|
|
125
|
-
elif isinstance(mcp_config, BaseMcpConfig):
|
|
126
|
-
validated_config = self._config_service.add_config(mcp_config)
|
|
127
|
-
else:
|
|
128
|
-
raise TypeError(f"mcp_config must be a BaseMcpConfig object or a dictionary, not {type(mcp_config)}.")
|
|
129
|
-
|
|
130
|
-
logger.info(f"Starting targeted MCP tool discovery for server: {validated_config.server_id}")
|
|
131
|
-
# Unregister any existing tools from this server before re-discovering.
|
|
132
|
-
self.unregister_tools_from_server(validated_config.server_id)
|
|
133
|
-
configs_to_process = [validated_config]
|
|
134
|
-
else:
|
|
135
|
-
logger.info("Starting full MCP tool discovery and registration process. Unregistering all existing MCP tools first.")
|
|
136
|
-
# Get a copy of all server IDs that have tools registered.
|
|
137
|
-
all_server_ids = list(self._registered_tools_by_server.keys())
|
|
138
|
-
for server_id in all_server_ids:
|
|
139
|
-
self.unregister_tools_from_server(server_id)
|
|
140
|
-
|
|
141
|
-
# The registrar's internal state should now be empty, but a clear() is a good safeguard.
|
|
142
|
-
self._registered_tools_by_server.clear()
|
|
143
|
-
|
|
144
|
-
configs_to_process = self._config_service.get_all_configs()
|
|
145
|
-
|
|
146
|
-
if not configs_to_process:
|
|
147
|
-
logger.info("No MCP server configurations to process. Skipping discovery.")
|
|
148
|
-
return []
|
|
149
|
-
|
|
150
|
-
schema_mapper = McpSchemaMapper()
|
|
151
|
-
registered_tool_definitions: List[ToolDefinition] = []
|
|
152
|
-
for server_config in configs_to_process:
|
|
153
|
-
if not server_config.enabled:
|
|
154
|
-
logger.info(f"MCP server '{server_config.server_id}' is disabled. Skipping.")
|
|
155
|
-
continue
|
|
156
|
-
|
|
157
|
-
logger.info(f"Discovering tools from MCP server: '{server_config.server_id}' ({server_config.transport_type.value})")
|
|
158
|
-
try:
|
|
159
|
-
handler = self._handler_registry.get(server_config.transport_type)
|
|
160
|
-
if not handler:
|
|
161
|
-
logger.error(f"No MCP call handler found for transport type '{server_config.transport_type.value}' on server '{server_config.server_id}'.")
|
|
162
|
-
continue
|
|
163
|
-
|
|
164
|
-
remote_tools_result = await handler.handle_call(
|
|
165
|
-
config=server_config,
|
|
166
|
-
remote_tool_name="list_tools",
|
|
167
|
-
arguments={}
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
if not isinstance(remote_tools_result, mcp_types.ListToolsResult):
|
|
171
|
-
logger.error(f"Expected ListToolsResult from handler for 'list_tools', but got {type(remote_tools_result)}. Skipping server '{server_config.server_id}'.")
|
|
172
|
-
continue
|
|
173
|
-
|
|
174
|
-
actual_remote_tools: list[mcp_types.Tool] = remote_tools_result.tools
|
|
175
|
-
logger.info(f"Discovered {len(actual_remote_tools)} tools from server '{server_config.server_id}'.")
|
|
176
|
-
|
|
177
|
-
for remote_tool in actual_remote_tools:
|
|
178
|
-
try:
|
|
179
|
-
tool_def = self._create_tool_definition_from_remote(remote_tool, server_config, handler, schema_mapper)
|
|
180
|
-
|
|
181
|
-
self._tool_registry.register_tool(tool_def)
|
|
182
|
-
self._registered_tools_by_server.setdefault(server_config.server_id, []).append(tool_def)
|
|
183
|
-
registered_tool_definitions.append(tool_def)
|
|
184
|
-
|
|
185
|
-
logger.info(f"Successfully registered MCP tool '{remote_tool.name}' from server '{server_config.server_id}' as '{tool_def.name}'.")
|
|
186
|
-
|
|
187
|
-
except Exception as e_tool:
|
|
188
|
-
logger.error(f"Failed to process or register remote tool '{remote_tool.name}' from server '{server_config.server_id}': {e_tool}", exc_info=True)
|
|
189
|
-
|
|
190
|
-
except Exception as e_server:
|
|
191
|
-
logger.error(f"Failed to discover tools from MCP server '{server_config.server_id}': {e_server}", exc_info=True)
|
|
192
|
-
|
|
193
|
-
logger.info(f"MCP tool discovery and registration process completed. Total tools registered: {len(registered_tool_definitions)}.")
|
|
194
|
-
return registered_tool_definitions
|
|
195
|
-
|
|
196
|
-
async def list_remote_tools(self, mcp_config: Union[BaseMcpConfig, Dict[str, Any]]) -> List[ToolDefinition]:
|
|
197
|
-
"""
|
|
198
|
-
Previews tools from a remote MCP server without registering them or storing the configuration.
|
|
199
|
-
This is a stateless "dry-run" or "preview" operation.
|
|
200
|
-
|
|
201
|
-
Args:
|
|
202
|
-
mcp_config: A single MCP server configuration, as a validated object or a raw dictionary.
|
|
203
|
-
|
|
204
|
-
Returns:
|
|
205
|
-
A list of ToolDefinition objects for all tools discovered on the server.
|
|
206
|
-
|
|
207
|
-
Raises:
|
|
208
|
-
TypeError: If mcp_config is not a supported type.
|
|
209
|
-
ValueError: If the provided configuration is invalid.
|
|
210
|
-
RuntimeError: If the connection or tool call to the remote server fails.
|
|
211
|
-
"""
|
|
212
|
-
validated_config: BaseMcpConfig
|
|
213
|
-
if isinstance(mcp_config, dict):
|
|
214
|
-
# Use the static method to parse the config without storing it in the service.
|
|
215
|
-
validated_config = McpConfigService.parse_mcp_config_dict(mcp_config)
|
|
216
|
-
elif isinstance(mcp_config, BaseMcpConfig):
|
|
217
|
-
validated_config = mcp_config
|
|
218
|
-
else:
|
|
219
|
-
raise TypeError(f"mcp_config must be a BaseMcpConfig object or a dictionary, not {type(mcp_config)}.")
|
|
220
|
-
|
|
221
|
-
logger.info(f"Previewing tools from MCP server: '{validated_config.server_id}' ({validated_config.transport_type.value})")
|
|
222
|
-
|
|
223
|
-
schema_mapper = McpSchemaMapper()
|
|
224
|
-
tool_definitions: List[ToolDefinition] = []
|
|
225
|
-
|
|
226
|
-
try:
|
|
227
|
-
handler = self._handler_registry.get(validated_config.transport_type)
|
|
228
|
-
if not handler:
|
|
229
|
-
raise ValueError(f"No MCP call handler found for transport type '{validated_config.transport_type.value}'.")
|
|
230
|
-
|
|
231
|
-
remote_tools_result = await handler.handle_call(
|
|
232
|
-
config=validated_config,
|
|
233
|
-
remote_tool_name="list_tools",
|
|
234
|
-
arguments={}
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
if not isinstance(remote_tools_result, mcp_types.ListToolsResult):
|
|
238
|
-
error_msg = f"Expected ListToolsResult from handler, but got {type(remote_tools_result)}. Cannot preview tools from server '{validated_config.server_id}'."
|
|
239
|
-
logger.error(error_msg)
|
|
240
|
-
raise RuntimeError(error_msg)
|
|
241
|
-
|
|
242
|
-
actual_remote_tools: list[mcp_types.Tool] = remote_tools_result.tools
|
|
243
|
-
logger.info(f"Discovered {len(actual_remote_tools)} tools from server '{validated_config.server_id}' for preview.")
|
|
244
|
-
|
|
245
|
-
for remote_tool in actual_remote_tools:
|
|
246
|
-
try:
|
|
247
|
-
tool_def = self._create_tool_definition_from_remote(remote_tool, validated_config, handler, schema_mapper)
|
|
248
|
-
tool_definitions.append(tool_def)
|
|
249
|
-
except Exception as e_tool:
|
|
250
|
-
logger.error(f"Failed to map remote tool '{remote_tool.name}' from server '{validated_config.server_id}' during preview: {e_tool}", exc_info=True)
|
|
251
|
-
# For a preview, we continue to show other valid tools.
|
|
252
|
-
|
|
253
|
-
except Exception as e_server:
|
|
254
|
-
logger.error(f"Failed to discover tools for preview from MCP server '{validated_config.server_id}': {e_server}", exc_info=True)
|
|
255
|
-
# Re-raise server-level exceptions to the caller as per AC5.
|
|
256
|
-
raise
|
|
257
|
-
|
|
258
|
-
logger.info(f"MCP tool preview completed. Found {len(tool_definitions)} tools.")
|
|
259
|
-
return tool_definitions
|
|
260
|
-
|
|
261
|
-
def get_registered_tools_for_server(self, server_id: str) -> List[ToolDefinition]:
|
|
262
|
-
"""
|
|
263
|
-
Returns a list of ToolDefinition objects that were successfully registered
|
|
264
|
-
from a specific MCP server during the most recent discovery process.
|
|
265
|
-
|
|
266
|
-
Args:
|
|
267
|
-
server_id: The unique ID of the MCP server to query.
|
|
268
|
-
|
|
269
|
-
Returns:
|
|
270
|
-
A list of ToolDefinition objects. Returns an empty list if the server ID
|
|
271
|
-
is not found or registered no tools.
|
|
272
|
-
"""
|
|
273
|
-
return self._registered_tools_by_server.get(server_id, [])
|
|
274
|
-
|
|
275
|
-
def get_all_registered_mcp_tools(self) -> List[ToolDefinition]:
|
|
276
|
-
"""
|
|
277
|
-
Returns a flat list of all ToolDefinitions registered from any MCP server.
|
|
278
|
-
"""
|
|
279
|
-
all_tools = []
|
|
280
|
-
for server_tools in self._registered_tools_by_server.values():
|
|
281
|
-
all_tools.extend(server_tools)
|
|
282
|
-
return all_tools
|
|
283
|
-
|
|
284
|
-
def is_server_registered(self, server_id: str) -> bool:
|
|
285
|
-
"""
|
|
286
|
-
Checks if any tools from a specific MCP server are currently registered.
|
|
287
|
-
|
|
288
|
-
Args:
|
|
289
|
-
server_id: The unique ID of the MCP server.
|
|
290
|
-
|
|
291
|
-
Returns:
|
|
292
|
-
True if the server has tools registered, False otherwise.
|
|
293
|
-
"""
|
|
294
|
-
is_registered = server_id in self._registered_tools_by_server
|
|
295
|
-
logger.debug(f"Checking if server '{server_id}' is registered: {is_registered}")
|
|
296
|
-
return is_registered
|
|
297
|
-
|
|
298
|
-
def unregister_tools_from_server(self, server_id: str) -> bool:
|
|
299
|
-
"""
|
|
300
|
-
Unregisters all tools associated with a given MCP server ID from the
|
|
301
|
-
main ToolRegistry and removes the server from internal tracking.
|
|
302
|
-
|
|
303
|
-
Args:
|
|
304
|
-
server_id: The unique ID of the MCP server whose tools should be unregistered.
|
|
305
|
-
|
|
306
|
-
Returns:
|
|
307
|
-
True if the server was found and its tools were processed for unregistration,
|
|
308
|
-
False if the server was not found in the registrar's state.
|
|
309
|
-
"""
|
|
310
|
-
if not self.is_server_registered(server_id):
|
|
311
|
-
logger.info(f"No tools found for server ID '{server_id}'. Nothing to unregister.")
|
|
312
|
-
return False
|
|
313
|
-
|
|
314
|
-
tools_to_unregister = self._registered_tools_by_server.get(server_id, [])
|
|
315
|
-
logger.info(f"Unregistering {len(tools_to_unregister)} tools from server ID: '{server_id}'...")
|
|
316
|
-
|
|
317
|
-
for tool_def in tools_to_unregister:
|
|
318
|
-
self._tool_registry.unregister_tool(tool_def.name)
|
|
319
|
-
|
|
320
|
-
# Remove the server from the tracking dictionary
|
|
321
|
-
del self._registered_tools_by_server[server_id]
|
|
322
|
-
logger.info(f"Successfully unregistered all tools and removed server '{server_id}' from registrar tracking.")
|
|
323
|
-
return True
|