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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,42 @@ dab init

DAB supports tables, views, and stored procedures. It works with SQL Server, Azure Cosmos DB, PostgreSQL, MySQL, and SQL Data Warehouse. Security is engine-level, but permissions are per entity.

The file `dab-config.json` is automatically created through this process. These are the resulting contents:

```json
{
"$schema": "https://github.com/Azure/data-api-builder/releases/download/v1.5.56/dab.draft.schema.json",
"data-source": {
"database-type": "mssql",
"connection-string": "@env('my-connection-string')",
"options": {
"set-session-context": false
}
},
"runtime": {
"rest": {
"enabled": true,
"path": "/api",
"request-body-strict": true
},
"graphql": {
"enabled": true,
"path": "/graphql",
"allow-introspection": true
},
"host": {
"cors": {
"origins": [],
"allow-credentials": false
},
"authentication": {
"provider": "AppService"
},
"mode": "development"
}
},
"entities": { }
}
```
dab add Actor
--source "dbo.Actor"
Expand Down
Binary file added docs/media/dab-aifoundry-architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
275 changes: 275 additions & 0 deletions docs/testing-guide/ai-foundry-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
# Deploying SQL MCP Server implemented in Data API builder and Integrating with Azure AI Foundry

This document provides an end‑to‑end guide to stand up a **SQL MCP Server** with **Model Context Protocol (MCP)** tools implemented in **Data API builder (DAB)** container that also exposes **REST** and **GraphQL** endpoints, and to integrate those MCP tools with an **Azure AI Foundry Agent**.

<img alt="Architecture diagram showing SQL MCP Server integration with Azure AI Foundry" src="../media/dab-aifoundry-architecture.png" />

## 1. Architecture Overview

**Components**
- **Azure SQL Database** hosting domain tables and stored procedures.
- **DAB container** (Azure Container Instances in this guide) that:
- reads `dab-config.json` from an **Azure Files** share at startup,
- exposes **REST**, **GraphQL**, and **MCP** endpoints.
- **Azure Storage (Files)** to store and version `dab-config.json`.
- **Azure AI Foundry Agent** configured with an **MCP tool** pointing to the SQL MCP Server endpoint.

**Flow**
1. DAB starts in ACI → reads `dab-config.json` from the mounted Azure Files share.
2. DAB exposes `/api` (REST), `/graphql` (GraphQL), and `/mcp` (MCP).
3. Azure AI Foundry Agent invokes MCP tools to read/update data via DAB’s surface (tables, views and stored procedures).


## 2. Prerequisites
- Azure Subscription with permissions for Resource Groups, Storage, ACI, and Azure SQL.
- Azure SQL Database provisioned and reachable from ACI.
- Azure CLI (`az`) and .NET SDK installed locally.
- DAB CLI version **1.7.81 or later**.
- Outbound network access from ACI to your Azure SQL server.


## 3. Prepare the Database

You need to create the necessary tables and stored procedures in your Azure SQL Database. Below is an example of how to create a simple `Products` table and a stored procedure to retrieve products by category.

**Example:**

1. Connect to your Azure SQL Database using Azure Data Studio, SQL Server Management Studio, or the Azure Portal's Query Editor.

2. Run the following SQL script to create a sample table and stored procedure:

```sql
-- Create Products table
CREATE TABLE Products (
ProductID INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(100) NOT NULL,
Category NVARCHAR(50) NOT NULL,
Price DECIMAL(10,2) NOT NULL
);

-- Create stored procedure to get products by category
CREATE PROCEDURE GetProductsByCategory
@Category NVARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT ProductID, Name, Category, Price
FROM Products
WHERE Category = @Category;
END;
```

## 4. Install DAB CLI and Bootstrap Configuration

```
dotnet tool install --global Microsoft.DataApiBuilder --version 1.7.81
export DATABASE_CONNECTION_STRING="Server=<server>.database.windows.net;Database=<db>;User ID=<user>;Password=<pwd>;Encrypt=True;"

dab init \
--database-type "mssql" \
--connection-string "@env('DATABASE_CONNECTION_STRING')" \
--host-mode "Development" \
--rest.enabled true \
--graphql.enabled true \
--mcp.enabled true \
--mcp.path "/mcp"

```

