agentpool 2.1.9__py3-none-any.whl → 2.5.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 (311) hide show
  1. acp/__init__.py +13 -4
  2. acp/acp_requests.py +20 -77
  3. acp/agent/connection.py +8 -0
  4. acp/agent/implementations/debug_server/debug_server.py +6 -2
  5. acp/agent/protocol.py +6 -0
  6. acp/bridge/README.md +15 -2
  7. acp/bridge/__init__.py +3 -2
  8. acp/bridge/__main__.py +60 -19
  9. acp/bridge/ws_server.py +173 -0
  10. acp/bridge/ws_server_cli.py +89 -0
  11. acp/client/connection.py +38 -29
  12. acp/client/implementations/default_client.py +3 -2
  13. acp/client/implementations/headless_client.py +2 -2
  14. acp/connection.py +2 -2
  15. acp/notifications.py +20 -50
  16. acp/schema/__init__.py +2 -0
  17. acp/schema/agent_responses.py +21 -0
  18. acp/schema/client_requests.py +3 -3
  19. acp/schema/session_state.py +63 -29
  20. acp/stdio.py +39 -9
  21. acp/task/supervisor.py +2 -2
  22. acp/transports.py +362 -2
  23. acp/utils.py +17 -4
  24. agentpool/__init__.py +6 -1
  25. agentpool/agents/__init__.py +2 -0
  26. agentpool/agents/acp_agent/acp_agent.py +407 -277
  27. agentpool/agents/acp_agent/acp_converters.py +196 -38
  28. agentpool/agents/acp_agent/client_handler.py +191 -26
  29. agentpool/agents/acp_agent/session_state.py +17 -6
  30. agentpool/agents/agent.py +607 -572
  31. agentpool/agents/agui_agent/__init__.py +0 -2
  32. agentpool/agents/agui_agent/agui_agent.py +176 -110
  33. agentpool/agents/agui_agent/agui_converters.py +0 -131
  34. agentpool/agents/agui_agent/helpers.py +3 -4
  35. agentpool/agents/base_agent.py +632 -17
  36. agentpool/agents/claude_code_agent/FORKING.md +191 -0
  37. agentpool/agents/claude_code_agent/__init__.py +13 -1
  38. agentpool/agents/claude_code_agent/claude_code_agent.py +1058 -291
  39. agentpool/agents/claude_code_agent/converters.py +74 -143
  40. agentpool/agents/claude_code_agent/history.py +474 -0
  41. agentpool/agents/claude_code_agent/models.py +77 -0
  42. agentpool/agents/claude_code_agent/static_info.py +100 -0
  43. agentpool/agents/claude_code_agent/usage.py +242 -0
  44. agentpool/agents/context.py +40 -0
  45. agentpool/agents/events/__init__.py +24 -0
  46. agentpool/agents/events/builtin_handlers.py +67 -1
  47. agentpool/agents/events/event_emitter.py +32 -2
  48. agentpool/agents/events/events.py +104 -3
  49. agentpool/agents/events/infer_info.py +145 -0
  50. agentpool/agents/events/processors.py +254 -0
  51. agentpool/agents/interactions.py +41 -6
  52. agentpool/agents/modes.py +67 -0
  53. agentpool/agents/slashed_agent.py +5 -4
  54. agentpool/agents/tool_call_accumulator.py +213 -0
  55. agentpool/agents/tool_wrapping.py +18 -6
  56. agentpool/common_types.py +56 -21
  57. agentpool/config_resources/__init__.py +38 -1
  58. agentpool/config_resources/acp_assistant.yml +2 -2
  59. agentpool/config_resources/agents.yml +3 -0
  60. agentpool/config_resources/agents_template.yml +1 -0
  61. agentpool/config_resources/claude_code_agent.yml +10 -6
  62. agentpool/config_resources/external_acp_agents.yml +2 -1
  63. agentpool/delegation/base_team.py +4 -30
  64. agentpool/delegation/pool.py +136 -289
  65. agentpool/delegation/team.py +58 -57
  66. agentpool/delegation/teamrun.py +51 -55
  67. agentpool/diagnostics/__init__.py +53 -0
  68. agentpool/diagnostics/lsp_manager.py +1593 -0
  69. agentpool/diagnostics/lsp_proxy.py +41 -0
  70. agentpool/diagnostics/lsp_proxy_script.py +229 -0
  71. agentpool/diagnostics/models.py +398 -0
  72. agentpool/functional/run.py +10 -4
  73. agentpool/mcp_server/__init__.py +0 -2
  74. agentpool/mcp_server/client.py +76 -32
  75. agentpool/mcp_server/conversions.py +54 -13
  76. agentpool/mcp_server/manager.py +34 -54
  77. agentpool/mcp_server/registries/official_registry_client.py +35 -1
  78. agentpool/mcp_server/tool_bridge.py +186 -139
  79. agentpool/messaging/__init__.py +0 -2
  80. agentpool/messaging/compaction.py +72 -197
  81. agentpool/messaging/connection_manager.py +11 -10
  82. agentpool/messaging/event_manager.py +5 -5
  83. agentpool/messaging/message_container.py +6 -30
  84. agentpool/messaging/message_history.py +99 -8
  85. agentpool/messaging/messagenode.py +52 -14
  86. agentpool/messaging/messages.py +54 -35
  87. agentpool/messaging/processing.py +12 -22
  88. agentpool/models/__init__.py +1 -1
  89. agentpool/models/acp_agents/base.py +6 -24
  90. agentpool/models/acp_agents/mcp_capable.py +126 -157
  91. agentpool/models/acp_agents/non_mcp.py +129 -95
  92. agentpool/models/agents.py +98 -76
  93. agentpool/models/agui_agents.py +1 -1
  94. agentpool/models/claude_code_agents.py +144 -19
  95. agentpool/models/file_parsing.py +0 -1
  96. agentpool/models/manifest.py +113 -50
  97. agentpool/prompts/conversion_manager.py +1 -1
  98. agentpool/prompts/prompts.py +5 -2
  99. agentpool/repomap.py +1 -1
  100. agentpool/resource_providers/__init__.py +11 -1
  101. agentpool/resource_providers/aggregating.py +56 -5
  102. agentpool/resource_providers/base.py +70 -4
  103. agentpool/resource_providers/codemode/code_executor.py +72 -5
  104. agentpool/resource_providers/codemode/helpers.py +2 -2
  105. agentpool/resource_providers/codemode/provider.py +64 -12
  106. agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
  107. agentpool/resource_providers/codemode/remote_provider.py +9 -12
  108. agentpool/resource_providers/filtering.py +3 -1
  109. agentpool/resource_providers/mcp_provider.py +89 -12
  110. agentpool/resource_providers/plan_provider.py +228 -46
  111. agentpool/resource_providers/pool.py +7 -3
  112. agentpool/resource_providers/resource_info.py +111 -0
  113. agentpool/resource_providers/static.py +4 -2
  114. agentpool/sessions/__init__.py +4 -1
  115. agentpool/sessions/manager.py +33 -5
  116. agentpool/sessions/models.py +59 -6
  117. agentpool/sessions/protocol.py +28 -0
  118. agentpool/sessions/session.py +11 -55
  119. agentpool/skills/registry.py +13 -8
  120. agentpool/storage/manager.py +572 -49
  121. agentpool/talk/registry.py +4 -4
  122. agentpool/talk/talk.py +9 -10
  123. agentpool/testing.py +538 -20
  124. agentpool/tool_impls/__init__.py +6 -0
  125. agentpool/tool_impls/agent_cli/__init__.py +42 -0
  126. agentpool/tool_impls/agent_cli/tool.py +95 -0
  127. agentpool/tool_impls/bash/__init__.py +64 -0
  128. agentpool/tool_impls/bash/helpers.py +35 -0
  129. agentpool/tool_impls/bash/tool.py +171 -0
  130. agentpool/tool_impls/delete_path/__init__.py +70 -0
  131. agentpool/tool_impls/delete_path/tool.py +142 -0
  132. agentpool/tool_impls/download_file/__init__.py +80 -0
  133. agentpool/tool_impls/download_file/tool.py +183 -0
  134. agentpool/tool_impls/execute_code/__init__.py +55 -0
  135. agentpool/tool_impls/execute_code/tool.py +163 -0
  136. agentpool/tool_impls/grep/__init__.py +80 -0
  137. agentpool/tool_impls/grep/tool.py +200 -0
  138. agentpool/tool_impls/list_directory/__init__.py +73 -0
  139. agentpool/tool_impls/list_directory/tool.py +197 -0
  140. agentpool/tool_impls/question/__init__.py +42 -0
  141. agentpool/tool_impls/question/tool.py +127 -0
  142. agentpool/tool_impls/read/__init__.py +104 -0
  143. agentpool/tool_impls/read/tool.py +305 -0
  144. agentpool/tools/__init__.py +2 -1
  145. agentpool/tools/base.py +114 -34
  146. agentpool/tools/manager.py +57 -1
  147. agentpool/ui/base.py +2 -2
  148. agentpool/ui/mock_provider.py +2 -2
  149. agentpool/ui/stdlib_provider.py +2 -2
  150. agentpool/utils/file_watcher.py +269 -0
  151. agentpool/utils/identifiers.py +121 -0
  152. agentpool/utils/pydantic_ai_helpers.py +46 -0
  153. agentpool/utils/streams.py +616 -2
  154. agentpool/utils/subprocess_utils.py +155 -0
  155. agentpool/utils/token_breakdown.py +461 -0
  156. agentpool/vfs_registry.py +7 -2
  157. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/METADATA +41 -27
  158. agentpool-2.5.0.dist-info/RECORD +579 -0
  159. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
  160. agentpool_cli/__main__.py +24 -0
  161. agentpool_cli/create.py +1 -1
  162. agentpool_cli/serve_acp.py +100 -21
  163. agentpool_cli/serve_agui.py +87 -0
  164. agentpool_cli/serve_opencode.py +119 -0
  165. agentpool_cli/ui.py +557 -0
  166. agentpool_commands/__init__.py +42 -5
  167. agentpool_commands/agents.py +75 -2
  168. agentpool_commands/history.py +62 -0
  169. agentpool_commands/mcp.py +176 -0
  170. agentpool_commands/models.py +56 -3
  171. agentpool_commands/pool.py +260 -0
  172. agentpool_commands/session.py +1 -1
  173. agentpool_commands/text_sharing/__init__.py +119 -0
  174. agentpool_commands/text_sharing/base.py +123 -0
  175. agentpool_commands/text_sharing/github_gist.py +80 -0
  176. agentpool_commands/text_sharing/opencode.py +462 -0
  177. agentpool_commands/text_sharing/paste_rs.py +59 -0
  178. agentpool_commands/text_sharing/pastebin.py +116 -0
  179. agentpool_commands/text_sharing/shittycodingagent.py +112 -0
  180. agentpool_commands/tools.py +57 -0
  181. agentpool_commands/utils.py +80 -30
  182. agentpool_config/__init__.py +30 -2
  183. agentpool_config/agentpool_tools.py +498 -0
  184. agentpool_config/builtin_tools.py +77 -22
  185. agentpool_config/commands.py +24 -1
  186. agentpool_config/compaction.py +258 -0
  187. agentpool_config/converters.py +1 -1
  188. agentpool_config/event_handlers.py +42 -0
  189. agentpool_config/events.py +1 -1
  190. agentpool_config/forward_targets.py +1 -4
  191. agentpool_config/jinja.py +3 -3
  192. agentpool_config/mcp_server.py +132 -6
  193. agentpool_config/nodes.py +1 -1
  194. agentpool_config/observability.py +44 -0
  195. agentpool_config/session.py +0 -3
  196. agentpool_config/storage.py +82 -38
  197. agentpool_config/task.py +3 -3
  198. agentpool_config/tools.py +11 -22
  199. agentpool_config/toolsets.py +109 -233
  200. agentpool_server/a2a_server/agent_worker.py +307 -0
  201. agentpool_server/a2a_server/server.py +23 -18
  202. agentpool_server/acp_server/acp_agent.py +234 -181
  203. agentpool_server/acp_server/commands/acp_commands.py +151 -156
  204. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +18 -17
  205. agentpool_server/acp_server/event_converter.py +651 -0
  206. agentpool_server/acp_server/input_provider.py +53 -10
  207. agentpool_server/acp_server/server.py +24 -90
  208. agentpool_server/acp_server/session.py +173 -331
  209. agentpool_server/acp_server/session_manager.py +8 -34
  210. agentpool_server/agui_server/server.py +3 -1
  211. agentpool_server/mcp_server/server.py +5 -2
  212. agentpool_server/opencode_server/.rules +95 -0
  213. agentpool_server/opencode_server/ENDPOINTS.md +401 -0
  214. agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
  215. agentpool_server/opencode_server/__init__.py +19 -0
  216. agentpool_server/opencode_server/command_validation.py +172 -0
  217. agentpool_server/opencode_server/converters.py +975 -0
  218. agentpool_server/opencode_server/dependencies.py +24 -0
  219. agentpool_server/opencode_server/input_provider.py +421 -0
  220. agentpool_server/opencode_server/models/__init__.py +250 -0
  221. agentpool_server/opencode_server/models/agent.py +53 -0
  222. agentpool_server/opencode_server/models/app.py +72 -0
  223. agentpool_server/opencode_server/models/base.py +26 -0
  224. agentpool_server/opencode_server/models/common.py +23 -0
  225. agentpool_server/opencode_server/models/config.py +37 -0
  226. agentpool_server/opencode_server/models/events.py +821 -0
  227. agentpool_server/opencode_server/models/file.py +88 -0
  228. agentpool_server/opencode_server/models/mcp.py +44 -0
  229. agentpool_server/opencode_server/models/message.py +179 -0
  230. agentpool_server/opencode_server/models/parts.py +323 -0
  231. agentpool_server/opencode_server/models/provider.py +81 -0
  232. agentpool_server/opencode_server/models/pty.py +43 -0
  233. agentpool_server/opencode_server/models/question.py +56 -0
  234. agentpool_server/opencode_server/models/session.py +111 -0
  235. agentpool_server/opencode_server/routes/__init__.py +29 -0
  236. agentpool_server/opencode_server/routes/agent_routes.py +473 -0
  237. agentpool_server/opencode_server/routes/app_routes.py +202 -0
  238. agentpool_server/opencode_server/routes/config_routes.py +302 -0
  239. agentpool_server/opencode_server/routes/file_routes.py +571 -0
  240. agentpool_server/opencode_server/routes/global_routes.py +94 -0
  241. agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
  242. agentpool_server/opencode_server/routes/message_routes.py +761 -0
  243. agentpool_server/opencode_server/routes/permission_routes.py +63 -0
  244. agentpool_server/opencode_server/routes/pty_routes.py +300 -0
  245. agentpool_server/opencode_server/routes/question_routes.py +128 -0
  246. agentpool_server/opencode_server/routes/session_routes.py +1276 -0
  247. agentpool_server/opencode_server/routes/tui_routes.py +139 -0
  248. agentpool_server/opencode_server/server.py +475 -0
  249. agentpool_server/opencode_server/state.py +151 -0
  250. agentpool_server/opencode_server/time_utils.py +8 -0
  251. agentpool_storage/__init__.py +12 -0
  252. agentpool_storage/base.py +184 -2
  253. agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
  254. agentpool_storage/claude_provider/__init__.py +42 -0
  255. agentpool_storage/claude_provider/provider.py +1089 -0
  256. agentpool_storage/file_provider.py +278 -15
  257. agentpool_storage/memory_provider.py +193 -12
  258. agentpool_storage/models.py +3 -0
  259. agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
  260. agentpool_storage/opencode_provider/__init__.py +16 -0
  261. agentpool_storage/opencode_provider/helpers.py +414 -0
  262. agentpool_storage/opencode_provider/provider.py +895 -0
  263. agentpool_storage/project_store.py +325 -0
  264. agentpool_storage/session_store.py +26 -6
  265. agentpool_storage/sql_provider/__init__.py +4 -2
  266. agentpool_storage/sql_provider/models.py +48 -0
  267. agentpool_storage/sql_provider/sql_provider.py +269 -3
  268. agentpool_storage/sql_provider/utils.py +12 -13
  269. agentpool_storage/zed_provider/__init__.py +16 -0
  270. agentpool_storage/zed_provider/helpers.py +281 -0
  271. agentpool_storage/zed_provider/models.py +130 -0
  272. agentpool_storage/zed_provider/provider.py +442 -0
  273. agentpool_storage/zed_provider.py +803 -0
  274. agentpool_toolsets/__init__.py +0 -2
  275. agentpool_toolsets/builtin/__init__.py +2 -12
  276. agentpool_toolsets/builtin/code.py +96 -57
  277. agentpool_toolsets/builtin/debug.py +118 -48
  278. agentpool_toolsets/builtin/execution_environment.py +115 -230
  279. agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
  280. agentpool_toolsets/builtin/skills.py +9 -4
  281. agentpool_toolsets/builtin/subagent_tools.py +64 -51
  282. agentpool_toolsets/builtin/workers.py +4 -2
  283. agentpool_toolsets/composio_toolset.py +2 -2
  284. agentpool_toolsets/entry_points.py +3 -1
  285. agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
  286. agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
  287. agentpool_toolsets/fsspec_toolset/grep.py +99 -7
  288. agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
  289. agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
  290. agentpool_toolsets/fsspec_toolset/toolset.py +500 -95
  291. agentpool_toolsets/mcp_discovery/__init__.py +5 -0
  292. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  293. agentpool_toolsets/mcp_discovery/toolset.py +511 -0
  294. agentpool_toolsets/mcp_run_toolset.py +87 -12
  295. agentpool_toolsets/notifications.py +33 -33
  296. agentpool_toolsets/openapi.py +3 -1
  297. agentpool_toolsets/search_toolset.py +3 -1
  298. agentpool-2.1.9.dist-info/RECORD +0 -474
  299. agentpool_config/resources.py +0 -33
  300. agentpool_server/acp_server/acp_tools.py +0 -43
  301. agentpool_server/acp_server/commands/spawn.py +0 -210
  302. agentpool_storage/text_log_provider.py +0 -275
  303. agentpool_toolsets/builtin/agent_management.py +0 -239
  304. agentpool_toolsets/builtin/chain.py +0 -288
  305. agentpool_toolsets/builtin/history.py +0 -36
  306. agentpool_toolsets/builtin/integration.py +0 -85
  307. agentpool_toolsets/builtin/tool_management.py +0 -90
  308. agentpool_toolsets/builtin/user_interaction.py +0 -52
  309. agentpool_toolsets/semantic_memory_toolset.py +0 -536
  310. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
  311. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -15,18 +15,15 @@ import asyncio
