Skip to content

Commit b9eb6cb

Browse files
committed
feat(appkit): reference agent-app, dev-playground chat UI, docs, and template
Final layer of the agents feature stack. Everything needed to exercise, demonstrate, and learn the feature. `apps/agent-app/` — a standalone app purpose-built around the agents feature. Ships with: - `server.ts` — full example of code-defined agents via `fromPlugin`: ```ts const support = createAgent({ instructions: "…", tools: { ...fromPlugin(analytics), ...fromPlugin(files), get_weather, "mcp.vector-search": mcpServer("vector-search", "https://…"), }, }); await createApp({ plugins: [server({ port }), analytics(), files(), agents({ agents: { support } })], }); ``` - `config/agents/assistant.md` — markdown-driven agent alongside the code-defined one, showing the asymmetric auto-inherit default. - Vite + React 19 + TailwindCSS frontend with a chat UI. - Databricks deployment config (`databricks.yml`, `app.yaml`) and deploy scripts. `apps/dev-playground/client/src/routes/agent.route.tsx` — chat UI with inline autocomplete (hits the `autocomplete` markdown agent) and a full threaded conversation panel (hits the default agent). `apps/dev-playground/server/index.ts` — adds a code-defined `helper` agent using `fromPlugin(analytics)` alongside the markdown-driven `autocomplete` agent in `config/agents/`. Exercises the mixed-style setup (markdown + code) against the same plugin list. `apps/dev-playground/config/agents/*.md` — both agents defined with valid YAML frontmatter. `docs/docs/plugins/agents.md` — progressive five-level guide: 1. Drop a markdown file → it just works. 2. Scope tools via `toolkits:` / `tools:` frontmatter. 3. Code-defined agents with `fromPlugin()`. 4. Sub-agents. 5. Standalone `runAgent()` (no `createApp` or HTTP). Plus a configuration reference, runtime API reference, and frontmatter schema table. `docs/docs/api/appkit/` — regenerated typedoc for the new public surface (fromPlugin, runAgent, AgentDefinition, AgentsPluginConfig, ToolkitEntry, ToolkitOptions, all adapter types, and the agents plugin factory). `template/appkit.plugins.json` — adds the `agent` plugin entry so `npx @databricks/appkit init --features agent` scaffolds the plugin correctly. - Full appkit vitest suite: 1311 tests passing - Typecheck clean across all 8 workspace projects - `pnpm docs:build` clean (no broken links) - `pnpm --filter=@databricks/appkit build:package` clean, publint clean Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> Documents the new `mcp` configuration block and the rules it enforces: same-origin-only by default, explicit `trustedHosts` for external MCP servers, plaintext `http://` refused outside localhost-in-dev, and DNS-level blocking of private / link-local IP ranges (covers cloud metadata services). See PR #302 for the policy implementation and PR #304 for the `AgentsPluginConfig.mcp` wiring. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> - `docs/docs/plugins/agents.md`: new "SQL agent tools" subsection covering `analytics.query` readOnly enforcement, `lakebase.query` opt-in via `exposeAsAgentTool`, and the approval flow. New "Human-in-the-loop approval for destructive tools" subsection documents the config, SSE event shape, and `POST /chat/approve` contract. - `apps/agent-app`: approval-card component rendered inline in the chat stream whenever an `appkit.approval_pending` event arrives. Destructive badge + Approve/Deny buttons POST to `/api/agent/approve` with the carried `streamId`/`approvalId`. - `apps/dev-playground/client`: matching approval-card on the agent route, using the existing appkit-ui `Button` component and Tailwind utility classes. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> Updates `docs/docs/plugins/agents.md` to document the new two-key auto-inherit model introduced in PR #302 (per-tool `autoInheritable` flag) and PR #304 (safe-by-default `autoInheritTools: { file: false, code: false }`). Adds an "Auto-inherit posture" subsection explaining that the developer must opt into `autoInheritTools` AND the plugin author must mark each tool `autoInheritable: true` for a tool to spread without explicit wiring. Includes a table documenting the `autoInheritable` marking on each core plugin tool, plus an example of the setup-time audit log so operators can see exactly what's inherited vs. skipped. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> - **Reference app no longer ships hardcoded dogfood URLs.** The three `https://e2-dogfood.staging.cloud.databricks.com/...` and `https://mario-mcp-hello-*.staging.aws.databricksapps.com/...` MCP URLs in `apps/agent-app/server.ts` are replaced with optional env-driven `VECTOR_SEARCH_MCP_URL` / `CUSTOM_MCP_URL` config. When set, their hostnames are auto-added to `agents({ mcp: { trustedHosts } })`. `.env.example` uses placeholder values the reader can replace instead of another team's workspace. - **`appkit.agent` → `appkit.agents` in the reference app.** The prior `appkit.agent as { list, getDefault }` cast papered over the plugin-name mismatch fixed in PR #304. The runtime key now matches the docs, the manifest, and the factory name; the cast is gone. - **Auto-inherit opt-in added to the reference config.** Since the defaults flipped to `{ file: false, code: false }` (PR #304, S-3), the reference now explicitly enables `autoInheritTools: { file: true }` so the markdown agents that ship alongside the code-defined one still pick up the analytics / files read-only tools. This is the pattern a real deployment should follow — opt in deliberately. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> - `apps/dev-playground/config/agents/autocomplete.md` sets `ephemeral: true`. Each debounced autocomplete keystroke no longer leaves an orphan thread in `InMemoryThreadStore` — the server now deletes the thread in the stream's `finally` (PR #304). Closes R1 from the MVP re-review. - `docs/docs/plugins/agents.md` documents the new `ephemeral` frontmatter key alongside the other AgentDefinition knobs. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> Documents the MVP resource caps landed in PR #304: the static request-body caps (enforced by the Zod schemas) and the three configurable runtime limits (`maxConcurrentStreamsPerUser`, `maxToolCalls`, `maxSubAgentDepth`). Includes the config-block shape in the main reference and a new "Resource limits" subsection under the Configuration section explaining the intent and per-user semantics of each cap. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
1 parent 8b0c28e commit b9eb6cb

