Skip to main content

uc-mcp-proxy

MCP stdio-to-Streamable-HTTP proxy with Databricks OAuth.

Lets any MCP client that speaks stdio (e.g. Claude Desktop, Claude Code) connect to any Databricks MCP server — Managed, External, or Apps — handling authentication automatically.

Installation

# Run directly (no install needed)
uvx uc-mcp-proxy --url <MCP_SERVER_URL>

# Or install globally
uv tool install uc-mcp-proxy

Requires Python 3.10+.

First-run authentication

uc-mcp-proxy expects a configured Databricks CLI profile. Set one up first:

databricks configure --host https://<workspace>

Then pass it to the proxy:

uvx uc-mcp-proxy --url <MCP_SERVER_URL> --profile <name>

If the profile uses OAuth U2M (auth_type = databricks-cli) and the cached token is expired, uc-mcp-proxy runs databricks auth login --profile <name> automatically the first time it launches, opening a browser tab. Subsequent runs use the refreshed token.

uc-mcp-proxy will only auto-login for OAuth (databricks-cli) profiles. For PAT, M2M, Azure, or other auth types, the proxy diagnoses the failure and points you at the right remediation — it never runs databricks auth login against a non-OAuth profile because that would overwrite your existing credentials in ~/.databrickscfg.

To skip the auto-login (CI / headless), pass --no-auto-login and ensure DATABRICKS_TOKEN or another credential is set in the environment.

Databricks MCP Server Types

Server Type URL Pattern
Managed MCP (UC Functions, Vector Search, Genie, SQL) https://<workspace>/api/2.0/mcp/functions/{catalog}/{schema}
External MCP (GitHub, Google Drive, and others) https://<workspace>/api/2.0/mcp/external/{connection_name}
Apps (custom MCP servers) https://<app-name>-<workspace-id>.<region>.databricksapps.com/<path>

An App is served from its own hostname on databricksapps.comnot from a path under your workspace host. Databricks assigns the URL when the app is created and it cannot be changed afterwards, so copy it from the Apps page in the workspace UI (or databricks apps list) rather than constructing it by hand — on the workspace we tested, the segment Databricks documents as <region> is the cloud name (aws), not a region like us-east-1. The <path> is whatever route the app serves MCP on; /mcp is the common convention.

Apps reject raw personal access tokens. The App front door requires an OAuth token. Either use --auth-type databricks-cli (browser-based OAuth U2M), or point a PAT profile at the App URL and the proxy exchanges the PAT for an app-scoped token automatically — see Databricks Apps from a PAT profile. The PAT must be one the exchange accepts (a Lakebox credential, not a classic dapi… PAT). Managed and External MCP servers work with PAT and other auth types as-is.

Usage

Claude Desktop / Claude Code (.mcp.json)

Add to your MCP client configuration:

{
  "mcpServers": {
    "unity-catalog": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "uc-mcp-proxy",
        "--url", "<MCP_SERVER_URL>"
      ]
    }
  }
}

CLI

uc-mcp-proxy --url <MCP_SERVER_URL> [--profile <DATABRICKS_PROFILE>] [--auth-type <AUTH_TYPE>]
Flag Description
--url (required) Remote MCP server URL
--profile Databricks CLI profile name (uses default if omitted)
--auth-type Databricks auth type, e.g. databricks-cli
--meta KEY=VALUE Meta parameter injected into tools/call _meta (repeatable)
--client-id App's oauth2_app_client_id — sets the RFC 8693 exchange audience explicitly, skipping auto-discovery
--pat-exchange Force the exchange on a non-App URL and bypass the classic-dapi-PAT refusal
--scope SCOPE OAuth scope for the exchange (repeatable; a value may list several space-separated). Overrides discovered scopes
--no-verify-ssl Disable SSL certificate verification (use with caution — see below)

Databricks Apps from a PAT profile

A Databricks App refuses a raw personal access token — its front door wants an OAuth token minted for that app. Rather than forcing a browser login, the proxy trades your PAT for one (RFC 8693 token exchange) on your behalf, and it works this out automatically.

The common case needs no exchange flags. Point a PAT profile at an App URL and the proxy detects the App host (*.databricksapps.com), looks up the app's oauth2_app_client_id and effective_user_api_scopes from workspace metadata, and runs the exchange:

uc-mcp-proxy \
  --url https://<app-name>-<workspace-id>.aws.databricksapps.com/mcp \
  --profile MY_PAT_PROFILE
{
  "mcpServers": {
    "databricks-app": {
      "command": "uvx",
      "args": [
        "uc-mcp-proxy",
        "--url", "https://<app-name>-<workspace-id>.aws.databricksapps.com/mcp",
        "--profile", "MY_PAT_PROFILE"
      ]
    }
  }
}

