agentpool 2.1.9__py3-none-any.whl → 2.2.3__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 -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/notifications.py +2 -1
- acp/stdio.py +39 -9
- acp/transports.py +362 -2
- acp/utils.py +15 -2
- agentpool/__init__.py +4 -1
- agentpool/agents/__init__.py +2 -0
- agentpool/agents/acp_agent/acp_agent.py +203 -88
- agentpool/agents/acp_agent/acp_converters.py +46 -21
- agentpool/agents/acp_agent/client_handler.py +157 -3
- agentpool/agents/acp_agent/session_state.py +4 -1
- agentpool/agents/agent.py +314 -107
- agentpool/agents/agui_agent/__init__.py +0 -2
- agentpool/agents/agui_agent/agui_agent.py +90 -21
- agentpool/agents/agui_agent/agui_converters.py +0 -131
- agentpool/agents/base_agent.py +163 -1
- agentpool/agents/claude_code_agent/claude_code_agent.py +626 -179
- agentpool/agents/claude_code_agent/converters.py +71 -3
- agentpool/agents/claude_code_agent/history.py +474 -0
- agentpool/agents/context.py +40 -0
- agentpool/agents/events/__init__.py +2 -0
- agentpool/agents/events/builtin_handlers.py +2 -1
- agentpool/agents/events/event_emitter.py +29 -2
- agentpool/agents/events/events.py +20 -0
- agentpool/agents/modes.py +54 -0
- agentpool/agents/tool_call_accumulator.py +213 -0
- agentpool/common_types.py +21 -0
- agentpool/config_resources/__init__.py +38 -1
- agentpool/config_resources/claude_code_agent.yml +3 -0
- agentpool/delegation/pool.py +37 -29
- agentpool/delegation/team.py +1 -0
- agentpool/delegation/teamrun.py +1 -0
- 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/mcp_server/__init__.py +0 -2
- agentpool/mcp_server/client.py +12 -3
- agentpool/mcp_server/manager.py +25 -31
- agentpool/mcp_server/registries/official_registry_client.py +25 -0
- agentpool/mcp_server/tool_bridge.py +78 -66
- agentpool/messaging/__init__.py +0 -2
- agentpool/messaging/compaction.py +72 -197
- agentpool/messaging/message_history.py +12 -0
- agentpool/messaging/messages.py +52 -9
- agentpool/messaging/processing.py +3 -1
- agentpool/models/acp_agents/base.py +0 -22
- agentpool/models/acp_agents/mcp_capable.py +8 -148
- agentpool/models/acp_agents/non_mcp.py +129 -72
- agentpool/models/agents.py +35 -13
- agentpool/models/claude_code_agents.py +33 -2
- agentpool/models/manifest.py +43 -0
- agentpool/repomap.py +1 -1
- agentpool/resource_providers/__init__.py +9 -1
- agentpool/resource_providers/aggregating.py +52 -3
- agentpool/resource_providers/base.py +57 -1
- agentpool/resource_providers/mcp_provider.py +23 -0
- agentpool/resource_providers/plan_provider.py +130 -41
- agentpool/resource_providers/pool.py +2 -0
- agentpool/resource_providers/static.py +2 -0
- agentpool/sessions/__init__.py +2 -1
- agentpool/sessions/manager.py +31 -2
- agentpool/sessions/models.py +50 -0
- agentpool/skills/registry.py +13 -8
- agentpool/storage/manager.py +217 -1
- agentpool/testing.py +537 -19
- 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 +690 -1
- agentpool/utils/subprocess_utils.py +155 -0
- agentpool/utils/token_breakdown.py +461 -0
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/METADATA +27 -7
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/RECORD +170 -112
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/WHEEL +1 -1
- agentpool_cli/__main__.py +4 -0
- agentpool_cli/serve_acp.py +41 -20
- agentpool_cli/serve_agui.py +87 -0
- agentpool_cli/serve_opencode.py +119 -0
- agentpool_commands/__init__.py +30 -0
- agentpool_commands/agents.py +74 -1
- agentpool_commands/history.py +62 -0
- agentpool_commands/mcp.py +176 -0
- agentpool_commands/models.py +56 -3
- agentpool_commands/tools.py +57 -0
- agentpool_commands/utils.py +51 -0
- agentpool_config/builtin_tools.py +77 -22
- agentpool_config/commands.py +24 -1
- agentpool_config/compaction.py +258 -0
- agentpool_config/mcp_server.py +131 -1
- agentpool_config/storage.py +46 -1
- agentpool_config/tools.py +7 -1
- agentpool_config/toolsets.py +92 -148
- agentpool_server/acp_server/acp_agent.py +134 -150
- agentpool_server/acp_server/commands/acp_commands.py +216 -51
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +10 -10
- agentpool_server/acp_server/server.py +23 -79
- agentpool_server/acp_server/session.py +181 -19
- agentpool_server/opencode_server/.rules +95 -0
- agentpool_server/opencode_server/ENDPOINTS.md +362 -0
- agentpool_server/opencode_server/__init__.py +27 -0
- agentpool_server/opencode_server/command_validation.py +172 -0
- agentpool_server/opencode_server/converters.py +869 -0
- agentpool_server/opencode_server/dependencies.py +24 -0
- agentpool_server/opencode_server/input_provider.py +269 -0
- agentpool_server/opencode_server/models/__init__.py +228 -0
- agentpool_server/opencode_server/models/agent.py +53 -0
- agentpool_server/opencode_server/models/app.py +60 -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 +647 -0
- agentpool_server/opencode_server/models/file.py +88 -0
- agentpool_server/opencode_server/models/mcp.py +25 -0
- agentpool_server/opencode_server/models/message.py +162 -0
- agentpool_server/opencode_server/models/parts.py +190 -0
- agentpool_server/opencode_server/models/provider.py +81 -0
- agentpool_server/opencode_server/models/pty.py +43 -0
- agentpool_server/opencode_server/models/session.py +99 -0
- agentpool_server/opencode_server/routes/__init__.py +25 -0
- agentpool_server/opencode_server/routes/agent_routes.py +442 -0
- agentpool_server/opencode_server/routes/app_routes.py +139 -0
- agentpool_server/opencode_server/routes/config_routes.py +241 -0
- agentpool_server/opencode_server/routes/file_routes.py +392 -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 +705 -0
- agentpool_server/opencode_server/routes/pty_routes.py +299 -0
- agentpool_server/opencode_server/routes/session_routes.py +1205 -0
- agentpool_server/opencode_server/routes/tui_routes.py +139 -0
- agentpool_server/opencode_server/server.py +430 -0
- agentpool_server/opencode_server/state.py +121 -0
- agentpool_server/opencode_server/time_utils.py +8 -0
- agentpool_storage/__init__.py +16 -0
- agentpool_storage/base.py +103 -0
- agentpool_storage/claude_provider.py +907 -0
- agentpool_storage/file_provider.py +129 -0
- agentpool_storage/memory_provider.py +61 -0
- agentpool_storage/models.py +3 -0
- agentpool_storage/opencode_provider.py +730 -0
- agentpool_storage/project_store.py +325 -0
- agentpool_storage/session_store.py +6 -0
- agentpool_storage/sql_provider/__init__.py +4 -2
- agentpool_storage/sql_provider/models.py +48 -0
- agentpool_storage/sql_provider/sql_provider.py +134 -1
- agentpool_storage/sql_provider/utils.py +10 -1
- agentpool_storage/text_log_provider.py +1 -0
- agentpool_toolsets/builtin/__init__.py +0 -8
- agentpool_toolsets/builtin/code.py +95 -56
- agentpool_toolsets/builtin/debug.py +16 -21
- agentpool_toolsets/builtin/execution_environment.py +99 -103
- agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
- agentpool_toolsets/builtin/skills.py +86 -4
- agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
- agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
- agentpool_toolsets/fsspec_toolset/grep.py +74 -2
- agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +159 -38
- agentpool_toolsets/mcp_discovery/__init__.py +5 -0
- agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
- agentpool_toolsets/mcp_discovery/toolset.py +454 -0
- agentpool_toolsets/mcp_run_toolset.py +84 -6
- agentpool_toolsets/builtin/agent_management.py +0 -239
- agentpool_toolsets/builtin/history.py +0 -36
- agentpool_toolsets/builtin/integration.py +0 -85
- agentpool_toolsets/builtin/tool_management.py +0 -90
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/entry_points.txt +0 -0
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Compaction configuration for message history management."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
from schemez import Schema
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from agentpool.messaging.compaction import CompactionPipeline, CompactionStep
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FilterThinkingConfig(Schema):
|
|
16
|
+
"""Configuration for FilterThinking step."""
|
|
17
|
+
|
|
18
|
+
type: Literal["filter_thinking"] = "filter_thinking"
|
|
19
|
+
|
|
20
|
+
def build(self) -> CompactionStep:
|
|
21
|
+
from agentpool.messaging.compaction import FilterThinking
|
|
22
|
+
|
|
23
|
+
return FilterThinking()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FilterRetryPromptsConfig(Schema):
|
|
27
|
+
"""Configuration for FilterRetryPrompts step."""
|
|
28
|
+
|
|
29
|
+
type: Literal["filter_retry_prompts"] = "filter_retry_prompts"
|
|
30
|
+
|
|
31
|
+
def build(self) -> CompactionStep:
|
|
32
|
+
from agentpool.messaging.compaction import FilterRetryPrompts
|
|
33
|
+
|
|
34
|
+
return FilterRetryPrompts()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FilterBinaryContentConfig(Schema):
|
|
38
|
+
"""Configuration for FilterBinaryContent step."""
|
|
39
|
+
|
|
40
|
+
type: Literal["filter_binary"] = "filter_binary"
|
|
41
|
+
keep_references: bool = False
|
|
42
|
+
|
|
43
|
+
def build(self) -> CompactionStep:
|
|
44
|
+
from agentpool.messaging.compaction import FilterBinaryContent
|
|
45
|
+
|
|
46
|
+
return FilterBinaryContent(keep_references=self.keep_references)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class FilterToolCallsConfig(Schema):
|
|
50
|
+
"""Configuration for FilterToolCalls step."""
|
|
51
|
+
|
|
52
|
+
type: Literal["filter_tools"] = "filter_tools"
|
|
53
|
+
exclude_tools: list[str] = Field(default_factory=list)
|
|
54
|
+
include_only: list[str] | None = None
|
|
55
|
+
|
|
56
|
+
def build(self) -> CompactionStep:
|
|
57
|
+
from agentpool.messaging.compaction import FilterToolCalls
|
|
58
|
+
|
|
59
|
+
return FilterToolCalls(exclude_tools=self.exclude_tools, include_only=self.include_only)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class FilterEmptyMessagesConfig(Schema):
|
|
63
|
+
"""Configuration for FilterEmptyMessages step."""
|
|
64
|
+
|
|
65
|
+
type: Literal["filter_empty"] = "filter_empty"
|
|
66
|
+
|
|
67
|
+
def build(self) -> CompactionStep:
|
|
68
|
+
from agentpool.messaging.compaction import FilterEmptyMessages
|
|
69
|
+
|
|
70
|
+
return FilterEmptyMessages()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TruncateToolOutputsConfig(Schema):
|
|
74
|
+
"""Configuration for TruncateToolOutputs step."""
|
|
75
|
+
|
|
76
|
+
type: Literal["truncate_tool_outputs"] = "truncate_tool_outputs"
|
|
77
|
+
max_length: int = 2000
|
|
78
|
+
suffix: str = "\n... [truncated]"
|
|
79
|
+
|
|
80
|
+
def build(self) -> CompactionStep:
|
|
81
|
+
from agentpool.messaging.compaction import TruncateToolOutputs
|
|
82
|
+
|
|
83
|
+
return TruncateToolOutputs(max_length=self.max_length, suffix=self.suffix)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TruncateTextPartsConfig(Schema):
|
|
87
|
+
"""Configuration for TruncateTextParts step."""
|
|
88
|
+
|
|
89
|
+
type: Literal["truncate_text"] = "truncate_text"
|
|
90
|
+
max_length: int = 5000
|
|
91
|
+
suffix: str = "\n... [truncated]"
|
|
92
|
+
|
|
93
|
+
def build(self) -> CompactionStep:
|
|
94
|
+
from agentpool.messaging.compaction import TruncateTextParts
|
|
95
|
+
|
|
96
|
+
return TruncateTextParts(max_length=self.max_length, suffix=self.suffix)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class KeepLastMessagesConfig(Schema):
|
|
100
|
+
"""Configuration for KeepLastMessages step."""
|
|
101
|
+
|
|
102
|
+
type: Literal["keep_last"] = "keep_last"
|
|
103
|
+
count: int = 10
|
|
104
|
+
count_pairs: bool = True
|
|
105
|
+
|
|
106
|
+
def build(self) -> CompactionStep:
|
|
107
|
+
from agentpool.messaging.compaction import KeepLastMessages
|
|
108
|
+
|
|
109
|
+
return KeepLastMessages(count=self.count, count_pairs=self.count_pairs)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class KeepFirstMessagesConfig(Schema):
|
|
113
|
+
"""Configuration for KeepFirstMessages step."""
|
|
114
|
+
|
|
115
|
+
type: Literal["keep_first"] = "keep_first"
|
|
116
|
+
count: int = 2
|
|
117
|
+
|
|
118
|
+
def build(self) -> CompactionStep:
|
|
119
|
+
from agentpool.messaging.compaction import KeepFirstMessages
|
|
120
|
+
|
|
121
|
+
return KeepFirstMessages(count=self.count)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class KeepFirstAndLastConfig(Schema):
|
|
125
|
+
"""Configuration for KeepFirstAndLast step."""
|
|
126
|
+
|
|
127
|
+
type: Literal["keep_first_last"] = "keep_first_last"
|
|
128
|
+
first_count: int = 2
|
|
129
|
+
last_count: int = 5
|
|
130
|
+
|
|
131
|
+
def build(self) -> CompactionStep:
|
|
132
|
+
from agentpool.messaging.compaction import KeepFirstAndLast
|
|
133
|
+
|
|
134
|
+
return KeepFirstAndLast(first_count=self.first_count, last_count=self.last_count)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class TokenBudgetConfig(Schema):
|
|
138
|
+
"""Configuration for TokenBudget step."""
|
|
139
|
+
|
|
140
|
+
type: Literal["token_budget"] = "token_budget"
|
|
141
|
+
max_tokens: int = 4000
|
|
142
|
+
model: str = "gpt-4o"
|
|
143
|
+
|
|
144
|
+
def build(self) -> CompactionStep:
|
|
145
|
+
from agentpool.messaging.compaction import TokenBudget
|
|
146
|
+
|
|
147
|
+
return TokenBudget(max_tokens=self.max_tokens, model=self.model)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class SummarizeConfig(Schema):
|
|
151
|
+
"""Configuration for Summarize step."""
|
|
152
|
+
|
|
153
|
+
type: Literal["summarize"] = "summarize"
|
|
154
|
+
model: str = "openai:gpt-4o-mini"
|
|
155
|
+
threshold: int = 15
|
|
156
|
+
keep_recent: int = 5
|
|
157
|
+
summary_prompt: str | None = None
|
|
158
|
+
|
|
159
|
+
def build(self) -> CompactionStep:
|
|
160
|
+
from agentpool.messaging.compaction import Summarize
|
|
161
|
+
|
|
162
|
+
kwargs: dict[str, Any] = {
|
|
163
|
+
"model": self.model,
|
|
164
|
+
"threshold": self.threshold,
|
|
165
|
+
"keep_recent": self.keep_recent,
|
|
166
|
+
}
|
|
167
|
+
if self.summary_prompt:
|
|
168
|
+
kwargs["summary_prompt"] = self.summary_prompt
|
|
169
|
+
return Summarize(**kwargs)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class WhenMessageCountExceedsConfig(Schema):
|
|
173
|
+
"""Configuration for WhenMessageCountExceeds wrapper."""
|
|
174
|
+
|
|
175
|
+
type: Literal["when_count_exceeds"] = "when_count_exceeds"
|
|
176
|
+
threshold: int = 20
|
|
177
|
+
step: "CompactionStepConfig"
|
|
178
|
+
|
|
179
|
+
def build(self) -> CompactionStep:
|
|
180
|
+
from agentpool.messaging.compaction import WhenMessageCountExceeds
|
|
181
|
+
|
|
182
|
+
return WhenMessageCountExceeds(step=self.step.build(), threshold=self.threshold)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# Union of all config types with discriminator
|
|
186
|
+
CompactionStepConfig = Annotated[
|
|
187
|
+
FilterThinkingConfig
|
|
188
|
+
| FilterRetryPromptsConfig
|
|
189
|
+
| FilterBinaryContentConfig
|
|
190
|
+
| FilterToolCallsConfig
|
|
191
|
+
| FilterEmptyMessagesConfig
|
|
192
|
+
| TruncateToolOutputsConfig
|
|
193
|
+
| TruncateTextPartsConfig
|
|
194
|
+
| KeepLastMessagesConfig
|
|
195
|
+
| KeepFirstMessagesConfig
|
|
196
|
+
| KeepFirstAndLastConfig
|
|
197
|
+
| TokenBudgetConfig
|
|
198
|
+
| SummarizeConfig
|
|
199
|
+
| WhenMessageCountExceedsConfig,
|
|
200
|
+
Field(discriminator="type"),
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
# Update forward reference
|
|
204
|
+
WhenMessageCountExceedsConfig.model_rebuild()
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class CompactionConfig(Schema):
|
|
208
|
+
"""Configuration for message compaction/summarization.
|
|
209
|
+
|
|
210
|
+
Example YAML:
|
|
211
|
+
```yaml
|
|
212
|
+
compaction:
|
|
213
|
+
steps:
|
|
214
|
+
- type: filter_thinking
|
|
215
|
+
- type: truncate_tool_outputs
|
|
216
|
+
max_length: 1000
|
|
217
|
+
- type: keep_last
|
|
218
|
+
count: 10
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Or use a preset:
|
|
222
|
+
```yaml
|
|
223
|
+
compaction:
|
|
224
|
+
preset: balanced
|
|
225
|
+
```
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
preset: Literal["minimal", "balanced", "summarizing"] | None = None
|
|
229
|
+
"""Use a predefined compaction pipeline.
|
|
230
|
+
|
|
231
|
+
- minimal: Aggressively minimize context (filter thinking, truncate, keep last 10)
|
|
232
|
+
- balanced: Balance context preservation with size (filter thinking, truncate,
|
|
233
|
+
keep first 2 + last 8)
|
|
234
|
+
- summarizing: Summarize old messages (filter thinking, summarize when > 20 messages)
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
steps: list[CompactionStepConfig] = Field(default_factory=list)
|
|
238
|
+
"""Ordered list of compaction steps to apply (ignored if preset is set)."""
|
|
239
|
+
|
|
240
|
+
def build(self) -> CompactionPipeline:
|
|
241
|
+
"""Build a CompactionPipeline from this configuration."""
|
|
242
|
+
from agentpool.messaging.compaction import (
|
|
243
|
+
CompactionPipeline,
|
|
244
|
+
balanced_context,
|
|
245
|
+
minimal_context,
|
|
246
|
+
summarizing_context,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if self.preset:
|
|
250
|
+
match self.preset:
|
|
251
|
+
case "minimal":
|
|
252
|
+
return minimal_context()
|
|
253
|
+
case "balanced":
|
|
254
|
+
return balanced_context()
|
|
255
|
+
case "summarizing":
|
|
256
|
+
return summarizing_context()
|
|
257
|
+
|
|
258
|
+
return CompactionPipeline(steps=[step.build() for step in self.steps])
|
agentpool_config/mcp_server.py
CHANGED
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import os
|
|
6
6
|
from typing import TYPE_CHECKING, Annotated, Literal, Self
|
|
7
7
|
|
|
8
|
-
from pydantic import ConfigDict, Field, HttpUrl
|
|
8
|
+
from pydantic import ConfigDict, Field, HttpUrl, model_validator
|
|
9
9
|
from schemez import Schema
|
|
10
10
|
|
|
11
11
|
|
|
@@ -73,6 +73,62 @@ class BaseMCPServerConfig(Schema):
|
|
|
73
73
|
)
|
|
74
74
|
"""Timeout for the server process in seconds."""
|
|
75
75
|
|
|
76
|
+
enabled_tools: list[str] | None = Field(
|
|
77
|
+
default=None,
|
|
78
|
+
examples=[["read_file", "list_directory"], ["search", "fetch"]],
|
|
79
|
+
title="Enabled tools",
|
|
80
|
+
)
|
|
81
|
+
"""If set, only these tools will be available (whitelist).
|
|
82
|
+
Mutually exclusive with disabled_tools."""
|
|
83
|
+
|
|
84
|
+
disabled_tools: list[str] | None = Field(
|
|
85
|
+
default=None,
|
|
86
|
+
examples=[["delete_file", "write_file"], ["dangerous_tool"]],
|
|
87
|
+
title="Disabled tools",
|
|
88
|
+
)
|
|
89
|
+
"""Tools to exclude from this server (blacklist). Mutually exclusive with enabled_tools."""
|
|
90
|
+
|
|
91
|
+
@model_validator(mode="after")
|
|
92
|
+
def _validate_tool_filters(self) -> Self:
|
|
93
|
+
"""Validate that enabled_tools and disabled_tools are mutually exclusive."""
|
|
94
|
+
if self.enabled_tools is not None and self.disabled_tools is not None:
|
|
95
|
+
msg = "Cannot specify both 'enabled_tools' and 'disabled_tools'"
|
|
96
|
+
raise ValueError(msg)
|
|
97
|
+
return self
|
|
98
|
+
|
|
99
|
+
def is_tool_allowed(self, tool_name: str) -> bool:
|
|
100
|
+
"""Check if a tool is allowed based on enabled/disabled lists.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
tool_name: Name of the tool to check
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
True if the tool is allowed, False otherwise
|
|
107
|
+
"""
|
|
108
|
+
if self.enabled_tools is not None:
|
|
109
|
+
return tool_name in self.enabled_tools
|
|
110
|
+
if self.disabled_tools is not None:
|
|
111
|
+
return tool_name not in self.disabled_tools
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
def needs_tool_filtering(self) -> bool:
|
|
115
|
+
"""Check if this config has tool filtering configured."""
|
|
116
|
+
return self.enabled_tools is not None or self.disabled_tools is not None
|
|
117
|
+
|
|
118
|
+
def wrap_with_mcp_filter(self) -> StdioMCPServerConfig:
|
|
119
|
+
"""Wrap this MCP server with mcp-filter for tool filtering.
|
|
120
|
+
|
|
121
|
+
Creates a new StdioMCPServerConfig that runs mcp-filter as a proxy,
|
|
122
|
+
applying the configured enabled_tools/disabled_tools filtering.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
A new StdioMCPServerConfig that wraps the original server with mcp-filter
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
NotImplementedError: Subclasses must implement this method
|
|
129
|
+
"""
|
|
130
|
+
raise NotImplementedError
|
|
131
|
+
|
|
76
132
|
def get_env_vars(self) -> dict[str, str]:
|
|
77
133
|
"""Get environment variables for the server process."""
|
|
78
134
|
env = os.environ.copy()
|
|
@@ -142,6 +198,34 @@ class StdioMCPServerConfig(BaseMCPServerConfig):
|
|
|
142
198
|
"""Generate a unique client ID for this stdio server configuration."""
|
|
143
199
|
return f"{self.command}_{' '.join(self.args)}"
|
|
144
200
|
|
|
201
|
+
def wrap_with_mcp_filter(self) -> StdioMCPServerConfig:
|
|
202
|
+
"""Wrap this stdio MCP server with mcp-filter for tool filtering.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
A new StdioMCPServerConfig that wraps this server with mcp-filter
|
|
206
|
+
"""
|
|
207
|
+
filter_args = ["mcp-filter", "run", "-t", "stdio", "--stdio-command", self.command]
|
|
208
|
+
|
|
209
|
+
# Add original args as a single --stdio-arg
|
|
210
|
+
if self.args:
|
|
211
|
+
filter_args.extend(["--stdio-arg", " ".join(self.args)])
|
|
212
|
+
|
|
213
|
+
# Add allowlist (exact tool names)
|
|
214
|
+
if self.enabled_tools:
|
|
215
|
+
filter_args.extend(["-a", ",".join(self.enabled_tools)])
|
|
216
|
+
|
|
217
|
+
# Add denylist (regex patterns)
|
|
218
|
+
if self.disabled_tools:
|
|
219
|
+
filter_args.extend(["-d", ",".join(self.disabled_tools)])
|
|
220
|
+
|
|
221
|
+
return StdioMCPServerConfig(
|
|
222
|
+
name=self.name,
|
|
223
|
+
command="uvx",
|
|
224
|
+
args=filter_args,
|
|
225
|
+
env=self.env,
|
|
226
|
+
timeout=self.timeout,
|
|
227
|
+
)
|
|
228
|
+
|
|
145
229
|
def to_pydantic_ai(self) -> MCPServerStdio:
|
|
146
230
|
"""Convert to pydantic-ai MCPServerStdio instance."""
|
|
147
231
|
from pydantic_ai.mcp import MCPServerStdio
|
|
@@ -186,6 +270,29 @@ class SSEMCPServerConfig(BaseMCPServerConfig):
|
|
|
186
270
|
"""Generate a unique client ID for this SSE server configuration."""
|
|
187
271
|
return f"sse_{self.url}"
|
|
188
272
|
|
|
273
|
+
def wrap_with_mcp_filter(self) -> StdioMCPServerConfig:
|
|
274
|
+
"""Wrap this SSE MCP server with mcp-filter for tool filtering.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
A new StdioMCPServerConfig that wraps this server with mcp-filter
|
|
278
|
+
"""
|
|
279
|
+
filter_args = ["mcp-filter", "run", "-t", "http", "--http-url", str(self.url)]
|
|
280
|
+
|
|
281
|
+
# Add allowlist (exact tool names)
|
|
282
|
+
if self.enabled_tools:
|
|
283
|
+
filter_args.extend(["-a", ",".join(self.enabled_tools)])
|
|
284
|
+
|
|
285
|
+
# Add denylist (regex patterns)
|
|
286
|
+
if self.disabled_tools:
|
|
287
|
+
filter_args.extend(["-d", ",".join(self.disabled_tools)])
|
|
288
|
+
|
|
289
|
+
return StdioMCPServerConfig(
|
|
290
|
+
name=self.name,
|
|
291
|
+
command="uvx",
|
|
292
|
+
args=filter_args,
|
|
293
|
+
timeout=self.timeout,
|
|
294
|
+
)
|
|
295
|
+
|
|
189
296
|
def to_pydantic_ai(self) -> MCPServerSSE:
|
|
190
297
|
"""Convert to pydantic-ai MCPServerSSE instance."""
|
|
191
298
|
from pydantic_ai.mcp import MCPServerSSE
|
|
@@ -225,6 +332,29 @@ class StreamableHTTPMCPServerConfig(BaseMCPServerConfig):
|
|
|
225
332
|
"""Generate a unique client ID for this streamable HTTP server configuration."""
|
|
226
333
|
return f"streamable_http_{self.url}"
|
|
227
334
|
|
|
335
|
+
def wrap_with_mcp_filter(self) -> StdioMCPServerConfig:
|
|
336
|
+
"""Wrap this HTTP MCP server with mcp-filter for tool filtering.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
A new StdioMCPServerConfig that wraps this server with mcp-filter
|
|
340
|
+
"""
|
|
341
|
+
filter_args = ["mcp-filter", "run", "-t", "http", "--http-url", str(self.url)]
|
|
342
|
+
|
|
343
|
+
# Add allowlist (exact tool names)
|
|
344
|
+
if self.enabled_tools:
|
|
345
|
+
filter_args.extend(["-a", ",".join(self.enabled_tools)])
|
|
346
|
+
|
|
347
|
+
# Add denylist (regex patterns)
|
|
348
|
+
if self.disabled_tools:
|
|
349
|
+
filter_args.extend(["-d", ",".join(self.disabled_tools)])
|
|
350
|
+
|
|
351
|
+
return StdioMCPServerConfig(
|
|
352
|
+
name=self.name,
|
|
353
|
+
command="uvx",
|
|
354
|
+
args=filter_args,
|
|
355
|
+
timeout=self.timeout,
|
|
356
|
+
)
|
|
357
|
+
|
|
228
358
|
def to_pydantic_ai(self) -> MCPServerStreamableHTTP:
|
|
229
359
|
"""Convert to pydantic-ai MCPServerStreamableHTTP instance."""
|
|
230
360
|
from pydantic_ai.mcp import MCPServerStreamableHTTP
|
agentpool_config/storage.py
CHANGED
|
@@ -170,8 +170,53 @@ class MemoryStorageConfig(BaseStorageProviderConfig):
|
|
|
170
170
|
"""In-memory storage configuration for testing."""
|
|
171
171
|
|
|
172
172
|
|
|
173
|
+
class ClaudeStorageConfig(BaseStorageProviderConfig):
|
|
174
|
+
"""Claude Code native storage format configuration.
|
|
175
|
+
|
|
176
|
+
Reads/writes to Claude Code's native JSONL format in ~/.claude/projects/.
|
|
177
|
+
Useful for sharing conversation history between agentpool and Claude Code CLI.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
model_config = ConfigDict(json_schema_extra={"x-doc-title": "Claude Storage"})
|
|
181
|
+
|
|
182
|
+
type: Literal["claude"] = Field("claude", init=False)
|
|
183
|
+
"""Claude Code native storage configuration."""
|
|
184
|
+
|
|
185
|
+
path: str = Field(
|
|
186
|
+
default="~/.claude",
|
|
187
|
+
examples=["~/.claude", "/home/user/.claude"],
|
|
188
|
+
title="Claude data directory",
|
|
189
|
+
)
|
|
190
|
+
"""Path to Claude data directory (default: ~/.claude)"""
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class OpenCodeStorageConfig(BaseStorageProviderConfig):
|
|
194
|
+
"""OpenCode native storage format configuration.
|
|
195
|
+
|
|
196
|
+
Reads from OpenCode's native JSON format in ~/.local/share/opencode/storage/.
|
|
197
|
+
Useful for importing conversation history from OpenCode.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
model_config = ConfigDict(json_schema_extra={"x-doc-title": "OpenCode Storage"})
|
|
201
|
+
|
|
202
|
+
type: Literal["opencode"] = Field("opencode", init=False)
|
|
203
|
+
"""OpenCode native storage configuration."""
|
|
204
|
+
|
|
205
|
+
path: str = Field(
|
|
206
|
+
default="~/.local/share/opencode/storage",
|
|
207
|
+
examples=["~/.local/share/opencode/storage"],
|
|
208
|
+
title="OpenCode storage directory",
|
|
209
|
+
)
|
|
210
|
+
"""Path to OpenCode storage directory."""
|
|
211
|
+
|
|
212
|
+
|
|
173
213
|
StorageProviderConfig = Annotated[
|
|
174
|
-
SQLStorageConfig
|
|
214
|
+
SQLStorageConfig
|
|
215
|
+
| FileStorageConfig
|
|
216
|
+
| TextLogConfig
|
|
217
|
+
| MemoryStorageConfig
|
|
218
|
+
| ClaudeStorageConfig
|
|
219
|
+
| OpenCodeStorageConfig,
|
|
175
220
|
Field(discriminator="type"),
|
|
176
221
|
]
|
|
177
222
|
|
agentpool_config/tools.py
CHANGED
|
@@ -109,4 +109,10 @@ class ImportToolConfig(BaseToolConfig):
|
|
|
109
109
|
)
|
|
110
110
|
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
from agentpool_config.builtin_tools import BuiltinToolConfig # noqa: E402
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
ToolConfig = Annotated[
|
|
116
|
+
ImportToolConfig | BuiltinToolConfig,
|
|
117
|
+
Field(discriminator="type"),
|
|
118
|
+
]
|