Skip to content

Commit 32fe438

Browse files
author
Achille Negrier
committed
Merge origin/main into make-mcp-heavy-sources-opt-in
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
2 parents b3124e1 + 3a4087b commit 32fe438

18 files changed

Lines changed: 2067 additions & 44 deletions

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Tests live in `tests/`.
2020

2121
- Use Python 3.12+.
2222
- Keep changes scoped to the requested behavior.
23-
- Follow the existing module boundaries: CLI orchestration in `cli.py`, agent-specific behavior in `agents/<name>.py`, shared agent dispatch in `agents/__init__.py`, Databricks calls in `databricks.py`, and presentation helpers in `ui.py`.
23+
- Follow the existing module boundaries: CLI orchestration in `cli.py`, agent-specific behavior in `agents/<name>.py`, shared agent dispatch in `agents/__init__.py`, Databricks calls in `databricks.py`, skill download (UC fetch client + on-disk writer + download orchestration) in `skills_download.py`, MCP-connection state glue in `mcp.py`, and presentation helpers in `ui.py`. Skill download persists no disk state — it writes files to `--path` (or the home dir) and registers only the schema-less skills MCP connection.
2424
- Prefer existing helpers for config file writes, state persistence, UI messages, and Databricks authentication.
2525
- Add or update focused tests for behavior changes.
2626
- Do not modify generated or lock files unless the dependency graph intentionally changes.

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,38 @@ Options are shown in this order:
9292
Discovered external MCP connections are listed directly. MCP auth uses a Databricks token that
9393
`ucode` sets when launching each tool.
9494

95+
To set up an agent and its MCP server(s) in one command, pass `--mcp` with fully-qualified
96+
service name(s) to `ucode configure`:
97+
98+
```bash
99+
ucode configure --agents claude --mcp system.ai.slack
100+
```
101+
102+
`--mcp` also works without `--agents` for MCP-only clients (it configures just the workspace,
103+
then registers the servers); pass a comma-separated list to register several at once.
104+
105+
### Skills (optional)
106+
107+
Configure Unity Catalog Skills for your coding tools with `ucode configure skills`. It has two
108+
mutually-exclusive modes, both scoped by `--location <catalog>.<schema>` (comma-separated for
109+
multiple schemas):
110+
111+
```bash
112+
# Download mode (default): fetch every skill in the schema to disk.
113+
ucode configure skills --location main.default --path /abs/project/dir
114+
115+
# MCP mode: expose the schema's skills as MCP tools instead of downloading.
116+
ucode configure skills --location main.default,ml.prod --mcp
117+
```
118+
119+
- **Download mode** writes each skill flat as `<leaf>/SKILL.md` (plus its bundled files) into both
120+
`.claude/skills/` and `.agents/skills/`. `--path` (an existing absolute directory) is optional;
121+
when omitted, skills are written under your home directory. Any pre-existing skill dir prompts
122+
before it's overwritten. It then registers a schema-less skills MCP connection (utility tools
123+
only), leaving any prior `--mcp` scope untouched.
124+
- **MCP mode** sets the connection's location set to exactly `<list>` (override-only) and rebuilds
125+
its `?schema=` URL; no files are downloaded and `--path` is rejected.
126+
95127
---
96128

97129
## Other Commands
@@ -107,6 +139,9 @@ Discovered external MCP connections are listed directly. MCP auth uses a Databri
107139
| `ucode configure --profiles DEFAULT` | Configure using existing Databricks CLI profiles (hosts come from `~/.databrickscfg`) |
108140
| `ucode configure --profiles DEFAULT --use-pat` | Authenticate with the profile's personal access token — no browser login |
109141
| `ucode configure --skip-validate` | Write configs without sending a test message through each agent |
142+
| `ucode configure --agents claude --mcp system.ai.slack` | Configure an agent and register its Databricks MCP server(s) in one command |
143+
| `ucode configure skills --location main.default [--path <dir>]` | Download a schema's skills to disk (under `<dir>`, or your home dir) and register a schema-less skills MCP connection |
144+
| `ucode configure skills --location main.default --mcp` | Expose a schema's skills as MCP tools (override-only) instead of downloading |
110145

111146
## Managed Local Files
112147

