solace-agent-mesh 1.0.5__py3-none-any.whl → 1.0.7__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.

Potentially problematic release.


This version of solace-agent-mesh might be problematic. Click here for more details.

Files changed (142) hide show
  1. solace_agent_mesh/agent/adk/artifacts/__init__.py +1 -0
  2. solace_agent_mesh/agent/adk/{filesystem_artifact_service.py → artifacts/filesystem_artifact_service.py} +14 -15
  3. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +440 -0
  4. solace_agent_mesh/agent/adk/callbacks.py +123 -159
  5. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +316 -0
  6. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +414 -0
  7. solace_agent_mesh/agent/adk/mcp_content_processor.py +665 -0
  8. solace_agent_mesh/agent/adk/services.py +35 -1
  9. solace_agent_mesh/agent/adk/setup.py +85 -45
  10. solace_agent_mesh/agent/adk/tool_wrapper.py +19 -3
  11. solace_agent_mesh/agent/protocol/event_handlers.py +1 -1
  12. solace_agent_mesh/agent/sac/app.py +67 -0
  13. solace_agent_mesh/agent/sac/component.py +14 -86
  14. solace_agent_mesh/assets/docs/404.html +3 -3
  15. solace_agent_mesh/assets/docs/assets/js/04989206.b9dfe831.js +1 -0
  16. solace_agent_mesh/assets/docs/assets/js/0e682baa.b3bbde9a.js +1 -0
  17. solace_agent_mesh/assets/docs/assets/js/1023fc19.364235d5.js +1 -0
  18. solace_agent_mesh/assets/docs/assets/js/1523c6b4.1b0ec6f9.js +1 -0
  19. solace_agent_mesh/assets/docs/assets/js/166ab619.e8f3a7c7.js +1 -0
  20. solace_agent_mesh/assets/docs/assets/js/21ceee5f.3bf39250.js +1 -0
  21. solace_agent_mesh/assets/docs/assets/js/3d406171.7d02a73b.js +1 -0
  22. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.8ccb9901.js +1 -0
  23. solace_agent_mesh/assets/docs/assets/js/442a8107.b3159bb2.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/4c2787c2.fc6804f2.js +1 -0
  25. solace_agent_mesh/assets/docs/assets/js/5b4258a4.0d080cd9.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/75384d09.ccd480c4.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/768e31b0.8b51cd70.js +1 -0
  28. solace_agent_mesh/assets/docs/assets/js/945fb41e.c63791d1.js +1 -0
  29. solace_agent_mesh/assets/docs/assets/js/{9eff14a2.036c35ea.js → 9eff14a2.472b0310.js} +1 -1
  30. solace_agent_mesh/assets/docs/assets/js/a3a92b25.4b7fa6a2.js +1 -0
  31. solace_agent_mesh/assets/docs/assets/js/aba87c2f.76376d7c.js +1 -0
  32. solace_agent_mesh/assets/docs/assets/js/ae4415af.7a2f0bbf.js +1 -0
  33. solace_agent_mesh/assets/docs/assets/js/b7006a3a.73a79653.js +1 -0
  34. solace_agent_mesh/assets/docs/assets/js/beecea0d.ae31f6a7.js +1 -0
  35. solace_agent_mesh/assets/docs/assets/js/c2c06897.587b4af5.js +1 -0
  36. solace_agent_mesh/assets/docs/assets/js/{cd3d4052.ca6eed8c.js → cd3d4052.b6535013.js} +1 -1
  37. solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +1 -0
  38. solace_agent_mesh/assets/docs/assets/js/f897a61a.0aa29dbb.js +1 -0
  39. solace_agent_mesh/assets/docs/assets/js/main.d79f063b.js +2 -0
  40. solace_agent_mesh/assets/docs/assets/js/runtime~main.6415ad00.js +1 -0
  41. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +28 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +6 -6
  43. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +8 -8
  44. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +5 -5
  45. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +5 -5
  46. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +34 -5
  47. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +5 -5
  49. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +6 -6
  51. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +72 -0
  52. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +5 -5
  53. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +7 -7
  54. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +35 -16
  55. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
  56. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +17 -11
  57. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
  58. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +5 -5
  59. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
  60. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +6 -6
  61. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +6 -6
  62. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +6 -6
  63. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
  64. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +8 -8
  65. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +14 -14
  66. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +8 -8
  67. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
  68. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +6 -6
  69. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +35 -23
  70. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
  71. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +6 -6
  72. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
  73. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  74. solace_agent_mesh/assets/docs/lunr-index-1756146501924.json +1 -0
  75. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  76. solace_agent_mesh/assets/docs/search-doc-1756146501924.json +1 -0
  77. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  78. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  79. solace_agent_mesh/cli/__init__.py +1 -1
  80. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +1 -1
  81. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +67 -10
  82. solace_agent_mesh/cli/commands/add_cmd/gateway_cmd.py +2 -2
  83. solace_agent_mesh/cli/commands/eval_cmd.py +8 -2
  84. solace_agent_mesh/cli/commands/init_cmd/__init__.py +20 -2
  85. solace_agent_mesh/cli/commands/init_cmd/env_step.py +25 -1
  86. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +45 -1
  87. solace_agent_mesh/cli/utils.py +21 -12
  88. solace_agent_mesh/client/webui/frontend/static/assets/main-BucUdn9m.js +673 -0
  89. solace_agent_mesh/client/webui/frontend/static/index.html +1 -1
  90. solace_agent_mesh/common/a2a_protocol.py +1 -1
  91. solace_agent_mesh/common/utils/initializer.py +4 -6
  92. solace_agent_mesh/common/utils/mime_helpers.py +60 -1
  93. solace_agent_mesh/config_portal/backend/server.py +1 -1
  94. solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-xSu2leR8.js → _index-MqsrTd6g.js} +9 -9
  95. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-950eb3be.js → manifest-28271392.js} +1 -1
  96. solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
  97. solace_agent_mesh/core_a2a/core_a2a_llm.txt +1 -1
  98. solace_agent_mesh/core_a2a/service.py +1 -1
  99. solace_agent_mesh/evaluation/run.py +149 -15
  100. solace_agent_mesh/evaluation/summary_builder.py +5 -3
  101. solace_agent_mesh/gateway/http_sse/dependencies.py +1 -1
  102. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +1 -1
  103. solace_agent_mesh/gateway/http_sse/services/task_service.py +1 -1
  104. solace_agent_mesh/llm_detail.txt +2 -2
  105. solace_agent_mesh/templates/agent_template.yaml +1 -1
  106. solace_agent_mesh/templates/plugin_agent_config_template.yaml +3 -3
  107. solace_agent_mesh/templates/plugin_readme_template.md +1 -1
  108. solace_agent_mesh/templates/shared_config.yaml +8 -1
  109. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/METADATA +5 -2
  110. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/RECORD +114 -109
  111. solace_agent_mesh/assets/docs/assets/js/04989206.da8246cd.js +0 -1
  112. solace_agent_mesh/assets/docs/assets/js/0e682baa.79f0ab22.js +0 -1
  113. solace_agent_mesh/assets/docs/assets/js/1023fc19.8e6d174c.js +0 -1
  114. solace_agent_mesh/assets/docs/assets/js/1523c6b4.91c7bc01.js +0 -1
  115. solace_agent_mesh/assets/docs/assets/js/166ab619.7d97ccaf.js +0 -1
  116. solace_agent_mesh/assets/docs/assets/js/21ceee5f.614fa8dd.js +0 -1
  117. solace_agent_mesh/assets/docs/assets/js/3d406171.9b081d5f.js +0 -1
  118. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.36090198.js +0 -1
  119. solace_agent_mesh/assets/docs/assets/js/442a8107.5ba94b65.js +0 -1
  120. solace_agent_mesh/assets/docs/assets/js/4c2787c2.66ee00e9.js +0 -1
  121. solace_agent_mesh/assets/docs/assets/js/5b4258a4.bda20761.js +0 -1
  122. solace_agent_mesh/assets/docs/assets/js/75384d09.c3991823.js +0 -1
  123. solace_agent_mesh/assets/docs/assets/js/768e31b0.a12673db.js +0 -1
  124. solace_agent_mesh/assets/docs/assets/js/945fb41e.74d728aa.js +0 -1
  125. solace_agent_mesh/assets/docs/assets/js/a3a92b25.26ca071f.js +0 -1
  126. solace_agent_mesh/assets/docs/assets/js/aba87c2f.a6b84da6.js +0 -1
  127. solace_agent_mesh/assets/docs/assets/js/ae4415af.96189a93.js +0 -1
  128. solace_agent_mesh/assets/docs/assets/js/b7006a3a.38c0cf3d.js +0 -1
  129. solace_agent_mesh/assets/docs/assets/js/bb2ef573.56931473.js +0 -1
  130. solace_agent_mesh/assets/docs/assets/js/c2c06897.63b76e9e.js +0 -1
  131. solace_agent_mesh/assets/docs/assets/js/f284c35a.5aff74ab.js +0 -1
  132. solace_agent_mesh/assets/docs/assets/js/f897a61a.862b0514.js +0 -1
  133. solace_agent_mesh/assets/docs/assets/js/main.946fa17b.js +0 -2
  134. solace_agent_mesh/assets/docs/assets/js/runtime~main.aa687c82.js +0 -1
  135. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +0 -17
  136. solace_agent_mesh/assets/docs/lunr-index-1755275703209.json +0 -1
  137. solace_agent_mesh/assets/docs/search-doc-1755275703209.json +0 -1
  138. solace_agent_mesh/client/webui/frontend/static/assets/main-DzKPMTRs.js +0 -673
  139. /solace_agent_mesh/assets/docs/assets/js/{main.946fa17b.js.LICENSE.txt → main.d79f063b.js.LICENSE.txt} +0 -0
  140. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/WHEEL +0 -0
  141. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/entry_points.txt +0 -0
  142. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/licenses/LICENSE +0 -0
