mcp-ticketer 0.4.2__py3-none-any.whl → 0.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.
Potentially problematic release.
This version of mcp-ticketer might be problematic. Click here for more details.
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/adapters/aitrackdown.py +254 -11
- mcp_ticketer/adapters/github.py +13 -13
- mcp_ticketer/adapters/hybrid.py +11 -11
- mcp_ticketer/adapters/jira.py +20 -24
- mcp_ticketer/cache/memory.py +6 -5
- mcp_ticketer/cli/codex_configure.py +2 -2
- mcp_ticketer/cli/configure.py +4 -5
- mcp_ticketer/cli/diagnostics.py +2 -2
- mcp_ticketer/cli/discover.py +4 -5
- mcp_ticketer/cli/gemini_configure.py +2 -2
- mcp_ticketer/cli/linear_commands.py +6 -7
- mcp_ticketer/cli/main.py +341 -250
- mcp_ticketer/cli/mcp_configure.py +1 -2
- mcp_ticketer/cli/ticket_commands.py +27 -30
- mcp_ticketer/cli/utils.py +23 -22
- mcp_ticketer/core/__init__.py +2 -1
- mcp_ticketer/core/adapter.py +82 -13
- mcp_ticketer/core/config.py +27 -29
- mcp_ticketer/core/env_discovery.py +10 -10
- mcp_ticketer/core/env_loader.py +8 -8
- mcp_ticketer/core/http_client.py +16 -16
- mcp_ticketer/core/mappers.py +10 -10
- mcp_ticketer/core/models.py +50 -20
- mcp_ticketer/core/project_config.py +40 -34
- mcp_ticketer/core/registry.py +2 -2
- mcp_ticketer/mcp/dto.py +32 -32
- mcp_ticketer/mcp/response_builder.py +2 -2
- mcp_ticketer/mcp/server.py +3 -3
- mcp_ticketer/mcp/server_sdk.py +2 -2
- mcp_ticketer/mcp/tools/attachment_tools.py +3 -4
- mcp_ticketer/mcp/tools/comment_tools.py +2 -2
- mcp_ticketer/mcp/tools/hierarchy_tools.py +8 -8
- mcp_ticketer/mcp/tools/pr_tools.py +2 -2
- mcp_ticketer/mcp/tools/search_tools.py +6 -6
- mcp_ticketer/mcp/tools/ticket_tools.py +12 -12
- mcp_ticketer/queue/health_monitor.py +4 -4
- mcp_ticketer/queue/manager.py +2 -2
- mcp_ticketer/queue/queue.py +16 -16
- mcp_ticketer/queue/ticket_registry.py +7 -7
- mcp_ticketer/queue/worker.py +2 -2
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.4.dist-info}/METADATA +61 -2
- mcp_ticketer-0.4.4.dist-info/RECORD +73 -0
- mcp_ticketer-0.4.2.dist-info/RECORD +0 -73
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.4.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.4.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.4.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.4.2.dist-info → mcp_ticketer-0.4.4.dist-info}/top_level.txt +0 -0
mcp_ticketer/mcp/dto.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Data Transfer Objects for MCP requests and responses."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, Field
|
|
6
6
|
|
|
@@ -10,32 +10,32 @@ class CreateTicketRequest(BaseModel):
|
|
|
10
10
|
"""Request to create a ticket."""
|
|
11
11
|
|
|
12
12
|
title: str = Field(..., min_length=1, description="Ticket title")
|
|
13
|
-
description:
|
|
13
|
+
description: str | None = Field(None, description="Ticket description")
|
|
14
14
|
priority: str = Field("medium", description="Ticket priority")
|
|
15
15
|
tags: list[str] = Field(default_factory=list, description="Ticket tags")
|
|
16
|
-
assignee:
|
|
16
|
+
assignee: str | None = Field(None, description="Ticket assignee")
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class CreateEpicRequest(BaseModel):
|
|
20
20
|
"""Request to create an epic."""
|
|
21
21
|
|
|
22
22
|
title: str = Field(..., min_length=1, description="Epic title")
|
|
23
|
-
description:
|
|
23
|
+
description: str | None = Field(None, description="Epic description")
|
|
24
24
|
child_issues: list[str] = Field(default_factory=list, description="Child issue IDs")
|
|
25
|
-
target_date:
|
|
26
|
-
lead_id:
|
|
25
|
+
target_date: str | None = Field(None, description="Target completion date")
|
|
26
|
+
lead_id: str | None = Field(None, description="Epic lead/owner ID")
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class CreateIssueRequest(BaseModel):
|
|
30
30
|
"""Request to create an issue."""
|
|
31
31
|
|
|
32
32
|
title: str = Field(..., min_length=1, description="Issue title")
|
|
33
|
-
description:
|
|
34
|
-
epic_id:
|
|
33
|
+
description: str | None = Field(None, description="Issue description")
|
|
34
|
+
epic_id: str | None = Field(None, description="Parent epic ID")
|
|
35
35
|
priority: str = Field("medium", description="Issue priority")
|
|
36
|
-
assignee:
|
|
36
|
+
assignee: str | None = Field(None, description="Issue assignee")
|
|
37
37
|
tags: list[str] = Field(default_factory=list, description="Issue tags")
|
|
38
|
-
estimated_hours:
|
|
38
|
+
estimated_hours: float | None = Field(
|
|
39
39
|
None, description="Estimated hours to complete"
|
|
40
40
|
)
|
|
41
41
|
|
|
@@ -45,11 +45,11 @@ class CreateTaskRequest(BaseModel):
|
|
|
45
45
|
|
|
46
46
|
title: str = Field(..., min_length=1, description="Task title")
|
|
47
47
|
parent_id: str = Field(..., description="Parent issue ID")
|
|
48
|
-
description:
|
|
48
|
+
description: str | None = Field(None, description="Task description")
|
|
49
49
|
priority: str = Field("medium", description="Task priority")
|
|
50
|
-
assignee:
|
|
50
|
+
assignee: str | None = Field(None, description="Task assignee")
|
|
51
51
|
tags: list[str] = Field(default_factory=list, description="Task tags")
|
|
52
|
-
estimated_hours:
|
|
52
|
+
estimated_hours: float | None = Field(
|
|
53
53
|
None, description="Estimated hours to complete"
|
|
54
54
|
)
|
|
55
55
|
|
|
@@ -77,11 +77,11 @@ class TransitionRequest(BaseModel):
|
|
|
77
77
|
class SearchRequest(BaseModel):
|
|
78
78
|
"""Request to search tickets."""
|
|
79
79
|
|
|
80
|
-
query:
|
|
81
|
-
state:
|
|
82
|
-
priority:
|
|
83
|
-
assignee:
|
|
84
|
-
tags:
|
|
80
|
+
query: str | None = Field(None, description="Search query text")
|
|
81
|
+
state: str | None = Field(None, description="Filter by ticket state")
|
|
82
|
+
priority: str | None = Field(None, description="Filter by priority")
|
|
83
|
+
assignee: str | None = Field(None, description="Filter by assignee")
|
|
84
|
+
tags: list[str] | None = Field(None, description="Filter by tags")
|
|
85
85
|
limit: int = Field(10, description="Maximum number of results")
|
|
86
86
|
|
|
87
87
|
|
|
@@ -90,7 +90,7 @@ class ListRequest(BaseModel):
|
|
|
90
90
|
|
|
91
91
|
limit: int = Field(10, description="Maximum number of tickets to return")
|
|
92
92
|
offset: int = Field(0, description="Number of tickets to skip")
|
|
93
|
-
filters:
|
|
93
|
+
filters: dict[str, Any] | None = Field(None, description="Additional filters")
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
class DeleteTicketRequest(BaseModel):
|
|
@@ -104,8 +104,8 @@ class CommentRequest(BaseModel):
|
|
|
104
104
|
|
|
105
105
|
operation: str = Field("add", description="Operation: 'add' or 'list'")
|
|
106
106
|
ticket_id: str = Field(..., description="Ticket ID")
|
|
107
|
-
content:
|
|
108
|
-
author:
|
|
107
|
+
content: str | None = Field(None, description="Comment content (for add)")
|
|
108
|
+
author: str | None = Field(None, description="Comment author (for add)")
|
|
109
109
|
limit: int = Field(10, description="Max comments to return (for list)")
|
|
110
110
|
offset: int = Field(0, description="Number of comments to skip (for list)")
|
|
111
111
|
|
|
@@ -115,12 +115,12 @@ class CreatePRRequest(BaseModel):
|
|
|
115
115
|
|
|
116
116
|
ticket_id: str = Field(..., description="Ticket ID")
|
|
117
117
|
base_branch: str = Field("main", description="Base branch")
|
|
118
|
-
head_branch:
|
|
119
|
-
title:
|
|
120
|
-
body:
|
|
118
|
+
head_branch: str | None = Field(None, description="Head branch")
|
|
119
|
+
title: str | None = Field(None, description="PR title")
|
|
120
|
+
body: str | None = Field(None, description="PR body")
|
|
121
121
|
draft: bool = Field(False, description="Create as draft PR")
|
|
122
|
-
github_owner:
|
|
123
|
-
github_repo:
|
|
122
|
+
github_owner: str | None = Field(None, description="GitHub owner (for Linear)")
|
|
123
|
+
github_repo: str | None = Field(None, description="GitHub repo (for Linear)")
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
class LinkPRRequest(BaseModel):
|
|
@@ -152,7 +152,7 @@ class IssueTasksRequest(BaseModel):
|
|
|
152
152
|
class HierarchyTreeRequest(BaseModel):
|
|
153
153
|
"""Request to get hierarchy tree."""
|
|
154
154
|
|
|
155
|
-
epic_id:
|
|
155
|
+
epic_id: str | None = Field(None, description="Specific epic ID (optional)")
|
|
156
156
|
max_depth: int = Field(3, description="Maximum depth of tree")
|
|
157
157
|
limit: int = Field(10, description="Max epics to return (if no epic_id)")
|
|
158
158
|
|
|
@@ -173,8 +173,8 @@ class SearchHierarchyRequest(BaseModel):
|
|
|
173
173
|
"""Request to search with hierarchy context."""
|
|
174
174
|
|
|
175
175
|
query: str = Field("", description="Search query")
|
|
176
|
-
state:
|
|
177
|
-
priority:
|
|
176
|
+
state: str | None = Field(None, description="Filter by state")
|
|
177
|
+
priority: str | None = Field(None, description="Filter by priority")
|
|
178
178
|
include_children: bool = Field(True, description="Include child tickets")
|
|
179
179
|
include_parents: bool = Field(True, description="Include parent tickets")
|
|
180
180
|
limit: int = Field(50, description="Maximum number of results")
|
|
@@ -184,9 +184,9 @@ class AttachRequest(BaseModel):
|
|
|
184
184
|
"""Request to attach file to ticket."""
|
|
185
185
|
|
|
186
186
|
ticket_id: str = Field(..., description="Ticket ID")
|
|
187
|
-
file_path:
|
|
188
|
-
file_content:
|
|
189
|
-
file_name:
|
|
187
|
+
file_path: str | None = Field(None, description="File path to attach")
|
|
188
|
+
file_content: str | None = Field(None, description="File content (base64)")
|
|
189
|
+
file_name: str | None = Field(None, description="File name")
|
|
190
190
|
|
|
191
191
|
|
|
192
192
|
class ListAttachmentsRequest(BaseModel):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Response builder utility for consistent MCP responses."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from .constants import JSONRPC_VERSION, STATUS_COMPLETED
|
|
6
6
|
|
|
@@ -36,7 +36,7 @@ class ResponseBuilder:
|
|
|
36
36
|
request_id: Any,
|
|
37
37
|
code: int,
|
|
38
38
|
message: str,
|
|
39
|
-
data:
|
|
39
|
+
data: dict[str, Any] | None = None,
|
|
40
40
|
) -> dict[str, Any]:
|
|
41
41
|
"""Build error response.
|
|
42
42
|
|
mcp_ticketer/mcp/server.py
CHANGED
|
@@ -4,7 +4,7 @@ import asyncio
|
|
|
4
4
|
import json
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
from dotenv import load_dotenv
|
|
10
10
|
|
|
@@ -70,7 +70,7 @@ class MCPTicketServer:
|
|
|
70
70
|
"""MCP server for ticket operations over stdio - synchronous implementation."""
|
|
71
71
|
|
|
72
72
|
def __init__(
|
|
73
|
-
self, adapter_type: str = "aitrackdown", config:
|
|
73
|
+
self, adapter_type: str = "aitrackdown", config: dict[str, Any] | None = None
|
|
74
74
|
):
|
|
75
75
|
"""Initialize MCP server.
|
|
76
76
|
|
|
@@ -1129,7 +1129,7 @@ async def main():
|
|
|
1129
1129
|
await server.run()
|
|
1130
1130
|
|
|
1131
1131
|
|
|
1132
|
-
def _load_env_configuration() ->
|
|
1132
|
+
def _load_env_configuration() -> dict[str, Any] | None:
|
|
1133
1133
|
"""Load adapter configuration from .env files.
|
|
1134
1134
|
|
|
1135
1135
|
Checks .env.local first (highest priority), then .env.
|
mcp_ticketer/mcp/server_sdk.py
CHANGED
|
@@ -10,7 +10,7 @@ startup and used by all tool implementations.
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import logging
|
|
13
|
-
from typing import Any
|
|
13
|
+
from typing import Any
|
|
14
14
|
|
|
15
15
|
from mcp.server.fastmcp import FastMCP
|
|
16
16
|
|
|
@@ -21,7 +21,7 @@ from ..core.registry import AdapterRegistry
|
|
|
21
21
|
mcp = FastMCP("mcp-ticketer")
|
|
22
22
|
|
|
23
23
|
# Global adapter instance
|
|
24
|
-
_adapter:
|
|
24
|
+
_adapter: BaseAdapter | None = None
|
|
25
25
|
|
|
26
26
|
# Configure logging
|
|
27
27
|
logger = logging.getLogger(__name__)
|
|
@@ -7,6 +7,7 @@ available in all adapters.
|
|
|
7
7
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
+
from ...core.models import Comment
|
|
10
11
|
from ..server_sdk import get_adapter, mcp
|
|
11
12
|
|
|
12
13
|
|
|
@@ -15,7 +16,7 @@ async def ticket_attach(
|
|
|
15
16
|
ticket_id: str,
|
|
16
17
|
file_path: str,
|
|
17
18
|
description: str = "",
|
|
18
|
-
) -> dict[str, Any]:
|
|
19
|
+
) -> dict[str, Any]: # Keep as dict for MCP compatibility
|
|
19
20
|
"""Attach a file to a ticket.
|
|
20
21
|
|
|
21
22
|
Uploads a file and associates it with the specified ticket. This
|
|
@@ -63,8 +64,6 @@ async def ticket_attach(
|
|
|
63
64
|
|
|
64
65
|
except AttributeError:
|
|
65
66
|
# Fallback: Add file reference as comment
|
|
66
|
-
from ...core.models import Comment
|
|
67
|
-
|
|
68
67
|
comment_text = f"Attachment: {file_path}"
|
|
69
68
|
if description:
|
|
70
69
|
comment_text += f"\nDescription: {description}"
|
|
@@ -102,7 +101,7 @@ async def ticket_attach(
|
|
|
102
101
|
@mcp.tool()
|
|
103
102
|
async def ticket_attachments(
|
|
104
103
|
ticket_id: str,
|
|
105
|
-
) -> dict[str, Any]:
|
|
104
|
+
) -> dict[str, Any]: # Keep as dict for MCP compatibility
|
|
106
105
|
"""Get all attachments for a ticket.
|
|
107
106
|
|
|
108
107
|
Retrieves a list of all files attached to the specified ticket.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
This module implements tools for adding and retrieving comments on tickets.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
from ...core.models import Comment
|
|
9
9
|
from ..server_sdk import get_adapter, mcp
|
|
@@ -13,7 +13,7 @@ from ..server_sdk import get_adapter, mcp
|
|
|
13
13
|
async def ticket_comment(
|
|
14
14
|
ticket_id: str,
|
|
15
15
|
operation: str,
|
|
16
|
-
text:
|
|
16
|
+
text: str | None = None,
|
|
17
17
|
limit: int = 10,
|
|
18
18
|
offset: int = 0,
|
|
19
19
|
) -> dict[str, Any]:
|
|
@@ -7,7 +7,7 @@ This module implements tools for managing the three-level ticket hierarchy:
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from datetime import datetime
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
from ...core.models import Epic, Priority, Task, TicketType
|
|
13
13
|
from ..server_sdk import get_adapter, mcp
|
|
@@ -17,9 +17,9 @@ from ..server_sdk import get_adapter, mcp
|
|
|
17
17
|
async def epic_create(
|
|
18
18
|
title: str,
|
|
19
19
|
description: str = "",
|
|
20
|
-
target_date:
|
|
21
|
-
lead_id:
|
|
22
|
-
child_issues:
|
|
20
|
+
target_date: str | None = None,
|
|
21
|
+
lead_id: str | None = None,
|
|
22
|
+
child_issues: list[str] | None = None,
|
|
23
23
|
) -> dict[str, Any]:
|
|
24
24
|
"""Create a new epic (strategic level container).
|
|
25
25
|
|
|
@@ -156,8 +156,8 @@ async def epic_issues(epic_id: str) -> dict[str, Any]:
|
|
|
156
156
|
async def issue_create(
|
|
157
157
|
title: str,
|
|
158
158
|
description: str = "",
|
|
159
|
-
epic_id:
|
|
160
|
-
assignee:
|
|
159
|
+
epic_id: str | None = None,
|
|
160
|
+
assignee: str | None = None,
|
|
161
161
|
priority: str = "medium",
|
|
162
162
|
) -> dict[str, Any]:
|
|
163
163
|
"""Create a new issue (standard work item).
|
|
@@ -258,8 +258,8 @@ async def issue_tasks(issue_id: str) -> dict[str, Any]:
|
|
|
258
258
|
async def task_create(
|
|
259
259
|
title: str,
|
|
260
260
|
description: str = "",
|
|
261
|
-
issue_id:
|
|
262
|
-
assignee:
|
|
261
|
+
issue_id: str | None = None,
|
|
262
|
+
assignee: str | None = None,
|
|
263
263
|
priority: str = "medium",
|
|
264
264
|
) -> dict[str, Any]:
|
|
265
265
|
"""Create a new task (sub-work item).
|
|
@@ -5,7 +5,7 @@ creating PRs from tickets. Note that PR functionality may not be available
|
|
|
5
5
|
in all adapters.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
from ..server_sdk import get_adapter, mcp
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ async def ticket_create_pr(
|
|
|
15
15
|
ticket_id: str,
|
|
16
16
|
title: str,
|
|
17
17
|
description: str = "",
|
|
18
|
-
source_branch:
|
|
18
|
+
source_branch: str | None = None,
|
|
19
19
|
target_branch: str = "main",
|
|
20
20
|
) -> dict[str, Any]:
|
|
21
21
|
"""Create a pull request linked to a ticket.
|
|
@@ -4,7 +4,7 @@ This module implements advanced search capabilities for tickets using
|
|
|
4
4
|
various filters and criteria.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
from ...core.models import Priority, SearchQuery, TicketState
|
|
10
10
|
from ..server_sdk import get_adapter, mcp
|
|
@@ -12,11 +12,11 @@ from ..server_sdk import get_adapter, mcp
|
|
|
12
12
|
|
|
13
13
|
@mcp.tool()
|
|
14
14
|
async def ticket_search(
|
|
15
|
-
query:
|
|
16
|
-
state:
|
|
17
|
-
priority:
|
|
18
|
-
tags:
|
|
19
|
-
assignee:
|
|
15
|
+
query: str | None = None,
|
|
16
|
+
state: str | None = None,
|
|
17
|
+
priority: str | None = None,
|
|
18
|
+
tags: list[str] | None = None,
|
|
19
|
+
assignee: str | None = None,
|
|
20
20
|
limit: int = 10,
|
|
21
21
|
) -> dict[str, Any]:
|
|
22
22
|
"""Search tickets using advanced filters.
|
|
@@ -4,7 +4,7 @@ This module implements the core create, read, update, delete, and list
|
|
|
4
4
|
operations for tickets using the FastMCP SDK.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
from ...core.models import Priority, Task, TicketState
|
|
10
10
|
from ..server_sdk import get_adapter, mcp
|
|
@@ -15,8 +15,8 @@ async def ticket_create(
|
|
|
15
15
|
title: str,
|
|
16
16
|
description: str = "",
|
|
17
17
|
priority: str = "medium",
|
|
18
|
-
tags:
|
|
19
|
-
assignee:
|
|
18
|
+
tags: list[str] | None = None,
|
|
19
|
+
assignee: str | None = None,
|
|
20
20
|
) -> dict[str, Any]:
|
|
21
21
|
"""Create a new ticket with specified details.
|
|
22
22
|
|
|
@@ -101,12 +101,12 @@ async def ticket_read(ticket_id: str) -> dict[str, Any]:
|
|
|
101
101
|
@mcp.tool()
|
|
102
102
|
async def ticket_update(
|
|
103
103
|
ticket_id: str,
|
|
104
|
-
title:
|
|
105
|
-
description:
|
|
106
|
-
priority:
|
|
107
|
-
state:
|
|
108
|
-
assignee:
|
|
109
|
-
tags:
|
|
104
|
+
title: str | None = None,
|
|
105
|
+
description: str | None = None,
|
|
106
|
+
priority: str | None = None,
|
|
107
|
+
state: str | None = None,
|
|
108
|
+
assignee: str | None = None,
|
|
109
|
+
tags: list[str] | None = None,
|
|
110
110
|
) -> dict[str, Any]:
|
|
111
111
|
"""Update an existing ticket.
|
|
112
112
|
|
|
@@ -214,9 +214,9 @@ async def ticket_delete(ticket_id: str) -> dict[str, Any]:
|
|
|
214
214
|
async def ticket_list(
|
|
215
215
|
limit: int = 10,
|
|
216
216
|
offset: int = 0,
|
|
217
|
-
state:
|
|
218
|
-
priority:
|
|
219
|
-
assignee:
|
|
217
|
+
state: str | None = None,
|
|
218
|
+
priority: str | None = None,
|
|
219
|
+
assignee: str | None = None,
|
|
220
220
|
) -> dict[str, Any]:
|
|
221
221
|
"""List tickets with pagination and optional filters.
|
|
222
222
|
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
import time
|
|
5
5
|
from datetime import datetime, timedelta
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
import psutil
|
|
10
10
|
|
|
@@ -30,8 +30,8 @@ class HealthAlert:
|
|
|
30
30
|
self,
|
|
31
31
|
level: HealthStatus,
|
|
32
32
|
message: str,
|
|
33
|
-
details:
|
|
34
|
-
timestamp:
|
|
33
|
+
details: dict[str, Any] | None = None,
|
|
34
|
+
timestamp: datetime | None = None,
|
|
35
35
|
):
|
|
36
36
|
self.level = level
|
|
37
37
|
self.message = message
|
|
@@ -52,7 +52,7 @@ class QueueHealthMonitor:
|
|
|
52
52
|
QUEUE_BACKLOG_WARNING = 10 # Warn if more than 10 pending items
|
|
53
53
|
QUEUE_BACKLOG_CRITICAL = 50 # Critical if more than 50 pending items
|
|
54
54
|
|
|
55
|
-
def __init__(self, queue:
|
|
55
|
+
def __init__(self, queue: Queue | None = None):
|
|
56
56
|
"""Initialize health monitor.
|
|
57
57
|
|
|
58
58
|
Args:
|
mcp_ticketer/queue/manager.py
CHANGED
|
@@ -7,7 +7,7 @@ import subprocess
|
|
|
7
7
|
import sys
|
|
8
8
|
import time
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
import psutil
|
|
13
13
|
|
|
@@ -304,7 +304,7 @@ class WorkerManager:
|
|
|
304
304
|
|
|
305
305
|
return status
|
|
306
306
|
|
|
307
|
-
def _get_pid(self) ->
|
|
307
|
+
def _get_pid(self) -> int | None:
|
|
308
308
|
"""Get worker PID from file.
|
|
309
309
|
|
|
310
310
|
Returns:
|
mcp_ticketer/queue/queue.py
CHANGED
|
@@ -8,7 +8,7 @@ from dataclasses import asdict, dataclass
|
|
|
8
8
|
from datetime import datetime, timedelta
|
|
9
9
|
from enum import Enum
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any
|
|
11
|
+
from typing import Any
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class QueueStatus(str, Enum):
|
|
@@ -30,12 +30,12 @@ class QueueItem:
|
|
|
30
30
|
operation: str
|
|
31
31
|
status: QueueStatus
|
|
32
32
|
created_at: datetime
|
|
33
|
-
processed_at:
|
|
34
|
-
error_message:
|
|
33
|
+
processed_at: datetime | None = None
|
|
34
|
+
error_message: str | None = None
|
|
35
35
|
retry_count: int = 0
|
|
36
|
-
result:
|
|
37
|
-
project_dir:
|
|
38
|
-
adapter_config:
|
|
36
|
+
result: dict[str, Any] | None = None
|
|
37
|
+
project_dir: str | None = None
|
|
38
|
+
adapter_config: dict[str, Any] | None = None # Adapter configuration
|
|
39
39
|
|
|
40
40
|
def to_dict(self) -> dict:
|
|
41
41
|
"""Convert to dictionary for storage."""
|
|
@@ -67,7 +67,7 @@ class QueueItem:
|
|
|
67
67
|
class Queue:
|
|
68
68
|
"""Thread-safe SQLite queue for ticket operations."""
|
|
69
69
|
|
|
70
|
-
def __init__(self, db_path:
|
|
70
|
+
def __init__(self, db_path: Path | None = None):
|
|
71
71
|
"""Initialize queue with database connection.
|
|
72
72
|
|
|
73
73
|
Args:
|
|
@@ -139,8 +139,8 @@ class Queue:
|
|
|
139
139
|
ticket_data: dict[str, Any],
|
|
140
140
|
adapter: str,
|
|
141
141
|
operation: str,
|
|
142
|
-
project_dir:
|
|
143
|
-
adapter_config:
|
|
142
|
+
project_dir: str | None = None,
|
|
143
|
+
adapter_config: dict[str, Any] | None = None,
|
|
144
144
|
) -> str:
|
|
145
145
|
"""Add item to queue.
|
|
146
146
|
|
|
@@ -186,7 +186,7 @@ class Queue:
|
|
|
186
186
|
|
|
187
187
|
return queue_id
|
|
188
188
|
|
|
189
|
-
def get_next_pending(self) ->
|
|
189
|
+
def get_next_pending(self) -> QueueItem | None:
|
|
190
190
|
"""Get next pending item from queue atomically.
|
|
191
191
|
|
|
192
192
|
Returns:
|
|
@@ -251,9 +251,9 @@ class Queue:
|
|
|
251
251
|
self,
|
|
252
252
|
queue_id: str,
|
|
253
253
|
status: QueueStatus,
|
|
254
|
-
error_message:
|
|
255
|
-
result:
|
|
256
|
-
expected_status:
|
|
254
|
+
error_message: str | None = None,
|
|
255
|
+
result: dict[str, Any] | None = None,
|
|
256
|
+
expected_status: QueueStatus | None = None,
|
|
257
257
|
) -> bool:
|
|
258
258
|
"""Update queue item status atomically.
|
|
259
259
|
|
|
@@ -328,7 +328,7 @@ class Queue:
|
|
|
328
328
|
raise
|
|
329
329
|
|
|
330
330
|
def increment_retry(
|
|
331
|
-
self, queue_id: str, expected_status:
|
|
331
|
+
self, queue_id: str, expected_status: QueueStatus | None = None
|
|
332
332
|
) -> int:
|
|
333
333
|
"""Increment retry count and reset to pending atomically.
|
|
334
334
|
|
|
@@ -387,7 +387,7 @@ class Queue:
|
|
|
387
387
|
conn.rollback()
|
|
388
388
|
raise
|
|
389
389
|
|
|
390
|
-
def get_item(self, queue_id: str) ->
|
|
390
|
+
def get_item(self, queue_id: str) -> QueueItem | None:
|
|
391
391
|
"""Get specific queue item by ID.
|
|
392
392
|
|
|
393
393
|
Args:
|
|
@@ -409,7 +409,7 @@ class Queue:
|
|
|
409
409
|
return QueueItem.from_row(row) if row else None
|
|
410
410
|
|
|
411
411
|
def list_items(
|
|
412
|
-
self, status:
|
|
412
|
+
self, status: QueueStatus | None = None, limit: int = 50
|
|
413
413
|
) -> list[QueueItem]:
|
|
414
414
|
"""List queue items.
|
|
415
415
|
|
|
@@ -5,13 +5,13 @@ import sqlite3
|
|
|
5
5
|
import threading
|
|
6
6
|
from datetime import datetime, timedelta
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class TicketRegistry:
|
|
12
12
|
"""Persistent registry for tracking ticket IDs and their lifecycle."""
|
|
13
13
|
|
|
14
|
-
def __init__(self, db_path:
|
|
14
|
+
def __init__(self, db_path: Path | None = None):
|
|
15
15
|
"""Initialize ticket registry.
|
|
16
16
|
|
|
17
17
|
Args:
|
|
@@ -130,10 +130,10 @@ class TicketRegistry:
|
|
|
130
130
|
self,
|
|
131
131
|
queue_id: str,
|
|
132
132
|
status: str,
|
|
133
|
-
ticket_id:
|
|
134
|
-
result_data:
|
|
135
|
-
error_message:
|
|
136
|
-
retry_count:
|
|
133
|
+
ticket_id: str | None = None,
|
|
134
|
+
result_data: dict[str, Any] | None = None,
|
|
135
|
+
error_message: str | None = None,
|
|
136
|
+
retry_count: int | None = None,
|
|
137
137
|
) -> None:
|
|
138
138
|
"""Update ticket operation status.
|
|
139
139
|
|
|
@@ -179,7 +179,7 @@ class TicketRegistry:
|
|
|
179
179
|
)
|
|
180
180
|
conn.commit()
|
|
181
181
|
|
|
182
|
-
def get_ticket_info(self, queue_id: str) ->
|
|
182
|
+
def get_ticket_info(self, queue_id: str) -> dict[str, Any] | None:
|
|
183
183
|
"""Get ticket information by queue ID.
|
|
184
184
|
|
|
185
185
|
Args:
|
mcp_ticketer/queue/worker.py
CHANGED
|
@@ -7,7 +7,7 @@ import threading
|
|
|
7
7
|
import time
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
from dotenv import load_dotenv
|
|
13
13
|
|
|
@@ -58,7 +58,7 @@ class Worker:
|
|
|
58
58
|
|
|
59
59
|
def __init__(
|
|
60
60
|
self,
|
|
61
|
-
queue:
|
|
61
|
+
queue: Queue | None = None,
|
|
62
62
|
batch_size: int = DEFAULT_BATCH_SIZE,
|
|
63
63
|
max_concurrent: int = DEFAULT_MAX_CONCURRENT,
|
|
64
64
|
):
|