15
15
  from contextlib import asynccontextmanager, suppress
16
16
  from dataclasses import dataclass, field, replace
17
17
  import inspect
18
- from typing import TYPE_CHECKING, Any, Literal, Self, get_args, get_origin
18
+ from typing import TYPE_CHECKING, Any, Self, get_args, get_origin
19
19
  from uuid import uuid4
20
20
 
21
21
  import anyio
22
- from fastmcp import FastMCP
23
- from fastmcp.tools import Tool as FastMCPTool
24
- from llmling_models.models.helpers import infer_model
25
22
  from pydantic import BaseModel, HttpUrl
26
23
 
27
- from agentpool.agents import Agent, ClaudeCodeAgent
28
- from agentpool.agents.acp_agent.acp_agent import ACPAgent
24
+ from agentpool.agents import Agent
29
25
  from agentpool.log import get_logger
26
+ from agentpool.resource_providers import ResourceChangeEvent
30
27
  from agentpool.utils.signatures import filter_schema_params, get_params_matching_predicate
31
28
 
32
29
 
@@ -34,16 +31,18 @@ if TYPE_CHECKING:
34
31
  from collections.abc import AsyncIterator, Callable
35
32
 
36
33
  from claude_agent_sdk.types import McpServerConfig
37
- from fastmcp import Context
34
+ from fastmcp import Context, FastMCP
38
35
  from fastmcp.tools.tool import ToolResult
