massgen 0.1.3__py3-none-any.whl → 0.1.5__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 massgen might be problematic. Click here for more details.
- massgen/__init__.py +1 -1
- massgen/api_params_handler/_chat_completions_api_params_handler.py +4 -0
- massgen/api_params_handler/_claude_api_params_handler.py +4 -0
- massgen/api_params_handler/_gemini_api_params_handler.py +4 -0
- massgen/api_params_handler/_response_api_params_handler.py +4 -0
- massgen/backend/base_with_custom_tool_and_mcp.py +25 -5
- massgen/backend/docs/permissions_and_context_files.md +2 -2
- massgen/backend/response.py +2 -0
- massgen/chat_agent.py +340 -20
- massgen/cli.py +326 -19
- massgen/configs/README.md +92 -41
- massgen/configs/memory/gpt5mini_gemini_baseline_research_to_implementation.yaml +94 -0
- massgen/configs/memory/gpt5mini_gemini_context_window_management.yaml +187 -0
- massgen/configs/memory/gpt5mini_gemini_research_to_implementation.yaml +127 -0
- massgen/configs/memory/gpt5mini_high_reasoning_gemini.yaml +107 -0
- massgen/configs/memory/single_agent_compression_test.yaml +64 -0
- massgen/configs/tools/custom_tools/crawl4ai_example.yaml +55 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_file_generation_multi.yaml +61 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_file_generation_single.yaml +29 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_image_generation_multi.yaml +51 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_image_generation_single.yaml +33 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_speech_generation_multi.yaml +55 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_speech_generation_single.yaml +33 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_video_generation_multi.yaml +47 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_video_generation_single.yaml +29 -0
- massgen/configs/tools/custom_tools/multimodal_tools/understand_audio.yaml +1 -1
- massgen/configs/tools/custom_tools/multimodal_tools/understand_file.yaml +1 -1
- massgen/configs/tools/custom_tools/multimodal_tools/understand_image.yaml +1 -1
- massgen/configs/tools/custom_tools/multimodal_tools/understand_video.yaml +1 -1
- massgen/configs/tools/custom_tools/multimodal_tools/youtube_video_analysis.yaml +1 -1
- massgen/filesystem_manager/_filesystem_manager.py +1 -0
- massgen/filesystem_manager/_path_permission_manager.py +148 -0
- massgen/memory/README.md +277 -0
- massgen/memory/__init__.py +26 -0
- massgen/memory/_base.py +193 -0
- massgen/memory/_compression.py +237 -0
- massgen/memory/_context_monitor.py +211 -0
- massgen/memory/_conversation.py +255 -0
- massgen/memory/_fact_extraction_prompts.py +333 -0
- massgen/memory/_mem0_adapters.py +257 -0
- massgen/memory/_persistent.py +687 -0
- massgen/memory/docker-compose.qdrant.yml +36 -0
- massgen/memory/docs/DESIGN.md +388 -0
- massgen/memory/docs/QUICKSTART.md +409 -0
- massgen/memory/docs/SUMMARY.md +319 -0
- massgen/memory/docs/agent_use_memory.md +408 -0
- massgen/memory/docs/orchestrator_use_memory.md +586 -0
- massgen/memory/examples.py +237 -0
- massgen/message_templates.py +160 -12
- massgen/orchestrator.py +223 -7
- massgen/tests/memory/test_agent_compression.py +174 -0
- massgen/{configs/tools → tests}/memory/test_context_window_management.py +30 -30
- massgen/tests/memory/test_force_compression.py +154 -0
- massgen/tests/memory/test_simple_compression.py +147 -0
- massgen/tests/test_agent_memory.py +534 -0
- massgen/tests/test_binary_file_blocking.py +274 -0
- massgen/tests/test_case_studies.md +12 -12
- massgen/tests/test_conversation_memory.py +382 -0
- massgen/tests/test_multimodal_size_limits.py +407 -0
- massgen/tests/test_orchestrator_memory.py +620 -0
- massgen/tests/test_persistent_memory.py +435 -0
- massgen/token_manager/token_manager.py +6 -0
- massgen/tool/_manager.py +7 -2
- massgen/tool/_multimodal_tools/image_to_image_generation.py +293 -0
- massgen/tool/_multimodal_tools/text_to_file_generation.py +455 -0
- massgen/tool/_multimodal_tools/text_to_image_generation.py +222 -0
- massgen/tool/_multimodal_tools/text_to_speech_continue_generation.py +226 -0
- massgen/tool/_multimodal_tools/text_to_speech_transcription_generation.py +217 -0
- massgen/tool/_multimodal_tools/text_to_video_generation.py +223 -0
- massgen/tool/_multimodal_tools/understand_audio.py +19 -1
- massgen/tool/_multimodal_tools/understand_file.py +6 -1
- massgen/tool/_multimodal_tools/understand_image.py +112 -8
- massgen/tool/_multimodal_tools/understand_video.py +32 -5
- massgen/tool/_web_tools/crawl4ai_tool.py +718 -0
- massgen/tool/docs/multimodal_tools.md +589 -0
- massgen/tools/__init__.py +8 -0
- massgen/tools/_planning_mcp_server.py +520 -0
- massgen/tools/planning_dataclasses.py +434 -0
- {massgen-0.1.3.dist-info → massgen-0.1.5.dist-info}/METADATA +142 -82
- {massgen-0.1.3.dist-info → massgen-0.1.5.dist-info}/RECORD +84 -41
- massgen/configs/tools/custom_tools/crawl4ai_mcp_example.yaml +0 -67
- massgen/configs/tools/custom_tools/crawl4ai_multi_agent_example.yaml +0 -68
- massgen/configs/tools/memory/README.md +0 -199
- massgen/configs/tools/memory/gpt5mini_gemini_context_window_management.yaml +0 -131
- massgen/configs/tools/memory/gpt5mini_gemini_no_persistent_memory.yaml +0 -133
- massgen/configs/tools/multimodal/gpt5mini_gpt5nano_documentation_evolution.yaml +0 -97
- {massgen-0.1.3.dist-info → massgen-0.1.5.dist-info}/WHEEL +0 -0
- {massgen-0.1.3.dist-info → massgen-0.1.5.dist-info}/entry_points.txt +0 -0
- {massgen-0.1.3.dist-info → massgen-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {massgen-0.1.3.dist-info → massgen-0.1.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Planning MCP Server for MassGen
|
|
5
|
+
|
|
6
|
+
This MCP server provides task planning and management tools for agents,
|
|
7
|
+
enabling them to create, track, and manage task plans with dependencies.
|
|
8
|
+
|
|
9
|
+
Tools provided:
|
|
10
|
+
- create_task_plan: Create a new task plan with dependencies
|
|
11
|
+
- add_task: Add a new task to the plan
|
|
12
|
+
- update_task_status: Update task status and detect newly ready tasks
|
|
13
|
+
- edit_task: Edit a task's description
|
|
14
|
+
- get_task_plan: Get the complete current task plan
|
|
15
|
+
- delete_task: Remove a task from the plan
|
|
16
|
+
- get_ready_tasks: Get tasks ready to start (dependencies satisfied)
|
|
17
|
+
- get_blocked_tasks: Get tasks blocked by dependencies
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import uuid
|
|
22
|
+
from typing import Any, Dict, List, Optional, Union
|
|
23
|
+
|
|
24
|
+
import fastmcp
|
|
25
|
+
|
|
26
|
+
from massgen.tools.planning_dataclasses import TaskPlan
|
|
27
|
+
|
|
28
|
+
# Global storage for task plans (keyed by agent_id)
|
|
29
|
+
_task_plans: Dict[str, TaskPlan] = {}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_or_create_plan(agent_id: str, orchestrator_id: str) -> TaskPlan:
|
|
33
|
+
"""
|
|
34
|
+
Get existing plan or create new one for agent.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
agent_id: Agent identifier
|
|
38
|
+
orchestrator_id: Orchestrator identifier
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
TaskPlan for the agent
|
|
42
|
+
"""
|
|
43
|
+
key = f"{orchestrator_id}:{agent_id}"
|
|
44
|
+
if key not in _task_plans:
|
|
45
|
+
_task_plans[key] = TaskPlan(agent_id=key)
|
|
46
|
+
return _task_plans[key]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _resolve_dependency_references(
|
|
50
|
+
task_list: List[Union[str, Dict[str, Any]]]
|
|
51
|
+
) -> List[Dict[str, Any]]:
|
|
52
|
+
"""
|
|
53
|
+
Resolve dependency references (indices -> IDs) in task list.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
task_list: List of task specifications
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
List of normalized task dictionaries with resolved dependencies
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ValueError: If dependencies are invalid
|
|
63
|
+
"""
|
|
64
|
+
# First pass: Generate IDs for all tasks
|
|
65
|
+
normalized_tasks = []
|
|
66
|
+
for i, task_spec in enumerate(task_list):
|
|
67
|
+
if isinstance(task_spec, str):
|
|
68
|
+
# Simple string task
|
|
69
|
+
task_dict = {
|
|
70
|
+
"id": f"task_{i}_{uuid.uuid4().hex[:8]}",
|
|
71
|
+
"description": task_spec,
|
|
72
|
+
"depends_on": []
|
|
73
|
+
}
|
|
74
|
+
elif isinstance(task_spec, dict):
|
|
75
|
+
# Dictionary task
|
|
76
|
+
task_dict = task_spec.copy()
|
|
77
|
+
if "id" not in task_dict:
|
|
78
|
+
task_dict["id"] = f"task_{i}_{uuid.uuid4().hex[:8]}"
|
|
79
|
+
if "depends_on" not in task_dict:
|
|
80
|
+
task_dict["depends_on"] = []
|
|
81
|
+
else:
|
|
82
|
+
raise ValueError(f"Invalid task specification at index {i}: {task_spec}")
|
|
83
|
+
|
|
84
|
+
normalized_tasks.append(task_dict)
|
|
85
|
+
|
|
86
|
+
# Second pass: Resolve index-based dependencies to IDs
|
|
87
|
+
for i, task_dict in enumerate(normalized_tasks):
|
|
88
|
+
resolved_deps = []
|
|
89
|
+
for dep in task_dict.get("depends_on", []):
|
|
90
|
+
if isinstance(dep, int):
|
|
91
|
+
# Index-based reference
|
|
92
|
+
if dep < 0 or dep >= len(normalized_tasks):
|
|
93
|
+
raise ValueError(
|
|
94
|
+
f"Task '{task_dict['id']}': Invalid dependency index {dep}"
|
|
95
|
+
)
|
|
96
|
+
if dep >= i:
|
|
97
|
+
raise ValueError(
|
|
98
|
+
f"Task '{task_dict['id']}': Dependencies must reference earlier tasks"
|
|
99
|
+
)
|
|
100
|
+
resolved_deps.append(normalized_tasks[dep]["id"])
|
|
101
|
+
else:
|
|
102
|
+
# ID-based reference
|
|
103
|
+
resolved_deps.append(dep)
|
|
104
|
+
|
|
105
|
+
task_dict["depends_on"] = resolved_deps
|
|
106
|
+
|
|
107
|
+
return normalized_tasks
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def create_server() -> fastmcp.FastMCP:
|
|
111
|
+
"""Factory function to create and configure the planning MCP server."""
|
|
112
|
+
|
|
113
|
+
parser = argparse.ArgumentParser(description="Planning MCP Server")
|
|
114
|
+
parser.add_argument(
|
|
115
|
+
"--agent-id",
|
|
116
|
+
type=str,
|
|
117
|
+
required=True,
|
|
118
|
+
help="ID of the agent using this planning server",
|
|
119
|
+
)
|
|
120
|
+
parser.add_argument(
|
|
121
|
+
"--orchestrator-id",
|
|
122
|
+
type=str,
|
|
123
|
+
required=True,
|
|
124
|
+
help="ID of the orchestrator managing this agent",
|
|
125
|
+
)
|
|
126
|
+
args = parser.parse_args()
|
|
127
|
+
|
|
128
|
+
# Create the FastMCP server
|
|
129
|
+
mcp = fastmcp.FastMCP("Agent Task Planning")
|
|
130
|
+
|
|
131
|
+
# Store agent and orchestrator IDs
|
|
132
|
+
mcp.agent_id = args.agent_id
|
|
133
|
+
mcp.orchestrator_id = args.orchestrator_id
|
|
134
|
+
|
|
135
|
+
@mcp.tool()
|
|
136
|
+
def create_task_plan(tasks: List[Union[str, Dict[str, Any]]]) -> Dict[str, Any]:
|
|
137
|
+
"""
|
|
138
|
+
Create a new task plan with a list of tasks.
|
|
139
|
+
|
|
140
|
+
Tasks can be simple strings or structured dictionaries with dependencies.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
tasks: List of task descriptions or task objects
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Dictionary with plan_id and created task list
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
# Simple tasks (no dependencies)
|
|
150
|
+
create_task_plan([
|
|
151
|
+
"Research existing authentication methods",
|
|
152
|
+
"Design new OAuth flow",
|
|
153
|
+
"Implement backend endpoints"
|
|
154
|
+
])
|
|
155
|
+
|
|
156
|
+
# Tasks with dependencies (by index)
|
|
157
|
+
create_task_plan([
|
|
158
|
+
"Research OAuth providers",
|
|
159
|
+
{
|
|
160
|
+
"description": "Implement OAuth endpoints",
|
|
161
|
+
"depends_on": [0] # Depends on task at index 0
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"description": "Write integration tests",
|
|
165
|
+
"depends_on": [1] # Depends on task at index 1
|
|
166
|
+
}
|
|
167
|
+
])
|
|
168
|
+
|
|
169
|
+
# Tasks with named IDs and dependencies
|
|
170
|
+
create_task_plan([
|
|
171
|
+
{
|
|
172
|
+
"id": "research_oauth",
|
|
173
|
+
"description": "Research OAuth providers"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"id": "implement_oauth",
|
|
177
|
+
"description": "Implement OAuth endpoints",
|
|
178
|
+
"depends_on": ["research_oauth"]
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"id": "integration_tests",
|
|
182
|
+
"description": "Run integration tests",
|
|
183
|
+
"depends_on": ["implement_oauth"]
|
|
184
|
+
}
|
|
185
|
+
])
|
|
186
|
+
|
|
187
|
+
Dependency Rules:
|
|
188
|
+
- Can reference by index (0-based) or by custom task ID
|
|
189
|
+
- Dependencies must reference earlier tasks in the list
|
|
190
|
+
- Circular dependencies are rejected
|
|
191
|
+
- Tasks with no dependencies can start immediately
|
|
192
|
+
- Tasks with dependencies wait until all deps are completed
|
|
193
|
+
"""
|
|
194
|
+
try:
|
|
195
|
+
# Get or create plan for this agent
|
|
196
|
+
plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
|
|
197
|
+
|
|
198
|
+
# Clear existing tasks (creating new plan)
|
|
199
|
+
plan.tasks.clear()
|
|
200
|
+
plan._task_index.clear()
|
|
201
|
+
|
|
202
|
+
# Validate and resolve dependencies
|
|
203
|
+
normalized_tasks = _resolve_dependency_references(tasks)
|
|
204
|
+
plan.validate_dependencies(normalized_tasks)
|
|
205
|
+
|
|
206
|
+
# Create tasks
|
|
207
|
+
created_tasks = []
|
|
208
|
+
for task_spec in normalized_tasks:
|
|
209
|
+
task = plan.add_task(
|
|
210
|
+
description=task_spec["description"],
|
|
211
|
+
task_id=task_spec["id"],
|
|
212
|
+
depends_on=task_spec.get("depends_on", [])
|
|
213
|
+
)
|
|
214
|
+
created_tasks.append(task.to_dict())
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
"success": True,
|
|
218
|
+
"operation": "create_task_plan",
|
|
219
|
+
"plan_id": plan.agent_id,
|
|
220
|
+
"tasks": created_tasks,
|
|
221
|
+
"summary": {
|
|
222
|
+
"total_tasks": len(created_tasks),
|
|
223
|
+
"ready_tasks": len(plan.get_ready_tasks()),
|
|
224
|
+
"blocked_tasks": len(plan.get_blocked_tasks())
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
except Exception as e:
|
|
229
|
+
return {
|
|
230
|
+
"success": False,
|
|
231
|
+
"operation": "create_task_plan",
|
|
232
|
+
"error": str(e)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@mcp.tool()
|
|
236
|
+
def add_task(
|
|
237
|
+
description: str,
|
|
238
|
+
after_task_id: Optional[str] = None,
|
|
239
|
+
depends_on: Optional[List[str]] = None
|
|
240
|
+
) -> Dict[str, Any]:
|
|
241
|
+
"""
|
|
242
|
+
Add a new task to the plan.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
description: Task description
|
|
246
|
+
after_task_id: Optional ID to insert after (otherwise appends)
|
|
247
|
+
depends_on: Optional list of task IDs this task depends on
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Dictionary with new task details
|
|
251
|
+
|
|
252
|
+
Example:
|
|
253
|
+
# Add task with dependencies
|
|
254
|
+
add_task(
|
|
255
|
+
"Deploy to production",
|
|
256
|
+
depends_on=["run_tests", "update_docs"]
|
|
257
|
+
)
|
|
258
|
+
"""
|
|
259
|
+
try:
|
|
260
|
+
plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
|
|
261
|
+
|
|
262
|
+
task = plan.add_task(
|
|
263
|
+
description=description,
|
|
264
|
+
after_task_id=after_task_id,
|
|
265
|
+
depends_on=depends_on or []
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
"success": True,
|
|
270
|
+
"operation": "add_task",
|
|
271
|
+
"task": task.to_dict()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
except Exception as e:
|
|
275
|
+
return {
|
|
276
|
+
"success": False,
|
|
277
|
+
"operation": "add_task",
|
|
278
|
+
"error": str(e)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@mcp.tool()
|
|
282
|
+
def update_task_status(
|
|
283
|
+
task_id: str,
|
|
284
|
+
status: str # Will be validated as Literal in the function
|
|
285
|
+
) -> Dict[str, Any]:
|
|
286
|
+
"""
|
|
287
|
+
Update the status of a task.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
task_id: ID of task to update
|
|
291
|
+
status: New status (pending/in_progress/completed/blocked)
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
Dictionary with updated task details and newly ready tasks
|
|
295
|
+
|
|
296
|
+
Example:
|
|
297
|
+
update_task_status("research_oauth", "completed")
|
|
298
|
+
"""
|
|
299
|
+
try:
|
|
300
|
+
# Validate status
|
|
301
|
+
valid_statuses = ["pending", "in_progress", "completed", "blocked"]
|
|
302
|
+
if status not in valid_statuses:
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"Invalid status '{status}'. Must be one of: {valid_statuses}"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
|
|
308
|
+
result = plan.update_task_status(task_id, status)
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
"success": True,
|
|
312
|
+
"operation": "update_task_status",
|
|
313
|
+
**result
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
except Exception as e:
|
|
317
|
+
return {
|
|
318
|
+
"success": False,
|
|
319
|
+
"operation": "update_task_status",
|
|
320
|
+
"error": str(e)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
@mcp.tool()
|
|
324
|
+
def edit_task(
|
|
325
|
+
task_id: str,
|
|
326
|
+
description: Optional[str] = None
|
|
327
|
+
) -> Dict[str, Any]:
|
|
328
|
+
"""
|
|
329
|
+
Edit a task's description.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
task_id: ID of task to edit
|
|
333
|
+
description: New description (if provided)
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Dictionary with updated task details
|
|
337
|
+
|
|
338
|
+
Example:
|
|
339
|
+
edit_task("research_oauth", "Research OAuth 2.0 providers and best practices")
|
|
340
|
+
"""
|
|
341
|
+
try:
|
|
342
|
+
plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
|
|
343
|
+
task = plan.edit_task(task_id, description)
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
"success": True,
|
|
347
|
+
"operation": "edit_task",
|
|
348
|
+
"task": task.to_dict()
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
except Exception as e:
|
|
352
|
+
return {
|
|
353
|
+
"success": False,
|
|
354
|
+
"operation": "edit_task",
|
|
355
|
+
"error": str(e)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
@mcp.tool()
|
|
359
|
+
def get_task_plan() -> Dict[str, Any]:
|
|
360
|
+
"""
|
|
361
|
+
Get the current task plan for this agent.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Dictionary with complete task plan including all tasks and their statuses
|
|
365
|
+
|
|
366
|
+
Example:
|
|
367
|
+
plan = get_task_plan()
|
|
368
|
+
print(f"Total tasks: {plan['summary']['total_tasks']}")
|
|
369
|
+
print(f"Ready tasks: {plan['summary']['ready_tasks']}")
|
|
370
|
+
"""
|
|
371
|
+
try:
|
|
372
|
+
plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
|
|
373
|
+
|
|
374
|
+
ready_tasks = plan.get_ready_tasks()
|
|
375
|
+
blocked_tasks = plan.get_blocked_tasks()
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
"success": True,
|
|
379
|
+
"operation": "get_task_plan",
|
|
380
|
+
"plan": plan.to_dict(),
|
|
381
|
+
"summary": {
|
|
382
|
+
"total_tasks": len(plan.tasks),
|
|
383
|
+
"completed_tasks": sum(1 for t in plan.tasks if t.status == "completed"),
|
|
384
|
+
"in_progress_tasks": sum(1 for t in plan.tasks if t.status == "in_progress"),
|
|
385
|
+
"ready_tasks": len(ready_tasks),
|
|
386
|
+
"blocked_tasks": len(blocked_tasks)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
except Exception as e:
|
|
391
|
+
return {
|
|
392
|
+
"success": False,
|
|
393
|
+
"operation": "get_task_plan",
|
|
394
|
+
"error": str(e)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
@mcp.tool()
|
|
398
|
+
def delete_task(task_id: str) -> Dict[str, Any]:
|
|
399
|
+
"""
|
|
400
|
+
Remove a task from the plan.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
task_id: ID of task to delete
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
Success confirmation
|
|
407
|
+
|
|
408
|
+
Raises:
|
|
409
|
+
Error if other tasks depend on this task
|
|
410
|
+
|
|
411
|
+
Example:
|
|
412
|
+
delete_task("obsolete_task_id")
|
|
413
|
+
"""
|
|
414
|
+
try:
|
|
415
|
+
plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
|
|
416
|
+
plan.delete_task(task_id)
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
"success": True,
|
|
420
|
+
"operation": "delete_task",
|
|
421
|
+
"deleted_task_id": task_id
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
except Exception as e:
|
|
425
|
+
return {
|
|
426
|
+
"success": False,
|
|
427
|
+
"operation": "delete_task",
|
|
428
|
+
"error": str(e)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
@mcp.tool()
|
|
432
|
+
def get_ready_tasks() -> Dict[str, Any]:
|
|
433
|
+
"""
|
|
434
|
+
Get all tasks that are ready to start (dependencies satisfied).
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
Dictionary with list of tasks that have status='pending' and all
|
|
438
|
+
dependencies completed
|
|
439
|
+
|
|
440
|
+
Use cases:
|
|
441
|
+
- Identify which tasks can be worked on now
|
|
442
|
+
- Find tasks that can be delegated in parallel
|
|
443
|
+
- Avoid blocking on incomplete dependencies
|
|
444
|
+
|
|
445
|
+
Example:
|
|
446
|
+
result = get_ready_tasks()
|
|
447
|
+
for task in result['ready_tasks']:
|
|
448
|
+
print(f"Ready: {task['description']}")
|
|
449
|
+
"""
|
|
450
|
+
try:
|
|
451
|
+
plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
|
|
452
|
+
ready_tasks = plan.get_ready_tasks()
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
"success": True,
|
|
456
|
+
"operation": "get_ready_tasks",
|
|
457
|
+
"ready_tasks": [t.to_dict() for t in ready_tasks],
|
|
458
|
+
"count": len(ready_tasks)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
except Exception as e:
|
|
462
|
+
return {
|
|
463
|
+
"success": False,
|
|
464
|
+
"operation": "get_ready_tasks",
|
|
465
|
+
"error": str(e)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
@mcp.tool()
|
|
469
|
+
def get_blocked_tasks() -> Dict[str, Any]:
|
|
470
|
+
"""
|
|
471
|
+
Get all tasks that are blocked by incomplete dependencies.
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
Dictionary with list of tasks that have status='pending' but
|
|
475
|
+
dependencies not completed, including what each task is waiting on
|
|
476
|
+
|
|
477
|
+
Use cases:
|
|
478
|
+
- Understand what's blocking progress
|
|
479
|
+
- Prioritize completing blocking tasks
|
|
480
|
+
- Visualize dependency chains
|
|
481
|
+
|
|
482
|
+
Example:
|
|
483
|
+
result = get_blocked_tasks()
|
|
484
|
+
for task in result['blocked_tasks']:
|
|
485
|
+
print(f"Blocked: {task['description']}")
|
|
486
|
+
print(f" Waiting on: {task['blocking_task_ids']}")
|
|
487
|
+
"""
|
|
488
|
+
try:
|
|
489
|
+
plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
|
|
490
|
+
blocked_tasks = plan.get_blocked_tasks()
|
|
491
|
+
|
|
492
|
+
# Add blocking task info for each blocked task
|
|
493
|
+
blocked_with_info = []
|
|
494
|
+
for task in blocked_tasks:
|
|
495
|
+
task_dict = task.to_dict()
|
|
496
|
+
task_dict["blocking_task_ids"] = plan.get_blocking_tasks(task.id)
|
|
497
|
+
blocked_with_info.append(task_dict)
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
"success": True,
|
|
501
|
+
"operation": "get_blocked_tasks",
|
|
502
|
+
"blocked_tasks": blocked_with_info,
|
|
503
|
+
"count": len(blocked_tasks)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
except Exception as e:
|
|
507
|
+
return {
|
|
508
|
+
"success": False,
|
|
509
|
+
"operation": "get_blocked_tasks",
|
|
510
|
+
"error": str(e)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return mcp
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
if __name__ == "__main__":
|
|
517
|
+
import asyncio
|
|
518
|
+
import fastmcp
|
|
519
|
+
|
|
520
|
+
asyncio.run(fastmcp.run(create_server))
|