@@ -27,7 +27,8 @@ from google.adk.memory import (
27
27
  VertexAiRagMemoryService,
28
28
  )
29
29
 
30
- from .filesystem_artifact_service import FilesystemArtifactService
30
+ from .artifacts.filesystem_artifact_service import FilesystemArtifactService
31
+ from .artifacts.s3_artifact_service import S3ArtifactService
31
32
 
32
33
  try:
33
34
  from sam_test_infrastructure.artifact_service.service import (
@@ -254,6 +255,39 @@ def initialize_artifact_service(component) -> BaseArtifactService:
254
255
  e,
255
256
  )
256
257
  raise
258
+ elif service_type == "s3":
259
+ bucket_name = config.get("bucket_name")
260
+ if not bucket_name or not bucket_name.strip():
261
+ raise ValueError(
262
+ f"{component.log_identifier} 'bucket_name' is required and cannot be empty for S3 artifact service."
263
+ )
264
+
265
+ try:
266
+ s3_config = {}
267
+
268
+ for key, value in config.items():
269
+ if key not in ["type", "bucket_name", "artifact_scope"]:
270
+ s3_config[key] = value
271
+
272
+ if "endpoint_url" not in s3_config:
273
+ s3_config["endpoint_url"] = "https://s3.amazonaws.com"
274
+
275
+ aws_access_key_id = config.get("aws_access_key_id") or os.environ.get("AWS_ACCESS_KEY_ID")
276
+ aws_secret_access_key = config.get("aws_secret_access_key") or os.environ.get("AWS_SECRET_ACCESS_KEY")
277
+
278
+ if aws_access_key_id:
279
+ s3_config["aws_access_key_id"] = aws_access_key_id
280
+ if aws_secret_access_key:
281
+ s3_config["aws_secret_access_key"] = aws_secret_access_key
282
+
283
+ concrete_service = S3ArtifactService(bucket_name=bucket_name, **s3_config)
284
+ except Exception as e:
285
+ log.error(
286
+ "%s Failed to initialize S3ArtifactService: %s",
287
+ component.log_identifier,
288
+ e,
289
+ )
290
+ raise
257
291
  elif service_type == "test_in_memory":
