Fix async MCP tool execution in Builder App wrapper#526
Open
philsalm wants to merge 1 commit into
Open
Conversation
The mcp-server's _patch_tool_decorator_for_async() converts every sync @mcp.tool registration into an async function via _wrap_sync_in_thread. The Builder App's wrapper still treated tools as sync and called ctx.run(fn, **args) directly inside a thread pool. For an async fn this returns the coroutine object instead of awaiting it, producing a ~50-char stringified coroutine repr as the "result" — which then breaks any agent that depends on tool output (execute_sql, get_table_stats_and_schema, manage_workspace_files, etc.). Symptoms: agent receives "result length: 50" with a RuntimeWarning: "coroutine 'execute_sql' was never awaited", then loops or stalls because it has no usable tool output. Fix: detect async functions with inspect.iscoroutinefunction() and run them inside a fresh event loop inside the thread — same isolation pattern EVENT_LOOP_FIX.md uses for the agent itself. 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
The Builder App's MCP tool wrapper at
databricks-builder-app/server/services/databricks_tools.py:292assumed all FastMCP tools were sync. But the mcp-server's_patch_tool_decorator_for_async()(indatabricks-mcp-server/databricks_mcp_server/server.py) intercepts@mcp.toolregistration and converts every sync function to async via_wrap_sync_in_thread. After that patch, every tool is async.The Builder App's wrapper called
ctx.run(fn, **parsed_args)inside a thread pool. For an asyncfn, that returns the coroutine object instead of awaiting it. The thread completes immediately with the coroutine object as its result. The agent receives the stringified coroutine repr (~50 bytes) as the tool output, doesn't know what to make of it, and either loops or stalls.Reproduction
Deploy
databricks-builder-appfrommainagainst any workspace. Open a project and ask the agent to do anything that touches an MCP tool — e.g. "What tables are in catalog X?". You'll see in the app logs:result length: 50is the length ofstr(<coroutine object execute_sql at 0x...>). The agent then has no useful tool output to act on, and downstream behavior is unpredictable.Fix
Detect async functions with
inspect.iscoroutinefunction()and run them inside a fresh event loop inside the thread — same isolation patternEVENT_LOOP_FIX.mdalready uses for the agent itself. Sync functions follow the original path.Test plan
mainagainst an internal workspace — every MCP tool call returns 50 bytes with the coroutine-not-awaited warning, and the agent stalls.databricks_tools.py.Related
_patch_tool_decorator_for_asyncintroduced indatabricks-mcp-server/databricks_mcp_server/server.py.This pull request and its description were written by Isaac.