src/ucode/agents/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from ucode.databricks import (
2121
BEDROCK_PROVIDER_TYPES,
2222
get_databricks_token,
23+
install_ai_tools,
2324
install_databricks_cli,
2425
map_bedrock_claude_models,
2526
resolve_provider_service,
@@ -65,6 +66,23 @@
6566
DEFAULT_TOOL = "codex"
6667
BUNDLE_VERSION = 1
6768

69+
# ucode tool -> `databricks aitools` agent id. gemini/pi aren't supported.
70+
AITOOLS_AGENT_TOKENS = {
71+
"claude": "claude-code",
72+
"codex": "codex",
73+
"opencode": "opencode",
74+
"copilot": "copilot",
75+
}
76+
77+
78+
def install_ai_tools_for_agents(tools: list[str], state: dict) -> None:
79+
"""Install Databricks AI Tools for the coding agents that support them
80+
(gemini/pi have no ``aitools`` support and are dropped)."""
81+
if state.get("databricks_ai_tools_enabled", True) is False:
82+
return
83+
agents = [AITOOLS_AGENT_TOKENS[tool] for tool in tools if tool in AITOOLS_AGENT_TOKENS]
84+
install_ai_tools(agents, state.get("profile"))
85+
6886

6987
def normalize_tool(tool: str) -> str:
7088
normalized = TOOL_ALIASES.get(tool.strip().lower())
@@ -380,6 +398,7 @@ def configure_single_tool(tool: str, state: dict) -> dict:
380398
available_tools = list(set((state.get("available_tools") or []) + [tool]))
381399
state["available_tools"] = available_tools
382400
save_state(state)
401+
install_ai_tools_for_agents([tool], state)
383402
return state
384403

385404

@@ -410,6 +429,7 @@ def configure_selected_tools(state: dict, tools: list[str]) -> dict:
410429
existing = state.get("available_tools") or []
411430
state["available_tools"] = sorted(set(existing) | set(tools))
412431
save_state(state)
432+
install_ai_tools_for_agents(tools, state)
413433
return state
414434

415435

src/ucode/agents/claude.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ def _resolve_web_search_model(state: dict) -> str | None:
9393
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
9494
"ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME",
9595
)
96+
# Env keys ucode used to write but no longer does; stripped from the managed
97+
# settings file on every launch so stale values never linger.
98+
CLAUDE_REMOVED_ENV_KEYS = ("CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS",)
9699
CLAUDE_TRACING_STOP_HOOK_SUFFIX = " autolog claude stop-hook"
97100
# Tracing is driven by an `mlflow autolog claude stop-hook` Stop hook, run by
98101
# the `mlflow` CLI on each session end. Pin to 3.11.x: 3.12 dropped the Unity
@@ -166,8 +169,13 @@ def render_overlay(
166169
env: dict[str, str] = {
167170
"ANTHROPIC_BASE_URL": base_url,
168171
"ANTHROPIC_CUSTOM_HEADERS": custom_headers,
169-
"CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1",
170172
"CLAUDE_CODE_API_KEY_HELPER_TTL_MS": "900000",
173+
# 1h prompt caching needs the extended-cache-ttl beta header, which
174+
# Claude Code only sends when experimental betas are enabled — so we must
175+
# not set CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS (see CLAUDE_REMOVED_ENV_KEYS).
176+
"ENABLE_PROMPT_CACHING_1H": "1",
177+
"ENABLE_TOOL_SEARCH": "1",
178+
"CLAUDE_CODE_USE_GATEWAY": "1",
171179
}
172180
# Intentionally NOT setting ANTHROPIC_MODEL. Setting it produces a duplicate
173181
# catalog row in Claude Code's /model picker (e.g. "Opus 4.8 (1M context) ✓")
@@ -343,6 +351,11 @@ def write_tool_config(
343351
for key in CLAUDE_MANAGED_MODEL_ENV_KEYS:
344352
if key not in overlay_env:
345353
merged_env.pop(key, None)
354+
# deep_merge_dict keeps keys already in the file, so drop the ones ucode no
355+
# longer writes.
356+
if isinstance(merged_env, dict):
357+
for key in CLAUDE_REMOVED_ENV_KEYS:
358+
merged_env.pop(key, None)
346359
write_json_file(CLAUDE_SETTINGS_PATH, merged)
347360

348361
if web_search_model:

0 commit comments

Comments
 (0)