Skip to content

Conversation

@jtcorbett
Copy link
Contributor

@jtcorbett jtcorbett commented Oct 21, 2025

TL;DR

Added a new install command to the MCP Agent CLI for adding MCP servers to client applications.

What changed?

  • Added a new install command that provides end-to-end installation of MCP servers to client applications
  • Supports multiple client applications: VSCode, Claude Code, Cursor, Claude Desktop, and ChatGPT
  • Handles authentication, server configuration, and writing server configuration to client config files
  • Automatically detects required secrets and configures them
  • Provides options for customizing server name, using existing secrets files, and forcing overwrites

How to test?

# Install to VSCode
mcp-agent install https://api.example.com/servers/my-server/mcp --client vscode

# Install to Claude Code with custom name
mcp-agent install app-id-123 --client claude_code --name my-server

# Install with existing secrets file
mcp-agent install my-server --client cursor --secrets-file secrets.yaml

# Dry run to validate without making changes
mcp-agent install my-server --client claude_desktop --dry-run

Summary by CodeRabbit

  • New Features
    • New top-level install CLI command to add MCP servers to supported clients (Claude Desktop, VSCode, Cursor, ChatGPT, Claude Code).
    • Supports dry-run preview, force overwrite, platform-aware config detection, and per-client authentication handling with clear user feedback.
  • Tests
    • Added comprehensive tests covering install flows, validations, merging behavior, transport detection, and dry-run/force scenarios.

Copy link
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new top-level install CLI command and exposes its submodule; implements client-specific MCP server installation logic (path resolution, JSON merge/write, server config builders, transport/auth handling, dry-run), and adds tests covering helpers and install flows.

Changes

Cohort / File(s) Summary
CLI Command Exports
src/mcp_agent/cli/commands/__init__.py
Added relative import of install and added "install" to __all__ to expose the new command module.
Install Command Implementation
src/mcp_agent/cli/commands/install.py
New Typer command module implementing install CLI: client detection, platform-aware config paths, _build_server_config, _merge_mcp_json, atomic _write_json, secret redaction, Claude Desktop/VSCode/Cursor/ChatGPT handling, dry-run/force options, subprocess integration for Claude Code, validation and error handling.
CLI Top-Level Registration
src/mcp_agent/cli/main.py
Imported install command module and registered it as a top-level Typer subcommand (install) with help text.
Tests
tests/cli/commands/test_install.py
New tests covering hostname derivation, server config building, JSON merging, error paths (missing API key, unsupported client, invalid URL), VSCode/Cursor install flows, duplicate handling (--force), ChatGPT auth rules, and transport detection.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant MCPClient as MCPAppClient
    participant Config
    participant FS as FileSystem

    User->>CLI: install <server_identifier> --client <target> [--dry-run/--force]
    CLI->>CLI: validate args, resolve api_key
    CLI->>MCPClient: fetch server info / validate URL
    alt server requires auth & client is chatgpt
        MCPClient-->>CLI: auth required
        CLI-->>User: exit with guidance (unauthenticated required)
    else proceed
        MCPClient-->>CLI: server metadata
        CLI->>Config: _build_server_config(server_url, api_key, client)
        Config->>Config: _merge_mcp_json(existing, server_name, server_config)
        alt dry-run
            CLI-->>User: show redacted merged config and target path
        else write
            Config->>FS: _write_json(path, data) (atomic + perms)
            FS-->>Config: success
            CLI-->>User: summary + post-install notes
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • saqadri

"I'm a rabbit with a config in paw,
I stitched your server into files you saw.
Dry-run to preview, force to replace,
VSCode, Claude, Cursor — each finds its place.
Hop on, run install, and enjoy the new space!"

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "add install command to CLI" directly and clearly describes the primary change in this pull request. The changeset introduces a new install command module, exports it through the package structure, registers it at the top-level CLI, and includes comprehensive tests—all of which align with the stated title. The title is concise, specific enough to convey meaning upon scanning history, and avoids vague or generic terminology.
Docstring Coverage ✅ Passed Docstring coverage is 96.55% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 10-21-add_install_command_to_cli

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jtcorbett jtcorbett marked this pull request as ready for review October 22, 2025 00:20
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (8)
src/mcp_agent/cli/commands/install.py (3)

240-245: Don’t require API key for ChatGPT-only flow

ChatGPT installs don’t need our API. Allow proceeding without a key and skip cloud lookups when absent.

