Skip to content
Open
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
0075046
Added mcp server draft
souravg-db2 Mar 13, 2026
0e8df4a
Restructured code
souravg-db2 Mar 16, 2026
a11e90d
Fix MCP server for Databricks Apps + Genie Code deployment
vb-dbrks Mar 19, 2026
cf51723
Remove development mode setting from Databricks configuration for the…
vb-dbrks Mar 19, 2026
7f88308
Fix Databricks Apps deployment: proper entry point and dependencies
vb-dbrks Mar 19, 2026
5f12914
comments
vb-dbrks Mar 19, 2026
b3bcad7
Add design spec: MCP server OBO support via Jobs API
souravg-db2 Jun 4, 2026
be128c5
Add implementation plan: MCP server OBO via Jobs API
souravg-db2 Jun 4, 2026
29f99ef
feat(mcp): add runner notebook for Jobs API dispatch
souravg-db2 Jun 4, 2026
75308d6
feat(mcp): replace Databricks Connect with Jobs API submit_notebook_j…
souravg-db2 Jun 4, 2026
8924398
feat(mcp): refactor tools to use Jobs API instead of Databricks Connect
souravg-db2 Jun 4, 2026
0762aec
chore(mcp): remove databricks-connect, add clusters scope, deploy not…
souravg-db2 Jun 4, 2026
64cd396
fix(mcp): use 'jobs' scope instead of invalid 'clusters' scope
souravg-db2 Jun 4, 2026
eb0c389
fix(mcp): use 'all-apis' scope for full API access including Jobs
souravg-db2 Jun 4, 2026
8914f87
fix(mcp): revert to known-valid scopes (sql, files.files)
souravg-db2 Jun 4, 2026
db67bff
fix(mcp): use SP with run_as for job submission instead of OBO token
souravg-db2 Jun 4, 2026
3a17418
added mcp changes
souravg-db2 Jun 10, 2026
876cf5b
updated mcp readme
souravg-db2 Jun 10, 2026
2f2f723
added changes for mcp server
souravg-db2 Jun 15, 2026
0644f57
Removed extra docs
souravg-db2 Jun 15, 2026
9b48b20
Updated documentation
souravg-db2 Jun 15, 2026
9c32530
Addedressed some review comments
souravg-db2 Jun 17, 2026
b3f0c1b
made fix per review comment
souravg-db2 Jun 22, 2026
1e6e9e5
added fix as per review comment . Also added test to CI
souravg-db2 Jun 22, 2026
30a2550
added changes for restricting allowed origins to databricks domains
souravg-db2 Jun 23, 2026
fd5da47
Merge branch 'main' into dqx/mcp
mwojtyczka Jun 24, 2026
2d16c24
feat(mcp): add checks persistence, table output, and data-contract tools
vb-dbrks Jun 24, 2026
e02e8c2
chore(mcp): trim server deps and remove dead code (review feedback)
vb-dbrks Jun 24, 2026
20996db
docs(mcp): note runner DQX dependency should be pinned in production
vb-dbrks Jun 24, 2026
3f3e0d9
fix(mcp): make app SP own the temp schema so it can clean up temp views
vb-dbrks Jun 24, 2026
77e5c8f
refactor(mcp): make temp-view cleanup stateless and restart/replica-safe
vb-dbrks Jun 24, 2026
a90f5d9
perf(mcp): make get_run_status a single non-blocking poll
vb-dbrks Jun 24, 2026
30e0ae1
test(mcp): add protocol-level and HTTP integration tests
vb-dbrks Jun 24, 2026
9b57b25
test(mcp): add agent-in-the-loop integration test (gated)
vb-dbrks Jun 24, 2026
da7fcc6
style(mcp): apply black formatting to satisfy make fmt
vb-dbrks Jun 24, 2026
27fbe75
test(mcp): scaffold and tear down an isolated table in the agent inte…
vb-dbrks Jun 24, 2026
3514dd7
build(mcp): runner installs the in-repo DQX wheel, not a PyPI release
vb-dbrks Jun 24, 2026
312d40e
ci(mcp): add MCP integration workflow that deploys to a CI workspace
vb-dbrks Jun 24, 2026
e8140ae
ci(mcp): gate integration workflow on CI config; clearer preflight
vb-dbrks Jun 24, 2026
1d9b6d2
test(mcp): run MCP integration via the acceptance harness (reuse work…
vb-dbrks Jun 24, 2026
7a2932b
test(mcp): resolve workspace auth from SDK instead of requiring DATAB…
vb-dbrks Jun 24, 2026
e31be73
ci(mcp): install Databricks CLI before the acceptance run
vb-dbrks Jun 25, 2026
0566855
test(mcp): surface full deploy output on failure
vb-dbrks Jun 25, 2026
bac43da
ci(mcp): retry bundle deploy to survive flaky terraform provider fetch
vb-dbrks Jun 25, 2026
ae2a7dc
ci(mcp): route terraform init through the Databricks registry proxy
vb-dbrks Jun 25, 2026
c7f91cd
docs(mcp): document all 12 tools, fix stale behavior, add examples
vb-dbrks Jun 25, 2026
4e88d66
test(mcp): add deterministic integration coverage for all 12 tools + …
vb-dbrks Jun 25, 2026
5dc91b3
ci(mcp): deploy the bundle with engine: direct (no Terraform)
vb-dbrks Jun 25, 2026
2dde58c
test(mcp): restructure integration suite as one self-contained test
vb-dbrks Jun 25, 2026
943c678
docs(mcp): overhaul the MCP server guide + add make mcp-deploy
vb-dbrks Jun 26, 2026
377e8ca
docs(mcp): match the "Getting help" wording used in the DQX Studio guide
vb-dbrks Jun 26, 2026
d93b60a
feat(mcp): grant-on-write table access + structured request logging
vb-dbrks Jun 26, 2026
6228ed5
docs(mcp): add Beta status badge and Feature lifecycle page
vb-dbrks Jun 26, 2026
a52a4bd
fix(mcp): make `make mcp-deploy` resolve build-constraints from repo …
vb-dbrks Jun 26, 2026
45dc2f6
docs(mcp): pin Feature lifecycle changelog links to released version
vb-dbrks Jun 26, 2026
1689228
ci(mcp): upload MCP integration test coverage to Codecov
vb-dbrks Jun 26, 2026
6cfb1ef
test(mcp): authenticate /mcp E2E with OAuth M2M (front-door requires it)
vb-dbrks Jun 26, 2026
38ab4ff
Added fix per reqvire comments
souravg-db2 Jun 29, 2026
9cdbaa7
Merge branch 'main' into dqx/mcp
mwojtyczka Jun 30, 2026
f79922d
mcp-server: enforce caller perms, narrow CORS, run the runner job as …
vb-dbrks Jul 1, 2026
af8438b
Merge branch 'main' into dqx/mcp
mwojtyczka Jul 1, 2026
53c4060
mcp-server: pass runner SP in ci_deploy.sh; fix docs mermaid diagram
vb-dbrks Jul 1, 2026
e94b01a
mcp-server: grant runner SP read on the bundle artifact path (wheel i…
vb-dbrks Jul 1, 2026
7e51e2c
added change to move the wheel file to volume
souravg-db2 Jul 2, 2026
9158e36
mcp-server: per-user output schemas, drop write pre-check & catalog s…
vb-dbrks Jul 3, 2026
9fca2cd
mcp-server: fix integration-test lint/type check
vb-dbrks Jul 3, 2026
cf52a82
mcp-server: fix run-result IDOR + temp-view leak, add mcp-destroy
vb-dbrks Jul 3, 2026
9d75d79
mcp-server: address Isaac Review findings on the run-result/IDOR changes
vb-dbrks Jul 3, 2026
bdc8f4b
mcp-server: wire runner SP for CI integration + fix fmt (unused import)
vb-dbrks Jul 3, 2026
3ebca4c
mcp-server: run the CI runner job as the deploying identity (fix run_…
vb-dbrks Jul 3, 2026
6ec81a2
mcp: don't upload code coverage from the integration workflow
vb-dbrks Jul 7, 2026
c66e63a
Merge branch 'main' into dqx/mcp
vb-dbrks Jul 7, 2026
eef6c72
Merge branch 'main' into dqx/mcp
mwojtyczka Jul 16, 2026
808a430
Merge branch 'main' into dqx/mcp
mwojtyczka Jul 16, 2026
1ca11f6
Merge branch 'main' into dqx/mcp
mwojtyczka Jul 17, 2026
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
98 changes: 98 additions & 0 deletions .github/workflows/mcp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# MCP server integration tests.
#
# Runs the tests/integration_mcp suite via the shared acceptance harness (same workspace,
# auth, and Model Serving endpoint as the anomaly/integration suites). The suite deploys an
# isolated MCP app from the bundle, drives it as an agent would, and tears it down — see
# tests/integration_mcp/conftest.py. No MCP-specific secrets are needed; auth comes from the
# acceptance action exactly like the other integration workflows.
name: mcp

on:
pull_request:
types: [opened, synchronize, ready_for_review]
paths:
- "mcp-server/**"
- "src/databricks/labs/dqx/**" # the runner installs the in-repo DQX wheel
- "tests/integration_mcp/**"
- ".github/workflows/mcp.yml"
merge_group:
types: [checks_requested]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false # let teardown finish so the isolated app/bundle is removed

jobs:
# Gate downstream work: fork PRs (no access to the tool environment) and drafts do not run.
not-a-fork:
runs-on:
group: databrickslabs-protected-runner-group
labels: linux-ubuntu-latest
if: github.event_name == 'pull_request' && !github.event.pull_request.draft && !github.event.pull_request.head.repo.fork
steps:
- run: echo "Not a fork PR, proceeding"

mcp-tests:
needs: not-a-fork
environment: tool
runs-on:
group: larger-runners
labels: larger
permissions:
id-token: write
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0

- name: Setup environment
uses: ./.github/actions/setup-env

- name: Pre-build DQX wheel
uses: ./.github/actions/prebuild-wheel

- name: Prepare code coverage configuration for MCP tests
run: |
cat > tests/integration_mcp/.coveragerc << EOF
[run]
source =
../../mcp-server/server
../../mcp-server/runner/src/dqx_mcp_runner
relative_files = true
parallel = true
EOF

# Required for the DAB (Databricks Asset Bundle) deploy the MCP suite runs (ci_deploy.sh).
# The CLI lands on PATH (via GITHUB_PATH) for the acceptance action's test subprocess.
- name: Install Databricks CLI
uses: databricks/setup-cli@596b0a354ba14aa59921aca1b02bd67c2b0a81a5 # v0.297.2

- name: Run MCP integration tests
uses: databrickslabs/sandbox/acceptance@83461e5dd7021feabb1a9ca3ee10d6f46b72092a # acceptance/v0.4.6
with:
vault_uri: ${{ secrets.VAULT_URI }}
timeout: 1h
codegen_path: tests/integration_mcp/.codegen.json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
# The runner job's run_as identity: a workspace SP with workspace + consumer access to the
# CI workspace. ci_deploy.sh requires this (bundle run_as); without it the deploy aborts.
DQX_MCP_RUNNER_SERVICE_PRINCIPAL_ID: ${{ secrets.TOOLS_CLIENT_ID }}
COVERAGE_FILE: ${{ github.workspace }}/.coverage # make sure the coverage report is preserved

- name: Merge coverage reports and convert them to XML
run: make combine-coverage

- name: Publish test coverage
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
use_oidc: true
files: coverage-combined.xml
flags: mcp
20 changes: 20 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,23 @@ jobs:
- name: Run app backend tests
run: make app-test
working-directory: .

mcp:
needs: not-a-fork
name: Build and Check MCP Server
runs-on:
group: databrickslabs-protected-runner-group
labels: linux-ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup environment
uses: ./.github/actions/setup-env

- name: Type-check MCP server
run: make mcp-check

- name: Run MCP server tests
run: make mcp-test
71 changes: 71 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ perf: ## Run performance benchmarks (long timeout)
anomaly: ## Run anomaly integration tests (long timeout, with reruns)
$(UV_RUN) pytest tests/integration_anomaly/ -v -n 10 --timeout 1200 --durations 20 --reruns 2 --reruns-delay 5

# The integration test's deploy (ci_deploy.sh -> bundle deploy) builds the artifact wheels with
# `uv build ../` from inside mcp-server/, so the global *relative* UV_BUILD_CONSTRAINT
# (.build-constraints.txt) would resolve against the wrong directory and fail with
# "File not found: .build-constraints.txt". Pin it to the absolute repo-root path (same fix as
# mcp-deploy). CI doesn't hit this — it runs via the acceptance harness, not make.
mcp-integration: export UV_BUILD_CONSTRAINT := $(CURDIR)/.build-constraints.txt
mcp-integration: ## Run MCP server integration tests (deploys an isolated app; requires workspace auth + Databricks CLI)
$(UV_RUN) pytest tests/integration_mcp/ -v --timeout 1800 --durations 10

coverage: ## Run all tests (excl. e2e/perf) and open HTML coverage report
$(UV_TEST) --ignore=tests/e2e --ignore=tests/perf --cov --cov-report=html tests/
open htmlcov/index.html
Expand Down Expand Up @@ -177,6 +186,60 @@ app-test: ## Run app backend pytest suite (K=<expr> filter, COV=1 for coverage)
$(if $(K),-k "$(K)") \
$(if $(COV),--cov=src/databricks_labs_dqx_app/backend --cov-report=term-missing --cov-report=xml:coverage-app.xml)

# Run the MCP server's unit-test suite (pytest, no Databricks/Spark dependencies).
# Usage: make mcp-test # run everything
# make mcp-test K=expr # forward -k filter to pytest
#
# pytest is not declared in mcp-server's pyproject/uv.lock — the suite only
# needs anyio, which ships transitively. With UV_FROZEN=1 we can't add pytest
# to the lock from here, so inject it ephemerally with ``uv run --with pytest``
# over the synced runtime env (anyio's pytest plugin handles the async tests).
mcp-test: ## Run MCP server pytest suite (K=<expr> filter)
cd mcp-server && uv run --with pytest pytest tests/ \
$(if $(K),-k "$(K)")

# Static type-check the MCP server (parity with app-check). basedpyright is not in mcp-server's
# pyproject/uv.lock (like pytest), so inject it ephemerally with ``uv run --with basedpyright`` over
# the synced runtime env. Scoped to ``server`` (the app package): the runner sub-project's
# pyspark/dqx deps aren't in this env — it type-checks separately as its own wheel. ``standard`` mode
# + ``--level error`` config lives in mcp-server/pyproject.toml [tool.basedpyright].
mcp-check: ## Type-check the MCP server with basedpyright (standard mode, errors only)
cd mcp-server && uv run --with basedpyright basedpyright --level error server

# One-command MCP server deploy (parity with app-deploy). Ensures the runner-wheel volume
# exists, deploys the bundle (app + runner job + setup job), runs the one-time setup job (UC
# grants + temp-schema ownership), then deploys & starts the app via ``bundle run``. The catalog
# is passed as CATALOG below and injected into the app as the DQX_CATALOG config value (no secret).
#
# Usage: make mcp-deploy PROFILE=my-profile CATALOG=main
# CATALOG is REQUIRED: workspace.artifact_path is the UC volume /Volumes/<CATALOG>/tmp/dqx_artifacts
# that hosts the runner wheels; it is resolved at deploy time and must pre-exist. It is also the
# catalog the app + runner use for temp views and per-user output schemas.
# TARGET defaults to the bundle's default target (dev); override with TARGET=<t>.
# The bundle artifact build runs `uv build ../` from inside mcp-server/, so the global
# relative UV_BUILD_CONSTRAINT (.build-constraints.txt) would resolve against the wrong
# directory. Pin it to the absolute repo-root path so the wheel build finds it.
mcp-deploy: export UV_BUILD_CONSTRAINT := $(CURDIR)/.build-constraints.txt
mcp-deploy: ## Deploy the MCP server bundle, run setup, and (re)deploy + start the app
@test -n "$(PROFILE)" || (echo "Usage: make mcp-deploy PROFILE=<databricks-profile> CATALOG=<catalog> [TARGET=<bundle-target>] [BUNDLE_VARS=...]"; exit 1)
@test -n "$(CATALOG)" || (echo "Usage: make mcp-deploy PROFILE=<databricks-profile> CATALOG=<catalog> [TARGET=<bundle-target>] [BUNDLE_VARS=...]"; exit 1)
cd mcp-server && DQX_MCP_CATALOG=$(CATALOG) DATABRICKS_PROFILE=$(PROFILE) ./scripts/ensure_artifacts_volume.sh
cd mcp-server && databricks bundle deploy -p $(PROFILE) $(if $(TARGET),-t $(TARGET)) --var catalog_name=$(CATALOG) $(BUNDLE_VARS)
cd mcp-server && databricks bundle run dqx_setup -p $(PROFILE) $(if $(TARGET),-t $(TARGET)) --var catalog_name=$(CATALOG) $(BUNDLE_VARS)
cd mcp-server && databricks bundle run mcp-dqx -p $(PROFILE) $(if $(TARGET),-t $(TARGET)) --var catalog_name=$(CATALOG) $(BUNDLE_VARS)

# One-command teardown (nothing in the MCP bundle is destroy-protected, so — unlike the Studio's
# multi-step unbind-then-destroy uninstall — this is a single command). Removes the app + runner/
# setup jobs, then drops the out-of-band runner-wheel volume. Leaves the <catalog>.tmp schema and any
# dqx_mcp_<user> output schemas intact (they may hold user data) — drop those manually if you want a
# fully clean wipe. runner_service_principal_id isn't needed to destroy, so its placeholder is fine.
mcp-destroy: ## Tear down the MCP server (app + jobs + runner-wheel volume). Requires PROFILE + CATALOG
@test -n "$(PROFILE)" || (echo "Usage: make mcp-destroy PROFILE=<databricks-profile> CATALOG=<catalog> [TARGET=<bundle-target>]"; exit 1)
@test -n "$(CATALOG)" || (echo "Usage: make mcp-destroy PROFILE=<databricks-profile> CATALOG=<catalog> [TARGET=<bundle-target>]"; exit 1)
cd mcp-server && databricks bundle destroy -p $(PROFILE) $(if $(TARGET),-t $(TARGET)) --auto-approve --var catalog_name=$(CATALOG) $(BUNDLE_VARS)
cd mcp-server && databricks volumes delete $(CATALOG).tmp.dqx_artifacts -p $(PROFILE) 2>/dev/null || true
@echo "Destroyed the MCP app + jobs and the dqx_artifacts volume. The $(CATALOG).tmp schema and any $(CATALOG).dqx_mcp_<user> output schemas are left intact — drop them manually if you want a full wipe."

##@ App deploy (require PROFILE=<databricks-profile>; most also need TARGET=<bundle-target>)

# Minimum Databricks CLI version required to deploy. The ``postgres_roles``
Expand Down Expand Up @@ -307,6 +370,14 @@ lock-app-dependencies: ## Regenerate app/uv.lock, app/yarn.lock, app/.build-cons
uv pip compile --generate-hashes --universal --no-header - > app/build-constraints-new.txt
mv app/build-constraints-new.txt app/.build-constraints.txt

# Regenerate the MCP server lockfile and scrub private-proxy URLs so the
# committed file resolves against whatever registry the install environment
# is configured for (JFrog in CI, public in fork PRs).
lock-mcp-dependencies: export UV_FROZEN := 0
lock-mcp-dependencies: ## Regenerate mcp-server/uv.lock
cd mcp-server && uv lock --exclude-newer "7 days"
perl -pi -e 's|registry = "https://[^"]*"|registry = "https://pypi.org/simple"|g' mcp-server/uv.lock

lock-dependencies: export UV_FROZEN := 0
lock-dependencies: ## Regenerate top-level uv.lock and .build-constraints.txt
uv lock --exclude-newer "7 days"
Expand Down
Loading
Loading