Skip to content

Commit 4b525fd

Browse files
committed
fix(mcp): pass --profile to discovery CLI calls and surface resolver failures in configure mcp
1 parent bcbae56 commit 4b525fd

4 files changed

Lines changed: 235 additions & 51 deletions

File tree

src/ucode/databricks.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ def _extract_connection_page(payload: object) -> tuple[list[dict], str | None]:
622622
return [item for item in raw_connections if isinstance(item, dict)], next_page_token
623623

624624

625-
def list_databricks_connections(workspace: str) -> list[dict]:
625+
def list_databricks_connections(workspace: str, profile: str | None = None) -> list[dict]:
626626
env = build_databricks_cli_env(workspace)
627627
connections: list[dict] = []
628628
page_token: str | None = None
@@ -634,6 +634,7 @@ def list_databricks_connections(workspace: str) -> list[dict]:
634634
"databricks",
635635
"connections",
636636
"list",
637+
*_profile_args(profile),
637638
"--max-results",
638639
"0",
639640
"--output",
@@ -684,7 +685,7 @@ def _extract_genie_spaces_page(payload: object) -> tuple[list[dict], str | None]
684685
return [item for item in raw_spaces if isinstance(item, dict)], next_page_token
685686

686687

687-
def list_genie_spaces(workspace: str) -> list[dict]:
688+
def list_genie_spaces(workspace: str, profile: str | None = None) -> list[dict]:
688689
env = build_databricks_cli_env(workspace)
689690
spaces: list[dict] = []
690691
page_token: str | None = None
@@ -696,6 +697,7 @@ def list_genie_spaces(workspace: str) -> list[dict]:
696697
"databricks",
697698
"genie",
698699
"list-spaces",
700+
*_profile_args(profile),
699701
"--page-size",
700702
"100",
701703
"--output",
@@ -743,14 +745,15 @@ def _extract_apps_payload(payload: object) -> list[dict]:
743745
raise RuntimeError("Databricks apps listing returned invalid JSON.")
744746

745747

