Skip to content

Commit 43c15b2

Browse files
committed
Fix types for mypy
1 parent 4fc15c2 commit 43c15b2

16 files changed

Lines changed: 70 additions & 48 deletions

dash/mcp/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
from dash.mcp._server import enable_mcp_server
44

55
__all__ = [
6-
enable_mcp_server,
6+
"enable_mcp_server",
77
]

dash/mcp/_server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ def _process_mcp_message(data: dict[str, Any]) -> dict[str, Any] | None:
204204
"""
205205
method = data.get("method", "")
206206
params = data.get("params", {}) or {}
207-
request_id = data.get("id")
207+
_id = data.get("id")
208+
request_id: str | int = _id if isinstance(_id, (str, int)) else ""
208209

209210
app = get_app()
210211
if not hasattr(app, "mcp_callback_map"):

dash/mcp/primitives/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
)
1010

1111
__all__ = [
12-
call_tool,
13-
list_resources,
14-
list_resource_templates,
15-
list_tools,
16-
read_resource,
12+
"call_tool",
13+
"list_resources",
14+
"list_resource_templates",
15+
"list_tools",
16+
"read_resource",
1717
]

dash/mcp/primitives/resources/resource_clientside_callbacks.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Resource,
1111
TextResourceContents,
1212
)
13+
from pydantic import AnyUrl
1314

1415
from dash import get_app
1516
from dash._utils import clean_property_name, split_callback_id
@@ -25,7 +26,7 @@ def get_resource(cls) -> Resource | None:
2526
if not _get_clientside_callbacks():
2627
return None
2728
return Resource(
28-
uri=cls.uri,
29+
uri=AnyUrl(cls.uri),
2930
name="dash_clientside_callbacks",
3031
description=(
3132
"Actions the user can take manually in the browser "
@@ -52,7 +53,7 @@ def read_resource(cls, uri: str = "") -> ReadResourceResult:
5253
return ReadResourceResult(
5354
contents=[
5455
TextResourceContents(
55-
uri=cls.uri,
56+
uri=AnyUrl(cls.uri),
5657
mimeType="application/json",
5758
text=json.dumps(data, default=str),
5859
)

dash/mcp/primitives/resources/resource_components.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
Resource,
1010
TextResourceContents,
1111
)
12+
from pydantic import AnyUrl
1213

1314
from dash import get_app
1415
from dash._layout_utils import traverse
@@ -22,7 +23,7 @@ class ComponentsResource(MCPResourceProvider):
2223
@classmethod
2324
def get_resource(cls) -> Resource | None:
2425
return Resource(
25-
uri=cls.uri,
26+
uri=AnyUrl(cls.uri),
2627
name="dash_components",
2728
description=(
2829
"All components with IDs in the app layout. "
@@ -41,7 +42,7 @@ def read_resource(cls, uri: str = "") -> ReadResourceResult:
4142
components = sorted(
4243
[
4344
{
44-
"id": str(comp.id),
45+
"id": str(getattr(comp, "id", None)),
4546
"type": getattr(comp, "_type", type(comp).__name__),
4647
}
4748
for comp, _ in traverse(layout)
@@ -53,7 +54,7 @@ def read_resource(cls, uri: str = "") -> ReadResourceResult:
5354
return ReadResourceResult(
5455
contents=[
5556
TextResourceContents(
56-
uri=cls.uri,
57+
uri=AnyUrl(cls.uri),
5758
mimeType="application/json",
5859
text=json.dumps(components),
5960
)

dash/mcp/primitives/resources/resource_layout.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Resource,
88
TextResourceContents,
99
)
10+
from pydantic import AnyUrl
1011

1112
from dash import get_app
1213
from dash._utils import to_json
@@ -20,7 +21,7 @@ class LayoutResource(MCPResourceProvider):
2021
@classmethod
2122
def get_resource(cls) -> Resource | None:
2223
return Resource(
23-
uri=cls.uri,
24+
uri=AnyUrl(cls.uri),
2425
name="dash_app_layout",
2526
description=(
2627
"Full component tree of the Dash app. "
@@ -35,7 +36,7 @@ def read_resource(cls, uri: str = "") -> ReadResourceResult:
3536
return ReadResourceResult(
3637
contents=[
3738
TextResourceContents(
38-
uri=cls.uri,
39+
uri=AnyUrl(cls.uri),
3940
mimeType="application/json",
4041
text=to_json(app.get_layout()),
4142
)

dash/mcp/primitives/resources/resource_page_layout.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
ResourceTemplate,
88
TextResourceContents,
99
)
10+
from pydantic import AnyUrl
1011

1112
from dash._pages import PAGE_REGISTRY
1213
from dash._utils import to_json
@@ -56,7 +57,7 @@ def read_resource(cls, uri: str) -> ReadResourceResult:
5657
return ReadResourceResult(
5758
contents=[
5859
TextResourceContents(
59-
uri=uri,
60+
uri=AnyUrl(uri),
6061
mimeType="application/json",
6162
text=to_json(page_layout),
6263
)

dash/mcp/primitives/resources/resource_pages.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
Resource,
1010
TextResourceContents,
1111
)
12+
from pydantic import AnyUrl
1213

1314
from dash._pages import PAGE_REGISTRY
1415

@@ -23,7 +24,7 @@ def get_resource(cls) -> Resource | None:
2324
if not PAGE_REGISTRY:
2425
return None
2526
return Resource(
26-
uri=cls.uri,
27+
uri=AnyUrl(cls.uri),
2728
name="dash_app_pages",
2829
description=(
2930
"List of all pages in this multi-page Dash app "
@@ -51,7 +52,7 @@ def read_resource(cls, uri: str = "") -> ReadResourceResult:
5152
return ReadResourceResult(
5253
contents=[
5354
TextResourceContents(
54-
uri=cls.uri,
55+
uri=AnyUrl(cls.uri),
5556
mimeType="application/json",
5657
text=json.dumps(pages, default=str),
5758
)

dash/mcp/primitives/tools/callback_adapter.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import json
1111
import typing
1212
from functools import cached_property
13-
from typing import Any
13+
from typing import Any, cast
1414

1515
from mcp.types import Tool
1616

@@ -27,6 +27,7 @@
2727
CallbackDependency,
2828
CallbackExecutionBody,
2929
CallbackInput,
30+
CallbackInputs,
3031
CallbackOutput,
3132
CallbackOutputTarget,
3233
WildcardId,
@@ -360,9 +361,7 @@ def _param_annotations(self) -> list[Any | None]:
360361
return [hints.get(func_name) for func_name, _ in self._dep_param_map]
361362

362363

363-
def _expand_dep(
364-
dep: CallbackDependency, value: Any
365-
) -> CallbackInput | list[CallbackInput]:
364+
def _expand_dep(dep: CallbackDependency, value: Any) -> CallbackInputs:
366365
"""Attach a concrete value to a callback dependency to produce a valid callback input.
367366
368367
For regular deps, returns ``{id, property, value}``.
@@ -371,20 +370,20 @@ def _expand_dep(
371370
"""
372371
pattern = parse_wildcard_id(dep.get("id", ""))
373372
if pattern is None:
374-
return {**dep, "value": value}
373+
return CallbackInput(id=dep["id"], property=dep["property"], value=value)
375374

376375
# LLM provides browser-like format
377376
if isinstance(value, list):
378-
return value
377+
return cast(list[CallbackInput], value)
379378
if isinstance(value, dict) and "id" in value:
380-
return value
381-
return {**dep, "value": value}
379+
return cast(CallbackInput, value)
380+
return CallbackInput(id=dep["id"], property=dep["property"], value=value)
382381

383382

384383
def _expand_output_spec(
385384
output_id: str,
386385
cb_info: dict,
387-
resolved_inputs: list[CallbackInput],
386+
resolved_inputs: list[CallbackInputs],
388387
) -> CallbackOutputTarget | list[CallbackOutputTarget]:
389388
"""Build the outputs spec, expanding wildcards to concrete IDs.
390389
@@ -407,15 +406,19 @@ def _expand_output_spec(
407406
if pattern is not None:
408407
concrete_ids = _derive_output_ids(pattern, resolved_inputs)
409408
if not concrete_ids:
410-
concrete_ids = [comp.id for comp in find_matching_components(pattern)]
411-
expanded = [{"id": cid, "property": prop} for cid in concrete_ids]
409+
concrete_ids = [
410+
getattr(comp, "id") for comp in find_matching_components(pattern)
411+
]
412+
expanded: list[CallbackDependency] = [
413+
CallbackDependency(id=cid, property=prop) for cid in concrete_ids
414+
]
412415
# ALL/ALLSMALLER → nested list; MATCH → single dict
413416
if len(expanded) == 1:
414417
results.append(expanded[0])
415418
else:
416419
results.append(expanded)
417420
else:
418-
results.append({"id": pid, "property": prop})
421+
results.append(CallbackDependency(id=pid, property=prop))
419422

420423
# Mirror the Dash renderer: single-output callbacks send a bare dict,
421424
# multi-output callbacks send a list. The framework's output value
@@ -427,7 +430,7 @@ def _expand_output_spec(
427430

428431
def _derive_output_ids(
429432
output_pattern: WildcardId,
430-
resolved_inputs: list[CallbackInput],
433+
resolved_inputs: list[CallbackInputs],
431434
) -> list[WildcardId] | None:
432435
"""Derive concrete output IDs from the resolved input entries.
433436
@@ -456,15 +459,19 @@ def _substitute(item_id: WildcardId) -> WildcardId | None:
456459
if isinstance(entry, list) and entry:
457460
concrete_ids = []
458461
for item in entry:
459-
out = _substitute(item.get("id"))
460-
if out:
461-
concrete_ids.append(out)
462+
item_id = item.get("id")
463+
if isinstance(item_id, dict):
464+
out = _substitute(item_id)
465+
if out:
466+
concrete_ids.append(out)
462467
if concrete_ids:
463468
return concrete_ids
464469
# MATCH: single {id, property, value} dict
465-
elif isinstance(entry, dict) and isinstance(entry.get("id"), dict):
466-
out = _substitute(entry["id"])
467-
if out:
468-
return [out]
470+
elif isinstance(entry, dict):
471+
entry_id = entry.get("id")
472+
if isinstance(entry_id, dict):
473+
out = _substitute(entry_id)
474+
if out:
475+
return [out]
469476

470477
return None

dash/mcp/primitives/tools/input_schemas/input_descriptions/description_pattern_matching.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def describe(cls, param: MCPInput) -> list[str]:
2323
return []
2424

2525
wildcard_key, wildcard_type = _find_wildcard(dep_id)
26-
if wildcard_key is None:
26+
if wildcard_key is None or wildcard_type is None:
2727
return []
2828

2929
non_wildcard = {k: v for k, v in dep_id.items() if k != wildcard_key}

0 commit comments

Comments
 (0)