-    effective_api_key = api_key or settings.API_KEY or load_api_key_credentials()
-    if not effective_api_key:
+    effective_api_key = api_key or settings.API_KEY or load_api_key_credentials()
+    if not effective_api_key and client_lc != "chatgpt":
         raise CLIError(
             "Must be logged in to install. Run 'mcp-agent login', set MCP_API_KEY environment variable, or specify --api-key option."
         )

Optionally instantiate MCPAppClient only if an API key is present or when verification is desired.


66-69: Minor: Unused path for Claude Code in CLIENT_CONFIGS

For claude_code we never write the file; consider noting that in description or removing path to avoid confusion.


361-385: VSCode writes project-local secrets; warn about Git

Storing API keys in .vscode/mcp.json risks accidental commits. Consider:

  • A warning advising .vscode/mcp.json in .gitignore.
  • A --no-embed-secret or --use-env option to avoid writing the key.
tests/cli/commands/test_install.py (5)

445-465: Add dry-run redaction assertion

After implementing redaction, assert that printed JSON does not contain the API key.

I can add a test capturing stdout and asserting "Bearer test-key" is absent.


422-443: Add ChatGPT SSE URL tests

Add cases for:

  • Input ending with /sse (no duplicate /sse).
  • Input ending with /mcp (display /sse).
    Capture output and assert the displayed URL.

253-285: File permission test (POSIX-only)

After hardening _write_json, add a POSIX-only test to assert 0o600 on created files.


396-420: Optional: ChatGPT flow without API key

If you relax the API-key requirement for ChatGPT, add a test to ensure it proceeds and shows instructions when no key is available.


287-322: Cursor format expectation

You’re asserting mcpServers for Cursor. Once docs are fixed, consider adding a test ensuring merge preserves unrelated keys in existing configs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3580266 and 20d5021.

📒 Files selected for processing (4)
  • src/mcp_agent/cli/commands/__init__.py (2 hunks)
  • src/mcp_agent/cli/commands/install.py (1 hunks)
  • src/mcp_agent/cli/main.py (2 hunks)
  • tests/cli/commands/test_install.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/mcp_agent/cli/commands/__init__.py (1)
src/mcp_agent/cli/commands/install.py (1)
  • install (181-448)
src/mcp_agent/cli/commands/install.py (3)
src/mcp_agent/cli/auth/main.py (1)
  • load_api_key_credentials (108-115)
src/mcp_agent/cli/exceptions.py (1)
  • CLIError (4-10)
src/mcp_agent/cli/utils/ux.py (2)
  • print_info (32-49)
  • print_success (52-63)
src/mcp_agent/cli/main.py (1)
src/mcp_agent/cli/commands/install.py (1)
  • install (181-448)
tests/cli/commands/test_install.py (2)
src/mcp_agent/cli/commands/install.py (3)
  • install (181-448)
  • _build_server_config (132-177)
  • _merge_mcp_json (81-123)
src/mcp_agent/cli/exceptions.py (1)
  • CLIError (4-10)
🔇 Additional comments (3)
src/mcp_agent/cli/commands/__init__.py (1)

25-26: Wiring looks good

install is properly imported and exported for CLI discovery.

Also applies to: 45-46

src/mcp_agent/cli/main.py (2)

41-42: Import OK

install group is correctly imported as install_cmd.


190-192: Top-level command registration is correct

install is mounted at root with clear help.

Copy link
Collaborator

@saqadri saqadri left a comment

Choose a reason for hiding this comment

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

What does the secrets file setting do?

@jtcorbett jtcorbett merged commit af72b71 into main Oct 22, 2025
7 of 8 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
tests/cli/commands/test_install.py (2)

415-441: Move typer import to module level.

The typer import on line 417 should be at the module level (top of file) rather than inside the test function for consistency and clarity.

Apply this diff:

 import json
 from unittest.mock import AsyncMock, MagicMock, patch
 
 import pytest