The credential must be one the exchange accepts. A Lakebox-generated environment credential works; a classic, hand-minted dapi… PAT does not — the platform rejects it as an exchange subject. When the proxy sees an App URL with a dapi… token it refuses up front with that explanation rather than leaking the token on a doomed request. See docs/token-exchange.md for the evidence.

Overrides, for when detection or discovery is not enough:

  • --client-id <oauth2_app_client_id> — set the audience explicitly and skip discovery (e.g. the identity cannot list the app). --scope then supplies the scopes, since discovery is skipped.
  • --scope "a b" — override the discovered scopes. Repeatable.
  • --pat-exchange — force the exchange when --url is not recognized as an App host, and bypass the classic-PAT refusal. Discovery still fills the client id and scopes unless --client-id is given.

Worth knowing:

  • The exchange requires a PAT profile. --client-id / --pat-exchange on any other auth type exit immediately rather than pretending to work. DATABRICKS_TOKEN counts as a PAT.
  • --scope is required by real Apps. An App with declared effective_user_api_scopes rejects a scope-less exchange; discovery supplies them automatically, and the proxy warns if it ends up with none to send.
  • The exchanged token is short-lived (about an hour) and is re-minted automatically, including once in-place if the app rejects it mid-session, so long sessions do not need restarting.
  • Your PAT is never forwarded to the app. It goes only to the workspace token endpoint, as the subject of the exchange. For any pat profile the proxy also stops populating X-Forwarded-Access-Token, which the platform supplies itself — a raw PAT there would be handed to arbitrary app code, and it would survive a cross-origin redirect that strips Authorization.
  • Verified on one AWS workspace at one point in time. The accepted request shape is undocumented, so treat it as empirical rather than contractual; a refusal prints the exact request that was sent so a platform change is diagnosable rather than mysterious.

Meta Parameters (Managed MCP)

Databricks Managed MCP servers accept configuration — for example, selecting a SQL warehouse — via the MCP _meta field in the JSON-RPC request body, not as HTTP headers. See the Databricks meta-param docs for the supported keys per server type.

Use --meta KEY=VALUE (repeatable) — the proxy merges these into params._meta on every outgoing tools/call request:

uvx uc-mcp-proxy \
  --url https://workspace.databricks.com/api/2.0/mcp/sql \
  --meta warehouse_id=abc123

Or in .mcp.json:

{
  "mcpServers": {
    "sql-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "uc-mcp-proxy",
        "--url", "https://workspace.databricks.com/api/2.0/mcp/sql",
        "--meta", "warehouse_id=abc123"
      ]
    }
  }
}

If the MCP client already sets a _meta key that the proxy is also configured to inject, the proxy value wins and a warning is written to stderr.

SSL Certificate Verification

Some Azure Databricks instances use self-signed or internally-signed certificates that are not trusted by the system's default CA bundle. This causes errors like:

SSL_CERTIFICATE_VERIFY_FAILED: certificate verify failed: unable to get local issuer certificate

Use --no-verify-ssl to disable certificate verification:

uvx uc-mcp-proxy \
  --url https://workspace.azuredatabricks.net/api/2.0/mcp/functions/main/default \
  --no-verify-ssl

Or in .mcp.json:

{
  "mcpServers": {
    "unity-catalog": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "uc-mcp-proxy",
        "--url", "https://workspace.azuredatabricks.net/api/2.0/mcp/functions/main/default",
        "--no-verify-ssl"
      ]
    }
  }
}

Security warning: --no-verify-ssl disables all certificate validation, which exposes connections to man-in-the-middle (MITM) attacks. Only use this flag in trusted network environments (e.g. a private corporate VPN) where you control the network path to the Databricks workspace.

Combined with --client-id the exposure is wider than usual: the token exchange sends your PAT in a request body to the workspace token endpoint, not only as a header. Anyone able to intercept that connection reads a long-lived, full-privilege credential.

How It Works

  1. Starts an MCP stdio server (stdin/stdout)
  2. Connects to the remote MCP server via Streamable HTTP
  3. Injects a fresh Databricks OAuth token on every HTTP request
  4. Bridges messages bidirectionally between the two transports

Authentication

Authentication is handled by the Databricks SDK. The SDK auto-detects the method, or you can force one with --auth-type.

