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.
- mcp_ticketer/__init__.py +10 -10
- mcp_ticketer/__version__.py +3 -3
- mcp_ticketer/adapters/__init__.py +2 -0
- mcp_ticketer/adapters/aitrackdown.py +796 -46
- mcp_ticketer/adapters/asana/__init__.py +15 -0
- mcp_ticketer/adapters/asana/adapter.py +1416 -0
- mcp_ticketer/adapters/asana/client.py +292 -0
- mcp_ticketer/adapters/asana/mappers.py +348 -0
- mcp_ticketer/adapters/asana/types.py +146 -0
- mcp_ticketer/adapters/github.py +879 -129
- mcp_ticketer/adapters/hybrid.py +11 -11
- mcp_ticketer/adapters/jira.py +973 -73
- mcp_ticketer/adapters/linear/__init__.py +24 -0
- mcp_ticketer/adapters/linear/adapter.py +2732 -0
- mcp_ticketer/adapters/linear/client.py +344 -0
- mcp_ticketer/adapters/linear/mappers.py +420 -0
- mcp_ticketer/adapters/linear/queries.py +479 -0
- mcp_ticketer/adapters/linear/types.py +360 -0
- mcp_ticketer/adapters/linear.py +10 -2315
- mcp_ticketer/analysis/__init__.py +23 -0
- mcp_ticketer/analysis/orphaned.py +218 -0
- mcp_ticketer/analysis/similarity.py +224 -0
- mcp_ticketer/analysis/staleness.py +266 -0
- mcp_ticketer/cache/memory.py +9 -8
- mcp_ticketer/cli/adapter_diagnostics.py +421 -0
- mcp_ticketer/cli/auggie_configure.py +116 -15
- mcp_ticketer/cli/codex_configure.py +274 -82
- mcp_ticketer/cli/configure.py +888 -151
- mcp_ticketer/cli/diagnostics.py +400 -157
- mcp_ticketer/cli/discover.py +297 -26
- mcp_ticketer/cli/gemini_configure.py +119 -26
- mcp_ticketer/cli/init_command.py +880 -0
- mcp_ticketer/cli/instruction_commands.py +435 -0
- mcp_ticketer/cli/linear_commands.py +616 -0
- mcp_ticketer/cli/main.py +203 -1165
- mcp_ticketer/cli/mcp_configure.py +474 -90
- mcp_ticketer/cli/mcp_server_commands.py +415 -0
- mcp_ticketer/cli/migrate_config.py +12 -8
- mcp_ticketer/cli/platform_commands.py +123 -0
- mcp_ticketer/cli/platform_detection.py +418 -0
- mcp_ticketer/cli/platform_installer.py +513 -0
- mcp_ticketer/cli/python_detection.py +126 -0
- mcp_ticketer/cli/queue_commands.py +15 -15
- mcp_ticketer/cli/setup_command.py +639 -0
- mcp_ticketer/cli/simple_health.py +90 -65
- mcp_ticketer/cli/ticket_commands.py +1013 -0
- mcp_ticketer/cli/update_checker.py +313 -0
- mcp_ticketer/cli/utils.py +114 -66
- mcp_ticketer/core/__init__.py +24 -1
- mcp_ticketer/core/adapter.py +250 -16
- mcp_ticketer/core/config.py +145 -37
- mcp_ticketer/core/env_discovery.py +101 -22
- mcp_ticketer/core/env_loader.py +349 -0
- mcp_ticketer/core/exceptions.py +160 -0
- mcp_ticketer/core/http_client.py +26 -26
- mcp_ticketer/core/instructions.py +405 -0
- mcp_ticketer/core/label_manager.py +732 -0
- mcp_ticketer/core/mappers.py +42 -30
- mcp_ticketer/core/models.py +280 -28
- mcp_ticketer/core/onepassword_secrets.py +379 -0
- mcp_ticketer/core/project_config.py +183 -49
- mcp_ticketer/core/registry.py +3 -3
- mcp_ticketer/core/session_state.py +171 -0
- mcp_ticketer/core/state_matcher.py +592 -0
- mcp_ticketer/core/url_parser.py +425 -0
- mcp_ticketer/core/validators.py +69 -0
- 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/server/constants.py +58 -0
- mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
- mcp_ticketer/mcp/server/dto.py +195 -0
- mcp_ticketer/mcp/server/main.py +1343 -0
- mcp_ticketer/mcp/server/response_builder.py +206 -0
- mcp_ticketer/mcp/server/routing.py +655 -0
- mcp_ticketer/mcp/server/server_sdk.py +151 -0
- mcp_ticketer/mcp/server/tools/__init__.py +56 -0
- mcp_ticketer/mcp/server/tools/analysis_tools.py +495 -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 +152 -0
- mcp_ticketer/mcp/server/tools/config_tools.py +1439 -0
- mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
- mcp_ticketer/mcp/server/tools/hierarchy_tools.py +921 -0
- mcp_ticketer/mcp/server/tools/instruction_tools.py +300 -0
- mcp_ticketer/mcp/server/tools/label_tools.py +948 -0
- mcp_ticketer/mcp/server/tools/pr_tools.py +152 -0
- mcp_ticketer/mcp/server/tools/search_tools.py +215 -0
- mcp_ticketer/mcp/server/tools/session_tools.py +170 -0
- mcp_ticketer/mcp/server/tools/ticket_tools.py +1268 -0
- mcp_ticketer/mcp/server/tools/user_ticket_tools.py +547 -0
- mcp_ticketer/queue/__init__.py +1 -0
- mcp_ticketer/queue/health_monitor.py +168 -136
- mcp_ticketer/queue/manager.py +95 -25
- mcp_ticketer/queue/queue.py +40 -21
- mcp_ticketer/queue/run_worker.py +6 -1
- mcp_ticketer/queue/ticket_registry.py +213 -155
- mcp_ticketer/queue/worker.py +109 -49
- mcp_ticketer-1.2.11.dist-info/METADATA +792 -0
- mcp_ticketer-1.2.11.dist-info/RECORD +110 -0
- mcp_ticketer/mcp/server.py +0 -1895
- mcp_ticketer-0.1.30.dist-info/METADATA +0 -413
- mcp_ticketer-0.1.30.dist-info/RECORD +0 -49
- {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/top_level.txt +0 -0
|
@@ -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 sys
|
|
8
7
|
from pathlib import Path
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
if sys.version_info >= (3, 11):
|
|
12
|
-
import tomllib
|
|
13
|
-
else:
|
|
14
|
-
import tomli as tomllib
|
|
8
|
+
from typing import Any
|
|
15
9
|
|
|
16
10
|
import tomli_w
|
|
11
|
+
import tomllib
|
|
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
|
|
|
@@ -28,6 +24,7 @@ def find_codex_config() -> Path:
|
|
|
28
24
|
No project-level or user-scoped configuration is available.
|
|
29
25
|
|
|
30
26
|
Returns:
|
|
27
|
+
-------
|
|
31
28
|
Path to Codex global config file at ~/.codex/config.toml
|
|
32
29
|
|
|
33
30
|
"""
|
|
@@ -36,13 +33,15 @@ def find_codex_config() -> Path:
|
|
|
36
33
|
return config_path
|
|
37
34
|
|
|
38
35
|
|
|
39
|
-
def load_codex_config(config_path: Path) ->
|
|
36
|
+
def load_codex_config(config_path: Path) -> dict[str, Any]:
|
|
40
37
|
"""Load existing Codex configuration or return empty structure.
|
|
41
38
|
|
|
42
39
|
Args:
|
|
40
|
+
----
|
|
43
41
|
config_path: Path to Codex config.toml file
|
|
44
42
|
|
|
45
43
|
Returns:
|
|
44
|
+
-------
|
|
46
45
|
Codex configuration dict with mcp_servers section
|
|
47
46
|
|
|
48
47
|
"""
|
|
@@ -61,10 +60,11 @@ def load_codex_config(config_path: Path) -> Dict[str, Any]:
|
|
|
61
60
|
return {"mcp_servers": {}}
|
|
62
61
|
|
|
63
62
|
|
|
64
|
-
def save_codex_config(config_path: Path, config:
|
|
63
|
+
def save_codex_config(config_path: Path, config: dict[str, Any]) -> None:
|
|
65
64
|
"""Save Codex configuration to TOML file.
|
|
66
65
|
|
|
67
66
|
Args:
|
|
67
|
+
----
|
|
68
68
|
config_path: Path to Codex config.toml file
|
|
69
69
|
config: Configuration to save
|
|
70
70
|
|
|
@@ -78,79 +78,219 @@ def save_codex_config(config_path: Path, config: Dict[str, Any]) -> None:
|
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
def create_codex_server_config(
|
|
81
|
-
|
|
82
|
-
) ->
|
|
81
|
+
python_path: str, project_config: dict, project_path: str | None = None
|
|
82
|
+
) -> dict[str, Any]:
|
|
83
83
|
"""Create Codex MCP server configuration for mcp-ticketer.
|
|
84
84
|
|
|
85
|
+
Uses the CLI command (mcp-ticketer mcp) which implements proper
|
|
86
|
+
Content-Length framing via FastMCP SDK, required for modern MCP clients.
|
|
87
|
+
|
|
85
88
|
Args:
|
|
86
|
-
|
|
89
|
+
----
|
|
90
|
+
python_path: Path to Python executable in mcp-ticketer venv
|
|
87
91
|
project_config: Project configuration from .mcp-ticketer/config.json
|
|
88
|
-
|
|
92
|
+
project_path: Project directory path (optional)
|
|
89
93
|
|
|
90
94
|
Returns:
|
|
95
|
+
-------
|
|
91
96
|
Codex MCP server configuration dict
|
|
92
97
|
|
|
93
98
|
"""
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
env_vars["MCP_TICKETER_ADAPTER"] = adapter
|
|
108
|
-
|
|
109
|
-
# Add adapter-specific environment variables
|
|
110
|
-
if adapter == "aitrackdown":
|
|
111
|
-
# Set base path for local adapter
|
|
112
|
-
base_path = adapter_config.get("base_path", ".aitrackdown")
|
|
113
|
-
if cwd:
|
|
114
|
-
# Use absolute path if cwd is provided
|
|
115
|
-
env_vars["MCP_TICKETER_BASE_PATH"] = str(Path(cwd) / base_path)
|
|
116
|
-
else:
|
|
117
|
-
env_vars["MCP_TICKETER_BASE_PATH"] = base_path
|
|
118
|
-
|
|
119
|
-
elif adapter == "linear":
|
|
120
|
-
if "api_key" in adapter_config:
|
|
121
|
-
env_vars["LINEAR_API_KEY"] = adapter_config["api_key"]
|
|
122
|
-
if "team_id" in adapter_config:
|
|
123
|
-
env_vars["LINEAR_TEAM_ID"] = adapter_config["team_id"]
|
|
124
|
-
|
|
125
|
-
elif adapter == "github":
|
|
126
|
-
if "token" in adapter_config:
|
|
127
|
-
env_vars["GITHUB_TOKEN"] = adapter_config["token"]
|
|
128
|
-
if "owner" in adapter_config:
|
|
129
|
-
env_vars["GITHUB_OWNER"] = adapter_config["owner"]
|
|
130
|
-
if "repo" in adapter_config:
|
|
131
|
-
env_vars["GITHUB_REPO"] = adapter_config["repo"]
|
|
132
|
-
|
|
133
|
-
elif adapter == "jira":
|
|
134
|
-
if "api_token" in adapter_config:
|
|
135
|
-
env_vars["JIRA_API_TOKEN"] = adapter_config["api_token"]
|
|
136
|
-
if "email" in adapter_config:
|
|
137
|
-
env_vars["JIRA_EMAIL"] = adapter_config["email"]
|
|
138
|
-
if "server" in adapter_config:
|
|
139
|
-
env_vars["JIRA_SERVER"] = adapter_config["server"]
|
|
140
|
-
if "project_key" in adapter_config:
|
|
141
|
-
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
99
|
+
# IMPORTANT: Use CLI command, NOT Python module invocation
|
|
100
|
+
# The CLI uses FastMCP SDK which implements proper Content-Length framing
|
|
101
|
+
# Legacy python -m mcp_ticketer.mcp.server uses line-delimited JSON (incompatible)
|
|
102
|
+
|
|
103
|
+
# Get mcp-ticketer CLI path from Python path
|
|
104
|
+
# If python_path is /path/to/venv/bin/python, CLI is /path/to/venv/bin/mcp-ticketer
|
|
105
|
+
python_dir = Path(python_path).parent
|
|
106
|
+
cli_path = str(python_dir / "mcp-ticketer")
|
|
107
|
+
|
|
108
|
+
# Build CLI arguments
|
|
109
|
+
args = ["mcp"]
|
|
110
|
+
if project_path:
|
|
111
|
+
args.extend(["--path", project_path])
|
|
142
112
|
|
|
143
113
|
# Create server configuration with Codex-specific structure
|
|
144
|
-
#
|
|
145
|
-
config:
|
|
146
|
-
"command":
|
|
147
|
-
"args":
|
|
148
|
-
"env": env_vars,
|
|
114
|
+
# No environment variables needed - config loaded from .mcp-ticketer/config.json
|
|
115
|
+
config: dict[str, Any] = {
|
|
116
|
+
"command": cli_path,
|
|
117
|
+
"args": args,
|
|
149
118
|
}
|
|
150
119
|
|
|
151
120
|
return config
|
|
152
121
|
|
|
153
122
|
|
|
123
|
+
def _test_configuration(adapter: str, project_config: dict) -> bool:
|
|
124
|
+
"""Test the configuration by validating adapter credentials.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
----
|
|
128
|
+
adapter: Adapter type (linear, github, jira, aitrackdown)
|
|
129
|
+
project_config: Project configuration dict
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
-------
|
|
133
|
+
True if validation passed, False otherwise
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
from ..core import AdapterRegistry
|
|
138
|
+
|
|
139
|
+
# Get adapter configuration
|
|
140
|
+
adapters_config = project_config.get("adapters", {})
|
|
141
|
+
adapter_config = adapters_config.get(adapter, {})
|
|
142
|
+
|
|
143
|
+
# Test adapter instantiation
|
|
144
|
+
console.print(f" Testing {adapter} adapter...")
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
adapter_instance = AdapterRegistry.get_adapter(adapter, adapter_config)
|
|
148
|
+
console.print(" [green]✓[/green] Adapter instantiated successfully")
|
|
149
|
+
|
|
150
|
+
# Test credentials if validation method exists
|
|
151
|
+
if hasattr(adapter_instance, "validate_credentials"):
|
|
152
|
+
console.print(f" Validating {adapter} credentials...")
|
|
153
|
+
is_valid, error_msg = adapter_instance.validate_credentials()
|
|
154
|
+
|
|
155
|
+
if is_valid:
|
|
156
|
+
console.print(" [green]✓[/green] Credentials are valid")
|
|
157
|
+
return True
|
|
158
|
+
else:
|
|
159
|
+
console.print(
|
|
160
|
+
f" [red]✗[/red] Credential validation failed: {error_msg}"
|
|
161
|
+
)
|
|
162
|
+
return False
|
|
163
|
+
else:
|
|
164
|
+
# No validation method, assume valid
|
|
165
|
+
console.print(
|
|
166
|
+
f" [yellow]○[/yellow] No credential validation available for {adapter}"
|
|
167
|
+
)
|
|
168
|
+
return True
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
console.print(f" [red]✗[/red] Adapter instantiation failed: {e}")
|
|
172
|
+
|
|
173
|
+
# Provide helpful error messages based on adapter type
|
|
174
|
+
if adapter == "linear":
|
|
175
|
+
console.print(
|
|
176
|
+
"\n [yellow]Linear requires:[/yellow] LINEAR_API_KEY and LINEAR_TEAM_ID"
|
|
177
|
+
)
|
|
178
|
+
elif adapter == "github":
|
|
179
|
+
console.print(
|
|
180
|
+
"\n [yellow]GitHub requires:[/yellow] GITHUB_TOKEN, GITHUB_OWNER, GITHUB_REPO"
|
|
181
|
+
)
|
|
182
|
+
elif adapter == "jira":
|
|
183
|
+
console.print(
|
|
184
|
+
"\n [yellow]JIRA requires:[/yellow] JIRA_SERVER, JIRA_EMAIL, JIRA_API_TOKEN"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
except Exception as e:
|
|
190
|
+
console.print(f" [red]✗[/red] Configuration test error: {e}")
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def detect_legacy_config(config_path: Path) -> tuple[bool, dict[str, Any] | None]:
|
|
195
|
+
"""Detect if existing config uses legacy Python module invocation.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
----
|
|
199
|
+
config_path: Path to Codex config.toml file
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
-------
|
|
203
|
+
Tuple of (is_legacy, server_config):
|
|
204
|
+
- is_legacy: True if config uses 'python -m mcp_ticketer.mcp.server'
|
|
205
|
+
- server_config: The legacy server config dict, or None if not legacy
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
if not config_path.exists():
|
|
209
|
+
return False, None
|
|
210
|
+
|
|
211
|
+
codex_config = load_codex_config(config_path)
|
|
212
|
+
mcp_servers = codex_config.get("mcp_servers", {})
|
|
213
|
+
|
|
214
|
+
if "mcp-ticketer" in mcp_servers:
|
|
215
|
+
server_config = mcp_servers["mcp-ticketer"]
|
|
216
|
+
args = server_config.get("args", [])
|
|
217
|
+
|
|
218
|
+
# Check for legacy pattern: ["-m", "mcp_ticketer.mcp.server", ...]
|
|
219
|
+
if len(args) >= 2 and args[0] == "-m" and "mcp_ticketer.mcp.server" in args[1]:
|
|
220
|
+
return True, server_config
|
|
221
|
+
|
|
222
|
+
return False, None
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def remove_codex_mcp(dry_run: bool = False) -> None:
|
|
226
|
+
"""Remove mcp-ticketer from Codex CLI configuration.
|
|
227
|
+
|
|
228
|
+
IMPORTANT: Codex CLI ONLY supports global configuration at ~/.codex/config.toml.
|
|
229
|
+
This will remove mcp-ticketer from the global configuration.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
----
|
|
233
|
+
dry_run: Show what would be removed without making changes
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
# Step 1: Find Codex config location (always global)
|
|
237
|
+
console.print("[cyan]🔍 Removing Codex CLI global configuration...[/cyan]")
|
|
238
|
+
console.print(
|
|
239
|
+
"[yellow]⚠ Note: Codex CLI only supports global configuration[/yellow]"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
codex_config_path = find_codex_config()
|
|
243
|
+
console.print(f"[dim]Config location: {codex_config_path}[/dim]")
|
|
244
|
+
|
|
245
|
+
# Step 2: Check if config file exists
|
|
246
|
+
if not codex_config_path.exists():
|
|
247
|
+
console.print(
|
|
248
|
+
f"[yellow]⚠ No configuration found at {codex_config_path}[/yellow]"
|
|
249
|
+
)
|
|
250
|
+
console.print("[dim]mcp-ticketer is not configured for Codex CLI[/dim]")
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
# Step 3: Load existing Codex configuration
|
|
254
|
+
codex_config = load_codex_config(codex_config_path)
|
|
255
|
+
|
|
256
|
+
# Step 4: Check if mcp-ticketer is configured
|
|
257
|
+
# NOTE: Use underscore mcp_servers, not camelCase
|
|
258
|
+
mcp_servers = codex_config.get("mcp_servers", {})
|
|
259
|
+
if "mcp-ticketer" not in mcp_servers:
|
|
260
|
+
console.print("[yellow]⚠ mcp-ticketer is not configured[/yellow]")
|
|
261
|
+
console.print(f"[dim]No mcp-ticketer entry found in {codex_config_path}[/dim]")
|
|
262
|
+
return
|
|
263
|
+
|
|
264
|
+
# Step 5: Show what would be removed (dry run or actual removal)
|
|
265
|
+
if dry_run:
|
|
266
|
+
console.print("\n[cyan]DRY RUN - Would remove:[/cyan]")
|
|
267
|
+
console.print(" Server name: mcp-ticketer")
|
|
268
|
+
console.print(f" From: {codex_config_path}")
|
|
269
|
+
console.print(" Scope: Global (all sessions)")
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
# Step 6: Remove mcp-ticketer from configuration
|
|
273
|
+
del codex_config["mcp_servers"]["mcp-ticketer"]
|
|
274
|
+
|
|
275
|
+
# Step 7: Save updated configuration
|
|
276
|
+
try:
|
|
277
|
+
save_codex_config(codex_config_path, codex_config)
|
|
278
|
+
console.print("\n[green]✓ Successfully removed mcp-ticketer[/green]")
|
|
279
|
+
console.print(f"[dim]Configuration updated: {codex_config_path}[/dim]")
|
|
280
|
+
|
|
281
|
+
# Next steps
|
|
282
|
+
console.print("\n[bold cyan]Next Steps:[/bold cyan]")
|
|
283
|
+
console.print("1. [bold]Restart Codex CLI[/bold] (required for changes)")
|
|
284
|
+
console.print("2. mcp-ticketer will no longer be available via MCP")
|
|
285
|
+
console.print(
|
|
286
|
+
"\n[yellow]⚠ Note: This removes global configuration affecting all Codex sessions[/yellow]"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
except Exception as e:
|
|
290
|
+
console.print(f"\n[red]✗ Failed to update configuration:[/red] {e}")
|
|
291
|
+
raise
|
|
292
|
+
|
|
293
|
+
|
|
154
294
|
def configure_codex_mcp(force: bool = False) -> None:
|
|
155
295
|
"""Configure Codex CLI to use mcp-ticketer.
|
|
156
296
|
|
|
@@ -160,21 +300,27 @@ def configure_codex_mcp(force: bool = False) -> None:
|
|
|
160
300
|
After configuration, you must restart Codex CLI for changes to take effect.
|
|
161
301
|
|
|
162
302
|
Args:
|
|
303
|
+
----
|
|
163
304
|
force: Overwrite existing configuration
|
|
164
305
|
|
|
165
306
|
Raises:
|
|
166
|
-
|
|
307
|
+
------
|
|
308
|
+
FileNotFoundError: If Python executable or project config not found
|
|
167
309
|
ValueError: If configuration is invalid
|
|
168
310
|
|
|
169
311
|
"""
|
|
170
|
-
# Step 1: Find
|
|
171
|
-
console.print("[cyan]🔍 Finding mcp-ticketer
|
|
312
|
+
# Step 1: Find Python executable
|
|
313
|
+
console.print("[cyan]🔍 Finding mcp-ticketer Python executable...[/cyan]")
|
|
172
314
|
try:
|
|
173
|
-
|
|
174
|
-
console.print(f"[green]✓[/green] Found: {
|
|
175
|
-
except
|
|
176
|
-
console.print(f"[red]✗[/red] {e}")
|
|
177
|
-
raise
|
|
315
|
+
python_path = get_mcp_ticketer_python()
|
|
316
|
+
console.print(f"[green]✓[/green] Found: {python_path}")
|
|
317
|
+
except Exception as e:
|
|
318
|
+
console.print(f"[red]✗[/red] Could not find Python executable: {e}")
|
|
319
|
+
raise FileNotFoundError(
|
|
320
|
+
"Could not find mcp-ticketer Python executable. "
|
|
321
|
+
"Please ensure mcp-ticketer is installed.\n"
|
|
322
|
+
"Install with: pip install mcp-ticketer or pipx install mcp-ticketer"
|
|
323
|
+
) from e
|
|
178
324
|
|
|
179
325
|
# Step 2: Load project configuration
|
|
180
326
|
console.print("\n[cyan]📖 Reading project configuration...[/cyan]")
|
|
@@ -195,6 +341,26 @@ def configure_codex_mcp(force: bool = False) -> None:
|
|
|
195
341
|
codex_config_path = find_codex_config()
|
|
196
342
|
console.print(f"[dim]Config location: {codex_config_path}[/dim]")
|
|
197
343
|
|
|
344
|
+
# Step 3.5: Check for legacy configuration (DETECTION & MIGRATION)
|
|
345
|
+
is_legacy, legacy_config = detect_legacy_config(codex_config_path)
|
|
346
|
+
if is_legacy:
|
|
347
|
+
console.print("\n[yellow]⚠ LEGACY CONFIGURATION DETECTED[/yellow]")
|
|
348
|
+
console.print(
|
|
349
|
+
"[yellow]Your current configuration uses the legacy line-delimited JSON server:[/yellow]"
|
|
350
|
+
)
|
|
351
|
+
console.print(f"[dim] Command: {legacy_config.get('command')}[/dim]")
|
|
352
|
+
console.print(f"[dim] Args: {legacy_config.get('args')}[/dim]")
|
|
353
|
+
console.print(
|
|
354
|
+
"\n[red]This legacy server is incompatible with modern MCP clients (Codex, Claude Desktop/Code).[/red]"
|
|
355
|
+
)
|
|
356
|
+
console.print(
|
|
357
|
+
"[red]The legacy server uses line-delimited JSON instead of Content-Length framing.[/red]"
|
|
358
|
+
)
|
|
359
|
+
console.print(
|
|
360
|
+
"\n[cyan]✨ Automatically migrating to modern FastMCP-based server...[/cyan]"
|
|
361
|
+
)
|
|
362
|
+
force = True # Auto-enable force mode for migration
|
|
363
|
+
|
|
198
364
|
# Step 4: Load existing Codex configuration
|
|
199
365
|
codex_config = load_codex_config(codex_config_path)
|
|
200
366
|
|
|
@@ -207,13 +373,17 @@ def configure_codex_mcp(force: bool = False) -> None:
|
|
|
207
373
|
console.print("[dim]Use --force to overwrite existing configuration[/dim]")
|
|
208
374
|
return
|
|
209
375
|
else:
|
|
210
|
-
|
|
376
|
+
if not is_legacy:
|
|
377
|
+
console.print("[yellow]⚠ Overwriting existing configuration[/yellow]")
|
|
378
|
+
# If is_legacy, we already printed migration message above
|
|
211
379
|
|
|
212
380
|
# Step 6: Create mcp-ticketer server config
|
|
213
381
|
# For global config, include current working directory for context
|
|
214
|
-
|
|
382
|
+
project_path = str(Path.cwd())
|
|
215
383
|
server_config = create_codex_server_config(
|
|
216
|
-
|
|
384
|
+
python_path=python_path,
|
|
385
|
+
project_config=project_config,
|
|
386
|
+
project_path=project_path,
|
|
217
387
|
)
|
|
218
388
|
|
|
219
389
|
# Step 7: Update Codex configuration
|
|
@@ -232,12 +402,34 @@ def configure_codex_mcp(force: bool = False) -> None:
|
|
|
232
402
|
console.print("\n[bold]Configuration Details:[/bold]")
|
|
233
403
|
console.print(" Server name: mcp-ticketer")
|
|
234
404
|
console.print(f" Adapter: {adapter}")
|
|
235
|
-
console.print(f"
|
|
405
|
+
console.print(f" Python: {python_path}")
|
|
406
|
+
console.print(f" Command: {server_config.get('command')}")
|
|
407
|
+
console.print(f" Args: {server_config.get('args')}")
|
|
408
|
+
console.print(" Protocol: Content-Length framing (FastMCP SDK)")
|
|
236
409
|
console.print(" Scope: global (Codex only supports global config)")
|
|
237
|
-
console.print(f"
|
|
238
|
-
|
|
410
|
+
console.print(f" Project path: {project_path}")
|
|
411
|
+
|
|
412
|
+
# Step 9: Test configuration
|
|
413
|
+
console.print("\n[cyan]🧪 Testing configuration...[/cyan]")
|
|
414
|
+
test_success = _test_configuration(adapter, project_config)
|
|
415
|
+
|
|
416
|
+
if not test_success:
|
|
417
|
+
console.print(
|
|
418
|
+
"[yellow]⚠ Configuration saved but validation failed. "
|
|
419
|
+
"Please check your credentials and settings.[/yellow]"
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
# Migration success message (if legacy config was detected)
|
|
423
|
+
if is_legacy:
|
|
424
|
+
console.print("\n[green]✅ Migration Complete![/green]")
|
|
425
|
+
console.print(
|
|
426
|
+
"[green]Your configuration has been upgraded from legacy line-delimited JSON[/green]"
|
|
427
|
+
)
|
|
428
|
+
console.print(
|
|
429
|
+
"[green]to modern Content-Length framing (FastMCP SDK).[/green]"
|
|
430
|
+
)
|
|
239
431
|
console.print(
|
|
240
|
-
|
|
432
|
+
"\n[cyan]This fixes MCP connection issues with Codex and other modern clients.[/cyan]"
|
|
241
433
|
)
|
|
242
434
|
|
|
243
435
|
# Next steps
|