Skip to content

MCP service policies (UC functions) not enforced — deny-all policy allows GitHub MCP tools through #67

@zjacobson1016

Description

@zjacobson1016

Summary

Unity Catalog MCP service policies (on-call UC functions returning STRUCT<result STRING, reason STRING>) appear to not be enforced for external MCP connections accessed via ucode claude. A policy function that explicitly returns deny for every GitHub MCP tool still allows tool calls to succeed.

Expected behavior

When a service policy UC function is attached to an MCP connection and returns result: 'deny' for a tool invocation, the MCP tool call should be blocked and the agent should receive the policy reason message.

Actual behavior

  • Service policy UC function is created and attached to the GitHub MCP connection (e.g. github-copilot-mcp or system_ai_agent_github_mcp)
  • Function returns deny for all known GitHub MCP tools (explicit allowlist of 42 tools, plus ELSE deny for unknown tools)
  • GitHub MCP tools continue to execute successfully through Claude Code launched via ucode claude
  • No deny reason is surfaced to the agent

Policy function example

CREATE OR REPLACE FUNCTION github_mcp_policy(actor VARIANT, context VARIANT)
RETURNS STRUCT<result STRING, reason STRING>
RETURN CASE
  WHEN context:tool.name::STRING IN (
      'add_comment_to_pending_review',
      'add_issue_comment',
      'add_reply_to_pull_request_comment',
      'create_branch',
      'create_or_update_file',
      'create_pull_request',
      'create_repository',
      'delete_file',
      'fork_repository',
      'get_commit',
      'get_file_contents',
      'get_label',
      'get_latest_release',
      'get_me',
      'get_release_by_tag',
      'get_tag',
      'get_team_members',
      'get_teams',
      'issue_read',
      'issue_write',
      'list_branches',
      'list_commits',
      'list_issue_types',
      'list_issues',
      'list_pull_requests',
      'list_releases',
      'list_repository_collaborators',
      'list_tags',
      'merge_pull_request',
      'pull_request_read',
      'pull_request_review_write',
      'push_files',
      'request_copilot_review',
      'run_secret_scanning',
      'search_code',
      'search_issues',
      'search_pull_requests',
      'search_repositories',
      'search_users',
      'sub_issue_write',
      'update_pull_request',
      'update_pull_request_branch'
    )
    THEN NAMED_STRUCT(
      'result', 'deny',
      'reason', CONCAT(
        'GitHub MCP tool ''', context:tool.name::STRING,
        ''' is not permitted on this connection (OAuth scopes: repo, read:project, read:org).'
      )
    )
  ELSE NAMED_STRUCT(
    'result', 'deny',
    'reason', CONCAT(
      'Unknown or unlisted GitHub MCP tool ''', context:tool.name::STRING,
      ''' is not permitted on this connection.'
    )
  )
END

Even a simplified deny-all variant (unconditional deny for every tool) does not block calls.

Reproduction

  1. Create a UC function like the above returning deny for all tools
  2. Attach it as the on-call service policy on a GitHub MCP external connection
  3. Register the connection via ucode configure mcp and select the GitHub MCP server
  4. Launch Claude via ucode claude (OAuth token required for MCP auth)
  5. Ask Claude to call any GitHub MCP tool (e.g. get_me, list_issues, search_repositories)
  6. Observe tool executes successfully instead of being denied

GitHub MCP tools in scope (42 tools from repos toolset)

Write: add_comment_to_pending_review, add_issue_comment, add_reply_to_pull_request_comment, create_branch, create_or_update_file, create_pull_request, create_repository, delete_file, fork_repository, issue_write, merge_pull_request, pull_request_review_write, push_files, request_copilot_review, sub_issue_write, update_pull_request, update_pull_request_branch

Read/search: get_commit, get_file_contents, get_label, get_latest_release, get_me, get_release_by_tag, get_tag, get_team_members, get_teams, issue_read, list_*, pull_request_read, run_secret_scanning, search_*

Possible causes to investigate

  • Service policy not invoked on MCP proxy path (/api/2.0/mcp/external/...)
  • Policy function not wired to the connection used by Claude MCP registration
  • context:tool.name path mismatch — policy receives different context shape than documented
  • Policy evaluation errors swallowed silently (tools allowed on policy failure)
  • OAuth/bearer token path bypassing policy enforcement layer

Environment

  • ucode claude with GitHub MCP registered from Databricks external MCP connection
  • Workspace: e2-dogfood.staging.cloud.databricks.com
  • Connection: github-copilot-mcp (also tested with system_ai_agent_github_mcp)
  • OAuth scopes on connection: repo, read:project, read:org
  • Claude Code MCP endpoint: https://<workspace>/api/2.0/mcp/external/<connection-name>

Impact

Service policies are intended to provide governance over MCP tool access (deny writes, require consent, etc.). If policies are not enforced, administrators cannot restrict MCP tool usage for coding agents launched via ucode.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions