<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic USE CONNECTION bypasses your MCP Service's Tool Selection and Policies in Community Articles</title>
    <link>https://community.databricks.com/t5/community-articles/use-connection-bypasses-your-mcp-service-s-tool-selection-and/m-p/169770#M1605</link>
    <description>&lt;P&gt;I registered a mock MCP server in Unity Catalog, exposed only its read tool, attached a deny policy, and then called the delete tool anyway. What decided the outcome was access to the underlying connection, and it came from grants on the schema, not on the service.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="orange-rule.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31489iAE4C6A56504062ED/image-size/large?v=v2&amp;amp;px=999" role="button" title="orange-rule.png" alt="orange-rule.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;WHAT THE EXPERIMENT SHOWED&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;Tool selection and service policies only govern calls &lt;STRONG&gt;through the MCP Service&lt;/STRONG&gt;. An identity with &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; called the blocked delete tool through the connection proxy.&lt;/LI&gt;&lt;LI&gt;On a &lt;STRONG&gt;schema-level connection&lt;/STRONG&gt;, &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; on the schema or &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the catalog also opened the proxy. A &lt;STRONG&gt;metastore-level connection&lt;/STRONG&gt; opened only for a grant on the connection itself.&lt;/LI&gt;&lt;LI&gt;Proxied calls are audited as &lt;FONT face="courier new, courier, monospace"&gt;ucHttpConnectionProxiedRequest&lt;/FONT&gt;, with the caller but &lt;STRONG&gt;no tool name&lt;/STRONG&gt;.&lt;/LI&gt;&lt;/UL&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;An MCP Service looks like the natural security boundary for an agent tool. You pick which tools it exposes, attach service policies, grant &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt;, and Unity Gateway logs each call. All of that applies to calls that go through the service. The external server also sits behind a Unity Catalog HTTP connection, and the connection has its own entry point.&lt;/P&gt;&lt;P&gt;Databricks says so plainly in the registration guide:&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;&lt;EM&gt;"Invoking an MCP Service requires no privilege on the underlying connection—&lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on the MCP Service is sufficient. Don't grant &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; to end users: it lets them call the external server directly through the connection, or register their own MCP Service on it, bypassing the tool selection, service policies, and auditing of your MCP Service. Reserve connection access for service authors and administrators."&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;— &lt;A href="https://docs.databricks.com/aws/en/ai-gateway/register-mcp-service" target="_blank" rel="noopener"&gt;Register an external MCP server&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;I wanted to see what each part of that warning looks like in practice.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="A caller reaches the same external MCP server by two paths: the MCP Service path checks EXECUTE, tool selection and the service policy; the connection proxy path checks USE CONNECTION only. Both arrive with the same bearer token." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31487i631A2E5EEB04CF54/image-size/large?v=v2&amp;amp;px=999" role="button" title="two-paths.png" alt="two-paths.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 1.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;Two paths to one server. &lt;FONT face="courier new, courier, monospace"&gt;POST /ai-gateway/mcp-services/&amp;lt;name&amp;gt;&lt;/FONT&gt; runs the service's tool selection, policies and &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; audit. &lt;FONT face="courier new, courier, monospace"&gt;POST /api/2.0/unity-catalog/connections/&amp;lt;name&amp;gt;/proxy&lt;/FONT&gt; checks connection privileges and forwards the request; none of the service controls run.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;The setup&lt;/FONT&gt;&lt;/H2&gt;&lt;UL&gt;&lt;LI&gt;A small FastMCP server with two tools: &lt;FONT face="courier new, courier, monospace"&gt;get_inventory&lt;/FONT&gt; (read) and &lt;FONT face="courier new, courier, monospace"&gt;delete_inventory_record&lt;/FONT&gt;, a mock write that returns &lt;FONT face="courier new, courier, monospace"&gt;"deleted": true&lt;/FONT&gt; and deletes nothing. Bearer-token protected, published through a temporary Cloudflare tunnel.&lt;/LI&gt;&lt;LI&gt;A schema-level HTTP connection, &lt;FONT face="courier new, courier, monospace"&gt;workspace.mcp_boundary.inventory_conn&lt;/FONT&gt;, which is the placement the docs recommend.&lt;/LI&gt;&lt;LI&gt;An MCP Service, &lt;FONT face="courier new, courier, monospace"&gt;inventory_readonly&lt;/FONT&gt;, with &lt;FONT face="courier new, courier, monospace"&gt;include_tool_selectors: ["get_*"]&lt;/FONT&gt;.&lt;/LI&gt;&lt;LI&gt;A custom service policy (Beta) that returns &lt;FONT face="courier new, courier, monospace"&gt;DENY&lt;/FONT&gt; when the tool is &lt;FONT face="courier new, courier, monospace"&gt;delete_inventory_record&lt;/FONT&gt;.&lt;/LI&gt;&lt;LI&gt;Three service principals calling with OAuth machine-to-machine tokens:&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;A&lt;/STRONG&gt; has &lt;FONT face="courier new, courier, monospace"&gt;USE CATALOG&lt;/FONT&gt;, &lt;FONT face="courier new, courier, monospace"&gt;USE SCHEMA&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on the service.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;B&lt;/STRONG&gt; has the same, plus &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt;.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;C&lt;/STRONG&gt; has &lt;FONT face="courier new, courier, monospace"&gt;USE CATALOG&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; on the schema, and nothing granted on the service or the connection.&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Each identity sent &lt;FONT face="courier new, courier, monospace"&gt;tools/list&lt;/FONT&gt;, a read and a delete down both paths.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="The MCP Service in Catalog Explorer with one tool listed: get_inventory." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31482iEA9109B6A94CF76B/image-size/large?v=v2&amp;amp;px=999" role="button" title="service-tools.jpg" alt="service-tools.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 2.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;The service as its consumers see it: one tool, &lt;FONT face="courier new, courier, monospace"&gt;get_inventory&lt;/FONT&gt;. The server behind it has two.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;What each identity could reach&lt;/FONT&gt;&lt;/H2&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;Identity&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Service: tools/list&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Service: delete&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Proxy: tools/list&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Proxy: delete&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;A (EXECUTE only)&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;get_inventory&lt;/TD&gt;&lt;TD&gt;-32003 blocked&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;B (+ USE CONNECTION)&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;get_inventory&lt;/TD&gt;&lt;TD&gt;-32003 blocked&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;C (ALL PRIVILEGES on schema)&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;get_inventory&lt;/TD&gt;&lt;TD&gt;-32003 blocked&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;Through the service, all three saw one tool, and the delete came back with the documented error, &lt;FONT face="courier new, courier, monospace"&gt;-32003 "Tool not allowed by MCP service configuration."&lt;/FONT&gt; On the proxy they split. A got &lt;FONT face="courier new, courier, monospace"&gt;403 PERMISSION_DENIED&lt;/FONT&gt;. B and C listed both tools and ran the delete:&lt;/P&gt;&lt;PRE&gt;=== User B: EXECUTE + USE CONNECTION ===
