mcp-ticketer 0.3.5__py3-none-any.whl → 0.12.0__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 +3 -3
- mcp_ticketer/adapters/__init__.py +2 -0
- mcp_ticketer/adapters/aitrackdown.py +263 -14
- mcp_ticketer/adapters/asana/__init__.py +15 -0
- mcp_ticketer/adapters/asana/adapter.py +1308 -0
- mcp_ticketer/adapters/asana/client.py +292 -0
- mcp_ticketer/adapters/asana/mappers.py +334 -0
- mcp_ticketer/adapters/asana/types.py +146 -0
- mcp_ticketer/adapters/github.py +326 -109
- mcp_ticketer/adapters/hybrid.py +11 -11
- mcp_ticketer/adapters/jira.py +271 -25
- mcp_ticketer/adapters/linear/adapter.py +693 -39
- mcp_ticketer/adapters/linear/client.py +61 -9
- mcp_ticketer/adapters/linear/mappers.py +9 -3
- mcp_ticketer/adapters/linear/queries.py +9 -7
- mcp_ticketer/cache/memory.py +9 -8
- mcp_ticketer/cli/adapter_diagnostics.py +1 -1
- mcp_ticketer/cli/auggie_configure.py +104 -15
- mcp_ticketer/cli/codex_configure.py +188 -32
- mcp_ticketer/cli/configure.py +37 -48
- mcp_ticketer/cli/diagnostics.py +20 -18
- mcp_ticketer/cli/discover.py +292 -26
- mcp_ticketer/cli/gemini_configure.py +107 -26
- mcp_ticketer/cli/instruction_commands.py +429 -0
- mcp_ticketer/cli/linear_commands.py +105 -22
- mcp_ticketer/cli/main.py +1830 -435
- mcp_ticketer/cli/mcp_configure.py +296 -89
- mcp_ticketer/cli/migrate_config.py +12 -8
- mcp_ticketer/cli/platform_commands.py +123 -0
- mcp_ticketer/cli/platform_detection.py +412 -0
- mcp_ticketer/cli/python_detection.py +126 -0
- mcp_ticketer/cli/queue_commands.py +15 -15
- mcp_ticketer/cli/simple_health.py +1 -1
- mcp_ticketer/cli/ticket_commands.py +773 -0
- mcp_ticketer/cli/update_checker.py +313 -0
- mcp_ticketer/cli/utils.py +67 -62
- mcp_ticketer/core/__init__.py +14 -1
- mcp_ticketer/core/adapter.py +84 -15
- mcp_ticketer/core/config.py +44 -39
- mcp_ticketer/core/env_discovery.py +42 -12
- mcp_ticketer/core/env_loader.py +15 -14
- mcp_ticketer/core/exceptions.py +3 -3
- mcp_ticketer/core/http_client.py +26 -26
- mcp_ticketer/core/instructions.py +405 -0
- mcp_ticketer/core/mappers.py +11 -11
- mcp_ticketer/core/models.py +50 -20
- mcp_ticketer/core/onepassword_secrets.py +379 -0
- mcp_ticketer/core/project_config.py +57 -35
- mcp_ticketer/core/registry.py +3 -3
- mcp_ticketer/defaults/ticket_instructions.md +644 -0
- mcp_ticketer/mcp/__init__.py +29 -1
- mcp_ticketer/mcp/__main__.py +60 -0
- mcp_ticketer/mcp/server/__init__.py +25 -0
- mcp_ticketer/mcp/server/__main__.py +60 -0
- mcp_ticketer/mcp/{dto.py → server/dto.py} +32 -32
- mcp_ticketer/mcp/{server.py → server/main.py} +127 -74
- mcp_ticketer/mcp/{response_builder.py → server/response_builder.py} +2 -2
- mcp_ticketer/mcp/server/server_sdk.py +93 -0
- mcp_ticketer/mcp/server/tools/__init__.py +47 -0
- mcp_ticketer/mcp/server/tools/attachment_tools.py +226 -0
- mcp_ticketer/mcp/server/tools/bulk_tools.py +273 -0
- mcp_ticketer/mcp/server/tools/comment_tools.py +90 -0
- mcp_ticketer/mcp/server/tools/config_tools.py +381 -0
- mcp_ticketer/mcp/server/tools/hierarchy_tools.py +532 -0
- mcp_ticketer/mcp/server/tools/instruction_tools.py +293 -0
- mcp_ticketer/mcp/server/tools/pr_tools.py +154 -0
- mcp_ticketer/mcp/server/tools/search_tools.py +206 -0
- mcp_ticketer/mcp/server/tools/ticket_tools.py +430 -0
- mcp_ticketer/mcp/server/tools/user_ticket_tools.py +382 -0
- mcp_ticketer/queue/__init__.py +1 -0
- mcp_ticketer/queue/health_monitor.py +5 -4
- mcp_ticketer/queue/manager.py +15 -51
- mcp_ticketer/queue/queue.py +19 -19
- mcp_ticketer/queue/run_worker.py +1 -1
- mcp_ticketer/queue/ticket_registry.py +14 -14
- mcp_ticketer/queue/worker.py +16 -14
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/METADATA +168 -32
- mcp_ticketer-0.12.0.dist-info/RECORD +91 -0
- mcp_ticketer-0.3.5.dist-info/RECORD +0 -62
- /mcp_ticketer/mcp/{constants.py → server/constants.py} +0 -0
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/top_level.txt +0 -0
|
@@ -47,29 +47,29 @@ class AdapterConfig:
|
|
|
47
47
|
enabled: bool = True
|
|
48
48
|
|
|
49
49
|
# Common fields (not all adapters use all fields)
|
|
50
|
-
api_key:
|
|
51
|
-
token:
|
|
50
|
+
api_key: str | None = None
|
|
51
|
+
token: str | None = None
|
|
52
52
|
|
|
53
53
|
# Linear-specific
|
|
54
|
-
team_id:
|
|
55
|
-
team_key:
|
|
56
|
-
workspace:
|
|
54
|
+
team_id: str | None = None
|
|
55
|
+
team_key: str | None = None
|
|
56
|
+
workspace: str | None = None
|
|
57
57
|
|
|
58
58
|
# JIRA-specific
|
|
59
|
-
server:
|
|
60
|
-
email:
|
|
61
|
-
api_token:
|
|
62
|
-
project_key:
|
|
59
|
+
server: str | None = None
|
|
60
|
+
email: str | None = None
|
|
61
|
+
api_token: str | None = None
|
|
62
|
+
project_key: str | None = None
|
|
63
63
|
|
|
64
64
|
# GitHub-specific
|
|
65
|
-
owner:
|
|
66
|
-
repo:
|
|
65
|
+
owner: str | None = None
|
|
66
|
+
repo: str | None = None
|
|
67
67
|
|
|
68
68
|
# AITrackdown-specific
|
|
69
|
-
base_path:
|
|
69
|
+
base_path: str | None = None
|
|
70
70
|
|
|
71
71
|
# Project ID (can be used by any adapter for scoping)
|
|
72
|
-
project_id:
|
|
72
|
+
project_id: str | None = None
|
|
73
73
|
|
|
74
74
|
# Additional adapter-specific configuration
|
|
75
75
|
additional_config: dict[str, Any] = field(default_factory=dict)
|
|
@@ -126,9 +126,9 @@ class ProjectConfig:
|
|
|
126
126
|
"""Configuration for a specific project."""
|
|
127
127
|
|
|
128
128
|
adapter: str
|
|
129
|
-
api_key:
|
|
130
|
-
project_id:
|
|
131
|
-
team_id:
|
|
129
|
+
api_key: str | None = None
|
|
130
|
+
project_id: str | None = None
|
|
131
|
+
team_id: str | None = None
|
|
132
132
|
additional_config: dict[str, Any] = field(default_factory=dict)
|
|
133
133
|
|
|
134
134
|
def to_dict(self) -> dict[str, Any]:
|
|
@@ -147,7 +147,7 @@ class HybridConfig:
|
|
|
147
147
|
|
|
148
148
|
enabled: bool = False
|
|
149
149
|
adapters: list[str] = field(default_factory=list)
|
|
150
|
-
primary_adapter:
|
|
150
|
+
primary_adapter: str | None = None
|
|
151
151
|
sync_strategy: SyncStrategy = SyncStrategy.PRIMARY_SOURCE
|
|
152
152
|
|
|
153
153
|
def to_dict(self) -> dict[str, Any]:
|
|
@@ -172,11 +172,16 @@ class TicketerConfig:
|
|
|
172
172
|
default_adapter: str = "aitrackdown"
|
|
173
173
|
project_configs: dict[str, ProjectConfig] = field(default_factory=dict)
|
|
174
174
|
adapters: dict[str, AdapterConfig] = field(default_factory=dict)
|
|
175
|
-
hybrid_mode:
|
|
175
|
+
hybrid_mode: HybridConfig | None = None
|
|
176
|
+
|
|
177
|
+
# Default values for ticket operations
|
|
178
|
+
default_user: str | None = None # Default assignee (user_id or email)
|
|
179
|
+
default_project: str | None = None # Default project/epic ID
|
|
180
|
+
default_epic: str | None = None # Alias for default_project (backward compat)
|
|
176
181
|
|
|
177
182
|
def to_dict(self) -> dict[str, Any]:
|
|
178
183
|
"""Convert to dictionary for JSON serialization."""
|
|
179
|
-
|
|
184
|
+
result = {
|
|
180
185
|
"default_adapter": self.default_adapter,
|
|
181
186
|
"project_configs": {
|
|
182
187
|
path: config.to_dict() for path, config in self.project_configs.items()
|
|
@@ -186,6 +191,14 @@ class TicketerConfig:
|
|
|
186
191
|
},
|
|
187
192
|
"hybrid_mode": self.hybrid_mode.to_dict() if self.hybrid_mode else None,
|
|
188
193
|
}
|
|
194
|
+
# Add optional fields if set
|
|
195
|
+
if self.default_user is not None:
|
|
196
|
+
result["default_user"] = self.default_user
|
|
197
|
+
if self.default_project is not None:
|
|
198
|
+
result["default_project"] = self.default_project
|
|
199
|
+
if self.default_epic is not None:
|
|
200
|
+
result["default_epic"] = self.default_epic
|
|
201
|
+
return result
|
|
189
202
|
|
|
190
203
|
@classmethod
|
|
191
204
|
def from_dict(cls, data: dict[str, Any]) -> "TicketerConfig":
|
|
@@ -212,6 +225,9 @@ class TicketerConfig:
|
|
|
212
225
|
project_configs=project_configs,
|
|
213
226
|
adapters=adapters,
|
|
214
227
|
hybrid_mode=hybrid_mode,
|
|
228
|
+
default_user=data.get("default_user"),
|
|
229
|
+
default_project=data.get("default_project"),
|
|
230
|
+
default_epic=data.get("default_epic"),
|
|
215
231
|
)
|
|
216
232
|
|
|
217
233
|
|
|
@@ -219,7 +235,7 @@ class ConfigValidator:
|
|
|
219
235
|
"""Validate adapter configurations."""
|
|
220
236
|
|
|
221
237
|
@staticmethod
|
|
222
|
-
def validate_linear_config(config: dict[str, Any]) -> tuple[bool,
|
|
238
|
+
def validate_linear_config(config: dict[str, Any]) -> tuple[bool, str | None]:
|
|
223
239
|
"""Validate Linear adapter configuration.
|
|
224
240
|
|
|
225
241
|
Returns:
|
|
@@ -241,7 +257,7 @@ class ConfigValidator:
|
|
|
241
257
|
return True, None
|
|
242
258
|
|
|
243
259
|
@staticmethod
|
|
244
|
-
def validate_github_config(config: dict[str, Any]) -> tuple[bool,
|
|
260
|
+
def validate_github_config(config: dict[str, Any]) -> tuple[bool, str | None]:
|
|
245
261
|
"""Validate GitHub adapter configuration.
|
|
246
262
|
|
|
247
263
|
Returns:
|
|
@@ -270,7 +286,7 @@ class ConfigValidator:
|
|
|
270
286
|
return True, None
|
|
271
287
|
|
|
272
288
|
@staticmethod
|
|
273
|
-
def validate_jira_config(config: dict[str, Any]) -> tuple[bool,
|
|
289
|
+
def validate_jira_config(config: dict[str, Any]) -> tuple[bool, str | None]:
|
|
274
290
|
"""Validate JIRA adapter configuration.
|
|
275
291
|
|
|
276
292
|
Returns:
|
|
@@ -292,7 +308,7 @@ class ConfigValidator:
|
|
|
292
308
|
@staticmethod
|
|
293
309
|
def validate_aitrackdown_config(
|
|
294
310
|
config: dict[str, Any],
|
|
295
|
-
) -> tuple[bool,
|
|
311
|
+
) -> tuple[bool, str | None]:
|
|
296
312
|
"""Validate AITrackdown adapter configuration.
|
|
297
313
|
|
|
298
314
|
Returns:
|
|
@@ -306,7 +322,7 @@ class ConfigValidator:
|
|
|
306
322
|
@classmethod
|
|
307
323
|
def validate(
|
|
308
324
|
cls, adapter_type: str, config: dict[str, Any]
|
|
309
|
-
) -> tuple[bool,
|
|
325
|
+
) -> tuple[bool, str | None]:
|
|
310
326
|
"""Validate configuration for any adapter type.
|
|
311
327
|
|
|
312
328
|
Args:
|
|
@@ -350,7 +366,7 @@ class ConfigResolver:
|
|
|
350
366
|
PROJECT_CONFIG_SUBPATH = ".mcp-ticketer" / Path("config.json")
|
|
351
367
|
|
|
352
368
|
def __init__(
|
|
353
|
-
self, project_path:
|
|
369
|
+
self, project_path: Path | None = None, enable_env_discovery: bool = True
|
|
354
370
|
):
|
|
355
371
|
"""Initialize config resolver.
|
|
356
372
|
|
|
@@ -361,8 +377,8 @@ class ConfigResolver:
|
|
|
361
377
|
"""
|
|
362
378
|
self.project_path = project_path or Path.cwd()
|
|
363
379
|
self.enable_env_discovery = enable_env_discovery
|
|
364
|
-
self._project_config:
|
|
365
|
-
self._discovered_config:
|
|
380
|
+
self._project_config: TicketerConfig | None = None
|
|
381
|
+
self._discovered_config: DiscoveryResult | None = None
|
|
366
382
|
|
|
367
383
|
def load_global_config(self) -> TicketerConfig:
|
|
368
384
|
"""Load default configuration (global config loading removed for security).
|
|
@@ -382,8 +398,8 @@ class ConfigResolver:
|
|
|
382
398
|
return default_config
|
|
383
399
|
|
|
384
400
|
def load_project_config(
|
|
385
|
-
self, project_path:
|
|
386
|
-
) ->
|
|
401
|
+
self, project_path: Path | None = None
|
|
402
|
+
) -> TicketerConfig | None:
|
|
387
403
|
"""Load project-specific configuration.
|
|
388
404
|
|
|
389
405
|
Args:
|
|
@@ -424,7 +440,7 @@ class ConfigResolver:
|
|
|
424
440
|
self.save_project_config(config)
|
|
425
441
|
|
|
426
442
|
def save_project_config(
|
|
427
|
-
self, config: TicketerConfig, project_path:
|
|
443
|
+
self, config: TicketerConfig, project_path: Path | None = None
|
|
428
444
|
) -> None:
|
|
429
445
|
"""Save project-specific configuration.
|
|
430
446
|
|
|
@@ -461,8 +477,8 @@ class ConfigResolver:
|
|
|
461
477
|
|
|
462
478
|
def resolve_adapter_config(
|
|
463
479
|
self,
|
|
464
|
-
adapter_name:
|
|
465
|
-
cli_overrides:
|
|
480
|
+
adapter_name: str | None = None,
|
|
481
|
+
cli_overrides: dict[str, Any] | None = None,
|
|
466
482
|
) -> dict[str, Any]:
|
|
467
483
|
"""Resolve adapter configuration with hierarchical precedence.
|
|
468
484
|
|
|
@@ -582,6 +598,12 @@ class ConfigResolver:
|
|
|
582
598
|
overrides["team_id"] = os.getenv("MCP_TICKETER_LINEAR_TEAM_ID")
|
|
583
599
|
if os.getenv("LINEAR_API_KEY"):
|
|
584
600
|
overrides["api_key"] = os.getenv("LINEAR_API_KEY")
|
|
601
|
+
if os.getenv("LINEAR_TEAM_ID"):
|
|
602
|
+
overrides["team_id"] = os.getenv("LINEAR_TEAM_ID")
|
|
603
|
+
if os.getenv("LINEAR_TEAM_KEY"):
|
|
604
|
+
overrides["team_key"] = os.getenv("LINEAR_TEAM_KEY")
|
|
605
|
+
if os.getenv("MCP_TICKETER_LINEAR_TEAM_KEY"):
|
|
606
|
+
overrides["team_key"] = os.getenv("MCP_TICKETER_LINEAR_TEAM_KEY")
|
|
585
607
|
|
|
586
608
|
elif adapter_type == AdapterType.GITHUB.value:
|
|
587
609
|
if os.getenv("MCP_TICKETER_GITHUB_TOKEN"):
|
|
@@ -623,7 +645,7 @@ class ConfigResolver:
|
|
|
623
645
|
|
|
624
646
|
return overrides
|
|
625
647
|
|
|
626
|
-
def get_hybrid_config(self) ->
|
|
648
|
+
def get_hybrid_config(self) -> HybridConfig | None:
|
|
627
649
|
"""Get hybrid mode configuration if enabled.
|
|
628
650
|
|
|
629
651
|
Returns:
|
|
@@ -655,10 +677,10 @@ class ConfigResolver:
|
|
|
655
677
|
|
|
656
678
|
|
|
657
679
|
# Singleton instance for global access
|
|
658
|
-
_default_resolver:
|
|
680
|
+
_default_resolver: ConfigResolver | None = None
|
|
659
681
|
|
|
660
682
|
|
|
661
|
-
def get_config_resolver(project_path:
|
|
683
|
+
def get_config_resolver(project_path: Path | None = None) -> ConfigResolver:
|
|
662
684
|
"""Get the global config resolver instance.
|
|
663
685
|
|
|
664
686
|
Args:
|
mcp_ticketer/core/registry.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Adapter registry for dynamic adapter management."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from .adapter import BaseAdapter
|
|
6
6
|
|
|
@@ -37,7 +37,7 @@ class AdapterRegistry:
|
|
|
37
37
|
|
|
38
38
|
@classmethod
|
|
39
39
|
def get_adapter(
|
|
40
|
-
cls, name: str, config:
|
|
40
|
+
cls, name: str, config: dict[str, Any] | None = None, force_new: bool = False
|
|
41
41
|
) -> BaseAdapter:
|
|
42
42
|
"""Get or create an adapter instance.
|
|
43
43
|
|
|
@@ -115,7 +115,7 @@ class AdapterRegistry:
|
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
def adapter_factory(adapter_type: str, config: dict[str, Any]) -> BaseAdapter:
|
|
118
|
-
"""
|
|
118
|
+
"""Create adapter instance using factory pattern.
|
|
119
119
|
|
|
120
120
|
Args:
|
|
121
121
|
adapter_type: Type of adapter to create
|