studio: show MCP "Import config" on the add-server form#6030
studio: show MCP "Import config" on the add-server form#6030NilayYadav wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the ability to bulk-import MCP servers from standard JSON configuration files (such as those from Claude Desktop, Cursor, Cline, or VS Code). It adds backend utilities to parse and join stdio commands across different platforms, exposes a new /import endpoint that handles duplicates and per-entry errors gracefully, and updates the frontend UI to support file uploads. The review feedback highlights three valuable improvements: filtering out None values in environment variables to prevent them from being coerced into the string "None", dynamically referencing the correct configuration key (mcpServers vs servers) in error messages, and wrapping the frontend toast error descriptions in a container with whitespace-pre-line to preserve newline formatting.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def _coerce_str_dict(value: dict) -> dict[str, str]: | ||
| return {str(k): str(v) for k, v in value.items()} |
There was a problem hiding this comment.
When parsing environment variables or headers, if a value is explicitly set to null in the JSON config, converting it to the string "None" via str(v) can lead to unexpected behavior (e.g., the subprocess receiving the literal string "None" as an environment variable or header value, which is truthy).
It is safer to defensively filter out keys with None values.
| def _coerce_str_dict(value: dict) -> dict[str, str]: | |
| return {str(k): str(v) for k, v in value.items()} | |
| def _coerce_str_dict(value: dict) -> dict[str, str]: | |
| return {str(k): str(v) for k, v in value.items() if v is not None} |
| servers = config.get("mcpServers") | ||
| if servers is None: | ||
| servers = config.get("servers") | ||
| if servers is None: | ||
| return [], ["Config has no 'mcpServers' (or 'servers') object."] | ||
| if not isinstance(servers, dict): | ||
| return [], ["'mcpServers' must be an object mapping name -> server."] |
There was a problem hiding this comment.
If the configuration uses the VS Code alias "servers" instead of "mcpServers", but the value is not a dictionary, the error message still hardcodes 'mcpServers'.
Dynamically referencing the key that was actually matched in the config provides clearer and more accurate error feedback to the user.
| servers = config.get("mcpServers") | |
| if servers is None: | |
| servers = config.get("servers") | |
| if servers is None: | |
| return [], ["Config has no 'mcpServers' (or 'servers') object."] | |
| if not isinstance(servers, dict): | |
| return [], ["'mcpServers' must be an object mapping name -> server."] | |
| servers_key = "mcpServers" if "mcpServers" in config else "servers" | |
| servers = config.get(servers_key) | |
| if servers is None: | |
| return [], ["Config has no 'mcpServers' (or 'servers') object."] | |
| if not isinstance(servers, dict): | |
| return [], [f"'{servers_key}' must be an object mapping name -> server."] |
| if (result.errors.length) { | ||
| toast.warning(summary, { description: result.errors.slice(0, 5).join("\n") }); | ||
| } else { |
There was a problem hiding this comment.
In standard HTML rendering, newlines (\n) in text content are collapsed into a single space unless the element has white-space: pre-line or similar styling. This causes the sliced error messages to run together on a single line, making them difficult to read.
Wrapping the description in a div with the Tailwind class whitespace-pre-line ensures that each error is rendered on its own line.
if (result.errors.length) {
toast.warning(summary, {
description: (
<div className="whitespace-pre-line">
{result.errors.slice(0, 5).join("\n")}
</div>
),
});
} else {
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b92230bf5c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if sys.platform == "win32": | ||
| import subprocess | ||
|
|
||
| return subprocess.list2cmdline(parts) |
There was a problem hiding this comment.
Preserve Windows args containing quotes during import
When importing a Windows stdio config whose args contain literal quotes (for example a JSON CLI option like {"foo":"bar"}), subprocess.list2cmdline() emits backslash-escaped quotes, but the stored string is later split by parse_stdio_command() using shlex.split(..., posix=False), which does not unescape those sequences. The MCP server process then receives '{\\"foo\\":\\"bar\\"}' instead of the original JSON argument, so these imported servers can fail even though the config is valid; either the joiner needs to match the existing parser, or the parser needs to understand Windows command-line escaping.
Useful? React with 👍 / 👎.
Summary
Follow-up to #5943, fixing the UX bug reported on #5936. The "Import config" button only rendered in the MCP server list view, but the composer opens the dialog straight into the add-server form (
openToCreate={true}). So users land on a screen with no import option and assume the feature is missing, it only appeared after manually adding a server first.This surfaces the import on the add-server form too, so it's visible the moment the dialog opens.
What's changed
chat-mcp-servers-dialog.tsx: moved the single hidden file input to the dialog root so both views share it, and added an "Import servers from a config file" banner + Import config button to the create form.Testing
mcpServers.jsonfrom the form → all servers added; list updates.