Skip to content

Add direct HTTP type support and custom json key#12

Open
andrewwdao wants to merge 1 commit into
ztripez:mainfrom
andrewwdao:feat/http
Open

Add direct HTTP type support and custom json key#12
andrewwdao wants to merge 1 commit into
ztripez:mainfrom
andrewwdao:feat/http

Conversation

@andrewwdao
Copy link
Copy Markdown

Introduces support for custom MCP server keys in configuration files, enhances flexibility for HTTP-based server definitions, and improves handling of optional fields in configuration models.

  • Added a mcp_key field to MCPClientConfig and LocationConfig models, allowing users to specify the key under which MCP servers are stored in config files (e.g., "mcpServers" or "servers"). The CLI and sync logic now respect this key throughout add, sync, and status operations. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]
  • Updated MCPServerConfig to make command, args, and env fields optional, and added support for HTTP/SSE transport with type, url, and serverUrl fields. Validation logic now only requires command for non-HTTP transports.
  • When saving or normalizing server configs, args and env fields are omitted if empty, rather than defaulting to empty lists or dicts. Adjusted test cases and model usage to reflect the new optionality of args and env. [1] [2]

The changes make the tool more adaptable to different config structures and use cases.

Copilot AI review requested due to automatic review settings April 3, 2026 09:18
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends mcp-sync’s configuration model and sync logic to support (1) custom JSON keys for where MCP servers are stored (e.g., mcpServers vs servers) and (2) HTTP/SSE-style server definitions where command/args/env may be absent.

Changes:

  • Add mcp_key to client/location config so sync/status/scan can read/write MCP servers under a configurable JSON key.
  • Extend MCPServerConfig to support HTTP transport (type, url, serverUrl) and make command/args/env optional where appropriate.
  • Normalize serialization to omit None fields (and adjust tests/docs accordingly).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
mcp_sync/config/models.py Adds mcp_key fields and HTTP-capable MCPServerConfig with updated validation.
mcp_sync/sync.py Uses exclude_none=True, respects per-location mcp_key for file sync/status, and adjusts vacuum normalization for optional fields.
mcp_sync/config/settings.py Persists configs with exclude_none=True and threads mcp_key through add_location.
mcp_sync/main.py Adds --mcp-key CLI flag for add-location and uses it in scan output.
mcp_sync/clients/repository.py Propagates mcp_key from client definitions into discovered locations.
mcp_sync/client_definitions.json Adds a VS Code MCP config entry using mcp_key: "servers".
tests/test_config_models.py Updates expectations for optional args/env and adds HTTP transport validation tests.
README.md Documents --mcp-key and adds an HTTP server example.
Comments suppressed due to low confidence (1)

mcp_sync/sync.py:353

  • This block no longer skips URL/HTTP servers explicitly, but the comment still claims it is checking for URL-based servers. With the new HTTP configs, this will fall through to full_command = [] and log “Skipping server ... - no valid command”, which is misleading and also doesn’t account for serverUrl. Consider detecting HTTP/url-based servers here (e.g., type=="http" or url/serverUrl present) and handling them explicitly (skip with a clear message, or implement CLI support) so sync behavior is deterministic.
                for name, config in new_servers.items():
                    if name not in current_servers or current_servers[name] != config:
                        # Check if this is a URL-based server (SSE/HTTP)
                        command = config.get("command", [])
                        args = config.get("args", []) or []
                        env_vars = config.get("env", {})

                        # Build full command array - combine command and args
                        if isinstance(command, str):
                            full_command = [command] + args
                        elif isinstance(command, list):
                            full_command = command + args
                        else:
                            full_command = []

                        self.logger.debug(
                            f"Processing server {name}: command={command}, args={args}, "
                            f"full_command={full_command}"
                        )
                        if full_command:
                            self.executor.add_mcp_server(
                                client_id, client_config, name, full_command, env_vars
                            )
                        else:
                            self.logger.warning(f"Skipping server {name} - no valid command")


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"""Test that HTTP transport ignores empty command values."""
config = MCPServerConfig(type="http", command=" ", url="https://example.com/mcp")
assert config.type == "http"
assert config.command == " "
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test name/docstring says HTTP transport “ignores empty command values”, but the assertion checks that whitespace is preserved (" "). If the intended behavior is to ignore empty/whitespace commands for HTTP, the model validator should normalize whitespace-only commands to None (or the test/docstring should be updated to reflect that whitespace is allowed and retained).

Suggested change
assert config.command == " "
assert config.command is None

