databricks-agentbricks 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. databricks_agentbricks/__init__.py +1 -0
  2. databricks_agentbricks/_group.py +217 -0
  3. databricks_agentbricks/agent_project.py +657 -0
  4. databricks_agentbricks/app_resources.py +139 -0
  5. databricks_agentbricks/cli/__init__.py +12 -0
  6. databricks_agentbricks/cli/app.py +108 -0
  7. databricks_agentbricks/cli/app_auth.py +198 -0
  8. databricks_agentbricks/cli/auth.py +142 -0
  9. databricks_agentbricks/cli/deploy.py +954 -0
  10. databricks_agentbricks/cli/dev.py +310 -0
  11. databricks_agentbricks/cli/endpoint.py +145 -0
  12. databricks_agentbricks/cli/endpoint_examples.py +33 -0
  13. databricks_agentbricks/cli/endpoint_output.py +53 -0
  14. databricks_agentbricks/cli/endpoint_request.py +71 -0
  15. databricks_agentbricks/cli/endpoint_transport.py +148 -0
  16. databricks_agentbricks/cli/help.py +467 -0
  17. databricks_agentbricks/cli/init.py +496 -0
  18. databricks_agentbricks/cli/mcp.py +75 -0
  19. databricks_agentbricks/cli/memory.py +520 -0
  20. databricks_agentbricks/cli/sandbox.py +823 -0
  21. databricks_agentbricks/cli/sessions.py +588 -0
  22. databricks_agentbricks/cli/tools.py +456 -0
  23. databricks_agentbricks/cli/tracing.py +724 -0
  24. databricks_agentbricks/databricks_cli.py +38 -0
  25. databricks_agentbricks/errors.py +114 -0
  26. databricks_agentbricks/lakebase_runtime_store.py +107 -0
  27. databricks_agentbricks/legacy_lakebase_runtime_store.py +74 -0
  28. databricks_agentbricks/project_config.py +157 -0
  29. databricks_agentbricks/project_types.py +55 -0
  30. databricks_agentbricks/py.typed +0 -0
  31. databricks_agentbricks/render.py +349 -0
  32. databricks_agentbricks/templates/agent-bricks-migrate/SKILL.md +101 -0
  33. databricks_agentbricks/templates/agent-langgraph/.env.example +36 -0
  34. databricks_agentbricks/templates/agent-langgraph/.gitignore +199 -0
  35. databricks_agentbricks/templates/agent-langgraph/AGENTKIT_CONTRACT.md +95 -0
  36. databricks_agentbricks/templates/agent-langgraph/AGENTS.md +31 -0
  37. databricks_agentbricks/templates/agent-langgraph/CLAUDE.md +1 -0
  38. databricks_agentbricks/templates/agent-langgraph/README.md +141 -0
  39. databricks_agentbricks/templates/agent-langgraph/agent/__init__.py +0 -0
  40. databricks_agentbricks/templates/agent-langgraph/agent/agent.py +177 -0
  41. databricks_agentbricks/templates/agent-langgraph/agent/mcps.py +27 -0
  42. databricks_agentbricks/templates/agent-langgraph/agent/tools/__init__.py +24 -0
  43. databricks_agentbricks/templates/agent-langgraph/agent/tools/sample_tool.py +15 -0
  44. databricks_agentbricks/templates/agent-langgraph/agent/tools/send_message.py +17 -0
  45. databricks_agentbricks/templates/agent-langgraph/app.yaml +15 -0
  46. databricks_agentbricks/templates/agent-langgraph/pyproject.toml +42 -0
  47. databricks_agentbricks/templates/agent-langgraph/runtime/__init__.py +0 -0
  48. databricks_agentbricks/templates/agent-langgraph/runtime/adapter.py +144 -0
  49. databricks_agentbricks/templates/agent-langgraph/runtime/main.py +25 -0
  50. databricks_agentbricks/templates/agent-langgraph/tests/__init__.py +0 -0
  51. databricks_agentbricks/templates/agent-langgraph/tests/test_agent.py +211 -0
  52. databricks_agentbricks/templates/agent-langgraph/tests/test_runtime.py +19 -0
  53. databricks_agentbricks/templates/agent-langgraph/tests/test_workspace.py +28 -0
  54. databricks_agentbricks/templates/agent-openai/.env.example +37 -0
  55. databricks_agentbricks/templates/agent-openai/.gitignore +199 -0
  56. databricks_agentbricks/templates/agent-openai/AGENTKIT_CONTRACT.md +107 -0
  57. databricks_agentbricks/templates/agent-openai/AGENTS.md +76 -0
  58. databricks_agentbricks/templates/agent-openai/CLAUDE.md +1 -0
  59. databricks_agentbricks/templates/agent-openai/README.md +143 -0
  60. databricks_agentbricks/templates/agent-openai/agent/__init__.py +0 -0
  61. databricks_agentbricks/templates/agent-openai/agent/agent.py +205 -0
  62. databricks_agentbricks/templates/agent-openai/agent/mcps.py +28 -0
  63. databricks_agentbricks/templates/agent-openai/agent/tools/__init__.py +24 -0
  64. databricks_agentbricks/templates/agent-openai/agent/tools/sample_tool.py +15 -0
  65. databricks_agentbricks/templates/agent-openai/agent/tools/send_message.py +18 -0
  66. databricks_agentbricks/templates/agent-openai/app.yaml +15 -0
  67. databricks_agentbricks/templates/agent-openai/pyproject.toml +42 -0
  68. databricks_agentbricks/templates/agent-openai/runtime/__init__.py +0 -0
  69. databricks_agentbricks/templates/agent-openai/runtime/adapter.py +188 -0
  70. databricks_agentbricks/templates/agent-openai/runtime/main.py +25 -0
  71. databricks_agentbricks/templates/agent-openai/tests/__init__.py +0 -0
  72. databricks_agentbricks/templates/agent-openai/tests/test_agent.py +326 -0
  73. databricks_agentbricks/templates/agent-openai/tests/test_runtime.py +19 -0
  74. databricks_agentbricks/templates/agent-openai/tests/test_workspace.py +28 -0
  75. databricks_agentbricks/templates/custom-agent-langgraph/.env.example +1 -0
  76. databricks_agentbricks/templates/custom-agent-langgraph/.gitignore +11 -0
  77. databricks_agentbricks/templates/custom-agent-langgraph/README.md +22 -0
  78. databricks_agentbricks/templates/custom-agent-langgraph/agent/__init__.py +0 -0
  79. databricks_agentbricks/templates/custom-agent-langgraph/agent/agent.py +27 -0
  80. databricks_agentbricks/templates/custom-agent-langgraph/app.yaml +1 -0
  81. databricks_agentbricks/templates/custom-agent-langgraph/pyproject.toml +38 -0
  82. databricks_agentbricks/templates/custom-agent-langgraph/runtime/__init__.py +0 -0
  83. databricks_agentbricks/templates/custom-agent-langgraph/runtime/main.py +31 -0
  84. databricks_agentbricks/templates/custom-agent-langgraph/tests/test_runtime.py +29 -0
  85. databricks_agentbricks/templates/custom-agent-openai/.env.example +1 -0
  86. databricks_agentbricks/templates/custom-agent-openai/.gitignore +11 -0
  87. databricks_agentbricks/templates/custom-agent-openai/README.md +22 -0
  88. databricks_agentbricks/templates/custom-agent-openai/agent/__init__.py +0 -0
  89. databricks_agentbricks/templates/custom-agent-openai/agent/agent.py +29 -0
  90. databricks_agentbricks/templates/custom-agent-openai/app.yaml +1 -0
  91. databricks_agentbricks/templates/custom-agent-openai/pyproject.toml +38 -0
  92. databricks_agentbricks/templates/custom-agent-openai/runtime/__init__.py +0 -0
  93. databricks_agentbricks/templates/custom-agent-openai/runtime/main.py +31 -0
  94. databricks_agentbricks/templates/custom-agent-openai/tests/test_runtime.py +29 -0
  95. databricks_agentbricks/templates/sandbox_mcp.py +39 -0
  96. databricks_agentbricks/templates/sandbox_mcp_langgraph.py +39 -0
  97. databricks_agentbricks/templates/ui/agent-langgraph/CHAT_APP.md +49 -0
  98. databricks_agentbricks/templates/ui/agent-langgraph/runtime/main.py +26 -0
  99. databricks_agentbricks/templates/ui/agent-langgraph/runtime/ui.py +528 -0
  100. databricks_agentbricks/templates/ui/agent-langgraph/tests/test_demo_ui.py +542 -0
  101. databricks_agentbricks/templates/ui/agent-langgraph/ui/agentbricks-mark.png +0 -0
  102. databricks_agentbricks/templates/ui/agent-langgraph/ui/app.js +1464 -0
  103. databricks_agentbricks/templates/ui/agent-langgraph/ui/index.html +281 -0
  104. databricks_agentbricks/templates/ui/agent-langgraph/ui/styles.css +1913 -0
  105. databricks_agentbricks/templates/ui/agent-openai/CHAT_APP.md +54 -0
  106. databricks_agentbricks/templates/ui/agent-openai/runtime/main.py +26 -0
  107. databricks_agentbricks/templates/ui/agent-openai/runtime/ui.py +521 -0
  108. databricks_agentbricks/templates/ui/agent-openai/tests/test_demo_ui.py +512 -0
  109. databricks_agentbricks/templates/ui/agent-openai/ui/agentbricks-mark.png +0 -0
  110. databricks_agentbricks/templates/ui/agent-openai/ui/app.js +1428 -0
  111. databricks_agentbricks/templates/ui/agent-openai/ui/index.html +281 -0
  112. databricks_agentbricks/templates/ui/agent-openai/ui/styles.css +1913 -0
  113. databricks_agentbricks/theme.py +72 -0
  114. databricks_agentbricks-0.2.0.dist-info/METADATA +897 -0
  115. databricks_agentbricks-0.2.0.dist-info/RECORD +159 -0
  116. databricks_agentbricks-0.2.0.dist-info/WHEEL +4 -0
  117. databricks_agentbricks-0.2.0.dist-info/entry_points.txt +2 -0
  118. databricks_agentbricks-0.2.0.dist-info/licenses/NOTICE +39 -0
  119. databricks_agentkit/__init__.py +67 -0
  120. databricks_agentkit/_api_client.py +673 -0
  121. databricks_agentkit/_pagination.py +25 -0
  122. databricks_agentkit/client.py +31 -0
  123. databricks_agentkit/langgraph/__init__.py +98 -0
  124. databricks_agentkit/langgraph/genie.py +50 -0
  125. databricks_agentkit/langgraph/mcp.py +216 -0
  126. databricks_agentkit/langgraph/memory.py +67 -0
  127. databricks_agentkit/langgraph/session_store.py +485 -0
  128. databricks_agentkit/memory_store.py +371 -0
  129. databricks_agentkit/models.py +330 -0
  130. databricks_agentkit/openai/__init__.py +93 -0
  131. databricks_agentkit/openai/genie.py +50 -0
  132. databricks_agentkit/openai/mcp.py +189 -0
  133. databricks_agentkit/openai/memory.py +67 -0
  134. databricks_agentkit/openai/sessions.py +157 -0
  135. databricks_agentkit/py.typed +0 -0
  136. databricks_agentkit/runtime/ARCHITECTURE.md +64 -0
  137. databricks_agentkit/runtime/README.md +184 -0
  138. databricks_agentkit/runtime/__init__.py +69 -0
  139. databricks_agentkit/runtime/app.py +316 -0
  140. databricks_agentkit/runtime/auth.py +149 -0
  141. databricks_agentkit/runtime/durability/__init__.py +1 -0
  142. databricks_agentkit/runtime/durability/execution.py +133 -0
  143. databricks_agentkit/runtime/durability/heartbeat.py +64 -0
  144. databricks_agentkit/runtime/durability/lakebase_runtime_store.py +648 -0
  145. databricks_agentkit/runtime/durability/recovery.py +70 -0
  146. databricks_agentkit/runtime/durability/store.py +33 -0
  147. databricks_agentkit/runtime/execution.py +160 -0
  148. databricks_agentkit/runtime/genie.py +224 -0
  149. databricks_agentkit/runtime/mcp_auth.py +114 -0
  150. databricks_agentkit/runtime/model_services.py +90 -0
  151. databricks_agentkit/runtime/runtime.py +273 -0
  152. databricks_agentkit/runtime/session_store_client.py +120 -0
  153. databricks_agentkit/runtime/store.py +287 -0
  154. databricks_agentkit/runtime/tool_manifest.py +223 -0
  155. databricks_agentkit/runtime/tracing.py +71 -0
  156. databricks_agentkit/runtime/types.py +118 -0
  157. databricks_agentkit/runtime/workspace.py +27 -0
  158. databricks_agentkit/session_store.py +434 -0
  159. databricks_agentkit/timefmt.py +85 -0
