This issue is for a: (mark with an x)
- [ ] bug report -> please search issues before submitting
- [x] feature request
- [ ] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)
Minimal steps to reproduce
N/A — code-quality / tech-debt follow-up, not a runtime bug.
Any log messages given by the failure
N/A
Expected/desired behavior
Strengthen type annotations across the runtime so defensive getattr(...) access and loose Any typing can be replaced with direct, statically-verified attribute access wherever the type is knowable. Annotate framework-return boundaries with concrete Microsoft Agent Framework types (AgentResponse, FunctionTool, etc.), tighten our own signatures/returns/fields, and remove getattr/or <default> fallbacks that strong typing renders dead — keeping mypy --strict clean with no new # type: ignore.
OS and Version?
N/A (all platforms)
Versions
agent-framework-core==1.3.* (ships py.typed; azure_functions_agents.* already runs mypy strict = true).
Mention any other details that might be useful
Context. Surfaced during the FRD 0006 multi-agent delegation work. The new delegation code (delegate path in runner.py, config SubagentRef/validation, registration/catalog.py, app.py composition) was already brought to strict typing there; this issue tracks the remaining pre-existing footprint across the rest of the runtime, deliberately deferred rather than bundled into the delegation PR to keep it reviewable.
Rough scope (src/ only, ~40 files): ~50 getattr( and ~250 Any annotations. Highest-density: runner.py (~20 getattr / ~48 Any), registration/_handlers.py (8/17), registration/_trigger_serialization.py (5/5), workflows/tools.py (4/15), registration/endpoints.py (1/18), _function_tool.py (1/21), config/validation.py (3/1), registration/capabilities.py (2/7), _observability.py (0/14), _blob_history.py (0/12).
Judgment-heavy, not mechanical — much of the Any is legitimate and should stay:
- Arbitrary user JSON/config (
dict[str, Any]: args, workflows, metadata, input_schema/response_schema, TRIGGER_TYPES).
- MAF tool union
ToolTypes = FunctionTool | MCPTool | Mapping[str, Any] | object (the trailing object makes list[Any] accurate, not under-typing).
- Dynamic MAF
Contents union parsing (needs isinstance dispatch).
- External libs without stubs (e.g.
azure.durable_functions -> workflow_durable_client: Any).
Suggested approach: own dedicated PR (not folded into feature PRs); type at framework-return boundaries rather than scattering cast(); keep typed guards where values are genuinely dynamic; gate on mypy --strict for touched files, no behavior change.
This issue is for a: (mark with an
x)Minimal steps to reproduce
Any log messages given by the failure
Expected/desired behavior
OS and Version?
Versions
Mention any other details that might be useful
Context. Surfaced during the FRD 0006 multi-agent delegation work. The new delegation code (delegate path in
runner.py,configSubagentRef/validation,registration/catalog.py,app.pycomposition) was already brought to strict typing there; this issue tracks the remaining pre-existing footprint across the rest of the runtime, deliberately deferred rather than bundled into the delegation PR to keep it reviewable.Rough scope (src/ only, ~40 files): ~50
getattr(and ~250Anyannotations. Highest-density:runner.py(~20 getattr / ~48 Any),registration/_handlers.py(8/17),registration/_trigger_serialization.py(5/5),workflows/tools.py(4/15),registration/endpoints.py(1/18),_function_tool.py(1/21),config/validation.py(3/1),registration/capabilities.py(2/7),_observability.py(0/14),_blob_history.py(0/12).Judgment-heavy, not mechanical — much of the
Anyis legitimate and should stay:dict[str, Any]:args,workflows,metadata,input_schema/response_schema,TRIGGER_TYPES).ToolTypes = FunctionTool | MCPTool | Mapping[str, Any] | object(the trailingobjectmakeslist[Any]accurate, not under-typing).Contentsunion parsing (needsisinstancedispatch).azure.durable_functions->workflow_durable_client: Any).Suggested approach: own dedicated PR (not folded into feature PRs); type at framework-return boundaries rather than scattering
cast(); keep typed guards where values are genuinely dynamic; gate onmypy --strictfor touched files, no behavior change.