39
36
  from pydantic_ai import RunContext
40
37
  from uvicorn import Server
41
38
 
42
- from acp.schema.mcp import HttpMcpServer, SseMcpServer
39
+ from acp.schema.mcp import HttpMcpServer
43
40
  from agentpool.agents import AgentContext
44
41
  from agentpool.agents.base_agent import BaseAgent
45
42
  from agentpool.tools.base import Tool
46
43
 
44
+ _ = ResourceChangeEvent # Used at runtime in method signature
45
+
47
46
 
48
47
  logger = get_logger(__name__)
49
48
 
@@ -130,11 +129,11 @@ def _create_stub_run_context(ctx: AgentContext[Any]) -> RunContext[Any]:
130
129
  match ctx.agent:
131
130
  case Agent():
132
131
  model = ctx.agent._model or TestModel()
133
- case ACPAgent() | ClaudeCodeAgent():
134
- try:
135
- model = infer_model(ctx.agent.model_name or "test")
136
- except Exception: # noqa: BLE001
137
- model = TestModel()
132
+ # case ACPAgent() | ClaudeCodeAgent():
133
+ # try:
134
+ # model = infer_model(ctx.agent.model_name or "test")
135
+ # except Exception:
136
+ # model = TestModel()
138
137
  case _:
139
138
  model = TestModel()
