Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions experimental/apps-mcp/cmd/init_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ After initialization:

configFile := tmpFile.Name()

// Create output directory if specified and doesn't exist
if outputDir != "" {
if err := os.MkdirAll(outputDir, 0o755); err != nil {
return fmt.Errorf("create output directory: %w", err)
}
}

r := template.Resolver{
TemplatePathOrUrl: templatePathOrUrl,
ConfigFile: configFile,
Expand Down
5 changes: 2 additions & 3 deletions experimental/apps-mcp/lib/prompts/apps.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ invoke_databricks_cli 'experimental apps-mcp tools validate ./your-app-location'

# Deployment

⚠️ Always use the sequence of commands:
⚠️ Use the deploy command which validates, deploys, and runs the app:

invoke_databricks_cli 'bundle deploy'
invoke_databricks_cli 'bundle run app'
invoke_databricks_cli 'experimental apps-mcp tools deploy'

# View and manage your app:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ The init-template command validates this automatically.

## TypeScript Import Rules

This template uses strict TypeScript settings with `verbatimModuleSyntax: true`. **Always use `import type` for type-only imports**:
This template uses strict TypeScript settings with `verbatimModuleSyntax: true`. **Always use `import type` for type-only imports**.

Template enforces `noUnusedLocals` - remove unused imports immediately or build fails.

**Type-only imports**:

```typescript
// ✅ CORRECT - use import type for types
Expand Down Expand Up @@ -166,6 +170,20 @@ WHERE DATE(timestamp_column) >= :start_date
- **Dates**: Format as `YYYY-MM-DD`, use with `DATE()` in SQL
- **Optional**: Use empty string default, check with `(:param = '' OR column = :param)`

## SQL Type Handling

Numeric fields from Databricks SQL (especially `ROUND()`, `AVG()`, `SUM()`) are returned as strings in JSON. Convert before using numeric methods:

```typescript
// ❌ WRONG - fails at runtime
<span>{row.total_amount.toFixed(2)}</span>

// ✅ CORRECT
<span>{Number(row.total_amount).toFixed(2)}</span>
```

Use helpers from `shared/types.ts`: `toNumber()`, `formatCurrency()`, `formatPercent()`.

## tRPC for Custom Endpoints:

**CRITICAL**: Do NOT use tRPC for SQL queries or data retrieval. Use `config/queries/` + `useAnalyticsQuery` instead.
Expand Down Expand Up @@ -363,6 +381,10 @@ npm run test:e2e:ui # Run with Playwright UI
- Feature components: `client/src/components/FeatureName.tsx`
- Split components when logic exceeds ~100 lines or component is reused

### Radix UI Constraints

- `SelectItem` cannot have `value=""`. Use sentinel value like `"all"` for "show all" options.

### Best Practices:

- Use shadcn/radix components (Button, Input, Card, etc.) for consistent UI
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
export interface QueryResult {
value: string;
}

// SQL type helpers - numeric fields from Databricks SQL return as strings
export const toNumber = (val: string | number | null | undefined): number =>
Number(val || 0);

export const formatCurrency = (val: string | number | null | undefined): string =>
`$${toNumber(val).toFixed(2)}`;

export const formatPercent = (val: string | number | null | undefined): string =>
`${toNumber(val).toFixed(1)}%`;
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ test('smoke test - app loads and displays data', async ({ page }) => {
// Navigate to the app
await page.goto('/');

// Wait for the page title to be visible
// ⚠️ UPDATE THESE SELECTORS after customizing App.tsx:
// - Change heading name to match your app title
// - Change data selector to match your primary data display
await expect(page.getByRole('heading', { name: 'Minimal Databricks App' })).toBeVisible();

// Wait for SQL query result to load (wait for "hello world" to appear)
await expect(page.getByText('hello world', { exact: true })).toBeVisible({ timeout: 30000 });

// Wait for health check to complete (wait for "OK" status)
Expand Down