mcp-ticketer 0.1.30__py3-none-any.whl → 1.2.11__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.

Files changed (109) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +3 -3
  3. mcp_ticketer/adapters/__init__.py +2 -0
  4. mcp_ticketer/adapters/aitrackdown.py +796 -46
  5. mcp_ticketer/adapters/asana/__init__.py +15 -0
  6. mcp_ticketer/adapters/asana/adapter.py +1416 -0
  7. mcp_ticketer/adapters/asana/client.py +292 -0
  8. mcp_ticketer/adapters/asana/mappers.py +348 -0
  9. mcp_ticketer/adapters/asana/types.py +146 -0
  10. mcp_ticketer/adapters/github.py +879 -129
  11. mcp_ticketer/adapters/hybrid.py +11 -11
  12. mcp_ticketer/adapters/jira.py +973 -73
  13. mcp_ticketer/adapters/linear/__init__.py +24 -0
  14. mcp_ticketer/adapters/linear/adapter.py +2732 -0
  15. mcp_ticketer/adapters/linear/client.py +344 -0
  16. mcp_ticketer/adapters/linear/mappers.py +420 -0
  17. mcp_ticketer/adapters/linear/queries.py +479 -0
  18. mcp_ticketer/adapters/linear/types.py +360 -0
  19. mcp_ticketer/adapters/linear.py +10 -2315
  20. mcp_ticketer/analysis/__init__.py +23 -0
  21. mcp_ticketer/analysis/orphaned.py +218 -0
  22. mcp_ticketer/analysis/similarity.py +224 -0
  23. mcp_ticketer/analysis/staleness.py +266 -0
  24. mcp_ticketer/cache/memory.py +9 -8
  25. mcp_ticketer/cli/adapter_diagnostics.py +421 -0
  26. mcp_ticketer/cli/auggie_configure.py +116 -15
  27. mcp_ticketer/cli/codex_configure.py +274 -82
  28. mcp_ticketer/cli/configure.py +888 -151
  29. mcp_ticketer/cli/diagnostics.py +400 -157
  30. mcp_ticketer/cli/discover.py +297 -26
  31. mcp_ticketer/cli/gemini_configure.py +119 -26
  32. mcp_ticketer/cli/init_command.py +880 -0
  33. mcp_ticketer/cli/instruction_commands.py +435 -0
  34. mcp_ticketer/cli/linear_commands.py +616 -0
  35. mcp_ticketer/cli/main.py +203 -1165
  36. mcp_ticketer/cli/mcp_configure.py +474 -90
  37. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  38. mcp_ticketer/cli/migrate_config.py +12 -8
  39. mcp_ticketer/cli/platform_commands.py +123 -0
  40. mcp_ticketer/cli/platform_detection.py +418 -0
  41. mcp_ticketer/cli/platform_installer.py +513 -0
  42. mcp_ticketer/cli/python_detection.py +126 -0
  43. mcp_ticketer/cli/queue_commands.py +15 -15
  44. mcp_ticketer/cli/setup_command.py +639 -0
  45. mcp_ticketer/cli/simple_health.py +90 -65
  46. mcp_ticketer/cli/ticket_commands.py +1013 -0
  47. mcp_ticketer/cli/update_checker.py +313 -0
  48. mcp_ticketer/cli/utils.py +114 -66
  49. mcp_ticketer/core/__init__.py +24 -1
  50. mcp_ticketer/core/adapter.py +250 -16
  51. mcp_ticketer/core/config.py +145 -37
  52. mcp_ticketer/core/env_discovery.py +101 -22
  53. mcp_ticketer/core/env_loader.py +349 -0
  54. mcp_ticketer/core/exceptions.py +160 -0
  55. mcp_ticketer/core/http_client.py +26 -26
  56. mcp_ticketer/core/instructions.py +405 -0
  57. mcp_ticketer/core/label_manager.py +732 -0
  58. mcp_ticketer/core/mappers.py +42 -30
  59. mcp_ticketer/core/models.py +280 -28
  60. mcp_ticketer/core/onepassword_secrets.py +379 -0
  61. mcp_ticketer/core/project_config.py +183 -49
  62. mcp_ticketer/core/registry.py +3 -3
  63. mcp_ticketer/core/session_state.py +171 -0
  64. mcp_ticketer/core/state_matcher.py +592 -0
  65. mcp_ticketer/core/url_parser.py +425 -0
  66. mcp_ticketer/core/validators.py +69 -0
  67. mcp_ticketer/defaults/ticket_instructions.md +644 -0
  68. mcp_ticketer/mcp/__init__.py +29 -1
  69. mcp_ticketer/mcp/__main__.py +60 -0
  70. mcp_ticketer/mcp/server/__init__.py +25 -0
  71. mcp_ticketer/mcp/server/__main__.py +60 -0
  72. mcp_ticketer/mcp/server/constants.py +58 -0
  73. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  74. mcp_ticketer/mcp/server/dto.py +195 -0
  75. mcp_ticketer/mcp/server/main.py +1343 -0
  76. mcp_ticketer/mcp/server/response_builder.py +206 -0
  77. mcp_ticketer/mcp/server/routing.py +655 -0
  78. mcp_ticketer/mcp/server/server_sdk.py +151 -0
  79. mcp_ticketer/mcp/server/tools/__init__.py +56 -0
  80. mcp_ticketer/mcp/server/tools/analysis_tools.py +495 -0
  81. mcp_ticketer/mcp/server/tools/attachment_tools.py +226 -0
  82. mcp_ticketer/mcp/server/tools/bulk_tools.py +273 -0
  83. mcp_ticketer/mcp/server/tools/comment_tools.py +152 -0
  84. mcp_ticketer/mcp/server/tools/config_tools.py +1439 -0
  85. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  86. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +921 -0
  87. mcp_ticketer/mcp/server/tools/instruction_tools.py +300 -0
  88. mcp_ticketer/mcp/server/tools/label_tools.py +948 -0
  89. mcp_ticketer/mcp/server/tools/pr_tools.py +152 -0
  90. mcp_ticketer/mcp/server/tools/search_tools.py +215 -0
  91. mcp_ticketer/mcp/server/tools/session_tools.py +170 -0
  92. mcp_ticketer/mcp/server/tools/ticket_tools.py +1268 -0
  93. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +547 -0
  94. mcp_ticketer/queue/__init__.py +1 -0
  95. mcp_ticketer/queue/health_monitor.py +168 -136
  96. mcp_ticketer/queue/manager.py +95 -25
  97. mcp_ticketer/queue/queue.py +40 -21
  98. mcp_ticketer/queue/run_worker.py +6 -1
  99. mcp_ticketer/queue/ticket_registry.py +213 -155
  100. mcp_ticketer/queue/worker.py +109 -49
  101. mcp_ticketer-1.2.11.dist-info/METADATA +792 -0
  102. mcp_ticketer-1.2.11.dist-info/RECORD +110 -0
  103. mcp_ticketer/mcp/server.py +0 -1895
  104. mcp_ticketer-0.1.30.dist-info/METADATA +0 -413
  105. mcp_ticketer-0.1.30.dist-info/RECORD +0 -49
  106. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/WHEEL +0 -0
  107. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/entry_points.txt +0 -0
  108. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/licenses/LICENSE +0 -0
  109. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,300 @@
