Skip to content

MCP Server Part 7: get_dash_component tool and callback tool execution - #3749

Merged
KoolADE85 merged 6 commits into
mcpfrom
feature/mcp-get-dash-component-tool
May 11, 2026
Merged

MCP Server Part 7: get_dash_component tool and callback tool execution#3749
KoolADE85 merged 6 commits into
mcpfrom
feature/mcp-get-dash-component-tool

Conversation

@KoolADE85

Copy link
Copy Markdown
Contributor

Summary

  • Wires up the full tool pipeline following the same pattern as resources:

    • list_tools() builds the tool list from all registered tool providers
    • call_tool(name, args) routes LLM calls to the appropriate callback function
  • Adds a static tool get_dash_component which allows LLMs to query a specific prop for a given ID.

Manual verification:

fromdashimportDash, html, dcc, Input, Outputimportjsonapp=Dash(__name__)
app.layout=html.Div([
dcc.Dropdown(id="city", options=["NYC", "LA"], value="NYC"),
dcc.Graph(id="chart"),
html.Div(id="summary"),
])
@app.callback(Output("chart", "figure"), Input("city", "value"))defupdate_chart(city):
return {"data": [], "layout": {"title": city}}
@app.callback(Output("summary", "children"), Input("city", "value"))defupdate_summary(city):
returnf"Showing data for {city}"withapp.server.app_context():
fromdash.mcp.primitives.toolsimportlist_tools, call_tool# List all tools — should include update_chart, update_summary, get_dash_componenttools=list_tools()
fortoolintools.tools:
print(f"- {tool.name}")
# Inspect a component — shows it's input to both callbacksresult=call_tool("get_dash_component", {"component_id": "city"})
print(json.dumps(json.loads(result.content[0].text), indent=2))
# "value" property should list both `update_chart` and `update_summary` in input_to_tool# Execute a callback via its toolresult=call_tool("update_summary", {"city": "LA"})
print(result.content[0].text) # Shows "Showing data for LA"

@github-actions

github-actionsBot commented Apr 16, 2026

Copy link
Copy Markdown

Thank you for your contribution to Dash! 🎉

This PR is exempt from requiring a linked issue due to its labels.

@KoolADE85
KoolADE85force-pushed the feature/mcp-formatted-tool-results branch from 0e54d74 to 8a41b76CompareApril 21, 2026 17:28
@KoolADE85
KoolADE85force-pushed the feature/mcp-get-dash-component-tool branch from 8c1f392 to f9b05daCompareApril 21, 2026 17:28
@KoolADE85
KoolADE85force-pushed the feature/mcp-formatted-tool-results branch from 8a41b76 to 4fb9d4aCompareApril 22, 2026 21:31
@KoolADE85
KoolADE85force-pushed the feature/mcp-get-dash-component-tool branch from f9b05da to bc340e6CompareApril 22, 2026 21:37
@KoolADE85
KoolADE85force-pushed the feature/mcp-formatted-tool-results branch from 4fb9d4a to 10a544bCompareApril 23, 2026 20:15
@KoolADE85
KoolADE85force-pushed the feature/mcp-get-dash-component-tool branch from bc340e6 to b3d4015CompareApril 23, 2026 20:24
@KoolADE85
KoolADE85force-pushed the feature/mcp-formatted-tool-results branch 2 times, most recently from d203cf9 to e73cd17CompareApril 30, 2026 15:23
@KoolADE85
KoolADE85force-pushed the feature/mcp-get-dash-component-tool branch 2 times, most recently from e407569 to 37e92f0CompareApril 30, 2026 16:02
@KoolADE85
KoolADE85force-pushed the feature/mcp-formatted-tool-results branch from 2acb39d to 04ca2feCompareApril 30, 2026 16:32
@KoolADE85
KoolADE85force-pushed the feature/mcp-get-dash-component-tool branch from 37e92f0 to 28d5fedCompareApril 30, 2026 16:45
@KoolADE85
KoolADE85force-pushed the feature/mcp-formatted-tool-results branch from 04ca2fe to 8f9d5a0CompareMay 6, 2026 21:31
@KoolADE85
KoolADE85force-pushed the feature/mcp-get-dash-component-tool branch 2 times, most recently from f098f88 to 79df12eCompareMay 8, 2026 15:40
@KoolADE85
KoolADE85 changed the base branch from feature/mcp-formatted-tool-results to mcpMay 8, 2026 17:07

@camdecostercamdecoster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a few suggestions. The one to pay attention to is the loop going through callbacks/inputs/outputs. Let me know what you think.

if prop_filter and prop_name != prop_filter:
continue

value = callback_map.get_initial_value(f"{comp_id}.{prop_name}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
value=callback_map.get_initial_value(f"{comp_id}.{prop_name}")
id_and_prop=f"{comp_id}.{prop_name}"
value=callback_map.get_initial_value(id_and_prop)


modified_by: list[str] = []
input_to: list[str] = []
id_and_prop = f"{comp_id}.{prop_name}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
id_and_prop = f"{comp_id}.{prop_name}"

def call_tool(cls, tool_name: str, arguments: dict[str, Any]) -> CallToolResult:
comp_id = arguments.get("component_id", "")
if not comp_id:
raise ValueError("component_id is required")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raiseValueError("component_id is required")
returnCallToolResult(
content=[TextContent(type="text", text="component_id is required")],
isError=True,
)

Comment threaddash/mcp/primitives/tools/tool_get_dash_component.py
Comment on lines +69 to +70
if component is None:
callback_map = get_app().mcp_callback_map

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ifcomponentisNone:
callback_map=get_app().mcp_callback_map
callback_map=get_app().mcp_callback_map
ifcomponentisNone:

isError=True,
)

callback_map = get_app().mcp_callback_map

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
callback_map = get_app().mcp_callback_map

Comment on lines +103 to +109
for cb in callback_map:
for out in cb.outputs:
if out["id_and_prop"] == id_and_prop:
modified_by.append(cb.tool_name)
for inp in cb.inputs:
if inp["id_and_prop"] == id_and_prop:
input_to.append(cb.tool_name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could get expensive for apps with a lot of callbacks, inputs, outputs. Is there a way to create and use an index for looking this up?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point! The dash renderer actually implements such a mapping (but in JS obviously). I'll do the same on the python side.

@KoolADE85
KoolADE85force-pushed the feature/mcp-get-dash-component-tool branch from f1b6cc2 to d25123bCompareMay 8, 2026 22:04
@KoolADE85
KoolADE85 merged commit 3c340d1 into mcpMay 11, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KoolADE85@camdecoster