emdash-core 0.1.7__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 (187) hide show
  1. emdash_core/__init__.py +3 -0
  2. emdash_core/agent/__init__.py +37 -0
  3. emdash_core/agent/agents.py +225 -0
  4. emdash_core/agent/code_reviewer.py +476 -0
  5. emdash_core/agent/compaction.py +143 -0
  6. emdash_core/agent/context_manager.py +140 -0
  7. emdash_core/agent/events.py +338 -0
  8. emdash_core/agent/handlers.py +224 -0
  9. emdash_core/agent/inprocess_subagent.py +377 -0
  10. emdash_core/agent/mcp/__init__.py +50 -0
  11. emdash_core/agent/mcp/client.py +346 -0
  12. emdash_core/agent/mcp/config.py +302 -0
  13. emdash_core/agent/mcp/manager.py +496 -0
  14. emdash_core/agent/mcp/tool_factory.py +213 -0
  15. emdash_core/agent/prompts/__init__.py +38 -0
  16. emdash_core/agent/prompts/main_agent.py +104 -0
  17. emdash_core/agent/prompts/subagents.py +131 -0
  18. emdash_core/agent/prompts/workflow.py +136 -0
  19. emdash_core/agent/providers/__init__.py +34 -0
  20. emdash_core/agent/providers/base.py +143 -0
  21. emdash_core/agent/providers/factory.py +80 -0
  22. emdash_core/agent/providers/models.py +220 -0
  23. emdash_core/agent/providers/openai_provider.py +463 -0
  24. emdash_core/agent/providers/transformers_provider.py +217 -0
  25. emdash_core/agent/research/__init__.py +81 -0
  26. emdash_core/agent/research/agent.py +143 -0
  27. emdash_core/agent/research/controller.py +254 -0
  28. emdash_core/agent/research/critic.py +428 -0
  29. emdash_core/agent/research/macros.py +469 -0
  30. emdash_core/agent/research/planner.py +449 -0
  31. emdash_core/agent/research/researcher.py +436 -0
  32. emdash_core/agent/research/state.py +523 -0
  33. emdash_core/agent/research/synthesizer.py +594 -0
  34. emdash_core/agent/reviewer_profile.py +475 -0
  35. emdash_core/agent/rules.py +123 -0
  36. emdash_core/agent/runner.py +601 -0
  37. emdash_core/agent/session.py +262 -0
  38. emdash_core/agent/spec_schema.py +66 -0
  39. emdash_core/agent/specification.py +479 -0
  40. emdash_core/agent/subagent.py +397 -0
  41. emdash_core/agent/subagent_prompts.py +13 -0
  42. emdash_core/agent/toolkit.py +482 -0
  43. emdash_core/agent/toolkits/__init__.py +64 -0
  44. emdash_core/agent/toolkits/base.py +96 -0
  45. emdash_core/agent/toolkits/explore.py +47 -0
  46. emdash_core/agent/toolkits/plan.py +55 -0
  47. emdash_core/agent/tools/__init__.py +141 -0
  48. emdash_core/agent/tools/analytics.py +436 -0
  49. emdash_core/agent/tools/base.py +131 -0
  50. emdash_core/agent/tools/coding.py +484 -0
  51. emdash_core/agent/tools/github_mcp.py +592 -0
  52. emdash_core/agent/tools/history.py +13 -0
  53. emdash_core/agent/tools/modes.py +153 -0
  54. emdash_core/agent/tools/plan.py +206 -0
  55. emdash_core/agent/tools/plan_write.py +135 -0
  56. emdash_core/agent/tools/search.py +412 -0
  57. emdash_core/agent/tools/spec.py +341 -0
  58. emdash_core/agent/tools/task.py +262 -0
  59. emdash_core/agent/tools/task_output.py +204 -0
  60. emdash_core/agent/tools/tasks.py +454 -0
  61. emdash_core/agent/tools/traversal.py +588 -0
  62. emdash_core/agent/tools/web.py +179 -0
  63. emdash_core/analytics/__init__.py +5 -0
  64. emdash_core/analytics/engine.py +1286 -0
  65. emdash_core/api/__init__.py +5 -0
  66. emdash_core/api/agent.py +308 -0
  67. emdash_core/api/agents.py +154 -0
  68. emdash_core/api/analyze.py +264 -0
  69. emdash_core/api/auth.py +173 -0
  70. emdash_core/api/context.py +77 -0
  71. emdash_core/api/db.py +121 -0
  72. emdash_core/api/embed.py +131 -0
  73. emdash_core/api/feature.py +143 -0
  74. emdash_core/api/health.py +93 -0
  75. emdash_core/api/index.py +162 -0
  76. emdash_core/api/plan.py +110 -0
  77. emdash_core/api/projectmd.py +210 -0
  78. emdash_core/api/query.py +320 -0
  79. emdash_core/api/research.py +122 -0
  80. emdash_core/api/review.py +161 -0
  81. emdash_core/api/router.py +76 -0
  82. emdash_core/api/rules.py +116 -0
  83. emdash_core/api/search.py +119 -0
  84. emdash_core/api/spec.py +99 -0
  85. emdash_core/api/swarm.py +223 -0
  86. emdash_core/api/tasks.py +109 -0
  87. emdash_core/api/team.py +120 -0
  88. emdash_core/auth/__init__.py +17 -0
  89. emdash_core/auth/github.py +389 -0
  90. emdash_core/config.py +74 -0
  91. emdash_core/context/__init__.py +52 -0
  92. emdash_core/context/models.py +50 -0
  93. emdash_core/context/providers/__init__.py +11 -0
  94. emdash_core/context/providers/base.py +74 -0
  95. emdash_core/context/providers/explored_areas.py +183 -0
  96. emdash_core/context/providers/touched_areas.py +360 -0
  97. emdash_core/context/registry.py +73 -0
  98. emdash_core/context/reranker.py +199 -0
  99. emdash_core/context/service.py +260 -0
  100. emdash_core/context/session.py +352 -0
  101. emdash_core/core/__init__.py +104 -0
  102. emdash_core/core/config.py +454 -0
  103. emdash_core/core/exceptions.py +55 -0
  104. emdash_core/core/models.py +265 -0
  105. emdash_core/core/review_config.py +57 -0
  106. emdash_core/db/__init__.py +67 -0
  107. emdash_core/db/auth.py +134 -0
  108. emdash_core/db/models.py +91 -0
  109. emdash_core/db/provider.py +222 -0
  110. emdash_core/db/providers/__init__.py +5 -0
  111. emdash_core/db/providers/supabase.py +452 -0
  112. emdash_core/embeddings/__init__.py +24 -0
  113. emdash_core/embeddings/indexer.py +534 -0
  114. emdash_core/embeddings/models.py +192 -0
  115. emdash_core/embeddings/providers/__init__.py +7 -0
  116. emdash_core/embeddings/providers/base.py +112 -0
  117. emdash_core/embeddings/providers/fireworks.py +141 -0
  118. emdash_core/embeddings/providers/openai.py +104 -0
  119. emdash_core/embeddings/registry.py +146 -0
  120. emdash_core/embeddings/service.py +215 -0
  121. emdash_core/graph/__init__.py +26 -0
  122. emdash_core/graph/builder.py +134 -0
  123. emdash_core/graph/connection.py +692 -0
  124. emdash_core/graph/schema.py +416 -0
  125. emdash_core/graph/writer.py +667 -0
  126. emdash_core/ingestion/__init__.py +7 -0
  127. emdash_core/ingestion/change_detector.py +150 -0
  128. emdash_core/ingestion/git/__init__.py +5 -0
  129. emdash_core/ingestion/git/commit_analyzer.py +196 -0
  130. emdash_core/ingestion/github/__init__.py +6 -0
  131. emdash_core/ingestion/github/pr_fetcher.py +296 -0
  132. emdash_core/ingestion/github/task_extractor.py +100 -0
  133. emdash_core/ingestion/orchestrator.py +540 -0
  134. emdash_core/ingestion/parsers/__init__.py +10 -0
  135. emdash_core/ingestion/parsers/base_parser.py +66 -0
  136. emdash_core/ingestion/parsers/call_graph_builder.py +121 -0
  137. emdash_core/ingestion/parsers/class_extractor.py +154 -0
  138. emdash_core/ingestion/parsers/function_extractor.py +202 -0
  139. emdash_core/ingestion/parsers/import_analyzer.py +119 -0
  140. emdash_core/ingestion/parsers/python_parser.py +123 -0
  141. emdash_core/ingestion/parsers/registry.py +72 -0
  142. emdash_core/ingestion/parsers/ts_ast_parser.js +313 -0
  143. emdash_core/ingestion/parsers/typescript_parser.py +278 -0
  144. emdash_core/ingestion/repository.py +346 -0
  145. emdash_core/models/__init__.py +38 -0
  146. emdash_core/models/agent.py +68 -0
  147. emdash_core/models/index.py +77 -0
  148. emdash_core/models/query.py +113 -0
  149. emdash_core/planning/__init__.py +7 -0
  150. emdash_core/planning/agent_api.py +413 -0
  151. emdash_core/planning/context_builder.py +265 -0
  152. emdash_core/planning/feature_context.py +232 -0
  153. emdash_core/planning/feature_expander.py +646 -0
  154. emdash_core/planning/llm_explainer.py +198 -0
  155. emdash_core/planning/similarity.py +509 -0
  156. emdash_core/planning/team_focus.py +821 -0
  157. emdash_core/server.py +153 -0
  158. emdash_core/sse/__init__.py +5 -0
  159. emdash_core/sse/stream.py +196 -0
  160. emdash_core/swarm/__init__.py +17 -0
  161. emdash_core/swarm/merge_agent.py +383 -0
  162. emdash_core/swarm/session_manager.py +274 -0
  163. emdash_core/swarm/swarm_runner.py +226 -0
  164. emdash_core/swarm/task_definition.py +137 -0
  165. emdash_core/swarm/worker_spawner.py +319 -0
  166. emdash_core/swarm/worktree_manager.py +278 -0
  167. emdash_core/templates/__init__.py +10 -0
  168. emdash_core/templates/defaults/agent-builder.md.template +82 -0
  169. emdash_core/templates/defaults/focus.md.template +115 -0
  170. emdash_core/templates/defaults/pr-review-enhanced.md.template +309 -0
  171. emdash_core/templates/defaults/pr-review.md.template +80 -0
  172. emdash_core/templates/defaults/project.md.template +85 -0
  173. emdash_core/templates/defaults/research_critic.md.template +112 -0
  174. emdash_core/templates/defaults/research_planner.md.template +85 -0
  175. emdash_core/templates/defaults/research_synthesizer.md.template +128 -0
  176. emdash_core/templates/defaults/reviewer.md.template +81 -0
  177. emdash_core/templates/defaults/spec.md.template +41 -0
  178. emdash_core/templates/defaults/tasks.md.template +78 -0
  179. emdash_core/templates/loader.py +296 -0
  180. emdash_core/utils/__init__.py +45 -0
  181. emdash_core/utils/git.py +84 -0
  182. emdash_core/utils/image.py +502 -0
  183. emdash_core/utils/logger.py +51 -0
  184. emdash_core-0.1.7.dist-info/METADATA +35 -0
  185. emdash_core-0.1.7.dist-info/RECORD +187 -0
  186. emdash_core-0.1.7.dist-info/WHEEL +4 -0
  187. emdash_core-0.1.7.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,377 @@
