Skip to content

feat: added connect cli command#819

Open
pmdroid wants to merge 8 commits intomainfrom
pascal/connect-command
Open

feat: added connect cli command#819
pmdroid wants to merge 8 commits intomainfrom
pascal/connect-command

Conversation

@pmdroid
Copy link
Copy Markdown
Member

@pmdroid pmdroid commented Apr 13, 2026

Summary

  • New arcade connect command that logs in, creates/reuses an Arcade Cloud gateway, and configures your MCP client in one step
  • Supports 5 clients: Claude Desktop, Cursor, VS Code, Windsurf, Amazon Q
  • Selection modes: --toolkit, --tool, --preset, --gateway, --all, or interactive picker
  • Reuses existing gateways when one already covers the requested tools
  • Resolves gateway names to slugs (--gateway opencode finds slug pascal_opencode)
  • OAuth auth by default, --api-key fallback with auto-created project key
  • --slug option to set a custom gateway slug on creation
  • Tool catalog cached to ~/.arcade/cache/tools.json (5min TTL, scoped to org/project)
  • Fills in the three previously placeholder configure_*_arcade() functions
❯ uv run arcade connect cursor --toolkit x                                                                      
Fetching tool catalog...

Setting up gateway for toolkits: x

Checking existing gateways...
Found existing gateway: quickstart-x (slug: gw_3CHqdAlQXSSQ28soevSheOJvXzs)

Configuring cursor to connect to gateway: gw_3CHqdAlQXSSQ28soevSheOJvXzs

Configured Cursor with Arcade gateway 'x'
 Gateway URL: https://api.arcade.dev/mcp/gw_3CHqdAlQXSSQ28soevSheOJvXzs
 Config file: /Users/pascal/.cursor/mcp.json
 Restart Cursor for changes to take effect.

Setup complete!
 Gateway URL: https://api.arcade.dev/mcp/gw_3CHqdAlQXSSQ28soevSheOJvXzs
 Auth: OAuth (handled by your MCP client)

Try asking your AI assistant:
 - Post a tweet saying 'Hello from Arcade!'
 - Search recent tweets about AI tools

Note

Medium Risk
Adds a new end-to-end flow that performs OAuth login, calls Arcade Engine/Coordinator APIs (gateway + API key creation), and writes MCP client config files, so failures could affect remote resource creation and local client configuration.

Overview
Adds a new arcade connect CLI command that logs in (if needed), fetches/caches the user’s tool catalog, creates or reuses an Arcade Cloud gateway (optionally with a custom --slug), and writes the appropriate MCP client config to point at the gateway.

Implements real Arcade Cloud gateway configuration for claude, cursor, and vscode (replacing prior placeholders) and extends support to Windsurf and Amazon Q, including optional --api-key mode that auto-creates a project API key and writes it as a Bearer header.

Refocuses arcade configure on local filesystem servers (and nudges remote usage to connect), adds toolkit config helpers, expands test coverage for gateway/toolkit configuration and the new connect flow, and bumps the package version to 1.14.0.

Reviewed by Cursor Bugbot for commit d9357c1. Bugbot is set up for automated code reviews on this repo. Configure here.

@pmdroid pmdroid requested a review from EricGustin April 13, 2026 04:43
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Toolkit config function missing two supported clients
    • Added windsurf and amazonq branches to configure_client_toolkit() and updated the error message to list all 5 supported clients.
  • ✅ Fixed: Unused fetch_account_gateways function is dead code
    • Removed the unused fetch_account_gateways function and its corresponding tests since run_connect uses list_gateways instead.

Create PR

Or push these changes by commenting:

@cursor push de9b64f9f1
Preview (de9b64f9f1)
diff --git a/libs/arcade-cli/arcade_cli/configure.py b/libs/arcade-cli/arcade_cli/configure.py
--- a/libs/arcade-cli/arcade_cli/configure.py
+++ b/libs/arcade-cli/arcade_cli/configure.py
@@ -897,9 +897,55 @@
         console.print(f"   Config file: {_format_path_for_display(_config_path)}", style="dim")
         console.print("   Restart VS Code for changes to take effect.", style="yellow")
 
+    elif client_lower == "windsurf":
+        _config_path = config_path or get_windsurf_config_path()
+        if _config_path and not _config_path.is_absolute():
+            _config_path = Path.cwd() / _config_path
+        _config_path.parent.mkdir(parents=True, exist_ok=True)
+
+        config = {}
+        if _config_path.exists():
+            with open(_config_path, encoding="utf-8") as f:
+                config = json.load(f)
+        if "mcpServers" not in config:
+            config["mcpServers"] = {}
+        _warn_overwrite(config, "mcpServers", server_name, _config_path)
+        config["mcpServers"][server_name] = server_config
+        with open(_config_path, "w", encoding="utf-8") as f:
+            json.dump(config, f, indent=2)
+
+        console.print(
+            f"[green]Configured Windsurf with Arcade toolkits: {', '.join(tool_packages)}[/green]"
+        )
+        console.print(f"   Config file: {_format_path_for_display(_config_path)}", style="dim")
+        console.print("   Restart Windsurf for changes to take effect.", style="yellow")
+
+    elif client_lower == "amazonq":
+        _config_path = config_path or get_amazonq_config_path()
+        if _config_path and not _config_path.is_absolute():
+            _config_path = Path.cwd() / _config_path
+        _config_path.parent.mkdir(parents=True, exist_ok=True)
+
+        config = {}
+        if _config_path.exists():
+            with open(_config_path, encoding="utf-8") as f:
+                config = json.load(f)
+        if "mcpServers" not in config:
+            config["mcpServers"] = {}
+        _warn_overwrite(config, "mcpServers", server_name, _config_path)
+        config["mcpServers"][server_name] = server_config
+        with open(_config_path, "w", encoding="utf-8") as f:
+            json.dump(config, f, indent=2)
+
+        console.print(
+            f"[green]Configured Amazon Q with Arcade toolkits: {', '.join(tool_packages)}[/green]"
+        )
+        console.print(f"   Config file: {_format_path_for_display(_config_path)}", style="dim")
+        console.print("   Restart Amazon Q for changes to take effect.", style="yellow")
+
     else:
         raise typer.BadParameter(
-            f"Unknown client: {client}. Supported clients: claude, cursor, vscode."
+            f"Unknown client: {client}. Supported clients: claude, cursor, vscode, windsurf, amazonq."
         )
 
 