+import typer
 from mcp_agent.cli.commands.install import (

Then remove line 417 from the test function.


1-533: Consider adding tests for additional scenarios.

While the current test coverage is good, consider adding tests for:

  • Claude Code subprocess installation path (currently untested)
  • Claude Desktop installation flow
  • Error handling: subprocess failures, JSON decode errors, file permission errors
  • Output message verification in dry-run mode

These additions would provide more comprehensive coverage of edge cases and error paths.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20d5021 and 88f7274.

📒 Files selected for processing (2)
  • src/mcp_agent/cli/commands/install.py (1 hunks)
  • tests/cli/commands/test_install.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/mcp_agent/cli/commands/install.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/cli/commands/test_install.py (1)
src/mcp_agent/cli/commands/install.py (4)
  • install (255-520)
  • _build_server_config (206-251)
  • _server_hostname (176-203)
  • _merge_mcp_json (86-128)
🔇 Additional comments (4)
tests/cli/commands/test_install.py (4)

1-14: LGTM!

Clean import structure with appropriate test dependencies and functions under test.


16-42: LGTM!

Well-structured fixtures that appropriately mock authenticated and unauthenticated app scenarios.


45-214: LGTM!

Comprehensive test coverage for helper functions including hostname extraction, server config building, and JSON merging across multiple client formats.


217-271: LGTM!

Good coverage of error validation paths. Minor note: tmp_path parameter on line 217 is unused but harmless.

Comment on lines +274 to +306
def test_install_vscode(tmp_path):
"""Test install to VSCode."""
vscode_config = tmp_path / ".vscode" / "mcp.json"

with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"

with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
install(
server_identifier=MOCK_APP_SERVER_URL,
client="vscode",
name="test-server",
dry_run=False,
force=False,
api_url="http://test-api",
api_key="test-key",
)

# Verify config file was created
assert vscode_config.exists()

# Verify config contents (VSCode format)
config = json.loads(vscode_config.read_text())
assert "servers" in config
assert "inputs" in config
assert "test-server" in config["servers"]
server = config["servers"]["test-server"]
assert server["url"] == MOCK_APP_SERVER_URL
assert server["type"] == "sse"
assert server["headers"]["Authorization"] == "Bearer test-key"

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mock MCPAppClient to avoid real API calls.

The test doesn't mock MCPAppClient, which means the install function will attempt to call mcp_client.get_app() during testing. While the implementation has a try-except that catches failures, making real API calls in unit tests creates reliability issues and can cause flaky tests.

Apply this pattern (similar to the ChatGPT tests) to mock the API client:

 def test_install_vscode(tmp_path):
     """Test install to VSCode."""
     vscode_config = tmp_path / ".vscode" / "mcp.json"
 
     with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
         with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
             mock_settings.API_KEY = "test-key"
             mock_settings.API_BASE_URL = "http://test-api"
 
-            with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
-                install(
+            with patch("mcp_agent.cli.commands.install.MCPAppClient") as mock_client_class:
+                mock_client = MagicMock()
+                mock_client.get_app = AsyncMock(return_value=None)
+                mock_client_class.return_value = mock_client
+
+                with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
+                    install(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_install_vscode(tmp_path):
"""Test install to VSCode."""
vscode_config = tmp_path / ".vscode" / "mcp.json"
with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"
with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
install(
server_identifier=MOCK_APP_SERVER_URL,
client="vscode",
name="test-server",
dry_run=False,
force=False,
api_url="http://test-api",
api_key="test-key",
)
# Verify config file was created
assert vscode_config.exists()
# Verify config contents (VSCode format)
config = json.loads(vscode_config.read_text())
assert "servers" in config
assert "inputs" in config
assert "test-server" in config["servers"]
server = config["servers"]["test-server"]
assert server["url"] == MOCK_APP_SERVER_URL
assert server["type"] == "sse"
assert server["headers"]["Authorization"] == "Bearer test-key"
def test_install_vscode(tmp_path):
"""Test install to VSCode."""
vscode_config = tmp_path / ".vscode" / "mcp.json"
with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"
with patch("mcp_agent.cli.commands.install.MCPAppClient") as mock_client_class:
mock_client = MagicMock()
mock_client.get_app = AsyncMock(return_value=None)
mock_client_class.return_value = mock_client
with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
install(
server_identifier=MOCK_APP_SERVER_URL,
client="vscode",
name="test-server",
dry_run=False,
force=False,
api_url="http://test-api",
api_key="test-key",
)
# Verify config file was created
assert vscode_config.exists()
# Verify config contents (VSCode format)
config = json.loads(vscode_config.read_text())
assert "servers" in config
assert "inputs" in config
assert "test-server" in config["servers"]
server = config["servers"]["test-server"]
assert server["url"] == MOCK_APP_SERVER_URL
assert server["type"] == "sse"
assert server["headers"]["Authorization"] == "Bearer test-key"
🤖 Prompt for AI Agents
In tests/cli/commands/test_install.py around lines 274 to 306, the test calls
install without mocking MCPAppClient so it can make real API calls; patch the
MCPAppClient used in mcp_agent.cli.commands.install to return a mock client
whose get_app method returns a predictable value (or raises as appropriate)
before calling install, and ensure the mock is configured in the same context as
the other patches (e.g., using patch(...) as mock_client and setting
mock_client.return_value.get_app.return_value or side_effect), so the install
flow uses the mocked API client and the test remains isolated and deterministic.

Comment on lines +308 to +343
def test_install_cursor_with_existing_config(tmp_path):
"""Test install to Cursor with existing configuration."""
cursor_config = tmp_path / ".cursor" / "mcp.json"
cursor_config.parent.mkdir(parents=True, exist_ok=True)

existing = {
"mcpServers": {
"existing-server": {
"url": "https://existing.com/mcp",
"transport": "http",
}
}
}
cursor_config.write_text(json.dumps(existing, indent=2))

with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"

with patch("mcp_agent.cli.commands.install.Path.home", return_value=tmp_path):
install(
server_identifier=MOCK_APP_SERVER_URL,
client="cursor",
name="new-server",
dry_run=False,
force=False,
api_url="http://test-api",
api_key="test-key",
)

config = json.loads(cursor_config.read_text())
assert len(config["mcpServers"]) == 2
assert "existing-server" in config["mcpServers"]
assert "new-server" in config["mcpServers"]

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mock MCPAppClient to avoid real API calls.

Same issue as the VSCode test—add MCPAppClient mock to prevent actual API calls during testing.

Comment on lines +345 to +377
def test_install_duplicate_without_force(tmp_path):
"""Test install fails when server already exists without --force."""
vscode_config = tmp_path / ".vscode" / "mcp.json"
vscode_config.parent.mkdir(parents=True, exist_ok=True)

existing = {
"servers": {
"test-server": {
"url": "https://old.com/mcp",
"type": "http",
}
},
"inputs": []
}
vscode_config.write_text(json.dumps(existing, indent=2))

with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"

with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
with pytest.raises(CLIError, match="already exists"):
install(
server_identifier=MOCK_APP_SERVER_URL,
client="vscode",
name="test-server",
dry_run=False,
force=False,
api_url="http://test-api",
api_key="test-key",
)

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mock MCPAppClient to avoid real API calls.

Same issue—add MCPAppClient mock to prevent actual API calls during testing.

🤖 Prompt for AI Agents
In tests/cli/commands/test_install.py around lines 345 to 377, the test still
allows real MCP API calls; patch the MCPAppClient used by the install command to
a mock to prevent network access. Add a patch for
"mcp_agent.cli.commands.install.MCPAppClient" (or the exact import path used in
the install module) that returns a simple Mock instance whose methods/attributes
used by install (constructor, any lookup or list calls) return safe dummy
values; wrap this mock patch in the same context managers as the other patches
so the test asserts the CLIError without making real API requests.

Comment on lines +379 to +413
def test_install_duplicate_with_force(tmp_path):
"""Test install overwrites when server exists with --force."""
vscode_config = tmp_path / ".vscode" / "mcp.json"
vscode_config.parent.mkdir(parents=True, exist_ok=True)

existing = {
"servers": {
"test-server": {
"url": "https://old.com/mcp",
"type": "http",
}
},
"inputs": []
}
vscode_config.write_text(json.dumps(existing, indent=2))

with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"

with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
install(
server_identifier=MOCK_APP_SERVER_URL,
client="vscode",
name="test-server",
dry_run=False,
force=True,
api_url="http://test-api",
api_key="test-key",
)

config = json.loads(vscode_config.read_text())
assert config["servers"]["test-server"]["url"] == MOCK_APP_SERVER_URL

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mock MCPAppClient to avoid real API calls.

Same issue—add MCPAppClient mock to prevent actual API calls during testing.

🤖 Prompt for AI Agents
In tests/cli/commands/test_install.py around lines 379 to 413, the test calls
install without mocking MCPAppClient which can trigger real API calls; patch
mcp_agent.cli.commands.install.MCPAppClient to a Mock before invoking install
and configure that mock to provide the methods/return values the install flow
expects (e.g., any create/get methods or attributes used to obtain the server
URL), so the test runs offline and only asserts the config file update.

Comment on lines +466 to +485
def test_install_dry_run(tmp_path, capsys):
"""Test install in dry run mode."""
with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"

with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
install(
server_identifier=MOCK_APP_SERVER_URL,
client="vscode",
name="test-server",
dry_run=True,
force=False,
api_url="http://test-api",
api_key="test-key",
)

vscode_config = tmp_path / ".vscode" / "mcp.json"
assert not vscode_config.exists()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mock MCPAppClient to avoid real API calls.

Same issue as other functional tests—add MCPAppClient mock to prevent actual API calls during testing.

🤖 Prompt for AI Agents
In tests/cli/commands/test_install.py around lines 466 to 485, the test calls
install() but does not mock MCPAppClient so the test may perform real API calls;
patch mcp_agent.cli.commands.install.MCPAppClient (e.g. using
unittest.mock.patch or patch.object) to return a mock client instance with the
attributes/methods the install flow expects (such as .get_application or
.register or context-manager behavior) and scope that patch around the install()
invocation so the test uses the fake client and no real network requests occur.

Comment on lines +488 to +510
def test_install_sse_transport_detection(tmp_path):
"""Test that SSE transport is detected from URL."""
vscode_config = tmp_path / ".vscode" / "mcp.json"

with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"

with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
install(
server_identifier="https://example.com/sse",
client="vscode",
name="test-server",
dry_run=False,
force=False,
api_url="http://test-api",
api_key="test-key",
)

config = json.loads(vscode_config.read_text())
assert config["servers"]["test-server"]["type"] == "sse"

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mock MCPAppClient to avoid real API calls.

Same issue—add MCPAppClient mock to prevent actual API calls during testing.

Comment on lines +512 to +533
def test_install_http_transport_detection(tmp_path):
"""Test that HTTP transport is detected from URL."""
vscode_config = tmp_path / ".vscode" / "mcp.json"

with patch("mcp_agent.cli.commands.install.load_api_key_credentials", return_value="test-key"):
with patch("mcp_agent.cli.commands.install.settings") as mock_settings:
mock_settings.API_KEY = "test-key"
mock_settings.API_BASE_URL = "http://test-api"

with patch("mcp_agent.cli.commands.install.Path.cwd", return_value=tmp_path):
install(
server_identifier="https://example.com/mcp",
client="vscode",
name="test-server",
dry_run=False,
force=False,
api_url="http://test-api",
api_key="test-key",
)

config = json.loads(vscode_config.read_text())
assert config["servers"]["test-server"]["type"] == "http"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mock MCPAppClient to avoid real API calls.

Same issue—add MCPAppClient mock to prevent actual API calls during testing.

🤖 Prompt for AI Agents
In tests/cli/commands/test_install.py around lines 512 to 533, the test calls
install without mocking MCPAppClient so it may perform real API calls; patch
mcp_agent.cli.commands.install.MCPAppClient to return a MagicMock before
invoking install, configure that mock (return_value) with any attributes/methods
the install flow expects (e.g., methods that fetch or create servers) to avoid
network calls, and apply the patch context the same way other dependencies are
patched so the install call uses the mocked client.

andrew-lastmile pushed a commit to andrew-lastmile/mcp-agent-fork that referenced this pull request Nov 4, 2025
### TL;DR

Added a new `install` command to the MCP Agent CLI for adding MCP servers to client applications.

### What changed?

- Added a new `install` command that provides end-to-end installation of MCP servers to client applications
- Supports multiple client applications: VSCode, Claude Code, Cursor, Claude Desktop, and ChatGPT
- Handles authentication, server configuration, and writing server configuration to client config files
- Automatically detects required secrets and configures them
- Provides options for customizing server name, using existing secrets files, and forcing overwrites

### How to test?

```
# Install to VSCode
mcp-agent install https://api.example.com/servers/my-server/mcp --client vscode

# Install to Claude Code with custom name
mcp-agent install app-id-123 --client claude_code --name my-server

# Install with existing secrets file
mcp-agent install my-server --client cursor --secrets-file secrets.yaml

# Dry run to validate without making changes
mcp-agent install my-server --client claude_desktop --dry-run
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

## Release Notes

- **New Features**
    - Added `install` CLI command to register MCP servers with supported client applications (Claude Desktop, VSCode, Cursor, ChatGPT, Claude Code)
    - Supports dry-run preview mode and force overwrite option for existing configurations
- **Tests**
    - Added comprehensive test coverage for install command validation and client-specific configurations

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

3 participants