aip-agents-binary 0.0.0__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 (447) hide show
  1. aip_agents/__init__.py +66 -0
  2. aip_agents/a2a/__init__.py +19 -0
  3. aip_agents/a2a/server/__init__.py +10 -0
  4. aip_agents/a2a/server/base_executor.py +1125 -0
  5. aip_agents/a2a/server/google_adk_executor.py +198 -0
  6. aip_agents/a2a/server/langflow_executor.py +180 -0
  7. aip_agents/a2a/server/langgraph_executor.py +270 -0
  8. aip_agents/a2a/types.py +232 -0
  9. aip_agents/a2ui/__init__.py +26 -0
  10. aip_agents/a2ui/catalog_provider.py +109 -0
  11. aip_agents/a2ui/examples/0.8/booking_form.json +1 -0
  12. aip_agents/a2ui/examples/0.8/confirmation.json +1 -0
  13. aip_agents/a2ui/examples/0.8/single_column_list.json +1 -0
  14. aip_agents/a2ui/examples/0.9/booking_form.json +1 -0
  15. aip_agents/a2ui/examples/0.9/confirmation.json +1 -0
  16. aip_agents/a2ui/examples/0.9/single_column_list.json +1 -0
  17. aip_agents/a2ui/middleware.py +754 -0
  18. aip_agents/a2ui/models.py +153 -0
  19. aip_agents/a2ui/public_url.py +138 -0
  20. aip_agents/a2ui/stream_filter.py +67 -0
  21. aip_agents/agent/__init__.py +72 -0
  22. aip_agents/agent/base_agent.py +1437 -0
  23. aip_agents/agent/base_langgraph_agent.py +4048 -0
  24. aip_agents/agent/google_adk_agent.py +932 -0
  25. aip_agents/agent/google_adk_constants.py +11 -0
  26. aip_agents/agent/graph_runtime.py +185 -0
  27. aip_agents/agent/hitl/__init__.py +24 -0
  28. aip_agents/agent/hitl/config.py +28 -0
  29. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  30. aip_agents/agent/hitl/manager.py +532 -0
  31. aip_agents/agent/hitl/models.py +18 -0
  32. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  33. aip_agents/agent/hitl/prompt/base.py +42 -0
  34. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  35. aip_agents/agent/hitl/registry.py +149 -0
  36. aip_agents/agent/interface.py +138 -0
  37. aip_agents/agent/interfaces.py +65 -0
  38. aip_agents/agent/langflow_agent.py +464 -0
  39. aip_agents/agent/langgraph_react_agent.py +3741 -0
  40. aip_agents/agent/model_invocation_adapter.py +346 -0
  41. aip_agents/agent/system_instruction_context.py +34 -0
  42. aip_agents/agent/tool_execution_engine.py +587 -0
  43. aip_agents/audio_interface/__init__.py +30 -0
  44. aip_agents/audio_interface/audio_agent_adapter.py +254 -0
  45. aip_agents/audio_interface/config.py +146 -0
  46. aip_agents/audio_interface/errors.py +15 -0
  47. aip_agents/audio_interface/livekit_audio_session.py +504 -0
  48. aip_agents/audio_interface/livekit_realtime_audio_session.py +142 -0
  49. aip_agents/audio_interface/session_factory.py +40 -0
  50. aip_agents/clients/__init__.py +10 -0
  51. aip_agents/clients/langflow/__init__.py +10 -0
  52. aip_agents/clients/langflow/client.py +477 -0
  53. aip_agents/clients/langflow/types.py +18 -0
  54. aip_agents/common/__init__.py +5 -0
  55. aip_agents/common/callable_introspection.py +17 -0
  56. aip_agents/constants.py +23 -0
  57. aip_agents/context/__init__.py +5 -0
  58. aip_agents/context/budget.py +237 -0
  59. aip_agents/context/compaction.py +339 -0
  60. aip_agents/context/metadata.py +169 -0
  61. aip_agents/context/offload.py +449 -0
  62. aip_agents/context/preparation.py +196 -0
  63. aip_agents/context/prompt.py +61 -0
  64. aip_agents/context/runtime.py +149 -0
  65. aip_agents/context/session_recall.py +69 -0
  66. aip_agents/context/state.py +48 -0
  67. aip_agents/context/utils.py +44 -0
  68. aip_agents/credentials/manager.py +132 -0
  69. aip_agents/examples/__init__.py +5 -0
  70. aip_agents/examples/audio_livekit_session.py +116 -0
  71. aip_agents/examples/compare_streaming_client.py +783 -0
  72. aip_agents/examples/compare_streaming_server.py +141 -0
  73. aip_agents/examples/gl_connectors_smoke.py +148 -0
  74. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  75. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  76. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_server.py +78 -0
  78. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  79. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  80. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  81. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  82. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  83. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +104 -0
  84. aip_agents/examples/hello_world_a2a_langchain_server.py +78 -0
  85. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +77 -0
  86. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  87. aip_agents/examples/hello_world_a2a_langflow_server.py +80 -0
  88. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  89. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  90. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +91 -0
  91. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  92. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  93. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  94. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  95. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  96. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  97. aip_agents/examples/hello_world_a2a_langgraph_server.py +83 -0
  98. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +78 -0
  99. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +131 -0
  100. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  101. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  102. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +250 -0
  103. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  104. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +79 -0
  105. aip_agents/examples/hello_world_filesystem.py +162 -0
  106. aip_agents/examples/hello_world_google_adk.py +41 -0
  107. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  108. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  109. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  110. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  111. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  112. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  113. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  114. aip_agents/examples/hello_world_langchain.py +28 -0
  115. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  116. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  117. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  118. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  119. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  120. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  121. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  122. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  123. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  124. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  125. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  126. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  127. aip_agents/examples/hello_world_langgraph.py +39 -0
  128. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
  129. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  130. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  131. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  132. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  133. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  134. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  135. aip_agents/examples/hello_world_langgraph_skills.py +62 -0
  136. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  137. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  138. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  139. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  140. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  141. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  142. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  143. aip_agents/examples/hello_world_multi_agent_tool_output_client.py +57 -0
  144. aip_agents/examples/hello_world_multi_agent_tool_output_server.py +131 -0
  145. aip_agents/examples/hello_world_pii_logger.py +21 -0
  146. aip_agents/examples/hello_world_ptc.py +49 -0
  147. aip_agents/examples/hello_world_ptc_custom_tools.py +83 -0
  148. aip_agents/examples/hello_world_sentry.py +133 -0
  149. aip_agents/examples/hello_world_step_limits.py +273 -0
  150. aip_agents/examples/hello_world_stock_a2a_server.py +100 -0
  151. aip_agents/examples/hello_world_tool_output_client.py +55 -0
  152. aip_agents/examples/hello_world_tool_output_server.py +113 -0
  153. aip_agents/examples/hitl_demo.py +724 -0
  154. aip_agents/examples/mcp_configs/configs.py +63 -0
  155. aip_agents/examples/mcp_servers/common.py +76 -0
  156. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  157. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  158. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  159. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  160. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  161. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  162. aip_agents/examples/pii_demo_langgraph_server.py +125 -0
  163. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  164. aip_agents/examples/pii_demo_multi_agent_server.py +246 -0
  165. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  166. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +87 -0
  167. aip_agents/examples/tools/__init__.py +27 -0
  168. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  169. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  170. aip_agents/examples/tools/data_generator_tool.py +103 -0
  171. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  172. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  173. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  174. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  175. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  176. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  177. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  178. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  179. aip_agents/examples/tools/multiply_tool.py +43 -0
  180. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  181. aip_agents/examples/tools/random_chart_tool.py +142 -0
  182. aip_agents/examples/tools/serper_tool.py +202 -0
  183. aip_agents/examples/tools/stock_tools.py +82 -0
  184. aip_agents/examples/tools/table_generator_tool.py +167 -0
  185. aip_agents/examples/tools/time_tool.py +82 -0
  186. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  187. aip_agents/guardrails/__init__.py +83 -0
  188. aip_agents/guardrails/engines/__init__.py +69 -0
  189. aip_agents/guardrails/engines/base.py +90 -0
  190. aip_agents/guardrails/engines/nemo.py +101 -0
  191. aip_agents/guardrails/engines/phrase_matcher.py +113 -0
  192. aip_agents/guardrails/exceptions.py +39 -0
  193. aip_agents/guardrails/manager.py +163 -0
  194. aip_agents/guardrails/middleware.py +199 -0
  195. aip_agents/guardrails/schemas.py +63 -0
  196. aip_agents/guardrails/utils.py +45 -0
  197. aip_agents/integration/__init__.py +43 -0
  198. aip_agents/integration/agent.py +58 -0
  199. aip_agents/integration/guardrails.py +52 -0
  200. aip_agents/integration/hitl.py +71 -0
  201. aip_agents/integration/ptc.py +95 -0
  202. aip_agents/integration/skills.py +37 -0
  203. aip_agents/integration/storage.py +81 -0
  204. aip_agents/integration/version.py +103 -0
  205. aip_agents/mcp/__init__.py +1 -0
  206. aip_agents/mcp/client/__init__.py +60 -0
  207. aip_agents/mcp/client/base_mcp_client.py +377 -0
  208. aip_agents/mcp/client/connection_manager.py +231 -0
  209. aip_agents/mcp/client/errors.py +119 -0
  210. aip_agents/mcp/client/google_adk/__init__.py +45 -0
  211. aip_agents/mcp/client/google_adk/client.py +383 -0
  212. aip_agents/mcp/client/langchain/__init__.py +11 -0
  213. aip_agents/mcp/client/langchain/client.py +265 -0
  214. aip_agents/mcp/client/persistent_session.py +704 -0
  215. aip_agents/mcp/client/session_pool.py +358 -0
  216. aip_agents/mcp/client/transports.py +288 -0
  217. aip_agents/mcp/utils/__init__.py +7 -0
  218. aip_agents/mcp/utils/config_validator.py +139 -0
  219. aip_agents/memory/__init__.py +14 -0
  220. aip_agents/memory/adapters/__init__.py +10 -0
  221. aip_agents/memory/adapters/base_adapter.py +815 -0
  222. aip_agents/memory/adapters/mem0.py +286 -0
  223. aip_agents/memory/base.py +84 -0
  224. aip_agents/memory/constants.py +49 -0
  225. aip_agents/memory/factory.py +86 -0
  226. aip_agents/memory/guidance.py +20 -0
  227. aip_agents/memory/simple_memory.py +47 -0
  228. aip_agents/middleware/__init__.py +65 -0
  229. aip_agents/middleware/backends/__init__.py +72 -0
  230. aip_agents/middleware/backends/in_memory.py +578 -0
  231. aip_agents/middleware/backends/local_disk.py +1203 -0
  232. aip_agents/middleware/backends/protocol.py +353 -0
  233. aip_agents/middleware/backends/sandbox.py +1431 -0
  234. aip_agents/middleware/backends/sandbox_files.py +113 -0
  235. aip_agents/middleware/backends/utils.py +293 -0
  236. aip_agents/middleware/base.py +239 -0
  237. aip_agents/middleware/filesystem.py +690 -0
  238. aip_agents/middleware/manager.py +291 -0
  239. aip_agents/middleware/memory.py +532 -0
  240. aip_agents/middleware/pii_middleware.py +192 -0
  241. aip_agents/middleware/schema.py +19 -0
  242. aip_agents/middleware/skills.py +872 -0
  243. aip_agents/middleware/task_delegation.py +1656 -0
  244. aip_agents/middleware/todolist.py +276 -0
  245. aip_agents/middleware/tools/__init__.py +23 -0
  246. aip_agents/middleware/tools/edit_file.py +70 -0
  247. aip_agents/middleware/tools/execute.py +428 -0
  248. aip_agents/middleware/tools/grep_file.py +79 -0
  249. aip_agents/middleware/tools/ls.py +71 -0
  250. aip_agents/middleware/tools/read_file.py +239 -0
  251. aip_agents/middleware/tools/skill_load.py +193 -0
  252. aip_agents/middleware/tools/skill_resource.py +338 -0
  253. aip_agents/middleware/tools/write_file.py +114 -0
  254. aip_agents/middleware/vision.py +322 -0
  255. aip_agents/ptc/__init__.py +87 -0
  256. aip_agents/ptc/custom_tools.py +511 -0
  257. aip_agents/ptc/custom_tools_payload.py +499 -0
  258. aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
  259. aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +24 -0
  260. aip_agents/ptc/custom_tools_templates/custom_init.py.template +26 -0
  261. aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +149 -0
  262. aip_agents/ptc/custom_tools_templates/custom_registry.py.template +112 -0
  263. aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
  264. aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +87 -0
  265. aip_agents/ptc/doc_gen.py +122 -0
  266. aip_agents/ptc/exceptions.py +57 -0
  267. aip_agents/ptc/executor.py +627 -0
  268. aip_agents/ptc/mcp/__init__.py +45 -0
  269. aip_agents/ptc/mcp/sandbox_bridge.py +679 -0
  270. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  271. aip_agents/ptc/mcp/templates/mcp_client.py.template +240 -0
  272. aip_agents/ptc/naming.py +430 -0
  273. aip_agents/ptc/payload.py +26 -0
  274. aip_agents/ptc/prompt_builder.py +674 -0
  275. aip_agents/ptc/ptc_helper.py +16 -0
  276. aip_agents/ptc/sandbox_bridge.py +297 -0
  277. aip_agents/ptc/template_utils.py +33 -0
  278. aip_agents/ptc/templates/__init__.py +1 -0
  279. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  280. aip_agents/ptc/tool_def_helpers.py +101 -0
  281. aip_agents/ptc/tool_enrichment.py +163 -0
  282. aip_agents/resilience/__init__.py +10 -0
  283. aip_agents/resilience/base.py +46 -0
  284. aip_agents/resilience/tool.py +1661 -0
  285. aip_agents/sandbox/__init__.py +62 -0
  286. aip_agents/sandbox/defaults.py +220 -0
  287. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1-datasaur.Dockerfile +120 -0
  288. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1.Dockerfile +100 -0
  289. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2-datasaur.Dockerfile +143 -0
  290. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2.Dockerfile +119 -0
  291. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3-datasaur.Dockerfile +173 -0
  292. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3.Dockerfile +130 -0
  293. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.Dockerfile +164 -0
  294. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.patch_js_context.py +41 -0
  295. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.requirements.txt +29 -0
  296. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2.Dockerfile +87 -0
  297. aip_agents/sandbox/e2b_runtime.py +27 -0
  298. aip_agents/sandbox/image_registry.py +98 -0
  299. aip_agents/sandbox/providers/__init__.py +76 -0
  300. aip_agents/sandbox/providers/base.py +392 -0
  301. aip_agents/sandbox/providers/bedrock.py +28 -0
  302. aip_agents/sandbox/providers/e2b.py +230 -0
  303. aip_agents/sandbox/providers/opensandbox.py +163 -0
  304. aip_agents/sandbox/sandbox_runtime.py +967 -0
  305. aip_agents/sandbox/template_builder.py +240 -0
  306. aip_agents/sandbox/types.py +141 -0
  307. aip_agents/sandbox/validation.py +50 -0
  308. aip_agents/schema/__init__.py +106 -0
  309. aip_agents/schema/a2a.py +58 -0
  310. aip_agents/schema/agent.py +117 -0
  311. aip_agents/schema/artifacts.py +75 -0
  312. aip_agents/schema/attachments.py +425 -0
  313. aip_agents/schema/hitl.py +157 -0
  314. aip_agents/schema/langgraph.py +47 -0
  315. aip_agents/schema/model_id.py +103 -0
  316. aip_agents/schema/model_usage.py +50 -0
  317. aip_agents/schema/runtime_metadata.py +78 -0
  318. aip_agents/schema/step_limit.py +108 -0
  319. aip_agents/schema/storage.py +40 -0
  320. aip_agents/schema/tool_payloads.py +68 -0
  321. aip_agents/sentry/__init__.py +11 -0
  322. aip_agents/sentry/sentry.py +267 -0
  323. aip_agents/skills/__init__.py +30 -0
  324. aip_agents/skills/errors.py +110 -0
  325. aip_agents/skills/installer.py +479 -0
  326. aip_agents/skills/models.py +769 -0
  327. aip_agents/skills/stager.py +224 -0
  328. aip_agents/skills/validation.py +53 -0
  329. aip_agents/storage/__init__.py +41 -0
  330. aip_agents/storage/base.py +85 -0
  331. aip_agents/storage/clients/__init__.py +12 -0
  332. aip_agents/storage/clients/minio_client.py +318 -0
  333. aip_agents/storage/config.py +62 -0
  334. aip_agents/storage/providers/__init__.py +15 -0
  335. aip_agents/storage/providers/base.py +106 -0
  336. aip_agents/storage/providers/memory.py +114 -0
  337. aip_agents/storage/providers/object_storage.py +214 -0
  338. aip_agents/tools/__init__.py +65 -0
  339. aip_agents/tools/agent_schedule_tool.py +392 -0
  340. aip_agents/tools/browser_use/__init__.py +83 -0
  341. aip_agents/tools/browser_use/action_parser.py +104 -0
  342. aip_agents/tools/browser_use/browser_use_tool.py +1118 -0
  343. aip_agents/tools/browser_use/llm_config.py +120 -0
  344. aip_agents/tools/browser_use/minio_storage.py +203 -0
  345. aip_agents/tools/browser_use/schemas.py +121 -0
  346. aip_agents/tools/browser_use/session.py +13 -0
  347. aip_agents/tools/browser_use/session_errors.py +132 -0
  348. aip_agents/tools/browser_use/steel_session_recording.py +321 -0
  349. aip_agents/tools/browser_use/streaming.py +263 -0
  350. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  351. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  352. aip_agents/tools/browser_use/types.py +37 -0
  353. aip_agents/tools/code_sandbox/__init__.py +48 -0
  354. aip_agents/tools/code_sandbox/code_sandbox_tool.py +350 -0
  355. aip_agents/tools/code_sandbox/constant.py +13 -0
  356. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +346 -0
  357. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +65 -0
  358. aip_agents/tools/computer_use/__init__.py +75 -0
  359. aip_agents/tools/computer_use/computer_use_tool.py +910 -0
  360. aip_agents/tools/computer_use/schemas.py +78 -0
  361. aip_agents/tools/computer_use/streaming.py +198 -0
  362. aip_agents/tools/computer_use/types.py +32 -0
  363. aip_agents/tools/constants.py +177 -0
  364. aip_agents/tools/date_range_tool.py +554 -0
  365. aip_agents/tools/document_loader/__init__.py +44 -0
  366. aip_agents/tools/document_loader/base_reader.py +302 -0
  367. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  368. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  369. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  370. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  371. aip_agents/tools/execute_ptc_code.py +582 -0
  372. aip_agents/tools/gl_connector/__init__.py +5 -0
  373. aip_agents/tools/gl_connector/tool.py +518 -0
  374. aip_agents/tools/gl_connector_tools.py +170 -0
  375. aip_agents/tools/memory_search/__init__.py +29 -0
  376. aip_agents/tools/memory_search/base.py +213 -0
  377. aip_agents/tools/memory_search/mem0.py +424 -0
  378. aip_agents/tools/memory_search/schema.py +81 -0
  379. aip_agents/tools/memory_search_tool.py +34 -0
  380. aip_agents/tools/streaming_common.py +577 -0
  381. aip_agents/tools/time_tool.py +117 -0
  382. aip_agents/tools/tool_config_injector.py +300 -0
  383. aip_agents/tools/web_search/__init__.py +15 -0
  384. aip_agents/tools/web_search/serper_tool.py +187 -0
  385. aip_agents/types/__init__.py +73 -0
  386. aip_agents/types/a2a_events.py +13 -0
  387. aip_agents/utils/__init__.py +91 -0
  388. aip_agents/utils/a2a_connector.py +1758 -0
  389. aip_agents/utils/artifact_helpers.py +542 -0
  390. aip_agents/utils/artifacts/__init__.py +20 -0
  391. aip_agents/utils/artifacts/tracker.py +367 -0
  392. aip_agents/utils/attachment_helpers.py +178 -0
  393. aip_agents/utils/constants.py +22 -0
  394. aip_agents/utils/datetime/__init__.py +34 -0
  395. aip_agents/utils/datetime/normalization.py +231 -0
  396. aip_agents/utils/datetime/timezone.py +206 -0
  397. aip_agents/utils/env_loader.py +27 -0
  398. aip_agents/utils/event_handler_registry.py +58 -0
  399. aip_agents/utils/file_prompt_utils.py +276 -0
  400. aip_agents/utils/file_watchers/__init__.py +33 -0
  401. aip_agents/utils/file_watchers/base.py +46 -0
  402. aip_agents/utils/file_watchers/e2b.py +128 -0
  403. aip_agents/utils/file_watchers/factory.py +39 -0
  404. aip_agents/utils/file_watchers/ls_diff.py +88 -0
  405. aip_agents/utils/file_watchers/sandbox.py +66 -0
  406. aip_agents/utils/file_watchers/watchdog.py +97 -0
  407. aip_agents/utils/final_response_builder.py +211 -0
  408. aip_agents/utils/formatter_llm_client.py +258 -0
  409. aip_agents/utils/langgraph/__init__.py +19 -0
  410. aip_agents/utils/langgraph/converter.py +399 -0
  411. aip_agents/utils/langgraph/delegation_context.py +22 -0
  412. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  413. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  414. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  415. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1329 -0
  416. aip_agents/utils/langgraph/tool_output_management.py +1047 -0
  417. aip_agents/utils/logger.py +195 -0
  418. aip_agents/utils/metadata/__init__.py +27 -0
  419. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  420. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  421. aip_agents/utils/metadata/activity_narrative/builder.py +822 -0
  422. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  423. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  424. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  425. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  426. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  427. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  428. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  429. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  430. aip_agents/utils/metadata_helper.py +363 -0
  431. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  432. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  433. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  434. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  435. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  436. aip_agents/utils/pii/__init__.py +29 -0
  437. aip_agents/utils/pii/pii_handler.py +555 -0
  438. aip_agents/utils/pii/pii_helper.py +225 -0
  439. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  440. aip_agents/utils/reference_helper.py +273 -0
  441. aip_agents/utils/sse_chunk_transformer.py +836 -0
  442. aip_agents/utils/step_limit_manager.py +265 -0
  443. aip_agents/utils/token_usage_helper.py +531 -0
  444. aip_agents_binary-0.0.0.dist-info/METADATA +825 -0
  445. aip_agents_binary-0.0.0.dist-info/RECORD +447 -0
  446. aip_agents_binary-0.0.0.dist-info/WHEEL +5 -0
  447. aip_agents_binary-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,358 @@
