mcp-ticketer 0.4.5__py3-none-any.whl → 0.4.9__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 +2 -2
- mcp_ticketer/adapters/linear/adapter.py +126 -4
- mcp_ticketer/cli/auggie_configure.py +9 -4
- mcp_ticketer/cli/codex_configure.py +9 -5
- mcp_ticketer/cli/gemini_configure.py +8 -4
- mcp_ticketer/cli/main.py +24 -4
- mcp_ticketer/cli/mcp_configure.py +7 -7
- mcp_ticketer/mcp/__init__.py +3 -3
- mcp_ticketer/mcp/server/__init__.py +21 -0
- mcp_ticketer/mcp/server/__main__.py +60 -0
- mcp_ticketer/mcp/{server.py → server/main.py} +2 -2
- mcp_ticketer/mcp/{server_sdk.py → server/server_sdk.py} +2 -2
- mcp_ticketer/mcp/{tools → server/tools}/attachment_tools.py +1 -1
- mcp_ticketer/mcp/{tools → server/tools}/bulk_tools.py +1 -1
- mcp_ticketer/mcp/{tools → server/tools}/comment_tools.py +1 -1
- mcp_ticketer/mcp/{tools → server/tools}/hierarchy_tools.py +1 -1
- mcp_ticketer/mcp/{tools → server/tools}/pr_tools.py +1 -1
- mcp_ticketer/mcp/{tools → server/tools}/search_tools.py +1 -1
- mcp_ticketer/mcp/{tools → server/tools}/ticket_tools.py +1 -1
- mcp_ticketer/queue/__init__.py +2 -3
- mcp_ticketer/queue/manager.py +1 -1
- {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/METADATA +1 -1
- {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/RECORD +32 -30
- /mcp_ticketer/mcp/{constants.py → server/constants.py} +0 -0
- /mcp_ticketer/mcp/{dto.py → server/dto.py} +0 -0
- /mcp_ticketer/mcp/{response_builder.py → server/response_builder.py} +0 -0
- /mcp_ticketer/mcp/{tools → server/tools}/__init__.py +0 -0
- {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/top_level.txt +0 -0
mcp_ticketer/__version__.py
CHANGED
|
@@ -8,8 +8,6 @@ from pathlib import Path
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
10
|
from ..core.adapter import BaseAdapter
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
11
|
from ..core.models import (
|
|
14
12
|
Attachment,
|
|
15
13
|
Comment,
|
|
@@ -21,6 +19,8 @@ from ..core.models import (
|
|
|
21
19
|
)
|
|
22
20
|
from ..core.registry import AdapterRegistry
|
|
23
21
|
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
24
|
# Import ai-trackdown-pytools when available
|
|
25
25
|
try:
|
|
26
26
|
from ai_trackdown_pytools import AITrackdown
|
|
@@ -94,10 +94,28 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
94
94
|
"Linear API key is required (api_key or LINEAR_API_KEY env var)"
|
|
95
95
|
)
|
|
96
96
|
|
|
97
|
-
# Clean API key - remove
|
|
98
|
-
# (The client will add
|
|
99
|
-
if self.api_key
|
|
100
|
-
|
|
97
|
+
# Clean API key - remove common prefixes if accidentally included in config
|
|
98
|
+
# (The client will add Bearer back when making requests)
|
|
99
|
+
if isinstance(self.api_key, str):
|
|
100
|
+
# Remove Bearer prefix
|
|
101
|
+
if self.api_key.startswith("Bearer "):
|
|
102
|
+
self.api_key = self.api_key.replace("Bearer ", "")
|
|
103
|
+
# Remove environment variable name prefix (e.g., "LINEAR_API_KEY=")
|
|
104
|
+
if "=" in self.api_key:
|
|
105
|
+
parts = self.api_key.split("=", 1)
|
|
106
|
+
if len(parts) == 2 and parts[0].upper() in (
|
|
107
|
+
"LINEAR_API_KEY",
|
|
108
|
+
"API_KEY",
|
|
109
|
+
):
|
|
110
|
+
self.api_key = parts[1]
|
|
111
|
+
|
|
112
|
+
# Validate API key format (Linear keys start with "lin_api_")
|
|
113
|
+
if not self.api_key.startswith("lin_api_"):
|
|
114
|
+
raise ValueError(
|
|
115
|
+
f"Invalid Linear API key format. Expected key starting with 'lin_api_', "
|
|
116
|
+
f"got: {self.api_key[:15]}... "
|
|
117
|
+
f"Please check your configuration and ensure the API key is correct."
|
|
118
|
+
)
|
|
101
119
|
|
|
102
120
|
self.workspace = config.get("workspace", "")
|
|
103
121
|
self.team_key = config.get("team_key")
|
|
@@ -194,6 +212,96 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
194
212
|
except Exception as e:
|
|
195
213
|
raise ValueError(f"Failed to resolve team '{self.team_key}': {e}")
|
|
196
214
|
|
|
215
|
+
async def _resolve_project_id(self, project_identifier: str) -> str | None:
|
|
216
|
+
"""Resolve project identifier (slug, name, short ID, or URL) to full UUID.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
project_identifier: Project slug, name, short ID, or URL
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Full Linear project UUID, or None if not found
|
|
223
|
+
|
|
224
|
+
Raises:
|
|
225
|
+
ValueError: If project lookup fails
|
|
226
|
+
|
|
227
|
+
Examples:
|
|
228
|
+
- "crm-smart-monitoring-system" (slug)
|
|
229
|
+
- "CRM Smart Monitoring System" (name)
|
|
230
|
+
- "f59a41a96c52" (short ID from URL)
|
|
231
|
+
- "https://linear.app/travel-bta/project/crm-smart-monitoring-system-f59a41a96c52/overview" (full URL)
|
|
232
|
+
|
|
233
|
+
"""
|
|
234
|
+
if not project_identifier:
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
# Extract slug/ID from URL if full URL provided
|
|
238
|
+
if project_identifier.startswith("http"):
|
|
239
|
+
# Extract slug-shortid from URL like:
|
|
240
|
+
# https://linear.app/travel-bta/project/crm-smart-monitoring-system-f59a41a96c52/overview
|
|
241
|
+
parts = project_identifier.split("/project/")
|
|
242
|
+
if len(parts) > 1:
|
|
243
|
+
slug_with_id = parts[1].split("/")[
|
|
244
|
+
0
|
|
245
|
+
] # Get "crm-smart-monitoring-system-f59a41a96c52"
|
|
246
|
+
project_identifier = slug_with_id
|
|
247
|
+
else:
|
|
248
|
+
raise ValueError(f"Invalid Linear project URL: {project_identifier}")
|
|
249
|
+
|
|
250
|
+
# If it looks like a full UUID already (exactly 36 chars with exactly 4 dashes), return it
|
|
251
|
+
# UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
252
|
+
if len(project_identifier) == 36 and project_identifier.count("-") == 4:
|
|
253
|
+
return project_identifier
|
|
254
|
+
|
|
255
|
+
# Query all projects and search for matching slug, name, or slugId
|
|
256
|
+
query = """
|
|
257
|
+
query GetProjects {
|
|
258
|
+
projects(first: 100) {
|
|
259
|
+
nodes {
|
|
260
|
+
id
|
|
261
|
+
name
|
|
262
|
+
slugId
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
result = await self.client.execute_query(query, {})
|
|
270
|
+
projects = result.get("projects", {}).get("nodes", [])
|
|
271
|
+
|
|
272
|
+
# Search for match by slug, slugId, name (case-insensitive)
|
|
273
|
+
project_lower = project_identifier.lower()
|
|
274
|
+
for project in projects:
|
|
275
|
+
# Check if identifier matches slug pattern (extracted from slugId)
|
|
276
|
+
slug_id = project.get("slugId", "")
|
|
277
|
+
if slug_id:
|
|
278
|
+
# slugId format: "crm-smart-monitoring-system-f59a41a96c52"
|
|
279
|
+
# Extract both the slug part and short ID
|
|
280
|
+
if "-" in slug_id:
|
|
281
|
+
parts = slug_id.rsplit(
|
|
282
|
+
"-", 1
|
|
283
|
+
) # Split from right to get last part
|
|
284
|
+
slug_part = parts[0] # "crm-smart-monitoring-system"
|
|
285
|
+
short_id = parts[1] if len(parts) > 1 else "" # "f59a41a96c52"
|
|
286
|
+
|
|
287
|
+
# Match full slugId, slug part, or short ID
|
|
288
|
+
if (
|
|
289
|
+
slug_id.lower() == project_lower
|
|
290
|
+
or slug_part.lower() == project_lower
|
|
291
|
+
or short_id.lower() == project_lower
|
|
292
|
+
):
|
|
293
|
+
return project["id"]
|
|
294
|
+
|
|
295
|
+
# Also check exact name match (case-insensitive)
|
|
296
|
+
if project["name"].lower() == project_lower:
|
|
297
|
+
return project["id"]
|
|
298
|
+
|
|
299
|
+
# No match found
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
raise ValueError(f"Failed to resolve project '{project_identifier}': {e}")
|
|
304
|
+
|
|
197
305
|
async def _load_workflow_states(self, team_id: str) -> None:
|
|
198
306
|
"""Load and cache workflow states for the team.
|
|
199
307
|
|
|
@@ -443,6 +551,20 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
443
551
|
# Remove labelIds if no labels resolved
|
|
444
552
|
issue_input.pop("labelIds", None)
|
|
445
553
|
|
|
554
|
+
# Resolve project ID if parent_epic is provided (supports slug, name, short ID, or URL)
|
|
555
|
+
if task.parent_epic:
|
|
556
|
+
project_id = await self._resolve_project_id(task.parent_epic)
|
|
557
|
+
if project_id:
|
|
558
|
+
issue_input["projectId"] = project_id
|
|
559
|
+
else:
|
|
560
|
+
# Log warning but don't fail - user may have provided invalid project
|
|
561
|
+
logging.getLogger(__name__).warning(
|
|
562
|
+
f"Could not resolve project identifier '{task.parent_epic}' to UUID. "
|
|
563
|
+
"Issue will be created without project assignment."
|
|
564
|
+
)
|
|
565
|
+
# Remove projectId if we couldn't resolve it
|
|
566
|
+
issue_input.pop("projectId", None)
|
|
567
|
+
|
|
446
568
|
try:
|
|
447
569
|
result = await self.client.execute_mutation(
|
|
448
570
|
CREATE_ISSUE_MUTATION, {"input": issue_input}
|
|
@@ -85,6 +85,11 @@ def create_auggie_server_config(
|
|
|
85
85
|
Auggie MCP server configuration dict
|
|
86
86
|
|
|
87
87
|
"""
|
|
88
|
+
# Get mcp-ticketer CLI command from same directory as Python
|
|
89
|
+
from pathlib import Path
|
|
90
|
+
|
|
91
|
+
python_dir = Path(python_path).parent
|
|
92
|
+
mcp_ticketer_cmd = str(python_dir / "mcp-ticketer")
|
|
88
93
|
# Get adapter configuration
|
|
89
94
|
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
90
95
|
adapters_config = project_config.get("adapters", {})
|
|
@@ -134,14 +139,14 @@ def create_auggie_server_config(
|
|
|
134
139
|
if "project_key" in adapter_config:
|
|
135
140
|
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
136
141
|
|
|
137
|
-
# Use
|
|
138
|
-
args = ["
|
|
142
|
+
# Use CLI command: mcp-ticketer mcp
|
|
143
|
+
args = ["mcp"]
|
|
139
144
|
if project_path:
|
|
140
145
|
args.append(project_path)
|
|
141
146
|
|
|
142
147
|
# Create server configuration (simpler than Gemini - no timeout/trust)
|
|
143
148
|
config = {
|
|
144
|
-
"command":
|
|
149
|
+
"command": mcp_ticketer_cmd,
|
|
145
150
|
"args": args,
|
|
146
151
|
"env": env_vars,
|
|
147
152
|
}
|
|
@@ -298,7 +303,7 @@ def configure_auggie_mcp(force: bool = False) -> None:
|
|
|
298
303
|
console.print(" Server name: mcp-ticketer")
|
|
299
304
|
console.print(f" Adapter: {adapter}")
|
|
300
305
|
console.print(f" Python: {python_path}")
|
|
301
|
-
console.print(" Command:
|
|
306
|
+
console.print(" Command: mcp-ticketer mcp")
|
|
302
307
|
console.print(" Scope: Global (affects all projects)")
|
|
303
308
|
console.print(f" Project path: {project_path}")
|
|
304
309
|
if "env" in server_config:
|
|
@@ -4,11 +4,11 @@ Codex CLI only supports global configuration at ~/.codex/config.toml.
|
|
|
4
4
|
Unlike Claude Code and Gemini CLI, there is no project-level configuration support.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import tomllib
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from typing import Any
|
|
10
9
|
|
|
11
10
|
import tomli_w
|
|
11
|
+
import tomllib
|
|
12
12
|
from rich.console import Console
|
|
13
13
|
|
|
14
14
|
from .mcp_configure import load_project_config
|
|
@@ -87,6 +87,10 @@ def create_codex_server_config(
|
|
|
87
87
|
Codex MCP server configuration dict
|
|
88
88
|
|
|
89
89
|
"""
|
|
90
|
+
# Get mcp-ticketer CLI command from same directory as Python
|
|
91
|
+
python_dir = Path(python_path).parent
|
|
92
|
+
mcp_ticketer_cmd = str(python_dir / "mcp-ticketer")
|
|
93
|
+
|
|
90
94
|
# Get adapter configuration
|
|
91
95
|
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
92
96
|
adapters_config = project_config.get("adapters", {})
|
|
@@ -136,14 +140,14 @@ def create_codex_server_config(
|
|
|
136
140
|
if "project_key" in adapter_config:
|
|
137
141
|
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
138
142
|
|
|
139
|
-
# Use
|
|
140
|
-
args = ["
|
|
143
|
+
# Use CLI command: mcp-ticketer mcp
|
|
144
|
+
args = ["mcp"]
|
|
141
145
|
if project_path:
|
|
142
146
|
args.append(project_path)
|
|
143
147
|
|
|
144
148
|
# Create server configuration with Codex-specific structure
|
|
145
149
|
config: dict[str, Any] = {
|
|
146
|
-
"command":
|
|
150
|
+
"command": mcp_ticketer_cmd,
|
|
147
151
|
"args": args,
|
|
148
152
|
"env": env_vars,
|
|
149
153
|
}
|
|
@@ -307,7 +311,7 @@ def configure_codex_mcp(force: bool = False) -> None:
|
|
|
307
311
|
console.print(" Server name: mcp-ticketer")
|
|
308
312
|
console.print(f" Adapter: {adapter}")
|
|
309
313
|
console.print(f" Python: {python_path}")
|
|
310
|
-
console.print(" Command:
|
|
314
|
+
console.print(" Command: mcp-ticketer mcp")
|
|
311
315
|
console.print(" Scope: global (Codex only supports global config)")
|
|
312
316
|
console.print(f" Project path: {project_path}")
|
|
313
317
|
if "env" in server_config:
|
|
@@ -87,6 +87,10 @@ def create_gemini_server_config(
|
|
|
87
87
|
Gemini MCP server configuration dict
|
|
88
88
|
|
|
89
89
|
"""
|
|
90
|
+
# Get mcp-ticketer CLI command from same directory as Python
|
|
91
|
+
python_dir = Path(python_path).parent
|
|
92
|
+
mcp_ticketer_cmd = str(python_dir / "mcp-ticketer")
|
|
93
|
+
|
|
90
94
|
# Get adapter configuration
|
|
91
95
|
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
92
96
|
adapters_config = project_config.get("adapters", {})
|
|
@@ -136,14 +140,14 @@ def create_gemini_server_config(
|
|
|
136
140
|
if "project_key" in adapter_config:
|
|
137
141
|
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
138
142
|
|
|
139
|
-
# Use
|
|
140
|
-
args = ["
|
|
143
|
+
# Use CLI command: mcp-ticketer mcp
|
|
144
|
+
args = ["mcp"]
|
|
141
145
|
if project_path:
|
|
142
146
|
args.append(project_path)
|
|
143
147
|
|
|
144
148
|
# Create server configuration with Gemini-specific options
|
|
145
149
|
config = {
|
|
146
|
-
"command":
|
|
150
|
+
"command": mcp_ticketer_cmd,
|
|
147
151
|
"args": args,
|
|
148
152
|
"env": env_vars,
|
|
149
153
|
"timeout": 15000, # 15 seconds timeout
|
|
@@ -300,7 +304,7 @@ def configure_gemini_mcp(
|
|
|
300
304
|
console.print(" Server name: mcp-ticketer")
|
|
301
305
|
console.print(f" Adapter: {adapter}")
|
|
302
306
|
console.print(f" Python: {python_path}")
|
|
303
|
-
console.print(" Command:
|
|
307
|
+
console.print(" Command: mcp-ticketer mcp")
|
|
304
308
|
console.print(f" Timeout: {server_config['timeout']}ms")
|
|
305
309
|
console.print(f" Trust: {server_config['trust']}")
|
|
306
310
|
if project_path:
|
mcp_ticketer/cli/main.py
CHANGED
|
@@ -575,7 +575,7 @@ def init(
|
|
|
575
575
|
)
|
|
576
576
|
|
|
577
577
|
# First try our improved .env configuration loader
|
|
578
|
-
from ..mcp.server import _load_env_configuration
|
|
578
|
+
from ..mcp.server.main import _load_env_configuration
|
|
579
579
|
|
|
580
580
|
env_config = _load_env_configuration()
|
|
581
581
|
|
|
@@ -1868,9 +1868,29 @@ mcp_app = typer.Typer(
|
|
|
1868
1868
|
name="mcp",
|
|
1869
1869
|
help="Configure MCP integration for AI clients (Claude, Gemini, Codex, Auggie)",
|
|
1870
1870
|
add_completion=False,
|
|
1871
|
+
invoke_without_command=True,
|
|
1871
1872
|
)
|
|
1872
1873
|
|
|
1873
1874
|
|
|
1875
|
+
@mcp_app.callback()
|
|
1876
|
+
def mcp_callback(
|
|
1877
|
+
ctx: typer.Context,
|
|
1878
|
+
project_path: str | None = typer.Argument(
|
|
1879
|
+
None, help="Project directory path (optional - uses cwd if not provided)"
|
|
1880
|
+
),
|
|
1881
|
+
):
|
|
1882
|
+
"""MCP command group - runs MCP server if no subcommand provided."""
|
|
1883
|
+
if ctx.invoked_subcommand is None:
|
|
1884
|
+
# No subcommand provided, run the serve command
|
|
1885
|
+
# Change to project directory if provided
|
|
1886
|
+
if project_path:
|
|
1887
|
+
import os
|
|
1888
|
+
|
|
1889
|
+
os.chdir(project_path)
|
|
1890
|
+
# Invoke the serve command through context
|
|
1891
|
+
ctx.invoke(mcp_serve, adapter=None, base_path=None)
|
|
1892
|
+
|
|
1893
|
+
|
|
1874
1894
|
@app.command()
|
|
1875
1895
|
def install(
|
|
1876
1896
|
platform: str | None = typer.Argument(
|
|
@@ -2210,8 +2230,8 @@ def mcp_serve(
|
|
|
2210
2230
|
2. Global: ~/.mcp-ticketer/config.json
|
|
2211
2231
|
3. Default: aitrackdown adapter with .aitrackdown base path
|
|
2212
2232
|
"""
|
|
2213
|
-
from ..mcp.server_sdk import configure_adapter
|
|
2214
|
-
from ..mcp.server_sdk import main as sdk_main
|
|
2233
|
+
from ..mcp.server.server_sdk import configure_adapter
|
|
2234
|
+
from ..mcp.server.server_sdk import main as sdk_main
|
|
2215
2235
|
|
|
2216
2236
|
# Load configuration (respects project-specific config in cwd)
|
|
2217
2237
|
config = load_config()
|
|
@@ -2225,7 +2245,7 @@ def mcp_serve(
|
|
|
2225
2245
|
adapter_config = adapters_config.get(adapter_type, {})
|
|
2226
2246
|
else:
|
|
2227
2247
|
# Priority 2: .env files
|
|
2228
|
-
from ..mcp.server import _load_env_configuration
|
|
2248
|
+
from ..mcp.server.main import _load_env_configuration
|
|
2229
2249
|
|
|
2230
2250
|
env_config = _load_env_configuration()
|
|
2231
2251
|
if env_config:
|
|
@@ -161,12 +161,12 @@ def create_mcp_server_config(
|
|
|
161
161
|
MCP server configuration dict matching Claude Code stdio pattern
|
|
162
162
|
|
|
163
163
|
"""
|
|
164
|
-
#
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
# Get mcp-ticketer CLI command from same directory as Python
|
|
165
|
+
python_dir = Path(python_path).parent
|
|
166
|
+
mcp_ticketer_cmd = str(python_dir / "mcp-ticketer")
|
|
167
167
|
|
|
168
|
-
# Use
|
|
169
|
-
args = ["
|
|
168
|
+
# Use CLI command: mcp-ticketer mcp
|
|
169
|
+
args = ["mcp"]
|
|
170
170
|
|
|
171
171
|
# Add project path if provided
|
|
172
172
|
if project_path:
|
|
@@ -175,7 +175,7 @@ def create_mcp_server_config(
|
|
|
175
175
|
# REQUIRED: Add "type": "stdio" for Claude Code compatibility
|
|
176
176
|
config = {
|
|
177
177
|
"type": "stdio",
|
|
178
|
-
"command":
|
|
178
|
+
"command": mcp_ticketer_cmd,
|
|
179
179
|
"args": args,
|
|
180
180
|
}
|
|
181
181
|
|
|
@@ -385,7 +385,7 @@ def configure_claude_mcp(global_config: bool = False, force: bool = False) -> No
|
|
|
385
385
|
console.print(" Server name: mcp-ticketer")
|
|
386
386
|
console.print(f" Adapter: {adapter}")
|
|
387
387
|
console.print(f" Python: {python_path}")
|
|
388
|
-
console.print(" Command:
|
|
388
|
+
console.print(" Command: mcp-ticketer mcp")
|
|
389
389
|
if project_path:
|
|
390
390
|
console.print(f" Project path: {project_path}")
|
|
391
391
|
if "env" in server_config:
|
mcp_ticketer/mcp/__init__.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
|
-
from .server import MCPTicketServer
|
|
6
|
+
from .server.main import MCPTicketServer
|
|
7
7
|
|
|
8
8
|
__all__ = ["MCPTicketServer"]
|
|
9
9
|
|
|
@@ -23,11 +23,11 @@ def __getattr__(name: str):
|
|
|
23
23
|
This prevents the RuntimeWarning when running:
|
|
24
24
|
python -m mcp_ticketer.mcp.server
|
|
25
25
|
|
|
26
|
-
The warning occurred because __init__.py imported server
|
|
26
|
+
The warning occurred because __init__.py imported server before
|
|
27
27
|
runpy could execute it as __main__.
|
|
28
28
|
"""
|
|
29
29
|
if name == "MCPTicketServer":
|
|
30
|
-
from .server import MCPTicketServer
|
|
30
|
+
from .server.main import MCPTicketServer
|
|
31
31
|
|
|
32
32
|
return MCPTicketServer
|
|
33
33
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""MCP Server package for mcp-ticketer.
|
|
2
|
+
|
|
3
|
+
This package provides the FastMCP server implementation for ticket management
|
|
4
|
+
operations via the Model Context Protocol (MCP).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .main import main
|
|
11
|
+
|
|
12
|
+
__all__ = ["main"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def __getattr__(name: str):
|
|
16
|
+
"""Lazy import to avoid premature module loading."""
|
|
17
|
+
if name == "main":
|
|
18
|
+
from .main import main
|
|
19
|
+
|
|
20
|
+
return main
|
|
21
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Main entry point for MCP server module invocation.
|
|
2
|
+
|
|
3
|
+
This module enables running the MCP server via:
|
|
4
|
+
python -m mcp_ticketer.mcp.server [project_path]
|
|
5
|
+
|
|
6
|
+
This is the preferred invocation method for MCP configurations as it:
|
|
7
|
+
- Works reliably across installation methods (pipx, pip, uv)
|
|
8
|
+
- Doesn't depend on binary path detection
|
|
9
|
+
- Follows the proven mcp-vector-search pattern
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from .main import main
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run_server() -> None:
|
|
20
|
+
"""Run the MCP server with optional project path argument.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
python -m mcp_ticketer.mcp.server
|
|
24
|
+
python -m mcp_ticketer.mcp.server /path/to/project
|
|
25
|
+
|
|
26
|
+
Arguments:
|
|
27
|
+
project_path (optional): Path to project directory for context
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
# Check for project path argument
|
|
31
|
+
if len(sys.argv) > 1:
|
|
32
|
+
project_path = Path(sys.argv[1])
|
|
33
|
+
|
|
34
|
+
# Validate project path exists
|
|
35
|
+
if not project_path.exists():
|
|
36
|
+
sys.stderr.write(f"Error: Project path does not exist: {project_path}\n")
|
|
37
|
+
sys.exit(1)
|
|
38
|
+
|
|
39
|
+
# Change to project directory for context
|
|
40
|
+
try:
|
|
41
|
+
import os
|
|
42
|
+
|
|
43
|
+
os.chdir(project_path)
|
|
44
|
+
sys.stderr.write(f"[MCP Server] Working directory: {project_path}\n")
|
|
45
|
+
except OSError as e:
|
|
46
|
+
sys.stderr.write(f"Warning: Could not change to project directory: {e}\n")
|
|
47
|
+
|
|
48
|
+
# Run the async main function
|
|
49
|
+
try:
|
|
50
|
+
asyncio.run(main())
|
|
51
|
+
except KeyboardInterrupt:
|
|
52
|
+
sys.stderr.write("\n[MCP Server] Interrupted by user\n")
|
|
53
|
+
sys.exit(0)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
sys.stderr.write(f"[MCP Server] Fatal error: {e}\n")
|
|
56
|
+
sys.exit(1)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
run_server()
|
|
@@ -11,8 +11,8 @@ from dotenv import load_dotenv
|
|
|
11
11
|
# Import adapters module to trigger registration
|
|
12
12
|
import mcp_ticketer.adapters # noqa: F401
|
|
13
13
|
|
|
14
|
-
from
|
|
15
|
-
from
|
|
14
|
+
from ...core import AdapterRegistry
|
|
15
|
+
from ...core.models import Comment, Epic, Priority, SearchQuery, Task, TicketState
|
|
16
16
|
from .constants import (
|
|
17
17
|
DEFAULT_BASE_PATH,
|
|
18
18
|
DEFAULT_LIMIT,
|
|
@@ -14,8 +14,8 @@ from typing import Any
|
|
|
14
14
|
|
|
15
15
|
from mcp.server.fastmcp import FastMCP
|
|
16
16
|
|
|
17
|
-
from
|
|
18
|
-
from
|
|
17
|
+
from ...core.adapter import BaseAdapter
|
|
18
|
+
from ...core.registry import AdapterRegistry
|
|
19
19
|
|
|
20
20
|
# Initialize FastMCP server
|
|
21
21
|
mcp = FastMCP("mcp-ticketer")
|
|
@@ -9,7 +9,7 @@ This module implements tools for managing the three-level ticket hierarchy:
|
|
|
9
9
|
from datetime import datetime
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from ....core.models import Epic, Priority, Task, TicketType
|
|
13
13
|
from ..server_sdk import get_adapter, mcp
|
|
14
14
|
|
|
15
15
|
|
mcp_ticketer/queue/__init__.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"""Async queue system for mcp-ticketer."""
|
|
2
2
|
|
|
3
|
-
from .queue import Queue, QueueItem, QueueStatus
|
|
4
|
-
from .worker import Worker
|
|
5
|
-
|
|
6
3
|
# Import manager last to avoid circular import
|
|
7
4
|
from .manager import WorkerManager
|
|
5
|
+
from .queue import Queue, QueueItem, QueueStatus
|
|
6
|
+
from .worker import Worker
|
|
8
7
|
|
|
9
8
|
__all__ = ["Queue", "QueueItem", "QueueStatus", "Worker", "WorkerManager"]
|
mcp_ticketer/queue/manager.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-ticketer
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.9
|
|
4
4
|
Summary: Universal ticket management interface for AI agents with MCP support
|
|
5
5
|
Author-email: MCP Ticketer Team <support@mcp-ticketer.io>
|
|
6
6
|
Maintainer-email: MCP Ticketer Team <support@mcp-ticketer.io>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
mcp_ticketer/__init__.py,sha256=Xx4WaprO5PXhVPbYi1L6tBmwmJMkYS-lMyG4ieN6QP0,717
|
|
2
|
-
mcp_ticketer/__version__.py,sha256=
|
|
2
|
+
mcp_ticketer/__version__.py,sha256=TgErXAgObQmg6Q5ACy63d_W9qLAaFynoPaauS16SAi0,1117
|
|
3
3
|
mcp_ticketer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
mcp_ticketer/adapters/__init__.py,sha256=B5DFllWn23hkhmrLykNO5uMMSdcFuuPHXyLw_jyFzuE,358
|
|
5
|
-
mcp_ticketer/adapters/aitrackdown.py,sha256=
|
|
5
|
+
mcp_ticketer/adapters/aitrackdown.py,sha256=cm8L7amB64ZCoGvWbnpH9zxZlXu6t-l8TWiYVSwDhlg,30320
|
|
6
6
|
mcp_ticketer/adapters/github.py,sha256=QeZefKs204g2nXZ9yDb3j-HwrufbXBPoXB0zLp6bvW0,47338
|
|
7
7
|
mcp_ticketer/adapters/hybrid.py,sha256=7ocRjK7N7FdXSUCeFc23jFevfVwcPvHPIsEPXV_4o1w,18997
|
|
8
8
|
mcp_ticketer/adapters/jira.py,sha256=9OtYAQfUdUQqEYjs61jzYpVrHu23hyP22mm-Bfn5KqA,35204
|
|
9
9
|
mcp_ticketer/adapters/linear.py,sha256=trm6ZhmlUl80sj51WAPAox_R2HQZXZ-h1QXJsrFYDCQ,587
|
|
10
10
|
mcp_ticketer/adapters/linear/__init__.py,sha256=6l0ZoR6ZHSRcytLfps2AZuk5R189Pq1GfR5-YDQt8-Q,731
|
|
11
|
-
mcp_ticketer/adapters/linear/adapter.py,sha256=
|
|
11
|
+
mcp_ticketer/adapters/linear/adapter.py,sha256=oQw_KgZWSdktfUhYEAonHUfK8z4CaiP18TC9pZqYv5Q,36251
|
|
12
12
|
mcp_ticketer/adapters/linear/client.py,sha256=0UmWlSEcRiwnSMFYKL89KMrPPL8S8uZ5V6rIY_KFOQU,8803
|
|
13
13
|
mcp_ticketer/adapters/linear/mappers.py,sha256=GN1X7bOcU-5dhDW3dAtSEGivinhFBc8hoKYot8c5tCo,9631
|
|
14
14
|
mcp_ticketer/adapters/linear/queries.py,sha256=K8y7xc3iH-q9LEUmg-0YDBhh546LAwLZDvVLkzx3yY4,7223
|
|
@@ -17,15 +17,15 @@ mcp_ticketer/cache/__init__.py,sha256=Xcd-cKnt-Cx7jBzvfzUUUPaGkmyXFi5XUFWw3Z4b7d
|
|
|
17
17
|
mcp_ticketer/cache/memory.py,sha256=rWphWZy7XTbHezC7HMRQN9ISUhYo0Pc2OTgLG30vHnI,5047
|
|
18
18
|
mcp_ticketer/cli/__init__.py,sha256=l9Q8iKmfGkTu0cssHBVqNZTsL4eAtFzOB25AED_0G6g,89
|
|
19
19
|
mcp_ticketer/cli/adapter_diagnostics.py,sha256=pQDdtDgBwSW04wdFEPVzwbul3KgfB9g6ZMS85qpYulY,14988
|
|
20
|
-
mcp_ticketer/cli/auggie_configure.py,sha256=
|
|
21
|
-
mcp_ticketer/cli/codex_configure.py,sha256=
|
|
20
|
+
mcp_ticketer/cli/auggie_configure.py,sha256=47Xay__bMDGmNPWR6u9zxk4IVsSlEB02xVI40v8dhkQ,11825
|
|
21
|
+
mcp_ticketer/cli/codex_configure.py,sha256=EYgyEPoAWoc0-vLLiUSEEFkDMl0K-eOQx6p_R_iqhWI,12344
|
|
22
22
|
mcp_ticketer/cli/configure.py,sha256=T4LczvZIc2FZM-joqICL8NpjCeThAUknEhJkHsmpdc8,16158
|
|
23
23
|
mcp_ticketer/cli/diagnostics.py,sha256=s7P4vDzPpthgiBJfAOXpunlhZ62buHg_BA5W7ghQBqg,29952
|
|
24
24
|
mcp_ticketer/cli/discover.py,sha256=paG8zElIFEK6lgVD-tHWoFDTuWQ185LirFp0fVlYgB0,13148
|
|
25
|
-
mcp_ticketer/cli/gemini_configure.py,sha256=
|
|
25
|
+
mcp_ticketer/cli/gemini_configure.py,sha256=6Czg-gJA_HrzMopKCvCLfRZeqkdR77OLV5W7W4ZG9hw,12739
|
|
26
26
|
mcp_ticketer/cli/linear_commands.py,sha256=YnmqRacAfTdF0CLr4BXOxFs3uwm_hVifbGdTe6t2CfA,17273
|
|
27
|
-
mcp_ticketer/cli/main.py,sha256=
|
|
28
|
-
mcp_ticketer/cli/mcp_configure.py,sha256=
|
|
27
|
+
mcp_ticketer/cli/main.py,sha256=W36hKxZZKoojjBUYl5U5MlXLaw-qX9i0nPrmvtsndtI,89926
|
|
28
|
+
mcp_ticketer/cli/mcp_configure.py,sha256=SfjA97OJAkYUfi9Mn-wqEt6p0xsgz_QZlATwxM_vjTo,14089
|
|
29
29
|
mcp_ticketer/cli/migrate_config.py,sha256=MYsr_C5ZxsGg0P13etWTWNrJ_lc6ElRCkzfQADYr3DM,5956
|
|
30
30
|
mcp_ticketer/cli/platform_commands.py,sha256=pTLRT2wot8dAmy1-roJWWOT0Cxu7j-06BlWDnZ9a4jY,3624
|
|
31
31
|
mcp_ticketer/cli/python_detection.py,sha256=qmhi0CIDKH_AUVGkJ9jyY1zBpx1cwiQNv0vnEvMYDFQ,4272
|
|
@@ -44,32 +44,34 @@ mcp_ticketer/core/mappers.py,sha256=okte6EV_OuPvnM1KXHUcfrpPd7TWnKh44X3_W3HxwiI,
|
|
|
44
44
|
mcp_ticketer/core/models.py,sha256=ABKdyAkEkKtMF_d6D8_qRL-2ujz1DshemHSyqTPUthA,14448
|
|
45
45
|
mcp_ticketer/core/project_config.py,sha256=DmLekuMuOgNtzg-olOU4Utv00DdCH1-CXuoooA-adMs,23609
|
|
46
46
|
mcp_ticketer/core/registry.py,sha256=gBeXcZ3grHl9gYFbyRp-C4IM7SD_KGTeXT_1jG8XrCc,3470
|
|
47
|
-
mcp_ticketer/mcp/__init__.py,sha256=
|
|
47
|
+
mcp_ticketer/mcp/__init__.py,sha256=XscFBOFeIxBNgA_8yPLl6c75-YJCcIN2I9eoLEGsBdM,864
|
|
48
48
|
mcp_ticketer/mcp/__main__.py,sha256=Fo_5KJOFako2gi1Z1kk5zEt2sGJW6BX6oXlYp7twYTs,1713
|
|
49
|
-
mcp_ticketer/mcp/
|
|
50
|
-
mcp_ticketer/mcp/
|
|
51
|
-
mcp_ticketer/mcp/
|
|
52
|
-
mcp_ticketer/mcp/server.py,sha256=
|
|
53
|
-
mcp_ticketer/mcp/
|
|
54
|
-
mcp_ticketer/mcp/
|
|
55
|
-
mcp_ticketer/mcp/
|
|
56
|
-
mcp_ticketer/mcp/tools/
|
|
57
|
-
mcp_ticketer/mcp/tools/
|
|
58
|
-
mcp_ticketer/mcp/tools/
|
|
59
|
-
mcp_ticketer/mcp/tools/
|
|
60
|
-
mcp_ticketer/mcp/tools/
|
|
61
|
-
mcp_ticketer/mcp/tools/
|
|
62
|
-
mcp_ticketer/
|
|
49
|
+
mcp_ticketer/mcp/server/__init__.py,sha256=2ll9kLEEiKP_NtvJghHVz6TG2p7xwIUBRqlxZKWRCvU,509
|
|
50
|
+
mcp_ticketer/mcp/server/__main__.py,sha256=xE1n94M5n2tKyT6qFIOXaqRXX7L--SxmCglKUPcljG0,1711
|
|
51
|
+
mcp_ticketer/mcp/server/constants.py,sha256=EBGsJtBPaTCvAm5rOMknckrXActrNIls7lRklnh1L4s,2072
|
|
52
|
+
mcp_ticketer/mcp/server/dto.py,sha256=FR_OBtaxrno8AsHynPwUUW715iAoaBkrr7Ud8HZTQW8,7233
|
|
53
|
+
mcp_ticketer/mcp/server/main.py,sha256=ddYl7u2vUwnuPRg-QP0sZS_Utils9nx-PEdxcIvqfy4,48834
|
|
54
|
+
mcp_ticketer/mcp/server/response_builder.py,sha256=DUfe1e0CcXPlepLq-cGH6b_THqoZEynYfVKkZEeLe0M,4933
|
|
55
|
+
mcp_ticketer/mcp/server/server_sdk.py,sha256=KGpMvvJAckKl5ReLsyYvNJCM44nZRgY-V7dkgENTFX0,2554
|
|
56
|
+
mcp_ticketer/mcp/server/tools/__init__.py,sha256=6miiC2Cru8u2TCrm9RYF1jxd7vu9SI7BPLUjtzwOxT8,1056
|
|
57
|
+
mcp_ticketer/mcp/server/tools/attachment_tools.py,sha256=c2ySBs0Zz1M_zbFszbsD0S8lJT8dnCzTy-mSsKEDPV0,5674
|
|
58
|
+
mcp_ticketer/mcp/server/tools/bulk_tools.py,sha256=H5RZylDsrYuTCDF1afUcVcm-Yv3cu052JwwBPW9-4YA,9200
|
|
59
|
+
mcp_ticketer/mcp/server/tools/comment_tools.py,sha256=XXPS4TCIDaxnoITdG4B38iOX9LOElqCZFus4FnULJeg,2702
|
|
60
|
+
mcp_ticketer/mcp/server/tools/hierarchy_tools.py,sha256=KcsZ4NAd00vOm-PZziAtAanOX8CB7baGkWNoIKDIako,10502
|
|
61
|
+
mcp_ticketer/mcp/server/tools/pr_tools.py,sha256=PoB5YABYIlrABw5-RPA8bTh8uHH3hituslV9ib9xUUU,4540
|
|
62
|
+
mcp_ticketer/mcp/server/tools/search_tools.py,sha256=60OwDXN9bLQKbe9apLmJDyM0TcTPCAv9Vp2X2gPBQX4,6952
|
|
63
|
+
mcp_ticketer/mcp/server/tools/ticket_tools.py,sha256=DrNxx8gKvmFOKpxR80hcmtXK1tZOmemBgAABlPAPl6E,8122
|
|
64
|
+
mcp_ticketer/queue/__init__.py,sha256=ut4EkrXng9RJlFPZRKUa3elhHo3MFGhshBXquZ16vcs,278
|
|
63
65
|
mcp_ticketer/queue/__main__.py,sha256=gc_tE9NUdK07OJfTZuD4t6KeBD_vxFQIhknGTQUG_jk,109
|
|
64
66
|
mcp_ticketer/queue/health_monitor.py,sha256=TDmPnYuZJb3yHNJlGFvE9UU-LfsKTrC4Vapyvdb3fso,12226
|
|
65
|
-
mcp_ticketer/queue/manager.py,sha256=
|
|
67
|
+
mcp_ticketer/queue/manager.py,sha256=dswFp3e0z-1hUIzoJ08raNxwJeX9_IYYu5WbHnlfZzQ,10712
|
|
66
68
|
mcp_ticketer/queue/queue.py,sha256=q9HDXgnlwspamMJIeu9og7qONttXHmFZHPSaMtJDPlw,17923
|
|
67
69
|
mcp_ticketer/queue/run_worker.py,sha256=WhoeamL8LKZ66TM8W1PkMPwjF2w_EDFMP-mevs6C1TM,1019
|
|
68
70
|
mcp_ticketer/queue/ticket_registry.py,sha256=xVg3i7Eb5rtQY-4bbw3zYY1h-C6jF1t1NZEGhObzD8g,15491
|
|
69
71
|
mcp_ticketer/queue/worker.py,sha256=AJHtpJZEhGoPuCDPXSMsn9DeODelo5f__0C__3zoN08,20970
|
|
70
|
-
mcp_ticketer-0.4.
|
|
71
|
-
mcp_ticketer-0.4.
|
|
72
|
-
mcp_ticketer-0.4.
|
|
73
|
-
mcp_ticketer-0.4.
|
|
74
|
-
mcp_ticketer-0.4.
|
|
75
|
-
mcp_ticketer-0.4.
|
|
72
|
+
mcp_ticketer-0.4.9.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
|
|
73
|
+
mcp_ticketer-0.4.9.dist-info/METADATA,sha256=_nIoXUxM3iodpZW31n9HYkeS_LTOd0GNKwZ36k88h3w,16020
|
|
74
|
+
mcp_ticketer-0.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
75
|
+
mcp_ticketer-0.4.9.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
|
|
76
|
+
mcp_ticketer-0.4.9.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
|
|
77
|
+
mcp_ticketer-0.4.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|