veadk-python 0.2.27__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.
Files changed (218) hide show
  1. veadk/__init__.py +37 -0
  2. veadk/a2a/__init__.py +13 -0
  3. veadk/a2a/agent_card.py +45 -0
  4. veadk/a2a/remote_ve_agent.py +390 -0
  5. veadk/a2a/utils/__init__.py +13 -0
  6. veadk/a2a/utils/agent_to_a2a.py +170 -0
  7. veadk/a2a/ve_a2a_server.py +93 -0
  8. veadk/a2a/ve_agent_executor.py +78 -0
  9. veadk/a2a/ve_middlewares.py +313 -0
  10. veadk/a2a/ve_task_store.py +37 -0
  11. veadk/agent.py +402 -0
  12. veadk/agent_builder.py +93 -0
  13. veadk/agents/loop_agent.py +68 -0
  14. veadk/agents/parallel_agent.py +72 -0
  15. veadk/agents/sequential_agent.py +64 -0
  16. veadk/auth/__init__.py +13 -0
  17. veadk/auth/base_auth.py +22 -0
  18. veadk/auth/ve_credential_service.py +203 -0
  19. veadk/auth/veauth/__init__.py +13 -0
  20. veadk/auth/veauth/apmplus_veauth.py +58 -0
  21. veadk/auth/veauth/ark_veauth.py +75 -0
  22. veadk/auth/veauth/base_veauth.py +50 -0
  23. veadk/auth/veauth/cozeloop_veauth.py +13 -0
  24. veadk/auth/veauth/opensearch_veauth.py +75 -0
  25. veadk/auth/veauth/postgresql_veauth.py +75 -0
  26. veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
  27. veadk/auth/veauth/speech_veauth.py +54 -0
  28. veadk/auth/veauth/utils.py +69 -0
  29. veadk/auth/veauth/vesearch_veauth.py +62 -0
  30. veadk/auth/veauth/viking_mem0_veauth.py +91 -0
  31. veadk/cli/__init__.py +13 -0
  32. veadk/cli/cli.py +58 -0
  33. veadk/cli/cli_clean.py +87 -0
  34. veadk/cli/cli_create.py +163 -0
  35. veadk/cli/cli_deploy.py +233 -0
  36. veadk/cli/cli_eval.py +215 -0
  37. veadk/cli/cli_init.py +214 -0
  38. veadk/cli/cli_kb.py +110 -0
  39. veadk/cli/cli_pipeline.py +285 -0
  40. veadk/cli/cli_prompt.py +86 -0
  41. veadk/cli/cli_update.py +106 -0
  42. veadk/cli/cli_uploadevalset.py +139 -0
  43. veadk/cli/cli_web.py +143 -0
  44. veadk/cloud/__init__.py +13 -0
  45. veadk/cloud/cloud_agent_engine.py +485 -0
  46. veadk/cloud/cloud_app.py +475 -0
  47. veadk/config.py +115 -0
  48. veadk/configs/__init__.py +13 -0
  49. veadk/configs/auth_configs.py +133 -0
  50. veadk/configs/database_configs.py +132 -0
  51. veadk/configs/model_configs.py +78 -0
  52. veadk/configs/tool_configs.py +54 -0
  53. veadk/configs/tracing_configs.py +110 -0
  54. veadk/consts.py +74 -0
  55. veadk/evaluation/__init__.py +17 -0
  56. veadk/evaluation/adk_evaluator/__init__.py +17 -0
  57. veadk/evaluation/adk_evaluator/adk_evaluator.py +302 -0
  58. veadk/evaluation/base_evaluator.py +642 -0
  59. veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
  60. veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +339 -0
  61. veadk/evaluation/eval_set_file_loader.py +48 -0
  62. veadk/evaluation/eval_set_recorder.py +146 -0
  63. veadk/evaluation/types.py +65 -0
  64. veadk/evaluation/utils/prometheus.py +196 -0
  65. veadk/integrations/__init__.py +13 -0
  66. veadk/integrations/ve_apig/__init__.py +13 -0
  67. veadk/integrations/ve_apig/ve_apig.py +349 -0
  68. veadk/integrations/ve_apig/ve_apig_utils.py +332 -0
  69. veadk/integrations/ve_code_pipeline/__init__.py +13 -0
  70. veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
  71. veadk/integrations/ve_cozeloop/__init__.py +13 -0
  72. veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
  73. veadk/integrations/ve_cr/__init__.py +13 -0
  74. veadk/integrations/ve_cr/ve_cr.py +220 -0
  75. veadk/integrations/ve_faas/__init__.py +13 -0
  76. veadk/integrations/ve_faas/template/cookiecutter.json +15 -0
  77. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  78. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  79. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/config.yaml.example +6 -0
  80. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +106 -0
  81. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__init__.py +13 -0
  82. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +25 -0
  83. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +202 -0
  84. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -0
  85. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +49 -0
  86. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/__init__.py +14 -0
  87. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/agent.py +27 -0
  88. veadk/integrations/ve_faas/ve_faas.py +754 -0
  89. veadk/integrations/ve_faas/ve_faas_utils.py +408 -0
  90. veadk/integrations/ve_faas/web_template/cookiecutter.json +20 -0
  91. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  92. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  93. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
  94. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +44 -0
  95. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
  96. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
  97. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
  98. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
  99. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
  100. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
  101. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
  102. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
  103. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
  104. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
  105. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
  106. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
  107. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
  108. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
  109. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
  110. veadk/integrations/ve_identity/__init__.py +110 -0
  111. veadk/integrations/ve_identity/auth_config.py +261 -0
  112. veadk/integrations/ve_identity/auth_mixins.py +650 -0
  113. veadk/integrations/ve_identity/auth_processor.py +385 -0
  114. veadk/integrations/ve_identity/function_tool.py +158 -0
  115. veadk/integrations/ve_identity/identity_client.py +864 -0
  116. veadk/integrations/ve_identity/mcp_tool.py +181 -0
  117. veadk/integrations/ve_identity/mcp_toolset.py +431 -0
  118. veadk/integrations/ve_identity/models.py +228 -0
  119. veadk/integrations/ve_identity/token_manager.py +188 -0
  120. veadk/integrations/ve_identity/utils.py +151 -0
  121. veadk/integrations/ve_prompt_pilot/__init__.py +13 -0
  122. veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +85 -0
  123. veadk/integrations/ve_tls/__init__.py +13 -0
  124. veadk/integrations/ve_tls/utils.py +116 -0
  125. veadk/integrations/ve_tls/ve_tls.py +212 -0
  126. veadk/integrations/ve_tos/ve_tos.py +710 -0
  127. veadk/integrations/ve_viking_db_memory/__init__.py +13 -0
  128. veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py +308 -0
  129. veadk/knowledgebase/__init__.py +17 -0
  130. veadk/knowledgebase/backends/__init__.py +13 -0
  131. veadk/knowledgebase/backends/base_backend.py +72 -0
  132. veadk/knowledgebase/backends/in_memory_backend.py +91 -0
  133. veadk/knowledgebase/backends/opensearch_backend.py +162 -0
  134. veadk/knowledgebase/backends/redis_backend.py +172 -0
  135. veadk/knowledgebase/backends/utils.py +92 -0
  136. veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +608 -0
  137. veadk/knowledgebase/entry.py +25 -0
  138. veadk/knowledgebase/knowledgebase.py +307 -0
  139. veadk/memory/__init__.py +35 -0
  140. veadk/memory/long_term_memory.py +365 -0
  141. veadk/memory/long_term_memory_backends/__init__.py +13 -0
  142. veadk/memory/long_term_memory_backends/base_backend.py +35 -0
  143. veadk/memory/long_term_memory_backends/in_memory_backend.py +67 -0
  144. veadk/memory/long_term_memory_backends/mem0_backend.py +155 -0
  145. veadk/memory/long_term_memory_backends/opensearch_backend.py +124 -0
  146. veadk/memory/long_term_memory_backends/redis_backend.py +140 -0
  147. veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +189 -0
  148. veadk/memory/short_term_memory.py +252 -0
  149. veadk/memory/short_term_memory_backends/__init__.py +13 -0
  150. veadk/memory/short_term_memory_backends/base_backend.py +31 -0
  151. veadk/memory/short_term_memory_backends/mysql_backend.py +49 -0
  152. veadk/memory/short_term_memory_backends/postgresql_backend.py +49 -0
  153. veadk/memory/short_term_memory_backends/sqlite_backend.py +55 -0
  154. veadk/memory/short_term_memory_processor.py +100 -0
  155. veadk/processors/__init__.py +26 -0
  156. veadk/processors/base_run_processor.py +120 -0
  157. veadk/prompts/__init__.py +13 -0
  158. veadk/prompts/agent_default_prompt.py +30 -0
  159. veadk/prompts/prompt_evaluator.py +20 -0
  160. veadk/prompts/prompt_memory_processor.py +55 -0
  161. veadk/prompts/prompt_optimization.py +150 -0
  162. veadk/runner.py +732 -0
  163. veadk/tools/__init__.py +13 -0
  164. veadk/tools/builtin_tools/__init__.py +13 -0
  165. veadk/tools/builtin_tools/agent_authorization.py +94 -0
  166. veadk/tools/builtin_tools/generate_image.py +23 -0
  167. veadk/tools/builtin_tools/image_edit.py +300 -0
  168. veadk/tools/builtin_tools/image_generate.py +446 -0
  169. veadk/tools/builtin_tools/lark.py +67 -0
  170. veadk/tools/builtin_tools/las.py +24 -0
  171. veadk/tools/builtin_tools/link_reader.py +66 -0
  172. veadk/tools/builtin_tools/llm_shield.py +381 -0
  173. veadk/tools/builtin_tools/load_knowledgebase.py +97 -0
  174. veadk/tools/builtin_tools/mcp_router.py +29 -0
  175. veadk/tools/builtin_tools/run_code.py +113 -0
  176. veadk/tools/builtin_tools/tts.py +253 -0
  177. veadk/tools/builtin_tools/vesearch.py +49 -0
  178. veadk/tools/builtin_tools/video_generate.py +363 -0
  179. veadk/tools/builtin_tools/web_scraper.py +76 -0
  180. veadk/tools/builtin_tools/web_search.py +83 -0
  181. veadk/tools/demo_tools.py +58 -0
  182. veadk/tools/load_knowledgebase_tool.py +149 -0
  183. veadk/tools/sandbox/__init__.py +13 -0
  184. veadk/tools/sandbox/browser_sandbox.py +37 -0
  185. veadk/tools/sandbox/code_sandbox.py +40 -0
  186. veadk/tools/sandbox/computer_sandbox.py +34 -0
  187. veadk/tracing/__init__.py +13 -0
  188. veadk/tracing/base_tracer.py +58 -0
  189. veadk/tracing/telemetry/__init__.py +13 -0
  190. veadk/tracing/telemetry/attributes/attributes.py +29 -0
  191. veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +180 -0
  192. veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +858 -0
  193. veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +152 -0
  194. veadk/tracing/telemetry/attributes/extractors/types.py +164 -0
  195. veadk/tracing/telemetry/exporters/__init__.py +13 -0
  196. veadk/tracing/telemetry/exporters/apmplus_exporter.py +558 -0
  197. veadk/tracing/telemetry/exporters/base_exporter.py +39 -0
  198. veadk/tracing/telemetry/exporters/cozeloop_exporter.py +129 -0
  199. veadk/tracing/telemetry/exporters/inmemory_exporter.py +248 -0
  200. veadk/tracing/telemetry/exporters/tls_exporter.py +139 -0
  201. veadk/tracing/telemetry/opentelemetry_tracer.py +320 -0
  202. veadk/tracing/telemetry/telemetry.py +411 -0
  203. veadk/types.py +47 -0
  204. veadk/utils/__init__.py +13 -0
  205. veadk/utils/audio_manager.py +95 -0
  206. veadk/utils/auth.py +294 -0
  207. veadk/utils/logger.py +59 -0
  208. veadk/utils/mcp_utils.py +44 -0
  209. veadk/utils/misc.py +184 -0
  210. veadk/utils/patches.py +101 -0
  211. veadk/utils/volcengine_sign.py +205 -0
  212. veadk/version.py +15 -0
  213. veadk_python-0.2.27.dist-info/METADATA +373 -0
  214. veadk_python-0.2.27.dist-info/RECORD +218 -0
  215. veadk_python-0.2.27.dist-info/WHEEL +5 -0
  216. veadk_python-0.2.27.dist-info/entry_points.txt +2 -0
  217. veadk_python-0.2.27.dist-info/licenses/LICENSE +201 -0
  218. veadk_python-0.2.27.dist-info/top_level.txt +1 -0
