alita-sdk 0.3.257__py3-none-any.whl → 0.3.562__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.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- alita_sdk/cli/agent/__init__.py +5 -0
- alita_sdk/cli/agent/default.py +258 -0
- alita_sdk/cli/agent_executor.py +155 -0
- alita_sdk/cli/agent_loader.py +215 -0
- alita_sdk/cli/agent_ui.py +228 -0
- alita_sdk/cli/agents.py +3601 -0
- alita_sdk/cli/callbacks.py +647 -0
- alita_sdk/cli/cli.py +168 -0
- alita_sdk/cli/config.py +306 -0
- alita_sdk/cli/context/__init__.py +30 -0
- alita_sdk/cli/context/cleanup.py +198 -0
- alita_sdk/cli/context/manager.py +731 -0
- alita_sdk/cli/context/message.py +285 -0
- alita_sdk/cli/context/strategies.py +289 -0
- alita_sdk/cli/context/token_estimation.py +127 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/input_handler.py +419 -0
- alita_sdk/cli/inventory.py +1073 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/toolkit.py +327 -0
- alita_sdk/cli/toolkit_loader.py +85 -0
- alita_sdk/cli/tools/__init__.py +43 -0
- alita_sdk/cli/tools/approval.py +224 -0
- alita_sdk/cli/tools/filesystem.py +1751 -0
- alita_sdk/cli/tools/planning.py +389 -0
- alita_sdk/cli/tools/terminal.py +414 -0
- alita_sdk/community/__init__.py +72 -12
- alita_sdk/community/inventory/__init__.py +236 -0
- alita_sdk/community/inventory/config.py +257 -0
- alita_sdk/community/inventory/enrichment.py +2137 -0
- alita_sdk/community/inventory/extractors.py +1469 -0
- alita_sdk/community/inventory/ingestion.py +3172 -0
- alita_sdk/community/inventory/knowledge_graph.py +1457 -0
- alita_sdk/community/inventory/parsers/__init__.py +218 -0
- alita_sdk/community/inventory/parsers/base.py +295 -0
- alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
- alita_sdk/community/inventory/parsers/go_parser.py +851 -0
- alita_sdk/community/inventory/parsers/html_parser.py +389 -0
- alita_sdk/community/inventory/parsers/java_parser.py +593 -0
- alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
- alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
- alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
- alita_sdk/community/inventory/parsers/python_parser.py +604 -0
- alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
- alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
- alita_sdk/community/inventory/parsers/text_parser.py +322 -0
- alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
- alita_sdk/community/inventory/patterns/__init__.py +61 -0
- alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
- alita_sdk/community/inventory/patterns/loader.py +348 -0
- alita_sdk/community/inventory/patterns/registry.py +198 -0
- alita_sdk/community/inventory/presets.py +535 -0
- alita_sdk/community/inventory/retrieval.py +1403 -0
- alita_sdk/community/inventory/toolkit.py +173 -0
- alita_sdk/community/inventory/toolkit_utils.py +176 -0
- alita_sdk/community/inventory/visualize.py +1370 -0
- alita_sdk/configurations/__init__.py +11 -0
- alita_sdk/configurations/ado.py +148 -2
- alita_sdk/configurations/azure_search.py +1 -1
- alita_sdk/configurations/bigquery.py +1 -1
- alita_sdk/configurations/bitbucket.py +94 -2
- alita_sdk/configurations/browser.py +18 -0
- alita_sdk/configurations/carrier.py +19 -0
- alita_sdk/configurations/confluence.py +130 -1
- alita_sdk/configurations/delta_lake.py +1 -1
- alita_sdk/configurations/figma.py +76 -5
- alita_sdk/configurations/github.py +65 -1
- alita_sdk/configurations/gitlab.py +81 -0
- alita_sdk/configurations/google_places.py +17 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/openapi.py +111 -0
- alita_sdk/configurations/postman.py +1 -1
- alita_sdk/configurations/qtest.py +72 -3
- alita_sdk/configurations/report_portal.py +115 -0
- alita_sdk/configurations/salesforce.py +19 -0
- alita_sdk/configurations/service_now.py +1 -12
- alita_sdk/configurations/sharepoint.py +167 -0
- alita_sdk/configurations/sonar.py +18 -0
- alita_sdk/configurations/sql.py +20 -0
- alita_sdk/configurations/testio.py +101 -0
- alita_sdk/configurations/testrail.py +88 -0
- alita_sdk/configurations/xray.py +94 -1
- alita_sdk/configurations/zephyr_enterprise.py +94 -1
- alita_sdk/configurations/zephyr_essential.py +95 -0
- alita_sdk/runtime/clients/artifact.py +21 -4
- alita_sdk/runtime/clients/client.py +458 -67
- alita_sdk/runtime/clients/mcp_discovery.py +342 -0
- alita_sdk/runtime/clients/mcp_manager.py +262 -0
- alita_sdk/runtime/clients/sandbox_client.py +352 -0
- alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
- alita_sdk/runtime/langchain/assistant.py +183 -43
- alita_sdk/runtime/langchain/constants.py +647 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
- alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
- alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
- alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
- alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
- alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
- alita_sdk/runtime/langchain/document_loaders/constants.py +189 -41
- alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
- alita_sdk/runtime/langchain/langraph_agent.py +407 -92
- alita_sdk/runtime/langchain/utils.py +102 -8
- alita_sdk/runtime/llms/preloaded.py +2 -6
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/skills/__init__.py +91 -0
- alita_sdk/runtime/skills/callbacks.py +498 -0
- alita_sdk/runtime/skills/discovery.py +540 -0
- alita_sdk/runtime/skills/executor.py +610 -0
- alita_sdk/runtime/skills/input_builder.py +371 -0
- alita_sdk/runtime/skills/models.py +330 -0
- alita_sdk/runtime/skills/registry.py +355 -0
- alita_sdk/runtime/skills/skill_runner.py +330 -0
- alita_sdk/runtime/toolkits/__init__.py +28 -0
- alita_sdk/runtime/toolkits/application.py +14 -4
- alita_sdk/runtime/toolkits/artifact.py +24 -9
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +780 -0
- alita_sdk/runtime/toolkits/planning.py +178 -0
- alita_sdk/runtime/toolkits/skill_router.py +238 -0
- alita_sdk/runtime/toolkits/subgraph.py +11 -6
- alita_sdk/runtime/toolkits/tools.py +314 -70
- alita_sdk/runtime/toolkits/vectorstore.py +11 -5
- alita_sdk/runtime/tools/__init__.py +24 -0
- alita_sdk/runtime/tools/application.py +16 -4
- alita_sdk/runtime/tools/artifact.py +367 -33
- alita_sdk/runtime/tools/data_analysis.py +183 -0
- alita_sdk/runtime/tools/function.py +100 -4
- alita_sdk/runtime/tools/graph.py +81 -0
- alita_sdk/runtime/tools/image_generation.py +218 -0
- alita_sdk/runtime/tools/llm.py +1013 -177
- alita_sdk/runtime/tools/loop.py +3 -1
- alita_sdk/runtime/tools/loop_output.py +3 -1
- alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
- alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
- alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
- alita_sdk/runtime/tools/planning/__init__.py +36 -0
- alita_sdk/runtime/tools/planning/models.py +246 -0
- alita_sdk/runtime/tools/planning/wrapper.py +607 -0
- alita_sdk/runtime/tools/router.py +2 -1
- alita_sdk/runtime/tools/sandbox.py +375 -0
- alita_sdk/runtime/tools/skill_router.py +776 -0
- alita_sdk/runtime/tools/tool.py +3 -1
- alita_sdk/runtime/tools/vectorstore.py +69 -65
- alita_sdk/runtime/tools/vectorstore_base.py +163 -90
- alita_sdk/runtime/utils/AlitaCallback.py +137 -21
- alita_sdk/runtime/utils/mcp_client.py +492 -0
- alita_sdk/runtime/utils/mcp_oauth.py +361 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
- alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
- alita_sdk/runtime/utils/streamlit.py +41 -14
- alita_sdk/runtime/utils/toolkit_utils.py +28 -9
- alita_sdk/runtime/utils/utils.py +48 -0
- alita_sdk/tools/__init__.py +135 -37
- alita_sdk/tools/ado/__init__.py +2 -2
- alita_sdk/tools/ado/repos/__init__.py +15 -19
- alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
- alita_sdk/tools/ado/test_plan/__init__.py +26 -8
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
- alita_sdk/tools/ado/wiki/__init__.py +27 -12
- alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
- alita_sdk/tools/ado/work_item/__init__.py +27 -12
- alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
- alita_sdk/tools/advanced_jira_mining/__init__.py +12 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +14 -11
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +13 -8
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +454 -110
- alita_sdk/tools/bitbucket/__init__.py +27 -19
- alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
- alita_sdk/tools/browser/__init__.py +41 -16
- alita_sdk/tools/browser/crawler.py +3 -1
- alita_sdk/tools/browser/utils.py +15 -6
- alita_sdk/tools/carrier/__init__.py +18 -17
- alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
- alita_sdk/tools/carrier/excel_reporter.py +8 -4
- alita_sdk/tools/chunkers/__init__.py +3 -1
- alita_sdk/tools/chunkers/code/codeparser.py +1 -1
- alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
- alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
- alita_sdk/tools/chunkers/universal_chunker.py +270 -0
- alita_sdk/tools/cloud/aws/__init__.py +11 -7
- alita_sdk/tools/cloud/azure/__init__.py +11 -7
- alita_sdk/tools/cloud/gcp/__init__.py +11 -7
- alita_sdk/tools/cloud/k8s/__init__.py +11 -7
- alita_sdk/tools/code/linter/__init__.py +9 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +20 -13
- alita_sdk/tools/code_indexer_toolkit.py +199 -0
- alita_sdk/tools/confluence/__init__.py +21 -14
- alita_sdk/tools/confluence/api_wrapper.py +197 -58
- alita_sdk/tools/confluence/loader.py +14 -2
- alita_sdk/tools/custom_open_api/__init__.py +11 -5
- alita_sdk/tools/elastic/__init__.py +10 -8
- alita_sdk/tools/elitea_base.py +546 -64
- alita_sdk/tools/figma/__init__.py +11 -8
- alita_sdk/tools/figma/api_wrapper.py +352 -153
- alita_sdk/tools/github/__init__.py +17 -17
- alita_sdk/tools/github/api_wrapper.py +9 -26
- alita_sdk/tools/github/github_client.py +81 -12
- alita_sdk/tools/github/schemas.py +2 -1
- alita_sdk/tools/github/tool.py +5 -1
- alita_sdk/tools/gitlab/__init__.py +18 -13
- alita_sdk/tools/gitlab/api_wrapper.py +224 -80
- alita_sdk/tools/gitlab_org/__init__.py +13 -10
- alita_sdk/tools/google/bigquery/__init__.py +13 -13
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +20 -11
- alita_sdk/tools/jira/__init__.py +21 -11
- alita_sdk/tools/jira/api_wrapper.py +315 -168
- alita_sdk/tools/keycloak/__init__.py +10 -8
- alita_sdk/tools/localgit/__init__.py +8 -3
- alita_sdk/tools/localgit/local_git.py +62 -54
- alita_sdk/tools/localgit/tool.py +5 -1
- alita_sdk/tools/memory/__init__.py +38 -14
- alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
- alita_sdk/tools/ocr/__init__.py +10 -8
- alita_sdk/tools/openapi/__init__.py +281 -108
- alita_sdk/tools/openapi/api_wrapper.py +883 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +18 -11
- alita_sdk/tools/pandas/api_wrapper.py +40 -45
- alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
- alita_sdk/tools/postman/__init__.py +10 -11
- alita_sdk/tools/postman/api_wrapper.py +19 -8
- alita_sdk/tools/postman/postman_analysis.py +8 -1
- alita_sdk/tools/pptx/__init__.py +10 -10
- alita_sdk/tools/qtest/__init__.py +21 -14
- alita_sdk/tools/qtest/api_wrapper.py +1784 -88
- alita_sdk/tools/rally/__init__.py +12 -10
- alita_sdk/tools/report_portal/__init__.py +22 -16
- alita_sdk/tools/salesforce/__init__.py +21 -16
- alita_sdk/tools/servicenow/__init__.py +20 -16
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +16 -14
- alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
- alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
- alita_sdk/tools/sharepoint/utils.py +8 -2
- alita_sdk/tools/slack/__init__.py +11 -7
- alita_sdk/tools/sql/__init__.py +21 -19
- alita_sdk/tools/sql/api_wrapper.py +71 -23
- alita_sdk/tools/testio/__init__.py +20 -13
- alita_sdk/tools/testrail/__init__.py +12 -11
- alita_sdk/tools/testrail/api_wrapper.py +214 -46
- alita_sdk/tools/utils/__init__.py +28 -4
- alita_sdk/tools/utils/content_parser.py +182 -62
- alita_sdk/tools/utils/text_operations.py +254 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
- alita_sdk/tools/xray/__init__.py +17 -14
- alita_sdk/tools/xray/api_wrapper.py +58 -113
- alita_sdk/tools/yagmail/__init__.py +8 -3
- alita_sdk/tools/zephyr/__init__.py +11 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +15 -9
- alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
- alita_sdk/tools/zephyr_essential/__init__.py +15 -10
- alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
- alita_sdk/tools/zephyr_essential/client.py +6 -4
- alita_sdk/tools/zephyr_scale/__init__.py +12 -8
- alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
- alita_sdk/tools/zephyr_squad/__init__.py +11 -7
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/METADATA +184 -37
- alita_sdk-0.3.562.dist-info/RECORD +450 -0
- alita_sdk-0.3.562.dist-info/entry_points.txt +2 -0
- alita_sdk/tools/bitbucket/tools.py +0 -304
- alita_sdk-0.3.257.dist-info/RECORD +0 -343
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP (Model Context Protocol) server integration.
|
|
3
|
+
|
|
4
|
+
Handles loading MCP server configurations and connecting to MCP servers
|
|
5
|
+
using langchain-mcp-adapters to load tools with persistent sessions.
|
|
6
|
+
|
|
7
|
+
Requires: pip install langchain-mcp-adapters
|
|
8
|
+
Documentation: https://docs.langchain.com/oss/python/langchain/mcp
|
|
9
|
+
Issue Reference: https://github.com/langchain-ai/langchain-mcp-adapters/issues/178
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Dict, Any, List, Optional
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
|
|
18
|
+
from .config import substitute_env_vars
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def load_mcp_config(file_path: str) -> Dict[str, Any]:
|
|
25
|
+
"""Load MCP configuration from JSON file (VSCode/Copilot format).
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
file_path: Path to mcp.json configuration file
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Dictionary with mcpServers configuration, or empty dict if file doesn't exist
|
|
32
|
+
"""
|
|
33
|
+
path = Path(file_path)
|
|
34
|
+
|
|
35
|
+
if not path.exists():
|
|
36
|
+
# Gracefully handle missing file - MCP is optional
|
|
37
|
+
return {}
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
with open(path) as f:
|
|
41
|
+
content = f.read()
|
|
42
|
+
|
|
43
|
+
# Apply environment variable substitution
|
|
44
|
+
content = substitute_env_vars(content)
|
|
45
|
+
config = json.loads(content)
|
|
46
|
+
|
|
47
|
+
# Validate structure
|
|
48
|
+
if 'mcpServers' not in config:
|
|
49
|
+
console.print(f"[yellow]Warning: {file_path} missing 'mcpServers' key[/yellow]")
|
|
50
|
+
return {}
|
|
51
|
+
|
|
52
|
+
return config
|
|
53
|
+
except json.JSONDecodeError as e:
|
|
54
|
+
console.print(f"[red]Error parsing {file_path}:[/red] {e}")
|
|
55
|
+
return {}
|
|
56
|
+
except Exception as e:
|
|
57
|
+
console.print(f"[red]Error loading {file_path}:[/red] {e}")
|
|
58
|
+
return {}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def load_mcp_tools(agent_def: Dict[str, Any], mcp_config_path: str) -> List[Dict[str, Any]]:
|
|
62
|
+
"""Load MCP tools from agent definition with tool-level filtering.
|
|
63
|
+
|
|
64
|
+
This function creates MCP server configuration objects that will be processed
|
|
65
|
+
by the runtime layer using langchain-mcp-adapters to connect to MCP servers
|
|
66
|
+
and load their tools.
|
|
67
|
+
|
|
68
|
+
The actual connection happens in create_mcp_client() which uses:
|
|
69
|
+
- langchain_mcp_adapters.client.MultiServerMCPClient for connection
|
|
70
|
+
- langchain_mcp_adapters.tools.load_mcp_tools for tool loading
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
agent_def: Agent definition dictionary containing mcps list
|
|
74
|
+
mcp_config_path: Path to mcp.json configuration file (workspace-level)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
List of MCP server configurations that will be used by MultiServerMCPClient
|
|
78
|
+
to connect to servers and load tools with filtering applied.
|
|
79
|
+
"""
|
|
80
|
+
import fnmatch
|
|
81
|
+
|
|
82
|
+
toolkit_configs = []
|
|
83
|
+
|
|
84
|
+
# Get MCP configuration
|
|
85
|
+
mcps = agent_def.get('mcps', [])
|
|
86
|
+
if not mcps:
|
|
87
|
+
return toolkit_configs
|
|
88
|
+
|
|
89
|
+
# Load mcp.json config file from workspace
|
|
90
|
+
mcp_config = load_mcp_config(mcp_config_path)
|
|
91
|
+
mcp_servers = mcp_config.get('mcpServers', {})
|
|
92
|
+
|
|
93
|
+
# Process each MCP entry
|
|
94
|
+
for mcp_entry in mcps:
|
|
95
|
+
# Handle both string and object formats
|
|
96
|
+
if isinstance(mcp_entry, str):
|
|
97
|
+
server_name = mcp_entry
|
|
98
|
+
agent_selected_tools = None
|
|
99
|
+
agent_excluded_tools = None
|
|
100
|
+
elif isinstance(mcp_entry, dict):
|
|
101
|
+
server_name = mcp_entry.get('name')
|
|
102
|
+
agent_selected_tools = mcp_entry.get('selected_tools')
|
|
103
|
+
agent_excluded_tools = mcp_entry.get('excluded_tools')
|
|
104
|
+
else:
|
|
105
|
+
console.print(f"[yellow]Warning: Invalid MCP entry format: {mcp_entry}[/yellow]")
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
if not server_name:
|
|
109
|
+
console.print(f"[yellow]Warning: MCP entry missing 'name': {mcp_entry}[/yellow]")
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
# Get server configuration
|
|
113
|
+
server_config = mcp_servers.get(server_name)
|
|
114
|
+
if not server_config:
|
|
115
|
+
console.print(f"[yellow]Warning: MCP server '{server_name}' not found in MCP configuration[/yellow]")
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
# Determine tool selection (agent overrides server defaults)
|
|
119
|
+
server_selected_tools = server_config.get('selected_tools')
|
|
120
|
+
server_excluded_tools = server_config.get('excluded_tools')
|
|
121
|
+
|
|
122
|
+
# Agent overrides take precedence
|
|
123
|
+
final_selected_tools = agent_selected_tools if agent_selected_tools is not None else server_selected_tools
|
|
124
|
+
final_excluded_tools = agent_excluded_tools if agent_excluded_tools is not None else server_excluded_tools
|
|
125
|
+
|
|
126
|
+
# Get connection details
|
|
127
|
+
server_type = server_config.get('type') # VSCode format: "stdio" or "streamable_http"
|
|
128
|
+
server_url = server_config.get('url')
|
|
129
|
+
server_command = server_config.get('command')
|
|
130
|
+
server_args = server_config.get('args', [])
|
|
131
|
+
server_env = server_config.get('env', {})
|
|
132
|
+
stateful = server_config.get('stateful', False)
|
|
133
|
+
|
|
134
|
+
# Build MCP server config for langchain-mcp-adapters
|
|
135
|
+
# Support both VSCode format (type: "stdio") and our format (command: "...")
|
|
136
|
+
if server_type == 'stdio' or server_command:
|
|
137
|
+
# stdio transport (subprocess)
|
|
138
|
+
if not server_command:
|
|
139
|
+
console.print(f"[red]Error: MCP server '{server_name}' has type 'stdio' but no 'command'[/red]")
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
console.print(f"[yellow]Note: MCP server '{server_name}' uses command/stdio connection[/yellow]")
|
|
143
|
+
mcp_server_config = {
|
|
144
|
+
'transport': 'stdio',
|
|
145
|
+
'command': server_command,
|
|
146
|
+
'args': server_args or []
|
|
147
|
+
}
|
|
148
|
+
elif server_type == 'streamable_http' or server_url:
|
|
149
|
+
# HTTP-based transport
|
|
150
|
+
if not server_url:
|
|
151
|
+
console.print(f"[red]Error: MCP server '{server_name}' has type 'streamable_http' but no 'url'[/red]")
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
mcp_server_config = {
|
|
155
|
+
'transport': 'streamable_http',
|
|
156
|
+
'url': server_url
|
|
157
|
+
}
|
|
158
|
+
else:
|
|
159
|
+
console.print(f"[red]Error: MCP server '{server_name}' has neither 'type'/'url' nor 'command'[/red]")
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
# Add environment variables if specified
|
|
163
|
+
if server_env:
|
|
164
|
+
mcp_server_config['env'] = server_env
|
|
165
|
+
|
|
166
|
+
# Wrap in toolkit config format
|
|
167
|
+
toolkit_config = {
|
|
168
|
+
'toolkit_type': 'mcp',
|
|
169
|
+
'name': server_name,
|
|
170
|
+
'mcp_server_config': mcp_server_config,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# Add tool filtering if specified
|
|
174
|
+
if final_selected_tools is not None:
|
|
175
|
+
toolkit_config['selected_tools'] = final_selected_tools
|
|
176
|
+
if final_excluded_tools is not None:
|
|
177
|
+
toolkit_config['excluded_tools'] = final_excluded_tools
|
|
178
|
+
|
|
179
|
+
toolkit_configs.append(toolkit_config)
|
|
180
|
+
|
|
181
|
+
# Display loaded tools info
|
|
182
|
+
if final_selected_tools:
|
|
183
|
+
tools_display = ', '.join(final_selected_tools)
|
|
184
|
+
console.print(f"✓ Loaded MCP server: [cyan]{server_name}[/cyan] (selected: {tools_display})")
|
|
185
|
+
elif final_excluded_tools:
|
|
186
|
+
console.print(f"✓ Loaded MCP server: [cyan]{server_name}[/cyan] (excluded: {', '.join(final_excluded_tools)})")
|
|
187
|
+
else:
|
|
188
|
+
console.print(f"✓ Loaded MCP server: [cyan]{server_name}[/cyan] (all tools)")
|
|
189
|
+
|
|
190
|
+
return toolkit_configs
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class MCPSessionManager:
|
|
194
|
+
"""Manages persistent MCP sessions for stateful tools like Playwright.
|
|
195
|
+
|
|
196
|
+
Holds MCP client and session context managers alive for the entire agent lifetime.
|
|
197
|
+
This is critical for stateful MCP servers that maintain state across tool calls
|
|
198
|
+
(e.g., Playwright browser sessions).
|
|
199
|
+
|
|
200
|
+
See: https://github.com/langchain-ai/langchain-mcp-adapters/issues/178
|
|
201
|
+
"""
|
|
202
|
+
def __init__(self):
|
|
203
|
+
self.client = None
|
|
204
|
+
self.sessions = {}
|
|
205
|
+
self._session_contexts = {}
|
|
206
|
+
|
|
207
|
+
def __del__(self):
|
|
208
|
+
"""Cleanup sessions when manager is garbage collected."""
|
|
209
|
+
# Best effort cleanup - sessions will be closed when process exits anyway
|
|
210
|
+
pass
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
async def load_mcp_tools_async(mcp_toolkit_configs: List[Dict[str, Any]]):
|
|
214
|
+
"""
|
|
215
|
+
Load actual MCP tools from servers with persistent sessions.
|
|
216
|
+
|
|
217
|
+
IMPORTANT: This function returns an MCPSessionManager that MUST be kept alive
|
|
218
|
+
for the entire agent session to maintain stateful MCP server state (e.g., browser).
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
mcp_toolkit_configs: List of MCP toolkit configurations with transport details
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Tuple of (session_manager, list_of_tools) where:
|
|
225
|
+
- session_manager: MCPSessionManager that holds client and persistent sessions
|
|
226
|
+
- list_of_tools: LangChain async tools from MCP that use persistent sessions
|
|
227
|
+
"""
|
|
228
|
+
if not mcp_toolkit_configs:
|
|
229
|
+
return None, []
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
from langchain_mcp_adapters.client import MultiServerMCPClient
|
|
233
|
+
from langchain_mcp_adapters.tools import load_mcp_tools
|
|
234
|
+
except ImportError:
|
|
235
|
+
console.print("[yellow]Warning: langchain-mcp-adapters not installed. MCP tools will not be available.[/yellow]")
|
|
236
|
+
console.print("[yellow]Install with: pip install langchain-mcp-adapters[/yellow]")
|
|
237
|
+
return None, []
|
|
238
|
+
|
|
239
|
+
# Build server configs for MultiServerMCPClient
|
|
240
|
+
server_configs = {}
|
|
241
|
+
for config in mcp_toolkit_configs:
|
|
242
|
+
server_name = config.get('name', 'unknown')
|
|
243
|
+
mcp_server_config = config.get('mcp_server_config', {})
|
|
244
|
+
transport = mcp_server_config.get('transport', 'stdio')
|
|
245
|
+
|
|
246
|
+
if transport == 'stdio':
|
|
247
|
+
command = mcp_server_config.get('command')
|
|
248
|
+
args = mcp_server_config.get('args', [])
|
|
249
|
+
env = mcp_server_config.get('env')
|
|
250
|
+
|
|
251
|
+
# Ensure command is a string
|
|
252
|
+
if isinstance(command, list):
|
|
253
|
+
command = command[0] if command else None
|
|
254
|
+
|
|
255
|
+
# Build config dict - only include env if it's set
|
|
256
|
+
config_dict = {
|
|
257
|
+
'transport': 'stdio',
|
|
258
|
+
'command': command,
|
|
259
|
+
'args': args
|
|
260
|
+
}
|
|
261
|
+
if env:
|
|
262
|
+
config_dict['env'] = env
|
|
263
|
+
|
|
264
|
+
server_configs[server_name] = config_dict
|
|
265
|
+
console.print(f"[dim]MCP server {server_name}: {command} {' '.join(args)}[/dim]")
|
|
266
|
+
|
|
267
|
+
elif transport == 'streamable_http':
|
|
268
|
+
server_configs[server_name] = {
|
|
269
|
+
'transport': 'streamable_http',
|
|
270
|
+
'url': mcp_server_config.get('url'),
|
|
271
|
+
'headers': mcp_server_config.get('headers')
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if not server_configs:
|
|
275
|
+
return None, []
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
# Create session manager to hold everything alive
|
|
279
|
+
manager = MCPSessionManager()
|
|
280
|
+
manager.client = MultiServerMCPClient(server_configs)
|
|
281
|
+
|
|
282
|
+
# Create PERSISTENT sessions for each server
|
|
283
|
+
# This keeps the MCP server subprocess and state alive for stateful tools
|
|
284
|
+
all_tools = []
|
|
285
|
+
|
|
286
|
+
for server_name in server_configs.keys():
|
|
287
|
+
# Enter the session context and keep it alive for entire agent lifetime
|
|
288
|
+
session_context = manager.client.session(server_name)
|
|
289
|
+
session = await session_context.__aenter__()
|
|
290
|
+
|
|
291
|
+
# Store both for cleanup and reference
|
|
292
|
+
manager.sessions[server_name] = session
|
|
293
|
+
manager._session_contexts[server_name] = session_context
|
|
294
|
+
|
|
295
|
+
# Load tools using the persistent session
|
|
296
|
+
tools = await load_mcp_tools(
|
|
297
|
+
session,
|
|
298
|
+
connection=manager.client.connections[server_name],
|
|
299
|
+
server_name=server_name
|
|
300
|
+
)
|
|
301
|
+
all_tools.extend(tools)
|
|
302
|
+
|
|
303
|
+
console.print(f"[dim]Created persistent session for {server_name}: {len(tools)} tools[/dim]")
|
|
304
|
+
|
|
305
|
+
console.print(f"✓ Loaded {len(all_tools)} MCP tools total from {len(mcp_toolkit_configs)} servers with persistent sessions")
|
|
306
|
+
|
|
307
|
+
return manager, all_tools
|
|
308
|
+
|
|
309
|
+
except ImportError:
|
|
310
|
+
# Already handled above
|
|
311
|
+
raise
|
|
312
|
+
except Exception as e:
|
|
313
|
+
logger.exception(f"Failed to create MCP client and sessions")
|
|
314
|
+
console.print(f"[red]Error: Failed to load MCP tools: {e}[/red]")
|
|
315
|
+
return None, []
|
alita_sdk/cli/toolkit.py
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Toolkit testing commands for Alita CLI.
|
|
3
|
+
|
|
4
|
+
Provides commands to list, inspect, and test toolkits directly from the command line.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Optional, Dict, Any
|
|
11
|
+
|
|
12
|
+
from .cli import get_client
|
|
13
|
+
from .toolkit_loader import load_toolkit_config
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.group()
|
|
19
|
+
def toolkit():
|
|
20
|
+
"""Toolkit testing commands."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@toolkit.command('list')
|
|
25
|
+
@click.option('--failed', is_flag=True, help='Show failed imports')
|
|
26
|
+
@click.pass_context
|
|
27
|
+
def toolkit_list(ctx, failed: bool):
|
|
28
|
+
"""List all available toolkits."""
|
|
29
|
+
formatter = ctx.obj['formatter']
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
# Import toolkit registry
|
|
33
|
+
from alita_sdk.tools import AVAILABLE_TOOLS, AVAILABLE_TOOLKITS, FAILED_IMPORTS
|
|
34
|
+
|
|
35
|
+
if failed:
|
|
36
|
+
# Show failed imports
|
|
37
|
+
if FAILED_IMPORTS:
|
|
38
|
+
click.echo("\nFailed toolkit imports:\n")
|
|
39
|
+
for name, error in FAILED_IMPORTS.items():
|
|
40
|
+
click.echo(f" - {name}: {error}")
|
|
41
|
+
click.echo(f"\nTotal failed: {len(FAILED_IMPORTS)}")
|
|
42
|
+
else:
|
|
43
|
+
click.echo("\nNo failed imports")
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# Build toolkit list
|
|
47
|
+
toolkits = []
|
|
48
|
+
for name, toolkit_dict in AVAILABLE_TOOLS.items():
|
|
49
|
+
toolkit_class_name = None
|
|
50
|
+
if 'toolkit_class' in toolkit_dict:
|
|
51
|
+
toolkit_class_name = toolkit_dict['toolkit_class'].__name__
|
|
52
|
+
|
|
53
|
+
toolkits.append({
|
|
54
|
+
'name': name,
|
|
55
|
+
'class_name': toolkit_class_name,
|
|
56
|
+
'has_get_tools': 'get_tools' in toolkit_dict
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
# Format and display
|
|
60
|
+
output = formatter.format_toolkit_list(toolkits)
|
|
61
|
+
click.echo(output)
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.exception("Failed to list toolkits")
|
|
65
|
+
click.echo(formatter.format_error(str(e)), err=True)
|
|
66
|
+
raise click.Abort()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@toolkit.command('schema')
|
|
70
|
+
@click.argument('toolkit_name')
|
|
71
|
+
@click.pass_context
|
|
72
|
+
def toolkit_schema(ctx, toolkit_name: str):
|
|
73
|
+
"""
|
|
74
|
+
Show configuration schema for a toolkit.
|
|
75
|
+
|
|
76
|
+
TOOLKIT_NAME: Name of the toolkit (e.g., 'jira', 'github', 'confluence')
|
|
77
|
+
"""
|
|
78
|
+
formatter = ctx.obj['formatter']
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
# Import toolkit registry
|
|
82
|
+
from alita_sdk.tools import AVAILABLE_TOOLKITS
|
|
83
|
+
|
|
84
|
+
# Find toolkit class
|
|
85
|
+
toolkit_class = None
|
|
86
|
+
for name, cls in AVAILABLE_TOOLKITS.items():
|
|
87
|
+
if name.lower().replace('toolkit', '').replace('alita', '').strip() == toolkit_name.lower():
|
|
88
|
+
toolkit_class = cls
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
if not toolkit_class:
|
|
92
|
+
# Try direct match
|
|
93
|
+
for name, cls in AVAILABLE_TOOLKITS.items():
|
|
94
|
+
if toolkit_name.lower() in name.lower():
|
|
95
|
+
toolkit_class = cls
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
if not toolkit_class:
|
|
99
|
+
available = [name.lower().replace('toolkit', '').replace('alita', '').strip()
|
|
100
|
+
for name in AVAILABLE_TOOLKITS.keys()]
|
|
101
|
+
raise click.ClickException(
|
|
102
|
+
f"Toolkit '{toolkit_name}' not found.\n"
|
|
103
|
+
f"Available toolkits: {', '.join(sorted(set(available)))}"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Get schema
|
|
107
|
+
schema_model = toolkit_class.toolkit_config_schema()
|
|
108
|
+
schema = schema_model.model_json_schema()
|
|
109
|
+
|
|
110
|
+
# Format and display
|
|
111
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
112
|
+
output = formatter.format_toolkit_schema(toolkit_name, schema)
|
|
113
|
+
else:
|
|
114
|
+
output = formatter.format_toolkit_schema(toolkit_name, schema)
|
|
115
|
+
|
|
116
|
+
click.echo(output)
|
|
117
|
+
|
|
118
|
+
except click.ClickException:
|
|
119
|
+
raise
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.exception(f"Failed to get schema for toolkit '{toolkit_name}'")
|
|
122
|
+
click.echo(formatter.format_error(str(e)), err=True)
|
|
123
|
+
raise click.Abort()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@toolkit.command('test')
|
|
127
|
+
@click.argument('toolkit_type')
|
|
128
|
+
@click.option('--tool', required=True, help='Tool name to execute')
|
|
129
|
+
@click.option('--config', 'config_file', type=click.File('r'),
|
|
130
|
+
help='Toolkit configuration JSON file')
|
|
131
|
+
@click.option('--params', type=click.File('r'),
|
|
132
|
+
help='Tool parameters JSON file')
|
|
133
|
+
@click.option('--param', multiple=True,
|
|
134
|
+
help='Tool parameter as key=value (can be used multiple times)')
|
|
135
|
+
@click.option('--llm-model', default='gpt-4o-mini',
|
|
136
|
+
help='LLM model to use (default: gpt-4o-mini)')
|
|
137
|
+
@click.option('--temperature', default=0.1, type=float,
|
|
138
|
+
help='LLM temperature (default: 0.1)')
|
|
139
|
+
@click.option('--max-tokens', default=1000, type=int,
|
|
140
|
+
help='LLM max tokens (default: 1000)')
|
|
141
|
+
@click.pass_context
|
|
142
|
+
def toolkit_test(ctx, toolkit_type: str, tool: str, config_file, params,
|
|
143
|
+
param, llm_model: str, temperature: float, max_tokens: int):
|
|
144
|
+
"""Test a specific tool from a toolkit.
|
|
145
|
+
|
|
146
|
+
TOOLKIT_TYPE: Type of toolkit (e.g., 'jira', 'github', 'confluence')
|
|
147
|
+
|
|
148
|
+
\b
|
|
149
|
+
Examples:
|
|
150
|
+
alita toolkit test jira --tool get_issue --config jira.json --params params.json
|
|
151
|
+
alita toolkit test jira --tool get_issue --config jira.json --param issue_key=PROJ-123
|
|
152
|
+
alita -o json toolkit test github --tool get_issue --config github.json
|
|
153
|
+
"""
|
|
154
|
+
formatter = ctx.obj['formatter']
|
|
155
|
+
client = get_client(ctx)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
# Load toolkit configuration
|
|
159
|
+
toolkit_config = {}
|
|
160
|
+
if config_file:
|
|
161
|
+
toolkit_config = load_toolkit_config(config_file.name)
|
|
162
|
+
logger.debug(f"Loaded toolkit config from {config_file.name}")
|
|
163
|
+
|
|
164
|
+
# Add the tool to selected_tools in the config
|
|
165
|
+
if 'selected_tools' not in toolkit_config:
|
|
166
|
+
toolkit_config['selected_tools'] = []
|
|
167
|
+
if tool not in toolkit_config['selected_tools']:
|
|
168
|
+
toolkit_config['selected_tools'].append(tool)
|
|
169
|
+
|
|
170
|
+
# Load tool parameters
|
|
171
|
+
tool_params = {}
|
|
172
|
+
if params:
|
|
173
|
+
tool_params = json.load(params)
|
|
174
|
+
logger.debug(f"Loaded tool params from {params.name}")
|
|
175
|
+
|
|
176
|
+
# Parse inline parameters
|
|
177
|
+
if param:
|
|
178
|
+
for param_pair in param:
|
|
179
|
+
if '=' not in param_pair:
|
|
180
|
+
raise click.ClickException(
|
|
181
|
+
f"Invalid parameter format: '{param_pair}'. "
|
|
182
|
+
"Use --param key=value"
|
|
183
|
+
)
|
|
184
|
+
key, value = param_pair.split('=', 1)
|
|
185
|
+
|
|
186
|
+
# Try to parse as JSON for complex values
|
|
187
|
+
try:
|
|
188
|
+
tool_params[key] = json.loads(value)
|
|
189
|
+
except json.JSONDecodeError:
|
|
190
|
+
tool_params[key] = value
|
|
191
|
+
|
|
192
|
+
logger.debug(f"Parsed {len(param)} inline parameters")
|
|
193
|
+
|
|
194
|
+
# Prepare full toolkit configuration
|
|
195
|
+
full_config = {
|
|
196
|
+
'toolkit_name': toolkit_type,
|
|
197
|
+
'type': toolkit_type,
|
|
198
|
+
'settings': toolkit_config
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
# LLM configuration
|
|
202
|
+
llm_config = {
|
|
203
|
+
'temperature': temperature,
|
|
204
|
+
'max_tokens': max_tokens,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# Execute test
|
|
208
|
+
logger.info(f"Testing tool '{tool}' from toolkit '{toolkit_type}'")
|
|
209
|
+
result = client.test_toolkit_tool(
|
|
210
|
+
toolkit_config=full_config,
|
|
211
|
+
tool_name=tool,
|
|
212
|
+
tool_params=tool_params,
|
|
213
|
+
llm_model=llm_model,
|
|
214
|
+
llm_config=llm_config
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Format and display result
|
|
218
|
+
output = formatter.format_toolkit_result(result)
|
|
219
|
+
click.echo(output)
|
|
220
|
+
|
|
221
|
+
# Exit with error code if test failed
|
|
222
|
+
if not result.get('success', False):
|
|
223
|
+
raise click.Abort()
|
|
224
|
+
|
|
225
|
+
except click.ClickException:
|
|
226
|
+
raise
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.exception("Failed to execute toolkit test")
|
|
229
|
+
click.echo(formatter.format_error(str(e)), err=True)
|
|
230
|
+
raise click.Abort()
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@toolkit.command('tools')
|
|
234
|
+
@click.argument('toolkit_type')
|
|
235
|
+
@click.option('--config', 'config_file', type=click.File('r'),
|
|
236
|
+
help='Toolkit configuration JSON file (required for some toolkits)')
|
|
237
|
+
@click.pass_context
|
|
238
|
+
def toolkit_tools(ctx, toolkit_type: str, config_file):
|
|
239
|
+
"""
|
|
240
|
+
List available tools for a specific toolkit.
|
|
241
|
+
|
|
242
|
+
TOOLKIT_TYPE: Type of toolkit (e.g., 'jira', 'github', 'confluence')
|
|
243
|
+
|
|
244
|
+
Some toolkits require configuration to determine available tools.
|
|
245
|
+
Use --config to provide configuration if needed.
|
|
246
|
+
"""
|
|
247
|
+
formatter = ctx.obj['formatter']
|
|
248
|
+
client = get_client(ctx)
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
# Load toolkit configuration if provided
|
|
252
|
+
toolkit_config = {}
|
|
253
|
+
if config_file:
|
|
254
|
+
toolkit_config = load_toolkit_config(config_file.name)
|
|
255
|
+
|
|
256
|
+
# Import and instantiate toolkit
|
|
257
|
+
from alita_sdk.tools import AVAILABLE_TOOLS
|
|
258
|
+
|
|
259
|
+
if toolkit_type not in AVAILABLE_TOOLS:
|
|
260
|
+
raise click.ClickException(
|
|
261
|
+
f"Toolkit '{toolkit_type}' not found. "
|
|
262
|
+
f"Use 'alita-cli toolkit list' to see available toolkits."
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
toolkit_entry = AVAILABLE_TOOLS[toolkit_type]
|
|
266
|
+
|
|
267
|
+
if 'toolkit_class' not in toolkit_entry:
|
|
268
|
+
raise click.ClickException(
|
|
269
|
+
f"Toolkit '{toolkit_type}' does not support tool listing"
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Get toolkit class and instantiate
|
|
273
|
+
toolkit_class = toolkit_entry['toolkit_class']
|
|
274
|
+
|
|
275
|
+
# Create minimal configuration
|
|
276
|
+
full_config = {
|
|
277
|
+
'toolkit_name': toolkit_type,
|
|
278
|
+
'type': toolkit_type,
|
|
279
|
+
'settings': toolkit_config
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
# Try to get available tools via API wrapper
|
|
283
|
+
try:
|
|
284
|
+
# Instantiate API wrapper if possible
|
|
285
|
+
api_wrapper_class = None
|
|
286
|
+
|
|
287
|
+
# Find API wrapper class by inspecting toolkit
|
|
288
|
+
import inspect
|
|
289
|
+
for name, obj in inspect.getmembers(toolkit_class):
|
|
290
|
+
if inspect.isclass(obj) and 'ApiWrapper' in obj.__name__:
|
|
291
|
+
api_wrapper_class = obj
|
|
292
|
+
break
|
|
293
|
+
|
|
294
|
+
if api_wrapper_class:
|
|
295
|
+
try:
|
|
296
|
+
api_wrapper = api_wrapper_class(**toolkit_config)
|
|
297
|
+
available_tools = api_wrapper.get_available_tools()
|
|
298
|
+
|
|
299
|
+
# Format tools list
|
|
300
|
+
click.echo(f"\nAvailable tools for {toolkit_type}:\n")
|
|
301
|
+
for tool_info in available_tools:
|
|
302
|
+
tool_name = tool_info.get('name', 'unknown')
|
|
303
|
+
description = tool_info.get('description', '')
|
|
304
|
+
click.echo(f" - {tool_name}")
|
|
305
|
+
if description:
|
|
306
|
+
click.echo(f" {description}")
|
|
307
|
+
|
|
308
|
+
click.echo(f"\nTotal: {len(available_tools)} tools")
|
|
309
|
+
return
|
|
310
|
+
|
|
311
|
+
except Exception as e:
|
|
312
|
+
logger.debug(f"Could not instantiate API wrapper: {e}")
|
|
313
|
+
|
|
314
|
+
# Fallback: show general info
|
|
315
|
+
click.echo(f"\n{toolkit_type} toolkit is available")
|
|
316
|
+
click.echo("Use 'alita-cli toolkit schema {toolkit_type}' to see configuration options")
|
|
317
|
+
|
|
318
|
+
except Exception as e:
|
|
319
|
+
logger.exception("Failed to list tools")
|
|
320
|
+
raise click.ClickException(str(e))
|
|
321
|
+
|
|
322
|
+
except click.ClickException:
|
|
323
|
+
raise
|
|
324
|
+
except Exception as e:
|
|
325
|
+
logger.exception("Failed to list toolkit tools")
|
|
326
|
+
click.echo(formatter.format_error(str(e)), err=True)
|
|
327
|
+
raise click.Abort()
|