agentpool 2.1.9__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 agentpool might be problematic. Click here for more details.
- acp/README.md +64 -0
- acp/__init__.py +172 -0
- acp/__main__.py +10 -0
- acp/acp_requests.py +285 -0
- acp/agent/__init__.py +6 -0
- acp/agent/connection.py +256 -0
- acp/agent/implementations/__init__.py +6 -0
- acp/agent/implementations/debug_server/__init__.py +1 -0
- acp/agent/implementations/debug_server/cli.py +79 -0
- acp/agent/implementations/debug_server/debug.html +234 -0
- acp/agent/implementations/debug_server/debug_server.py +496 -0
- acp/agent/implementations/testing.py +91 -0
- acp/agent/protocol.py +65 -0
- acp/bridge/README.md +162 -0
- acp/bridge/__init__.py +6 -0
- acp/bridge/__main__.py +91 -0
- acp/bridge/bridge.py +246 -0
- acp/bridge/py.typed +0 -0
- acp/bridge/settings.py +15 -0
- acp/client/__init__.py +7 -0
- acp/client/connection.py +251 -0
- acp/client/implementations/__init__.py +7 -0
- acp/client/implementations/default_client.py +185 -0
- acp/client/implementations/headless_client.py +266 -0
- acp/client/implementations/noop_client.py +110 -0
- acp/client/protocol.py +61 -0
- acp/connection.py +280 -0
- acp/exceptions.py +46 -0
- acp/filesystem.py +524 -0
- acp/notifications.py +832 -0
- acp/py.typed +0 -0
- acp/schema/__init__.py +265 -0
- acp/schema/agent_plan.py +30 -0
- acp/schema/agent_requests.py +126 -0
- acp/schema/agent_responses.py +256 -0
- acp/schema/base.py +39 -0
- acp/schema/capabilities.py +230 -0
- acp/schema/client_requests.py +247 -0
- acp/schema/client_responses.py +96 -0
- acp/schema/common.py +81 -0
- acp/schema/content_blocks.py +188 -0
- acp/schema/mcp.py +82 -0
- acp/schema/messages.py +171 -0
- acp/schema/notifications.py +82 -0
- acp/schema/protocol_stuff.md +3 -0
- acp/schema/session_state.py +160 -0
- acp/schema/session_updates.py +419 -0
- acp/schema/slash_commands.py +51 -0
- acp/schema/terminal.py +15 -0
- acp/schema/tool_call.py +347 -0
- acp/stdio.py +250 -0
- acp/task/__init__.py +53 -0
- acp/task/debug.py +197 -0
- acp/task/dispatcher.py +93 -0
- acp/task/queue.py +69 -0
- acp/task/sender.py +82 -0
- acp/task/state.py +87 -0
- acp/task/supervisor.py +93 -0
- acp/terminal_handle.py +30 -0
- acp/tool_call_reporter.py +199 -0
- acp/tool_call_state.py +178 -0
- acp/transports.py +104 -0
- acp/utils.py +240 -0
- agentpool/__init__.py +63 -0
- agentpool/__main__.py +7 -0
- agentpool/agents/__init__.py +30 -0
- agentpool/agents/acp_agent/__init__.py +5 -0
- agentpool/agents/acp_agent/acp_agent.py +837 -0
- agentpool/agents/acp_agent/acp_converters.py +294 -0
- agentpool/agents/acp_agent/client_handler.py +317 -0
- agentpool/agents/acp_agent/session_state.py +44 -0
- agentpool/agents/agent.py +1264 -0
- agentpool/agents/agui_agent/__init__.py +19 -0
- agentpool/agents/agui_agent/agui_agent.py +677 -0
- agentpool/agents/agui_agent/agui_converters.py +423 -0
- agentpool/agents/agui_agent/chunk_transformer.py +204 -0
- agentpool/agents/agui_agent/event_types.py +83 -0
- agentpool/agents/agui_agent/helpers.py +192 -0
- agentpool/agents/architect.py +71 -0
- agentpool/agents/base_agent.py +177 -0
- agentpool/agents/claude_code_agent/__init__.py +11 -0
- agentpool/agents/claude_code_agent/claude_code_agent.py +1021 -0
- agentpool/agents/claude_code_agent/converters.py +243 -0
- agentpool/agents/context.py +105 -0
- agentpool/agents/events/__init__.py +61 -0
- agentpool/agents/events/builtin_handlers.py +129 -0
- agentpool/agents/events/event_emitter.py +320 -0
- agentpool/agents/events/events.py +561 -0
- agentpool/agents/events/tts_handlers.py +186 -0
- agentpool/agents/interactions.py +419 -0
- agentpool/agents/slashed_agent.py +244 -0
- agentpool/agents/sys_prompts.py +178 -0
- agentpool/agents/tool_wrapping.py +184 -0
- agentpool/base_provider.py +28 -0
- agentpool/common_types.py +226 -0
- agentpool/config_resources/__init__.py +16 -0
- agentpool/config_resources/acp_assistant.yml +24 -0
- agentpool/config_resources/agents.yml +109 -0
- agentpool/config_resources/agents_template.yml +18 -0
- agentpool/config_resources/agui_test.yml +18 -0
- agentpool/config_resources/claude_code_agent.yml +16 -0
- agentpool/config_resources/claude_style_subagent.md +30 -0
- agentpool/config_resources/external_acp_agents.yml +77 -0
- agentpool/config_resources/opencode_style_subagent.md +19 -0
- agentpool/config_resources/tts_test_agents.yml +78 -0
- agentpool/delegation/__init__.py +8 -0
- agentpool/delegation/base_team.py +504 -0
- agentpool/delegation/message_flow_tracker.py +39 -0
- agentpool/delegation/pool.py +1129 -0
- agentpool/delegation/team.py +325 -0
- agentpool/delegation/teamrun.py +343 -0
- agentpool/docs/__init__.py +5 -0
- agentpool/docs/gen_examples.py +42 -0
- agentpool/docs/utils.py +370 -0
- agentpool/functional/__init__.py +20 -0
- agentpool/functional/py.typed +0 -0
- agentpool/functional/run.py +80 -0
- agentpool/functional/structure.py +136 -0
- agentpool/hooks/__init__.py +20 -0
- agentpool/hooks/agent_hooks.py +247 -0
- agentpool/hooks/base.py +119 -0
- agentpool/hooks/callable.py +140 -0
- agentpool/hooks/command.py +180 -0
- agentpool/hooks/prompt.py +122 -0
- agentpool/jinja_filters.py +132 -0
- agentpool/log.py +224 -0
- agentpool/mcp_server/__init__.py +17 -0
- agentpool/mcp_server/client.py +429 -0
- agentpool/mcp_server/constants.py +32 -0
- agentpool/mcp_server/conversions.py +172 -0
- agentpool/mcp_server/helpers.py +47 -0
- agentpool/mcp_server/manager.py +232 -0
- agentpool/mcp_server/message_handler.py +164 -0
- agentpool/mcp_server/registries/__init__.py +1 -0
- agentpool/mcp_server/registries/official_registry_client.py +345 -0
- agentpool/mcp_server/registries/pulsemcp_client.py +88 -0
- agentpool/mcp_server/tool_bridge.py +548 -0
- agentpool/messaging/__init__.py +58 -0
- agentpool/messaging/compaction.py +928 -0
- agentpool/messaging/connection_manager.py +319 -0
- agentpool/messaging/context.py +66 -0
- agentpool/messaging/event_manager.py +426 -0
- agentpool/messaging/events.py +39 -0
- agentpool/messaging/message_container.py +209 -0
- agentpool/messaging/message_history.py +491 -0
- agentpool/messaging/messagenode.py +377 -0
- agentpool/messaging/messages.py +655 -0
- agentpool/messaging/processing.py +76 -0
- agentpool/mime_utils.py +95 -0
- agentpool/models/__init__.py +21 -0
- agentpool/models/acp_agents/__init__.py +22 -0
- agentpool/models/acp_agents/base.py +308 -0
- agentpool/models/acp_agents/mcp_capable.py +790 -0
- agentpool/models/acp_agents/non_mcp.py +842 -0
- agentpool/models/agents.py +450 -0
- agentpool/models/agui_agents.py +89 -0
- agentpool/models/claude_code_agents.py +238 -0
- agentpool/models/file_agents.py +116 -0
- agentpool/models/file_parsing.py +367 -0
- agentpool/models/manifest.py +658 -0
- agentpool/observability/__init__.py +9 -0
- agentpool/observability/observability_registry.py +97 -0
- agentpool/prompts/__init__.py +1 -0
- agentpool/prompts/base.py +27 -0
- agentpool/prompts/builtin_provider.py +75 -0
- agentpool/prompts/conversion_manager.py +95 -0
- agentpool/prompts/convert.py +96 -0
- agentpool/prompts/manager.py +204 -0
- agentpool/prompts/parts/zed.md +33 -0
- agentpool/prompts/prompts.py +581 -0
- agentpool/py.typed +0 -0
- agentpool/queries/tree-sitter-language-pack/README.md +7 -0
- agentpool/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- agentpool/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- agentpool/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
- agentpool/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- agentpool/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- agentpool/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- agentpool/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- agentpool/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- agentpool/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- agentpool/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- agentpool/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- agentpool/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- agentpool/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- agentpool/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- agentpool/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- agentpool/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
- agentpool/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- agentpool/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- agentpool/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- agentpool/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- agentpool/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- agentpool/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- agentpool/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- agentpool/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- agentpool/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- agentpool/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- agentpool/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- agentpool/queries/tree-sitter-languages/README.md +24 -0
- agentpool/queries/tree-sitter-languages/c-tags.scm +9 -0
- agentpool/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- agentpool/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- agentpool/queries/tree-sitter-languages/dart-tags.scm +91 -0
- agentpool/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- agentpool/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- agentpool/queries/tree-sitter-languages/elm-tags.scm +19 -0
- agentpool/queries/tree-sitter-languages/fortran-tags.scm +15 -0
- agentpool/queries/tree-sitter-languages/go-tags.scm +30 -0
- agentpool/queries/tree-sitter-languages/haskell-tags.scm +3 -0
- agentpool/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- agentpool/queries/tree-sitter-languages/java-tags.scm +20 -0
- agentpool/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- agentpool/queries/tree-sitter-languages/julia-tags.scm +60 -0
- agentpool/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- agentpool/queries/tree-sitter-languages/matlab-tags.scm +10 -0
- agentpool/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- agentpool/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- agentpool/queries/tree-sitter-languages/php-tags.scm +26 -0
- agentpool/queries/tree-sitter-languages/python-tags.scm +12 -0
- agentpool/queries/tree-sitter-languages/ql-tags.scm +26 -0
- agentpool/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- agentpool/queries/tree-sitter-languages/rust-tags.scm +60 -0
- agentpool/queries/tree-sitter-languages/scala-tags.scm +65 -0
- agentpool/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- agentpool/queries/tree-sitter-languages/zig-tags.scm +3 -0
- agentpool/repomap.py +1231 -0
- agentpool/resource_providers/__init__.py +17 -0
- agentpool/resource_providers/aggregating.py +54 -0
- agentpool/resource_providers/base.py +172 -0
- agentpool/resource_providers/codemode/__init__.py +9 -0
- agentpool/resource_providers/codemode/code_executor.py +215 -0
- agentpool/resource_providers/codemode/default_prompt.py +19 -0
- agentpool/resource_providers/codemode/helpers.py +83 -0
- agentpool/resource_providers/codemode/progress_executor.py +212 -0
- agentpool/resource_providers/codemode/provider.py +150 -0
- agentpool/resource_providers/codemode/remote_mcp_execution.py +143 -0
- agentpool/resource_providers/codemode/remote_provider.py +171 -0
- agentpool/resource_providers/filtering.py +42 -0
- agentpool/resource_providers/mcp_provider.py +246 -0
- agentpool/resource_providers/plan_provider.py +196 -0
- agentpool/resource_providers/pool.py +69 -0
- agentpool/resource_providers/static.py +289 -0
- agentpool/running/__init__.py +20 -0
- agentpool/running/decorators.py +56 -0
- agentpool/running/discovery.py +101 -0
- agentpool/running/executor.py +284 -0
- agentpool/running/injection.py +111 -0
- agentpool/running/py.typed +0 -0
- agentpool/running/run_nodes.py +87 -0
- agentpool/server.py +122 -0
- agentpool/sessions/__init__.py +13 -0
- agentpool/sessions/manager.py +302 -0
- agentpool/sessions/models.py +71 -0
- agentpool/sessions/session.py +239 -0
- agentpool/sessions/store.py +163 -0
- agentpool/skills/__init__.py +5 -0
- agentpool/skills/manager.py +120 -0
- agentpool/skills/registry.py +210 -0
- agentpool/skills/skill.py +36 -0
- agentpool/storage/__init__.py +17 -0
- agentpool/storage/manager.py +419 -0
- agentpool/storage/serialization.py +136 -0
- agentpool/talk/__init__.py +13 -0
- agentpool/talk/registry.py +128 -0
- agentpool/talk/stats.py +159 -0
- agentpool/talk/talk.py +604 -0
- agentpool/tasks/__init__.py +20 -0
- agentpool/tasks/exceptions.py +25 -0
- agentpool/tasks/registry.py +33 -0
- agentpool/testing.py +129 -0
- agentpool/text_templates/__init__.py +39 -0
- agentpool/text_templates/system_prompt.jinja +30 -0
- agentpool/text_templates/tool_call_default.jinja +13 -0
- agentpool/text_templates/tool_call_markdown.jinja +25 -0
- agentpool/text_templates/tool_call_simple.jinja +5 -0
- agentpool/tools/__init__.py +16 -0
- agentpool/tools/base.py +269 -0
- agentpool/tools/exceptions.py +9 -0
- agentpool/tools/manager.py +255 -0
- agentpool/tools/tool_call_info.py +87 -0
- agentpool/ui/__init__.py +2 -0
- agentpool/ui/base.py +89 -0
- agentpool/ui/mock_provider.py +81 -0
- agentpool/ui/stdlib_provider.py +150 -0
- agentpool/utils/__init__.py +44 -0
- agentpool/utils/baseregistry.py +185 -0
- agentpool/utils/count_tokens.py +62 -0
- agentpool/utils/dag.py +184 -0
- agentpool/utils/importing.py +206 -0
- agentpool/utils/inspection.py +334 -0
- agentpool/utils/model_capabilities.py +25 -0
- agentpool/utils/network.py +28 -0
- agentpool/utils/now.py +22 -0
- agentpool/utils/parse_time.py +87 -0
- agentpool/utils/result_utils.py +35 -0
- agentpool/utils/signatures.py +305 -0
- agentpool/utils/streams.py +112 -0
- agentpool/utils/tasks.py +186 -0
- agentpool/vfs_registry.py +250 -0
- agentpool-2.1.9.dist-info/METADATA +336 -0
- agentpool-2.1.9.dist-info/RECORD +474 -0
- agentpool-2.1.9.dist-info/WHEEL +4 -0
- agentpool-2.1.9.dist-info/entry_points.txt +14 -0
- agentpool-2.1.9.dist-info/licenses/LICENSE +22 -0
- agentpool_cli/__init__.py +34 -0
- agentpool_cli/__main__.py +66 -0
- agentpool_cli/agent.py +175 -0
- agentpool_cli/cli_types.py +23 -0
- agentpool_cli/common.py +163 -0
- agentpool_cli/create.py +175 -0
- agentpool_cli/history.py +217 -0
- agentpool_cli/log.py +78 -0
- agentpool_cli/py.typed +0 -0
- agentpool_cli/run.py +84 -0
- agentpool_cli/serve_acp.py +177 -0
- agentpool_cli/serve_api.py +69 -0
- agentpool_cli/serve_mcp.py +74 -0
- agentpool_cli/serve_vercel.py +233 -0
- agentpool_cli/store.py +171 -0
- agentpool_cli/task.py +84 -0
- agentpool_cli/utils.py +104 -0
- agentpool_cli/watch.py +54 -0
- agentpool_commands/__init__.py +180 -0
- agentpool_commands/agents.py +199 -0
- agentpool_commands/base.py +45 -0
- agentpool_commands/commands.py +58 -0
- agentpool_commands/completers.py +110 -0
- agentpool_commands/connections.py +175 -0
- agentpool_commands/markdown_utils.py +31 -0
- agentpool_commands/models.py +62 -0
- agentpool_commands/prompts.py +78 -0
- agentpool_commands/py.typed +0 -0
- agentpool_commands/read.py +77 -0
- agentpool_commands/resources.py +210 -0
- agentpool_commands/session.py +48 -0
- agentpool_commands/tools.py +269 -0
- agentpool_commands/utils.py +189 -0
- agentpool_commands/workers.py +163 -0
- agentpool_config/__init__.py +53 -0
- agentpool_config/builtin_tools.py +265 -0
- agentpool_config/commands.py +237 -0
- agentpool_config/conditions.py +301 -0
- agentpool_config/converters.py +30 -0
- agentpool_config/durable.py +331 -0
- agentpool_config/event_handlers.py +600 -0
- agentpool_config/events.py +153 -0
- agentpool_config/forward_targets.py +251 -0
- agentpool_config/hook_conditions.py +331 -0
- agentpool_config/hooks.py +241 -0
- agentpool_config/jinja.py +206 -0
- agentpool_config/knowledge.py +41 -0
- agentpool_config/loaders.py +350 -0
- agentpool_config/mcp_server.py +243 -0
- agentpool_config/nodes.py +202 -0
- agentpool_config/observability.py +191 -0
- agentpool_config/output_types.py +55 -0
- agentpool_config/pool_server.py +267 -0
- agentpool_config/prompt_hubs.py +105 -0
- agentpool_config/prompts.py +185 -0
- agentpool_config/py.typed +0 -0
- agentpool_config/resources.py +33 -0
- agentpool_config/session.py +119 -0
- agentpool_config/skills.py +17 -0
- agentpool_config/storage.py +288 -0
- agentpool_config/system_prompts.py +190 -0
- agentpool_config/task.py +162 -0
- agentpool_config/teams.py +52 -0
- agentpool_config/tools.py +112 -0
- agentpool_config/toolsets.py +1033 -0
- agentpool_config/workers.py +86 -0
- agentpool_prompts/__init__.py +1 -0
- agentpool_prompts/braintrust_hub.py +235 -0
- agentpool_prompts/fabric.py +75 -0
- agentpool_prompts/langfuse_hub.py +79 -0
- agentpool_prompts/promptlayer_provider.py +59 -0
- agentpool_prompts/py.typed +0 -0
- agentpool_server/__init__.py +9 -0
- agentpool_server/a2a_server/__init__.py +5 -0
- agentpool_server/a2a_server/a2a_types.py +41 -0
- agentpool_server/a2a_server/server.py +190 -0
- agentpool_server/a2a_server/storage.py +81 -0
- agentpool_server/acp_server/__init__.py +22 -0
- agentpool_server/acp_server/acp_agent.py +786 -0
- agentpool_server/acp_server/acp_tools.py +43 -0
- agentpool_server/acp_server/commands/__init__.py +18 -0
- agentpool_server/acp_server/commands/acp_commands.py +594 -0
- agentpool_server/acp_server/commands/debug_commands.py +376 -0
- agentpool_server/acp_server/commands/docs_commands/__init__.py +39 -0
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +169 -0
- agentpool_server/acp_server/commands/docs_commands/get_schema.py +176 -0
- agentpool_server/acp_server/commands/docs_commands/get_source.py +110 -0
- agentpool_server/acp_server/commands/docs_commands/git_diff.py +111 -0
- agentpool_server/acp_server/commands/docs_commands/helpers.py +33 -0
- agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py +90 -0
- agentpool_server/acp_server/commands/spawn.py +210 -0
- agentpool_server/acp_server/converters.py +235 -0
- agentpool_server/acp_server/input_provider.py +338 -0
- agentpool_server/acp_server/server.py +288 -0
- agentpool_server/acp_server/session.py +969 -0
- agentpool_server/acp_server/session_manager.py +313 -0
- agentpool_server/acp_server/syntax_detection.py +250 -0
- agentpool_server/acp_server/zed_tools.md +90 -0
- agentpool_server/aggregating_server.py +309 -0
- agentpool_server/agui_server/__init__.py +11 -0
- agentpool_server/agui_server/server.py +128 -0
- agentpool_server/base.py +189 -0
- agentpool_server/http_server.py +164 -0
- agentpool_server/mcp_server/__init__.py +6 -0
- agentpool_server/mcp_server/server.py +314 -0
- agentpool_server/mcp_server/zed_wrapper.py +110 -0
- agentpool_server/openai_api_server/__init__.py +5 -0
- agentpool_server/openai_api_server/completions/__init__.py +1 -0
- agentpool_server/openai_api_server/completions/helpers.py +81 -0
- agentpool_server/openai_api_server/completions/models.py +98 -0
- agentpool_server/openai_api_server/responses/__init__.py +1 -0
- agentpool_server/openai_api_server/responses/helpers.py +74 -0
- agentpool_server/openai_api_server/responses/models.py +96 -0
- agentpool_server/openai_api_server/server.py +242 -0
- agentpool_server/py.typed +0 -0
- agentpool_storage/__init__.py +9 -0
- agentpool_storage/base.py +310 -0
- agentpool_storage/file_provider.py +378 -0
- agentpool_storage/formatters.py +129 -0
- agentpool_storage/memory_provider.py +396 -0
- agentpool_storage/models.py +108 -0
- agentpool_storage/py.typed +0 -0
- agentpool_storage/session_store.py +262 -0
- agentpool_storage/sql_provider/__init__.py +21 -0
- agentpool_storage/sql_provider/cli.py +146 -0
- agentpool_storage/sql_provider/models.py +249 -0
- agentpool_storage/sql_provider/queries.py +15 -0
- agentpool_storage/sql_provider/sql_provider.py +444 -0
- agentpool_storage/sql_provider/utils.py +234 -0
- agentpool_storage/text_log_provider.py +275 -0
- agentpool_toolsets/__init__.py +15 -0
- agentpool_toolsets/builtin/__init__.py +33 -0
- agentpool_toolsets/builtin/agent_management.py +239 -0
- agentpool_toolsets/builtin/chain.py +288 -0
- agentpool_toolsets/builtin/code.py +398 -0
- agentpool_toolsets/builtin/debug.py +291 -0
- agentpool_toolsets/builtin/execution_environment.py +381 -0
- agentpool_toolsets/builtin/file_edit/__init__.py +11 -0
- agentpool_toolsets/builtin/file_edit/file_edit.py +747 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/__init__.py +5 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/example_usage.py +311 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/streaming_fuzzy_matcher.py +443 -0
- agentpool_toolsets/builtin/history.py +36 -0
- agentpool_toolsets/builtin/integration.py +85 -0
- agentpool_toolsets/builtin/skills.py +77 -0
- agentpool_toolsets/builtin/subagent_tools.py +324 -0
- agentpool_toolsets/builtin/tool_management.py +90 -0
- agentpool_toolsets/builtin/user_interaction.py +52 -0
- agentpool_toolsets/builtin/workers.py +128 -0
- agentpool_toolsets/composio_toolset.py +96 -0
- agentpool_toolsets/config_creation.py +192 -0
- agentpool_toolsets/entry_points.py +47 -0
- agentpool_toolsets/fsspec_toolset/__init__.py +7 -0
- agentpool_toolsets/fsspec_toolset/diagnostics.py +115 -0
- agentpool_toolsets/fsspec_toolset/grep.py +450 -0
- agentpool_toolsets/fsspec_toolset/helpers.py +631 -0
- agentpool_toolsets/fsspec_toolset/streaming_diff_parser.py +249 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +1384 -0
- agentpool_toolsets/mcp_run_toolset.py +61 -0
- agentpool_toolsets/notifications.py +146 -0
- agentpool_toolsets/openapi.py +118 -0
- agentpool_toolsets/py.typed +0 -0
- agentpool_toolsets/search_toolset.py +202 -0
- agentpool_toolsets/semantic_memory_toolset.py +536 -0
- agentpool_toolsets/streaming_tools.py +265 -0
- agentpool_toolsets/vfs_toolset.py +124 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
"""MCP server bridge for exposing ToolManager tools to ACP agents.
|
|
2
|
+
|
|
3
|
+
This module provides a bridge that exposes a ToolManager's tools as an MCP server
|
|
4
|
+
using HTTP transport. This allows ACP agents (external agents like Claude Code,
|
|
5
|
+
Gemini CLI, etc.) to use our internal toolsets like SubagentTools,
|
|
6
|
+
AgentManagementTools, etc.
|
|
7
|
+
|
|
8
|
+
The bridge runs in-process on the same event loop, providing direct access to
|
|
9
|
+
the pool and avoiding IPC serialization overhead.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
from contextlib import asynccontextmanager, suppress
|
|
16
|
+
from dataclasses import dataclass, field, replace
|
|
17
|
+
import inspect
|
|
18
|
+
from typing import TYPE_CHECKING, Any, Literal, Self, get_args, get_origin
|
|
19
|
+
from uuid import uuid4
|
|
20
|
+
|
|
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
|
+
from pydantic import BaseModel, HttpUrl
|
|
26
|
+
|
|
27
|
+
from agentpool.agents import Agent, ClaudeCodeAgent
|
|
28
|
+
from agentpool.agents.acp_agent.acp_agent import ACPAgent
|
|
29
|
+
from agentpool.log import get_logger
|
|
30
|
+
from agentpool.utils.signatures import filter_schema_params, get_params_matching_predicate
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from collections.abc import AsyncIterator, Callable
|
|
35
|
+
|
|
36
|
+
from claude_agent_sdk.types import McpServerConfig
|
|
37
|
+
from fastmcp import Context
|
|
38
|
+
from fastmcp.tools.tool import ToolResult
|
|
39
|
+
from pydantic_ai import RunContext
|
|
40
|
+
from uvicorn import Server
|
|
41
|
+
|
|
42
|
+
from acp.schema.mcp import HttpMcpServer, SseMcpServer
|
|
43
|
+
from agentpool.agents import AgentContext
|
|
44
|
+
from agentpool.agents.base_agent import BaseAgent
|
|
45
|
+
from agentpool.tools.base import Tool
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
logger = get_logger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _is_agent_context_type(annotation: Any) -> bool:
|
|
52
|
+
"""Check if annotation is AgentContext."""
|
|
53
|
+
if annotation is None or annotation is inspect.Parameter.empty:
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
# Handle string annotations (forward references)
|
|
57
|
+
if isinstance(annotation, str):
|
|
58
|
+
base_name = annotation.split("[")[0].strip()
|
|
59
|
+
return base_name == "AgentContext"
|
|
60
|
+
|
|
61
|
+
# Check direct class match by name
|
|
62
|
+
if isinstance(annotation, type) and annotation.__name__ == "AgentContext":
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
# Check generic origin (e.g., AgentContext[SomeDeps])
|
|
66
|
+
origin = get_origin(annotation)
|
|
67
|
+
if origin is not None:
|
|
68
|
+
if isinstance(origin, type) and origin.__name__ == "AgentContext":
|
|
69
|
+
return True
|
|
70
|
+
# Handle Union types (e.g., AgentContext | None)
|
|
71
|
+
if origin is type(None) or str(origin) in ("typing.Union", "types.UnionType"):
|
|
72
|
+
return any(_is_agent_context_type(arg) for arg in get_args(annotation))
|
|
73
|
+
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _get_context_param_names(fn: Callable[..., Any]) -> set[str]:
|
|
78
|
+
"""Get parameter names that are AgentContext (to be auto-injected)."""
|
|
79
|
+
return get_params_matching_predicate(fn, lambda p: _is_agent_context_type(p.annotation))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _is_run_context_type(annotation: Any) -> bool:
|
|
83
|
+
"""Check if annotation is pydantic-ai RunContext."""
|
|
84
|
+
if annotation is None or annotation is inspect.Parameter.empty:
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
# Handle string annotations (forward references)
|
|
88
|
+
if isinstance(annotation, str):
|
|
89
|
+
base_name = annotation.split("[")[0].strip()
|
|
90
|
+
return base_name == "RunContext"
|
|
91
|
+
|
|
92
|
+
# Check direct class match by name
|
|
93
|
+
if isinstance(annotation, type) and annotation.__name__ == "RunContext":
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
# Check generic origin (e.g., RunContext[SomeDeps])
|
|
97
|
+
origin = get_origin(annotation)
|
|
98
|
+
if origin is not None:
|
|
99
|
+
if isinstance(origin, type) and origin.__name__ == "RunContext":
|
|
100
|
+
return True
|
|
101
|
+
# Handle Union types (e.g., RunContext | None)
|
|
102
|
+
if origin is type(None) or str(origin) in ("typing.Union", "types.UnionType"):
|
|
103
|
+
return any(_is_run_context_type(arg) for arg in get_args(annotation))
|
|
104
|
+
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _get_run_context_param_names(fn: Callable[..., Any]) -> set[str]:
|
|
109
|
+
"""Get parameter names that are RunContext (to be auto-injected)."""
|
|
110
|
+
return get_params_matching_predicate(fn, lambda p: _is_run_context_type(p.annotation))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _create_stub_run_context(ctx: AgentContext[Any]) -> RunContext[Any]:
|
|
114
|
+
"""Create a stub RunContext from AgentContext for MCP bridge tool invocations.
|
|
115
|
+
|
|
116
|
+
This provides a best-effort RunContext for tools that require it when
|
|
117
|
+
invoked via MCP bridge. Not all fields can be populated accurately since
|
|
118
|
+
we're outside of pydantic-ai's normal execution flow.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
ctx: The AgentContext available in the bridge
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
A RunContext with available information populated
|
|
125
|
+
"""
|
|
126
|
+
from pydantic_ai import RunContext
|
|
127
|
+
from pydantic_ai.models.test import TestModel
|
|
128
|
+
from pydantic_ai.usage import RunUsage
|
|
129
|
+
|
|
130
|
+
match ctx.agent:
|
|
131
|
+
case Agent():
|
|
132
|
+
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()
|
|
138
|
+
case _:
|
|
139
|
+
model = TestModel()
|
|
140
|
+
# Create a minimal usage object
|
|
141
|
+
return RunContext(
|
|
142
|
+
deps=ctx.data,
|
|
143
|
+
model=model,
|
|
144
|
+
usage=RunUsage(),
|
|
145
|
+
prompt=None,
|
|
146
|
+
messages=[],
|
|
147
|
+
tool_name=ctx.tool_name,
|
|
148
|
+
tool_call_id=ctx.tool_call_id,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _convert_to_tool_result(result: Any) -> ToolResult:
|
|
153
|
+
"""Convert a tool's return value to a FastMCP ToolResult.
|
|
154
|
+
|
|
155
|
+
Handles different result types appropriately:
|
|
156
|
+
- ToolResult: Pass through unchanged
|
|
157
|
+
- dict: Use as structured_content (enables programmatic access by clients)
|
|
158
|
+
- Pydantic models: Serialize to dict for structured_content
|
|
159
|
+
- Other types: Pass to ToolResult(content=...) which handles conversion internally
|
|
160
|
+
"""
|
|
161
|
+
from fastmcp.tools.tool import ToolResult
|
|
162
|
+
|
|
163
|
+
# Already a ToolResult - pass through
|
|
164
|
+
if isinstance(result, ToolResult):
|
|
165
|
+
return result
|
|
166
|
+
# Dict - use as structured_content (FastMCP auto-populates content as JSON)
|
|
167
|
+
if isinstance(result, dict):
|
|
168
|
+
return ToolResult(structured_content=result)
|
|
169
|
+
# Pydantic model - serialize to dict for structured_content
|
|
170
|
+
if isinstance(result, BaseModel):
|
|
171
|
+
return ToolResult(structured_content=result.model_dump(mode="json"))
|
|
172
|
+
# All other types (str, list, ContentBlock, Image, None, primitives, etc.)
|
|
173
|
+
# ToolResult's internal _convert_to_content handles these correctly
|
|
174
|
+
return ToolResult(content=result if result is not None else "")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _extract_tool_call_id(context: Context | None) -> str:
|
|
178
|
+
"""Extract Claude's original tool_call_id from request metadata.
|
|
179
|
+
|
|
180
|
+
Claude Code passes the tool_use_id via the _meta field as
|
|
181
|
+
'claudecode/toolUseId'. This allows us to maintain consistent
|
|
182
|
+
tool_call_ids across ToolCallStartEvent and ToolCallCompleteEvent.
|
|
183
|
+
|
|
184
|
+
Falls back to generating a UUID if not available.
|
|
185
|
+
"""
|
|
186
|
+
if context is not None:
|
|
187
|
+
try:
|
|
188
|
+
request_ctx = context.request_context
|
|
189
|
+
if request_ctx and request_ctx.meta:
|
|
190
|
+
# Access extra fields on the Meta object (extra="allow" in pydantic)
|
|
191
|
+
meta_dict = request_ctx.meta.model_dump()
|
|
192
|
+
claude_tool_id = meta_dict.get("claudecode/toolUseId")
|
|
193
|
+
if isinstance(claude_tool_id, str):
|
|
194
|
+
logger.debug("Extracted Claude tool_call_id", tool_call_id=claude_tool_id)
|
|
195
|
+
return claude_tool_id
|
|
196
|
+
except (AttributeError, LookupError) as e:
|
|
197
|
+
logger.warning("Error extracting tool_call_id from MCP context", error=str(e))
|
|
198
|
+
# Generate fallback UUID if no tool_call_id found in meta
|
|
199
|
+
return str(uuid4())
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@dataclass
|
|
203
|
+
class BridgeConfig:
|
|
204
|
+
"""Configuration for the ToolManager MCP bridge."""
|
|
205
|
+
|
|
206
|
+
host: str = "127.0.0.1"
|
|
207
|
+
"""Host to bind the HTTP server to."""
|
|
208
|
+
|
|
209
|
+
port: int = 0
|
|
210
|
+
"""Port to bind to (0 = auto-select available port)."""
|
|
211
|
+
|
|
212
|
+
transport: Literal["sse", "streamable-http"] = "sse"
|
|
213
|
+
"""Transport protocol: 'sse' or 'streamable-http'."""
|
|
214
|
+
|
|
215
|
+
server_name: str = "agentpool-toolmanager"
|
|
216
|
+
"""Name for the MCP server."""
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@dataclass
|
|
220
|
+
class ToolManagerBridge:
|
|
221
|
+
"""Exposes a node's tools as an MCP server for ACP agents.
|
|
222
|
+
|
|
223
|
+
This bridge allows external ACP agents to access our internal toolsets
|
|
224
|
+
(SubagentTools, AgentManagementTools, etc.) via HTTP MCP transport.
|
|
225
|
+
|
|
226
|
+
The node's existing context is used for tool invocations, providing
|
|
227
|
+
pool access and proper configuration without reconstruction.
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
```python
|
|
231
|
+
async with AgentPool() as pool:
|
|
232
|
+
agent = pool.agents["my_agent"]
|
|
233
|
+
bridge = ToolManagerBridge(node=agent, config=BridgeConfig(port=8765))
|
|
234
|
+
async with bridge:
|
|
235
|
+
# Bridge is running, get MCP config for ACP agent
|
|
236
|
+
mcp_config = bridge.get_mcp_server_config()
|
|
237
|
+
# Pass to ACP agent...
|
|
238
|
+
```
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
node: BaseAgent[Any, Any]
|
|
242
|
+
"""The node whose tools to expose."""
|
|
243
|
+
|
|
244
|
+
config: BridgeConfig = field(default_factory=BridgeConfig)
|
|
245
|
+
"""Bridge configuration."""
|
|
246
|
+
|
|
247
|
+
_mcp: FastMCP | None = field(default=None, init=False, repr=False)
|
|
248
|
+
"""FastMCP server instance."""
|
|
249
|
+
|
|
250
|
+
_server: Server | None = field(default=None, init=False, repr=False)
|
|
251
|
+
"""Uvicorn server instance."""
|
|
252
|
+
|
|
253
|
+
_server_task: asyncio.Task[None] | None = field(default=None, init=False, repr=False)
|
|
254
|
+
"""Background task running the server."""
|
|
255
|
+
|
|
256
|
+
_actual_port: int | None = field(default=None, init=False, repr=False)
|
|
257
|
+
"""Actual port the server is bound to."""
|
|
258
|
+
|
|
259
|
+
async def __aenter__(self) -> Self:
|
|
260
|
+
"""Start the MCP server."""
|
|
261
|
+
await self.start()
|
|
262
|
+
return self
|
|
263
|
+
|
|
264
|
+
async def __aexit__(self, *args: object) -> None:
|
|
265
|
+
"""Stop the MCP server."""
|
|
266
|
+
await self.stop()
|
|
267
|
+
|
|
268
|
+
async def start(self) -> None:
|
|
269
|
+
"""Start the HTTP MCP server in the background."""
|
|
270
|
+
self._mcp = FastMCP(name=self.config.server_name)
|
|
271
|
+
await self._register_tools()
|
|
272
|
+
await self._start_server()
|
|
273
|
+
|
|
274
|
+
async def stop(self) -> None:
|
|
275
|
+
"""Stop the HTTP MCP server."""
|
|
276
|
+
if self._server:
|
|
277
|
+
self._server.should_exit = True
|
|
278
|
+
if self._server_task:
|
|
279
|
+
try:
|
|
280
|
+
await asyncio.wait_for(self._server_task, timeout=5.0)
|
|
281
|
+
except TimeoutError:
|
|
282
|
+
self._server_task.cancel()
|
|
283
|
+
with suppress(asyncio.CancelledError):
|
|
284
|
+
await self._server_task
|
|
285
|
+
self._server = None
|
|
286
|
+
self._server_task = None
|
|
287
|
+
self._mcp = None
|
|
288
|
+
self._actual_port = None
|
|
289
|
+
logger.info("ToolManagerBridge stopped")
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def port(self) -> int:
|
|
293
|
+
"""Get the actual port the server is running on."""
|
|
294
|
+
if self._actual_port is None:
|
|
295
|
+
msg = "Server not started"
|
|
296
|
+
raise RuntimeError(msg)
|
|
297
|
+
return self._actual_port
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def url(self) -> str:
|
|
301
|
+
"""Get the server URL."""
|
|
302
|
+
path = "/sse" if self.config.transport == "sse" else "/mcp"
|
|
303
|
+
return f"http://{self.config.host}:{self.port}{path}"
|
|
304
|
+
|
|
305
|
+
def get_mcp_server_config(self) -> HttpMcpServer | SseMcpServer:
|
|
306
|
+
"""Get ACP-compatible MCP server configuration.
|
|
307
|
+
|
|
308
|
+
Returns config suitable for passing to ACP agent's NewSessionRequest.
|
|
309
|
+
"""
|
|
310
|
+
from acp.schema import HttpMcpServer, SseMcpServer
|
|
311
|
+
|
|
312
|
+
url = HttpUrl(self.url)
|
|
313
|
+
if self.config.transport == "sse":
|
|
314
|
+
return SseMcpServer(name=self.config.server_name, url=url, headers=[])
|
|
315
|
+
return HttpMcpServer(name=self.config.server_name, url=url, headers=[])
|
|
316
|
+
|
|
317
|
+
def get_claude_mcp_server_config(self) -> dict[str, McpServerConfig]:
|
|
318
|
+
"""Get Claude Agent SDK-compatible MCP server configuration.
|
|
319
|
+
|
|
320
|
+
Returns a dict suitable for passing to ClaudeAgentOptions.mcp_servers.
|
|
321
|
+
Uses HTTP transport to preserve MCP _meta field which contains
|
|
322
|
+
claudecode/toolUseId needed for proper tool_call_id correlation.
|
|
323
|
+
|
|
324
|
+
Note: SDK transport (passing FastMCP instance directly) would be faster
|
|
325
|
+
but the Claude Agent SDK's _handle_sdk_mcp_request drops the _meta field,
|
|
326
|
+
breaking tool_call_id propagation for progress events.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Dict mapping server name to McpHttpServerConfig
|
|
330
|
+
|
|
331
|
+
Raises:
|
|
332
|
+
RuntimeError: If bridge not started (no server running)
|
|
333
|
+
"""
|
|
334
|
+
if self._actual_port is None:
|
|
335
|
+
msg = "Bridge not started - call start() first"
|
|
336
|
+
raise RuntimeError(msg)
|
|
337
|
+
|
|
338
|
+
# Use HTTP transport to preserve _meta field with claudecode/toolUseId
|
|
339
|
+
# SDK transport drops _meta in Claude Agent SDK's query.py
|
|
340
|
+
url = f"http://{self.config.host}:{self.port}/mcp"
|
|
341
|
+
return {self.config.server_name: {"type": "http", "url": url}}
|
|
342
|
+
|
|
343
|
+
async def _register_tools(self) -> None:
|
|
344
|
+
"""Register all node tools with the FastMCP server."""
|
|
345
|
+
if not self._mcp:
|
|
346
|
+
return
|
|
347
|
+
|
|
348
|
+
tools = await self.node.tools.get_tools(state="enabled")
|
|
349
|
+
for tool in tools:
|
|
350
|
+
self._register_single_tool(tool)
|
|
351
|
+
logger.info("Registered tools with MCP bridge", tools=[t.name for t in tools])
|
|
352
|
+
|
|
353
|
+
def _register_single_tool(self, tool: Tool) -> None:
|
|
354
|
+
"""Register a single tool with the FastMCP server."""
|
|
355
|
+
if not self._mcp:
|
|
356
|
+
return
|
|
357
|
+
# Create a custom FastMCP Tool that wraps our tool
|
|
358
|
+
bridge_tool = _BridgeTool(tool=tool, bridge=self)
|
|
359
|
+
self._mcp.add_tool(bridge_tool)
|
|
360
|
+
|
|
361
|
+
async def invoke_tool_with_context(
|
|
362
|
+
self,
|
|
363
|
+
tool: Tool,
|
|
364
|
+
ctx: AgentContext[Any],
|
|
365
|
+
kwargs: dict[str, Any],
|
|
366
|
+
) -> Any:
|
|
367
|
+
"""Invoke a tool with proper context injection.
|
|
368
|
+
|
|
369
|
+
Handles tools that expect AgentContext, RunContext, or neither.
|
|
370
|
+
"""
|
|
371
|
+
fn = tool.callable
|
|
372
|
+
|
|
373
|
+
# Inject AgentContext parameters
|
|
374
|
+
context_param_names = _get_context_param_names(fn)
|
|
375
|
+
for param_name in context_param_names:
|
|
376
|
+
if param_name not in kwargs:
|
|
377
|
+
kwargs[param_name] = ctx
|
|
378
|
+
|
|
379
|
+
# Inject RunContext parameters (as stub since we're outside pydantic-ai)
|
|
380
|
+
run_context_param_names = _get_run_context_param_names(fn)
|
|
381
|
+
if run_context_param_names:
|
|
382
|
+
stub_run_ctx = _create_stub_run_context(ctx)
|
|
383
|
+
for param_name in run_context_param_names:
|
|
384
|
+
if param_name not in kwargs:
|
|
385
|
+
kwargs[param_name] = stub_run_ctx
|
|
386
|
+
|
|
387
|
+
# Execute the tool
|
|
388
|
+
result = fn(**kwargs)
|
|
389
|
+
if inspect.isawaitable(result):
|
|
390
|
+
result = await result
|
|
391
|
+
return result
|
|
392
|
+
|
|
393
|
+
async def _start_server(self) -> None:
|
|
394
|
+
"""Start the uvicorn server in the background."""
|
|
395
|
+
import socket
|
|
396
|
+
|
|
397
|
+
import uvicorn
|
|
398
|
+
|
|
399
|
+
if not self._mcp:
|
|
400
|
+
msg = "MCP server not initialized"
|
|
401
|
+
raise RuntimeError(msg)
|
|
402
|
+
|
|
403
|
+
# Determine actual port (auto-select if 0)
|
|
404
|
+
port = self.config.port
|
|
405
|
+
if port == 0:
|
|
406
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
407
|
+
s.bind((self.config.host, 0))
|
|
408
|
+
port = s.getsockname()[1]
|
|
409
|
+
self._actual_port = port
|
|
410
|
+
# Create the ASGI app
|
|
411
|
+
app = self._mcp.http_app(transport=self.config.transport)
|
|
412
|
+
# Configure uvicorn
|
|
413
|
+
cfg = uvicorn.Config(app=app, host=self.config.host, port=port, log_level="warning")
|
|
414
|
+
self._server = uvicorn.Server(cfg)
|
|
415
|
+
# Start server in background task
|
|
416
|
+
name = f"mcp-bridge-{self.config.server_name}"
|
|
417
|
+
self._server_task = asyncio.create_task(self._server.serve(), name=name)
|
|
418
|
+
await anyio.sleep(0.1) # Wait briefly for server to start
|
|
419
|
+
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)
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
@asynccontextmanager
|
|
471
|
+
async def create_tool_bridge(
|
|
472
|
+
node: BaseAgent[Any, Any],
|
|
473
|
+
*,
|
|
474
|
+
host: str = "127.0.0.1",
|
|
475
|
+
port: int = 0,
|
|
476
|
+
transport: Literal["sse", "streamable-http"] = "sse",
|
|
477
|
+
) -> AsyncIterator[ToolManagerBridge]:
|
|
478
|
+
"""Create and start a ToolManagerBridge as a context manager.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
node: The node whose tools to expose
|
|
482
|
+
host: Host to bind to
|
|
483
|
+
port: Port to bind to (0 = auto-select)
|
|
484
|
+
transport: Transport protocol ('sse' or 'streamable-http')
|
|
485
|
+
|
|
486
|
+
Yields:
|
|
487
|
+
Running ToolManagerBridge instance
|
|
488
|
+
"""
|
|
489
|
+
config = BridgeConfig(host=host, port=port, transport=transport)
|
|
490
|
+
bridge = ToolManagerBridge(node=node, config=config)
|
|
491
|
+
async with bridge:
|
|
492
|
+
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()]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Core messsaging classes for AgentPool."""
|
|
2
|
+
|
|
3
|
+
from agentpool.messaging.messages import ChatMessage, TokenCost, AgentResponse, TeamResponse
|
|
4
|
+
from agentpool.messaging.message_container import ChatMessageList
|
|
5
|
+
from agentpool.messaging.event_manager import EventManager
|
|
6
|
+
from agentpool.messaging.messagenode import MessageNode
|
|
7
|
+
from agentpool.messaging.message_history import MessageHistory
|
|
8
|
+
from agentpool.messaging.compaction import (
|
|
9
|
+
CompactionPipeline,
|
|
10
|
+
CompactionPipelineConfig,
|
|
11
|
+
CompactionStep,
|
|
12
|
+
FilterBinaryContent,
|
|
13
|
+
FilterEmptyMessages,
|
|
14
|
+
FilterRetryPrompts,
|
|
15
|
+
FilterThinking,
|
|
16
|
+
FilterToolCalls,
|
|
17
|
+
KeepFirstAndLast,
|
|
18
|
+
KeepFirstMessages,
|
|
19
|
+
KeepLastMessages,
|
|
20
|
+
Summarize,
|
|
21
|
+
TokenBudget,
|
|
22
|
+
TruncateTextParts,
|
|
23
|
+
TruncateToolOutputs,
|
|
24
|
+
WhenMessageCountExceeds,
|
|
25
|
+
balanced_context,
|
|
26
|
+
minimal_context,
|
|
27
|
+
summarizing_context,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"AgentResponse",
|
|
32
|
+
"ChatMessage",
|
|
33
|
+
"ChatMessageList",
|
|
34
|
+
"CompactionPipeline",
|
|
35
|
+
"CompactionPipelineConfig",
|
|
36
|
+
"CompactionStep",
|
|
37
|
+
"EventManager",
|
|
38
|
+
"FilterBinaryContent",
|
|
39
|
+
"FilterEmptyMessages",
|
|
40
|
+
"FilterRetryPrompts",
|
|
41
|
+
"FilterThinking",
|
|
42
|
+
"FilterToolCalls",
|
|
43
|
+
"KeepFirstAndLast",
|
|
44
|
+
"KeepFirstMessages",
|
|
45
|
+
"KeepLastMessages",
|
|
46
|
+
"MessageHistory",
|
|
47
|
+
"MessageNode",
|
|
48
|
+
"Summarize",
|
|
49
|
+
"TeamResponse",
|
|
50
|
+
"TokenBudget",
|
|
51
|
+
"TokenCost",
|
|
52
|
+
"TruncateTextParts",
|
|
53
|
+
"TruncateToolOutputs",
|
|
54
|
+
"WhenMessageCountExceeds",
|
|
55
|
+
"balanced_context",
|
|
56
|
+
"minimal_context",
|
|
57
|
+
"summarizing_context",
|
|
58
|
+
]
|