Skip to content

Commit fcd6d87

Browse files
committed
Fix install prompt on headless Windows environments
1 parent 0e214da commit fcd6d87

File tree

1 file changed

+47
-13
lines changed

1 file changed

+47
-13
lines changed

src/mcpm/commands/install.py

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
"""
2-
Install command for adding MCP servers to the global configuration
3-
"""
1+
"""Install command for adding MCP servers to the global configuration"""
42

53
import json
4+
import logging
65
import os
76
import re
87
from enum import Enum
8+
from typing import Optional
99

1010
from prompt_toolkit import PromptSession
1111
from prompt_toolkit.formatted_text import HTML
@@ -24,12 +24,36 @@
2424
from mcpm.utils.rich_click_config import click
2525

2626
console = Console()
27+
logger = logging.getLogger(__name__)
2728
repo_manager = RepositoryManager()
2829
profile_config_manager = ProfileConfigManager()
2930
global_config_manager = GlobalConfigManager()
3031

31-
# Create a prompt session with custom styling
32-
prompt_session = PromptSession()
32+
# Create a prompt session with custom styling (lazy to support headless Windows runs)
33+
prompt_session: Optional[PromptSession] = None
34+
_prompt_session_error: Optional[Exception] = None
35+
36+
37+
def _get_prompt_session() -> Optional[PromptSession]:
38+
"""Lazily create a PromptSession, tolerating environments without a console."""
39+
40+
global prompt_session, _prompt_session_error # noqa: PLW0603 - shared module state
41+
42+
if prompt_session is not None:
43+
return prompt_session
44+
45+
if _prompt_session_error is not None:
46+
return None
47+
48+
try:
49+
prompt_session = PromptSession()
50+
except Exception as exc: # prompt_toolkit raises NoConsoleScreenBufferError on Windows
51+
_prompt_session_error = exc
52+
logger.debug("Falling back to basic input prompts: %s", exc)
53+
prompt_session = None
54+
55+
return prompt_session
56+
3357
style = Style.from_dict(
3458
{
3559
"prompt": "ansicyan bold",
@@ -88,14 +112,24 @@ def prompt_with_default(prompt_text, default="", hide_input=False, required=Fals
88112
# console.print(f"Default: [yellow]{default}[/]")
89113

90114
# Get user input
115+
session = _get_prompt_session()
116+
91117
try:
92-
result = prompt_session.prompt(
93-
message=HTML(f"<prompt>{prompt_text}</prompt> > "),
94-
style=style,
95-
default=default,
96-
is_password=hide_input,
97-
key_bindings=kb,
98-
)
118+
if session is not None:
119+
result = session.prompt(
120+
message=HTML(f"<prompt>{prompt_text}</prompt> > "),
121+
style=style,
122+
default=default,
123+
is_password=hide_input,
124+
key_bindings=kb,
125+
)
126+
else:
127+
# Basic fallback for environments without an interactive console (e.g., headless Windows)
128+
prompt_parts = [prompt_text]
129+
if default:
130+
prompt_parts.append(f"[{default}]")
131+
prompt_display = " ".join(prompt_parts) + " > "
132+
result = input(prompt_display) # noqa: PLW1513 - suppressed by KeyboardInterrupt handling
99133

100134
# Empty result for non-required field means leave it empty
101135
if not result.strip() and not required:
@@ -111,7 +145,7 @@ def prompt_with_default(prompt_text, default="", hide_input=False, required=Fals
111145
return prompt_with_default(prompt_text, default, hide_input, required)
112146

113147
return result
114-
except KeyboardInterrupt:
148+
except (KeyboardInterrupt, EOFError):
115149
raise click.Abort()
116150

117151

0 commit comments

Comments
 (0)