Use when wiring tools into an OpenAI Agents SDK agent: local Python function tools, DatabricksMCPClient connections, and MCPServerSse for MCP servers. Track A Step 3. Builds on shared concepts from F3 (Tools and Data Access).
Scanned 9/12/2026
Install to Claude Code
npx -y skills add databricks-solutions/vibe-coding-workshop-template --skill 03-tools-and-mcp --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of 03 Tools And Mcp?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/databricks-solutions-03-tools-and-mcp)More formats (shields.io, HTML) on the badges page.
---
name: 03-tools-and-mcp
description: >
Use when wiring tools into an OpenAI Agents SDK agent: local Python function
tools, DatabricksMCPClient connections, and MCPServerSse for MCP servers.
Track A Step 3. Builds on shared concepts from F3 (Tools and Data Access).
license: Apache-2.0
clients: [ide_cli, genie_code]
bundle_resource: none
deploy_verb: none
deploy_note: "Tool + MCP wiring (local function tools, DatabricksMCPClient, MCPServerSse) — code, no bundle resource. Resolves identically on both clients; on Genie Code use its built-in tool surface for ad-hoc calls and runDatabricksCli for grants. See `skills/genie-code-environment`."
coverage: full
metadata:
last_verified: "2026-06-05"
volatility: high
upstream_sources: []
author: "prashanth-subrahmanyam"
version: "3.1.0"
domain: "genai-agents"
pipeline_position: "A3"
consumes: "customized_agent, agent_class, mcp_server_knowledge"
produces: "agent_with_tools, mcp_connections, resource_grants"
grounded_in: "docs.databricks.com/aws/en/generative-ai/agent-framework/author-agent, docs.databricks.com/aws/en/generative-ai/agent-framework/build-agent-tool, openai.github.io/openai-agents-python/tools/"
fields_read:
- agent.tools
- agent.mcp_servers
- agent.knowledge_base_backend
- docs.agent_tool_plan.selected_tools
- docs.agent_tool_plan.selected_mcp_servers
---
# Track A Step 3: Agent SDK Tool Wiring
Wire tools into your OpenAI Agents SDK agent so it can query data, search
documents, call functions, and connect to external services.
This step focuses on **how to wire tools into the Agent class**. For
background on which MCP servers exist, how external MCP works, resource
grants, and retriever schemas, see
[F3: Tools and Data Access](../../../foundation/03-tools-and-data-access/SKILL.md).
## Tool Plan Input Contract
For the Agents Accelerator, prefer `docs/agent_tool_plan.yaml` over inferred
defaults. Wire only the tools listed in `selected_tools[]`.
Tool families not selected are skipped and recorded in verification as skipped,
not failed.
For SQL MCP, enforce the read-only guardrails from the plan before any smoke
test. Reject generated SQL containing `INSERT`, `UPDATE`, `DELETE`, `DROP`,
`ALTER`, `CREATE`, `MERGE`, or `TRUNCATE` when `readonly: true`.
Use `docs/agent_tool_plan.yaml.verification.tool_smoke_tests[]` as the source
of smoke prompts. Every selected tool must produce at least one MLflow TOOL span.
OpenAI Agents SDK SQL MCP wiring example:
```python
from agents.mcp import MCPServerSse
sql_mcp = MCPServerSse(
url=f"{host}/api/2.0/mcp/sql",
headers={"Authorization": f"Bearer {workspace_client.config.token}"},
)
```
## When to Use
- Your agent is running (Steps 1-2) but only generates text — it needs to
**do things**: query data, search documents, call APIs.
- You want to connect to **Databricks managed MCP servers** or **external
MCP servers** from an OpenAI Agents SDK agent.
- You need to add **local function tools** for custom business logic.
## Foundation Prerequisite Check
Foundation Step 3 (Tools and Data Access) should be understood before this
step. Verify:
```bash
python3 -c "from databricks_mcp import DatabricksMCPClient; print('F3: OK')" 2>/dev/null || echo "F3: FAIL — pip install databricks-mcp"
```
If F3 fails, install `databricks-mcp` and review
[F3: Tools and Data Access](../../../foundation/03-tools-and-data-access/SKILL.md).
---
## Canonical Tool Catalog (pick what your agent needs)
Track A custom agents typically combine **three classes** of tools. Pick from
this catalog based on the data your agent must reach:
| Class | What it answers | Backing skill | Wire-in pattern |
|---|---|---|---|
| **Knowledge Assistant** (managed doc Q&A with citations) | "What does our policy say about X?" | [F5: Knowledge Assistant Lifecycle](../../../foundation/05-knowledge-assistant/SKILL.md) — produces `ka_endpoint_name` | Call the KA Model Serving endpoint from a `@function_tool` (see _Wiring KA as a function tool_ below) |
| **Genie Space** (NL → SQL on UC tables, conversational analytics) | "How many active customers signed up last week?" | [`data_product_accelerator/skills/semantic-layer/03-genie-space-patterns`](../../../../data_product_accelerator/skills/semantic-layer/03-genie-space-patterns/SKILL.md) and [`04-genie-space-export-import-api`](../../../../data_product_accelerator/skills/semantic-layer/04-genie-space-export-import-api/SKILL.md) — produces `genie_space_id` | Call Genie via the Databricks SDK from a `@function_tool`, **or** wire as Genie MCP server (see F3) |
| **Unity Catalog functions** (deterministic Python or SQL functions, registered in UC) | "Compute next-tier-distance for this customer" | [F3: Tools and Data Access](../../../foundation/03-tools-and-data-access/SKILL.md) (UC Functions MCP section) | Wire as UC Functions MCP server, or call directly via the SDK |
| **Vector Search MCP** (custom retrieval pipeline) | "Custom hybrid search with re-ranker" | F3 (Vector Search MCP) | `MCPServerSse` direct wiring (see below) |
| **Local Python tools** (business logic, math, formatting) | "Compute margin", "format response" | This skill — `@function_tool` (see below) | Decorator |
> **Canonical default for the SkyLoyalty walkthrough:** Knowledge Assistant
> + Genie Space + a small set of `@function_tool` helpers. This pairing
> covers structured analytics (Genie), unstructured Q&A (KA), and bespoke
> business logic (functions) without a custom RAG stack.
### Serving-endpoint API shape per endpoint kind
Different endpoint kinds use different request shapes. The most common bug
in this skill's history is reaching for Chat Completions on every endpoint;
**KA uses Responses API and Genie uses a two-call SDK sequence**. Use this
table as the canonical wiring reference:
| Endpoint kind | Wire via | Request shape | Notes |
|---|---|---|---|
| **Knowledge Assistant** | Raw `requests.post` to `{host}/serving-endpoints/{name}/invocations` | Responses API: `{"input": [{"role": "user", "content": "..."}]}` | Parse `output[].content[].text`. Do NOT use `serving_endpoints.query(messages=...)` — KA rejects `messages` and the SDK's typing surfaces it as `'dict' object has no attribute 'as_dict'`. |
| **Foundation Model API** (Sonnet, llama-3.3, gpt-5-2) | `databricks_openai.AsyncDatabricksOpenAI` or `OpenAI(base_url=...)` | OpenAI Chat Completions: `messages=[...]` | Standard OpenAI-compatible client patterns apply. |
| **Genie Space** | SDK two-call sequence | (1) `w.genie.start_conversation_and_wait(space_id=..., content=...)` returns description + attachments[] (2) `w.genie.get_message_query_result_by_attachment(space_id, conversation_id, message_id, attachment_id)` returns rows | First call alone surfaces the SQL plan as prose, not the rows — always make both calls. See **Wiring Genie as a function tool** below. |
| **AI Gateway endpoint** | REST `POST /api/2.0/serving-endpoints` | `external_model.databricks-model-serving` shape | See [F4: AI Gateway](../../../foundation/04-ai-gateway/SKILL.md) for the create-endpoint payload. |
### Wiring KA as a function tool (Track A pattern)
KA exposes a Model Serving endpoint, but **KA serves the Responses API, not
Chat Completions.** A `serving_endpoints.query(name=..., messages=[...])`
call against a KA endpoint fails with `Invalid request: 'messages' field is
not supported` (and may surface earlier as a Python `'dict' object has no
attribute 'as_dict'` typing trap inside the SDK shim).
Call the invocations URL directly with the Responses-API request body:
```json
{"input": [{"role": "user", "content": "question"}]}
```
and parse the response as `output[].content[].text`:
```python
import os
import requests
from agents import function_tool
from databricks.sdk import WorkspaceClient
KA_ENDPOINT = os.environ["KA_ENDPOINT_NAME"] # captured in F5
@function_tool
def search_policy_docs(query: str) -> str:
"""Search policy documents and return cited answers.
Args:
query: Natural-language question about loyalty program policy.
"""
w = WorkspaceClient()
host = w.config.host.rstrip("/")
url = f"{host}/serving-endpoints/{KA_ENDPOINT}/invocations"
# KA uses the Responses API: `input`, NOT Chat Completions `messages`.
body = {"input": [{"role": "user", "content": query}]}
resp = requests.post(
url,
headers={"Authorization": f"Bearer {w.config.token}",
"Content-Type": "application/json"},
json=body,
timeout=60,
)
resp.raise_for_status()
payload = resp.json()
# Responses API: payload["output"][i]["content"][j]["text"]
parts = []
for item in payload.get("output", []):
for chunk in item.get("content", []):
text = chunk.get("text")
if text:
parts.append(text)
return "\n".join(parts) or "(no answer)"
```
> **DON'T** use `w.serving_endpoints.query(name=..., messages=[...])` against
> a KA endpoint. KA does not accept Chat Completions `messages`. Use raw
> `requests.post` with `{"input": [...]}` against the invocations URL as
> shown above. (Foundation Model API endpoints — Sonnet, llama-3.3, gpt-5-2 —
> do accept Chat Completions and can use the OpenAI-compatible client.)
Declare the KA endpoint as a `serving_endpoint` resource (`CAN_QUERY`) in
`databricks.yml` so the deployed agent has access — see the resource-kind
reference below.
### Wiring Genie as a function tool
Genie spaces require a **two-call sequence** via the SDK. The first call
returns the planner description and message metadata; the rows live on the
attachment and must be fetched in a second call. Tools that return only the
first call's output deliver SQL-as-prose to the LLM, not data.
1. `w.genie.start_conversation_and_wait(space_id=..., content=...)` — starts
the conversation, runs the planner, and returns a `GenieMessage` whose
`attachments[]` carry the query metadata (each attachment has an
`attachment_id`). The message's `content`/text is the **description**, not
the rows.
2. `w.genie.get_message_query_result_by_attachment(space_id=..., conversation_id=..., message_id=..., attachment_id=...)`
— pulls the actual SQL execution result (rows + columns) for the chosen
attachment.
Combine both into a single tool so the Agents SDK gets both the description
and the rows in one return value:
```python
import os
from agents import function_tool
from databricks.sdk import WorkspaceClient
GENIE_SPACE_ID = os.environ["GENIE_SPACE_ID"] # captured during semantic-layer setup
@function_tool
def query_loyalty_analytics(question: str) -> str:
"""Answer analytics questions over loyalty data using Genie.
Args:
question: Natural-language analytics question (e.g. "active members by tier").
"""
w = WorkspaceClient()
# CALL 1: start_conversation_and_wait — planner runs, returns description.
msg = w.genie.start_conversation_and_wait(
space_id=GENIE_SPACE_ID,
content=question,
)
description = (msg.content or "").strip()
if not msg.attachments:
return description or "(no answer)"
# CALL 2: get_message_query_result_by_attachment — fetches the rows.
attachment_id = msg.attachments[0].attachment_id
result = w.genie.get_message_query_result_by_attachment(
space_id=GENIE_SPACE_ID,
conversation_id=msg.conversation_id,
message_id=msg.message_id,
attachment_id=attachment_id,
)
# Render rows as a small markdown-ish table for the LLM.
rows_text = "(no rows)"
sm = getattr(result, "statement_response", None)
if sm and getattr(sm, "result", None) and getattr(sm.result, "data_array", None):
rows = sm.result.data_array
cols = [c.name for c in (sm.manifest.schema.columns or [])] if sm.manifest else []
header = " | ".join(cols)
body = "\n".join(" | ".join(str(c) for c in row) for row in rows)
rows_text = f"{header}\n{body}" if header else body
return f"{description}\n\n{rows_text}"
```
> **DON'T** stop after `start_conversation_and_wait`. The first call may
> return only a description (and zero attachments for refusals); always
> fetch `get_message_query_result_by_attachment` for each attachment whose
> rows you want to surface to the LLM.
Declare a `genie_space` resource in `databricks.yml`. **Bundle bindings use
`name` + `space_id`** (not a bare `id`):
```yaml
- name: genie-space
genie_space:
space_id: ${var.genie_space_id}
permission: CAN_RUN
```
The outer `name:` is the resource binding (referenced by `valueFrom:` in
`app.yaml`); the inner `space_id:` is the Genie Space identifier the
platform looks up. Plus declare the underlying `sql_warehouse` the space
uses.
---
## Local Function Tools
### OpenAI Agents SDK: `@function_tool`
The simplest way to give your agent capabilities — define Python functions
and decorate them:
```python
from agents import Agent, function_tool
from datetime import datetime
@function_tool
def get_current_time() -> str:
"""Get the current date and time in ISO format."""
return datetime.now().isoformat()
@function_tool
def calculate_metrics(revenue: float, cost: float) -> dict:
"""Calculate profit margin and ROI from revenue and cost.
Args:
revenue: Total revenue in dollars.
cost: Total cost in dollars.
"""
profit = revenue - cost
margin = (profit / revenue * 100) if revenue > 0 else 0
return {"profit": profit, "margin_pct": round(margin, 2)}
@function_tool
def search_knowledge_base(query: str, max_results: int = 5) -> list[dict]:
"""Search the internal knowledge base for relevant documents.
Args:
query: Natural language search query.
max_results: Maximum number of results to return (default 5).
"""
from databricks.sdk import WorkspaceClient
w = WorkspaceClient()
results = w.vector_search_indexes.query_index(
index_name="prod.docs.knowledge_index",
columns=["content", "source", "score"],
query_text=query,
num_results=max_results,
)
return [
{"content": r["content"], "source": r["source"], "score": r["score"]}
for r in results.result.data_array
]
agent = Agent(
name="analyst",
instructions="You are a data analyst. Use tools to answer questions accurately.",
model="databricks-claude-sonnet-4-6",
tools=[get_current_time, calculate_metrics, search_knowledge_base],
)
```
**Tool docstrings are critical.** The LLM uses them to decide when and how to
call each tool. Write clear descriptions with typed arguments.
---
## Wiring MCP Servers with `DatabricksMCPClient`
The `databricks-mcp` library (covered in
[F3](../../../foundation/03-tools-and-data-access/SKILL.md)) is the recommended
way to connect to managed and external MCP servers. For the OpenAI Agents SDK,
you can wire MCP tools alongside local function tools.
### Using `MCPServerSse` (Direct Wiring)
The OpenAI Agents SDK natively supports MCP via `MCPServerSse`:
```python
from agents import Agent
from agents.mcp import MCPServerSse
from databricks.sdk import WorkspaceClient
ws = WorkspaceClient()
host = ws.config.host
ai_search_mcp = MCPServerSse(
# AI Search (formerly Vector Search); legacy /mcp/vector-search/ prefix still works
url=f"https://{host}/api/2.0/mcp/ai-search/prod/docs/knowledge_index",
headers={"Authorization": f"Bearer {ws.config.token}"},
)
sql_mcp = MCPServerSse(
url=f"https://{host}/api/2.0/mcp/sql",
headers={"Authorization": f"Bearer {ws.config.token}"},
)
github_mcp = MCPServerSse(
url=f"https://{host}/api/2.0/mcp/external/github_connection",
headers={"Authorization": f"Bearer {ws.config.token}"},
)
agent = Agent(
name="analyst",
instructions="Use tools to search documents, query data, and check code.",
model="databricks-claude-sonnet-4-6",
mcp_servers=[ai_search_mcp, sql_mcp, github_mcp],
)
```
### Combining Local Tools and MCP Servers
Mix `@function_tool` definitions with MCP servers:
```python
agent = Agent(
name="analyst",
instructions="Use tools to answer questions. Use calculate_metrics for math.",
model="databricks-claude-sonnet-4-6",
tools=[calculate_metrics, get_current_time],
mcp_servers=[ai_search_mcp, sql_mcp],
)
```
### Environment-Aware Auth for MCP
Handle auth consistently across local dev and Databricks Apps:
```python
import os
from databricks.sdk import WorkspaceClient
def get_mcp_headers():
w = WorkspaceClient()
return {"Authorization": f"Bearer {w.config.token}"}
def get_workspace_host():
return os.environ.get("DATABRICKS_HOST", "").rstrip("/")
```
Use `get_mcp_headers()` when constructing `MCPServerSse`. Adjust if using OBO
(see [A4: Authentication](../04-authentication/SKILL.md)).
---
## Resource Grants for Deployment
When your agent uses MCP servers or other Databricks resources, you need to
declare workspace resource attachments and UC grants.
For the complete grant matrix, `app.yaml`/`databricks.yml` examples, UC SQL
patterns, MLflow `resources` declarations, and the `get_databricks_resources()`
auto-discovery pattern, see
[F3: Resource Grants](../../../foundation/03-tools-and-data-access/references/resource-grants.md).
### Resource-kind quick reference
When declaring resources in `databricks.yml`, the kind depends on what the
resource is. **Use `uc_securable` only for UC objects that have no dedicated
top-level resource type** (functions, tables, schemas, volumes, models).
Warehouses, serving endpoints, and UC connections have their own top-level
kinds — do not wrap them in `uc_securable`.
| Resource | Bundle kind | Permission | Inner identifier field |
|----------|-------------|------------|------------------------|
| SQL warehouse | `sql_warehouse` | `CAN_USE` | `id:` |
| LLM / chat model endpoint (route from `runtime_config.llm`) | `serving_endpoint` | `CAN_QUERY` | `name:` |
| Knowledge Assistant endpoint | `serving_endpoint` | `CAN_QUERY` | `name:` (the KA's serving endpoint name) |
| Vector Search endpoint | `serving_endpoint` | `CAN_QUERY` | `name:` |
| Genie Space | `genie_space` | `CAN_RUN` | `space_id:` (NOT `id:` — see note below) |
| UC connection (external HTTP/MCP) | `connection` | `USE CONNECTION` | `name:` |
| UC function (incl. `system.ai.python_exec`) | `uc_securable` with `securable_type: FUNCTION` | `EXECUTE` (via `USAGE` + `EXECUTE` grants on the securable) | `securable_full_name:` |
| UC table / view | `uc_securable` with `securable_type: TABLE` | `SELECT` | `securable_full_name:` |
| UC schema | `uc_securable` with `securable_type: SCHEMA` | `USE_SCHEMA` | `securable_full_name:` |
| UC model | `uc_securable` with `securable_type: MODEL` | `USE_MODEL` or `EXECUTE` per scenario | `securable_full_name:` |
| Another Databricks App (backend) | `app` | `CAN_USE` — see [`06d-appkit-agent-app-proxy`](../../../../apps_lakebase/skills/06d-appkit-agent-app-proxy/references/app-resource-declaration.md) | `name:` |
> **`genie_space` resource shape:** The DAB binding takes the outer `name:`
> (the resource binding label, used by `valueFrom:` in `app.yaml`) plus the
> inner `space_id:` (the Genie Space identifier). It does **not** accept a
> bare `id:` like `sql_warehouse` does. Using `id:` silently fails
> validation in some CLI versions and leaves the app unable to resolve the
> resource at runtime. Always use `name` + `space_id`.
Model grants are derived from `docs/agent_tool_plan.yaml.runtime_config.llm`.
- When `provider == "databricks"`, grant `CAN_QUERY` on `runtime_config.llm.endpoint`.
- When `provider == "ai_gateway"`, do not create or configure Gateway. Verify the pre-provisioned endpoint and required permissions are documented, then leave provisioning to the optional Gateway hardening step.
Verify the bundle schema accepts each kind your agent needs before deploying:
> **Client note:** IDE runs this in a terminal; Genie Code runs the `databricks bundle …` command via `runDatabricksCli`. See `skills/genie-code-environment`.
```bash
# Generic "does this kind exist in my CLI's schema?"
databricks bundle schema \
| jq '.definitions | keys[]' \
| grep -i 'AppResource_'
```
Expected output includes `AppResource_SQL_WAREHOUSE`, `AppResource_SERVING_ENDPOINT`, `AppResource_UC_SECURABLE`, `AppResource_CONNECTION`, `AppResource_GENIE_SPACE`, and (for CLI >= 0.295.0) `AppResource_APP`. If a needed kind is missing, upgrade the CLI.
### Canonical agent-app `app.yaml` + `databricks.yml` (KA + Genie + LLM)
Most Track A agents need a triad of resources: an LLM endpoint, a KA
endpoint, and a Genie Space (with its underlying SQL warehouse). The
canonical declaration is:
```yaml
# app.yaml — Agent App
command:
- mlflow
- genai
- agent-server
- serve
- --module
- agent
env:
- name: LLM_ENDPOINT
valueFrom: llm-endpoint
- name: KA_ENDPOINT_NAME
valueFrom: ka-endpoint
- name: GENIE_SPACE_ID
valueFrom: genie-space
- name: WAREHOUSE_ID
valueFrom: sql-warehouse
```
```yaml
# databricks.yml — Agent App resource block
resources:
apps:
${var.agent_app_name}:
source_code_path: ./
resources:
- name: llm-endpoint
serving_endpoint:
name: databricks-claude-sonnet-4-6
permission: CAN_QUERY
- name: ka-endpoint
serving_endpoint:
name: ${var.ka_endpoint_name}
permission: CAN_QUERY
- name: genie-space
genie_space:
space_id: ${var.genie_space_id}
permission: CAN_RUN
- name: sql-warehouse
sql_warehouse:
id: ${var.warehouse_id}
permission: CAN_USE
```
This is the resource shape the SkyLoyalty walkthrough (canonical) uses. The
`valueFrom: <resource-name>` mapping in `app.yaml` resolves to the resource's
URL or id at runtime — your agent reads `os.environ["KA_ENDPOINT_NAME"]` etc.
without hardcoding workspace identifiers.
---
## Custom Retriever Schemas
If your agent includes a custom retriever (not using AI Bridge tools like
`VectorSearchRetrieverTool`), you must declare the retriever's output schema
for AI Playground source links and evaluation judges.
For the complete pattern, see
[F3: Custom Retriever Schemas](../../../foundation/03-tools-and-data-access/references/custom-retriever-schemas.md).
**SDLC connection:** The retriever schema declared here feeds directly into the
SDLC evaluation pipeline. [S4: Evaluation Runs](../../../sdlc/04-evaluation-runs/SKILL.md)
uses `doc_uri` to run groundedness and relevance judges against your eval
dataset's `expected_retrieved_context`.
---
## Testing Tools Locally
### Interactive test
```bash
uv run start-app
# Then in browser at http://localhost:8000:
# "What time is it?" -> should invoke get_current_time
# "Calculate margin for $1M revenue and $600K cost" -> should invoke calculate_metrics
```
### Programmatic test
```python
import asyncio
from agents import Runner
async def test_tools():
result = await Runner.run(agent, "What time is it?")
print(f"Output: {result.final_output}")
print(f"Tools called: {[s.name for s in result.raw_responses]}")
asyncio.run(test_tools())
```
### Verify in MLflow traces
After calling the agent, check MLflow traces for TOOL spans alongside AGENT
and LLM spans. Each tool call should appear as a distinct span with:
- Input: the tool arguments
- Output: the tool return value
- Span type: TOOL
---
## DO / DON'T
### DO — Write detailed tool docstrings with typed args
```python
@function_tool
def search_products(
query: str,
category: str = "all",
max_results: int = 10,
) -> list[dict]:
"""Search the product catalog by keyword.
Args:
query: Natural language search query for products.
category: Filter by product category. Use "all" for no filter.
max_results: Maximum number of products to return.
"""
...
```
### DON'T — Use vague docstrings or skip type hints
```python
@function_tool
def search(q, n=10):
"""Search for stuff.""" # LLM won't know what or how to search
...
```
### DO — Handle tool errors gracefully
```python
@function_tool
def query_database(sql: str) -> str:
"""Execute a SQL query against the analytics warehouse."""
try:
result = execute_sql(sql)
return format_results(result)
except Exception as e:
return f"Query failed: {str(e)}. Please try a simpler query."
```
### DON'T — Let tools raise unhandled exceptions
```python
@function_tool
def query_database(sql: str) -> str:
"""Execute a SQL query."""
return execute_sql(sql) # Exception crashes the agent loop
```
---
## Common Issues
| Issue | Fix |
|-------|-----|
| Tool not called by agent | Improve the tool's docstring — the LLM needs clear description of when to use it |
| `MCPServerSse` connection refused | Check MCP URL format and auth headers; verify the endpoint exists |
| `PermissionDenied` on Vector Search | Grant SP access — see [F3: Resource Grants](../../../foundation/03-tools-and-data-access/references/resource-grants.md) |
| Tool returns but agent ignores result | Check that the tool return type is `str` or simple `dict` — complex objects may confuse the LLM |
| MCP tools not appearing | Verify the MCP server URL is correct and the server is running |
| `ModuleNotFoundError` in tool | Move imports inside the tool function for serialization safety |
---
## Validation Gate
All must pass before proceeding:
- [ ] At least one local `@function_tool` added and callable by the agent
- [ ] Agent correctly invokes the tool when prompted (visible in chat and traces)
- [ ] MCP connection configured for at least one Databricks service (if applicable)
- [ ] Resource grants identified for all tools that need workspace resources
- [ ] Tool calls appear as TOOL spans in MLflow traces
- [ ] Tool errors are handled gracefully (no unhandled exceptions)
- [ ] If using custom retrievers: `mlflow.models.set_retriever_schema()` called at module level
- [ ] **KA tool uses Responses API** — request body is `{"input": [...]}` against `/serving-endpoints/{name}/invocations`, NOT `serving_endpoints.query(messages=[...])`
- [ ] **Genie tool issues both calls** — `start_conversation_and_wait` followed by `get_message_query_result_by_attachment` for each attachment whose rows are returned to the LLM
- [ ] **`databricks.yml` `genie_space` resource uses `name` + `space_id`** (not bare `id:`)
## Next Step
After passing this gate, **load and execute**
`tracks/A-custom-agent-apps/04-authentication/SKILL.md` (A4: Authentication).
## Notes to Carry Forward
| Key | Value |
|-----|-------|
| `local_tools` | List of `@function_tool` function names |
| `mcp_servers` | List of MCP server URLs configured |
| `app_yaml_resources` | Resources declared in `app.yaml` |
| `uc_grants_needed` | UC GRANT statements for deployment |
---
## Related Skills
| Skill | Relationship |
|-------|-------------|
| [F3: Tools and Data Access](../../../foundation/03-tools-and-data-access/SKILL.md) | Foundation — MCP servers, grants, retrievers |
| [F5: Knowledge Assistant Lifecycle](../../../foundation/05-knowledge-assistant/SKILL.md) | Tool catalog — managed doc Q&A; produces `ka_endpoint_name` |
| [Genie Space Patterns](../../../../data_product_accelerator/skills/semantic-layer/03-genie-space-patterns/SKILL.md) | Tool catalog — NL → SQL on UC tables; produces `genie_space_id` |
| [Genie Space Export/Import API](../../../../data_product_accelerator/skills/semantic-layer/04-genie-space-export-import-api/SKILL.md) | Tool catalog — versioning Genie spaces across environments |
| [A2: Agent Framework](../02-agent-framework/SKILL.md) | Previous step — produces `agent_class` |
| [A4: Authentication](../04-authentication/SKILL.md) | Next step |
| [S4: Evaluation Runs](../../../sdlc/04-evaluation-runs/SKILL.md) | Custom retriever schemas enable groundedness/relevance judges |
## References
- [Build a tool for your agent](https://docs.databricks.com/aws/en/generative-ai/agent-framework/build-agent-tool)
- [MCP servers on Databricks](https://docs.databricks.com/aws/en/generative-ai/agent-framework/mcp-server)
- [Author an agent](https://docs.databricks.com/aws/en/generative-ai/agent-framework/author-agent)
- [OpenAI Agents SDK — Tools](https://openai.github.io/openai-agents-python/tools/)
- [OpenAI Agents SDK — MCP](https://openai.github.io/openai-agents-python/mcp/)
- [Add resources to a Databricks app](https://docs.databricks.com/aws/en/dev-tools/databricks-apps/resources)
## Version History
| Version | Date | Changes |
|---------|------|---------|
| 3.1.0 | 2026-04-26 | Document tool/data-product API shapes (Phase 3.8): KA serving uses Responses API (`{"input": [...]}`) not Chat Completions `messages`; Genie requires the two-call sequence (`start_conversation_and_wait` returns description, `get_message_query_result_by_attachment` returns rows); DAB `genie_space` resource uses `name` + `space_id`, not bare `id`. Added serving-endpoint-API-shape table covering KA / FMAPI / Genie / AI Gateway. |
| 3.0.0 | 2026-04-12 | Refactored: MCP concepts, resource grants, retriever schemas moved to F3. Focus on Agent SDK wiring with DatabricksMCPClient and MCPServerSse. |
| 2.0.0 | 2026-04-10 | Progressive disclosure: MCP config and resource grants in references/ |
| 1.0.0 | 2026-04-10 | Initial skill: function tools, MCP servers, resource grants, testing patterns |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!