140
139
  # Create a minimal usage object
@@ -153,25 +152,35 @@ def _convert_to_tool_result(result: Any) -> ToolResult:
153
152
  """Convert a tool's return value to a FastMCP ToolResult.
154
153
 
155
154
  Handles different result types appropriately:
156
- - ToolResult: Pass through unchanged
155
+ - FastMCP ToolResult: Pass through unchanged
156
+ - AgentPool ToolResult: Convert to FastMCP format
157
157
  - dict: Use as structured_content (enables programmatic access by clients)
158
158
  - Pydantic models: Serialize to dict for structured_content
159
159
  - Other types: Pass to ToolResult(content=...) which handles conversion internally
160
160
  """
161
- from fastmcp.tools.tool import ToolResult
161
+ from fastmcp.tools.tool import ToolResult as FastMCPToolResult
162
+
163
+ from agentpool.tools.base import ToolResult as AgentPoolToolResult
162
164
 
163
- # Already a ToolResult - pass through
164
- if isinstance(result, ToolResult):
165
+ # Already a FastMCP ToolResult - pass through
166
+ if isinstance(result, FastMCPToolResult):
165
167
  return result
168
+ # AgentPool ToolResult - convert to FastMCP format
169
+ if isinstance(result, AgentPoolToolResult):
170
+ return FastMCPToolResult(
171
+ content=result.content,
172
+ structured_content=result.structured_content,
173
+ meta=result.metadata,
174
+ )
166
175
  # Dict - use as structured_content (FastMCP auto-populates content as JSON)
