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.
- acp/__init__.py +13 -4
- acp/acp_requests.py +20 -77
- acp/agent/connection.py +8 -0
- acp/agent/implementations/debug_server/debug_server.py +6 -2
- acp/agent/protocol.py +6 -0
- acp/bridge/README.md +15 -2
- acp/bridge/__init__.py +3 -2
- acp/bridge/__main__.py +60 -19
- acp/bridge/ws_server.py +173 -0
- acp/bridge/ws_server_cli.py +89 -0
- acp/client/connection.py +38 -29
- acp/client/implementations/default_client.py +3 -2
- acp/client/implementations/headless_client.py +2 -2
- acp/connection.py +2 -2
- acp/notifications.py +20 -50
- acp/schema/__init__.py +2 -0
- acp/schema/agent_responses.py +21 -0
- acp/schema/client_requests.py +3 -3
- acp/schema/session_state.py +63 -29
- acp/stdio.py +39 -9
- acp/task/supervisor.py +2 -2
- acp/transports.py +362 -2
- acp/utils.py +17 -4
- agentpool/__init__.py +6 -1
- agentpool/agents/__init__.py +2 -0
- agentpool/agents/acp_agent/acp_agent.py +407 -277
- agentpool/agents/acp_agent/acp_converters.py +196 -38
- agentpool/agents/acp_agent/client_handler.py +191 -26
- agentpool/agents/acp_agent/session_state.py +17 -6
- agentpool/agents/agent.py +607 -572
- agentpool/agents/agui_agent/__init__.py +0 -2
- agentpool/agents/agui_agent/agui_agent.py +176 -110
- agentpool/agents/agui_agent/agui_converters.py +0 -131
- agentpool/agents/agui_agent/helpers.py +3 -4
- agentpool/agents/base_agent.py +632 -17
- agentpool/agents/claude_code_agent/FORKING.md +191 -0
- agentpool/agents/claude_code_agent/__init__.py +13 -1
- agentpool/agents/claude_code_agent/claude_code_agent.py +1058 -291
- agentpool/agents/claude_code_agent/converters.py +74 -143
- agentpool/agents/claude_code_agent/history.py +474 -0
- agentpool/agents/claude_code_agent/models.py +77 -0
- agentpool/agents/claude_code_agent/static_info.py +100 -0
- agentpool/agents/claude_code_agent/usage.py +242 -0
- agentpool/agents/context.py +40 -0
- agentpool/agents/events/__init__.py +24 -0
- agentpool/agents/events/builtin_handlers.py +67 -1
- agentpool/agents/events/event_emitter.py +32 -2
- agentpool/agents/events/events.py +104 -3
- agentpool/agents/events/infer_info.py +145 -0
- agentpool/agents/events/processors.py +254 -0
- agentpool/agents/interactions.py +41 -6
- agentpool/agents/modes.py +67 -0
- agentpool/agents/slashed_agent.py +5 -4
- agentpool/agents/tool_call_accumulator.py +213 -0
- agentpool/agents/tool_wrapping.py +18 -6
- agentpool/common_types.py +56 -21
- agentpool/config_resources/__init__.py +38 -1
- agentpool/config_resources/acp_assistant.yml +2 -2
- agentpool/config_resources/agents.yml +3 -0
- agentpool/config_resources/agents_template.yml +1 -0
- agentpool/config_resources/claude_code_agent.yml +10 -6
- agentpool/config_resources/external_acp_agents.yml +2 -1
- agentpool/delegation/base_team.py +4 -30
- agentpool/delegation/pool.py +136 -289
- agentpool/delegation/team.py +58 -57
- agentpool/delegation/teamrun.py +51 -55
- agentpool/diagnostics/__init__.py +53 -0
- agentpool/diagnostics/lsp_manager.py +1593 -0
- agentpool/diagnostics/lsp_proxy.py +41 -0
- agentpool/diagnostics/lsp_proxy_script.py +229 -0
- agentpool/diagnostics/models.py +398 -0
- agentpool/functional/run.py +10 -4
- agentpool/mcp_server/__init__.py +0 -2
- agentpool/mcp_server/client.py +76 -32
- agentpool/mcp_server/conversions.py +54 -13
- agentpool/mcp_server/manager.py +34 -54
- agentpool/mcp_server/registries/official_registry_client.py +35 -1
- agentpool/mcp_server/tool_bridge.py +186 -139
- agentpool/messaging/__init__.py +0 -2
- agentpool/messaging/compaction.py +72 -197
- agentpool/messaging/connection_manager.py +11 -10
- agentpool/messaging/event_manager.py +5 -5
- agentpool/messaging/message_container.py +6 -30
- agentpool/messaging/message_history.py +99 -8
- agentpool/messaging/messagenode.py +52 -14
- agentpool/messaging/messages.py +54 -35
- agentpool/messaging/processing.py +12 -22
- agentpool/models/__init__.py +1 -1
- agentpool/models/acp_agents/base.py +6 -24
- agentpool/models/acp_agents/mcp_capable.py +126 -157
- agentpool/models/acp_agents/non_mcp.py +129 -95
- agentpool/models/agents.py +98 -76
- agentpool/models/agui_agents.py +1 -1
- agentpool/models/claude_code_agents.py +144 -19
- agentpool/models/file_parsing.py +0 -1
- agentpool/models/manifest.py +113 -50
- agentpool/prompts/conversion_manager.py +1 -1
- agentpool/prompts/prompts.py +5 -2
- agentpool/repomap.py +1 -1
- agentpool/resource_providers/__init__.py +11 -1
- agentpool/resource_providers/aggregating.py +56 -5
- agentpool/resource_providers/base.py +70 -4
- agentpool/resource_providers/codemode/code_executor.py +72 -5
- agentpool/resource_providers/codemode/helpers.py +2 -2
- agentpool/resource_providers/codemode/provider.py +64 -12
- agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
- agentpool/resource_providers/codemode/remote_provider.py +9 -12
- agentpool/resource_providers/filtering.py +3 -1
- agentpool/resource_providers/mcp_provider.py +89 -12
- agentpool/resource_providers/plan_provider.py +228 -46
- agentpool/resource_providers/pool.py +7 -3
- agentpool/resource_providers/resource_info.py +111 -0
- agentpool/resource_providers/static.py +4 -2
- agentpool/sessions/__init__.py +4 -1
- agentpool/sessions/manager.py +33 -5
- agentpool/sessions/models.py +59 -6
- agentpool/sessions/protocol.py +28 -0
- agentpool/sessions/session.py +11 -55
- agentpool/skills/registry.py +13 -8
- agentpool/storage/manager.py +572 -49
- agentpool/talk/registry.py +4 -4
- agentpool/talk/talk.py +9 -10
- agentpool/testing.py +538 -20
- agentpool/tool_impls/__init__.py +6 -0
- agentpool/tool_impls/agent_cli/__init__.py +42 -0
- agentpool/tool_impls/agent_cli/tool.py +95 -0
- agentpool/tool_impls/bash/__init__.py +64 -0
- agentpool/tool_impls/bash/helpers.py +35 -0
- agentpool/tool_impls/bash/tool.py +171 -0
- agentpool/tool_impls/delete_path/__init__.py +70 -0
- agentpool/tool_impls/delete_path/tool.py +142 -0
- agentpool/tool_impls/download_file/__init__.py +80 -0
- agentpool/tool_impls/download_file/tool.py +183 -0
- agentpool/tool_impls/execute_code/__init__.py +55 -0
- agentpool/tool_impls/execute_code/tool.py +163 -0
- agentpool/tool_impls/grep/__init__.py +80 -0
- agentpool/tool_impls/grep/tool.py +200 -0
- agentpool/tool_impls/list_directory/__init__.py +73 -0
- agentpool/tool_impls/list_directory/tool.py +197 -0
- agentpool/tool_impls/question/__init__.py +42 -0
- agentpool/tool_impls/question/tool.py +127 -0
- agentpool/tool_impls/read/__init__.py +104 -0
- agentpool/tool_impls/read/tool.py +305 -0
- agentpool/tools/__init__.py +2 -1
- agentpool/tools/base.py +114 -34
- agentpool/tools/manager.py +57 -1
- agentpool/ui/base.py +2 -2
- agentpool/ui/mock_provider.py +2 -2
- agentpool/ui/stdlib_provider.py +2 -2
- agentpool/utils/file_watcher.py +269 -0
- agentpool/utils/identifiers.py +121 -0
- agentpool/utils/pydantic_ai_helpers.py +46 -0
- agentpool/utils/streams.py +616 -2
- agentpool/utils/subprocess_utils.py +155 -0
- agentpool/utils/token_breakdown.py +461 -0
- agentpool/vfs_registry.py +7 -2
- {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/METADATA +41 -27
- agentpool-2.5.0.dist-info/RECORD +579 -0
- {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
- agentpool_cli/__main__.py +24 -0
- agentpool_cli/create.py +1 -1
- agentpool_cli/serve_acp.py +100 -21
- agentpool_cli/serve_agui.py +87 -0
- agentpool_cli/serve_opencode.py +119 -0
- agentpool_cli/ui.py +557 -0
- agentpool_commands/__init__.py +42 -5
- agentpool_commands/agents.py +75 -2
- agentpool_commands/history.py +62 -0
- agentpool_commands/mcp.py +176 -0
- agentpool_commands/models.py +56 -3
- agentpool_commands/pool.py +260 -0
- agentpool_commands/session.py +1 -1
- agentpool_commands/text_sharing/__init__.py +119 -0
- agentpool_commands/text_sharing/base.py +123 -0
- agentpool_commands/text_sharing/github_gist.py +80 -0
- agentpool_commands/text_sharing/opencode.py +462 -0
- agentpool_commands/text_sharing/paste_rs.py +59 -0
- agentpool_commands/text_sharing/pastebin.py +116 -0
- agentpool_commands/text_sharing/shittycodingagent.py +112 -0
- agentpool_commands/tools.py +57 -0
- agentpool_commands/utils.py +80 -30
- agentpool_config/__init__.py +30 -2
- agentpool_config/agentpool_tools.py +498 -0
- agentpool_config/builtin_tools.py +77 -22
- agentpool_config/commands.py +24 -1
- agentpool_config/compaction.py +258 -0
- agentpool_config/converters.py +1 -1
- agentpool_config/event_handlers.py +42 -0
- agentpool_config/events.py +1 -1
- agentpool_config/forward_targets.py +1 -4
- agentpool_config/jinja.py +3 -3
- agentpool_config/mcp_server.py +132 -6
- agentpool_config/nodes.py +1 -1
- agentpool_config/observability.py +44 -0
- agentpool_config/session.py +0 -3
- agentpool_config/storage.py +82 -38
- agentpool_config/task.py +3 -3
- agentpool_config/tools.py +11 -22
- agentpool_config/toolsets.py +109 -233
- agentpool_server/a2a_server/agent_worker.py +307 -0
- agentpool_server/a2a_server/server.py +23 -18
- agentpool_server/acp_server/acp_agent.py +234 -181
- agentpool_server/acp_server/commands/acp_commands.py +151 -156
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +18 -17
- agentpool_server/acp_server/event_converter.py +651 -0
- agentpool_server/acp_server/input_provider.py +53 -10
- agentpool_server/acp_server/server.py +24 -90
- agentpool_server/acp_server/session.py +173 -331
- agentpool_server/acp_server/session_manager.py +8 -34
- agentpool_server/agui_server/server.py +3 -1
- agentpool_server/mcp_server/server.py +5 -2
- agentpool_server/opencode_server/.rules +95 -0
- agentpool_server/opencode_server/ENDPOINTS.md +401 -0
- agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
- agentpool_server/opencode_server/__init__.py +19 -0
- agentpool_server/opencode_server/command_validation.py +172 -0
- agentpool_server/opencode_server/converters.py +975 -0
- agentpool_server/opencode_server/dependencies.py +24 -0
- agentpool_server/opencode_server/input_provider.py +421 -0
- agentpool_server/opencode_server/models/__init__.py +250 -0
- agentpool_server/opencode_server/models/agent.py +53 -0
- agentpool_server/opencode_server/models/app.py +72 -0
- agentpool_server/opencode_server/models/base.py +26 -0
- agentpool_server/opencode_server/models/common.py +23 -0
- agentpool_server/opencode_server/models/config.py +37 -0
- agentpool_server/opencode_server/models/events.py +821 -0
- agentpool_server/opencode_server/models/file.py +88 -0
- agentpool_server/opencode_server/models/mcp.py +44 -0
- agentpool_server/opencode_server/models/message.py +179 -0
- agentpool_server/opencode_server/models/parts.py +323 -0
- agentpool_server/opencode_server/models/provider.py +81 -0
- agentpool_server/opencode_server/models/pty.py +43 -0
- agentpool_server/opencode_server/models/question.py +56 -0
- agentpool_server/opencode_server/models/session.py +111 -0
- agentpool_server/opencode_server/routes/__init__.py +29 -0
- agentpool_server/opencode_server/routes/agent_routes.py +473 -0
- agentpool_server/opencode_server/routes/app_routes.py +202 -0
- agentpool_server/opencode_server/routes/config_routes.py +302 -0
- agentpool_server/opencode_server/routes/file_routes.py +571 -0
- agentpool_server/opencode_server/routes/global_routes.py +94 -0
- agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
- agentpool_server/opencode_server/routes/message_routes.py +761 -0
- agentpool_server/opencode_server/routes/permission_routes.py +63 -0
- agentpool_server/opencode_server/routes/pty_routes.py +300 -0
- agentpool_server/opencode_server/routes/question_routes.py +128 -0
- agentpool_server/opencode_server/routes/session_routes.py +1276 -0
- agentpool_server/opencode_server/routes/tui_routes.py +139 -0
- agentpool_server/opencode_server/server.py +475 -0
- agentpool_server/opencode_server/state.py +151 -0
- agentpool_server/opencode_server/time_utils.py +8 -0
- agentpool_storage/__init__.py +12 -0
- agentpool_storage/base.py +184 -2
- agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
- agentpool_storage/claude_provider/__init__.py +42 -0
- agentpool_storage/claude_provider/provider.py +1089 -0
- agentpool_storage/file_provider.py +278 -15
- agentpool_storage/memory_provider.py +193 -12
- agentpool_storage/models.py +3 -0
- agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
- agentpool_storage/opencode_provider/__init__.py +16 -0
- agentpool_storage/opencode_provider/helpers.py +414 -0
- agentpool_storage/opencode_provider/provider.py +895 -0
- agentpool_storage/project_store.py +325 -0
- agentpool_storage/session_store.py +26 -6
- agentpool_storage/sql_provider/__init__.py +4 -2
- agentpool_storage/sql_provider/models.py +48 -0
- agentpool_storage/sql_provider/sql_provider.py +269 -3
- agentpool_storage/sql_provider/utils.py +12 -13
- agentpool_storage/zed_provider/__init__.py +16 -0
- agentpool_storage/zed_provider/helpers.py +281 -0
- agentpool_storage/zed_provider/models.py +130 -0
- agentpool_storage/zed_provider/provider.py +442 -0
- agentpool_storage/zed_provider.py +803 -0
- agentpool_toolsets/__init__.py +0 -2
- agentpool_toolsets/builtin/__init__.py +2 -12
- agentpool_toolsets/builtin/code.py +96 -57
- agentpool_toolsets/builtin/debug.py +118 -48
- agentpool_toolsets/builtin/execution_environment.py +115 -230
- agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
- agentpool_toolsets/builtin/skills.py +9 -4
- agentpool_toolsets/builtin/subagent_tools.py +64 -51
- agentpool_toolsets/builtin/workers.py +4 -2
- agentpool_toolsets/composio_toolset.py +2 -2
- agentpool_toolsets/entry_points.py +3 -1
- agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
- agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
- agentpool_toolsets/fsspec_toolset/grep.py +99 -7
- agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
- agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +500 -95
- agentpool_toolsets/mcp_discovery/__init__.py +5 -0
- agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
- agentpool_toolsets/mcp_discovery/toolset.py +511 -0
- agentpool_toolsets/mcp_run_toolset.py +87 -12
- agentpool_toolsets/notifications.py +33 -33
- agentpool_toolsets/openapi.py +3 -1
- agentpool_toolsets/search_toolset.py +3 -1
- agentpool-2.1.9.dist-info/RECORD +0 -474
- agentpool_config/resources.py +0 -33
- agentpool_server/acp_server/acp_tools.py +0 -43
- agentpool_server/acp_server/commands/spawn.py +0 -210
- agentpool_storage/text_log_provider.py +0 -275
- agentpool_toolsets/builtin/agent_management.py +0 -239
- agentpool_toolsets/builtin/chain.py +0 -288
- agentpool_toolsets/builtin/history.py +0 -36
- agentpool_toolsets/builtin/integration.py +0 -85
- agentpool_toolsets/builtin/tool_management.py +0 -90
- agentpool_toolsets/builtin/user_interaction.py +0 -52
- agentpool_toolsets/semantic_memory_toolset.py +0 -536
- {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
- {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,
|
|
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
|
|
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
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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,
|
|
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
|
|
177
|
+
return FastMCPToolResult(structured_content=result)
|
|
169
178
|
# Pydantic model - serialize to dict for structured_content
|
|
170
179
|
if isinstance(result, BaseModel):
|
|
171
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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=
|
|
561
|
+
app = self._mcp.http_app(transport="http")
|
|
412
562
|
# Configure uvicorn
|
|
413
|
-
cfg = uvicorn.Config(
|
|
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
|
|
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
|
|
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()]
|
agentpool/messaging/__init__.py
CHANGED
|
@@ -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",
|