+ );
+}
diff --git a/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/src/index.css b/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/src/index.css
new file mode 100644
index 0000000000..08a3ac9e1e
--- /dev/null
+++ b/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/src/index.css
@@ -0,0 +1,68 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}
diff --git a/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/src/main.jsx b/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/src/main.jsx
new file mode 100644
index 0000000000..b9a1a6deac
--- /dev/null
+++ b/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/src/main.jsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.jsx'
+
+createRoot(document.getElementById('root')).render(
+
+
+ ,
+)
diff --git a/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/vite.config.js b/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/vite.config.js
new file mode 100644
index 0000000000..8b0f57b91a
--- /dev/null
+++ b/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/vite.config.js
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+})
diff --git a/examples/mcp/databricks_mcp_cookbook.ipynb b/examples/mcp/databricks_mcp_cookbook.ipynb
index 455703eec2..de1dcb7def 100644
--- a/examples/mcp/databricks_mcp_cookbook.ipynb
+++ b/examples/mcp/databricks_mcp_cookbook.ipynb
@@ -21,14 +21,14 @@
"\n",
"\n",
"\n",
- "This cookbook outlines the process for building a supply-chain copilot with the OpenAI Agent SDK and Databricks Managed MCP. MCP enables the agent to tap structured and unstructured enterprise data, such as inventory, sales, supplier feeds, local events, and more, for real-time visibility, early detection of material shortages, and proactive recommendations. An orchestration layer underpins the system, unifying:\n",
+ "This cookbook outlines the process for building a supply-chain copilot with the OpenAI Agent SDK and Databricks Managed MCP. MCP enables the agent to query structured and unstructured enterprise data, such as inventory, sales, supplier feeds, local events, and more, for real-time visibility, early detection of material shortages, and proactive recommendations. An orchestration layer underpins the system, unifying:\n",
"- Queries against structured inventory, demand, and supplier data\n",
- "- Classical forecasting models for fine-grained demand and lead-time predictions \n",
- "- Graph based route optimizations \n",
- "- Vector-indexed e-mail archives that enable semantic search across unstructured communications for delay signals\n",
- "- Generative AI workflows that surface insights and mitigation options in plain language\n",
+ "- Time series forecasting for every wholesaler\n",
+ "- Graph based raw material requirements and transport optimizations\n",
+ "- Vector-indexed e-mail archives that enable semantic search across unstructured communications \n",
+ "- Revenue risk calculation\n",
"\n",
- "By the end of this guide you will deploy a production-ready template that queries distributed data sources, predictive models, highlights emerging bottlenecks, and recommends proactive actions. It can address questions such as:\n",
+ "By the end of this guide you will deploy a template that queries distributed data sources, predictive models, highlights emerging bottlenecks, and recommends proactive actions. It can address questions such as:\n",
"- What products are dependent on L6HUK material?\n",
"- How much revenue is at risk if we can’t produce the forecasted amount of product autoclave_1?\n",
"- Which products have delays right now?\n",
@@ -37,14 +37,12 @@
"- Are there any shortages with one of the following raw materials: O4GRQ, Q5U3A, OAIFB or 58RJD?\n",
"- What are the delays associated with wholesaler 9?\n",
"\n",
- "Stakeholders can submit a single natural-language prompt and receive answers instantly.\n",
+ "Stakeholders can submit a natural-language prompt and receive answers instantly.\n",
"This guide walks you through each step to implement this solution in your own environment.\n",
"\n",
"## Architecture\n",
"\n",
- "Answering supply-chain operations questions requires modelling how upstream bottlenecks cascade through production, logistics, and fulfilment so that stakeholders can shorten lead times, avoid excess stock, and control costs. \n",
- "\n",
- "The architecture presented in this cookbook layers an OpenAI Agent on top of your existing analytics workloads in Databricks. You can expose Databricks components as callable Unity Catalog functions. The agent, implemented with the OpenAI Agent SDK and connecting to [Databricks Managed MCP servers](https://docs.databricks.com/aws/en/generative-ai/agent-framework/mcp), orchestrates those tools alongside generative-AI workflows. It queries Databricks and returns consolidated insights in plain language. \n",
+ "The architecture presented in this cookbook layers an OpenAI Agent on top of your existing analytics workloads in Databricks. You can expose Databricks components as callable Unity Catalog functions. The agent is implemented with the [OpenAI Agent SDK](https://openai.github.io/openai-agents-python/) and connects to [Databricks Managed MCP servers](https://docs.databricks.com/aws/en/generative-ai/agent-framework/mcp). \n",
"\n",
"The result is a single, near-real-time conversational interface that delivers fine-grained forecasts, dynamic inventory recommendations, and data-driven decisions across the supply chain. The architecture yields an agent layer that harnesses your existing enterprise data (structured and unstructured), classical ML models, and graph-analytics capabilities.\n",
"\n",
@@ -66,16 +64,9 @@
"source": [
"## Set up Databricks authentication\n",
"\n",
- "The easiest way is to add a profile to `~/.databrickscfg`. The snippet’s `WorkspaceClient(profile=...)` call will pick that up. It tells the SDK which of those stored credentials to load, so your code never needs to embed tokens. Another option would be to create environment variables such as `DATABRICKS_HOST` and `DATABRICKS_TOKEN`, but using `~/.databrickscfg` is recommended."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "! pip install databricks-cli"
+ "You can set up your Databricks authentication by adding a profile to `~/.databrickscfg`. A [Databricks configuration profile](https://docs.databricks.com/aws/en/dev-tools/auth/config-profiles) contains settings and other information that Databricks needs to authenticate. \n",
+ "\n",
+ "The snippet’s `WorkspaceClient(profile=...)` call will pick that up. It tells the SDK which of those stored credentials to load, so that your code never needs to embed tokens. Another option would be to create environment variables such as `DATABRICKS_HOST` and `DATABRICKS_TOKEN`, but using `~/.databrickscfg` is recommended."
]
},
{
@@ -92,14 +83,17 @@
},
"source": [
"Generate a workspace [personal access token (PAT)](https://docs.databricks.com/aws/en/dev-tools/auth/pat#databricks-personal-access-tokens-for-workspace-users) via Settings → Developer → Access tokens → Generate new token, then record it in `~/.databrickscfg`.\n",
+ "\n",
+ "To create this Databricks configuration profile file, run the [Databricks CLI](https://docs.databricks.com/aws/en/dev-tools/cli/) databricks configure command, or follow these steps:\n",
"- If `~/.databrickscfg` is missing, create it: touch `~/.databrickscfg`\n",
"- Open the file: `nano ~/.databrickscfg`\n",
"- Insert a profile section that lists the workspace URL and personal-access token (PAT) (additional profiles can be added at any time):\n",
"\n",
- "```\n",
+ "\n",
+ "```bash\n",
"[DEFAULT]\n",
- "host = https://dbc-a1b2345c-d6e7.cloud.databricks.com\n",
- "token = dapi123... # paste your PAT here\n",
+ "host = https://dbc-a1b2345c-d6e7.cloud.databricks.com # add your workspace URL here\n",
+ "token = dapi123... # add your PAT here\n",
"```"
]
},
@@ -117,11 +111,9 @@
},
"source": [
"\n",
- "You can run this sanity check command with the Databricks CLI or SDK. If it returns data without prompting for credentials, the host is correct and your token is valid:\n",
+ "You can then run this sanity check command `databricks clusters list` with the Databricks CLI or SDK. If it returns data without prompting for credentials, the host is correct and your token is valid.\n",
"\n",
- "`databricks clusters list`\n",
- "\n",
- "As a pre-requisite, Serverless compute and Unity Catalog must be enabled in the workspace. "
+ "As a pre-requisite, Serverless compute and Unity Catalog must be enabled in the Databricks workspace. "
]
},
{
@@ -137,31 +129,29 @@
}
},
"source": [
- "## Databricks Supply Chain set up \n",
+ "## (Optional) Databricks Supply Chain set up \n",
"\n",
- "You can kick start your project with Databricks’ Supply-Chain Optimization Solution Accelerator (or any other accelerator if working in a different industry). Clone the accelerator’s GitHub [repository](https://github.com/lararachidi/agent-supply-chain/blob/main/README.md) into your Databricks workspace and follow the instructions in the README [file](https://github.com/lararachidi/agent-supply-chain/blob/main/README.md). Running the solution will stand up every asset the Agent will later reach via MCP, from raw enterprise tables and unstructured e-mails to classical ML models and graph workloads.\n",
+ "This cookbook can be used to work with your own Databricks supply chain datasets and analytical workloads.\n",
"\n",
- "```\n",
- "git clone https://github.com/lararachidi/agent-supply-chain.git\n",
- "```\n",
+ "Alternatively, you can accelerate your setup by using a tailored version of the Databricks’ Supply Chain Optimization Solution Accelerator. To do so, you can clone this GitHub [repository](https://github.com/lara-openai/databricks-supply-chain) into your Databricks workspace and follow the instructions in the README [file](https://github.com/lara-openai/databricks-supply-chain/blob/main/README.md). Running the solution will stand up every asset the Agent will later reach via MCP, from raw enterprise tables and unstructured e-mails to classical ML models and graph workloads. \n",
"\n",
- "The sample data mirrors a realistic pharma network: three plants manufacture 30 products, ship them to five distribution centres, and each distribution center serves 30-60 wholesalers. The repo ships time-series demand for every product-wholesaler pair, a distribution center-to-wholesaler mapping, a plant-to-distribution center cost matrix, plant output caps, and an e-mail archive flagging shipment delays.\n",
+ "If you prefer to use your own datasets and models, make sure to wrap relevant components as Unity Catalog functions and define a Vector Search index as shown in the accelerator. You can also expose Genie Spaces.\n",
"\n",
+ "The sample data mirrors a realistic pharma network: three plants manufacture 30 products, ship them to five distribution centers, and each distribution center serves 30-60 wholesalers. The repo ships time-series demand for every product-wholesaler pair, a distribution center-to-wholesaler mapping, a plant-to-distribution center cost matrix, plant output caps, and an e-mail archive flagging shipment delays. \n",
"\n",
"\n",
"\n",
- "The notebooks turn these raw feeds into governed, callable artefacts:\n",
- "- Demand forecasting & aggregation ([notebook 2](https://github.com/lararachidi/agent-supply-chain/blob/main/02_Fine_Grained_Demand_Forecasting.py)): Generates one-week-ahead SKU demand for every wholesaler and distribution center with a Holt-Winters seasonal model (or any preferred time-series approach). It leverages Spark’s parallelisation for large-scale forecasting tasks by using Pandas UDFs (taking your single node data science code and distributing it across multiple nodes). Forecasts are then rolled up to DC-level totals for each product. The output is a table product_demand_forecasted with aggregate forecasts at the distribution center level.\n",
- "- Raw-material planning ([notebook 3](https://github.com/lararachidi/agent-supply-chain/blob/main/03_Derive_Raw_Material_Demand.py)): Constructs a product-to-material using graph processing, propagating demand up the bill-of-materials hierarchy to calculate component requirements at scale. We transform the bill‑of‑materials into a graph so product forecasts can be translated into precise raw‑material requirements, yielding two tables: raw_material_demand and raw_material_supply.\n",
- "- Transportation optimisation ([notebook 4](https://github.com/lararachidi/agent-supply-chain/blob/main/04_Optimize_Transportation.py)): Minimises plant to distribution center transportation cost under capacity and demand constraints, leveraging Pandas UDFs, outputting recommendations in shipment_recommendations.\n",
- "- Semantic e-mail search ([notebook 6](https://github.com/lararachidi/agent-supply-chain/blob/main/06_Vector_Search.py)): Embeds supply-chain manager e-mails in a vector index using OpenAI embedding models, enabling semantic queries that surface delay and risk signals.\n",
+ "Answering supply-chain operations questions requires modelling how upstream bottlenecks cascade through production, logistics, and fulfilment so that stakeholders can shorten lead times, avoid excess stock, and control costs. The notebooks turn these raw feeds into governed, callable artefacts:\n",
+ "- Demand forecasting & aggregation ([notebook 2](https://github.com/lara-openai/databricks-supply-chain/blob/main/02_Fine_Grained_Demand_Forecasting.py)): Generates one-week-ahead SKU demand for every wholesaler and distribution center with a Holt-Winters seasonal model (or any preferred time-series approach). It leverages Spark’s parallelisation for large-scale forecasting tasks by using Pandas UDFs (taking your single node data science code and distributing it across multiple nodes). Forecasts are then rolled up to DC-level totals for each product. The output is a table product_demand_forecasted with aggregate forecasts at the distribution center level.\n",
+ "- Raw-material planning ([notebook 3](https://github.com/lara-openai/databricks-supply-chain/blob/main/03_Derive_Raw_Material_Demand.py)): Constructs a product-to-material using graph processing, propagating demand up the bill-of-materials hierarchy to calculate component requirements at scale. We transform the bill‑of‑materials into a graph so product forecasts can be translated into precise raw‑material requirements, yielding two tables: raw_material_demand and raw_material_supply.\n",
+ "- Transportation optimisation ([notebook 4](https://github.com/lara-openai/databricks-supply-chain/blob/main/04_Optimize_Transportation.py)): Minimises plant to distribution center transportation cost under capacity and demand constraints, leveraging Pandas UDFs, outputting recommendations in shipment_recommendations.\n",
+ "- Semantic e-mail search ([notebook 6](https://github.com/lara-openai/databricks-supply-chain/blob/main/06_Vector_Search.py)): Embeds supply-chain manager e-mails in a vector index using OpenAI embedding models, enabling semantic queries that surface delay and risk signals.\n",
"\n",
- "Each insight is wrapped as a Unity Catalog (UC) function in [notebook 5](https://github.com/lararachidi/agent-supply-chain/blob/main/05_Data_Analysis_%26_Functions.py) and [notebook 7](https://github.com/lararachidi/agent-supply-chain/blob/main/07_More_Functions.py), e.g. product_from_raw, raw_from_product, revenue_risk, lookup_product_demand, query_unstructured_emails. Because UC governs tables, models, and vector indexes alike, the Agent can decide at runtime whether to forecast, trace a BOM dependency, gauge revenue impact, fetch history, or search e-mails, always within the caller’s data-access rights.\n",
+ "Each insight is wrapped as a Unity Catalog (UC) function in [notebook 5](https://github.com/lara-openai/databricks-supply-chain/blob/main/05_Data_Analysis_%26_Functions.py) and [notebook 7](https://github.com/lara-openai/databricks-supply-chain/blob/main/07_More_Functions.py), e.g. product_from_raw, raw_from_product, revenue_risk, lookup_product_demand, query_unstructured_emails. Because UC governs tables, models, and vector indexes alike, the Agent can decide at runtime whether to forecast, trace a BOM dependency, gauge revenue impact, fetch history, or search e-mails, always within the caller’s data-access rights.\n",
"\n",
"The result is an end-to-end pipeline that forecasts demand, identifies raw‑material gaps, optimizes logistics, surfaces hidden risks, and lets analysts ask ad‑hoc questions and surface delay warnings.\n",
"\n",
- "After all notebooks have been executed, the Databricks environment is ready, you can proceed to build the Agent and connect it to Databricks.\n",
- "\n"
+ "After all notebooks have been executed (by running notebook 1), the Databricks environment is ready, you can proceed to build the Agent and connect it to Databricks."
]
},
{
@@ -177,14 +167,14 @@
}
},
"source": [
- "## Connect the Agent to Databricks MCP servers\n",
+ "## Connect to Databricks MCP servers\n",
"\n",
"Currently, the [MCP spec](https://openai.github.io/openai-agents-python/mcp/) defines three kinds of servers, based on the transport mechanism they use: \n",
"- stdio servers run as a subprocess of your application. You can think of them as running \"locally\".\n",
"- HTTP over SSE servers run remotely. You connect to them via a URL.\n",
"- Streamable HTTP servers run remotely using the Streamable HTTP transport defined in the MCP spec.\n",
"\n",
- "[Databricks-hosted MCP endpoints](https://docs.databricks.com/aws/en/generative-ai/agent-framework/mcp) (vector-search, Unity Catalog functions, Genie) sit behind standard HTTPS URLs and implement the Streamable HTTP transport defined in the MCP spec. Make sure that your workspace is serverless enabled so that you can connect to the Databricks managed MCP for functions. "
+ "[Databricks-hosted MCP endpoints](https://docs.databricks.com/aws/en/generative-ai/agent-framework/mcp) (vector-search, Unity Catalog functions, Genie) sit behind standard HTTPS URLs and implement the Streamable HTTP transport defined in the MCP spec. Make sure that your workspace is serverless enabled so that you can connect to the Databricks managed MCP. "
]
},
{
@@ -200,17 +190,55 @@
}
},
"source": [
- "## Integrate Databricks MCP servers into an OpenAI Agent \n",
- "\n",
- "Clone this GitHub [repository](https://github.com/lara-openai/agent-supply-chain-mcp) to integrate the Databricks MCP servers into our OpenAI Agent.\n",
- "\n",
- "```\n",
- "git clone https://github.com/lara-openai/agent-supply-chain-mcp.git\n",
- "```\n",
- "\n",
- "The [main.py file](https://github.com/lara-openai/agent-supply-chain-mcp/blob/main/main.py) orchestrates the agent logic, using the OpenAI Agent SDK and exposing Databricks MCP vector-search endpoints and Unity Catalog functions as callable tools. It starts by reading environment variables that point to the target catalog, schema, and Unity Catalog (UC) function path, then exposes two tools: vector_search, which queries a Databricks Vector Search index, and uc_function, which executes Unity Catalog functions via MCP. Both tools make authenticated, POST requests through httpx, returning raw JSON from the Databricks REST API. Both helpers obtain the workspace host and Personal Access Token through the _databricks_ctx() utility (backed by DatabricksOAuthClientProvider) and issue authenticated POST requests with httpx, returning raw JSON responses. \n",
+ "## Integrate Databricks MCP servers into an OpenAI Agent "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The OpenAI Agent is available [here](https://github.com/openai/openai-cookbook/blob/main/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/README.md). Start by installing the required dependencies:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pip install -r requirements.txt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You will need an OpenAI API key to securely access the API. If you're new to the OpenAI API, [sign up for an account](https://platform.openai.com/signup). You can follow [these steps](https://platform.openai.com/docs/libraries?project_id=proj_2NqyDkmG63zyr3TzOh64F2ac#create-and-export-an-api-key) to create a key and store it in a safe location. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This cookbook shows how to serve this Agent with FastAPI and chat through a React UI. However, `main.py` is set up as a self‑contained REPL, so after installing the required dependencies and setting up the necessary credentials (including the Databricks host and personal-access token as described above), you can run the Agent directly from the command line with a single command:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "python main.py"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The [main.py](https://github.com/openai/openai-cookbook/blob/main/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/main.py) file orchestrates the agent logic, using the OpenAI Agent SDK and exposing Databricks MCP vector-search endpoints and Unity Catalog functions as callable tools. It starts by reading environment variables that point to the target catalog, schema, and Unity Catalog (UC) function path, then exposes two tools: vector_search, which queries a Databricks Vector Search index, and uc_function, which executes Unity Catalog functions via MCP. Both tools make authenticated, POST requests through httpx, returning raw JSON from the Databricks REST API. Both helpers obtain the workspace host and Personal Access Token through the _databricks_ctx() utility (backed by DatabricksOAuthClientProvider) and issue authenticated POST requests with httpx, returning raw JSON responses. \n",
"\n",
- "Inside run_agent(), the script instantiates an Agent called “Assistant” that is hard-scoped to supply-chain topics. Every response must invoke one of the two registered tools, and guardrails force the agent to refuse anything outside logistics, inventory, procurement or forecasting. Each user prompt is processed inside an SDK trace context. A simple REPL drives the interaction: user input is wrapped in an OpenTelemetry-style trace, dispatched through Runner.run, and the final answer (or guardrail apology) is printed. The program is kicked off through an asyncio.run call in main(), making the whole flow fully asynchronous and non-blocking. \n"
+ "Inside run_agent(), the script instantiates an Agent called “Assistant” that is hard-scoped to supply-chain topics. Every response must invoke one of the two registered tools, and guardrails force the agent to refuse anything outside logistics, inventory, procurement or forecasting. Each user prompt is processed inside an SDK trace context. A simple REPL drives the interaction: user input is wrapped in an OpenTelemetry-style trace, dispatched through Runner.run, and the final answer (or guardrail apology) is printed. The program is kicked off through an asyncio.run call in main(), making the whole flow fully asynchronous and non-blocking. "
]
},
{
@@ -229,13 +257,27 @@
"outputs": [],
"source": [
"\"\"\"\n",
- "Code snippets from main.py file \n",
+ "CLI assistant that uses Databricks MCP Vector Search and UC Functions via the OpenAI Agents SDK.\n",
"\"\"\"\n",
"\n",
- "CATALOG = os.getenv(\"MCP_VECTOR_CATALOG\", \"main\")\n",
+ "import asyncio\n",
+ "import os\n",
+ "import httpx\n",
+ "from typing import Dict, Any\n",
+ "from agents import Agent, Runner, function_tool, gen_trace_id, trace\n",
+ "from agents.exceptions import (\n",
+ " InputGuardrailTripwireTriggered,\n",
+ " OutputGuardrailTripwireTriggered,\n",
+ ")\n",
+ "from agents.model_settings import ModelSettings\n",
+ "from databricks_mcp import DatabricksOAuthClientProvider\n",
+ "from databricks.sdk import WorkspaceClient\n",
+ "from supply_chain_guardrails import supply_chain_guardrail\n",
+ "\n",
+ "CATALOG = os.getenv(\"MCP_VECTOR_CATALOG\", \"main\") # override catalog, schema, functions_path name if your data assets sit in a different location\n",
"SCHEMA = os.getenv(\"MCP_VECTOR_SCHEMA\", \"supply_chain_db\")\n",
"FUNCTIONS_PATH = os.getenv(\"MCP_FUNCTIONS_PATH\", \"main/supply_chain_db\")\n",
- "DATABRICKS_PROFILE = os.getenv(\"DATABRICKS_PROFILE\", \"DEFAULT\")\n",
+ "DATABRICKS_PROFILE = os.getenv(\"DATABRICKS_PROFILE\", \"DEFAULT\") # override if using a different profile name \n",
"HTTP_TIMEOUT = 30.0 # seconds\n",
"\n",
"\n",
@@ -295,7 +337,15 @@
" except InputGuardrailTripwireTriggered:\n",
" print(\"Assistant: Sorry, I can only help with supply-chain questions.\")\n",
" except OutputGuardrailTripwireTriggered:\n",
- " print(\"Assistant: Sorry, I can only help with supply-chain questions.\")"
+ " print(\"Assistant: Sorry, I can only help with supply-chain questions.\")\n",
+ "\n",
+ "\n",
+ "def main():\n",
+ " asyncio.run(run_agent())\n",
+ "\n",
+ "\n",
+ "if __name__ == \"__main__\":\n",
+ " main()"
]
},
{
@@ -311,9 +361,77 @@
}
},
"source": [
- "[databricks_mcp.py](https://github.com/lara-openai/agent-supply-chain-mcp/blob/main/databricks_mcp.py) serves as a focused authentication abstraction: it obtains the Personal Access Token from a given WorkspaceClient (ws.config.token) and shields the rest of the application from Databricks‑specific OAuth logic. By confining all token‑handling details to this single module, any future changes to Databricks’ authentication scheme can be accommodated by updating this file.\n",
+ "[databricks_mcp.py](https://github.com/openai/openai-cookbook/blob/main/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/databricks_mcp.py) serves as a focused authentication abstraction: it obtains the Personal Access Token we created earlier from a given WorkspaceClient (ws.config.token) and shields the rest of the application from Databricks‑specific OAuth logic. By confining all token‑handling details to this single module, any future changes to Databricks’ authentication scheme can be accommodated by updating this file.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"\"\"\n",
+ "Databricks OAuth client provider for MCP servers.\n",
+ "\"\"\"\n",
+ "\n",
+ "class DatabricksOAuthClientProvider:\n",
+ " def __init__(self, ws):\n",
+ " self.ws = ws\n",
"\n",
- "[supply_chain_guardrails.py](https://github.com/lara-openai/agent-supply-chain-mcp/blob/main/supply_chain_guardrails.py) implements a lightweight output guardrail by spinning up a second agent (“Supply‑chain check”) that classifies candidate answers. The main agent hands its draft reply to this checker, which returns a Pydantic object with a Boolean is_supply_chain. If that flag is false, the guardrail raises a tripwire and the caller swaps in a refusal.\n"
+ " def get_token(self):\n",
+ " # For Databricks SDK >=0.57.0, token is available as ws.config.token\n",
+ " return self.ws.config.token"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "[supply_chain_guardrails.py](https://github.com/openai/openai-cookbook/blob/main/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/supply_chain_guardrails.py) implements a lightweight output guardrail by spinning up a second agent (“Supply‑chain check”) that classifies candidate answers. The main agent hands its draft reply to this checker, which returns a Pydantic object with a Boolean is_supply_chain. If that flag is false, the guardrail raises a tripwire and the caller swaps in a refusal."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"\"\"\n",
+ "Output guardrail that blocks answers not related to supply-chain topics.\n",
+ "\"\"\"\n",
+ "from __future__ import annotations\n",
+ "\n",
+ "from pydantic import BaseModel\n",
+ "from agents import Agent, Runner, GuardrailFunctionOutput\n",
+ "from agents import output_guardrail\n",
+ "from agents.run_context import RunContextWrapper\n",
+ "\n",
+ "class SupplyChainCheckOutput(BaseModel):\n",
+ " reasoning: str\n",
+ " is_supply_chain: bool\n",
+ "\n",
+ "\n",
+ "guardrail_agent = Agent(\n",
+ " name=\"Supply-chain check\",\n",
+ " instructions=(\n",
+ " \"Check if the text is within the domain of supply-chain analytics and operations \"\n",
+ " \"Return JSON strictly matching the SupplyChainCheckOutput schema\"\n",
+ " ),\n",
+ " output_type=SupplyChainCheckOutput,\n",
+ ")\n",
+ "\n",
+ "\n",
+ "@output_guardrail\n",
+ "async def supply_chain_guardrail(\n",
+ " ctx: RunContextWrapper, agent: Agent, output\n",
+ ") -> GuardrailFunctionOutput:\n",
+ " \"\"\"Output guardrail that blocks non-supply-chain answers\"\"\"\n",
+ " text = output if isinstance(output, str) else getattr(output, \"response\", str(output))\n",
+ " result = await Runner.run(guardrail_agent, text, context=ctx.context)\n",
+ " return GuardrailFunctionOutput(\n",
+ " output_info=result.final_output,\n",
+ " tripwire_triggered=not result.final_output.is_supply_chain,\n",
+ " )"
]
},
{
@@ -329,9 +447,32 @@
}
},
"source": [
- "## Serve the agent with FastAPI \n",
+ "## Serve the agent with FastAPI "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To kick off the backend (Fast API), run the following command: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "python -m uvicorn api_server:app --reload --port 8000"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The API will be available at http://localhost:8000 (for FastAPI docs go to: http://localhost:8000/docs). \n",
"\n",
- "The [api_server.py file](https://github.com/lara-openai/agent-supply-chain-mcp/blob/main/api_server.py) is a FastAPI backend that exposes your agent as a streaming /chat API endpoint. At startup it configures CORS so a local front-end can talk to it, then defines build_mcp_servers(), which authenticates to the caller’s Databricks workspace, constructs two HTTP “server tools” (one for vector search, one for Unity-Catalog functions), and pre-connects them for low-latency use. Each incoming POST to /chat contains a single user message; the handler spins up a fresh Agent whose mcp_servers list is populated by those streaming tools and whose model is forced to call a tool for every turn. "
+ "The [api_server.py](https://github.com/openai/openai-cookbook/blob/main/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/api_server.py) is a FastAPI backend that exposes your agent as a streaming /chat API endpoint. At startup it configures CORS so a local front-end can talk to it, then defines `build_mcp_servers()`, which authenticates to the caller’s Databricks workspace, constructs two HTTP “server tools” (one for vector search, one for Unity-Catalog functions), and pre-connects them for low-latency use. Each incoming POST to /chat contains a single user message. The handler spins up a fresh Agent whose mcp_servers list is populated by those streaming tools and whose model is forced to call a tool for every turn. "
]
},
{
@@ -350,53 +491,144 @@
"outputs": [],
"source": [
"\"\"\"\n",
- "Code snippets from api_server.py file \n",
+ "FastAPI wrapper that exposes the agent as a streaming `/chat` endpoint.\n",
"\"\"\"\n",
"\n",
+ "import os\n",
+ "import asyncio\n",
+ "import logging\n",
+ "from fastapi import FastAPI\n",
+ "from fastapi.responses import StreamingResponse\n",
+ "from fastapi.middleware.cors import CORSMiddleware\n",
+ "from pydantic import BaseModel\n",
+ "from agents.exceptions import (\n",
+ " InputGuardrailTripwireTriggered,\n",
+ " OutputGuardrailTripwireTriggered,\n",
+ ")\n",
+ "from agents import Agent, Runner, gen_trace_id, trace\n",
+ "from agents.mcp import MCPServerStreamableHttp, MCPServerStreamableHttpParams\n",
+ "from agents.model_settings import ModelSettings\n",
+ "from databricks_mcp import DatabricksOAuthClientProvider\n",
+ "from databricks.sdk import WorkspaceClient\n",
+ "\n",
+ "from supply_chain_guardrails import supply_chain_guardrail\n",
+ "\n",
+ "CATALOG = os.getenv(\"MCP_VECTOR_CATALOG\", \"main\")\n",
+ "SCHEMA = os.getenv(\"MCP_VECTOR_SCHEMA\", \"supply_chain_db\")\n",
+ "FUNCTIONS_PATH = os.getenv(\"MCP_FUNCTIONS_PATH\", \"main/supply_chain_db\")\n",
+ "DATABRICKS_PROFILE = os.getenv(\"DATABRICKS_PROFILE\", \"DEFAULT\")\n",
+ "HTTP_TIMEOUT = 30.0 # seconds\n",
+ "\n",
+ "app = FastAPI()\n",
+ "\n",
+ "# Allow local dev front‑end\n",
+ "app.add_middleware(\n",
+ " CORSMiddleware,\n",
+ " allow_origins=[\"http://localhost:5173\"],\n",
+ " allow_credentials=True,\n",
+ " allow_methods=[\"*\"],\n",
+ " allow_headers=[\"*\"],\n",
+ ")\n",
+ "\n",
+ "class ChatRequest(BaseModel):\n",
+ " message: str\n",
+ "\n",
+ "\n",
"async def build_mcp_servers():\n",
- " \"\"\"Initialise Databricks MCP vector & UC‑function servers.\"\"\"\n",
- " ws = WorkspaceClient(profile=DATABRICKS_PROFILE)\n",
- " token = DatabricksOAuthClientProvider(ws).get_token()\n",
- "\n",
- " base = ws.config.host\n",
- " vector_url = f\"{base}/api/2.0/mcp/vector-search/{CATALOG}/{SCHEMA}\"\n",
- " fn_url = f\"{base}/api/2.0/mcp/functions/{FUNCTIONS_PATH}\"\n",
- "\n",
- " async def _proxy_tool(request_json: dict, url: str):\n",
- " import httpx\n",
- "\n",
- " headers = {\"Authorization\": f\"Bearer {token}\"}\n",
- " async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:\n",
- " resp = await client.post(url, json=request_json, headers=headers)\n",
- " resp.raise_for_status()\n",
- " return resp.json()\n",
- "\n",
- " headers = {\"Authorization\": f\"Bearer {token}\"}\n",
- "\n",
- " servers = [\n",
- " MCPServerStreamableHttp(\n",
- " MCPServerStreamableHttpParams(\n",
- " url=vector_url,\n",
- " headers=headers,\n",
- " timeout=HTTP_TIMEOUT,\n",
- " ),\n",
- " name=\"vector_search\",\n",
- " client_session_timeout_seconds=60,\n",
- " ),\n",
- " MCPServerStreamableHttp(\n",
- " MCPServerStreamableHttpParams(\n",
- " url=fn_url,\n",
- " headers=headers,\n",
- " timeout=HTTP_TIMEOUT,\n",
- " ),\n",
- " name=\"uc_functions\",\n",
- " client_session_timeout_seconds=60,\n",
- " ),\n",
- " ]\n",
- "\n",
- " # Ensure servers are initialized before use\n",
- " await asyncio.gather(*(s.connect() for s in servers))\n",
- " return servers"
+ " \"\"\"Initialise Databricks MCP vector & UC‑function servers.\"\"\"\n",
+ " ws = WorkspaceClient(profile=DATABRICKS_PROFILE)\n",
+ " token = DatabricksOAuthClientProvider(ws).get_token()\n",
+ "\n",
+ " base = ws.config.host\n",
+ " vector_url = f\"{base}/api/2.0/mcp/vector-search/{CATALOG}/{SCHEMA}\"\n",
+ " fn_url = f\"{base}/api/2.0/mcp/functions/{FUNCTIONS_PATH}\"\n",
+ "\n",
+ " async def _proxy_tool(request_json: dict, url: str):\n",
+ " import httpx\n",
+ "\n",
+ " headers = {\"Authorization\": f\"Bearer {token}\"}\n",
+ " async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:\n",
+ " resp = await client.post(url, json=request_json, headers=headers)\n",
+ " resp.raise_for_status()\n",
+ " return resp.json()\n",
+ "\n",
+ " headers = {\"Authorization\": f\"Bearer {token}\"}\n",
+ "\n",
+ " servers = [\n",
+ " MCPServerStreamableHttp(\n",
+ " MCPServerStreamableHttpParams(\n",
+ " url=vector_url,\n",
+ " headers=headers,\n",
+ " timeout=HTTP_TIMEOUT,\n",
+ " ),\n",
+ " name=\"vector_search\",\n",
+ " client_session_timeout_seconds=60,\n",
+ " ),\n",
+ " MCPServerStreamableHttp(\n",
+ " MCPServerStreamableHttpParams(\n",
+ " url=fn_url,\n",
+ " headers=headers,\n",
+ " timeout=HTTP_TIMEOUT,\n",
+ " ),\n",
+ " name=\"uc_functions\",\n",
+ " client_session_timeout_seconds=60,\n",
+ " ),\n",
+ " ]\n",
+ "\n",
+ " # Ensure servers are initialized before use\n",
+ " await asyncio.gather(*(s.connect() for s in servers))\n",
+ " return servers\n",
+ "\n",
+ "\n",
+ "@app.post(\"/chat\")\n",
+ "async def chat_endpoint(req: ChatRequest):\n",
+ " try:\n",
+ " servers = await build_mcp_servers()\n",
+ "\n",
+ " agent = Agent(\n",
+ " name=\"Assistant\",\n",
+ " instructions=\"Use the tools to answer the questions.\",\n",
+ " mcp_servers=servers,\n",
+ " model_settings=ModelSettings(tool_choice=\"required\"),\n",
+ " output_guardrails=[supply_chain_guardrail],\n",
+ " )\n",
+ "\n",
+ " trace_id = gen_trace_id()\n",
+ "\n",
+ " async def agent_stream():\n",
+ " logging.info(f\"[AGENT_STREAM] Input message: {req.message}\")\n",
+ " try:\n",
+ " with trace(workflow_name=\"Databricks MCP Example\", trace_id=trace_id):\n",
+ " result = await Runner.run(starting_agent=agent, input=req.message)\n",
+ " logging.info(f\"[AGENT_STREAM] Raw agent result: {result}\")\n",
+ " try:\n",
+ " logging.info(\n",
+ " f\"[AGENT_STREAM] RunResult __dict__: {getattr(result, '__dict__', str(result))}\"\n",
+ " )\n",
+ " raw_responses = getattr(result, \"raw_responses\", None)\n",
+ " logging.info(f\"[AGENT_STREAM] RunResult raw_responses: {raw_responses}\")\n",
+ " except Exception as log_exc:\n",
+ " logging.warning(f\"[AGENT_STREAM] Could not log RunResult details: {log_exc}\")\n",
+ " yield result.final_output\n",
+ " except InputGuardrailTripwireTriggered:\n",
+ " # Off-topic question denied by guardrail\n",
+ " yield \"Sorry, I can only help with supply-chain questions.\"\n",
+ " except OutputGuardrailTripwireTriggered:\n",
+ " # Out-of-scope answer blocked by guardrail\n",
+ " yield \"Sorry, I can only help with supply-chain questions.\"\n",
+ " except Exception:\n",
+ " logging.exception(\"[AGENT_STREAM] Exception during agent run\")\n",
+ " yield \"[ERROR] Exception during agent run. Check backend logs for details.\"\n",
+ "\n",
+ " return StreamingResponse(agent_stream(), media_type=\"text/plain\")\n",
+ "\n",
+ " except Exception:\n",
+ " logging.exception(\"chat_endpoint failed\")\n",
+ " return StreamingResponse(\n",
+ " (line.encode() for line in [\"Internal server error 🙈\"]),\n",
+ " media_type=\"text/plain\",\n",
+ " status_code=500,\n",
+ " )"
]
},
{
@@ -428,17 +660,74 @@
}
},
"source": [
- "## Engage users through a React chat UI\n",
- "\n",
- "The React chat UI in the [/ui folder](https://github.com/lara-openai/agent-supply-chain-mcp/tree/main/ui) provides a user-friendly web interface for interacting with the backend agent. It features components for displaying the conversation history and a text input for sending messages. \n",
+ "## Engage users through a React chat UI"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In a different terminal, run the following to start the Frontend (React UI):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "cd ui\n",
+ "npm install\n",
+ "npm run dev"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The app will be available at http://localhost:5173"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The React chat UI in the [/ui folder](https://github.com/openai/openai-cookbook/blob/main/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui) provides a user-friendly web interface for interacting with the backend agent. It features components for displaying the conversation history and a text input for sending messages. \n",
"\n",
"When a user submits a message, the UI sends it to the backend /chat endpoint and streams the agent’s response in real time, updating the chat window as new content arrives. The design emphasizes a conversational experience, making it easy for users to ask questions and receive answers from the Databricks-powered agent, all within a responsive and interactive web application.\n",
"\n",
- "In particular, the file ui/src/components/ChatUI.jsx contains the core logic for the chat interface, including how user messages are sent to the backend and how streaming responses from the agent are handled and displayed in real time.\n",
- "\n",
+ "In particular, the file [ChatUI.jsx](https://github.com/openai/openai-cookbook/blob/main/examples/mcp/building-a-supply-chain-copilot-with-agent-sdk-and-databricks-mcp/ui/src/components/ChatUI.jsx) file contains the core logic for the chat interface, including how user messages are sent to the backend and how streaming responses from the agent are handled and displayed in real time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"\"\"\n",
+ "Code snippet handling the token stream coming from the FastAPI /chat endpoint.\n",
+ "\"\"\"\n",
+ "const reader = response.body.getReader();\n",
+ "while (true) {\n",
+ " const { done, value } = await reader.read();\n",
+ " if (done) break;\n",
+ " assistantMsg.text += new TextDecoder().decode(value);\n",
+ " setMessages(m => {\n",
+ " const copy = [...m];\n",
+ " copy[copy.length - 1] = { ...assistantMsg };\n",
+ " return copy;\n",
+ " });\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
"The UI streams and displays the agent’s response as it arrives, creating a smooth, real-time chat experience. Highlighting this will clearly show your readers how the UI achieves interactive, conversational feedback from your backend agent.\n",
"\n",
- "\n"
+ ""
]
},
{
@@ -500,9 +789,10 @@
}
},
"source": [
- "## Future improvements\n",
+ "## Next Steps\n",
"\n",
- "Future improvements include adding multi-turn capabilities and Genie space MCP servers."
+ "* You can consider adding multi-turn capabilities\n",
+ "* You can also add Genie Space MCP servers if you’d like to adapt this setup to your own workspace"
]
},
{
@@ -522,7 +812,6 @@
"- Databricks Managed MCP [documentation](https://docs.databricks.com/aws/en/generative-ai/agent-framework/mcp)\n",
"- OpenAI Agent SDK [documentation](https://openai.github.io/openai-agents-python/)\n",
"- OpenAI Agent Guardrails [documentation](https://openai.github.io/openai-agents-python/guardrails/)\n",
- "- Github [repo](https://github.com/lararachidi/agent-supply-chain/blob/main/README.md) to set up the Databricks supply chain tables and functions\n",
"- Openai-agents-python example [snippets](https://github.com/openai/openai-agents-python/tree/main/examples/mcp/streamablehttp_example)"
]
}
diff --git a/registry.yaml b/registry.yaml
index 8d3c4f3c71..649b52d42e 100644
--- a/registry.yaml
+++ b/registry.yaml
@@ -65,11 +65,9 @@
authors:
- lara-openai
tags:
- - agents
+ - agents-sdk
- mcp
- - vector-search
- - supply-chain
- - databricks
+ - tracing
- title: Eval Driven System Design - From Prototype to Production
path: examples/partners/eval_driven_system_design/receipt_inspection.ipynb