MCP Service (Unity Gateway)  tools/list                    HTTP 200  tools=['get_inventory']
MCP Service (Unity Gateway)  call delete_inventory_record  HTTP 200  {"code": -32003, "message": "Tool not allowed by MCP service configuration."}
Connection proxy (direct)    tools/list                    HTTP 200  tools=['get_inventory', 'delete_inventory_record']
Connection proxy (direct)    call delete_inventory_record  HTTP 200  result={"sku":"SKU-001","deleted":true,"note":"MOCK - nothing was actually deleted"}&lt;/PRE&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;The policy never sees the proxy&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;For a second run I widened the tool selection to every tool, so the deny policy was the only guard left. Through the service, both A and B got the policy's reason back as a JSON-RPC error: &lt;FONT face="courier new, courier, monospace"&gt;-32003 "Deleting inventory records is not permitted through this service."&lt;/FONT&gt; B then sent the same delete to the proxy and got &lt;FONT face="courier new, courier, monospace"&gt;"deleted": true&lt;/FONT&gt;. The policy ran on the path it is attached to. Nothing consulted it on the other path. (The policy docs describe a block as an HTTP 200 response carrying a &lt;FONT face="courier new, courier, monospace"&gt;databricks_service_policy&lt;/FONT&gt; object; through this MCP Service it arrived as the JSON-RPC error above.)&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="The Policies tab of the MCP Service showing deny_inventory_delete applied to all account users." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31481i710109200EE26EE5/image-size/large?v=v2&amp;amp;px=999" role="button" title="service-policy.jpg" alt="service-policy.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 3.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;The deny policy is attached to the service and applies to all account users. It does not apply to the connection.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;USE CONNECTION can come from the schema&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;User C mattered more than I expected. Nobody granted C &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt;. C held &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; on the schema that contains the connection, and its proxy calls went through exactly as B's did. When I revoked that grant and left C with &lt;FONT face="courier new, courier, monospace"&gt;USE SCHEMA&lt;/FONT&gt; only, the proxy returned &lt;FONT face="courier new, courier, monospace"&gt;403&lt;/FONT&gt; and the service returned &lt;FONT face="courier new, courier, monospace"&gt;-32007 "Not authorized to invoke MCP service."&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;This follows from the docs' own advice. They recommend creating the connection at the schema level "so it's governed alongside the MCP Service." In this workspace the schema-level connection picked up schema grants, so &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; or &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the schema reached the connection. The schema was also the only place I could grant it: &lt;FONT face="courier new, courier, monospace"&gt;GRANT … ON CONNECTION&lt;/FONT&gt; with the three-part name did not parse, and the connection's Permissions tab returned an error. Here, a review that only read the connection's own permission list would have missed both grants.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Schema permissions: A has USE SCHEMA; C has ALL PRIVILEGES; B has CREATE SERVICE, USE CONNECTION and USE SCHEMA." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31483i6241D92238A0EE4D/image-size/large?v=v2&amp;amp;px=999" role="button" title="schema-grants.jpg" alt="schema-grants.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 4.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;Grants on the schema, not on the connection. Two of these rows open the proxy path.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;Where the connection lives decides who inherits it&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;To check that placement was the cause, I created a second connection to the same server at the metastore level, with its own MCP Service limited to &lt;FONT face="courier new, courier, monospace"&gt;get_*&lt;/FONT&gt;. I also added User D, who has &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the catalog and &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on both services. Then I ran every identity against both connections.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Nested view of metastore, catalog and schema: the schema-level connection opened for B, C and D; the metastore-level connection opened only for B." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31488i746F77DD59E85D9F/image-size/large?v=v2&amp;amp;px=999" role="button" title="placement.png" alt="placement.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 5.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;The same grants, two placements. Grants on the schema and catalog reached the connection inside them and stopped at the one outside.&lt;/EM&gt;&lt;/P&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;Identity&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Grant that could open the proxy&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Schema-level connection&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Metastore-level connection&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;A&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;none&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;B&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the schema; on the metastore connection directly&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools, deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools, deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;C&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; on the schema&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools, deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;D&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the catalog&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools, deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;Through either MCP Service, all four saw only &lt;FONT face="courier new, courier, monospace"&gt;get_inventory&lt;/FONT&gt;, and the delete returned &lt;FONT face="courier new, courier, monospace"&gt;-32003&lt;/FONT&gt;. On the proxy, the schema-level connection accepted grants from its schema and its catalog. The metastore-level connection accepted only the grant made on the connection itself, and refused everyone else with &lt;FONT face="courier new, courier, monospace"&gt;User does not have any privileges on Connection 'mcp_boundary_ms_conn'&lt;/FONT&gt;. The docs' reason for recommending the schema level is that the connection is then governed alongside the MCP Service. The same placement lets connection access arrive from the schema and the catalog above it.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Permissions of the metastore-level connection: one row, mcp-exp-user-b with USE CONNECTION." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31486i42B1CD53F182857E/image-size/large?v=v2&amp;amp;px=999" role="button" title="ms-conn-grants.jpg" alt="ms-conn-grants.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 6.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;At the metastore level the connection has its own permission list, and it was the only grant that opened the proxy.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;The second bypass: your own MCP Service&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;The warning also covers registering another MCP Service on the same connection. That needs &lt;FONT face="courier new, courier, monospace"&gt;CREATE SERVICE&lt;/FONT&gt; too. B's first attempt failed with &lt;FONT face="courier new, courier, monospace"&gt;User does not have CREATE SERVICE on Schema&lt;/FONT&gt;. After I added that grant, B created &lt;FONT face="courier new, courier, monospace"&gt;shadow_b&lt;/FONT&gt; with no tool selection; it listed both tools and ran the delete. As the schema owner, and not a metastore admin, my list of MCP services in the schema still returned only &lt;FONT face="courier new, courier, monospace"&gt;inventory_readonly&lt;/FONT&gt;. C also created a service, but calls through it failed with &lt;FONT face="courier new, courier, monospace"&gt;Insufficient privileges on connection … to vend credentials&lt;/FONT&gt;. I can't explain that difference yet.&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;What the logs show&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;The mock server can't tell the callers apart. A's governed read and B's direct delete arrived with the same header set: &lt;FONT face="courier new, courier, monospace"&gt;User-Agent: Databricks-MCP-Proxy/1.0&lt;/FONT&gt;, the connection's one bearer token, and no header naming the caller or the path. Calls blocked by tool selection or the policy never reached it.&lt;/P&gt;&lt;P&gt;Databricks does record both paths in &lt;FONT face="courier new, courier, monospace"&gt;system.access.audit&lt;/FONT&gt;, at different levels of detail. A governed call is an &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; row with the service name and &lt;FONT face="courier new, courier, monospace"&gt;tool_name&lt;/FONT&gt;. B's blocked delete is an &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; row with status &lt;FONT face="courier new, courier, monospace"&gt;403&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;tool_name&lt;/FONT&gt; &lt;FONT face="courier new, courier, monospace"&gt;delete_inventory_record&lt;/FONT&gt;, even though the caller received HTTP 200 with a JSON-RPC error. A proxied call is a &lt;FONT face="courier new, courier, monospace"&gt;ucHttpConnectionProxiedRequest&lt;/FONT&gt; row with the caller and the connection, but no tool name. You can see that B used the connection. You can't see from that row that B ran a delete. Calls through &lt;FONT face="courier new, courier, monospace"&gt;shadow_b&lt;/FONT&gt; appear as &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; rows naming &lt;FONT face="courier new, courier, monospace"&gt;shadow_b&lt;/FONT&gt;. This query surfaces all three patterns. Replace the service name with your own:&lt;/P&gt;&lt;PRE&gt;SELECT event_time,
       coalesce(user_identity.email, user_identity.subject_name) AS caller,
       action_name,
       response.status_code AS status,
       request_params['connection_name'] AS connection_name,
       coalesce(request_params['fully_qualified_name'], request_params['mcp_service_id']) AS mcp_service,
       request_params['tool_name'] AS tool_name