68 files changed

Lines changed: 4565 additions & 54 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/agent-app/.env.example

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Databricks workspace (auto-injected by platform on deploy)
2+
DATABRICKS_HOST=https://your-workspace.cloud.databricks.com
3+
4+
# Agent LLM endpoint (Model Serving endpoint name)
5+
DATABRICKS_AGENT_ENDPOINT=databricks-claude-sonnet-4-5
6+
7+
# Analytics plugin — SQL warehouse ID
8+
DATABRICKS_WAREHOUSE_ID=your-warehouse-id
9+
10+
# Files plugin — Volume path (catalog.schema.volume)
11+
DATABRICKS_VOLUME_FILES=/Volumes/your-catalog/your-schema/your-volume
12+
13+
# Optional: Custom MCP servers the agent can call. When set, the hostname
14+
# is automatically added to agents({ mcp: { trustedHosts } }).
15+
# VECTOR_SEARCH_MCP_URL=https://<workspace>/api/2.0/mcp/vector-search/<catalog>/<schema>/<index>
16+
# CUSTOM_MCP_URL=https://<your-mcp-server>/mcp

apps/agent-app/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
dist
3+
.env

apps/agent-app/app.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
command: ['node', '--import', 'tsx', 'server.ts']
2+
env:
3+
- name: DATABRICKS_WAREHOUSE_ID
4+
valueFrom: sql-warehouse
5+
- name: DATABRICKS_AGENT_ENDPOINT
6+
valueFrom: serving-endpoint
7+
- name: DATABRICKS_VOLUME_FILES
8+
valueFrom: volume
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
endpoint: databricks-claude-sonnet-4-5
3+
default: true
4+
toolkits:
5+
- files: [files.list, files.upload, files.delete]
6+
agents:
7+
- support
8+
- researcher
9+
---
10+
11+
You are a front-desk dispatcher running on Databricks.
12+
13+
Delegate requests to the right specialist:
14+
15+
- `agent-support` — data analysis (SQL via analytics), file browsing, and general questions.
16+
- `agent-researcher` — research and knowledge lookups that benefit from MCP-hosted tools (vector search, custom endpoints).
17+
18+
Only use your own tools (`files.upload`, `files.delete`, `files.list`) for
19+
file-management actions the user explicitly asks for. Destructive ones
20+
(`upload`, `delete`) will prompt the user for approval before running.
21+
22+
Keep your own responses short — mostly routing decisions plus a brief summary
23+
of what the specialist returned.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
endpoint: databricks-claude-sonnet-4-5
3+
toolkits:
4+
- analytics
5+
- files
6+
tools:
7+
- get_weather
8+
# Optional MCP servers — uncomment the ones whose env vars are set in
9+
# .env (VECTOR_SEARCH_MCP_URL, CUSTOM_MCP_URL). `server.ts` only
10+
# registers each as ambient when its URL is configured, so leaving a
11+
# reference here while the env var is unset will fail at startup.
12+
# - mcp.vector-search
13+
# - mcp.custom
14+
---
15+
16+
You help customers with data analysis, file browsing, and general questions.
17+
Use the available tools as needed and summarize results concisely.

apps/agent-app/databricks.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
bundle:
2+
name: appkit-agent-app
3+
4+
variables:
5+
sql_warehouse_id:
6+
description: SQL Warehouse ID for analytics queries
7+
serving_endpoint_name:
8+
description: Model Serving endpoint name for the agent LLM
9+
volume_full_name:
10+
description: "UC Volume full name (e.g. catalog.schema.volume_name)"
11+
12+
resources:
13+
apps:
14+
agent_app:
15+
name: "appkit-agent-app"
16+
description: "AppKit agent with auto-discovered tools from analytics, files, and genie plugins"
17+
source_code_path: ./
18+
19+
user_api_scopes:
20+
- sql
21+
- files.files
22+
- dashboards.genie
23+
24+
resources:
25+
- name: sql-warehouse
26+
sql_warehouse:
27+
id: ${var.sql_warehouse_id}
28+
permission: CAN_USE
29+
30+
- name: serving-endpoint
31+
serving_endpoint:
32+
name: ${var.serving_endpoint_name}
33+
permission: CAN_QUERY
34+
35+
- name: volume
36+
uc_securable:
37+
securable_type: VOLUME
38+
securable_full_name: ${var.volume_full_name}
39+
permission: WRITE_VOLUME
40+
41+
targets:
42+
dogfood:
43+
default: true
44+
workspace:
45+
host: https://e2-dogfood.staging.cloud.databricks.com
46+
47+
variables:
48+
sql_warehouse_id: dd43ee29fedd958d
49+
serving_endpoint_name: databricks-claude-sonnet-4-5
50+
volume_full_name: main.mario.mario-vol

apps/agent-app/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>AppKit Agent</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/main.tsx"></script>
11+
</body>
12+
</html>

apps/agent-app/package.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "agent-app",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "NODE_ENV=development tsx watch server.ts",
8+
"build": "tsc -b && vite build",
9+
"preview": "vite preview"
10+
},
11+
"dependencies": {
12+
"@databricks/appkit": "workspace:*",
13+
"@databricks/appkit-ui": "workspace:*",
14+
"@databricks/sdk-experimental": "^0.16.0",
15+
"dotenv": "^16.6.1",
16+
"lucide-react": "^0.511.0",
17+
"react": "19.2.0",
18+
"react-dom": "19.2.0",
19+
"marked": "^15.0.0",
20+
"zod": "^4.0.0"
21+
},
22+
"devDependencies": {
23+
"@tailwindcss/postcss": "4.1.17",
24+
"@types/node": "24.10.1",
25+
"@types/react": "19.2.7",
26+
"@types/react-dom": "19.2.3",
27+
"@vitejs/plugin-react": "5.1.1",
28+
"autoprefixer": "10.4.21",
29+
"postcss": "8.5.6",
30+
"tailwindcss": "4.1.17",
31+
"tailwindcss-animate": "1.0.7",
32+
"tw-animate-css": "1.4.0",
33+
"tsx": "4.20.6",
34+
"typescript": "5.9.3",
35+
"vite": "npm:rolldown-vite@7.1.14"
36+
},
37+
"overrides": {
38+
"vite": "npm:rolldown-vite@7.1.14"
39+
}
40+
}

apps/agent-app/postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
"@tailwindcss/postcss": {},
4+
autoprefixer: {},
5+
},
6+
};

