Add Tools.aexecute_tool, which awaits async def tool callables and runs
blocking sync tools in a worker thread via asyncio.to_thread. The per-call
work (argument validation, policy evaluation, trace events, and tool-result
message building) is factored into shared helpers, so the sync execute_tool
and the new async path stay in lockstep with no duplicated tool-loop logic.
Use Pydantic JSON Schema output for callable tool parameters so annotations like list[str], dict[str, int], and optionals do not fall back to invalid stringified JSON Schema types.
The issues was due to loss of information when converting
from MCP tool spec to python callable to OpenAI spec.
Keeping the original schema intact to prevent the loss
between intermediate conversions.