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,645 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
MCP Tool Adapters for aitrackdown Ticket Management
|
3
|
-
====================================================
|
4
|
-
|
5
|
-
Provides MCP tool wrappers for common aitrackdown operations, enabling
|
6
|
-
ticket management through Claude Code's MCP interface.
|
7
|
-
|
8
|
-
WHY: These tools allow Claude Code to interact with aitrackdown for
|
9
|
-
ticket management without requiring direct CLI access, providing a
|
10
|
-
seamless integration experience.
|
11
|
-
|
12
|
-
DESIGN DECISIONS:
|
13
|
-
- Thin wrappers that delegate to aitrackdown CLI for actual operations
|
14
|
-
- Focus on the most common operations that benefit from Claude integration
|
15
|
-
- Structured responses for better error handling and user feedback
|
16
|
-
- Async execution to avoid blocking the MCP gateway
|
17
|
-
"""
|
18
|
-
|
19
|
-
import asyncio
|
20
|
-
import json
|
21
|
-
from datetime import datetime, timezone
|
22
|
-
|
23
|
-
from claude_mpm.services.mcp_gateway.core.interfaces import (
|
24
|
-
MCPToolDefinition,
|
25
|
-
MCPToolInvocation,
|
26
|
-
MCPToolResult,
|
27
|
-
)
|
28
|
-
from claude_mpm.services.mcp_gateway.tools.base_adapter import BaseToolAdapter
|
29
|
-
|
30
|
-
|
31
|
-
class TicketCreateTool(BaseToolAdapter):
|
32
|
-
"""
|
33
|
-
Create new tickets using aitrackdown.
|
34
|
-
|
35
|
-
WHY: Creating tickets is the most fundamental operation for tracking work items.
|
36
|
-
This tool provides a simple interface for creating tasks, issues, and epics.
|
37
|
-
"""
|
38
|
-
|
39
|
-
def __init__(self):
|
40
|
-
"""Initialize the ticket create tool."""
|
41
|
-
definition = MCPToolDefinition(
|
42
|
-
name="ticket_create",
|
43
|
-
description="Create a new ticket (task, issue, or epic) using aitrackdown",
|
44
|
-
input_schema={
|
45
|
-
"type": "object",
|
46
|
-
"properties": {
|
47
|
-
"type": {
|
48
|
-
"type": "string",
|
49
|
-
"enum": ["task", "issue", "epic"],
|
50
|
-
"description": "Type of ticket to create",
|
51
|
-
},
|
52
|
-
"title": {"type": "string", "description": "Title of the ticket"},
|
53
|
-
"description": {
|
54
|
-
"type": "string",
|
55
|
-
"description": "Detailed description of the ticket",
|
56
|
-
},
|
57
|
-
"priority": {
|
58
|
-
"type": "string",
|
59
|
-
"enum": ["low", "medium", "high", "critical"],
|
60
|
-
"description": "Priority level",
|
61
|
-
"default": "medium",
|
62
|
-
},
|
63
|
-
"tags": {
|
64
|
-
"type": "array",
|
65
|
-
"items": {"type": "string"},
|
66
|
-
"description": "Tags to associate with the ticket",
|
67
|
-
},
|
68
|
-
"parent_epic": {
|
69
|
-
"type": "string",
|
70
|
-
"description": "Parent epic ID (for issues)",
|
71
|
-
},
|
72
|
-
"parent_issue": {
|
73
|
-
"type": "string",
|
74
|
-
"description": "Parent issue ID (for tasks)",
|
75
|
-
},
|
76
|
-
},
|
77
|
-
"required": ["type", "title"],
|
78
|
-
},
|
79
|
-
)
|
80
|
-
super().__init__(definition)
|
81
|
-
|
82
|
-
async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
|
83
|
-
"""
|
84
|
-
Create a ticket using aitrackdown CLI.
|
85
|
-
|
86
|
-
Args:
|
87
|
-
invocation: Tool invocation request
|
88
|
-
|
89
|
-
Returns:
|
90
|
-
Tool execution result with created ticket ID
|
91
|
-
"""
|
92
|
-
start_time = datetime.now(timezone.utc)
|
93
|
-
|
94
|
-
try:
|
95
|
-
params = invocation.parameters
|
96
|
-
|
97
|
-
# Build aitrackdown command
|
98
|
-
cmd = ["aitrackdown", "create", params["type"], params["title"]]
|
99
|
-
|
100
|
-
# Add optional parameters
|
101
|
-
if "description" in params:
|
102
|
-
cmd.extend(["--description", params["description"]])
|
103
|
-
|
104
|
-
if "priority" in params:
|
105
|
-
cmd.extend(["--priority", params["priority"]])
|
106
|
-
|
107
|
-
if params.get("tags"):
|
108
|
-
# aitrackdown uses --tag for tags (singular)
|
109
|
-
for tag in params["tags"]:
|
110
|
-
cmd.extend(["--tag", tag])
|
111
|
-
|
112
|
-
# For tasks, use --issue to associate with parent issue
|
113
|
-
if params["type"] == "task" and "parent_issue" in params:
|
114
|
-
cmd.extend(["--issue", params["parent_issue"]])
|
115
|
-
|
116
|
-
# Execute command asynchronously
|
117
|
-
process = await asyncio.create_subprocess_exec(
|
118
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
119
|
-
)
|
120
|
-
|
121
|
-
stdout, stderr = await process.communicate()
|
122
|
-
|
123
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
124
|
-
|
125
|
-
if process.returncode == 0:
|
126
|
-
# Parse ticket ID from output
|
127
|
-
output = stdout.decode().strip()
|
128
|
-
# aitrackdown typically returns "Created ticket: TSK-XXXX" or similar
|
129
|
-
ticket_id = None
|
130
|
-
for line in output.split("\n"):
|
131
|
-
if "TSK-" in line or "ISS-" in line or "EP-" in line:
|
132
|
-
# Extract ticket ID
|
133
|
-
import re
|
134
|
-
|
135
|
-
match = re.search(r"(TSK|ISS|EP)-\d+", line)
|
136
|
-
if match:
|
137
|
-
ticket_id = match.group(0)
|
138
|
-
break
|
139
|
-
|
140
|
-
self._update_metrics(True, execution_time)
|
141
|
-
|
142
|
-
return MCPToolResult(
|
143
|
-
success=True,
|
144
|
-
data={
|
145
|
-
"ticket_id": ticket_id or "Unknown",
|
146
|
-
"type": params["type"],
|
147
|
-
"title": params["title"],
|
148
|
-
"message": output,
|
149
|
-
},
|
150
|
-
execution_time=execution_time,
|
151
|
-
metadata={"tool": "ticket_create", "operation": "create"},
|
152
|
-
)
|
153
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
154
|
-
self._update_metrics(False, execution_time)
|
155
|
-
|
156
|
-
return MCPToolResult(
|
157
|
-
success=False,
|
158
|
-
error=f"Failed to create ticket: {error_msg}",
|
159
|
-
execution_time=execution_time,
|
160
|
-
)
|
161
|
-
|
162
|
-
except Exception as e:
|
163
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
164
|
-
self._update_metrics(False, execution_time)
|
165
|
-
|
166
|
-
return MCPToolResult(
|
167
|
-
success=False,
|
168
|
-
error=f"Ticket creation failed: {e!s}",
|
169
|
-
execution_time=execution_time,
|
170
|
-
)
|
171
|
-
|
172
|
-
|
173
|
-
class TicketListTool(BaseToolAdapter):
|
174
|
-
"""
|
175
|
-
List tickets with optional filters using aitrackdown status command.
|
176
|
-
|
177
|
-
WHY: Users need to review and browse existing tickets. This tool provides
|
178
|
-
a quick way to list recent tickets with filtering capabilities.
|
179
|
-
"""
|
180
|
-
|
181
|
-
def __init__(self):
|
182
|
-
"""Initialize the ticket list tool."""
|
183
|
-
definition = MCPToolDefinition(
|
184
|
-
name="ticket_list",
|
185
|
-
description="List tickets with optional filters using status command",
|
186
|
-
input_schema={
|
187
|
-
"type": "object",
|
188
|
-
"properties": {
|
189
|
-
"limit": {
|
190
|
-
"type": "number",
|
191
|
-
"description": "Maximum number of tickets to return",
|
192
|
-
"default": 10,
|
193
|
-
},
|
194
|
-
"type": {
|
195
|
-
"type": "string",
|
196
|
-
"enum": ["all", "task", "issue", "epic"],
|
197
|
-
"description": "Filter by ticket type",
|
198
|
-
"default": "all",
|
199
|
-
},
|
200
|
-
"status": {
|
201
|
-
"type": "string",
|
202
|
-
"enum": [
|
203
|
-
"all",
|
204
|
-
"open",
|
205
|
-
"in_progress",
|
206
|
-
"done",
|
207
|
-
"closed",
|
208
|
-
"blocked",
|
209
|
-
],
|
210
|
-
"description": "Filter by status",
|
211
|
-
"default": "all",
|
212
|
-
},
|
213
|
-
"priority": {
|
214
|
-
"type": "string",
|
215
|
-
"enum": ["all", "low", "medium", "high", "critical"],
|
216
|
-
"description": "Filter by priority",
|
217
|
-
"default": "all",
|
218
|
-
},
|
219
|
-
},
|
220
|
-
"required": [],
|
221
|
-
},
|
222
|
-
)
|
223
|
-
super().__init__(definition)
|
224
|
-
|
225
|
-
async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
|
226
|
-
"""
|
227
|
-
List tickets using aitrackdown CLI.
|
228
|
-
|
229
|
-
Args:
|
230
|
-
invocation: Tool invocation request
|
231
|
-
|
232
|
-
Returns:
|
233
|
-
Tool execution result with list of tickets
|
234
|
-
"""
|
235
|
-
start_time = datetime.now(timezone.utc)
|
236
|
-
|
237
|
-
try:
|
238
|
-
params = invocation.parameters
|
239
|
-
limit = params.get("limit", 10)
|
240
|
-
|
241
|
-
# Build aitrackdown command - use status tasks for listing
|
242
|
-
cmd = ["aitrackdown", "status", "tasks", "--limit", str(limit)]
|
243
|
-
|
244
|
-
# Add filters
|
245
|
-
if params.get("status") and params["status"] != "all":
|
246
|
-
cmd.extend(["--status", params["status"]])
|
247
|
-
|
248
|
-
# Execute command asynchronously
|
249
|
-
process = await asyncio.create_subprocess_exec(
|
250
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
251
|
-
)
|
252
|
-
|
253
|
-
stdout, stderr = await process.communicate()
|
254
|
-
|
255
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
256
|
-
|
257
|
-
if process.returncode == 0:
|
258
|
-
try:
|
259
|
-
# Try to parse JSON output
|
260
|
-
tickets = json.loads(stdout.decode())
|
261
|
-
except json.JSONDecodeError:
|
262
|
-
# Fallback to text parsing if JSON fails
|
263
|
-
output = stdout.decode().strip()
|
264
|
-
tickets = {"raw_output": output, "count": output.count("\n") + 1}
|
265
|
-
|
266
|
-
self._update_metrics(True, execution_time)
|
267
|
-
|
268
|
-
return MCPToolResult(
|
269
|
-
success=True,
|
270
|
-
data=tickets,
|
271
|
-
execution_time=execution_time,
|
272
|
-
metadata={
|
273
|
-
"tool": "ticket_list",
|
274
|
-
"operation": "list",
|
275
|
-
"count": len(tickets) if isinstance(tickets, list) else 1,
|
276
|
-
},
|
277
|
-
)
|
278
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
279
|
-
self._update_metrics(False, execution_time)
|
280
|
-
|
281
|
-
return MCPToolResult(
|
282
|
-
success=False,
|
283
|
-
error=f"Failed to list tickets: {error_msg}",
|
284
|
-
execution_time=execution_time,
|
285
|
-
)
|
286
|
-
|
287
|
-
except Exception as e:
|
288
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
289
|
-
self._update_metrics(False, execution_time)
|
290
|
-
|
291
|
-
return MCPToolResult(
|
292
|
-
success=False,
|
293
|
-
error=f"Ticket listing failed: {e!s}",
|
294
|
-
execution_time=execution_time,
|
295
|
-
)
|
296
|
-
|
297
|
-
|
298
|
-
class TicketUpdateTool(BaseToolAdapter):
|
299
|
-
"""
|
300
|
-
Update ticket status or priority.
|
301
|
-
|
302
|
-
WHY: Tickets need to be updated as work progresses. This tool provides
|
303
|
-
a simple interface for updating ticket status and priority.
|
304
|
-
"""
|
305
|
-
|
306
|
-
def __init__(self):
|
307
|
-
"""Initialize the ticket update tool."""
|
308
|
-
definition = MCPToolDefinition(
|
309
|
-
name="ticket_update",
|
310
|
-
description="Update a ticket's status or priority",
|
311
|
-
input_schema={
|
312
|
-
"type": "object",
|
313
|
-
"properties": {
|
314
|
-
"ticket_id": {
|
315
|
-
"type": "string",
|
316
|
-
"description": "Ticket ID to update (e.g., TSK-0001)",
|
317
|
-
},
|
318
|
-
"status": {
|
319
|
-
"type": "string",
|
320
|
-
"enum": [
|
321
|
-
"open",
|
322
|
-
"in-progress",
|
323
|
-
"ready",
|
324
|
-
"tested",
|
325
|
-
"done",
|
326
|
-
"waiting",
|
327
|
-
],
|
328
|
-
"description": "New status for the ticket (workflow state)",
|
329
|
-
},
|
330
|
-
"priority": {
|
331
|
-
"type": "string",
|
332
|
-
"enum": ["low", "medium", "high", "critical"],
|
333
|
-
"description": "New priority for the ticket",
|
334
|
-
},
|
335
|
-
"comment": {
|
336
|
-
"type": "string",
|
337
|
-
"description": "Optional comment for the update",
|
338
|
-
},
|
339
|
-
},
|
340
|
-
"required": ["ticket_id"],
|
341
|
-
},
|
342
|
-
)
|
343
|
-
super().__init__(definition)
|
344
|
-
|
345
|
-
async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
|
346
|
-
"""
|
347
|
-
Update a ticket using aitrackdown CLI.
|
348
|
-
|
349
|
-
Args:
|
350
|
-
invocation: Tool invocation request
|
351
|
-
|
352
|
-
Returns:
|
353
|
-
Tool execution result
|
354
|
-
"""
|
355
|
-
start_time = datetime.now(timezone.utc)
|
356
|
-
|
357
|
-
try:
|
358
|
-
params = invocation.parameters
|
359
|
-
ticket_id = params["ticket_id"]
|
360
|
-
|
361
|
-
# Determine which update to perform
|
362
|
-
if "status" in params:
|
363
|
-
# Use transition command for status updates
|
364
|
-
cmd = ["aitrackdown", "transition", ticket_id, params["status"]]
|
365
|
-
|
366
|
-
if "comment" in params:
|
367
|
-
cmd.extend(["--comment", params["comment"]])
|
368
|
-
elif "priority" in params:
|
369
|
-
# For priority updates, we might need to use edit or transition
|
370
|
-
# aitrackdown doesn't have a direct update command, so use transition
|
371
|
-
cmd = ["aitrackdown", "transition", ticket_id, "open"]
|
372
|
-
cmd.extend(["--comment", f"Priority changed to {params['priority']}"])
|
373
|
-
|
374
|
-
if "comment" in params:
|
375
|
-
cmd.extend(["--comment", params["comment"]])
|
376
|
-
else:
|
377
|
-
return MCPToolResult(
|
378
|
-
success=False,
|
379
|
-
error="No update fields provided (status or priority required)",
|
380
|
-
execution_time=0.0,
|
381
|
-
)
|
382
|
-
|
383
|
-
# Execute command asynchronously
|
384
|
-
process = await asyncio.create_subprocess_exec(
|
385
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
386
|
-
)
|
387
|
-
|
388
|
-
stdout, stderr = await process.communicate()
|
389
|
-
|
390
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
391
|
-
|
392
|
-
if process.returncode == 0:
|
393
|
-
self._update_metrics(True, execution_time)
|
394
|
-
|
395
|
-
return MCPToolResult(
|
396
|
-
success=True,
|
397
|
-
data={
|
398
|
-
"ticket_id": ticket_id,
|
399
|
-
"updated_fields": [
|
400
|
-
k for k in ["status", "priority"] if k in params
|
401
|
-
],
|
402
|
-
"message": stdout.decode().strip(),
|
403
|
-
},
|
404
|
-
execution_time=execution_time,
|
405
|
-
metadata={"tool": "ticket_update", "operation": "update"},
|
406
|
-
)
|
407
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
408
|
-
self._update_metrics(False, execution_time)
|
409
|
-
|
410
|
-
return MCPToolResult(
|
411
|
-
success=False,
|
412
|
-
error=f"Failed to update ticket: {error_msg}",
|
413
|
-
execution_time=execution_time,
|
414
|
-
)
|
415
|
-
|
416
|
-
except Exception as e:
|
417
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
418
|
-
self._update_metrics(False, execution_time)
|
419
|
-
|
420
|
-
return MCPToolResult(
|
421
|
-
success=False,
|
422
|
-
error=f"Ticket update failed: {e!s}",
|
423
|
-
execution_time=execution_time,
|
424
|
-
)
|
425
|
-
|
426
|
-
|
427
|
-
class TicketViewTool(BaseToolAdapter):
|
428
|
-
"""
|
429
|
-
View detailed ticket information.
|
430
|
-
|
431
|
-
WHY: Users need to see full ticket details including description, metadata,
|
432
|
-
and all associated information for understanding context and status.
|
433
|
-
"""
|
434
|
-
|
435
|
-
def __init__(self):
|
436
|
-
"""Initialize the ticket view tool."""
|
437
|
-
definition = MCPToolDefinition(
|
438
|
-
name="ticket_view",
|
439
|
-
description="View detailed information about a specific ticket",
|
440
|
-
input_schema={
|
441
|
-
"type": "object",
|
442
|
-
"properties": {
|
443
|
-
"ticket_id": {
|
444
|
-
"type": "string",
|
445
|
-
"description": "Ticket ID to view (e.g., TSK-0001)",
|
446
|
-
},
|
447
|
-
"format": {
|
448
|
-
"type": "string",
|
449
|
-
"enum": ["json", "text"],
|
450
|
-
"description": "Output format",
|
451
|
-
"default": "json",
|
452
|
-
},
|
453
|
-
},
|
454
|
-
"required": ["ticket_id"],
|
455
|
-
},
|
456
|
-
)
|
457
|
-
super().__init__(definition)
|
458
|
-
|
459
|
-
async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
|
460
|
-
"""
|
461
|
-
View a ticket using aitrackdown CLI.
|
462
|
-
|
463
|
-
Args:
|
464
|
-
invocation: Tool invocation request
|
465
|
-
|
466
|
-
Returns:
|
467
|
-
Tool execution result with ticket details
|
468
|
-
"""
|
469
|
-
start_time = datetime.now(timezone.utc)
|
470
|
-
|
471
|
-
try:
|
472
|
-
params = invocation.parameters
|
473
|
-
ticket_id = params["ticket_id"]
|
474
|
-
format_type = params.get("format", "json")
|
475
|
-
|
476
|
-
# Build aitrackdown command - use show for viewing
|
477
|
-
cmd = ["aitrackdown", "show", ticket_id]
|
478
|
-
|
479
|
-
# Execute command asynchronously
|
480
|
-
process = await asyncio.create_subprocess_exec(
|
481
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
482
|
-
)
|
483
|
-
|
484
|
-
stdout, stderr = await process.communicate()
|
485
|
-
|
486
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
487
|
-
|
488
|
-
if process.returncode == 0:
|
489
|
-
output = stdout.decode().strip()
|
490
|
-
|
491
|
-
if format_type == "json":
|
492
|
-
try:
|
493
|
-
ticket_data = json.loads(output)
|
494
|
-
except json.JSONDecodeError:
|
495
|
-
ticket_data = {"raw_output": output}
|
496
|
-
else:
|
497
|
-
ticket_data = {"raw_output": output}
|
498
|
-
|
499
|
-
self._update_metrics(True, execution_time)
|
500
|
-
|
501
|
-
return MCPToolResult(
|
502
|
-
success=True,
|
503
|
-
data=ticket_data,
|
504
|
-
execution_time=execution_time,
|
505
|
-
metadata={
|
506
|
-
"tool": "ticket_view",
|
507
|
-
"operation": "view",
|
508
|
-
"ticket_id": ticket_id,
|
509
|
-
},
|
510
|
-
)
|
511
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
512
|
-
self._update_metrics(False, execution_time)
|
513
|
-
|
514
|
-
return MCPToolResult(
|
515
|
-
success=False,
|
516
|
-
error=f"Failed to view ticket: {error_msg}",
|
517
|
-
execution_time=execution_time,
|
518
|
-
)
|
519
|
-
|
520
|
-
except Exception as e:
|
521
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
522
|
-
self._update_metrics(False, execution_time)
|
523
|
-
|
524
|
-
return MCPToolResult(
|
525
|
-
success=False,
|
526
|
-
error=f"Ticket view failed: {e!s}",
|
527
|
-
execution_time=execution_time,
|
528
|
-
)
|
529
|
-
|
530
|
-
|
531
|
-
class TicketSearchTool(BaseToolAdapter):
|
532
|
-
"""
|
533
|
-
Search tickets by keywords.
|
534
|
-
|
535
|
-
WHY: Users need to find specific tickets based on content, tags, or other criteria.
|
536
|
-
This tool provides keyword search across ticket titles and descriptions.
|
537
|
-
"""
|
538
|
-
|
539
|
-
def __init__(self):
|
540
|
-
"""Initialize the ticket search tool."""
|
541
|
-
definition = MCPToolDefinition(
|
542
|
-
name="ticket_search",
|
543
|
-
description="Search tickets by keywords in title or description",
|
544
|
-
input_schema={
|
545
|
-
"type": "object",
|
546
|
-
"properties": {
|
547
|
-
"query": {"type": "string", "description": "Search query keywords"},
|
548
|
-
"limit": {
|
549
|
-
"type": "number",
|
550
|
-
"description": "Maximum number of results",
|
551
|
-
"default": 10,
|
552
|
-
},
|
553
|
-
"type": {
|
554
|
-
"type": "string",
|
555
|
-
"enum": ["all", "task", "issue", "epic"],
|
556
|
-
"description": "Filter by ticket type",
|
557
|
-
"default": "all",
|
558
|
-
},
|
559
|
-
},
|
560
|
-
"required": ["query"],
|
561
|
-
},
|
562
|
-
)
|
563
|
-
super().__init__(definition)
|
564
|
-
|
565
|
-
async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
|
566
|
-
"""
|
567
|
-
Search tickets using aitrackdown CLI.
|
568
|
-
|
569
|
-
Args:
|
570
|
-
invocation: Tool invocation request
|
571
|
-
|
572
|
-
Returns:
|
573
|
-
Tool execution result with matching tickets
|
574
|
-
"""
|
575
|
-
start_time = datetime.now(timezone.utc)
|
576
|
-
|
577
|
-
try:
|
578
|
-
params = invocation.parameters
|
579
|
-
query = params["query"]
|
580
|
-
limit = params.get("limit", 10)
|
581
|
-
|
582
|
-
# Build aitrackdown command - use search tasks
|
583
|
-
cmd = ["aitrackdown", "search", "tasks", query, "--limit", str(limit)]
|
584
|
-
|
585
|
-
if params.get("type") and params["type"] != "all":
|
586
|
-
cmd.extend(["--type", params["type"]])
|
587
|
-
|
588
|
-
# Execute command asynchronously
|
589
|
-
process = await asyncio.create_subprocess_exec(
|
590
|
-
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
591
|
-
)
|
592
|
-
|
593
|
-
stdout, stderr = await process.communicate()
|
594
|
-
|
595
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
596
|
-
|
597
|
-
if process.returncode == 0:
|
598
|
-
try:
|
599
|
-
# Try to parse JSON output
|
600
|
-
results = json.loads(stdout.decode())
|
601
|
-
except json.JSONDecodeError:
|
602
|
-
# Fallback to text parsing if JSON fails
|
603
|
-
output = stdout.decode().strip()
|
604
|
-
results = {"raw_output": output, "query": query}
|
605
|
-
|
606
|
-
self._update_metrics(True, execution_time)
|
607
|
-
|
608
|
-
return MCPToolResult(
|
609
|
-
success=True,
|
610
|
-
data=results,
|
611
|
-
execution_time=execution_time,
|
612
|
-
metadata={
|
613
|
-
"tool": "ticket_search",
|
614
|
-
"operation": "search",
|
615
|
-
"query": query,
|
616
|
-
},
|
617
|
-
)
|
618
|
-
error_msg = stderr.decode() if stderr else stdout.decode()
|
619
|
-
self._update_metrics(False, execution_time)
|
620
|
-
|
621
|
-
return MCPToolResult(
|
622
|
-
success=False,
|
623
|
-
error=f"Failed to search tickets: {error_msg}",
|
624
|
-
execution_time=execution_time,
|
625
|
-
)
|
626
|
-
|
627
|
-
except Exception as e:
|
628
|
-
execution_time = (datetime.now(timezone.utc) - start_time).total_seconds()
|
629
|
-
self._update_metrics(False, execution_time)
|
630
|
-
|
631
|
-
return MCPToolResult(
|
632
|
-
success=False,
|
633
|
-
error=f"Ticket search failed: {e!s}",
|
634
|
-
execution_time=execution_time,
|
635
|
-
)
|
636
|
-
|
637
|
-
|
638
|
-
# Export all ticket tools
|
639
|
-
__all__ = [
|
640
|
-
"TicketCreateTool",
|
641
|
-
"TicketListTool",
|
642
|
-
"TicketSearchTool",
|
643
|
-
"TicketUpdateTool",
|
644
|
-
"TicketViewTool",
|
645
|
-
]
|