Skip to content

Improve configure mcp UX: search wizard, faster/robust discovery & apply - #225

Open
nachill-databricks wants to merge 5 commits into
databricks:mainfrom
nachill-databricks:make-mcp-heavy-sources-opt-in
Open

Improve configure mcp UX: search wizard, faster/robust discovery & apply#225
nachill-databricks wants to merge 5 commits into
databricks:mainfrom
nachill-databricks:make-mcp-heavy-sources-opt-in

Conversation

@nachill-databricks

@nachill-databricks nachill-databricks commented Jul 20, 2026

Copy link
Copy Markdown

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 configure so users don't have to know configure mcp exists.

What changed

1. Fixed a crash during discovery

A network read timeout raises a bare TimeoutError (an OSError, not a URLError), which slipped past the error handling in _http_get_json and 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 mcp

The interactive flow is now:

  1. 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.
  2. Server picker — choose servers from what was found.

Extras:

  • Press ← (Left arrow) in the picker to go back to the search screen without restarting the command.
  • Ctrl-A toggles select-all / select-none in the picker.

3. Better feedback while searching and saving

  • Live progress counters on the slow, walk-based sources: the spinner now shows e.g. Searching Vector Search… 3/8 endpoints, 2 found instead of a blank spinner.
  • Post-save summary: Added 2, removed 1 MCP servers across Claude Code, Codex instead of a bare Saved.
  • Picker labels are now consistently prefixed: MCP: for MCP services and Connection: for external connections (matching the existing App: / 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_changes now runs those operations concurrently across clients (serial within a client, since each mutates one shared config file) with a live Configuring N/M progress 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 configure

After workspace + agents are set up in the fully-interactive ucode configure (no --agent/--agents/--workspaces flags), it now asks Configure MCP servers now? and runs the wizard if accepted. Flag-driven / scripted runs and --dry-run are 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 3 tests/test_cli.py::TestStatus/TestRevert failures that are pre-existing on main (verified by stashing this branch's changes) and unrelated to this work.
  • uv run ruff check ., uv run ruff format --check, and uv run ty check src/ all pass.

This pull request and its description were written by Isaac.

Achille Negrier added 3 commits July 20, 2026 13:53
…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
Comment thread src/ucode/databricks.py Outdated
_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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should do this for -mcp as well along with configure mcp unless this already works for both?

@nachill-databricks nachill-databricks Jul 27, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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.

Comment thread src/ucode/databricks.py
return sorted(pairs), None


def list_all_mcp_services(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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?

@nachill-databricks nachill-databricks Jul 27, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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.

Comment thread src/ucode/databricks.py
*,
deadline_seconds: float = _MCP_SERVICES_WALK_DEADLINE_SECONDS,
on_progress: Callable[[int, int, int], None] | None = None,
) -> tuple[list[str], str | None]:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If it always returns back None for the reason, Do we need to return that back. Basically can this just be:

list[str] | None:

@nachill-databricks nachill-databricks Jul 27, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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.

Comment thread src/ucode/mcp.py
Comment on lines +1174 to +1195
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 []
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we deprecate Vector Search and UC Functions from here to stop promoting it?

@nachill-databricks nachill-databricks Jul 27, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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
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