diff --git a/libs/arcade-cli/arcade_cli/connect.py b/libs/arcade-cli/arcade_cli/connect.py
--- a/libs/arcade-cli/arcade_cli/connect.py
+++ b/libs/arcade-cli/arcade_cli/connect.py
@@ -286,39 +286,6 @@
     return toolkits
 
 
-def fetch_account_gateways(base_url: str | None = None) -> list[dict]:
-    """Fetch MCP gateways (workers of type 'mcp') from the user's account.
-
-    Returns a list of dicts with keys: id, enabled, uri.
-    """
-    from arcadepy import APIConnectionError
-
-    from arcade_cli.utils import compute_base_url, get_arcade_client
-
-    url = base_url or compute_base_url(False, False, PROD_ENGINE_HOST, None, default_port=None)
-    client = get_arcade_client(url)
-
-    gateways: list[dict] = []
-    try:
-        workers = client.workers.list(limit=100)
-        for w in workers.items:
-            entry: dict = {
-                "id": w.id,
-                "enabled": getattr(w, "enabled", True),
-                "type": getattr(w, "type", "unknown"),
-            }
-            mcp = getattr(w, "mcp", None)
-            if mcp:
-                entry["uri"] = getattr(mcp, "uri", None)
-            gateways.append(entry)
-    except APIConnectionError:
-        console.print(f"Could not connect to Arcade Engine at {url}.", style="bold red")
-    except Exception as e:
-        logger.debug("Failed to fetch gateways: %s", e)
-
-    return gateways
-
-
 def list_gateways(
     access_token: str,
     base_url: str | None = None,

diff --git a/libs/tests/cli/test_connect.py b/libs/tests/cli/test_connect.py
--- a/libs/tests/cli/test_connect.py
+++ b/libs/tests/cli/test_connect.py
@@ -10,7 +10,6 @@
 import pytest
 from arcade_cli.connect import (
     ensure_login,
-    fetch_account_gateways,
     fetch_available_toolkits,
     get_toolkit_examples,
     run_connect,
@@ -120,43 +119,6 @@
 
 
 # ---------------------------------------------------------------------------
-# fetch_account_gateways
-# ---------------------------------------------------------------------------
-
-
-class TestFetchAccountGateways:
-    def test_returns_gateway_list(self) -> None:
-        worker = SimpleNamespace(
-            id="my-gw",
-            enabled=True,
-            type="mcp",
-            mcp=SimpleNamespace(uri="https://api.arcade.dev/mcp/my-gw"),
-        )
-        mock_client = MagicMock()
-        mock_client.workers.list.return_value = SimpleNamespace(items=[worker])
-
-        with patch("arcade_cli.utils.get_arcade_client", return_value=mock_client):
-            result = fetch_account_gateways("https://api.example.com")
-
-        assert len(result) == 1
-        assert result[0]["id"] == "my-gw"
-        assert result[0]["enabled"] is True
-        assert result[0]["uri"] == "https://api.arcade.dev/mcp/my-gw"
-
-    @patch("arcade_cli.connect.console")
-    def test_connection_error_returns_empty(self, _console: MagicMock) -> None:
-        from arcadepy import APIConnectionError
-
-        mock_client = MagicMock()
-        mock_client.workers.list.side_effect = APIConnectionError(request=MagicMock())
-
-        with patch("arcade_cli.utils.get_arcade_client", return_value=mock_client):
-            result = fetch_account_gateways("https://api.example.com")
-
-        assert result == []
-
-
-# ---------------------------------------------------------------------------
 # run_quickstart — gateway mode (direct slug)
 # ---------------------------------------------------------------------------

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 13, 2026

Codecov Report

❌ Patch coverage is 79.61336% with 116 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
libs/arcade-cli/arcade_cli/connect.py 82.00% 61 Missing ⚠️
libs/arcade-cli/arcade_cli/configure.py 81.51% 39 Missing ⚠️
libs/arcade-cli/arcade_cli/main.py 15.78% 16 Missing ⚠️
Files with missing lines Coverage Δ
libs/arcade-cli/arcade_cli/main.py 32.44% <15.78%> (-1.37%) ⬇️
libs/arcade-cli/arcade_cli/configure.py 75.87% <81.51%> (+5.72%) ⬆️
libs/arcade-cli/arcade_cli/connect.py 82.00% <82.00%> (ø)

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Member

@EricGustin EricGustin left a comment

Choose a reason for hiding this comment

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

The code LGTM. I'm down for getting this in, but also I think that connect and configure are near synonyms in most peoples' mental model. If we think that connect is the best term for this, then I'd like to see the following:

  1. We rewrite configure's help menu to be very clear that it is for local servers that you have on your local filesystem.
  2. Ensure connect's help menu is very clear that it is for remote gateways

),
show_choices=True,
),
toolkit: Optional[list[str]] = typer.Option(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

since this is user-facing, we should try to not use the word 'toolkit'

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 027c184. Configure here.

@pmdroid pmdroid requested a review from EricGustin April 14, 2026 15:51
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