1
+ """In-process sub-agent runner.
2
+
3
+ Runs sub-agents in the same process for better UX (real-time events)
4
+ while keeping isolated message histories.
5
+ """
6
+
7
+ import json
8
+ import time
9
+ import uuid
10
+ from dataclasses import dataclass, asdict
11
+ from pathlib import Path
12
+ from typing import Optional
13
+ from concurrent.futures import ThreadPoolExecutor, Future
14
+
15
+ from .toolkits import get_toolkit
16
+ from .subagent_prompts import get_subagent_prompt
17
+ from .providers import get_provider
18
+ from .providers.factory import DEFAULT_MODEL
19
+ from ..utils.logger import log
20
+
21
+
22
+ @dataclass
23
+ class SubAgentResult:
24
+ """Result from a sub-agent execution."""
25
+
26
+ success: bool
27
+ agent_type: str
28
+ agent_id: str
29
+ task: str
30
+ summary: str
31
+ files_explored: list[str]
32
+ findings: list[dict]
33
+ iterations: int
34
+ tools_used: list[str]
35
+ execution_time: float
36
+ error: Optional[str] = None
37
+
38
+ def to_dict(self) -> dict:
39
+ return asdict(self)
40
+
41
+
42
+ class InProcessSubAgent:
43
+ """Sub-agent that runs in the same process.
44
+
45
+ Benefits over subprocess:
46
+ - Real-time event streaming to parent emitter
47
+ - No stdout/stderr parsing
48
+ - Simpler debugging
49
+ - Natural UI integration
50
+
51
+ Each sub-agent has its own:
52
+ - Message history (isolated)
53
+ - Agent ID (for event tagging)
54
+ - Toolkit instance
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ subagent_type: str,
60
+ repo_root: Path,
61
+ emitter=None,
62
+ model: Optional[str] = None,
63
+ max_turns: int = 10,
64
+ agent_id: Optional[str] = None,
65
+ ):
66
+ """Initialize in-process sub-agent.
67
+
68
+ Args:
69
+ subagent_type: Type of agent (Explore, Plan, etc.)
70
+ repo_root: Repository root directory
71
+ emitter: Parent emitter for events (optional)
72
+ model: Model to use (defaults to fast model)
73
+ max_turns: Maximum iterations
74
+ agent_id: Optional agent ID (generated if not provided)
75
+ """
76
+ self.subagent_type = subagent_type
77
+ self.repo_root = repo_root.resolve()
78
+ self.emitter = emitter
79
+ self.max_turns = max_turns
80
+ self.agent_id = agent_id or str(uuid.uuid4())[:8]
81
+
82
+ # Get toolkit for this agent type
83
+ self.toolkit = get_toolkit(subagent_type, repo_root)
84
+
85
+ # Get model and create provider
86
+ model_name = model or DEFAULT_MODEL
87
+ self.provider = get_provider(model_name)
88
+
89
+ # Get system prompt
90
+ self.system_prompt = get_subagent_prompt(subagent_type)
91
+
92
+ # Tracking
93
+ self.files_explored: set[str] = set()
94
+ self.tools_used: list[str] = []
95
+
96
+ def _emit(self, event_type: str, **data) -> None:
97
+ """Emit event with agent tagging.
98
+
99
+ Uses the generic emit() method to preserve subagent_id and subagent_type
100
+ in the event data, allowing the UI to display sub-agent events differently.
101
+ """
102
+ if self.emitter and hasattr(self.emitter, "emit"):
103
+ from .events import EventType
104
+
105
+ # Tag event with agent info
106
+ data["subagent_id"] = self.agent_id
107
+ data["subagent_type"] = self.subagent_type
108
+
109
+ # Map event types
110
+ event_map = {
111
+ "tool_start": EventType.TOOL_START,
112
+ "tool_result": EventType.TOOL_RESULT,
113
+ }
114
+
115
+ if event_type in event_map:
116
+ self.emitter.emit(event_map[event_type], data)
117
+
118
+ def run(self, prompt: str) -> SubAgentResult:
119
+ """Execute the task and return results.
120
+
121
+ Args:
122
+ prompt: The task to perform
123
+
124
+ Returns:
125
+ SubAgentResult with findings
126
+ """
127
+ start_time = time.time()
128
+ messages = []
129
+ iterations = 0
130
+ last_content = ""
131
+ error = None
132
+
133
+ # Add user message
134
+ messages.append({"role": "user", "content": prompt})
135
+
136
+ log.info(
137
+ "SubAgent {} starting: type={} prompt={}",
138
+ self.agent_id,
139
+ self.subagent_type,
140
+ prompt[:50] + "..." if len(prompt) > 50 else prompt,
141
+ )
142
+
143
+ try:
144
+ # Agent loop
145
+ while iterations < self.max_turns:
146
+ iterations += 1
147
+
148
+ log.debug(f"SubAgent {self.agent_id} turn {iterations}/{self.max_turns}")
149
+
150
+ # Call LLM
151
+ response = self.provider.chat(
152
+ messages=messages,
153
+ tools=self.toolkit.get_all_schemas(),
154
+ system=self.system_prompt,
155
+ )
156
+
157
+ # Add assistant response
158
+ assistant_msg = self.provider.format_assistant_message(response)
159
+ if assistant_msg:
160
+ messages.append(assistant_msg)
161
+
162
+ # Save content
163
+ if response.content:
164
+ last_content = response.content
165
+
166
+ # Check if done
167
+ if not response.tool_calls:
168
+ break
169
+
170
+ # Execute tool calls
171
+ for tool_call in response.tool_calls:
172
+ self.tools_used.append(tool_call.name)
173
+
174
+ # Parse arguments
175
+ try:
176
+ args = json.loads(tool_call.arguments) if tool_call.arguments else {}
177
+ except (json.JSONDecodeError, TypeError):
178
+ args = {}
179
+
180
+ # Emit tool start
181
+ self._emit("tool_start", name=tool_call.name, args=args)
182
+
183
+ # Track files
184
+ if "path" in args:
185
+ self.files_explored.add(args["path"])
186
+
187
+ # Execute tool
188
+ result = self.toolkit.execute(tool_call.name, **args)
189
+
190
+ # Emit tool result
191
+ summary = str(result.data)[:100] if result.data else ""
192
+ self._emit(
193
+ "tool_result",
194
+ name=tool_call.name,
195
+ success=result.success,
196
+ summary=summary,
197
+ )
198
+
199
+ # Add tool result to messages
200
+ tool_result_msg = self.provider.format_tool_result(
201
+ tool_call.id,
202
+ json.dumps(result.to_dict(), indent=2),
203
+ )
204
+ if tool_result_msg:
205
+ messages.append(tool_result_msg)
206
+
207
+ except Exception as e:
208
+ log.exception(f"SubAgent {self.agent_id} failed")
209
+ error = str(e)
210
+
211
+ execution_time = time.time() - start_time
212
+
213
+ log.info(
214
+ "SubAgent {} completed: {} turns, {} files, {:.1f}s",
215
+ self.agent_id,
216
+ iterations,
217
+ len(self.files_explored),
218
+ execution_time,
219
+ )
220
+
221
+ return SubAgentResult(
222
+ success=error is None,
223
+ agent_type=self.subagent_type,
224
+ agent_id=self.agent_id,
225
+ task=prompt,
226
+ summary=last_content or "No response generated",
227
+ files_explored=list(self.files_explored),
228
+ findings=self._extract_findings(messages),
229
+ iterations=iterations,
230
+ tools_used=list(set(self.tools_used)),
231
+ execution_time=execution_time,
232
+ error=error,
233
+ )
234
+
235
+ def _extract_findings(self, messages: list[dict]) -> list[dict]:
236
+ """Extract key findings from tool results."""
237
+ findings = []
238
+ for msg in messages:
239
+ if msg and msg.get("role") == "tool":
240
+ try:
241
+ content = json.loads(msg.get("content", "{}"))
242
+ if content and content.get("success") and content.get("data"):
243
+ findings.append(content["data"])
244
+ except (json.JSONDecodeError, TypeError):
245
+ pass
246
+ return findings[-10:]
247
+
248
+
249
+ # Thread pool for parallel execution
250
+ _executor: Optional[ThreadPoolExecutor] = None
251
+
252
+
253
+ def _get_executor() -> ThreadPoolExecutor:
254
+ """Get or create thread pool executor."""
255
+ global _executor
256
+ if _executor is None:
257
+ _executor = ThreadPoolExecutor(max_workers=4, thread_name_prefix="subagent")
258
+ return _executor
259
+
260
+
261
+ def run_subagent(
262
+ subagent_type: str,
263
+ prompt: str,
264
+ repo_root: Path,
265
+ emitter=None,
266
+ model: Optional[str] = None,
267
+ max_turns: int = 10,
268
+ ) -> SubAgentResult:
269
+ """Run a sub-agent synchronously.
270
+
271
+ Args:
272
+ subagent_type: Type of agent (Explore, Plan)
273
+ prompt: Task to perform
274
+ repo_root: Repository root
275
+ emitter: Event emitter
276
+ model: Model to use
277
+ max_turns: Max iterations
278
+
279
+ Returns:
280
+ SubAgentResult
281
+ """
282
+ agent = InProcessSubAgent(
283
+ subagent_type=subagent_type,
284
+ repo_root=repo_root,
285
+ emitter=emitter,
286
+ model=model,
287
+ max_turns=max_turns,
288
+ )
289
+ return agent.run(prompt)
290
+
291
+
292
+ def run_subagent_async(
293
+ subagent_type: str,
294
+ prompt: str,
295
+ repo_root: Path,
296
+ emitter=None,
297
+ model: Optional[str] = None,
298
+ max_turns: int = 10,
299
+ ) -> Future[SubAgentResult]:
300
+ """Run a sub-agent asynchronously (returns Future).
301
+
302
+ Args:
303
+ subagent_type: Type of agent (Explore, Plan)
304
+ prompt: Task to perform
305
+ repo_root: Repository root
306
+ emitter: Event emitter
307
+ model: Model to use
308
+ max_turns: Max iterations
309
+
310
+ Returns:
311
+ Future[SubAgentResult] - call .result() to get result
312
+ """
313
+ executor = _get_executor()
314
+ return executor.submit(
315
+ run_subagent,
316
+ subagent_type=subagent_type,
317
+ prompt=prompt,
318
+ repo_root=repo_root,
319
+ emitter=emitter,
320
+ model=model,
321
+ max_turns=max_turns,
322
+ )
323
+
324
+
325
+ def run_subagents_parallel(
326
+ tasks: list[dict],
327
+ repo_root: Path,
328
+ emitter=None,
329
+ ) -> list[SubAgentResult]:
330
+ """Run multiple sub-agents in parallel.
331
+
332
+ Args:
333
+ tasks: List of task dicts with keys:
334
+ - subagent_type: str
335
+ - prompt: str
336
+ - model: str (optional)
337
+ - max_turns: int (optional)
338
+ repo_root: Repository root
339
+ emitter: Shared event emitter
340
+
341
+ Returns:
342
+ List of SubAgentResults in same order as tasks
343
+ """
344
+ futures = []
345
+ for task in tasks:
346
+ future = run_subagent_async(
347
+ subagent_type=task.get("subagent_type", "Explore"),
348
+ prompt=task["prompt"],
349
+ repo_root=repo_root,
350
+ emitter=emitter,
351
+ model=task.get("model"),
352
+ max_turns=task.get("max_turns", 10),
353
+ )
354
+ futures.append(future)
355
+
356
+ # Wait for all to complete and gather results
357
+ results = []
358
+ for future in futures:
359
+ try:
360
+ results.append(future.result())
361
+ except Exception as e:
362
+ log.exception("Sub-agent failed")
363
+ results.append(SubAgentResult(
364
+ success=False,
365
+ agent_type="unknown",
366
+ agent_id="error",
367
+ task="",
368
+ summary="",
369
+ files_explored=[],
370
+ findings=[],
371
+ iterations=0,
372
+ tools_used=[],
373
+ execution_time=0,
374
+ error=str(e),
375
+ ))
376
+
377
+ return results
@@ -0,0 +1,50 @@
1
+ """MCP (Model Context Protocol) integration for dynamic tool loading.
2
+
3
+ This module provides:
4
+ - MCPServerManager: Manages lifecycle of MCP servers
5
+ - GenericMCPClient: Client for communicating with MCP servers
6
+ - MCPConfigFile: Configuration loading/saving
7
+ - Tool factory functions for creating tools from MCP servers
8
+ """
9
+
10
+ from .config import (
11
+ MCPServerConfig,
12
+ MCPConfigFile,
13
+ get_default_mcp_config_path,
14
+ ensure_mcp_config,
15
+ )
16
+ from .client import (
17
+ GenericMCPClient,
18
+ MCPToolInfo,
19
+ MCPResponse,
20
+ MCPError,
21
+ )
22
+ from .manager import (
23
+ MCPServerManager,
24
+ get_mcp_manager,
25
+ reset_mcp_manager,
26
+ )
27
+ from .tool_factory import (
28
+ MCPDynamicTool,
29
+ create_tools_from_mcp,
30
+ )
31
+
32
+ __all__ = [
33
+ # Config
34
+ "MCPServerConfig",
35
+ "MCPConfigFile",
36
+ "get_default_mcp_config_path",
37
+ "ensure_mcp_config",
38
+ # Client
39
+ "GenericMCPClient",
40
+ "MCPToolInfo",
41
+ "MCPResponse",
42
+ "MCPError",
43
+ # Manager
44
+ "MCPServerManager",
45
+ "get_mcp_manager",
46
+ "reset_mcp_manager",
47
+ # Tool factory
48
+ "MCPDynamicTool",
49
+ "create_tools_from_mcp",
50
+ ]