mcp-ticketer 2.0.1__py3-none-any.whl → 2.2.13__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/_version_scm.py +1 -0
- mcp_ticketer/adapters/aitrackdown.py +122 -0
- mcp_ticketer/adapters/asana/adapter.py +121 -0
- mcp_ticketer/adapters/github/__init__.py +26 -0
- mcp_ticketer/adapters/{github.py → github/adapter.py} +1506 -365
- mcp_ticketer/adapters/github/client.py +335 -0
- mcp_ticketer/adapters/github/mappers.py +797 -0
- mcp_ticketer/adapters/github/queries.py +692 -0
- mcp_ticketer/adapters/github/types.py +460 -0
- mcp_ticketer/adapters/jira/__init__.py +35 -0
- mcp_ticketer/adapters/{jira.py → jira/adapter.py} +250 -678
- mcp_ticketer/adapters/jira/client.py +271 -0
- mcp_ticketer/adapters/jira/mappers.py +246 -0
- mcp_ticketer/adapters/jira/queries.py +216 -0
- mcp_ticketer/adapters/jira/types.py +304 -0
- mcp_ticketer/adapters/linear/adapter.py +1000 -92
- mcp_ticketer/adapters/linear/client.py +91 -1
- mcp_ticketer/adapters/linear/mappers.py +107 -0
- mcp_ticketer/adapters/linear/queries.py +112 -2
- mcp_ticketer/adapters/linear/types.py +50 -10
- mcp_ticketer/cli/configure.py +524 -89
- mcp_ticketer/cli/install_mcp_server.py +418 -0
- mcp_ticketer/cli/main.py +10 -0
- mcp_ticketer/cli/mcp_configure.py +177 -49
- mcp_ticketer/cli/platform_installer.py +9 -0
- mcp_ticketer/cli/setup_command.py +157 -1
- mcp_ticketer/cli/ticket_commands.py +443 -81
- mcp_ticketer/cli/utils.py +113 -0
- mcp_ticketer/core/__init__.py +28 -0
- mcp_ticketer/core/adapter.py +367 -1
- mcp_ticketer/core/milestone_manager.py +252 -0
- mcp_ticketer/core/models.py +345 -0
- mcp_ticketer/core/project_utils.py +281 -0
- mcp_ticketer/core/project_validator.py +376 -0
- mcp_ticketer/core/session_state.py +6 -1
- mcp_ticketer/core/state_matcher.py +36 -3
- mcp_ticketer/mcp/server/__main__.py +2 -1
- mcp_ticketer/mcp/server/routing.py +68 -0
- mcp_ticketer/mcp/server/tools/__init__.py +7 -4
- mcp_ticketer/mcp/server/tools/attachment_tools.py +3 -1
- mcp_ticketer/mcp/server/tools/config_tools.py +233 -35
- mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
- mcp_ticketer/mcp/server/tools/search_tools.py +30 -1
- mcp_ticketer/mcp/server/tools/ticket_tools.py +37 -1
- mcp_ticketer/queue/queue.py +68 -0
- {mcp_ticketer-2.0.1.dist-info → mcp_ticketer-2.2.13.dist-info}/METADATA +33 -3
- {mcp_ticketer-2.0.1.dist-info → mcp_ticketer-2.2.13.dist-info}/RECORD +72 -36
- mcp_ticketer-2.2.13.dist-info/top_level.txt +2 -0
- py_mcp_installer/examples/phase3_demo.py +178 -0
- py_mcp_installer/scripts/manage_version.py +54 -0
- py_mcp_installer/setup.py +6 -0
- py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
- py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
- py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
- py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
- py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
- py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
- py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
- py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
- py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
- py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
- py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
- py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
- py_mcp_installer/src/py_mcp_installer/types.py +222 -0
- py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
- py_mcp_installer/tests/__init__.py +0 -0
- py_mcp_installer/tests/platforms/__init__.py +0 -0
- py_mcp_installer/tests/test_platform_detector.py +17 -0
- mcp_ticketer-2.0.1.dist-info/top_level.txt +0 -1
- {mcp_ticketer-2.0.1.dist-info → mcp_ticketer-2.2.13.dist-info}/WHEEL +0 -0
- {mcp_ticketer-2.0.1.dist-info → mcp_ticketer-2.2.13.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-2.0.1.dist-info → mcp_ticketer-2.2.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"""GitHub-specific type definitions and conversion utilities.
|
|
2
|
+
|
|
3
|
+
This module contains:
|
|
4
|
+
- State and priority mappings between GitHub and universal models
|
|
5
|
+
- Type conversion helper functions
|
|
6
|
+
- GitHub-specific constants and enums
|
|
7
|
+
- TypedDict definitions for GitHub API responses
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any, TypedDict
|
|
13
|
+
|
|
14
|
+
from ...core.models import Priority, TicketState
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GitHubStateMapping:
|
|
18
|
+
"""GitHub issue states and label-based extended states.
|
|
19
|
+
|
|
20
|
+
Design Decision: GitHub's two-state model (open/closed)
|
|
21
|
+
|
|
22
|
+
GitHub natively only supports two states: 'open' and 'closed'.
|
|
23
|
+
To support richer workflow states, we use labels to extend the state model.
|
|
24
|
+
|
|
25
|
+
Rationale:
|
|
26
|
+
- Maintains compatibility with GitHub's API limitations
|
|
27
|
+
- Allows flexible workflow states through labeling
|
|
28
|
+
- Enables state transitions without closing issues
|
|
29
|
+
|
|
30
|
+
Trade-offs:
|
|
31
|
+
- State changes require label management (more API calls)
|
|
32
|
+
- Labels are user-visible and can be manually modified
|
|
33
|
+
- No built-in state transition validation in GitHub
|
|
34
|
+
|
|
35
|
+
Extension Point: Custom state labels can be configured per repository
|
|
36
|
+
through adapter configuration.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# GitHub native states
|
|
40
|
+
OPEN = "open"
|
|
41
|
+
CLOSED = "closed"
|
|
42
|
+
|
|
43
|
+
# Extended states via labels
|
|
44
|
+
# These labels represent workflow states beyond GitHub's native open/closed
|
|
45
|
+
STATE_LABELS = {
|
|
46
|
+
TicketState.IN_PROGRESS: "in-progress",
|
|
47
|
+
TicketState.READY: "ready",
|
|
48
|
+
TicketState.TESTED: "tested",
|
|
49
|
+
TicketState.WAITING: "waiting",
|
|
50
|
+
TicketState.BLOCKED: "blocked",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Priority labels mapping
|
|
54
|
+
# Multiple label patterns support different team conventions
|
|
55
|
+
PRIORITY_LABELS = {
|
|
56
|
+
Priority.CRITICAL: ["P0", "critical", "urgent"],
|
|
57
|
+
Priority.HIGH: ["P1", "high"],
|
|
58
|
+
Priority.MEDIUM: ["P2", "medium"],
|
|
59
|
+
Priority.LOW: ["P3", "low"],
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_universal_state(
|
|
64
|
+
github_state: str,
|
|
65
|
+
labels: list[str],
|
|
66
|
+
) -> TicketState:
|
|
67
|
+
"""Convert GitHub state + labels to universal TicketState.
|
|
68
|
+
|
|
69
|
+
GitHub has only two states (open/closed), so we use labels to infer
|
|
70
|
+
the extended workflow state.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
----
|
|
74
|
+
github_state: GitHub issue state ('open' or 'closed')
|
|
75
|
+
labels: List of label names attached to the issue
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
-------
|
|
79
|
+
Universal ticket state enum value
|
|
80
|
+
|
|
81
|
+
Performance:
|
|
82
|
+
-----------
|
|
83
|
+
Time Complexity: O(n*m) where n=number of labels, m=state labels to check
|
|
84
|
+
Worst case: ~5 state labels * ~20 issue labels = 100 comparisons
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
-------
|
|
88
|
+
>>> get_universal_state("open", ["in-progress", "bug"])
|
|
89
|
+
TicketState.IN_PROGRESS
|
|
90
|
+
>>> get_universal_state("closed", [])
|
|
91
|
+
TicketState.CLOSED
|
|
92
|
+
"""
|
|
93
|
+
# Closed issues are always CLOSED state
|
|
94
|
+
if github_state == "closed":
|
|
95
|
+
return TicketState.CLOSED
|
|
96
|
+
|
|
97
|
+
# Normalize labels for comparison
|
|
98
|
+
label_names = [label.lower() for label in labels]
|
|
99
|
+
|
|
100
|
+
# Check for extended state labels
|
|
101
|
+
for state, label_name in GitHubStateMapping.STATE_LABELS.items():
|
|
102
|
+
if label_name.lower() in label_names:
|
|
103
|
+
return state
|
|
104
|
+
|
|
105
|
+
# Default to OPEN if no state label found
|
|
106
|
+
return TicketState.OPEN
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def extract_state_from_issue(issue: dict[str, Any]) -> TicketState:
|
|
110
|
+
"""Extract ticket state from GitHub issue data.
|
|
111
|
+
|
|
112
|
+
Handles multiple GitHub API response formats:
|
|
113
|
+
- REST API v3: labels as array of objects
|
|
114
|
+
- GraphQL API v4: labels.nodes as array
|
|
115
|
+
- Legacy formats: labels as array of strings
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
----
|
|
119
|
+
issue: GitHub issue data from REST or GraphQL API
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
-------
|
|
123
|
+
Universal ticket state
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
-------
|
|
127
|
+
>>> issue = {"state": "open", "labels": [{"name": "ready"}]}
|
|
128
|
+
>>> extract_state_from_issue(issue)
|
|
129
|
+
TicketState.READY
|
|
130
|
+
"""
|
|
131
|
+
# Extract labels from various formats
|
|
132
|
+
labels = []
|
|
133
|
+
if "labels" in issue:
|
|
134
|
+
if isinstance(issue["labels"], list):
|
|
135
|
+
# REST API format: array of objects or strings
|
|
136
|
+
labels = [
|
|
137
|
+
label.get("name", "") if isinstance(label, dict) else str(label)
|
|
138
|
+
for label in issue["labels"]
|
|
139
|
+
]
|
|
140
|
+
elif isinstance(issue["labels"], dict) and "nodes" in issue["labels"]:
|
|
141
|
+
# GraphQL format: labels.nodes array
|
|
142
|
+
labels = [label["name"] for label in issue["labels"]["nodes"]]
|
|
143
|
+
|
|
144
|
+
return get_universal_state(issue["state"], labels)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_priority_from_labels(
|
|
148
|
+
labels: list[str],
|
|
149
|
+
custom_priority_scheme: dict[str, list[str]] | None = None,
|
|
150
|
+
) -> Priority:
|
|
151
|
+
"""Extract priority from GitHub issue labels.
|
|
152
|
+
|
|
153
|
+
Priority is inferred from labels since GitHub has no native priority field.
|
|
154
|
+
Supports custom priority label schemes for team-specific conventions.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
----
|
|
158
|
+
labels: List of label names
|
|
159
|
+
custom_priority_scheme: Optional custom mapping of priority -> label patterns
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
-------
|
|
163
|
+
Priority enum value (defaults to MEDIUM if not found)
|
|
164
|
+
|
|
165
|
+
Performance:
|
|
166
|
+
-----------
|
|
167
|
+
Time Complexity: O(n*m) where n=labels, m=priority patterns
|
|
168
|
+
Expected: ~20 labels * ~12 priority patterns = 240 comparisons worst case
|
|
169
|
+
|
|
170
|
+
Example:
|
|
171
|
+
-------
|
|
172
|
+
>>> get_priority_from_labels(["P0", "bug"])
|
|
173
|
+
Priority.CRITICAL
|
|
174
|
+
>>> get_priority_from_labels(["enhancement"])
|
|
175
|
+
Priority.MEDIUM # default
|
|
176
|
+
"""
|
|
177
|
+
label_names = [label.lower() for label in labels]
|
|
178
|
+
|
|
179
|
+
# Check custom priority scheme first
|
|
180
|
+
if custom_priority_scheme:
|
|
181
|
+
for priority_str, label_patterns in custom_priority_scheme.items():
|
|
182
|
+
for pattern in label_patterns:
|
|
183
|
+
if any(pattern.lower() in label for label in label_names):
|
|
184
|
+
return Priority(priority_str)
|
|
185
|
+
|
|
186
|
+
# Check default priority labels
|
|
187
|
+
for priority, priority_labels in GitHubStateMapping.PRIORITY_LABELS.items():
|
|
188
|
+
for priority_label in priority_labels:
|
|
189
|
+
if priority_label.lower() in label_names:
|
|
190
|
+
return priority
|
|
191
|
+
|
|
192
|
+
return Priority.MEDIUM
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def get_priority_label(
|
|
196
|
+
priority: Priority,
|
|
197
|
+
custom_priority_scheme: dict[str, list[str]] | None = None,
|
|
198
|
+
) -> str:
|
|
199
|
+
"""Get label name for a priority level.
|
|
200
|
+
|
|
201
|
+
Returns the first matching label from custom scheme or default labels.
|
|
202
|
+
Falls back to P0/P1/P2/P3 notation if no match found.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
----
|
|
206
|
+
priority: Universal priority enum
|
|
207
|
+
custom_priority_scheme: Optional custom priority label mapping
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
-------
|
|
211
|
+
Label name to apply to issue
|
|
212
|
+
|
|
213
|
+
Example:
|
|
214
|
+
-------
|
|
215
|
+
>>> get_priority_label(Priority.CRITICAL)
|
|
216
|
+
'P0'
|
|
217
|
+
>>> get_priority_label(Priority.HIGH, {"high": ["urgent", "high-priority"]})
|
|
218
|
+
'urgent'
|
|
219
|
+
"""
|
|
220
|
+
# Check custom scheme first
|
|
221
|
+
if custom_priority_scheme:
|
|
222
|
+
labels = custom_priority_scheme.get(priority.value, [])
|
|
223
|
+
if labels:
|
|
224
|
+
return labels[0]
|
|
225
|
+
|
|
226
|
+
# Use default labels
|
|
227
|
+
labels = GitHubStateMapping.PRIORITY_LABELS.get(priority, [])
|
|
228
|
+
if labels:
|
|
229
|
+
return labels[0]
|
|
230
|
+
|
|
231
|
+
# Fallback to P0-P3 notation
|
|
232
|
+
priority_index = list(Priority).index(priority)
|
|
233
|
+
return f"P{priority_index}"
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def get_state_label(state: TicketState) -> str | None:
|
|
237
|
+
"""Get the label name for extended workflow states.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
----
|
|
241
|
+
state: Universal ticket state
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
-------
|
|
245
|
+
Label name if state requires a label, None for native GitHub states
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
-------
|
|
249
|
+
>>> get_state_label(TicketState.IN_PROGRESS)
|
|
250
|
+
'in-progress'
|
|
251
|
+
>>> get_state_label(TicketState.OPEN)
|
|
252
|
+
None # Native GitHub state, no label needed
|
|
253
|
+
"""
|
|
254
|
+
return GitHubStateMapping.STATE_LABELS.get(state)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def get_github_state(state: TicketState) -> str:
|
|
258
|
+
"""Map universal state to GitHub native state.
|
|
259
|
+
|
|
260
|
+
Only two valid values: 'open' or 'closed'.
|
|
261
|
+
Extended states map to 'open' with additional labels.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
----
|
|
265
|
+
state: Universal ticket state
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
-------
|
|
269
|
+
GitHub state string ('open' or 'closed')
|
|
270
|
+
|
|
271
|
+
Example:
|
|
272
|
+
-------
|
|
273
|
+
>>> get_github_state(TicketState.IN_PROGRESS)
|
|
274
|
+
'open'
|
|
275
|
+
>>> get_github_state(TicketState.CLOSED)
|
|
276
|
+
'closed'
|
|
277
|
+
"""
|
|
278
|
+
if state in (TicketState.DONE, TicketState.CLOSED):
|
|
279
|
+
return GitHubStateMapping.CLOSED
|
|
280
|
+
return GitHubStateMapping.OPEN
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# =============================================================================
|
|
284
|
+
# GitHub Projects V2 Type Definitions
|
|
285
|
+
# =============================================================================
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class ProjectV2Owner(TypedDict, total=False):
|
|
289
|
+
"""GitHub ProjectV2 owner (Organization or User).
|
|
290
|
+
|
|
291
|
+
Attributes:
|
|
292
|
+
__typename: Type discriminator ("Organization" or "User")
|
|
293
|
+
login: Owner login name
|
|
294
|
+
id: Owner node ID
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
__typename: str
|
|
298
|
+
login: str
|
|
299
|
+
id: str
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class ProjectV2PageInfo(TypedDict, total=False):
|
|
303
|
+
"""GraphQL pagination info for ProjectV2 queries.
|
|
304
|
+
|
|
305
|
+
Attributes:
|
|
306
|
+
hasNextPage: Whether more results exist
|
|
307
|
+
endCursor: Cursor for next page
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
hasNextPage: bool
|
|
311
|
+
endCursor: str | None
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class ProjectV2ItemsConnection(TypedDict, total=False):
|
|
315
|
+
"""ProjectV2 items connection.
|
|
316
|
+
|
|
317
|
+
Attributes:
|
|
318
|
+
totalCount: Total number of items in project
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
totalCount: int
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class ProjectV2Node(TypedDict, total=False):
|
|
325
|
+
"""GitHub ProjectV2 GraphQL node.
|
|
326
|
+
|
|
327
|
+
Represents a single GitHub Projects V2 project from the GraphQL API.
|
|
328
|
+
This type matches the structure returned by PROJECT_V2_FRAGMENT.
|
|
329
|
+
|
|
330
|
+
Design Decision: Total vs Required Fields
|
|
331
|
+
-----------------------------------------
|
|
332
|
+
Using total=False allows optional fields to be omitted, matching the
|
|
333
|
+
GraphQL API where many fields are nullable or may not be queried.
|
|
334
|
+
|
|
335
|
+
Required fields (id, number, title) are still enforced at the Pydantic
|
|
336
|
+
model level in map_github_projectv2_to_project().
|
|
337
|
+
|
|
338
|
+
Attributes:
|
|
339
|
+
id: GitHub node ID (e.g., "PVT_kwDOABcdefgh")
|
|
340
|
+
number: Project number (e.g., 5)
|
|
341
|
+
title: Project title
|
|
342
|
+
shortDescription: Brief description (max 256 chars)
|
|
343
|
+
readme: Markdown readme content
|
|
344
|
+
public: Whether project is publicly visible
|
|
345
|
+
closed: Whether project is closed
|
|
346
|
+
url: Direct URL to project
|
|
347
|
+
createdAt: ISO timestamp of creation
|
|
348
|
+
updatedAt: ISO timestamp of last update
|
|
349
|
+
closedAt: ISO timestamp of closure (if closed)
|
|
350
|
+
owner: Owner (Organization or User)
|
|
351
|
+
items: Items connection with totalCount
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
id: str
|
|
355
|
+
number: int
|
|
356
|
+
title: str
|
|
357
|
+
shortDescription: str | None
|
|
358
|
+
readme: str | None
|
|
359
|
+
public: bool
|
|
360
|
+
closed: bool
|
|
361
|
+
url: str
|
|
362
|
+
createdAt: str
|
|
363
|
+
updatedAt: str
|
|
364
|
+
closedAt: str | None
|
|
365
|
+
owner: ProjectV2Owner
|
|
366
|
+
items: ProjectV2ItemsConnection | None
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class ProjectV2Response(TypedDict, total=False):
|
|
370
|
+
"""Response from GET_PROJECT_QUERY or GET_PROJECT_BY_ID_QUERY.
|
|
371
|
+
|
|
372
|
+
Single project query response wrapping the project node.
|
|
373
|
+
|
|
374
|
+
Attributes:
|
|
375
|
+
organization: Organization containing projectV2 field
|
|
376
|
+
node: Direct node lookup result
|
|
377
|
+
"""
|
|
378
|
+
|
|
379
|
+
organization: dict[str, ProjectV2Node | None]
|
|
380
|
+
node: ProjectV2Node | None
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
class ProjectV2Connection(TypedDict, total=False):
|
|
384
|
+
"""Connection of ProjectV2 nodes with pagination.
|
|
385
|
+
|
|
386
|
+
Attributes:
|
|
387
|
+
totalCount: Total number of projects
|
|
388
|
+
pageInfo: Pagination information
|
|
389
|
+
nodes: List of project nodes
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
totalCount: int
|
|
393
|
+
pageInfo: ProjectV2PageInfo
|
|
394
|
+
nodes: list[ProjectV2Node]
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class ProjectListResponse(TypedDict, total=False):
|
|
398
|
+
"""Response from LIST_PROJECTS_QUERY.
|
|
399
|
+
|
|
400
|
+
Attributes:
|
|
401
|
+
organization: Organization containing projectsV2 connection
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
organization: dict[str, ProjectV2Connection]
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
class ProjectItemContent(TypedDict, total=False):
|
|
408
|
+
"""Content of a project item (Issue, PR, or DraftIssue).
|
|
409
|
+
|
|
410
|
+
Attributes:
|
|
411
|
+
__typename: Content type discriminator
|
|
412
|
+
id: Content node ID
|
|
413
|
+
number: Issue/PR number (not present for DraftIssue)
|
|
414
|
+
title: Content title
|
|
415
|
+
state: Content state (OPEN/CLOSED for issues, etc.)
|
|
416
|
+
labels: Labels connection (issues only)
|
|
417
|
+
"""
|
|
418
|
+
|
|
419
|
+
__typename: str
|
|
420
|
+
id: str
|
|
421
|
+
number: int | None
|
|
422
|
+
title: str
|
|
423
|
+
state: str | None
|
|
424
|
+
labels: dict[str, list[dict[str, str]]] | None
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
class ProjectItemNode(TypedDict, total=False):
|
|
428
|
+
"""Single project item node.
|
|
429
|
+
|
|
430
|
+
Attributes:
|
|
431
|
+
id: Project item ID (not the same as content ID)
|
|
432
|
+
content: The actual content (Issue, PR, or DraftIssue)
|
|
433
|
+
"""
|
|
434
|
+
|
|
435
|
+
id: str
|
|
436
|
+
content: ProjectItemContent
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
class ProjectItemsConnection(TypedDict, total=False):
|
|
440
|
+
"""Connection of project items with pagination.
|
|
441
|
+
|
|
442
|
+
Attributes:
|
|
443
|
+
totalCount: Total items in project
|
|
444
|
+
pageInfo: Pagination info
|
|
445
|
+
nodes: List of project item nodes
|
|
446
|
+
"""
|
|
447
|
+
|
|
448
|
+
totalCount: int
|
|
449
|
+
pageInfo: ProjectV2PageInfo
|
|
450
|
+
nodes: list[ProjectItemNode]
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class ProjectItemsResponse(TypedDict, total=False):
|
|
454
|
+
"""Response from PROJECT_ITEMS_QUERY.
|
|
455
|
+
|
|
456
|
+
Attributes:
|
|
457
|
+
node: ProjectV2 node containing items connection
|
|
458
|
+
"""
|
|
459
|
+
|
|
460
|
+
node: dict[str, ProjectItemsConnection]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""JIRA adapter for universal ticket management.
|
|
2
|
+
|
|
3
|
+
This module provides a unified interface to JIRA REST API v3, supporting both
|
|
4
|
+
JIRA Cloud and JIRA Server/Data Center.
|
|
5
|
+
|
|
6
|
+
Public API:
|
|
7
|
+
-----------
|
|
8
|
+
JiraAdapter: Main adapter class for JIRA operations
|
|
9
|
+
JiraIssueType: Enum of common JIRA issue types
|
|
10
|
+
JiraPriority: Enum of standard JIRA priority levels
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
------
|
|
14
|
+
from mcp_ticketer.adapters.jira import JiraAdapter
|
|
15
|
+
|
|
16
|
+
config = {
|
|
17
|
+
"server": "https://company.atlassian.net",
|
|
18
|
+
"email": "user@example.com",
|
|
19
|
+
"api_token": "your-token",
|
|
20
|
+
"project_key": "PROJ",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
adapter = JiraAdapter(config)
|
|
24
|
+
tickets = await adapter.list(limit=10)
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from .adapter import JiraAdapter
|
|
29
|
+
from .types import JiraIssueType, JiraPriority
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"JiraAdapter",
|
|
33
|
+
"JiraIssueType",
|
|
34
|
+
"JiraPriority",
|
|
35
|
+
]
|