Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
882586b
Add layout traversal utilities for Dash component trees
KoolADE85 Apr 1, 2026
4d55ef8
Make Dash components compatible with Pydantic types
KoolADE85 Apr 1, 2026
998d8d7
Extract get_layout() from serve_layout()
KoolADE85 Apr 1, 2026
66eda33
Fix build issues for dash-table and dash-core-components
KoolADE85 Apr 1, 2026
0cdb67a
Add CallbackDispatchBody type hints to dispatch methods
KoolADE85 Apr 1, 2026
b64c3fe
Use python3.8 compatible pydantic
KoolADE85 Apr 1, 2026
a85aa6c
lint
KoolADE85 Apr 1, 2026
4ecaf7d
Fix lint error on CI
KoolADE85 Apr 1, 2026
069b049
Rename layout.py to _layout_utils.py
KoolADE85 Apr 6, 2026
d684507
Make NumberType import backwards-compatible in generated components
KoolADE85 Apr 6, 2026
17a7541
Fix type checker for NumberType implementation
KoolADE85 Apr 6, 2026
dc35bff
Clean up import for pyright
KoolADE85 Apr 7, 2026
00eb203
Add callback adapter core for MCP tool generation
KoolADE85 Apr 1, 2026
fcfa765
Fix type errors
KoolADE85 Apr 1, 2026
8c89d51
Fix import path
KoolADE85 Apr 6, 2026
eeadb7f
tighten up types used around callbacks
KoolADE85 Apr 13, 2026
5b09f99
Remove redundant error code
KoolADE85 Apr 13, 2026
fa99ab0
lint
KoolADE85 Apr 14, 2026
cd1f4d4
Add MCP resource providers for layout, components, pages, and clients…
KoolADE85 Apr 1, 2026
2f00c0e
Fix import path
KoolADE85 Apr 6, 2026
5e472b2
Implement base class for each resource type
KoolADE85 Apr 13, 2026
f8d9739
lint
KoolADE85 Apr 16, 2026
6d4e2a0
Fix pylint errors
KoolADE85 Apr 22, 2026
ef1ea3f
Implement callbacks as tools with rich input/output schema and descri…
KoolADE85 Apr 1, 2026
9060647
Refactor description sources to accept CallbackAdapter instances
KoolADE85 Apr 8, 2026
554ddc0
Fix pylint error
KoolADE85 Apr 8, 2026
2cfa193
Fix regression in CallbackAdapter
KoolADE85 Apr 16, 2026
dce8e9a
Refactor tool descriptions/schemas to use a base class (just as resou…
KoolADE85 Apr 16, 2026
e29cb9c
Disable docstrings by default in MCP tool descriptions
KoolADE85 Apr 21, 2026
991dfa5
lint
KoolADE85 Apr 22, 2026
4d7411c
Move all hard-coded text into a `prop_roles.py` file where it can be …
KoolADE85 Apr 24, 2026
b357495
Add pattern-matching callback support to input schemas and descriptions
KoolADE85 Apr 1, 2026
6906b3d
clean up duplicated code
KoolADE85 Apr 16, 2026
6a52dba
Refactor unit tests to conform to existing test patterns
KoolADE85 Apr 23, 2026
7ce9939
Add result formatters for Plotly figures and tabular data
KoolADE85 Apr 1, 2026
acf75a0
Update type names
KoolADE85 Apr 16, 2026
d01d43a
Refactor tool result formatters to use a base class (just as resource…
KoolADE85 Apr 16, 2026
92cf27b
lint
KoolADE85 Apr 16, 2026
13d0fbf
Make import top-level
KoolADE85 Apr 22, 2026
ef8fc34
Refactor unit tests to conform to existing test patterns
KoolADE85 Apr 23, 2026
88589cb
Use TABULAR/PLOTLY_FIGURE PropRole.matches() in formatters
KoolADE85 Apr 30, 2026
55f6514
Tighten formatter types: required-field access + plotly stub ignore
KoolADE85 Apr 30, 2026
0be83d3
code review feedback
KoolADE85 May 8, 2026
bcc86c8
Add get_dash_component tool and callback tool dispatch pipeline
KoolADE85 Apr 1, 2026
a95b3bf
Refactor tools to use a base class (just as resources do)
KoolADE85 Apr 16, 2026
9e4cc2c
lint
KoolADE85 Apr 16, 2026
c079065
Refactor unit tests to conform to existing test patterns
KoolADE85 Apr 23, 2026
9988753
Implement a mapping of "id+prop" to callbacks that are outputs/inputs…
KoolADE85 May 8, 2026
c2f06ae
Code review feedback
KoolADE85 May 8, 2026
34a4d98
Make run_callback compatible with 4.2.0 changes
KoolADE85 May 12, 2026
227eeed
Wire MCP server, SSE transport, and Dash app integration
KoolADE85 Apr 1, 2026
001a7b7
Enforce session management per MCP spec (404 for unknown sessions, 40…
KoolADE85 Apr 9, 2026
3fe035b
remove unused code
KoolADE85 Apr 15, 2026
3b44944
Fix types for mypy
KoolADE85 Apr 17, 2026
9dc1f10
Remove unused SSE code for initial MCP implementation
KoolADE85 Apr 20, 2026
f640328
add app-level config for exposing callback docstrings in MCP tools
KoolADE85 Apr 21, 2026
d108faf
Disable MCP server by default
KoolADE85 Apr 21, 2026
eb94e14
lint
KoolADE85 Apr 22, 2026
fdaf822
fix leaky state in tests
KoolADE85 Apr 23, 2026
bfc61f2
Refactor unit tests to conform to existing test patterns
KoolADE85 Apr 23, 2026
8c7d720
Add decorator to make any function mcp-enabled
KoolADE85 May 4, 2026
4f7329c
Notify connected agents of server restarts and hot-reloads so they ca…
KoolADE85 May 8, 2026
cf8e9d9
Migrate MCP server to use v4.2.0 backend abstractions
KoolADE85 May 13, 2026
6c3fec8
Fix async (Quart) route registration
KoolADE85 May 13, 2026
5e47755
Fix session management with multiple workers
KoolADE85 May 13, 2026
9566de0
Fix CI errors after rebase
KoolADE85 May 13, 2026
296b7ac
code review feedback
KoolADE85 May 14, 2026
15f2111
Implement background callback support
KoolADE85 Apr 8, 2026
ee4e48c
Ensure that background callback expiry is communicated in MCP tool
KoolADE85 Apr 8, 2026
bf442f7
lint
KoolADE85 Apr 30, 2026
576145c
Install diskcache extra in lint-unit and typing CI jobs
KoolADE85 May 8, 2026
0fef9b8
Fix mypy errors in CI
KoolADE85 May 8, 2026
fd8c8d9
Fix bug storing metadata on background callbacks
KoolADE85 May 12, 2026
3cc14d8
Enable programmatic querying of background callback status/result; re…
KoolADE85 May 13, 2026
9d5fa61
Fix get_task status race and CallbackExecutionResponse type annotation
robertclaus May 15, 2026
ef778a2
code review feedback
KoolADE85 May 19, 2026
b78fe2a
Revert match/case refactor due to compatibility problems
KoolADE85 May 20, 2026
16da30a
automatically load env vars with direnv
KoolADE85 May 20, 2026
1861302
bump version and changelog
KoolADE85 May 21, 2026
3558bd3
changelog
KoolADE85 May 21, 2026
3aed371
Merge pull request #3793 from plotly/release/4.3.0rc0
KoolADE85 May 21, 2026
f1ec286
Merge remote-tracking branch 'origin/dev' into mcp
KoolADE85 Jun 2, 2026
d04009c
Implement configuration function to customize MCP server
KoolADE85 May 26, 2026
17f67ec
Fix CI test
KoolADE85 Jun 1, 2026
52b2858
Fix @mcp_enabled decorator not working if defined after the app const…
KoolADE85 Jun 1, 2026
c5c4829
Address PR feedback
KoolADE85 Jun 3, 2026
28fd89d
Merge pull request #3796 from plotly/feature/mcp-configuration-function
KoolADE85 Jun 3, 2026
63e61d1
Merge remote-tracking branch 'origin/dev' into mcp
KoolADE85 Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ for venv_dir in .venv .env venv env .venv* env* ENV; do
break
fi
done

# Include optional, local-only environment overrides
if [ -f .env ]; then
dotenv
fi
6 changes: 4 additions & 2 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ jobs:
run: |
python -m pip install --upgrade pip wheel
python -m pip install "setuptools<80.0.0"
find packages -name dash-*.whl -print -exec sh -c 'pip install "{}[dev,ci,testing]"' \;
find packages -name dash-*.whl -print -exec sh -c 'pip install "{}[dev,ci,testing,diskcache]"' \;

- name: Install dash-renderer dependencies
working-directory: dash/dash-renderer
Expand All @@ -122,6 +122,8 @@ jobs:
echo "DISPLAY=:99" >> $GITHUB_ENV

- name: Run lint
env:
PYLINT_EXTRA_ARGS: ${{ matrix.python-version == '3.8' && '--ignored-modules=mcp' || '' }}
run: npm run lint

- name: Run unit tests
Expand Down Expand Up @@ -229,7 +231,7 @@ jobs:
run: |
python -m pip install --upgrade pip wheel
python -m pip install "setuptools<80.0.0"
find packages -name dash-*.whl -print -exec sh -c 'pip install "{}[ci,testing,dev]"' \;
find packages -name dash-*.whl -print -exec sh -c 'pip install "{}[ci,testing,dev,diskcache]"' \;

- name: Build/Setup test components
run: npm run setup-tests.py # TODO build the packages and save them to packages/ in build job
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ max-returns=6
max-statements=50

# Minimum number of public methods for a class (see R0903).
min-public-methods=2
min-public-methods=1


[IMPORTS]
Expand Down
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,30 @@ This project adheres to [Semantic Versioning](https://semver.org/).

## [UNRELEASED]

### Fixed
## [4.3.0rc1] - Unreleased

## Added
- [#3796](https://github.com/plotly/dash/pull/3796) MCP: Add `configure_mcp_server()` to toggle which content the MCP server exposes (`include_layout`, `include_callbacks`, `include_clientside_callbacks`, `include_pages`, `expose_callback_docstrings`). Only the parameters explicitly passed are updated; omitted parameters retain their current value.

## Changed
- [#3796](https://github.com/plotly/dash/pull/3796) MCP: Remove the `mcp_expose_docstrings` `Dash()` constructor argument; callback docstring exposure is now controlled via `configure_mcp_server(expose_callback_docstrings=...)`.

## Fixed
- [#3805](https://github.com/plotly/dash/pull/3805) Fix FastAPI POST routes deadlock caused by middleware consuming request body. Fixes [#3801](https://github.com/plotly/dash/issues/3801).

## [4.3.0rc0] - 2026-05-21

## Added
- [#3710](https://github.com/plotly/dash/pull/3710) MCP: Framework utilities, types for interacting with layout
- [#3711](https://github.com/plotly/dash/pull/3711) MCP: `CallbackAdapter` for representing callback-related data in MCP-friendly format
- [#3712](https://github.com/plotly/dash/pull/3712) MCP: `Resources` for exposing app layout, components, and pages
- [#3731](https://github.com/plotly/dash/pull/3731) MCP: Expose callbacks as `Tools`
- [#3747](https://github.com/plotly/dash/pull/3747) MCP: Support pattern-matching callbacks in Tools
- [#3748](https://github.com/plotly/dash/pull/3748) MCP: Format callback results for LLM consumption (rendered graphs, markdown tables)
- [#3749](https://github.com/plotly/dash/pull/3749) MCP: `get_dash_component` Tool and callback execution
- [#3750](https://github.com/plotly/dash/pull/3750) MCP: Server routes, `mcp_enabled` function decorator, and Streamable HTTP transport
- [#3766](https://github.com/plotly/dash/pull/3766) MCP: Support background callbacks in Tools

## [4.2.0] - 2026-06-01 - *The Freedom Update*

This release marks a major milestone for Dash, bringing unprecedented flexibility to how you build and deploy your applications.
Expand Down
4 changes: 2 additions & 2 deletions components/dash-core-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"build:js": "webpack --mode production",
"build:backends": "dash-generate-components ./src/components dash_core_components -p package-info.json && cp dash_core_components_base/** dash_core_components/ && dash-generate-components ./src/components dash_core_components -p package-info.json -k RangeSlider,Slider,Dropdown,RadioItems,Checklist,DatePickerSingle,DatePickerRange,Input,Link --r-prefix 'dcc' --r-suggests 'dash,dashHtmlComponents,jsonlite,plotly' --jl-prefix 'dcc' && black dash_core_components",
"build": "run-s prepublishOnly build:js build:backends",
"postbuild": "es-check es2015 dash_core_components/*.js",
"postbuild": "es-check es2017 dash_core_components/*.js",
"build:watch": "watch 'npm run build' src",
"format": "run-s private::format.*",
"lint": "run-s private::lint.*"
Expand Down Expand Up @@ -126,6 +126,6 @@
"react-dom": "16 - 19"
},
"browserslist": [
"last 10 years and not dead"
"last 11 years and not dead"
]
}
2 changes: 1 addition & 1 deletion components/dash-table/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,6 @@
"npm": ">=6.1.0"
},
"browserslist": [
"last 10 years and not dead"
"last 11 years and not dead"
]
}
66 changes: 50 additions & 16 deletions dash/_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

from .background_callback.managers import BaseBackgroundCallbackManager
from ._callback_context import context_value
from .types import CallbackExecutionResponse
from ._no_update import NoUpdate
from . import _validate

Expand Down Expand Up @@ -85,6 +86,8 @@ def callback(
hidden: Optional[bool] = None,
websocket: Optional[bool] = False,
persistent: Optional[bool] = False,
mcp_enabled: Optional[bool] = None,
mcp_expose_docstring: Optional[bool] = None,
**_kwargs,
) -> Callable[[Callable[Params, ReturnVar]], Callable[Params, ReturnVar]]:
"""
Expand Down Expand Up @@ -242,6 +245,8 @@ def callback(
hidden=hidden,
websocket=websocket,
persistent=persistent,
mcp_enabled=mcp_enabled,
mcp_expose_docstring=mcp_expose_docstring,
)

return cast(
Expand Down Expand Up @@ -295,6 +300,8 @@ def insert_callback(
hidden=None,
websocket=False,
persistent=False,
mcp_enabled=None,
mcp_expose_docstring=None,
) -> str:
if prevent_initial_call is None:
prevent_initial_call = config_prevent_initial_callbacks
Expand Down Expand Up @@ -338,6 +345,8 @@ def insert_callback(
"allow_dynamic_callbacks": dynamic_creator,
"no_output": no_output,
"websocket": websocket,
"mcp_enabled": mcp_enabled,
"mcp_expose_docstring": mcp_expose_docstring,
}
callback_list.append(callback_spec)

Expand Down Expand Up @@ -453,10 +462,13 @@ def _setup_background_callback(
return to_json(data)


def _progress_background_callback(response, callback_manager, background):
def _progress_background_callback(
response, callback_manager, background, cache_key=None
):
progress_outputs = background.get("progress")
adapter = get_app().backend.request_adapter()
cache_key = adapter.args.get("cacheKey")
if cache_key is None:
adapter = get_app().backend.request_adapter()
cache_key = adapter.args.get("cacheKey")

if progress_outputs:
# Get the progress before the result as it would be erased after the results.
Expand All @@ -468,21 +480,38 @@ def _progress_background_callback(response, callback_manager, background):


def _update_background_callback(
error_handler, callback_ctx, response, kwargs, background, multi
error_handler,
callback_ctx,
response,
kwargs,
background,
multi,
cache_key=None,
job_id=None,
):
"""Set up the background callback and manage jobs."""
callback_manager = _get_callback_manager(kwargs, background)

adapter = get_app().backend.request_adapter()
cache_key = adapter.args.get("cacheKey") if adapter else None
job_id = adapter.args.get("job") if adapter else None
if cache_key is None or job_id is None:
adapter = get_app().backend.request_adapter()
cache_key = cache_key or (adapter.args.get("cacheKey") if adapter else None)
job_id = job_id or (adapter.args.get("job") if adapter else None)

_progress_background_callback(response, callback_manager, background)
_progress_background_callback(
response, callback_manager, background, cache_key=cache_key
)

output_value = callback_manager.get_result(cache_key, job_id)

return _handle_rest_background_callback(
output_value, callback_manager, response, error_handler, callback_ctx, multi
output_value,
callback_manager,
response,
error_handler,
callback_ctx,
multi,
cache_key=cache_key,
job_id=job_id,
)


Expand All @@ -494,10 +523,13 @@ def _handle_rest_background_callback(
callback_ctx,
multi,
has_update=False,
cache_key=None,
job_id=None,
):
adapter = get_app().backend.request_adapter()
cache_key = adapter.args.get("cacheKey") if adapter else None
job_id = adapter.args.get("job") if adapter else None
if cache_key is None or job_id is None:
adapter = get_app().backend.request_adapter()
cache_key = cache_key or (adapter.args.get("cacheKey") if adapter else None)
job_id = job_id or (adapter.args.get("job") if adapter else None)
# Must get job_running after get_result since get_results terminates it.
job_running = callback_manager.job_running(job_id)
if not job_running and output_value is callback_manager.UNDEFINED:
Expand Down Expand Up @@ -546,7 +578,7 @@ def _prepare_response(
output_value,
output_spec,
multi,
response,
response: CallbackExecutionResponse,
callback_ctx,
app,
original_packages,
Expand All @@ -558,7 +590,7 @@ def _prepare_response(
allow_dynamic_callbacks,
):
"""Prepare the response object based on the callback output."""
component_ids = collections.defaultdict(dict)
component_ids: dict = collections.defaultdict(dict)

if has_output:
if not multi:
Expand Down Expand Up @@ -677,6 +709,8 @@ def register_callback(
hidden=_kwargs.get("hidden", None),
websocket=_kwargs.get("websocket", False),
persistent=_kwargs.get("persistent", False),
mcp_enabled=_kwargs.get("mcp_enabled", None),
mcp_expose_docstring=_kwargs.get("mcp_expose_docstring"),
)

# pylint: disable=too-many-locals
Expand Down Expand Up @@ -711,7 +745,7 @@ def add_context(*args, **kwargs):
args, kwargs, inputs_state_indices, has_output, insert_output
)

response: dict = {"multi": True} # type: ignore
response: CallbackExecutionResponse = {"multi": True}
jsonResponse: Optional[str] = None
try:
if background is not None:
Expand Down Expand Up @@ -783,7 +817,7 @@ async def async_add_context(*args, **kwargs):
args, kwargs, inputs_state_indices, has_output, insert_output
)

response = {"multi": True}
response: CallbackExecutionResponse = {"multi": True}

try:
if background is not None:
Expand Down
2 changes: 2 additions & 0 deletions dash/_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ def load_dash_env_vars():
"DASH_DISABLE_VERSION_CHECK",
"DASH_PRUNE_ERRORS",
"DASH_COMPRESS",
"DASH_MCP_ENABLED",
"DASH_MCP_PATH",
"HOST",
"PORT",
)
Expand Down
Loading
Loading