claude-mpm 4.4.0__py3-none-any.whl → 4.4.4__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/WORKFLOW.md +2 -14
- claude_mpm/agents/agent_loader.py +3 -2
- claude_mpm/agents/agent_loader_integration.py +2 -1
- claude_mpm/agents/async_agent_loader.py +2 -2
- claude_mpm/agents/base_agent_loader.py +2 -2
- claude_mpm/agents/frontmatter_validator.py +1 -0
- claude_mpm/agents/system_agent_config.py +2 -1
- claude_mpm/cli/commands/configure.py +2 -29
- claude_mpm/cli/commands/doctor.py +44 -5
- claude_mpm/cli/commands/mpm_init.py +117 -63
- claude_mpm/cli/parsers/configure_parser.py +6 -15
- claude_mpm/cli/startup_logging.py +1 -3
- claude_mpm/config/agent_config.py +1 -1
- claude_mpm/config/paths.py +2 -1
- claude_mpm/core/agent_name_normalizer.py +1 -0
- claude_mpm/core/config.py +2 -1
- claude_mpm/core/config_aliases.py +2 -1
- claude_mpm/core/file_utils.py +0 -1
- claude_mpm/core/framework/__init__.py +38 -0
- claude_mpm/core/framework/formatters/__init__.py +11 -0
- claude_mpm/core/framework/formatters/capability_generator.py +367 -0
- claude_mpm/core/framework/formatters/content_formatter.py +288 -0
- claude_mpm/core/framework/formatters/context_generator.py +184 -0
- claude_mpm/core/framework/loaders/__init__.py +13 -0
- claude_mpm/core/framework/loaders/agent_loader.py +206 -0
- claude_mpm/core/framework/loaders/file_loader.py +223 -0
- claude_mpm/core/framework/loaders/instruction_loader.py +161 -0
- claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
- claude_mpm/core/framework/processors/__init__.py +11 -0
- claude_mpm/core/framework/processors/memory_processor.py +230 -0
- claude_mpm/core/framework/processors/metadata_processor.py +146 -0
- claude_mpm/core/framework/processors/template_processor.py +244 -0
- claude_mpm/core/framework_loader.py +298 -1795
- claude_mpm/core/log_manager.py +2 -1
- claude_mpm/core/tool_access_control.py +1 -0
- claude_mpm/core/unified_agent_registry.py +2 -1
- claude_mpm/core/unified_paths.py +1 -0
- claude_mpm/experimental/cli_enhancements.py +1 -0
- claude_mpm/hooks/__init__.py +9 -1
- claude_mpm/hooks/base_hook.py +1 -0
- claude_mpm/hooks/instruction_reinforcement.py +1 -0
- claude_mpm/hooks/kuzu_memory_hook.py +359 -0
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/scripts/mpm_doctor.py +1 -0
- claude_mpm/services/agents/loading/agent_profile_loader.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -1
- claude_mpm/services/agents/loading/framework_agent_loader.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -0
- claude_mpm/services/agents/management/agent_management_service.py +1 -1
- claude_mpm/services/agents/memory/memory_categorization_service.py +0 -1
- claude_mpm/services/agents/memory/memory_file_service.py +6 -2
- claude_mpm/services/agents/memory/memory_format_service.py +0 -1
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/async_session_logger.py +1 -1
- claude_mpm/services/claude_session_logger.py +1 -0
- claude_mpm/services/core/path_resolver.py +2 -0
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +126 -25
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +399 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +4 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +259 -32
- claude_mpm/services/event_bus/direct_relay.py +2 -1
- claude_mpm/services/event_bus/event_bus.py +1 -0
- claude_mpm/services/event_bus/relay.py +3 -2
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +1 -1
- claude_mpm/services/infrastructure/daemon_manager.py +1 -1
- claude_mpm/services/mcp_config_manager.py +67 -4
- claude_mpm/services/mcp_gateway/core/process_pool.py +320 -0
- claude_mpm/services/mcp_gateway/core/startup_verification.py +2 -2
- claude_mpm/services/mcp_gateway/main.py +3 -13
- claude_mpm/services/mcp_gateway/server/stdio_server.py +4 -10
- claude_mpm/services/mcp_gateway/tools/__init__.py +14 -2
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +38 -6
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +527 -0
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/project/archive_manager.py +159 -96
- claude_mpm/services/project/documentation_manager.py +64 -45
- claude_mpm/services/project/enhanced_analyzer.py +132 -89
- claude_mpm/services/project/project_organizer.py +225 -131
- claude_mpm/services/response_tracker.py +1 -1
- claude_mpm/services/shared/__init__.py +2 -1
- claude_mpm/services/shared/service_factory.py +8 -5
- claude_mpm/services/socketio/server/eventbus_integration.py +1 -1
- claude_mpm/services/unified/__init__.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/__init__.py +3 -3
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +97 -53
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +81 -40
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +277 -178
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +196 -112
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +83 -49
- claude_mpm/services/unified/config_strategies/__init__.py +175 -0
- claude_mpm/services/unified/config_strategies/config_schema.py +735 -0
- claude_mpm/services/unified/config_strategies/context_strategy.py +750 -0
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1009 -0
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +879 -0
- claude_mpm/services/unified/config_strategies/unified_config_service.py +814 -0
- claude_mpm/services/unified/config_strategies/validation_strategy.py +1144 -0
- claude_mpm/services/unified/deployment_strategies/__init__.py +7 -7
- claude_mpm/services/unified/deployment_strategies/base.py +24 -28
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +168 -88
- claude_mpm/services/unified/deployment_strategies/local.py +49 -34
- claude_mpm/services/unified/deployment_strategies/utils.py +39 -43
- claude_mpm/services/unified/deployment_strategies/vercel.py +30 -24
- claude_mpm/services/unified/interfaces.py +0 -26
- claude_mpm/services/unified/migration.py +17 -40
- claude_mpm/services/unified/strategies.py +9 -26
- claude_mpm/services/unified/unified_analyzer.py +48 -44
- claude_mpm/services/unified/unified_config.py +21 -19
- claude_mpm/services/unified/unified_deployment.py +21 -26
- claude_mpm/storage/state_storage.py +1 -0
- claude_mpm/utils/agent_dependency_loader.py +18 -6
- claude_mpm/utils/common.py +14 -12
- claude_mpm/utils/database_connector.py +15 -12
- claude_mpm/utils/error_handler.py +1 -0
- claude_mpm/utils/log_cleanup.py +1 -0
- claude_mpm/utils/path_operations.py +1 -0
- claude_mpm/utils/session_logging.py +1 -1
- claude_mpm/utils/subprocess_utils.py +1 -0
- claude_mpm/validation/agent_validator.py +1 -1
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/METADATA +23 -17
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/RECORD +126 -105
- claude_mpm/cli/commands/configure_tui.py +0 -1927
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +0 -645
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +0 -602
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/top_level.txt +0 -0
@@ -1,602 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Unified Ticket Tool for MCP Gateway
|
3
|
-
====================================
|
4
|
-
|
5
|
-
Provides a single, unified interface for all ticket management operations
|
6
|
-
through the MCP Gateway, consolidating create, list, update, view, and search
|
7
|
-
functionality into one tool with an operation parameter.
|
8
|
-
|
9
|
-
WHY: Having 5 separate ticket tools creates unnecessary complexity. A single
|
10
|
-
tool with an operation parameter provides a cleaner, more intuitive API that
|
11
|
-
matches the mental model of "ticket operations" better.
|
12
|
-
|
13
|
-
DESIGN DECISIONS:
|
14
|
-
- Single tool with operation parameter for cleaner API
|
15
|
-
- Conditional parameter validation based on operation type
|
16
|
-
- Reuses existing logic from separate tools for consistency
|
17
|
-
- Maintains same error handling and metrics tracking patterns
|
18
|
-
- Uses JSON Schema oneOf for operation-specific parameter validation
|
19
|
-
"""
|
20
|
-
|
21
|
-
import asyncio
|
22
|
-
import json
|
23
|
-
import re
|
24
|
-
from datetime import datetime, timezone
|
25
|
-
from typing import Any, Dict, Optional
|
26
|
-
|
27
|
-
from claude_mpm.services.mcp_gateway.core.interfaces import (
|
28
|
-
MCPToolDefinition,
|
29
|
-
MCPToolInvocation,
|
30
|
-
MCPToolResult,
|
31
|
-
)
|
32
|
-
from claude_mpm.services.mcp_gateway.tools.base_adapter import BaseToolAdapter
|
33
|
-
|
34
|
-
|
35
|
-
class UnifiedTicketTool(BaseToolAdapter):
|
36
|
-
"""
|
37
|
-
Unified ticket management tool for aitrackdown operations.
|
38
|
-
|
39
|
-
WHY: Consolidates all ticket operations (create, list, update, view, search)
|
40
|
-
into a single tool with an operation parameter, providing a cleaner and more
|
41
|
-
intuitive interface for ticket management.
|
42
|
-
|
43
|
-
DESIGN DECISIONS:
|
44
|
-
- Use operation parameter to route to appropriate handler
|
45
|
-
- Implement conditional parameter validation per operation
|
46
|
-
- Maintain backward compatibility with existing aitrackdown CLI
|
47
|
-
- Preserve all error handling and metrics from original tools
|
48
|
-
"""
|
49
|
-
|
50
|
-
def __init__(self):
|
51
|
-
"""Initialize the unified ticket tool with comprehensive schema."""
|
52
|
-
definition = MCPToolDefinition(
|
53
|
-
name="ticket",
|
54
|
-
description="Unified ticket management tool for all aitrackdown operations",
|
55
|
-
input_schema={
|
56
|
-
"type": "object",
|
57
|
-
"properties": {
|
58
|
-
"operation": {
|
59
|
-
"type": "string",
|
60
|
-
"enum": ["create", "list", "update", "view", "search"],
|
61
|
-
"description": "The ticket operation to perform",
|
62
|
-
},
|
63
|
-
# Create operation parameters
|
64
|
-
"type": {
|
65
|
-
"type": "string",
|
66
|
-
"enum": ["task", "issue", "epic"],
|
67
|
-
"description": "Type of ticket (for create operation)",
|
68
|
-
},
|
69
|
-
"title": {
|
70
|
-
"type": "string",
|
71
|
-
"description": "Title of the ticket (for create operation)",
|
72
|
-
},
|
73
|
-
"description": {
|
74
|
-
"type": "string",
|
75
|
-
"description": "Detailed description (for create operation)",
|
76
|
-
},
|
77
|
-
"priority": {
|
78
|
-
"type": "string",
|
79
|
-
"enum": ["low", "medium", "high", "critical"],
|
80
|
-
"description": "Priority level",
|
81
|
-
},
|
82
|
-
"tags": {
|
83
|
-
"type": "array",
|
84
|
-
"items": {"type": "string"},
|
85
|
-
"description": "Tags to associate with the ticket (for create)",
|
86
|
-
},
|
87
|
-
"parent_epic": {
|
88
|
-
"type": "string",
|
89
|
-
"description": "Parent epic ID for issues (create operation)",
|
90
|
-
},
|
91
|
-
"parent_issue": {
|
92
|
-
"type": "string",
|
93
|
-
"description": "Parent issue ID for tasks (create operation)",
|
94
|
-
},
|
95
|
-
# List operation parameters
|
96
|
-
"limit": {
|
97
|
-
"type": "number",
|
98
|
-
"description": "Maximum number of results to return",
|
99
|
-
"default": 10,
|
100
|
-
},
|
101
|
-
# Update operation parameters
|
102
|
-
"ticket_id": {
|
103
|
-
"type": "string",
|
104
|
-
"description": "Ticket ID (for update/view operations)",
|
105
|
-
},
|
106
|
-
"status": {
|
107
|
-
"type": "string",
|
108
|
-
"enum": [
|
109
|
-
"all",
|
110
|
-
"open",
|
111
|
-
"in-progress",
|
112
|
-
"ready",
|
113
|
-
"tested",
|
114
|
-
"done",
|
115
|
-
"waiting",
|
116
|
-
"closed",
|
117
|
-
"blocked",
|
118
|
-
],
|
119
|
-
"description": "Status filter or new status",
|
120
|
-
},
|
121
|
-
"comment": {
|
122
|
-
"type": "string",
|
123
|
-
"description": "Comment for update operation",
|
124
|
-
},
|
125
|
-
# View operation parameters
|
126
|
-
"format": {
|
127
|
-
"type": "string",
|
128
|
-
"enum": ["json", "text"],
|
129
|
-
"description": "Output format (for view operation)",
|
130
|
-
"default": "json",
|
131
|
-
},
|
132
|
-
# Search operation parameters
|
133
|
-
"query": {
|
134
|
-
"type": "string",
|
135
|
-
"description": "Search query keywords (for search operation)",
|
136
|
-
},
|
137
|
-
},
|
138
|
-
"required": ["operation"],
|
139
|
-
# Note: Additional validation is handled in the invoke method
|
140
|
-
# to avoid using allOf/oneOf/anyOf at the top level which is not
|
141
|
-
# supported by the Claude API
|
142
|
-
},
|
143
|
-
)
|
144
|
-
super().__init__(definition)
|
145
|
-
|
146
|
-
async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
|
147
|
-
"""
|
148
|
-
Route the invocation to the appropriate operation handler.
|
149
|
-
|
150
|
-
Args:
|
151
|
-
invocation: Tool invocation request
|
152
|
-
|
153
|
-
Returns:
|
154
|
-
Tool execution result from the specific operation
|
155
|
-
"""
|
156
|
-
operation = invocation.parameters.get("operation")
|
157
|
-
|
158
|
-
if not operation:
|
159
|
-
return MCPToolResult(
|
160
|
-
success=False,
|
161
|
-
error="Operation parameter is required",
|
162
|
-
execution_time=0.0,
|
163
|
-
)
|
164
|
-
|
165
|
-
# Validate required parameters based on operation type
|
166
|
-
validation_error = self._validate_parameters(operation, invocation.parameters)
|
167
|
-
if validation_error:
|
168
|
-
return MCPToolResult(
|
169
|
-
success=False,
|
170
|
-
error=validation_error,
|
171
|
-
execution_time=0.0,
|
172
|
-
)
|
173
|
-
|
174
|
-
# Route to appropriate handler based on operation
|
175
|
-
handlers = {
|
176
|
-
"create": self._handle_create,
|
177
|
-
"list": self._handle_list,
|
178
|
-
"update": self._handle_update,
|
179
|
-
"view": self._handle_view,
|
180
|
-
"search": self._handle_search,
|
181
|
-
}
|
182
|
-
|
183
|
-
handler = handlers.get(operation)
|
184
|
-
if not handler:
|
185
|
-
return MCPToolResult(
|
186
|
-
success=False,
|
187
|
-
error=f"Unknown operation: {operation}",
|
188
|
-
execution_time=0.0,
|
189
|
-
)
|
190
|
-
|
191
|
-
return await handler(invocation.parameters)
|
192
|
-
|
193
|
-
def _validate_parameters(
|
194
|
-
self, operation: str, params: Dict[str, Any]
|
195
|
-
) -> Optional[str]:
|
196
|
-
"""
|
197
|
-
Validate parameters based on the operation type.
|
198
|
-
|
199
|
-
Args:
|
200
|
-
operation: The operation being performed
|
201
|
-
params: Parameters provided for the operation
|
202
|
-
|
203
|
-
Returns:
|
204
|
-
Error message if validation fails, None if valid
|
205
|
-
"""
|
206
|
-
if operation == "create":
|
207
|
-
if "type" not in params:
|
208
|
-
return "'type' parameter is required for create operation"
|
209
|
-
if "title" not in params:
|
210
|
-
return "'title' parameter is required for create operation"
|
211
|
-
if params["type"] not in ["task", "issue", "epic"]:
|
212
|
-
return f"Invalid type '{params['type']}'. Must be 'task', 'issue', or 'epic'"
|
213
|
-
|
214
|
-
elif operation == "update":
|
215
|
-
if "ticket_id" not in params:
|
216
|
-
return "'ticket_id' parameter is required for update operation"
|
217
|
-
|
218
|
-
elif operation == "view":
|
219
|
-
if "ticket_id" not in params:
|
220
|
-
return "'ticket_id' parameter is required for view operation"
|
221
|
-
|
222
|
-
elif operation == "search":
|
223
|
-
if "query" not in params:
|
224
|
-
return "'query' parameter is required for search operation"
|
225
|
-
|
226
|
-
elif operation == "list":
|
227
|
-
# List operation has no required parameters beyond operation itself
|
228
|
-
pass
|
229
|
-
|
230
|
-
else:
|
231
|
-
return f"Unknown operation: {operation}"
|
232
|
-
|
233
|
-
return None
|
234
|
-
|
235
|
-
async def _handle_create(self, params: Dict[str, Any]) -> MCPToolResult:
|
236
|
-
"""
|
237
|
-
Handle ticket creation operation.
|
238
|
-
|
239
|
-
Args:
|
240
|
-
params: Parameters for ticket creation
|
241
|
-
|
242
|
-
Returns:
|
243
|
-
Tool execution result with created ticket ID
|
244
|
-
"""
|
245
|
-
start_time = datetime.now(timezone.utc)
|
246
|
-
|
247
|
-
try:
|
248
|
-
# Build aitrackdown command
|
249
|
-
cmd = ["aitrackdown", "create", params["type"], params["title"]]
|
250
|
-
|
251
|
-
# Add optional parameters
|
252
|
-
if "description" in params:
|
253
|
-
cmd.extend(["--description", params["description"]])
|
254
|
-
|
255
|
-
if "priority" in params:
|
256
|
-
cmd.extend(["--priority", params["priority"]])
|
257
|
-
|
258
|
-
if params.get("tags"):
|
259
|
-
for tag in params["tags"]:
|
260
|
-
cmd.extend(["--tag", tag])
|
261
|
-
|
262
|
-
# For tasks, use --issue to associate with parent issue
|
263
|
-
if params["type"] == "task" and "parent_issue" in params:
|
264
|
-
cmd.extend(["--issue", params["parent_issue"]])
|
265
|
-
|
266
|
-
# Execute command asynchronously
|
267
|
-
process = await asyncio.create_subprocess_exec(
|
268
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
269
|
-
)
|
270
|
-
|
271
|
-
stdout, stderr = await process.communicate()
|
272
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
273
|
-
|
274
|
-
if process.returncode == 0:
|
275
|
-
# Parse ticket ID from output
|
276
|
-
output = stdout.decode().strip()
|
277
|
-
ticket_id = None
|
278
|
-
for line in output.split("\n"):
|
279
|
-
if "TSK-" in line or "ISS-" in line or "EP-" in line:
|
280
|
-
match = re.search(r"(TSK|ISS|EP)-\d+", line)
|
281
|
-
if match:
|
282
|
-
ticket_id = match.group(0)
|
283
|
-
break
|
284
|
-
|
285
|
-
self._update_metrics(True, execution_time)
|
286
|
-
|
287
|
-
return MCPToolResult(
|
288
|
-
success=True,
|
289
|
-
data={
|
290
|
-
"ticket_id": ticket_id or "Unknown",
|
291
|
-
"type": params["type"],
|
292
|
-
"title": params["title"],
|
293
|
-
"message": output,
|
294
|
-
},
|
295
|
-
execution_time=execution_time,
|
296
|
-
metadata={"tool": "ticket", "operation": "create"},
|
297
|
-
)
|
298
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
299
|
-
self._update_metrics(False, execution_time)
|
300
|
-
|
301
|
-
return MCPToolResult(
|
302
|
-
success=False,
|
303
|
-
error=f"Failed to create ticket: {error_msg}",
|
304
|
-
execution_time=execution_time,
|
305
|
-
)
|
306
|
-
|
307
|
-
except Exception as e:
|
308
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
309
|
-
self._update_metrics(False, execution_time)
|
310
|
-
|
311
|
-
return MCPToolResult(
|
312
|
-
success=False,
|
313
|
-
error=f"Ticket creation failed: {e!s}",
|
314
|
-
execution_time=execution_time,
|
315
|
-
)
|
316
|
-
|
317
|
-
async def _handle_list(self, params: Dict[str, Any]) -> MCPToolResult:
|
318
|
-
"""
|
319
|
-
Handle ticket listing operation.
|
320
|
-
|
321
|
-
Args:
|
322
|
-
params: Parameters for ticket listing
|
323
|
-
|
324
|
-
Returns:
|
325
|
-
Tool execution result with list of tickets
|
326
|
-
"""
|
327
|
-
start_time = datetime.now(timezone.utc)
|
328
|
-
|
329
|
-
try:
|
330
|
-
limit = params.get("limit", 10)
|
331
|
-
|
332
|
-
# Build aitrackdown command - use status tasks for listing
|
333
|
-
cmd = ["aitrackdown", "status", "tasks", "--limit", str(limit)]
|
334
|
-
|
335
|
-
# Add filters
|
336
|
-
if params.get("status") and params["status"] != "all":
|
337
|
-
cmd.extend(["--status", params["status"]])
|
338
|
-
|
339
|
-
# Execute command asynchronously
|
340
|
-
process = await asyncio.create_subprocess_exec(
|
341
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
342
|
-
)
|
343
|
-
|
344
|
-
stdout, stderr = await process.communicate()
|
345
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
346
|
-
|
347
|
-
if process.returncode == 0:
|
348
|
-
try:
|
349
|
-
# Try to parse JSON output
|
350
|
-
tickets = json.loads(stdout.decode())
|
351
|
-
except json.JSONDecodeError:
|
352
|
-
# Fallback to text parsing if JSON fails
|
353
|
-
output = stdout.decode().strip()
|
354
|
-
tickets = {"raw_output": output, "count": output.count("\n") + 1}
|
355
|
-
|
356
|
-
self._update_metrics(True, execution_time)
|
357
|
-
|
358
|
-
return MCPToolResult(
|
359
|
-
success=True,
|
360
|
-
data=tickets,
|
361
|
-
execution_time=execution_time,
|
362
|
-
metadata={
|
363
|
-
"tool": "ticket",
|
364
|
-
"operation": "list",
|
365
|
-
"count": len(tickets) if isinstance(tickets, list) else 1,
|
366
|
-
},
|
367
|
-
)
|
368
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
369
|
-
self._update_metrics(False, execution_time)
|
370
|
-
|
371
|
-
return MCPToolResult(
|
372
|
-
success=False,
|
373
|
-
error=f"Failed to list tickets: {error_msg}",
|
374
|
-
execution_time=execution_time,
|
375
|
-
)
|
376
|
-
|
377
|
-
except Exception as e:
|
378
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
379
|
-
self._update_metrics(False, execution_time)
|
380
|
-
|
381
|
-
return MCPToolResult(
|
382
|
-
success=False,
|
383
|
-
error=f"Ticket listing failed: {e!s}",
|
384
|
-
execution_time=execution_time,
|
385
|
-
)
|
386
|
-
|
387
|
-
async def _handle_update(self, params: Dict[str, Any]) -> MCPToolResult:
|
388
|
-
"""
|
389
|
-
Handle ticket update operation.
|
390
|
-
|
391
|
-
Args:
|
392
|
-
params: Parameters for ticket update
|
393
|
-
|
394
|
-
Returns:
|
395
|
-
Tool execution result
|
396
|
-
"""
|
397
|
-
start_time = datetime.now(timezone.utc)
|
398
|
-
|
399
|
-
try:
|
400
|
-
ticket_id = params["ticket_id"]
|
401
|
-
|
402
|
-
# Determine which update to perform
|
403
|
-
if "status" in params and params["status"] != "all":
|
404
|
-
# Use transition command for status updates
|
405
|
-
cmd = ["aitrackdown", "transition", ticket_id, params["status"]]
|
406
|
-
|
407
|
-
if "comment" in params:
|
408
|
-
cmd.extend(["--comment", params["comment"]])
|
409
|
-
elif "priority" in params:
|
410
|
-
# For priority updates, use transition with comment
|
411
|
-
cmd = ["aitrackdown", "transition", ticket_id, "open"]
|
412
|
-
cmd.extend(["--comment", f"Priority changed to {params['priority']}"])
|
413
|
-
|
414
|
-
if "comment" in params:
|
415
|
-
cmd.extend(["--comment", params["comment"]])
|
416
|
-
else:
|
417
|
-
return MCPToolResult(
|
418
|
-
success=False,
|
419
|
-
error="No update fields provided (status or priority required)",
|
420
|
-
execution_time=0.0,
|
421
|
-
)
|
422
|
-
|
423
|
-
# Execute command asynchronously
|
424
|
-
process = await asyncio.create_subprocess_exec(
|
425
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
426
|
-
)
|
427
|
-
|
428
|
-
stdout, stderr = await process.communicate()
|
429
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
430
|
-
|
431
|
-
if process.returncode == 0:
|
432
|
-
self._update_metrics(True, execution_time)
|
433
|
-
|
434
|
-
return MCPToolResult(
|
435
|
-
success=True,
|
436
|
-
data={
|
437
|
-
"ticket_id": ticket_id,
|
438
|
-
"updated_fields": [
|
439
|
-
k for k in ["status", "priority"] if k in params
|
440
|
-
],
|
441
|
-
"message": stdout.decode().strip(),
|
442
|
-
},
|
443
|
-
execution_time=execution_time,
|
444
|
-
metadata={"tool": "ticket", "operation": "update"},
|
445
|
-
)
|
446
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
447
|
-
self._update_metrics(False, execution_time)
|
448
|
-
|
449
|
-
return MCPToolResult(
|
450
|
-
success=False,
|
451
|
-
error=f"Failed to update ticket: {error_msg}",
|
452
|
-
execution_time=execution_time,
|
453
|
-
)
|
454
|
-
|
455
|
-
except Exception as e:
|
456
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
457
|
-
self._update_metrics(False, execution_time)
|
458
|
-
|
459
|
-
return MCPToolResult(
|
460
|
-
success=False,
|
461
|
-
error=f"Ticket update failed: {e!s}",
|
462
|
-
execution_time=execution_time,
|
463
|
-
)
|
464
|
-
|
465
|
-
async def _handle_view(self, params: Dict[str, Any]) -> MCPToolResult:
|
466
|
-
"""
|
467
|
-
Handle ticket viewing operation.
|
468
|
-
|
469
|
-
Args:
|
470
|
-
params: Parameters for ticket viewing
|
471
|
-
|
472
|
-
Returns:
|
473
|
-
Tool execution result with ticket details
|
474
|
-
"""
|
475
|
-
start_time = datetime.now(timezone.utc)
|
476
|
-
|
477
|
-
try:
|
478
|
-
ticket_id = params["ticket_id"]
|
479
|
-
format_type = params.get("format", "json")
|
480
|
-
|
481
|
-
# Build aitrackdown command - use show for viewing
|
482
|
-
cmd = ["aitrackdown", "show", ticket_id]
|
483
|
-
|
484
|
-
# Execute command asynchronously
|
485
|
-
process = await asyncio.create_subprocess_exec(
|
486
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
487
|
-
)
|
488
|
-
|
489
|
-
stdout, stderr = await process.communicate()
|
490
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
491
|
-
|
492
|
-
if process.returncode == 0:
|
493
|
-
output = stdout.decode().strip()
|
494
|
-
|
495
|
-
if format_type == "json":
|
496
|
-
try:
|
497
|
-
ticket_data = json.loads(output)
|
498
|
-
except json.JSONDecodeError:
|
499
|
-
ticket_data = {"raw_output": output}
|
500
|
-
else:
|
501
|
-
ticket_data = {"raw_output": output}
|
502
|
-
|
503
|
-
self._update_metrics(True, execution_time)
|
504
|
-
|
505
|
-
return MCPToolResult(
|
506
|
-
success=True,
|
507
|
-
data=ticket_data,
|
508
|
-
execution_time=execution_time,
|
509
|
-
metadata={
|
510
|
-
"tool": "ticket",
|
511
|
-
"operation": "view",
|
512
|
-
"ticket_id": ticket_id,
|
513
|
-
},
|
514
|
-
)
|
515
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
516
|
-
self._update_metrics(False, execution_time)
|
517
|
-
|
518
|
-
return MCPToolResult(
|
519
|
-
success=False,
|
520
|
-
error=f"Failed to view ticket: {error_msg}",
|
521
|
-
execution_time=execution_time,
|
522
|
-
)
|
523
|
-
|
524
|
-
except Exception as e:
|
525
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
526
|
-
self._update_metrics(False, execution_time)
|
527
|
-
|
528
|
-
return MCPToolResult(
|
529
|
-
success=False,
|
530
|
-
error=f"Ticket view failed: {e!s}",
|
531
|
-
execution_time=execution_time,
|
532
|
-
)
|
533
|
-
|
534
|
-
async def _handle_search(self, params: Dict[str, Any]) -> MCPToolResult:
|
535
|
-
"""
|
536
|
-
Handle ticket search operation.
|
537
|
-
|
538
|
-
Args:
|
539
|
-
params: Parameters for ticket search
|
540
|
-
|
541
|
-
Returns:
|
542
|
-
Tool execution result with matching tickets
|
543
|
-
"""
|
544
|
-
start_time = datetime.now(timezone.utc)
|
545
|
-
|
546
|
-
try:
|
547
|
-
query = params["query"]
|
548
|
-
limit = params.get("limit", 10)
|
549
|
-
|
550
|
-
# Build aitrackdown command - use search tasks
|
551
|
-
cmd = ["aitrackdown", "search", "tasks", query, "--limit", str(limit)]
|
552
|
-
|
553
|
-
if params.get("type") and params["type"] != "all":
|
554
|
-
cmd.extend(["--type", params["type"]])
|
555
|
-
|
556
|
-
# Execute command asynchronously
|
557
|
-
process = await asyncio.create_subprocess_exec(
|
558
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
559
|
-
)
|
560
|
-
|
561
|
-
stdout, stderr = await process.communicate()
|
562
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
563
|
-
|
564
|
-
if process.returncode == 0:
|
565
|
-
try:
|
566
|
-
# Try to parse JSON output
|
567
|
-
results = json.loads(stdout.decode())
|
568
|
-
except json.JSONDecodeError:
|
569
|
-
# Fallback to text parsing if JSON fails
|
570
|
-
output = stdout.decode().strip()
|
571
|
-
results = {"raw_output": output, "query": query}
|
572
|
-
|
573
|
-
self._update_metrics(True, execution_time)
|
574
|
-
|
575
|
-
return MCPToolResult(
|
576
|
-
success=True,
|
577
|
-
data=results,
|
578
|
-
execution_time=execution_time,
|
579
|
-
metadata={"tool": "ticket", "operation": "search", "query": query},
|
580
|
-
)
|
581
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
582
|
-
self._update_metrics(False, execution_time)
|
583
|
-
|
584
|
-
return MCPToolResult(
|
585
|
-
success=False,
|
586
|
-
error=f"Failed to search tickets: {error_msg}",
|
587
|
-
execution_time=execution_time,
|
588
|
-
)
|
589
|
-
|
590
|
-
except Exception as e:
|
591
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
592
|
-
self._update_metrics(False, execution_time)
|
593
|
-
|
594
|
-
return MCPToolResult(
|
595
|
-
success=False,
|
596
|
-
error=f"Ticket search failed: {e!s}",
|
597
|
-
execution_time=execution_time,
|
598
|
-
)
|
599
|
-
|
600
|
-
|
601
|
-
# Export the unified ticket tool
|
602
|
-
__all__ = ["UnifiedTicketTool"]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|