746-
def list_databricks_apps(workspace: str) -> list[dict]:
748+
def list_databricks_apps(workspace: str, profile: str | None = None) -> list[dict]:
747749
env = build_databricks_cli_env(workspace)
748750
try:
749751
result = run(
750752
[
751753
"databricks",
752754
"apps",
753755
"list",
756+
*_profile_args(profile),
754757
"--limit",
755758
"1000",
756759
"--output",

src/ucode/mcp.py

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,8 @@ def external_mcp_connection_names(connections: list[dict]) -> list[str]:
346346
return sorted(names)
347347

348348

349-
def discover_external_mcp_connection_names(workspace: str) -> list[str]:
350-
return external_mcp_connection_names(list_databricks_connections(workspace))
349+
def discover_external_mcp_connection_names(workspace: str, profile: str | None = None) -> list[str]:
350+
return external_mcp_connection_names(list_databricks_connections(workspace, profile))
351351

352352

353353
def genie_mcp_servers(spaces: list[dict], workspace: str) -> list[dict]:
@@ -372,8 +372,8 @@ def genie_mcp_servers(spaces: list[dict], workspace: str) -> list[dict]:
372372
return sorted(servers, key=lambda server: str(server["title"]).lower())
373373

374374

375-
def discover_genie_mcp_servers(workspace: str) -> list[dict]:
376-
return genie_mcp_servers(list_genie_spaces(workspace), workspace)
375+
def discover_genie_mcp_servers(workspace: str, profile: str | None = None) -> list[dict]:
376+
return genie_mcp_servers(list_genie_spaces(workspace, profile), workspace)
377377

378378

379379
def app_mcp_servers(apps: list[dict]) -> list[dict]:
@@ -403,8 +403,8 @@ def app_mcp_servers(apps: list[dict]) -> list[dict]:
403403
return sorted(servers, key=lambda server: str(server["title"]).lower())
404404

405405

406-
def discover_app_mcp_servers(workspace: str) -> list[dict]:
407-
return app_mcp_servers(list_databricks_apps(workspace))
406+
def discover_app_mcp_servers(workspace: str, profile: str | None = None) -> list[dict]:
407+
return app_mcp_servers(list_databricks_apps(workspace, profile))
408408

409409

410410
def _picker_style() -> questionary.Style:
@@ -674,35 +674,35 @@ def _resolve_mcp_selection(
674674
selection: str,
675675
workspace: str,
676676
available_app_servers: list[dict] | None = None,
677-
) -> tuple[str, str] | None:
677+
) -> tuple[str, str]:
678678
if selection.startswith(APP_MCP_SELECTION_PREFIX):
679679
app_name = selection.removeprefix(APP_MCP_SELECTION_PREFIX)
680680
if not app_name:
681-
return None
681+
raise RuntimeError("missing Databricks app name")
682682
server = _servers_by_name(available_app_servers or []).get(f"databricks-app-{app_name}")
683683
if not server:
684-
return None
684+
raise RuntimeError(f"Databricks app `{app_name}` was not in the discovered app list")
685685
url = server.get("url")
686686
if not isinstance(url, str) or not url:
687-
return None
687+
raise RuntimeError(f"Databricks app `{app_name}` has no MCP URL")
688688
return f"databricks-app-{app_name}", url
689689

690690
if selection.startswith(GENIE_SPACE_SELECTION_PREFIX):
691691
space_id = selection.removeprefix(GENIE_SPACE_SELECTION_PREFIX)
692692
if not space_id:
693-
return None
693+
raise RuntimeError("missing Genie space id")
694694
return f"databricks-genie-{space_id}", f"{workspace}/api/2.0/mcp/genie/{space_id}"
695695

696696
if selection.startswith(EXTERNAL_MCP_SELECTION_PREFIX):
697697
server_name = selection.removeprefix(EXTERNAL_MCP_SELECTION_PREFIX)
698698
if not server_name:
699-
return None
699+
raise RuntimeError("missing external connection name")
700700
return server_name, f"{workspace}/api/2.0/mcp/external/{server_name}"
701701

702702
if selection == SQL_MCP_VALUE:
703703
return "databricks-sql", f"{workspace}/api/2.0/mcp/sql"
704704

705-
return None
705+
raise RuntimeError(f"unrecognized selection prefix in `{selection}`")
706706

707707

708708
def _discover_mcp_source(label: str, discover: Callable[[], list[Any]]) -> list[Any]:
@@ -766,7 +766,8 @@ def configure_mcp_command() -> int:
766766
client for client in MCP_CLIENTS if client in configured_tools and client not in clients
767767
]
768768

769-
ensure_databricks_auth(workspace, state.get("profile"))
769+
profile = state.get("profile")
770+
ensure_databricks_auth(workspace, profile)
770771

771772
print_section("MCP Servers")
772773
client_names = ", ".join(str(MCP_CLIENTS[client]["display"]) for client in clients)
@@ -779,15 +780,15 @@ def configure_mcp_command() -> int:
779780

780781
available_external_mcp_names = _discover_mcp_source(
781782
"external connections",
782-
lambda: discover_external_mcp_connection_names(workspace),
783+
lambda: discover_external_mcp_connection_names(workspace, profile),
783784
)
784785
available_genie_mcp_servers = _discover_mcp_source(
785786
"Genie spaces",
786-
lambda: discover_genie_mcp_servers(workspace),
787+
lambda: discover_genie_mcp_servers(workspace, profile),
787788
)
788789
available_app_mcp_servers = _discover_mcp_source(
789790
"Databricks apps",
790-
lambda: discover_app_mcp_servers(workspace),
791+
lambda: discover_app_mcp_servers(workspace, profile),
791792
)
792793

793794
original_mcp_servers: list[dict] = list(state.get("mcp_servers") or [])
@@ -814,14 +815,15 @@ def configure_mcp_command() -> int:
814815
working_names.add(selection)
815816