167
176
  if isinstance(result, dict):
168
- return ToolResult(structured_content=result)
177
+ return FastMCPToolResult(structured_content=result)
169
178
  # Pydantic model - serialize to dict for structured_content
170
179
  if isinstance(result, BaseModel):
171
- return ToolResult(structured_content=result.model_dump(mode="json"))
180
+ return FastMCPToolResult(structured_content=result.model_dump(mode="json"))
172
181
  # All other types (str, list, ContentBlock, Image, None, primitives, etc.)
173
182
  # ToolResult's internal _convert_to_content handles these correctly
174
- return ToolResult(content=result if result is not None else "")
183
+ return FastMCPToolResult(content=result if result is not None else "")
175
184
 
176
185
 
177
186
  def _extract_tool_call_id(context: Context | None) -> str:
@@ -209,9 +218,6 @@ class BridgeConfig:
209
218
  port: int = 0
210
219
  """Port to bind to (0 = auto-select available port)."""
211
220
 
212
- transport: Literal["sse", "streamable-http"] = "sse"
213
- """Transport protocol: 'sse' or 'streamable-http'."""
214
-
215
221
  server_name: str = "agentpool-toolmanager"
216
222
  """Name for the MCP server."""
217
223
 
@@ -244,6 +250,9 @@ class ToolManagerBridge:
244
250
  config: BridgeConfig = field(default_factory=BridgeConfig)
245
251
  """Bridge configuration."""
246
252
 
253
+ current_deps: Any = field(default=None, init=False, repr=False)
254
+ """Current dependencies for tool invocations (set by run_stream)."""
255
+
247
256
  _mcp: FastMCP | None = field(default=None, init=False, repr=False)
248
257
  """FastMCP server instance."""
249
258
 
@@ -267,12 +276,18 @@ class ToolManagerBridge:
267
276
 
268
277
  async def start(self) -> None:
269
278
  """Start the HTTP MCP server in the background."""
279
+ from fastmcp import FastMCP
280
+
270
281
  self._mcp = FastMCP(name=self.config.server_name)
271
282
  await self._register_tools()
283
+ self._subscribe_to_tool_changes()
272
284
  await self._start_server()
273
285
 
274
286
  async def stop(self) -> None:
275
287
  """Stop the HTTP MCP server."""
288
+ # Unsubscribe from tool changes
289
+ self._unsubscribe_from_tool_changes()
290
+
276
291
  if self._server:
277
292
  self._server.should_exit = True
278
293
  if self._server_task:
@@ -288,6 +303,75 @@ class ToolManagerBridge:
288
303
  self._actual_port = None