Copilot uses AI. Check for mistakes.
Comment thread mcp_sync/config/models.py
Comment on lines +18 to 20
# command is only required if transport type is not HTTP
if (self.type or "").strip().lower() != "http" and (not self.command or not self.command.strip()):
raise ValueError("Command cannot be empty")
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For type="http", command is optional, but there is no validation that an HTTP endpoint is actually provided (url or serverUrl). As-is, MCPServerConfig(type="http") will validate successfully but is not usable downstream. Consider validating that at least one of url/serverUrl is set when type is HTTP (and possibly normalizing/validating those fields).

Suggested change
# command is only required if transport type is not HTTP
if (self.type or "").strip().lower() != "http" and (not self.command or not self.command.strip()):
raise ValueError("Command cannot be empty")
transport_type = (self.type or "").strip().lower()
command = (self.command or "").strip()
url = (self.url or "").strip()
server_url = (self.serverUrl or "").strip()
# command is only required if transport type is not HTTP
if transport_type != "http" and not command:
raise ValueError("Command cannot be empty")
# HTTP transport requires at least one endpoint to be configured
if transport_type == "http" and not url and not server_url:
raise ValueError("HTTP transport requires either 'url' or 'serverUrl'")

Copilot uses AI. Check for mistakes.
Comment thread mcp_sync/sync.py
Comment on lines +607 to 610
# Support URL-based servers
config_data = server_info["config"].copy()
if "url" in config_data and "command" not in config_data:
self.logger.info(
f"Skipping URL-based server {server_name} - "
"not supported by current config model"
)
result.skipped_servers.append(server_name)
continue

# Normalize command format for MCPServerConfig validation
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vacuum_configs now claims to “Support URL-based servers”, but it still discovers servers only under the hard-coded mcpServers key earlier in this method and it registers discovered client locations without propagating their mcp_key. This means configs that store servers under a custom key (e.g. servers) won’t be vacuumed/imported correctly. Consider using each location’s mcp_key when reading servers and passing mcp_key into settings.add_location(...) when registering discovered clients.

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +188
path=path,
name=location_name,
type="manual",
mcp_key=mcp_key or "mcpServers"
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LocationConfig already defaults mcp_key to "mcpServers", so forcing mcp_key=mcp_key or "mcpServers" makes the value always persisted even when the user didn’t specify it (and defeats the intent of exclude_none=True to keep configs minimal). Consider passing mcp_key=mcp_key and letting the model default apply when it’s omitted.

Suggested change
path=path,
name=location_name,
type="manual",
mcp_key=mcp_key or "mcpServers"
path=path,
name=location_name,
type="manual",
mcp_key=mcp_key

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +208 to +211
},
"http-server": {
"type": "http",
"url": "https://example.com/mcp"
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON example here is not valid: the custom-server object is missing a comma after the env block, braces/indentation are mismatched, and the http-server object isn’t properly aligned/closed. This will confuse users copying the example; please fix the snippet to be valid JSON.

Suggested change
},
"http-server": {
"type": "http",
"url": "https://example.com/mcp"
},
"http-server": {
"type": "http",
"url": "https://example.com/mcp"

Copilot uses AI. Check for mistakes.
},
"vscode-mcp": {
"name": "VSCode Copilot",
"description": "Vscode Copilot MCP settings",
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor capitalization inconsistency in the description: “Vscode Copilot MCP settings” should use consistent product casing (e.g., “VS Code” / “VSCode”).

Suggested change
"description": "Vscode Copilot MCP settings",
"description": "VS Code Copilot MCP settings",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d17667703

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread mcp_sync/config/models.py
@model_validator(mode="after")
def validate_command(self):
# command is only required if transport type is not HTTP
if (self.type or "").strip().lower() != "http" and (not self.command or not self.command.strip()):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow URL-only servers to bypass command validation

The new vacuum path now attempts to import URL-based servers, but this validator still rejects any entry without command unless type is explicitly "http". In practice, this codebase already identifies remote transports by URL presence (for example, the CLI sync path checks url), so configs like { "url": "..." } will now fail with Command cannot be empty and never import, which breaks the advertised direct HTTP support. Treat url/serverUrl as remote transport indicators (or normalize type) before enforcing a command.

Useful? React with 👍 / 👎.

Comment thread mcp_sync/sync.py
Comment on lines 330 to 333
# Check if this is a URL-based server (SSE/HTTP)
url = config.get("url")
if url:
# This is a URL-based server - skip for now
self.logger.info(
f"Skipping URL-based server {name} (URL: {url}) - "
"CLI client URL support not fully implemented"
)
continue

command = config.get("command", [])
args = config.get("args", []) or []
env_vars = config.get("env", {})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude serverUrl transports from CLI command sync

This add/update block now processes all changed servers as command-based entries, but newly supported serverUrl configs can reach it without any command. That yields an empty command, gets skipped, and still marks the location as updated, so repeated syncs keep reporting changes without applying them. Add a guard for serverUrl (or type == "http") before command assembly so remote transports are consistently excluded from CLI command sync.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants