From 2f8edbdbd584f39f2b7fbe2ebff7d4b5b843100d Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 20:24:38 -0700 Subject: [PATCH 01/11] feat: Support multiple --mcp-servers-file arguments Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- cecli/args.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cecli/args.py b/cecli/args.py index 6d26d53bd13..c27f4b6cb7b 100644 --- a/cecli/args.py +++ b/cecli/args.py @@ -366,8 +366,9 @@ def get_parser(default_config_files, git_root): group.add_argument( "--mcp-servers-file", metavar="MCP_CONFIG_FILE", - help="Specify a file path with MCP server configurations", - default=None, + help="Specify a file path with MCP server configurations (can be specified multiple times)", + action="append", + default=[], ) group.add_argument( "--mcp-transport", From a51c0d3b0ce0ce4b3e0691aeeaf67742c224d959 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 20:29:25 -0700 Subject: [PATCH 02/11] refactor: Update mcp/utils.py to handle multiple server files Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- cecli/mcp/utils.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cecli/mcp/utils.py b/cecli/mcp/utils.py index 0bfc919f991..a0e2fbe5a80 100644 --- a/cecli/mcp/utils.py +++ b/cecli/mcp/utils.py @@ -149,7 +149,7 @@ def _parse_mcp_servers_from_file(file_path, io, verbose=False, mcp_transport="st def load_mcp_servers( - mcp_servers, mcp_servers_file, io, verbose=False, mcp_transport="stdio" + mcp_servers, mcp_servers_files, io, verbose=False, mcp_transport="stdio" ) -> list["McpServer"]: """Load MCP servers from a JSON string or file.""" servers = [] @@ -160,9 +160,14 @@ def load_mcp_servers( if servers: return servers - # If JSON string failed or wasn't provided, try the file - if mcp_servers_file: - servers = _parse_mcp_servers_from_file(mcp_servers_file, io, verbose, mcp_transport) + # If JSON string failed or wasn't provided, try the files + if mcp_servers_files: + servers = [] + for mcp_servers_file in mcp_servers_files: + file_servers = _parse_mcp_servers_from_file( + mcp_servers_file, io, verbose, mcp_transport + ) + servers.extend(file_servers) if not servers: # A default MCP server is actually now necessary for the overall agentic loop From 5beb691e13fe3a2ca016c93b31777be8b1419664 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 20:32:16 -0700 Subject: [PATCH 03/11] feat: Add test for multiple mcp-servers-file arguments Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- tests/basic/test_main.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index 3fa6730c035..4fc891c1511 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1368,6 +1368,45 @@ def test_mcp_servers_parsing(dummy_io, git_temp_dir, mocker): assert len(kwargs["mcp_manager"].servers) > 0 assert hasattr(kwargs["mcp_manager"].servers[0], "name") + +def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): + mock_coder_create = mocker.patch("cecli.coders.Coder.create") + mock_coder_instance = MagicMock() + mock_coder_instance.mcp_manager = False + mock_coder_instance._autosave_future = mock_autosave_future() + mock_coder_create.return_value = mock_coder_instance + + mcp_file1 = Path("mcp_servers1.json") + mcp_content1 = {"mcpServers": {"server1": {"command": "cmd1"}}} + mcp_file1.write_text(json.dumps(mcp_content1)) + + mcp_file2 = Path("mcp_servers2.json") + mcp_content2 = {"mcpServers": {"server2": {"command": "cmd2"}}} + mcp_file2.write_text(json.dumps(mcp_content2)) + + main( + [ + "--mcp-servers-file", + str(mcp_file1), + "--mcp-servers-file", + str(mcp_file2), + "--exit", + "--yes-always", + ], + **dummy_io, + ) + + mock_coder_create.assert_called_once() + _, kwargs = mock_coder_create.call_args + + assert "mcp_manager" in kwargs + mcp_manager = kwargs["mcp_manager"] + assert mcp_manager is not None + assert len(mcp_manager.servers) == 2 + server_names = {server.name for server in mcp_manager.servers} + assert "server1" in server_names + assert "server2" in server_names + mock_coder_create.reset_mock() mock_coder_instance._autosave_future = mock_autosave_future() mcp_file = Path("mcp_servers.json") From 59c5166a7f1850992f1de4e9705330cf0254d581 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 20:51:03 -0700 Subject: [PATCH 04/11] feat: Add support for multiple --mcp-servers-file arguments Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- tests/basic/test_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index 4fc891c1511..82cc962fa3e 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1377,11 +1377,11 @@ def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): mock_coder_create.return_value = mock_coder_instance mcp_file1 = Path("mcp_servers1.json") - mcp_content1 = {"mcpServers": {"server1": {"command": "cmd1"}}} + mcp_content1 = {"mcpServers": {"server1": {"command": "uvx"}}} mcp_file1.write_text(json.dumps(mcp_content1)) mcp_file2 = Path("mcp_servers2.json") - mcp_content2 = {"mcpServers": {"server2": {"command": "cmd2"}}} + mcp_content2 = {"mcpServers": {"server2": {"command": "uvx"}}} mcp_file2.write_text(json.dumps(mcp_content2)) main( From 7307d76d2178ffe0f7d52d71ca1e5078e3541496 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 21:02:12 -0700 Subject: [PATCH 05/11] feat: Add support for multiple --mcp-servers-file arguments Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- tests/basic/test_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index 82cc962fa3e..82efdef5669 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1377,11 +1377,11 @@ def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): mock_coder_create.return_value = mock_coder_instance mcp_file1 = Path("mcp_servers1.json") - mcp_content1 = {"mcpServers": {"server1": {"command": "uvx"}}} + mcp_content1 = {"mcpServers": {"server1": {"command": "python"}}} mcp_file1.write_text(json.dumps(mcp_content1)) mcp_file2 = Path("mcp_servers2.json") - mcp_content2 = {"mcpServers": {"server2": {"command": "uvx"}}} + mcp_content2 = {"mcpServers": {"server2": {"command": "python"}}} mcp_file2.write_text(json.dumps(mcp_content2)) main( From f37c97f1dbc94756fcb2b3916593eaa25fcde139 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 21:05:01 -0700 Subject: [PATCH 06/11] feat: Add test for multiple mcp-servers-file arguments Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- tests/basic/test_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index 82efdef5669..c5f0600b3c7 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1377,11 +1377,11 @@ def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): mock_coder_create.return_value = mock_coder_instance mcp_file1 = Path("mcp_servers1.json") - mcp_content1 = {"mcpServers": {"server1": {"command": "python"}}} + mcp_content1 = {"mcpServers": {"server1": {"notes": "notes for server1"}}} mcp_file1.write_text(json.dumps(mcp_content1)) mcp_file2 = Path("mcp_servers2.json") - mcp_content2 = {"mcpServers": {"server2": {"command": "python"}}} + mcp_content2 = {"mcpServers": {"server2": {"notes": "notes for server2"}}} mcp_file2.write_text(json.dumps(mcp_content2)) main( From 7e1ade08c0c4e1279ef4962b3ff248e8bea364e7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 21:13:48 -0700 Subject: [PATCH 07/11] feat: Add test for multiple mcp-servers-file arguments Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- tests/basic/test_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index c5f0600b3c7..f9cae0d5a3d 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1377,11 +1377,11 @@ def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): mock_coder_create.return_value = mock_coder_instance mcp_file1 = Path("mcp_servers1.json") - mcp_content1 = {"mcpServers": {"server1": {"notes": "notes for server1"}}} + mcp_content1 = {"mcpServers": {"server1": {}}} mcp_file1.write_text(json.dumps(mcp_content1)) mcp_file2 = Path("mcp_servers2.json") - mcp_content2 = {"mcpServers": {"server2": {"notes": "notes for server2"}}} + mcp_content2 = {"mcpServers": {"server2": {}}} mcp_file2.write_text(json.dumps(mcp_content2)) main( From 1794eca4f77495261d3d658a19c450c65f1d9114 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 21:17:19 -0700 Subject: [PATCH 08/11] feat: Add support for multiple --mcp-servers-file arguments Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- tests/basic/test_main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index f9cae0d5a3d..f47dc654e50 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1370,6 +1370,7 @@ def test_mcp_servers_parsing(dummy_io, git_temp_dir, mocker): def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): + mocker.patch("cecli.mcp.server.McpServer.start", new_callable=AsyncMock) mock_coder_create = mocker.patch("cecli.coders.Coder.create") mock_coder_instance = MagicMock() mock_coder_instance.mcp_manager = False @@ -1377,11 +1378,11 @@ def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): mock_coder_create.return_value = mock_coder_instance mcp_file1 = Path("mcp_servers1.json") - mcp_content1 = {"mcpServers": {"server1": {}}} + mcp_content1 = {"mcpServers": {"server1": {"command": "cmd1"}}} mcp_file1.write_text(json.dumps(mcp_content1)) mcp_file2 = Path("mcp_servers2.json") - mcp_content2 = {"mcpServers": {"server2": {}}} + mcp_content2 = {"mcpServers": {"server2": {"command": "cmd2"}}} mcp_file2.write_text(json.dumps(mcp_content2)) main( From 73855129ddf6c340b186a691ac2353065054d334 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 25 Mar 2026 23:58:09 -0700 Subject: [PATCH 09/11] cli-3: multiple mcp servers files --- tests/basic/test_main.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index f47dc654e50..8ee766ce46e 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1368,6 +1368,20 @@ def test_mcp_servers_parsing(dummy_io, git_temp_dir, mocker): assert len(kwargs["mcp_manager"].servers) > 0 assert hasattr(kwargs["mcp_manager"].servers[0], "name") + mock_coder_create.reset_mock() + mock_coder_instance._autosave_future = mock_autosave_future() + mcp_file = Path("mcp_servers.json") + mcp_content = {"mcpServers": {"git": {"command": "uvx", "args": ["mcp-server-git"]}}} + mcp_file.write_text(json.dumps(mcp_content)) + main(["--mcp-servers-file", str(mcp_file), "--exit", "--yes-always"], **dummy_io) + mock_coder_create.assert_called_once() + _, kwargs = mock_coder_create.call_args + + assert "mcp_manager" in kwargs + assert kwargs["mcp_manager"] is not None + assert len(kwargs["mcp_manager"].servers) > 0 + assert hasattr(kwargs["mcp_manager"].servers[0], "name") + def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): mocker.patch("cecli.mcp.server.McpServer.start", new_callable=AsyncMock) @@ -1407,17 +1421,3 @@ def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): server_names = {server.name for server in mcp_manager.servers} assert "server1" in server_names assert "server2" in server_names - - mock_coder_create.reset_mock() - mock_coder_instance._autosave_future = mock_autosave_future() - mcp_file = Path("mcp_servers.json") - mcp_content = {"mcpServers": {"git": {"command": "uvx", "args": ["mcp-server-git"]}}} - mcp_file.write_text(json.dumps(mcp_content)) - main(["--mcp-servers-file", str(mcp_file), "--exit", "--yes-always"], **dummy_io) - mock_coder_create.assert_called_once() - _, kwargs = mock_coder_create.call_args - - assert "mcp_manager" in kwargs - assert kwargs["mcp_manager"] is not None - assert len(kwargs["mcp_manager"].servers) > 0 - assert hasattr(kwargs["mcp_manager"].servers[0], "name") From 570985394244455826d5422753c9681ee984c772 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 26 Mar 2026 11:27:21 -0700 Subject: [PATCH 10/11] feat: Add support for multiple --mcp-servers-files and deprecate old arg Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- cecli/args.py | 9 ++++++--- cecli/deprecated_args.py | 11 +++++++++++ cecli/main.py | 11 ++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/cecli/args.py b/cecli/args.py index c27f4b6cb7b..2a8b9ebeea8 100644 --- a/cecli/args.py +++ b/cecli/args.py @@ -14,7 +14,7 @@ MarkdownHelpFormatter, YamlHelpFormatter, ) -from cecli.deprecated_args import add_deprecated_model_args +from cecli.deprecated_args import add_deprecated_mcp_args, add_deprecated_model_args from .dump import dump # noqa: F401 @@ -364,8 +364,8 @@ def get_parser(default_config_files, git_root): default=None, ) group.add_argument( - "--mcp-servers-file", - metavar="MCP_CONFIG_FILE", + "--mcp-servers-files", + metavar="MCP_CONFIG_FILES", help="Specify a file path with MCP server configurations (can be specified multiple times)", action="append", default=[], @@ -1090,6 +1090,9 @@ def get_parser(default_config_files, git_root): # Add deprecated model shortcut arguments add_deprecated_model_args(parser, group) + group = parser.add_argument_group("Deprecated agent settings") + add_deprecated_mcp_args(parser, group) + return parser diff --git a/cecli/deprecated_args.py b/cecli/deprecated_args.py index e5b3986f31f..ca80605fa8b 100644 --- a/cecli/deprecated_args.py +++ b/cecli/deprecated_args.py @@ -142,6 +142,17 @@ def add_deprecated_model_args(parser, group): ) +def add_deprecated_mcp_args(parser, group): + """Add deprecated mcp arguments to the argparse parser.""" + group.add_argument( + "--mcp-servers-file", + help=argparse.SUPPRESS, + action="append", + dest="mcp_servers_file_deprecated", + env_var="CECLI_MCP_SERVERS_FILE", + ) + + def handle_deprecated_model_args(args, io): """Handle deprecated model shortcut arguments and provide appropriate warnings.""" # Define model mapping diff --git a/cecli/main.py b/cecli/main.py index 2fea8b4946a..b9fc425509b 100644 --- a/cecli/main.py +++ b/cecli/main.py @@ -1065,8 +1065,17 @@ def apply_model_overrides(model_name): # Default since some models do not have max_input_tokens specified somehow args.context_compaction_max_tokens = 65536 try: + if getattr(args, "mcp_servers_file_deprecated", None): + io.tool_warning( + "The --mcp-servers-file argument is deprecated and will be removed in a future" + " version. Please use --mcp-servers-files instead." + ) + if not args.mcp_servers_files: + args.mcp_servers_files = [] + args.mcp_servers_files.extend(args.mcp_servers_file_deprecated) + mcp_servers = load_mcp_servers( - args.mcp_servers, args.mcp_servers_file, io, args.verbose, args.mcp_transport + args.mcp_servers, args.mcp_servers_files, io, args.verbose, args.mcp_transport ) mcp_manager = await McpServerManager.from_servers(mcp_servers, io, args.verbose) # Load hooks if specified From d5f618a6c9b0304d2c3dc5a92dd7cc48a8c57715 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 26 Mar 2026 13:19:52 -0700 Subject: [PATCH 11/11] fix: Update tests/basic/test_main.py for argument and mock changes Co-authored-by: cecli (openai/gemini_cli/gemini-2.5-pro) --- tests/basic/test_main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index 8ee766ce46e..4bd8456e842 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1373,7 +1373,7 @@ def test_mcp_servers_parsing(dummy_io, git_temp_dir, mocker): mcp_file = Path("mcp_servers.json") mcp_content = {"mcpServers": {"git": {"command": "uvx", "args": ["mcp-server-git"]}}} mcp_file.write_text(json.dumps(mcp_content)) - main(["--mcp-servers-file", str(mcp_file), "--exit", "--yes-always"], **dummy_io) + main(["--mcp-servers-files", str(mcp_file), "--exit", "--yes-always"], **dummy_io) mock_coder_create.assert_called_once() _, kwargs = mock_coder_create.call_args @@ -1384,7 +1384,7 @@ def test_mcp_servers_parsing(dummy_io, git_temp_dir, mocker): def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): - mocker.patch("cecli.mcp.server.McpServer.start", new_callable=AsyncMock) + mocker.patch("cecli.mcp.server.McpServer.connect", new_callable=AsyncMock) mock_coder_create = mocker.patch("cecli.coders.Coder.create") mock_coder_instance = MagicMock() mock_coder_instance.mcp_manager = False @@ -1401,9 +1401,9 @@ def test_mcp_servers_file_multiple(dummy_io, git_temp_dir, mocker): main( [ - "--mcp-servers-file", + "--mcp-servers-files", str(mcp_file1), - "--mcp-servers-file", + "--mcp-servers-files", str(mcp_file2), "--exit", "--yes-always",