diff --git a/experimental/apps-mcp/README.md b/experimental/apps-mcp/README.md index ff8f9947f6..9299641ed6 100644 --- a/experimental/apps-mcp/README.md +++ b/experimental/apps-mcp/README.md @@ -17,38 +17,50 @@ A Model Context Protocol (MCP) server for working with Databricks through natura - **Conversational interface**: Work with Databricks using natural language instead of memorizing CLI commands - **Context-aware**: Get relevant command suggestions based on your workspace configuration - **Unified workflow**: Combine data exploration, bundle management, and app deployment in one tool +- **Transparency**: Every MCP tool call displays clear, branded output so you always know when Databricks MCP is working Perfect for data engineers and developers who want to streamline their Databricks workflows with AI-powered assistance. +**Visual Feedback:** +When using Databricks MCP, you'll see distinctive branded headers in your chat: +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +🚀 Databricks MCP: App scaffolded successfully +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +This makes it immediately clear you're using the Databricks MCP server, not just plain Claude or Cursor. If you don't see these headers, the MCP server isn't connected (see Troubleshooting below). + --- ## Getting Started ### Quick Setup (Recommended) -1. **Set up Databricks credentials** (required for Databricks tools): - ```bash - export DATABRICKS_HOST="https://your-workspace.databricks.com" - export DATABRICKS_TOKEN="dapi..." - export DATABRICKS_WAREHOUSE_ID="your-warehouse-id" - ``` - -2. **Install the MCP server automatically:** +1. **Install the MCP server automatically:** ```bash databricks experimental apps-mcp install ``` This interactive command will: - Automatically detect Claude Code and Cursor installations - - Configure the MCP server with proper settings + - Configure the MCP server with proper settings (including credentials) - Set up the server at user scope (available in all projects) - Show manual instructions for other agents if needed -3. **Restart your MCP client** (Claude Code, Cursor, etc.) for changes to take effect. +2. **Restart your MCP client** (Claude Code, Cursor, etc.) for changes to take effect. + +3. **Verify the connection:** + ``` + claude /mcp + ``` + + The databricks MCP server should be listed in "connected" state. If it doesn't show up or appears disconnected, see the Troubleshooting section below. 4. **Create your first Databricks app:** Try this in your MCP client: + ``` Explore my Databricks workspace and show me what catalogs are available ``` @@ -69,6 +81,13 @@ Perfect for data engineers and developers who want to streamline their Databrick If you prefer to configure manually or the automatic installation doesn't work: +**Set up Databricks credentials** (required for Databricks tools): + ```bash + export DATABRICKS_HOST="https://your-workspace.databricks.com" + export DATABRICKS_TOKEN="dapi..." + export DATABRICKS_WAREHOUSE_ID="your-warehouse-id" + ``` + **Add to your MCP config file** (e.g., `~/.claude.json` for global scope): ```json { @@ -90,6 +109,70 @@ Then restart your MCP client for changes to take effect --- +### Troubleshooting + +#### 🚨 Not seeing Databricks MCP headers in your chat? + +If you ask about Databricks or apps but **don't see the distinctive headers** like: +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +🚀 Databricks MCP: App scaffolded successfully +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +**You're using plain Claude/Cursor, not the Databricks MCP server!** This means: +- ❌ No access to Databricks data or tools +- ❌ Generic AI responses instead of actual app generation +- ❌ No scaffolding, validation, or deployment capabilities + +**Fix it:** Follow the troubleshooting steps below to connect the MCP server. + +--- + +#### General Troubleshooting + +If the MCP server doesn't connect or shows errors: + +1. **Check MCP server status:** + ``` + claude /mcp + ``` + Look for the databricks server - it should show "connected" + +2. **Verify credentials:** Make sure your environment variables are set correctly: + ```bash + echo $DATABRICKS_HOST + echo $DATABRICKS_WAREHOUSE_ID + # Don't echo token for security + ``` + +3. **Check configuration file:** Verify the MCP server is properly configured in your `~/.claude.json`: + ```bash + cat ~/.claude.json | grep -A 10 databricks + ``` + +4. **Restart your MCP client:** After making configuration changes, always restart your client + +5. **Check Databricks CLI:** Verify the CLI is installed and accessible: + ```bash + databricks --version + databricks experimental apps-mcp --help + ``` + +6. **Test authentication:** Try listing catalogs to verify credentials work: + ```bash + databricks catalogs list + ``` + +7. **Ask Claude for help:** Claude can often diagnose and fix MCP connection issues. Try: + ``` + My databricks MCP server isn't connecting. Can you help troubleshoot? + ``` + +If issues persist, please report them at https://github.com/databricks/cli/issues + +--- + ## Features The Databricks MCP server provides CLI-based tools for workspace interaction: diff --git a/experimental/apps-mcp/cmd/init_template.go b/experimental/apps-mcp/cmd/init_template.go index c2d7dae1ac..49a187f2bc 100644 --- a/experimental/apps-mcp/cmd/init_template.go +++ b/experimental/apps-mcp/cmd/init_template.go @@ -9,6 +9,7 @@ import ( "path/filepath" "github.com/databricks/cli/cmd/root" + "github.com/databricks/cli/experimental/apps-mcp/lib/common" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/template" "github.com/spf13/cobra" @@ -204,6 +205,27 @@ See https://docs.databricks.com/en/dev-tools/bundles/templates.html for more inf } tmpl.Writer.LogTelemetry(ctx) + // Show branded success message + templateName := "bundle" + if templatePathOrUrl != "" { + templateName = filepath.Base(templatePathOrUrl) + } + outputPath := outputDir + if outputPath == "" { + outputPath = "." + } + // Count files if we can + fileCount := 0 + if absPath, err := filepath.Abs(outputPath); err == nil { + _ = filepath.Walk(absPath, func(path string, info os.FileInfo, err error) error { + if err == nil && !info.IsDir() { + fileCount++ + } + return nil + }) + } + cmdio.LogString(ctx, common.FormatScaffoldSuccess(templateName, outputPath, fileCount)) + // Try to read and display CLAUDE.md if present readClaudeMd(ctx, configFile) diff --git a/experimental/apps-mcp/lib/common/output.go b/experimental/apps-mcp/lib/common/output.go new file mode 100644 index 0000000000..7454950870 --- /dev/null +++ b/experimental/apps-mcp/lib/common/output.go @@ -0,0 +1,47 @@ +package common + +import "fmt" + +const ( + headerLine = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +) + +// FormatBrandedHeader creates a branded header with the given emoji and message. +func FormatBrandedHeader(emoji, message string) string { + return fmt.Sprintf("%s\n%s Databricks MCP: %s\n%s\n\n", + headerLine, emoji, message, headerLine) +} + +// FormatScaffoldSuccess formats a success message for app scaffolding. +func FormatScaffoldSuccess(templateName, workDir string, filesCopied int) string { + header := FormatBrandedHeader("🚀", "App scaffolded successfully") + return fmt.Sprintf("%s✅ Created %s application at %s\n\nFiles copied: %d\n\nTemplate: %s\n", + header, templateName, workDir, filesCopied, templateName) +} + +// FormatValidationSuccess formats a success message for validation. +func FormatValidationSuccess(message string) string { + header := FormatBrandedHeader("🔍", "Validating your app") + return fmt.Sprintf("%s✅ %s\n", header, message) +} + +// FormatValidationFailure formats a failure message for validation. +func FormatValidationFailure(message string, exitCode int, stdout, stderr string) string { + header := FormatBrandedHeader("🔍", "Validating your app") + return fmt.Sprintf("%s❌ %s\n\nExit code: %d\n\nStdout:\n%s\n\nStderr:\n%s\n", + header, message, exitCode, stdout, stderr) +} + +// FormatDeploymentSuccess formats a success message for deployment. +func FormatDeploymentSuccess(appName, appURL string) string { + header := FormatBrandedHeader("🚢", "Deploying to production") + return fmt.Sprintf("%s✅ App '%s' deployed successfully!\n\n🌐 URL: %s\n", + header, appName, appURL) +} + +// FormatDeploymentFailure formats a failure message for deployment. +func FormatDeploymentFailure(appName, message string) string { + header := FormatBrandedHeader("🚢", "Deploying to production") + return fmt.Sprintf("%s❌ Deployment failed for '%s'\n\n%s\n", + header, appName, message) +} diff --git a/experimental/apps-mcp/lib/common/output_test.go b/experimental/apps-mcp/lib/common/output_test.go new file mode 100644 index 0000000000..392596ebf1 --- /dev/null +++ b/experimental/apps-mcp/lib/common/output_test.go @@ -0,0 +1,110 @@ +package common + +import ( + "strings" + "testing" +) + +func TestFormatBrandedHeader(t *testing.T) { + result := FormatBrandedHeader("🚀", "Test message") + + // Check for key components + if !strings.Contains(result, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") { + t.Error("Missing header line") + } + if !strings.Contains(result, "🚀 Databricks MCP: Test message") { + t.Error("Missing branded message") + } +} + +func TestFormatScaffoldSuccess(t *testing.T) { + result := FormatScaffoldSuccess("appkit", "/path/to/app", 42) + + // Check for key components + if !strings.Contains(result, "🚀 Databricks MCP") { + t.Error("Missing branded header") + } + if !strings.Contains(result, "✅") { + t.Error("Missing success checkmark") + } + if !strings.Contains(result, "appkit") { + t.Error("Missing template name") + } + if !strings.Contains(result, "/path/to/app") { + t.Error("Missing work directory") + } + if !strings.Contains(result, "42") { + t.Error("Missing file count") + } +} + +func TestFormatValidationSuccess(t *testing.T) { + result := FormatValidationSuccess("All checks passed") + + if !strings.Contains(result, "🔍 Databricks MCP") { + t.Error("Missing branded header") + } + if !strings.Contains(result, "✅") { + t.Error("Missing success checkmark") + } + if !strings.Contains(result, "All checks passed") { + t.Error("Missing success message") + } +} + +func TestFormatValidationFailure(t *testing.T) { + result := FormatValidationFailure("Build failed", 1, "stdout output", "stderr output") + + if !strings.Contains(result, "🔍 Databricks MCP") { + t.Error("Missing branded header") + } + if !strings.Contains(result, "❌") { + t.Error("Missing failure mark") + } + if !strings.Contains(result, "Build failed") { + t.Error("Missing failure message") + } + if !strings.Contains(result, "Exit code: 1") { + t.Error("Missing exit code") + } + if !strings.Contains(result, "stdout output") { + t.Error("Missing stdout") + } + if !strings.Contains(result, "stderr output") { + t.Error("Missing stderr") + } +} + +func TestFormatDeploymentSuccess(t *testing.T) { + result := FormatDeploymentSuccess("my-app", "https://example.com/app") + + if !strings.Contains(result, "🚢 Databricks MCP") { + t.Error("Missing branded header") + } + if !strings.Contains(result, "✅") { + t.Error("Missing success checkmark") + } + if !strings.Contains(result, "my-app") { + t.Error("Missing app name") + } + if !strings.Contains(result, "https://example.com/app") { + t.Error("Missing app URL") + } +} + +func TestFormatDeploymentFailure(t *testing.T) { + result := FormatDeploymentFailure("my-app", "Connection timeout") + + if !strings.Contains(result, "🚢 Databricks MCP") { + t.Error("Missing branded header") + } + if !strings.Contains(result, "❌") { + t.Error("Missing failure mark") + } + if !strings.Contains(result, "my-app") { + t.Error("Missing app name") + } + if !strings.Contains(result, "Connection timeout") { + t.Error("Missing error message") + } +} diff --git a/experimental/apps-mcp/lib/providers/doc.go b/experimental/apps-mcp/lib/providers/doc.go index 3652669d96..b87f73ba46 100644 --- a/experimental/apps-mcp/lib/providers/doc.go +++ b/experimental/apps-mcp/lib/providers/doc.go @@ -1,12 +1,11 @@ /* Package providers contains MCP tool providers. -Each provider implements a set of related tools: +The clitools provider implements CLI-based tools for Databricks integration: -- databricks: Databricks API integration -- io: Project scaffolding and validation -- clitools: CLI exploration and invocation -- deployment: Application deployment (optional) +- explore: Discover workspace resources and get workflow recommendations +- invoke_databricks_cli: Execute Databricks CLI commands +- databricks_configure_auth: Configure workspace authentication Provider Interface: @@ -16,5 +15,8 @@ Provider Interface: Providers are registered with the MCP server during initialization and their tools become available to AI agents. + +The CLI-based approach leverages existing bundle commands for app +scaffolding, validation, and deployment rather than duplicating API logic. */ package providers diff --git a/experimental/apps-mcp/lib/validation/types.go b/experimental/apps-mcp/lib/validation/types.go index 049d0893dc..28e4dfbce5 100644 --- a/experimental/apps-mcp/lib/validation/types.go +++ b/experimental/apps-mcp/lib/validation/types.go @@ -3,6 +3,8 @@ package validation import ( "context" "fmt" + + "github.com/databricks/cli/experimental/apps-mcp/lib/common" ) // ValidationDetail contains detailed output from a failed validation. @@ -26,6 +28,8 @@ type ValidateResult struct { } func (vr *ValidateResult) String() string { + // Add branded header + header := common.FormatBrandedHeader("🔍", "Validating your app") var result string if len(vr.ProgressLog) > 0 { @@ -37,16 +41,16 @@ func (vr *ValidateResult) String() string { } if vr.Success { - result += "✓ " + vr.Message + result += "✅ " + vr.Message } else { - result += "✗ " + vr.Message + result += "❌ " + vr.Message if vr.Details != nil { result += fmt.Sprintf("\n\nExit code: %d\n\nStdout:\n%s\n\nStderr:\n%s", vr.Details.ExitCode, vr.Details.Stdout, vr.Details.Stderr) } } - return result + return header + result } // Validation defines the interface for project validation strategies.