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
1 change: 1 addition & 0 deletions .nextchanges/bundles/ai-gateway-mcp-service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Add bundle support for the AI Gateway `mcp_service` resource (direct engine). ([#6633](https://github.com/databricks/cli/pull/6633))
9 changes: 9 additions & 0 deletions acceptance/bundle/deployment/bind/mcp_service/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
bundle:
name: test-bundle

resources:
mcp_services:
mcp1:
parent: schemas/main.myschema
mcp_service_id: mysvc
comment: bound service
3 changes: 3 additions & 0 deletions acceptance/bundle/deployment/bind/mcp_service/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions acceptance/bundle/deployment/bind/mcp_service/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

>>> [CLI] bundle deployment bind mcp1 main.myschema.mysvc --auto-approve
Successfully bound mcp_service with an id 'main.myschema.mysvc'
Run 'bundle deploy' to deploy changes to your workspace

>>> [CLI] bundle summary
Name: test-bundle
Target: default
Workspace:
User: [USERNAME]
Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
Resources:
MCP services:
mcp1:
Name: mysvc
URL: [DATABRICKS_URL]/explore/data/mcp-services/main/myschema/mysvc?w=[NUMID]

>>> [CLI] bundle deployment unbind mcp1

>>> [CLI] bundle summary
Name: test-bundle
Target: default
Workspace:
User: [USERNAME]
Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
Resources:
MCP services:
mcp1:
Name: mysvc
URL: (not deployed)
5 changes: 5 additions & 0 deletions acceptance/bundle/deployment/bind/mcp_service/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
trace $CLI bundle deployment bind mcp1 main.myschema.mysvc --auto-approve
trace $CLI bundle summary

trace $CLI bundle deployment unbind mcp1
trace $CLI bundle summary
18 changes: 18 additions & 0 deletions acceptance/bundle/deployment/bind/mcp_service/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# AI Gateway securables are direct-engine only.
EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"]

Cloud = false

Ignore = [
".databricks",
]

# The bind flow issues a GET to confirm the remote resource exists before binding.
[[Server]]
Pattern = "GET /api/2.1/unity-catalog/mcp-services/{name}"
Response.Body = '''
{
"name": "mcp-services/main.myschema.mysvc",
"comment": "bound service"
}
'''
9 changes: 9 additions & 0 deletions acceptance/bundle/invariant/configs/mcp_service.yml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
bundle:
name: test-bundle-$UNIQUE_NAME

resources:
mcp_services:
foo:
parent: schemas/main.default
mcp_service_id: test-mcp-service-$UNIQUE_NAME
comment: test mcp service
17 changes: 17 additions & 0 deletions acceptance/bundle/python/mcp_services-support/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
bundle:
name: my_project

sync: {paths: []} # don't need to copy files

python:
resources:
- "resources:load_resources"
mutators:
- "mutators:update_mcp_service"

resources:
mcp_services:
my_mcp_service_1:
parent: "schemas/main.default"
mcp_service_id: "my_mcp_service_1"
comment: "My MCP service"
11 changes: 11 additions & 0 deletions acceptance/bundle/python/mcp_services-support/mutators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from dataclasses import replace

from databricks.bundles.mcp_services import McpService
from databricks.bundles.core import mcp_service_mutator


@mcp_service_mutator
def update_mcp_service(mcp_service: McpService) -> McpService:
assert isinstance(mcp_service.comment, str)

return replace(mcp_service, comment=f"{mcp_service.comment} (updated)")
4 changes: 4 additions & 0 deletions acceptance/bundle/python/mcp_services-support/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions acceptance/bundle/python/mcp_services-support/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

>>> uv run [UV_ARGS] -q [CLI] bundle validate --output json
{
"experimental": {
"python": {
"mutators": [
"mutators:update_mcp_service"
],
"resources": [
"resources:load_resources"
]
}
},
"resources": {
"mcp_services": {
"my_mcp_service_1": {
"comment": "My MCP service (updated)",
"mcp_service_id": "my_mcp_service_1",
"parent": "schemas/main.default"
},
"my_mcp_service_2": {
"comment": "My MCP service (2) (updated)",
"mcp_service_id": "my_mcp_service_2",
"parent": "schemas/main.default"
}
}
}
}
16 changes: 16 additions & 0 deletions acceptance/bundle/python/mcp_services-support/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from databricks.bundles.core import Resources


def load_resources() -> Resources:
resources = Resources()

resources.add_mcp_service(
"my_mcp_service_2",
{
"parent": "schemas/main.default",
"mcp_service_id": "my_mcp_service_2",
"comment": "My MCP service (2)",
},
)

return resources
5 changes: 5 additions & 0 deletions acceptance/bundle/python/mcp_services-support/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

trace uv run $UV_ARGS -q $CLI bundle validate --output json | \
jq "pick(.experimental.python, .resources)"

rm -fr .databricks __pycache__
4 changes: 4 additions & 0 deletions acceptance/bundle/python/mcp_services-support/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Cloud = false # tests don't interact with APIs

# mcp_services are only supported in the current version of the wheel
EnvMatrix.PYDAB_VERSION = ["current"]
21 changes: 21 additions & 0 deletions acceptance/bundle/refschema/out.fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2082,6 +2082,27 @@ resources.jobs.*.permissions[*].group_name string ALL
resources.jobs.*.permissions[*].level iam.PermissionLevel ALL
resources.jobs.*.permissions[*].service_principal_name string ALL
resources.jobs.*.permissions[*].user_name string ALL
resources.mcp_services.*.comment string ALL
resources.mcp_services.*.config *catalog.McpServiceConfig ALL
resources.mcp_services.*.config.include_tool_selectors []string ALL
resources.mcp_services.*.config.include_tool_selectors[*] string ALL
resources.mcp_services.*.config.rate_limits []catalog.RateLimit ALL
resources.mcp_services.*.config.rate_limits[*] catalog.RateLimit ALL
resources.mcp_services.*.config.rate_limits[*].key catalog.RateLimitRateLimitKey ALL
resources.mcp_services.*.config.rate_limits[*].principal string ALL
resources.mcp_services.*.config.rate_limits[*].renewal_period catalog.RateLimitRateLimitRenewalPeriod ALL
resources.mcp_services.*.config.rate_limits[*].requests int64 ALL
resources.mcp_services.*.config.rate_limits[*].tokens int64 ALL
resources.mcp_services.*.config.source_connection *catalog.McpServiceConfigSourceConnection ALL
resources.mcp_services.*.config.source_connection.is_deleted bool ALL
resources.mcp_services.*.config.source_connection.name string ALL
resources.mcp_services.*.id string INPUT
resources.mcp_services.*.lifecycle resources.Lifecycle INPUT
resources.mcp_services.*.lifecycle.prevent_destroy bool INPUT
resources.mcp_services.*.mcp_service_id string ALL
resources.mcp_services.*.modified_status string INPUT
resources.mcp_services.*.parent string ALL
resources.mcp_services.*.url string INPUT
resources.model_services.*.comment string ALL
resources.model_services.*.config *catalog.ModelServiceConfig ALL
resources.model_services.*.config.inference_table *catalog.InferenceTableConfig ALL
Expand Down
9 changes: 9 additions & 0 deletions acceptance/bundle/resources/mcp_services/basic/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
bundle:
name: test-bundle

resources:
mcp_services:
mcp1:
parent: schemas/main.myschema
mcp_service_id: myservice
comment: COMMENT1
3 changes: 3 additions & 0 deletions acceptance/bundle/resources/mcp_services/basic/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 92 additions & 0 deletions acceptance/bundle/resources/mcp_services/basic/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@

=== Initial summary before deploy
>>> [CLI] bundle summary -o json
{
"comment": "COMMENT1",
"mcp_service_id": "myservice",
"modified_status": "created",
"parent": "schemas/main.myschema"
}

=== Verify it does not exist yet
>>> musterr [CLI] ai-gateway get-mcp-service mcp-services/main.myschema.myservice
Error: Resource catalog.McpService not found: main.myschema.myservice

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files...
Created mcp_services.mcp1
Files: 3 uploaded, 0 deleted
Resources: 1 created, 0 changed, 0 deleted, 0 unchanged

>>> print_requests.py //unity
{
"method": "POST",
"path": "/api/2.1/unity-catalog/mcp-services",
"q": {
"mcp_service_id": "myservice",
"parent": "schemas/main.myschema"
},
"body": {
"comment": "COMMENT1"
}
}

=== Summary should show the id and the Catalog Explorer url
>>> [CLI] bundle summary -o json
{
"id": "main.myschema.myservice",
"url": "[DATABRICKS_URL]/explore/data/mcp-services/main/myschema/myservice?w=[NUMID]"
}

=== Verify deployment
>>> [CLI] ai-gateway get-mcp-service mcp-services/main.myschema.myservice
{
"name": "mcp-services/main.myschema.myservice",
"comment": "COMMENT1"
}
Comment on lines +42 to +46

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printing the whole API response here is going to cause the golden files to drift over time as more fields get added -> let's just assert the fields that you want to ensure the resource got deployed as expected.

I think this is also in model_services so if you could clean that up as a follow-up that'd be great :-)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are applying filtering to { "name", "comment" } through jq, so I think we should be good. Model service is already clean, mcp and mps are too now

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, somehow missed that you use jq already


=== Update comment (should update in place, not recreate)
>>> update_file.py databricks.yml COMMENT1 COMMENT2

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files...
Updated mcp_services.mcp1
Files: 1 uploaded, 0 deleted
Resources: 0 created, 1 changed, 0 deleted, 0 unchanged

>>> print_requests.py //unity
{
"method": "PATCH",
"path": "/api/2.1/unity-catalog/mcp-services/main.myschema.myservice",
"q": {
"update_mask": "*"
},
"body": {
"comment": "COMMENT2"
}
}

>>> [CLI] ai-gateway get-mcp-service mcp-services/main.myschema.myservice
"COMMENT2"

=== Change an immutable field (should plan a recreate)
>>> update_file.py databricks.yml myservice myservice-renamed

>>> [CLI] bundle plan
recreate mcp_services.mcp1

Plan: 1 to add, 0 to change, 1 to delete, 0 unchanged

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete resources.mcp_services.mcp1

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default

Destroy: 1 deleted

>>> print_requests.py //unity
{
"method": "DELETE",
"path": "/api/2.1/unity-catalog/mcp-services/main.myschema.myservice"
}
29 changes: 29 additions & 0 deletions acceptance/bundle/resources/mcp_services/basic/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
title "Initial summary before deploy"
trace $CLI bundle summary -o json | jq .resources.mcp_services.mcp1

title "Verify it does not exist yet"
trace musterr $CLI ai-gateway get-mcp-service mcp-services/main.myschema.myservice

trace $CLI bundle deploy
trace print_requests.py //unity

title "Summary should show the id and the Catalog Explorer url"
trace $CLI bundle summary -o json | jq ".resources.mcp_services.mcp1 | {id, url}"

title "Verify deployment"
trace $CLI ai-gateway get-mcp-service mcp-services/main.myschema.myservice | jq '{name, comment}'

title "Update comment (should update in place, not recreate)"
trace update_file.py databricks.yml COMMENT1 COMMENT2
trace $CLI bundle deploy
trace print_requests.py //unity
trace $CLI ai-gateway get-mcp-service mcp-services/main.myschema.myservice | jq .comment

title "Change an immutable field (should plan a recreate)"
trace update_file.py databricks.yml myservice myservice-renamed
trace $CLI bundle plan

trace $CLI bundle destroy --auto-approve
trace print_requests.py //unity

rm -f out.requests.txt
7 changes: 7 additions & 0 deletions acceptance/bundle/resources/mcp_services/basic/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Local only: this test inspects the recorded request stream (print_requests),
# which has no equivalent against a real workspace.
Cloud = false

Ignore = [
".databricks",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
bundle:
name: deploy-mcp-service-test-$UNIQUE_NAME

resources:
mcp_services:
mcp:
parent: schemas/main.default
mcp_service_id: test_mcp_$UNIQUE_NAME
comment: "Points at a UC connection hosting an MCP server"
config:
source_connection:
name: connections/main.default.mcp_conn_$UNIQUE_NAME

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions acceptance/bundle/resources/mcp_services/lifecycle/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/deploy-mcp-service-test-[UNIQUE_NAME]/default/files...
Created mcp_services.mcp
Files: 4 uploaded, 0 deleted
Resources: 1 created, 0 changed, 0 deleted, 0 unchanged

>>> [CLI] ai-gateway get-mcp-service mcp-services/main.default.test_mcp_[UNIQUE_NAME]
{
"name": "mcp-services/main.default.test_mcp_[UNIQUE_NAME]",
"comment": "Points at a UC connection hosting an MCP server"
}

>>> [CLI] bundle plan
Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete resources.mcp_services.mcp

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/deploy-mcp-service-test-[UNIQUE_NAME]/default

Destroy: 1 deleted
Loading
Loading