aip-agents-binary 0.6.4__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 aip-agents-binary might be problematic. Click here for more details.

Files changed (612) hide show
  1. aip_agents/__init__.py +65 -0
  2. aip_agents/__init__.pyi +19 -0
  3. aip_agents/a2a/__init__.py +19 -0
  4. aip_agents/a2a/__init__.pyi +3 -0
  5. aip_agents/a2a/server/__init__.py +10 -0
  6. aip_agents/a2a/server/__init__.pyi +4 -0
  7. aip_agents/a2a/server/base_executor.py +1086 -0
  8. aip_agents/a2a/server/base_executor.pyi +73 -0
  9. aip_agents/a2a/server/google_adk_executor.py +198 -0
  10. aip_agents/a2a/server/google_adk_executor.pyi +51 -0
  11. aip_agents/a2a/server/langflow_executor.py +180 -0
  12. aip_agents/a2a/server/langflow_executor.pyi +43 -0
  13. aip_agents/a2a/server/langgraph_executor.py +270 -0
  14. aip_agents/a2a/server/langgraph_executor.pyi +47 -0
  15. aip_agents/a2a/types.py +232 -0
  16. aip_agents/a2a/types.pyi +132 -0
  17. aip_agents/agent/__init__.py +27 -0
  18. aip_agents/agent/__init__.pyi +9 -0
  19. aip_agents/agent/base_agent.py +970 -0
  20. aip_agents/agent/base_agent.pyi +221 -0
  21. aip_agents/agent/base_langgraph_agent.py +3037 -0
  22. aip_agents/agent/base_langgraph_agent.pyi +233 -0
  23. aip_agents/agent/google_adk_agent.py +926 -0
  24. aip_agents/agent/google_adk_agent.pyi +141 -0
  25. aip_agents/agent/google_adk_constants.py +6 -0
  26. aip_agents/agent/google_adk_constants.pyi +3 -0
  27. aip_agents/agent/hitl/__init__.py +24 -0
  28. aip_agents/agent/hitl/__init__.pyi +6 -0
  29. aip_agents/agent/hitl/config.py +28 -0
  30. aip_agents/agent/hitl/config.pyi +15 -0
  31. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  32. aip_agents/agent/hitl/langgraph_hitl_mixin.pyi +42 -0
  33. aip_agents/agent/hitl/manager.py +532 -0
  34. aip_agents/agent/hitl/manager.pyi +200 -0
  35. aip_agents/agent/hitl/models.py +18 -0
  36. aip_agents/agent/hitl/models.pyi +3 -0
  37. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  38. aip_agents/agent/hitl/prompt/__init__.pyi +4 -0
  39. aip_agents/agent/hitl/prompt/base.py +42 -0
  40. aip_agents/agent/hitl/prompt/base.pyi +24 -0
  41. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  42. aip_agents/agent/hitl/prompt/deferred.pyi +30 -0
  43. aip_agents/agent/hitl/registry.py +149 -0
  44. aip_agents/agent/hitl/registry.pyi +101 -0
  45. aip_agents/agent/interface.py +138 -0
  46. aip_agents/agent/interface.pyi +81 -0
  47. aip_agents/agent/interfaces.py +65 -0
  48. aip_agents/agent/interfaces.pyi +44 -0
  49. aip_agents/agent/langflow_agent.py +464 -0
  50. aip_agents/agent/langflow_agent.pyi +133 -0
  51. aip_agents/agent/langgraph_memory_enhancer_agent.py +767 -0
  52. aip_agents/agent/langgraph_memory_enhancer_agent.pyi +50 -0
  53. aip_agents/agent/langgraph_react_agent.py +2856 -0
  54. aip_agents/agent/langgraph_react_agent.pyi +170 -0
  55. aip_agents/agent/system_instruction_context.py +34 -0
  56. aip_agents/agent/system_instruction_context.pyi +13 -0
  57. aip_agents/clients/__init__.py +10 -0
  58. aip_agents/clients/__init__.pyi +4 -0
  59. aip_agents/clients/langflow/__init__.py +10 -0
  60. aip_agents/clients/langflow/__init__.pyi +4 -0
  61. aip_agents/clients/langflow/client.py +477 -0
  62. aip_agents/clients/langflow/client.pyi +140 -0
  63. aip_agents/clients/langflow/types.py +18 -0
  64. aip_agents/clients/langflow/types.pyi +7 -0
  65. aip_agents/constants.py +23 -0
  66. aip_agents/constants.pyi +7 -0
  67. aip_agents/credentials/manager.py +132 -0
  68. aip_agents/examples/__init__.py +5 -0
  69. aip_agents/examples/__init__.pyi +0 -0
  70. aip_agents/examples/compare_streaming_client.py +783 -0
  71. aip_agents/examples/compare_streaming_client.pyi +48 -0
  72. aip_agents/examples/compare_streaming_server.py +142 -0
  73. aip_agents/examples/compare_streaming_server.pyi +18 -0
  74. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  75. aip_agents/examples/hello_world_a2a_google_adk_client.pyi +9 -0
  76. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +9 -0
  78. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  79. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +9 -0
  80. aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
  81. aip_agents/examples/hello_world_a2a_google_adk_server.pyi +15 -0
  82. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  83. aip_agents/examples/hello_world_a2a_langchain_client.pyi +5 -0
  84. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  85. aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +5 -0
  86. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  87. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +5 -0
  88. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  89. aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +5 -0
  90. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  91. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +5 -0
  92. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
  93. aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +15 -0
  94. aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
  95. aip_agents/examples/hello_world_a2a_langchain_server.pyi +15 -0
  96. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
  97. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +15 -0
  98. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  99. aip_agents/examples/hello_world_a2a_langflow_client.pyi +9 -0
  100. aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
  101. aip_agents/examples/hello_world_a2a_langflow_server.pyi +14 -0
  102. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  103. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +5 -0
  104. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  105. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +5 -0
  106. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
  107. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +16 -0
  108. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  109. aip_agents/examples/hello_world_a2a_langgraph_client.pyi +9 -0
  110. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  111. aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +9 -0
  112. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  113. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +2 -0
  114. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  115. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +9 -0
  116. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  117. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +5 -0
  118. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  119. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +5 -0
  120. aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
  121. aip_agents/examples/hello_world_a2a_langgraph_server.pyi +14 -0
  122. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
  123. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +15 -0
  124. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
  125. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +15 -0
  126. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  127. aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +48 -0
  128. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  129. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +48 -0
  130. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
  131. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +45 -0
  132. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  133. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +5 -0
  134. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
  135. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +15 -0
  136. aip_agents/examples/hello_world_google_adk.py +41 -0
  137. aip_agents/examples/hello_world_google_adk.pyi +5 -0
  138. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  139. aip_agents/examples/hello_world_google_adk_mcp_http.pyi +5 -0
  140. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  141. aip_agents/examples/hello_world_google_adk_mcp_http_stream.pyi +5 -0
  142. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  143. aip_agents/examples/hello_world_google_adk_mcp_sse.pyi +5 -0
  144. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  145. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.pyi +5 -0
  146. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  147. aip_agents/examples/hello_world_google_adk_mcp_stdio.pyi +5 -0
  148. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  149. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.pyi +5 -0
  150. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  151. aip_agents/examples/hello_world_google_adk_stream.pyi +5 -0
  152. aip_agents/examples/hello_world_langchain.py +28 -0
  153. aip_agents/examples/hello_world_langchain.pyi +5 -0
  154. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  155. aip_agents/examples/hello_world_langchain_lm_invoker.pyi +2 -0
  156. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  157. aip_agents/examples/hello_world_langchain_mcp_http.pyi +5 -0
  158. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  159. aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +16 -0
  160. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  161. aip_agents/examples/hello_world_langchain_mcp_http_stream.pyi +5 -0
  162. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  163. aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +18 -0
  164. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  165. aip_agents/examples/hello_world_langchain_mcp_sse.pyi +5 -0
  166. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  167. aip_agents/examples/hello_world_langchain_mcp_sse_stream.pyi +5 -0
  168. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  169. aip_agents/examples/hello_world_langchain_mcp_stdio.pyi +5 -0
  170. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  171. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.pyi +5 -0
  172. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  173. aip_agents/examples/hello_world_langchain_stream.pyi +5 -0
  174. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  175. aip_agents/examples/hello_world_langchain_stream_lm_invoker.pyi +5 -0
  176. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  177. aip_agents/examples/hello_world_langflow_agent.pyi +35 -0
  178. aip_agents/examples/hello_world_langgraph.py +39 -0
  179. aip_agents/examples/hello_world_langgraph.pyi +5 -0
  180. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
  181. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +5 -0
  182. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  183. aip_agents/examples/hello_world_langgraph_mcp_http.pyi +5 -0
  184. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  185. aip_agents/examples/hello_world_langgraph_mcp_http_stream.pyi +5 -0
  186. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  187. aip_agents/examples/hello_world_langgraph_mcp_sse.pyi +5 -0
  188. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  189. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.pyi +5 -0
  190. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  191. aip_agents/examples/hello_world_langgraph_mcp_stdio.pyi +5 -0
  192. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  193. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.pyi +5 -0
  194. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  195. aip_agents/examples/hello_world_langgraph_stream.pyi +5 -0
  196. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  197. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.pyi +5 -0
  198. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  199. aip_agents/examples/hello_world_model_switch_cli.pyi +30 -0
  200. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  201. aip_agents/examples/hello_world_multi_agent_adk.pyi +6 -0
  202. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  203. aip_agents/examples/hello_world_multi_agent_langchain.pyi +5 -0
  204. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  205. aip_agents/examples/hello_world_multi_agent_langgraph.pyi +5 -0
  206. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  207. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi +5 -0
  208. aip_agents/examples/hello_world_pii_logger.py +21 -0
  209. aip_agents/examples/hello_world_pii_logger.pyi +5 -0
  210. aip_agents/examples/hello_world_ptc.py +49 -0
  211. aip_agents/examples/hello_world_ptc.pyi +5 -0
  212. aip_agents/examples/hello_world_sentry.py +133 -0
  213. aip_agents/examples/hello_world_sentry.pyi +21 -0
  214. aip_agents/examples/hello_world_step_limits.py +273 -0
  215. aip_agents/examples/hello_world_step_limits.pyi +17 -0
  216. aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
  217. aip_agents/examples/hello_world_stock_a2a_server.pyi +17 -0
  218. aip_agents/examples/hello_world_tool_output_client.py +55 -0
  219. aip_agents/examples/hello_world_tool_output_client.pyi +5 -0
  220. aip_agents/examples/hello_world_tool_output_server.py +114 -0
  221. aip_agents/examples/hello_world_tool_output_server.pyi +19 -0
  222. aip_agents/examples/hitl_demo.py +724 -0
  223. aip_agents/examples/hitl_demo.pyi +67 -0
  224. aip_agents/examples/mcp_configs/configs.py +63 -0
  225. aip_agents/examples/mcp_servers/common.py +76 -0
  226. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  227. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  228. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  229. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  230. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  231. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  232. aip_agents/examples/pii_demo_langgraph_client.pyi +5 -0
  233. aip_agents/examples/pii_demo_langgraph_server.py +126 -0
  234. aip_agents/examples/pii_demo_langgraph_server.pyi +20 -0
  235. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  236. aip_agents/examples/pii_demo_multi_agent_client.pyi +5 -0
  237. aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
  238. aip_agents/examples/pii_demo_multi_agent_server.pyi +40 -0
  239. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  240. aip_agents/examples/todolist_planning_a2a_langchain_client.pyi +5 -0
  241. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
  242. aip_agents/examples/todolist_planning_a2a_langgraph_server.pyi +19 -0
  243. aip_agents/examples/tools/__init__.py +27 -0
  244. aip_agents/examples/tools/__init__.pyi +9 -0
  245. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  246. aip_agents/examples/tools/adk_arithmetic_tools.pyi +24 -0
  247. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  248. aip_agents/examples/tools/adk_weather_tool.pyi +18 -0
  249. aip_agents/examples/tools/data_generator_tool.py +103 -0
  250. aip_agents/examples/tools/data_generator_tool.pyi +15 -0
  251. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  252. aip_agents/examples/tools/data_visualization_tool.pyi +19 -0
  253. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  254. aip_agents/examples/tools/image_artifact_tool.pyi +26 -0
  255. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  256. aip_agents/examples/tools/langchain_arithmetic_tools.pyi +17 -0
  257. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  258. aip_agents/examples/tools/langchain_currency_exchange_tool.pyi +20 -0
  259. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  260. aip_agents/examples/tools/langchain_graph_artifact_tool.pyi +25 -0
  261. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  262. aip_agents/examples/tools/langchain_weather_tool.pyi +19 -0
  263. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  264. aip_agents/examples/tools/langgraph_streaming_tool.pyi +43 -0
  265. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  266. aip_agents/examples/tools/mock_retrieval_tool.pyi +13 -0
  267. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  268. aip_agents/examples/tools/pii_demo_tools.pyi +54 -0
  269. aip_agents/examples/tools/random_chart_tool.py +142 -0
  270. aip_agents/examples/tools/random_chart_tool.pyi +20 -0
  271. aip_agents/examples/tools/serper_tool.py +202 -0
  272. aip_agents/examples/tools/serper_tool.pyi +16 -0
  273. aip_agents/examples/tools/stock_tools.py +82 -0
  274. aip_agents/examples/tools/stock_tools.pyi +36 -0
  275. aip_agents/examples/tools/table_generator_tool.py +167 -0
  276. aip_agents/examples/tools/table_generator_tool.pyi +22 -0
  277. aip_agents/examples/tools/time_tool.py +82 -0
  278. aip_agents/examples/tools/time_tool.pyi +15 -0
  279. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  280. aip_agents/examples/tools/weather_forecast_tool.pyi +14 -0
  281. aip_agents/executor/agent_executor.py +473 -0
  282. aip_agents/executor/base.py +48 -0
  283. aip_agents/guardrails/__init__.py +83 -0
  284. aip_agents/guardrails/__init__.pyi +6 -0
  285. aip_agents/guardrails/engines/__init__.py +69 -0
  286. aip_agents/guardrails/engines/__init__.pyi +4 -0
  287. aip_agents/guardrails/engines/base.py +90 -0
  288. aip_agents/guardrails/engines/base.pyi +61 -0
  289. aip_agents/guardrails/engines/nemo.py +101 -0
  290. aip_agents/guardrails/engines/nemo.pyi +46 -0
  291. aip_agents/guardrails/engines/phrase_matcher.py +113 -0
  292. aip_agents/guardrails/engines/phrase_matcher.pyi +48 -0
  293. aip_agents/guardrails/exceptions.py +39 -0
  294. aip_agents/guardrails/exceptions.pyi +23 -0
  295. aip_agents/guardrails/manager.py +163 -0
  296. aip_agents/guardrails/manager.pyi +42 -0
  297. aip_agents/guardrails/middleware.py +199 -0
  298. aip_agents/guardrails/middleware.pyi +87 -0
  299. aip_agents/guardrails/schemas.py +63 -0
  300. aip_agents/guardrails/schemas.pyi +43 -0
  301. aip_agents/guardrails/utils.py +45 -0
  302. aip_agents/guardrails/utils.pyi +19 -0
  303. aip_agents/mcp/__init__.py +1 -0
  304. aip_agents/mcp/__init__.pyi +0 -0
  305. aip_agents/mcp/client/__init__.py +14 -0
  306. aip_agents/mcp/client/__init__.pyi +5 -0
  307. aip_agents/mcp/client/base_mcp_client.py +369 -0
  308. aip_agents/mcp/client/base_mcp_client.pyi +148 -0
  309. aip_agents/mcp/client/connection_manager.py +228 -0
  310. aip_agents/mcp/client/connection_manager.pyi +51 -0
  311. aip_agents/mcp/client/google_adk/__init__.py +11 -0
  312. aip_agents/mcp/client/google_adk/__init__.pyi +3 -0
  313. aip_agents/mcp/client/google_adk/client.py +381 -0
  314. aip_agents/mcp/client/google_adk/client.pyi +75 -0
  315. aip_agents/mcp/client/langchain/__init__.py +11 -0
  316. aip_agents/mcp/client/langchain/__init__.pyi +3 -0
  317. aip_agents/mcp/client/langchain/client.py +265 -0
  318. aip_agents/mcp/client/langchain/client.pyi +48 -0
  319. aip_agents/mcp/client/persistent_session.py +612 -0
  320. aip_agents/mcp/client/persistent_session.pyi +122 -0
  321. aip_agents/mcp/client/session_pool.py +351 -0
  322. aip_agents/mcp/client/session_pool.pyi +101 -0
  323. aip_agents/mcp/client/transports.py +263 -0
  324. aip_agents/mcp/client/transports.pyi +132 -0
  325. aip_agents/mcp/utils/__init__.py +7 -0
  326. aip_agents/mcp/utils/__init__.pyi +0 -0
  327. aip_agents/mcp/utils/config_validator.py +139 -0
  328. aip_agents/mcp/utils/config_validator.pyi +82 -0
  329. aip_agents/memory/__init__.py +14 -0
  330. aip_agents/memory/__init__.pyi +5 -0
  331. aip_agents/memory/adapters/__init__.py +10 -0
  332. aip_agents/memory/adapters/__init__.pyi +4 -0
  333. aip_agents/memory/adapters/base_adapter.py +811 -0
  334. aip_agents/memory/adapters/base_adapter.pyi +176 -0
  335. aip_agents/memory/adapters/mem0.py +84 -0
  336. aip_agents/memory/adapters/mem0.pyi +22 -0
  337. aip_agents/memory/base.py +84 -0
  338. aip_agents/memory/base.pyi +60 -0
  339. aip_agents/memory/constants.py +49 -0
  340. aip_agents/memory/constants.pyi +25 -0
  341. aip_agents/memory/factory.py +86 -0
  342. aip_agents/memory/factory.pyi +24 -0
  343. aip_agents/memory/guidance.py +20 -0
  344. aip_agents/memory/guidance.pyi +3 -0
  345. aip_agents/memory/simple_memory.py +47 -0
  346. aip_agents/memory/simple_memory.pyi +23 -0
  347. aip_agents/middleware/__init__.py +17 -0
  348. aip_agents/middleware/__init__.pyi +5 -0
  349. aip_agents/middleware/base.py +96 -0
  350. aip_agents/middleware/base.pyi +75 -0
  351. aip_agents/middleware/manager.py +150 -0
  352. aip_agents/middleware/manager.pyi +84 -0
  353. aip_agents/middleware/todolist.py +274 -0
  354. aip_agents/middleware/todolist.pyi +125 -0
  355. aip_agents/ptc/__init__.py +48 -0
  356. aip_agents/ptc/__init__.pyi +10 -0
  357. aip_agents/ptc/doc_gen.py +122 -0
  358. aip_agents/ptc/doc_gen.pyi +40 -0
  359. aip_agents/ptc/exceptions.py +39 -0
  360. aip_agents/ptc/exceptions.pyi +22 -0
  361. aip_agents/ptc/executor.py +143 -0
  362. aip_agents/ptc/executor.pyi +73 -0
  363. aip_agents/ptc/mcp/__init__.py +45 -0
  364. aip_agents/ptc/mcp/__init__.pyi +7 -0
  365. aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
  366. aip_agents/ptc/mcp/sandbox_bridge.pyi +47 -0
  367. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  368. aip_agents/ptc/mcp/templates/__init__.pyi +0 -0
  369. aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
  370. aip_agents/ptc/naming.py +184 -0
  371. aip_agents/ptc/naming.pyi +76 -0
  372. aip_agents/ptc/payload.py +26 -0
  373. aip_agents/ptc/payload.pyi +15 -0
  374. aip_agents/ptc/prompt_builder.py +571 -0
  375. aip_agents/ptc/prompt_builder.pyi +55 -0
  376. aip_agents/ptc/ptc_helper.py +16 -0
  377. aip_agents/ptc/ptc_helper.pyi +1 -0
  378. aip_agents/ptc/sandbox_bridge.py +58 -0
  379. aip_agents/ptc/sandbox_bridge.pyi +25 -0
  380. aip_agents/ptc/template_utils.py +33 -0
  381. aip_agents/ptc/template_utils.pyi +13 -0
  382. aip_agents/ptc/templates/__init__.py +1 -0
  383. aip_agents/ptc/templates/__init__.pyi +0 -0
  384. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  385. aip_agents/sandbox/__init__.py +43 -0
  386. aip_agents/sandbox/__init__.pyi +5 -0
  387. aip_agents/sandbox/defaults.py +9 -0
  388. aip_agents/sandbox/defaults.pyi +2 -0
  389. aip_agents/sandbox/e2b_runtime.py +267 -0
  390. aip_agents/sandbox/e2b_runtime.pyi +51 -0
  391. aip_agents/sandbox/template_builder.py +131 -0
  392. aip_agents/sandbox/template_builder.pyi +36 -0
  393. aip_agents/sandbox/types.py +24 -0
  394. aip_agents/sandbox/types.pyi +14 -0
  395. aip_agents/sandbox/validation.py +50 -0
  396. aip_agents/sandbox/validation.pyi +20 -0
  397. aip_agents/schema/__init__.py +69 -0
  398. aip_agents/schema/__init__.pyi +9 -0
  399. aip_agents/schema/a2a.py +56 -0
  400. aip_agents/schema/a2a.pyi +40 -0
  401. aip_agents/schema/agent.py +111 -0
  402. aip_agents/schema/agent.pyi +65 -0
  403. aip_agents/schema/hitl.py +157 -0
  404. aip_agents/schema/hitl.pyi +89 -0
  405. aip_agents/schema/langgraph.py +37 -0
  406. aip_agents/schema/langgraph.pyi +28 -0
  407. aip_agents/schema/model_id.py +97 -0
  408. aip_agents/schema/model_id.pyi +54 -0
  409. aip_agents/schema/step_limit.py +108 -0
  410. aip_agents/schema/step_limit.pyi +63 -0
  411. aip_agents/schema/storage.py +40 -0
  412. aip_agents/schema/storage.pyi +21 -0
  413. aip_agents/sentry/__init__.py +11 -0
  414. aip_agents/sentry/__init__.pyi +3 -0
  415. aip_agents/sentry/sentry.py +151 -0
  416. aip_agents/sentry/sentry.pyi +48 -0
  417. aip_agents/storage/__init__.py +41 -0
  418. aip_agents/storage/__init__.pyi +8 -0
  419. aip_agents/storage/base.py +85 -0
  420. aip_agents/storage/base.pyi +58 -0
  421. aip_agents/storage/clients/__init__.py +12 -0
  422. aip_agents/storage/clients/__init__.pyi +3 -0
  423. aip_agents/storage/clients/minio_client.py +318 -0
  424. aip_agents/storage/clients/minio_client.pyi +137 -0
  425. aip_agents/storage/config.py +62 -0
  426. aip_agents/storage/config.pyi +29 -0
  427. aip_agents/storage/providers/__init__.py +15 -0
  428. aip_agents/storage/providers/__init__.pyi +5 -0
  429. aip_agents/storage/providers/base.py +106 -0
  430. aip_agents/storage/providers/base.pyi +88 -0
  431. aip_agents/storage/providers/memory.py +114 -0
  432. aip_agents/storage/providers/memory.pyi +79 -0
  433. aip_agents/storage/providers/object_storage.py +214 -0
  434. aip_agents/storage/providers/object_storage.pyi +98 -0
  435. aip_agents/tools/__init__.py +64 -0
  436. aip_agents/tools/__init__.pyi +11 -0
  437. aip_agents/tools/browser_use/__init__.py +82 -0
  438. aip_agents/tools/browser_use/__init__.pyi +14 -0
  439. aip_agents/tools/browser_use/action_parser.py +103 -0
  440. aip_agents/tools/browser_use/action_parser.pyi +18 -0
  441. aip_agents/tools/browser_use/browser_use_tool.py +1120 -0
  442. aip_agents/tools/browser_use/browser_use_tool.pyi +50 -0
  443. aip_agents/tools/browser_use/llm_config.py +120 -0
  444. aip_agents/tools/browser_use/llm_config.pyi +52 -0
  445. aip_agents/tools/browser_use/minio_storage.py +198 -0
  446. aip_agents/tools/browser_use/minio_storage.pyi +109 -0
  447. aip_agents/tools/browser_use/schemas.py +119 -0
  448. aip_agents/tools/browser_use/schemas.pyi +32 -0
  449. aip_agents/tools/browser_use/session.py +76 -0
  450. aip_agents/tools/browser_use/session.pyi +4 -0
  451. aip_agents/tools/browser_use/session_errors.py +132 -0
  452. aip_agents/tools/browser_use/session_errors.pyi +53 -0
  453. aip_agents/tools/browser_use/steel_session_recording.py +317 -0
  454. aip_agents/tools/browser_use/steel_session_recording.pyi +63 -0
  455. aip_agents/tools/browser_use/streaming.py +815 -0
  456. aip_agents/tools/browser_use/streaming.pyi +81 -0
  457. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  458. aip_agents/tools/browser_use/structured_data_parser.pyi +86 -0
  459. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  460. aip_agents/tools/browser_use/structured_data_recovery.pyi +43 -0
  461. aip_agents/tools/browser_use/types.py +78 -0
  462. aip_agents/tools/browser_use/types.pyi +45 -0
  463. aip_agents/tools/code_sandbox/__init__.py +26 -0
  464. aip_agents/tools/code_sandbox/__init__.pyi +3 -0
  465. aip_agents/tools/code_sandbox/constant.py +13 -0
  466. aip_agents/tools/code_sandbox/constant.pyi +4 -0
  467. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +306 -0
  468. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +102 -0
  469. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
  470. aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +29 -0
  471. aip_agents/tools/constants.py +177 -0
  472. aip_agents/tools/constants.pyi +138 -0
  473. aip_agents/tools/date_range_tool.py +554 -0
  474. aip_agents/tools/date_range_tool.pyi +21 -0
  475. aip_agents/tools/document_loader/__init__.py +44 -0
  476. aip_agents/tools/document_loader/__init__.pyi +7 -0
  477. aip_agents/tools/document_loader/base_reader.py +302 -0
  478. aip_agents/tools/document_loader/base_reader.pyi +75 -0
  479. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  480. aip_agents/tools/document_loader/docx_reader_tool.pyi +10 -0
  481. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  482. aip_agents/tools/document_loader/excel_reader_tool.pyi +26 -0
  483. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  484. aip_agents/tools/document_loader/pdf_reader_tool.pyi +11 -0
  485. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  486. aip_agents/tools/document_loader/pdf_splitter.pyi +18 -0
  487. aip_agents/tools/execute_ptc_code.py +308 -0
  488. aip_agents/tools/execute_ptc_code.pyi +90 -0
  489. aip_agents/tools/gl_connector/__init__.py +5 -0
  490. aip_agents/tools/gl_connector/__init__.pyi +3 -0
  491. aip_agents/tools/gl_connector/tool.py +383 -0
  492. aip_agents/tools/gl_connector/tool.pyi +74 -0
  493. aip_agents/tools/gl_connector_tools.py +119 -0
  494. aip_agents/tools/gl_connector_tools.pyi +39 -0
  495. aip_agents/tools/memory_search/__init__.py +29 -0
  496. aip_agents/tools/memory_search/__init__.pyi +5 -0
  497. aip_agents/tools/memory_search/base.py +200 -0
  498. aip_agents/tools/memory_search/base.pyi +69 -0
  499. aip_agents/tools/memory_search/mem0.py +365 -0
  500. aip_agents/tools/memory_search/mem0.pyi +29 -0
  501. aip_agents/tools/memory_search/schema.py +81 -0
  502. aip_agents/tools/memory_search/schema.pyi +25 -0
  503. aip_agents/tools/memory_search_tool.py +34 -0
  504. aip_agents/tools/memory_search_tool.pyi +3 -0
  505. aip_agents/tools/time_tool.py +117 -0
  506. aip_agents/tools/time_tool.pyi +16 -0
  507. aip_agents/tools/tool_config_injector.py +300 -0
  508. aip_agents/tools/tool_config_injector.pyi +26 -0
  509. aip_agents/tools/web_search/__init__.py +15 -0
  510. aip_agents/tools/web_search/__init__.pyi +3 -0
  511. aip_agents/tools/web_search/serper_tool.py +187 -0
  512. aip_agents/tools/web_search/serper_tool.pyi +19 -0
  513. aip_agents/types/__init__.py +70 -0
  514. aip_agents/types/__init__.pyi +36 -0
  515. aip_agents/types/a2a_events.py +13 -0
  516. aip_agents/types/a2a_events.pyi +3 -0
  517. aip_agents/utils/__init__.py +79 -0
  518. aip_agents/utils/__init__.pyi +11 -0
  519. aip_agents/utils/a2a_connector.py +1757 -0
  520. aip_agents/utils/a2a_connector.pyi +146 -0
  521. aip_agents/utils/artifact_helpers.py +502 -0
  522. aip_agents/utils/artifact_helpers.pyi +203 -0
  523. aip_agents/utils/constants.py +22 -0
  524. aip_agents/utils/constants.pyi +10 -0
  525. aip_agents/utils/datetime/__init__.py +34 -0
  526. aip_agents/utils/datetime/__init__.pyi +4 -0
  527. aip_agents/utils/datetime/normalization.py +231 -0
  528. aip_agents/utils/datetime/normalization.pyi +95 -0
  529. aip_agents/utils/datetime/timezone.py +206 -0
  530. aip_agents/utils/datetime/timezone.pyi +48 -0
  531. aip_agents/utils/env_loader.py +27 -0
  532. aip_agents/utils/env_loader.pyi +10 -0
  533. aip_agents/utils/event_handler_registry.py +58 -0
  534. aip_agents/utils/event_handler_registry.pyi +23 -0
  535. aip_agents/utils/file_prompt_utils.py +176 -0
  536. aip_agents/utils/file_prompt_utils.pyi +21 -0
  537. aip_agents/utils/final_response_builder.py +211 -0
  538. aip_agents/utils/final_response_builder.pyi +34 -0
  539. aip_agents/utils/formatter_llm_client.py +231 -0
  540. aip_agents/utils/formatter_llm_client.pyi +71 -0
  541. aip_agents/utils/langgraph/__init__.py +19 -0
  542. aip_agents/utils/langgraph/__init__.pyi +3 -0
  543. aip_agents/utils/langgraph/converter.py +128 -0
  544. aip_agents/utils/langgraph/converter.pyi +49 -0
  545. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  546. aip_agents/utils/langgraph/tool_managers/__init__.pyi +5 -0
  547. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  548. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +35 -0
  549. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  550. aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +48 -0
  551. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1096 -0
  552. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +56 -0
  553. aip_agents/utils/langgraph/tool_output_management.py +1047 -0
  554. aip_agents/utils/langgraph/tool_output_management.pyi +329 -0
  555. aip_agents/utils/logger.py +195 -0
  556. aip_agents/utils/logger.pyi +60 -0
  557. aip_agents/utils/metadata/__init__.py +27 -0
  558. aip_agents/utils/metadata/__init__.pyi +5 -0
  559. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  560. aip_agents/utils/metadata/activity_metadata_helper.pyi +25 -0
  561. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  562. aip_agents/utils/metadata/activity_narrative/__init__.pyi +7 -0
  563. aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
  564. aip_agents/utils/metadata/activity_narrative/builder.pyi +35 -0
  565. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  566. aip_agents/utils/metadata/activity_narrative/constants.pyi +10 -0
  567. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  568. aip_agents/utils/metadata/activity_narrative/context.pyi +32 -0
  569. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  570. aip_agents/utils/metadata/activity_narrative/formatters.pyi +48 -0
  571. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  572. aip_agents/utils/metadata/activity_narrative/utils.pyi +12 -0
  573. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  574. aip_agents/utils/metadata/schemas/__init__.pyi +4 -0
  575. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  576. aip_agents/utils/metadata/schemas/activity_schema.pyi +18 -0
  577. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  578. aip_agents/utils/metadata/schemas/thinking_schema.pyi +20 -0
  579. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  580. aip_agents/utils/metadata/thinking_metadata_helper.pyi +4 -0
  581. aip_agents/utils/metadata_helper.py +358 -0
  582. aip_agents/utils/metadata_helper.pyi +117 -0
  583. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  584. aip_agents/utils/name_preprocessor/__init__.pyi +6 -0
  585. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  586. aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +52 -0
  587. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  588. aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +38 -0
  589. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  590. aip_agents/utils/name_preprocessor/name_preprocessor.pyi +41 -0
  591. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  592. aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +34 -0
  593. aip_agents/utils/pii/__init__.py +25 -0
  594. aip_agents/utils/pii/__init__.pyi +5 -0
  595. aip_agents/utils/pii/pii_handler.py +397 -0
  596. aip_agents/utils/pii/pii_handler.pyi +96 -0
  597. aip_agents/utils/pii/pii_helper.py +207 -0
  598. aip_agents/utils/pii/pii_helper.pyi +78 -0
  599. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  600. aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +73 -0
  601. aip_agents/utils/reference_helper.py +273 -0
  602. aip_agents/utils/reference_helper.pyi +81 -0
  603. aip_agents/utils/sse_chunk_transformer.py +831 -0
  604. aip_agents/utils/sse_chunk_transformer.pyi +166 -0
  605. aip_agents/utils/step_limit_manager.py +265 -0
  606. aip_agents/utils/step_limit_manager.pyi +112 -0
  607. aip_agents/utils/token_usage_helper.py +156 -0
  608. aip_agents/utils/token_usage_helper.pyi +60 -0
  609. aip_agents_binary-0.6.4.dist-info/METADATA +673 -0
  610. aip_agents_binary-0.6.4.dist-info/RECORD +612 -0
  611. aip_agents_binary-0.6.4.dist-info/WHEEL +5 -0
  612. aip_agents_binary-0.6.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,612 @@