289
304
  logger.info("ToolManagerBridge stopped")
290
305
 
306
+ def _subscribe_to_tool_changes(self) -> None:
307
+ """Subscribe to tool changes from all providers via signals."""
308
+ for provider in self.node.tools.providers:
309
+ provider.tools_changed.connect(self._on_tools_changed)
310
+
311
+ def _unsubscribe_from_tool_changes(self) -> None:
312
+ """Disconnect from tool change signals on all providers."""
313
+ for provider in self.node.tools.providers:
314
+ provider.tools_changed.disconnect(self._on_tools_changed)
315
+
316
+ async def _on_tools_changed(self, event: ResourceChangeEvent) -> None:
317
+ """Handle tool changes from a provider."""
318
+ logger.info(
319
+ "Tools changed in provider, refreshing MCP tools",
320
+ provider=event.provider_name,
321
+ provider_kind=event.provider_kind,
322
+ )
323
+ if self._mcp:
324
+ await self._refresh_tools()
325
+
326
+ async def _refresh_tools(self) -> None:
327
+ """Refresh tools registered with the MCP server.
328
+
329
+ Uses FastMCP's add_tool/remove_tool API which automatically sends
330
+ ToolListChanged notifications when called within a request context.
331
+
332
+ Note: FastMCP only sends notifications when inside a request context
333
+ (ContextVar-based). Outside of requests, tools are updated but clients
334
+ won't receive a push notification - they'll see changes on next list_tools.
335
+
336
+ Future improvement: Access StreamableHTTPSessionManager._server_instances
337
+ to broadcast ToolListChanged to all connected sessions regardless of context.
338
+ """
339
+ if not self._mcp:
340
+ return
341
+
342
+ # Get current and new tool sets
343
+ # Support both old and new FastMCP API
344
+ if hasattr(self._mcp, "_tool_manager"):
345
+ # Old API (<=2.12.4): direct access to _tool_manager
346
+ current_names = set(self._mcp._tool_manager._tools.keys())
347
+ elif hasattr(self._mcp, "_local_provider"):
348
+ # New API (git): tools stored in _local_provider._components
349
+ # Keys are prefixed with 'tool:', e.g., 'tool:bash'
350
+ current_names = {
351
+ key.removeprefix("tool:")
352
+ for key in self._mcp._local_provider._components
353
+ if key.startswith("tool:")
354
+ }
355
+ else:
356
+ # Fallback: use async get_tools() method
357
+ current_tools = await self._mcp.get_tools()
358
+ current_names = {t.name for t in current_tools} # type: ignore[attr-defined]
359
+ new_tools = await self.node.tools.get_tools(state="enabled")
360
+ new_names = {t.name for t in new_tools}
361
+
362
+ # Remove tools that are no longer present
363
+ for name in current_names - new_names:
364
+ with suppress(Exception):
365
+ self._mcp.remove_tool(name)
366
+
367
+ # Add/update tools
368
+ for tool in new_tools:
369
+ if tool.name in current_names:
370
+ # Remove and re-add to update
371
+ with suppress(Exception):
372
+ self._mcp.remove_tool(tool.name)
373
+ self._register_single_tool(tool)
374
+
291
375
  @property
292
376
  def port(self) -> int:
293
377
  """Get the actual port the server is running on."""
@@ -299,19 +383,16 @@ class ToolManagerBridge:
299
383
  @property
300
384
  def url(self) -> str:
301
385
  """Get the server URL."""
302
- path = "/sse" if self.config.transport == "sse" else "/mcp"
303
- return f"http://{self.config.host}:{self.port}{path}"
386
+ return f"http://{self.config.host}:{self.port}/mcp"
304
387
 
305
- def get_mcp_server_config(self) -> HttpMcpServer | SseMcpServer:
388
+ def get_mcp_server_config(self) -> HttpMcpServer:
306
389
  """Get ACP-compatible MCP server configuration.
307
390
 
308
391
  Returns config suitable for passing to ACP agent's NewSessionRequest.
309
392
  """
310
- from acp.schema import HttpMcpServer, SseMcpServer
393
+ from acp.schema import HttpMcpServer
311
394
 
312
395
  url = HttpUrl(self.url)
313
- if self.config.transport == "sse":
314
- return SseMcpServer(name=self.config.server_name, url=url, headers=[])
315
396
  return HttpMcpServer(name=self.config.server_name, url=url, headers=[])
316
397
 
317
398
  def get_claude_mcp_server_config(self) -> dict[str, McpServerConfig]:
@@ -354,6 +435,75 @@ class ToolManagerBridge:
354
435
  """Register a single tool with the FastMCP server."""
355
436
  if not self._mcp:
356
437
  return
