Skip to content

Commit 837bbcf

Browse files
Experimental aitools: improve discoverability, tool registration, warehouse lookup (#3946)
## Changes This adds a few improvements to the experimental aitools: * Improve discoverability of the tools for AI; they were sometimes not being used, especially in plan mode (this should be refined further based on eval) * Fix Claude Code registration so it happens at the user level * Fix warehouse lookup not working or returning inaccessible warehouses ## Tests * Manual validation & benchmarks * Existing tests --------- Co-authored-by: Claude <[email protected]>
1 parent 43fb036 commit 837bbcf

File tree

6 files changed

+110
-20
lines changed

6 files changed

+110
-20
lines changed

experimental/aitools/agents/claude.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ func InstallClaude() error {
2525
return err
2626
}
2727

28-
removeCmd := exec.Command("claude", "mcp", "remove", "databricks-aitools")
28+
removeCmd := exec.Command("claude", "mcp", "remove", "--scope", "user", "databricks-aitools")
2929
_ = removeCmd.Run()
3030

3131
cmd := exec.Command("claude", "mcp", "add",
32+
"--scope", "user",
3233
"--transport", "stdio",
3334
"databricks-aitools",
3435
"--",

experimental/aitools/tools/add_project_resource.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
var AddProjectResourceTool = Tool{
1818
Definition: ToolDefinition{
1919
Name: "add_project_resource",
20-
Description: "Add a new resource (app, job, pipeline, dashboard, ...) to an existing Databricks project. Use this when the user wants to add a new resource to an existing project.",
20+
Description: "📋 DURING PLAN MODE: Include this tool when task involves: building 'SQL pipelines' / 'data pipelines' / 'ETL workflows', creating 'materialized views' / 'data transformations', building 'Databricks apps' / 'applications', creating 'dashboards' / 'visualizations', or setting up 'scheduled jobs' / 'workflows'.\n\nMANDATORY - USE THIS TO ADD RESOURCES: Add a new resource (app, job, pipeline, dashboard) to an existing Databricks project. Use this when the user wants to add a new resource to an existing project.",
2121
InputSchema: map[string]any{
2222
"type": "object",
2323
"properties": map[string]any{

experimental/aitools/tools/analyze_project.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
var AnalyzeProjectTool = Tool{
1818
Definition: ToolDefinition{
1919
Name: "analyze_project",
20-
Description: "REQUIRED FIRST STEP: If databricks.yml exists in the directory, you MUST call this tool before using Read, Glob, or any other tools. Databricks projects require specialized commands that differ from standard Python/Node.js workflows - attempting standard approaches will fail. This tool is fast and provides the correct commands for preview/deploy/run operations.",
20+
Description: "📋 DURING PLAN MODE: Include this as your FIRST step if you see a databricks.yml file exists, task involves deploying to workspaces, validating configurations, or working with an existing Databricks project.\n\nMANDATORY - REQUIRED FIRST STEP: If databricks.yml exists in the directory, you MUST call this tool before using Read, Glob, or any other tools. Databricks projects require specialized commands that differ from standard Python/Node.js workflows - attempting standard approaches will fail. This tool is fast and provides the correct commands for preview/deploy/run operations.",
2121
InputSchema: map[string]any{
2222
"type": "object",
2323
"properties": map[string]any{

experimental/aitools/tools/explore.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import (
1111
"github.com/databricks/cli/libs/databrickscfg/profile"
1212
"github.com/databricks/cli/libs/env"
1313
"github.com/databricks/cli/libs/exec"
14+
"github.com/databricks/cli/libs/log"
1415
)
1516

1617
// ExploreTool provides guidance on exploring Databricks workspaces and resources.
1718
var ExploreTool = Tool{
1819
Definition: ToolDefinition{
1920
Name: "explore",
20-
Description: "CALL THIS FIRST when user mentions a workspace by name or asks about workspace resources. Shows available workspaces/profiles, default warehouse, and provides guidance on exploring jobs, clusters, catalogs, and other Databricks resources. Use this to discover what's available before running CLI commands.",
21+
Description: "**REQUIRED DURING PLAN MODE** - Call this FIRST when planning ANY Databricks work. Use this to discover available workspaces, warehouses, and get workflow recommendations for your specific task. Even if you're just reading an assignment document, call this first. Especially important when task involves: creating Databricks projects/apps/pipelines/jobs, SQL pipelines or data transformation workflows, deploying code to multiple environments (dev/prod), or working with databricks.yml files. You DON'T need a workspace name - call this when starting ANY Databricks planning to understand workspace capabilities and recommended tooling before you create your plan.",
2122
InputSchema: map[string]any{
2223
"type": "object",
2324
"properties": map[string]any{},
@@ -26,7 +27,8 @@ var ExploreTool = Tool{
2627
Handler: func(ctx context.Context, params map[string]any) (string, error) {
2728
warehouse, err := GetDefaultWarehouse(ctx)
2829
if err != nil {
29-
return "", fmt.Errorf("failed to get default warehouse: %w\n\nTo use data exploration features, you need a SQL warehouse. You can create one in the Databricks workspace UI under 'SQL Warehouses'", err)
30+
log.Debugf(ctx, "Failed to get default warehouse (non-fatal): %v", err)
31+
warehouse = nil
3032
}
3133

3234
currentProfile := getCurrentProfile(ctx)
@@ -43,22 +45,26 @@ type warehouse struct {
4345
}
4446

4547
// GetDefaultWarehouse finds a suitable SQL warehouse for queries.
46-
// It prefers RUNNING warehouses, then falls back to STOPPED ones (which auto-start).
48+
// It filters out warehouses the user cannot access and prefers RUNNING warehouses,
49+
// then falls back to STOPPED ones (which auto-start).
4750
func GetDefaultWarehouse(ctx context.Context) (*warehouse, error) {
4851
executor, err := exec.NewCommandExecutor("")
4952
if err != nil {
5053
return nil, fmt.Errorf("failed to create command executor: %w", err)
5154
}
5255

53-
output, err := executor.Exec(ctx, fmt.Sprintf(`"%s" warehouses list --output json`, GetCLIPath()))
56+
output, err := executor.Exec(ctx, fmt.Sprintf(`"%s" api get "/api/2.0/sql/warehouses?skip_cannot_use=true" --output json`, GetCLIPath()))
5457
if err != nil {
5558
return nil, fmt.Errorf("failed to list warehouses: %w\nOutput: %s", err, output)
5659
}
5760

58-
var warehouses []warehouse
59-
if err := json.Unmarshal(output, &warehouses); err != nil {
61+
var response struct {
62+
Warehouses []warehouse `json:"warehouses"`
63+
}
64+
if err := json.Unmarshal(output, &response); err != nil {
6065
return nil, fmt.Errorf("failed to parse warehouses: %w", err)
6166
}
67+
warehouses := response.Warehouses
6268

6369
if len(warehouses) == 0 {
6470
return nil, errors.New("no SQL warehouses found in workspace")
@@ -143,10 +149,18 @@ func generateExploreGuidance(warehouse *warehouse, currentProfile string, profil
143149
profilesInfo += " invoke_databricks_cli '--profile prod catalogs list'\n"
144150
}
145151

152+
// Handle warehouse information (may be nil if lookup failed)
153+
warehouseName := ""
154+
warehouseID := ""
155+
if warehouse != nil {
156+
warehouseName = warehouse.Name
157+
warehouseID = warehouse.ID
158+
}
159+
146160
return prompts.MustExecuteTemplate("explore.tmpl", map[string]string{
147161
"WorkspaceInfo": workspaceInfo,
148-
"WarehouseName": warehouse.Name,
149-
"WarehouseID": warehouse.ID,
162+
"WarehouseName": warehouseName,
163+
"WarehouseID": warehouseID,
150164
"ProfilesInfo": profilesInfo,
151165
})
152166
}

experimental/aitools/tools/init_project.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
var InitProjectTool = Tool{
1919
Definition: ToolDefinition{
2020
Name: "init_project",
21-
Description: "Initialize a new Databricks project structure. Use this to create a new project. After initialization, use add_project_resource to add specific resources (apps, jobs, pipelines, dashboards) as needed.",
21+
Description: "📋 DURING PLAN MODE: Include this tool in your plan if task mentions ANY of: creating a 'new project' / 'Databricks project' / 'project structure', deploying to 'multiple environments' / 'dev and prod' / 'dev/staging/prod', building 'from scratch', or working with 'databricks.yml' files. DO NOT manually create project files - use this tool instead to get proper structure and templates.\n\nMANDATORY - USE THIS TO CREATE NEW PROJECTS: Initialize a new Databricks project. Use this to create a new Databricks project. After initialization, use add_project_resource to add specific resources (apps, jobs, pipelines, dashboards) as needed.",
2222
InputSchema: map[string]any{
2323
"type": "object",
2424
"properties": map[string]any{

experimental/aitools/tools/prompts/explore.tmpl

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,24 @@
1010
Databricks Data Exploration Guide
1111
=====================================
1212

13-
{{.WorkspaceInfo}}
14-
Default SQL Warehouse: {{.WarehouseName}} ({{.WarehouseID}}){{.ProfilesInfo}}
13+
{{.WorkspaceInfo}}{{if .WarehouseName}}
14+
Default SQL Warehouse: {{.WarehouseName}} ({{.WarehouseID}}){{else}}
15+
Note: No SQL warehouse detected. SQL queries will require warehouse_id to be specified manually.{{end}}{{.ProfilesInfo}}
1516

1617
IMPORTANT: Use the invoke_databricks_cli tool to run all commands below!
1718

1819

1920
1. EXECUTING SQL QUERIES
20-
Run SQL queries using the Statement Execution API with inline JSON:
21-
invoke_databricks_cli 'api post /api/2.0/sql/statements --json {"warehouse_id":"<warehouse_id>","statement":"SELECT * FROM <catalog>.<schema>.<table> LIMIT 10","wait_timeout":"30s"}'
21+
Run queries with auto-wait (max 50s):
22+
invoke_databricks_cli 'api post /api/2.0/sql/statements --json {"warehouse_id":"{{if .WarehouseID}}{{.WarehouseID}}{{else}}<warehouse_id>{{end}}","statement":"SELECT * FROM <catalog>.<schema>.<table> LIMIT 10","wait_timeout":"50s"}'
2223

23-
Examples:
24-
- Simple query: {"warehouse_id":"<id>","statement":"SELECT 42 as answer","wait_timeout":"10s"}
25-
- Table query: {"warehouse_id":"<id>","statement":"SELECT * FROM catalog.schema.table LIMIT 10","wait_timeout":"30s"}
24+
Response has status.state:
25+
- "SUCCEEDED" → Results in result.data_array (you're done!)
26+
- "PENDING" → Warehouse starting or query slow. Poll with:
27+
invoke_databricks_cli 'api get /api/2.0/sql/statements/<statement_id>'
28+
Repeat every 5-10s until "SUCCEEDED"
2629

27-
Note: Use the warehouse ID shown above. Results are returned in JSON format.
30+
Note: First query on stopped warehouse takes 60-120s startup time
2831

2932

3033
2. EXPLORING JOBS AND WORKFLOWS
@@ -74,3 +77,75 @@ Getting Started:
7477
- Use the commands above to explore what resources exist in the workspace
7578
- All commands support --output json for programmatic access
7679
- Remember to add --profile <name> when working with non-default workspaces
80+
81+
82+
WORKFLOW PATTERNS FOR DATABRICKS PROJECTS
83+
==========================================
84+
85+
Creating a New Databricks Project:
86+
When to use: Building a new project from scratch, setting up deployment to multiple environments
87+
Tools sequence:
88+
1. init_project (creates proper project structure with templates)
89+
2. add_project_resource (for each resource you need: pipeline/job/app/dashboard)
90+
3. analyze_project (provides deployment commands)
91+
4. invoke_databricks_cli 'bundle validate'
92+
💡 Tip: Use init_project even if you know YAML syntax - it uses templates and best practices
93+
94+
Working with Existing Databricks Project:
95+
When to use: databricks.yml file already exists in the directory
96+
Tools sequence:
97+
1. analyze_project (MANDATORY FIRST STEP - provides specialized commands)
98+
2. [Make your changes to project files]
99+
3. invoke_databricks_cli 'bundle validate'
100+
💡 Tip: ALWAYS call analyze_project before making changes - Databricks projects
101+
require specialized commands that differ from standard Python/Node.js workflows
102+
103+
Adding Resources to Existing Project:
104+
When to use: Adding pipelines, jobs, apps, or dashboards to an existing project
105+
Tools sequence:
106+
1. add_project_resource (with type: 'pipeline', 'job', 'app', or 'dashboard')
107+
2. analyze_project (to get updated deployment commands)
108+
3. invoke_databricks_cli 'bundle validate'
109+
110+
111+
PATTERN MATCHING: If Your Task Mentions...
112+
===========================================
113+
114+
"new project" / "create a project" / "Databricks project" / "project structure"
115+
→ Use init_project first (don't create files manually!)
116+
→ Then add_project_resource for each resource (pipeline/job/app/dashboard)
117+
118+
"SQL pipeline" / "data pipeline" / "materialized views" / "ETL" / "DLT"
119+
→ Use add_project_resource with type='pipeline' or type='job'
120+
121+
"Databricks app" / "application" / "build an app"
122+
→ Use add_project_resource with type='app'
123+
124+
"dashboard" / "Lakeview dashboard" / "visualization"
125+
→ Use add_project_resource with type='dashboard'
126+
127+
"Databricks job" / "scheduled job" / "workflow"
128+
→ Use add_project_resource with type='job'
129+
130+
"deploy to dev and prod" / "multiple environments" / "dev/staging/prod"
131+
→ Use init_project (sets up multi-environment structure automatically)
132+
133+
"databricks.yml" / "bundle configuration" / "Asset Bundle"
134+
→ If creating new: use init_project (don't create manually!)
135+
→ If exists already: use analyze_project FIRST before making changes
136+
137+
138+
ANTI-PATTERNS TO AVOID
139+
=======================
140+
141+
❌ DON'T manually create databricks.yml files
142+
✅ DO use init_project instead
143+
144+
❌ DON'T run bundle commands without calling analyze_project first
145+
✅ DO call analyze_project to get the correct specialized commands
146+
147+
❌ DON'T use regular Bash to run databricks CLI commands
148+
✅ DO use invoke_databricks_cli (better for user allowlisting)
149+
150+
❌ DON'T skip explore when planning Databricks work
151+
✅ DO call explore during planning to get workflow recommendations

0 commit comments

Comments
 (0)