* fix(mcp): keep ToolRuntime injection for sync-wrapped MCP tools
make_sync_tool_wrapper attached an annotation-less wrapper to tool.func,
which made LangGraph's ToolNode stop detecting the coroutine's
"runtime" parameter (_get_all_injected_args falls back to func first
and its type hints are empty). Every MCP tool in a sync agent caller
then ran with runtime=None: resolve_runtime_user_id fell through to the
default user, and the background-submit wrapper lost run_id/tool_call_id
on the TaskSubmitRequest, so completion notifications launched under the
default lead agent instead of the thread's agent.
Wrap the generator and both sync_wrapper variants with functools.wraps
so get_type_hints still sees the original annotations.
Adds a regression test that drives a func-patched pooled MCP tool
through a real ToolNode and asserts the ToolRuntime is injected with
the thread's user context. It fails on main (runtime=None) and passes
with the fix.
* docs(mcp): record sync-wrapper annotation contract; extend regression coverage
Address review feedback on #5164:
- Expand the Notes block in make_sync_tool_wrapper to state the functools.wraps
contract (copies __name__/__qualname__/__doc__/__annotations__/__dict__ and
sets __wrapped__) and why that is what keeps get_type_hints resolving string
annotations from callers like
mcp/tools.py and skill_manage_tool.py. Drop the no-op wraps on the inner
run_coroutine so the wrap surface stays minimal.
- Rename the regression test to test_func_patched_mcp_tool_keeps_toolnode_runtime_injection.
- Add test_sync_wrapped_builtin_tools_still_resolve_runtime to pin that the
built-in tools (which carry runtime as a pydantic schema field) keep resolving
runtime after their func is wrapped by make_sync_tool_wrapper, so a future
wrapper refactor cannot silently regress per-user resolution for them.