@@ -0,0 +1,456 @@
1
+ """Discover available integrations and manage manifest-backed tool bindings."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pathlib
6
+ import re
7
+ from typing import Any, Literal
8
+
9
+ import click
10
+
11
+ from databricks_agentbricks import render
12
+ from databricks_agentbricks.agent_project import AgentProject, Scope, ToolSpec
13
+ from databricks_agentbricks.cli.help import _example_epilog
14
+ from databricks_agentbricks.cli.mcp import _add_command, _list_services, _validate_schema
15
+ from databricks_agentbricks.errors import AgentCliError
16
+ from databricks_agentbricks.project_config import require_managed_tool_support
17
+
18
+
19
+ def _identifier(value: str) -> str:
20
+ normalized = re.sub(r"[^A-Za-z0-9_]", "_", value.strip()).strip("_").lower()
21
+ if not normalized or normalized[0].isdigit():
22
+ raise AgentCliError(f"Could not derive a Python identifier from {value!r}.")
23
+ return normalized
24
+
25
+
26
+ def _default_id(resource: str) -> str:
27
+ return _identifier(resource.rsplit(".", 1)[-1])
28
+
29
+
30
+ def _require_arg(value: str, label: str) -> str:
31
+ """Reject an empty/whitespace positional argument with a clear message."""
32
+ if value is None or not value.strip():
33
+ raise AgentCliError(f"A {label} is required.")
34
+ return value
35
+
36
+
37
+ def _source_value(spec: ToolSpec) -> str:
38
+ # For a sandbox tool, the useful detail is the allowed scopes, not the (constant)
39
+ # 'system.ai.sandbox' service name that duplicates the KIND column.
40
+ if spec.source.kind == "sandbox" and spec.policy.downscope:
41
+ return ", ".join(s.resource for s in spec.policy.downscope)
42
+ return spec.source.service or spec.source.function or spec.source.space_id or spec.source.kind
43
+
44
+
45
+ def _tool_record(spec: ToolSpec) -> dict[str, str]:
46
+ record = {
47
+ "id": spec.id,
48
+ "kind": spec.source.kind,
49
+ "source": _source_value(spec),
50
+ }
51
+ if spec.source.kind in ("mcp", "sandbox", "uc_function", "genie_one", "genie_agent"):
52
+ record["auth"] = spec.auth or (
53
+ "app/default" if spec.source.kind == "uc_function" else "unspecified"
54
+ )
55
+ return record
56
+
57
+
58
+ def _emit_change(
59
+ obj: Any, project: AgentProject, spec: ToolSpec, changed_files: list[pathlib.Path]
60
+ ) -> None:
61
+ payload = {
62
+ "schema_version": 1,
63
+ "changed": bool(changed_files),
64
+ "changed_files": [str(path) for path in changed_files],
65
+ "manifest": str(project.path),
66
+ "tool": _tool_record(spec),
67
+ }
68
+ if getattr(obj, "output", "text") == "json":
69
+ render.emit_json(payload)
70
+ return
71
+ if changed_files:
72
+ render.success(
73
+ f"Added {spec.id}",
74
+ fields={"Kind": spec.source.kind, "Manifest": str(project.path)},
75
+ )
76
+ else:
77
+ click.echo(f"Tool {spec.id!r} is already configured in {project.path}")
78
+ click.echo(f"Review {project.path} to check configured managed tools and MCP bindings.")
79
+
80
+
81
+ def _add_spec(obj: Any, source: pathlib.Path, spec: ToolSpec) -> None:
82
+ # Managed bindings are framework-neutral agent.toml entries. Both Agent Bricks server runtime
83
+ # adapters read them; custom-server projects wire tools directly in agent code.
84
+ project = AgentProject.load(source)
85
+ require_managed_tool_support(project.root)
86
+ changed = project.add_tool(spec)
87
+ if spec.source.kind == "mcp":
88
+ service = spec.source.service
89
+ try:
90
+ obj.client().get_mcp_service(service)
91
+ except AgentCliError as exc:
92
+ raise AgentCliError(
93
+ f"Could not validate MCP service {service!r}: {exc.message}",
94
+ error_code=exc.error_code,
95
+ hint=exc.hint
96
+ or "Run `ab tools list --kind mcp` with the same --profile to find "
97
+ "available services.",
98
+ ) from exc
99
+ changed_files = [project.write()] if changed else []
100
+ _emit_change(obj, project, spec, changed_files)
101
+
102
+
103
+ def add_sandbox_to_manifest(
104
+ obj: Any,
105
+ source: pathlib.Path,
106
+ scopes: tuple[str, ...],
107
+ permission: str,
108
+ *,
109
+ tool_id: str = "sandbox",
110
+ auth: Literal["user", "app"] = "user",
111
+ ) -> None:
112
+ """Shared implementation for the nested command and compatibility alias."""
113
+ parsed: list[Scope] = []
114
+ seen: set[tuple[str, str]] = set()
115
+ for value in scopes:
116
+ scope = Scope.parse(value, permission)
117
+ identity = (scope.kind, scope.value)
118
+ if identity not in seen:
119
+ parsed.append(scope)
120
+ seen.add(identity)
121
+ _add_spec(obj, source, ToolSpec.sandbox(tool_id, scopes=parsed, auth=auth))
122
+
123
+
124
+ @click.group()
125
+ def tools() -> None:
126
+ """Discover available integrations and manage an agent's tool bindings.
127
+
128
+ Tools are what let an agent act beyond the language model itself — query governed data, call a
129
+ service, or run a function — and each one is recorded in agent.toml so `ab dev` / `ab deploy`
130
+ wire it in. `ab tools add` manages these Databricks-managed tool types:
131
+
132
+ \b
133
+ sandbox Query Unity Catalog data via system.ai.sandbox, scoped
134
+ to the tables, volumes, or paths you choose.
135
+ mcp A Databricks-managed MCP service (see `ab tools list --kind mcp`),
136
+ e.g. system.ai.web_search.
137
+ uc-function An existing Unity Catalog function (catalog.schema.function).
138
+ genie-one Workspace-wide Genie One MCP tools.
139
+ genie-agent Native Genie conversation tools for a configured space ID.
140
+
141
+ Browse available integrations with `ab tools list`, add one with `ab tools add <type>`,
142
+ and drop a binding with `ab tools remove`. Review agent.toml for configured managed tools
143
+ and MCP bindings. The list shows addable integrations, not configured bindings or individual
144
+ operations inside an MCP service. Custom Python tools are code-first — write them directly
145
+ in your project's code rather than through the CLI.
146
+ """
147
+
148
+
149
+ @tools.group("add")
150
+ def add() -> None:
151
+ """Add a managed sandbox, MCP service, UC function, or Genie tool binding.
152
+
153
+ Subcommands target the current directory by default.
154
+
155
+ Pass --source PATH to target another project.
156
+
157
+ Review that project's agent.toml to check configured managed tools and MCP bindings.
158
+ """
159
+
160
+
161
+ def _source_option(function):
162
+ return click.option(
163
+ "--source",
164
+ type=click.Path(exists=True, file_okay=False, path_type=pathlib.Path),
165
+ default=pathlib.Path("."),
166
+ show_default=True,
167
+ help="Agent Bricks project containing agent.toml.",
168
+ )(function)
169
+
170
+
171
+ @add.command("sandbox")
172
+ @click.option(
173
+ "--scope",
174
+ "scopes",
175
+ multiple=True,
176
+ required=True,
177
+ help="Allowed table:, volume:, or workspace: resource. Repeat for multiple scopes.",
178
+ )
179
+ @click.option(
180
+ "--permission",
181
+ type=click.Choice(["read_only", "read_write"]),
182
+ default="read_only",
183
+ show_default=True,
184
+ )
185
+ @click.option("--name", "tool_id", default="sandbox", show_default=True)
186
+ @click.option("--auth", type=click.Choice(["user", "app"]), default="user", show_default=True)
187
+ @_source_option
188
+ @click.pass_obj
189
+ def add_sandbox(
190
+ obj: Any,
191
+ scopes: tuple[str, ...],
192
+ permission: str,
193
+ tool_id: str,
194
+ source: pathlib.Path,
195
+ auth: Literal["user", "app"],
196
+ ) -> None:
197
+ """Add a data sandbox tool (system.ai.sandbox), scoped to specific Unity Catalog resources.
198
+
199
+ Review the target project's agent.toml to check configured managed tools and MCP bindings.
200
+ """
201
+ add_sandbox_to_manifest(obj, source.resolve(), scopes, permission, tool_id=tool_id, auth=auth)
202
+
203
+
204
+ @add.command("mcp")
205
+ @click.argument("service")
206
+ @click.option("--name", "tool_id", default=None)
207
+ @click.option("--auth", type=click.Choice(["user", "app"]), default="user", show_default=True)
208
+ @_source_option
209
+ @click.pass_obj
210
+ def add_mcp(
211
+ obj: Any,
212
+ service: str,
213
+ tool_id: str | None,
214
+ source: pathlib.Path,
215
+ auth: Literal["user", "app"],
216
+ ) -> None:
217
+ """Validate and add a Databricks-managed MCP service as a tool.
218
+
219
+ Use `ab tools list --kind mcp` for available services. Review the target project's
220
+ agent.toml to check configured managed tools and MCP bindings.
221
+ """
222
+ _require_arg(service, "managed MCP service name (e.g. system.ai.web_search)")
223
+ _add_spec(
224
+ obj,
225
+ source.resolve(),
226
+ ToolSpec.mcp(tool_id or _default_id(service), service=service, auth=auth),
227
+ )
228
+
229
+
230
+ @add.command("uc-function")
231
+ @click.argument("function_name")
232
+ @click.option("--name", "tool_id", default=None)
233
+ @_source_option
234
+ @click.pass_obj
235
+ def add_uc_function(
236
+ obj: Any,
237
+ function_name: str,
238
+ tool_id: str | None,
239
+ source: pathlib.Path,
240
+ ) -> None:
241
+ """Add an existing Unity Catalog function (catalog.schema.function) as a tool.
242
+
243
+ Review the target project's agent.toml to check configured managed tools and MCP bindings.
244
+ """
245
+ _require_arg(function_name, "Unity Catalog function name (catalog.schema.function)")
246
+ _add_spec(
247
+ obj,
248
+ source.resolve(),
249
+ ToolSpec.uc_function(
250
+ tool_id or _default_id(function_name),
251
+ function=function_name,
252
+ ),
253
+ )
254
+
255
+
256
+ @add.command("genie-one", epilog=_example_epilog(("ab tools add genie-one",)))
257
+ @click.option("--name", "tool_id", default="genie_one", show_default=True)
258
+ @click.option("--auth", type=click.Choice(["user", "app"]), default="user", show_default=True)
259
+ @_source_option
260
+ @click.pass_obj
261
+ def add_genie_one(
262
+ obj: Any, tool_id: str, auth: Literal["user", "app"], source: pathlib.Path
263
+ ) -> None:
264
+ """Add workspace-wide Genie One MCP tools."""
265
+ _add_spec(obj, source.resolve(), ToolSpec.genie_one(tool_id, auth=auth))
266
+
267
+
268
+ @add.command(
269
+ "genie-agent",
270
+ epilog=_example_epilog(
271
+ ("ab tools add genie-agent 0123456789abcdef0123456789abcdef --name sales",)
272
+ ),
273
+ )
274
+ @click.argument("space_id")
275
+ @click.option("--name", "tool_id", default="genie_agent", show_default=True)
276
+ @click.option("--auth", type=click.Choice(["user", "app"]), default="user", show_default=True)
277
+ @_source_option
278
+ @click.pass_obj
279
+ def add_genie_agent(
280
+ obj: Any,
281
+ space_id: str,
282
+ tool_id: str,
283
+ auth: Literal["user", "app"],
284
+ source: pathlib.Path,
285
+ ) -> None:
286
+ """Add native Genie conversation tools for a 32-character lowercase hexadecimal SPACE_ID."""
287
+ _add_spec(
288
+ obj,
289
+ source.resolve(),
290
+ ToolSpec.genie_agent(tool_id, space_id=space_id, auth=auth),
291
+ )
292
+
293
+
294
+ @tools.command("list")
295
+ @click.option(
296
+ "--kind",
297
+ type=click.Choice(["sandbox", "mcp", "uc-function", "genie-one", "genie-agent"]),
298
+ help=(
299
+ "Show one integration kind. Sandbox, uc-function, genie-one, and genie-agent show local "
300
+ "add recipes only."
301
+ ),
302
+ )
303
+ @click.option(
304
+ "--schema",
305
+ help="Two-part UC schema: catalog.schema (default: system.ai). Requires --kind mcp.",
306
+ )
307
+ @click.pass_obj
308
+ def list_tools(obj: Any, kind: str | None, schema: str | None) -> None:
309
+ """List available integrations to add, not configured agent bindings.
310
+
311
+ By default, show built-in add recipes plus caller-visible MCP Services in system.ai.
312
+ --kind mcp limits discovery to MCP Services; --schema catalog.schema replaces system.ai.
313
+ Sandbox recipes require scopes; UC-function and Genie Agent recipes require concrete resource
314
+ identifiers. Genie One needs no additional argument.
315
+
316
+ No agent project is required. MCP discovery uses your Databricks profile; local recipes do
317
+ not authenticate. This does not scan every workspace schema or list individual MCP operations.
318
+ API failures return a nonzero exit status and mark discovery incomplete, not empty.
319
+
320
+ Review agent.toml to check configured managed tools and MCP bindings. The former configured
321
+ list and --source option are removed. JSON discovery uses schema_version 2 and available_tools.
322
+ """
323
+ if schema is not None and kind != "mcp":
324
+ raise AgentCliError("--schema requires --kind mcp.")
325
+ mcp_schema = (
326
+ _validate_schema("system.ai" if schema is None else schema)
327
+ if kind in (None, "mcp")
328
+ else None
329
+ )
330
+ sandbox_command = _add_command("system.ai.sandbox")
331
+ recipes = [
332
+ {"name": "sandbox", "kind": "sandbox", "add_command": sandbox_command},
333
+ {
334
+ "name": "uc-function",
335
+ "kind": "uc-function",
336
+ "add_command": "ab tools add uc-function catalog.schema.function",
337
+ },
338
+ {
339
+ "name": "genie-one",
340
+ "kind": "genie-one",
341
+ "add_command": "ab tools add genie-one",
342
+ },
343
+ {
344
+ "name": "genie-agent",
345
+ "kind": "genie-agent",
346
+ "add_command": "ab tools add genie-agent SPACE_ID",
347
+ },
348
+ ]
349
+ rows = [recipe for recipe in recipes if kind is None or recipe["kind"] == kind]
350
+ discovery_error = None
351
+ if mcp_schema is not None:
352
+ try:
353
+ services = _list_services(obj.client(), mcp_schema)
354
+ except AgentCliError as exc:
355
+ discovery_error = exc
356
+ else:
357
+ for service in services:
358
+ name = service["name"]
359
+ if name == "system.ai.sandbox" and kind is None:
360
+ continue
361
+ rows.append(
362
+ {
363
+ "name": name,
364
+ "kind": "mcp",
365
+ "add_command": _add_command(name),
366
+ }
367
+ )
368
+ if getattr(obj, "output", "text") == "json":
369
+ errors = []
370
+ if discovery_error is not None:
371
+ error = {"message": discovery_error.message}
372
+ if discovery_error.error_code:
373
+ error["code"] = discovery_error.error_code
374
+ if discovery_error.hint:
375
+ error["hint"] = discovery_error.hint
376
+ errors.append(error)
377
+ render.emit_json(
378
+ {
379
+ "schema_version": 2,
380
+ "available_tools": rows,
381
+ "mcp_schema": mcp_schema,
382
+ "complete": discovery_error is None,
383
+ "errors": errors,
384
+ }
385
+ )
386
+ else:
387
+ if kind != "mcp":
388
+ render.resource_table(
389
+ "Built-in add recipes",
390
+ [("Kind", "left"), ("Add command (replace example resources)", "left")],
391
+ [(row["kind"], row["add_command"]) for row in rows if row["kind"] != "mcp"],
392
+ )
393
+ if mcp_schema is not None:
394
+ render.resource_table(
395
+ "Available MCP Services",
396
+ [("Service", "left"), ("Add command", "left")],
397
+ [(row["name"], row["add_command"]) for row in rows if row["kind"] == "mcp"],
398
+ subtitle=f"Caller-visible in {mcp_schema}"
399
+ + (" — discovery incomplete" if discovery_error is not None else ""),
400
+ )
401
+ click.echo("Tip: focus on MCP services with `ab tools list --kind mcp`.")
402
+ click.echo(
403
+ "Discover services in another UC schema with "
404
+ "`ab tools list --kind mcp --schema catalog.schema`."
405
+ )
406
+ click.echo("Review agent.toml to check configured managed tools and MCP bindings.")
407
+ if discovery_error is not None:
408
+ discovery_error.show()
409
+ if discovery_error is not None:
410
+ raise click.exceptions.Exit(1)
411
+
412
+
413
+ @tools.command("remove")
414
+ @click.argument("tool_id")
415
+ @click.argument("mcp_service", required=False)
416
+ @_source_option
417
+ @click.pass_obj
418
+ def remove_tool(
419
+ obj: Any,
420
+ tool_id: str,
421
+ mcp_service: str | None,
422
+ source: pathlib.Path,
423
+ ) -> None:
424
+ """Remove a managed tool binding from this agent."""
425
+ project = AgentProject.load(source)
426
+ if mcp_service is not None:
427
+ if tool_id != "mcp":
428
+ raise AgentCliError("A second argument is supported only for `tools remove mcp`.")
429
+ ToolSpec.mcp(_default_id(mcp_service), service=mcp_service)
430
+ matches = [
431
+ tool
432
+ for tool in project.tools
433
+ if tool.source.kind == "mcp" and tool.source.service == mcp_service
434
+ ]
435
+ if len(matches) > 1:
436
+ raise AgentCliError(
437
+ f"Multiple bindings use MCP service {mcp_service!r}.",
438
+ hint=f"Review {project.path}, then remove the intended binding by ID.",
439
+ )
440
+ tool_id = matches[0].id if matches else _default_id(mcp_service)
441
+ changed = project.remove_tool(tool_id)
442
+ changed_files = [project.write()] if changed else []
443
+ if getattr(obj, "output", "text") == "json":
444
+ render.emit_json(
445
+ {
446
+ "schema_version": 1,
447
+ "changed": changed,
448
+ "changed_files": [str(path) for path in changed_files],
449
+ "tool_id": tool_id,
450
+ }
451
+ )
452
+ return
453
+ if changed:
454
+ render.success("Removed " + tool_id, fields={"Manifest": str(project.path)})
455
+ else:
456
+ click.echo(f"Tool {tool_id!r} is not configured in {project.path}")