griptape-nodes 0.55.1__py3-none-any.whl → 0.56.1__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.
- griptape_nodes/app/app.py +10 -15
- griptape_nodes/app/watch.py +35 -67
- griptape_nodes/bootstrap/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/utils/python_subprocess_executor.py +122 -0
- griptape_nodes/bootstrap/workflow_executors/local_session_workflow_executor.py +418 -0
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +37 -8
- griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +326 -0
- griptape_nodes/bootstrap/workflow_executors/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +51 -0
- griptape_nodes/bootstrap/workflow_publishers/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +43 -0
- griptape_nodes/bootstrap/workflow_publishers/subprocess_workflow_publisher.py +84 -0
- griptape_nodes/bootstrap/workflow_publishers/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +54 -0
- griptape_nodes/cli/commands/engine.py +4 -15
- griptape_nodes/cli/commands/init.py +88 -0
- griptape_nodes/cli/commands/models.py +2 -0
- griptape_nodes/cli/main.py +6 -1
- griptape_nodes/cli/shared.py +1 -0
- griptape_nodes/exe_types/core_types.py +130 -0
- griptape_nodes/exe_types/node_types.py +125 -13
- griptape_nodes/machines/control_flow.py +10 -0
- griptape_nodes/machines/dag_builder.py +21 -2
- griptape_nodes/machines/parallel_resolution.py +25 -10
- griptape_nodes/node_library/workflow_registry.py +73 -3
- griptape_nodes/retained_mode/events/agent_events.py +2 -0
- griptape_nodes/retained_mode/events/base_events.py +18 -17
- griptape_nodes/retained_mode/events/execution_events.py +15 -3
- griptape_nodes/retained_mode/events/flow_events.py +63 -7
- griptape_nodes/retained_mode/events/mcp_events.py +363 -0
- griptape_nodes/retained_mode/events/node_events.py +3 -4
- griptape_nodes/retained_mode/events/resource_events.py +290 -0
- griptape_nodes/retained_mode/events/workflow_events.py +57 -2
- griptape_nodes/retained_mode/griptape_nodes.py +17 -1
- griptape_nodes/retained_mode/managers/agent_manager.py +67 -4
- griptape_nodes/retained_mode/managers/event_manager.py +31 -13
- griptape_nodes/retained_mode/managers/flow_manager.py +731 -33
- griptape_nodes/retained_mode/managers/library_manager.py +15 -23
- griptape_nodes/retained_mode/managers/mcp_manager.py +364 -0
- griptape_nodes/retained_mode/managers/model_manager.py +184 -83
- griptape_nodes/retained_mode/managers/node_manager.py +15 -4
- griptape_nodes/retained_mode/managers/os_manager.py +118 -1
- griptape_nodes/retained_mode/managers/resource_components/__init__.py +1 -0
- griptape_nodes/retained_mode/managers/resource_components/capability_field.py +41 -0
- griptape_nodes/retained_mode/managers/resource_components/comparator.py +18 -0
- griptape_nodes/retained_mode/managers/resource_components/resource_instance.py +236 -0
- griptape_nodes/retained_mode/managers/resource_components/resource_type.py +79 -0
- griptape_nodes/retained_mode/managers/resource_manager.py +306 -0
- griptape_nodes/retained_mode/managers/resource_types/__init__.py +1 -0
- griptape_nodes/retained_mode/managers/resource_types/cpu_resource.py +108 -0
- griptape_nodes/retained_mode/managers/resource_types/os_resource.py +87 -0
- griptape_nodes/retained_mode/managers/settings.py +45 -0
- griptape_nodes/retained_mode/managers/sync_manager.py +10 -3
- griptape_nodes/retained_mode/managers/workflow_manager.py +447 -263
- griptape_nodes/traits/multi_options.py +5 -1
- griptape_nodes/traits/options.py +10 -2
- {griptape_nodes-0.55.1.dist-info → griptape_nodes-0.56.1.dist-info}/METADATA +2 -2
- {griptape_nodes-0.55.1.dist-info → griptape_nodes-0.56.1.dist-info}/RECORD +60 -37
- {griptape_nodes-0.55.1.dist-info → griptape_nodes-0.56.1.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.55.1.dist-info → griptape_nodes-0.56.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import importlib.util
|
|
4
5
|
import json
|
|
5
6
|
import logging
|
|
@@ -895,7 +896,8 @@ class LibraryManager:
|
|
|
895
896
|
continue # SKIP IT
|
|
896
897
|
|
|
897
898
|
# Attempt to load nodes from the library.
|
|
898
|
-
library_load_results =
|
|
899
|
+
library_load_results = await asyncio.to_thread(
|
|
900
|
+
self._attempt_load_nodes_from_library,
|
|
899
901
|
library_data=library_data,
|
|
900
902
|
library=library,
|
|
901
903
|
base_dir=base_dir,
|
|
@@ -1521,7 +1523,7 @@ class LibraryManager:
|
|
|
1521
1523
|
for library_result in metadata_result.successful_libraries:
|
|
1522
1524
|
if library_result.library_schema.name == LibraryManager.SANDBOX_LIBRARY_NAME:
|
|
1523
1525
|
# Handle sandbox library - use the schema we already have
|
|
1524
|
-
self._attempt_generate_sandbox_library_from_schema(
|
|
1526
|
+
await self._attempt_generate_sandbox_library_from_schema(
|
|
1525
1527
|
library_schema=library_result.library_schema, sandbox_directory=library_result.file_path
|
|
1526
1528
|
)
|
|
1527
1529
|
else:
|
|
@@ -1853,7 +1855,7 @@ class LibraryManager:
|
|
|
1853
1855
|
problems=problems,
|
|
1854
1856
|
)
|
|
1855
1857
|
|
|
1856
|
-
def _attempt_generate_sandbox_library_from_schema(
|
|
1858
|
+
async def _attempt_generate_sandbox_library_from_schema(
|
|
1857
1859
|
self, library_schema: LibrarySchema, sandbox_directory: str
|
|
1858
1860
|
) -> None:
|
|
1859
1861
|
"""Generate sandbox library using an existing schema, loading actual node classes."""
|
|
@@ -1945,7 +1947,8 @@ class LibraryManager:
|
|
|
1945
1947
|
return
|
|
1946
1948
|
|
|
1947
1949
|
# Load nodes into the library
|
|
1948
|
-
library_load_results =
|
|
1950
|
+
library_load_results = await asyncio.to_thread(
|
|
1951
|
+
self._attempt_load_nodes_from_library,
|
|
1949
1952
|
library_data=library_data,
|
|
1950
1953
|
library=library,
|
|
1951
1954
|
base_dir=sandbox_library_dir,
|
|
@@ -2041,32 +2044,21 @@ class LibraryManager:
|
|
|
2041
2044
|
config_mgr = GriptapeNodes.ConfigManager()
|
|
2042
2045
|
user_libraries_section = "app_events.on_app_initialization_complete.libraries_to_register"
|
|
2043
2046
|
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
# Add from config
|
|
2047
|
-
config_libraries = config_mgr.get_config_value(user_libraries_section, default=[])
|
|
2048
|
-
libraries_to_process.extend(config_libraries)
|
|
2049
|
-
|
|
2050
|
-
# Add from workspace - recursive discovery of library JSON files
|
|
2051
|
-
workspace_path = config_mgr.workspace_path
|
|
2052
|
-
libraries_to_process.append(str(workspace_path))
|
|
2053
|
-
|
|
2054
|
-
library_files = []
|
|
2047
|
+
discovered_libraries = set()
|
|
2055
2048
|
|
|
2056
2049
|
def process_path(path: Path) -> None:
|
|
2057
2050
|
"""Process a path, handling both files and directories."""
|
|
2058
2051
|
if path.is_dir():
|
|
2059
2052
|
# Process all library JSON files recursively in the directory
|
|
2060
|
-
|
|
2053
|
+
discovered_libraries.update(path.rglob(LibraryManager.LIBRARY_CONFIG_FILENAME))
|
|
2061
2054
|
elif path.suffix == ".json":
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
# Process library paths
|
|
2065
|
-
for library_to_process in libraries_to_process:
|
|
2066
|
-
library_path = Path(library_to_process)
|
|
2055
|
+
discovered_libraries.add(path)
|
|
2067
2056
|
|
|
2068
|
-
|
|
2057
|
+
# Add from config
|
|
2058
|
+
config_libraries = config_mgr.get_config_value(user_libraries_section, default=[])
|
|
2059
|
+
for library_path_str in config_libraries:
|
|
2060
|
+
library_path = Path(library_path_str)
|
|
2069
2061
|
if library_path.exists():
|
|
2070
2062
|
process_path(library_path)
|
|
2071
2063
|
|
|
2072
|
-
return
|
|
2064
|
+
return list(discovered_libraries)
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"""MCP (Model Context Protocol) server management.
|
|
2
|
+
|
|
3
|
+
Handles MCP server configurations, enabling/disabling servers, and provides
|
|
4
|
+
event-based interface for frontend and backend interactions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from griptape_nodes.retained_mode.events.mcp_events import (
|
|
11
|
+
CreateMCPServerRequest,
|
|
12
|
+
CreateMCPServerResultFailure,
|
|
13
|
+
CreateMCPServerResultSuccess,
|
|
14
|
+
DeleteMCPServerRequest,
|
|
15
|
+
DeleteMCPServerResultFailure,
|
|
16
|
+
DeleteMCPServerResultSuccess,
|
|
17
|
+
DisableMCPServerRequest,
|
|
18
|
+
DisableMCPServerResultFailure,
|
|
19
|
+
DisableMCPServerResultSuccess,
|
|
20
|
+
EnableMCPServerRequest,
|
|
21
|
+
EnableMCPServerResultFailure,
|
|
22
|
+
EnableMCPServerResultSuccess,
|
|
23
|
+
GetEnabledMCPServersRequest,
|
|
24
|
+
GetEnabledMCPServersResultFailure,
|
|
25
|
+
GetEnabledMCPServersResultSuccess,
|
|
26
|
+
GetMCPServerRequest,
|
|
27
|
+
GetMCPServerResultFailure,
|
|
28
|
+
GetMCPServerResultSuccess,
|
|
29
|
+
ListMCPServersRequest,
|
|
30
|
+
ListMCPServersResultFailure,
|
|
31
|
+
ListMCPServersResultSuccess,
|
|
32
|
+
UpdateMCPServerRequest,
|
|
33
|
+
UpdateMCPServerResultFailure,
|
|
34
|
+
UpdateMCPServerResultSuccess,
|
|
35
|
+
)
|
|
36
|
+
from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
|
|
37
|
+
from griptape_nodes.retained_mode.managers.event_manager import EventManager
|
|
38
|
+
from griptape_nodes.retained_mode.managers.settings import MCPServerConfig
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger("griptape_nodes")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class MCPManager:
|
|
44
|
+
"""Manager for MCP server configurations and operations."""
|
|
45
|
+
|
|
46
|
+
def __init__(self, event_manager: EventManager | None = None, config_manager: ConfigManager | None = None) -> None:
|
|
47
|
+
"""Initialize the MCPManager.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
event_manager: The EventManager instance to use for event handling.
|
|
51
|
+
config_manager: The ConfigManager instance to use for configuration management.
|
|
52
|
+
"""
|
|
53
|
+
self.config_manager = config_manager
|
|
54
|
+
if event_manager is not None:
|
|
55
|
+
# Register event handlers
|
|
56
|
+
event_manager.assign_manager_to_request_type(ListMCPServersRequest, self.on_list_mcp_servers_request)
|
|
57
|
+
event_manager.assign_manager_to_request_type(GetMCPServerRequest, self.on_get_mcp_server_request)
|
|
58
|
+
event_manager.assign_manager_to_request_type(CreateMCPServerRequest, self.on_create_mcp_server_request)
|
|
59
|
+
event_manager.assign_manager_to_request_type(UpdateMCPServerRequest, self.on_update_mcp_server_request)
|
|
60
|
+
event_manager.assign_manager_to_request_type(DeleteMCPServerRequest, self.on_delete_mcp_server_request)
|
|
61
|
+
event_manager.assign_manager_to_request_type(EnableMCPServerRequest, self.on_enable_mcp_server_request)
|
|
62
|
+
event_manager.assign_manager_to_request_type(DisableMCPServerRequest, self.on_disable_mcp_server_request)
|
|
63
|
+
event_manager.assign_manager_to_request_type(
|
|
64
|
+
GetEnabledMCPServersRequest, self.on_get_enabled_mcp_servers_request
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def _get_mcp_servers(self, filter_by: dict[str, Any] | None = None) -> list[MCPServerConfig]:
|
|
68
|
+
"""Get the current MCP servers configuration from the config manager.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
filter_by: Optional dict of field=value pairs to filter by.
|
|
72
|
+
Keys should match server config field names, values are the expected values.
|
|
73
|
+
"""
|
|
74
|
+
if self.config_manager is None:
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
mcp_config_data = self.config_manager.get_config_value("mcp_servers", default=[])
|
|
78
|
+
if not mcp_config_data:
|
|
79
|
+
return []
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
servers = [MCPServerConfig.model_validate(server) for server in mcp_config_data]
|
|
83
|
+
if filter_by:
|
|
84
|
+
filtered_servers = []
|
|
85
|
+
for server in servers:
|
|
86
|
+
match = True
|
|
87
|
+
for field, value in filter_by.items():
|
|
88
|
+
if getattr(server, field, None) != value:
|
|
89
|
+
match = False
|
|
90
|
+
break
|
|
91
|
+
if match:
|
|
92
|
+
filtered_servers.append(server)
|
|
93
|
+
return filtered_servers
|
|
94
|
+
return servers # noqa: TRY300
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error("Failed to parse MCP servers configuration: %s", e)
|
|
97
|
+
return []
|
|
98
|
+
|
|
99
|
+
def _save_mcp_servers(self, servers: list[MCPServerConfig]) -> None:
|
|
100
|
+
"""Save the MCP servers configuration to the config manager."""
|
|
101
|
+
if self.config_manager is None:
|
|
102
|
+
logger.warning("No config manager available, cannot save MCP configuration")
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
self.config_manager.set_config_value("mcp_servers", [server.model_dump() for server in servers])
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.error("Failed to save MCP servers configuration: %s", e)
|
|
109
|
+
|
|
110
|
+
def _update_server_fields(self, server_config: MCPServerConfig, request: UpdateMCPServerRequest) -> None:
|
|
111
|
+
"""Update server configuration fields from request."""
|
|
112
|
+
# Map request fields to server config attributes
|
|
113
|
+
field_mapping = {
|
|
114
|
+
"new_name": "name",
|
|
115
|
+
"transport": "transport",
|
|
116
|
+
"enabled": "enabled",
|
|
117
|
+
"command": "command",
|
|
118
|
+
"args": "args",
|
|
119
|
+
"env": "env",
|
|
120
|
+
"cwd": "cwd",
|
|
121
|
+
"encoding": "encoding",
|
|
122
|
+
"encoding_error_handler": "encoding_error_handler",
|
|
123
|
+
"url": "url",
|
|
124
|
+
"headers": "headers",
|
|
125
|
+
"timeout": "timeout",
|
|
126
|
+
"sse_read_timeout": "sse_read_timeout",
|
|
127
|
+
"terminate_on_close": "terminate_on_close",
|
|
128
|
+
"description": "description",
|
|
129
|
+
"capabilities": "capabilities",
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Update fields that are not None
|
|
133
|
+
for request_field, config_field in field_mapping.items():
|
|
134
|
+
value = getattr(request, request_field, None)
|
|
135
|
+
if value is not None:
|
|
136
|
+
setattr(server_config, config_field, value)
|
|
137
|
+
|
|
138
|
+
def on_list_mcp_servers_request(
|
|
139
|
+
self, request: ListMCPServersRequest
|
|
140
|
+
) -> ListMCPServersResultSuccess | ListMCPServersResultFailure:
|
|
141
|
+
"""Handle list MCP servers request."""
|
|
142
|
+
try:
|
|
143
|
+
servers = self._get_mcp_servers()
|
|
144
|
+
except Exception as e:
|
|
145
|
+
logger.error("Failed to list MCP servers: %s", e)
|
|
146
|
+
return ListMCPServersResultFailure(result_details=f"Failed to list MCP servers: {e}")
|
|
147
|
+
|
|
148
|
+
if request.include_disabled:
|
|
149
|
+
servers_dict = {server.name: server.model_dump() for server in servers}
|
|
150
|
+
else:
|
|
151
|
+
enabled_servers = [server for server in servers if server.enabled]
|
|
152
|
+
servers_dict = {server.name: server.model_dump() for server in enabled_servers}
|
|
153
|
+
|
|
154
|
+
# Success path after exception handling
|
|
155
|
+
return ListMCPServersResultSuccess(
|
|
156
|
+
# Pydantic model.model_dump() returns dict[str, Any] which matches MCPServerConfig TypedDict structure
|
|
157
|
+
servers=servers_dict, # type: ignore[arg-type]
|
|
158
|
+
result_details=f"Successfully listed {len(servers_dict)} MCP servers",
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def on_get_mcp_server_request(
|
|
162
|
+
self, request: GetMCPServerRequest
|
|
163
|
+
) -> GetMCPServerResultSuccess | GetMCPServerResultFailure:
|
|
164
|
+
"""Handle get MCP server request."""
|
|
165
|
+
servers = self._get_mcp_servers(filter_by={"name": request.name})
|
|
166
|
+
server_config = servers[0] if servers else None
|
|
167
|
+
|
|
168
|
+
if server_config is None:
|
|
169
|
+
return GetMCPServerResultFailure(result_details=f"Failed to get MCP server '{request.name}' - not found")
|
|
170
|
+
|
|
171
|
+
# Success path after exception handling
|
|
172
|
+
return GetMCPServerResultSuccess(
|
|
173
|
+
# Pydantic model.model_dump() returns dict[str, Any] which matches MCPServerConfig TypedDict structure
|
|
174
|
+
server_config=server_config.model_dump(), # type: ignore[arg-type]
|
|
175
|
+
result_details=f"Successfully retrieved MCP server '{request.name}'",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def on_create_mcp_server_request(
|
|
179
|
+
self, request: CreateMCPServerRequest
|
|
180
|
+
) -> CreateMCPServerResultSuccess | CreateMCPServerResultFailure:
|
|
181
|
+
"""Handle create MCP server request."""
|
|
182
|
+
try:
|
|
183
|
+
servers = self._get_mcp_servers()
|
|
184
|
+
except Exception as e:
|
|
185
|
+
logger.error("Failed to create MCP server '%s': %s", request.name, e)
|
|
186
|
+
return CreateMCPServerResultFailure(result_details=f"Failed to create MCP server '{request.name}': {e}")
|
|
187
|
+
|
|
188
|
+
# Check if server already exists
|
|
189
|
+
for server in servers:
|
|
190
|
+
if server.name == request.name:
|
|
191
|
+
return CreateMCPServerResultFailure(
|
|
192
|
+
result_details=f"Failed to create MCP server '{request.name}' - already exists"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Create new server configuration
|
|
196
|
+
server_config = MCPServerConfig(
|
|
197
|
+
name=request.name,
|
|
198
|
+
enabled=request.enabled,
|
|
199
|
+
transport=request.transport,
|
|
200
|
+
# StdioConnection fields
|
|
201
|
+
command=request.command,
|
|
202
|
+
args=request.args or [],
|
|
203
|
+
env=request.env or {},
|
|
204
|
+
cwd=request.cwd,
|
|
205
|
+
encoding=request.encoding,
|
|
206
|
+
encoding_error_handler=request.encoding_error_handler,
|
|
207
|
+
# HTTP-based connection fields
|
|
208
|
+
url=request.url,
|
|
209
|
+
headers=request.headers,
|
|
210
|
+
timeout=request.timeout,
|
|
211
|
+
sse_read_timeout=request.sse_read_timeout,
|
|
212
|
+
terminate_on_close=request.terminate_on_close,
|
|
213
|
+
# Common fields
|
|
214
|
+
description=request.description,
|
|
215
|
+
capabilities=request.capabilities or [],
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
servers.append(server_config)
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
self._save_mcp_servers(servers)
|
|
222
|
+
except Exception as e:
|
|
223
|
+
logger.error("Failed to save MCP server '%s': %s", request.name, e)
|
|
224
|
+
return CreateMCPServerResultFailure(result_details=f"Failed to save MCP server '{request.name}': {e}")
|
|
225
|
+
|
|
226
|
+
# Success path after exception handling
|
|
227
|
+
return CreateMCPServerResultSuccess(
|
|
228
|
+
name=request.name, result_details=f"Successfully created MCP server '{request.name}'"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def on_update_mcp_server_request(
|
|
232
|
+
self, request: UpdateMCPServerRequest
|
|
233
|
+
) -> UpdateMCPServerResultSuccess | UpdateMCPServerResultFailure:
|
|
234
|
+
"""Handle update MCP server request."""
|
|
235
|
+
servers = self._get_mcp_servers(filter_by={"name": request.name})
|
|
236
|
+
server_config = servers[0] if servers else None
|
|
237
|
+
|
|
238
|
+
if server_config is None:
|
|
239
|
+
return UpdateMCPServerResultFailure(
|
|
240
|
+
result_details=f"Failed to update MCP server '{request.name}' - not found"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Update only provided fields
|
|
244
|
+
self._update_server_fields(server_config, request)
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
self._save_mcp_servers(servers)
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.error("Failed to save MCP server '%s': %s", request.name, e)
|
|
250
|
+
return UpdateMCPServerResultFailure(result_details=f"Failed to save MCP server '{request.name}': {e}")
|
|
251
|
+
|
|
252
|
+
# Success path after exception handling
|
|
253
|
+
return UpdateMCPServerResultSuccess(
|
|
254
|
+
name=request.name, result_details=f"Successfully updated MCP server '{request.name}'"
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def on_delete_mcp_server_request(
|
|
258
|
+
self, request: DeleteMCPServerRequest
|
|
259
|
+
) -> DeleteMCPServerResultSuccess | DeleteMCPServerResultFailure:
|
|
260
|
+
"""Handle delete MCP server request."""
|
|
261
|
+
try:
|
|
262
|
+
servers = self._get_mcp_servers()
|
|
263
|
+
|
|
264
|
+
# Find and remove server by server_id
|
|
265
|
+
server_found = False
|
|
266
|
+
for i, server in enumerate(servers):
|
|
267
|
+
if server.name == request.name:
|
|
268
|
+
servers.pop(i)
|
|
269
|
+
server_found = True
|
|
270
|
+
break
|
|
271
|
+
|
|
272
|
+
self._save_mcp_servers(servers)
|
|
273
|
+
|
|
274
|
+
except Exception as e:
|
|
275
|
+
logger.error("Failed to delete MCP server '%s': %s", request.name, e)
|
|
276
|
+
return DeleteMCPServerResultFailure(result_details=f"Failed to delete MCP server '{request.name}': {e}")
|
|
277
|
+
|
|
278
|
+
if not server_found:
|
|
279
|
+
return DeleteMCPServerResultFailure(
|
|
280
|
+
result_details=f"Failed to delete MCP server '{request.name}' - not found"
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Success path after exception handling
|
|
284
|
+
return DeleteMCPServerResultSuccess(
|
|
285
|
+
name=request.name, result_details=f"Successfully deleted MCP server '{request.name}'"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def on_enable_mcp_server_request(
|
|
289
|
+
self, request: EnableMCPServerRequest
|
|
290
|
+
) -> EnableMCPServerResultSuccess | EnableMCPServerResultFailure:
|
|
291
|
+
"""Handle enable MCP server request."""
|
|
292
|
+
servers = self._get_mcp_servers()
|
|
293
|
+
server_config = None
|
|
294
|
+
for server in servers:
|
|
295
|
+
if server.name == request.name:
|
|
296
|
+
server_config = server
|
|
297
|
+
break
|
|
298
|
+
|
|
299
|
+
if server_config is None:
|
|
300
|
+
return EnableMCPServerResultFailure(
|
|
301
|
+
result_details=f"Failed to enable MCP server '{request.name}' - not found"
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
server_config.enabled = True
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
self._save_mcp_servers(servers)
|
|
308
|
+
except Exception as e:
|
|
309
|
+
logger.error("Failed to save MCP server '%s': %s", request.name, e)
|
|
310
|
+
return EnableMCPServerResultFailure(result_details=f"Failed to save MCP server '{request.name}': {e}")
|
|
311
|
+
|
|
312
|
+
# Success path after exception handling
|
|
313
|
+
return EnableMCPServerResultSuccess(
|
|
314
|
+
name=request.name, result_details=f"Successfully enabled MCP server '{request.name}'"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
def on_disable_mcp_server_request(
|
|
318
|
+
self, request: DisableMCPServerRequest
|
|
319
|
+
) -> DisableMCPServerResultSuccess | DisableMCPServerResultFailure:
|
|
320
|
+
"""Handle disable MCP server request."""
|
|
321
|
+
servers = self._get_mcp_servers()
|
|
322
|
+
server_config = None
|
|
323
|
+
for server in servers:
|
|
324
|
+
if server.name == request.name:
|
|
325
|
+
server_config = server
|
|
326
|
+
break
|
|
327
|
+
|
|
328
|
+
if server_config is None:
|
|
329
|
+
return DisableMCPServerResultFailure(
|
|
330
|
+
result_details=f"Failed to disable MCP server '{request.name}' - not found"
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
server_config.enabled = False
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
self._save_mcp_servers(servers)
|
|
337
|
+
except Exception as e:
|
|
338
|
+
logger.error("Failed to save MCP server '%s': %s", request.name, e)
|
|
339
|
+
return DisableMCPServerResultFailure(result_details=f"Failed to save MCP server '{request.name}': {e}")
|
|
340
|
+
|
|
341
|
+
# Success path after exception handling
|
|
342
|
+
return DisableMCPServerResultSuccess(
|
|
343
|
+
name=request.name, result_details=f"Successfully disabled MCP server '{request.name}'"
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
def on_get_enabled_mcp_servers_request(
|
|
347
|
+
self,
|
|
348
|
+
request: GetEnabledMCPServersRequest, # noqa: ARG002
|
|
349
|
+
) -> GetEnabledMCPServersResultSuccess | GetEnabledMCPServersResultFailure:
|
|
350
|
+
"""Handle get enabled MCP servers request."""
|
|
351
|
+
try:
|
|
352
|
+
enabled_servers = self._get_mcp_servers(filter_by={"enabled": True})
|
|
353
|
+
servers_dict = {server.name: server.model_dump() for server in enabled_servers}
|
|
354
|
+
|
|
355
|
+
except Exception as e:
|
|
356
|
+
logger.error("Failed to get enabled MCP servers: %s", e)
|
|
357
|
+
return GetEnabledMCPServersResultFailure(result_details=f"Failed to get enabled MCP servers: {e}")
|
|
358
|
+
|
|
359
|
+
# Success path after exception handling
|
|
360
|
+
return GetEnabledMCPServersResultSuccess(
|
|
361
|
+
# Pydantic model.model_dump() returns dict[str, Any] which matches MCPServerConfig TypedDict structure
|
|
362
|
+
servers=servers_dict, # type: ignore[arg-type]
|
|
363
|
+
result_details=f"Successfully retrieved {len(servers_dict)} enabled MCP servers",
|
|
364
|
+
)
|