816817
for selection in add_selections:
817-
resolved = _resolve_mcp_selection(
818-
selection,
819-
workspace,
820-
available_app_mcp_servers,
821-
)
822-
if resolved is None:
818+
try:
819+
entry_name, url = _resolve_mcp_selection(
820+
selection,
821+
workspace,
822+
available_app_mcp_servers,
823+
)
824+
except RuntimeError as exc:
825+
print_warning(f"Skipped MCP selection `{selection}`: {exc}.")
823826
continue
824-
entry_name, url = resolved
825827
if entry_name in working_names:
826828
continue
827829
working_mcp_servers.append(
@@ -839,4 +841,7 @@ def configure_mcp_command() -> int:
839841
state["mcp_servers"] = working_mcp_servers
840842
save_state(state)
841843
print_success("Saved")
844+
elif not selections and not original_mcp_servers:
845+
# User submitted the picker without toggling anything --> make it clear nothing was selected
846+
print_note("No MCP servers selected. Press space to toggle an item, then enter to save.")
842847
return 0

tests/test_databricks.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,20 @@ def fake_run(args, **kwargs):
375375
assert calls[0]["kwargs"]["env"]["DATABRICKS_HOST"] == WS
376376
assert calls[1]["args"][-2:] == ["--page-token", "next-page"]
377377

378+
def test_passes_profile_when_provided(self, monkeypatch):
379+
calls: list[list[str]] = []
380+
381+
def fake_run(args, **kwargs):
382+
calls.append(args)
383+
return subprocess.CompletedProcess(args, 0, stdout=json.dumps({"connections": []}))
384+
385+
monkeypatch.setattr(db_mod, "run", fake_run)
386+
387+
list_databricks_connections(WS, "my-profile")
388+
389+
assert "--profile" in calls[0]
390+
assert calls[0][calls[0].index("--profile") + 1] == "my-profile"
391+
378392
def test_raises_on_invalid_json(self, monkeypatch):
379393
def fake_run(args, **kwargs):
380394
return subprocess.CompletedProcess(args, 0, stdout="not-json")
@@ -418,6 +432,20 @@ def fake_run(args, **kwargs):
418432
assert calls[0]["kwargs"]["env"]["DATABRICKS_HOST"] == WS
419433
assert calls[1]["args"][-2:] == ["--page-token", "next-page"]
420434

435+
def test_passes_profile_when_provided(self, monkeypatch):
436+
calls: list[list[str]] = []
437+
438+
def fake_run(args, **kwargs):
439+
calls.append(args)
440+
return subprocess.CompletedProcess(args, 0, stdout=json.dumps({"spaces": []}))
441+
442+
monkeypatch.setattr(db_mod, "run", fake_run)
443+
444+
list_genie_spaces(WS, "my-profile")
445+
446+
assert "--profile" in calls[0]
447+
assert calls[0][calls[0].index("--profile") + 1] == "my-profile"
448+
421449
def test_raises_on_invalid_json(self, monkeypatch):
422450
def fake_run(args, **kwargs):
423451
return subprocess.CompletedProcess(args, 0, stdout="not-json")
@@ -461,6 +489,20 @@ def fake_run(args, **kwargs):
461489
]
462490
assert calls[0]["kwargs"]["env"]["DATABRICKS_HOST"] == WS
463491

492+
def test_passes_profile_when_provided(self, monkeypatch):
493+
calls: list[list[str]] = []
494+
495+
def fake_run(args, **kwargs):
496+
calls.append(args)
497+
return subprocess.CompletedProcess(args, 0, stdout=json.dumps([]))
498+
499+
monkeypatch.setattr(db_mod, "run", fake_run)
500+
501+
list_databricks_apps(WS, "my-profile")
502+
503+
assert "--profile" in calls[0]
504+
assert calls[0][calls[0].index("--profile") + 1] == "my-profile"
505+
464506
def test_accepts_object_wrapped_apps(self, monkeypatch):
465507
def fake_run(args, **kwargs):
466508
return subprocess.CompletedProcess(

0 commit comments

Comments
 (0)