Skip to content

Commit 0e801d0

Browse files
feat(cli): let shared mcp configure target mistral vibe
Reuse the existing MCP configure flags where possible and map them onto Mistral Vibe's native transport and auth fields so the host works through the same CLI workflow as the other integrations.
1 parent f213971 commit 0e801d0

File tree

3 files changed

+281
-96
lines changed

3 files changed

+281
-96
lines changed

hatch/cli/__main__.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -585,10 +585,10 @@ def _setup_mcp_commands(subparsers):
585585
)
586586
server_type_group.add_argument(
587587
"--url",
588-
help="Server URL for remote MCP servers (SSE transport) [hosts: all except claude-desktop, claude-code]",
588+
help="Server URL for remote MCP servers (SSE/streamable transport) [hosts: all except claude-desktop, claude-code]",
589589
)
590590
server_type_group.add_argument(
591-
"--http-url", help="HTTP streaming endpoint URL [hosts: gemini]"
591+
"--http-url", help="HTTP streaming endpoint URL [hosts: gemini, mistral-vibe]"
592592
)
593593

594594
mcp_configure_parser.add_argument(
@@ -667,12 +667,12 @@ def _setup_mcp_commands(subparsers):
667667
mcp_configure_parser.add_argument(
668668
"--startup-timeout",
669669
type=int,
670-
help="Server startup timeout in seconds (default: 10) [hosts: codex]",
670+
help="Server startup timeout in seconds (default: 10) [hosts: codex, mistral-vibe]",
671671
)
672672
mcp_configure_parser.add_argument(
673673
"--tool-timeout",
674674
type=int,
675-
help="Tool execution timeout in seconds (default: 60) [hosts: codex]",
675+
help="Tool execution timeout in seconds (default: 60) [hosts: codex, mistral-vibe]",
676676
)
677677
mcp_configure_parser.add_argument(
678678
"--enabled",
@@ -683,12 +683,35 @@ def _setup_mcp_commands(subparsers):
683683
mcp_configure_parser.add_argument(
684684
"--bearer-token-env-var",
685685
type=str,
686-
help="Name of environment variable containing bearer token for Authorization header [hosts: codex]",
686+
help="Name of environment variable containing bearer token for Authorization header [hosts: codex, mistral-vibe]",
687687
)
688688
mcp_configure_parser.add_argument(
689689
"--env-header",
690690
action="append",
691-
help="HTTP header from environment variable in KEY=ENV_VAR_NAME format [hosts: codex]",
691+
help="HTTP header from environment variable in KEY=ENV_VAR_NAME format [hosts: codex, mistral-vibe]",
692+
)
693+
694+
# Mistral Vibe-specific arguments
695+
mcp_configure_parser.add_argument(
696+
"--prompt", help="Per-server prompt override [hosts: mistral-vibe]"
697+
)
698+
mcp_configure_parser.add_argument(
699+
"--sampling-enabled",
700+
action="store_true",
701+
default=None,
702+
help="Enable model sampling for tool calls [hosts: mistral-vibe]",
703+
)
704+
mcp_configure_parser.add_argument(
705+
"--api-key-env",
706+
help="Environment variable containing API key for remote auth [hosts: mistral-vibe]",
707+
)
708+
mcp_configure_parser.add_argument(
709+
"--api-key-header",
710+
help="HTTP header name used for API key injection [hosts: mistral-vibe]",
711+
)
712+
mcp_configure_parser.add_argument(
713+
"--api-key-format",
714+
help="Formatting template for API key header values [hosts: mistral-vibe]",
692715
)
693716

694717
mcp_configure_parser.add_argument(

hatch/cli/cli_mcp.py

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- codex: OpenAI Codex
1414
- lm-studio: LM Studio
1515
- gemini: Google Gemini
16+
- mistral-vibe: Mistral Vibe CLI
1617
1718
Command Groups:
1819
Discovery:
@@ -71,6 +72,72 @@
7172
)
7273

7374

75+
def _apply_mistral_vibe_cli_mappings(
76+
config_data: dict,
77+
*,
78+
command: Optional[str],
79+
url: Optional[str],
80+
http_url: Optional[str],
81+
bearer_token_env_var: Optional[str],
82+
env_header: Optional[list],
83+
api_key_env: Optional[str],
84+
api_key_header: Optional[str],
85+
api_key_format: Optional[str],
86+
) -> dict:
87+
"""Map generic CLI flags to Mistral Vibe's host-native MCP fields."""
88+
result = dict(config_data)
89+
result.pop("cwd", None)
90+
91+
if command is not None:
92+
result["transport"] = "stdio"
93+
elif http_url is not None:
94+
result.pop("httpUrl", None)
95+
result["url"] = http_url
96+
result["transport"] = "http"
97+
elif url is not None:
98+
result["transport"] = "streamable-http"
99+
100+
if env_header and len(env_header) > 1:
101+
raise ValidationError(
102+
"mistral-vibe supports at most one --env-header mapping",
103+
field="--env-header",
104+
suggestion=(
105+
"Use a single KEY=ENV_VAR pair or the dedicated --api-key-* flags"
106+
),
107+
)
108+
109+
mapped_api_key_env = api_key_env
110+
mapped_api_key_header = api_key_header
111+
mapped_api_key_format = api_key_format
112+
113+
if env_header:
114+
header_name, env_var_name = env_header[0].split("=", 1)
115+
if mapped_api_key_header is None:
116+
mapped_api_key_header = header_name
117+
if mapped_api_key_env is None:
118+
mapped_api_key_env = env_var_name
119+
120+
if bearer_token_env_var is not None:
121+
if mapped_api_key_env is None:
122+
mapped_api_key_env = bearer_token_env_var
123+
if mapped_api_key_header is None:
124+
mapped_api_key_header = "Authorization"
125+
if mapped_api_key_format is None:
126+
mapped_api_key_format = "Bearer {api_key}"
127+
128+
if mapped_api_key_env is not None:
129+
result["api_key_env"] = mapped_api_key_env
130+
if mapped_api_key_header is not None:
131+
result["api_key_header"] = mapped_api_key_header
132+
if mapped_api_key_format is not None:
133+
result["api_key_format"] = mapped_api_key_format
134+
135+
result.pop("bearer_token_env_var", None)
136+
result.pop("env_http_headers", None)
137+
138+
return result
139+
140+
74141
def handle_mcp_discover_hosts(args: Namespace) -> int:
75142
"""Handle 'hatch mcp discover hosts' command.
76143
@@ -1493,6 +1560,11 @@ def handle_mcp_configure(args: Namespace) -> int:
14931560
startup_timeout: Optional[int] = getattr(args, "startup_timeout", None)
14941561
tool_timeout: Optional[int] = getattr(args, "tool_timeout", None)
14951562
enabled: Optional[bool] = getattr(args, "enabled", None)
1563+
prompt: Optional[str] = getattr(args, "prompt", None)
1564+
sampling_enabled: Optional[bool] = getattr(args, "sampling_enabled", None)
1565+
api_key_env: Optional[str] = getattr(args, "api_key_env", None)
1566+
api_key_header: Optional[str] = getattr(args, "api_key_header", None)
1567+
api_key_format: Optional[str] = getattr(args, "api_key_format", None)
14961568
bearer_token_env_var: Optional[str] = getattr(
14971569
args, "bearer_token_env_var", None
14981570
)
@@ -1604,7 +1676,7 @@ def handle_mcp_configure(args: Namespace) -> int:
16041676
config_data["timeout"] = timeout
16051677
if trust:
16061678
config_data["trust"] = trust
1607-
if cwd is not None:
1679+
if cwd is not None and host_type != MCPHostType.MISTRAL_VIBE:
16081680
config_data["cwd"] = cwd
16091681
if http_url is not None:
16101682
config_data["httpUrl"] = http_url
@@ -1636,11 +1708,21 @@ def handle_mcp_configure(args: Namespace) -> int:
16361708
config_data["startup_timeout_sec"] = startup_timeout
16371709
if tool_timeout is not None:
16381710
config_data["tool_timeout_sec"] = tool_timeout
1711+
if prompt is not None:
1712+
config_data["prompt"] = prompt
1713+
if sampling_enabled is not None:
1714+
config_data["sampling_enabled"] = sampling_enabled
1715+
if api_key_env is not None:
1716+
config_data["api_key_env"] = api_key_env
1717+
if api_key_header is not None:
1718+
config_data["api_key_header"] = api_key_header
1719+
if api_key_format is not None:
1720+
config_data["api_key_format"] = api_key_format
16391721
if enabled is not None:
16401722
config_data["enabled"] = enabled
1641-
if bearer_token_env_var is not None:
1723+
if bearer_token_env_var is not None and host_type != MCPHostType.MISTRAL_VIBE:
16421724
config_data["bearer_token_env_var"] = bearer_token_env_var
1643-
if env_header is not None:
1725+
if env_header is not None and host_type != MCPHostType.MISTRAL_VIBE:
16441726
env_http_headers = {}
16451727
for header_spec in env_header:
16461728
if "=" in header_spec:
@@ -1649,6 +1731,19 @@ def handle_mcp_configure(args: Namespace) -> int:
16491731
if env_http_headers:
16501732
config_data["env_http_headers"] = env_http_headers
16511733

1734+
if host_type == MCPHostType.MISTRAL_VIBE:
1735+
config_data = _apply_mistral_vibe_cli_mappings(
1736+
config_data,
1737+
command=command,
1738+
url=url,
1739+
http_url=http_url,
1740+
bearer_token_env_var=bearer_token_env_var,
1741+
env_header=env_header,
1742+
api_key_env=api_key_env,
1743+
api_key_header=api_key_header,
1744+
api_key_format=api_key_format,
1745+
)
1746+
16521747
# Partial update merge logic
16531748
if is_update:
16541749
existing_data = existing_config.model_dump(
@@ -1661,6 +1756,7 @@ def handle_mcp_configure(args: Namespace) -> int:
16611756
existing_data.pop("command", None)
16621757
existing_data.pop("args", None)
16631758
existing_data.pop("type", None)
1759+
existing_data.pop("transport", None)
16641760

16651761
if command is not None and (
16661762
existing_config.url is not None
@@ -1670,6 +1766,10 @@ def handle_mcp_configure(args: Namespace) -> int:
16701766
existing_data.pop("httpUrl", None)
16711767
existing_data.pop("headers", None)
16721768
existing_data.pop("type", None)
1769+
existing_data.pop("transport", None)
1770+
existing_data.pop("api_key_env", None)
1771+
existing_data.pop("api_key_header", None)
1772+
existing_data.pop("api_key_format", None)
16731773

16741774
merged_data = {**existing_data, **config_data}
16751775
config_data = merged_data

0 commit comments

Comments
 (0)