alita-sdk 0.3.462__py3-none-any.whl → 0.3.627__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/agent/__init__.py +5 -0
- alita_sdk/cli/agent/default.py +258 -0
- alita_sdk/cli/agent_executor.py +15 -3
- alita_sdk/cli/agent_loader.py +56 -8
- alita_sdk/cli/agent_ui.py +93 -31
- alita_sdk/cli/agents.py +2274 -230
- alita_sdk/cli/callbacks.py +96 -25
- alita_sdk/cli/cli.py +10 -1
- alita_sdk/cli/config.py +162 -9
- 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/input_handler.py +419 -0
- alita_sdk/cli/inventory.py +1073 -0
- alita_sdk/cli/testcases/__init__.py +94 -0
- alita_sdk/cli/testcases/data_generation.py +119 -0
- alita_sdk/cli/testcases/discovery.py +96 -0
- alita_sdk/cli/testcases/executor.py +84 -0
- alita_sdk/cli/testcases/logger.py +85 -0
- alita_sdk/cli/testcases/parser.py +172 -0
- alita_sdk/cli/testcases/prompts.py +91 -0
- alita_sdk/cli/testcases/reporting.py +125 -0
- alita_sdk/cli/testcases/setup.py +108 -0
- alita_sdk/cli/testcases/test_runner.py +282 -0
- alita_sdk/cli/testcases/utils.py +39 -0
- alita_sdk/cli/testcases/validation.py +90 -0
- alita_sdk/cli/testcases/workflow.py +196 -0
- alita_sdk/cli/toolkit.py +14 -17
- alita_sdk/cli/toolkit_loader.py +35 -5
- alita_sdk/cli/tools/__init__.py +36 -2
- alita_sdk/cli/tools/approval.py +224 -0
- alita_sdk/cli/tools/filesystem.py +910 -64
- 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 +1 -1
- alita_sdk/configurations/ado.py +141 -20
- alita_sdk/configurations/bitbucket.py +0 -3
- alita_sdk/configurations/confluence.py +76 -42
- alita_sdk/configurations/figma.py +76 -0
- alita_sdk/configurations/gitlab.py +17 -5
- alita_sdk/configurations/openapi.py +329 -0
- alita_sdk/configurations/qtest.py +72 -1
- alita_sdk/configurations/report_portal.py +96 -0
- alita_sdk/configurations/sharepoint.py +148 -0
- alita_sdk/configurations/testio.py +83 -0
- alita_sdk/runtime/clients/artifact.py +3 -3
- alita_sdk/runtime/clients/client.py +353 -48
- alita_sdk/runtime/clients/sandbox_client.py +0 -21
- alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
- alita_sdk/runtime/langchain/assistant.py +123 -26
- alita_sdk/runtime/langchain/constants.py +642 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +6 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
- alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
- alita_sdk/runtime/langchain/document_loaders/constants.py +12 -7
- alita_sdk/runtime/langchain/langraph_agent.py +279 -73
- alita_sdk/runtime/langchain/utils.py +82 -15
- alita_sdk/runtime/llms/preloaded.py +2 -6
- 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 +7 -0
- alita_sdk/runtime/toolkits/application.py +21 -9
- alita_sdk/runtime/toolkits/artifact.py +15 -5
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +139 -251
- alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
- alita_sdk/runtime/toolkits/planning.py +178 -0
- alita_sdk/runtime/toolkits/skill_router.py +238 -0
- alita_sdk/runtime/toolkits/subgraph.py +251 -6
- alita_sdk/runtime/toolkits/tools.py +238 -32
- alita_sdk/runtime/toolkits/vectorstore.py +11 -5
- alita_sdk/runtime/tools/__init__.py +3 -1
- alita_sdk/runtime/tools/application.py +20 -6
- alita_sdk/runtime/tools/artifact.py +511 -28
- alita_sdk/runtime/tools/data_analysis.py +183 -0
- alita_sdk/runtime/tools/function.py +43 -15
- alita_sdk/runtime/tools/image_generation.py +50 -44
- alita_sdk/runtime/tools/llm.py +852 -67
- alita_sdk/runtime/tools/loop.py +3 -1
- alita_sdk/runtime/tools/loop_output.py +3 -1
- alita_sdk/runtime/tools/mcp_remote_tool.py +25 -10
- alita_sdk/runtime/tools/mcp_server_tool.py +7 -6
- 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 -4
- alita_sdk/runtime/tools/sandbox.py +9 -6
- alita_sdk/runtime/tools/skill_router.py +776 -0
- alita_sdk/runtime/tools/tool.py +3 -1
- alita_sdk/runtime/tools/vectorstore.py +7 -2
- alita_sdk/runtime/tools/vectorstore_base.py +51 -11
- alita_sdk/runtime/utils/AlitaCallback.py +137 -21
- alita_sdk/runtime/utils/constants.py +5 -1
- alita_sdk/runtime/utils/mcp_client.py +492 -0
- alita_sdk/runtime/utils/mcp_oauth.py +202 -5
- alita_sdk/runtime/utils/mcp_sse_client.py +36 -7
- alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
- alita_sdk/runtime/utils/serialization.py +155 -0
- alita_sdk/runtime/utils/streamlit.py +6 -10
- alita_sdk/runtime/utils/toolkit_utils.py +16 -5
- alita_sdk/runtime/utils/utils.py +36 -0
- alita_sdk/tools/__init__.py +113 -29
- alita_sdk/tools/ado/repos/__init__.py +51 -33
- alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
- alita_sdk/tools/ado/test_plan/__init__.py +25 -9
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
- alita_sdk/tools/ado/utils.py +1 -18
- alita_sdk/tools/ado/wiki/__init__.py +25 -8
- alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
- alita_sdk/tools/ado/work_item/__init__.py +26 -9
- alita_sdk/tools/ado/work_item/ado_wrapper.py +56 -3
- alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +11 -8
- alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +170 -45
- alita_sdk/tools/bitbucket/__init__.py +17 -12
- alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
- alita_sdk/tools/browser/__init__.py +5 -4
- alita_sdk/tools/carrier/__init__.py +5 -6
- alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
- alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
- alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
- alita_sdk/tools/chunkers/__init__.py +3 -1
- alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
- alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
- alita_sdk/tools/chunkers/universal_chunker.py +270 -0
- alita_sdk/tools/cloud/aws/__init__.py +10 -7
- alita_sdk/tools/cloud/azure/__init__.py +10 -7
- alita_sdk/tools/cloud/gcp/__init__.py +10 -7
- alita_sdk/tools/cloud/k8s/__init__.py +10 -7
- alita_sdk/tools/code/linter/__init__.py +10 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +10 -7
- alita_sdk/tools/code_indexer_toolkit.py +73 -23
- alita_sdk/tools/confluence/__init__.py +21 -15
- alita_sdk/tools/confluence/api_wrapper.py +78 -23
- alita_sdk/tools/confluence/loader.py +4 -2
- alita_sdk/tools/custom_open_api/__init__.py +12 -5
- alita_sdk/tools/elastic/__init__.py +11 -8
- alita_sdk/tools/elitea_base.py +493 -30
- alita_sdk/tools/figma/__init__.py +58 -11
- alita_sdk/tools/figma/api_wrapper.py +1235 -143
- alita_sdk/tools/figma/figma_client.py +73 -0
- alita_sdk/tools/figma/toon_tools.py +2748 -0
- alita_sdk/tools/github/__init__.py +13 -14
- alita_sdk/tools/github/github_client.py +224 -100
- alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
- alita_sdk/tools/github/schemas.py +14 -5
- alita_sdk/tools/github/tool.py +5 -1
- alita_sdk/tools/github/tool_prompts.py +9 -22
- alita_sdk/tools/gitlab/__init__.py +15 -11
- alita_sdk/tools/gitlab/api_wrapper.py +207 -41
- alita_sdk/tools/gitlab_org/__init__.py +10 -8
- alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
- alita_sdk/tools/google/bigquery/__init__.py +13 -12
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +10 -8
- alita_sdk/tools/google_places/api_wrapper.py +1 -1
- alita_sdk/tools/jira/__init__.py +17 -11
- alita_sdk/tools/jira/api_wrapper.py +91 -40
- alita_sdk/tools/keycloak/__init__.py +11 -8
- alita_sdk/tools/localgit/__init__.py +9 -3
- alita_sdk/tools/localgit/local_git.py +62 -54
- alita_sdk/tools/localgit/tool.py +5 -1
- alita_sdk/tools/memory/__init__.py +11 -3
- alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
- alita_sdk/tools/ocr/__init__.py +11 -8
- alita_sdk/tools/openapi/__init__.py +490 -114
- alita_sdk/tools/openapi/api_wrapper.py +1368 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +20 -12
- alita_sdk/tools/pandas/api_wrapper.py +38 -25
- alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
- alita_sdk/tools/postman/__init__.py +11 -11
- alita_sdk/tools/pptx/__init__.py +10 -9
- alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
- alita_sdk/tools/qtest/__init__.py +30 -10
- alita_sdk/tools/qtest/api_wrapper.py +430 -13
- alita_sdk/tools/rally/__init__.py +10 -8
- alita_sdk/tools/rally/api_wrapper.py +1 -1
- alita_sdk/tools/report_portal/__init__.py +12 -9
- alita_sdk/tools/salesforce/__init__.py +10 -9
- alita_sdk/tools/servicenow/__init__.py +17 -14
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +10 -8
- alita_sdk/tools/sharepoint/api_wrapper.py +4 -4
- alita_sdk/tools/slack/__init__.py +10 -8
- alita_sdk/tools/slack/api_wrapper.py +2 -2
- alita_sdk/tools/sql/__init__.py +11 -9
- alita_sdk/tools/testio/__init__.py +10 -8
- alita_sdk/tools/testrail/__init__.py +11 -8
- alita_sdk/tools/testrail/api_wrapper.py +1 -1
- alita_sdk/tools/utils/__init__.py +9 -4
- alita_sdk/tools/utils/content_parser.py +77 -3
- alita_sdk/tools/utils/text_operations.py +410 -0
- alita_sdk/tools/utils/tool_prompts.py +79 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +17 -13
- alita_sdk/tools/xray/__init__.py +12 -9
- alita_sdk/tools/yagmail/__init__.py +9 -3
- alita_sdk/tools/zephyr/__init__.py +9 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +11 -8
- alita_sdk/tools/zephyr_essential/__init__.py +10 -8
- alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
- alita_sdk/tools/zephyr_essential/client.py +2 -2
- alita_sdk/tools/zephyr_scale/__init__.py +11 -9
- alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
- alita_sdk/tools/zephyr_squad/__init__.py +10 -8
- {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +147 -7
- alita_sdk-0.3.627.dist-info/RECORD +468 -0
- alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
- alita_sdk-0.3.462.dist-info/RECORD +0 -384
- alita_sdk-0.3.462.dist-info/entry_points.txt +0 -2
- {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
|
@@ -6,19 +6,17 @@ Following MCP specification: https://modelcontextprotocol.io/specification/2025-
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
import re
|
|
9
|
-
import requests
|
|
10
9
|
import asyncio
|
|
11
10
|
from typing import List, Optional, Any, Dict, Literal, ClassVar, Union
|
|
12
11
|
|
|
13
12
|
from langchain_core.tools import BaseToolkit, BaseTool
|
|
14
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field, SecretStr
|
|
15
14
|
|
|
16
15
|
from ..tools.mcp_server_tool import McpServerTool
|
|
17
16
|
from ..tools.mcp_remote_tool import McpRemoteTool
|
|
18
17
|
from ..tools.mcp_inspect_tool import McpInspectTool
|
|
19
|
-
from ...tools.utils import TOOLKIT_SPLITTER, clean_string
|
|
20
18
|
from ..models.mcp_models import McpConnectionConfig
|
|
21
|
-
from ..utils.
|
|
19
|
+
from ..utils.mcp_client import McpClient
|
|
22
20
|
from ..utils.mcp_oauth import (
|
|
23
21
|
McpAuthorizationRequired,
|
|
24
22
|
canonical_resource,
|
|
@@ -41,110 +39,6 @@ def safe_int(value, default):
|
|
|
41
39
|
logger.warning(f"Invalid integer value '{value}', using default {default}")
|
|
42
40
|
return default
|
|
43
41
|
|
|
44
|
-
def optimize_tool_name(prefix: str, tool_name: str, max_total_length: int = 64) -> str:
|
|
45
|
-
"""
|
|
46
|
-
Optimize tool name to fit within max_total_length while preserving meaning.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
prefix: The toolkit prefix (already cleaned)
|
|
50
|
-
tool_name: The original tool name
|
|
51
|
-
max_total_length: Maximum total length for the full tool name (default: 64)
|
|
52
|
-
|
|
53
|
-
Returns:
|
|
54
|
-
Optimized full tool name in format: prefix___tool_name
|
|
55
|
-
"""
|
|
56
|
-
splitter = TOOLKIT_SPLITTER
|
|
57
|
-
splitter_len = len(splitter)
|
|
58
|
-
prefix_len = len(prefix)
|
|
59
|
-
|
|
60
|
-
# Calculate available space for tool name
|
|
61
|
-
available_space = max_total_length - prefix_len - splitter_len
|
|
62
|
-
|
|
63
|
-
if available_space <= 0:
|
|
64
|
-
logger.error(f"Prefix '{prefix}' is too long ({prefix_len} chars), cannot create valid tool name")
|
|
65
|
-
# Fallback: truncate prefix itself
|
|
66
|
-
prefix = prefix[:max_total_length - splitter_len - 10] # Leave 10 chars for tool name
|
|
67
|
-
available_space = max_total_length - len(prefix) - splitter_len
|
|
68
|
-
|
|
69
|
-
# If tool name fits, use it as-is
|
|
70
|
-
if len(tool_name) <= available_space:
|
|
71
|
-
return f'{prefix}{splitter}{tool_name}'
|
|
72
|
-
|
|
73
|
-
# Tool name is too long, need to optimize
|
|
74
|
-
logger.debug(f"Tool name '{tool_name}' is too long ({len(tool_name)} chars), optimizing to fit {available_space} chars")
|
|
75
|
-
|
|
76
|
-
# Split tool name into parts (handle camelCase, snake_case, and mixed)
|
|
77
|
-
# First, split by underscores and hyphens
|
|
78
|
-
parts = re.split(r'[_-]', tool_name)
|
|
79
|
-
|
|
80
|
-
# Further split camelCase within each part
|
|
81
|
-
all_parts = []
|
|
82
|
-
for part in parts:
|
|
83
|
-
# Insert underscore before uppercase letters (camelCase to snake_case)
|
|
84
|
-
snake_case_part = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', part)
|
|
85
|
-
all_parts.extend(snake_case_part.split('_'))
|
|
86
|
-
|
|
87
|
-
# Filter out empty parts
|
|
88
|
-
all_parts = [p for p in all_parts if p]
|
|
89
|
-
|
|
90
|
-
# Remove redundant prefix words (case-insensitive comparison)
|
|
91
|
-
# Only remove if prefix is meaningful (>= 3 chars) to avoid over-filtering
|
|
92
|
-
prefix_lower = prefix.lower()
|
|
93
|
-
filtered_parts = []
|
|
94
|
-
for part in all_parts:
|
|
95
|
-
part_lower = part.lower()
|
|
96
|
-
# Skip if this part contains the prefix or the prefix contains this part
|
|
97
|
-
# But only if both are meaningful (>= 3 chars)
|
|
98
|
-
should_remove = False
|
|
99
|
-
if len(prefix_lower) >= 3 and len(part_lower) >= 3:
|
|
100
|
-
if part_lower in prefix_lower or prefix_lower in part_lower:
|
|
101
|
-
should_remove = True
|
|
102
|
-
logger.debug(f"Removing redundant part '{part}' (matches prefix '{prefix}')")
|
|
103
|
-
|
|
104
|
-
if not should_remove:
|
|
105
|
-
filtered_parts.append(part)
|
|
106
|
-
|
|
107
|
-
# If we removed all parts, keep the original parts
|
|
108
|
-
if not filtered_parts:
|
|
109
|
-
filtered_parts = all_parts
|
|
110
|
-
|
|
111
|
-
# Reconstruct tool name with filtered parts
|
|
112
|
-
optimized_name = '_'.join(filtered_parts)
|
|
113
|
-
|
|
114
|
-
# If still too long, truncate intelligently
|
|
115
|
-
if len(optimized_name) > available_space:
|
|
116
|
-
# Strategy: Keep beginning and end, as they often contain the most important info
|
|
117
|
-
# For example: "projectalita_github_io_list_branches" -> "projectalita_list_branches"
|
|
118
|
-
|
|
119
|
-
# Try removing middle parts first
|
|
120
|
-
if len(filtered_parts) > 2:
|
|
121
|
-
# Keep first and last parts, remove middle
|
|
122
|
-
kept_parts = [filtered_parts[0], filtered_parts[-1]]
|
|
123
|
-
optimized_name = '_'.join(kept_parts)
|
|
124
|
-
|
|
125
|
-
# If still too long, add parts from the end until we run out of space
|
|
126
|
-
if len(optimized_name) <= available_space and len(filtered_parts) > 2:
|
|
127
|
-
for i in range(len(filtered_parts) - 2, 0, -1):
|
|
128
|
-
candidate = '_'.join([filtered_parts[0]] + filtered_parts[i:])
|
|
129
|
-
if len(candidate) <= available_space:
|
|
130
|
-
optimized_name = candidate
|
|
131
|
-
break
|
|
132
|
-
|
|
133
|
-
# If still too long, just truncate
|
|
134
|
-
if len(optimized_name) > available_space:
|
|
135
|
-
# Try to truncate at word boundary
|
|
136
|
-
truncated = optimized_name[:available_space]
|
|
137
|
-
last_underscore = truncated.rfind('_')
|
|
138
|
-
if last_underscore > available_space * 0.7: # Keep if we're not losing too much
|
|
139
|
-
optimized_name = truncated[:last_underscore]
|
|
140
|
-
else:
|
|
141
|
-
optimized_name = truncated
|
|
142
|
-
|
|
143
|
-
full_name = f'{prefix}{splitter}{optimized_name}'
|
|
144
|
-
logger.info(f"Optimized tool name: '{tool_name}' ({len(tool_name)} chars) -> '{optimized_name}' ({len(optimized_name)} chars), full: '{full_name}' ({len(full_name)} chars)")
|
|
145
|
-
|
|
146
|
-
return full_name
|
|
147
|
-
|
|
148
42
|
class McpToolkit(BaseToolkit):
|
|
149
43
|
"""
|
|
150
44
|
MCP Toolkit for connecting to a single remote MCP server and exposing its tools.
|
|
@@ -154,9 +48,6 @@ class McpToolkit(BaseToolkit):
|
|
|
154
48
|
tools: List[BaseTool] = []
|
|
155
49
|
toolkit_name: Optional[str] = None
|
|
156
50
|
|
|
157
|
-
# Class variable (not Pydantic field) for tool name length limit
|
|
158
|
-
toolkit_max_length: ClassVar[int] = 0 # No limit for MCP tool names
|
|
159
|
-
|
|
160
51
|
def __getstate__(self):
|
|
161
52
|
"""Custom serialization for pickle compatibility."""
|
|
162
53
|
state = self.__dict__.copy()
|
|
@@ -208,28 +99,32 @@ class McpToolkit(BaseToolkit):
|
|
|
208
99
|
}
|
|
209
100
|
)
|
|
210
101
|
),
|
|
211
|
-
|
|
212
|
-
|
|
102
|
+
client_id=(
|
|
103
|
+
Optional[str],
|
|
213
104
|
Field(
|
|
214
|
-
default=
|
|
215
|
-
description="
|
|
105
|
+
default=None,
|
|
106
|
+
description="OAuth Client ID (if applicable)"
|
|
216
107
|
)
|
|
217
108
|
),
|
|
218
|
-
|
|
219
|
-
|
|
109
|
+
client_secret=(
|
|
110
|
+
Optional[SecretStr],
|
|
220
111
|
Field(
|
|
221
|
-
default=
|
|
222
|
-
description="
|
|
223
|
-
json_schema_extra={
|
|
224
|
-
'tooltip': 'static: use registry, dynamic: live discovery, hybrid: try dynamic first'
|
|
225
|
-
}
|
|
112
|
+
default=None,
|
|
113
|
+
description="OAuth Client Secret (if applicable)"
|
|
226
114
|
)
|
|
227
115
|
),
|
|
228
|
-
|
|
229
|
-
|
|
116
|
+
scopes=(
|
|
117
|
+
Optional[List[str]],
|
|
118
|
+
Field(
|
|
119
|
+
default=None,
|
|
120
|
+
description="OAuth Scopes (if applicable)"
|
|
121
|
+
)
|
|
122
|
+
),
|
|
123
|
+
timeout=(
|
|
124
|
+
Union[int, str], # TODO: remove one I will figure out why UI sends str
|
|
230
125
|
Field(
|
|
231
126
|
default=300,
|
|
232
|
-
description="
|
|
127
|
+
description="Request timeout in seconds (1-3600)"
|
|
233
128
|
)
|
|
234
129
|
),
|
|
235
130
|
selected_tools=(
|
|
@@ -259,7 +154,7 @@ class McpToolkit(BaseToolkit):
|
|
|
259
154
|
__config__=ConfigDict(
|
|
260
155
|
json_schema_extra={
|
|
261
156
|
'metadata': {
|
|
262
|
-
"label": "
|
|
157
|
+
"label": "Remote MCP",
|
|
263
158
|
"icon_url": None,
|
|
264
159
|
"categories": ["other"],
|
|
265
160
|
"extra_categories": ["remote tools", "sse", "http"],
|
|
@@ -275,8 +170,6 @@ class McpToolkit(BaseToolkit):
|
|
|
275
170
|
url: str,
|
|
276
171
|
headers: Optional[Dict[str, str]] = None,
|
|
277
172
|
timeout: int = 60,
|
|
278
|
-
discovery_mode: str = "hybrid",
|
|
279
|
-
discovery_interval: int = 300,
|
|
280
173
|
selected_tools: List[str] = None,
|
|
281
174
|
enable_caching: bool = True,
|
|
282
175
|
cache_ttl: int = 300,
|
|
@@ -297,8 +190,6 @@ class McpToolkit(BaseToolkit):
|
|
|
297
190
|
url: MCP server HTTP URL
|
|
298
191
|
headers: HTTP headers for authentication
|
|
299
192
|
timeout: Request timeout in seconds
|
|
300
|
-
discovery_mode: Discovery mode ('static', 'dynamic', 'hybrid')
|
|
301
|
-
discovery_interval: Discovery interval in seconds (for periodic discovery)
|
|
302
193
|
selected_tools: List of specific tools to enable (empty = all tools)
|
|
303
194
|
enable_caching: Whether to enable caching
|
|
304
195
|
cache_ttl: Cache TTL in seconds
|
|
@@ -317,7 +208,6 @@ class McpToolkit(BaseToolkit):
|
|
|
317
208
|
|
|
318
209
|
# Convert numeric parameters that may come as strings from UI
|
|
319
210
|
timeout = safe_int(timeout, 60)
|
|
320
|
-
discovery_interval = safe_int(discovery_interval, 300)
|
|
321
211
|
cache_ttl = safe_int(cache_ttl, 300)
|
|
322
212
|
|
|
323
213
|
logger.info(f"Creating MCP toolkit: {toolkit_name}")
|
|
@@ -363,8 +253,7 @@ class McpToolkit(BaseToolkit):
|
|
|
363
253
|
connection_config=connection_config,
|
|
364
254
|
timeout=timeout,
|
|
365
255
|
selected_tools=selected_tools,
|
|
366
|
-
client=client
|
|
367
|
-
discovery_mode=discovery_mode
|
|
256
|
+
client=client
|
|
368
257
|
)
|
|
369
258
|
|
|
370
259
|
return toolkit
|
|
@@ -376,8 +265,7 @@ class McpToolkit(BaseToolkit):
|
|
|
376
265
|
connection_config: McpConnectionConfig,
|
|
377
266
|
timeout: int,
|
|
378
267
|
selected_tools: List[str],
|
|
379
|
-
client
|
|
380
|
-
discovery_mode: str = "dynamic"
|
|
268
|
+
client
|
|
381
269
|
) -> List[BaseTool]:
|
|
382
270
|
"""
|
|
383
271
|
Create tools from a single MCP server. Always performs live discovery when connection config is provided.
|
|
@@ -423,19 +311,23 @@ class McpToolkit(BaseToolkit):
|
|
|
423
311
|
|
|
424
312
|
logger.info(f"Successfully created {len(tools)} MCP tools from toolkit '{toolkit_name}' via direct discovery")
|
|
425
313
|
|
|
314
|
+
except McpAuthorizationRequired:
|
|
315
|
+
# Authorization is required; surface upstream so the caller can prompt the user
|
|
316
|
+
logger.info(f"MCP toolkit '{toolkit_name}' requires OAuth authorization")
|
|
317
|
+
raise
|
|
426
318
|
except Exception as e:
|
|
427
319
|
logger.error(f"Direct discovery failed for MCP toolkit '{toolkit_name}': {e}", exc_info=True)
|
|
428
320
|
logger.error(f"Discovery error details - URL: {connection_config.url}, Timeout: {timeout}s")
|
|
429
|
-
|
|
430
|
-
#
|
|
431
|
-
|
|
432
|
-
|
|
321
|
+
|
|
322
|
+
# For new MCP toolkits (no client), don't silently return empty - surface the error
|
|
323
|
+
# This helps users understand why tool discovery failed
|
|
324
|
+
if not client:
|
|
325
|
+
logger.warning(f"No fallback available for toolkit '{toolkit_name}' - re-raising discovery error")
|
|
433
326
|
raise
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
logger.warning(f"No fallback available for toolkit '{toolkit_name}' - returning empty tools list")
|
|
327
|
+
|
|
328
|
+
# Only fall back to static discovery for existing toolkits with a client
|
|
329
|
+
logger.info(f"Falling back to static discovery for toolkit '{toolkit_name}'")
|
|
330
|
+
tools = cls._create_tools_static(toolkit_name, selected_tools, timeout, client)
|
|
439
331
|
|
|
440
332
|
# Don't add inspection tool to agent - it's only for internal use by toolkit
|
|
441
333
|
# inspection_tool = cls._create_inspection_tool(
|
|
@@ -459,28 +351,34 @@ class McpToolkit(BaseToolkit):
|
|
|
459
351
|
toolkit_name: str,
|
|
460
352
|
connection_config: McpConnectionConfig,
|
|
461
353
|
timeout: int
|
|
462
|
-
) -> List[Dict[str, Any]]:
|
|
354
|
+
) -> tuple[List[Dict[str, Any]], Optional[str]]:
|
|
463
355
|
"""
|
|
464
356
|
Discover tools and prompts from MCP server using SSE client.
|
|
465
|
-
|
|
466
|
-
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Tuple of (tool_list, server_session_id) - session_id may be server-provided
|
|
467
360
|
"""
|
|
468
|
-
|
|
361
|
+
initial_session_id = connection_config.session_id
|
|
469
362
|
|
|
470
|
-
if not
|
|
471
|
-
logger.warning(f"[MCP Session] No session_id provided for '{toolkit_name}' -
|
|
472
|
-
logger.warning(f"[MCP Session] Frontend should generate a UUID and include it with mcp_tokens")
|
|
363
|
+
if not initial_session_id:
|
|
364
|
+
logger.warning(f"[MCP Session] No session_id provided for '{toolkit_name}' - will generate one")
|
|
473
365
|
|
|
474
366
|
# Run async discovery in sync context
|
|
475
367
|
try:
|
|
476
|
-
all_tools = asyncio.run(
|
|
368
|
+
all_tools, server_session_id = asyncio.run(
|
|
477
369
|
cls._discover_tools_async(
|
|
478
370
|
toolkit_name=toolkit_name,
|
|
479
371
|
connection_config=connection_config,
|
|
480
372
|
timeout=timeout
|
|
481
373
|
)
|
|
482
374
|
)
|
|
483
|
-
|
|
375
|
+
# Return tools and the session_id (server-provided or generated)
|
|
376
|
+
logger.info(f"[MCP Session] Final session_id for '{toolkit_name}': {server_session_id}")
|
|
377
|
+
return all_tools, server_session_id
|
|
378
|
+
except McpAuthorizationRequired:
|
|
379
|
+
# Re-raise auth required exceptions directly
|
|
380
|
+
logger.info(f"[MCP SSE] Authorization required for '{toolkit_name}'")
|
|
381
|
+
raise
|
|
484
382
|
except Exception as e:
|
|
485
383
|
logger.error(f"[MCP SSE] Discovery failed for '{toolkit_name}': {e}")
|
|
486
384
|
raise
|
|
@@ -491,9 +389,12 @@ class McpToolkit(BaseToolkit):
|
|
|
491
389
|
toolkit_name: str,
|
|
492
390
|
connection_config: McpConnectionConfig,
|
|
493
391
|
timeout: int
|
|
494
|
-
) -> List[Dict[str, Any]]:
|
|
392
|
+
) -> tuple[List[Dict[str, Any]], Optional[str]]:
|
|
495
393
|
"""
|
|
496
394
|
Async implementation of tool discovery using SSE client.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
Tuple of (tool_list, server_session_id)
|
|
497
398
|
"""
|
|
498
399
|
all_tools = []
|
|
499
400
|
session_id = connection_config.session_id
|
|
@@ -505,65 +406,74 @@ class McpToolkit(BaseToolkit):
|
|
|
505
406
|
session_id = str(uuid.uuid4())
|
|
506
407
|
logger.info(f"[MCP SSE] Generated temporary session_id for OAuth: {session_id}")
|
|
507
408
|
|
|
508
|
-
logger.info(f"[MCP
|
|
409
|
+
logger.info(f"[MCP] Discovering from {connection_config.url} with session {session_id}")
|
|
509
410
|
|
|
510
411
|
# Prepare headers
|
|
511
412
|
headers = {}
|
|
512
413
|
if connection_config.headers:
|
|
513
414
|
headers.update(connection_config.headers)
|
|
514
415
|
|
|
515
|
-
# Create SSE
|
|
516
|
-
client =
|
|
416
|
+
# Create unified MCP client (auto-detects SSE vs Streamable HTTP)
|
|
417
|
+
client = McpClient(
|
|
517
418
|
url=connection_config.url,
|
|
518
419
|
session_id=session_id,
|
|
519
420
|
headers=headers,
|
|
520
421
|
timeout=timeout
|
|
521
422
|
)
|
|
522
423
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
424
|
+
server_session_id = None
|
|
425
|
+
async with client:
|
|
426
|
+
# Initialize MCP session
|
|
427
|
+
await client.initialize()
|
|
428
|
+
logger.info(f"[MCP] Session initialized for '{toolkit_name}' (transport={client.detected_transport})")
|
|
429
|
+
|
|
430
|
+
# Capture server-provided session_id (from mcp-session-id header)
|
|
431
|
+
server_session_id = client.server_session_id
|
|
432
|
+
if server_session_id:
|
|
433
|
+
logger.info(f"[MCP] Server provided session_id: {server_session_id}")
|
|
434
|
+
|
|
435
|
+
# Discover tools
|
|
436
|
+
tools = await client.list_tools()
|
|
437
|
+
all_tools.extend(tools)
|
|
438
|
+
logger.info(f"[MCP] Discovered {len(tools)} tools from '{toolkit_name}'")
|
|
439
|
+
|
|
440
|
+
# Discover prompts
|
|
441
|
+
try:
|
|
442
|
+
prompts = await client.list_prompts()
|
|
443
|
+
# Convert prompts to tool format
|
|
444
|
+
for prompt in prompts:
|
|
445
|
+
prompt_tool = {
|
|
446
|
+
"name": f"prompt_{prompt.get('name', 'unnamed')}",
|
|
447
|
+
"description": prompt.get('description', f"Execute prompt: {prompt.get('name')}"),
|
|
448
|
+
"inputSchema": {
|
|
449
|
+
"type": "object",
|
|
450
|
+
"properties": {
|
|
451
|
+
"arguments": {
|
|
452
|
+
"type": "object",
|
|
453
|
+
"description": "Arguments for the prompt template",
|
|
454
|
+
"properties": {
|
|
455
|
+
arg.get("name"): {
|
|
456
|
+
"type": "string",
|
|
457
|
+
"description": arg.get("description", ""),
|
|
458
|
+
"required": arg.get("required", False)
|
|
459
|
+
}
|
|
460
|
+
for arg in prompt.get("arguments", [])
|
|
551
461
|
}
|
|
552
|
-
for arg in prompt.get("arguments", [])
|
|
553
462
|
}
|
|
554
463
|
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
logger.warning(f"[MCP SSE] Failed to discover prompts: {e}")
|
|
464
|
+
},
|
|
465
|
+
"_mcp_type": "prompt",
|
|
466
|
+
"_mcp_prompt_name": prompt.get('name')
|
|
467
|
+
}
|
|
468
|
+
all_tools.append(prompt_tool)
|
|
469
|
+
logger.info(f"[MCP] Discovered {len(prompts)} prompts from '{toolkit_name}'")
|
|
470
|
+
except Exception as e:
|
|
471
|
+
logger.warning(f"[MCP] Failed to discover prompts: {e}")
|
|
564
472
|
|
|
565
|
-
logger.info(f"[MCP
|
|
566
|
-
|
|
473
|
+
logger.info(f"[MCP] Total discovered {len(all_tools)} items from '{toolkit_name}'")
|
|
474
|
+
# Return tools and server-provided session_id (use server's if available, else the one we sent)
|
|
475
|
+
final_session_id = server_session_id or session_id
|
|
476
|
+
return all_tools, final_session_id
|
|
567
477
|
|
|
568
478
|
@classmethod
|
|
569
479
|
def _create_tool_from_dict(
|
|
@@ -577,28 +487,23 @@ class McpToolkit(BaseToolkit):
|
|
|
577
487
|
) -> Optional[BaseTool]:
|
|
578
488
|
"""Create a BaseTool from a tool/prompt dictionary (from direct HTTP discovery)."""
|
|
579
489
|
try:
|
|
580
|
-
#
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
# Clean toolkit name for prefixing
|
|
584
|
-
clean_prefix = clean_string(toolkit_name, max_length_value)
|
|
585
|
-
|
|
586
|
-
# Optimize tool name to fit within 64 character limit
|
|
587
|
-
full_tool_name = optimize_tool_name(clean_prefix, tool_dict.get("name", "unknown"))
|
|
490
|
+
# Use original tool name directly
|
|
491
|
+
tool_name = tool_dict.get("name", "unknown")
|
|
588
492
|
|
|
589
493
|
# Check if this is a prompt (converted to tool)
|
|
590
494
|
is_prompt = tool_dict.get("_mcp_type") == "prompt"
|
|
591
495
|
item_type = "prompt" if is_prompt else "tool"
|
|
592
496
|
|
|
593
|
-
# Build description and ensure it doesn't exceed 1000 characters
|
|
594
|
-
|
|
497
|
+
# Build description with toolkit context and ensure it doesn't exceed 1000 characters
|
|
498
|
+
base_description = tool_dict.get('description', '')
|
|
499
|
+
description = f"{base_description}\nToolkit: {toolkit_name} ({connection_config.url})"
|
|
595
500
|
if len(description) > 1000:
|
|
596
501
|
description = description[:997] + "..."
|
|
597
|
-
logger.debug(f"Trimmed description for tool '{
|
|
502
|
+
logger.debug(f"Trimmed description for tool '{tool_name}' to 1000 chars")
|
|
598
503
|
|
|
599
504
|
# Use McpRemoteTool for remote MCP servers (HTTP/SSE)
|
|
600
505
|
return McpRemoteTool(
|
|
601
|
-
name=
|
|
506
|
+
name=tool_name,
|
|
602
507
|
description=description,
|
|
603
508
|
args_schema=McpServerTool.create_pydantic_model_from_schema(
|
|
604
509
|
tool_dict.get("inputSchema", {})
|
|
@@ -610,11 +515,12 @@ class McpToolkit(BaseToolkit):
|
|
|
610
515
|
tool_timeout_sec=timeout,
|
|
611
516
|
is_prompt=is_prompt,
|
|
612
517
|
prompt_name=tool_dict.get("_mcp_prompt_name") if is_prompt else None,
|
|
613
|
-
original_tool_name=
|
|
614
|
-
session_id=session_id # Pass session ID for stateful SSE servers
|
|
518
|
+
original_tool_name=tool_name, # Store original name for MCP server invocation
|
|
519
|
+
session_id=session_id, # Pass session ID for stateful SSE servers
|
|
520
|
+
metadata={"toolkit_name": toolkit_name, "toolkit_type": "mcp"}
|
|
615
521
|
)
|
|
616
522
|
except Exception as e:
|
|
617
|
-
logger.error(f"Failed to create MCP tool '{
|
|
523
|
+
logger.error(f"Failed to create MCP tool '{tool_name}' from toolkit '{toolkit_name}': {e}")
|
|
618
524
|
return None
|
|
619
525
|
|
|
620
526
|
@classmethod
|
|
@@ -673,7 +579,7 @@ class McpToolkit(BaseToolkit):
|
|
|
673
579
|
# We don't have full connection config in static mode, so create a basic one
|
|
674
580
|
# The inspection tool will work as long as the server is accessible
|
|
675
581
|
inspection_tool = McpInspectTool(
|
|
676
|
-
name=
|
|
582
|
+
name="mcp_inspect",
|
|
677
583
|
server_name=toolkit_name,
|
|
678
584
|
server_url="", # Will be populated by the client if available
|
|
679
585
|
description=f"Inspect available tools, prompts, and resources from MCP toolkit '{toolkit_name}'"
|
|
@@ -695,30 +601,26 @@ class McpToolkit(BaseToolkit):
|
|
|
695
601
|
) -> Optional[BaseTool]:
|
|
696
602
|
"""Create a BaseTool from discovered metadata."""
|
|
697
603
|
try:
|
|
698
|
-
#
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
# Clean server name for prefixing (use tool_metadata.server instead of toolkit_name)
|
|
702
|
-
clean_prefix = clean_string(tool_metadata.server, max_length_value)
|
|
703
|
-
# Optimize tool name to fit within 64 character limit
|
|
704
|
-
full_tool_name = optimize_tool_name(clean_prefix, tool_metadata.name)
|
|
604
|
+
# Use original tool name directly
|
|
605
|
+
tool_name = tool_metadata.name
|
|
705
606
|
|
|
706
|
-
# Build description and ensure it doesn't exceed 1000 characters
|
|
707
|
-
description = f"
|
|
607
|
+
# Build description with toolkit context and ensure it doesn't exceed 1000 characters
|
|
608
|
+
description = f"{tool_metadata.description}\nToolkit: {toolkit_name}"
|
|
708
609
|
if len(description) > 1000:
|
|
709
610
|
description = description[:997] + "..."
|
|
710
|
-
logger.debug(f"Trimmed description for tool '{
|
|
611
|
+
logger.debug(f"Trimmed description for tool '{tool_name}' to 1000 chars")
|
|
711
612
|
|
|
712
613
|
return McpServerTool(
|
|
713
|
-
name=
|
|
614
|
+
name=tool_name,
|
|
714
615
|
description=description,
|
|
715
616
|
args_schema=McpServerTool.create_pydantic_model_from_schema(tool_metadata.input_schema),
|
|
716
617
|
client=client,
|
|
717
618
|
server=tool_metadata.server,
|
|
718
|
-
tool_timeout_sec=timeout
|
|
619
|
+
tool_timeout_sec=timeout,
|
|
620
|
+
metadata={"toolkit_name": toolkit_name, "toolkit_type": name}
|
|
719
621
|
)
|
|
720
622
|
except Exception as e:
|
|
721
|
-
logger.error(f"Failed to create MCP tool '{
|
|
623
|
+
logger.error(f"Failed to create MCP tool '{tool_name}' from server '{tool_metadata.server}': {e}")
|
|
722
624
|
return None
|
|
723
625
|
|
|
724
626
|
@classmethod
|
|
@@ -731,33 +633,29 @@ class McpToolkit(BaseToolkit):
|
|
|
731
633
|
) -> Optional[BaseTool]:
|
|
732
634
|
"""Create a single MCP tool."""
|
|
733
635
|
try:
|
|
734
|
-
#
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
# Clean toolkit name for prefixing
|
|
738
|
-
clean_prefix = clean_string(toolkit_name, max_length_value)
|
|
739
|
-
|
|
740
|
-
# Optimize tool name to fit within 64 character limit
|
|
741
|
-
full_tool_name = optimize_tool_name(clean_prefix, available_tool["name"])
|
|
636
|
+
# Use original tool name directly
|
|
637
|
+
tool_name = available_tool["name"]
|
|
742
638
|
|
|
743
|
-
# Build description and ensure it doesn't exceed 1000 characters
|
|
744
|
-
|
|
639
|
+
# Build description with toolkit context and ensure it doesn't exceed 1000 characters
|
|
640
|
+
base_description = available_tool.get('description', '')
|
|
641
|
+
description = f"{base_description}\nToolkit: {toolkit_name}"
|
|
745
642
|
if len(description) > 1000:
|
|
746
643
|
description = description[:997] + "..."
|
|
747
|
-
logger.debug(f"Trimmed description for tool '{
|
|
644
|
+
logger.debug(f"Trimmed description for tool '{tool_name}' to 1000 chars")
|
|
748
645
|
|
|
749
646
|
return McpServerTool(
|
|
750
|
-
name=
|
|
647
|
+
name=tool_name,
|
|
751
648
|
description=description,
|
|
752
649
|
args_schema=McpServerTool.create_pydantic_model_from_schema(
|
|
753
650
|
available_tool.get("inputSchema", {})
|
|
754
651
|
),
|
|
755
652
|
client=client,
|
|
756
653
|
server=toolkit_name,
|
|
757
|
-
tool_timeout_sec=timeout
|
|
654
|
+
tool_timeout_sec=timeout,
|
|
655
|
+
metadata={"toolkit_name": toolkit_name, "toolkit_type": name}
|
|
758
656
|
)
|
|
759
657
|
except Exception as e:
|
|
760
|
-
logger.error(f"Failed to create MCP tool '{
|
|
658
|
+
logger.error(f"Failed to create MCP tool '{tool_name}' from toolkit '{toolkit_name}': {e}")
|
|
761
659
|
return None
|
|
762
660
|
|
|
763
661
|
@classmethod
|
|
@@ -768,16 +666,8 @@ class McpToolkit(BaseToolkit):
|
|
|
768
666
|
) -> Optional[BaseTool]:
|
|
769
667
|
"""Create the inspection tool for the MCP toolkit."""
|
|
770
668
|
try:
|
|
771
|
-
# Store toolkit_max_length in local variable to avoid contextual access issues
|
|
772
|
-
max_length_value = cls.toolkit_max_length
|
|
773
|
-
|
|
774
|
-
# Clean toolkit name for prefixing
|
|
775
|
-
clean_prefix = clean_string(toolkit_name, max_length_value)
|
|
776
|
-
|
|
777
|
-
full_tool_name = f'{clean_prefix}{TOOLKIT_SPLITTER}mcp_inspect'
|
|
778
|
-
|
|
779
669
|
return McpInspectTool(
|
|
780
|
-
name=
|
|
670
|
+
name="mcp_inspect",
|
|
781
671
|
server_name=toolkit_name,
|
|
782
672
|
server_url=connection_config.url,
|
|
783
673
|
server_headers=connection_config.headers,
|
|
@@ -858,8 +748,6 @@ def get_tools(tool_config: dict, alita_client, llm=None, memory_store=None) -> L
|
|
|
858
748
|
url=url,
|
|
859
749
|
headers=headers,
|
|
860
750
|
timeout=safe_int(settings.get('timeout'), 60),
|
|
861
|
-
discovery_mode=settings.get('discovery_mode', 'dynamic'),
|
|
862
|
-
discovery_interval=safe_int(settings.get('discovery_interval'), 300),
|
|
863
751
|
selected_tools=settings.get('selected_tools', []),
|
|
864
752
|
enable_caching=settings.get('enable_caching', True),
|
|
865
753
|
cache_ttl=safe_int(settings.get('cache_ttl'), 300),
|