258
292
  if TestInMemoryArtifactService is None:
259
293
  log.error(
@@ -10,6 +10,7 @@ from solace_ai_connector.common.utils import import_module
10
10
 
11
11
  from .app_llm_agent import AppLlmAgent
12
12
  from .tool_wrapper import ADKToolWrapper
13
+ from .embed_resolving_mcp_toolset import EmbedResolvingMCPToolset
13
14
  from google.adk.runners import Runner
14
15
  from google.adk.models import BaseLlm
15
16
  from google.adk.tools import BaseTool, ToolContext
@@ -18,7 +19,10 @@ from google.adk.agents.callback_context import CallbackContext
18
19
  from google.adk.models.llm_request import LlmRequest
19
20
  from google.adk.models.llm_response import LlmResponse
20
21
  from google.adk.tools.mcp_tool import MCPToolset
21
- from google.adk.tools.mcp_tool.mcp_session_manager import SseServerParams, StdioConnectionParams
22
+ from google.adk.tools.mcp_tool.mcp_session_manager import (
23
+ SseServerParams,
24
+ StdioConnectionParams,
25
+ )
22
26
 
23
27
  from mcp import StdioServerParameters
24
28
 
@@ -55,11 +59,27 @@ async def load_adk_tools(
55
59
  loaded_tool_names: Set[str] = set()
56
60
  tools_config = component.get_config("tools", [])
57
61
 
62
+ def _check_and_register_tool_name(name: str, source: str):
63
+ """Checks for duplicate tool names and raises ValueError if found."""
64
+ if name in loaded_tool_names:
65
+ raise ValueError(
66
+ f"Configuration Error: Duplicate tool name '{name}' found from source '{source}'. "
67
+ "This name is already in use. Please resolve the conflict by renaming or "
68
+ "disabling one of the tools in your agent's configuration."
69
+ )
70
+ loaded_tool_names.add(name)
71
+
58
72
  if not tools_config:
59
73
  log.info(
60
74
  "%s No explicit tools configured in 'tools' list.", component.log_identifier
61
75
  )
62
76
  else:
77
+ log.debug(
78
+ "%s Processing %d tool configurations: %s",
79
+ component.log_identifier,
80
+ len(tools_config),
81
+ [tc.get("tool_type") for tc in tools_config],
82
+ )
63
83
  log.info(
64
84
  "%s Loading tools from 'tools' list configuration...",
65
85
  component.log_identifier,
@@ -91,6 +111,7 @@ async def load_adk_tools(
91
111
  func,
92
112
  specific_tool_config,
93
113
  function_name,
114
+ origin="python",
94
115
  raw_string_args=tool_config.get("raw_string_args", []),
95
116
  )
96
117
 
@@ -101,34 +122,23 @@ async def load_adk_tools(
101
122
  if tool_description:
102
123
  tool_callable.__doc__ = tool_description
103
124
 
104
- if function_name not in loaded_tool_names:
105
- loaded_tools.append(tool_callable)
106
- loaded_tool_names.add(function_name)
107
- log.info(
108
- "%s Loaded Python tool: %s from %s.",
109
- component.log_identifier,
110
- function_name,
111
- module_name,
112
- )
113
- else:
114
- log.debug(
115
- "%s Python tool '%s' already loaded. Skipping duplicate.",
116
- component.log_identifier,
117
- function_name,
118
- )
125
+ _check_and_register_tool_name(
126
+ function_name, f"python:{module_name}"
127
+ )
128
+ loaded_tools.append(tool_callable)
129
+ log.info(
130
+ "%s Loaded Python tool: %s from %s.",
131
+ component.log_identifier,
132
+ function_name,
133
+ module_name,
134
+ )
119
135
 
120
136
  elif tool_type == "builtin":
121
137
  tool_name = tool_config.get("tool_name")
122
138
  if not tool_name:
123
139
  raise ValueError("'tool_name' required for builtin tool.")
124
140
 
125
- if tool_name in loaded_tool_names:
126
- log.debug(
127
- "%s Tool '%s' already loaded. Skipping duplicate.",
128
- component.log_identifier,
129
- tool_name,
130
- )
131
- continue
141
+ _check_and_register_tool_name(tool_name, "builtin")
132
142
 
133
143
  sam_tool_def = tool_registry.get_tool_by_name(tool_name)
134
144
  if sam_tool_def:
@@ -137,11 +147,11 @@ async def load_adk_tools(
137
147
  sam_tool_def.implementation,
138
148
  specific_tool_config,
139
149
  sam_tool_def.name,
150
+ origin="builtin",
140
151
  raw_string_args=sam_tool_def.raw_string_args,
141
152
  )
142
153
  loaded_tools.append(tool_callable)
143
154
  enabled_builtin_tools.append(sam_tool_def)
144
- loaded_tool_names.add(sam_tool_def.name)
145
155
  log.info(
146
156
  "%s Loaded SAM built-in tool: %s",
147
157
  component.log_identifier,
@@ -151,8 +161,8 @@ async def load_adk_tools(
151
161
 
152
162
  adk_tool = getattr(adk_tools_module, tool_name, None)
153
163
  if adk_tool and isinstance(adk_tool, (BaseTool, Callable)):
164
+ adk_tool.origin = "adk_builtin"
154
165
  loaded_tools.append(adk_tool)
155
- loaded_tool_names.add(tool_name)
156
166
  log.info(
157
167
  "%s Loaded ADK built-in tool: %s",
158
168
  component.log_identifier,
@@ -211,20 +221,22 @@ async def load_adk_tools(
211
221
 
212
222
  group_tool_count = 0
213
223
  for tool_def in tools_in_group:
214
- if tool_def.name not in loaded_tool_names:
215
- specific_tool_config = tool_config.get(
216
- "tool_configs", {}
217
- ).get(tool_def.name)
218
- tool_callable = ADKToolWrapper(
219
- tool_def.implementation,
220
- specific_tool_config,
221
- tool_def.name,
222
- raw_string_args=tool_def.raw_string_args,
223
- )
224
- loaded_tools.append(tool_callable)
225
- enabled_builtin_tools.append(tool_def)
226
- loaded_tool_names.add(tool_def.name)
227
- group_tool_count += 1
224
+ _check_and_register_tool_name(
225
+ tool_def.name, f"builtin-group:{group_name}"
226
+ )
227
+ specific_tool_config = tool_config.get("tool_configs", {}).get(
228
+ tool_def.name
229
+ )
230
+ tool_callable = ADKToolWrapper(
231
+ tool_def.implementation,
232
+ specific_tool_config,
233
+ tool_def.name,
234
+ origin="builtin",
235
+ raw_string_args=tool_def.raw_string_args,
236
+ )
237
+ loaded_tools.append(tool_callable)
238
+ enabled_builtin_tools.append(tool_def)
239
+ group_tool_count += 1
228
240
  log.info(
229
241
  "Loaded %d tools from built-in group: %s",
230
242
  group_tool_count,
@@ -249,7 +261,6 @@ async def load_adk_tools(
249
261
  }
250
262
  connection_args["timeout"] = connection_args.get("timeout", 30)
251
263
 
252
-
253
264
  environment_variables = tool_config.get("environment_variables")
254
265
  env_param = {}
255
266
  if connection_type == "stdio" and environment_variables:
@@ -292,9 +303,9 @@ async def load_adk_tools(
292
303
  **final_connection_args,
293
304
  env=env_param if env_param else None,
294
305
  ),
295
- timeout=connection_args.get("timeout")
306
+ timeout=connection_args.get("timeout"),
296
307
  )
297
-
308
+
298
309
  elif connection_type == "sse":
299
310
  connection_params = SseServerParams(**connection_args)
300
311
  else:
@@ -310,10 +321,37 @@ async def load_adk_tools(
310
321
  tool_name,
311
322
  )
312
323
 
313
- mcp_toolset_instance = MCPToolset(
324
+ mcp_toolset_instance = EmbedResolvingMCPToolset(
314
325
  connection_params=connection_params,
315
326
  tool_filter=tool_filter_list,
327
+ tool_config=tool_config,
316
328
  )
329
+ mcp_toolset_instance.origin = "mcp"
330
+
331
+ # Check for duplicates from the MCP server
332
+ try:
333
+ mcp_tools = await mcp_toolset_instance.get_tools()
334
+ log.debug(
335
+ "%s Successfully discovered %d tools from MCP server: %s",
336
+ component.log_identifier,
337
+ len(mcp_tools),
338
+ [tool.name for tool in mcp_tools],
339
+ )
340
+ for mcp_tool in mcp_tools:
341
+ log.debug(
342
+ "%s Registering MCP tool: %s",
343
+ component.log_identifier,
344
+ mcp_tool.name,
345
+ )
346
+ _check_and_register_tool_name(mcp_tool.name, "mcp")
347
+ except Exception as e:
348
+ log.error(
349
+ "%s Failed to discover tools from MCP server: %s",
350
+ component.log_identifier,
351
+ str(e),
352
+ )
353
+ raise
354
+
317
355
  loaded_tools.append(mcp_toolset_instance)
318
356
  log.info(
319
357
  "%s Initialized MCPToolset (filter: %s) for server: %s",
@@ -344,7 +382,9 @@ async def load_adk_tools(
344
382
  internal_tool_names.append("_continue_generation")
345
383
 
346
384
  for tool_name in internal_tool_names:
347
- if tool_name in loaded_tool_names:
385
+ try:
386
+ _check_and_register_tool_name(tool_name, "internal")
387
+ except ValueError:
348
388
  log.debug(
349
389
  "%s Internal tool '%s' was already loaded explicitly. Skipping implicit load.",
350
390
  component.log_identifier,
@@ -359,13 +399,13 @@ async def load_adk_tools(
359
399
  tool_def.implementation,
360
400
  None, # No specific config for internal tools
361
401
  tool_def.name,
402
+ origin="internal",
362
403
  )
363
404
 
364
405
  tool_callable.__doc__ = tool_def.description
365
406
 
366
407
  loaded_tools.append(tool_callable)
367
408
  enabled_builtin_tools.append(tool_def)
368
- loaded_tool_names.add(tool_def.name)
369
409
  log.info(
370
410
  "%s Implicitly loaded internal framework tool: %s",
371
411
  component.log_identifier,
@@ -13,6 +13,7 @@ from ...common.utils.embeds import (
13
13
  resolve_embeds_in_string,
14
14
  evaluate_embed,
15
15
  EARLY_EMBED_TYPES,
16
+ LATE_EMBED_TYPES,
16
17
  EMBED_DELIMITER_OPEN,
17
18
  )
18
19
 
@@ -31,15 +32,30 @@ class ADKToolWrapper:
31
32
  original_func: Callable,
32
33
  tool_config: Optional[Dict],
33
34
  tool_name: str,
35
+ origin: str,
34
36
  raw_string_args: Optional[List[str]] = None,
35
37
  ):
36
38
  self._original_func = original_func
37
39
  self._tool_config = tool_config or {}
38
40
  self._tool_name = tool_name
41
+ self.origin = origin
39
42
  self._raw_string_args = set(raw_string_args) if raw_string_args else set()
40
43
  self._is_async = inspect.iscoroutinefunction(original_func)
41
44
 
42
- functools.update_wrapper(self, original_func)
45
+ # Ensure __name__ attribute is always set before functools.update_wrapper
46
+ self.__name__ = tool_name
47
+
48
+ try:
49
+ functools.update_wrapper(self, original_func)
50
+ except AttributeError as e:
51
+ log.debug(
52
+ "Could not fully update wrapper for tool '%s': %s. Using fallback attributes.",
53
+ self._tool_name,
54
+ e,
55
+ )
56
+ # Ensure essential attributes are set even if update_wrapper fails
57
+ self.__name__ = tool_name
58
+ self.__doc__ = getattr(original_func, "__doc__", None)
43
59
 
44
60
  try:
45
61
  self.__code__ = original_func.__code__
@@ -78,7 +94,7 @@ class ADKToolWrapper:
78
94
  text=arg,
79
95
  context=context_for_embeds,
80
96
  resolver_func=evaluate_embed,
81
- types_to_resolve=EARLY_EMBED_TYPES,
97
+ types_to_resolve=EARLY_EMBED_TYPES.union(LATE_EMBED_TYPES),
82
98
  log_identifier=log_identifier,
83
99
  config=self._tool_config,
84
100
  )
@@ -99,7 +115,7 @@ class ADKToolWrapper:
99
115
  text=value,
100
116
  context=context_for_embeds,
101
117
  resolver_func=evaluate_embed,
102
- types_to_resolve=EARLY_EMBED_TYPES,
118
+ types_to_resolve=EARLY_EMBED_TYPES.union(LATE_EMBED_TYPES),
103
119
  log_identifier=log_identifier,
104
120
  config=self._tool_config,
105
121
  )
@@ -253,7 +253,7 @@ async def handle_a2a_request(component, message: SolaceMessage):
253
253
  logical_task_id,
254
254
  )
255
255
 
256
- peer_sub_tasks = task_context.peer_sub_tasks
256
+ peer_sub_tasks = task_context.active_peer_sub_tasks
257
257
  if peer_sub_tasks:
258
258
  for sub_task_info in peer_sub_tasks:
259
259
  sub_task_id = sub_task_info.get("sub_task_id")
@@ -302,6 +302,73 @@ class SamAgentApp(App):
302
302
  },
303
303
  },
304
304
  },
305
+ # --- MCP Intelligent Processing Config ---
306
+ {
307
+ "name": "mcp_intelligent_processing",
308
+ "required": False,
309
+ "type": "object",
310
+ "default": {},
311
+ "description": "Configuration for intelligent processing of MCP tool responses into typed artifacts.",
312
+ "properties": {
313
+ "enable_intelligent_processing": {
314
+ "type": "boolean",
315
+ "required": False,
316
+ "default": True,
317
+ "description": "Enable intelligent content-aware processing of MCP responses. When disabled, falls back to raw JSON saving.",
318
+ },
319
+ "enable_text_format_detection": {
320
+ "type": "boolean",
321
+ "required": False,
322
+ "default": True,
323
+ "description": "Enable detection and parsing of structured text formats (CSV, JSON, YAML) within text content.",
324
+ },
325
+ "enable_content_parsing": {
326
+ "type": "boolean",
327
+ "required": False,
328
+ "default": True,
329
+ "description": "Enable parsing and validation of detected content formats for enhanced metadata.",
330
+ },
331
+ "fallback_to_raw_on_error": {
332
+ "type": "boolean",
333
+ "required": False,
334
+ "default": True,
335
+ "description": "Fall back to raw JSON saving if intelligent processing fails.",
336
+ },
337
+ "save_raw_alongside_intelligent": {
338
+ "type": "boolean",
339
+ "required": False,
340
+ "default": False,
341
+ "description": "Save both intelligent artifacts and raw JSON response for debugging/comparison.",
342
+ },
343
+ "max_content_items": {
344
+ "type": "integer",
345
+ "required": False,
346
+ "default": 50,
347
+ "description": "Maximum number of content items to process from a single MCP response.",
348
+ },
349
+ "max_single_item_size_mb": {
350
+ "type": "integer",
351
+ "required": False,
352
+ "default": 100,
353
+ "description": "Maximum size in MB for a single content item before skipping intelligent processing.",
354
+ },
355
+ },
356
+ },
357
+ # --- MCP Tool Response Thresholds ---
358
+ {
359
+ "name": "mcp_tool_response_save_threshold_bytes",
360
+ "required": False,
361
+ "type": "integer",
362
+ "default": 2048,
363
+ "description": "Threshold in bytes above which MCP tool responses are saved as artifacts.",
364
+ },
365
+ {
366
+ "name": "mcp_tool_llm_return_max_bytes",
367
+ "required": False,
368
+ "type": "integer",
369
+ "default": 4096,
370
+ "description": "Maximum size in bytes of MCP tool response content returned directly to the LLM.",
371
+ },
305
372
  # --- Artifact Handling ---
306
373
  {
307
374
  "name": "artifact_handling_mode",
@@ -1042,35 +1042,30 @@ class SamAgentComponent(ComponentBase):
1042
1042
 
1043
1043
  for func_decl in original_tool.function_declarations:
1044
1044
  func_decl_name = func_decl.name
1045
- tool_source_for_log = "unknown"
1046
- tool_matched_for_capability_lookup = False
1045
+ tool_object = llm_request.tools_dict.get(func_decl_name)
1046
+ origin = getattr(tool_object, "origin", "unknown")
1047
1047
 
1048
1048
  feature_descriptor = {
1049
1049
  "feature_type": "tool_function",
1050
1050
  "function_name": func_decl_name,
1051
- "tool_source": self._determine_tool_source(func_decl_name),
1052
- "tool_metadata": self._get_tool_metadata(func_decl_name),
1051
+ "tool_source": origin,
1052
+ "tool_metadata": {"function_name": func_decl_name},
1053
1053
  }
1054
1054
 
1055
- if func_decl_name.startswith(PEER_TOOL_PREFIX):
1055
+ if origin == "peer_agent":
1056
1056
  peer_name = func_decl_name.replace(PEER_TOOL_PREFIX, "", 1)
1057
1057
  feature_descriptor["tool_metadata"]["peer_agent_name"] = peer_name
1058
- tool_source_for_log = f"PeerAgentTool ({peer_name})"
1059
- tool_matched_for_capability_lookup = True
1060
-
1061
- if not tool_matched_for_capability_lookup:
1058
+ elif origin == "builtin":
1062
1059
  tool_def = tool_registry.get_tool_by_name(func_decl_name)
1063
1060
  if tool_def:
1064
1061
  feature_descriptor["tool_metadata"][
1065
1062
  "tool_category"
1066
1063
  ] = tool_def.category
1067
- feature_descriptor["tool_metadata"]["builtin_tool"] = True
1068
- tool_source_for_log = (
1069
- f"Built-in Tool ({tool_def.category}/{func_decl_name})"
1070
- )
1071
- tool_matched_for_capability_lookup = True
1072
-
1073
- if not tool_matched_for_capability_lookup:
1064
+ feature_descriptor["tool_metadata"][
1065
+ "required_scopes"
1066
+ ] = tool_def.required_scopes
1067
+ elif origin in ["python", "mcp", "adk_builtin"]:
1068
+ # Find the explicit config for this tool to pass to the resolver
1074
1069
  for tool_cfg in explicit_tools_config:
1075
1070
  cfg_tool_type = tool_cfg.get("tool_type")
1076
1071
  cfg_tool_name = tool_cfg.get("tool_name")
@@ -1082,24 +1077,11 @@ class SamAgentComponent(ComponentBase):
1082
1077
  cfg_tool_type in ["builtin", "mcp"]
1083
1078
  and cfg_tool_name == func_decl_name
1084
1079
  ):
1085
- feature_descriptor["tool_metadata"][
1086
- "tool_type"
1087
- ] = cfg_tool_type
1088
1080
  feature_descriptor["tool_metadata"][
1089
1081
  "tool_config"
1090
1082
  ] = tool_cfg
1091
- tool_source_for_log = f"Explicitly configured tool ({cfg_tool_type}: {cfg_tool_name or cfg_func_name})"
1092
- tool_matched_for_capability_lookup = True
1093
1083
  break
1094
1084
 
1095
- if not tool_matched_for_capability_lookup:
1096
- log.debug(
1097
- "%s FunctionDeclaration '%s' not found in any known configuration for capability checking. Assuming feature is available.",
1098
- log_id_prefix,
1099
- func_decl_name,
1100
- )
1101
- tool_source_for_log = "Unmatched/Implicit FunctionDeclaration"
1102
-
1103
1085
  context = {
1104
1086
  "agent_context": self.get_agent_context(),
1105
1087
  "filter_phase": "pre_llm",
@@ -1116,14 +1098,14 @@ class SamAgentComponent(ComponentBase):
1116
1098
  "%s FunctionDeclaration '%s' (Source: %s) permitted.",
1117
1099
  log_id_prefix,
1118
1100
  func_decl_name,
1119
- tool_source_for_log,
1101
+ origin,
1120
1102
  )
1121
1103
  else:
1122
1104
  log.info(
1123
1105
  "%s FunctionDeclaration '%s' (Source: %s) FILTERED OUT due to configuration restrictions.",
1124
1106
  log_id_prefix,
1125
1107
  func_decl_name,
1126
- tool_source_for_log,
1108
+ origin,
1127
1109
  )
1128
1110
 
1129
1111
  if permitted_declarations_for_this_tool:
@@ -1132,7 +1114,7 @@ class SamAgentComponent(ComponentBase):
1132
1114
 
1133
1115
  final_filtered_genai_tools.append(scoped_tool)
1134
1116
  log.debug(
1135
- "%s Keeping genai.Tool (original name/type preserved, declarations filtered) as it has %d permitted FunctionDeclaration(s).",
1117
+ "%s Keeping genai.Tool as it has %d permitted FunctionDeclaration(s).",
1136
1118
  log_id_prefix,
1137
1119
  len(permitted_declarations_for_this_tool),
1138
1120
  )
@@ -1169,60 +1151,6 @@ class SamAgentComponent(ComponentBase):
1169
1151
 
1170
1152
  return None
1171
1153
 
1172
- def _determine_tool_source(self, function_name: str) -> str:
1173
- """Determine the source/type of a tool function."""
1174
- if function_name.startswith("peer_"):
1175
- return "peer_agent"
1176
-
1177
- tool_def = tool_registry.get_tool_by_name(function_name)
1178
- if tool_def:
1179
- category_map = {
1180
- "artifact_management": "builtin_artifact",
1181
- "data_analysis": "builtin_data",
1182
- }
1183
- return category_map.get(tool_def.category, "builtin_other")
1184
-
1185
- return "explicit_tool"
1186
-
1187
- def _get_tool_metadata(self, function_name: str) -> Dict[str, Any]:
1188
- """Get metadata for a tool function."""
1189
- metadata = {"function_name": function_name}
1190
-
1191
- if function_name.startswith("peer_"):
1192
- peer_name = function_name.replace("peer_", "", 1)
1193
- metadata.update(
1194
- {"peer_agent_name": peer_name, "operation_type": "delegation"}
1195
- )
1196
- return metadata
1197
-
1198
- tool_def = tool_registry.get_tool_by_name(function_name)
1199
- if tool_def:
1200
- metadata.update(
1201
- {
1202
- "tool_category": tool_def.category,
1203
- "required_scopes": tool_def.required_scopes,
1204
- "builtin_tool": True,
1205
- }
1206
- )
1207
- return metadata
1208
-
1209
- explicit_tools_config = self.get_config("tools", [])
1210
- for tool_cfg in explicit_tools_config:
1211
- cfg_tool_name = tool_cfg.get("tool_name")
1212
- cfg_func_name = tool_cfg.get("function_name")
1213
- if (
1214
- tool_cfg.get("tool_type") == "python" and cfg_func_name == function_name
1215
- ) or (
1216
- tool_cfg.get("tool_type") in ["builtin", "mcp"]
1217
- and cfg_tool_name == function_name
1218
- ):
1219
- metadata.update(
1220
- {"tool_type": tool_cfg.get("tool_type"), "tool_config": tool_cfg}
1221
- )
1222
- break
1223
-
1224
- return metadata
1225
-
1226
1154
  def get_agent_context(self) -> Dict[str, Any]:
1227
1155
  """Get agent context for middleware calls."""
1228
1156
  return {