## 5. Add all required entities (tables and stored procedures) to `dab-config.json` and enable MCP tools in the config

Here is how to add a table entity and a stored procedure to your `dab-config.json`, and ensure MCP tools are enabled:

1. **Open your `dab-config.json` file.**

2. **Add an entity (table) definition** under the `"entities"` section. For example, to expose a `Customers` table:
```
"entities": {
"Customers": {
"source": "Customers",
"rest": true,
"graphql": true,
"mcp": true,
"permissions": [
{
"role": "anonymous",
"actions": [ "read", "create", "update", "delete" ]
}
]
}
}
```

3. **Add a stored procedure** under the "entities" section. For example, to expose a stored procedure called GetCustomerOrders:

```
"GetCustomerOrders": {
"source": {
"object": "GetCustomerOrders",
"type": "stored-procedure"
},
"rest": true,
"graphql": true,
"mcp": true,
"permissions": [
{
"role": "anonymous",
"actions": [ "execute" ]
}
]
}
```

Note: Make sure the "entities" section is a valid JSON object. If you have multiple entities, separate them with commas.

4. **Ensure MCP is enabled in the "runtime" section:**

```
"runtime": {
"rest": { "enabled": true },
"graphql": { "enabled": true },
"mcp": {
"enabled": true,
"path": "/mcp"
}
}
```

5. **Example dab-config.json structure:**

```
{
"data-source": {
"database-type": "mssql",
"connection-string": "@env('DATABASE_CONNECTION_STRING')"
},
"runtime": {
"rest": { "enabled": true },
"graphql": { "enabled": true },
"mcp": {
"enabled": true,
"path": "/mcp"
}
},
"entities": {
"Customers": {
"source": "Customers",
"rest": true,
"graphql": true,
"mcp": true,
"permissions": [
{
"role": "anonymous",
"actions": [ "read", "create", "update", "delete" ]
}
]
},
"GetCustomerOrders": {
"source": {
"object": "GetCustomerOrders",
"type": "stored-procedure"
},
"rest": true,
"graphql": true,
"mcp": true,
"permissions": [
{
"role": "anonymous",
"actions": [ "execute" ]
}
]
}
}
}
```

6. **Save the file.**

## 6. Store dab-config.json in Azure Files

1. **Create a Storage Account** (if you don't have one):
az storage account create
--name
--resource-group
--location
--sku Standard_LRS


2. **Create a File Share**:
az storage share create
--name
--account-name


3. **Upload `dab-config.json` to the File Share**:
az storage file upload
--account-name
--share-name
--source ./dab-config.json
--path dab-config.json


4. **Retrieve the Storage Account key** (needed for mounting in ACI):
az storage account keys list
--account-name
--resource-group

Use the value of `key1` or `key2` as `<StorageAccountKey>` in the next step.


## 7. Deploy DAB to Azure Container Instances

```
az container create \
--resource-group <RG> \
--name dab-mcp-demo \
--image mcr.microsoft.com/azure-databases/data-api-builder:1.7.81-rc \
--dns-name-label <globally-unique-label> \
--ports 5000 \
--location <location> \
--environment-variables DAB_CONFIG_PATH="/aci/dab-config.json" \
--azure-file-volume-share-name <FileShareName> \
--azure-file-volume-account-name <StorageAccountName> \
--azure-file-volume-account-key <StorageAccountKey> \
--azure-file-volume-mount-path "/aci" \
--os-type Linux \
--cpu 1 \
--memory 1.5 \
--command-line "dotnet Azure.DataApiBuilder.Service.dll --ConfigFileName $DAB_CONFIG_PATH --LogLevel Debug"
```

## 8. Integrate with Azure AI Foundry

Follow these steps to connect your SQL MCP endpoint deployed in DAB to Azure AI Foundry and test the integration:

1. **Create or Open a Project**
- Navigate to the [Azure AI Foundry portal](https://ai.azure.com/foundry) and sign in.
- On the dashboard, click **Projects** in the left navigation pane.
- To create a new project, click **New Project**, enter a name (e.g., `DAB-MCP-Demo`), and click **Create**.
- To use an existing project, select it from the list.

2. **Add an Agent**
- Within your project, go to the **Agents** tab.
- Click **Add Agent**.
- Enter an agent name (e.g., `DAB-MCP-Agent`).
- (Optional) Add a description.
- Click **Create**.

3. **Configure the MCP Tool**
- In the agent's configuration page, go to the **Tools** section.
- Click **Add Tool** and select **MCP** from the tool type dropdown.
- In the **MCP Endpoint URL** field, enter your SQL MCP endpoint in DAB, e.g., `http://<fqdn>/mcp`.
- (Optional) Configure authentication if your endpoint requires it.
- Click **Save** to add the tool.

4. **Test in Playground**
- Go to the **Playground** tab in your project.
- Select the agent you created from the agent dropdown.
- In the input box, enter a prompt that will trigger the MCP tool, such as:
```
Get all records from the Customers entity.
```
- Click **Run**.
- The agent should invoke the MCP tool, which will call your DAB MCP endpoint and return the results.
- **Expected Result:** You should see the data returned from your DAB instance displayed in the Playground output panel.
- If there are errors, check the DAB container logs and ensure the MCP endpoint is reachable from Azure AI Foundry.
2 changes: 1 addition & 1 deletion schemas/dab.draft.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@
"description": "Custom authentication provider defined by the user. Use the JWT property to configure the custom provider."
}
],
"default": "StaticWebApps"
"default": "AppService"
},
"jwt": {
"type": "object",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,30 @@ public Task<CallToolResult> ExecuteAsync(
}
}