apps/agent-app/server.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import {
2+
agents,
3+
analytics,
4+
createAgent,
5+
createApp,
6+
files,
7+
mcpServer,
8+
server,
9+
tool,
10+
} from "@databricks/appkit";
11+
import { z } from "zod";
12+
13+
const port = Number(process.env.DATABRICKS_APP_PORT) || 8003;
14+
15+
// Ambient function tool. Referenced from `config/agents/support.md` under
16+
// `tools: [get_weather]`. Markdown frontmatter looks up this name against
17+
// the `tools:` record passed to `agents({ tools: { get_weather } })` below.
18+
const get_weather = tool({
19+
name: "get_weather",
20+
description: "Get the current weather for a city",
21+
schema: z.object({
22+
city: z.string().describe("City name"),
23+
}),
24+
execute: async ({ city }) => `The weather in ${city} is sunny, 22°C`,
25+
});
26+
27+
// MCP servers are conditional on runtime env vars — something markdown
28+
// frontmatter can't express. This is the motivating case for defining
29+
// the `researcher` agent in code below: it wires whatever MCP tools are
30+
// configured at boot, and is always callable (with a graceful fallback
31+
// when nothing is wired).
32+
//
33+
// Any MCP URL configured here must also be allowlisted via
34+
// `agents({ mcp: { trustedHosts: [...] } })` before outbound calls will
35+
// be allowed by the zero-trust host policy.
36+
const customMcpServers: Record<string, ReturnType<typeof mcpServer>> = {};
37+
if (process.env.VECTOR_SEARCH_MCP_URL) {
38+
customMcpServers["mcp.vector-search"] = mcpServer(
39+
"vector-search",
40+
process.env.VECTOR_SEARCH_MCP_URL,
41+
);
42+
}
43+
if (process.env.CUSTOM_MCP_URL) {
44+
customMcpServers["mcp.custom"] = mcpServer(
45+
"custom",
46+
process.env.CUSTOM_MCP_URL,
47+
);
48+
}
49+
50+
// Code-defined research specialist. `assistant.md` references this by name
51+
// under `agents: [researcher]`; the agents plugin resolves that reference
52+
// against both markdown siblings and code-defined agents, with code winning
53+
// on collision. Defined in code so its MCP toolset can flex on env vars.
54+
const researcher = createAgent({
55+
instructions:
56+
"You are a research specialist. When MCP tools are available " +
57+
"(vector search, custom endpoints), prefer them for knowledge lookups. " +
58+
"If no MCP tools are configured, say so briefly and answer from general " +
59+
"knowledge. Always include your source or note when you're answering " +
60+
"without search.",
61+
tools: {
62+
get_weather,
63+
...customMcpServers,
64+
},
65+
});
66+
67+
const trustedMcpHosts = [
68+
process.env.VECTOR_SEARCH_MCP_URL,
69+
process.env.CUSTOM_MCP_URL,
70+
]
71+
.filter((u): u is string => typeof u === "string" && u.length > 0)
72+
.map((u) => new URL(u).hostname);
73+
74+
const appkit = await createApp({
75+
plugins: [
76+
server({ port }),
77+
analytics(),
78+
files(),
79+
agents({
80+
// Code-defined agents merged with markdown agents; code wins on name
81+
// collision. Markdown `agents: [...]` frontmatter can reference either.
82+
agents: { researcher },
83+
// Ambient tool library for markdown agents referencing names under
84+
// their `tools:` frontmatter.
85+
tools: { get_weather, ...customMcpServers },
86+
// Enables auto-inherit of read-only plugin tools (analytics/files) into
87+
// markdown agents that declare no explicit `toolkits:` / `tools:`. Both
88+
// assistant.md and support.md are explicit, so this is a no-op today,
89+
// but kept as a knob markdown authors can rely on.
90+
autoInheritTools: { file: true },
91+
mcp: { trustedHosts: trustedMcpHosts },
92+
}),
93+
],
94+
});
95+
96+
console.log(
97+
`Agent app running on port ${port}. ` +
98+
`Agents: ${appkit.agents.list().join(", ") || "(none)"}. ` +
99+
`Default: ${appkit.agents.getDefault() ?? "(none)"}.`,
100+
);

0 commit comments

Comments
 (0)