1
+ """Persistent MCP Session wrapper for connection reuse.
2
+
3
+ This module implements persistent MCP sessions that reuse connections across
4
+ multiple tool calls, avoiding the session recreation overhead.
5
+
6
+ Authors:
7
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
8
+ """
9
+
10
+ import asyncio
11
+ from collections.abc import Awaitable, Callable
12
+ from typing import Any
13
+
14
+ from gllm_tools.mcp.client.config import MCPConfiguration
15
+ from mcp import ClientSession
16
+ from mcp.types import CallToolResult, Tool
17
+
18
+ from aip_agents.mcp.client.connection_manager import MCPConnectionManager
19
+ from aip_agents.mcp.utils.config_validator import validate_allowed_tools_list
20
+ from aip_agents.utils.logger import get_logger
21
+
22
+ logger = get_logger(__name__)
23
+
24
+
25
+ class PersistentMCPSession:
26
+ """Persistent MCP session that reuses connections.
27
+
28
+ This session wrapper manages the connection lifecycle and caches tools
29
+ to avoid repeated initialization overhead. It provides automatic reconnection
30
+ and thread-safe operations.
31
+
32
+ Tool Filtering:
33
+ When allowed_tools is configured, tools are filtered inline during list_tools()
34
+ and permission checked in call_tool() using set lookup.
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ server_name: str,
40
+ config: MCPConfiguration,
41
+ allowed_tools: list[str] | None = None,
42
+ ):
43
+ """Initialize persistent session.
44
+
45
+ Args:
46
+ server_name: Name of the MCP server
47
+ config: MCP server configuration
48
+ allowed_tools: Optional list of tool names to allow. None or empty means all tools allowed.
49
+ """
50
+ self.server_name = server_name
51
+ self.config = config
52
+ self.connection_manager = MCPConnectionManager(server_name, config)
53
+ self.client_session: ClientSession | None = None
54
+ self.tools: list[Tool] = []
55
+
56
+ # Keep only the set for fast permission checks
57
+ validated_allowed = validate_allowed_tools_list(allowed_tools, "'allowed_tools' parameter")
58
+ self._allowed_tools_set: set[str] | None = set(validated_allowed) if validated_allowed else None
59
+ self._warned_unknown_tools: set[str] = set()
60
+ self._filtered_tools_cache: list[Tool] | None = None # Cache for filtered tools
61
+
62
+ # Log allowed tools configuration
63
+ if self._allowed_tools_set:
64
+ logger.info(
65
+ f"Session for '{server_name}' configured with {len(self._allowed_tools_set)} allowed tool(s): "
66
+ f"{', '.join(sorted(self._allowed_tools_set))}"
67
+ )
68
+ else:
69
+ logger.debug(f"Session for '{server_name}' allows all tools (no restriction)")
70
+
71
+ self._initialized = False
72
+ self._lock = asyncio.Lock()
73
+ self._owner_task: asyncio.Task | None = None
74
+ self._owner_ready: asyncio.Event = asyncio.Event()
75
+ self._owner_exception: Exception | None = None
76
+ self._timeout = float(config.get("timeout", 30.0))
77
+ self._request_queue: asyncio.Queue[
78
+ tuple[Callable[..., Awaitable[Any]], tuple[Any, ...], asyncio.Future, bool]
79
+ ] = asyncio.Queue()
80
+
81
+ async def initialize(self) -> None:
82
+ """Initialize session once and cache tools.
83
+
84
+ This method is idempotent and can be called multiple times safely.
85
+
86
+ Raises:
87
+ Exception: If session initialization fails
88
+ """
89
+ if self._initialized:
90
+ return
91
+
92
+ async with self._lock:
93
+ # Double-check pattern
94
+ if self._initialized:
95
+ return
96
+ if self._owner_task is None or self._owner_task.done():
97
+ self._owner_ready = asyncio.Event()
98
+ self._owner_exception = None
99
+ self._owner_task = asyncio.create_task(self._owner_loop())
100
+
101
+ try:
102
+ await asyncio.wait_for(self._owner_ready.wait(), timeout=self._timeout)
103
+ except asyncio.CancelledError:
104
+ if self._owner_task and not self._owner_task.done():
105
+ self._owner_task.cancel()
106
+ raise
107
+ except TimeoutError as e:
108
+ logger.error(f"Initialization timed out for {self.server_name} after {self._timeout}s")
109
+ if self._owner_task and not self._owner_task.done():
110
+ self._owner_task.cancel()
111
+ self._owner_exception = ConnectionError(
112
+ f"Initialization timed out for {self.server_name} after {self._timeout}s"
113
+ )
114
+ self._owner_ready.set()
115
+ raise self._owner_exception from e
116
+ if self._owner_exception:
117
+ raise self._owner_exception
118
+
119
+ async def call_tool(self, name: str, arguments: dict[str, Any]) -> CallToolResult:
120
+ """Call MCP tool using persistent session.
121
+
122
+ Args:
123
+ name (str): Tool name
124
+ arguments (dict[str, Any]): Tool arguments
125
+
126
+ Returns:
127
+ CallToolResult: Tool call result
128
+
129
+ Raises:
130
+ Exception: If tool call fails
131
+ """
132
+ return await self._run_in_owner(self._call_tool_impl, name, arguments)
133
+
134
+ async def _call_tool_impl(self, name: str, arguments: dict[str, Any]) -> CallToolResult:
135
+ """Call MCP tool using the owner task.
136
+
137
+ Args:
138
+ name: Tool name.
139
+ arguments: Tool arguments.
140
+
141
+ Returns:
142
+ CallToolResult: Tool call result.
143
+ """
144
+ await self._ensure_connected_impl()
145
+
146
+ if self._allowed_tools_set and name not in self._allowed_tools_set:
147
+ allowed_display = ", ".join(sorted(self._allowed_tools_set))
148
+ error_msg = (
149
+ f"Tool '{name}' is not allowed on server '{self.server_name}' (allowed tools: {allowed_display})"
150
+ )
151
+ logger.warning(f"[{self.server_name}] Tool '{name}' blocked: not in allowed_tools ({allowed_display})")
152
+ raise PermissionError(error_msg)
153
+
154
+ try:
155
+ logger.debug(f"Calling tool '{name}' on {self.server_name} with args: {arguments}")
156
+ result = await self.client_session.call_tool(name, arguments)
157
+ logger.debug(f"Tool '{name}' completed successfully")
158
+ return result
159
+ except Exception as e:
160
+ self._handle_connection_error(e, f"Tool call '{name}'")
161
+
162
+ async def read_resource(self, uri: str) -> Any:
163
+ """Read an MCP resource using persistent session.
164
+
165
+ Args:
166
+ uri (str): The URI of the resource to read
167
+
168
+ Returns:
169
+ Any: The resource content
170
+
171
+ Raises:
172
+ Exception: If resource reading fails
173
+ """
174
+ return await self._run_in_owner(self._execute_read_resource_impl, uri)
175
+
176
+ async def _execute_read_resource_impl(self, uri: str) -> Any:
177
+ """Execute the reading of an MCP resource.
178
+
179
+ Args:
180
+ uri (str): The URI of the resource to read
181
+
182
+ Returns:
183
+ Any: The resource content
184
+
185
+ Raises:
186
+ Exception: If resource reading fails
187
+ """
188
+ await self._ensure_connected_impl()
189
+ try:
190
+ logger.debug(f"Reading resource '{uri}' on {self.server_name}")
191
+ result = await self.client_session.read_resource(uri)
192
+ logger.debug(f"Resource '{uri}' read successfully")
193
+ return result
194
+ except Exception as e:
195
+ self._handle_connection_error(e, f"Reading resource '{uri}'")
196
+
197
+ async def list_tools(self) -> list[Tool]:
198
+ """Get cached tools list with allowed tools filtering applied.
199
+
200
+ Returns:
201
+ list[Tool]: a copy of list of available tools, filtered to only allowed tools if configured
202
+ """
203
+ return await self._run_in_owner(self._list_tools_impl)
204
+
205
+ async def _list_tools_impl(self) -> list[Tool]:
206
+ """Return the cached tools list from the owner task.
207
+
208
+ Returns:
209
+ list[Tool]: Filtered tool list if allowed tools are configured, otherwise all tools.
210
+ """
211
+ await self._ensure_connected_impl()
212
+
213
+ if not self._allowed_tools_set:
214
+ return list(self.tools)
215
+
216
+ if self._filtered_tools_cache is None:
217
+ self._filtered_tools_cache = [tool for tool in self.tools if tool.name in self._allowed_tools_set]
218
+
219
+ return list(self._filtered_tools_cache)
220
+
221
+ def get_tools_count(self) -> int:
222
+ """Get count of allowed tools.
223
+
224
+ Returns:
225
+ Count of allowed tools
226
+ """
227
+ if not self._allowed_tools_set:
228
+ return len(self.tools)
229
+
230
+ if self._filtered_tools_cache is not None:
231
+ return len(self._filtered_tools_cache)
232
+
233
+ return sum(1 for tool in self.tools if tool.name in self._allowed_tools_set)
234
+
235
+ async def ensure_connected(self) -> None:
236
+ """Ensure connection is healthy, reconnect if needed.
237
+
238
+ This method provides automatic reconnection capability.
239
+
240
+ Raises:
241
+ Exception: If reconnection fails
242
+ """
243
+ await self._run_in_owner(self._ensure_connected_impl)
244
+
245
+ async def _ensure_connected_impl(self) -> None:
246
+ """Ensure the session is connected, reconnecting if needed."""
247
+ if not self._initialized or not self.connection_manager.is_connected:
248
+ logger.info(f"Reconnecting session for {self.server_name}")
249
+ await self._initialize_impl()
250
+
251
+ def _handle_connection_error(self, e: Exception, operation: str) -> None:
252
+ """Handle connection-related errors with logging and reconnection marking.
253
+
254
+ Args:
255
+ e (Exception): The exception that occurred
256
+ operation (str): The operation that failed
257
+ """
258
+ logger.error(f"{operation} failed on {self.server_name}: {e}")
259
+ if not self.connection_manager.is_connected:
260
+ logger.info(f"Connection lost for {self.server_name}, marking for reconnection")
261
+ self._initialized = False
262
+ raise ConnectionError(f"{operation} failed on {self.server_name}: {str(e)}") from e
263
+
264
+ async def disconnect(self) -> None:
265
+ """Disconnect session gracefully.
266
+
267
+ This method cleans up all resources and connections.
268
+ Always succeeds, even if the session was already in an error state.
269
+ """
270
+ logger.info(f"Disconnecting session for {self.server_name}")
271
+
272
+ if self._owner_task is None or self._owner_task.done():
273
+ await self._disconnect_impl()
274
+ return
275
+
276
+ try:
277
+ await self._run_in_owner(self._disconnect_impl, shutdown=True, ensure_initialized=False)
278
+ except ConnectionError:
279
+ # Owner task already failed; just clean up directly
280
+ logger.debug(f"Owner task already failed for {self.server_name}, cleaning up directly")
281
+ await self._disconnect_impl()
282
+ finally:
283
+ await self._await_owner_shutdown()
284
+
285
+ async def _await_owner_shutdown(self) -> None:
286
+ """Wait for the owner task to exit, cancelling on timeout."""
287
+ if not self._owner_task:
288
+ return
289
+
290
+ owner_task = self._owner_task
291
+ try:
292
+ await asyncio.wait_for(owner_task, timeout=self._timeout)
293
+ except TimeoutError:
294
+ logger.warning(f"Owner task for {self.server_name} did not exit within {self._timeout}s, cancelling")
295
+ owner_task.cancel()
296
+ try:
297
+ await owner_task
298
+ except (asyncio.CancelledError, Exception):
299
+ pass
300
+ except asyncio.CancelledError:
301
+ pass
302
+ except Exception:
303
+ pass
304
+ finally:
305
+ self._owner_task = None
306
+
307
+ async def _cleanup_on_error(self) -> None:
308
+ """Internal cleanup method for error scenarios."""
309
+ try:
310
+ if self.client_session:
311
+ await self.client_session.__aexit__(None, None, None)
312
+ self.client_session = None
313
+ except Exception as e:
314
+ logger.debug(f"Ignored cleanup error for client_session: {e}")
315
+
316
+ try:
317
+ await self.connection_manager.stop()
318
+ except Exception as e:
319
+ logger.debug(f"Ignored cleanup error for connection_manager: {e}")
320
+
321
+ self._initialized = False
322
+ self.tools.clear()
323
+ self._filtered_tools_cache = None # Clear cache on error cleanup
324
+
325
+ @property
326
+ def is_initialized(self) -> bool:
327
+ """Check if session is initialized.
328
+
329
+ Returns:
330
+ bool: True if initialized and connected, False otherwise
331
+ """
332
+ return self._initialized and self.connection_manager.is_connected
333
+
334
+ @property
335
+ def allowed_tools(self) -> list[str] | None:
336
+ """Return the configured allowed tools, sorted if present.
337
+
338
+ Returns:
339
+ Sorted list of allowed tool names, or None if unrestricted.
340
+ """
341
+ if not self._allowed_tools_set:
342
+ return None
343
+ return sorted(self._allowed_tools_set)
344
+
345
+ def update_allowed_tools(self, allowed_tools: list[str] | None) -> bool:
346
+ """Update the list of allowed tools for this session.
347
+
348
+ Args:
349
+ allowed_tools: New list of allowed tool names or None for no restriction.
350
+ None and empty list both mean 'no restrictions, allow all tools'.
351
+
352
+ Returns:
353
+ bool: True if the configuration changed, False otherwise.
354
+
355
+ Raises:
356
+ ValueError: If allowed_tools contains invalid entries.
357
+ """
358
+ # Validate first - ensures consistent error handling regardless of current state
359
+ validated = validate_allowed_tools_list(allowed_tools, f"Server '{self.server_name}'")
360
+ new_set = set(validated) if validated else None
361
+
362
+ # Check if actually changed
363
+ if self._allowed_tools_set == new_set:
364
+ logger.debug(f"Allowed tools unchanged for {self.server_name}")
365
+ return False
366
+
367
+ # Log and update
368
+ old_display = sorted(self._allowed_tools_set) if self._allowed_tools_set else None
369
+ self._allowed_tools_set = new_set
370
+ self._filtered_tools_cache = None # Invalidate cache when allowed_tools changes
371
+ logger.debug(f"Updated allowed_tools for {self.server_name}: {old_display} -> {validated}")
372
+
373
+ # Warn immediately if we already have cached tools
374
+ if self.tools:
375
+ self._warn_on_unknown_allowed_tools(validated, self.tools)
376
+ return True
377
+
378
+ def _warn_on_unknown_allowed_tools(self, allowed_tools: list[str] | None, available_tools: list[Tool]) -> None:
379
+ """Emit warnings for allowed tool names that are not exposed by the server.
380
+
381
+ Warnings are deduplicated - each unknown tool is only warned about once per session.
382
+
383
+ Args:
384
+ allowed_tools: Configured whitelist of tool names, or None for no restriction.
385
+ available_tools: Tools currently exposed by the server.
386
+ """
387
+ if not allowed_tools or not available_tools:
388
+ return
389
+
390
+ available_names = {tool.name for tool in available_tools}
391
+ unknown = [tool_name for tool_name in allowed_tools if tool_name not in available_names]
392
+ for tool_name in unknown:
393
+ # Only warn once per tool name
394
+ if tool_name not in self._warned_unknown_tools:
395
+ self._warned_unknown_tools.add(tool_name)
396
+ logger.warning(
397
+ f"[{self.server_name}] Tool '{tool_name}' not found in available tools but specified in allowed_tools"
398
+ )
399
+
400
+ async def _owner_loop(self) -> None:
401
+ """Run the owner task loop and process queued requests.
402
+
403
+ Returns:
404
+ None
405
+ """
406
+ shutdown_requested = False
407
+ try:
408
+ shutdown_requested = await self._initialize_owner()
409
+ if shutdown_requested:
410
+ return
411
+
412
+ while True:
413
+ func, args, future, shutdown = await self._request_queue.get()
414
+ if await self._process_owner_request(func, args, future):
415
+ continue
416
+ if shutdown:
417
+ shutdown_requested = True
418
+ break
419
+ finally:
420
+ # Drain and cancel any pending requests to avoid hanging callers
421
+ await self._drain_pending_requests()
422
+ if not shutdown_requested and (
423
+ self._initialized or self.client_session or self.connection_manager.transport_type is not None
424
+ ):
425
+ await self._cleanup_on_error()
426
+ self._owner_task = None
427
+
428
+ async def _drain_pending_requests(self) -> None:
429
+ """Cancel all pending requests in the queue.
430
+
431
+ This prevents callers from hanging when the owner loop exits unexpectedly.
432
+ """
433
+ error = ConnectionError(f"Session for {self.server_name} is shutting down")
434
+ while not self._request_queue.empty():
435
+ try:
436
+ _, _, future, _ = self._request_queue.get_nowait()
437
+ if not future.done():
438
+ future.set_exception(error)
439
+ except asyncio.QueueEmpty:
440
+ break
441
+
442
+ async def _initialize_owner(self) -> bool:
443
+ """Initialize the owner task and signal readiness.
444
+
445
+ Returns:
446
+ bool: True when initialization fails and the loop should stop.
447
+ """
448
+ try:
449
+ await self._initialize_impl()
450
+ except Exception as e:
451
+ self._owner_exception = e
452
+ self._owner_ready.set()
453
+ return True
454
+
455
+ self._owner_ready.set()
456
+ return False
457
+
458
+ async def _process_owner_request(
459
+ self,
460
+ func: Callable[..., Awaitable[Any]],
461
+ args: tuple[Any, ...],
462
+ future: asyncio.Future,
463
+ ) -> bool:
464
+ """Process a single queued request.
465
+
466
+ Args:
467
+ func: Coroutine function to execute.
468
+ args: Positional arguments for the function.
469
+ future: Future to resolve with the result or exception.
470
+
471
+ Returns:
472
+ bool: True if the request was skipped due to cancellation.
473
+ """
474
+ if future.cancelled():
475
+ return True
476
+
477
+ try:
478
+ result = await func(*args)
479
+ except asyncio.CancelledError as e:
480
+ # Owner task was cancelled - resolve future to prevent hanging caller
481
+ if not future.cancelled():
482
+ future.set_exception(e)
483
+ raise
484
+ except Exception as e:
485
+ if not future.cancelled():
486
+ future.set_exception(e)
487
+ else:
488
+ if not future.cancelled():
489
+ future.set_result(result)
490
+ return False
491
+
492
+ async def _run_in_owner(
493
+ self,
494
+ func: Callable[..., Awaitable[Any]],
495
+ *args: Any,
496
+ shutdown: bool = False,
497
+ ensure_initialized: bool = True,
498
+ ) -> Any:
499
+ """Execute a coroutine on the owner task.
500
+
501
+ Args:
502
+ func: Coroutine function to execute.
503
+ *args: Positional arguments to pass to func.
504
+ shutdown: Whether this request should shut down the owner loop.
505
+ ensure_initialized: Whether to initialize the owner task if needed.
506
+
507
+ Returns:
508
+ Any: The result of the coroutine call.
509
+
510
+ Raises:
511
+ ConnectionError: If the owner task died or session is shutting down.
512
+ """
513
+ if ensure_initialized:
514
+ await self.initialize()
515
+ else:
516
+ # For non-init calls (like disconnect), check if owner is alive
517
+ if self._owner_task is None or self._owner_task.done():
518
+ return await func(*args)
519
+ try:
520
+ await asyncio.wait_for(self._owner_ready.wait(), timeout=self._timeout)
521
+ except TimeoutError as e:
522
+ # Owner task is stuck, cancel it and raise
523
+ if self._owner_task and not self._owner_task.done():
524
+ self._owner_task.cancel()
525
+ raise ConnectionError(
526
+ f"Session for {self.server_name} initialization timed out after {self._timeout}s"
527
+ ) from e
528
+ if self._owner_exception:
529
+ # Propagate the error instead of silently returning None
530
+ raise ConnectionError(
531
+ f"Session for {self.server_name} failed: {self._owner_exception}"
532
+ ) from self._owner_exception
533
+
534
+ # Check if owner task died after initialization (race condition guard)
535
+ if self._owner_task is None or self._owner_task.done():
536
+ raise ConnectionError(f"Session for {self.server_name} is no longer active")
537
+
538
+ loop = asyncio.get_running_loop()
539
+ future: asyncio.Future = loop.create_future()
540
+ await self._request_queue.put((func, args, future, shutdown))
541
+ return await future
542
+
543
+ async def _initialize_impl(self) -> None:
544
+ """Initialize the underlying MCP session on the owner task."""
545
+ if self._initialized:
546
+ return
547
+
548
+ try:
549
+ logger.info(f"Initializing persistent session for {self.server_name}")
550
+
551
+ # Start connection manager
552
+ read_stream, write_stream = await self.connection_manager.start()
553
+
554
+ # Create client session
555
+ self.client_session = ClientSession(read_stream, write_stream)
556
+ await self.client_session.__aenter__()
557
+
558
+ # MCP handshake
559
+ result = await self.client_session.initialize()
560
+ logger.debug(f"MCP handshake complete for {self.server_name}: {result.capabilities}")
561
+
562
+ # Discover and cache tools
563
+ if result.capabilities.tools:
564
+ tools_result = await self.client_session.list_tools()
565
+ self.tools = tools_result.tools if tools_result else []
566
+ self._filtered_tools_cache = None # Invalidate cache when tools change
567
+ logger.info(f"Cached {len(self.tools)} tools for {self.server_name}")
568
+ else:
569
+ logger.info(f"No tools available for {self.server_name}")
570
+
571
+ # Warn once per initialization if allowed_tools references unknown names
572
+ if self._allowed_tools_set:
573
+ self._warn_on_unknown_allowed_tools(list(self._allowed_tools_set), self.tools)
574
+
575
+ # Discover resources (for future use)
576
+ if result.capabilities.resources:
577
+ try:
578
+ resources_result = await self.client_session.list_resources()
579
+ if resources_result and resources_result.resources:
580
+ logger.debug(f"Found {len(resources_result.resources)} resources for {self.server_name}")
581
+ except Exception:
582
+ logger.debug(f"Could not list resources for {self.server_name}, skipping")
583
+
584
+ self._initialized = True
585
+ logger.info(f"Session initialization complete for {self.server_name}")
586
+
587
+ except Exception as e:
588
+ logger.error(f"Failed to initialize session for {self.server_name}: {e}", exc_info=True)
589
+ await self._cleanup_on_error()
590
+ raise ConnectionError(f"Failed to initialize MCP session for {self.server_name}: {str(e)}") from e
591
+
592
+ async def _disconnect_impl(self) -> None:
593
+ """Disconnect the underlying MCP session on the owner task."""
594
+ try:
595
+ # Close client session
596
+ if self.client_session:
597
+ try:
598
+ await self.client_session.__aexit__(None, None, None)
599
+ except Exception as e:
600
+ logger.warning(f"Error closing client session for {self.server_name}: {e}")
601
+ self.client_session = None
602
+
603
+ # Stop connection manager
604
+ await self.connection_manager.stop()
605
+
606
+ except Exception as e:
607
+ logger.error(f"Error during disconnect for {self.server_name}: {e}")
608
+ finally:
609
+ self._initialized = False
610
+ self.tools.clear()
611
+ self._filtered_tools_cache = None # Clear cache on disconnect
612
+ logger.info(f"Session disconnected for {self.server_name}")