Auth type Managed / External MCP Apps MCP
databricks-cli — token from ~/.databrickscfg ✅ recommended
pat — personal access token ✅ auto-exchange 1
oauth-m2m — service principal 2
OAuth U2M — browser-based login

Troubleshooting

When the remote MCP server refuses a request, the proxy prints a diagnosis to stderr naming the status, the URL, the profile, and the auth type in use.

Message Meaning Fix
rejected your credentials (HTTP 401) The token was minted locally but the server rejected it — it may have expired, or this profile's identity is not recognized by the target. Refresh the profile's credentials. For databricks-cli, run databricks auth login --profile <name>. Against a Databricks App this also appears when the identity simply lacks CAN_USE on the app — check the app's permissions before assuming the credential is bad.
refused this request (HTTP 403) Authenticated successfully, but not authorized for this target. Check your grants on the target. Pointing a pat profile at a Databricks App without --client-id produces this — Apps reject a raw PAT and need an OAuth token.
refused the PAT token exchange (HTTP 400) The workspace would not exchange your PAT for an app token. The message prints the exact request that was sent. Check --client-id is the app's oauth2_app_client_id and that --scope matches its effective_user_api_scopes. Note an invalid audience error can also mean the requested token type was rejected, so a correct --client-id is not proof the flag is at fault.
rejected your credentials (HTTP 401) whose remediation names --client-id The exchange succeeded but the app refused the resulting token, twice — so it is configuration, not expiry. Confirm this identity has CAN USE on the app, and that --scope covers what the app requires.
no MCP endpoint at this URL (HTTP 404) The URL is wrong. Not an auth failure. Check --url.
the MCP session expired server-side (HTTP 404) The server no longer recognizes this session. Restart the MCP client to establish a new session.
the remote MCP server failed (HTTP 5xx) Server-side error, not an authentication problem. Retry; check Databricks service status.

A failure on the background server→client stream is reported but does not stop the proxy: the SDK retries a bounded number of times and then stops, so server-initiated messages may be lost while tool calls keep working.

Development

uv sync                        # install dependencies
uv run pytest -m unit -v       # run unit tests
uv run pytest -m integration -v # run integration tests
uv build                       # build package

License

MIT

  1. An App rejects a PAT sent as-is, because it requires an OAuth token. That is a statement about the token, not about your profile: a PAT can be exchanged for an app-scoped OAuth token (RFC 8693), and uc-mcp-proxy does this automatically when a PAT profile points at an App URL — no --client-id needed. The exchange only accepts certain PAT credentials (a Lakebox-generated one, not a classic dapi… PAT). See Databricks Apps from a PAT profile.

  2. Verified against a live App-hosted MCP server: initialize, tools/list, and tools/call all succeed through the proxy with a service principal's oauth-m2m credentials. The principal must be granted CAN_USE on the app first — without that grant the App answers 401, not 403, so a missing permission is easy to misread as the auth type being unsupported.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uc_mcp_proxy-0.6.0.tar.gz (203.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

uc_mcp_proxy-0.6.0-py3-none-any.whl (45.5 kB view details)

Uploaded Python 3

File details

Details for the file uc_mcp_proxy-0.6.0.tar.gz.

File metadata

  • Download URL: uc_mcp_proxy-0.6.0.tar.gz
  • Upload date:
  • Size: 203.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for uc_mcp_proxy-0.6.0.tar.gz
Algorithm Hash digest
SHA256 993789b196f101badf2a6d834d8b81ac6dd2c3e467abb7eca0505fd23bbaa4b0
MD5 929c8f986641ad432585ad65634c787f
BLAKE2b-256 7cf2bdc27d4082de45005aa8d87dd7d4074a6c77583cee79b00745e319732239

See more details on using hashes here.

Provenance

The following attestation bundles were made for uc_mcp_proxy-0.6.0.tar.gz:

Publisher: publish.yml on IceRhymers/uc-mcp-proxy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file uc_mcp_proxy-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: uc_mcp_proxy-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 45.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for uc_mcp_proxy-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5c46fe1e907b88d678ae4fa6646f79ec0d28e98270f245c7639295ea53d8e93c
MD5 2ee7bbc1b30751be29de60a948134932
BLAKE2b-256 0da877496ae858b2d66339cdd9dc6a64d6cc16baf81cf1c06dd1104c340fbc74

See more details on using hashes here.

Provenance

The following attestation bundles were made for uc_mcp_proxy-0.6.0-py3-none-any.whl:

Publisher: publish.yml on IceRhymers/uc-mcp-proxy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 files

0.5.1

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page