1
+ """MCP Session Pool for centralized session management.
2
+
3
+ This module manages a pool of persistent MCP sessions, providing centralized
4
+ initialization, tool collection, and cleanup.
5
+
6
+ Authors:
7
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
8
+ """
9
+
10
+ import asyncio
11
+
12
+ from gllm_tools.mcp.client.config import MCPConfiguration
13
+ from mcp.types import Tool
14
+
15
+ from aip_agents.mcp.client.persistent_session import PersistentMCPSession
16
+ from aip_agents.mcp.utils.config_validator import validate_allowed_tools_config, validate_allowed_tools_list
17
+ from aip_agents.utils.logger import get_logger
18
+
19
+ logger = get_logger(__name__)
20
+
21
+
22
+ class MCPSessionPool:
23
+ """Manages pool of persistent MCP sessions.
24
+
25
+ This pool provides centralized management of MCP sessions, including
26
+ initialization, tool collection, and resource cleanup. Sessions are
27
+ reused across the agent lifecycle.
28
+ """
29
+
30
+ def __init__(self):
31
+ """Initialize empty session pool."""
32
+ self.sessions: dict[str, PersistentMCPSession] = {}
33
+ self._lock = asyncio.Lock()
34
+ self._initialized = False
35
+
36
+ async def get_or_create_session(
37
+ self,
38
+ server_name: str,
39
+ config: MCPConfiguration,
40
+ allowed_tools: list[str] | None = None,
41
+ ) -> PersistentMCPSession:
42
+ """Get existing session or create new one.
43
+
44
+ Args:
45
+ server_name (str): Name of the MCP server
46
+ config (MCPConfiguration): MCP server configuration
47
+ allowed_tools (list[str] | None): Optional list of tool names to allow. None means all tools allowed.
48
+
49
+ Returns:
50
+ PersistentMCPSession: Persistent MCP session
51
+
52
+ Raises:
53
+ Exception: If session creation fails
54
+ """
55
+ async with self._lock:
56
+ if server_name in self.sessions:
57
+ return self._update_existing_session(server_name, allowed_tools)
58
+
59
+ return await self._create_and_initialize_session(server_name, config, allowed_tools)
60
+
61
+ def _update_existing_session(
62
+ self,
63
+ server_name: str,
64
+ allowed_tools: list[str] | None,
65
+ ) -> PersistentMCPSession:
66
+ """Update existing session's allowed_tools if changed.
67
+
68
+ Args:
69
+ server_name (str): Name of the MCP server
70
+ allowed_tools (list[str] | None): Optional list of tool names to allow
71
+
72
+ Returns:
73
+ PersistentMCPSession: Updated existing session
74
+ """
75
+ existing_session = self.sessions[server_name]
76
+ if existing_session.update_allowed_tools(allowed_tools):
77
+ logger.debug(f"Reconfigured allowed_tools for {server_name}")
78
+ else:
79
+ logger.debug(f"Reusing existing session for {server_name} (allowed_tools unchanged)")
80
+ return existing_session
81
+
82
+ async def _create_and_initialize_session(
83
+ self,
84
+ server_name: str,
85
+ config: MCPConfiguration,
86
+ allowed_tools: list[str] | None,
87
+ ) -> PersistentMCPSession:
88
+ """Create and initialize a new session.
89
+
90
+ Args:
91
+ server_name (str): Name of the MCP server
92
+ config (MCPConfiguration): MCP server configuration
93
+ allowed_tools (list[str] | None): Optional list of tool names to allow
94
+
95
+ Returns:
96
+ PersistentMCPSession: Newly created and initialized session
97
+
98
+ Raises:
99
+ Exception: If session creation or initialization fails
100
+ """
101
+ logger.info(f"Creating new session for {server_name}")
102
+ session = PersistentMCPSession(
103
+ server_name,
104
+ config,
105
+ allowed_tools=allowed_tools,
106
+ )
107
+
108
+ # Initialize session first, only store if successful
109
+ try:
110
+ await session.initialize()
111
+ # Only store session after successful initialization
112
+ self.sessions[server_name] = session
113
+ logger.info(f"Session created and cached for {server_name}")
114
+ return session
115
+ except asyncio.CancelledError:
116
+ await session.disconnect()
117
+ raise
118
+ except Exception:
119
+ # Clean up session on failure
120
+ await session.disconnect()
121
+ raise
122
+
123
+ async def initialize_all_sessions(self, server_configs: dict[str, MCPConfiguration]) -> None:
124
+ """Initialize all sessions and cache tools.
125
+
126
+ This method initializes all configured MCP servers concurrently
127
+ for better performance.
128
+
129
+ Args:
130
+ server_configs (dict[str, MCPConfiguration]): Dictionary of server configurations
131
+
132
+ Raises:
133
+ Exception: If any session initialization fails
134
+ """
135
+ if self._initialized:
136
+ logger.debug("Session pool already initialized")
137
+ return
138
+
139
+ logger.info(f"Initializing session pool with {len(server_configs)} servers")
140
+
141
+ # Initialize all sessions concurrently
142
+ initialization_tasks = []
143
+ for server_name, config in server_configs.items():
144
+ allowed_tools = self._extract_allowed_tools(server_name, config)
145
+ task = self._initialize_single_session(server_name, config, allowed_tools)
146
+ initialization_tasks.append(task)
147
+
148
+ if initialization_tasks:
149
+ try:
150
+ await asyncio.gather(*initialization_tasks)
151
+ self._initialized = True
152
+ logger.info(f"Session pool initialized with {len(self.sessions)} active sessions")
153
+ except asyncio.CancelledError:
154
+ await self.close_all_sessions()
155
+ self._initialized = False
156
+ raise
157
+ except Exception as e:
158
+ logger.error(f"Failed to initialize session pool: {e}")
159
+ # Cleanup any partially initialized sessions
160
+ await self.close_all_sessions()
161
+ # Ensure _initialized remains False to allow retrying
162
+ self._initialized = False
163
+ raise
164
+ else:
165
+ self._initialized = True
166
+ logger.info("Session pool initialized (no servers configured)")
167
+
168
+ def _extract_allowed_tools(
169
+ self,
170
+ server_name: str,
171
+ config: MCPConfiguration,
172
+ ) -> list[str] | None:
173
+ """Extract allowed_tools from config.
174
+
175
+ Args:
176
+ server_name (str): Name of the MCP server
177
+ config (MCPConfiguration): MCP server configuration
178
+
179
+ Returns:
180
+ list[str] | None: List of allowed tool names, or None if no restriction
181
+
182
+ Raises:
183
+ ValueError: If allowed_tools is not a list of strings
184
+ """
185
+ allowed_tools: list[str] | None = None
186
+
187
+ # Try object attribute first (avoids double property call from hasattr+getattr)
188
+ try:
189
+ raw_allowed = config.allowed_tools
190
+ # Use unified validation logic
191
+ allowed_tools = validate_allowed_tools_list(raw_allowed, f"Server '{server_name}'")
192
+ except AttributeError:
193
+ # Check if config is dict-like
194
+ if isinstance(config, dict):
195
+ allowed_tools = validate_allowed_tools_config(config, server_name)
196
+
197
+ if allowed_tools:
198
+ logger.debug(f"Server '{server_name}' has {len(allowed_tools)} allowed tools")
199
+ else:
200
+ logger.debug(f"Server '{server_name}' allows all tools (no restriction)")
201
+
202
+ return allowed_tools
203
+
204
+ async def _initialize_single_session(
205
+ self,
206
+ server_name: str,
207
+ config: MCPConfiguration,
208
+ allowed_tools: list[str] | None = None,
209
+ ) -> None:
210
+ """Initialize a single session (internal method).
211
+
212
+ Args:
213
+ server_name (str): Name of the MCP server
214
+ config (MCPConfiguration): MCP server configuration
215
+ allowed_tools (list[str] | None): Optional list of tool names to allow
216
+ """
217
+ try:
218
+ await self.get_or_create_session(server_name, config, allowed_tools)
219
+ logger.debug(f"Session initialized for {server_name}")
220
+ except Exception as e:
221
+ logger.debug(f"Failed to initialize session for {server_name}: {e}")
222
+ raise
223
+
224
+ def get_all_active_sessions(self) -> dict[str, PersistentMCPSession]:
225
+ """Get all active sessions.
226
+
227
+ Returns:
228
+ dict[str, PersistentMCPSession]: Dictionary of active sessions by server name
229
+ """
230
+ return {name: session for name, session in self.sessions.items() if session.is_initialized}
231
+
232
+ @property
233
+ def active_sessions(self) -> list[str]:
234
+ """Get list of active session names.
235
+
236
+ Returns:
237
+ list[str]: List of active session names
238
+ """
239
+ return [name for name, session in self.sessions.items() if session.is_initialized]
240
+
241
+ async def get_all_tools(self) -> list[Tool]:
242
+ """Get all cached tools from all active sessions.
243
+
244
+ Returns:
245
+ list[Tool]: List of all available tools across all sessions
246
+ """
247
+ # Create snapshot of sessions under lock to prevent race conditions
248
+ async with self._lock:
249
+ session_snapshot = dict(self.sessions.items())
250
+
251
+ # Prepare tasks for active sessions
252
+ server_names = list(session_snapshot.keys())
253
+ tasks = [session_snapshot[name].list_tools() for name in server_names]
254
+
255
+ results = []
256
+ if tasks:
257
+ results = await asyncio.gather(*tasks, return_exceptions=True)
258
+
259
+ all_tools: list[Tool] = []
260
+ for server_name, result in zip(server_names, results, strict=False):
261
+ if isinstance(result, Exception):
262
+ logger.warning(f"Failed to get tools from {server_name}: {result}")
263
+ else:
264
+ all_tools.extend(result)
265
+ logger.debug(f"Added {len(result)} tools from {server_name}")
266
+
267
+ logger.debug(f"Total tools available: {len(all_tools)}")
268
+ return all_tools
269
+
270
+ async def close_session(self, server_name: str) -> None:
271
+ """Close specific session.
272
+
273
+ Args:
274
+ server_name (str): Name of the server session to close
275
+ """
276
+ async with self._lock:
277
+ if server_name in self.sessions:
278
+ try:
279
+ logger.info(f"Closing session for {server_name}")
280
+ await self.sessions[server_name].disconnect()
281
+ except Exception as e:
282
+ logger.warning(f"Error closing session {server_name}: {e}")
283
+ finally:
284
+ del self.sessions[server_name]
285
+ logger.info(f"Session {server_name} removed from pool")
286
+
287
+ async def close_all_sessions(self) -> None:
288
+ """Close all sessions gracefully.
289
+
290
+ This method ensures all resources are cleaned up properly.
291
+ """
292
+ async with self._lock:
293
+ logger.info("Closing all sessions in pool")
294
+
295
+ # Create list of sessions to close (to avoid modification during iteration)
296
+ sessions_to_close = list(self.sessions.keys())
297
+
298
+ # Close sessions concurrently for faster shutdown
299
+ close_tasks = []
300
+ for server_name in sessions_to_close:
301
+ task = self._close_single_session(server_name)
302
+ close_tasks.append(task)
303
+
304
+ if close_tasks:
305
+ await asyncio.gather(*close_tasks, return_exceptions=True)
306
+
307
+ # Clear all session data
308
+ self.sessions.clear()
309
+ self._initialized = False
310
+
311
+ logger.info("All sessions closed and pool cleared")
312
+
313
+ async def _close_single_session(self, server_name: str) -> None:
314
+ """Close a single session (internal method).
315
+
316
+ Args:
317
+ server_name (str): Name of the server session to close
318
+ """
319
+ try:
320
+ if server_name in self.sessions:
321
+ await self.sessions[server_name].disconnect()
322
+ logger.debug(f"Session {server_name} closed successfully")
323
+ except Exception as e:
324
+ logger.warning(f"Error closing session {server_name}: {e}")
325
+
326
+ @property
327
+ def is_initialized(self) -> bool:
328
+ """Check if session pool is initialized.
329
+
330
+ Returns:
331
+ bool: True if initialized, False otherwise
332
+ """
333
+ return self._initialized
334
+
335
+ @property
336
+ def session_count(self) -> int:
337
+ """Get number of active sessions.
338
+
339
+ Returns:
340
+ int: Number of active sessions
341
+ """
342
+ return len(self.sessions)
343
+
344
+ def get_session(self, server_name: str) -> PersistentMCPSession:
345
+ """Get specific session by name.
346
+
347
+ Args:
348
+ server_name (str): Name of the server
349
+
350
+ Returns:
351
+ PersistentMCPSession: The requested session
352
+
353
+ Raises:
354
+ KeyError: If session doesn't exist
355
+ """
356
+ if server_name not in self.sessions:
357
+ raise KeyError(f"Session '{server_name}' not found")
358
+ return self.sessions[server_name]
@@ -0,0 +1,288 @@
1
+ """MCP Transport Handlers.
2
+
3
+ This module provides abstract and concrete transport classes for STDIO, SSE, and streamable HTTP.
4
+ Each transport handles connection establishment specific to its protocol.
5
+
6
+ Authors:
7
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
8
+ """
9
+
10
+ from abc import ABC, abstractmethod
11
+ from collections.abc import AsyncIterator
12
+ from enum import StrEnum
13
+ from types import TracebackType
14
+ from typing import Any, Protocol
15
+
16
+ import httpx
17
+ from gllm_tools.mcp.client.config import MCPConfiguration
18
+ from mcp.client.sse import sse_client
19
+ from mcp.client.stdio import StdioServerParameters, stdio_client
20
+ from mcp.client.streamable_http import streamable_http_client
21
+
22
+ from aip_agents.mcp.client.errors import build_startup_error, get_exception_status_code, is_auth_status_code
23
+ from aip_agents.utils.logger import get_logger
24
+
25
+
26
+ class TransportContext(Protocol):
27
+ """Protocol defining the interface for async context managers used in MCP transport connections."""
28
+
29
+ async def __aenter__(self):
30
+ """Enter the async context, establishing the connection and returning read/write streams."""
31
+ ...
32
+
33
+ async def __aexit__(
34
+ self,
35
+ _exc_type: type[BaseException] | None,
36
+ _exc_val: BaseException | None,
37
+ _exc_tb: TracebackType | None,
38
+ ):
39
+ """Exit the async context, performing cleanup and closing the connection.
40
+
41
+ Args:
42
+ _exc_type: Exception type if an exception occurred.
43
+ _exc_val: Exception value if an exception occurred.
44
+ _exc_tb: Exception traceback if an exception occurred.
45
+ """
46
+ ...
47
+
48
+
49
+ logger = get_logger(__name__)
50
+
51
+
52
+ DEFAULT_TIMEOUT: float = 30.0
53
+ """Default connection timeout in seconds."""
54
+
55
+
56
+ def _sanitize_headers(config: MCPConfiguration) -> dict[str, str]:
57
+ """Remove headers with None values to avoid invalid HTTP headers.
58
+
59
+ Args:
60
+ config (MCPConfiguration): Transport configuration containing optional headers.
61
+
62
+ Returns:
63
+ dict[str, str]: Filtered headers with None values removed.
64
+ """
65
+ headers = config.get("headers", {}) or {}
66
+ return {key: value for key, value in headers.items() if value is not None}
67
+
68
+
69
+ class TransportType(StrEnum):
70
+ """Enum for supported MCP transport types."""
71
+
72
+ HTTP = "http"
73
+ SSE = "sse"
74
+ STDIO = "stdio"
75
+
76
+
77
+ class Transport(ABC):
78
+ """Abstract base class for MCP transports."""
79
+
80
+ def __init__(self, server_name: str, config: MCPConfiguration) -> None:
81
+ """Initialize the transport.
82
+
83
+ Args:
84
+ server_name (str): Name of the MCP server.
85
+ config (MCPConfiguration): Configuration for the transport.
86
+ """
87
+ self.server_name = server_name
88
+ self.config = config
89
+ self.ctx: Any = None
90
+
91
+ @abstractmethod
92
+ async def connect(self) -> tuple[AsyncIterator[bytes], AsyncIterator[bytes], TransportContext]:
93
+ """Establish connection and return read/write streams and context manager.
94
+
95
+ Returns:
96
+ tuple[AsyncIterator[bytes], AsyncIterator[bytes], Any]:
97
+ (read_stream, write_stream, ctx)
98
+ Where:
99
+ - read_stream: AsyncIterator[bytes] for reading from the server.
100
+ - write_stream: AsyncIterator[bytes] for writing to the server.
101
+ - ctx: The async context manager instance for cleanup via __aexit__.
102
+
103
+ Raises:
104
+ ValueError: If required config (e.g., URL or command) is missing.
105
+ ConnectionError: If connection establishment fails.
106
+ """
107
+ pass
108
+
109
+ async def close(self) -> None:
110
+ """Clean up the transport connection."""
111
+ if self.ctx:
112
+ try:
113
+ await self.ctx.__aexit__(None, None, None)
114
+ except Exception as e:
115
+ logger.warning(f"Error during transport cleanup for {self.server_name}: {e}")
116
+
117
+
118
+ class SSETransport(Transport):
119
+ """SSE transport handler."""
120
+
121
+ async def connect(self) -> tuple[AsyncIterator[bytes], AsyncIterator[bytes], TransportContext]:
122
+ """Connect using SSE transport.
123
+
124
+ Builds SSE URL from config, initializes client with timeout, and enters context.
125
+
126
+ Returns:
127
+ tuple[AsyncIterator[bytes], AsyncIterator[bytes], Any]: (read_stream, write_stream, ctx)
128
+
129
+ Raises:
130
+ ValueError: If URL is missing.
131
+ ConnectionError: If SSE connection fails.
132
+ """
133
+ base_url = self.config.get("url", "").rstrip("/")
134
+ if not base_url:
135
+ raise ValueError("URL is required for SSE transport")
136
+
137
+ url = f"{base_url}/sse" if not base_url.endswith("/sse") else base_url
138
+ timeout = self.config.get("timeout", DEFAULT_TIMEOUT)
139
+ headers = _sanitize_headers(self.config)
140
+ logger.debug(f"Attempting SSE connection to {url} with headers: {list(headers.keys())}")
141
+ try:
142
+ self.ctx = sse_client(url=url, timeout=timeout, sse_read_timeout=300.0, headers=headers)
143
+ read_stream, write_stream = await self.ctx.__aenter__()
144
+ logger.info(f"Connected to {self.server_name} via SSE")
145
+ return read_stream, write_stream, self.ctx
146
+ except Exception as e:
147
+ status_code = get_exception_status_code(e)
148
+ if is_auth_status_code(status_code):
149
+ raise build_startup_error(
150
+ server_name=self.server_name,
151
+ message=f"MCP server '{self.server_name}' startup failed: {status_code} {str(e)}",
152
+ transport=TransportType.SSE.value,
153
+ cause=e,
154
+ status_code=status_code,
155
+ ) from e
156
+ raise ConnectionError(f"SSE connection failed for {self.server_name}: {str(e)}") from e
157
+
158
+
159
+ class HTTPTransport(Transport):
160
+ """Streamable HTTP transport handler."""
161
+
162
+ def __init__(self, server_name: str, config: MCPConfiguration) -> None:
163
+ """Initialize the HTTP transport.
164
+
165
+ Args:
166
+ server_name (str): Name of the MCP server.
167
+ config (MCPConfiguration): Configuration for the transport.
168
+ """
169
+ super().__init__(server_name, config)
170
+ self._http_client: httpx.AsyncClient | None = None
171
+
172
+ async def close(self) -> None:
173
+ """Clean up the transport connection and any owned HTTP client."""
174
+ await super().close()
175
+ if self._http_client:
176
+ try:
177
+ await self._http_client.aclose()
178
+ except Exception as e:
179
+ logger.warning(f"Error during HTTP client cleanup for {self.server_name}: {e}")
180
+ finally:
181
+ self._http_client = None
182
+
183
+ async def connect(self) -> tuple[AsyncIterator[bytes], AsyncIterator[bytes], TransportContext]:
184
+ """Connect using streamable HTTP transport.
185
+
186
+ Builds MCP URL from config, initializes client with timeout, and enters context.
187
+
188
+ Returns:
189
+ tuple[AsyncIterator[bytes], AsyncIterator[bytes], Any]: (read_stream, write_stream, ctx)
190
+
191
+ Raises:
192
+ ValueError: If URL is missing.
193
+ ConnectionError: If HTTP connection fails.
194
+ """
195
+ base_url = self.config.get("url", "").rstrip("/")
196
+ if not base_url:
197
+ raise ValueError("URL is required for HTTP transport")
198
+
199
+ url = f"{base_url}/mcp" if not base_url.endswith("/mcp") else base_url
200
+ timeout = self.config.get("timeout", DEFAULT_TIMEOUT)
201
+ headers = _sanitize_headers(self.config)
202
+ logger.debug(f"Attempting streamable HTTP connection to {url} with headers: {list(headers.keys())}")
203
+ try:
204
+ http_client = httpx.AsyncClient(
205
+ timeout=httpx.Timeout(timeout),
206
+ headers=headers,
207
+ follow_redirects=True,
208
+ )
209
+ self._http_client = http_client
210
+ self.ctx = streamable_http_client(url=url, http_client=http_client)
211
+ read_stream, write_stream, _ = await self.ctx.__aenter__()
212
+ logger.info(f"Connected to {self.server_name} via HTTP")
213
+ return read_stream, write_stream, self.ctx
214
+ except Exception as e:
215
+ if self._http_client:
216
+ try:
217
+ await self._http_client.aclose()
218
+ except Exception as close_exc:
219
+ logger.warning(f"Error during HTTP client cleanup for {self.server_name}: {close_exc}")
220
+ finally:
221
+ self._http_client = None
222
+ status_code = get_exception_status_code(e)
223
+ if is_auth_status_code(status_code):
224
+ raise build_startup_error(
225
+ server_name=self.server_name,
226
+ message=f"MCP server '{self.server_name}' startup failed: {status_code} {str(e)}",
227
+ transport=TransportType.HTTP.value,
228
+ cause=e,
229
+ status_code=status_code,
230
+ ) from e
231
+ raise ConnectionError(f"HTTP connection failed for {self.server_name}: {str(e)}") from e
232
+
233
+
234
+ class StdioTransport(Transport):
235
+ """STDIO transport handler."""
236
+
237
+ async def connect(self) -> tuple[AsyncIterator[bytes], AsyncIterator[bytes], TransportContext]:
238
+ """Connect using STDIO transport.
239
+
240
+ Initializes stdio client from command/args/env in config and enters context.
241
+
242
+ Returns:
243
+ tuple[AsyncIterator[bytes], AsyncIterator[bytes], Any]: (read_stream, write_stream, ctx)
244
+
245
+ Raises:
246
+ ValueError: If command is missing.
247
+ ConnectionError: If STDIO connection fails.
248
+ """
249
+ command = self.config.get("command")
250
+ args = self.config.get("args", [])
251
+ env = self.config.get("env")
252
+
253
+ if not command:
254
+ raise ValueError("Command is required for stdio transport")
255
+
256
+ logger.debug(f"Attempting stdio connection with command: {command}, args: {args}")
257
+ try:
258
+ stdio_params = StdioServerParameters(command=command, args=args, env=env)
259
+ self.ctx = stdio_client(stdio_params)
260
+ read_stream, write_stream = await self.ctx.__aenter__()
261
+ logger.info(f"Connected to {self.server_name} via STDIO")
262
+ return read_stream, write_stream, self.ctx
263
+ except Exception as e:
264
+ raise ConnectionError(f"STDIO connection failed for {self.server_name}: {str(e)}") from e
265
+
266
+
267
+ def create_transport(server_name: str, config: MCPConfiguration, transport_type: TransportType | str) -> Transport:
268
+ """Factory to create the appropriate transport instance.
269
+
270
+ Args:
271
+ server_name (str): Server name
272
+ config (MCPConfiguration): Config
273
+ transport_type (str): Transport type ('http', 'sse', 'stdio')
274
+
275
+ Returns:
276
+ Transport: Concrete transport instance
277
+
278
+ Raises:
279
+ ValueError: If transport_type is unsupported.
280
+ """
281
+ if transport_type == TransportType.HTTP:
282
+ return HTTPTransport(server_name, config)
283
+ elif transport_type == TransportType.SSE:
284
+ return SSETransport(server_name, config)
285
+ elif transport_type == TransportType.STDIO:
286
+ return StdioTransport(server_name, config)
287
+ else:
288
+ raise ValueError(f"Unsupported transport type: {transport_type}")
@@ -0,0 +1,7 @@
1
+ """MCP utility modules.
2
+
3
+ Authors:
4
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
5
+ """
6
+
7
+ __all__: list[str] = []