1
+ """Ticket instructions management tools.
2
+
3
+ This module implements MCP tools for managing ticket writing instructions,
4
+ allowing AI agents to query and customize the guidelines that help create
5
+ well-structured, consistent tickets.
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from ....core.instructions import (
12
+ InstructionsError,
13
+ InstructionsValidationError,
14
+ TicketInstructionsManager,
15
+ )
16
+ from ..server_sdk import mcp
17
+
18
+
19
+ @mcp.tool()
20
+ async def instructions_get() -> dict[str, Any]:
21
+ """Get current ticket writing instructions.
22
+
23
+ Retrieves the active instructions for the current project, which may be
24
+ custom project-specific instructions or the default embedded instructions.
25
+
26
+ Returns:
27
+ -------
28
+ A dictionary containing:
29
+ - status: "completed" or "error"
30
+ - instructions: The full instruction text (if successful)
31
+ - source: "custom" or "default" indicating which instructions are active
32
+ - path: Path to custom instructions file (if exists)
33
+ - error: Error message (if failed)
34
+
35
+ Example response:
36
+ {
37
+ "status": "completed",
38
+ "instructions": "# Ticket Writing Guidelines...",
39
+ "source": "custom",
40
+ "path": "/path/to/project/.mcp-ticketer/instructions.md"
41
+ }
42
+
43
+ """
44
+ try:
45
+ # Use current working directory as project directory
46
+ manager = TicketInstructionsManager(project_dir=Path.cwd())
47
+
48
+ # Get instructions
49
+ instructions = manager.get_instructions()
50
+
51
+ # Determine source
52
+ source = "custom" if manager.has_custom_instructions() else "default"
53
+
54
+ # Build response
55
+ response: dict[str, Any] = {
56
+ "status": "completed",
57
+ "instructions": instructions,
58
+ "source": source,
59
+ }
60
+
61
+ # Add path if custom instructions exist
62
+ if source == "custom":
63
+ response["path"] = str(manager.get_instructions_path())
64
+
65
+ return response
66
+
67
+ except InstructionsError as e:
68
+ return {
69
+ "status": "error",
70
+ "error": f"Failed to get instructions: {str(e)}",
71
+ }
72
+ except Exception as e:
73
+ return {
74
+ "status": "error",
75
+ "error": f"Unexpected error: {str(e)}",
76
+ }
77
+
78
+
79
+ @mcp.tool()
80
+ async def instructions_set(content: str, source: str = "inline") -> dict[str, Any]:
81
+ r"""Set custom ticket writing instructions for the project.
82
+
83
+ Creates or overwrites custom instructions with the provided content.
84
+ The content is validated before saving.
85
+
86
+ Args:
87
+ ----
88
+ content: The custom instructions content (markdown text)
89
+ source: Source type - "inline" for direct content or "file" for file path
90
+ (currently only "inline" is supported by MCP tools)
91
+
92
+ Returns:
93
+ -------
94
+ A dictionary containing:
95
+ - status: "completed" or "error"
96
+ - message: Success or error message
97
+ - path: Path where instructions were saved (if successful)
98
+ - error: Detailed error message (if failed)
99
+
100
+ Example:
101
+ -------
102
+ To set custom instructions:
103
+ instructions_set(
104
+ content="# Our Team's Ticket Guidelines\\n\\n...",
105
+ source="inline"
106
+ )
107
+
108
+ """
109
+ try:
110
+ # Validate source parameter
111
+ if source not in ["inline", "file"]:
112
+ return {
113
+ "status": "error",
114
+ "error": f"Invalid source '{source}'. Must be 'inline' or 'file'",
115
+ }
116
+
117
+ # Use current working directory as project directory
118
+ manager = TicketInstructionsManager(project_dir=Path.cwd())
119
+
120
+ # Set instructions
121
+ manager.set_instructions(content)
122
+
123
+ # Get path where instructions were saved
124
+ inst_path = manager.get_instructions_path()
125
+
126
+ return {
127
+ "status": "completed",
128
+ "message": "Custom instructions saved successfully",
129
+ "path": str(inst_path),
130
+ }
131
+
132
+ except InstructionsValidationError as e:
133
+ return {
134
+ "status": "error",
135
+ "error": f"Validation failed: {str(e)}",
136
+ "message": "Instructions content did not pass validation checks",
137
+ }
138
+ except InstructionsError as e:
139
+ return {
140
+ "status": "error",
141
+ "error": f"Failed to set instructions: {str(e)}",
142
+ }
143
+ except Exception as e:
144
+ return {
145
+ "status": "error",
146
+ "error": f"Unexpected error: {str(e)}",
147
+ }
148
+
149
+
150
+ @mcp.tool()
151
+ async def instructions_reset() -> dict[str, Any]:
152
+ """Reset to default instructions by deleting custom instructions.
153
+
154
+ Removes any custom project-specific instructions, causing the system
155
+ to revert to using the default embedded instructions.
156
+
157
+ Returns:
158
+ -------
159
+ A dictionary containing:
160
+ - status: "completed" or "error"
161
+ - message: Description of what happened
162
+ - error: Error message (if failed)
163
+
164
+ Example response (when custom instructions existed):
165
+ {
166
+ "status": "completed",
167
+ "message": "Custom instructions deleted. Now using defaults."
168
+ }
169
+
170
+ Example response (when no custom instructions):
171
+ {
172
+ "status": "completed",
173
+ "message": "No custom instructions to delete. Already using defaults."
174
+ }
175
+
176
+ """
177
+ try:
178
+ # Use current working directory as project directory
179
+ manager = TicketInstructionsManager(project_dir=Path.cwd())
180
+
181
+ # Check if custom instructions exist
182
+ if not manager.has_custom_instructions():
183
+ return {
184
+ "status": "completed",
185
+ "message": "No custom instructions to delete. Already using defaults.",
186
+ }
187
+
188
+ # Delete custom instructions
189
+ deleted = manager.delete_instructions()
190
+
191
+ if deleted:
192
+ return {
193
+ "status": "completed",
194
+ "message": "Custom instructions deleted. Now using defaults.",
195
+ }
196
+ else:
197
+ return {
198
+ "status": "completed",
199
+ "message": "No custom instructions found to delete.",
200
+ }
201
+
202
+ except InstructionsError as e:
203
+ return {
204
+ "status": "error",
205
+ "error": f"Failed to reset instructions: {str(e)}",
206
+ }
207
+ except Exception as e:
208
+ return {
209
+ "status": "error",
210
+ "error": f"Unexpected error: {str(e)}",
211
+ }
212
+
213
+
214
+ @mcp.tool()
215
+ async def instructions_validate(content: str) -> dict[str, Any]:
216
+ """Validate ticket instructions content without saving.
217
+
218
+ Checks if the provided content meets validation requirements:
219
+ - Not empty
220
+ - Minimum length (100 characters)
221
+ - Contains markdown headers (warning only)
222
+
223
+ This allows AI agents to validate content before attempting to save it.
224
+
225
+ Args:
226
+ ----
227
+ content: The instructions content to validate (markdown text)
228
+
229
+ Returns:
230
+ -------
231
+ A dictionary containing:
232
+ - status: "valid" or "invalid"
233
+ - warnings: List of non-critical issues (e.g., missing headers)
234
+ - errors: List of critical validation failures
235
+ - message: Summary message
236
+
237
+ Example response (valid):
238
+ {
239
+ "status": "valid",
240
+ "warnings": ["No markdown headers found"],
241
+ "errors": [],
242
+ "message": "Content is valid but has 1 warning"
243
+ }
244
+
245
+ Example response (invalid):
246
+ {
247
+ "status": "invalid",
248
+ "warnings": [],
249
+ "errors": ["Content too short (50 characters). Minimum 100 required."],
250
+ "message": "Content validation failed"
251
+ }
252
+
253
+ """
254
+ warnings: list[str] = []
255
+ errors: list[str] = []
256
+
257
+ try:
258
+ # Check for empty content
259
+ if not content or not content.strip():
260
+ errors.append("Instructions content cannot be empty")
261
+ else:
262
+ # Check minimum length
263
+ if len(content.strip()) < 100:
264
+ errors.append(
265
+ f"Content too short ({len(content)} characters). "
266
+ "Minimum 100 characters required for meaningful guidelines."
267
+ )
268
+
269
+ # Check for markdown headers (warning only)
270
+ if not any(line.strip().startswith("#") for line in content.split("\n")):
271
+ warnings.append(
272
+ "No markdown headers found. "
273
+ "Consider using headers for better structure."
274
+ )
275
+
276
+ # Determine status
277
+ if errors:
278
+ status = "invalid"
279
+ message = "Content validation failed"
280
+ elif warnings:
281
+ status = "valid"
282
+ message = f"Content is valid but has {len(warnings)} warning(s)"
283
+ else:
284
+ status = "valid"
285
+ message = "Content is valid with no issues"
286
+
287
+ return {
288
+ "status": status,
289
+ "warnings": warnings,
290
+ "errors": errors,
291
+ "message": message,
292
+ }
293
+
294
+ except Exception as e:
295
+ return {
296
+ "status": "error",
297
+ "warnings": [],
298
+ "errors": [f"Validation error: {str(e)}"],
299
+ "message": "Validation process failed",
300
+ }