Support URL elicitation on MCP tool calls#450
Open
artjen wants to merge 3 commits into
Open
Conversation
Wire a URL-only elicitation callback into DatabricksMCPClient's ClientSession for both list_tools and call_tool. The MCP SDK ships no default way to surface elicitation to users (its default callback just replies "Elicitation not supported" and advertises no capability), so define one internally: - _handle_url_elicitation: validates the server-supplied URL's scheme against an http(s) allowlist, surfaces a security warning + confirmation prompt, opens the browser on accept, and returns accept/decline/cancel. Cancels when there is no interactive stdin. - _url_only_elicitation_callback: routes URL-mode requests to the handler and rejects form-mode with INVALID_REQUEST so servers can fall back. The callback is module-private; the public DatabricksMCPClient constructor is unchanged. Co-authored-by: Isaac
URL elicitation (ElicitRequestURLParams / ElicitRequestFormParams and the mode-tagged ElicitRequestParams union) was introduced in mcp 1.23.0. The prior floor mcp>=1.13.0 let the lowest-direct CI resolution install a release without those symbols, breaking import of the new tests and the params.mode dispatch. Co-authored-by: Isaac
Author
Pre-existing CI failures (unrelated to this PR)This PR only touches
All checks that exercise this change are green: For reference, the one regression this PR did introduce — |
databricks-mcp is frequently embedded in custom agents (often headless in Model Serving, or async). Hardcoding an interactive input()/webbrowser elicitation callback was wrong for that: it silently cancels in headless contexts and blocks the event loop from acall_tool. - Add an optional `elicitation_callback` to DatabricksMCPClient; default None means the ClientSession advertises no elicitation capability (safe default). Agents inject their own callback to surface the URL through their channel. - Provide `interactive_url_elicitation_callback` (now exported) for local/CLI use, and run its blocking prompt/browser off the event loop via run_in_executor so it is safe from async code. - Rename the internal callback accordingly; document the model in the README. Co-authored-by: Isaac
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds URL-mode elicitation to
DatabricksMCPClientin an embedding-safe way. The MCP SDK ships no default way to surface elicitation to users, so this makes the client's elicitation callback injectable.Because
databricks-mcpis frequently embedded in custom agents — often headless (Model Serving) or async — an interactiveinput()/webbrowsercallback can't be the imposed default: it silently cancels where there's no TTY and blocks the event loop fromacall_tool. So:DatabricksMCPClient(..., elicitation_callback=None)— new optional param. DefaultNonemeans theClientSessionadvertises no elicitation capability (safe for headless/async). Agents inject their own callback to surface the URL through their own channel (UI, response stream, approval card).interactive_url_elicitation_callback(exported) — a ready-made opt-in handler for local/CLI use: scheme-validates the server URL (http/https only), shows a security warning + confirmation prompt, and opens the approved URL. Its blocking prompt/browser runs off the event loop viarun_in_executor, so it's safe from async code. URL-mode only; form-mode is rejected withINVALID_REQUEST.Scope is the core client only; threading the callback through the OpenAI/LangChain wrappers is a follow-up.
The URL-elicitation handling draws on the MCP Python SDK's reference example: https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/url_elicitation_client.py
How do you know it works?
TestUrlElicitationcovers: accept/decline/cancel, no-stdin (EOFError), disallowed-scheme rejection, form-mode rejection, elicitation off by default, an injected callback being stored, and both session paths forwarding eitherNone(default) or the injected callback. Full unit suite passes under bothuvresolutions (lowest-directandhighest) — 133 passed — andruff check/formatare clean.Also bumps the
mcpfloor to>=1.23.0(the release that introduced URL elicitation), so thelowest-directCI resolution has the required symbols.This pull request and its description were written by Isaac.