mcp-ticketer 0.4.4__py3-none-any.whl → 0.4.8__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/__init__.py +3 -12
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/adapters/aitrackdown.py +2 -9
- mcp_ticketer/adapters/github.py +2 -1
- mcp_ticketer/adapters/jira.py +2 -1
- mcp_ticketer/adapters/linear/adapter.py +28 -25
- mcp_ticketer/adapters/linear/client.py +2 -1
- mcp_ticketer/adapters/linear/mappers.py +2 -1
- mcp_ticketer/cli/adapter_diagnostics.py +4 -2
- mcp_ticketer/cli/auggie_configure.py +35 -15
- mcp_ticketer/cli/codex_configure.py +38 -31
- mcp_ticketer/cli/configure.py +3 -9
- mcp_ticketer/cli/discover.py +2 -6
- mcp_ticketer/cli/gemini_configure.py +38 -25
- mcp_ticketer/cli/main.py +4 -3
- mcp_ticketer/cli/mcp_configure.py +115 -78
- mcp_ticketer/cli/python_detection.py +126 -0
- mcp_ticketer/core/__init__.py +2 -1
- mcp_ticketer/mcp/__init__.py +29 -1
- mcp_ticketer/mcp/__main__.py +60 -0
- mcp_ticketer/mcp/server/__init__.py +21 -0
- mcp_ticketer/mcp/server/__main__.py +60 -0
- mcp_ticketer/mcp/{server.py → server/main.py} +15 -35
- mcp_ticketer/mcp/{tools → server/tools}/__init__.py +7 -9
- mcp_ticketer/queue/__init__.py +1 -0
- mcp_ticketer/queue/manager.py +10 -46
- mcp_ticketer/queue/ticket_registry.py +5 -5
- {mcp_ticketer-0.4.4.dist-info → mcp_ticketer-0.4.8.dist-info}/METADATA +13 -4
- mcp_ticketer-0.4.8.dist-info/RECORD +77 -0
- mcp_ticketer-0.4.4.dist-info/RECORD +0 -73
- /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/{server_sdk.py → server/server_sdk.py} +0 -0
- /mcp_ticketer/mcp/{tools → server/tools}/attachment_tools.py +0 -0
- /mcp_ticketer/mcp/{tools → server/tools}/bulk_tools.py +0 -0
- /mcp_ticketer/mcp/{tools → server/tools}/comment_tools.py +0 -0
- /mcp_ticketer/mcp/{tools → server/tools}/hierarchy_tools.py +0 -0
- /mcp_ticketer/mcp/{tools → server/tools}/pr_tools.py +0 -0
- /mcp_ticketer/mcp/{tools → server/tools}/search_tools.py +0 -0
- /mcp_ticketer/mcp/{tools → server/tools}/ticket_tools.py +0 -0
- {mcp_ticketer-0.4.4.dist-info → mcp_ticketer-0.4.8.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.4.4.dist-info → mcp_ticketer-0.4.8.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.4.4.dist-info → mcp_ticketer-0.4.8.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.4.4.dist-info → mcp_ticketer-0.4.8.dist-info}/top_level.txt +0 -0
mcp_ticketer/__init__.py
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
"""MCP Ticketer - Universal ticket management interface."""
|
|
2
2
|
|
|
3
|
-
from .__version__ import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
__copyright__,
|
|
7
|
-
__description__,
|
|
8
|
-
__license__,
|
|
9
|
-
__title__,
|
|
10
|
-
__version__,
|
|
11
|
-
__version_info__,
|
|
12
|
-
get_user_agent,
|
|
13
|
-
get_version,
|
|
14
|
-
)
|
|
3
|
+
from .__version__ import (__author__, __author_email__, __copyright__,
|
|
4
|
+
__description__, __license__, __title__, __version__,
|
|
5
|
+
__version_info__, get_user_agent, get_version)
|
|
15
6
|
|
|
16
7
|
__all__ = [
|
|
17
8
|
"__version__",
|
mcp_ticketer/__version__.py
CHANGED
|
@@ -10,15 +10,8 @@ from typing import Any
|
|
|
10
10
|
from ..core.adapter import BaseAdapter
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
|
-
from ..core.models import (
|
|
14
|
-
|
|
15
|
-
Comment,
|
|
16
|
-
Epic,
|
|
17
|
-
Priority,
|
|
18
|
-
SearchQuery,
|
|
19
|
-
Task,
|
|
20
|
-
TicketState,
|
|
21
|
-
)
|
|
13
|
+
from ..core.models import (Attachment, Comment, Epic, Priority, SearchQuery,
|
|
14
|
+
Task, TicketState)
|
|
22
15
|
from ..core.registry import AdapterRegistry
|
|
23
16
|
|
|
24
17
|
# Import ai-trackdown-pytools when available
|
mcp_ticketer/adapters/github.py
CHANGED
|
@@ -9,7 +9,8 @@ import httpx
|
|
|
9
9
|
|
|
10
10
|
from ..core.adapter import BaseAdapter
|
|
11
11
|
from ..core.env_loader import load_adapter_config, validate_adapter_config
|
|
12
|
-
from ..core.models import Comment, Epic, Priority, SearchQuery, Task,
|
|
12
|
+
from ..core.models import (Comment, Epic, Priority, SearchQuery, Task,
|
|
13
|
+
TicketState)
|
|
13
14
|
from ..core.registry import AdapterRegistry
|
|
14
15
|
|
|
15
16
|
|
mcp_ticketer/adapters/jira.py
CHANGED
|
@@ -13,7 +13,8 @@ from httpx import AsyncClient, HTTPStatusError, TimeoutException
|
|
|
13
13
|
|
|
14
14
|
from ..core.adapter import BaseAdapter
|
|
15
15
|
from ..core.env_loader import load_adapter_config, validate_adapter_config
|
|
16
|
-
from ..core.models import Comment, Epic, Priority, SearchQuery, Task,
|
|
16
|
+
from ..core.models import (Comment, Epic, Priority, SearchQuery, Task,
|
|
17
|
+
TicketState)
|
|
17
18
|
from ..core.registry import AdapterRegistry
|
|
18
19
|
|
|
19
20
|
logger = logging.getLogger(__name__)
|
|
@@ -20,27 +20,15 @@ from ...core.adapter import BaseAdapter
|
|
|
20
20
|
from ...core.models import Comment, Epic, SearchQuery, Task, TicketState
|
|
21
21
|
from ...core.registry import AdapterRegistry
|
|
22
22
|
from .client import LinearGraphQLClient
|
|
23
|
-
from .mappers import (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
from .
|
|
31
|
-
|
|
32
|
-
CREATE_ISSUE_MUTATION,
|
|
33
|
-
LIST_ISSUES_QUERY,
|
|
34
|
-
SEARCH_ISSUES_QUERY,
|
|
35
|
-
UPDATE_ISSUE_MUTATION,
|
|
36
|
-
WORKFLOW_STATES_QUERY,
|
|
37
|
-
)
|
|
38
|
-
from .types import (
|
|
39
|
-
LinearStateMapping,
|
|
40
|
-
build_issue_filter,
|
|
41
|
-
get_linear_priority,
|
|
42
|
-
get_linear_state_type,
|
|
43
|
-
)
|
|
23
|
+
from .mappers import (build_linear_issue_input,
|
|
24
|
+
build_linear_issue_update_input,
|
|
25
|
+
map_linear_comment_to_comment, map_linear_issue_to_task,
|
|
26
|
+
map_linear_project_to_epic)
|
|
27
|
+
from .queries import (ALL_FRAGMENTS, CREATE_ISSUE_MUTATION, LIST_ISSUES_QUERY,
|
|
28
|
+
SEARCH_ISSUES_QUERY, UPDATE_ISSUE_MUTATION,
|
|
29
|
+
WORKFLOW_STATES_QUERY)
|
|
30
|
+
from .types import (LinearStateMapping, build_issue_filter,
|
|
31
|
+
get_linear_priority, get_linear_state_type)
|
|
44
32
|
|
|
45
33
|
|
|
46
34
|
class LinearAdapter(BaseAdapter[Task]):
|
|
@@ -94,10 +82,25 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
94
82
|
"Linear API key is required (api_key or LINEAR_API_KEY env var)"
|
|
95
83
|
)
|
|
96
84
|
|
|
97
|
-
# Clean API key - remove
|
|
98
|
-
# (The client will add
|
|
99
|
-
if self.api_key
|
|
100
|
-
|
|
85
|
+
# Clean API key - remove common prefixes if accidentally included in config
|
|
86
|
+
# (The client will add Bearer back when making requests)
|
|
87
|
+
if isinstance(self.api_key, str):
|
|
88
|
+
# Remove Bearer prefix
|
|
89
|
+
if self.api_key.startswith("Bearer "):
|
|
90
|
+
self.api_key = self.api_key.replace("Bearer ", "")
|
|
91
|
+
# Remove environment variable name prefix (e.g., "LINEAR_API_KEY=")
|
|
92
|
+
if "=" in self.api_key:
|
|
93
|
+
parts = self.api_key.split("=", 1)
|
|
94
|
+
if len(parts) == 2 and parts[0].upper() in ("LINEAR_API_KEY", "API_KEY"):
|
|
95
|
+
self.api_key = parts[1]
|
|
96
|
+
|
|
97
|
+
# Validate API key format (Linear keys start with "lin_api_")
|
|
98
|
+
if not self.api_key.startswith("lin_api_"):
|
|
99
|
+
raise ValueError(
|
|
100
|
+
f"Invalid Linear API key format. Expected key starting with 'lin_api_', "
|
|
101
|
+
f"got: {self.api_key[:15]}... "
|
|
102
|
+
f"Please check your configuration and ensure the API key is correct."
|
|
103
|
+
)
|
|
101
104
|
|
|
102
105
|
self.workspace = config.get("workspace", "")
|
|
103
106
|
self.team_key = config.get("team_key")
|
|
@@ -16,7 +16,8 @@ except ImportError:
|
|
|
16
16
|
HTTPXAsyncTransport = None
|
|
17
17
|
TransportError = Exception
|
|
18
18
|
|
|
19
|
-
from ...core.exceptions import AdapterError, AuthenticationError,
|
|
19
|
+
from ...core.exceptions import (AdapterError, AuthenticationError,
|
|
20
|
+
RateLimitError)
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class LinearGraphQLClient:
|
|
@@ -6,7 +6,8 @@ from datetime import datetime
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
from ...core.models import Comment, Epic, Priority, Task, TicketState
|
|
9
|
-
from .types import extract_linear_metadata, get_universal_priority,
|
|
9
|
+
from .types import (extract_linear_metadata, get_universal_priority,
|
|
10
|
+
get_universal_state)
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def map_linear_issue_to_task(issue_data: dict[str, Any]) -> Task:
|
|
@@ -197,7 +197,8 @@ def _test_adapter_instantiation(console: Console) -> None:
|
|
|
197
197
|
if primary:
|
|
198
198
|
adapter_type = primary.adapter_type
|
|
199
199
|
# Build config from discovery
|
|
200
|
-
from ..mcp.server import
|
|
200
|
+
from ..mcp.server import \
|
|
201
|
+
_build_adapter_config_from_env_vars
|
|
201
202
|
|
|
202
203
|
config = _build_adapter_config_from_env_vars(adapter_type, {})
|
|
203
204
|
else:
|
|
@@ -384,7 +385,8 @@ def get_adapter_status() -> dict[str, Any]:
|
|
|
384
385
|
adapter_type = primary.adapter_type
|
|
385
386
|
status["configuration_source"] = primary.found_in
|
|
386
387
|
# Build basic config
|
|
387
|
-
from ..mcp.server import
|
|
388
|
+
from ..mcp.server import \
|
|
389
|
+
_build_adapter_config_from_env_vars
|
|
388
390
|
|
|
389
391
|
config = _build_adapter_config_from_env_vars(adapter_type, {})
|
|
390
392
|
else:
|
|
@@ -10,7 +10,8 @@ from typing import Any
|
|
|
10
10
|
|
|
11
11
|
from rich.console import Console
|
|
12
12
|
|
|
13
|
-
from .mcp_configure import
|
|
13
|
+
from .mcp_configure import load_project_config
|
|
14
|
+
from .python_detection import get_mcp_ticketer_python
|
|
14
15
|
|
|
15
16
|
console = Console()
|
|
16
17
|
|
|
@@ -71,13 +72,14 @@ def save_auggie_config(config_path: Path, config: dict[str, Any]) -> None:
|
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
def create_auggie_server_config(
|
|
74
|
-
|
|
75
|
+
python_path: str, project_config: dict[str, Any], project_path: str | None = None
|
|
75
76
|
) -> dict[str, Any]:
|
|
76
77
|
"""Create Auggie MCP server configuration for mcp-ticketer.
|
|
77
78
|
|
|
78
79
|
Args:
|
|
79
|
-
|
|
80
|
+
python_path: Path to Python executable in mcp-ticketer venv
|
|
80
81
|
project_config: Project configuration from .mcp-ticketer/config.json
|
|
82
|
+
project_path: Project directory path (optional)
|
|
81
83
|
|
|
82
84
|
Returns:
|
|
83
85
|
Auggie MCP server configuration dict
|
|
@@ -91,6 +93,10 @@ def create_auggie_server_config(
|
|
|
91
93
|
# Build environment variables
|
|
92
94
|
env_vars = {}
|
|
93
95
|
|
|
96
|
+
# Add PYTHONPATH for project context
|
|
97
|
+
if project_path:
|
|
98
|
+
env_vars["PYTHONPATH"] = project_path
|
|
99
|
+
|
|
94
100
|
# Add adapter type
|
|
95
101
|
env_vars["MCP_TICKETER_ADAPTER"] = adapter
|
|
96
102
|
|
|
@@ -128,10 +134,15 @@ def create_auggie_server_config(
|
|
|
128
134
|
if "project_key" in adapter_config:
|
|
129
135
|
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
130
136
|
|
|
137
|
+
# Use module invocation pattern: python -m mcp_ticketer.mcp.server
|
|
138
|
+
args = ["-m", "mcp_ticketer.mcp.server"]
|
|
139
|
+
if project_path:
|
|
140
|
+
args.append(project_path)
|
|
141
|
+
|
|
131
142
|
# Create server configuration (simpler than Gemini - no timeout/trust)
|
|
132
143
|
config = {
|
|
133
|
-
"command":
|
|
134
|
-
"args":
|
|
144
|
+
"command": python_path,
|
|
145
|
+
"args": args,
|
|
135
146
|
"env": env_vars,
|
|
136
147
|
}
|
|
137
148
|
|
|
@@ -214,18 +225,22 @@ def configure_auggie_mcp(force: bool = False) -> None:
|
|
|
214
225
|
force: Overwrite existing configuration
|
|
215
226
|
|
|
216
227
|
Raises:
|
|
217
|
-
FileNotFoundError: If
|
|
228
|
+
FileNotFoundError: If Python executable or project config not found
|
|
218
229
|
ValueError: If configuration is invalid
|
|
219
230
|
|
|
220
231
|
"""
|
|
221
|
-
# Step 1: Find
|
|
222
|
-
console.print("[cyan]🔍 Finding mcp-ticketer
|
|
232
|
+
# Step 1: Find Python executable
|
|
233
|
+
console.print("[cyan]🔍 Finding mcp-ticketer Python executable...[/cyan]")
|
|
223
234
|
try:
|
|
224
|
-
|
|
225
|
-
console.print(f"[green]✓[/green] Found: {
|
|
226
|
-
except
|
|
227
|
-
console.print(f"[red]✗[/red] {e}")
|
|
228
|
-
raise
|
|
235
|
+
python_path = get_mcp_ticketer_python()
|
|
236
|
+
console.print(f"[green]✓[/green] Found: {python_path}")
|
|
237
|
+
except Exception as e:
|
|
238
|
+
console.print(f"[red]✗[/red] Could not find Python executable: {e}")
|
|
239
|
+
raise FileNotFoundError(
|
|
240
|
+
"Could not find mcp-ticketer Python executable. "
|
|
241
|
+
"Please ensure mcp-ticketer is installed.\n"
|
|
242
|
+
"Install with: pip install mcp-ticketer or pipx install mcp-ticketer"
|
|
243
|
+
)
|
|
229
244
|
|
|
230
245
|
# Step 2: Load project configuration
|
|
231
246
|
console.print("\n[cyan]📖 Reading project configuration...[/cyan]")
|
|
@@ -259,8 +274,11 @@ def configure_auggie_mcp(force: bool = False) -> None:
|
|
|
259
274
|
console.print("[yellow]⚠ Overwriting existing configuration[/yellow]")
|
|
260
275
|
|
|
261
276
|
# Step 6: Create mcp-ticketer server config
|
|
277
|
+
project_path = str(Path.cwd())
|
|
262
278
|
server_config = create_auggie_server_config(
|
|
263
|
-
|
|
279
|
+
python_path=python_path,
|
|
280
|
+
project_config=project_config,
|
|
281
|
+
project_path=project_path,
|
|
264
282
|
)
|
|
265
283
|
|
|
266
284
|
# Step 7: Update Auggie configuration
|
|
@@ -279,8 +297,10 @@ def configure_auggie_mcp(force: bool = False) -> None:
|
|
|
279
297
|
console.print("\n[bold]Configuration Details:[/bold]")
|
|
280
298
|
console.print(" Server name: mcp-ticketer")
|
|
281
299
|
console.print(f" Adapter: {adapter}")
|
|
282
|
-
console.print(f"
|
|
300
|
+
console.print(f" Python: {python_path}")
|
|
301
|
+
console.print(" Command: python -m mcp_ticketer.mcp.server")
|
|
283
302
|
console.print(" Scope: Global (affects all projects)")
|
|
303
|
+
console.print(f" Project path: {project_path}")
|
|
284
304
|
if "env" in server_config:
|
|
285
305
|
console.print(
|
|
286
306
|
f" Environment variables: {list(server_config['env'].keys())}"
|
|
@@ -4,19 +4,15 @@ 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
|
|
7
|
+
import tomllib
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
-
if sys.version_info >= (3, 11):
|
|
12
|
-
import tomllib
|
|
13
|
-
else:
|
|
14
|
-
import tomli as tomllib
|
|
15
|
-
|
|
16
11
|
import tomli_w
|
|
17
12
|
from rich.console import Console
|
|
18
13
|
|
|
19
|
-
from .mcp_configure import
|
|
14
|
+
from .mcp_configure import load_project_config
|
|
15
|
+
from .python_detection import get_mcp_ticketer_python
|
|
20
16
|
|
|
21
17
|
console = Console()
|
|
22
18
|
|
|
@@ -78,14 +74,14 @@ def save_codex_config(config_path: Path, config: dict[str, Any]) -> None:
|
|
|
78
74
|
|
|
79
75
|
|
|
80
76
|
def create_codex_server_config(
|
|
81
|
-
|
|
77
|
+
python_path: str, project_config: dict, project_path: str | None = None
|
|
82
78
|
) -> dict[str, Any]:
|
|
83
79
|
"""Create Codex MCP server configuration for mcp-ticketer.
|
|
84
80
|
|
|
85
81
|
Args:
|
|
86
|
-
|
|
82
|
+
python_path: Path to Python executable in mcp-ticketer venv
|
|
87
83
|
project_config: Project configuration from .mcp-ticketer/config.json
|
|
88
|
-
|
|
84
|
+
project_path: Project directory path (optional, not used for global config)
|
|
89
85
|
|
|
90
86
|
Returns:
|
|
91
87
|
Codex MCP server configuration dict
|
|
@@ -99,9 +95,9 @@ def create_codex_server_config(
|
|
|
99
95
|
# Build environment variables
|
|
100
96
|
env_vars: dict[str, str] = {}
|
|
101
97
|
|
|
102
|
-
# Add PYTHONPATH
|
|
103
|
-
if
|
|
104
|
-
env_vars["PYTHONPATH"] =
|
|
98
|
+
# Add PYTHONPATH for project context
|
|
99
|
+
if project_path:
|
|
100
|
+
env_vars["PYTHONPATH"] = project_path
|
|
105
101
|
|
|
106
102
|
# Add adapter type
|
|
107
103
|
env_vars["MCP_TICKETER_ADAPTER"] = adapter
|
|
@@ -110,9 +106,9 @@ def create_codex_server_config(
|
|
|
110
106
|
if adapter == "aitrackdown":
|
|
111
107
|
# Set base path for local adapter
|
|
112
108
|
base_path = adapter_config.get("base_path", ".aitrackdown")
|
|
113
|
-
if
|
|
114
|
-
# Use absolute path if
|
|
115
|
-
env_vars["MCP_TICKETER_BASE_PATH"] = str(Path(
|
|
109
|
+
if project_path:
|
|
110
|
+
# Use absolute path if project_path is provided
|
|
111
|
+
env_vars["MCP_TICKETER_BASE_PATH"] = str(Path(project_path) / base_path)
|
|
116
112
|
else:
|
|
117
113
|
env_vars["MCP_TICKETER_BASE_PATH"] = base_path
|
|
118
114
|
|
|
@@ -140,11 +136,15 @@ def create_codex_server_config(
|
|
|
140
136
|
if "project_key" in adapter_config:
|
|
141
137
|
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
142
138
|
|
|
139
|
+
# Use module invocation pattern: python -m mcp_ticketer.mcp.server
|
|
140
|
+
args = ["-m", "mcp_ticketer.mcp.server"]
|
|
141
|
+
if project_path:
|
|
142
|
+
args.append(project_path)
|
|
143
|
+
|
|
143
144
|
# Create server configuration with Codex-specific structure
|
|
144
|
-
# NOTE: Codex uses nested dict structure for env vars
|
|
145
145
|
config: dict[str, Any] = {
|
|
146
|
-
"command":
|
|
147
|
-
"args":
|
|
146
|
+
"command": python_path,
|
|
147
|
+
"args": args,
|
|
148
148
|
"env": env_vars,
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -231,18 +231,22 @@ def configure_codex_mcp(force: bool = False) -> None:
|
|
|
231
231
|
force: Overwrite existing configuration
|
|
232
232
|
|
|
233
233
|
Raises:
|
|
234
|
-
FileNotFoundError: If
|
|
234
|
+
FileNotFoundError: If Python executable or project config not found
|
|
235
235
|
ValueError: If configuration is invalid
|
|
236
236
|
|
|
237
237
|
"""
|
|
238
|
-
# Step 1: Find
|
|
239
|
-
console.print("[cyan]🔍 Finding mcp-ticketer
|
|
238
|
+
# Step 1: Find Python executable
|
|
239
|
+
console.print("[cyan]🔍 Finding mcp-ticketer Python executable...[/cyan]")
|
|
240
240
|
try:
|
|
241
|
-
|
|
242
|
-
console.print(f"[green]✓[/green] Found: {
|
|
243
|
-
except
|
|
244
|
-
console.print(f"[red]✗[/red] {e}")
|
|
245
|
-
raise
|
|
241
|
+
python_path = get_mcp_ticketer_python()
|
|
242
|
+
console.print(f"[green]✓[/green] Found: {python_path}")
|
|
243
|
+
except Exception as e:
|
|
244
|
+
console.print(f"[red]✗[/red] Could not find Python executable: {e}")
|
|
245
|
+
raise FileNotFoundError(
|
|
246
|
+
"Could not find mcp-ticketer Python executable. "
|
|
247
|
+
"Please ensure mcp-ticketer is installed.\n"
|
|
248
|
+
"Install with: pip install mcp-ticketer or pipx install mcp-ticketer"
|
|
249
|
+
)
|
|
246
250
|
|
|
247
251
|
# Step 2: Load project configuration
|
|
248
252
|
console.print("\n[cyan]📖 Reading project configuration...[/cyan]")
|
|
@@ -279,9 +283,11 @@ def configure_codex_mcp(force: bool = False) -> None:
|
|
|
279
283
|
|
|
280
284
|
# Step 6: Create mcp-ticketer server config
|
|
281
285
|
# For global config, include current working directory for context
|
|
282
|
-
|
|
286
|
+
project_path = str(Path.cwd())
|
|
283
287
|
server_config = create_codex_server_config(
|
|
284
|
-
|
|
288
|
+
python_path=python_path,
|
|
289
|
+
project_config=project_config,
|
|
290
|
+
project_path=project_path,
|
|
285
291
|
)
|
|
286
292
|
|
|
287
293
|
# Step 7: Update Codex configuration
|
|
@@ -300,9 +306,10 @@ def configure_codex_mcp(force: bool = False) -> None:
|
|
|
300
306
|
console.print("\n[bold]Configuration Details:[/bold]")
|
|
301
307
|
console.print(" Server name: mcp-ticketer")
|
|
302
308
|
console.print(f" Adapter: {adapter}")
|
|
303
|
-
console.print(f"
|
|
309
|
+
console.print(f" Python: {python_path}")
|
|
310
|
+
console.print(" Command: python -m mcp_ticketer.mcp.server")
|
|
304
311
|
console.print(" Scope: global (Codex only supports global config)")
|
|
305
|
-
console.print(f"
|
|
312
|
+
console.print(f" Project path: {project_path}")
|
|
306
313
|
if "env" in server_config:
|
|
307
314
|
console.print(
|
|
308
315
|
f" Environment variables: {list(server_config['env'].keys())}"
|
mcp_ticketer/cli/configure.py
CHANGED
|
@@ -8,15 +8,9 @@ from rich.panel import Panel
|
|
|
8
8
|
from rich.prompt import Confirm, Prompt
|
|
9
9
|
from rich.table import Table
|
|
10
10
|
|
|
11
|
-
from ..core.project_config import (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
ConfigResolver,
|
|
15
|
-
ConfigValidator,
|
|
16
|
-
HybridConfig,
|
|
17
|
-
SyncStrategy,
|
|
18
|
-
TicketerConfig,
|
|
19
|
-
)
|
|
11
|
+
from ..core.project_config import (AdapterConfig, AdapterType, ConfigResolver,
|
|
12
|
+
ConfigValidator, HybridConfig, SyncStrategy,
|
|
13
|
+
TicketerConfig)
|
|
20
14
|
|
|
21
15
|
console = Console()
|
|
22
16
|
|
mcp_ticketer/cli/discover.py
CHANGED
|
@@ -6,12 +6,8 @@ import typer
|
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
|
|
8
8
|
from ..core.env_discovery import DiscoveredAdapter, EnvDiscovery
|
|
9
|
-
from ..core.project_config import (
|
|
10
|
-
|
|
11
|
-
ConfigResolver,
|
|
12
|
-
ConfigValidator,
|
|
13
|
-
TicketerConfig,
|
|
14
|
-
)
|
|
9
|
+
from ..core.project_config import (AdapterConfig, ConfigResolver,
|
|
10
|
+
ConfigValidator, TicketerConfig)
|
|
15
11
|
|
|
16
12
|
console = Console()
|
|
17
13
|
app = typer.Typer(help="Auto-discover configuration from .env files")
|
|
@@ -6,7 +6,8 @@ from typing import Literal
|
|
|
6
6
|
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
|
|
9
|
-
from .mcp_configure import
|
|
9
|
+
from .mcp_configure import load_project_config
|
|
10
|
+
from .python_detection import get_mcp_ticketer_python
|
|
10
11
|
|
|
11
12
|
console = Console()
|
|
12
13
|
|
|
@@ -73,14 +74,14 @@ def save_gemini_config(config_path: Path, config: dict) -> None:
|
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def create_gemini_server_config(
|
|
76
|
-
|
|
77
|
+
python_path: str, project_config: dict, project_path: str | None = None
|
|
77
78
|
) -> dict:
|
|
78
79
|
"""Create Gemini MCP server configuration for mcp-ticketer.
|
|
79
80
|
|
|
80
81
|
Args:
|
|
81
|
-
|
|
82
|
+
python_path: Path to Python executable in mcp-ticketer venv
|
|
82
83
|
project_config: Project configuration from .mcp-ticketer/config.json
|
|
83
|
-
|
|
84
|
+
project_path: Project directory path (optional)
|
|
84
85
|
|
|
85
86
|
Returns:
|
|
86
87
|
Gemini MCP server configuration dict
|
|
@@ -94,9 +95,9 @@ def create_gemini_server_config(
|
|
|
94
95
|
# Build environment variables
|
|
95
96
|
env_vars = {}
|
|
96
97
|
|
|
97
|
-
# Add PYTHONPATH
|
|
98
|
-
if
|
|
99
|
-
env_vars["PYTHONPATH"] =
|
|
98
|
+
# Add PYTHONPATH for project context
|
|
99
|
+
if project_path:
|
|
100
|
+
env_vars["PYTHONPATH"] = project_path
|
|
100
101
|
|
|
101
102
|
# Add adapter type
|
|
102
103
|
env_vars["MCP_TICKETER_ADAPTER"] = adapter
|
|
@@ -105,9 +106,9 @@ def create_gemini_server_config(
|
|
|
105
106
|
if adapter == "aitrackdown":
|
|
106
107
|
# Set base path for local adapter
|
|
107
108
|
base_path = adapter_config.get("base_path", ".aitrackdown")
|
|
108
|
-
if
|
|
109
|
-
# Use absolute path if
|
|
110
|
-
env_vars["MCP_TICKETER_BASE_PATH"] = str(Path(
|
|
109
|
+
if project_path:
|
|
110
|
+
# Use absolute path if project_path is provided
|
|
111
|
+
env_vars["MCP_TICKETER_BASE_PATH"] = str(Path(project_path) / base_path)
|
|
111
112
|
else:
|
|
112
113
|
env_vars["MCP_TICKETER_BASE_PATH"] = base_path
|
|
113
114
|
|
|
@@ -135,10 +136,15 @@ def create_gemini_server_config(
|
|
|
135
136
|
if "project_key" in adapter_config:
|
|
136
137
|
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
137
138
|
|
|
139
|
+
# Use module invocation pattern: python -m mcp_ticketer.mcp.server
|
|
140
|
+
args = ["-m", "mcp_ticketer.mcp.server"]
|
|
141
|
+
if project_path:
|
|
142
|
+
args.append(project_path)
|
|
143
|
+
|
|
138
144
|
# Create server configuration with Gemini-specific options
|
|
139
145
|
config = {
|
|
140
|
-
"command":
|
|
141
|
-
"args":
|
|
146
|
+
"command": python_path,
|
|
147
|
+
"args": args,
|
|
142
148
|
"env": env_vars,
|
|
143
149
|
"timeout": 15000, # 15 seconds timeout
|
|
144
150
|
"trust": False, # Don't trust by default (security)
|
|
@@ -223,18 +229,22 @@ def configure_gemini_mcp(
|
|
|
223
229
|
force: Overwrite existing configuration
|
|
224
230
|
|
|
225
231
|
Raises:
|
|
226
|
-
FileNotFoundError: If
|
|
232
|
+
FileNotFoundError: If Python executable or project config not found
|
|
227
233
|
ValueError: If configuration is invalid
|
|
228
234
|
|
|
229
235
|
"""
|
|
230
|
-
# Step 1: Find
|
|
231
|
-
console.print("[cyan]🔍 Finding mcp-ticketer
|
|
236
|
+
# Step 1: Find Python executable
|
|
237
|
+
console.print("[cyan]🔍 Finding mcp-ticketer Python executable...[/cyan]")
|
|
232
238
|
try:
|
|
233
|
-
|
|
234
|
-
console.print(f"[green]✓[/green] Found: {
|
|
235
|
-
except
|
|
236
|
-
console.print(f"[red]✗[/red] {e}")
|
|
237
|
-
raise
|
|
239
|
+
python_path = get_mcp_ticketer_python()
|
|
240
|
+
console.print(f"[green]✓[/green] Found: {python_path}")
|
|
241
|
+
except Exception as e:
|
|
242
|
+
console.print(f"[red]✗[/red] Could not find Python executable: {e}")
|
|
243
|
+
raise FileNotFoundError(
|
|
244
|
+
"Could not find mcp-ticketer Python executable. "
|
|
245
|
+
"Please ensure mcp-ticketer is installed.\n"
|
|
246
|
+
"Install with: pip install mcp-ticketer or pipx install mcp-ticketer"
|
|
247
|
+
)
|
|
238
248
|
|
|
239
249
|
# Step 2: Load project configuration
|
|
240
250
|
console.print("\n[cyan]📖 Reading project configuration...[/cyan]")
|
|
@@ -266,9 +276,11 @@ def configure_gemini_mcp(
|
|
|
266
276
|
console.print("[yellow]⚠ Overwriting existing configuration[/yellow]")
|
|
267
277
|
|
|
268
278
|
# Step 6: Create mcp-ticketer server config
|
|
269
|
-
|
|
279
|
+
project_path = str(Path.cwd()) if scope == "project" else None
|
|
270
280
|
server_config = create_gemini_server_config(
|
|
271
|
-
|
|
281
|
+
python_path=python_path,
|
|
282
|
+
project_config=project_config,
|
|
283
|
+
project_path=project_path,
|
|
272
284
|
)
|
|
273
285
|
|
|
274
286
|
# Step 7: Update Gemini configuration
|
|
@@ -287,11 +299,12 @@ def configure_gemini_mcp(
|
|
|
287
299
|
console.print("\n[bold]Configuration Details:[/bold]")
|
|
288
300
|
console.print(" Server name: mcp-ticketer")
|
|
289
301
|
console.print(f" Adapter: {adapter}")
|
|
290
|
-
console.print(f"
|
|
302
|
+
console.print(f" Python: {python_path}")
|
|
303
|
+
console.print(" Command: python -m mcp_ticketer.mcp.server")
|
|
291
304
|
console.print(f" Timeout: {server_config['timeout']}ms")
|
|
292
305
|
console.print(f" Trust: {server_config['trust']}")
|
|
293
|
-
if
|
|
294
|
-
console.print(f"
|
|
306
|
+
if project_path:
|
|
307
|
+
console.print(f" Project path: {project_path}")
|
|
295
308
|
if "env" in server_config:
|
|
296
309
|
console.print(
|
|
297
310
|
f" Environment variables: {list(server_config['env'].keys())}"
|
mcp_ticketer/cli/main.py
CHANGED
|
@@ -20,7 +20,8 @@ from ..core.models import Comment, SearchQuery
|
|
|
20
20
|
from ..queue import Queue, QueueStatus, WorkerManager
|
|
21
21
|
from ..queue.health_monitor import HealthStatus, QueueHealthMonitor
|
|
22
22
|
from ..queue.ticket_registry import TicketRegistry
|
|
23
|
-
from .configure import configure_wizard, set_adapter_config,
|
|
23
|
+
from .configure import (configure_wizard, set_adapter_config,
|
|
24
|
+
show_current_config)
|
|
24
25
|
from .diagnostics import run_diagnostics
|
|
25
26
|
from .discover import app as discover_app
|
|
26
27
|
from .migrate_config import migrate_config_command
|
|
@@ -575,7 +576,7 @@ def init(
|
|
|
575
576
|
)
|
|
576
577
|
|
|
577
578
|
# First try our improved .env configuration loader
|
|
578
|
-
from ..mcp.server import _load_env_configuration
|
|
579
|
+
from ..mcp.server.main import _load_env_configuration
|
|
579
580
|
|
|
580
581
|
env_config = _load_env_configuration()
|
|
581
582
|
|
|
@@ -2225,7 +2226,7 @@ def mcp_serve(
|
|
|
2225
2226
|
adapter_config = adapters_config.get(adapter_type, {})
|
|
2226
2227
|
else:
|
|
2227
2228
|
# Priority 2: .env files
|
|
2228
|
-
from ..mcp.server import _load_env_configuration
|
|
2229
|
+
from ..mcp.server.main import _load_env_configuration
|
|
2229
2230
|
|
|
2230
2231
|
env_config = _load_env_configuration()
|
|
2231
2232
|
if env_config:
|