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.
Files changed (174) hide show
  1. acp/__init__.py +13 -0
  2. acp/bridge/README.md +15 -2
  3. acp/bridge/__init__.py +3 -2
  4. acp/bridge/__main__.py +60 -19
  5. acp/bridge/ws_server.py +173 -0
  6. acp/bridge/ws_server_cli.py +89 -0
  7. acp/notifications.py +2 -1
  8. acp/stdio.py +39 -9
  9. acp/transports.py +362 -2
  10. acp/utils.py +15 -2
  11. agentpool/__init__.py +4 -1
  12. agentpool/agents/__init__.py +2 -0
  13. agentpool/agents/acp_agent/acp_agent.py +203 -88
  14. agentpool/agents/acp_agent/acp_converters.py +46 -21
  15. agentpool/agents/acp_agent/client_handler.py +157 -3
  16. agentpool/agents/acp_agent/session_state.py +4 -1
  17. agentpool/agents/agent.py +314 -107
  18. agentpool/agents/agui_agent/__init__.py +0 -2
  19. agentpool/agents/agui_agent/agui_agent.py +90 -21
  20. agentpool/agents/agui_agent/agui_converters.py +0 -131
  21. agentpool/agents/base_agent.py +163 -1
  22. agentpool/agents/claude_code_agent/claude_code_agent.py +626 -179
  23. agentpool/agents/claude_code_agent/converters.py +71 -3
  24. agentpool/agents/claude_code_agent/history.py +474 -0
  25. agentpool/agents/context.py +40 -0
  26. agentpool/agents/events/__init__.py +2 -0
  27. agentpool/agents/events/builtin_handlers.py +2 -1
  28. agentpool/agents/events/event_emitter.py +29 -2
  29. agentpool/agents/events/events.py +20 -0
  30. agentpool/agents/modes.py +54 -0
  31. agentpool/agents/tool_call_accumulator.py +213 -0
  32. agentpool/common_types.py +21 -0
  33. agentpool/config_resources/__init__.py +38 -1
  34. agentpool/config_resources/claude_code_agent.yml +3 -0
  35. agentpool/delegation/pool.py +37 -29
  36. agentpool/delegation/team.py +1 -0
  37. agentpool/delegation/teamrun.py +1 -0
  38. agentpool/diagnostics/__init__.py +53 -0
  39. agentpool/diagnostics/lsp_manager.py +1593 -0
  40. agentpool/diagnostics/lsp_proxy.py +41 -0
  41. agentpool/diagnostics/lsp_proxy_script.py +229 -0
  42. agentpool/diagnostics/models.py +398 -0
  43. agentpool/mcp_server/__init__.py +0 -2
  44. agentpool/mcp_server/client.py +12 -3
  45. agentpool/mcp_server/manager.py +25 -31
  46. agentpool/mcp_server/registries/official_registry_client.py +25 -0
  47. agentpool/mcp_server/tool_bridge.py +78 -66
  48. agentpool/messaging/__init__.py +0 -2
  49. agentpool/messaging/compaction.py +72 -197
  50. agentpool/messaging/message_history.py +12 -0
  51. agentpool/messaging/messages.py +52 -9
  52. agentpool/messaging/processing.py +3 -1
  53. agentpool/models/acp_agents/base.py +0 -22
  54. agentpool/models/acp_agents/mcp_capable.py +8 -148
  55. agentpool/models/acp_agents/non_mcp.py +129 -72
  56. agentpool/models/agents.py +35 -13
  57. agentpool/models/claude_code_agents.py +33 -2
  58. agentpool/models/manifest.py +43 -0
  59. agentpool/repomap.py +1 -1
  60. agentpool/resource_providers/__init__.py +9 -1
  61. agentpool/resource_providers/aggregating.py +52 -3
  62. agentpool/resource_providers/base.py +57 -1
  63. agentpool/resource_providers/mcp_provider.py +23 -0
  64. agentpool/resource_providers/plan_provider.py +130 -41
  65. agentpool/resource_providers/pool.py +2 -0
  66. agentpool/resource_providers/static.py +2 -0
  67. agentpool/sessions/__init__.py +2 -1
  68. agentpool/sessions/manager.py +31 -2
  69. agentpool/sessions/models.py +50 -0
  70. agentpool/skills/registry.py +13 -8
  71. agentpool/storage/manager.py +217 -1
  72. agentpool/testing.py +537 -19
  73. agentpool/utils/file_watcher.py +269 -0
  74. agentpool/utils/identifiers.py +121 -0
  75. agentpool/utils/pydantic_ai_helpers.py +46 -0
  76. agentpool/utils/streams.py +690 -1
  77. agentpool/utils/subprocess_utils.py +155 -0
  78. agentpool/utils/token_breakdown.py +461 -0
  79. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/METADATA +27 -7
  80. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/RECORD +170 -112
  81. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/WHEEL +1 -1
  82. agentpool_cli/__main__.py +4 -0
  83. agentpool_cli/serve_acp.py +41 -20
  84. agentpool_cli/serve_agui.py +87 -0
  85. agentpool_cli/serve_opencode.py +119 -0
  86. agentpool_commands/__init__.py +30 -0
  87. agentpool_commands/agents.py +74 -1
  88. agentpool_commands/history.py +62 -0
  89. agentpool_commands/mcp.py +176 -0
  90. agentpool_commands/models.py +56 -3
  91. agentpool_commands/tools.py +57 -0
  92. agentpool_commands/utils.py +51 -0
  93. agentpool_config/builtin_tools.py +77 -22
  94. agentpool_config/commands.py +24 -1
  95. agentpool_config/compaction.py +258 -0
  96. agentpool_config/mcp_server.py +131 -1
  97. agentpool_config/storage.py +46 -1
  98. agentpool_config/tools.py +7 -1
  99. agentpool_config/toolsets.py +92 -148
  100. agentpool_server/acp_server/acp_agent.py +134 -150
  101. agentpool_server/acp_server/commands/acp_commands.py +216 -51
  102. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +10 -10
  103. agentpool_server/acp_server/server.py +23 -79
  104. agentpool_server/acp_server/session.py +181 -19
  105. agentpool_server/opencode_server/.rules +95 -0
  106. agentpool_server/opencode_server/ENDPOINTS.md +362 -0
  107. agentpool_server/opencode_server/__init__.py +27 -0
  108. agentpool_server/opencode_server/command_validation.py +172 -0
  109. agentpool_server/opencode_server/converters.py +869 -0
  110. agentpool_server/opencode_server/dependencies.py +24 -0
  111. agentpool_server/opencode_server/input_provider.py +269 -0
  112. agentpool_server/opencode_server/models/__init__.py +228 -0
  113. agentpool_server/opencode_server/models/agent.py +53 -0
  114. agentpool_server/opencode_server/models/app.py +60 -0
  115. agentpool_server/opencode_server/models/base.py +26 -0
  116. agentpool_server/opencode_server/models/common.py +23 -0
  117. agentpool_server/opencode_server/models/config.py +37 -0
  118. agentpool_server/opencode_server/models/events.py +647 -0
  119. agentpool_server/opencode_server/models/file.py +88 -0
  120. agentpool_server/opencode_server/models/mcp.py +25 -0
  121. agentpool_server/opencode_server/models/message.py +162 -0
  122. agentpool_server/opencode_server/models/parts.py +190 -0
  123. agentpool_server/opencode_server/models/provider.py +81 -0
  124. agentpool_server/opencode_server/models/pty.py +43 -0
  125. agentpool_server/opencode_server/models/session.py +99 -0
  126. agentpool_server/opencode_server/routes/__init__.py +25 -0
  127. agentpool_server/opencode_server/routes/agent_routes.py +442 -0
  128. agentpool_server/opencode_server/routes/app_routes.py +139 -0
  129. agentpool_server/opencode_server/routes/config_routes.py +241 -0
  130. agentpool_server/opencode_server/routes/file_routes.py +392 -0
  131. agentpool_server/opencode_server/routes/global_routes.py +94 -0
  132. agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
  133. agentpool_server/opencode_server/routes/message_routes.py +705 -0
  134. agentpool_server/opencode_server/routes/pty_routes.py +299 -0
  135. agentpool_server/opencode_server/routes/session_routes.py +1205 -0
  136. agentpool_server/opencode_server/routes/tui_routes.py +139 -0
  137. agentpool_server/opencode_server/server.py +430 -0
  138. agentpool_server/opencode_server/state.py +121 -0
  139. agentpool_server/opencode_server/time_utils.py +8 -0
  140. agentpool_storage/__init__.py +16 -0
  141. agentpool_storage/base.py +103 -0
  142. agentpool_storage/claude_provider.py +907 -0
  143. agentpool_storage/file_provider.py +129 -0
  144. agentpool_storage/memory_provider.py +61 -0
  145. agentpool_storage/models.py +3 -0
  146. agentpool_storage/opencode_provider.py +730 -0
  147. agentpool_storage/project_store.py +325 -0
  148. agentpool_storage/session_store.py +6 -0
  149. agentpool_storage/sql_provider/__init__.py +4 -2
  150. agentpool_storage/sql_provider/models.py +48 -0
  151. agentpool_storage/sql_provider/sql_provider.py +134 -1
  152. agentpool_storage/sql_provider/utils.py +10 -1
  153. agentpool_storage/text_log_provider.py +1 -0
  154. agentpool_toolsets/builtin/__init__.py +0 -8
  155. agentpool_toolsets/builtin/code.py +95 -56
  156. agentpool_toolsets/builtin/debug.py +16 -21
  157. agentpool_toolsets/builtin/execution_environment.py +99 -103
  158. agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
  159. agentpool_toolsets/builtin/skills.py +86 -4
  160. agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
  161. agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
  162. agentpool_toolsets/fsspec_toolset/grep.py +74 -2
  163. agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
  164. agentpool_toolsets/fsspec_toolset/toolset.py +159 -38
  165. agentpool_toolsets/mcp_discovery/__init__.py +5 -0
  166. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  167. agentpool_toolsets/mcp_discovery/toolset.py +454 -0
  168. agentpool_toolsets/mcp_run_toolset.py +84 -6
  169. agentpool_toolsets/builtin/agent_management.py +0 -239
  170. agentpool_toolsets/builtin/history.py +0 -36
  171. agentpool_toolsets/builtin/integration.py +0 -85
  172. agentpool_toolsets/builtin/tool_management.py +0 -90
  173. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/entry_points.txt +0 -0
  174. {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])
@@ -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
@@ -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 | FileStorageConfig | TextLogConfig | MemoryStorageConfig,
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
- ToolConfig = Annotated[ImportToolConfig, Field(discriminator="type")]
112
+ from agentpool_config.builtin_tools import BuiltinToolConfig # noqa: E402
113
+
114
+
115
+ ToolConfig = Annotated[
116
+ ImportToolConfig | BuiltinToolConfig,
117
+ Field(discriminator="type"),
118
+ ]