438
+ from fastmcp.tools import Tool as FastMCPTool
439
+
440
+ class _BridgeTool(FastMCPTool):
441
+ """Custom FastMCP Tool that wraps a agentpool Tool.
442
+
443
+ This allows us to use our own schema and invoke tools with AgentContext.
444
+ """
445
+
446
+ def __init__(self, tool: Tool, bridge: ToolManagerBridge) -> None:
447
+ # Get input schema from our tool
448
+ schema = tool.schema["function"]
449
+ input_schema = schema.get("parameters", {"type": "object", "properties": {}})
450
+ # Filter out context parameters - they're auto-injected by the bridge
451
+ context_params = _get_context_param_names(tool.get_callable())
452
+ run_context_params = _get_run_context_param_names(tool.get_callable())
453
+ all_context_params = context_params | run_context_params
454
+ filtered_schema = filter_schema_params(input_schema, all_context_params)
455
+ desc = tool.description or "No description"
456
+ super().__init__(name=tool.name, description=desc, parameters=filtered_schema)
457
+ # Set these AFTER super().__init__() to avoid being overwritten
458
+ self._tool = tool
459
+ self._bridge = bridge
460
+
461
+ async def run(self, arguments: dict[str, Any]) -> ToolResult:
462
+ """Execute the wrapped tool with context bridging."""
463
+ from fastmcp.server.dependencies import get_context
464
+
465
+ # Get FastMCP context from context variable (not passed as parameter)
466
+ try:
467
+ mcp_context: Context | None = get_context()
468
+ except LookupError:
469
+ mcp_context = None
470
+
471
+ # Try to get Claude's original tool_call_id from request metadata
472
+ tool_call_id = _extract_tool_call_id(mcp_context)
473
+ # Get deps from bridge (set by run_stream on the agent)
474
+ current_deps = self._bridge.current_deps
475
+ # Create context with tool-specific metadata from node's context.
476
+ ctx = replace(
477
+ self._bridge.node.get_context(data=current_deps),
478
+ tool_name=self._tool.name,
479
+ tool_call_id=tool_call_id,
480
+ tool_input=arguments,
481
+ )
482
+ # Invoke with context - copy arguments since invoke_tool_with_context
483
+ # modifies kwargs in-place to inject context parameters
484
+ result = await self._bridge.invoke_tool_with_context(
485
+ self._tool, ctx, arguments.copy()
486
+ )
487
+
488
+ # Emit metadata event for ClaudeCodeAgent to correlate
489
+ # (works around Claude SDK stripping MCP _meta field)
490
+ from agentpool.agents.events import ToolResultMetadataEvent
491
+ from agentpool.tools.base import ToolResult as AgentPoolToolResult
492
+
493
+ if isinstance(result, AgentPoolToolResult) and result.metadata:
494
+ logger.info(
495
+ "Emitting ToolResultMetadataEvent",
496
+ tool_call_id=tool_call_id,
497
+ metadata_keys=list(result.metadata.keys()),
498
+ )
499
+ event = ToolResultMetadataEvent(
500
+ tool_call_id=tool_call_id,
501
+ metadata=result.metadata,
502
+ )
503
+ await ctx.events.emit_event(event)
504
+
505
+ return _convert_to_tool_result(result)
506
+
357
507
  # Create a custom FastMCP Tool that wraps our tool
358
508
  bridge_tool = _BridgeTool(tool=tool, bridge=self)
359
509
  self._mcp.add_tool(bridge_tool)
@@ -368,7 +518,7 @@ class ToolManagerBridge:
368
518
 
369
519
  Handles tools that expect AgentContext, RunContext, or neither.
370
520
  """
371
- fn = tool.callable
521
+ fn = tool.get_callable()
372
522
 
373
523
  # Inject AgentContext parameters
374
524
  context_param_names = _get_context_param_names(fn)
@@ -408,63 +558,18 @@ class ToolManagerBridge:
408
558
  port = s.getsockname()[1]
409
559
  self._actual_port = port
410
560
  # Create the ASGI app
411
- app = self._mcp.http_app(transport=self.config.transport)
561
+ app = self._mcp.http_app(transport="http")
412
562
  # Configure uvicorn
413
- cfg = uvicorn.Config(app=app, host=self.config.host, port=port, log_level="warning")
563
+ cfg = uvicorn.Config(
564
+ app=app, host=self.config.host, port=port, log_level="warning", ws="websockets-sansio"
565
+ )
414
566
  self._server = uvicorn.Server(cfg)
415
567
  # Start server in background task
416
568
  name = f"mcp-bridge-{self.config.server_name}"
417
569
  self._server_task = asyncio.create_task(self._server.serve(), name=name)
418
570
  await anyio.sleep(0.1) # Wait briefly for server to start
419
571
  msg = "ToolManagerBridge started"
420
- logger.info(msg, url=self.url, transport=self.config.transport)
421
-
422
-
423
- class _BridgeTool(FastMCPTool):
424
- """Custom FastMCP Tool that wraps a agentpool Tool.
425
-
426
- This allows us to use our own schema and invoke tools with AgentContext.
427
- """
428
-
429
- def __init__(self, tool: Tool, bridge: ToolManagerBridge) -> None:
430
- # Get input schema from our tool
431
- schema = tool.schema["function"]
432
- input_schema = schema.get("parameters", {"type": "object", "properties": {}})
433
- # Filter out context parameters - they're auto-injected by the bridge
434
- context_params = _get_context_param_names(tool.callable)
435
- run_context_params = _get_run_context_param_names(tool.callable)
436
- all_context_params = context_params | run_context_params
437
- filtered_schema = filter_schema_params(input_schema, all_context_params)
438
- desc = tool.description or "No description"
439
- super().__init__(name=tool.name, description=desc, parameters=filtered_schema)
440
- # Set these AFTER super().__init__() to avoid being overwritten
441
- self._tool = tool
442
- self._bridge = bridge
443
-
444
- async def run(self, arguments: dict[str, Any]) -> ToolResult:
445
- """Execute the wrapped tool with context bridging."""
446
- from fastmcp.server.dependencies import get_context
447
-
448
- # Get FastMCP context from context variable (not passed as parameter)
449
- try:
450
- mcp_context: Context | None = get_context()
451
- except LookupError:
452
- mcp_context = None
453
-
454
- # Try to get Claude's original tool_call_id from request metadata
455
- tool_call_id = _extract_tool_call_id(mcp_context)
456
- # Create context with tool-specific metadata from node's context.
457
- ctx = replace(
458
- self._bridge.node.get_context(),
459
- tool_name=self._tool.name,
460
- tool_call_id=tool_call_id,
461
- tool_input=arguments,
462
- )
463
-
464
- # Invoke with context - copy arguments since invoke_tool_with_context
465
- # modifies kwargs in-place to inject context parameters
466
- result = await self._bridge.invoke_tool_with_context(self._tool, ctx, arguments.copy())
467
- return _convert_to_tool_result(result)
572
+ logger.info(msg, url=self.url)
468
573
 
469
574
 
470
575
  @asynccontextmanager
@@ -473,7 +578,6 @@ async def create_tool_bridge(
473
578
  *,
474
579
  host: str = "127.0.0.1",
475
580
  port: int = 0,
476
- transport: Literal["sse", "streamable-http"] = "sse",
477
581
  ) -> AsyncIterator[ToolManagerBridge]:
478
582
  """Create and start a ToolManagerBridge as a context manager.
