Improve configure mcp UX: search wizard, faster/robust discovery & apply - #225
Improve configure mcp UX: search wizard, faster/robust discovery & apply#225nachill-databricks wants to merge 5 commits into
configure mcp UX: search wizard, faster/robust discovery & apply#225Conversation
…apply
Rework the interactive `ucode configure mcp` flow and fix several
scaling/robustness problems surfaced on large workspaces.
Discovery:
- Fix a crash where a socket read timeout (bare TimeoutError, an OSError,
not a URLError) escaped `_http_get_json` and killed the whole command;
it is now surfaced as a reason and skipped best-effort.
- Add a live progress counter to every walk-based source (Vector Search,
UC functions, workspace-wide MCP services) so slow discovery shows
"N/M ... K found" instead of an opaque spinner.
- Add `list_all_mcp_services`: a workspace-wide MCP-services walk
(catalogs -> schemas -> mcp-services) under a wall-clock budget.
Interactive flow:
- Two-step wizard: a "Search for:" screen chooses which sources to
search (External, Apps, MCP services, Genie pre-checked; Vector Search
and UC functions off by default since they walk the workspace), then
the server picker. Pressing Left (<-) in the picker returns to the
search screen without restarting.
- Add a Ctrl-A select-all/none hotkey to the picker.
- Prefix MCP services with "MCP:" and external connections with
"Connection:" to match the existing App:/Genie:/Vector Search: labels.
- Print a post-save summary ("Added N, removed M ... across <clients>")
instead of a bare "Saved".
Apply:
- Parallelize `apply_mcp_server_changes` across clients (serial within a
client, since each mutates one shared config) with a live
"Configuring N/M" progress spinner, so large diffs no longer look
frozen. (Per-server CLI spawns remain; see code comments.)
Co-authored-by: Isaac
After the workspace and agents are configured in the fully-interactive flow (no --agent/--agents/--workspaces flags), prompt "Configure MCP servers now?" and run the MCP wizard if accepted. This makes MCP setup discoverable as the natural next step instead of requiring users to know `ucode configure mcp` exists. Flag-driven and scripted runs are unaffected: the prompt only fires on the fully-interactive path, and never in --dry-run. Co-authored-by: Isaac
The picker rendered the prompt from a PromptSession container, which expands to fill the terminal height. In a tall window that pushed the choices list to the very bottom, leaving a big blank gap between the "Search for:"/"MCP:" prompt and the options. Render the prompt as a fixed 1-row window instead, so the choices follow immediately regardless of terminal height. Co-authored-by: Isaac
| _UC_FUNCTION_PROBE_TIMEOUT = 5 | ||
| _VECTOR_SEARCH_DEADLINE_SECONDS = 15.0 | ||
| _UC_FUNCTIONS_DEADLINE_SECONDS = 20.0 | ||
| # This walk runs on every `configure mcp` (not opt-in), so keep the budget tight: |
There was a problem hiding this comment.
Should do this for -mcp as well along with configure mcp unless this already works for both?
There was a problem hiding this comment.
This only affects the interactive picker. The --mcp flag takes exact service names, so it skips the workspace-wide scan entirely and this budget never applies to it. So it's already fine for both.
| return sorted(pairs), None | ||
|
|
||
|
|
||
| def list_all_mcp_services( |
There was a problem hiding this comment.
In the long run maybe we can only MCP servers from system.ai as default via this interactive flow? If we add 100+ MCP service, this list will be long :D
So might be easier to focus on system.ai maybe and let customers manually add other?
There was a problem hiding this comment.
Most MCP services actually live outside system.ai for our users, so defaulting to system.ai-only would hide the majority of them — that's why the full scan stays the default. I've bumped the scan budget to 30s so it has time to enumerate them on larger workspaces. The long-list problem is real, but I'd rather solve it with better search/filtering than by narrowing the default scope.
| *, | ||
| deadline_seconds: float = _MCP_SERVICES_WALK_DEADLINE_SECONDS, | ||
| on_progress: Callable[[int, int, int], None] | None = None, | ||
| ) -> tuple[list[str], str | None]: |
There was a problem hiding this comment.
If it always returns back None for the reason, Do we need to return that back. Basically can this just be:
list[str] | None:
There was a problem hiding this comment.
The reason isn't always None — it's set when the list comes back empty ("no UC catalogs found", "deadline exceeded…"), and a couple of tests check those strings. It also matches the tuple[list, str | None] shape of the other list_*/discover_* functions here. The caller ignores it today, but I'd keep it for consistency and in case we want to surface the reason later. Happy to change it if you'd rather.
| vector_search = ( | ||
| _discover_mcp_source_with_progress( | ||
| "Vector Search", | ||
| "endpoints", | ||
| lambda on_progress: discover_vector_search_mcp_servers( | ||
| workspace, profile, on_progress=on_progress | ||
| ), | ||
| ) | ||
| if "vector-search" in sources | ||
| else [] | ||
| ) | ||
| uc_functions = ( | ||
| _discover_mcp_source_with_progress( | ||
| "UC functions", | ||
| "schemas", | ||
| lambda on_progress: discover_uc_functions_mcp_servers( | ||
| workspace, profile, on_progress=on_progress | ||
| ), | ||
| ) | ||
| if "uc-functions" in sources | ||
| else [] | ||
| ) |
There was a problem hiding this comment.
Should we deprecate Vector Search and UC Functions from here to stop promoting it?
There was a problem hiding this comment.
That's basically what this PR does. Both are now unchecked by default on the search screen (labeled "slower") since they scan the whole workspace — you have to opt in. This stops promoting them without removing them. We can fully remove them later if no one uses them.
Resolve conflicts: - cli.py: keep both the new --mcp flag (from main) and the interactive "Configure MCP servers now?" step; both call configure_mcp_command. - mcp.py: keep main's setup_mcp_clients refactor and skills-connection handling; feed skills-excluded picker_servers into the new two-step search wizard. - ui.py: union the Callable/Iterator imports. - tests: keep both new test classes; add the new search-sources and MCP prompt mocks to two main-origin tests so they pass under the wizard flow. Co-authored-by: Isaac
Most MCP services live outside `system.ai`, so the workspace-wide walk needs more time to enumerate them; too tight a budget silently truncates the picker list on larger workspaces. Bump the deadline from 8s to 30s. Co-authored-by: Isaac
Summary
Overhauls the MCP setup experience in ucode. Fixes crashes and slowness that show up on large workspaces (100s of MCP servers), reworks the interactive picker into a clearer wizard, and surfaces MCP setup as a natural step of
ucode configureso users don't have to knowconfigure mcpexists.What changed
1. Fixed a crash during discovery
A network read timeout raises a bare
TimeoutError(anOSError, not aURLError), which slipped past the error handling in_http_get_jsonand killed the whole command mid-discovery (e.g. "timed out Discovering Vector Search…"). Timeouts are now caught and treated as a skippable failure, like every other discovery error.2. New two-step "search wizard" for
configure mcpThe interactive flow is now:
Search for:screen — pick which sources to search. External connections, Databricks apps, MCP services, and Genie spaces are pre-checked; Vector Search and UC functions are off by default because they walk the whole workspace and are slow. Just press Enter to accept the defaults.Extras:
3. Better feedback while searching and saving
Searching Vector Search… 3/8 endpoints, 2 foundinstead of a blank spinner.Added 2, removed 1 MCP servers across Claude Code, Codexinstead of a bareSaved.MCP:for MCP services andConnection:for external connections (matching the existingApp:/Genie:/Vector Search:labels).4. Fixed the "frozen" apply for many servers
Saving 100+ servers appeared to hang because ucode invokes each tool's CLI once per server (claude/codex/gemini), serially.
apply_mcp_server_changesnow runs those operations concurrently across clients (serial within a client, since each mutates one shared config file) with a liveConfiguring N/Mprogress spinner. It no longer looks frozen. (Per-server CLI spawns remain — collapsing them into direct config-file writes is a possible future follow-up, noted in code.)5. MCP offered at the end of
ucode configureAfter workspace + agents are set up in the fully-interactive
ucode configure(no--agent/--agents/--workspacesflags), it now asksConfigure MCP servers now?and runs the wizard if accepted. Flag-driven / scripted runs and--dry-runare unaffected — they never prompt.Also added
list_all_mcp_services: a workspace-wide MCP-services walk (catalogs → schemas → mcp-services) under a wall-clock budget, feeding the "MCP services" source.Testing
uv run pytest— green except 3tests/test_cli.py::TestStatus/TestRevertfailures that are pre-existing onmain(verified by stashing this branch's changes) and unrelated to this work.uv run ruff check .,uv run ruff format --check, anduv run ty check src/all pass.This pull request and its description were written by Isaac.