// Get current user's role for permission filtering
// For discovery tools like describe_entities, we use the first valid role from the header
// This differs from operation-specific tools that check permissions per entity per operation
if (httpContext != null && authResolver.IsValidRoleContext(httpContext))
{
string roleHeader = httpContext.Request.Headers[AuthorizationResolver.CLIENT_ROLE_HEADER].ToString();
if (!string.IsNullOrWhiteSpace(roleHeader))
{
string[] roles = roleHeader
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);

if (roles.Length > 1)
{
logger?.LogWarning("Multiple roles detected in request header: [{Roles}]. Using first role '{FirstRole}' for entity discovery. " +
"Consider using a single role for consistent permission reporting.",
string.Join(", ", roles), roles[0]);
}

// For discovery operations, take the first role from comma-separated list
// This provides a consistent view of available entities for the primary role
currentUserRole = roles.FirstOrDefault();
}
}

(bool nameOnly, HashSet<string>? entityFilter) = ParseArguments(arguments, logger);

if (currentUserRole == null)
Expand Down
39 changes: 39 additions & 0 deletions src/Azure.DataApiBuilder.Mcp/Core/McpProtocolDefaults.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Azure.DataApiBuilder.Product;
using Microsoft.Extensions.Configuration;

namespace Azure.DataApiBuilder.Mcp.Core
{
/// <summary>
/// Centralized defaults and configuration keys for MCP protocol settings.
/// </summary>
public static class McpProtocolDefaults
{
/// <summary>
/// Default MCP server name advertised during initialization.
/// </summary>
public const string MCP_SERVER_NAME = "SQL MCP Server";
/// <summary>
/// Default MCP server version advertised during initialization.
/// </summary>
public static readonly string MCP_SERVER_VERSION = ProductInfo.GetProductVersion();
/// <summary>
/// Default MCP protocol version advertised when no configuration override is provided.
/// </summary>
public const string DEFAULT_PROTOCOL_VERSION = "2025-06-18";

/// <summary>
/// Configuration key used to override the MCP protocol version.
/// </summary>
public const string PROTOCOL_VERSION_CONFIG_KEY = "MCP:ProtocolVersion";

/// <summary>
/// Helper to resolve the effective protocol version from configuration.
/// Falls back to <see cref="DEFAULT_PROTOCOL_VERSION"/> when the key is not set.
/// </summary>
public static string ResolveProtocolVersion(IConfiguration? configuration)
{
return configuration?.GetValue<string>(PROTOCOL_VERSION_CONFIG_KEY) ?? DEFAULT_PROTOCOL_VERSION;
}
}
}

Loading