479
583
 
@@ -481,68 +585,11 @@ async def create_tool_bridge(
481
585
  node: The node whose tools to expose
482
586
  host: Host to bind to
483
587
  port: Port to bind to (0 = auto-select)
484
- transport: Transport protocol ('sse' or 'streamable-http')
485
588
 
486
589
  Yields:
487
590
  Running ToolManagerBridge instance
488
591
  """
489
- config = BridgeConfig(host=host, port=port, transport=transport)
592
+ config = BridgeConfig(host=host, port=port)
490
593
  bridge = ToolManagerBridge(node=node, config=config)
491
594
  async with bridge:
492
595
  yield bridge
493
-
494
-
495
- class ToolBridgeRegistry:
496
- """Registry for managing multiple tool bridges.
497
-
498
- Useful when multiple ACP agents need access to different toolsets.
499
- """
500
-
501
- def __init__(self) -> None:
502
- self._bridges: dict[str, ToolManagerBridge] = {}
503
- self._port_counter = 18000 # Start port range for auto-allocation
504
-
505
- async def create_bridge(self, name: str, node: BaseAgent[Any, Any]) -> ToolManagerBridge:
506
- """Create and register a new bridge.
507
-
508
- Args:
509
- name: Unique name for this bridge
510
- node: The node whose tools to expose
511
-
512
- Returns:
513
- Started ToolManagerBridge
514
- """
515
- if name in self._bridges:
516
- msg = f"Bridge {name!r} already exists"
517
- raise ValueError(msg)
518
-
519
- config = BridgeConfig(port=self._port_counter, server_name=f"agentpool-{name}")
520
- self._port_counter += 1
521
-
522
- bridge = ToolManagerBridge(node=node, config=config)
523
- await bridge.start()
524
- self._bridges[name] = bridge
525
- return bridge
526
-
527
- async def get_bridge(self, name: str) -> ToolManagerBridge:
528
- """Get a bridge by name."""
529
- if name not in self._bridges:
530
- msg = f"Bridge {name!r} not found"
531
- raise KeyError(msg)
532
- return self._bridges[name]
533
-
534
- async def remove_bridge(self, name: str) -> None:
535
- """Stop and remove a bridge."""
536
- if name in self._bridges:
537
- await self._bridges[name].stop()
538
- del self._bridges[name]
539
-
540
- async def close_all(self) -> None:
541
- """Stop all bridges."""
542
- for bridge in list(self._bridges.values()):
543
- await bridge.stop()
544
- self._bridges.clear()
545
-
546
- def get_all_mcp_configs(self) -> list[HttpMcpServer | SseMcpServer]:
547
- """Get MCP server configs for all active bridges."""
548
- return [bridge.get_mcp_server_config() for bridge in self._bridges.values()]
@@ -7,7 +7,6 @@ from agentpool.messaging.messagenode import MessageNode
7
7
  from agentpool.messaging.message_history import MessageHistory
8
8
  from agentpool.messaging.compaction import (
9
9
  CompactionPipeline,
10
- CompactionPipelineConfig,
11
10
  CompactionStep,
12
11
  FilterBinaryContent,
13
12
  FilterEmptyMessages,
@@ -32,7 +31,6 @@ __all__ = [
32
31
  "ChatMessage",
33
32
  "ChatMessageList",
34
33
  "CompactionPipeline",
35
- "CompactionPipelineConfig",
36
34
  "CompactionStep",
37
35
  "EventManager",
38
36
  "FilterBinaryContent",