@@ -0,0 +1,181 @@
1
+ # Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Any
18
+ from typing_extensions import override
19
+
20
+ from mcp.types import Tool as McpBaseTool
21
+ from google.genai.types import FunctionDeclaration
22
+ from google.adk.auth.auth_credential import AuthCredential
23
+ from google.adk.tools.base_tool import BaseTool
24
+ from google.adk.tools.tool_context import ToolContext
25
+ from google.adk.tools._gemini_schema_util import _to_gemini_schema
26
+ from google.adk.tools.mcp_tool.mcp_session_manager import MCPSessionManager
27
+ from google.adk.tools.mcp_tool.mcp_session_manager import retry_on_closed_resource
28
+
29
+ from veadk.integrations.ve_identity.auth_config import VeIdentityAuthConfig
30
+ from veadk.integrations.ve_identity.auth_mixins import (
31
+ VeIdentityAuthMixin,
32
+ AuthRequiredException,
33
+ )
34
+ from veadk.integrations.ve_identity.utils import generate_headers
35
+ from veadk.utils.logger import get_logger
36
+
37
+ logger = get_logger(__name__)
38
+
39
+
40
+ class VeIdentityMcpTool(VeIdentityAuthMixin, BaseTool):
41
+ """Unified MCP tool with automatic VeIdentity authentication.
42
+
43
+ This tool wraps an MCP Tool interface and automatically handles authentication
44
+ based on the provided auth configuration. It supports both API Key and OAuth2 authentication.
45
+
46
+ Examples:
47
+ # API Key authentication
48
+ api_key_tool = VeIdentityMcpTool(
49
+ mcp_tool=mcp_tool,
50
+ mcp_session_manager=session_manager,
51
+ auth_config=api_key_auth("my-provider")
52
+ )
53
+
54
+ # OAuth2 authentication
55
+ oauth2_tool = VeIdentityMcpTool(
56
+ mcp_tool=mcp_tool,
57
+ mcp_session_manager=session_manager,
58
+ auth_config=oauth2_auth(
59
+ provider_name="my-provider",
60
+ scopes=["read", "write"],
61
+ auth_flow="M2M"
62
+ )
63
+ )
64
+
65
+ Note: For API key authentication, only header-based API keys are supported.
66
+ Query and cookie-based API keys will result in authentication errors.
67
+ """
68
+
69
+ def __init__(
70
+ self,
71
+ *,
72
+ mcp_tool: McpBaseTool,
73
+ mcp_session_manager: MCPSessionManager,
74
+ auth_config: VeIdentityAuthConfig,
75
+ ):
76
+ """Initialize the unified Identity MCP tool.
77
+
78
+ Args:
79
+ mcp_tool: The MCP tool to wrap.
80
+ mcp_session_manager: The MCP session manager to use for communication.
81
+ auth_config: Authentication configuration (ApiKeyAuthConfig, WorkloadAuthConfig, or OAuth2AuthConfig).
82
+
83
+ Raises:
84
+ ValueError: If mcp_tool or mcp_session_manager is None.
85
+ """
86
+ if mcp_tool is None:
87
+ raise ValueError("mcp_tool cannot be None")
88
+ if mcp_session_manager is None:
89
+ raise ValueError("mcp_session_manager cannot be None")
90
+
91
+ # Initialize mixins first
92
+ super().__init__(
93
+ name=mcp_tool.name,
94
+ description=mcp_tool.description if mcp_tool.description else "",
95
+ auth_config=auth_config,
96
+ )
97
+ self._mcp_tool = mcp_tool
98
+ self._mcp_session_manager = mcp_session_manager
99
+
100
+ @override
101
+ def _get_declaration(self) -> FunctionDeclaration:
102
+ """Gets the function declaration for the tool.
103
+
104
+ Returns:
105
+ FunctionDeclaration: The Gemini function declaration for the tool.
106
+ """
107
+ schema_dict = self._mcp_tool.inputSchema
108
+ schema = _to_gemini_schema(schema_dict)
109
+
110
+ return FunctionDeclaration(
111
+ name=self.name,
112
+ description=self.description,
113
+ parameters=schema,
114
+ )
115
+
116
+ @override
117
+ async def run_async(
118
+ self, *, args: dict[str, Any], tool_context: ToolContext
119
+ ) -> Any:
120
+ """Execute the wrapped MCP tool with Identity authentication.
121
+
122
+ This method handles authentication based on the configured auth type.
123
+
124
+ Args:
125
+ args: Arguments to pass to the wrapped tool.
126
+ tool_context: The tool context for accessing session state and auth.
127
+
128
+ Returns:
129
+ The result from the wrapped tool, or an auth pending message for OAuth2.
130
+ """
131
+ try:
132
+ return await self.run_with_identity_auth(
133
+ args=args, tool_context=tool_context
134
+ )
135
+ except AuthRequiredException as e:
136
+ # Only OAuth2 can raise this exception
137
+ return e.message
138
+
139
+ async def _execute_with_credential(
140
+ self,
141
+ *,
142
+ args: dict[str, Any],
143
+ tool_context: ToolContext,
144
+ credential: AuthCredential,
145
+ ) -> Any:
146
+ """Execute the MCP tool with the provided credential.
147
+
148
+ Args:
149
+ args: Arguments to pass to the tool.
150
+ tool_context: The tool context.
151
+ credential: The authentication credential (API key or OAuth2).
152
+
153
+ Returns:
154
+ The result from the tool execution.
155
+ """
156
+ return await self._run_async_impl(
157
+ args=args, tool_context=tool_context, credential=credential
158
+ )
159
+
160
+ @retry_on_closed_resource
161
+ async def _run_async_impl(
162
+ self, *, args, tool_context: ToolContext, credential: AuthCredential
163
+ ):
164
+ """Runs the tool asynchronously.
165
+
166
+ Args:
167
+ args: The arguments as a dict to pass to the tool.
168
+ tool_context: The tool context of the current invocation.
169
+ credential: The authentication credential.
170
+
171
+ Returns:
172
+ Any: The response from the tool.
173
+ """
174
+ # Extract headers from credential for session pooling
175
+ headers = generate_headers(credential)
176
+
177
+ # Get the session from the session manager
178
+ session = await self._mcp_session_manager.create_session(headers=headers)
179
+
180
+ response = await session.call_tool(self._mcp_tool.name, arguments=args)
181
+ return response
@@ -0,0 +1,431 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ import sys
19
+ from typing import List, Dict, Any
20
+ from typing import Optional
21
+ from typing import TextIO
22
+ from typing import Union
23
+
24
+
25
+ from pydantic import model_validator, field_validator
26
+ from typing_extensions import override
27
+
28
+ from mcp import StdioServerParameters, ClientSession
29
+ from mcp.types import ListToolsResult
30
+
31
+ from google.adk.tools.mcp_tool.mcp_session_manager import (
32
+ retry_on_closed_resource,
33
+ SseConnectionParams,
34
+ StdioConnectionParams,
35
+ StreamableHTTPConnectionParams,
36
+ MCPSessionManager,
37
+ )
38
+ from google.adk.tools.base_tool import BaseTool
39
+ from google.adk.tools.base_toolset import BaseToolset, ToolPredicate
40
+ from google.adk.tools.tool_configs import ToolArgsConfig, BaseToolConfig
41
+ from google.adk.agents.readonly_context import ReadonlyContext
42
+
43
+ from veadk.integrations.ve_identity.auth_config import VeIdentityAuthConfig
44
+ from veadk.integrations.ve_identity.auth_mixins import VeIdentityAuthMixin
45
+ from veadk.integrations.ve_identity.mcp_tool import VeIdentityMcpTool
46
+ from veadk.integrations.ve_identity.utils import generate_headers
47
+
48
+ logger = logging.getLogger(__name__)
49
+
50
+
51
+ class VeIdentityMcpToolset(VeIdentityAuthMixin, BaseToolset):
52
+ """Connects to a MCP Server, and retrieves MCP Tools into ADK Tools.
53
+
54
+ Unified MCP toolset with automatic Identity authentication.
55
+
56
+ This toolset manages the connection to an MCP server and provides tools
57
+ that can be used by an agent. It properly implements the BaseToolset
58
+ interface for easy integration with the agent framework.
59
+
60
+ Examples:
61
+ With API Key authentication:
62
+
63
+ from veadk.integrations.ve_identity import VeIdentityMcpToolset, api_key_auth
64
+ from mcp import StdioServerParameters
65
+
66
+ toolset = VeIdentityMcpToolset(
67
+ auth_config=api_key_auth("my-provider"),
68
+ connection_params=StdioServerParameters(
69
+ command='npx',
70
+ args=["-y", "@modelcontextprotocol/server-filesystem"],
71
+ ),
72
+ tool_filter=['read_file', 'list_directory']
73
+ )
74
+
75
+ With OAuth2 authentication:
76
+
77
+ from veadk.integrations.ve_identity import VeIdentityMcpToolset, oauth2_auth
78
+ from mcp import StdioServerParameters
79
+
80
+ toolset = VeIdentityMcpToolset(
81
+ auth_config=oauth2_auth(
82
+ provider_name="github",
83
+ scopes=["repo", "user"],
84
+ auth_flow="M2M"
85
+ ),
86
+ connection_params=StdioServerParameters(
87
+ command='npx',
88
+ args=["-y", "@modelcontextprotocol/server-filesystem"],
89
+ ),
90
+ tool_filter=['read_file', 'list_directory']
91
+ )
92
+
93
+ Using in an agent:
94
+
95
+ agent = LlmAgent(
96
+ model='gemini-2.0-flash',
97
+ name='enterprise_assistant',
98
+ instruction='Help user accessing their file systems',
99
+ tools=[toolset],
100
+ )
101
+
102
+ # Cleanup is handled automatically by the agent framework
103
+ # But you can also manually close if needed:
104
+ # await toolset.close()
105
+ """
106
+
107
+ def __init__(
108
+ self,
109
+ *,
110
+ auth_config: VeIdentityAuthConfig,
111
+ connection_params: Union[
112
+ StdioServerParameters,
113
+ StdioConnectionParams,
114
+ SseConnectionParams,
115
+ StreamableHTTPConnectionParams,
116
+ ],
117
+ tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
118
+ tool_name_prefix: Optional[str] = None,
119
+ errlog: TextIO = sys.stderr,
120
+ ):
121
+ """Initializes the MCPToolset.
122
+
123
+ Args:
124
+ auth_config: Authentication configuration (ApiKeyAuthConfig, WorkloadAuthConfig, or OAuth2AuthConfig).
125
+ connection_params: The connection parameters to the MCP server. Can be:
126
+ ``StdioConnectionParams`` for using local mcp server (e.g. using ``npx`` or
127
+ ``python3``); or ``SseConnectionParams`` for a local/remote SSE server; or
128
+ ``StreamableHTTPConnectionParams`` for local/remote Streamable http
129
+ server. Note, ``StdioServerParameters`` is also supported for using local
130
+ mcp server (e.g. using ``npx`` or ``python3`` ), but it does not support
131
+ timeout, and we recommend to use ``StdioConnectionParams`` instead when
132
+ timeout is needed.
133
+ tool_filter: Optional filter to select specific tools. Can be either: - A
134
+ list of tool names to include - A ToolPredicate function for custom
135
+ filtering logic
136
+ tool_name_prefix: A prefix to be added to the name of each tool in this
137
+ toolset.
138
+ errlog: TextIO stream for error logging.
139
+ """
140
+ if not connection_params:
141
+ raise ValueError("Missing connection params in VeIdentityMcpToolset.")
142
+
143
+ # Initialize mixins first
144
+ super().__init__(
145
+ auth_config=auth_config,
146
+ )
147
+
148
+ # Store Identity specific configuration
149
+ self._auth_config = auth_config
150
+ self._connection_params = connection_params
151
+ self._tool_filter = tool_filter
152
+ self._tool_name_prefix = tool_name_prefix
153
+ self._errlog = errlog
154
+
155
+ # Create MCP session manager
156
+ self._mcp_session_manager = MCPSessionManager(
157
+ connection_params=connection_params,
158
+ errlog=errlog,
159
+ )
160
+
161
+ @retry_on_closed_resource
162
+ @override
163
+ async def get_tools(
164
+ self,
165
+ readonly_context: Optional[ReadonlyContext] = None,
166
+ ) -> List[BaseTool]:
167
+ """Return all tools in the toolset based on the provided context.
168
+
169
+ Args:
170
+ readonly_context: Context used to filter tools available to the agent.
171
+ If None, all tools in the toolset are returned.
172
+
173
+ Returns:
174
+ List[BaseTool]: A list of tools available under the specified context.
175
+ """
176
+ if readonly_context is None:
177
+ raise ValueError("Readonly context is required for VeIdentityMcpToolset.")
178
+
179
+ # Get credential for authentication
180
+ credential = await self._get_credential(tool_context=readonly_context)
181
+
182
+ headers = generate_headers(credential)
183
+ # Get session from session manager
184
+ session: ClientSession = await self._mcp_session_manager.create_session(
185
+ headers=headers
186
+ )
187
+
188
+ # Fetch available tools from the MCP server
189
+ tools_response: ListToolsResult = await session.list_tools()
190
+
191
+ # Apply filtering based on context and tool_filter
192
+ tools = []
193
+ for tool in tools_response.tools:
194
+ mcp_tool = VeIdentityMcpTool(
195
+ mcp_tool=tool,
196
+ mcp_session_manager=self._mcp_session_manager,
197
+ auth_config=self._auth_config,
198
+ )
199
+
200
+ # Apply tool name prefix if specified
201
+ if self._tool_name_prefix:
202
+ mcp_tool._name = f"{self._tool_name_prefix}{mcp_tool.name}"
203
+
204
+ if self._is_tool_selected(mcp_tool, readonly_context):
205
+ tools.append(mcp_tool)
206
+ return tools
207
+
208
+ def _is_tool_selected(
209
+ self, tool: BaseTool, readonly_context: Optional[ReadonlyContext]
210
+ ) -> bool:
211
+ """Check if a tool should be included based on filters and context."""
212
+ # Apply tool filter if specified
213
+ if self._tool_filter is not None:
214
+ if callable(self._tool_filter):
215
+ # ToolPredicate function
216
+ if not self._tool_filter(tool, readonly_context):
217
+ return False
218
+ elif isinstance(self._tool_filter, list):
219
+ # List of tool names
220
+ if tool.name not in self._tool_filter:
221
+ return False
222
+
223
+ return True
224
+
225
+ async def close(self) -> None:
226
+ """Performs cleanup and releases resources held by the toolset.
227
+
228
+ This method closes the MCP session and cleans up all associated resources.
229
+ It's designed to be safe to call multiple times and handles cleanup errors
230
+ gracefully to avoid blocking application shutdown.
231
+ """
232
+ try:
233
+ await self._mcp_session_manager.close()
234
+ except Exception as e:
235
+ # Log the error but don't re-raise to avoid blocking shutdown
236
+ print(f"Warning: Error during MCPToolset cleanup: {e}", file=self._errlog)
237
+
238
+ @override
239
+ @classmethod
240
+ def from_config(
241
+ cls, config: ToolArgsConfig, config_abs_path: str
242
+ ) -> "VeIdentityMcpToolset":
243
+ """Create VeIdentityMcpToolset from configuration.
244
+
245
+ Priority order:
246
+ 1. If config_abs_path points to an existing file, load configuration from that file
247
+ 2. Otherwise, use the provided config object directly
248
+
249
+ Args:
250
+ config: Configuration object (used as fallback).
251
+ config_abs_path: Absolute path to the config file. If this file exists,
252
+ it will be loaded as the primary configuration source.
253
+
254
+ Returns:
255
+ VeIdentityMcpToolset instance.
256
+ """
257
+ import os
258
+
259
+ # Priority 1: Try to load from config_abs_path if it exists
260
+ if config_abs_path and os.path.exists(config_abs_path):
261
+ try:
262
+ file_config_dict = cls._load_config_from_file(config_abs_path)
263
+ # Let Pydantic handle the validation and conversion
264
+ ve_identity_config = VeIdentityMcpToolsetConfig.model_validate(
265
+ file_config_dict
266
+ )
267
+
268
+ except Exception as e:
269
+ # If file loading fails, fall back to config parameter
270
+ print(f"Warning: Failed to load config from {config_abs_path}: {e}")
271
+ print("Falling back to provided config parameter")
272
+ ve_identity_config = VeIdentityMcpToolsetConfig.model_validate(
273
+ config.model_dump()
274
+ )
275
+ else:
276
+ # Priority 2: Use provided config object
277
+ ve_identity_config = VeIdentityMcpToolsetConfig.model_validate(
278
+ config.model_dump()
279
+ )
280
+
281
+ # Determine which connection params to use
282
+ if ve_identity_config.stdio_server_params:
283
+ connection_params = ve_identity_config.stdio_server_params
284
+ elif ve_identity_config.stdio_connection_params:
285
+ connection_params = ve_identity_config.stdio_connection_params
286
+ elif ve_identity_config.sse_connection_params:
287
+ connection_params = ve_identity_config.sse_connection_params
288
+ elif ve_identity_config.streamable_http_connection_params:
289
+ connection_params = ve_identity_config.streamable_http_connection_params
290
+ else:
291
+ raise ValueError(
292
+ "No connection params found in VeIdentityMcpToolsetConfig."
293
+ )
294
+
295
+ return cls(
296
+ auth_config=ve_identity_config.auth_config,
297
+ connection_params=connection_params,
298
+ tool_filter=ve_identity_config.tool_filter,
299
+ tool_name_prefix=ve_identity_config.tool_name_prefix,
300
+ )
301
+
302
+ @classmethod
303
+ def _load_config_from_file(cls, file_path: str) -> dict:
304
+ """Load configuration from JSON or YAML file.
305
+
306
+ Args:
307
+ file_path: Path to the configuration file.
308
+
309
+ Returns:
310
+ Configuration dictionary.
311
+
312
+ Raises:
313
+ FileNotFoundError: If the config file doesn't exist.
314
+ ValueError: If the file format is not supported or invalid.
315
+ """
316
+ import os
317
+ import json
318
+ import yaml
319
+ from pathlib import Path
320
+
321
+ if not os.path.exists(file_path):
322
+ raise FileNotFoundError(f"Configuration file not found: {file_path}")
323
+
324
+ file_ext = Path(file_path).suffix.lower()
325
+
326
+ try:
327
+ with open(file_path, "r", encoding="utf-8") as f:
328
+ if file_ext in [".json"]:
329
+ return json.load(f)
330
+ elif file_ext in [".yaml", ".yml"]:
331
+ return yaml.safe_load(f)
332
+ else:
333
+ # Try to detect format by content
334
+ content = f.read()
335
+ f.seek(0)
336
+
337
+ # Try JSON first
338
+ try:
339
+ return json.loads(content)
340
+ except json.JSONDecodeError:
341
+ # Try YAML
342
+ try:
343
+ return yaml.safe_load(content)
344
+ except yaml.YAMLError:
345
+ raise ValueError(
346
+ f"Unsupported configuration file format: {file_ext}"
347
+ )
348
+
349
+ except Exception as e:
350
+ if isinstance(e, (FileNotFoundError, ValueError)):
351
+ raise
352
+ raise ValueError(f"Failed to load configuration from {file_path}: {e}")
353
+
354
+
355
+ class VeIdentityMcpToolsetConfig(BaseToolConfig):
356
+ """Configuration for VeIdentityMcpToolset."""
357
+
358
+ model_config = {"arbitrary_types_allowed": True}
359
+
360
+ # Authentication configuration
361
+ auth_config: Union[VeIdentityAuthConfig, Dict[str, Any]]
362
+
363
+ # Connection parameters (exactly one must be set)
364
+ stdio_server_params: Optional[StdioServerParameters] = None
365
+ stdio_connection_params: Optional[StdioConnectionParams] = None
366
+ sse_connection_params: Optional[SseConnectionParams] = None
367
+ streamable_http_connection_params: Optional[StreamableHTTPConnectionParams] = None
368
+
369
+ # Optional toolset configuration
370
+ tool_filter: Optional[List[str]] = None
371
+ tool_name_prefix: Optional[str] = None
372
+
373
+ @field_validator("auth_config", mode="before")
374
+ @classmethod
375
+ def _validate_auth_config(cls, v):
376
+ """Convert dict to proper auth config object."""
377
+ if isinstance(v, dict):
378
+ from veadk.integrations.ve_identity.auth_config import (
379
+ api_key_auth,
380
+ oauth2_auth,
381
+ )
382
+
383
+ provider_name = v.get("provider_name")
384
+ if not provider_name:
385
+ raise ValueError("provider_name is required in auth_config")
386
+
387
+ region = v.get("region", "cn-beijing")
388
+
389
+ # Determine auth type based on presence of OAuth2-specific fields
390
+ has_scopes = "scopes" in v
391
+ has_auth_flow = "auth_flow" in v
392
+
393
+ if has_scopes or has_auth_flow:
394
+ # OAuth2 configuration
395
+ scopes = v.get("scopes")
396
+ auth_flow = v.get("auth_flow")
397
+ callback_url = v.get("callback_url")
398
+ force_authentication = v.get("force_authentication", False)
399
+ response_for_auth_required = v.get("response_for_auth_required")
400
+
401
+ return oauth2_auth(
402
+ provider_name=provider_name,
403
+ scopes=scopes,
404
+ auth_flow=auth_flow,
405
+ callback_url=callback_url,
406
+ force_authentication=force_authentication,
407
+ response_for_auth_required=response_for_auth_required,
408
+ region=region,
409
+ )
410
+ else:
411
+ # API Key configuration
412
+ return api_key_auth(provider_name=provider_name, region=region)
413
+ return v
414
+
415
+ @model_validator(mode="after")
416
+ def _check_only_one_connection_param(self):
417
+ """Ensure exactly one connection parameter is set."""
418
+ connection_fields = [
419
+ self.stdio_server_params,
420
+ self.stdio_connection_params,
421
+ self.sse_connection_params,
422
+ self.streamable_http_connection_params,
423
+ ]
424
+ populated_fields = [f for f in connection_fields if f is not None]
425
+
426
+ if len(populated_fields) != 1:
427
+ raise ValueError(
428
+ "Exactly one of stdio_server_params, stdio_connection_params, "
429
+ "sse_connection_params, streamable_http_connection_params must be set."
430
+ )
431
+ return self