FROM system.access.audit
WHERE event_date &amp;gt;= current_date() - INTERVAL 30 DAYS
  AND (
        action_name = 'ucHttpConnectionProxiedRequest'
     OR action_name = 'createMcpService'
     OR (action_name = 'mcpCall'
         AND coalesce(request_params['fully_qualified_name'], '')
             &amp;lt;&amp;gt; 'workspace.mcp_boundary.inventory_readonly')
  )
ORDER BY event_time DESC&lt;/PRE&gt;&lt;P&gt;In my workspace it returned 30 rows. They included every proxied request (B's and C's succeeded, A's were denied with &lt;FONT face="courier new, courier, monospace"&gt;403&lt;/FONT&gt;), the &lt;FONT face="courier new, courier, monospace"&gt;createMcpService&lt;/FONT&gt; rows for &lt;FONT face="courier new, courier, monospace"&gt;shadow_b&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;shadow_c&lt;/FONT&gt;, and the &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; rows through both. Audit rows appeared roughly 7 to 15 minutes after the calls.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="system.access.audit rows for A and B: mcpCall rows name the tool; ucHttpConnectionProxiedRequest rows name only the connection." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31485i770E783A4FC99D5E/image-size/large?v=v2&amp;amp;px=999" role="button" title="audit-rows.jpg" alt="audit-rows.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 7.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;&lt;FONT face="courier new, courier, monospace"&gt;system.access.audit&lt;/FONT&gt; for A and B during the first run, with identities relabelled. Service calls name the tool. Proxied calls name only the connection.&lt;/EM&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="orange-rule.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31489iAE4C6A56504062ED/image-size/large?v=v2&amp;amp;px=999" role="button" title="orange-rule.png" alt="orange-rule.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;The permission model to use&lt;/FONT&gt;&lt;/H2&gt;&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;Consumers:&lt;/STRONG&gt; &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on the MCP Service, plus &lt;FONT face="courier new, courier, monospace"&gt;USE CATALOG&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;USE SCHEMA&lt;/FONT&gt;. That was all A needed to use the read tool, and it reached nothing else.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Service authors and admins:&lt;/STRONG&gt; &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt;, and &lt;FONT face="courier new, courier, monospace"&gt;CREATE SERVICE&lt;/FONT&gt; where they register services.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;A schema-level connection:&lt;/STRONG&gt; give consumer groups &lt;FONT face="courier new, courier, monospace"&gt;USE SCHEMA&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;USE CATALOG&lt;/FONT&gt; only. &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; or &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on that schema, or &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on its catalog, is connection access. Keep &lt;FONT face="courier new, courier, monospace"&gt;CREATE SERVICE&lt;/FONT&gt; for service authors.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;A metastore-level connection:&lt;/STRONG&gt; it did not inherit the schema or catalog grants I tested, so its own permission list is the one to review.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Reviews:&lt;/STRONG&gt; for a schema-level connection, check grants on the connection, its schema and its catalog. Run the audit query above for proxied requests and for services you didn't create.&lt;/LI&gt;&lt;/UL&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;Conclusion&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;An MCP Service is a real control. In this experiment, tool selection hid the delete tool, the deny policy blocked it, and the audit log recorded the blocked attempt with the tool's name. None of that applied to the connection underneath. Any identity that could use the connection reached the server directly, and on a schema-level connection that included identities whose only relevant grant was on the schema or catalog above it.&lt;/P&gt;&lt;P&gt;So when you review who can reach an MCP server, start with the connection: where it lives, who holds &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on it, and which grants on the levels above it carry that privilege down. Give consumers &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on the service and nothing that adds up to &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt;.&lt;/P&gt;</description>
    <pubDate>Fri, 25 Sep 2026 01:06:55 GMT</pubDate>
    <dc:creator>ivanvyd</dc:creator>
    <dc:date>2026-09-25T01:06:55Z</dc:date>
    <item>
      <title>USE CONNECTION bypasses your MCP Service's Tool Selection and Policies</title>
      <link>https://community.databricks.com/t5/community-articles/use-connection-bypasses-your-mcp-service-s-tool-selection-and/m-p/169770#M1605</link>
      <description>&lt;P&gt;I registered a mock MCP server in Unity Catalog, exposed only its read tool, attached a deny policy, and then called the delete tool anyway. What decided the outcome was access to the underlying connection, and it came from grants on the schema, not on the service.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="orange-rule.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31489iAE4C6A56504062ED/image-size/large?v=v2&amp;amp;px=999" role="button" title="orange-rule.png" alt="orange-rule.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;WHAT THE EXPERIMENT SHOWED&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;Tool selection and service policies only govern calls &lt;STRONG&gt;through the MCP Service&lt;/STRONG&gt;. An identity with &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; called the blocked delete tool through the connection proxy.&lt;/LI&gt;&lt;LI&gt;On a &lt;STRONG&gt;schema-level connection&lt;/STRONG&gt;, &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; on the schema or &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the catalog also opened the proxy. A &lt;STRONG&gt;metastore-level connection&lt;/STRONG&gt; opened only for a grant on the connection itself.&lt;/LI&gt;&lt;LI&gt;Proxied calls are audited as &lt;FONT face="courier new, courier, monospace"&gt;ucHttpConnectionProxiedRequest&lt;/FONT&gt;, with the caller but &lt;STRONG&gt;no tool name&lt;/STRONG&gt;.&lt;/LI&gt;&lt;/UL&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;An MCP Service looks like the natural security boundary for an agent tool. You pick which tools it exposes, attach service policies, grant &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt;, and Unity Gateway logs each call. All of that applies to calls that go through the service. The external server also sits behind a Unity Catalog HTTP connection, and the connection has its own entry point.&lt;/P&gt;&lt;P&gt;Databricks says so plainly in the registration guide:&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;&lt;EM&gt;"Invoking an MCP Service requires no privilege on the underlying connection—&lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on the MCP Service is sufficient. Don't grant &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; to end users: it lets them call the external server directly through the connection, or register their own MCP Service on it, bypassing the tool selection, service policies, and auditing of your MCP Service. Reserve connection access for service authors and administrators."&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;— &lt;A href="https://docs.databricks.com/aws/en/ai-gateway/register-mcp-service" target="_blank" rel="noopener"&gt;Register an external MCP server&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;I wanted to see what each part of that warning looks like in practice.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="A caller reaches the same external MCP server by two paths: the MCP Service path checks EXECUTE, tool selection and the service policy; the connection proxy path checks USE CONNECTION only. Both arrive with the same bearer token." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31487i631A2E5EEB04CF54/image-size/large?v=v2&amp;amp;px=999" role="button" title="two-paths.png" alt="two-paths.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 1.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;Two paths to one server. &lt;FONT face="courier new, courier, monospace"&gt;POST /ai-gateway/mcp-services/&amp;lt;name&amp;gt;&lt;/FONT&gt; runs the service's tool selection, policies and &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; audit. &lt;FONT face="courier new, courier, monospace"&gt;POST /api/2.0/unity-catalog/connections/&amp;lt;name&amp;gt;/proxy&lt;/FONT&gt; checks connection privileges and forwards the request; none of the service controls run.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;The setup&lt;/FONT&gt;&lt;/H2&gt;&lt;UL&gt;&lt;LI&gt;A small FastMCP server with two tools: &lt;FONT face="courier new, courier, monospace"&gt;get_inventory&lt;/FONT&gt; (read) and &lt;FONT face="courier new, courier, monospace"&gt;delete_inventory_record&lt;/FONT&gt;, a mock write that returns &lt;FONT face="courier new, courier, monospace"&gt;"deleted": true&lt;/FONT&gt; and deletes nothing. Bearer-token protected, published through a temporary Cloudflare tunnel.&lt;/LI&gt;&lt;LI&gt;A schema-level HTTP connection, &lt;FONT face="courier new, courier, monospace"&gt;workspace.mcp_boundary.inventory_conn&lt;/FONT&gt;, which is the placement the docs recommend.&lt;/LI&gt;&lt;LI&gt;An MCP Service, &lt;FONT face="courier new, courier, monospace"&gt;inventory_readonly&lt;/FONT&gt;, with &lt;FONT face="courier new, courier, monospace"&gt;include_tool_selectors: ["get_*"]&lt;/FONT&gt;.&lt;/LI&gt;&lt;LI&gt;A custom service policy (Beta) that returns &lt;FONT face="courier new, courier, monospace"&gt;DENY&lt;/FONT&gt; when the tool is &lt;FONT face="courier new, courier, monospace"&gt;delete_inventory_record&lt;/FONT&gt;.&lt;/LI&gt;&lt;LI&gt;Three service principals calling with OAuth machine-to-machine tokens:&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;A&lt;/STRONG&gt; has &lt;FONT face="courier new, courier, monospace"&gt;USE CATALOG&lt;/FONT&gt;, &lt;FONT face="courier new, courier, monospace"&gt;USE SCHEMA&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on the service.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;B&lt;/STRONG&gt; has the same, plus &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt;.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;C&lt;/STRONG&gt; has &lt;FONT face="courier new, courier, monospace"&gt;USE CATALOG&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; on the schema, and nothing granted on the service or the connection.&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Each identity sent &lt;FONT face="courier new, courier, monospace"&gt;tools/list&lt;/FONT&gt;, a read and a delete down both paths.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="The MCP Service in Catalog Explorer with one tool listed: get_inventory." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31482iEA9109B6A94CF76B/image-size/large?v=v2&amp;amp;px=999" role="button" title="service-tools.jpg" alt="service-tools.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 2.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;The service as its consumers see it: one tool, &lt;FONT face="courier new, courier, monospace"&gt;get_inventory&lt;/FONT&gt;. The server behind it has two.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;What each identity could reach&lt;/FONT&gt;&lt;/H2&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;Identity&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Service: tools/list&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Service: delete&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Proxy: tools/list&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Proxy: delete&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;A (EXECUTE only)&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;get_inventory&lt;/TD&gt;&lt;TD&gt;-32003 blocked&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;B (+ USE CONNECTION)&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;get_inventory&lt;/TD&gt;&lt;TD&gt;-32003 blocked&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;C (ALL PRIVILEGES on schema)&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;get_inventory&lt;/TD&gt;&lt;TD&gt;-32003 blocked&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;Through the service, all three saw one tool, and the delete came back with the documented error, &lt;FONT face="courier new, courier, monospace"&gt;-32003 "Tool not allowed by MCP service configuration."&lt;/FONT&gt; On the proxy they split. A got &lt;FONT face="courier new, courier, monospace"&gt;403 PERMISSION_DENIED&lt;/FONT&gt;. B and C listed both tools and ran the delete:&lt;/P&gt;&lt;PRE&gt;=== User B: EXECUTE + USE CONNECTION ===
MCP Service (Unity Gateway)  tools/list                    HTTP 200  tools=['get_inventory']
MCP Service (Unity Gateway)  call delete_inventory_record  HTTP 200  {"code": -32003, "message": "Tool not allowed by MCP service configuration."}
Connection proxy (direct)    tools/list                    HTTP 200  tools=['get_inventory', 'delete_inventory_record']
Connection proxy (direct)    call delete_inventory_record  HTTP 200  result={"sku":"SKU-001","deleted":true,"note":"MOCK - nothing was actually deleted"}&lt;/PRE&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;The policy never sees the proxy&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;For a second run I widened the tool selection to every tool, so the deny policy was the only guard left. Through the service, both A and B got the policy's reason back as a JSON-RPC error: &lt;FONT face="courier new, courier, monospace"&gt;-32003 "Deleting inventory records is not permitted through this service."&lt;/FONT&gt; B then sent the same delete to the proxy and got &lt;FONT face="courier new, courier, monospace"&gt;"deleted": true&lt;/FONT&gt;. The policy ran on the path it is attached to. Nothing consulted it on the other path. (The policy docs describe a block as an HTTP 200 response carrying a &lt;FONT face="courier new, courier, monospace"&gt;databricks_service_policy&lt;/FONT&gt; object; through this MCP Service it arrived as the JSON-RPC error above.)&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="The Policies tab of the MCP Service showing deny_inventory_delete applied to all account users." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31481i710109200EE26EE5/image-size/large?v=v2&amp;amp;px=999" role="button" title="service-policy.jpg" alt="service-policy.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 3.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;The deny policy is attached to the service and applies to all account users. It does not apply to the connection.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;USE CONNECTION can come from the schema&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;User C mattered more than I expected. Nobody granted C &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt;. C held &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; on the schema that contains the connection, and its proxy calls went through exactly as B's did. When I revoked that grant and left C with &lt;FONT face="courier new, courier, monospace"&gt;USE SCHEMA&lt;/FONT&gt; only, the proxy returned &lt;FONT face="courier new, courier, monospace"&gt;403&lt;/FONT&gt; and the service returned &lt;FONT face="courier new, courier, monospace"&gt;-32007 "Not authorized to invoke MCP service."&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;This follows from the docs' own advice. They recommend creating the connection at the schema level "so it's governed alongside the MCP Service." In this workspace the schema-level connection picked up schema grants, so &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; or &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the schema reached the connection. The schema was also the only place I could grant it: &lt;FONT face="courier new, courier, monospace"&gt;GRANT … ON CONNECTION&lt;/FONT&gt; with the three-part name did not parse, and the connection's Permissions tab returned an error. Here, a review that only read the connection's own permission list would have missed both grants.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Schema permissions: A has USE SCHEMA; C has ALL PRIVILEGES; B has CREATE SERVICE, USE CONNECTION and USE SCHEMA." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31483i6241D92238A0EE4D/image-size/large?v=v2&amp;amp;px=999" role="button" title="schema-grants.jpg" alt="schema-grants.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 4.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;Grants on the schema, not on the connection. Two of these rows open the proxy path.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;Where the connection lives decides who inherits it&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;To check that placement was the cause, I created a second connection to the same server at the metastore level, with its own MCP Service limited to &lt;FONT face="courier new, courier, monospace"&gt;get_*&lt;/FONT&gt;. I also added User D, who has &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the catalog and &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on both services. Then I ran every identity against both connections.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Nested view of metastore, catalog and schema: the schema-level connection opened for B, C and D; the metastore-level connection opened only for B." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31488i746F77DD59E85D9F/image-size/large?v=v2&amp;amp;px=999" role="button" title="placement.png" alt="placement.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 5.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;The same grants, two placements. Grants on the schema and catalog reached the connection inside them and stopped at the one outside.&lt;/EM&gt;&lt;/P&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;Identity&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Grant that could open the proxy&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Schema-level connection&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;STRONG&gt;Metastore-level connection&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;A&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;none&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;B&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the schema; on the metastore connection directly&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools, deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools, deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;C&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; on the schema&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools, deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;STRONG&gt;D&lt;/STRONG&gt;&lt;/TD&gt;&lt;TD&gt;&lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on the catalog&lt;/TD&gt;&lt;TD&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;both tools, deleted: true&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;&lt;TD&gt;403&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;Through either MCP Service, all four saw only &lt;FONT face="courier new, courier, monospace"&gt;get_inventory&lt;/FONT&gt;, and the delete returned &lt;FONT face="courier new, courier, monospace"&gt;-32003&lt;/FONT&gt;. On the proxy, the schema-level connection accepted grants from its schema and its catalog. The metastore-level connection accepted only the grant made on the connection itself, and refused everyone else with &lt;FONT face="courier new, courier, monospace"&gt;User does not have any privileges on Connection 'mcp_boundary_ms_conn'&lt;/FONT&gt;. The docs' reason for recommending the schema level is that the connection is then governed alongside the MCP Service. The same placement lets connection access arrive from the schema and the catalog above it.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Permissions of the metastore-level connection: one row, mcp-exp-user-b with USE CONNECTION." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31486i42B1CD53F182857E/image-size/large?v=v2&amp;amp;px=999" role="button" title="ms-conn-grants.jpg" alt="ms-conn-grants.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 6.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;At the metastore level the connection has its own permission list, and it was the only grant that opened the proxy.&lt;/EM&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;The second bypass: your own MCP Service&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;The warning also covers registering another MCP Service on the same connection. That needs &lt;FONT face="courier new, courier, monospace"&gt;CREATE SERVICE&lt;/FONT&gt; too. B's first attempt failed with &lt;FONT face="courier new, courier, monospace"&gt;User does not have CREATE SERVICE on Schema&lt;/FONT&gt;. After I added that grant, B created &lt;FONT face="courier new, courier, monospace"&gt;shadow_b&lt;/FONT&gt; with no tool selection; it listed both tools and ran the delete. As the schema owner, and not a metastore admin, my list of MCP services in the schema still returned only &lt;FONT face="courier new, courier, monospace"&gt;inventory_readonly&lt;/FONT&gt;. C also created a service, but calls through it failed with &lt;FONT face="courier new, courier, monospace"&gt;Insufficient privileges on connection … to vend credentials&lt;/FONT&gt;. I can't explain that difference yet.&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;What the logs show&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;The mock server can't tell the callers apart. A's governed read and B's direct delete arrived with the same header set: &lt;FONT face="courier new, courier, monospace"&gt;User-Agent: Databricks-MCP-Proxy/1.0&lt;/FONT&gt;, the connection's one bearer token, and no header naming the caller or the path. Calls blocked by tool selection or the policy never reached it.&lt;/P&gt;&lt;P&gt;Databricks does record both paths in &lt;FONT face="courier new, courier, monospace"&gt;system.access.audit&lt;/FONT&gt;, at different levels of detail. A governed call is an &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; row with the service name and &lt;FONT face="courier new, courier, monospace"&gt;tool_name&lt;/FONT&gt;. B's blocked delete is an &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; row with status &lt;FONT face="courier new, courier, monospace"&gt;403&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;tool_name&lt;/FONT&gt; &lt;FONT face="courier new, courier, monospace"&gt;delete_inventory_record&lt;/FONT&gt;, even though the caller received HTTP 200 with a JSON-RPC error. A proxied call is a &lt;FONT face="courier new, courier, monospace"&gt;ucHttpConnectionProxiedRequest&lt;/FONT&gt; row with the caller and the connection, but no tool name. You can see that B used the connection. You can't see from that row that B ran a delete. Calls through &lt;FONT face="courier new, courier, monospace"&gt;shadow_b&lt;/FONT&gt; appear as &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; rows naming &lt;FONT face="courier new, courier, monospace"&gt;shadow_b&lt;/FONT&gt;. This query surfaces all three patterns. Replace the service name with your own:&lt;/P&gt;&lt;PRE&gt;SELECT event_time,
       coalesce(user_identity.email, user_identity.subject_name) AS caller,
       action_name,
       response.status_code AS status,
       request_params['connection_name'] AS connection_name,
       coalesce(request_params['fully_qualified_name'], request_params['mcp_service_id']) AS mcp_service,
       request_params['tool_name'] AS tool_name
FROM system.access.audit
WHERE event_date &amp;gt;= current_date() - INTERVAL 30 DAYS
  AND (
        action_name = 'ucHttpConnectionProxiedRequest'
     OR action_name = 'createMcpService'
     OR (action_name = 'mcpCall'
         AND coalesce(request_params['fully_qualified_name'], '')
             &amp;lt;&amp;gt; 'workspace.mcp_boundary.inventory_readonly')
  )
ORDER BY event_time DESC&lt;/PRE&gt;&lt;P&gt;In my workspace it returned 30 rows. They included every proxied request (B's and C's succeeded, A's were denied with &lt;FONT face="courier new, courier, monospace"&gt;403&lt;/FONT&gt;), the &lt;FONT face="courier new, courier, monospace"&gt;createMcpService&lt;/FONT&gt; rows for &lt;FONT face="courier new, courier, monospace"&gt;shadow_b&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;shadow_c&lt;/FONT&gt;, and the &lt;FONT face="courier new, courier, monospace"&gt;mcpCall&lt;/FONT&gt; rows through both. Audit rows appeared roughly 7 to 15 minutes after the calls.&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="system.access.audit rows for A and B: mcpCall rows name the tool; ucHttpConnectionProxiedRequest rows name only the connection." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31485i770E783A4FC99D5E/image-size/large?v=v2&amp;amp;px=999" role="button" title="audit-rows.jpg" alt="audit-rows.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;FONT color="#E0661A"&gt;&lt;STRONG&gt;Figure 7.&lt;/STRONG&gt;&lt;/FONT&gt; &lt;EM&gt;&lt;FONT face="courier new, courier, monospace"&gt;system.access.audit&lt;/FONT&gt; for A and B during the first run, with identities relabelled. Service calls name the tool. Proxied calls name only the connection.&lt;/EM&gt;&lt;/P&gt;&lt;P align="center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="orange-rule.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/31489iAE4C6A56504062ED/image-size/large?v=v2&amp;amp;px=999" role="button" title="orange-rule.png" alt="orange-rule.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;The permission model to use&lt;/FONT&gt;&lt;/H2&gt;&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;Consumers:&lt;/STRONG&gt; &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on the MCP Service, plus &lt;FONT face="courier new, courier, monospace"&gt;USE CATALOG&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;USE SCHEMA&lt;/FONT&gt;. That was all A needed to use the read tool, and it reached nothing else.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Service authors and admins:&lt;/STRONG&gt; &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt;, and &lt;FONT face="courier new, courier, monospace"&gt;CREATE SERVICE&lt;/FONT&gt; where they register services.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;A schema-level connection:&lt;/STRONG&gt; give consumer groups &lt;FONT face="courier new, courier, monospace"&gt;USE SCHEMA&lt;/FONT&gt; and &lt;FONT face="courier new, courier, monospace"&gt;USE CATALOG&lt;/FONT&gt; only. &lt;FONT face="courier new, courier, monospace"&gt;ALL PRIVILEGES&lt;/FONT&gt; or &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on that schema, or &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on its catalog, is connection access. Keep &lt;FONT face="courier new, courier, monospace"&gt;CREATE SERVICE&lt;/FONT&gt; for service authors.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;A metastore-level connection:&lt;/STRONG&gt; it did not inherit the schema or catalog grants I tested, so its own permission list is the one to review.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Reviews:&lt;/STRONG&gt; for a schema-level connection, check grants on the connection, its schema and its catalog. Run the audit query above for proxied requests and for services you didn't create.&lt;/LI&gt;&lt;/UL&gt;&lt;H2&gt;&lt;FONT color="#E0661A"&gt;Conclusion&lt;/FONT&gt;&lt;/H2&gt;&lt;P&gt;An MCP Service is a real control. In this experiment, tool selection hid the delete tool, the deny policy blocked it, and the audit log recorded the blocked attempt with the tool's name. None of that applied to the connection underneath. Any identity that could use the connection reached the server directly, and on a schema-level connection that included identities whose only relevant grant was on the schema or catalog above it.&lt;/P&gt;&lt;P&gt;So when you review who can reach an MCP server, start with the connection: where it lives, who holds &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt; on it, and which grants on the levels above it carry that privilege down. Give consumers &lt;FONT face="courier new, courier, monospace"&gt;EXECUTE&lt;/FONT&gt; on the service and nothing that adds up to &lt;FONT face="courier new, courier, monospace"&gt;USE CONNECTION&lt;/FONT&gt;.&lt;/P&gt;</description>
      <pubDate>Fri, 25 Sep 2026 01:06:55 GMT</pubDate>
      <guid>https://community.databricks.com/t5/community-articles/use-connection-bypasses-your-mcp-service-s-tool-selection-and/m-p/169770#M1605</guid>
      <dc:creator>ivanvyd</dc:creator>
      <dc:date>2026-09-25T01:06:55Z</dc:date>
    </item>
  </channel>
</rss>

