Skip to content

MCP Server Part 4: Expose callbacks as tools#3731

Open
KoolADE85 wants to merge 3 commits intofeature/mcp-resourcesfrom
feature/mcp-tools
Open

MCP Server Part 4: Expose callbacks as tools#3731
KoolADE85 wants to merge 3 commits intofeature/mcp-resourcesfrom
feature/mcp-tools

Conversation

@KoolADE85
Copy link
Copy Markdown
Contributor

Summary

This PR turns Dash callbacks into fully-described MCP tools. Each tool has four main parts:

Tool description (descriptions/)

A human-readable summary of what the callback does. Built from pluggable sources that each contribute lines:

  • description_outputs.py — semantic summary of what the callback returns (e.g. "Returns chart/visualization data" for a Graph.figure output)
  • description_docstring.py — the callback function's Python docstring

Each source extracts what it needs from a given CallbackAdapter in order to produce text descriptions. New sources can be added by appending to the _SOURCES list.

Input schema (input_schemas/)

JSON Schema that describes valid inputs for each callback parameter. Each schema is derived from:

  • Python type annotations on the callback function (e.g. def callback(metric: str):{"type": "string"})
  • Automatic introspection of the callback's components (e.g. Input("dropdown", "value"){"type": { "anyOf": ["string", "number", "boolean", null]}})
  • Manually curated overrides for components where introspection is insufficient (e.g. the "date" format of date pickers; dropdown values are an array only when multi=True)

Input description (input_descriptions/)

Each parameter also gets a text description assembled from various sources:

  • Text content of an html.Label that is associated with the input
  • Per-parameter text from the callback's docstring
  • A list of props that are used in the app (options, min/max, value, etc) and any chained callbacks that set values for this input

Output schema (output_schemas/)

JSON Schema for the tool's outputSchema field

  • A generic schema derived from the dict that Dash callbacks return to dash-renderer.

Manual testing

You can see the JSON that will be presented to LLMs by testing manually:

from dash import Dash, html, dcc, Input, Output
from dash._get_app import app_context
import json

app = Dash(__name__)
app.layout = html.Div([
    html.Label("Choose a metric", htmlFor="metric"),
    dcc.Dropdown(id="metric", options=["revenue", "users"], value="revenue"),
    html.Label(["Threshold value", dcc.Input(id="threshold", type="number", value=100)]),
    dcc.Dropdown(id="sub-metric"),
    html.Div(id="result"),
])

@app.callback(Output("sub-metric", "options"), Input("metric", "value"))
def update_sub_metrics(metric):
    """Update available sub-metrics based on the selected metric."""
    if metric == "revenue": return ["gross", "net", "recurring"]
    return ["daily", "monthly"]

@app.callback(Output("result", "children"), Input("metric", "value"),
              Input("threshold", "value"), Input("sub-metric", "value"))
def filter_data(metric: str, threshold: float, sub_metric: str):
    """Filter data by metric and threshold."""
    return f"{metric}/{sub_metric} > {threshold}"

with app.server.app_context():
    app_context.set(app)
    from dash.mcp.primitives.tools.callback_adapter_collection import CallbackAdapterCollection
    collection = CallbackAdapterCollection(app)
    for adapter in collection:
        tool = adapter.as_mcp_tool
        print(tool)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

Thank you for your contribution to Dash! 🎉

⚠️ Action Required: Could you please link an issue to this PR? This helps us track the context and purpose of changes. If an issue doesn't exist yet, create one before linking it.

You can link an issue by:

  • Adding a reference in the PR description (e.g., Fixes #123, Closes #456, or Refs #789)
  • Using GitHub's UI to link a related issue

This check will automatically pass once an issue is linked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant